grepmax 0.7.5 → 0.7.7
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
|
@@ -61,6 +61,7 @@ const searcher_1 = require("../lib/search/searcher");
|
|
|
61
61
|
const retriever_1 = require("../lib/skeleton/retriever");
|
|
62
62
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
63
63
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
64
|
+
const file_utils_1 = require("../lib/utils/file-utils");
|
|
64
65
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
65
66
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
66
67
|
const project_root_1 = require("../lib/utils/project-root");
|
|
@@ -93,7 +94,7 @@ const TOOLS = [
|
|
|
93
94
|
},
|
|
94
95
|
detail: {
|
|
95
96
|
type: "string",
|
|
96
|
-
description: "Output detail: 'pointer' (default, metadata only
|
|
97
|
+
description: "Output detail: 'pointer' (default, metadata only), 'code' (4-line snippets), or 'full' (complete chunk content with line numbers)",
|
|
97
98
|
},
|
|
98
99
|
min_score: {
|
|
99
100
|
type: "number",
|
|
@@ -111,6 +112,14 @@ const TOOLS = [
|
|
|
111
112
|
type: "string",
|
|
112
113
|
description: "Exclude files under this path prefix (e.g. 'tests/' or 'dist/').",
|
|
113
114
|
},
|
|
115
|
+
language: {
|
|
116
|
+
type: "string",
|
|
117
|
+
description: "Filter by file extension (e.g. 'ts', 'py', 'go'). Omit the dot.",
|
|
118
|
+
},
|
|
119
|
+
role: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Filter by chunk role: 'ORCHESTRATION' (logic/flow), 'DEFINITION' (types/classes), or 'IMPLEMENTATION'.",
|
|
122
|
+
},
|
|
114
123
|
},
|
|
115
124
|
required: ["query"],
|
|
116
125
|
},
|
|
@@ -131,7 +140,7 @@ const TOOLS = [
|
|
|
131
140
|
},
|
|
132
141
|
detail: {
|
|
133
142
|
type: "string",
|
|
134
|
-
description: "Output detail: 'pointer' (default) or '
|
|
143
|
+
description: "Output detail: 'pointer' (default), 'code' (snippets), or 'full' (complete content)",
|
|
135
144
|
},
|
|
136
145
|
min_score: {
|
|
137
146
|
type: "number",
|
|
@@ -149,19 +158,31 @@ const TOOLS = [
|
|
|
149
158
|
type: "string",
|
|
150
159
|
description: "Exclude files under this path prefix (e.g. 'tests/').",
|
|
151
160
|
},
|
|
161
|
+
language: {
|
|
162
|
+
type: "string",
|
|
163
|
+
description: "Filter by file extension (e.g. 'ts', 'py').",
|
|
164
|
+
},
|
|
165
|
+
role: {
|
|
166
|
+
type: "string",
|
|
167
|
+
description: "Filter by role: 'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'.",
|
|
168
|
+
},
|
|
152
169
|
},
|
|
153
170
|
required: ["query"],
|
|
154
171
|
},
|
|
155
172
|
},
|
|
156
173
|
{
|
|
157
174
|
name: "code_skeleton",
|
|
158
|
-
description: "Show the structure of
|
|
175
|
+
description: "Show the structure of source files — signatures with bodies collapsed (~4x fewer tokens). Accepts a file path, a directory path, or comma-separated file paths.",
|
|
159
176
|
inputSchema: {
|
|
160
177
|
type: "object",
|
|
161
178
|
properties: {
|
|
162
179
|
target: {
|
|
163
180
|
type: "string",
|
|
164
|
-
description: "File path
|
|
181
|
+
description: "File path, directory path (e.g. 'src/lib/search/'), or comma-separated files (e.g. 'src/a.ts,src/b.ts'). Relative to project root.",
|
|
182
|
+
},
|
|
183
|
+
limit: {
|
|
184
|
+
type: "number",
|
|
185
|
+
description: "Max files for directory mode (default 10, max 20). Ignored for single files.",
|
|
165
186
|
},
|
|
166
187
|
},
|
|
167
188
|
required: ["target"],
|
|
@@ -445,6 +466,12 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
445
466
|
if (typeof args.exclude === "string" && args.exclude) {
|
|
446
467
|
filters.exclude = args.exclude;
|
|
447
468
|
}
|
|
469
|
+
if (typeof args.language === "string" && args.language) {
|
|
470
|
+
filters.language = args.language;
|
|
471
|
+
}
|
|
472
|
+
if (typeof args.role === "string" && args.role) {
|
|
473
|
+
filters.role = args.role;
|
|
474
|
+
}
|
|
448
475
|
const result = yield searcher.search(query, limit, { rerank: true }, Object.keys(filters).length > 0 ? filters : undefined, pathPrefix);
|
|
449
476
|
if (!result.data || result.data.length === 0) {
|
|
450
477
|
return ok("No matches found.");
|
|
@@ -480,16 +507,17 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
480
507
|
? ` ${parentStr}${callsStr}`
|
|
481
508
|
: "";
|
|
482
509
|
let snippet = "";
|
|
483
|
-
if (detail === "code") {
|
|
510
|
+
if (detail === "code" || detail === "full") {
|
|
484
511
|
const raw = typeof r.content === "string"
|
|
485
512
|
? r.content
|
|
486
513
|
: typeof r.text === "string"
|
|
487
514
|
? r.text
|
|
488
515
|
: "";
|
|
489
|
-
const
|
|
516
|
+
const allLines = raw.split("\n");
|
|
517
|
+
const linesToShow = detail === "full" ? allLines : allLines.slice(0, 4);
|
|
490
518
|
snippet =
|
|
491
519
|
"\n" +
|
|
492
|
-
|
|
520
|
+
linesToShow
|
|
493
521
|
.map((l, i) => `${startLine + i + 1}│${l}`)
|
|
494
522
|
.join("\n");
|
|
495
523
|
}
|
|
@@ -533,36 +561,71 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
533
561
|
const target = String(args.target || "");
|
|
534
562
|
if (!target)
|
|
535
563
|
return err("Missing required parameter: target");
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
564
|
+
const fileLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 20);
|
|
565
|
+
// Determine targets: comma-separated, directory, or single file
|
|
566
|
+
let targets;
|
|
567
|
+
if (target.includes(",")) {
|
|
568
|
+
targets = target
|
|
569
|
+
.split(",")
|
|
570
|
+
.map((t) => t.trim())
|
|
571
|
+
.filter(Boolean);
|
|
539
572
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
573
|
+
else {
|
|
574
|
+
const absPath = path.resolve(projectRoot, target);
|
|
575
|
+
if (fs.existsSync(absPath) && fs.statSync(absPath).isDirectory()) {
|
|
576
|
+
const entries = fs.readdirSync(absPath, { withFileTypes: true });
|
|
577
|
+
targets = entries
|
|
578
|
+
.filter((e) => e.isFile() &&
|
|
579
|
+
(0, file_utils_1.isIndexableFile)(path.join(absPath, e.name)))
|
|
580
|
+
.map((e) => path.relative(projectRoot, path.join(absPath, e.name)))
|
|
581
|
+
.slice(0, fileLimit);
|
|
582
|
+
if (targets.length === 0) {
|
|
583
|
+
return err(`No indexable files found in ${target}`);
|
|
584
|
+
}
|
|
547
585
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
// Index may not exist yet — fall through to live generation
|
|
551
|
-
}
|
|
552
|
-
// Generate skeleton from file
|
|
553
|
-
try {
|
|
554
|
-
const content = fs.readFileSync(absPath, "utf-8");
|
|
555
|
-
const skel = yield getSkeletonizer();
|
|
556
|
-
const result = yield skel.skeletonizeFile(absPath, content);
|
|
557
|
-
if (!result.success && result.error) {
|
|
558
|
-
return err(`Skeleton generation failed: ${result.error}`);
|
|
586
|
+
else {
|
|
587
|
+
targets = [target];
|
|
559
588
|
}
|
|
560
|
-
return ok(`// ${target} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
|
|
561
589
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
590
|
+
// Generate skeletons for all targets
|
|
591
|
+
const parts = [];
|
|
592
|
+
const skel = yield getSkeletonizer();
|
|
593
|
+
for (const t of targets) {
|
|
594
|
+
const absPath = path.resolve(projectRoot, t);
|
|
595
|
+
if (!fs.existsSync(absPath)) {
|
|
596
|
+
parts.push(`// ${t} — file not found`);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
// Try cached skeleton first
|
|
600
|
+
try {
|
|
601
|
+
const db = getVectorDb();
|
|
602
|
+
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
603
|
+
if (cached) {
|
|
604
|
+
const tokens = Math.ceil(cached.length / 4);
|
|
605
|
+
parts.push(`// ${t} (~${tokens} tokens)\n\n${cached}`);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
catch (_a) {
|
|
610
|
+
// Index may not exist yet — fall through to live generation
|
|
611
|
+
}
|
|
612
|
+
// Generate live
|
|
613
|
+
try {
|
|
614
|
+
const content = fs.readFileSync(absPath, "utf-8");
|
|
615
|
+
const result = yield skel.skeletonizeFile(absPath, content);
|
|
616
|
+
if (result.success) {
|
|
617
|
+
parts.push(`// ${t} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
parts.push(`// ${t} — skeleton failed: ${result.error}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
catch (e) {
|
|
624
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
625
|
+
parts.push(`// ${t} — error: ${msg}`);
|
|
626
|
+
}
|
|
565
627
|
}
|
|
628
|
+
return ok(parts.join("\n\n---\n\n"));
|
|
566
629
|
});
|
|
567
630
|
}
|
|
568
631
|
function handleTraceCalls(args) {
|
|
@@ -290,6 +290,17 @@ class Searcher {
|
|
|
290
290
|
: excludeFilter;
|
|
291
291
|
whereClauseParts.push(`path NOT LIKE '${(0, filter_builder_1.escapeSqlString)(absExclude)}%'`);
|
|
292
292
|
}
|
|
293
|
+
// Handle language filter (by file extension)
|
|
294
|
+
const langFilter = _filters === null || _filters === void 0 ? void 0 : _filters.language;
|
|
295
|
+
if (typeof langFilter === "string" && langFilter) {
|
|
296
|
+
const ext = langFilter.startsWith(".") ? langFilter : `.${langFilter}`;
|
|
297
|
+
whereClauseParts.push(`path LIKE '%${(0, filter_builder_1.escapeSqlString)(ext)}'`);
|
|
298
|
+
}
|
|
299
|
+
// Handle role filter
|
|
300
|
+
const roleFilter = _filters === null || _filters === void 0 ? void 0 : _filters.role;
|
|
301
|
+
if (typeof roleFilter === "string" && roleFilter) {
|
|
302
|
+
whereClauseParts.push(`role = '${(0, filter_builder_1.escapeSqlString)(roleFilter)}'`);
|
|
303
|
+
}
|
|
293
304
|
// Handle --def (definition) filter
|
|
294
305
|
const defFilter = _filters === null || _filters === void 0 ? void 0 : _filters.def;
|
|
295
306
|
if (typeof defFilter === "string" && defFilter) {
|
package/package.json
CHANGED
|
@@ -39,11 +39,13 @@ Parameters:
|
|
|
39
39
|
- `limit` (optional): Max results (default 3, max 50)
|
|
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
|
-
- `detail` (optional): `"pointer"` (default) or `"
|
|
42
|
+
- `detail` (optional): `"pointer"` (default), `"code"` (4-line snippets), or `"full"` (complete chunk with line numbers)
|
|
43
43
|
- `min_score` (optional): Filter by minimum relevance score (0-1)
|
|
44
44
|
- `max_per_file` (optional): Cap results per file for diversity
|
|
45
45
|
- `file` (optional): Filter to files matching this name (e.g. "syncer.ts"). Matches filename, not full path.
|
|
46
46
|
- `exclude` (optional): Exclude files under this path prefix (e.g. "tests/" or "dist/")
|
|
47
|
+
- `language` (optional): Filter by file extension (e.g. "ts", "py", "go"). Omit the dot.
|
|
48
|
+
- `role` (optional): Filter by chunk role: "ORCHESTRATION" (logic/flow), "DEFINITION" (types), or "IMPLEMENTATION"
|
|
47
49
|
|
|
48
50
|
**When to use which mode:**
|
|
49
51
|
- `pointer` — navigation, finding locations, understanding architecture
|
|
@@ -55,8 +57,9 @@ Search ALL indexed code across every directory. Same parameters as semantic_sear
|
|
|
55
57
|
Use sparingly. Prefer `semantic_search` when you know which directory to search.
|
|
56
58
|
|
|
57
59
|
### code_skeleton
|
|
58
|
-
File structure — signatures with bodies collapsed (~4x fewer tokens).
|
|
59
|
-
- `target` (required): File path
|
|
60
|
+
File or directory structure — signatures with bodies collapsed (~4x fewer tokens).
|
|
61
|
+
- `target` (required): File path, directory path (e.g. "src/lib/search/"), or comma-separated files
|
|
62
|
+
- `limit` (optional): Max files for directory mode (default 10, max 20)
|
|
60
63
|
|
|
61
64
|
### trace_calls
|
|
62
65
|
Call graph — who calls a symbol and what it calls. Callers and callees include file:line locations. Unscoped — follows calls across all indexed directories.
|