grepmax 0.1.0 → 0.2.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/LICENSE +1 -1
- package/NOTICE +2 -2
- package/README.md +72 -72
- package/dist/commands/claude-code.js +6 -6
- package/dist/commands/codex.js +17 -17
- package/dist/commands/doctor.js +6 -5
- package/dist/commands/droid.js +22 -22
- package/dist/commands/index.js +1 -1
- package/dist/commands/list.js +82 -19
- package/dist/commands/mcp.js +161 -142
- package/dist/commands/opencode.js +26 -26
- package/dist/commands/search.js +23 -13
- package/dist/commands/serve.js +30 -30
- package/dist/commands/setup.js +51 -40
- package/dist/commands/skeleton.js +19 -13
- package/dist/commands/symbols.js +40 -2
- package/dist/commands/verify.js +1 -1
- package/dist/commands/watch.js +206 -0
- package/dist/config.js +37 -7
- package/dist/eval.js +14 -14
- package/dist/index.js +11 -7
- package/dist/lib/core/languages.js +28 -0
- package/dist/lib/index/chunker.js +6 -3
- package/dist/lib/index/grammar-loader.js +2 -2
- package/dist/lib/index/ignore-patterns.js +1 -1
- package/dist/lib/index/index-config.js +50 -10
- package/dist/lib/index/sync-helpers.js +1 -1
- package/dist/lib/index/syncer.js +67 -45
- package/dist/lib/index/walker.js +3 -3
- package/dist/lib/index/watcher.js +4 -4
- package/dist/lib/output/formatter.js +1 -1
- package/dist/lib/search/searcher.js +9 -9
- package/dist/lib/setup/model-loader.js +3 -3
- package/dist/lib/setup/setup-helpers.js +2 -4
- package/dist/lib/skeleton/body-fields.js +20 -0
- package/dist/lib/skeleton/retriever.js +1 -1
- package/dist/lib/skeleton/skeletonizer.js +8 -2
- package/dist/lib/skeleton/summary-formatter.js +1 -4
- package/dist/lib/store/meta-cache.js +28 -3
- package/dist/lib/store/vector-db.js +17 -9
- package/dist/lib/utils/formatter.js +3 -3
- package/dist/lib/utils/lock.js +1 -1
- package/dist/lib/utils/project-registry.js +83 -0
- package/dist/lib/utils/project-root.js +32 -57
- package/dist/lib/utils/watcher-registry.js +100 -0
- package/dist/lib/workers/colbert-math.js +2 -2
- package/dist/lib/workers/download-worker.js +2 -2
- package/dist/lib/workers/embeddings/colbert.js +2 -2
- package/dist/lib/workers/embeddings/granite.js +4 -4
- package/dist/lib/workers/embeddings/mlx-client.js +1 -1
- package/dist/lib/workers/orchestrator.js +8 -8
- package/dist/lib/workers/pool.js +1 -1
- package/dist/lib/workers/worker.js +4 -1
- package/package.json +20 -21
- package/plugins/{osgrep → grepmax}/.claude-plugin/plugin.json +4 -4
- package/plugins/grepmax/hooks/start.js +63 -0
- package/plugins/grepmax/hooks/stop.js +3 -0
- package/plugins/{osgrep/skills/osgrep → grepmax/skills/gmax}/SKILL.md +11 -11
- package/plugins/osgrep/hooks/start.js +0 -90
- package/plugins/osgrep/hooks/stop.js +0 -3
- /package/plugins/{osgrep → grepmax}/hooks.json +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Index configuration — tracks which models built the current index.
|
|
4
4
|
* Prevents searching with incompatible vectors after a model swap.
|
|
5
5
|
*
|
|
6
|
-
* Also stores user preferences from `
|
|
6
|
+
* Also stores user preferences from `gmax setup` (embed mode, MLX model).
|
|
7
7
|
*/
|
|
8
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -39,12 +39,47 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.readGlobalConfig = readGlobalConfig;
|
|
43
|
+
exports.writeGlobalConfig = writeGlobalConfig;
|
|
44
|
+
exports.getModelIdsForTier = getModelIdsForTier;
|
|
42
45
|
exports.readIndexConfig = readIndexConfig;
|
|
43
46
|
exports.writeIndexConfig = writeIndexConfig;
|
|
44
47
|
exports.writeSetupConfig = writeSetupConfig;
|
|
45
48
|
exports.checkModelMismatch = checkModelMismatch;
|
|
46
49
|
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
47
51
|
const config_1 = require("../../config");
|
|
52
|
+
const GLOBAL_CONFIG_PATH = path.join(config_1.PATHS.globalRoot, "config.json");
|
|
53
|
+
function readGlobalConfig() {
|
|
54
|
+
try {
|
|
55
|
+
const raw = fs.readFileSync(GLOBAL_CONFIG_PATH, "utf-8");
|
|
56
|
+
return JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch (_a) {
|
|
59
|
+
const tier = config_1.MODEL_TIERS[config_1.DEFAULT_MODEL_TIER];
|
|
60
|
+
return {
|
|
61
|
+
modelTier: config_1.DEFAULT_MODEL_TIER,
|
|
62
|
+
vectorDim: tier.vectorDim,
|
|
63
|
+
embedMode: "cpu",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function writeGlobalConfig(config) {
|
|
68
|
+
fs.mkdirSync(path.dirname(GLOBAL_CONFIG_PATH), { recursive: true });
|
|
69
|
+
fs.writeFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve the effective model IDs for a given tier.
|
|
73
|
+
*/
|
|
74
|
+
function getModelIdsForTier(tierName) {
|
|
75
|
+
var _a;
|
|
76
|
+
const tier = (_a = config_1.MODEL_TIERS[tierName]) !== null && _a !== void 0 ? _a : config_1.MODEL_TIERS[config_1.DEFAULT_MODEL_TIER];
|
|
77
|
+
return {
|
|
78
|
+
embed: tier.onnxModel,
|
|
79
|
+
colbert: config_1.MODEL_IDS.colbert,
|
|
80
|
+
vectorDim: tier.vectorDim,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
48
83
|
function readIndexConfig(configPath) {
|
|
49
84
|
try {
|
|
50
85
|
const raw = fs.readFileSync(configPath, "utf-8");
|
|
@@ -55,32 +90,37 @@ function readIndexConfig(configPath) {
|
|
|
55
90
|
}
|
|
56
91
|
}
|
|
57
92
|
function writeIndexConfig(configPath, extra) {
|
|
93
|
+
var _a;
|
|
58
94
|
const existing = readIndexConfig(configPath);
|
|
95
|
+
const tierName = (_a = existing === null || existing === void 0 ? void 0 : existing.modelTier) !== null && _a !== void 0 ? _a : readGlobalConfig().modelTier;
|
|
96
|
+
const tierIds = getModelIdsForTier(tierName);
|
|
59
97
|
const config = Object.assign({
|
|
60
98
|
// Preserve user preferences from setup
|
|
61
|
-
embedMode: existing === null || existing === void 0 ? void 0 : existing.embedMode, mlxModel: existing === null || existing === void 0 ? void 0 : existing.mlxModel,
|
|
99
|
+
embedMode: existing === null || existing === void 0 ? void 0 : existing.embedMode, mlxModel: existing === null || existing === void 0 ? void 0 : existing.mlxModel, modelTier: tierName,
|
|
62
100
|
// Model identity from current run
|
|
63
|
-
embedModel:
|
|
64
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2)
|
|
101
|
+
embedModel: tierIds.embed, colbertModel: tierIds.colbert, vectorDim: tierIds.vectorDim, indexedAt: new Date().toISOString() }, extra);
|
|
102
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
65
103
|
}
|
|
66
104
|
/**
|
|
67
105
|
* Write only user preferences without touching model identity fields.
|
|
68
|
-
* Used by `
|
|
106
|
+
* Used by `gmax setup` before any indexing has happened.
|
|
69
107
|
*/
|
|
70
108
|
function writeSetupConfig(configPath, prefs) {
|
|
71
109
|
const existing = readIndexConfig(configPath);
|
|
72
|
-
const config = Object.assign(Object.assign({}, existing), { embedMode: prefs.embedMode, mlxModel: prefs.mlxModel });
|
|
73
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2)
|
|
110
|
+
const config = Object.assign(Object.assign({}, existing), { embedMode: prefs.embedMode, mlxModel: prefs.mlxModel, modelTier: prefs.modelTier });
|
|
111
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
74
112
|
}
|
|
75
113
|
function checkModelMismatch(configPath) {
|
|
76
114
|
const stored = readIndexConfig(configPath);
|
|
77
115
|
if (!stored)
|
|
78
116
|
return false; // No config yet — first index
|
|
79
|
-
|
|
117
|
+
const globalConfig = readGlobalConfig();
|
|
118
|
+
const tierIds = getModelIdsForTier(globalConfig.modelTier);
|
|
119
|
+
if (stored.embedModel && stored.embedModel !== tierIds.embed)
|
|
80
120
|
return true;
|
|
81
|
-
if (stored.colbertModel !==
|
|
121
|
+
if (stored.colbertModel && stored.colbertModel !== tierIds.colbert)
|
|
82
122
|
return true;
|
|
83
|
-
if (stored.vectorDim !== undefined && stored.vectorDim !==
|
|
123
|
+
if (stored.vectorDim !== undefined && stored.vectorDim !== tierIds.vectorDim)
|
|
84
124
|
return true;
|
|
85
125
|
return false;
|
|
86
126
|
}
|
|
@@ -56,7 +56,7 @@ function createIndexingSpinner(root, label = "Indexing files...", options = {})
|
|
|
56
56
|
info.filePath.startsWith("Processing") ||
|
|
57
57
|
info.filePath.startsWith("Checking for changes"))) {
|
|
58
58
|
spinner.text = info.filePath;
|
|
59
|
-
if (process.env.
|
|
59
|
+
if (process.env.GMAX_DEBUG_INDEX === "1") {
|
|
60
60
|
console.log(`[progress] ${info.filePath}`);
|
|
61
61
|
}
|
|
62
62
|
return;
|
package/dist/lib/index/syncer.js
CHANGED
|
@@ -57,10 +57,11 @@ const meta_cache_1 = require("../store/meta-cache");
|
|
|
57
57
|
const vector_db_1 = require("../store/vector-db");
|
|
58
58
|
const file_utils_1 = require("../utils/file-utils");
|
|
59
59
|
const lock_1 = require("../utils/lock");
|
|
60
|
+
const project_registry_1 = require("../utils/project-registry");
|
|
60
61
|
const project_root_1 = require("../utils/project-root");
|
|
61
62
|
const pool_1 = require("../workers/pool");
|
|
62
|
-
const walker_1 = require("./walker");
|
|
63
63
|
const index_config_1 = require("./index-config");
|
|
64
|
+
const walker_1 = require("./walker");
|
|
64
65
|
function flushBatch(db, meta, vectors, pendingMeta, pendingDeletes, dryRun) {
|
|
65
66
|
return __awaiter(this, void 0, void 0, function* () {
|
|
66
67
|
if (dryRun)
|
|
@@ -87,6 +88,16 @@ function createNoopMetaCache() {
|
|
|
87
88
|
return new Set(store.keys());
|
|
88
89
|
});
|
|
89
90
|
},
|
|
91
|
+
getKeysWithPrefix(prefix) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const keys = new Set();
|
|
94
|
+
for (const k of store.keys()) {
|
|
95
|
+
if (k.startsWith(prefix))
|
|
96
|
+
keys.add(k);
|
|
97
|
+
}
|
|
98
|
+
return keys;
|
|
99
|
+
});
|
|
100
|
+
},
|
|
90
101
|
put: (filePath, entry) => {
|
|
91
102
|
store.set(filePath, entry);
|
|
92
103
|
},
|
|
@@ -101,15 +112,20 @@ function initialSync(options) {
|
|
|
101
112
|
var _a, e_1, _b, _c;
|
|
102
113
|
const { projectRoot, dryRun = false, reset = false, onProgress, signal, } = options;
|
|
103
114
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
115
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
116
|
+
// Path prefix for scoping — all absolute paths for this project start with this
|
|
117
|
+
const rootPrefix = resolvedRoot.endsWith("/")
|
|
118
|
+
? resolvedRoot
|
|
119
|
+
: `${resolvedRoot}/`;
|
|
104
120
|
// Propagate project root to worker processes
|
|
105
|
-
process.env.
|
|
121
|
+
process.env.GMAX_PROJECT_ROOT = paths.root;
|
|
106
122
|
let lock = null;
|
|
107
123
|
const vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
108
124
|
const treatAsEmptyCache = reset && dryRun;
|
|
109
125
|
let metaCache = null;
|
|
110
126
|
try {
|
|
111
127
|
if (!dryRun) {
|
|
112
|
-
lock = yield (0, lock_1.acquireWriterLockWithRetry)(paths.
|
|
128
|
+
lock = yield (0, lock_1.acquireWriterLockWithRetry)(paths.dataDir);
|
|
113
129
|
// Open MetaCache only after lock is acquired
|
|
114
130
|
metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
115
131
|
}
|
|
@@ -117,33 +133,28 @@ function initialSync(options) {
|
|
|
117
133
|
metaCache = createNoopMetaCache();
|
|
118
134
|
}
|
|
119
135
|
if (!dryRun) {
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
const isInconsistent = (hasRows && !hasMeta) || (!hasRows && hasMeta);
|
|
136
|
+
// Scope checks to this project's paths only
|
|
137
|
+
const projectKeys = yield metaCache.getKeysWithPrefix(rootPrefix);
|
|
123
138
|
const modelChanged = (0, index_config_1.checkModelMismatch)(paths.configPath);
|
|
124
|
-
if (reset ||
|
|
139
|
+
if (reset || modelChanged) {
|
|
125
140
|
if (modelChanged) {
|
|
126
141
|
const stored = (0, index_config_1.readIndexConfig)(paths.configPath);
|
|
127
142
|
console.warn(`[syncer] Embedding model changed: ${stored === null || stored === void 0 ? void 0 : stored.embedModel} → ${config_1.MODEL_IDS.embed}. Forcing full re-index.`);
|
|
128
143
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
metaCache.close();
|
|
134
|
-
try {
|
|
135
|
-
fs.rmSync(paths.lmdbPath, { force: true });
|
|
144
|
+
// Only delete this project's data from the centralized store
|
|
145
|
+
yield vectorDb.deletePathsWithPrefix(rootPrefix);
|
|
146
|
+
for (const key of projectKeys) {
|
|
147
|
+
metaCache.delete(key);
|
|
136
148
|
}
|
|
137
|
-
catch (_d) { }
|
|
138
|
-
metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
139
149
|
}
|
|
140
150
|
}
|
|
141
151
|
let total = 0;
|
|
142
152
|
onProgress === null || onProgress === void 0 ? void 0 : onProgress({ processed: 0, indexed: 0, total, filePath: "Scanning..." });
|
|
143
153
|
const pool = (0, pool_1.getWorkerPool)();
|
|
154
|
+
// Get only this project's cached paths (scoped by prefix)
|
|
144
155
|
const cachedPaths = dryRun || treatAsEmptyCache
|
|
145
156
|
? new Set()
|
|
146
|
-
: yield metaCache.
|
|
157
|
+
: yield metaCache.getKeysWithPrefix(rootPrefix);
|
|
147
158
|
const seenPaths = new Set();
|
|
148
159
|
const visitedRealPaths = new Set();
|
|
149
160
|
const batch = [];
|
|
@@ -196,12 +207,12 @@ function initialSync(options) {
|
|
|
196
207
|
yield flushLock;
|
|
197
208
|
});
|
|
198
209
|
const isTimeoutError = (err) => { var _a; return err instanceof Error && ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("timed out")); };
|
|
199
|
-
const processFileWithRetry = (
|
|
210
|
+
const processFileWithRetry = (absPath) => __awaiter(this, void 0, void 0, function* () {
|
|
200
211
|
let retries = 0;
|
|
201
212
|
while (true) {
|
|
202
213
|
try {
|
|
203
214
|
return yield pool.processFile({
|
|
204
|
-
path:
|
|
215
|
+
path: absPath,
|
|
205
216
|
absolutePath: absPath,
|
|
206
217
|
});
|
|
207
218
|
}
|
|
@@ -227,18 +238,16 @@ function initialSync(options) {
|
|
|
227
238
|
}
|
|
228
239
|
});
|
|
229
240
|
try {
|
|
230
|
-
for (var
|
|
231
|
-
additionalPatterns: ["**/.git/**", "**/.osgrep/**"],
|
|
232
|
-
})),
|
|
233
|
-
_c =
|
|
234
|
-
|
|
241
|
+
for (var _d = true, _e = __asyncValues((0, walker_1.walk)(paths.root, {
|
|
242
|
+
additionalPatterns: ["**/.git/**", "**/.gmax/**", "**/.osgrep/**"],
|
|
243
|
+
})), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
244
|
+
_c = _f.value;
|
|
245
|
+
_d = false;
|
|
235
246
|
const relPath = _c;
|
|
236
247
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
237
248
|
shouldSkipCleanup = true;
|
|
238
249
|
break;
|
|
239
250
|
}
|
|
240
|
-
// Walker yields relative paths
|
|
241
|
-
// if (ignoreFilter.ignores(relPath)) continue; // Handled by walker
|
|
242
251
|
const absPath = path.join(paths.root, relPath);
|
|
243
252
|
// Check real path to avoid duplicates and loops
|
|
244
253
|
try {
|
|
@@ -247,7 +256,7 @@ function initialSync(options) {
|
|
|
247
256
|
continue;
|
|
248
257
|
visitedRealPaths.add(realPath);
|
|
249
258
|
}
|
|
250
|
-
catch (
|
|
259
|
+
catch (_g) {
|
|
251
260
|
// Skip broken symlinks or inaccessible files
|
|
252
261
|
continue;
|
|
253
262
|
}
|
|
@@ -263,18 +272,19 @@ function initialSync(options) {
|
|
|
263
272
|
if (!(0, file_utils_1.isIndexableFile)(absPath, stats.size)) {
|
|
264
273
|
return;
|
|
265
274
|
}
|
|
275
|
+
// Use absolute path as the key for MetaCache
|
|
266
276
|
const cached = treatAsEmptyCache
|
|
267
277
|
? undefined
|
|
268
|
-
: metaCache.get(
|
|
278
|
+
: metaCache.get(absPath);
|
|
269
279
|
if (cached &&
|
|
270
280
|
cached.mtimeMs === stats.mtimeMs &&
|
|
271
281
|
cached.size === stats.size) {
|
|
272
282
|
processed += 1;
|
|
273
|
-
seenPaths.add(
|
|
283
|
+
seenPaths.add(absPath);
|
|
274
284
|
markProgress(relPath);
|
|
275
285
|
return;
|
|
276
286
|
}
|
|
277
|
-
const result = yield processFileWithRetry(
|
|
287
|
+
const result = yield processFileWithRetry(absPath);
|
|
278
288
|
const metaEntry = {
|
|
279
289
|
hash: result.hash,
|
|
280
290
|
mtimeMs: result.mtimeMs,
|
|
@@ -282,41 +292,41 @@ function initialSync(options) {
|
|
|
282
292
|
};
|
|
283
293
|
if (result.shouldDelete) {
|
|
284
294
|
if (!dryRun) {
|
|
285
|
-
pendingDeletes.add(
|
|
286
|
-
pendingMeta.set(
|
|
295
|
+
pendingDeletes.add(absPath);
|
|
296
|
+
pendingMeta.set(absPath, metaEntry);
|
|
287
297
|
yield flush(false);
|
|
288
298
|
}
|
|
289
299
|
processed += 1;
|
|
290
|
-
seenPaths.add(
|
|
300
|
+
seenPaths.add(absPath);
|
|
291
301
|
markProgress(relPath);
|
|
292
302
|
return;
|
|
293
303
|
}
|
|
294
304
|
if (cached && cached.hash === result.hash) {
|
|
295
305
|
if (!dryRun) {
|
|
296
|
-
metaCache.put(
|
|
306
|
+
metaCache.put(absPath, metaEntry);
|
|
297
307
|
}
|
|
298
308
|
processed += 1;
|
|
299
|
-
seenPaths.add(
|
|
309
|
+
seenPaths.add(absPath);
|
|
300
310
|
markProgress(relPath);
|
|
301
311
|
return;
|
|
302
312
|
}
|
|
303
313
|
if (dryRun) {
|
|
304
314
|
processed += 1;
|
|
305
315
|
indexed += 1;
|
|
306
|
-
seenPaths.add(
|
|
316
|
+
seenPaths.add(absPath);
|
|
307
317
|
markProgress(relPath);
|
|
308
318
|
return;
|
|
309
319
|
}
|
|
310
|
-
pendingDeletes.add(
|
|
320
|
+
pendingDeletes.add(absPath);
|
|
311
321
|
if (result.vectors.length > 0) {
|
|
312
322
|
batch.push(...result.vectors);
|
|
313
|
-
pendingMeta.set(
|
|
323
|
+
pendingMeta.set(absPath, metaEntry);
|
|
314
324
|
indexed += 1;
|
|
315
325
|
}
|
|
316
326
|
else {
|
|
317
|
-
pendingMeta.set(
|
|
327
|
+
pendingMeta.set(absPath, metaEntry);
|
|
318
328
|
}
|
|
319
|
-
seenPaths.add(
|
|
329
|
+
seenPaths.add(absPath);
|
|
320
330
|
processed += 1;
|
|
321
331
|
markProgress(relPath);
|
|
322
332
|
yield flush(false);
|
|
@@ -325,10 +335,10 @@ function initialSync(options) {
|
|
|
325
335
|
const code = err === null || err === void 0 ? void 0 : err.code;
|
|
326
336
|
if (code === "ENOENT") {
|
|
327
337
|
// Treat missing files as deletions.
|
|
328
|
-
pendingDeletes.add(
|
|
329
|
-
pendingMeta.delete(
|
|
338
|
+
pendingDeletes.add(absPath);
|
|
339
|
+
pendingMeta.delete(absPath);
|
|
330
340
|
if (!dryRun) {
|
|
331
|
-
metaCache.delete(
|
|
341
|
+
metaCache.delete(absPath);
|
|
332
342
|
}
|
|
333
343
|
processed += 1;
|
|
334
344
|
markProgress(relPath);
|
|
@@ -337,7 +347,7 @@ function initialSync(options) {
|
|
|
337
347
|
}
|
|
338
348
|
failedFiles += 1;
|
|
339
349
|
processed += 1;
|
|
340
|
-
seenPaths.add(
|
|
350
|
+
seenPaths.add(absPath);
|
|
341
351
|
console.error(`[sync] Failed to process ${relPath}:`, err);
|
|
342
352
|
markProgress(relPath);
|
|
343
353
|
}
|
|
@@ -347,7 +357,7 @@ function initialSync(options) {
|
|
|
347
357
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
348
358
|
finally {
|
|
349
359
|
try {
|
|
350
|
-
if (!
|
|
360
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
351
361
|
}
|
|
352
362
|
finally { if (e_1) throw e_1.error; }
|
|
353
363
|
}
|
|
@@ -370,6 +380,7 @@ function initialSync(options) {
|
|
|
370
380
|
});
|
|
371
381
|
yield vectorDb.createFTSIndex();
|
|
372
382
|
}
|
|
383
|
+
// Stale cleanup: only remove paths scoped to this project's root
|
|
373
384
|
const stale = Array.from(cachedPaths).filter((p) => !seenPaths.has(p));
|
|
374
385
|
if (!dryRun && stale.length > 0 && !shouldSkipCleanup) {
|
|
375
386
|
yield vectorDb.deletePaths(stale);
|
|
@@ -380,6 +391,17 @@ function initialSync(options) {
|
|
|
380
391
|
// Write model config so future runs can detect model changes
|
|
381
392
|
if (!dryRun) {
|
|
382
393
|
(0, index_config_1.writeIndexConfig)(paths.configPath);
|
|
394
|
+
// Register project in global registry
|
|
395
|
+
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
396
|
+
(0, project_registry_1.registerProject)({
|
|
397
|
+
root: paths.root,
|
|
398
|
+
name: path.basename(paths.root),
|
|
399
|
+
vectorDim: globalConfig.vectorDim,
|
|
400
|
+
modelTier: globalConfig.modelTier,
|
|
401
|
+
embedMode: globalConfig.embedMode,
|
|
402
|
+
lastIndexed: new Date().toISOString(),
|
|
403
|
+
chunkCount: indexed,
|
|
404
|
+
});
|
|
383
405
|
}
|
|
384
406
|
// Finalize total so callers can display a meaningful summary.
|
|
385
407
|
total = processed;
|
package/dist/lib/index/walker.js
CHANGED
|
@@ -86,7 +86,7 @@ function getIgnoreFilter(dir, ignoreFiles) {
|
|
|
86
86
|
filter = (0, ignore_1.default)();
|
|
87
87
|
filter.add(content);
|
|
88
88
|
}
|
|
89
|
-
catch (
|
|
89
|
+
catch (_err) {
|
|
90
90
|
// Ignore missing files
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -95,7 +95,7 @@ function getIgnoreFilter(dir, ignoreFiles) {
|
|
|
95
95
|
}
|
|
96
96
|
function walk(rootDir_1) {
|
|
97
97
|
return __asyncGenerator(this, arguments, function* walk_1(rootDir, options = {}) {
|
|
98
|
-
const ignoreFiles = options.ignoreFiles || [".gitignore", ".
|
|
98
|
+
const ignoreFiles = options.ignoreFiles || [".gitignore", ".gmaxignore"];
|
|
99
99
|
const rootParams = (0, ignore_1.default)().add(ignore_patterns_1.DEFAULT_IGNORE_PATTERNS);
|
|
100
100
|
if (options.additionalPatterns) {
|
|
101
101
|
rootParams.add(options.additionalPatterns);
|
|
@@ -125,7 +125,7 @@ function _walk(currentDir, rootDir, stack, ignoreFiles) {
|
|
|
125
125
|
try {
|
|
126
126
|
entries = yield __await(fs.readdir(currentDir, { withFileTypes: true }));
|
|
127
127
|
}
|
|
128
|
-
catch (
|
|
128
|
+
catch (_err) {
|
|
129
129
|
return yield __await(void 0);
|
|
130
130
|
}
|
|
131
131
|
for (const entry of entries) {
|
|
@@ -56,7 +56,7 @@ const pool_1 = require("../workers/pool");
|
|
|
56
56
|
exports.WATCHER_IGNORE_PATTERNS = [
|
|
57
57
|
"**/node_modules/**",
|
|
58
58
|
"**/.git/**",
|
|
59
|
-
"**/.
|
|
59
|
+
"**/.gmax/**",
|
|
60
60
|
"**/dist/**",
|
|
61
61
|
"**/build/**",
|
|
62
62
|
"**/out/**",
|
|
@@ -66,12 +66,12 @@ exports.WATCHER_IGNORE_PATTERNS = [
|
|
|
66
66
|
"**/venv/**",
|
|
67
67
|
"**/.next/**",
|
|
68
68
|
"**/lancedb/**",
|
|
69
|
-
/(^|[
|
|
69
|
+
/(^|[/\\])\../, // dotfiles
|
|
70
70
|
];
|
|
71
71
|
const DEBOUNCE_MS = 2000;
|
|
72
72
|
const FTS_REBUILD_INTERVAL_MS = 5 * 60 * 1000;
|
|
73
73
|
function startWatcher(opts) {
|
|
74
|
-
const { projectRoot, vectorDb, metaCache,
|
|
74
|
+
const { projectRoot, vectorDb, metaCache, dataDir, onReindex } = opts;
|
|
75
75
|
const pending = new Map();
|
|
76
76
|
let debounceTimer = null;
|
|
77
77
|
let processing = false;
|
|
@@ -104,7 +104,7 @@ function startWatcher(opts) {
|
|
|
104
104
|
const start = Date.now();
|
|
105
105
|
let reindexed = 0;
|
|
106
106
|
try {
|
|
107
|
-
const lock = yield (0, lock_1.acquireWriterLockWithRetry)(
|
|
107
|
+
const lock = yield (0, lock_1.acquireWriterLockWithRetry)(dataDir, {
|
|
108
108
|
maxRetries: 3,
|
|
109
109
|
retryDelayMs: 500,
|
|
110
110
|
});
|
|
@@ -143,7 +143,7 @@ function formatTrace(graph) {
|
|
|
143
143
|
lines.push("");
|
|
144
144
|
}
|
|
145
145
|
// 2. Center (The Symbol)
|
|
146
|
-
lines.push(style.bold(
|
|
146
|
+
lines.push(style.bold(`▶ ${graph.center.symbol}`));
|
|
147
147
|
lines.push(` ${style.dim(`Defined in ${graph.center.file}:${graph.center.line}`)}`);
|
|
148
148
|
lines.push(` ${style.dim(`Role: ${graph.center.role}`)}`);
|
|
149
149
|
lines.push("");
|
|
@@ -137,7 +137,7 @@ class Searcher {
|
|
|
137
137
|
// Item 6: Anchors are recall helpers, not rank contenders
|
|
138
138
|
if (record.is_anchor) {
|
|
139
139
|
// Minimal penalty to break ties
|
|
140
|
-
const anchorPenalty = Number.parseFloat((_a = process.env.
|
|
140
|
+
const anchorPenalty = Number.parseFloat((_a = process.env.GMAX_ANCHOR_PENALTY) !== null && _a !== void 0 ? _a : "") || 0.99;
|
|
141
141
|
adjusted *= anchorPenalty;
|
|
142
142
|
}
|
|
143
143
|
else {
|
|
@@ -197,7 +197,7 @@ class Searcher {
|
|
|
197
197
|
const isTestPath = /(^|\/)(__tests__|tests?|specs?|benchmark)(\/|$)/i.test(pathStr) ||
|
|
198
198
|
/\.(test|spec)\.[cm]?[jt]sx?$/i.test(pathStr);
|
|
199
199
|
if (isTestPath) {
|
|
200
|
-
const testPenalty = Number.parseFloat((_c = process.env.
|
|
200
|
+
const testPenalty = Number.parseFloat((_c = process.env.GMAX_TEST_PENALTY) !== null && _c !== void 0 ? _c : "") || 0.5;
|
|
201
201
|
adjusted *= testPenalty;
|
|
202
202
|
}
|
|
203
203
|
if (pathStr.endsWith(".md") ||
|
|
@@ -206,7 +206,7 @@ class Searcher {
|
|
|
206
206
|
pathStr.endsWith(".json") ||
|
|
207
207
|
pathStr.endsWith(".lock") ||
|
|
208
208
|
pathStr.includes("/docs/")) {
|
|
209
|
-
const docPenalty = Number.parseFloat((_d = process.env.
|
|
209
|
+
const docPenalty = Number.parseFloat((_d = process.env.GMAX_DOC_PENALTY) !== null && _d !== void 0 ? _d : "") || 0.6;
|
|
210
210
|
adjusted *= docPenalty; // Downweight docs/data
|
|
211
211
|
}
|
|
212
212
|
// Import-only penalty
|
|
@@ -297,7 +297,7 @@ class Searcher {
|
|
|
297
297
|
// For now, let's just rely on boosting.
|
|
298
298
|
}
|
|
299
299
|
const whereClause = whereClauseParts.length > 0 ? whereClauseParts.join(" AND ") : undefined;
|
|
300
|
-
const envPreK = Number.parseInt((_d = process.env.
|
|
300
|
+
const envPreK = Number.parseInt((_d = process.env.GMAX_PRE_K) !== null && _d !== void 0 ? _d : "", 10);
|
|
301
301
|
const PRE_RERANK_K = Number.isFinite(envPreK) && envPreK > 0
|
|
302
302
|
? envPreK
|
|
303
303
|
: Math.max(finalLimit * 5, 500);
|
|
@@ -363,17 +363,17 @@ class Searcher {
|
|
|
363
363
|
.filter(Boolean);
|
|
364
364
|
// Item 8: Widen PRE_RERANK_K
|
|
365
365
|
// Retrieve a wide set for Stage 1 filtering
|
|
366
|
-
const envStage1 = Number.parseInt((_e = process.env.
|
|
366
|
+
const envStage1 = Number.parseInt((_e = process.env.GMAX_STAGE1_K) !== null && _e !== void 0 ? _e : "", 10);
|
|
367
367
|
const STAGE1_K = Number.isFinite(envStage1) && envStage1 > 0 ? envStage1 : 200;
|
|
368
368
|
const topCandidates = fused.slice(0, STAGE1_K);
|
|
369
369
|
// Item 9: Two-stage rerank
|
|
370
370
|
// Stage 1: Cheap pooled cosine filter
|
|
371
371
|
let stage2Candidates = topCandidates;
|
|
372
|
-
const envStage2K = Number.parseInt((_f = process.env.
|
|
372
|
+
const envStage2K = Number.parseInt((_f = process.env.GMAX_STAGE2_K) !== null && _f !== void 0 ? _f : "", 10);
|
|
373
373
|
const STAGE2_K = Number.isFinite(envStage2K) && envStage2K > 0 ? envStage2K : 40;
|
|
374
|
-
const envRerankTop = Number.parseInt((_g = process.env.
|
|
374
|
+
const envRerankTop = Number.parseInt((_g = process.env.GMAX_RERANK_TOP) !== null && _g !== void 0 ? _g : "", 10);
|
|
375
375
|
const RERANK_TOP = Number.isFinite(envRerankTop) && envRerankTop > 0 ? envRerankTop : 20;
|
|
376
|
-
const envBlend = Number.parseFloat((_h = process.env.
|
|
376
|
+
const envBlend = Number.parseFloat((_h = process.env.GMAX_RERANK_BLEND) !== null && _h !== void 0 ? _h : "");
|
|
377
377
|
const FUSED_WEIGHT = Number.isFinite(envBlend) && envBlend >= 0 ? envBlend : 0.5;
|
|
378
378
|
if (queryPooled && topCandidates.length > STAGE2_K) {
|
|
379
379
|
const cosineScores = topCandidates.map((doc) => {
|
|
@@ -439,7 +439,7 @@ class Searcher {
|
|
|
439
439
|
// Item 10: Per-file diversification
|
|
440
440
|
const seenFiles = new Map();
|
|
441
441
|
const diversified = [];
|
|
442
|
-
const envMaxPerFile = Number.parseInt((_j = process.env.
|
|
442
|
+
const envMaxPerFile = Number.parseInt((_j = process.env.GMAX_MAX_PER_FILE) !== null && _j !== void 0 ? _j : "", 10);
|
|
443
443
|
const MAX_PER_FILE = Number.isFinite(envMaxPerFile) && envMaxPerFile > 0 ? envMaxPerFile : 3;
|
|
444
444
|
for (const item of uniqueScored) {
|
|
445
445
|
const path = item.record.path || "";
|
|
@@ -50,9 +50,9 @@ const path = __importStar(require("node:path"));
|
|
|
50
50
|
const node_worker_threads_1 = require("node:worker_threads");
|
|
51
51
|
const config_1 = require("../../config");
|
|
52
52
|
const HOMEDIR = os.homedir();
|
|
53
|
-
const CACHE_DIR = path.join(HOMEDIR, ".
|
|
54
|
-
const LOG_MODELS = process.env.
|
|
55
|
-
process.env.
|
|
53
|
+
const CACHE_DIR = path.join(HOMEDIR, ".gmax", "models");
|
|
54
|
+
const LOG_MODELS = process.env.GMAX_DEBUG_MODELS === "1" ||
|
|
55
|
+
process.env.GMAX_DEBUG_MODELS === "true";
|
|
56
56
|
/**
|
|
57
57
|
* Triggers the download of models by spawning a worker thread.
|
|
58
58
|
* This prevents the main thread from loading onnxruntime, avoiding exit crashes.
|
|
@@ -58,7 +58,7 @@ function getPaths() {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
* Idempotent helper that ensures
|
|
61
|
+
* Idempotent helper that ensures gmax directories and models exist.
|
|
62
62
|
* Returns status about work performed so callers can decide what to show.
|
|
63
63
|
*/
|
|
64
64
|
function ensureSetup() {
|
|
@@ -67,9 +67,7 @@ function ensureSetup() {
|
|
|
67
67
|
const dirs = [paths.root, paths.models, paths.grammars];
|
|
68
68
|
const needsDirs = dirs.some((dir) => !fs.existsSync(dir));
|
|
69
69
|
let createdDirs = false;
|
|
70
|
-
const dirSpinner = !silent && needsDirs
|
|
71
|
-
? (0, ora_1.default)("Preparing osgrep directories...").start()
|
|
72
|
-
: null;
|
|
70
|
+
const dirSpinner = !silent && needsDirs ? (0, ora_1.default)("Preparing gmax directories...").start() : null;
|
|
73
71
|
try {
|
|
74
72
|
if (needsDirs) {
|
|
75
73
|
dirs.forEach((dir) => {
|
|
@@ -114,6 +114,18 @@ exports.BODY_FIELDS = {
|
|
|
114
114
|
class_declaration: null, // Container (class/interface/enum)
|
|
115
115
|
object_declaration: null, // Container (object/companion)
|
|
116
116
|
},
|
|
117
|
+
bash: {
|
|
118
|
+
function_definition: "body", // positional fallback → compound_statement
|
|
119
|
+
},
|
|
120
|
+
scala: {
|
|
121
|
+
function_definition: "body", // positional — expression bodies
|
|
122
|
+
class_definition: null, // Container
|
|
123
|
+
object_definition: null, // Container
|
|
124
|
+
trait_definition: null, // Container
|
|
125
|
+
},
|
|
126
|
+
lua: {
|
|
127
|
+
function_declaration: "body", // named "body" field → block
|
|
128
|
+
},
|
|
117
129
|
};
|
|
118
130
|
/**
|
|
119
131
|
* Container types - these hold methods/functions but shouldn't be elided themselves.
|
|
@@ -139,6 +151,14 @@ exports.CONTAINER_TYPES = {
|
|
|
139
151
|
"protocol_body",
|
|
140
152
|
],
|
|
141
153
|
kotlin: ["class_declaration", "class_body", "object_declaration"],
|
|
154
|
+
bash: [],
|
|
155
|
+
scala: [
|
|
156
|
+
"class_definition",
|
|
157
|
+
"object_definition",
|
|
158
|
+
"trait_definition",
|
|
159
|
+
"template_body",
|
|
160
|
+
],
|
|
161
|
+
lua: [],
|
|
142
162
|
};
|
|
143
163
|
/**
|
|
144
164
|
* Check if a node type is a container (holds methods).
|
|
@@ -205,7 +205,11 @@ class Skeletonizer {
|
|
|
205
205
|
// Try named field first, then fall back to finding child by type
|
|
206
206
|
// (some grammars like Kotlin use positional children, not named fields)
|
|
207
207
|
const bodyNode = ((_a = node.childForFieldName) === null || _a === void 0 ? void 0 : _a.call(node, bodyFieldName)) ||
|
|
208
|
-
(node.namedChildren || []).find((c) => c.type === "function_body" ||
|
|
208
|
+
(node.namedChildren || []).find((c) => c.type === "function_body" ||
|
|
209
|
+
c.type === "compound_statement" ||
|
|
210
|
+
c.type === "block" ||
|
|
211
|
+
c.type === "template_body" ||
|
|
212
|
+
c.type === bodyFieldName) ||
|
|
209
213
|
null;
|
|
210
214
|
if (bodyNode) {
|
|
211
215
|
const summary = this.createSummary(bodyNode, langId, opts);
|
|
@@ -359,7 +363,9 @@ class Skeletonizer {
|
|
|
359
363
|
const seen = new Set();
|
|
360
364
|
const extract = (n) => {
|
|
361
365
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
362
|
-
if (n.type === "call_expression" ||
|
|
366
|
+
if (n.type === "call_expression" ||
|
|
367
|
+
n.type === "call" ||
|
|
368
|
+
n.type === "function_call") {
|
|
363
369
|
const func = (_a = n.childForFieldName) === null || _a === void 0 ? void 0 : _a.call(n, "function");
|
|
364
370
|
if (func) {
|
|
365
371
|
let funcName = func.text;
|
|
@@ -60,7 +60,6 @@ function getCommentPrefix(style) {
|
|
|
60
60
|
return "#";
|
|
61
61
|
case "dash":
|
|
62
62
|
return "--";
|
|
63
|
-
case "slash":
|
|
64
63
|
default:
|
|
65
64
|
return "//";
|
|
66
65
|
}
|
|
@@ -85,8 +84,6 @@ function getCommentStyle(langId) {
|
|
|
85
84
|
* Format the skeleton file header comment.
|
|
86
85
|
*/
|
|
87
86
|
function formatSkeletonHeader(filePath, tokenEstimate, langId) {
|
|
88
|
-
const prefix = langId
|
|
89
|
-
? getCommentPrefix(getCommentStyle(langId))
|
|
90
|
-
: "//";
|
|
87
|
+
const prefix = langId ? getCommentPrefix(getCommentStyle(langId)) : "//";
|
|
91
88
|
return `${prefix} ${filePath} (skeleton, ~${tokenEstimate} tokens)`;
|
|
92
89
|
}
|