@bike4mind/cli 0.2.11 → 0.2.12-ctrl-c-clear-input.17412
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/README.md +21 -0
- package/dist/{ImageStore-RQZ7OF7P.js → ImageStore-MMUOUPI2.js} +0 -1
- package/dist/{artifactExtractor-F2VLARCK.js → artifactExtractor-URGQJDMT.js} +0 -1
- package/dist/{chunk-RJFNIMVH.js → chunk-7N7AZPLK.js} +1 -1
- package/dist/chunk-BDQBOLYG.js +0 -0
- package/dist/chunk-BPFEGDC7.js +0 -0
- package/dist/{chunk-BU5TP3BH.js → chunk-INYINU6X.js} +1 -1
- package/dist/{chunk-ID4NS6KF.js → chunk-JXROCVSM.js} +1 -1
- package/dist/{chunk-AMDXHL6S.js → chunk-OCYRD7D6.js} +1 -1
- package/dist/chunk-OHR7UCTC.js +0 -0
- package/dist/{chunk-ZHCX7BLB.js → chunk-XM52K32A.js} +1 -1
- package/dist/{create-MXBWYABO.js → create-IGUAAYLC.js} +3 -4
- package/dist/{formatConverter-I7EIUVDY.js → formatConverter-5QEJDW24.js} +0 -1
- package/dist/index.js +709 -379
- package/dist/{llmMarkdownGenerator-AVPOXVE5.js → llmMarkdownGenerator-QLIULZ4H.js} +0 -1
- package/dist/{markdownGenerator-FBXMTP3B.js → markdownGenerator-WE5PLZRI.js} +0 -1
- package/dist/{mementoService-4WLVEWPZ.js → mementoService-OGW5IEJX.js} +3 -4
- package/dist/{notificationDeduplicator-UW7D75QS.js → notificationDeduplicator-UTHJHMSZ.js} +1 -2
- package/dist/{src-YU4VN5YG.js → src-557ODV2Y.js} +2 -3
- package/dist/{src-SYE7WXBJ.js → src-7JPWO5SS.js} +0 -1
- package/dist/{subtractCredits-FGISXF2D.js → subtractCredits-QSGEUH6U.js} +3 -4
- package/dist/{utils-JPMDGUBL.js → utils-JCHWDM4Z.js} +0 -1
- package/package.json +20 -16
- package/dist/chunk-PDX44BCA.js +0 -11
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
getEffectiveApiKey,
|
|
5
5
|
getOpenWeatherKey,
|
|
6
6
|
getSerperKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-XM52K32A.js";
|
|
8
|
+
import "./chunk-JXROCVSM.js";
|
|
9
|
+
import "./chunk-7N7AZPLK.js";
|
|
10
10
|
import {
|
|
11
11
|
BFLImageService,
|
|
12
12
|
BaseStorage,
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
OpenAIBackend,
|
|
16
16
|
OpenAIImageService,
|
|
17
17
|
XAIImageService
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-INYINU6X.js";
|
|
19
19
|
import {
|
|
20
20
|
Logger
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-OCYRD7D6.js";
|
|
22
22
|
import "./chunk-BPFEGDC7.js";
|
|
23
23
|
import {
|
|
24
24
|
AiEvents,
|
|
@@ -74,9 +74,6 @@ import {
|
|
|
74
74
|
b4mLLMTools,
|
|
75
75
|
getMcpProviderMetadata
|
|
76
76
|
} from "./chunk-OHR7UCTC.js";
|
|
77
|
-
import {
|
|
78
|
-
__require
|
|
79
|
-
} from "./chunk-PDX44BCA.js";
|
|
80
77
|
|
|
81
78
|
// src/index.tsx
|
|
82
79
|
import React17, { useState as useState8, useEffect as useEffect3, useCallback, useRef as useRef2 } from "react";
|
|
@@ -182,11 +179,11 @@ function CustomTextInput({
|
|
|
182
179
|
setCursorOffset(Math.min(value.length, cursorOffset + 1));
|
|
183
180
|
return;
|
|
184
181
|
}
|
|
185
|
-
if (!key.ctrl && !key.meta && input.length
|
|
182
|
+
if (!key.ctrl && !key.meta && input.length > 0) {
|
|
186
183
|
const sanitizedInput = input === "\r" ? "\n" : input;
|
|
187
184
|
const newValue = value.slice(0, cursorOffset) + sanitizedInput + value.slice(cursorOffset);
|
|
188
185
|
onChange(newValue);
|
|
189
|
-
setCursorOffset(cursorOffset +
|
|
186
|
+
setCursorOffset(cursorOffset + sanitizedInput.length);
|
|
190
187
|
}
|
|
191
188
|
},
|
|
192
189
|
{ isActive: !disabled }
|
|
@@ -713,7 +710,7 @@ var ImageInputDetector = class {
|
|
|
713
710
|
if (!this.looksLikeFilePath(input)) return null;
|
|
714
711
|
let filepath = input.trim();
|
|
715
712
|
filepath = filepath.replace(/^["']|["']$/g, "");
|
|
716
|
-
filepath = filepath.replace(
|
|
713
|
+
filepath = filepath.replace(/\\(.)/g, "$1");
|
|
717
714
|
if (!existsSync(filepath)) return null;
|
|
718
715
|
const stats = statSync2(filepath);
|
|
719
716
|
if (!stats.isFile()) return null;
|
|
@@ -740,7 +737,15 @@ var ImageInputDetector = class {
|
|
|
740
737
|
*/
|
|
741
738
|
static looksLikeFilePath(input) {
|
|
742
739
|
const trimmed = input.trim();
|
|
743
|
-
if (trimmed.startsWith("/")
|
|
740
|
+
if (trimmed.startsWith("/")) {
|
|
741
|
+
const hasMultipleSegments = trimmed.indexOf("/", 1) !== -1;
|
|
742
|
+
const hasImageExtension = this.SUPPORTED_EXTENSIONS.some((ext) => trimmed.toLowerCase().endsWith(ext));
|
|
743
|
+
if (hasMultipleSegments || hasImageExtension) {
|
|
744
|
+
return true;
|
|
745
|
+
}
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
if (trimmed.match(/^[a-zA-Z]:\\/)) {
|
|
744
749
|
return true;
|
|
745
750
|
}
|
|
746
751
|
if (trimmed.startsWith("~")) {
|
|
@@ -768,7 +773,74 @@ var ImageInputDetector = class {
|
|
|
768
773
|
}
|
|
769
774
|
};
|
|
770
775
|
|
|
776
|
+
// src/store/index.ts
|
|
777
|
+
import { create } from "zustand";
|
|
778
|
+
var useCliStore = create((set) => ({
|
|
779
|
+
// Session state
|
|
780
|
+
session: null,
|
|
781
|
+
setSession: (session) => set({ session }),
|
|
782
|
+
addMessage: (message) => set((state) => {
|
|
783
|
+
if (!state.session) return state;
|
|
784
|
+
return {
|
|
785
|
+
session: {
|
|
786
|
+
...state.session,
|
|
787
|
+
messages: [...state.session.messages, message],
|
|
788
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
}),
|
|
792
|
+
// Pending messages
|
|
793
|
+
pendingMessages: [],
|
|
794
|
+
addPendingMessage: (message) => set((state) => ({ pendingMessages: [...state.pendingMessages, message] })),
|
|
795
|
+
updatePendingMessage: (index, message) => set((state) => {
|
|
796
|
+
const updated = [...state.pendingMessages];
|
|
797
|
+
updated[index] = message;
|
|
798
|
+
return { pendingMessages: updated };
|
|
799
|
+
}),
|
|
800
|
+
clearPendingMessages: () => set({ pendingMessages: [] }),
|
|
801
|
+
completePendingMessage: (index, finalMessage) => set((state) => {
|
|
802
|
+
const pending = [...state.pendingMessages];
|
|
803
|
+
pending.splice(index, 1);
|
|
804
|
+
const session = state.session;
|
|
805
|
+
if (!session) return { pendingMessages: pending };
|
|
806
|
+
return {
|
|
807
|
+
pendingMessages: pending,
|
|
808
|
+
session: {
|
|
809
|
+
...session,
|
|
810
|
+
messages: [...session.messages, finalMessage],
|
|
811
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
}),
|
|
815
|
+
// UI state
|
|
816
|
+
isThinking: false,
|
|
817
|
+
setIsThinking: (thinking) => set({ isThinking: thinking }),
|
|
818
|
+
// Input state (for Ctrl+C clearing)
|
|
819
|
+
inputValue: "",
|
|
820
|
+
setInputValue: (value) => set({ inputValue: value }),
|
|
821
|
+
clearInput: () => set({ inputValue: "" }),
|
|
822
|
+
// Permission prompt
|
|
823
|
+
permissionPrompt: null,
|
|
824
|
+
setPermissionPrompt: (prompt) => set({ permissionPrompt: prompt }),
|
|
825
|
+
// Config editor
|
|
826
|
+
showConfigEditor: false,
|
|
827
|
+
setShowConfigEditor: (show) => set({ showConfigEditor: show }),
|
|
828
|
+
// Exit handling
|
|
829
|
+
exitRequested: false,
|
|
830
|
+
setExitRequested: (requested) => set({ exitRequested: requested })
|
|
831
|
+
}));
|
|
832
|
+
|
|
771
833
|
// src/components/InputPrompt.tsx
|
|
834
|
+
function looksLikeFilePath(input) {
|
|
835
|
+
const trimmed = input.trim();
|
|
836
|
+
if (trimmed.startsWith("/")) {
|
|
837
|
+
const hasMultipleSegments = trimmed.indexOf("/", 1) !== -1;
|
|
838
|
+
if (hasMultipleSegments) return true;
|
|
839
|
+
}
|
|
840
|
+
if (trimmed.startsWith("~")) return true;
|
|
841
|
+
if (trimmed.match(/^[a-zA-Z]:\\/)) return true;
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
772
844
|
function findAtTrigger(value) {
|
|
773
845
|
for (let i = value.length - 1; i >= 0; i--) {
|
|
774
846
|
if (value[i] === "@") {
|
|
@@ -795,7 +867,9 @@ function InputPrompt({
|
|
|
795
867
|
onPrefillConsumed,
|
|
796
868
|
onBashModeChange
|
|
797
869
|
}) {
|
|
798
|
-
const
|
|
870
|
+
const value = useCliStore((state) => state.inputValue);
|
|
871
|
+
const setInputValue = useCliStore((state) => state.setInputValue);
|
|
872
|
+
const setValue = setInputValue;
|
|
799
873
|
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
800
874
|
const [historyIndex, setHistoryIndex] = useState2(-1);
|
|
801
875
|
const [tempInput, setTempInput] = useState2("");
|
|
@@ -812,7 +886,7 @@ function InputPrompt({
|
|
|
812
886
|
useEffect(() => {
|
|
813
887
|
onBashModeChange?.(isBashMode);
|
|
814
888
|
}, [isBashMode, onBashModeChange]);
|
|
815
|
-
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
889
|
+
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active && !looksLikeFilePath(value);
|
|
816
890
|
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
817
891
|
const filteredCommands = useMemo(() => {
|
|
818
892
|
if (!shouldShowCommandAutocomplete) return [];
|
|
@@ -851,10 +925,11 @@ function InputPrompt({
|
|
|
851
925
|
if (shouldShowCommandAutocomplete && filteredCommands.length > 0) {
|
|
852
926
|
if (key.upArrow) {
|
|
853
927
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredCommands.length - 1);
|
|
928
|
+
return;
|
|
854
929
|
} else if (key.downArrow) {
|
|
855
930
|
setSelectedIndex((prev) => prev < filteredCommands.length - 1 ? prev + 1 : 0);
|
|
931
|
+
return;
|
|
856
932
|
}
|
|
857
|
-
return;
|
|
858
933
|
}
|
|
859
934
|
if (!shouldShowCommandAutocomplete && !fileAutocomplete?.active && history.length > 0) {
|
|
860
935
|
if (key.upArrow) {
|
|
@@ -992,59 +1067,6 @@ var ThoughtStream = React6.memo(function ThoughtStream2({ isThinking }) {
|
|
|
992
1067
|
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", gap: 1 }, isThinking && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Thinking...")));
|
|
993
1068
|
});
|
|
994
1069
|
|
|
995
|
-
// src/store/index.ts
|
|
996
|
-
import { create } from "zustand";
|
|
997
|
-
var useCliStore = create((set) => ({
|
|
998
|
-
// Session state
|
|
999
|
-
session: null,
|
|
1000
|
-
setSession: (session) => set({ session }),
|
|
1001
|
-
addMessage: (message) => set((state) => {
|
|
1002
|
-
if (!state.session) return state;
|
|
1003
|
-
return {
|
|
1004
|
-
session: {
|
|
1005
|
-
...state.session,
|
|
1006
|
-
messages: [...state.session.messages, message],
|
|
1007
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
}),
|
|
1011
|
-
// Pending messages
|
|
1012
|
-
pendingMessages: [],
|
|
1013
|
-
addPendingMessage: (message) => set((state) => ({ pendingMessages: [...state.pendingMessages, message] })),
|
|
1014
|
-
updatePendingMessage: (index, message) => set((state) => {
|
|
1015
|
-
const updated = [...state.pendingMessages];
|
|
1016
|
-
updated[index] = message;
|
|
1017
|
-
return { pendingMessages: updated };
|
|
1018
|
-
}),
|
|
1019
|
-
clearPendingMessages: () => set({ pendingMessages: [] }),
|
|
1020
|
-
completePendingMessage: (index, finalMessage) => set((state) => {
|
|
1021
|
-
const pending = [...state.pendingMessages];
|
|
1022
|
-
pending.splice(index, 1);
|
|
1023
|
-
const session = state.session;
|
|
1024
|
-
if (!session) return { pendingMessages: pending };
|
|
1025
|
-
return {
|
|
1026
|
-
pendingMessages: pending,
|
|
1027
|
-
session: {
|
|
1028
|
-
...session,
|
|
1029
|
-
messages: [...session.messages, finalMessage],
|
|
1030
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
|
-
}),
|
|
1034
|
-
// UI state
|
|
1035
|
-
isThinking: false,
|
|
1036
|
-
setIsThinking: (thinking) => set({ isThinking: thinking }),
|
|
1037
|
-
// Permission prompt
|
|
1038
|
-
permissionPrompt: null,
|
|
1039
|
-
setPermissionPrompt: (prompt) => set({ permissionPrompt: prompt }),
|
|
1040
|
-
// Config editor
|
|
1041
|
-
showConfigEditor: false,
|
|
1042
|
-
setShowConfigEditor: (show) => set({ showConfigEditor: show }),
|
|
1043
|
-
// Exit handling
|
|
1044
|
-
exitRequested: false,
|
|
1045
|
-
setExitRequested: (requested) => set({ exitRequested: requested })
|
|
1046
|
-
}));
|
|
1047
|
-
|
|
1048
1070
|
// src/components/AgentThinking.tsx
|
|
1049
1071
|
var AgentThinking = React7.memo(function AgentThinking2() {
|
|
1050
1072
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
@@ -1333,7 +1355,7 @@ function MessageItem({ message }) {
|
|
|
1333
1355
|
const isUser = message.role === "user";
|
|
1334
1356
|
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), isUser && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1335
1357
|
if (step.type === "thought") {
|
|
1336
|
-
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content
|
|
1358
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content}`));
|
|
1337
1359
|
}
|
|
1338
1360
|
if (step.type === "action") {
|
|
1339
1361
|
const toolName = step.metadata?.toolName || "unknown";
|
|
@@ -1358,7 +1380,7 @@ function isNameSuffix(value) {
|
|
|
1358
1380
|
|
|
1359
1381
|
// src/utils/processFileReferences.ts
|
|
1360
1382
|
var FILE_REFERENCE_REGEX = /(?:^|\s)@([^\s@]+)/g;
|
|
1361
|
-
function
|
|
1383
|
+
function looksLikeFilePath2(ref) {
|
|
1362
1384
|
if (ref.includes("/") || ref.includes(path2.sep)) {
|
|
1363
1385
|
return true;
|
|
1364
1386
|
}
|
|
@@ -1381,7 +1403,7 @@ function extractFileReferences(message) {
|
|
|
1381
1403
|
let match;
|
|
1382
1404
|
while ((match = FILE_REFERENCE_REGEX.exec(message)) !== null) {
|
|
1383
1405
|
const ref = match[1];
|
|
1384
|
-
if (
|
|
1406
|
+
if (looksLikeFilePath2(ref)) {
|
|
1385
1407
|
references.push(ref);
|
|
1386
1408
|
}
|
|
1387
1409
|
}
|
|
@@ -3093,6 +3115,21 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3093
3115
|
let finalAnswer = "";
|
|
3094
3116
|
let reachedMaxIterations = false;
|
|
3095
3117
|
while (iterations < maxIterations) {
|
|
3118
|
+
if (options.signal?.aborted) {
|
|
3119
|
+
this.context.logger.info("[ReActAgent] Operation aborted by user");
|
|
3120
|
+
const result2 = {
|
|
3121
|
+
finalAnswer: "Interrupted",
|
|
3122
|
+
steps: this.steps,
|
|
3123
|
+
completionInfo: {
|
|
3124
|
+
totalTokens: this.totalTokens,
|
|
3125
|
+
iterations,
|
|
3126
|
+
toolCalls: this.toolCallCount,
|
|
3127
|
+
reachedMaxIterations: false
|
|
3128
|
+
}
|
|
3129
|
+
};
|
|
3130
|
+
this.emit("complete", result2);
|
|
3131
|
+
return result2;
|
|
3132
|
+
}
|
|
3096
3133
|
iterations++;
|
|
3097
3134
|
this.context.logger.debug(`[ReActAgent] Starting iteration ${iterations}/${maxIterations}`);
|
|
3098
3135
|
let iterationComplete = false;
|
|
@@ -3106,7 +3143,8 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3106
3143
|
stream: false,
|
|
3107
3144
|
tools: this.context.tools,
|
|
3108
3145
|
maxTokens,
|
|
3109
|
-
temperature
|
|
3146
|
+
temperature,
|
|
3147
|
+
abortSignal: options.signal
|
|
3110
3148
|
},
|
|
3111
3149
|
async (texts, completionInfo) => {
|
|
3112
3150
|
for (const text of texts) {
|
|
@@ -3249,6 +3287,9 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3249
3287
|
this.emit("final_answer", finalStep);
|
|
3250
3288
|
}
|
|
3251
3289
|
}
|
|
3290
|
+
if (options.signal?.aborted && !finalAnswer) {
|
|
3291
|
+
finalAnswer = "Interrupted";
|
|
3292
|
+
}
|
|
3252
3293
|
const result = {
|
|
3253
3294
|
finalAnswer,
|
|
3254
3295
|
steps: this.steps,
|
|
@@ -3356,6 +3397,70 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
3356
3397
|
}
|
|
3357
3398
|
};
|
|
3358
3399
|
|
|
3400
|
+
// src/core/prompts.ts
|
|
3401
|
+
var TOOL_GREP_SEARCH = "grep_search";
|
|
3402
|
+
var TOOL_GLOB_FILES = "glob_files";
|
|
3403
|
+
var TOOL_FILE_READ = "file_read";
|
|
3404
|
+
var TOOL_EDIT_LOCAL_FILE = "edit_local_file";
|
|
3405
|
+
var TOOL_CREATE_FILE = "create_file";
|
|
3406
|
+
var TOOL_BASH_EXECUTE = "bash_execute";
|
|
3407
|
+
var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
|
|
3408
|
+
var TOOL_WRITE_TODOS = "write_todos";
|
|
3409
|
+
var EXPLORE_SUBAGENT_TYPE = "explore";
|
|
3410
|
+
function buildCoreSystemPrompt(contextSection = "") {
|
|
3411
|
+
return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
|
|
3412
|
+
|
|
3413
|
+
CORE BEHAVIOR:
|
|
3414
|
+
- Be proactive: Take action instead of asking for permission or clarification
|
|
3415
|
+
- Make smart assumptions: If unclear, choose the most reasonable interpretation
|
|
3416
|
+
- Use conversation history: Reference previous exchanges to understand context
|
|
3417
|
+
- Complete tasks fully: Don't just show what to do - actually do it
|
|
3418
|
+
|
|
3419
|
+
FOR SOFTWARE ENGINEERING TASKS:
|
|
3420
|
+
When requested to perform tasks like fixing bugs, adding features, refactoring, or explaining code, follow this sequence:
|
|
3421
|
+
1. **Understand:** Think about the user's request and the relevant codebase context. Use '${TOOL_GREP_SEARCH}' and '${TOOL_GLOB_FILES}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${TOOL_FILE_READ}' to understand context and validate any assumptions you may have. If you need to read multiple files, you should make multiple parallel calls to '${TOOL_FILE_READ}'.
|
|
3422
|
+
When the task involves **complex refactoring, codebase exploration or system-wide analysis**, your **first and primary action** must be to delegate to the '${EXPLORE_SUBAGENT_TYPE}' agent using the '${TOOL_SUBAGENT_DELEGATE}' tool. Use it to build a comprehensive understanding of the code, its structure, and dependencies. For **simple, targeted searches** (like finding a specific function name, file path, or variable declaration), you should use '${TOOL_GREP_SEARCH}' or '${TOOL_GLOB_FILES}' directly.
|
|
3423
|
+
2. **Plan:** Build a coherent and grounded (based on the understanding in step 1) plan for how you intend to resolve the user's task. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.
|
|
3424
|
+
If '${EXPLORE_SUBAGENT_TYPE}' subagent was used, do not ignore the output of the agent, you must use it as the foundation of your plan. For complex tasks, break them down into smaller, manageable subtasks and use the \`${TOOL_WRITE_TODOS}\` tool to track your progress. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.
|
|
3425
|
+
3. **Implement:** Use the available tools (e.g., '${TOOL_EDIT_LOCAL_FILE}', '${TOOL_CREATE_FILE}', '${TOOL_BASH_EXECUTE}' ...) to act on the plan, strictly adhering to the project's established conventions.
|
|
3426
|
+
4. **Verify (Tests):** If applicable and feasible, verify the changes using the project's testing procedures. Identify the correct test commands and frameworks by examining 'README' files, build/package configuration (e.g., 'package.json'), or existing test execution patterns. NEVER assume standard test commands. When executing test commands, prefer "run once" or "CI" modes to ensure the command terminates after completion.
|
|
3427
|
+
5. **Verify (Standards):** VERY IMPORTANT: After making code changes, execute the project-specific build, linting and type-checking commands (e.g., 'tsc', 'npm run lint', 'ruff check .') that you have identified for this project (or obtained from the user). This ensures code quality and adherence to standards.
|
|
3428
|
+
6. **Finalize:** After all verification passes, consider the task complete. Do not remove or revert any changes or created files (like tests). Await the user's next instruction.
|
|
3429
|
+
|
|
3430
|
+
SUBAGENT DELEGATION:
|
|
3431
|
+
- You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
|
|
3432
|
+
- Use subagents for focused exploration, planning, or review tasks:
|
|
3433
|
+
* explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
|
|
3434
|
+
* plan: Break down complex tasks into actionable steps
|
|
3435
|
+
* review: Analyze code quality and identify issues
|
|
3436
|
+
- Subagents keep the main conversation clean and run faster with optimized models
|
|
3437
|
+
- Delegate when you need to search extensively or analyze code structure
|
|
3438
|
+
|
|
3439
|
+
FOR GENERAL TASKS:
|
|
3440
|
+
- Use available tools to get information (weather, web search, calculations, etc.)
|
|
3441
|
+
- When user asks follow-up questions, use conversation context to understand what they're referring to
|
|
3442
|
+
- If user asks "how about X?" after a previous question, apply the same question type to X
|
|
3443
|
+
|
|
3444
|
+
## Shell tool output token efficiency:
|
|
3445
|
+
IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
|
|
3446
|
+
|
|
3447
|
+
- Always prefer command flags that reduce output verbosity when using '${TOOL_BASH_EXECUTE}'.
|
|
3448
|
+
- Aim to minimize tool output tokens while still capturing necessary information.
|
|
3449
|
+
- If a command is expected to produce a lot of output, use quiet or silent flags where available and appropriate.
|
|
3450
|
+
- Always consider the trade-off between output verbosity and the need for information. If a command's full output is essential for understanding the result, avoid overly aggressive quieting that might obscure important details.
|
|
3451
|
+
- If a command does not have quiet/silent flags or for commands with potentially long output that may not be useful, redirect stdout and stderr to temp files in the project's temporary directory. For example: 'command > <temp_dir>/out.log 2> <temp_dir>/err.log'.
|
|
3452
|
+
- After the command runs, inspect the temp files (e.g. '<temp_dir>/out.log' and '<temp_dir>/err.log') using commands like 'grep', 'tail', 'head', ... (or platform equivalents). Remove the temp files when done.
|
|
3453
|
+
|
|
3454
|
+
EXAMPLES:
|
|
3455
|
+
- "what should I wear in Texas?" \u2192 use weather tool for Texas
|
|
3456
|
+
- "how about Japan?" \u2192 use weather tool for Japan (applying same question from context)
|
|
3457
|
+
- "enhance README" \u2192 ${TOOL_FILE_READ} \u2192 generate \u2192 ${TOOL_EDIT_LOCAL_FILE}
|
|
3458
|
+
- "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
|
|
3459
|
+
- "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
|
|
3460
|
+
|
|
3461
|
+
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3359
3464
|
// ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
|
|
3360
3465
|
import { randomBytes } from "crypto";
|
|
3361
3466
|
import range from "lodash/range.js";
|
|
@@ -8707,98 +8812,80 @@ var createFileTool = {
|
|
|
8707
8812
|
};
|
|
8708
8813
|
|
|
8709
8814
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
8710
|
-
import {
|
|
8711
|
-
import {
|
|
8815
|
+
import { glob } from "glob";
|
|
8816
|
+
import { stat } from "fs/promises";
|
|
8712
8817
|
import path9 from "path";
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
if (!includeHidden && entry.name.startsWith(".")) {
|
|
8728
|
-
continue;
|
|
8729
|
-
}
|
|
8730
|
-
const relativePath = basePath ? path9.join(basePath, entry.name) : entry.name;
|
|
8731
|
-
const fullPath = path9.join(dir, entry.name);
|
|
8732
|
-
if (matchGlob(relativePath, pattern)) {
|
|
8733
|
-
matches.push(relativePath);
|
|
8734
|
-
}
|
|
8735
|
-
if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
|
|
8736
|
-
const subMatches = await walkDir(fullPath, pattern, includeHidden, maxResults - matches.length, relativePath);
|
|
8737
|
-
matches.push(...subMatches);
|
|
8738
|
-
}
|
|
8739
|
-
}
|
|
8740
|
-
} catch (error) {
|
|
8741
|
-
}
|
|
8742
|
-
return matches;
|
|
8743
|
-
}
|
|
8744
|
-
async function listFiles(params) {
|
|
8745
|
-
const { pattern, cwd: relativeCwd, includeHidden = false, maxResults = 1e3 } = params;
|
|
8818
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
8819
|
+
"**/node_modules/**",
|
|
8820
|
+
"**/.git/**",
|
|
8821
|
+
"**/dist/**",
|
|
8822
|
+
"**/build/**",
|
|
8823
|
+
"**/.next/**",
|
|
8824
|
+
"**/coverage/**",
|
|
8825
|
+
"**/.turbo/**",
|
|
8826
|
+
"**/.sst/**",
|
|
8827
|
+
"**/*.min.js",
|
|
8828
|
+
"**/*.min.css"
|
|
8829
|
+
];
|
|
8830
|
+
async function findFiles(params) {
|
|
8831
|
+
const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
|
|
8746
8832
|
const baseCwd = process.cwd();
|
|
8747
|
-
const
|
|
8748
|
-
if (!
|
|
8749
|
-
throw new Error(`Access denied: Cannot
|
|
8750
|
-
}
|
|
8751
|
-
const
|
|
8833
|
+
const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
|
|
8834
|
+
if (!targetDir.startsWith(baseCwd)) {
|
|
8835
|
+
throw new Error(`Access denied: Cannot search outside of current working directory`);
|
|
8836
|
+
}
|
|
8837
|
+
const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS2 : [];
|
|
8838
|
+
const matches = await glob(pattern, {
|
|
8839
|
+
cwd: targetDir,
|
|
8840
|
+
dot: false,
|
|
8841
|
+
// Don't match hidden files by default
|
|
8842
|
+
ignore: ignorePatterns,
|
|
8843
|
+
absolute: true,
|
|
8844
|
+
nodir: true,
|
|
8845
|
+
// Only return files, not directories
|
|
8846
|
+
nocase: !case_sensitive,
|
|
8847
|
+
// Case-insensitive if requested
|
|
8848
|
+
maxDepth: 20
|
|
8849
|
+
// Reasonable depth limit
|
|
8850
|
+
});
|
|
8752
8851
|
if (matches.length === 0) {
|
|
8753
|
-
return `No files found matching pattern: ${pattern}`;
|
|
8852
|
+
return `No files found matching pattern: ${pattern}${dir_path ? ` in ${dir_path}` : ""}`;
|
|
8754
8853
|
}
|
|
8755
|
-
const
|
|
8756
|
-
for (const
|
|
8854
|
+
const filesWithStats = [];
|
|
8855
|
+
for (const filePath of matches) {
|
|
8757
8856
|
try {
|
|
8758
|
-
const
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
size: stats.size,
|
|
8763
|
-
isDirectory: stats.isDirectory(),
|
|
8764
|
-
modified: stats.mtime.toISOString()
|
|
8857
|
+
const stats = await stat(filePath);
|
|
8858
|
+
filesWithStats.push({
|
|
8859
|
+
path: filePath,
|
|
8860
|
+
mtime: stats.mtimeMs
|
|
8765
8861
|
});
|
|
8766
|
-
} catch
|
|
8862
|
+
} catch {
|
|
8767
8863
|
continue;
|
|
8768
8864
|
}
|
|
8769
8865
|
}
|
|
8770
|
-
|
|
8771
|
-
const
|
|
8772
|
-
|
|
8773
|
-
const
|
|
8774
|
-
` : ""
|
|
8775
|
-
const
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
return `${summary}${cwdInfo}
|
|
8866
|
+
filesWithStats.sort((a, b) => b.mtime - a.mtime);
|
|
8867
|
+
const MAX_RESULTS = 500;
|
|
8868
|
+
const truncated = filesWithStats.length > MAX_RESULTS;
|
|
8869
|
+
const results = truncated ? filesWithStats.slice(0, MAX_RESULTS) : filesWithStats;
|
|
8870
|
+
const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
|
|
8871
|
+
const dirInfo = dir_path ? `
|
|
8872
|
+
Directory: ${dir_path}` : "";
|
|
8873
|
+
const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
|
|
8874
|
+
return `${summary}${dirInfo}
|
|
8875
|
+
|
|
8781
8876
|
${filesList}`;
|
|
8782
8877
|
}
|
|
8783
|
-
function formatBytes(bytes) {
|
|
8784
|
-
if (bytes === 0)
|
|
8785
|
-
return "0 B";
|
|
8786
|
-
const k = 1024;
|
|
8787
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
8788
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
8789
|
-
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
|
|
8790
|
-
}
|
|
8791
8878
|
var globFilesTool = {
|
|
8792
8879
|
name: "glob_files",
|
|
8793
8880
|
implementation: (context) => ({
|
|
8794
8881
|
toolFn: async (value) => {
|
|
8795
8882
|
const params = value;
|
|
8796
|
-
context.logger.info("\u{1F50D} GlobFiles:
|
|
8883
|
+
context.logger.info("\u{1F50D} GlobFiles: Finding files", {
|
|
8797
8884
|
pattern: params.pattern,
|
|
8798
|
-
|
|
8885
|
+
dir_path: params.dir_path || "."
|
|
8799
8886
|
});
|
|
8800
8887
|
try {
|
|
8801
|
-
const result = await
|
|
8888
|
+
const result = await findFiles(params);
|
|
8802
8889
|
context.logger.info("\u2705 GlobFiles: Success");
|
|
8803
8890
|
return result;
|
|
8804
8891
|
} catch (error) {
|
|
@@ -8808,25 +8895,25 @@ var globFilesTool = {
|
|
|
8808
8895
|
},
|
|
8809
8896
|
toolSchema: {
|
|
8810
8897
|
name: "glob_files",
|
|
8811
|
-
description: "
|
|
8898
|
+
description: "Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Use this to locate files by name or extension when you need to discover or explore files in a project.",
|
|
8812
8899
|
parameters: {
|
|
8813
8900
|
type: "object",
|
|
8814
8901
|
properties: {
|
|
8815
8902
|
pattern: {
|
|
8816
8903
|
type: "string",
|
|
8817
|
-
description: '
|
|
8904
|
+
description: 'The glob pattern to match against file paths. Supports wildcards (*, **), character ranges ([abc]), and brace expansion ({ts,tsx}). Examples: "*.ts" for TypeScript files in current dir, "src/**/*.tsx" for all TSX files in src tree, "**/*.{js,ts}" for all JS/TS files.'
|
|
8818
8905
|
},
|
|
8819
|
-
|
|
8906
|
+
dir_path: {
|
|
8820
8907
|
type: "string",
|
|
8821
|
-
description: "
|
|
8908
|
+
description: "Optional: The absolute path to the directory to search in. If not specified, uses the current working directory. Must be within the current working directory."
|
|
8822
8909
|
},
|
|
8823
|
-
|
|
8910
|
+
case_sensitive: {
|
|
8824
8911
|
type: "boolean",
|
|
8825
|
-
description: "
|
|
8912
|
+
description: "Optional: Whether the pattern matching should be case-sensitive. Defaults to true. Set to false for case-insensitive matching on case-sensitive file systems."
|
|
8826
8913
|
},
|
|
8827
|
-
|
|
8828
|
-
type: "
|
|
8829
|
-
description: "
|
|
8914
|
+
respect_git_ignore: {
|
|
8915
|
+
type: "boolean",
|
|
8916
|
+
description: "Optional: Whether to respect common ignore patterns (node_modules, .git, dist, build, etc.). Defaults to true. Set to false to include all files."
|
|
8830
8917
|
}
|
|
8831
8918
|
},
|
|
8832
8919
|
required: ["pattern"]
|
|
@@ -8836,158 +8923,144 @@ var globFilesTool = {
|
|
|
8836
8923
|
};
|
|
8837
8924
|
|
|
8838
8925
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
8839
|
-
import {
|
|
8840
|
-
import {
|
|
8926
|
+
import { globStream } from "glob";
|
|
8927
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
8841
8928
|
import path10 from "path";
|
|
8842
8929
|
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8930
|
+
var DEFAULT_IGNORE_PATTERNS3 = [
|
|
8931
|
+
"**/node_modules/**",
|
|
8932
|
+
"**/.git/**",
|
|
8933
|
+
"**/dist/**",
|
|
8934
|
+
"**/build/**",
|
|
8935
|
+
"**/.next/**",
|
|
8936
|
+
"**/coverage/**",
|
|
8937
|
+
"**/*.min.js",
|
|
8938
|
+
"**/*.min.css",
|
|
8939
|
+
"**/package-lock.json",
|
|
8940
|
+
"**/pnpm-lock.yaml",
|
|
8941
|
+
"**/yarn.lock"
|
|
8942
|
+
];
|
|
8943
|
+
async function isBinaryFile2(filePath) {
|
|
8850
8944
|
try {
|
|
8851
|
-
const
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
const fullPath = path10.join(dir, entry.name);
|
|
8861
|
-
if (matchGlob2(relativePath, pattern)) {
|
|
8862
|
-
matches.push(relativePath);
|
|
8863
|
-
}
|
|
8864
|
-
if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
|
|
8865
|
-
const subMatches = await walkDir2(fullPath, pattern, maxResults - matches.length, relativePath);
|
|
8866
|
-
matches.push(...subMatches);
|
|
8867
|
-
}
|
|
8868
|
-
}
|
|
8869
|
-
} catch (error) {
|
|
8945
|
+
const { openSync, readSync, closeSync } = await import("fs");
|
|
8946
|
+
const buffer = Buffer.alloc(8192);
|
|
8947
|
+
const fd = openSync(filePath, "r");
|
|
8948
|
+
const bytesRead = readSync(fd, buffer, 0, 8192, 0);
|
|
8949
|
+
closeSync(fd);
|
|
8950
|
+
const chunk = buffer.slice(0, bytesRead);
|
|
8951
|
+
return chunk.includes(0);
|
|
8952
|
+
} catch {
|
|
8953
|
+
return true;
|
|
8870
8954
|
}
|
|
8871
|
-
|
|
8955
|
+
}
|
|
8956
|
+
function isPathWithinWorkspace(targetPath, baseCwd) {
|
|
8957
|
+
const resolvedTarget = path10.resolve(targetPath);
|
|
8958
|
+
const resolvedBase = path10.resolve(baseCwd);
|
|
8959
|
+
return resolvedTarget.startsWith(resolvedBase);
|
|
8872
8960
|
}
|
|
8873
8961
|
async function searchFiles2(params) {
|
|
8874
|
-
const { pattern,
|
|
8962
|
+
const { pattern, dir_path, include } = params;
|
|
8875
8963
|
const baseCwd = process.cwd();
|
|
8876
|
-
const
|
|
8877
|
-
if (!
|
|
8878
|
-
throw new Error(`
|
|
8964
|
+
const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
|
|
8965
|
+
if (!isPathWithinWorkspace(targetDir, baseCwd)) {
|
|
8966
|
+
throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
|
|
8967
|
+
}
|
|
8968
|
+
try {
|
|
8969
|
+
const stats = await stat2(targetDir);
|
|
8970
|
+
if (!stats.isDirectory()) {
|
|
8971
|
+
throw new Error(`Path is not a directory: ${dir_path}`);
|
|
8972
|
+
}
|
|
8973
|
+
} catch (error) {
|
|
8974
|
+
if (error.code === "ENOENT") {
|
|
8975
|
+
throw new Error(`Path does not exist: ${dir_path}`);
|
|
8976
|
+
}
|
|
8977
|
+
throw error;
|
|
8879
8978
|
}
|
|
8880
8979
|
let searchRegex;
|
|
8881
8980
|
try {
|
|
8882
|
-
|
|
8883
|
-
searchRegex = new RegExp(pattern, flags);
|
|
8981
|
+
searchRegex = new RegExp(pattern, "i");
|
|
8884
8982
|
} catch (error) {
|
|
8885
8983
|
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
8886
8984
|
}
|
|
8887
|
-
const
|
|
8888
|
-
const
|
|
8889
|
-
|
|
8890
|
-
|
|
8985
|
+
const globPattern = include || "**/*";
|
|
8986
|
+
const filesStream = globStream(globPattern, {
|
|
8987
|
+
cwd: targetDir,
|
|
8988
|
+
dot: false,
|
|
8989
|
+
// Skip hidden files
|
|
8990
|
+
ignore: DEFAULT_IGNORE_PATTERNS3,
|
|
8991
|
+
absolute: true,
|
|
8992
|
+
nodir: true
|
|
8993
|
+
// Only match files, not directories
|
|
8994
|
+
});
|
|
8995
|
+
const allMatches = [];
|
|
8996
|
+
let filesSearched = 0;
|
|
8997
|
+
const maxMatches = 500;
|
|
8998
|
+
for await (const filePath of filesStream) {
|
|
8999
|
+
if (allMatches.length >= maxMatches) {
|
|
9000
|
+
break;
|
|
9001
|
+
}
|
|
9002
|
+
const fileAbsPath = filePath;
|
|
8891
9003
|
try {
|
|
8892
|
-
const stats =
|
|
8893
|
-
if (stats.
|
|
9004
|
+
const stats = await stat2(fileAbsPath);
|
|
9005
|
+
if (stats.size > MAX_FILE_SIZE3) {
|
|
8894
9006
|
continue;
|
|
8895
9007
|
}
|
|
8896
|
-
if (isBinaryFile2(
|
|
9008
|
+
if (await isBinaryFile2(fileAbsPath)) {
|
|
8897
9009
|
continue;
|
|
8898
9010
|
}
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
for (const file of filteredFiles) {
|
|
8910
|
-
if (matches.length >= maxResults) {
|
|
8911
|
-
break;
|
|
8912
|
-
}
|
|
8913
|
-
filesSearched++;
|
|
8914
|
-
const fullPath = path10.join(targetCwd, file);
|
|
8915
|
-
try {
|
|
8916
|
-
const content = readFileSync3(fullPath, "utf-8");
|
|
8917
|
-
const lines = content.split("\n");
|
|
8918
|
-
for (let i = 0; i < lines.length; i++) {
|
|
8919
|
-
if (matches.length >= maxResults) {
|
|
8920
|
-
break;
|
|
8921
|
-
}
|
|
8922
|
-
const line = lines[i];
|
|
8923
|
-
if (searchRegex.test(line)) {
|
|
8924
|
-
const match = {
|
|
8925
|
-
file,
|
|
8926
|
-
line: i + 1,
|
|
8927
|
-
// 1-indexed line numbers
|
|
8928
|
-
content: line.trim()
|
|
8929
|
-
};
|
|
8930
|
-
if (contextLines > 0) {
|
|
8931
|
-
const before = [];
|
|
8932
|
-
const after = [];
|
|
8933
|
-
for (let j = Math.max(0, i - contextLines); j < i; j++) {
|
|
8934
|
-
before.push(lines[j]);
|
|
8935
|
-
}
|
|
8936
|
-
for (let j = i + 1; j <= Math.min(lines.length - 1, i + contextLines); j++) {
|
|
8937
|
-
after.push(lines[j]);
|
|
8938
|
-
}
|
|
8939
|
-
match.context = { before, after };
|
|
8940
|
-
}
|
|
8941
|
-
matches.push(match);
|
|
9011
|
+
filesSearched++;
|
|
9012
|
+
const content = await readFile(fileAbsPath, "utf8");
|
|
9013
|
+
const lines = content.split(/\r?\n/);
|
|
9014
|
+
lines.forEach((line, index) => {
|
|
9015
|
+
if (allMatches.length < maxMatches && searchRegex.test(line)) {
|
|
9016
|
+
allMatches.push({
|
|
9017
|
+
filePath: path10.relative(targetDir, fileAbsPath) || path10.basename(fileAbsPath),
|
|
9018
|
+
lineNumber: index + 1,
|
|
9019
|
+
line
|
|
9020
|
+
});
|
|
8942
9021
|
}
|
|
8943
|
-
|
|
9022
|
+
});
|
|
9023
|
+
} catch (readError) {
|
|
9024
|
+
const errCode = readError.code;
|
|
9025
|
+
if (errCode !== "ENOENT" && errCode !== "EACCES") {
|
|
9026
|
+
console.debug(`Could not read ${fileAbsPath}: ${readError.message}`);
|
|
8944
9027
|
}
|
|
8945
|
-
} catch {
|
|
8946
|
-
continue;
|
|
8947
9028
|
}
|
|
8948
9029
|
}
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
9030
|
+
const searchDirDisplay = dir_path || ".";
|
|
9031
|
+
const filterInfo = include ? ` (filter: "${include}")` : "";
|
|
9032
|
+
if (allMatches.length === 0) {
|
|
9033
|
+
return `No matches found for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}.
|
|
9034
|
+
Searched ${filesSearched} file(s).`;
|
|
8952
9035
|
}
|
|
8953
|
-
const
|
|
8954
|
-
|
|
9036
|
+
const matchesByFile = allMatches.reduce((acc, match) => {
|
|
9037
|
+
if (!acc[match.filePath]) {
|
|
9038
|
+
acc[match.filePath] = [];
|
|
9039
|
+
}
|
|
9040
|
+
acc[match.filePath].push(match);
|
|
9041
|
+
acc[match.filePath].sort((a, b) => a.lineNumber - b.lineNumber);
|
|
9042
|
+
return acc;
|
|
9043
|
+
}, {});
|
|
9044
|
+
const matchCount = allMatches.length;
|
|
9045
|
+
const matchTerm = matchCount === 1 ? "match" : "matches";
|
|
9046
|
+
const truncated = matchCount >= maxMatches;
|
|
9047
|
+
let result = `Found ${matchCount} ${matchTerm}${truncated ? " (truncated)" : ""} for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}:
|
|
8955
9048
|
`;
|
|
8956
|
-
|
|
9049
|
+
result += `Searched ${filesSearched} file(s)
|
|
9050
|
+
---
|
|
8957
9051
|
`;
|
|
8958
|
-
const
|
|
8959
|
-
`
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
${match.context.before.map((l) => ` ${l}`).join("\n")}`;
|
|
8969
|
-
}
|
|
8970
|
-
if (match.context.after.length > 0) {
|
|
8971
|
-
result = `${result}
|
|
8972
|
-
Context after:
|
|
8973
|
-
${match.context.after.map((l) => ` ${l}`).join("\n")}`;
|
|
8974
|
-
}
|
|
8975
|
-
}
|
|
8976
|
-
return result;
|
|
8977
|
-
}).join("\n");
|
|
8978
|
-
return `${summary}${patternInfo}${cwdInfo}${matchesList}`;
|
|
8979
|
-
}
|
|
8980
|
-
function isBinaryFile2(filePath) {
|
|
8981
|
-
try {
|
|
8982
|
-
const buffer = Buffer.alloc(8192);
|
|
8983
|
-
const fd = __require("fs").openSync(filePath, "r");
|
|
8984
|
-
const bytesRead = __require("fs").readSync(fd, buffer, 0, 8192, 0);
|
|
8985
|
-
__require("fs").closeSync(fd);
|
|
8986
|
-
const chunk = buffer.slice(0, bytesRead);
|
|
8987
|
-
return chunk.includes(0);
|
|
8988
|
-
} catch {
|
|
8989
|
-
return true;
|
|
9052
|
+
for (const filePath in matchesByFile) {
|
|
9053
|
+
result += `File: ${filePath}
|
|
9054
|
+
`;
|
|
9055
|
+
matchesByFile[filePath].forEach((match) => {
|
|
9056
|
+
const trimmedLine = match.line.trim();
|
|
9057
|
+
const displayLine = trimmedLine.length > 200 ? trimmedLine.slice(0, 200) + "..." : trimmedLine;
|
|
9058
|
+
result += `L${match.lineNumber}: ${displayLine}
|
|
9059
|
+
`;
|
|
9060
|
+
});
|
|
9061
|
+
result += "---\n";
|
|
8990
9062
|
}
|
|
9063
|
+
return result.trim();
|
|
8991
9064
|
}
|
|
8992
9065
|
var grepSearchTool = {
|
|
8993
9066
|
name: "grep_search",
|
|
@@ -8996,8 +9069,8 @@ var grepSearchTool = {
|
|
|
8996
9069
|
const params = value;
|
|
8997
9070
|
context.logger.info("\u{1F50D} GrepSearch: Searching files", {
|
|
8998
9071
|
pattern: params.pattern,
|
|
8999
|
-
|
|
9000
|
-
|
|
9072
|
+
dir_path: params.dir_path || ".",
|
|
9073
|
+
include: params.include || "**/*"
|
|
9001
9074
|
});
|
|
9002
9075
|
try {
|
|
9003
9076
|
const result = await searchFiles2(params);
|
|
@@ -9010,33 +9083,21 @@ var grepSearchTool = {
|
|
|
9010
9083
|
},
|
|
9011
9084
|
toolSchema: {
|
|
9012
9085
|
name: "grep_search",
|
|
9013
|
-
description: "
|
|
9086
|
+
description: "Searches for a regular expression pattern within the content of files in a specified directory (or current working directory). Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.",
|
|
9014
9087
|
parameters: {
|
|
9015
9088
|
type: "object",
|
|
9016
9089
|
properties: {
|
|
9017
9090
|
pattern: {
|
|
9018
9091
|
type: "string",
|
|
9019
|
-
description: "
|
|
9092
|
+
description: "The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*')."
|
|
9020
9093
|
},
|
|
9021
|
-
|
|
9094
|
+
dir_path: {
|
|
9022
9095
|
type: "string",
|
|
9023
|
-
description:
|
|
9096
|
+
description: "Optional: The absolute path to the directory to search within. If omitted, searches the current working directory."
|
|
9024
9097
|
},
|
|
9025
|
-
|
|
9098
|
+
include: {
|
|
9026
9099
|
type: "string",
|
|
9027
|
-
description: "
|
|
9028
|
-
},
|
|
9029
|
-
caseSensitive: {
|
|
9030
|
-
type: "boolean",
|
|
9031
|
-
description: "Whether the search should be case-sensitive (default: false)"
|
|
9032
|
-
},
|
|
9033
|
-
maxResults: {
|
|
9034
|
-
type: "number",
|
|
9035
|
-
description: "Maximum number of matches to return (default: 100). Prevents overwhelming output."
|
|
9036
|
-
},
|
|
9037
|
-
contextLines: {
|
|
9038
|
-
type: "number",
|
|
9039
|
-
description: "Number of context lines to show before and after each match (default: 0). Similar to grep -C option."
|
|
9100
|
+
description: "Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores)."
|
|
9040
9101
|
}
|
|
9041
9102
|
},
|
|
9042
9103
|
required: ["pattern"]
|
|
@@ -9047,7 +9108,7 @@ var grepSearchTool = {
|
|
|
9047
9108
|
|
|
9048
9109
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
9049
9110
|
import { promises as fs10 } from "fs";
|
|
9050
|
-
import { existsSync as existsSync6, statSync as
|
|
9111
|
+
import { existsSync as existsSync6, statSync as statSync5 } from "fs";
|
|
9051
9112
|
import path11 from "path";
|
|
9052
9113
|
async function deleteFile(params) {
|
|
9053
9114
|
const { path: filePath, recursive = false } = params;
|
|
@@ -9060,7 +9121,7 @@ async function deleteFile(params) {
|
|
|
9060
9121
|
if (!existsSync6(resolvedPath)) {
|
|
9061
9122
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
9062
9123
|
}
|
|
9063
|
-
const stats =
|
|
9124
|
+
const stats = statSync5(resolvedPath);
|
|
9064
9125
|
const isDirectory = stats.isDirectory();
|
|
9065
9126
|
const size = stats.size;
|
|
9066
9127
|
if (isDirectory && !recursive) {
|
|
@@ -9081,7 +9142,7 @@ var deleteFileTool = {
|
|
|
9081
9142
|
toolFn: async (value) => {
|
|
9082
9143
|
const params = value;
|
|
9083
9144
|
const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
|
|
9084
|
-
const isDirectory = existsSync6(resolvedPath) &&
|
|
9145
|
+
const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
|
|
9085
9146
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
9086
9147
|
path: params.path,
|
|
9087
9148
|
recursive: params.recursive
|
|
@@ -9857,7 +9918,7 @@ var ToolErrorType;
|
|
|
9857
9918
|
|
|
9858
9919
|
// src/utils/diffPreview.ts
|
|
9859
9920
|
import * as Diff from "diff";
|
|
9860
|
-
import { readFile } from "fs/promises";
|
|
9921
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
9861
9922
|
import { existsSync as existsSync8 } from "fs";
|
|
9862
9923
|
async function generateFileDiffPreview(args) {
|
|
9863
9924
|
try {
|
|
@@ -9872,7 +9933,7 @@ ${preview}${hasMore ? `
|
|
|
9872
9933
|
|
|
9873
9934
|
... (${lines2.length - 20} more lines)` : ""}`;
|
|
9874
9935
|
}
|
|
9875
|
-
const currentContent = await
|
|
9936
|
+
const currentContent = await readFile2(args.path, "utf-8");
|
|
9876
9937
|
const patch = Diff.createPatch(
|
|
9877
9938
|
args.path,
|
|
9878
9939
|
currentContent,
|
|
@@ -10781,6 +10842,82 @@ async function loadContextFiles(projectDir) {
|
|
|
10781
10842
|
};
|
|
10782
10843
|
}
|
|
10783
10844
|
|
|
10845
|
+
// src/utils/formatStep.ts
|
|
10846
|
+
var MAX_INPUT_LENGTH = 500;
|
|
10847
|
+
var formatStep = (step) => {
|
|
10848
|
+
if (step.type === "action" && step.metadata?.toolInput) {
|
|
10849
|
+
let parsedInput = step.metadata.toolInput;
|
|
10850
|
+
if (typeof parsedInput === "string") {
|
|
10851
|
+
try {
|
|
10852
|
+
parsedInput = JSON.parse(parsedInput);
|
|
10853
|
+
} catch {
|
|
10854
|
+
}
|
|
10855
|
+
}
|
|
10856
|
+
if (step.metadata.toolName === "edit_local_file") {
|
|
10857
|
+
const pathOnly = typeof parsedInput === "object" && parsedInput !== null && "path" in parsedInput ? { path: parsedInput.path } : parsedInput;
|
|
10858
|
+
return {
|
|
10859
|
+
...step,
|
|
10860
|
+
metadata: {
|
|
10861
|
+
...step.metadata,
|
|
10862
|
+
toolInput: pathOnly
|
|
10863
|
+
}
|
|
10864
|
+
};
|
|
10865
|
+
}
|
|
10866
|
+
if (step.metadata.toolName === "write_todos") {
|
|
10867
|
+
const todos = typeof parsedInput === "object" && parsedInput !== null && "todos" in parsedInput ? parsedInput.todos : null;
|
|
10868
|
+
const count = Array.isArray(todos) ? todos.length : 0;
|
|
10869
|
+
return {
|
|
10870
|
+
...step,
|
|
10871
|
+
metadata: {
|
|
10872
|
+
...step.metadata,
|
|
10873
|
+
toolInput: `${count} todo${count !== 1 ? "s" : ""}`
|
|
10874
|
+
}
|
|
10875
|
+
};
|
|
10876
|
+
}
|
|
10877
|
+
const inputStr = typeof parsedInput === "string" ? parsedInput : JSON.stringify(parsedInput);
|
|
10878
|
+
if (inputStr.length > MAX_INPUT_LENGTH) {
|
|
10879
|
+
const truncatedInput = inputStr.slice(0, MAX_INPUT_LENGTH) + `... (${inputStr.length - MAX_INPUT_LENGTH} more chars)`;
|
|
10880
|
+
return {
|
|
10881
|
+
...step,
|
|
10882
|
+
metadata: {
|
|
10883
|
+
...step.metadata,
|
|
10884
|
+
toolInput: truncatedInput
|
|
10885
|
+
}
|
|
10886
|
+
};
|
|
10887
|
+
}
|
|
10888
|
+
}
|
|
10889
|
+
if (step.type === "observation" && step.content) {
|
|
10890
|
+
if (step.metadata?.toolName === "file_read") {
|
|
10891
|
+
const lineCount = step.content.split("\n").length;
|
|
10892
|
+
return {
|
|
10893
|
+
...step,
|
|
10894
|
+
content: `Read ${lineCount} line${lineCount !== 1 ? "s" : ""}`
|
|
10895
|
+
};
|
|
10896
|
+
}
|
|
10897
|
+
if (step.metadata?.toolName === "grep_search") {
|
|
10898
|
+
const match = step.content.match(/^Found (\d+) file\(s\)/);
|
|
10899
|
+
if (match) {
|
|
10900
|
+
const count = parseInt(match[1], 10);
|
|
10901
|
+
return {
|
|
10902
|
+
...step,
|
|
10903
|
+
content: `Found ${count} file${count !== 1 ? "s" : ""}`
|
|
10904
|
+
};
|
|
10905
|
+
}
|
|
10906
|
+
}
|
|
10907
|
+
if (step.metadata?.toolName === "glob_files") {
|
|
10908
|
+
const match = step.content.match(/^Found (\d+) file\(s\)/);
|
|
10909
|
+
if (match) {
|
|
10910
|
+
const count = parseInt(match[1], 10);
|
|
10911
|
+
return {
|
|
10912
|
+
...step,
|
|
10913
|
+
content: `Found ${count} file${count !== 1 ? "s" : ""}`
|
|
10914
|
+
};
|
|
10915
|
+
}
|
|
10916
|
+
}
|
|
10917
|
+
}
|
|
10918
|
+
return step;
|
|
10919
|
+
};
|
|
10920
|
+
|
|
10784
10921
|
// src/utils/argumentSubstitution.ts
|
|
10785
10922
|
function substituteArguments(template, args) {
|
|
10786
10923
|
let result = template;
|
|
@@ -11248,6 +11385,10 @@ var ServerLlmBackend = class {
|
|
|
11248
11385
|
*/
|
|
11249
11386
|
async complete(model, messages, options, callback) {
|
|
11250
11387
|
logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
|
|
11388
|
+
if (options.abortSignal?.aborted) {
|
|
11389
|
+
logger.debug("[ServerLlmBackend] Request aborted before start");
|
|
11390
|
+
return;
|
|
11391
|
+
}
|
|
11251
11392
|
return new Promise(async (resolve3, reject) => {
|
|
11252
11393
|
try {
|
|
11253
11394
|
logger.debug("[ServerLlmBackend] Making streaming request...");
|
|
@@ -11348,7 +11489,25 @@ var ServerLlmBackend = class {
|
|
|
11348
11489
|
}
|
|
11349
11490
|
}
|
|
11350
11491
|
});
|
|
11492
|
+
if (options.abortSignal) {
|
|
11493
|
+
const abortHandler = () => {
|
|
11494
|
+
logger.debug("[ServerLlmBackend] Abort signal received, destroying stream");
|
|
11495
|
+
response.data.destroy();
|
|
11496
|
+
resolve3();
|
|
11497
|
+
};
|
|
11498
|
+
if (options.abortSignal.aborted) {
|
|
11499
|
+
abortHandler();
|
|
11500
|
+
return;
|
|
11501
|
+
}
|
|
11502
|
+
options.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
11503
|
+
response.data.on("close", () => {
|
|
11504
|
+
options.abortSignal?.removeEventListener("abort", abortHandler);
|
|
11505
|
+
});
|
|
11506
|
+
}
|
|
11351
11507
|
response.data.on("data", (chunk) => {
|
|
11508
|
+
if (options.abortSignal?.aborted) {
|
|
11509
|
+
return;
|
|
11510
|
+
}
|
|
11352
11511
|
parser.feed(chunk.toString());
|
|
11353
11512
|
});
|
|
11354
11513
|
response.data.on("end", () => {
|
|
@@ -11360,9 +11519,23 @@ var ServerLlmBackend = class {
|
|
|
11360
11519
|
}
|
|
11361
11520
|
});
|
|
11362
11521
|
response.data.on("error", (error) => {
|
|
11522
|
+
if (options.abortSignal?.aborted) {
|
|
11523
|
+
resolve3();
|
|
11524
|
+
return;
|
|
11525
|
+
}
|
|
11363
11526
|
reject(error);
|
|
11364
11527
|
});
|
|
11365
11528
|
} catch (error) {
|
|
11529
|
+
if (options.abortSignal?.aborted) {
|
|
11530
|
+
logger.debug("[ServerLlmBackend] Request was aborted, resolving gracefully");
|
|
11531
|
+
resolve3();
|
|
11532
|
+
return;
|
|
11533
|
+
}
|
|
11534
|
+
if (isAxiosError(error) && error.code === "ERR_CANCELED") {
|
|
11535
|
+
logger.debug("[ServerLlmBackend] Request was canceled, resolving gracefully");
|
|
11536
|
+
resolve3();
|
|
11537
|
+
return;
|
|
11538
|
+
}
|
|
11366
11539
|
logger.error("LLM completion failed", error);
|
|
11367
11540
|
if (isAxiosError(error)) {
|
|
11368
11541
|
logger.debug(
|
|
@@ -11513,8 +11686,10 @@ var ServerLlmBackend = class {
|
|
|
11513
11686
|
logger.debug(` Body: ${logger.formatBytes(bodySize)}`);
|
|
11514
11687
|
logger.debug(` Preview: ${bodyStr.substring(0, 200)}`);
|
|
11515
11688
|
const response = await axiosInstance.post(this.completionsEndpoint, requestBody, {
|
|
11516
|
-
responseType: "stream"
|
|
11689
|
+
responseType: "stream",
|
|
11517
11690
|
// Auth header is automatically injected by ApiClient interceptor
|
|
11691
|
+
// Pass abort signal to cancel request if user presses ESC
|
|
11692
|
+
signal: options.abortSignal
|
|
11518
11693
|
});
|
|
11519
11694
|
logger.debug(`\u2190 ${response.status} ${response.statusText}`);
|
|
11520
11695
|
return response;
|
|
@@ -11658,7 +11833,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
11658
11833
|
// package.json
|
|
11659
11834
|
var package_default = {
|
|
11660
11835
|
name: "@bike4mind/cli",
|
|
11661
|
-
version: "0.2.
|
|
11836
|
+
version: "0.2.12-ctrl-c-clear-input.17412+46c1f7133",
|
|
11662
11837
|
type: "module",
|
|
11663
11838
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
11664
11839
|
license: "UNLICENSED",
|
|
@@ -11695,7 +11870,8 @@ var package_default = {
|
|
|
11695
11870
|
test: "vitest run",
|
|
11696
11871
|
"test:watch": "vitest",
|
|
11697
11872
|
start: "node dist/index.js",
|
|
11698
|
-
prepublishOnly: "pnpm build"
|
|
11873
|
+
prepublishOnly: "pnpm build",
|
|
11874
|
+
postinstall: `node -e "try { require('better-sqlite3') } catch(e) { if(e.message.includes('bindings')) { console.log('\\n\u26A0\uFE0F Rebuilding better-sqlite3 native bindings...'); require('child_process').execSync('pnpm rebuild better-sqlite3', {stdio:'inherit'}) } }"`
|
|
11699
11875
|
},
|
|
11700
11876
|
dependencies: {
|
|
11701
11877
|
"@anthropic-ai/sdk": "^0.22.0",
|
|
@@ -11728,6 +11904,7 @@ var package_default = {
|
|
|
11728
11904
|
"eventsource-parser": "^3.0.6",
|
|
11729
11905
|
"file-type": "^18.7.0",
|
|
11730
11906
|
"fuse.js": "^7.1.0",
|
|
11907
|
+
glob: "^13.0.0",
|
|
11731
11908
|
"gray-matter": "^4.0.3",
|
|
11732
11909
|
ink: "^6.5.1",
|
|
11733
11910
|
"ink-select-input": "^6.2.0",
|
|
@@ -11761,11 +11938,11 @@ var package_default = {
|
|
|
11761
11938
|
zustand: "^4.5.4"
|
|
11762
11939
|
},
|
|
11763
11940
|
devDependencies: {
|
|
11764
|
-
"@bike4mind/agents": "
|
|
11765
|
-
"@bike4mind/common": "
|
|
11766
|
-
"@bike4mind/mcp": "
|
|
11767
|
-
"@bike4mind/services": "
|
|
11768
|
-
"@bike4mind/utils": "
|
|
11941
|
+
"@bike4mind/agents": "0.1.0",
|
|
11942
|
+
"@bike4mind/common": "2.41.1-ctrl-c-clear-input.17412+46c1f7133",
|
|
11943
|
+
"@bike4mind/mcp": "1.21.1-ctrl-c-clear-input.17412+46c1f7133",
|
|
11944
|
+
"@bike4mind/services": "2.36.1-ctrl-c-clear-input.17412+46c1f7133",
|
|
11945
|
+
"@bike4mind/utils": "2.1.6-ctrl-c-clear-input.17412+46c1f7133",
|
|
11769
11946
|
"@types/better-sqlite3": "^7.6.13",
|
|
11770
11947
|
"@types/diff": "^5.0.9",
|
|
11771
11948
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -11777,7 +11954,8 @@ var package_default = {
|
|
|
11777
11954
|
tsx: "^4.21.0",
|
|
11778
11955
|
typescript: "^5.9.3",
|
|
11779
11956
|
vitest: "^3.2.4"
|
|
11780
|
-
}
|
|
11957
|
+
},
|
|
11958
|
+
gitHead: "46c1f7133ac46ff30ab650cda7ec3c332e35fb4c"
|
|
11781
11959
|
};
|
|
11782
11960
|
|
|
11783
11961
|
// src/config/constants.ts
|
|
@@ -12133,6 +12311,131 @@ function createSubagentDelegateTool(orchestrator, parentSessionId) {
|
|
|
12133
12311
|
};
|
|
12134
12312
|
}
|
|
12135
12313
|
|
|
12314
|
+
// src/tools/writeTodosTool.ts
|
|
12315
|
+
var VALID_STATUSES = ["pending", "in_progress", "completed", "cancelled"];
|
|
12316
|
+
function validateTodos(todos) {
|
|
12317
|
+
if (!Array.isArray(todos)) {
|
|
12318
|
+
throw new Error("write_todos: todos must be an array");
|
|
12319
|
+
}
|
|
12320
|
+
if (todos.length === 0) {
|
|
12321
|
+
throw new Error("write_todos: todos array cannot be empty");
|
|
12322
|
+
}
|
|
12323
|
+
let inProgressCount = 0;
|
|
12324
|
+
const validatedTodos = todos.map((item, index) => {
|
|
12325
|
+
if (!item || typeof item !== "object") {
|
|
12326
|
+
throw new Error(`write_todos: item at index ${index} must be an object`);
|
|
12327
|
+
}
|
|
12328
|
+
const { description, status } = item;
|
|
12329
|
+
if (typeof description !== "string" || description.trim() === "") {
|
|
12330
|
+
throw new Error(`write_todos: item at index ${index} must have a non-empty description`);
|
|
12331
|
+
}
|
|
12332
|
+
if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
|
|
12333
|
+
throw new Error(
|
|
12334
|
+
`write_todos: item at index ${index} has invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`
|
|
12335
|
+
);
|
|
12336
|
+
}
|
|
12337
|
+
if (status === "in_progress") {
|
|
12338
|
+
inProgressCount++;
|
|
12339
|
+
}
|
|
12340
|
+
return {
|
|
12341
|
+
description: description.trim(),
|
|
12342
|
+
status
|
|
12343
|
+
};
|
|
12344
|
+
});
|
|
12345
|
+
if (inProgressCount > 1) {
|
|
12346
|
+
throw new Error(`write_todos: only one task can be 'in_progress' at a time, but found ${inProgressCount}`);
|
|
12347
|
+
}
|
|
12348
|
+
return validatedTodos;
|
|
12349
|
+
}
|
|
12350
|
+
function formatTodosOutput(todos) {
|
|
12351
|
+
const statusEmoji = {
|
|
12352
|
+
pending: "\u2B1C",
|
|
12353
|
+
in_progress: "\u{1F504}",
|
|
12354
|
+
completed: "\u2705",
|
|
12355
|
+
cancelled: "\u274C"
|
|
12356
|
+
};
|
|
12357
|
+
const lines = todos.map((todo, index) => {
|
|
12358
|
+
const emoji = statusEmoji[todo.status];
|
|
12359
|
+
return `${index + 1}. ${emoji} [${todo.status}] ${todo.description}`;
|
|
12360
|
+
});
|
|
12361
|
+
return lines.join("\n");
|
|
12362
|
+
}
|
|
12363
|
+
function createWriteTodosTool(todoStore) {
|
|
12364
|
+
return {
|
|
12365
|
+
toolFn: async (args) => {
|
|
12366
|
+
const params = args;
|
|
12367
|
+
const validatedTodos = validateTodos(params.todos);
|
|
12368
|
+
todoStore.todos = validatedTodos;
|
|
12369
|
+
if (todoStore.onUpdate) {
|
|
12370
|
+
todoStore.onUpdate(validatedTodos);
|
|
12371
|
+
}
|
|
12372
|
+
const output = formatTodosOutput(validatedTodos);
|
|
12373
|
+
return `Todo list updated successfully:
|
|
12374
|
+
|
|
12375
|
+
${output}`;
|
|
12376
|
+
},
|
|
12377
|
+
toolSchema: {
|
|
12378
|
+
name: "write_todos",
|
|
12379
|
+
description: `Create or update a list of subtasks for complex multi-step requests.
|
|
12380
|
+
|
|
12381
|
+
**When to use this tool:**
|
|
12382
|
+
- When handling complex requests that require multiple steps
|
|
12383
|
+
- To break down a large task into smaller, manageable subtasks
|
|
12384
|
+
- To track progress through a multi-step workflow
|
|
12385
|
+
|
|
12386
|
+
**Important guidelines:**
|
|
12387
|
+
- Call this tool early when receiving complex requests
|
|
12388
|
+
- Update the list immediately when starting, completing, or cancelling tasks
|
|
12389
|
+
- Only ONE task should be 'in_progress' at a time
|
|
12390
|
+
- Never batch updates - update as soon as state changes
|
|
12391
|
+
|
|
12392
|
+
**Status values:**
|
|
12393
|
+
- pending: Task not yet started
|
|
12394
|
+
- in_progress: Currently working on this task (only ONE allowed)
|
|
12395
|
+
- completed: Task finished successfully
|
|
12396
|
+
- cancelled: Task will not be completed
|
|
12397
|
+
|
|
12398
|
+
**Example:**
|
|
12399
|
+
If asked to "create a new React component with tests", break it down:
|
|
12400
|
+
1. Create component file \u2192 in_progress
|
|
12401
|
+
2. Add component logic \u2192 pending
|
|
12402
|
+
3. Create test file \u2192 pending
|
|
12403
|
+
4. Write unit tests \u2192 pending`,
|
|
12404
|
+
parameters: {
|
|
12405
|
+
type: "object",
|
|
12406
|
+
properties: {
|
|
12407
|
+
todos: {
|
|
12408
|
+
type: "array",
|
|
12409
|
+
description: "Complete list of todo items. This replaces any existing list. Each item needs a description and status.",
|
|
12410
|
+
items: {
|
|
12411
|
+
type: "object",
|
|
12412
|
+
properties: {
|
|
12413
|
+
description: {
|
|
12414
|
+
type: "string",
|
|
12415
|
+
description: "Clear, concise description of the task"
|
|
12416
|
+
},
|
|
12417
|
+
status: {
|
|
12418
|
+
type: "string",
|
|
12419
|
+
enum: ["pending", "in_progress", "completed", "cancelled"],
|
|
12420
|
+
description: "Current status of the task. Only ONE task can be 'in_progress' at a time."
|
|
12421
|
+
}
|
|
12422
|
+
},
|
|
12423
|
+
required: ["description", "status"]
|
|
12424
|
+
}
|
|
12425
|
+
}
|
|
12426
|
+
},
|
|
12427
|
+
required: ["todos"]
|
|
12428
|
+
}
|
|
12429
|
+
}
|
|
12430
|
+
};
|
|
12431
|
+
}
|
|
12432
|
+
function createTodoStore(onUpdate) {
|
|
12433
|
+
return {
|
|
12434
|
+
todos: [],
|
|
12435
|
+
onUpdate
|
|
12436
|
+
};
|
|
12437
|
+
}
|
|
12438
|
+
|
|
12136
12439
|
// src/index.tsx
|
|
12137
12440
|
process.removeAllListeners("warning");
|
|
12138
12441
|
process.on("warning", (warning) => {
|
|
@@ -12164,7 +12467,8 @@ function CliApp() {
|
|
|
12164
12467
|
trustLocationSelector: null,
|
|
12165
12468
|
rewindSelector: null,
|
|
12166
12469
|
sessionSelector: null,
|
|
12167
|
-
orchestrator: null
|
|
12470
|
+
orchestrator: null,
|
|
12471
|
+
abortController: null
|
|
12168
12472
|
});
|
|
12169
12473
|
const [isInitialized, setIsInitialized] = useState8(false);
|
|
12170
12474
|
const [initError, setInitError] = useState8(null);
|
|
@@ -12210,15 +12514,28 @@ function CliApp() {
|
|
|
12210
12514
|
}, 100);
|
|
12211
12515
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
12212
12516
|
useInput6((input, key) => {
|
|
12517
|
+
if (key.escape) {
|
|
12518
|
+
if (state.abortController) {
|
|
12519
|
+
logger.debug("[ABORT] ESC pressed - aborting current operation...");
|
|
12520
|
+
state.abortController.abort();
|
|
12521
|
+
setState((prev) => ({ ...prev, abortController: null }));
|
|
12522
|
+
useCliStore.getState().setIsThinking(false);
|
|
12523
|
+
}
|
|
12524
|
+
return;
|
|
12525
|
+
}
|
|
12213
12526
|
if (key.ctrl && input === "c") {
|
|
12214
12527
|
const now = Date.now();
|
|
12215
12528
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
12216
|
-
logger.debug("[EXIT Second Ctrl+C - cleaning up and exiting...");
|
|
12529
|
+
logger.debug("[EXIT] Second Ctrl+C - cleaning up and exiting...");
|
|
12217
12530
|
performCleanup().then(() => {
|
|
12218
12531
|
exit();
|
|
12219
12532
|
});
|
|
12220
12533
|
} else {
|
|
12221
|
-
logger.debug("[EXIT First Ctrl+C - press again to exit");
|
|
12534
|
+
logger.debug("[EXIT] First Ctrl+C - press again to exit");
|
|
12535
|
+
const currentInput = useCliStore.getState().inputValue;
|
|
12536
|
+
if (currentInput.length > 0) {
|
|
12537
|
+
useCliStore.getState().clearInput();
|
|
12538
|
+
}
|
|
12222
12539
|
exitTimestamp = now;
|
|
12223
12540
|
setExitRequested(true);
|
|
12224
12541
|
setTimeout(() => {
|
|
@@ -12407,7 +12724,9 @@ function CliApp() {
|
|
|
12407
12724
|
subagentConfigs
|
|
12408
12725
|
});
|
|
12409
12726
|
const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
|
|
12410
|
-
const
|
|
12727
|
+
const todoStore = createTodoStore();
|
|
12728
|
+
const writeTodosTool = createWriteTodosTool(todoStore);
|
|
12729
|
+
const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool, writeTodosTool];
|
|
12411
12730
|
console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
|
|
12412
12731
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
12413
12732
|
const contextResult = await loadContextFiles(projectDir);
|
|
@@ -12427,46 +12746,7 @@ function CliApp() {
|
|
|
12427
12746
|
Follow these project-specific instructions:
|
|
12428
12747
|
|
|
12429
12748
|
${contextResult.mergedContent}` : "";
|
|
12430
|
-
const cliSystemPrompt =
|
|
12431
|
-
|
|
12432
|
-
CORE BEHAVIOR:
|
|
12433
|
-
- Be proactive: Take action instead of asking for permission or clarification
|
|
12434
|
-
- Make smart assumptions: If unclear, choose the most reasonable interpretation
|
|
12435
|
-
- Use conversation history: Reference previous exchanges to understand context
|
|
12436
|
-
- Complete tasks fully: Don't just show what to do - actually do it
|
|
12437
|
-
|
|
12438
|
-
FOR CODING TASKS:
|
|
12439
|
-
- You have file system access (file_read, create_file, glob_files, grep_search, delete_file)
|
|
12440
|
-
- When user says "enhance", "update", "fix", "improve" a file:
|
|
12441
|
-
* Use file_read to get current content
|
|
12442
|
-
* Generate the improved version
|
|
12443
|
-
* Use create_file to write it back
|
|
12444
|
-
* Don't just show suggestions - make the changes
|
|
12445
|
-
- When searching for code: Use grep_search or glob_files to find relevant files
|
|
12446
|
-
- Permission system will ask for approval before any file writes
|
|
12447
|
-
|
|
12448
|
-
SUBAGENT DELEGATION:
|
|
12449
|
-
- You have access to specialized subagents via the subagent_delegate tool
|
|
12450
|
-
- Use subagents for focused exploration, planning, or review tasks:
|
|
12451
|
-
* explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
|
|
12452
|
-
* plan: Break down complex tasks into actionable steps
|
|
12453
|
-
* review: Analyze code quality and identify issues
|
|
12454
|
-
- Subagents keep the main conversation clean and run faster with optimized models
|
|
12455
|
-
- Delegate when you need to search extensively or analyze code structure
|
|
12456
|
-
|
|
12457
|
-
FOR GENERAL TASKS:
|
|
12458
|
-
- Use available tools to get information (weather, web search, calculations, etc.)
|
|
12459
|
-
- When user asks follow-up questions, use conversation context to understand what they're referring to
|
|
12460
|
-
- If user asks "how about X?" after a previous question, apply the same question type to X
|
|
12461
|
-
|
|
12462
|
-
EXAMPLES:
|
|
12463
|
-
- "what should I wear in Texas?" \u2192 use weather tool for Texas
|
|
12464
|
-
- "how about Japan?" \u2192 use weather tool for Japan (applying same question from context)
|
|
12465
|
-
- "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
|
|
12466
|
-
- "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
|
|
12467
|
-
- "find all components using React hooks" \u2192 subagent_delegate(task="find all components using React hooks", type="explore")
|
|
12468
|
-
|
|
12469
|
-
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
12749
|
+
const cliSystemPrompt = buildCoreSystemPrompt(contextSection);
|
|
12470
12750
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
12471
12751
|
const agent = new ReActAgent({
|
|
12472
12752
|
userId: config.userId,
|
|
@@ -12486,38 +12766,27 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12486
12766
|
const lastIdx = pendingMessages.length - 1;
|
|
12487
12767
|
if (lastIdx >= 0 && pendingMessages[lastIdx].role === "assistant") {
|
|
12488
12768
|
const existingSteps = pendingMessages[lastIdx].metadata?.steps || [];
|
|
12489
|
-
const
|
|
12490
|
-
let truncatedStep = step;
|
|
12491
|
-
if (step.type === "action" && step.metadata?.toolInput) {
|
|
12492
|
-
const inputStr = typeof step.metadata.toolInput === "string" ? step.metadata.toolInput : JSON.stringify(step.metadata.toolInput);
|
|
12493
|
-
if (inputStr.length > MAX_INPUT_LENGTH) {
|
|
12494
|
-
const truncatedInput = inputStr.slice(0, MAX_INPUT_LENGTH) + `... (${inputStr.length - MAX_INPUT_LENGTH} more chars)`;
|
|
12495
|
-
truncatedStep = {
|
|
12496
|
-
...step,
|
|
12497
|
-
metadata: {
|
|
12498
|
-
...step.metadata,
|
|
12499
|
-
toolInput: truncatedInput
|
|
12500
|
-
}
|
|
12501
|
-
};
|
|
12502
|
-
}
|
|
12503
|
-
}
|
|
12769
|
+
const formattedStep = formatStep(step);
|
|
12504
12770
|
updatePendingMessage(lastIdx, {
|
|
12505
12771
|
...pendingMessages[lastIdx],
|
|
12506
12772
|
metadata: {
|
|
12507
12773
|
...pendingMessages[lastIdx].metadata,
|
|
12508
|
-
steps: [...existingSteps,
|
|
12774
|
+
steps: [...existingSteps, formattedStep]
|
|
12509
12775
|
}
|
|
12510
12776
|
});
|
|
12511
12777
|
}
|
|
12512
12778
|
};
|
|
12513
12779
|
agent.on("thought", stepHandler);
|
|
12780
|
+
agent.on("observation", stepHandler);
|
|
12514
12781
|
agent.on("action", stepHandler);
|
|
12515
12782
|
orchestrator.setBeforeRunCallback((subagent, _subagentType) => {
|
|
12516
12783
|
subagent.on("thought", stepHandler);
|
|
12784
|
+
subagent.on("observation", stepHandler);
|
|
12517
12785
|
subagent.on("action", stepHandler);
|
|
12518
12786
|
});
|
|
12519
12787
|
orchestrator.setAfterRunCallback((subagent, _subagentType) => {
|
|
12520
12788
|
subagent.off("thought", stepHandler);
|
|
12789
|
+
subagent.off("observation", stepHandler);
|
|
12521
12790
|
subagent.off("action", stepHandler);
|
|
12522
12791
|
});
|
|
12523
12792
|
setState((prev) => ({
|
|
@@ -12564,6 +12833,8 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12564
12833
|
return;
|
|
12565
12834
|
}
|
|
12566
12835
|
useCliStore.getState().setIsThinking(true);
|
|
12836
|
+
const abortController = new AbortController();
|
|
12837
|
+
setState((prev) => ({ ...prev, abortController }));
|
|
12567
12838
|
const currentSteps = [];
|
|
12568
12839
|
const stepHandler = (step) => {
|
|
12569
12840
|
currentSteps.push(step);
|
|
@@ -12623,7 +12894,8 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12623
12894
|
content: msg.content
|
|
12624
12895
|
}));
|
|
12625
12896
|
const result = await state.agent.run(messageContent, {
|
|
12626
|
-
previousMessages: previousMessages.length > 0 ? previousMessages : void 0
|
|
12897
|
+
previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
|
|
12898
|
+
signal: abortController.signal
|
|
12627
12899
|
});
|
|
12628
12900
|
const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
|
|
12629
12901
|
if (permissionDenied) {
|
|
@@ -12641,7 +12913,7 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12641
12913
|
content: result.finalAnswer,
|
|
12642
12914
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12643
12915
|
metadata: {
|
|
12644
|
-
steps: result.steps,
|
|
12916
|
+
steps: result.steps.map(formatStep),
|
|
12645
12917
|
tokenUsage: {
|
|
12646
12918
|
prompt: 0,
|
|
12647
12919
|
completion: 0,
|
|
@@ -12668,6 +12940,33 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12668
12940
|
useCliStore.getState().setIsThinking(false);
|
|
12669
12941
|
} catch (error) {
|
|
12670
12942
|
useCliStore.getState().setIsThinking(false);
|
|
12943
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
12944
|
+
logger.debug("[ABORT] Custom command aborted by user");
|
|
12945
|
+
const currentSession2 = useCliStore.getState().session;
|
|
12946
|
+
if (currentSession2) {
|
|
12947
|
+
const messages2 = [...currentSession2.messages];
|
|
12948
|
+
const lastMessage2 = messages2[messages2.length - 1];
|
|
12949
|
+
if (lastMessage2 && lastMessage2.role === "assistant") {
|
|
12950
|
+
messages2[messages2.length - 1] = {
|
|
12951
|
+
...lastMessage2,
|
|
12952
|
+
content: "\u26A0\uFE0F Operation cancelled by user",
|
|
12953
|
+
metadata: {
|
|
12954
|
+
...lastMessage2.metadata,
|
|
12955
|
+
cancelled: true
|
|
12956
|
+
}
|
|
12957
|
+
};
|
|
12958
|
+
}
|
|
12959
|
+
const sessionWithCancel = {
|
|
12960
|
+
...currentSession2,
|
|
12961
|
+
messages: messages2,
|
|
12962
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12963
|
+
};
|
|
12964
|
+
setState((prev) => ({ ...prev, session: sessionWithCancel }));
|
|
12965
|
+
setStoreSession(sessionWithCancel);
|
|
12966
|
+
await state.sessionStore.save(sessionWithCancel);
|
|
12967
|
+
}
|
|
12968
|
+
return;
|
|
12969
|
+
}
|
|
12671
12970
|
if (error?.message?.includes("Permission denied")) {
|
|
12672
12971
|
console.log("\n\u26A0\uFE0F Action blocked by permission settings");
|
|
12673
12972
|
const currentSession2 = useCliStore.getState().session;
|
|
@@ -12708,6 +13007,7 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12708
13007
|
setState((prev) => ({ ...prev, session: sessionWithError }));
|
|
12709
13008
|
setStoreSession(sessionWithError);
|
|
12710
13009
|
} finally {
|
|
13010
|
+
setState((prev) => ({ ...prev, abortController: null }));
|
|
12711
13011
|
state.agent.off("thought", stepHandler);
|
|
12712
13012
|
state.agent.off("action", stepHandler);
|
|
12713
13013
|
}
|
|
@@ -12727,6 +13027,8 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12727
13027
|
return;
|
|
12728
13028
|
}
|
|
12729
13029
|
useCliStore.getState().setIsThinking(true);
|
|
13030
|
+
const abortController = new AbortController();
|
|
13031
|
+
setState((prev) => ({ ...prev, abortController }));
|
|
12730
13032
|
try {
|
|
12731
13033
|
let messageContent = message;
|
|
12732
13034
|
let userMessageContent = message;
|
|
@@ -12764,7 +13066,8 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12764
13066
|
content: msg.content
|
|
12765
13067
|
}));
|
|
12766
13068
|
const result = await state.agent.run(messageContent, {
|
|
12767
|
-
previousMessages: previousMessages.length > 0 ? previousMessages : void 0
|
|
13069
|
+
previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
|
|
13070
|
+
signal: abortController.signal
|
|
12768
13071
|
});
|
|
12769
13072
|
const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
|
|
12770
13073
|
if (permissionDenied) {
|
|
@@ -12783,7 +13086,7 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12783
13086
|
completion: 0,
|
|
12784
13087
|
total: result.completionInfo.totalTokens
|
|
12785
13088
|
},
|
|
12786
|
-
steps: result.steps,
|
|
13089
|
+
steps: result.steps.map(formatStep),
|
|
12787
13090
|
// Complete history: thoughts, actions, observations
|
|
12788
13091
|
permissionDenied
|
|
12789
13092
|
}
|
|
@@ -12804,6 +13107,30 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12804
13107
|
await state.sessionStore.save(updatedSession);
|
|
12805
13108
|
} catch (error) {
|
|
12806
13109
|
useCliStore.getState().clearPendingMessages();
|
|
13110
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
13111
|
+
logger.debug("[ABORT] Operation aborted by user");
|
|
13112
|
+
const currentSession = useCliStore.getState().session;
|
|
13113
|
+
if (currentSession) {
|
|
13114
|
+
const cancelMessage = {
|
|
13115
|
+
id: uuidv410(),
|
|
13116
|
+
role: "assistant",
|
|
13117
|
+
content: "\u26A0\uFE0F Operation cancelled by user",
|
|
13118
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13119
|
+
metadata: {
|
|
13120
|
+
cancelled: true
|
|
13121
|
+
}
|
|
13122
|
+
};
|
|
13123
|
+
const sessionWithCancel = {
|
|
13124
|
+
...currentSession,
|
|
13125
|
+
messages: [...currentSession.messages, cancelMessage],
|
|
13126
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13127
|
+
};
|
|
13128
|
+
setState((prev) => ({ ...prev, session: sessionWithCancel }));
|
|
13129
|
+
setStoreSession(sessionWithCancel);
|
|
13130
|
+
await state.sessionStore.save(sessionWithCancel);
|
|
13131
|
+
}
|
|
13132
|
+
return;
|
|
13133
|
+
}
|
|
12807
13134
|
if (error instanceof Error) {
|
|
12808
13135
|
if (error.message.includes("Authentication failed") || error.message.includes("Authentication expired")) {
|
|
12809
13136
|
console.log("\n\u274C Authentication failed");
|
|
@@ -12812,6 +13139,9 @@ Remember: Use context from previous messages to understand follow-up questions.$
|
|
|
12812
13139
|
}
|
|
12813
13140
|
}
|
|
12814
13141
|
console.error("Error processing message:", error);
|
|
13142
|
+
} finally {
|
|
13143
|
+
setState((prev) => ({ ...prev, abortController: null }));
|
|
13144
|
+
useCliStore.getState().setIsThinking(false);
|
|
12815
13145
|
}
|
|
12816
13146
|
};
|
|
12817
13147
|
const handleBashCommand = useCallback(
|
|
@@ -12862,7 +13192,7 @@ ${output}` : output.trim() || "(no output)",
|
|
|
12862
13192
|
if (!imageStore) {
|
|
12863
13193
|
if (!imageStoreInitPromise.current) {
|
|
12864
13194
|
imageStoreInitPromise.current = (async () => {
|
|
12865
|
-
const { ImageStore: ImageStoreClass } = await import("./ImageStore-
|
|
13195
|
+
const { ImageStore: ImageStoreClass } = await import("./ImageStore-MMUOUPI2.js");
|
|
12866
13196
|
const newImageStore = new ImageStoreClass();
|
|
12867
13197
|
setState((prev) => ({
|
|
12868
13198
|
...prev,
|