@coreyuan/vector-mind 1.0.30 → 1.0.32
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 +2 -2
- package/dist/builtin-conventions.js +20 -0
- package/dist/builtin-conventions.js.map +1 -1
- package/dist/builtin-instructions.d.ts +1 -0
- package/dist/builtin-instructions.js +1 -0
- package/dist/builtin-instructions.js.map +1 -1
- package/dist/index.js +512 -156
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from "node:fs";
|
|
|
4
4
|
import * as readline from "node:readline";
|
|
5
5
|
import crypto from "node:crypto";
|
|
6
6
|
import os from "node:os";
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
import chokidar from "chokidar";
|
|
9
10
|
import Database from "better-sqlite3";
|
|
@@ -13,9 +14,9 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
13
14
|
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
14
15
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
15
16
|
import { BUILTIN_CONVENTIONS } from "./builtin-conventions.js";
|
|
16
|
-
import { BUILTIN_ARCHITECTURE_AND_CODE_ORGANIZATION_INSTRUCTIONS, BUILTIN_DESTRUCTIVE_OPERATION_GUARD_INSTRUCTIONS, BUILTIN_PLAN_LITE_INSTRUCTIONS, BUILTIN_WRITE_POLICY_INSTRUCTIONS, } from "./builtin-instructions.js";
|
|
17
|
+
import { BUILTIN_ARCHITECTURE_AND_CODE_ORGANIZATION_INSTRUCTIONS, BUILTIN_DESTRUCTIVE_OPERATION_GUARD_INSTRUCTIONS, BUILTIN_LOW_OVERHEAD_WORKFLOW_INSTRUCTIONS, BUILTIN_PLAN_LITE_INSTRUCTIONS, BUILTIN_WRITE_POLICY_INSTRUCTIONS, } from "./builtin-instructions.js";
|
|
17
18
|
const SERVER_NAME = "vector-mind";
|
|
18
|
-
const SERVER_VERSION = "1.0.
|
|
19
|
+
const SERVER_VERSION = "1.0.32";
|
|
19
20
|
const rootFromEnv = process.env.VECTORMIND_ROOT?.trim() ?? "";
|
|
20
21
|
const prettyJsonOutput = ["1", "true", "on", "yes"].includes((process.env.VECTORMIND_PRETTY_JSON ?? "").trim().toLowerCase());
|
|
21
22
|
const debugLogEnabled = ["1", "true", "on", "yes"].includes((process.env.VECTORMIND_DEBUG_LOG ?? "").trim().toLowerCase());
|
|
@@ -64,6 +65,11 @@ const PENDING_PRUNE_EVERY = (() => {
|
|
|
64
65
|
return 500;
|
|
65
66
|
return n;
|
|
66
67
|
})();
|
|
68
|
+
const RIPGREP_RESOLVE_TIMEOUT_MS = 5_000;
|
|
69
|
+
const RIPGREP_SEARCH_TIMEOUT_MS = 30_000;
|
|
70
|
+
const RIPGREP_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
71
|
+
let cachedRipgrepCommand;
|
|
72
|
+
let cachedRipgrepResolveError = null;
|
|
67
73
|
function getCodexHomeDir() {
|
|
68
74
|
const raw = process.env.CODEX_HOME?.trim();
|
|
69
75
|
if (raw)
|
|
@@ -253,7 +259,7 @@ function summarizeActivityEvent(e) {
|
|
|
253
259
|
case "semantic_search":
|
|
254
260
|
return `semantic_search mode=${String(d.mode ?? "")} q=${String(d.query ?? "")} matches=${String(d.matches ?? "")}`;
|
|
255
261
|
case "grep":
|
|
256
|
-
return `grep q=${String(d.query ?? "")} matches=${String(d.matches ?? "")} truncated=${String(d.truncated ?? "")}`;
|
|
262
|
+
return `grep backend=${String(d.backend ?? "")} q=${String(d.query ?? "")} matches=${String(d.matches ?? "")} truncated=${String(d.truncated ?? "")}`;
|
|
257
263
|
case "query_codebase":
|
|
258
264
|
return `query_codebase q=${String(d.query ?? "")} matches=${String(d.matches ?? "")}`;
|
|
259
265
|
case "read_file_lines":
|
|
@@ -505,6 +511,22 @@ const IGNORED_PATH_SEGMENTS = new Set([
|
|
|
505
511
|
"x64",
|
|
506
512
|
"x86",
|
|
507
513
|
].map((s) => s.toLowerCase()));
|
|
514
|
+
const NOISE_FILE_SUFFIXES = [
|
|
515
|
+
".min.js",
|
|
516
|
+
".min.css",
|
|
517
|
+
".bundle.js",
|
|
518
|
+
".bundle.css",
|
|
519
|
+
".chunk.js",
|
|
520
|
+
".chunk.css",
|
|
521
|
+
];
|
|
522
|
+
const NOISE_FILE_BASENAMES = [
|
|
523
|
+
"package-lock.json",
|
|
524
|
+
"pnpm-lock.yaml",
|
|
525
|
+
"yarn.lock",
|
|
526
|
+
"bun.lockb",
|
|
527
|
+
"cargo.lock",
|
|
528
|
+
"composer.lock",
|
|
529
|
+
];
|
|
508
530
|
const IGNORED_LIKE_PATTERNS = (() => {
|
|
509
531
|
const patterns = [];
|
|
510
532
|
for (const seg of IGNORED_PATH_SEGMENTS) {
|
|
@@ -608,27 +630,11 @@ function pruneIgnoredIndexesByPathPatterns() {
|
|
|
608
630
|
function pruneFilenameNoiseIndexes() {
|
|
609
631
|
if (!db)
|
|
610
632
|
return { chunks_deleted: 0, symbols_deleted: 0 };
|
|
611
|
-
const suffixes = [
|
|
612
|
-
".min.js",
|
|
613
|
-
".min.css",
|
|
614
|
-
".bundle.js",
|
|
615
|
-
".bundle.css",
|
|
616
|
-
".chunk.js",
|
|
617
|
-
".chunk.css",
|
|
618
|
-
];
|
|
619
|
-
const baseNames = [
|
|
620
|
-
"package-lock.json",
|
|
621
|
-
"pnpm-lock.yaml",
|
|
622
|
-
"yarn.lock",
|
|
623
|
-
"bun.lockb",
|
|
624
|
-
"cargo.lock",
|
|
625
|
-
"composer.lock",
|
|
626
|
-
];
|
|
627
633
|
try {
|
|
628
|
-
const suffixWhere =
|
|
629
|
-
const baseWhere =
|
|
630
|
-
const suffixArgs =
|
|
631
|
-
const baseArgs =
|
|
634
|
+
const suffixWhere = NOISE_FILE_SUFFIXES.map(() => "LOWER(file_path) LIKE ?").join(" OR ");
|
|
635
|
+
const baseWhere = NOISE_FILE_BASENAMES.map(() => "LOWER(file_path) LIKE ?").join(" OR ");
|
|
636
|
+
const suffixArgs = NOISE_FILE_SUFFIXES.map((s) => `%${s}`);
|
|
637
|
+
const baseArgs = NOISE_FILE_BASENAMES.map((n) => `%/${n}`);
|
|
632
638
|
const whereParts = [];
|
|
633
639
|
const args = [];
|
|
634
640
|
if (suffixWhere) {
|
|
@@ -710,21 +716,9 @@ function isSymbolIndexableFile(filePath) {
|
|
|
710
716
|
}
|
|
711
717
|
function shouldIgnoreContentFile(filePath) {
|
|
712
718
|
const base = path.basename(filePath).toLowerCase();
|
|
713
|
-
|
|
714
|
-
"package-lock.json",
|
|
715
|
-
"pnpm-lock.yaml",
|
|
716
|
-
"yarn.lock",
|
|
717
|
-
"bun.lockb",
|
|
718
|
-
"cargo.lock",
|
|
719
|
-
"composer.lock",
|
|
720
|
-
]);
|
|
721
|
-
if (ignoreNames.has(base))
|
|
722
|
-
return true;
|
|
723
|
-
if (base.endsWith(".min.js") || base.endsWith(".min.css"))
|
|
724
|
-
return true;
|
|
725
|
-
if (base.endsWith(".bundle.js") || base.endsWith(".bundle.css"))
|
|
719
|
+
if (NOISE_FILE_BASENAMES.includes(base))
|
|
726
720
|
return true;
|
|
727
|
-
if (
|
|
721
|
+
if (NOISE_FILE_SUFFIXES.some((suffix) => base.endsWith(suffix)))
|
|
728
722
|
return true;
|
|
729
723
|
return false;
|
|
730
724
|
}
|
|
@@ -1175,13 +1169,14 @@ const GrepArgsSchema = ProjectRootArgSchema.merge(z.object({
|
|
|
1175
1169
|
// If case_sensitive is omitted and smart_case=true, uppercase => case-sensitive, otherwise case-insensitive.
|
|
1176
1170
|
smart_case: z.boolean().optional().default(true),
|
|
1177
1171
|
case_sensitive: z.boolean().optional(),
|
|
1178
|
-
//
|
|
1172
|
+
// Compatibility knob for the indexed fallback when ripgrep is unavailable.
|
|
1179
1173
|
literal_hint: z.string().optional().default(""),
|
|
1180
|
-
//
|
|
1174
|
+
// Compatibility knob for the indexed fallback when ripgrep is unavailable.
|
|
1181
1175
|
kinds: z.array(z.string().min(1)).optional(),
|
|
1182
1176
|
include_paths: z.array(z.string().min(1)).optional(),
|
|
1183
1177
|
exclude_paths: z.array(z.string().min(1)).optional(),
|
|
1184
1178
|
max_results: z.number().int().min(1).max(5000).optional().default(200),
|
|
1179
|
+
// Compatibility knob for the indexed fallback when ripgrep is unavailable.
|
|
1185
1180
|
max_candidates: z.number().int().min(1).max(50_000).optional(),
|
|
1186
1181
|
}));
|
|
1187
1182
|
const ReadFileLinesArgsSchema = ProjectRootArgSchema.merge(z.object({
|
|
@@ -1922,6 +1917,385 @@ function compileGrepRegex(opts) {
|
|
|
1922
1917
|
const source = opts.mode === "literal" ? escapeRegExp(opts.query) : opts.query;
|
|
1923
1918
|
return new RegExp(source, flags);
|
|
1924
1919
|
}
|
|
1920
|
+
function trimGrepText(input, maxChars) {
|
|
1921
|
+
if (input.length <= maxChars)
|
|
1922
|
+
return input;
|
|
1923
|
+
return `${input.slice(0, maxChars)}…`;
|
|
1924
|
+
}
|
|
1925
|
+
function buildGrepPreviewSnippet(lineText, col, maxChars = 500) {
|
|
1926
|
+
const clean = lineText.replace(/\r$/, "");
|
|
1927
|
+
if (clean.length <= maxChars)
|
|
1928
|
+
return clean;
|
|
1929
|
+
const matchIndex = Math.max(0, col - 1);
|
|
1930
|
+
let start = Math.max(0, matchIndex - Math.floor(maxChars * 0.35));
|
|
1931
|
+
if (start + maxChars > clean.length)
|
|
1932
|
+
start = Math.max(0, clean.length - maxChars);
|
|
1933
|
+
const end = Math.min(clean.length, start + maxChars);
|
|
1934
|
+
let snippet = clean.slice(start, end);
|
|
1935
|
+
if (start > 0)
|
|
1936
|
+
snippet = `…${snippet}`;
|
|
1937
|
+
if (end < clean.length)
|
|
1938
|
+
snippet = `${snippet}…`;
|
|
1939
|
+
return snippet;
|
|
1940
|
+
}
|
|
1941
|
+
function extractGrepMatchText(opts) {
|
|
1942
|
+
const clean = opts.lineText.replace(/\r$/, "");
|
|
1943
|
+
const startIndex = Math.max(0, opts.col - 1);
|
|
1944
|
+
if (opts.mode === "literal") {
|
|
1945
|
+
const slice = clean.slice(startIndex, startIndex + opts.query.length) || opts.query;
|
|
1946
|
+
return trimGrepText(slice, 200);
|
|
1947
|
+
}
|
|
1948
|
+
try {
|
|
1949
|
+
const flags = opts.caseSensitive ? "m" : "im";
|
|
1950
|
+
const anchored = new RegExp(opts.query, flags);
|
|
1951
|
+
const tail = clean.slice(startIndex);
|
|
1952
|
+
const found = anchored.exec(tail);
|
|
1953
|
+
if (found?.index === 0 && found[0])
|
|
1954
|
+
return trimGrepText(found[0], 200);
|
|
1955
|
+
}
|
|
1956
|
+
catch { }
|
|
1957
|
+
const fallback = clean.slice(startIndex, Math.min(clean.length, startIndex + 200));
|
|
1958
|
+
return trimGrepText(fallback || opts.query, 200);
|
|
1959
|
+
}
|
|
1960
|
+
function toProcessText(value) {
|
|
1961
|
+
if (typeof value === "string")
|
|
1962
|
+
return value;
|
|
1963
|
+
if (value == null)
|
|
1964
|
+
return "";
|
|
1965
|
+
return value.toString("utf8");
|
|
1966
|
+
}
|
|
1967
|
+
function formatProcessFailure(result) {
|
|
1968
|
+
if (result.error)
|
|
1969
|
+
return `${result.error.name}: ${result.error.message}`;
|
|
1970
|
+
const stderr = toProcessText(result.stderr).trim();
|
|
1971
|
+
if (stderr)
|
|
1972
|
+
return stderr;
|
|
1973
|
+
const stdout = toProcessText(result.stdout).trim();
|
|
1974
|
+
if (stdout)
|
|
1975
|
+
return stdout;
|
|
1976
|
+
if (typeof result.status === "number")
|
|
1977
|
+
return `exit ${result.status}`;
|
|
1978
|
+
if (result.signal)
|
|
1979
|
+
return `signal ${result.signal}`;
|
|
1980
|
+
return "unknown failure";
|
|
1981
|
+
}
|
|
1982
|
+
function buildRipgrepEnv() {
|
|
1983
|
+
const env = { ...process.env };
|
|
1984
|
+
delete env.RIPGREP_CONFIG_PATH;
|
|
1985
|
+
return env;
|
|
1986
|
+
}
|
|
1987
|
+
function pushUniqueCandidate(candidates, seen, raw) {
|
|
1988
|
+
const value = raw?.trim();
|
|
1989
|
+
if (!value || seen.has(value))
|
|
1990
|
+
return;
|
|
1991
|
+
seen.add(value);
|
|
1992
|
+
candidates.push(value);
|
|
1993
|
+
}
|
|
1994
|
+
function listChildDirsSafe(dirPath) {
|
|
1995
|
+
try {
|
|
1996
|
+
return fs
|
|
1997
|
+
.readdirSync(dirPath, { withFileTypes: true })
|
|
1998
|
+
.filter((entry) => entry.isDirectory())
|
|
1999
|
+
.map((entry) => path.join(dirPath, entry.name));
|
|
2000
|
+
}
|
|
2001
|
+
catch {
|
|
2002
|
+
return [];
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
function collectRipgrepCandidates() {
|
|
2006
|
+
const candidates = [];
|
|
2007
|
+
const seen = new Set();
|
|
2008
|
+
const override = process.env.VECTORMIND_RG_PATH?.trim();
|
|
2009
|
+
if (override)
|
|
2010
|
+
pushUniqueCandidate(candidates, seen, path.resolve(override));
|
|
2011
|
+
if (process.platform === "win32") {
|
|
2012
|
+
pushUniqueCandidate(candidates, seen, "rg.exe");
|
|
2013
|
+
pushUniqueCandidate(candidates, seen, "rg");
|
|
2014
|
+
}
|
|
2015
|
+
else {
|
|
2016
|
+
pushUniqueCandidate(candidates, seen, "rg");
|
|
2017
|
+
}
|
|
2018
|
+
for (const rawDir of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
2019
|
+
const dir = rawDir.trim().replace(/^"+|"+$/g, "");
|
|
2020
|
+
if (!dir)
|
|
2021
|
+
continue;
|
|
2022
|
+
if (process.platform === "win32") {
|
|
2023
|
+
pushUniqueCandidate(candidates, seen, path.join(dir, "rg.exe"));
|
|
2024
|
+
pushUniqueCandidate(candidates, seen, path.join(dir, "rg"));
|
|
2025
|
+
}
|
|
2026
|
+
else {
|
|
2027
|
+
pushUniqueCandidate(candidates, seen, path.join(dir, "rg"));
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
if (process.platform === "win32") {
|
|
2031
|
+
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
2032
|
+
const programsDir = localAppData ? path.join(localAppData, "Programs") : "";
|
|
2033
|
+
if (programsDir && fs.existsSync(programsDir)) {
|
|
2034
|
+
for (const appDir of listChildDirsSafe(programsDir)) {
|
|
2035
|
+
pushUniqueCandidate(candidates, seen, path.join(appDir, "resources", "app", "node_modules", "@vscode", "ripgrep", "bin", "rg.exe"));
|
|
2036
|
+
pushUniqueCandidate(candidates, seen, path.join(appDir, "resources", "app", "extensions", "kiro.kiro-agent", "node_modules", "@vscode", "ripgrep", "bin", "rg.exe"));
|
|
2037
|
+
for (const childDir of listChildDirsSafe(appDir)) {
|
|
2038
|
+
pushUniqueCandidate(candidates, seen, path.join(childDir, "resources", "app", "node_modules", "@vscode", "ripgrep", "bin", "rg.exe"));
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
return candidates;
|
|
2044
|
+
}
|
|
2045
|
+
function resolveRipgrepCommand() {
|
|
2046
|
+
if (typeof cachedRipgrepCommand !== "undefined") {
|
|
2047
|
+
if (cachedRipgrepCommand)
|
|
2048
|
+
return { ok: true, command: cachedRipgrepCommand };
|
|
2049
|
+
return { ok: false, error: cachedRipgrepResolveError ?? "ripgrep unavailable", attempts: [] };
|
|
2050
|
+
}
|
|
2051
|
+
const env = buildRipgrepEnv();
|
|
2052
|
+
const attempts = [];
|
|
2053
|
+
for (const candidate of collectRipgrepCandidates()) {
|
|
2054
|
+
const probe = spawnSync(candidate, ["--version"], {
|
|
2055
|
+
cwd: projectRoot || undefined,
|
|
2056
|
+
env,
|
|
2057
|
+
encoding: "utf8",
|
|
2058
|
+
windowsHide: true,
|
|
2059
|
+
timeout: RIPGREP_RESOLVE_TIMEOUT_MS,
|
|
2060
|
+
maxBuffer: 256 * 1024,
|
|
2061
|
+
});
|
|
2062
|
+
if (probe.status === 0) {
|
|
2063
|
+
cachedRipgrepCommand = candidate;
|
|
2064
|
+
cachedRipgrepResolveError = null;
|
|
2065
|
+
return { ok: true, command: candidate };
|
|
2066
|
+
}
|
|
2067
|
+
attempts.push(`${candidate}: ${formatProcessFailure(probe)}`);
|
|
2068
|
+
}
|
|
2069
|
+
cachedRipgrepCommand = null;
|
|
2070
|
+
cachedRipgrepResolveError = attempts.slice(0, 8).join(" | ") || "ripgrep unavailable";
|
|
2071
|
+
return { ok: false, error: cachedRipgrepResolveError, attempts };
|
|
2072
|
+
}
|
|
2073
|
+
function appendBuiltInRipgrepExcludes(args) {
|
|
2074
|
+
for (const segment of IGNORED_PATH_SEGMENTS) {
|
|
2075
|
+
args.push("-g", `!${segment}/**`);
|
|
2076
|
+
args.push("-g", `!**/${segment}/**`);
|
|
2077
|
+
}
|
|
2078
|
+
for (const baseName of NOISE_FILE_BASENAMES) {
|
|
2079
|
+
args.push("-g", `!**/${baseName}`);
|
|
2080
|
+
}
|
|
2081
|
+
for (const suffix of NOISE_FILE_SUFFIXES) {
|
|
2082
|
+
args.push("-g", `!**/*${suffix}`);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
function runRipgrepSearch(opts) {
|
|
2086
|
+
const resolved = resolveRipgrepCommand();
|
|
2087
|
+
if (!resolved.ok) {
|
|
2088
|
+
return { ok: false, unavailable: true, error: resolved.error, attempts: resolved.attempts };
|
|
2089
|
+
}
|
|
2090
|
+
const args = ["--vimgrep", "--no-heading", "--color", "never", "-m", String(opts.maxResults)];
|
|
2091
|
+
args.push(opts.caseSensitive ? "-s" : "-i");
|
|
2092
|
+
if (opts.mode === "literal")
|
|
2093
|
+
args.push("-F");
|
|
2094
|
+
appendBuiltInRipgrepExcludes(args);
|
|
2095
|
+
args.push("--", opts.query, ".");
|
|
2096
|
+
const result = spawnSync(resolved.command, args, {
|
|
2097
|
+
cwd: projectRoot,
|
|
2098
|
+
env: buildRipgrepEnv(),
|
|
2099
|
+
encoding: "utf8",
|
|
2100
|
+
windowsHide: true,
|
|
2101
|
+
timeout: RIPGREP_SEARCH_TIMEOUT_MS,
|
|
2102
|
+
maxBuffer: RIPGREP_MAX_BUFFER_BYTES,
|
|
2103
|
+
});
|
|
2104
|
+
if (result.error) {
|
|
2105
|
+
return {
|
|
2106
|
+
ok: false,
|
|
2107
|
+
unavailable: false,
|
|
2108
|
+
error: formatProcessFailure(result),
|
|
2109
|
+
attempts: [],
|
|
2110
|
+
rg_command: resolved.command,
|
|
2111
|
+
exit_status: result.status,
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
const status = result.status ?? 0;
|
|
2115
|
+
if (status !== 0 && status !== 1) {
|
|
2116
|
+
return {
|
|
2117
|
+
ok: false,
|
|
2118
|
+
unavailable: false,
|
|
2119
|
+
error: formatProcessFailure(result),
|
|
2120
|
+
attempts: [],
|
|
2121
|
+
rg_command: resolved.command,
|
|
2122
|
+
exit_status: status,
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
const matches = [];
|
|
2126
|
+
let totalMatches = 0;
|
|
2127
|
+
let truncated = false;
|
|
2128
|
+
for (const rawLine of toProcessText(result.stdout).split(/\r?\n/)) {
|
|
2129
|
+
if (!rawLine)
|
|
2130
|
+
continue;
|
|
2131
|
+
const parsed = /^(.*?):(\d+):(\d+):(.*)$/.exec(rawLine);
|
|
2132
|
+
if (!parsed)
|
|
2133
|
+
continue;
|
|
2134
|
+
const filePath = path.posix
|
|
2135
|
+
.normalize(parsed[1].replace(/\\/g, "/"))
|
|
2136
|
+
.replace(/^\.\/+/, "");
|
|
2137
|
+
const lineNumber = Number.parseInt(parsed[2] ?? "0", 10);
|
|
2138
|
+
const colNumber = Number.parseInt(parsed[3] ?? "0", 10);
|
|
2139
|
+
const lineText = (parsed[4] ?? "").replace(/\r$/, "");
|
|
2140
|
+
if (!filePath || !Number.isFinite(lineNumber) || !Number.isFinite(colNumber))
|
|
2141
|
+
continue;
|
|
2142
|
+
if (shouldIgnoreDbFilePath(filePath))
|
|
2143
|
+
continue;
|
|
2144
|
+
if (shouldIgnoreContentFile(filePath))
|
|
2145
|
+
continue;
|
|
2146
|
+
if (!passesPathFilters(filePath, opts.includePaths, opts.excludePaths))
|
|
2147
|
+
continue;
|
|
2148
|
+
totalMatches += 1;
|
|
2149
|
+
if (matches.length >= opts.maxResults) {
|
|
2150
|
+
truncated = true;
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
matches.push({
|
|
2154
|
+
file_path: filePath,
|
|
2155
|
+
kind: "file_match",
|
|
2156
|
+
line: lineNumber,
|
|
2157
|
+
col: colNumber,
|
|
2158
|
+
preview: buildGrepPreviewSnippet(lineText, colNumber),
|
|
2159
|
+
match: extractGrepMatchText({
|
|
2160
|
+
lineText,
|
|
2161
|
+
query: opts.query,
|
|
2162
|
+
mode: opts.mode,
|
|
2163
|
+
caseSensitive: opts.caseSensitive,
|
|
2164
|
+
col: colNumber,
|
|
2165
|
+
}),
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
return {
|
|
2169
|
+
ok: true,
|
|
2170
|
+
backend: "ripgrep",
|
|
2171
|
+
rg_command: resolved.command,
|
|
2172
|
+
matches,
|
|
2173
|
+
truncated,
|
|
2174
|
+
total_matches: totalMatches,
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
function runIndexedGrepSearch(opts) {
|
|
2178
|
+
const hint = (() => {
|
|
2179
|
+
if (opts.mode === "literal")
|
|
2180
|
+
return opts.query;
|
|
2181
|
+
const explicit = opts.literalHint.trim();
|
|
2182
|
+
if (explicit)
|
|
2183
|
+
return explicit;
|
|
2184
|
+
return extractLongestLiteralFromRegex(opts.query);
|
|
2185
|
+
})();
|
|
2186
|
+
if (opts.mode === "regex" && hint.trim().length < 3) {
|
|
2187
|
+
throw new Error("Regex has no sufficiently long literal anchor for indexed narrowing. Provide literal_hint (>= 3 chars) or narrow with include_paths.");
|
|
2188
|
+
}
|
|
2189
|
+
let re;
|
|
2190
|
+
try {
|
|
2191
|
+
re = compileGrepRegex({
|
|
2192
|
+
query: opts.query,
|
|
2193
|
+
mode: opts.mode,
|
|
2194
|
+
caseSensitive: opts.caseSensitive,
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
catch (err) {
|
|
2198
|
+
throw new Error(`Invalid pattern: ${String(err)}`);
|
|
2199
|
+
}
|
|
2200
|
+
const maxCandidates = opts.maxCandidates ?? Math.min(50_000, Math.max(1000, opts.maxResults * 200));
|
|
2201
|
+
const candidates = (() => {
|
|
2202
|
+
if (ftsAvailable) {
|
|
2203
|
+
const matchQuery = buildFtsMatchQuery(hint);
|
|
2204
|
+
const placeholders = opts.kinds.map(() => "?").join(", ");
|
|
2205
|
+
const stmt = db.prepare(`
|
|
2206
|
+
SELECT
|
|
2207
|
+
m.id as id,
|
|
2208
|
+
m.kind as kind,
|
|
2209
|
+
m.content as content,
|
|
2210
|
+
m.file_path as file_path,
|
|
2211
|
+
m.start_line as start_line,
|
|
2212
|
+
m.end_line as end_line
|
|
2213
|
+
FROM ${FTS_TABLE_NAME}
|
|
2214
|
+
JOIN memory_items m ON m.id = ${FTS_TABLE_NAME}.rowid
|
|
2215
|
+
WHERE ${FTS_TABLE_NAME} MATCH ?
|
|
2216
|
+
AND m.kind IN (${placeholders})
|
|
2217
|
+
ORDER BY m.file_path ASC, m.start_line ASC, m.id ASC
|
|
2218
|
+
LIMIT ?
|
|
2219
|
+
`);
|
|
2220
|
+
return stmt.all(matchQuery, ...opts.kinds, maxCandidates);
|
|
2221
|
+
}
|
|
2222
|
+
const needle = opts.mode === "literal" ? opts.query : hint;
|
|
2223
|
+
const escaped = escapeLike(needle);
|
|
2224
|
+
const like = `%${escaped}%`;
|
|
2225
|
+
const placeholders = opts.kinds.map(() => "?").join(", ");
|
|
2226
|
+
const stmt = db.prepare(`
|
|
2227
|
+
SELECT
|
|
2228
|
+
id,
|
|
2229
|
+
kind,
|
|
2230
|
+
content,
|
|
2231
|
+
file_path,
|
|
2232
|
+
start_line,
|
|
2233
|
+
end_line
|
|
2234
|
+
FROM memory_items
|
|
2235
|
+
WHERE content LIKE ? ESCAPE '\\'
|
|
2236
|
+
AND kind IN (${placeholders})
|
|
2237
|
+
ORDER BY file_path ASC, start_line ASC, id ASC
|
|
2238
|
+
LIMIT ?
|
|
2239
|
+
`);
|
|
2240
|
+
return stmt.all(like, ...opts.kinds, maxCandidates);
|
|
2241
|
+
})();
|
|
2242
|
+
const matches = [];
|
|
2243
|
+
let candidatesScanned = 0;
|
|
2244
|
+
let truncated = false;
|
|
2245
|
+
for (const c of candidates) {
|
|
2246
|
+
candidatesScanned += 1;
|
|
2247
|
+
if (!c.file_path || c.start_line == null)
|
|
2248
|
+
continue;
|
|
2249
|
+
if (shouldIgnoreDbFilePath(c.file_path))
|
|
2250
|
+
continue;
|
|
2251
|
+
if (!passesPathFilters(c.file_path, opts.includePaths, opts.excludePaths))
|
|
2252
|
+
continue;
|
|
2253
|
+
const content = c.content ?? "";
|
|
2254
|
+
const lineStarts = buildLineStarts(content);
|
|
2255
|
+
re.lastIndex = 0;
|
|
2256
|
+
let m;
|
|
2257
|
+
while ((m = re.exec(content)) !== null) {
|
|
2258
|
+
const idx = m.index ?? 0;
|
|
2259
|
+
const matched = m[0] ?? "";
|
|
2260
|
+
if (!matched) {
|
|
2261
|
+
if (re.lastIndex >= content.length)
|
|
2262
|
+
break;
|
|
2263
|
+
re.lastIndex += 1;
|
|
2264
|
+
continue;
|
|
2265
|
+
}
|
|
2266
|
+
const lineIdx = lineIndexForOffset(lineStarts, idx);
|
|
2267
|
+
const lineStart = lineStarts[lineIdx] ?? 0;
|
|
2268
|
+
const lineEnd = lineIdx + 1 < lineStarts.length
|
|
2269
|
+
? (lineStarts[lineIdx + 1] ?? content.length) - 1
|
|
2270
|
+
: content.length;
|
|
2271
|
+
const previewRaw = content.slice(lineStart, Math.max(lineStart, lineEnd));
|
|
2272
|
+
matches.push({
|
|
2273
|
+
file_path: c.file_path,
|
|
2274
|
+
kind: c.kind,
|
|
2275
|
+
line: c.start_line + lineIdx,
|
|
2276
|
+
col: idx - lineStart + 1,
|
|
2277
|
+
preview: trimGrepText(previewRaw, 500),
|
|
2278
|
+
match: trimGrepText(matched, 200),
|
|
2279
|
+
});
|
|
2280
|
+
if (matches.length >= opts.maxResults) {
|
|
2281
|
+
truncated = true;
|
|
2282
|
+
break;
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
if (truncated)
|
|
2286
|
+
break;
|
|
2287
|
+
}
|
|
2288
|
+
return {
|
|
2289
|
+
backend: "indexed_fallback",
|
|
2290
|
+
hint,
|
|
2291
|
+
kinds: opts.kinds,
|
|
2292
|
+
include_paths: opts.includePaths ?? [],
|
|
2293
|
+
exclude_paths: opts.excludePaths ?? [],
|
|
2294
|
+
candidates: { total: candidates.length, scanned: candidatesScanned },
|
|
2295
|
+
matches,
|
|
2296
|
+
truncated,
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
1925
2299
|
function resolveProjectPathUnderRoot(inputPath, opts = {}) {
|
|
1926
2300
|
const normalizedInput = inputPath.trim() || ".";
|
|
1927
2301
|
const abs = path.isAbsolute(normalizedInput) ? normalizedInput : path.join(projectRoot, normalizedInput);
|
|
@@ -2150,15 +2524,19 @@ function buildServerInstructions() {
|
|
|
2150
2524
|
"Built-in architecture and code-organization policy:",
|
|
2151
2525
|
BUILTIN_ARCHITECTURE_AND_CODE_ORGANIZATION_INSTRUCTIONS,
|
|
2152
2526
|
"",
|
|
2527
|
+
"Built-in low-overhead execution and heavy-thread policy:",
|
|
2528
|
+
BUILTIN_LOW_OVERHEAD_WORKFLOW_INSTRUCTIONS,
|
|
2529
|
+
"",
|
|
2153
2530
|
"Required workflow:",
|
|
2154
|
-
"- On every new conversation/session: call bootstrap_context({ query: <current goal> }) first (or at least get_brain_dump()) to restore context and retrieve relevant matches from the local memory store (vector if enabled; otherwise FTS/LIKE).",
|
|
2531
|
+
"- On every new conversation/session for analysis/design/development work: call bootstrap_context({ query: <current goal> }) first (or at least get_brain_dump()) to restore context and retrieve relevant matches from the local memory store (vector if enabled; otherwise FTS/LIKE).",
|
|
2155
2532
|
" - Output is compact by default. Use include_content=true only when you truly need full text (it increases tokens).",
|
|
2156
2533
|
" - Tune output size with: requirements_limit/changes_limit/notes_limit, preview_chars, pending_limit/pending_offset.",
|
|
2157
2534
|
" - Prefer read_memory_item(id, offset, limit) to fetch full text on demand instead of returning large content in other tool outputs.",
|
|
2535
|
+
"- For pure execution-first tasks with explicit targets (for example compile/build/run/launch/package/publish/test rerun), you may skip retrieval and go straight to the minimum necessary shell or host tools unless code/context lookup is actually needed to unblock execution.",
|
|
2158
2536
|
"- To read local Codex skill/prompt/rule files (for example SKILL.md under CODEX_HOME or AGENTS_HOME), prefer read_codex_text_file({ path }) instead of assuming a filesystem MCP resource server exists.",
|
|
2159
2537
|
"- For project file/directory browsing, prefer list_project_files({ path, recursive?, max_depth? }) over shelling out to Get-ChildItem/ls. It respects ignore rules and keeps output bounded.",
|
|
2160
2538
|
"- For small/medium raw file reads, prefer read_file_text({ path, offset?, max_chars? }) over Get-Content -Raw. Use read_file_lines(...) when you need deterministic line ranges or the file may be large.",
|
|
2161
|
-
"- For
|
|
2539
|
+
"- For raw repo text search with exact file+line+col matches, prefer grep({ query: <pattern> }). It uses ripgrep against real project files when available, applies built-in noise filters, and only falls back to indexed search if ripgrep is unavailable.",
|
|
2162
2540
|
"- To read a bounded segment of a file, prefer read_file_lines({ path: <file>, from_line/to_line or total_count }) over unbounded file reads.",
|
|
2163
2541
|
"- BEFORE editing code: call start_requirement(title, background) to set the active requirement.",
|
|
2164
2542
|
"- AFTER editing + saving: call get_pending_changes() to see unsynced files, then call sync_change_intent(intent, files). (You can omit files to auto-link all pending changes.)",
|
|
@@ -2167,6 +2545,7 @@ function buildServerInstructions() {
|
|
|
2167
2545
|
"- When you need full text for a specific note/summary/match: call read_memory_item(id, offset, limit) and page through it.",
|
|
2168
2546
|
"- When asked to locate code (class/function/type): call query_codebase(query) instead of guessing.",
|
|
2169
2547
|
"- When you need to recall relevant context from history/code/docs: call semantic_search(query, ...) instead of guessing.",
|
|
2548
|
+
"- If the current thread is already heavy or the user reports it has become slow, switch to a lighter workflow: avoid redundant retrieval, keep outputs compact, and recommend continuing substantial new analysis in a fresh thread after a short handoff summary.",
|
|
2170
2549
|
"",
|
|
2171
2550
|
"If tool output conflicts with assumptions, trust the tool output.",
|
|
2172
2551
|
].join("\n");
|
|
@@ -2732,7 +3111,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
2732
3111
|
},
|
|
2733
3112
|
{
|
|
2734
3113
|
name: "grep",
|
|
2735
|
-
description: "
|
|
3114
|
+
description: "Repo text search with precise file/line/col matches, powered by ripgrep against real project files plus built-in noise filters. Falls back to indexed search only when ripgrep is unavailable.",
|
|
2736
3115
|
inputSchema: toJsonSchemaCompat(GrepArgsSchema),
|
|
2737
3116
|
},
|
|
2738
3117
|
{
|
|
@@ -3434,15 +3813,53 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3434
3813
|
const includePaths = args.include_paths?.length ? args.include_paths : null;
|
|
3435
3814
|
const excludePaths = args.exclude_paths?.length ? args.exclude_paths : null;
|
|
3436
3815
|
const maxResults = args.max_results;
|
|
3437
|
-
const
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3816
|
+
const caseSensitive = args.case_sensitive ?? (smartCase ? hasUppercaseAscii(q) : true);
|
|
3817
|
+
const ripgrepResult = runRipgrepSearch({
|
|
3818
|
+
query: q,
|
|
3819
|
+
mode,
|
|
3820
|
+
smartCase,
|
|
3821
|
+
caseSensitive,
|
|
3822
|
+
includePaths,
|
|
3823
|
+
excludePaths,
|
|
3824
|
+
maxResults,
|
|
3825
|
+
});
|
|
3826
|
+
if (ripgrepResult.ok) {
|
|
3827
|
+
logActivity("grep", {
|
|
3828
|
+
backend: ripgrepResult.backend,
|
|
3829
|
+
rg_command: ripgrepResult.rg_command,
|
|
3830
|
+
query: q,
|
|
3831
|
+
mode,
|
|
3832
|
+
case_sensitive: caseSensitive,
|
|
3833
|
+
smart_case: smartCase,
|
|
3834
|
+
include_paths: includePaths ?? [],
|
|
3835
|
+
exclude_paths: excludePaths ?? [],
|
|
3836
|
+
matches: ripgrepResult.matches.length,
|
|
3837
|
+
total_matches: ripgrepResult.total_matches,
|
|
3838
|
+
truncated: ripgrepResult.truncated,
|
|
3839
|
+
});
|
|
3840
|
+
return {
|
|
3841
|
+
content: [
|
|
3842
|
+
{
|
|
3843
|
+
type: "text",
|
|
3844
|
+
text: toolJson({
|
|
3845
|
+
ok: true,
|
|
3846
|
+
backend: ripgrepResult.backend,
|
|
3847
|
+
rg_command: ripgrepResult.rg_command,
|
|
3848
|
+
query: q,
|
|
3849
|
+
mode,
|
|
3850
|
+
case_sensitive: caseSensitive,
|
|
3851
|
+
smart_case: smartCase,
|
|
3852
|
+
include_paths: includePaths ?? [],
|
|
3853
|
+
exclude_paths: excludePaths ?? [],
|
|
3854
|
+
matches: ripgrepResult.matches,
|
|
3855
|
+
total_matches: ripgrepResult.total_matches,
|
|
3856
|
+
truncated: ripgrepResult.truncated,
|
|
3857
|
+
}),
|
|
3858
|
+
},
|
|
3859
|
+
],
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
if (!ripgrepResult.unavailable) {
|
|
3446
3863
|
return {
|
|
3447
3864
|
isError: true,
|
|
3448
3865
|
content: [
|
|
@@ -3450,19 +3867,31 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3450
3867
|
type: "text",
|
|
3451
3868
|
text: toolJson({
|
|
3452
3869
|
ok: false,
|
|
3453
|
-
|
|
3870
|
+
backend: "ripgrep",
|
|
3871
|
+
error: ripgrepResult.error,
|
|
3872
|
+
rg_command: ripgrepResult.rg_command,
|
|
3873
|
+
exit_status: ripgrepResult.exit_status,
|
|
3454
3874
|
query: q,
|
|
3455
3875
|
mode,
|
|
3456
|
-
literal_hint: args.literal_hint,
|
|
3457
3876
|
}),
|
|
3458
3877
|
},
|
|
3459
3878
|
],
|
|
3460
3879
|
};
|
|
3461
3880
|
}
|
|
3462
|
-
|
|
3463
|
-
let re;
|
|
3881
|
+
let indexedResult;
|
|
3464
3882
|
try {
|
|
3465
|
-
|
|
3883
|
+
indexedResult = runIndexedGrepSearch({
|
|
3884
|
+
query: q,
|
|
3885
|
+
mode,
|
|
3886
|
+
smartCase,
|
|
3887
|
+
caseSensitive,
|
|
3888
|
+
literalHint: args.literal_hint,
|
|
3889
|
+
kinds,
|
|
3890
|
+
includePaths,
|
|
3891
|
+
excludePaths,
|
|
3892
|
+
maxResults,
|
|
3893
|
+
maxCandidates: args.max_candidates,
|
|
3894
|
+
});
|
|
3466
3895
|
}
|
|
3467
3896
|
catch (err) {
|
|
3468
3897
|
return {
|
|
@@ -3470,114 +3899,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3470
3899
|
content: [
|
|
3471
3900
|
{
|
|
3472
3901
|
type: "text",
|
|
3473
|
-
text: toolJson({
|
|
3902
|
+
text: toolJson({
|
|
3903
|
+
ok: false,
|
|
3904
|
+
backend: "indexed_fallback",
|
|
3905
|
+
fallback_reason: "ripgrep_unavailable",
|
|
3906
|
+
ripgrep_error: ripgrepResult.error,
|
|
3907
|
+
ripgrep_attempts: ripgrepResult.attempts,
|
|
3908
|
+
error: String(err),
|
|
3909
|
+
query: q,
|
|
3910
|
+
mode,
|
|
3911
|
+
literal_hint: args.literal_hint,
|
|
3912
|
+
}),
|
|
3474
3913
|
},
|
|
3475
3914
|
],
|
|
3476
3915
|
};
|
|
3477
3916
|
}
|
|
3478
|
-
const maxCandidates = args.max_candidates ?? Math.min(50_000, Math.max(1000, maxResults * 200));
|
|
3479
|
-
const candidates = (() => {
|
|
3480
|
-
if (ftsAvailable) {
|
|
3481
|
-
const matchQuery = buildFtsMatchQuery(hint);
|
|
3482
|
-
const placeholders = kinds.map(() => "?").join(", ");
|
|
3483
|
-
const stmt = db.prepare(`
|
|
3484
|
-
SELECT
|
|
3485
|
-
m.id as id,
|
|
3486
|
-
m.kind as kind,
|
|
3487
|
-
m.content as content,
|
|
3488
|
-
m.file_path as file_path,
|
|
3489
|
-
m.start_line as start_line,
|
|
3490
|
-
m.end_line as end_line
|
|
3491
|
-
FROM ${FTS_TABLE_NAME}
|
|
3492
|
-
JOIN memory_items m ON m.id = ${FTS_TABLE_NAME}.rowid
|
|
3493
|
-
WHERE ${FTS_TABLE_NAME} MATCH ?
|
|
3494
|
-
AND m.kind IN (${placeholders})
|
|
3495
|
-
ORDER BY m.file_path ASC, m.start_line ASC, m.id ASC
|
|
3496
|
-
LIMIT ?
|
|
3497
|
-
`);
|
|
3498
|
-
return stmt.all(matchQuery, ...kinds, maxCandidates);
|
|
3499
|
-
}
|
|
3500
|
-
const needle = mode === "literal" ? q : hint;
|
|
3501
|
-
const escaped = escapeLike(needle);
|
|
3502
|
-
const like = `%${escaped}%`;
|
|
3503
|
-
const placeholders = kinds.map(() => "?").join(", ");
|
|
3504
|
-
const stmt = db.prepare(`
|
|
3505
|
-
SELECT
|
|
3506
|
-
id,
|
|
3507
|
-
kind,
|
|
3508
|
-
content,
|
|
3509
|
-
file_path,
|
|
3510
|
-
start_line,
|
|
3511
|
-
end_line
|
|
3512
|
-
FROM memory_items
|
|
3513
|
-
WHERE content LIKE ? ESCAPE '\\'
|
|
3514
|
-
AND kind IN (${placeholders})
|
|
3515
|
-
ORDER BY file_path ASC, start_line ASC, id ASC
|
|
3516
|
-
LIMIT ?
|
|
3517
|
-
`);
|
|
3518
|
-
return stmt.all(like, ...kinds, maxCandidates);
|
|
3519
|
-
})();
|
|
3520
|
-
const matches = [];
|
|
3521
|
-
let candidatesScanned = 0;
|
|
3522
|
-
let truncated = false;
|
|
3523
|
-
for (const c of candidates) {
|
|
3524
|
-
candidatesScanned += 1;
|
|
3525
|
-
if (!c.file_path || c.start_line == null)
|
|
3526
|
-
continue;
|
|
3527
|
-
if (shouldIgnoreDbFilePath(c.file_path))
|
|
3528
|
-
continue;
|
|
3529
|
-
if (!passesPathFilters(c.file_path, includePaths, excludePaths))
|
|
3530
|
-
continue;
|
|
3531
|
-
const content = c.content ?? "";
|
|
3532
|
-
const lineStarts = buildLineStarts(content);
|
|
3533
|
-
re.lastIndex = 0;
|
|
3534
|
-
let m;
|
|
3535
|
-
while ((m = re.exec(content)) !== null) {
|
|
3536
|
-
const idx = m.index ?? 0;
|
|
3537
|
-
const matched = m[0] ?? "";
|
|
3538
|
-
if (!matched) {
|
|
3539
|
-
if (re.lastIndex >= content.length)
|
|
3540
|
-
break;
|
|
3541
|
-
re.lastIndex += 1;
|
|
3542
|
-
continue;
|
|
3543
|
-
}
|
|
3544
|
-
const lineIdx = lineIndexForOffset(lineStarts, idx);
|
|
3545
|
-
const lineStart = lineStarts[lineIdx] ?? 0;
|
|
3546
|
-
const lineEnd = lineIdx + 1 < lineStarts.length
|
|
3547
|
-
? (lineStarts[lineIdx + 1] ?? content.length) - 1
|
|
3548
|
-
: content.length;
|
|
3549
|
-
const previewRaw = content.slice(lineStart, Math.max(lineStart, lineEnd));
|
|
3550
|
-
const preview = previewRaw.length > 500 ? `${previewRaw.slice(0, 500)}…` : previewRaw;
|
|
3551
|
-
const matchText = matched.length > 200 ? `${matched.slice(0, 200)}…` : matched;
|
|
3552
|
-
matches.push({
|
|
3553
|
-
file_path: c.file_path,
|
|
3554
|
-
kind: c.kind,
|
|
3555
|
-
line: c.start_line + lineIdx,
|
|
3556
|
-
col: idx - lineStart + 1,
|
|
3557
|
-
preview,
|
|
3558
|
-
match: matchText,
|
|
3559
|
-
});
|
|
3560
|
-
if (matches.length >= maxResults) {
|
|
3561
|
-
truncated = true;
|
|
3562
|
-
break;
|
|
3563
|
-
}
|
|
3564
|
-
}
|
|
3565
|
-
if (truncated)
|
|
3566
|
-
break;
|
|
3567
|
-
}
|
|
3568
3917
|
logActivity("grep", {
|
|
3918
|
+
backend: indexedResult.backend,
|
|
3919
|
+
fallback_reason: "ripgrep_unavailable",
|
|
3920
|
+
ripgrep_error: ripgrepResult.error,
|
|
3569
3921
|
query: q,
|
|
3570
3922
|
mode,
|
|
3571
3923
|
case_sensitive: caseSensitive,
|
|
3572
3924
|
smart_case: smartCase,
|
|
3573
|
-
hint,
|
|
3925
|
+
hint: indexedResult.hint,
|
|
3574
3926
|
kinds,
|
|
3575
3927
|
include_paths: includePaths ?? [],
|
|
3576
3928
|
exclude_paths: excludePaths ?? [],
|
|
3577
|
-
candidates: candidates.
|
|
3578
|
-
candidates_scanned:
|
|
3579
|
-
matches: matches.length,
|
|
3580
|
-
truncated,
|
|
3929
|
+
candidates: indexedResult.candidates.total,
|
|
3930
|
+
candidates_scanned: indexedResult.candidates.scanned,
|
|
3931
|
+
matches: indexedResult.matches.length,
|
|
3932
|
+
truncated: indexedResult.truncated,
|
|
3581
3933
|
});
|
|
3582
3934
|
return {
|
|
3583
3935
|
content: [
|
|
@@ -3585,17 +3937,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3585
3937
|
type: "text",
|
|
3586
3938
|
text: toolJson({
|
|
3587
3939
|
ok: true,
|
|
3940
|
+
backend: indexedResult.backend,
|
|
3941
|
+
fallback_reason: "ripgrep_unavailable",
|
|
3942
|
+
ripgrep_error: ripgrepResult.error,
|
|
3943
|
+
ripgrep_attempts: ripgrepResult.attempts,
|
|
3588
3944
|
query: q,
|
|
3589
3945
|
mode,
|
|
3590
3946
|
case_sensitive: caseSensitive,
|
|
3591
3947
|
smart_case: smartCase,
|
|
3592
|
-
hint,
|
|
3948
|
+
hint: indexedResult.hint,
|
|
3593
3949
|
kinds,
|
|
3594
3950
|
include_paths: includePaths ?? [],
|
|
3595
3951
|
exclude_paths: excludePaths ?? [],
|
|
3596
|
-
candidates:
|
|
3597
|
-
matches,
|
|
3598
|
-
truncated,
|
|
3952
|
+
candidates: indexedResult.candidates,
|
|
3953
|
+
matches: indexedResult.matches,
|
|
3954
|
+
truncated: indexedResult.truncated,
|
|
3599
3955
|
}),
|
|
3600
3956
|
},
|
|
3601
3957
|
],
|