grepmax 0.23.0 → 0.25.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/.claude-plugin/marketplace.json +1 -1
- package/README.md +25 -6
- package/dist/commands/add.js +38 -31
- package/dist/commands/config.js +16 -15
- package/dist/commands/context.js +38 -13
- package/dist/commands/doctor.js +76 -83
- package/dist/commands/extract.js +12 -1
- package/dist/commands/impact.js +33 -1
- package/dist/commands/index.js +23 -24
- package/dist/commands/list.js +23 -14
- package/dist/commands/mcp.js +494 -162
- package/dist/commands/peek.js +16 -5
- package/dist/commands/repair.js +54 -120
- package/dist/commands/search-output.js +30 -9
- package/dist/commands/search-run.js +75 -47
- package/dist/commands/search-skeletons.js +28 -18
- package/dist/commands/search.js +45 -49
- package/dist/commands/serve.js +415 -373
- package/dist/commands/setup.js +2 -2
- package/dist/commands/similar.js +5 -5
- package/dist/commands/skeleton.js +67 -41
- package/dist/commands/status.js +5 -2
- package/dist/commands/summarize.js +6 -0
- package/dist/commands/surprises.js +150 -0
- package/dist/commands/watch.js +102 -38
- package/dist/config.js +3 -0
- package/dist/eval-surprising-connections.js +191 -0
- package/dist/index.js +2 -0
- package/dist/lib/analysis/surprising-connections.js +600 -0
- package/dist/lib/daemon/daemon.js +1101 -379
- package/dist/lib/daemon/ipc-handler.js +122 -13
- package/dist/lib/daemon/mlx-server-manager.js +268 -125
- package/dist/lib/daemon/process-manager.js +7 -4
- package/dist/lib/daemon/search-handler.js +23 -9
- package/dist/lib/daemon/watcher-manager.js +353 -110
- package/dist/lib/graph/impact-rollup.js +202 -0
- package/dist/lib/graph/impact.js +15 -1
- package/dist/lib/help/agent-cheatsheet.js +1 -0
- package/dist/lib/index/batch-processor.js +231 -67
- package/dist/lib/index/embedding-generation.js +109 -0
- package/dist/lib/index/embedding-status.js +23 -0
- package/dist/lib/index/file-policy.js +273 -0
- package/dist/lib/index/ignore-patterns.js +4 -0
- package/dist/lib/index/index-config.js +18 -4
- package/dist/lib/index/syncer.js +256 -79
- package/dist/lib/index/walker.js +66 -86
- package/dist/lib/index/watcher-batch.js +9 -0
- package/dist/lib/index/watcher.js +129 -3
- package/dist/lib/llm/server.js +6 -0
- package/dist/lib/search/searcher.js +30 -19
- package/dist/lib/skeleton/skeletonizer.js +118 -0
- package/dist/lib/skeleton/summary-formatter.js +8 -1
- package/dist/lib/store/store-lease.js +473 -0
- package/dist/lib/store/vector-db.js +302 -57
- package/dist/lib/utils/blocked-roots.js +63 -0
- package/dist/lib/utils/cross-project.js +7 -3
- package/dist/lib/utils/daemon-client.js +24 -1
- package/dist/lib/utils/daemon-launcher.js +38 -13
- package/dist/lib/utils/doctor-status.js +76 -0
- package/dist/lib/utils/file-utils.js +74 -4
- package/dist/lib/utils/keyed-mutex.js +101 -0
- package/dist/lib/utils/logger.js +57 -1
- package/dist/lib/utils/mlx-hf-cache.js +114 -0
- package/dist/lib/utils/operation-coordinator.js +146 -0
- package/dist/lib/utils/path-containment.js +106 -0
- package/dist/lib/utils/process.js +44 -0
- package/dist/lib/utils/project-registry.js +351 -3
- package/dist/lib/utils/scope-filter.js +3 -9
- package/dist/lib/utils/stale-hint.js +12 -19
- package/dist/lib/utils/watcher-launcher.js +5 -1
- package/dist/lib/utils/watcher-store.js +2 -2
- package/dist/lib/workers/colbert-math.js +15 -12
- package/dist/lib/workers/embeddings/colbert.js +3 -2
- package/dist/lib/workers/embeddings/granite.js +4 -3
- package/dist/lib/workers/embeddings/mlx-client.js +59 -16
- package/dist/lib/workers/orchestrator.js +39 -8
- package/dist/lib/workers/pool.js +150 -83
- package/dist/lib/workers/process-child.js +11 -2
- package/dist/lib/workers/serialized-handler.js +10 -0
- package/dist/lib/workers/worker.js +13 -4
- package/mlx-embed-server/server.py +21 -3
- package/mlx-embed-server/summarizer.py +8 -0
- package/package.json +6 -3
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +3 -170
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "grepmax",
|
|
11
11
|
"source": "./plugins/grepmax",
|
|
12
12
|
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.25.0",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ gmax test handleAuth # Find tests via reverse call graph
|
|
|
73
73
|
gmax impact handleAuth # Dependents + affected tests
|
|
74
74
|
gmax similar handleAuth # Find similar code patterns
|
|
75
75
|
gmax audit --top 10 # God nodes, hub files, file cycles, dead candidates
|
|
76
|
+
gmax surprises --experimental --agent # Experimental: similar but graph-disconnected file pairs
|
|
76
77
|
gmax dead handleAuth # Unused-symbol check via call graph (DEAD / PUBLIC EXPORT / LIVE)
|
|
77
78
|
gmax context "auth system" --budget 4000 # Token-budgeted topic summary
|
|
78
79
|
gmax context src/lib/auth.ts --budget 4000 # Deterministic file/path context
|
|
@@ -134,6 +135,7 @@ Plugins auto-update when you run `npm install -g grepmax@latest` — no need to
|
|
|
134
135
|
| `peek_symbol` | Compact overview: signature + callers + callees. |
|
|
135
136
|
| `dead` | Unused-symbol check via call graph. Returns `DEAD`, `PUBLIC EXPORT`, or `LIVE` with caller count. |
|
|
136
137
|
| `audit` | Graph summary: god nodes, hub files, symbol-derived file dependency cycles, and dead-code candidates. |
|
|
138
|
+
| `surprising_connections` | Experimental orientation signal: embedding-similar file pairs with no direct indexed static file edge. Requires `experimental:true`. |
|
|
137
139
|
| `get_neighbors` | Graph primitive: symbols reachable from a node along call edges within N hops. |
|
|
138
140
|
| `find_paths` | Graph primitive: shortest call-graph path between two symbols. |
|
|
139
141
|
| `subgraph_for_files` | Graph primitive: local dependency subgraph for a set of files. |
|
|
@@ -177,6 +179,18 @@ gmax "query" [options]
|
|
|
177
179
|
| `--seed-symbol <name>` | Bias results toward an identifier you're working with (repeatable). | — |
|
|
178
180
|
| `--skeleton` | Show file skeletons for top matches. | `false` |
|
|
179
181
|
| `--context-for-llm` | Full function bodies + imports per result. | `false` |
|
|
182
|
+
|
|
183
|
+
## Experimental Orientation
|
|
184
|
+
|
|
185
|
+
`gmax surprises --experimental` finds file pairs that are semantically similar but not already connected by the indexed static graph. Use it for architecture orientation, duplicate-logic sweeps, and cross-package drift checks, not as proof that two files are unrelated.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
gmax surprises --experimental --agent
|
|
189
|
+
gmax surprises --experimental --in packages/app/src --dir-depth 4 --agent
|
|
190
|
+
gmax surprises --experimental --exclude generated --top 10
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The CLI and MCP output include score, max similarity, pair count, representative symbols, directory buckets, top similarities, applied penalties, and `gmax skeleton` follow-up hints. On large monorepos, prefer `--in`/`--exclude`; the default scan is capped at 50,000 rows and user-provided `--max-rows` is capped at 100,000. If a narrow `--in` scope returns no findings, increase `--dir-depth` so subdirectories are compared inside the scope.
|
|
180
194
|
| `--budget <tokens>` | Cap output tokens (for `--context-for-llm`). | `8000` |
|
|
181
195
|
| `--explain` | Show scoring breakdown per result. | `false` |
|
|
182
196
|
| `--scores` | Show relevance scores. | `false` |
|
|
@@ -318,16 +332,21 @@ worker RAM, and dense-vector storage while *lowering* recall on code search.
|
|
|
318
332
|
Unless you have a measured reason to switch (e.g. a recall complaint on a very
|
|
319
333
|
large repo — benchmark it first), stay on 384d.
|
|
320
334
|
|
|
321
|
-
|
|
335
|
+
The model tier is part of the persisted embedding generation. You can change the desired
|
|
336
|
+
configuration, but existing indexes remain on their built generation until you explicitly run the
|
|
337
|
+
guarded whole-corpus rebuild:
|
|
322
338
|
|
|
323
339
|
```bash
|
|
324
|
-
gmax config --model-tier standard #
|
|
325
|
-
gmax
|
|
340
|
+
gmax config --model-tier standard # changes desired configuration only
|
|
341
|
+
gmax config # shows configured vs built identity
|
|
342
|
+
gmax status # reports current / legacy / stale / unbuilt per project
|
|
343
|
+
gmax repair --rebuild # guarded whole-corpus rebuild through the daemon
|
|
326
344
|
```
|
|
327
345
|
|
|
328
|
-
A
|
|
329
|
-
|
|
330
|
-
|
|
346
|
+
A model change cannot be applied by a per-project `gmax index --reset`, even when vector widths
|
|
347
|
+
match: one query embedding cannot safely search rows from another embedding space. Stale-generation
|
|
348
|
+
search and sync fail before mutation, preserving the existing index. Run `gmax repair --rebuild` to
|
|
349
|
+
replace the whole corpus, or restore the prior model tier as the non-destructive alternative.
|
|
331
350
|
|
|
332
351
|
### Ignoring Files
|
|
333
352
|
|
package/dist/commands/add.js
CHANGED
|
@@ -54,30 +54,12 @@ const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
|
54
54
|
const syncer_1 = require("../lib/index/syncer");
|
|
55
55
|
const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
56
56
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
57
|
+
const blocked_roots_1 = require("../lib/utils/blocked-roots");
|
|
57
58
|
const exit_1 = require("../lib/utils/exit");
|
|
58
59
|
const project_marker_1 = require("../lib/utils/project-marker");
|
|
59
60
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
60
61
|
const project_root_1 = require("../lib/utils/project-root");
|
|
61
62
|
const watcher_launcher_1 = require("../lib/utils/watcher-launcher");
|
|
62
|
-
function getBlockedRoots() {
|
|
63
|
-
const home = os.homedir();
|
|
64
|
-
return new Set([
|
|
65
|
-
home,
|
|
66
|
-
path.dirname(home),
|
|
67
|
-
"/",
|
|
68
|
-
"/tmp",
|
|
69
|
-
"/private",
|
|
70
|
-
"/private/tmp",
|
|
71
|
-
"/private/var",
|
|
72
|
-
"/var",
|
|
73
|
-
"/usr",
|
|
74
|
-
"/opt",
|
|
75
|
-
"/etc",
|
|
76
|
-
"/System",
|
|
77
|
-
"/Library",
|
|
78
|
-
"/Applications",
|
|
79
|
-
].map((p) => path.resolve(p)));
|
|
80
|
-
}
|
|
81
63
|
function logBlockedAttempt(reason, attempted, extra) {
|
|
82
64
|
var _a, _b;
|
|
83
65
|
try {
|
|
@@ -101,17 +83,16 @@ Examples:
|
|
|
101
83
|
gmax add . --no-index Register only, index later with gmax index
|
|
102
84
|
`)
|
|
103
85
|
.action((dir, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
-
var _a, _b, _c, _d, _e;
|
|
86
|
+
var _a, _b, _c, _d, _e, _f;
|
|
105
87
|
let vectorDb = null;
|
|
106
88
|
try {
|
|
107
89
|
const targetDir = dir ? path.resolve(dir) : process.cwd();
|
|
108
90
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(targetDir)) !== null && _a !== void 0 ? _a : targetDir;
|
|
109
91
|
const projectName = path.basename(projectRoot);
|
|
110
|
-
|
|
111
|
-
if (blocked.has(path.resolve(projectRoot))) {
|
|
92
|
+
if ((0, blocked_roots_1.isBlockedProjectRoot)(projectRoot)) {
|
|
112
93
|
logBlockedAttempt("blocked_root", projectRoot);
|
|
113
94
|
console.error(`Refusing to add ${projectRoot}: this path is blocked from indexing.\n` +
|
|
114
|
-
`(Blocked:
|
|
95
|
+
`(Blocked: ${blocked_roots_1.BLOCKED_ROOTS_DESCRIPTION}.)\n` +
|
|
115
96
|
`Pick a specific project subdirectory instead.\n` +
|
|
116
97
|
`Diagnostic logged to ~/.gmax/logs/blocked-add.log (cwd=${process.cwd()} ppid=${process.ppid}).`);
|
|
117
98
|
process.exitCode = 1;
|
|
@@ -221,14 +202,18 @@ Examples:
|
|
|
221
202
|
if (!done.ok) {
|
|
222
203
|
throw new Error((_c = done.error) !== null && _c !== void 0 ? _c : "daemon add failed");
|
|
223
204
|
}
|
|
224
|
-
|
|
225
|
-
|
|
205
|
+
if (done.degraded === true) {
|
|
206
|
+
(0, project_registry_1.registerProject)(Object.assign(Object.assign({}, pendingEntry), { status: "pending" }));
|
|
207
|
+
spinner.warn(`Added ${projectName}, but indexing is incomplete (${(_d = done.scanErrors) !== null && _d !== void 0 ? _d : 0} scan errors, ${(_e = done.failedFiles) !== null && _e !== void 0 ? _e : 0} failed)`);
|
|
208
|
+
process.exitCode = 1;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const failedFiles = (_f = done.failedFiles) !== null && _f !== void 0 ? _f : 0;
|
|
226
212
|
const failedSuffix = failedFiles > 0 ? ` · ${failedFiles} failed` : "";
|
|
227
213
|
spinner.succeed(`Added ${projectName} (${done.total} files, ${done.indexed} chunks${failedSuffix})`);
|
|
228
214
|
// Watcher already started by daemon's addProject
|
|
229
215
|
}
|
|
230
216
|
catch (e) {
|
|
231
|
-
(0, project_registry_1.registerProject)(pendingEntry);
|
|
232
217
|
spinner.fail(`Failed to index ${projectName}`);
|
|
233
218
|
throw e;
|
|
234
219
|
}
|
|
@@ -242,12 +227,34 @@ Examples:
|
|
|
242
227
|
projectRoot,
|
|
243
228
|
onProgress,
|
|
244
229
|
});
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
230
|
+
if (result.degraded) {
|
|
231
|
+
(0, project_registry_1.registerProject)(Object.assign(Object.assign({}, pendingEntry), { status: "pending" }));
|
|
232
|
+
spinner.warn(`Added ${projectName}, but indexing is incomplete (${result.scanErrors} scan errors, ${result.failedFiles} failed)`);
|
|
233
|
+
process.exitCode = 1;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const prefix = projectRoot.endsWith("/")
|
|
237
|
+
? projectRoot
|
|
238
|
+
: `${projectRoot}/`;
|
|
239
|
+
const chunkCount = yield vectorDb.countRowsForPath(prefix);
|
|
240
|
+
(0, project_registry_1.stampProjectFullSync)({
|
|
241
|
+
root: projectRoot,
|
|
242
|
+
name: projectName,
|
|
243
|
+
generation: result.generation,
|
|
244
|
+
embedMode: result.embedMode,
|
|
245
|
+
chunkCount,
|
|
246
|
+
chunkerVersion: config_1.CONFIG.CHUNKER_VERSION,
|
|
247
|
+
expectedFingerprint: result.registryExpectation.embeddingFingerprint,
|
|
248
|
+
expectedRebuildId: result.registryExpectation.rebuildId,
|
|
249
|
+
});
|
|
250
|
+
const failedSuffix = result.failedFiles > 0 ? ` · ${result.failedFiles} failed` : "";
|
|
251
|
+
spinner.succeed(`Added ${projectName} (${result.total} files, ${result.indexed} chunks${failedSuffix})`);
|
|
252
|
+
}
|
|
248
253
|
}
|
|
249
254
|
catch (e) {
|
|
250
|
-
(
|
|
255
|
+
if (!(e instanceof project_registry_1.ProjectRegistryConflictError)) {
|
|
256
|
+
(0, project_registry_1.registerProject)(pendingEntry);
|
|
257
|
+
}
|
|
251
258
|
spinner.fail(`Failed to index ${projectName}`);
|
|
252
259
|
throw e;
|
|
253
260
|
}
|
|
@@ -271,7 +278,7 @@ Examples:
|
|
|
271
278
|
try {
|
|
272
279
|
yield vectorDb.close();
|
|
273
280
|
}
|
|
274
|
-
catch (
|
|
281
|
+
catch (_g) { }
|
|
275
282
|
}
|
|
276
283
|
yield (0, exit_1.gracefulExit)();
|
|
277
284
|
}
|
package/dist/commands/config.js
CHANGED
|
@@ -12,8 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.config = void 0;
|
|
13
13
|
const commander_1 = require("commander");
|
|
14
14
|
const config_1 = require("../config");
|
|
15
|
+
const embedding_status_1 = require("../lib/index/embedding-status");
|
|
15
16
|
const index_config_1 = require("../lib/index/index-config");
|
|
16
17
|
const exit_1 = require("../lib/utils/exit");
|
|
18
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
17
19
|
const project_root_1 = require("../lib/utils/project-root");
|
|
18
20
|
exports.config = new commander_1.Command("config")
|
|
19
21
|
.description("View or update gmax configuration")
|
|
@@ -28,24 +30,29 @@ Examples:
|
|
|
28
30
|
gmax config --query-log on Enable query logging
|
|
29
31
|
`)
|
|
30
32
|
.action((_opts, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
-
var _a, _b, _c, _d;
|
|
33
|
+
var _a, _b, _c, _d, _e;
|
|
32
34
|
const options = cmd.optsWithGlobals();
|
|
33
35
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
34
36
|
const paths = (0, project_root_1.ensureProjectPaths)(process.cwd());
|
|
35
|
-
const
|
|
37
|
+
const project = (0, project_registry_1.getProject)((_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd());
|
|
36
38
|
const hasUpdates = options.embedMode !== undefined ||
|
|
37
39
|
options.modelTier !== undefined ||
|
|
38
40
|
options.queryLog !== undefined;
|
|
39
41
|
if (!hasUpdates) {
|
|
40
42
|
// Show current config
|
|
41
|
-
const tier = (
|
|
43
|
+
const tier = (_b = config_1.MODEL_TIERS[globalConfig.modelTier]) !== null && _b !== void 0 ? _b : config_1.MODEL_TIERS.small;
|
|
42
44
|
console.log("gmax configuration (~/.gmax/config.json)\n");
|
|
43
45
|
console.log(` Model tier: ${globalConfig.modelTier} (${tier.vectorDim}d, ${tier.params})`);
|
|
44
46
|
console.log(` Embed mode: ${globalConfig.embedMode}`);
|
|
45
47
|
console.log(` Embed model: ${globalConfig.embedMode === "gpu" ? tier.mlxModel : tier.onnxModel}`);
|
|
48
|
+
const identity = (0, embedding_status_1.projectEmbeddingStatus)(project, globalConfig);
|
|
49
|
+
console.log(` Configured: ${identity.configured.tier} ${identity.configured.vectorDim}d [${(0, embedding_status_1.embeddingFingerprintLabel)(identity.configured.fingerprint)}]`);
|
|
50
|
+
if (identity.built) {
|
|
51
|
+
console.log(` Built: ${identity.built.tier} ${identity.built.vectorDim}d [${(0, embedding_status_1.embeddingFingerprintLabel)(identity.built.fingerprint)}] (${identity.state})`);
|
|
52
|
+
}
|
|
46
53
|
console.log(` Query log: ${globalConfig.queryLog ? "on" : "off"}`);
|
|
47
|
-
if (
|
|
48
|
-
console.log(` Last indexed: ${
|
|
54
|
+
if (project === null || project === void 0 ? void 0 : project.lastIndexed) {
|
|
55
|
+
console.log(` Last indexed: ${project.lastIndexed}`);
|
|
49
56
|
}
|
|
50
57
|
console.log(`\nTo change: gmax config --embed-mode <cpu|gpu> --model-tier <small|standard> --query-log <on|off>`);
|
|
51
58
|
yield (0, exit_1.gracefulExit)();
|
|
@@ -80,11 +87,10 @@ Examples:
|
|
|
80
87
|
// Reload config after queryLog write
|
|
81
88
|
Object.assign(globalConfig, (0, index_config_1.readGlobalConfig)());
|
|
82
89
|
}
|
|
83
|
-
const newTier = (
|
|
84
|
-
const newMode = (
|
|
85
|
-
const tier = (
|
|
90
|
+
const newTier = (_c = options.modelTier) !== null && _c !== void 0 ? _c : globalConfig.modelTier;
|
|
91
|
+
const newMode = (_d = options.embedMode) !== null && _d !== void 0 ? _d : globalConfig.embedMode;
|
|
92
|
+
const tier = (_e = config_1.MODEL_TIERS[newTier]) !== null && _e !== void 0 ? _e : config_1.MODEL_TIERS.small;
|
|
86
93
|
const tierChanged = newTier !== globalConfig.modelTier;
|
|
87
|
-
const dimChanged = tier.vectorDim !== globalConfig.vectorDim;
|
|
88
94
|
(0, index_config_1.writeGlobalConfig)({
|
|
89
95
|
modelTier: newTier,
|
|
90
96
|
vectorDim: tier.vectorDim,
|
|
@@ -99,12 +105,7 @@ Examples:
|
|
|
99
105
|
});
|
|
100
106
|
console.log(`Updated: embed-mode=${newMode}, model-tier=${newTier} (${tier.vectorDim}d)`);
|
|
101
107
|
if (tierChanged) {
|
|
102
|
-
|
|
103
|
-
// the shared `chunks` table is fixed-width, so it must be dropped and
|
|
104
|
-
// rebuilt. A same-dim model swap (future tiers) can use a per-project reset.
|
|
105
|
-
console.log(dimChanged
|
|
106
|
-
? `⚠️ Model tier changed (${globalConfig.vectorDim}d → ${tier.vectorDim}d). The shared vector table is fixed-width — run \`${config_1.REBUILD_COMMAND}\` to drop it and re-embed every project at the new dim.`
|
|
107
|
-
: "⚠️ Model tier changed — run `gmax index --reset` per project to re-embed with the new model.");
|
|
108
|
+
console.log(`Model tier changed (${globalConfig.vectorDim}d → ${tier.vectorDim}d). Existing indexes are preserved and search is blocked until you run 'gmax repair --rebuild'.`);
|
|
108
109
|
}
|
|
109
110
|
yield (0, exit_1.gracefulExit)();
|
|
110
111
|
}));
|
package/dist/commands/context.js
CHANGED
|
@@ -53,7 +53,9 @@ const vector_db_1 = require("../lib/store/vector-db");
|
|
|
53
53
|
const arrow_1 = require("../lib/utils/arrow");
|
|
54
54
|
const budget_pack_1 = require("../lib/utils/budget-pack");
|
|
55
55
|
const exit_1 = require("../lib/utils/exit");
|
|
56
|
+
const file_utils_1 = require("../lib/utils/file-utils");
|
|
56
57
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
58
|
+
const path_containment_1 = require("../lib/utils/path-containment");
|
|
57
59
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
58
60
|
const project_root_1 = require("../lib/utils/project-root");
|
|
59
61
|
function estimateTokens(text) {
|
|
@@ -106,8 +108,11 @@ function resolveExistingPath(target, root, projectRoot) {
|
|
|
106
108
|
path.resolve(projectRoot, target),
|
|
107
109
|
];
|
|
108
110
|
for (const candidate of candidates) {
|
|
109
|
-
if (fs.existsSync(candidate))
|
|
110
|
-
|
|
111
|
+
if (!fs.existsSync(candidate))
|
|
112
|
+
continue;
|
|
113
|
+
return (0, path_containment_1.resolveContainedPath)(projectRoot, candidate, {
|
|
114
|
+
verifyExistingTarget: true,
|
|
115
|
+
});
|
|
111
116
|
}
|
|
112
117
|
return null;
|
|
113
118
|
}
|
|
@@ -137,7 +142,7 @@ function renderPathContext(target, absPath, projectRoot, budget) {
|
|
|
137
142
|
}
|
|
138
143
|
return state;
|
|
139
144
|
}
|
|
140
|
-
const content =
|
|
145
|
+
const content = (0, file_utils_1.readContainedTextFileSync)(projectRoot, absPath);
|
|
141
146
|
const skeletonizer = new skeleton_1.Skeletonizer();
|
|
142
147
|
yield skeletonizer.init();
|
|
143
148
|
if (skeletonizer.isSupported(absPath).supported) {
|
|
@@ -196,8 +201,9 @@ exports.context = new commander_1.Command("context")
|
|
|
196
201
|
}
|
|
197
202
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
198
203
|
// Phase 1: Semantic search
|
|
199
|
-
const response = yield searcher.search(topic, maxResults, { rerank: true }, {}, projectRoot);
|
|
200
|
-
|
|
204
|
+
const response = yield searcher.search(topic, maxResults, { rerank: true }, {}, `${projectRoot}/`);
|
|
205
|
+
const scopedData = response.data.filter((result) => (0, path_containment_1.isPathWithin)(projectRoot, chunkPath(result)));
|
|
206
|
+
if (scopedData.length === 0) {
|
|
201
207
|
console.log(`No results found for "${topic}".`);
|
|
202
208
|
return;
|
|
203
209
|
}
|
|
@@ -208,8 +214,8 @@ exports.context = new commander_1.Command("context")
|
|
|
208
214
|
sections.push(header);
|
|
209
215
|
tokensUsed += estimateTokens(header);
|
|
210
216
|
// Phase 2: Entry points (ORCHESTRATION role results)
|
|
211
|
-
const orchestrators =
|
|
212
|
-
const entryPoints = orchestrators.length > 0 ? orchestrators :
|
|
217
|
+
const orchestrators = scopedData.filter((r) => r.role === "ORCHESTRATION");
|
|
218
|
+
const entryPoints = orchestrators.length > 0 ? orchestrators : scopedData.slice(0, 3);
|
|
213
219
|
const epSection = ["\n## Entry Points"];
|
|
214
220
|
for (const r of entryPoints.slice(0, 5)) {
|
|
215
221
|
const p = chunkPath(r);
|
|
@@ -230,13 +236,21 @@ exports.context = new commander_1.Command("context")
|
|
|
230
236
|
const topChunks = entryPoints.slice(0, 3);
|
|
231
237
|
const bodyBlobs = topChunks.map((r) => {
|
|
232
238
|
var _a, _b;
|
|
233
|
-
|
|
239
|
+
let absP;
|
|
240
|
+
try {
|
|
241
|
+
absP = (0, path_containment_1.resolveContainedPath)(projectRoot, chunkPath(r), {
|
|
242
|
+
verifyExistingTarget: true,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (_c) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
234
248
|
const startLine = chunkStartLine(r);
|
|
235
249
|
const endLine = chunkEndLine(r);
|
|
236
250
|
const sym = (_b = (_a = (0, arrow_1.toArr)(r.defined_symbols)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "";
|
|
237
251
|
const parentSym = String(r.parent_symbol || "");
|
|
238
252
|
try {
|
|
239
|
-
const content =
|
|
253
|
+
const content = (0, file_utils_1.readContainedTextFileSync)(projectRoot, absP);
|
|
240
254
|
const allLines = content.split("\n");
|
|
241
255
|
const body = allLines
|
|
242
256
|
.slice(startLine, Math.min(endLine + 1, allLines.length))
|
|
@@ -255,7 +269,7 @@ exports.context = new commander_1.Command("context")
|
|
|
255
269
|
}
|
|
256
270
|
return `\n--- ${relPath(projectRoot, absP)}:${startLine + 1} ${label} ---\n${enclosing}${body}`;
|
|
257
271
|
}
|
|
258
|
-
catch (
|
|
272
|
+
catch (_d) {
|
|
259
273
|
return null; // File not readable — drop
|
|
260
274
|
}
|
|
261
275
|
});
|
|
@@ -279,7 +293,18 @@ exports.context = new commander_1.Command("context")
|
|
|
279
293
|
}
|
|
280
294
|
// Phase 4: File skeletons for unique files
|
|
281
295
|
const uniqueFiles = [
|
|
282
|
-
...new Set(
|
|
296
|
+
...new Set(scopedData
|
|
297
|
+
.map((r) => {
|
|
298
|
+
try {
|
|
299
|
+
return (0, path_containment_1.resolveContainedPath)(projectRoot, chunkPath(r), {
|
|
300
|
+
verifyExistingTarget: true,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
catch (_a) {
|
|
304
|
+
return "";
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
.filter(Boolean)),
|
|
283
308
|
].slice(0, 5);
|
|
284
309
|
const skelSection = ["\n## File Structure"];
|
|
285
310
|
const skeletonizer = new skeleton_1.Skeletonizer();
|
|
@@ -288,7 +313,7 @@ exports.context = new commander_1.Command("context")
|
|
|
288
313
|
if (!skeletonizer.isSupported(absP).supported)
|
|
289
314
|
continue;
|
|
290
315
|
try {
|
|
291
|
-
const content =
|
|
316
|
+
const content = (0, file_utils_1.readContainedTextFileSync)(projectRoot, absP);
|
|
292
317
|
const result = yield skeletonizer.skeletonizeFile(absP, content);
|
|
293
318
|
if (!result.success)
|
|
294
319
|
continue;
|
|
@@ -311,7 +336,7 @@ exports.context = new commander_1.Command("context")
|
|
|
311
336
|
// Phase 5: Related files summary
|
|
312
337
|
const table = yield vectorDb.ensureTable();
|
|
313
338
|
const allSymbols = new Set();
|
|
314
|
-
for (const r of
|
|
339
|
+
for (const r of scopedData) {
|
|
315
340
|
for (const s of (0, arrow_1.toArr)(r.defined_symbols))
|
|
316
341
|
allSymbols.add(s);
|
|
317
342
|
}
|