code-ollama 0.24.1 → 0.26.0
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/assets/{tui-CboegfoT.js → tui-c76QePyQ.js} +28 -35
- package/dist/cli.js +231 -194
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as LABEL, A as loadConfig, B as CHAT, C as checkHealth, D as pullModel, E as listModels, F as withSystemMessage, G as ASSISTANT, H as SEARCH_SETTINGS, I as HEADER_PREFIX, J as PLAN_GENERATION_INSTRUCTION, K as SYSTEM, L as WARNING, M as removeClipboardImage, N as saveClipboardImage, O as sanitizeAssistantContent, P as resetSystemMessage, Q as AUTO, R as LIST$1, S as TOOL_INTENT_CORRECTION, T as hasUncalledToolIntent, U as SESSION_MANAGER, V as MODEL_MANAGER, W as THEME_SETTINGS, X as BACK, Y as PLAN_INSTRUCTION, Z as CATALOG, _ as loadSession, a as normalizeToolCall, at as VERSION, b as reset, c as WRITE_TOOLS, d as write, et as PLAN, f as appendMessage, g as listSessions, h as deleteSessionIfEmpty, i as formatToolResultContent, it as NAME, j as saveConfig, k as streamChat, l as tick, m as deleteSession, n as executeTool, nt as APPROVE, o as READ_TOOLS, ot as LIST, p as createSession, q as USER, r as executeToolCall, rt as REJECT, s as TOOLS, t as checkForUpdate, tt as SAFE, u as color, v as updateSessionModel, w as deleteModel, x as setClearHandler, y as clear, z as getTheme } from "../cli.js";
|
|
2
2
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { basename, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -3255,46 +3255,36 @@ function UpdateBanner({ onLoad, theme }) {
|
|
|
3255
3255
|
});
|
|
3256
3256
|
}
|
|
3257
3257
|
//#endregion
|
|
3258
|
-
//#region src/components/App/constants.ts
|
|
3259
|
-
var Screen = /* @__PURE__ */ function(Screen) {
|
|
3260
|
-
Screen["Chat"] = "chat";
|
|
3261
|
-
Screen["ModelManager"] = "model-manager";
|
|
3262
|
-
Screen["SearchSettings"] = "search-settings";
|
|
3263
|
-
Screen["SessionManager"] = "session-manager";
|
|
3264
|
-
Screen["ThemeSettings"] = "theme-settings";
|
|
3265
|
-
return Screen;
|
|
3266
|
-
}({});
|
|
3267
|
-
//#endregion
|
|
3268
3258
|
//#region src/components/App/hooks/useScreenRouter.ts
|
|
3269
|
-
function useScreenRouter() {
|
|
3259
|
+
function useScreenRouter({ initialScreen } = {}) {
|
|
3270
3260
|
const { exit } = useApp();
|
|
3271
|
-
const [currentScreen, setScreen] = useState(
|
|
3261
|
+
const [currentScreen, setScreen] = useState(initialScreen ?? "chat");
|
|
3272
3262
|
return {
|
|
3273
3263
|
currentScreen,
|
|
3274
3264
|
setScreen,
|
|
3275
3265
|
handleClose: useCallback(() => {
|
|
3276
|
-
setScreen(
|
|
3266
|
+
setScreen(CHAT);
|
|
3277
3267
|
}, []),
|
|
3278
3268
|
handleCommand: useCallback((command, callbacks) => {
|
|
3279
3269
|
const { onCreateSession, onSetPreviewThemeId, model, theme } = callbacks;
|
|
3280
3270
|
switch (command) {
|
|
3281
3271
|
case "/session":
|
|
3282
|
-
setScreen(
|
|
3272
|
+
setScreen(SESSION_MANAGER);
|
|
3283
3273
|
break;
|
|
3284
3274
|
case "/model":
|
|
3285
|
-
setScreen(
|
|
3275
|
+
setScreen(MODEL_MANAGER);
|
|
3286
3276
|
break;
|
|
3287
3277
|
case "/search":
|
|
3288
|
-
setScreen(
|
|
3278
|
+
setScreen(SEARCH_SETTINGS);
|
|
3289
3279
|
break;
|
|
3290
3280
|
case "/theme":
|
|
3291
3281
|
onSetPreviewThemeId(theme);
|
|
3292
|
-
setScreen(
|
|
3282
|
+
setScreen(THEME_SETTINGS);
|
|
3293
3283
|
break;
|
|
3294
3284
|
case "/clear": {
|
|
3295
3285
|
resetSystemMessage();
|
|
3296
3286
|
const nextSession = onCreateSession(model);
|
|
3297
|
-
setScreen(
|
|
3287
|
+
setScreen(CHAT);
|
|
3298
3288
|
clear(nextSession.metadata.id);
|
|
3299
3289
|
break;
|
|
3300
3290
|
}
|
|
@@ -3381,7 +3371,7 @@ function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
|
|
|
3381
3371
|
activeTheme,
|
|
3382
3372
|
handleThemeClose: useCallback(() => {
|
|
3383
3373
|
setPreviewThemeId(null);
|
|
3384
|
-
setScreen(
|
|
3374
|
+
setScreen(CHAT);
|
|
3385
3375
|
}, [setScreen]),
|
|
3386
3376
|
handleThemePreview,
|
|
3387
3377
|
handleThemeSave: useCallback((themeId) => {
|
|
@@ -3473,13 +3463,13 @@ function ReadinessCheck({ errorMessage, onCommand, setupState, theme = getTheme(
|
|
|
3473
3463
|
}
|
|
3474
3464
|
//#endregion
|
|
3475
3465
|
//#region src/components/App/App.tsx
|
|
3476
|
-
function App({ sessionId }) {
|
|
3466
|
+
function App({ sessionId, initialScreen }) {
|
|
3477
3467
|
const [appConfig, setConfig] = useState(() => loadConfig());
|
|
3478
3468
|
const [mode, setMode] = useState(SAFE);
|
|
3479
3469
|
const [isLoaded, setIsLoaded] = useState(false);
|
|
3480
3470
|
const [setupState, setSetupState] = useState(() => appConfig.model ? ReadinessState.Ready : ReadinessState.MissingModelConfig);
|
|
3481
3471
|
const [setupErrorMessage, setSetupErrorMessage] = useState(null);
|
|
3482
|
-
const { currentScreen, setScreen, handleClose, handleCommand } = useScreenRouter();
|
|
3472
|
+
const { currentScreen, setScreen, handleClose, handleCommand } = useScreenRouter({ initialScreen });
|
|
3483
3473
|
const { activeSession, setSession, handleCreateSession, handleOpenSession, handleDeleteSession, handleMessagesChange } = useSessionManager({
|
|
3484
3474
|
sessionId,
|
|
3485
3475
|
model: appConfig.model ?? "",
|
|
@@ -3496,7 +3486,7 @@ function App({ sessionId }) {
|
|
|
3496
3486
|
}
|
|
3497
3487
|
return;
|
|
3498
3488
|
}
|
|
3499
|
-
if (currentScreen !==
|
|
3489
|
+
if (currentScreen !== "chat") return;
|
|
3500
3490
|
// v8 ignore next
|
|
3501
3491
|
if (isMounted) {
|
|
3502
3492
|
setSetupErrorMessage(null);
|
|
@@ -3533,7 +3523,7 @@ function App({ sessionId }) {
|
|
|
3533
3523
|
...current,
|
|
3534
3524
|
metadata: updateSessionModel(current.metadata.id, newModel)
|
|
3535
3525
|
}));
|
|
3536
|
-
setScreen(
|
|
3526
|
+
setScreen(CHAT);
|
|
3537
3527
|
}, [setScreen, setSession]);
|
|
3538
3528
|
const { activeTheme, handleThemeClose, handleThemePreview, handleThemeSave, setPreviewThemeId } = useThemeSettings({
|
|
3539
3529
|
currentTheme: appConfig.theme,
|
|
@@ -3556,7 +3546,7 @@ function App({ sessionId }) {
|
|
|
3556
3546
|
]);
|
|
3557
3547
|
let screenContent;
|
|
3558
3548
|
switch (currentScreen) {
|
|
3559
|
-
case
|
|
3549
|
+
case MODEL_MANAGER:
|
|
3560
3550
|
screenContent = /* @__PURE__ */ jsx(ModelManager, {
|
|
3561
3551
|
currentModel: appConfig.model ?? "",
|
|
3562
3552
|
onSelect: handleUpdateConfig,
|
|
@@ -3564,7 +3554,7 @@ function App({ sessionId }) {
|
|
|
3564
3554
|
theme: activeTheme
|
|
3565
3555
|
});
|
|
3566
3556
|
break;
|
|
3567
|
-
case
|
|
3557
|
+
case SEARCH_SETTINGS:
|
|
3568
3558
|
screenContent = /* @__PURE__ */ jsx(SearchSettings, {
|
|
3569
3559
|
currentUrl: appConfig.searxngBaseUrl,
|
|
3570
3560
|
onSave: handleUpdateConfig,
|
|
@@ -3572,26 +3562,26 @@ function App({ sessionId }) {
|
|
|
3572
3562
|
theme: activeTheme
|
|
3573
3563
|
});
|
|
3574
3564
|
break;
|
|
3575
|
-
case
|
|
3565
|
+
case SESSION_MANAGER:
|
|
3576
3566
|
screenContent = /* @__PURE__ */ jsx(SessionManager, {
|
|
3577
3567
|
currentSessionId: activeSession.metadata.id,
|
|
3578
3568
|
onClose: handleClose,
|
|
3579
3569
|
onDelete: (sessionId) => {
|
|
3580
3570
|
handleDeleteSession(sessionId);
|
|
3581
|
-
setScreen(
|
|
3571
|
+
setScreen(SESSION_MANAGER);
|
|
3582
3572
|
},
|
|
3583
3573
|
onNew: () => {
|
|
3584
3574
|
handleCreateSession();
|
|
3585
|
-
setScreen(
|
|
3575
|
+
setScreen(CHAT);
|
|
3586
3576
|
},
|
|
3587
3577
|
onOpen: (sessionId) => {
|
|
3588
3578
|
handleOpenSession(sessionId);
|
|
3589
|
-
setScreen(
|
|
3579
|
+
setScreen(CHAT);
|
|
3590
3580
|
},
|
|
3591
3581
|
theme: activeTheme
|
|
3592
3582
|
});
|
|
3593
3583
|
break;
|
|
3594
|
-
case
|
|
3584
|
+
case THEME_SETTINGS:
|
|
3595
3585
|
screenContent = /* @__PURE__ */ jsx(ThemeSettings, {
|
|
3596
3586
|
currentTheme: appConfig.theme,
|
|
3597
3587
|
onClose: handleThemeClose,
|
|
@@ -3599,7 +3589,7 @@ function App({ sessionId }) {
|
|
|
3599
3589
|
onSave: handleThemeSave
|
|
3600
3590
|
});
|
|
3601
3591
|
break;
|
|
3602
|
-
case
|
|
3592
|
+
case CHAT:
|
|
3603
3593
|
screenContent = setupState === ReadinessState.Ready ? /* @__PURE__ */ jsx(Chat, {
|
|
3604
3594
|
initialMessages: activeSession.messages,
|
|
3605
3595
|
model: appConfig.model,
|
|
@@ -3687,15 +3677,18 @@ function ExitHint({ action = "go back" }) {
|
|
|
3687
3677
|
}
|
|
3688
3678
|
//#endregion
|
|
3689
3679
|
//#region src/tui.tsx
|
|
3690
|
-
function renderApp(
|
|
3680
|
+
function renderApp(options = {}) {
|
|
3691
3681
|
let resetKey = 0;
|
|
3692
|
-
const app = render(/* @__PURE__ */ jsx(App, {
|
|
3682
|
+
const app = render(/* @__PURE__ */ jsx(App, {
|
|
3683
|
+
sessionId: options.sessionId,
|
|
3684
|
+
initialScreen: options.initialScreen
|
|
3685
|
+
}, resetKey), {
|
|
3693
3686
|
exitOnCtrlC: false,
|
|
3694
3687
|
maxFps: 60
|
|
3695
3688
|
});
|
|
3696
3689
|
setClearHandler((nextSessionId) => {
|
|
3697
3690
|
reset();
|
|
3698
|
-
app.rerender(/* @__PURE__ */ jsx(App, { sessionId: nextSessionId ?? sessionId }, ++resetKey));
|
|
3691
|
+
app.rerender(/* @__PURE__ */ jsx(App, { sessionId: nextSessionId ?? options.sessionId }, ++resetKey));
|
|
3699
3692
|
});
|
|
3700
3693
|
}
|
|
3701
3694
|
//#endregion
|
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ var LIST$1 = [
|
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region package.json
|
|
52
52
|
var name = "code-ollama";
|
|
53
|
-
var version = "0.
|
|
53
|
+
var version = "0.26.0";
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/constants/package.ts
|
|
56
56
|
var NAME = name;
|
|
@@ -252,6 +252,13 @@ var USER = "user";
|
|
|
252
252
|
var ASSISTANT = "assistant";
|
|
253
253
|
var SYSTEM = "system";
|
|
254
254
|
//#endregion
|
|
255
|
+
//#region src/constants/screen.ts
|
|
256
|
+
var CHAT = "chat";
|
|
257
|
+
var MODEL_MANAGER = "model-manager";
|
|
258
|
+
var SEARCH_SETTINGS = "search-settings";
|
|
259
|
+
var SESSION_MANAGER = "session-manager";
|
|
260
|
+
var THEME_SETTINGS = "theme-settings";
|
|
261
|
+
//#endregion
|
|
255
262
|
//#region src/constants/theme.ts
|
|
256
263
|
var DEFAULT_THEME_ID = "github-dark";
|
|
257
264
|
var LIST = [
|
|
@@ -852,6 +859,10 @@ var TOOLS = [
|
|
|
852
859
|
maxLines: {
|
|
853
860
|
type: "number",
|
|
854
861
|
description: "Optional maximum number of lines to read; cannot be combined with endLine"
|
|
862
|
+
},
|
|
863
|
+
maxChars: {
|
|
864
|
+
type: "number",
|
|
865
|
+
description: `Optional maximum number of characters to return; defaults to 50000; applies after any line-range selection`
|
|
855
866
|
}
|
|
856
867
|
}, ["path"]),
|
|
857
868
|
defineTool(WRITE_FILE, "Write content to a file at the specified path", {
|
|
@@ -958,56 +969,10 @@ var TOOLS = [
|
|
|
958
969
|
var READ_TOOLS = new Set(READ_TOOL_NAMES);
|
|
959
970
|
var WRITE_TOOLS = new Set(WRITE_TOOL_NAMES);
|
|
960
971
|
//#endregion
|
|
961
|
-
//#region src/utils/tools/
|
|
962
|
-
var execAsync = promisify(exec);
|
|
963
|
-
var SHELL_EXEC_OPTIONS = {
|
|
964
|
-
timeout: 3e4,
|
|
965
|
-
maxBuffer: 1024 * 1024
|
|
966
|
-
};
|
|
967
|
-
function getErrorOutput(error) {
|
|
968
|
-
if (typeof error !== "object" || error === null) return "";
|
|
969
|
-
const output = error;
|
|
970
|
-
return [output.stdout, output.stderr].filter((value) => typeof value === "string" && !!value).join("\n");
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Execute shell command with shared options (throws on error)
|
|
974
|
-
*/
|
|
975
|
-
function execShell(command) {
|
|
976
|
-
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Execute shell command
|
|
980
|
-
*/
|
|
981
|
-
async function runShell(command) {
|
|
982
|
-
try {
|
|
983
|
-
const { stdout, stderr } = await execShell(command);
|
|
984
|
-
return { content: stdout || stderr };
|
|
985
|
-
} catch (error) {
|
|
986
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
987
|
-
return {
|
|
988
|
-
content: getErrorOutput(error),
|
|
989
|
-
error: `Command failed: ${message}`,
|
|
990
|
-
// v8 ignore next
|
|
991
|
-
...error instanceof Error && error.stack ? { stack: error.stack } : {}
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
//#endregion
|
|
996
|
-
//#region src/utils/tools/filesystem.ts
|
|
972
|
+
//#region src/utils/tools/filesystem/diff.ts
|
|
997
973
|
var DIFF_CONTEXT_LINES = 3;
|
|
998
974
|
var DIFF_MAX_LINES = 120;
|
|
999
975
|
var DIFF_MAX_CHARS = 12e3;
|
|
1000
|
-
var DEFAULT_FIND_FILES_IGNORED_DIRS = [
|
|
1001
|
-
"node_modules",
|
|
1002
|
-
"__pycache__",
|
|
1003
|
-
".*cache",
|
|
1004
|
-
".tox",
|
|
1005
|
-
".venv",
|
|
1006
|
-
"venv",
|
|
1007
|
-
"dist",
|
|
1008
|
-
"build",
|
|
1009
|
-
"coverage"
|
|
1010
|
-
];
|
|
1011
976
|
function splitLines(content) {
|
|
1012
977
|
return content.split("\n");
|
|
1013
978
|
}
|
|
@@ -1055,59 +1020,9 @@ function truncateDiff(diff) {
|
|
|
1055
1020
|
visibleLines: Math.min(visibleLines.length, lines.length)
|
|
1056
1021
|
};
|
|
1057
1022
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
const camelCase = words.map((word, index) => index === 0 ? word.toLowerCase() : capitalize(word.toLowerCase())).join("");
|
|
1062
|
-
const pascalCase = words.map((word) => capitalize(word.toLowerCase())).join("");
|
|
1063
|
-
const snakeCase = words.map((word) => word.toLowerCase()).join("_");
|
|
1064
|
-
const upperSnakeCase = snakeCase.toUpperCase();
|
|
1065
|
-
const flexibleWhitespace = words.join(String.raw`\s+`);
|
|
1066
|
-
return Array.from(new Set([
|
|
1067
|
-
pattern,
|
|
1068
|
-
flexibleWhitespace,
|
|
1069
|
-
snakeCase,
|
|
1070
|
-
upperSnakeCase,
|
|
1071
|
-
camelCase,
|
|
1072
|
-
pascalCase
|
|
1073
|
-
]));
|
|
1074
|
-
}
|
|
1075
|
-
function capitalize(value) {
|
|
1076
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1077
|
-
}
|
|
1078
|
-
function escapeRegExp(value) {
|
|
1079
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1080
|
-
}
|
|
1081
|
-
function fileMatchesPattern(filePath, pattern) {
|
|
1082
|
-
const trimmedPattern = pattern?.trim();
|
|
1083
|
-
if (!trimmedPattern) return true;
|
|
1084
|
-
const normalizedPath = filePath.toLowerCase();
|
|
1085
|
-
const normalizedFileName = normalizedPath.slice(Math.max(normalizedPath.lastIndexOf("/"), normalizedPath.lastIndexOf("\\")) + 1);
|
|
1086
|
-
const normalizedPattern = trimmedPattern.toLowerCase();
|
|
1087
|
-
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedPath.includes(normalizedPattern);
|
|
1088
|
-
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1089
|
-
if (char === "*") return ".*";
|
|
1090
|
-
if (char === "?") return ".";
|
|
1091
|
-
return escapeRegExp(char);
|
|
1092
|
-
}).join("");
|
|
1093
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
1094
|
-
return regex.test(normalizedPath) || regex.test(normalizedFileName);
|
|
1095
|
-
}
|
|
1096
|
-
function valueMatchesWildcardPattern(value, pattern) {
|
|
1097
|
-
const normalizedValue = value.toLowerCase();
|
|
1098
|
-
const normalizedPattern = pattern.trim().toLowerCase();
|
|
1099
|
-
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedValue === normalizedPattern;
|
|
1100
|
-
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1101
|
-
if (char === "*") return ".*";
|
|
1102
|
-
if (char === "?") return ".";
|
|
1103
|
-
return escapeRegExp(char);
|
|
1104
|
-
}).join("");
|
|
1105
|
-
return new RegExp(`^${regexPattern}$`).test(normalizedValue);
|
|
1106
|
-
}
|
|
1107
|
-
function directoryMatchesIgnoredPattern(dirName, ignoredDirs) {
|
|
1108
|
-
for (const ignoredDir of ignoredDirs) if (valueMatchesWildcardPattern(dirName, ignoredDir)) return true;
|
|
1109
|
-
return false;
|
|
1110
|
-
}
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/utils/tools/filesystem/files.ts
|
|
1025
|
+
var READ_FILE_MAX_CHARS = 5e4;
|
|
1111
1026
|
function formatNumberedLines(lines, startLine) {
|
|
1112
1027
|
return lines.map((line, index) => `${String(startLine + index)}: ${line}`).join("\n");
|
|
1113
1028
|
}
|
|
@@ -1121,17 +1036,24 @@ function readFile(filePath, options = {}) {
|
|
|
1121
1036
|
error: `File not found: ${filePath}`
|
|
1122
1037
|
};
|
|
1123
1038
|
const content = readFileSync(filePath, "utf8");
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1039
|
+
const isPartialRead = options.startLine !== void 0 || options.endLine !== void 0 || options.maxLines !== void 0;
|
|
1040
|
+
let output;
|
|
1041
|
+
if (!isPartialRead) output = content;
|
|
1042
|
+
else {
|
|
1043
|
+
const lines = content.split("\n");
|
|
1044
|
+
const startLine = options.startLine ?? 1;
|
|
1045
|
+
const endLine = options.endLine ?? startLine + (options.maxLines ?? lines.length) - 1;
|
|
1046
|
+
const startIndex = startLine - 1;
|
|
1047
|
+
const endIndex = Math.min(lines.length, endLine);
|
|
1048
|
+
if (startIndex >= lines.length) return {
|
|
1049
|
+
content: "",
|
|
1050
|
+
error: "Invalid line range"
|
|
1051
|
+
};
|
|
1052
|
+
output = formatNumberedLines(lines.slice(startIndex, endIndex), startLine);
|
|
1053
|
+
}
|
|
1054
|
+
const cap = options.maxChars ?? READ_FILE_MAX_CHARS;
|
|
1055
|
+
if (output.length > cap) return { content: `${output.slice(0, cap)}\n[file truncated: showing ${String(cap)} of ${String(output.length)} chars]` };
|
|
1056
|
+
return { content: output };
|
|
1135
1057
|
} catch (error) {
|
|
1136
1058
|
return {
|
|
1137
1059
|
content: "",
|
|
@@ -1195,6 +1117,186 @@ function editFile(filePath, oldText, newText) {
|
|
|
1195
1117
|
};
|
|
1196
1118
|
}
|
|
1197
1119
|
}
|
|
1120
|
+
//#endregion
|
|
1121
|
+
//#region src/utils/tools/filesystem/find.ts
|
|
1122
|
+
var DEFAULT_FIND_FILES_IGNORED_DIRS = [
|
|
1123
|
+
"node_modules",
|
|
1124
|
+
"__pycache__",
|
|
1125
|
+
".*cache",
|
|
1126
|
+
".tox",
|
|
1127
|
+
".venv",
|
|
1128
|
+
"venv",
|
|
1129
|
+
"dist",
|
|
1130
|
+
"build",
|
|
1131
|
+
"coverage"
|
|
1132
|
+
];
|
|
1133
|
+
function escapeRegExp(value) {
|
|
1134
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1135
|
+
}
|
|
1136
|
+
function fileMatchesPattern(filePath, pattern) {
|
|
1137
|
+
const trimmedPattern = pattern?.trim();
|
|
1138
|
+
if (!trimmedPattern) return true;
|
|
1139
|
+
const normalizedPath = filePath.toLowerCase();
|
|
1140
|
+
const normalizedFileName = normalizedPath.slice(Math.max(normalizedPath.lastIndexOf("/"), normalizedPath.lastIndexOf("\\")) + 1);
|
|
1141
|
+
const normalizedPattern = trimmedPattern.toLowerCase();
|
|
1142
|
+
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedPath.includes(normalizedPattern);
|
|
1143
|
+
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1144
|
+
if (char === "*") return ".*";
|
|
1145
|
+
if (char === "?") return ".";
|
|
1146
|
+
return escapeRegExp(char);
|
|
1147
|
+
}).join("");
|
|
1148
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
1149
|
+
return regex.test(normalizedPath) || regex.test(normalizedFileName);
|
|
1150
|
+
}
|
|
1151
|
+
function valueMatchesWildcardPattern(value, pattern) {
|
|
1152
|
+
const normalizedValue = value.toLowerCase();
|
|
1153
|
+
const normalizedPattern = pattern.trim().toLowerCase();
|
|
1154
|
+
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedValue === normalizedPattern;
|
|
1155
|
+
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1156
|
+
if (char === "*") return ".*";
|
|
1157
|
+
if (char === "?") return ".";
|
|
1158
|
+
return escapeRegExp(char);
|
|
1159
|
+
}).join("");
|
|
1160
|
+
return new RegExp(`^${regexPattern}$`).test(normalizedValue);
|
|
1161
|
+
}
|
|
1162
|
+
function directoryMatchesIgnoredPattern(dirName, ignoredDirs) {
|
|
1163
|
+
for (const ignoredDir of ignoredDirs) if (valueMatchesWildcardPattern(dirName, ignoredDir)) return true;
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Recursively find files by path
|
|
1168
|
+
*/
|
|
1169
|
+
function findFiles(dirPath, options = {}) {
|
|
1170
|
+
try {
|
|
1171
|
+
if (!existsSync(dirPath)) return {
|
|
1172
|
+
content: "",
|
|
1173
|
+
error: `Directory not found: ${dirPath}`
|
|
1174
|
+
};
|
|
1175
|
+
if (!statSync(dirPath).isDirectory()) return {
|
|
1176
|
+
content: "",
|
|
1177
|
+
error: `Path is not a directory: ${dirPath}`
|
|
1178
|
+
};
|
|
1179
|
+
const results = [];
|
|
1180
|
+
const includeHidden = options.includeHidden ?? false;
|
|
1181
|
+
const ignoredDirs = new Set(options.ignoredDirs ?? DEFAULT_FIND_FILES_IGNORED_DIRS);
|
|
1182
|
+
function searchDirectory(currentPath) {
|
|
1183
|
+
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1184
|
+
for (const entry of entries) {
|
|
1185
|
+
const fullPath = join(currentPath, entry.name);
|
|
1186
|
+
if (entry.isDirectory()) {
|
|
1187
|
+
if (entry.name !== ".git" && !directoryMatchesIgnoredPattern(entry.name, ignoredDirs) && (includeHidden || !entry.name.startsWith("."))) searchDirectory(fullPath);
|
|
1188
|
+
} else if (entry.isFile() && (includeHidden || !entry.name.startsWith(".")) && fileMatchesPattern(fullPath, options.pattern)) results.push(fullPath);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
searchDirectory(dirPath);
|
|
1192
|
+
return { content: results.join("\n") };
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
return {
|
|
1195
|
+
content: "",
|
|
1196
|
+
error: `Failed to find files: ${error instanceof Error ? error.message : String(error)}`
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
//#endregion
|
|
1201
|
+
//#region src/utils/tools/shell.ts
|
|
1202
|
+
var execAsync = promisify(exec);
|
|
1203
|
+
var SHELL_EXEC_OPTIONS = {
|
|
1204
|
+
timeout: 3e4,
|
|
1205
|
+
maxBuffer: 1024 * 1024
|
|
1206
|
+
};
|
|
1207
|
+
function getErrorOutput(error) {
|
|
1208
|
+
if (typeof error !== "object" || error === null) return "";
|
|
1209
|
+
const output = error;
|
|
1210
|
+
return [output.stdout, output.stderr].filter((value) => typeof value === "string" && !!value).join("\n");
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Execute shell command with shared options (throws on error)
|
|
1214
|
+
*/
|
|
1215
|
+
function execShell(command) {
|
|
1216
|
+
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Execute shell command
|
|
1220
|
+
*/
|
|
1221
|
+
async function runShell(command) {
|
|
1222
|
+
try {
|
|
1223
|
+
const { stdout, stderr } = await execShell(command);
|
|
1224
|
+
return { content: stdout || stderr };
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1227
|
+
return {
|
|
1228
|
+
content: getErrorOutput(error),
|
|
1229
|
+
error: `Command failed: ${message}`,
|
|
1230
|
+
// v8 ignore next
|
|
1231
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {}
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
//#endregion
|
|
1236
|
+
//#region src/utils/tools/filesystem/grep.ts
|
|
1237
|
+
function capitalize(value) {
|
|
1238
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1239
|
+
}
|
|
1240
|
+
function buildSearchPatterns(pattern) {
|
|
1241
|
+
const words = pattern.trim().split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
1242
|
+
if (words.length < 2) return [pattern];
|
|
1243
|
+
const camelCase = words.map((word, index) => index === 0 ? word.toLowerCase() : capitalize(word.toLowerCase())).join("");
|
|
1244
|
+
const pascalCase = words.map((word) => capitalize(word.toLowerCase())).join("");
|
|
1245
|
+
const snakeCase = words.map((word) => word.toLowerCase()).join("_");
|
|
1246
|
+
const upperSnakeCase = snakeCase.toUpperCase();
|
|
1247
|
+
const flexibleWhitespace = words.join(String.raw`\s+`);
|
|
1248
|
+
return Array.from(new Set([
|
|
1249
|
+
pattern,
|
|
1250
|
+
flexibleWhitespace,
|
|
1251
|
+
snakeCase,
|
|
1252
|
+
upperSnakeCase,
|
|
1253
|
+
camelCase,
|
|
1254
|
+
pascalCase
|
|
1255
|
+
]));
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Search for pattern in files using ripgrep if available, fallback to Node.js
|
|
1259
|
+
*/
|
|
1260
|
+
async function grepSearch(pattern, dirPath) {
|
|
1261
|
+
const patterns = buildSearchPatterns(pattern);
|
|
1262
|
+
for (const searchPattern of patterns) try {
|
|
1263
|
+
const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${searchPattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
|
|
1264
|
+
if (stdout) return { content: stdout };
|
|
1265
|
+
} catch {}
|
|
1266
|
+
try {
|
|
1267
|
+
if (!existsSync(dirPath)) return {
|
|
1268
|
+
content: "",
|
|
1269
|
+
error: `Directory not found: ${dirPath}`
|
|
1270
|
+
};
|
|
1271
|
+
const regexes = patterns.map((searchPattern) => new RegExp(searchPattern));
|
|
1272
|
+
const results = [];
|
|
1273
|
+
function searchDirectory(currentPath) {
|
|
1274
|
+
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1275
|
+
for (const entry of entries) {
|
|
1276
|
+
const fullPath = join(currentPath, entry.name);
|
|
1277
|
+
if (entry.isDirectory()) {
|
|
1278
|
+
if (!entry.name.startsWith(".") && entry.name !== "node_modules") searchDirectory(fullPath);
|
|
1279
|
+
} else if (entry.isFile()) try {
|
|
1280
|
+
const lines = readFileSync(fullPath, "utf8").split("\n");
|
|
1281
|
+
for (let i = 0; i < lines.length; i++) for (const regex of regexes) if (regex.test(lines[i])) {
|
|
1282
|
+
results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
} catch {}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
searchDirectory(dirPath);
|
|
1289
|
+
if (!results.length) return { content: "No matches found" };
|
|
1290
|
+
return { content: results.join("\n") };
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
return {
|
|
1293
|
+
content: "",
|
|
1294
|
+
error: `Search failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
//#endregion
|
|
1299
|
+
//#region src/utils/tools/filesystem/paths.ts
|
|
1198
1300
|
/**
|
|
1199
1301
|
* Create a directory and any missing parent directories
|
|
1200
1302
|
*/
|
|
@@ -1285,81 +1387,6 @@ function listDir(dirPath) {
|
|
|
1285
1387
|
};
|
|
1286
1388
|
}
|
|
1287
1389
|
}
|
|
1288
|
-
/**
|
|
1289
|
-
* Recursively find files by path
|
|
1290
|
-
*/
|
|
1291
|
-
function findFiles(dirPath, options = {}) {
|
|
1292
|
-
try {
|
|
1293
|
-
if (!existsSync(dirPath)) return {
|
|
1294
|
-
content: "",
|
|
1295
|
-
error: `Directory not found: ${dirPath}`
|
|
1296
|
-
};
|
|
1297
|
-
if (!statSync(dirPath).isDirectory()) return {
|
|
1298
|
-
content: "",
|
|
1299
|
-
error: `Path is not a directory: ${dirPath}`
|
|
1300
|
-
};
|
|
1301
|
-
const results = [];
|
|
1302
|
-
const includeHidden = options.includeHidden ?? false;
|
|
1303
|
-
const ignoredDirs = new Set(options.ignoredDirs ?? DEFAULT_FIND_FILES_IGNORED_DIRS);
|
|
1304
|
-
function searchDirectory(currentPath) {
|
|
1305
|
-
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1306
|
-
for (const entry of entries) {
|
|
1307
|
-
const fullPath = join(currentPath, entry.name);
|
|
1308
|
-
if (entry.isDirectory()) {
|
|
1309
|
-
if (entry.name !== ".git" && !directoryMatchesIgnoredPattern(entry.name, ignoredDirs) && (includeHidden || !entry.name.startsWith("."))) searchDirectory(fullPath);
|
|
1310
|
-
} else if (entry.isFile() && (includeHidden || !entry.name.startsWith(".")) && fileMatchesPattern(fullPath, options.pattern)) results.push(fullPath);
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
searchDirectory(dirPath);
|
|
1314
|
-
return { content: results.join("\n") };
|
|
1315
|
-
} catch (error) {
|
|
1316
|
-
return {
|
|
1317
|
-
content: "",
|
|
1318
|
-
error: `Failed to find files: ${error instanceof Error ? error.message : String(error)}`
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Search for pattern in files using ripgrep if available, fallback to Node.js
|
|
1324
|
-
*/
|
|
1325
|
-
async function grepSearch(pattern, dirPath) {
|
|
1326
|
-
const patterns = buildSearchPatterns(pattern);
|
|
1327
|
-
for (const searchPattern of patterns) try {
|
|
1328
|
-
const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${searchPattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
|
|
1329
|
-
if (stdout) return { content: stdout };
|
|
1330
|
-
} catch {}
|
|
1331
|
-
try {
|
|
1332
|
-
if (!existsSync(dirPath)) return {
|
|
1333
|
-
content: "",
|
|
1334
|
-
error: `Directory not found: ${dirPath}`
|
|
1335
|
-
};
|
|
1336
|
-
const regexes = patterns.map((searchPattern) => new RegExp(searchPattern));
|
|
1337
|
-
const results = [];
|
|
1338
|
-
function searchDirectory(currentPath) {
|
|
1339
|
-
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1340
|
-
for (const entry of entries) {
|
|
1341
|
-
const fullPath = join(currentPath, entry.name);
|
|
1342
|
-
if (entry.isDirectory()) {
|
|
1343
|
-
if (!entry.name.startsWith(".") && entry.name !== "node_modules") searchDirectory(fullPath);
|
|
1344
|
-
} else if (entry.isFile()) try {
|
|
1345
|
-
const lines = readFileSync(fullPath, "utf8").split("\n");
|
|
1346
|
-
for (let i = 0; i < lines.length; i++) for (const regex of regexes) if (regex.test(lines[i])) {
|
|
1347
|
-
results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
|
|
1348
|
-
break;
|
|
1349
|
-
}
|
|
1350
|
-
} catch {}
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
searchDirectory(dirPath);
|
|
1354
|
-
if (!results.length) return { content: "No matches found" };
|
|
1355
|
-
return { content: results.join("\n") };
|
|
1356
|
-
} catch (error) {
|
|
1357
|
-
return {
|
|
1358
|
-
content: "",
|
|
1359
|
-
error: `Search failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
1390
|
//#endregion
|
|
1364
1391
|
//#region src/utils/tools/web/fetch.ts
|
|
1365
1392
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -1568,7 +1595,8 @@ function validateArgs(name, args) {
|
|
|
1568
1595
|
for (const key of [
|
|
1569
1596
|
"startLine",
|
|
1570
1597
|
"endLine",
|
|
1571
|
-
"maxLines"
|
|
1598
|
+
"maxLines",
|
|
1599
|
+
"maxChars"
|
|
1572
1600
|
]) if (args[key] !== void 0 && !Number.isInteger(args[key])) return {
|
|
1573
1601
|
content: "",
|
|
1574
1602
|
error: `Invalid optional numeric argument: ${key} (received keys: ${received})`
|
|
@@ -1577,6 +1605,10 @@ function validateArgs(name, args) {
|
|
|
1577
1605
|
content: "",
|
|
1578
1606
|
error: "Invalid read range: startLine, endLine, and maxLines must be >= 1"
|
|
1579
1607
|
};
|
|
1608
|
+
if (typeof args.maxChars === "number" && args.maxChars < 1) return {
|
|
1609
|
+
content: "",
|
|
1610
|
+
error: "Invalid read range: maxChars must be >= 1"
|
|
1611
|
+
};
|
|
1580
1612
|
if (args.endLine !== void 0 && args.maxLines !== void 0) return {
|
|
1581
1613
|
content: "",
|
|
1582
1614
|
error: "Invalid read range: endLine cannot be combined with maxLines"
|
|
@@ -1684,6 +1716,7 @@ async function executeTool(name, args, options) {
|
|
|
1684
1716
|
switch (name) {
|
|
1685
1717
|
case READ_FILE: return readFile(stringArgs.path, {
|
|
1686
1718
|
endLine: args.endLine,
|
|
1719
|
+
maxChars: args.maxChars,
|
|
1687
1720
|
maxLines: args.maxLines,
|
|
1688
1721
|
startLine: args.startLine
|
|
1689
1722
|
});
|
|
@@ -1756,15 +1789,19 @@ cli.command("run <model> <prompt>", "Run a one-off prompt").action(async (model,
|
|
|
1756
1789
|
process.exitCode = 1;
|
|
1757
1790
|
}
|
|
1758
1791
|
});
|
|
1759
|
-
cli.command("resume
|
|
1792
|
+
cli.command("resume [sessionId]", "Resume a saved session").action(async (sessionId) => {
|
|
1760
1793
|
try {
|
|
1794
|
+
if (!sessionId) {
|
|
1795
|
+
await launchTui({ initialScreen: SESSION_MANAGER });
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1761
1798
|
const loaded = loadSession(sessionId);
|
|
1762
1799
|
if (loaded.metadata.directory && loaded.metadata.directory !== process.cwd()) {
|
|
1763
1800
|
writeError(color(`${WARNING} Cannot resume: session belongs to ${loaded.metadata.directory}\n`, "yellow"));
|
|
1764
1801
|
process.exitCode = 1;
|
|
1765
1802
|
return;
|
|
1766
1803
|
}
|
|
1767
|
-
await launchTui(sessionId);
|
|
1804
|
+
await launchTui({ sessionId });
|
|
1768
1805
|
} catch (error) {
|
|
1769
1806
|
writeError(`Error: ${error instanceof Error ? error.message : "Unknown error"}\n`);
|
|
1770
1807
|
process.exitCode = 1;
|
|
@@ -1839,10 +1876,10 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1839
1876
|
]);
|
|
1840
1877
|
else await launchTui();
|
|
1841
1878
|
}
|
|
1842
|
-
async function launchTui(
|
|
1843
|
-
const { renderApp } = await import("./assets/tui-
|
|
1879
|
+
async function launchTui(options = {}) {
|
|
1880
|
+
const { renderApp } = await import("./assets/tui-c76QePyQ.js");
|
|
1844
1881
|
reset();
|
|
1845
|
-
renderApp(
|
|
1882
|
+
renderApp(options);
|
|
1846
1883
|
}
|
|
1847
1884
|
// v8 ignore start
|
|
1848
1885
|
function isEntrypoint(argv1 = process.argv[1]) {
|
|
@@ -1856,4 +1893,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
|
|
|
1856
1893
|
if (isEntrypoint()) main();
|
|
1857
1894
|
// v8 ignore stop
|
|
1858
1895
|
//#endregion
|
|
1859
|
-
export {
|
|
1896
|
+
export { LABEL as $, loadConfig as A, CHAT as B, checkHealth as C, pullModel as D, listModels as E, withSystemMessage as F, ASSISTANT as G, SEARCH_SETTINGS as H, HEADER_PREFIX as I, PLAN_GENERATION_INSTRUCTION as J, SYSTEM as K, WARNING as L, removeClipboardImage as M, saveClipboardImage as N, sanitizeAssistantContent as O, resetSystemMessage as P, AUTO as Q, LIST as R, TOOL_INTENT_CORRECTION as S, hasUncalledToolIntent as T, SESSION_MANAGER as U, MODEL_MANAGER as V, THEME_SETTINGS as W, BACK as X, PLAN_INSTRUCTION as Y, CATALOG as Z, loadSession as _, normalizeToolCall as a, VERSION as at, reset as b, WRITE_TOOLS as c, write as d, PLAN as et, appendMessage as f, listSessions as g, deleteSessionIfEmpty as h, formatToolResultContent as i, NAME as it, saveConfig as j, streamChat as k, tick as l, deleteSession as m, main, executeTool as n, APPROVE as nt, READ_TOOLS as o, LIST$1 as ot, createSession as p, USER as q, executeToolCall as r, REJECT as rt, TOOLS as s, checkForUpdate as t, SAFE as tt, color as u, updateSessionModel as v, deleteModel as w, setClearHandler as x, clear as y, getTheme as z };
|