grepmax 0.7.7 → 0.7.9
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/commands/mcp.js
CHANGED
|
@@ -120,6 +120,10 @@ const TOOLS = [
|
|
|
120
120
|
type: "string",
|
|
121
121
|
description: "Filter by chunk role: 'ORCHESTRATION' (logic/flow), 'DEFINITION' (types/classes), or 'IMPLEMENTATION'.",
|
|
122
122
|
},
|
|
123
|
+
context_lines: {
|
|
124
|
+
type: "number",
|
|
125
|
+
description: "Include N lines before and after the chunk (like grep -C). Only with detail 'code' or 'full'. Max 20.",
|
|
126
|
+
},
|
|
123
127
|
},
|
|
124
128
|
required: ["query"],
|
|
125
129
|
},
|
|
@@ -166,6 +170,18 @@ const TOOLS = [
|
|
|
166
170
|
type: "string",
|
|
167
171
|
description: "Filter by role: 'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'.",
|
|
168
172
|
},
|
|
173
|
+
projects: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "Comma-separated project names to include (e.g. 'platform,osgrep'). Use index_status to see names.",
|
|
176
|
+
},
|
|
177
|
+
exclude_projects: {
|
|
178
|
+
type: "string",
|
|
179
|
+
description: "Comma-separated project names to exclude (e.g. 'capstone,power').",
|
|
180
|
+
},
|
|
181
|
+
context_lines: {
|
|
182
|
+
type: "number",
|
|
183
|
+
description: "Include N lines before/after chunk. Only with detail 'code' or 'full'. Max 20.",
|
|
184
|
+
},
|
|
169
185
|
},
|
|
170
186
|
required: ["query"],
|
|
171
187
|
},
|
|
@@ -472,6 +488,32 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
472
488
|
if (typeof args.role === "string" && args.role) {
|
|
473
489
|
filters.role = args.role;
|
|
474
490
|
}
|
|
491
|
+
if (searchAll) {
|
|
492
|
+
const allProjects = (0, project_registry_1.listProjects)();
|
|
493
|
+
if (typeof args.projects === "string" && args.projects) {
|
|
494
|
+
const names = args.projects
|
|
495
|
+
.split(",")
|
|
496
|
+
.map((s) => s.trim());
|
|
497
|
+
const roots = names
|
|
498
|
+
.map((n) => { var _a; return (_a = allProjects.find((p) => p.name === n)) === null || _a === void 0 ? void 0 : _a.root; })
|
|
499
|
+
.filter(Boolean);
|
|
500
|
+
if (roots.length > 0) {
|
|
501
|
+
filters.project_roots = roots.join(",");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (typeof args.exclude_projects === "string" &&
|
|
505
|
+
args.exclude_projects) {
|
|
506
|
+
const names = args.exclude_projects
|
|
507
|
+
.split(",")
|
|
508
|
+
.map((s) => s.trim());
|
|
509
|
+
const roots = names
|
|
510
|
+
.map((n) => { var _a; return (_a = allProjects.find((p) => p.name === n)) === null || _a === void 0 ? void 0 : _a.root; })
|
|
511
|
+
.filter(Boolean);
|
|
512
|
+
if (roots.length > 0) {
|
|
513
|
+
filters.exclude_project_roots = roots.join(",");
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
475
517
|
const result = yield searcher.search(query, limit, { rerank: true }, Object.keys(filters).length > 0 ? filters : undefined, pathPrefix);
|
|
476
518
|
if (!result.data || result.data.length === 0) {
|
|
477
519
|
return ok("No matches found.");
|
|
@@ -507,19 +549,42 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
507
549
|
? ` ${parentStr}${callsStr}`
|
|
508
550
|
: "";
|
|
509
551
|
let snippet = "";
|
|
552
|
+
const contextN = typeof args.context_lines === "number"
|
|
553
|
+
? Math.min(Math.max(args.context_lines, 0), 20)
|
|
554
|
+
: 0;
|
|
510
555
|
if (detail === "code" || detail === "full") {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
556
|
+
if (contextN > 0 && absPath) {
|
|
557
|
+
// Read surrounding context from file
|
|
558
|
+
try {
|
|
559
|
+
const fileContent = fs.readFileSync(absPath, "utf-8");
|
|
560
|
+
const fileLines = fileContent.split("\n");
|
|
561
|
+
const ctxStart = Math.max(0, startLine - contextN);
|
|
562
|
+
const ctxEnd = Math.min(fileLines.length, endLine + 1 + contextN);
|
|
563
|
+
snippet =
|
|
564
|
+
"\n" +
|
|
565
|
+
fileLines
|
|
566
|
+
.slice(ctxStart, ctxEnd)
|
|
567
|
+
.map((l, i) => `${ctxStart + i + 1}│${l}`)
|
|
568
|
+
.join("\n");
|
|
569
|
+
}
|
|
570
|
+
catch (_o) {
|
|
571
|
+
// Fall through to chunk content
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (!snippet) {
|
|
575
|
+
const raw = typeof r.content === "string"
|
|
576
|
+
? r.content
|
|
577
|
+
: typeof r.text === "string"
|
|
578
|
+
? r.text
|
|
579
|
+
: "";
|
|
580
|
+
const allLines = raw.split("\n");
|
|
581
|
+
const linesToShow = detail === "full" ? allLines : allLines.slice(0, 4);
|
|
582
|
+
snippet =
|
|
583
|
+
"\n" +
|
|
584
|
+
linesToShow
|
|
585
|
+
.map((l, i) => `${startLine + i + 1}│${l}`)
|
|
586
|
+
.join("\n");
|
|
587
|
+
}
|
|
523
588
|
}
|
|
524
589
|
const text = line1 +
|
|
525
590
|
(summaryStr ? `\n${summaryStr}` : "") +
|
|
@@ -556,6 +621,31 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
556
621
|
}
|
|
557
622
|
});
|
|
558
623
|
}
|
|
624
|
+
function annotateSkeletonLines(skeleton, sourceContent) {
|
|
625
|
+
const sourceLines = sourceContent.split("\n");
|
|
626
|
+
const skelLines = skeleton.split("\n");
|
|
627
|
+
const used = new Set();
|
|
628
|
+
return skelLines
|
|
629
|
+
.map((skelLine) => {
|
|
630
|
+
const trimmed = skelLine.trim();
|
|
631
|
+
if (!trimmed ||
|
|
632
|
+
trimmed.startsWith("//") ||
|
|
633
|
+
trimmed.startsWith("*") ||
|
|
634
|
+
trimmed.startsWith("/*")) {
|
|
635
|
+
return skelLine;
|
|
636
|
+
}
|
|
637
|
+
// Match against source lines using first 40 chars
|
|
638
|
+
const matchStr = trimmed.slice(0, 40);
|
|
639
|
+
for (let i = 0; i < sourceLines.length; i++) {
|
|
640
|
+
if (!used.has(i) && sourceLines[i].includes(matchStr)) {
|
|
641
|
+
used.add(i);
|
|
642
|
+
return `${String(i + 1).padStart(4)}│${skelLine}`;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return skelLine;
|
|
646
|
+
})
|
|
647
|
+
.join("\n");
|
|
648
|
+
}
|
|
559
649
|
function handleCodeSkeleton(args) {
|
|
560
650
|
return __awaiter(this, void 0, void 0, function* () {
|
|
561
651
|
const target = String(args.target || "");
|
|
@@ -596,25 +686,35 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
596
686
|
parts.push(`// ${t} — file not found`);
|
|
597
687
|
continue;
|
|
598
688
|
}
|
|
689
|
+
// Read source for line annotations
|
|
690
|
+
let sourceContent = "";
|
|
691
|
+
try {
|
|
692
|
+
sourceContent = fs.readFileSync(absPath, "utf-8");
|
|
693
|
+
}
|
|
694
|
+
catch (_a) { }
|
|
599
695
|
// Try cached skeleton first
|
|
600
696
|
try {
|
|
601
697
|
const db = getVectorDb();
|
|
602
698
|
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
603
699
|
if (cached) {
|
|
604
700
|
const tokens = Math.ceil(cached.length / 4);
|
|
605
|
-
|
|
701
|
+
const annotated = sourceContent
|
|
702
|
+
? annotateSkeletonLines(cached, sourceContent)
|
|
703
|
+
: cached;
|
|
704
|
+
parts.push(`// ${t} (~${tokens} tokens)\n\n${annotated}`);
|
|
606
705
|
continue;
|
|
607
706
|
}
|
|
608
707
|
}
|
|
609
|
-
catch (
|
|
708
|
+
catch (_b) {
|
|
610
709
|
// Index may not exist yet — fall through to live generation
|
|
611
710
|
}
|
|
612
711
|
// Generate live
|
|
613
712
|
try {
|
|
614
|
-
const content = fs.readFileSync(absPath, "utf-8");
|
|
713
|
+
const content = sourceContent || fs.readFileSync(absPath, "utf-8");
|
|
615
714
|
const result = yield skel.skeletonizeFile(absPath, content);
|
|
616
715
|
if (result.success) {
|
|
617
|
-
|
|
716
|
+
const annotated = annotateSkeletonLines(result.skeleton, content);
|
|
717
|
+
parts.push(`// ${t} (~${result.tokenEstimate} tokens)\n\n${annotated}`);
|
|
618
718
|
}
|
|
619
719
|
else {
|
|
620
720
|
parts.push(`// ${t} — skeleton failed: ${result.error}`);
|
|
@@ -698,7 +798,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
698
798
|
const table = yield db.ensureTable();
|
|
699
799
|
let query = table
|
|
700
800
|
.query()
|
|
701
|
-
.select(["defined_symbols", "path", "start_line"])
|
|
801
|
+
.select(["defined_symbols", "path", "start_line", "role", "is_exported"])
|
|
702
802
|
.where("array_length(defined_symbols) > 0")
|
|
703
803
|
.limit(pattern ? 10000 : Math.max(limit * 50, 2000));
|
|
704
804
|
if (pathPrefix) {
|
|
@@ -714,6 +814,8 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
714
814
|
const defs = toStringArray(row.defined_symbols);
|
|
715
815
|
const rowPath = String(row.path || "");
|
|
716
816
|
const line = Number(row.start_line || 0);
|
|
817
|
+
const role = String(row.role || "");
|
|
818
|
+
const exported = Boolean(row.is_exported);
|
|
717
819
|
for (const sym of defs) {
|
|
718
820
|
if (pattern && !sym.toLowerCase().includes(pattern.toLowerCase())) {
|
|
719
821
|
continue;
|
|
@@ -728,6 +830,8 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
728
830
|
count: 1,
|
|
729
831
|
path: rowPath,
|
|
730
832
|
line: Math.max(1, line + 1),
|
|
833
|
+
role,
|
|
834
|
+
exported,
|
|
731
835
|
});
|
|
732
836
|
}
|
|
733
837
|
}
|
|
@@ -746,7 +850,9 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
746
850
|
const rel = e.path.startsWith(projectRoot)
|
|
747
851
|
? e.path.slice(projectRoot.length + 1)
|
|
748
852
|
: e.path;
|
|
749
|
-
|
|
853
|
+
const roleTag = e.role ? ` [${e.role.slice(0, 4)}]` : "";
|
|
854
|
+
const expTag = e.exported ? " exported" : "";
|
|
855
|
+
return `${e.symbol}${roleTag}${expTag}\t${rel}:${e.line}`;
|
|
750
856
|
});
|
|
751
857
|
return ok(lines.join("\n"));
|
|
752
858
|
}
|
|
@@ -301,6 +301,24 @@ class Searcher {
|
|
|
301
301
|
if (typeof roleFilter === "string" && roleFilter) {
|
|
302
302
|
whereClauseParts.push(`role = '${(0, filter_builder_1.escapeSqlString)(roleFilter)}'`);
|
|
303
303
|
}
|
|
304
|
+
// Handle project roots filter (from search_all projects param)
|
|
305
|
+
const projectRoots = _filters === null || _filters === void 0 ? void 0 : _filters.project_roots;
|
|
306
|
+
if (typeof projectRoots === "string" && projectRoots) {
|
|
307
|
+
const roots = projectRoots.split(",");
|
|
308
|
+
const clauses = roots.map((r) => {
|
|
309
|
+
const prefix = r.endsWith("/") ? r : `${r}/`;
|
|
310
|
+
return `path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`;
|
|
311
|
+
});
|
|
312
|
+
whereClauseParts.push(`(${clauses.join(" OR ")})`);
|
|
313
|
+
}
|
|
314
|
+
// Handle exclude project roots filter
|
|
315
|
+
const excludeRoots = _filters === null || _filters === void 0 ? void 0 : _filters.exclude_project_roots;
|
|
316
|
+
if (typeof excludeRoots === "string" && excludeRoots) {
|
|
317
|
+
for (const r of excludeRoots.split(",")) {
|
|
318
|
+
const prefix = r.endsWith("/") ? r : `${r}/`;
|
|
319
|
+
whereClauseParts.push(`path NOT LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
304
322
|
// Handle --def (definition) filter
|
|
305
323
|
const defFilter = _filters === null || _filters === void 0 ? void 0 : _filters.def;
|
|
306
324
|
if (typeof defFilter === "string" && defFilter) {
|
package/package.json
CHANGED
|
@@ -40,6 +40,7 @@ Parameters:
|
|
|
40
40
|
- `root` (optional): Absolute path to search a different indexed directory.
|
|
41
41
|
- `path` (optional): Restrict to path prefix (e.g. "src/auth/"). Relative to the search root.
|
|
42
42
|
- `detail` (optional): `"pointer"` (default), `"code"` (4-line snippets), or `"full"` (complete chunk with line numbers)
|
|
43
|
+
- `context_lines` (optional): Include N lines before/after the chunk (like grep -C). Only with detail "code" or "full". Max 20.
|
|
43
44
|
- `min_score` (optional): Filter by minimum relevance score (0-1)
|
|
44
45
|
- `max_per_file` (optional): Cap results per file for diversity
|
|
45
46
|
- `file` (optional): Filter to files matching this name (e.g. "syncer.ts"). Matches filename, not full path.
|
|
@@ -52,7 +53,11 @@ Parameters:
|
|
|
52
53
|
- `code` — comparing implementations, finding duplicates, checking syntax
|
|
53
54
|
|
|
54
55
|
### search_all
|
|
55
|
-
Search ALL indexed code across every directory. Same parameters as semantic_search (query, limit, detail, min_score, max_per_file) but without `root` or `path
|
|
56
|
+
Search ALL indexed code across every directory. Same parameters as semantic_search (query, limit, detail, min_score, max_per_file, file, exclude, language, role) but without `root` or `path`.
|
|
57
|
+
|
|
58
|
+
Additional parameters:
|
|
59
|
+
- `projects` (optional): Comma-separated project names to include (e.g. "platform,osgrep"). Use `index_status` to see names.
|
|
60
|
+
- `exclude_projects` (optional): Comma-separated project names to exclude (e.g. "capstone,power")
|
|
56
61
|
|
|
57
62
|
Use sparingly. Prefer `semantic_search` when you know which directory to search.
|
|
58
63
|
|
|
@@ -66,11 +71,13 @@ Call graph — who calls a symbol and what it calls. Callers and callees include
|
|
|
66
71
|
- `symbol` (required): Function/method/class name
|
|
67
72
|
|
|
68
73
|
### list_symbols
|
|
69
|
-
List indexed symbols with definition locations.
|
|
74
|
+
List indexed symbols with definition locations, role, and export status.
|
|
70
75
|
- `pattern` (optional): Filter by name (case-insensitive substring match)
|
|
71
76
|
- `limit` (optional): Max results (default 20, max 100)
|
|
72
77
|
- `path` (optional): Only symbols under this path prefix
|
|
73
78
|
|
|
79
|
+
Output: `symbolName [ORCH] exported src/path/file.ts:42`
|
|
80
|
+
|
|
74
81
|
### index_status
|
|
75
82
|
Check centralized index health — chunks, files, indexed directories, model info, watcher status.
|
|
76
83
|
|