grepmax 0.26.27 → 0.26.28
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 +40 -0
- package/dist/commands/audit.js +28 -260
- package/dist/commands/context.js +42 -26
- package/dist/commands/dead.js +37 -35
- package/dist/commands/doctor.js +280 -10
- package/dist/commands/extract.js +79 -49
- package/dist/commands/impact.js +74 -20
- package/dist/commands/log.js +25 -25
- package/dist/commands/mcp.js +622 -474
- package/dist/commands/peek.js +49 -47
- package/dist/commands/project.js +29 -107
- package/dist/commands/related.js +43 -52
- package/dist/commands/search-run.js +23 -8
- package/dist/commands/search.js +7 -2
- package/dist/commands/similar.js +50 -76
- package/dist/commands/skeleton.js +137 -83
- package/dist/commands/status.js +90 -25
- package/dist/commands/surprises.js +73 -16
- package/dist/commands/symbols.js +58 -77
- package/dist/commands/test-find.js +51 -17
- package/dist/commands/trace.js +26 -11
- package/dist/lib/daemon/daemon.js +26 -0
- package/dist/lib/daemon/graph-handler.js +853 -0
- package/dist/lib/daemon/ipc-handler.js +19 -2
- package/dist/lib/daemon/read-verbs.js +105 -0
- package/dist/lib/daemon/rows-handler.js +665 -0
- package/dist/lib/daemon/vector-handler.js +199 -0
- package/dist/lib/utils/daemon-client.js +11 -0
- package/dist/lib/utils/store-access.js +381 -0
- package/dist/lib/utils/tests-footer.js +19 -2
- package/dist/lib/utils/watcher-launcher.js +10 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/similar.js
CHANGED
|
@@ -44,16 +44,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.similar = void 0;
|
|
46
46
|
const commander_1 = require("commander");
|
|
47
|
-
const
|
|
48
|
-
const
|
|
47
|
+
const rows_handler_1 = require("../lib/daemon/rows-handler");
|
|
48
|
+
const vector_handler_1 = require("../lib/daemon/vector-handler");
|
|
49
49
|
const agent_errors_1 = require("../lib/utils/agent-errors");
|
|
50
|
-
const
|
|
50
|
+
const daemon_client_1 = require("../lib/utils/daemon-client");
|
|
51
51
|
const exit_1 = require("../lib/utils/exit");
|
|
52
|
-
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
53
52
|
const path_containment_1 = require("../lib/utils/path-containment");
|
|
54
53
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
55
54
|
const project_root_1 = require("../lib/utils/project-root");
|
|
56
55
|
const stale_hint_1 = require("../lib/utils/stale-hint");
|
|
56
|
+
const store_access_1 = require("../lib/utils/store-access");
|
|
57
57
|
exports.similar = new commander_1.Command("similar")
|
|
58
58
|
.description("Find semantically similar code to a symbol or file")
|
|
59
59
|
.argument("<target>", "Symbol name or file path")
|
|
@@ -67,7 +67,6 @@ exports.similar = new commander_1.Command("similar")
|
|
|
67
67
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
68
68
|
const limit = Math.min(Math.max(Number.parseInt(opts.maxCount || "5", 10), 1), 25);
|
|
69
69
|
const threshold = Number.parseFloat(opts.threshold || "0") || 0;
|
|
70
|
-
let vectorDb = null;
|
|
71
70
|
try {
|
|
72
71
|
const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
|
|
73
72
|
if (root === null)
|
|
@@ -76,78 +75,57 @@ exports.similar = new commander_1.Command("similar")
|
|
|
76
75
|
(0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
|
|
77
76
|
(0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
|
|
78
77
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
79
|
-
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
80
|
-
const table = yield vectorDb.ensureTable();
|
|
81
78
|
const isFile = target.includes("/") || (target.includes(".") && !target.includes(" "));
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
// Resolved here, not daemon-side: verifyExistingTarget looks at this
|
|
80
|
+
// process's filesystem, and a path outside the project must fail with the
|
|
81
|
+
// containment error the user expects before anything reaches the store.
|
|
82
|
+
const absPath = isFile
|
|
83
|
+
? (0, path_containment_1.resolveContainedPath)(projectRoot, target, {
|
|
86
84
|
verifyExistingTarget: true,
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
85
|
+
})
|
|
86
|
+
: undefined;
|
|
87
|
+
const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
|
|
88
|
+
const scope = resolveScope({
|
|
89
|
+
projectRoot,
|
|
90
|
+
in: opts.in,
|
|
91
|
+
exclude: opts.exclude,
|
|
92
|
+
});
|
|
93
|
+
// The source chunk's 384-float vector never leaves the store: the daemon
|
|
94
|
+
// looks it up, runs the vector search, and returns the ranked chunks.
|
|
95
|
+
const result = yield (0, store_access_1.withStoreRead)("similar", {
|
|
96
|
+
daemon: () => (0, daemon_client_1.sendDaemonCommand)({
|
|
97
|
+
cmd: "vector.similar",
|
|
98
|
+
projectRoot,
|
|
99
|
+
absPath,
|
|
100
|
+
symbol: isFile ? undefined : target,
|
|
101
|
+
scope: (0, rows_handler_1.scopeToWire)(scope),
|
|
102
|
+
limit,
|
|
103
|
+
threshold,
|
|
104
|
+
}, { timeoutMs: 60000 }),
|
|
105
|
+
render: (resp) => resp,
|
|
106
|
+
inProcess: () => (0, rows_handler_1.withLocalStore)(paths.lancedbDir, (deps) => (0, vector_handler_1.runSimilar)(deps, {
|
|
107
|
+
projectRoot,
|
|
108
|
+
absPath,
|
|
109
|
+
symbol: isFile ? undefined : target,
|
|
110
|
+
scope,
|
|
111
|
+
limit,
|
|
112
|
+
threshold,
|
|
113
|
+
})),
|
|
114
|
+
fallbackOnUnknownVerb: true,
|
|
115
|
+
});
|
|
116
|
+
if (result.status === "not-found") {
|
|
104
117
|
console.log((isFile
|
|
105
118
|
? (0, agent_errors_1.fileNotFoundLines)(target, { agent: opts.agent })
|
|
106
119
|
: (0, agent_errors_1.symbolNotFoundLines)(target, { agent: opts.agent })).join("\n"));
|
|
107
120
|
process.exitCode = 1;
|
|
108
121
|
return;
|
|
109
122
|
}
|
|
110
|
-
|
|
111
|
-
const sourceVector = source.vector;
|
|
112
|
-
const sourcePath = String(source.path || "");
|
|
113
|
-
if (!sourceVector || sourceVector.length === 0) {
|
|
123
|
+
if (result.status === "no-vector") {
|
|
114
124
|
console.log("Source chunk has no embedding vector.");
|
|
115
125
|
process.exitCode = 1;
|
|
116
126
|
return;
|
|
117
127
|
}
|
|
118
|
-
|
|
119
|
-
const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
|
|
120
|
-
const scope = resolveScope({
|
|
121
|
-
projectRoot,
|
|
122
|
-
in: opts.in,
|
|
123
|
-
exclude: opts.exclude,
|
|
124
|
-
});
|
|
125
|
-
const pathScope = buildScopeWhere(scope);
|
|
126
|
-
const results = yield (0, ann_config_1.configureAnnVectorQuery)(table.vectorSearch(sourceVector))
|
|
127
|
-
.select([
|
|
128
|
-
"path",
|
|
129
|
-
"start_line",
|
|
130
|
-
"end_line",
|
|
131
|
-
"defined_symbols",
|
|
132
|
-
"role",
|
|
133
|
-
"content",
|
|
134
|
-
"_distance",
|
|
135
|
-
])
|
|
136
|
-
.where(pathScope)
|
|
137
|
-
.limit(limit + 5) // fetch extra to account for self-filtering
|
|
138
|
-
.toArray();
|
|
139
|
-
// Filter out self and apply threshold
|
|
140
|
-
const filtered = results.filter((r) => {
|
|
141
|
-
if (r.path === sourcePath && r.start_line === source.start_line)
|
|
142
|
-
return false;
|
|
143
|
-
if (threshold > 0) {
|
|
144
|
-
// LanceDB returns L2 distance; convert to similarity
|
|
145
|
-
const sim = 1 / (1 + (r._distance || 0));
|
|
146
|
-
if (sim < threshold)
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
return true;
|
|
150
|
-
});
|
|
128
|
+
const filtered = result.results;
|
|
151
129
|
if (filtered.length === 0) {
|
|
152
130
|
console.log(`No similar code found for ${target}.`);
|
|
153
131
|
return;
|
|
@@ -155,7 +133,7 @@ exports.similar = new commander_1.Command("similar")
|
|
|
155
133
|
const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
|
156
134
|
if (opts.agent) {
|
|
157
135
|
for (const r of filtered.slice(0, limit)) {
|
|
158
|
-
const sym = (_c = (_b =
|
|
136
|
+
const sym = (_c = (_b = r.defined_symbols) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : "";
|
|
159
137
|
const line = ((_d = r.start_line) !== null && _d !== void 0 ? _d : 0) + 1;
|
|
160
138
|
const role = (r.role || "IMPL").slice(0, 4);
|
|
161
139
|
const dist = ((_e = r._distance) !== null && _e !== void 0 ? _e : 0).toFixed(3);
|
|
@@ -165,7 +143,7 @@ exports.similar = new commander_1.Command("similar")
|
|
|
165
143
|
else {
|
|
166
144
|
console.log(`Code similar to ${target}:\n`);
|
|
167
145
|
for (const r of filtered.slice(0, limit)) {
|
|
168
|
-
const sym = (_g = (_f =
|
|
146
|
+
const sym = (_g = (_f = r.defined_symbols) === null || _f === void 0 ? void 0 : _f[0]) !== null && _g !== void 0 ? _g : "";
|
|
169
147
|
const line = ((_h = r.start_line) !== null && _h !== void 0 ? _h : 0) + 1;
|
|
170
148
|
const role = r.role || "IMPLEMENTATION";
|
|
171
149
|
const dist = ((_j = r._distance) !== null && _j !== void 0 ? _j : 0).toFixed(3);
|
|
@@ -174,17 +152,13 @@ exports.similar = new commander_1.Command("similar")
|
|
|
174
152
|
}
|
|
175
153
|
}
|
|
176
154
|
catch (error) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
155
|
+
if (!(0, store_access_1.reportStoreAccessRefusal)(error)) {
|
|
156
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
157
|
+
console.error("Similar search failed:", msg);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
}
|
|
180
160
|
}
|
|
181
161
|
finally {
|
|
182
|
-
if (vectorDb) {
|
|
183
|
-
try {
|
|
184
|
-
yield vectorDb.close();
|
|
185
|
-
}
|
|
186
|
-
catch (_k) { }
|
|
187
|
-
}
|
|
188
162
|
yield (0, exit_1.gracefulExit)();
|
|
189
163
|
}
|
|
190
164
|
}));
|
|
@@ -54,19 +54,18 @@ exports.skeleton = void 0;
|
|
|
54
54
|
const fs = __importStar(require("node:fs"));
|
|
55
55
|
const commander_1 = require("commander");
|
|
56
56
|
const config_1 = require("../config");
|
|
57
|
+
const rows_handler_1 = require("../lib/daemon/rows-handler");
|
|
57
58
|
const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
58
|
-
const syncer_1 = require("../lib/index/syncer");
|
|
59
59
|
const searcher_1 = require("../lib/search/searcher");
|
|
60
60
|
const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
61
|
-
const retriever_1 = require("../lib/skeleton/retriever");
|
|
62
61
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
63
|
-
const
|
|
62
|
+
const daemon_client_1 = require("../lib/utils/daemon-client");
|
|
64
63
|
const exit_1 = require("../lib/utils/exit");
|
|
65
64
|
const file_utils_1 = require("../lib/utils/file-utils");
|
|
66
|
-
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
67
65
|
const path_containment_1 = require("../lib/utils/path-containment");
|
|
68
66
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
69
67
|
const project_root_1 = require("../lib/utils/project-root");
|
|
68
|
+
const store_access_1 = require("../lib/utils/store-access");
|
|
70
69
|
/**
|
|
71
70
|
* Check if target looks like a file path.
|
|
72
71
|
*/
|
|
@@ -83,34 +82,115 @@ function isSymbolLike(target) {
|
|
|
83
82
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(target) && !target.includes(" ");
|
|
84
83
|
}
|
|
85
84
|
/**
|
|
86
|
-
*
|
|
85
|
+
* `--sync`. With a daemon up this is the existing `ensure-project` streaming
|
|
86
|
+
* verb — the daemon already owns first-run indexing and is the only process
|
|
87
|
+
* that should be writing to the store. `initialSync` runs here only when no
|
|
88
|
+
* daemon is listening.
|
|
87
89
|
*/
|
|
88
|
-
function
|
|
90
|
+
function syncIndex(projectRoot, lancedbDir) {
|
|
89
91
|
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, "Syncing...", { verbose: false });
|
|
93
|
+
yield (0, store_access_1.withStoreRead)("skeleton sync", {
|
|
94
|
+
daemon: () => __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
var _a;
|
|
96
|
+
try {
|
|
97
|
+
const done = yield (0, daemon_client_1.sendStreamingCommand)({ cmd: "ensure-project", root: projectRoot }, (msg) => {
|
|
98
|
+
var _a, _b, _c;
|
|
99
|
+
return onProgress({
|
|
100
|
+
processed: Number((_a = msg.processed) !== null && _a !== void 0 ? _a : 0),
|
|
101
|
+
total: Number((_b = msg.total) !== null && _b !== void 0 ? _b : 0),
|
|
102
|
+
indexed: Number((_c = msg.indexed) !== null && _c !== void 0 ? _c : 0),
|
|
103
|
+
filePath: typeof msg.filePath === "string" ? msg.filePath : "",
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return Object.assign(Object.assign({}, done), { ok: done.ok !== false });
|
|
103
107
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
catch (err) {
|
|
109
|
+
// sendStreamingCommand rejects with the socket errno as the message,
|
|
110
|
+
// which is exactly what the access policy classifies on.
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: (_a = err === null || err === void 0 ? void 0 : err.code) !== null && _a !== void 0 ? _a : err === null || err === void 0 ? void 0 : err.message,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
117
|
+
render: () => {
|
|
118
|
+
spinner.succeed("Sync complete");
|
|
119
|
+
},
|
|
120
|
+
inProcess: () => __awaiter(this, void 0, void 0, function* () {
|
|
121
|
+
const { initialSync } = yield Promise.resolve().then(() => __importStar(require("../lib/index/syncer")));
|
|
122
|
+
const result = yield initialSync({ projectRoot, onProgress });
|
|
123
|
+
if (result.degraded) {
|
|
124
|
+
spinner.warn(`Sync incomplete: ${result.scanErrors} scan error(s), ${result.failedFiles} file failure(s)`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const prefix = projectRoot.endsWith("/")
|
|
128
|
+
? projectRoot
|
|
129
|
+
: `${projectRoot}/`;
|
|
130
|
+
const chunkCount = yield (0, rows_handler_1.withLocalStore)(lancedbDir, (deps) => deps.vectorDb.countRowsForPath(prefix));
|
|
131
|
+
(0, project_registry_1.stampProjectFullSync)({
|
|
132
|
+
root: projectRoot,
|
|
133
|
+
generation: result.generation,
|
|
134
|
+
embedMode: result.embedMode,
|
|
135
|
+
chunkCount,
|
|
136
|
+
chunkerVersion: config_1.CONFIG.CHUNKER_VERSION,
|
|
137
|
+
expectedFingerprint: result.registryExpectation.embeddingFingerprint,
|
|
138
|
+
expectedRebuildId: result.registryExpectation.rebuildId,
|
|
139
|
+
});
|
|
140
|
+
spinner.succeed("Sync complete");
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The stored skeleton for a file, and/or the file that defines a symbol. One
|
|
147
|
+
* verb covers both because symbol mode always follows the lookup with the
|
|
148
|
+
* skeleton fetch.
|
|
149
|
+
*/
|
|
150
|
+
function lookupSkeleton(projectRoot, lancedbDir, req) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
return (0, store_access_1.withStoreRead)("skeleton", {
|
|
153
|
+
daemon: () => (0, daemon_client_1.sendDaemonCommand)({
|
|
154
|
+
cmd: "rows.skeleton",
|
|
155
|
+
projectRoot,
|
|
156
|
+
path: req.path,
|
|
157
|
+
symbol: req.symbol,
|
|
158
|
+
}, { timeoutMs: 30000 }),
|
|
159
|
+
render: (resp) => {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
return ({
|
|
162
|
+
path: ((_a = resp.path) !== null && _a !== void 0 ? _a : null),
|
|
163
|
+
skeleton: ((_b = resp.skeleton) !== null && _b !== void 0 ? _b : null),
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
inProcess: () => (0, rows_handler_1.withLocalStore)(lancedbDir, (deps) => (0, rows_handler_1.runSkeleton)(deps, Object.assign({ projectRoot }, req))),
|
|
167
|
+
fallbackOnUnknownVerb: true,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Query mode's search. Routed through the daemon's `search` verb so the
|
|
173
|
+
* Searcher — and with it the PageRank cache under ~/.gmax/pagerank/ — stays in
|
|
174
|
+
* one process.
|
|
175
|
+
*/
|
|
176
|
+
function searchFiles(projectRoot, lancedbDir, query, limit) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
return (0, store_access_1.withStoreRead)("skeleton search", {
|
|
179
|
+
daemon: () => (0, daemon_client_1.sendDaemonCommand)({
|
|
180
|
+
cmd: "search",
|
|
181
|
+
projectRoot,
|
|
182
|
+
query,
|
|
183
|
+
limit,
|
|
184
|
+
pathPrefix: projectRoot,
|
|
185
|
+
}, { timeoutMs: 60000 }),
|
|
186
|
+
render: (resp) => { var _a; return ((_a = resp.data) !== null && _a !== void 0 ? _a : []); },
|
|
187
|
+
inProcess: () => (0, rows_handler_1.withLocalStore)(lancedbDir, (deps) => __awaiter(this, void 0, void 0, function* () {
|
|
188
|
+
var _a;
|
|
189
|
+
const searcher = new searcher_1.Searcher(deps.vectorDb);
|
|
190
|
+
const results = yield searcher.search(query, limit, {}, {}, `${projectRoot}/`);
|
|
191
|
+
return (_a = results.data) !== null && _a !== void 0 ? _a : [];
|
|
192
|
+
})),
|
|
193
|
+
});
|
|
114
194
|
});
|
|
115
195
|
}
|
|
116
196
|
exports.skeleton = new commander_1.Command("skeleton")
|
|
@@ -129,36 +209,15 @@ Examples:
|
|
|
129
209
|
`)
|
|
130
210
|
.action((target, options, _cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
131
211
|
var _a, _b, _c, _d, _e;
|
|
132
|
-
let vectorDb = null;
|
|
133
212
|
try {
|
|
134
213
|
// Initialize
|
|
135
214
|
yield (0, setup_helpers_1.ensureSetup)();
|
|
136
215
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
|
|
137
216
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
138
|
-
|
|
217
|
+
const lancedbDir = paths.lancedbDir;
|
|
139
218
|
// Sync if requested
|
|
140
219
|
if (options.sync) {
|
|
141
|
-
|
|
142
|
-
const result = yield (0, syncer_1.initialSync)({ projectRoot, onProgress });
|
|
143
|
-
if (result.degraded) {
|
|
144
|
-
spinner.warn(`Sync incomplete: ${result.scanErrors} scan error(s), ${result.failedFiles} file failure(s)`);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
const prefix = projectRoot.endsWith("/")
|
|
148
|
-
? projectRoot
|
|
149
|
-
: `${projectRoot}/`;
|
|
150
|
-
const chunkCount = yield vectorDb.countRowsForPath(prefix);
|
|
151
|
-
(0, project_registry_1.stampProjectFullSync)({
|
|
152
|
-
root: projectRoot,
|
|
153
|
-
generation: result.generation,
|
|
154
|
-
embedMode: result.embedMode,
|
|
155
|
-
chunkCount,
|
|
156
|
-
chunkerVersion: config_1.CONFIG.CHUNKER_VERSION,
|
|
157
|
-
expectedFingerprint: result.registryExpectation.embeddingFingerprint,
|
|
158
|
-
expectedRebuildId: result.registryExpectation.rebuildId,
|
|
159
|
-
});
|
|
160
|
-
spinner.succeed("Sync complete");
|
|
161
|
-
}
|
|
220
|
+
yield syncIndex(projectRoot, lancedbDir);
|
|
162
221
|
}
|
|
163
222
|
// Initialize skeletonizer
|
|
164
223
|
const skeletonizer = new skeletonizer_1.Skeletonizer();
|
|
@@ -216,17 +275,15 @@ Examples:
|
|
|
216
275
|
process.exitCode = 1;
|
|
217
276
|
return;
|
|
218
277
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
278
|
+
// Use absolute path for DB lookup (centralized index stores absolute paths)
|
|
279
|
+
const cached = (yield lookupSkeleton(projectRoot, lancedbDir, { path: filePath })).skeleton;
|
|
280
|
+
if (cached) {
|
|
281
|
+
outputResult({
|
|
282
|
+
success: true,
|
|
283
|
+
skeleton: cached,
|
|
284
|
+
tokenEstimate: Math.ceil(cached.length / 4),
|
|
285
|
+
}, options);
|
|
286
|
+
return;
|
|
230
287
|
}
|
|
231
288
|
const content = (0, file_utils_1.readContainedTextFileSync)(projectRoot, filePath);
|
|
232
289
|
const result = yield skeletonizer.skeletonizeFile(filePath, content, skeletonOpts);
|
|
@@ -234,15 +291,17 @@ Examples:
|
|
|
234
291
|
}
|
|
235
292
|
else if (isSymbolLike(target) && !target.includes(" ")) {
|
|
236
293
|
// === SYMBOL MODE ===
|
|
237
|
-
const
|
|
238
|
-
|
|
294
|
+
const found = yield lookupSkeleton(projectRoot, lancedbDir, {
|
|
295
|
+
symbol: target,
|
|
296
|
+
});
|
|
297
|
+
if (!found.path) {
|
|
239
298
|
console.error(`Symbol not found in index: ${target}`);
|
|
240
299
|
console.error("Try running 'gmax index' first or use a search query.");
|
|
241
300
|
process.exitCode = 1;
|
|
242
301
|
return;
|
|
243
302
|
}
|
|
244
303
|
// filePath from DB is absolute (centralized index)
|
|
245
|
-
const absolutePath = (0, path_containment_1.resolveContainedPath)(projectRoot,
|
|
304
|
+
const absolutePath = (0, path_containment_1.resolveContainedPath)(projectRoot, found.path, {
|
|
246
305
|
verifyExistingTarget: true,
|
|
247
306
|
});
|
|
248
307
|
if (!fs.existsSync(absolutePath)) {
|
|
@@ -250,7 +309,7 @@ Examples:
|
|
|
250
309
|
process.exitCode = 1;
|
|
251
310
|
return;
|
|
252
311
|
}
|
|
253
|
-
const cached =
|
|
312
|
+
const cached = found.skeleton;
|
|
254
313
|
if (cached) {
|
|
255
314
|
outputResult({
|
|
256
315
|
success: true,
|
|
@@ -265,10 +324,9 @@ Examples:
|
|
|
265
324
|
}
|
|
266
325
|
else {
|
|
267
326
|
// === QUERY MODE ===
|
|
268
|
-
const searcher = new searcher_1.Searcher(vectorDb);
|
|
269
327
|
const limit = Math.min(Number.parseInt(options.limit, 10) || 3, 10);
|
|
270
|
-
const
|
|
271
|
-
if (
|
|
328
|
+
const searchData = yield searchFiles(projectRoot, lancedbDir, target, limit);
|
|
329
|
+
if (searchData.length === 0) {
|
|
272
330
|
console.error(`No results found for: ${target}`);
|
|
273
331
|
process.exitCode = 1;
|
|
274
332
|
return;
|
|
@@ -276,7 +334,7 @@ Examples:
|
|
|
276
334
|
// Get unique file paths from results
|
|
277
335
|
const seenPaths = new Set();
|
|
278
336
|
const filePaths = [];
|
|
279
|
-
for (const result of
|
|
337
|
+
for (const result of searchData) {
|
|
280
338
|
const resultPath = (_e = result.metadata) === null || _e === void 0 ? void 0 : _e.path;
|
|
281
339
|
if (resultPath && !seenPaths.has(resultPath)) {
|
|
282
340
|
seenPaths.add(resultPath);
|
|
@@ -314,7 +372,9 @@ Examples:
|
|
|
314
372
|
continue;
|
|
315
373
|
}
|
|
316
374
|
// Try cache first
|
|
317
|
-
const cached = yield (
|
|
375
|
+
const cached = (yield lookupSkeleton(projectRoot, lancedbDir, {
|
|
376
|
+
path: absolutePath,
|
|
377
|
+
})).skeleton;
|
|
318
378
|
if (cached) {
|
|
319
379
|
results.push({
|
|
320
380
|
file: filePath,
|
|
@@ -345,19 +405,13 @@ Examples:
|
|
|
345
405
|
}
|
|
346
406
|
}
|
|
347
407
|
catch (error) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
408
|
+
if (!(0, store_access_1.reportStoreAccessRefusal)(error)) {
|
|
409
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
410
|
+
console.error("Error:", message);
|
|
411
|
+
process.exitCode = 1;
|
|
412
|
+
}
|
|
351
413
|
}
|
|
352
414
|
finally {
|
|
353
|
-
if (vectorDb) {
|
|
354
|
-
try {
|
|
355
|
-
yield vectorDb.close();
|
|
356
|
-
}
|
|
357
|
-
catch (_g) {
|
|
358
|
-
// Ignore close errors
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
415
|
const code = typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
362
416
|
yield (0, exit_1.gracefulExit)(code);
|
|
363
417
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -48,12 +48,87 @@ const commander_1 = require("commander");
|
|
|
48
48
|
const config_1 = require("../config");
|
|
49
49
|
const embedding_status_1 = require("../lib/index/embedding-status");
|
|
50
50
|
const index_config_1 = require("../lib/index/index-config");
|
|
51
|
+
const daemon_client_1 = require("../lib/utils/daemon-client");
|
|
51
52
|
const exit_1 = require("../lib/utils/exit");
|
|
52
53
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
53
54
|
const lock_1 = require("../lib/utils/lock");
|
|
54
55
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
55
56
|
const project_root_1 = require("../lib/utils/project-root");
|
|
57
|
+
const store_access_1 = require("../lib/utils/store-access");
|
|
56
58
|
const watcher_store_1 = require("../lib/utils/watcher-store");
|
|
59
|
+
/**
|
|
60
|
+
* Ask the daemon first. Both verbs already exist, so this works against an
|
|
61
|
+
* unrestarted daemon, and it is what makes `status` survive a sandbox that
|
|
62
|
+
* denies writes to ~/.gmax: `listWatchers()` opens LMDB, which needs its lock
|
|
63
|
+
* file even to read, and died with "Attempting to setup locks" before the
|
|
64
|
+
* command ever reached the store.
|
|
65
|
+
*
|
|
66
|
+
* The in-process branch is entered only when nothing is listening on the
|
|
67
|
+
* socket. A lease/LMDB denial there is converted by withStoreRead into the
|
|
68
|
+
* one-line filesystem hint.
|
|
69
|
+
*/
|
|
70
|
+
function loadStatusView(projects) {
|
|
71
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
return (0, store_access_1.withStoreRead)("status", {
|
|
73
|
+
daemon: () => (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" }),
|
|
74
|
+
render: (resp) => __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
const watchers = new Map();
|
|
76
|
+
const entries = Array.isArray(resp.projects) ? resp.projects : [];
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (entry && typeof entry.root === "string") {
|
|
79
|
+
watchers.set(entry.root, { status: "watching" });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// One project-stats call per project. A failure here is not fatal: the
|
|
83
|
+
// renderer falls back to the registry's cached chunkCount, exactly as
|
|
84
|
+
// the in-process path does when the LanceDB query fails.
|
|
85
|
+
const chunkCounts = new Map();
|
|
86
|
+
for (const project of projects) {
|
|
87
|
+
const stats = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "project-stats", root: project.root }, { timeoutMs: 30000 });
|
|
88
|
+
if (stats.ok && typeof stats.chunks === "number") {
|
|
89
|
+
chunkCounts.set(project.root, stats.chunks);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { watchers, chunkCounts };
|
|
93
|
+
}),
|
|
94
|
+
inProcess: () => __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
(0, watcher_store_1.listWatchers)(); // cleans stale entries as side effect
|
|
96
|
+
const watchers = new Map();
|
|
97
|
+
for (const project of projects) {
|
|
98
|
+
const watcher = (0, watcher_store_1.getWatcherForProject)(project.root);
|
|
99
|
+
if (watcher)
|
|
100
|
+
watchers.set(project.root, { status: watcher.status });
|
|
101
|
+
}
|
|
102
|
+
const chunkCounts = new Map();
|
|
103
|
+
try {
|
|
104
|
+
const { VectorDB } = yield Promise.resolve().then(() => __importStar(require("../lib/store/vector-db")));
|
|
105
|
+
const db = new VectorDB(config_1.PATHS.lancedbDir);
|
|
106
|
+
const table = yield db.ensureTable();
|
|
107
|
+
for (const project of projects) {
|
|
108
|
+
const prefix = project.root.endsWith("/")
|
|
109
|
+
? project.root
|
|
110
|
+
: `${project.root}/`;
|
|
111
|
+
const rows = yield table
|
|
112
|
+
.query()
|
|
113
|
+
.select(["id"])
|
|
114
|
+
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
115
|
+
.toArray();
|
|
116
|
+
chunkCounts.set(project.root, rows.length);
|
|
117
|
+
}
|
|
118
|
+
yield db.close();
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
// A sandbox denial is not "the query failed" — let it surface as the
|
|
122
|
+
// one-line refusal instead of silently degrading to cached counts.
|
|
123
|
+
if ((0, store_access_1.classifyLeaseError)(err) === "sandboxed")
|
|
124
|
+
throw err;
|
|
125
|
+
console.warn(`[status] Failed to query LanceDB for live chunk counts, using cached counts`);
|
|
126
|
+
}
|
|
127
|
+
return { watchers, chunkCounts };
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
57
132
|
const style = {
|
|
58
133
|
bold: (s) => `\x1b[1m${s}\x1b[22m`,
|
|
59
134
|
dim: (s) => `\x1b[2m${s}\x1b[22m`,
|
|
@@ -102,34 +177,24 @@ Examples:
|
|
|
102
177
|
var _a, _b, _c, _d;
|
|
103
178
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
104
179
|
const projects = (0, project_registry_1.listProjects)();
|
|
105
|
-
(0, watcher_store_1.listWatchers)(); // cleans stale entries as side effect
|
|
106
180
|
const indexing = (0, lock_1.isLocked)(config_1.PATHS.globalRoot);
|
|
107
181
|
const currentRoot = (0, project_root_1.findProjectRoot)(process.cwd());
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
console.log(`\n${style.bold("gmax")} · ${globalConfig.modelTier} (${globalConfig.vectorDim}d, ${globalConfig.embedMode})${indexing ? style.yellow(" · indexing...") : ""}`);
|
|
111
|
-
}
|
|
112
|
-
// Query live chunk counts from LanceDB
|
|
113
|
-
const chunkCounts = new Map();
|
|
182
|
+
// Resolved before the header so a sandbox refusal prints one clean line.
|
|
183
|
+
let view;
|
|
114
184
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
: `${project.root}/`;
|
|
122
|
-
const rows = yield table
|
|
123
|
-
.query()
|
|
124
|
-
.select(["id"])
|
|
125
|
-
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
126
|
-
.toArray();
|
|
127
|
-
chunkCounts.set(project.root, rows.length);
|
|
185
|
+
view = yield loadStatusView(projects);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if ((0, store_access_1.reportStoreAccessRefusal)(err)) {
|
|
189
|
+
yield (0, exit_1.gracefulExit)(2);
|
|
190
|
+
return;
|
|
128
191
|
}
|
|
129
|
-
|
|
192
|
+
throw err;
|
|
130
193
|
}
|
|
131
|
-
|
|
132
|
-
|
|
194
|
+
const { watchers, chunkCounts } = view;
|
|
195
|
+
if (!opts.agent) {
|
|
196
|
+
// Header
|
|
197
|
+
console.log(`\n${style.bold("gmax")} · ${globalConfig.modelTier} (${globalConfig.vectorDim}d, ${globalConfig.embedMode})${indexing ? style.yellow(" · indexing...") : ""}`);
|
|
133
198
|
}
|
|
134
199
|
if (projects.length === 0) {
|
|
135
200
|
if (opts.agent) {
|
|
@@ -143,7 +208,7 @@ Examples:
|
|
|
143
208
|
}
|
|
144
209
|
if (opts.agent) {
|
|
145
210
|
for (const project of projects) {
|
|
146
|
-
const watcher =
|
|
211
|
+
const watcher = watchers.get(project.root);
|
|
147
212
|
const projectStatus = (_a = project.status) !== null && _a !== void 0 ? _a : "indexed";
|
|
148
213
|
let st;
|
|
149
214
|
if (projectStatus === "pending")
|
|
@@ -172,7 +237,7 @@ Examples:
|
|
|
172
237
|
console.log();
|
|
173
238
|
for (const project of projects) {
|
|
174
239
|
const isCurrent = project.root === currentRoot;
|
|
175
|
-
const watcher =
|
|
240
|
+
const watcher = watchers.get(project.root);
|
|
176
241
|
// Status column
|
|
177
242
|
let statusStr;
|
|
178
243
|
const projectStatus = (_c = project.status) !== null && _c !== void 0 ? _c : "indexed";
|