grepmax 0.17.3 → 0.17.4
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/context.js +118 -21
- package/dist/commands/mcp.js +305 -118
- package/dist/commands/search.js +40 -102
- package/dist/lib/output/agent-search-formatter.js +163 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/context.js
CHANGED
|
@@ -44,18 +44,111 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.context = void 0;
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
47
48
|
const commander_1 = require("commander");
|
|
48
49
|
const searcher_1 = require("../lib/search/searcher");
|
|
49
50
|
const skeleton_1 = require("../lib/skeleton");
|
|
50
51
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
51
|
-
const
|
|
52
|
+
const arrow_1 = require("../lib/utils/arrow");
|
|
52
53
|
const exit_1 = require("../lib/utils/exit");
|
|
54
|
+
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
53
55
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
54
56
|
const project_root_1 = require("../lib/utils/project-root");
|
|
55
|
-
const arrow_1 = require("../lib/utils/arrow");
|
|
56
57
|
function estimateTokens(text) {
|
|
57
58
|
return Math.ceil(text.length / 4);
|
|
58
59
|
}
|
|
60
|
+
function addSection(state, text, budget) {
|
|
61
|
+
const tokens = estimateTokens(text);
|
|
62
|
+
if (state.tokensUsed + tokens > budget)
|
|
63
|
+
return false;
|
|
64
|
+
state.sections.push(text);
|
|
65
|
+
state.tokensUsed += tokens;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function relPath(projectRoot, p) {
|
|
69
|
+
return p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
|
70
|
+
}
|
|
71
|
+
function chunkPath(chunk) {
|
|
72
|
+
const metadata = chunk.metadata;
|
|
73
|
+
return String(chunk.path || (metadata === null || metadata === void 0 ? void 0 : metadata.path) || "");
|
|
74
|
+
}
|
|
75
|
+
function chunkStartLine(chunk) {
|
|
76
|
+
var _a, _b, _c, _d;
|
|
77
|
+
return Number((_d = (_b = (_a = chunk.start_line) !== null && _a !== void 0 ? _a : chunk.startLine) !== null && _b !== void 0 ? _b : (_c = chunk.generated_metadata) === null || _c === void 0 ? void 0 : _c.start_line) !== null && _d !== void 0 ? _d : 0);
|
|
78
|
+
}
|
|
79
|
+
function chunkEndLine(chunk) {
|
|
80
|
+
var _a, _b, _c, _d;
|
|
81
|
+
const start = chunkStartLine(chunk);
|
|
82
|
+
return Number((_d = (_b = (_a = chunk.end_line) !== null && _a !== void 0 ? _a : chunk.endLine) !== null && _b !== void 0 ? _b : (_c = chunk.generated_metadata) === null || _c === void 0 ? void 0 : _c.end_line) !== null && _d !== void 0 ? _d : start);
|
|
83
|
+
}
|
|
84
|
+
function resolveExistingPath(target, root, projectRoot) {
|
|
85
|
+
const candidates = [
|
|
86
|
+
path.isAbsolute(target) ? target : path.resolve(root, target),
|
|
87
|
+
path.resolve(projectRoot, target),
|
|
88
|
+
];
|
|
89
|
+
for (const candidate of candidates) {
|
|
90
|
+
if (fs.existsSync(candidate))
|
|
91
|
+
return candidate;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function renderPathContext(target, absPath, projectRoot, budget) {
|
|
96
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
const state = {
|
|
98
|
+
sections: [],
|
|
99
|
+
tokensUsed: 0,
|
|
100
|
+
};
|
|
101
|
+
const header = `=== Context: "${target}" ===`;
|
|
102
|
+
addSection(state, header, budget);
|
|
103
|
+
const stat = fs.statSync(absPath);
|
|
104
|
+
const targetSection = [
|
|
105
|
+
"\n## Target",
|
|
106
|
+
`${relPath(projectRoot, absPath)} [${stat.isDirectory() ? "directory" : "file"}]`,
|
|
107
|
+
].join("\n");
|
|
108
|
+
addSection(state, targetSection, budget);
|
|
109
|
+
if (stat.isDirectory()) {
|
|
110
|
+
const entries = fs
|
|
111
|
+
.readdirSync(absPath, { withFileTypes: true })
|
|
112
|
+
.filter((entry) => !entry.name.startsWith("."))
|
|
113
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
114
|
+
.slice(0, 40)
|
|
115
|
+
.map((entry) => `${entry.isDirectory() ? "dir " : "file"} ${entry.name}`);
|
|
116
|
+
if (entries.length > 0) {
|
|
117
|
+
addSection(state, ["\n## Directory Entries", ...entries].join("\n"), budget);
|
|
118
|
+
}
|
|
119
|
+
return state;
|
|
120
|
+
}
|
|
121
|
+
const content = fs.readFileSync(absPath, "utf-8");
|
|
122
|
+
const skeletonizer = new skeleton_1.Skeletonizer();
|
|
123
|
+
yield skeletonizer.init();
|
|
124
|
+
if (skeletonizer.isSupported(absPath).supported) {
|
|
125
|
+
try {
|
|
126
|
+
const result = yield skeletonizer.skeletonizeFile(absPath, content);
|
|
127
|
+
if (result.success) {
|
|
128
|
+
addSection(state, [
|
|
129
|
+
"\n## File Structure",
|
|
130
|
+
`--- ${relPath(projectRoot, absPath)} (skeleton, ~${result.tokenEstimate} tokens) ---`,
|
|
131
|
+
result.skeleton,
|
|
132
|
+
].join("\n"), budget);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (_a) {
|
|
136
|
+
// Skeleton is a convenience in path mode; fall through to excerpt.
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const lines = content.split("\n");
|
|
140
|
+
const excerptLines = lines.slice(0, Math.min(lines.length, 120));
|
|
141
|
+
const omitted = lines.length > excerptLines.length
|
|
142
|
+
? `\n... (+${lines.length - excerptLines.length} more lines)`
|
|
143
|
+
: "";
|
|
144
|
+
addSection(state, [
|
|
145
|
+
"\n## File Excerpt",
|
|
146
|
+
`--- ${relPath(projectRoot, absPath)}:1 ---`,
|
|
147
|
+
`${excerptLines.join("\n")}${omitted}`,
|
|
148
|
+
].join("\n"), budget);
|
|
149
|
+
return state;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
59
152
|
exports.context = new commander_1.Command("context")
|
|
60
153
|
.description("Generate a token-budgeted topic summary (search + skeleton + extract)")
|
|
61
154
|
.argument("<topic>", "Natural language topic or directory path")
|
|
@@ -64,7 +157,7 @@ exports.context = new commander_1.Command("context")
|
|
|
64
157
|
.option("--root <dir>", "Project root directory")
|
|
65
158
|
.option("--agent", "Compact output for AI agents", false)
|
|
66
159
|
.action((topic, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
|
-
var _a, _b, _c, _d, _e
|
|
160
|
+
var _a, _b, _c, _d, _e;
|
|
68
161
|
const budget = Number.parseInt(opts.budget || "4000", 10) || 4000;
|
|
69
162
|
const maxResults = Number.parseInt(opts.maxResults || "10", 10) || 10;
|
|
70
163
|
let vectorDb = null;
|
|
@@ -75,8 +168,14 @@ exports.context = new commander_1.Command("context")
|
|
|
75
168
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
76
169
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
77
170
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
171
|
+
const pathTarget = resolveExistingPath(topic, root, projectRoot);
|
|
172
|
+
if (pathTarget) {
|
|
173
|
+
const rendered = yield renderPathContext(topic, pathTarget, projectRoot, budget);
|
|
174
|
+
rendered.sections.push(`\n(~${rendered.tokensUsed}/${budget} tokens used)`);
|
|
175
|
+
console.log(rendered.sections.join("\n"));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
78
178
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
79
|
-
const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
|
80
179
|
// Phase 1: Semantic search
|
|
81
180
|
const response = yield searcher.search(topic, maxResults, { rerank: true }, {}, projectRoot);
|
|
82
181
|
if (response.data.length === 0) {
|
|
@@ -94,11 +193,11 @@ exports.context = new commander_1.Command("context")
|
|
|
94
193
|
const entryPoints = orchestrators.length > 0 ? orchestrators : response.data.slice(0, 3);
|
|
95
194
|
const epSection = ["\n## Entry Points"];
|
|
96
195
|
for (const r of entryPoints.slice(0, 5)) {
|
|
97
|
-
const p =
|
|
98
|
-
const line =
|
|
99
|
-
const sym = (
|
|
196
|
+
const p = chunkPath(r);
|
|
197
|
+
const line = chunkStartLine(r);
|
|
198
|
+
const sym = (_c = (_b = (0, arrow_1.toArr)(r.defined_symbols)) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : "";
|
|
100
199
|
const role = String(r.role || "IMPLEMENTATION");
|
|
101
|
-
epSection.push(`${
|
|
200
|
+
epSection.push(`${relPath(projectRoot, p)}:${line + 1} ${sym} [${role}]`);
|
|
102
201
|
}
|
|
103
202
|
const epText = epSection.join("\n");
|
|
104
203
|
if (tokensUsed + estimateTokens(epText) <= budget) {
|
|
@@ -109,24 +208,24 @@ exports.context = new commander_1.Command("context")
|
|
|
109
208
|
const topChunks = entryPoints.slice(0, 3);
|
|
110
209
|
const bodySection = ["\n## Key Functions"];
|
|
111
210
|
for (const r of topChunks) {
|
|
112
|
-
const absP =
|
|
113
|
-
const startLine =
|
|
114
|
-
const endLine =
|
|
115
|
-
const sym = (
|
|
211
|
+
const absP = chunkPath(r);
|
|
212
|
+
const startLine = chunkStartLine(r);
|
|
213
|
+
const endLine = chunkEndLine(r);
|
|
214
|
+
const sym = (_e = (_d = (0, arrow_1.toArr)(r.defined_symbols)) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : "";
|
|
116
215
|
try {
|
|
117
216
|
const content = fs.readFileSync(absP, "utf-8");
|
|
118
217
|
const allLines = content.split("\n");
|
|
119
218
|
const body = allLines
|
|
120
219
|
.slice(startLine, Math.min(endLine + 1, allLines.length))
|
|
121
220
|
.join("\n");
|
|
122
|
-
const blob = `\n--- ${
|
|
221
|
+
const blob = `\n--- ${relPath(projectRoot, absP)}:${startLine + 1} ${sym} ---\n${body}`;
|
|
123
222
|
const blobTokens = estimateTokens(blob);
|
|
124
223
|
if (tokensUsed + blobTokens > budget)
|
|
125
224
|
break;
|
|
126
225
|
bodySection.push(blob);
|
|
127
226
|
tokensUsed += blobTokens;
|
|
128
227
|
}
|
|
129
|
-
catch (
|
|
228
|
+
catch (_f) {
|
|
130
229
|
// File not readable — skip
|
|
131
230
|
}
|
|
132
231
|
}
|
|
@@ -135,9 +234,7 @@ exports.context = new commander_1.Command("context")
|
|
|
135
234
|
}
|
|
136
235
|
// Phase 4: File skeletons for unique files
|
|
137
236
|
const uniqueFiles = [
|
|
138
|
-
...new Set(response.data
|
|
139
|
-
.map((r) => String(r.path || ""))
|
|
140
|
-
.filter(Boolean)),
|
|
237
|
+
...new Set(response.data.map((r) => chunkPath(r)).filter(Boolean)),
|
|
141
238
|
].slice(0, 5);
|
|
142
239
|
const skelSection = ["\n## File Structure"];
|
|
143
240
|
const skeletonizer = new skeleton_1.Skeletonizer();
|
|
@@ -150,14 +247,14 @@ exports.context = new commander_1.Command("context")
|
|
|
150
247
|
const result = yield skeletonizer.skeletonizeFile(absP, content);
|
|
151
248
|
if (!result.success)
|
|
152
249
|
continue;
|
|
153
|
-
const blob = `\n--- ${
|
|
250
|
+
const blob = `\n--- ${relPath(projectRoot, absP)} (skeleton, ~${result.tokenEstimate} tokens) ---\n${result.skeleton}`;
|
|
154
251
|
const blobTokens = estimateTokens(blob);
|
|
155
252
|
if (tokensUsed + blobTokens > budget)
|
|
156
253
|
break;
|
|
157
254
|
skelSection.push(blob);
|
|
158
255
|
tokensUsed += blobTokens;
|
|
159
256
|
}
|
|
160
|
-
catch (
|
|
257
|
+
catch (_g) {
|
|
161
258
|
// Skip unreadable files
|
|
162
259
|
}
|
|
163
260
|
}
|
|
@@ -195,7 +292,7 @@ exports.context = new commander_1.Command("context")
|
|
|
195
292
|
if (topRelated.length > 0) {
|
|
196
293
|
const relSection = ["\n## Related Files"];
|
|
197
294
|
for (const [p, count] of topRelated) {
|
|
198
|
-
relSection.push(`${
|
|
295
|
+
relSection.push(`${relPath(projectRoot, p)} — ${count} shared symbol${count > 1 ? "s" : ""}`);
|
|
199
296
|
}
|
|
200
297
|
const relText = relSection.join("\n");
|
|
201
298
|
if (tokensUsed + estimateTokens(relText) <= budget) {
|
|
@@ -218,7 +315,7 @@ exports.context = new commander_1.Command("context")
|
|
|
218
315
|
try {
|
|
219
316
|
yield vectorDb.close();
|
|
220
317
|
}
|
|
221
|
-
catch (
|
|
318
|
+
catch (_h) { }
|
|
222
319
|
}
|
|
223
320
|
yield (0, exit_1.gracefulExit)();
|
|
224
321
|
}
|