grepmax 0.17.21 → 0.17.22
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/impact.js +20 -11
- package/dist/commands/search-run.js +227 -0
- package/dist/commands/search-skeletons.js +133 -0
- package/dist/commands/search.js +43 -450
- package/dist/lib/daemon/daemon.js +14 -101
- package/dist/lib/daemon/search-handler.js +159 -0
- package/dist/lib/output/compact-results.js +244 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/impact.js
CHANGED
|
@@ -58,10 +58,13 @@ exports.impact = new commander_1.Command("impact")
|
|
|
58
58
|
.option("--root <dir>", "Project root directory")
|
|
59
59
|
.option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
|
|
60
60
|
.option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
|
|
61
|
+
.option("--no-tests", "Skip affected-test analysis; show production blast radius only")
|
|
61
62
|
.option("--agent", "Compact output for AI agents", false)
|
|
62
63
|
.action((target, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
64
|
var _a;
|
|
64
65
|
const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
|
|
66
|
+
// commander maps --no-tests → opts.tests === false (defaults true).
|
|
67
|
+
const includeTests = opts.tests !== false;
|
|
65
68
|
let vectorDb = null;
|
|
66
69
|
try {
|
|
67
70
|
const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
|
|
@@ -96,10 +99,14 @@ exports.impact = new commander_1.Command("impact")
|
|
|
96
99
|
const queryRoot = opts.in && opts.in.length > 0
|
|
97
100
|
? scope.pathPrefix.replace(/\/$/, "")
|
|
98
101
|
: projectRoot;
|
|
99
|
-
// Run dependents and tests in parallel
|
|
102
|
+
// Run dependents and tests in parallel. --no-tests skips the test
|
|
103
|
+
// traversal entirely so the affected-tests section is omitted (not just
|
|
104
|
+
// empty) below.
|
|
100
105
|
const [dependents, tests] = yield Promise.all([
|
|
101
106
|
(0, impact_1.findDependents)(symbols, vectorDb, queryRoot, excludePaths, undefined, scope.excludePrefixes),
|
|
102
|
-
|
|
107
|
+
includeTests
|
|
108
|
+
? (0, impact_1.findTests)(symbols, vectorDb, queryRoot, depth, scope.excludePrefixes)
|
|
109
|
+
: Promise.resolve([]),
|
|
103
110
|
]);
|
|
104
111
|
// Separate test files from non-test dependents
|
|
105
112
|
const nonTestDeps = dependents.filter((d) => !(0, impact_1.isTestPath)(d.file));
|
|
@@ -127,16 +134,18 @@ exports.impact = new commander_1.Command("impact")
|
|
|
127
134
|
else {
|
|
128
135
|
console.log("Direct dependents: none found");
|
|
129
136
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
137
|
+
if (includeTests) {
|
|
138
|
+
console.log("");
|
|
139
|
+
if (tests.length > 0) {
|
|
140
|
+
console.log(`Affected tests (${tests.length}):`);
|
|
141
|
+
for (const t of tests) {
|
|
142
|
+
const hopLabel = t.hops === 0 ? "calls directly" : `${t.hops} hop${t.hops > 1 ? "s" : ""} away`;
|
|
143
|
+
console.log(` ${rel(t.file)}:${t.line + 1} ${t.symbol} (${hopLabel})`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log("Affected tests: none found");
|
|
136
148
|
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.log("Affected tests: none found");
|
|
140
149
|
}
|
|
141
150
|
}
|
|
142
151
|
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.runSearch = runSearch;
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const grammar_loader_1 = require("../lib/index/grammar-loader");
|
|
48
|
+
const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
49
|
+
const syncer_1 = require("../lib/index/syncer");
|
|
50
|
+
const searcher_1 = require("../lib/search/searcher");
|
|
51
|
+
const vector_db_1 = require("../lib/store/vector-db");
|
|
52
|
+
const lock_1 = require("../lib/utils/lock");
|
|
53
|
+
/**
|
|
54
|
+
* Acquire search results, picking the path: daemon-mediated first (ships the
|
|
55
|
+
* query over IPC to the already-warm daemon), in-process fallback otherwise
|
|
56
|
+
* (opens VectorDB, ensures the index, runs Searcher). The presentation/render
|
|
57
|
+
* stage stays in the command action.
|
|
58
|
+
*
|
|
59
|
+
* On success/dry-run the opened VectorDB (if any) is returned for the caller to
|
|
60
|
+
* close after rendering; if indexing throws, it is closed here before rethrow.
|
|
61
|
+
*/
|
|
62
|
+
function runSearch(params) {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
var _a, _b;
|
|
65
|
+
const { pattern, options, projectRoot, effectiveRoot, paths, projectStatus, searchFilters, pathFilter, seeds, } = params;
|
|
66
|
+
// Tracks a DB opened by the in-process path so it can be (a) returned to the
|
|
67
|
+
// caller for closing after render, or (b) closed here if indexing throws.
|
|
68
|
+
let openedDb = null;
|
|
69
|
+
try {
|
|
70
|
+
// Daemon-mediated search: ships query+args over IPC, daemon runs the
|
|
71
|
+
// hybrid+rerank against its already-warm VectorDB and worker pool.
|
|
72
|
+
// Drops cold-start cost (~17s wall, 6GB RAM in the CLI) to <1s. Falls
|
|
73
|
+
// back to in-process on any failure.
|
|
74
|
+
let searchResult = null;
|
|
75
|
+
let precomputedSkeletons;
|
|
76
|
+
let precomputedGraph;
|
|
77
|
+
let indexState;
|
|
78
|
+
if (!options.sync && !options.dryRun) {
|
|
79
|
+
try {
|
|
80
|
+
const { isDaemonRunning, sendDaemonCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
81
|
+
if (yield isDaemonRunning()) {
|
|
82
|
+
const resp = yield sendDaemonCommand({
|
|
83
|
+
cmd: "search",
|
|
84
|
+
projectRoot: effectiveRoot,
|
|
85
|
+
query: pattern,
|
|
86
|
+
limit: parseInt(options.m, 10),
|
|
87
|
+
filters: Object.keys(searchFilters).length > 0
|
|
88
|
+
? searchFilters
|
|
89
|
+
: undefined,
|
|
90
|
+
pathPrefix: pathFilter,
|
|
91
|
+
rerank: process.env.GMAX_RERANK === "1",
|
|
92
|
+
explain: options.explain,
|
|
93
|
+
seeds,
|
|
94
|
+
includeSkeletons: options.skeleton,
|
|
95
|
+
includeGraph: options.symbol,
|
|
96
|
+
}, { timeoutMs: 60000 });
|
|
97
|
+
if (resp.ok) {
|
|
98
|
+
searchResult = {
|
|
99
|
+
data: resp.data,
|
|
100
|
+
warnings: resp.warnings,
|
|
101
|
+
};
|
|
102
|
+
precomputedSkeletons = resp.skeletons;
|
|
103
|
+
precomputedGraph = resp.graph;
|
|
104
|
+
indexState = resp.indexState;
|
|
105
|
+
}
|
|
106
|
+
else if (process.env.GMAX_DEBUG === "1") {
|
|
107
|
+
console.error(`[search] daemon path unavailable: ${(_a = resp.error) !== null && _a !== void 0 ? _a : "unknown"}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
if (process.env.GMAX_DEBUG === "1") {
|
|
113
|
+
console.error("[search] daemon attempt threw:", err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// In-process fallback: open VectorDB, ensure index, run Searcher.
|
|
118
|
+
// Only entered when the daemon path didn't produce results.
|
|
119
|
+
if (!searchResult) {
|
|
120
|
+
const vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
121
|
+
openedDb = vectorDb;
|
|
122
|
+
// Check for active indexing lock and warn if present
|
|
123
|
+
const locked = (0, lock_1.isLocked)(paths.dataDir);
|
|
124
|
+
if (!options.agent && locked) {
|
|
125
|
+
console.warn("⚠️ Warning: Indexing in progress... search results may be incomplete.");
|
|
126
|
+
}
|
|
127
|
+
// No daemon here, so no precise pending count — surface the coarse
|
|
128
|
+
// signal (active lock or initial index not yet complete) so agent mode
|
|
129
|
+
// still gets a partial-index footer.
|
|
130
|
+
if (!indexState && (locked || projectStatus === "pending")) {
|
|
131
|
+
indexState = { indexing: true, pendingFiles: 0 };
|
|
132
|
+
}
|
|
133
|
+
const hasRows = yield vectorDb.hasAnyRows();
|
|
134
|
+
const needsSync = options.sync || !hasRows;
|
|
135
|
+
if (needsSync) {
|
|
136
|
+
const isTTY = process.stdout.isTTY;
|
|
137
|
+
let abortController;
|
|
138
|
+
let signal;
|
|
139
|
+
if (!isTTY) {
|
|
140
|
+
abortController = new AbortController();
|
|
141
|
+
signal = abortController.signal;
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
abortController === null || abortController === void 0 ? void 0 : abortController.abort();
|
|
144
|
+
}, 60000); // 60 seconds timeout for non-TTY auto-indexing
|
|
145
|
+
}
|
|
146
|
+
const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, options.sync ? "Indexing..." : "Indexing repository (first run)...");
|
|
147
|
+
try {
|
|
148
|
+
yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
|
|
149
|
+
const result = yield (0, syncer_1.initialSync)({
|
|
150
|
+
projectRoot,
|
|
151
|
+
dryRun: options.dryRun,
|
|
152
|
+
onProgress,
|
|
153
|
+
signal,
|
|
154
|
+
});
|
|
155
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
156
|
+
spinner.warn(`Indexing timed out (${result.processed}/${result.total}). Results may be partial.`);
|
|
157
|
+
}
|
|
158
|
+
if (options.dryRun) {
|
|
159
|
+
spinner.succeed(`Dry run complete (${result.processed}/${result.total}) • would have indexed ${result.indexed}`);
|
|
160
|
+
console.log((0, sync_helpers_1.formatDryRunSummary)(result, {
|
|
161
|
+
actionDescription: "would have indexed",
|
|
162
|
+
includeTotal: true,
|
|
163
|
+
}));
|
|
164
|
+
return { kind: "dry-run", vectorDb: openedDb };
|
|
165
|
+
}
|
|
166
|
+
yield vectorDb.createFTSIndex();
|
|
167
|
+
// Update registry after sync
|
|
168
|
+
const { readGlobalConfig } = yield Promise.resolve().then(() => __importStar(require("../lib/index/index-config")));
|
|
169
|
+
const { registerProject } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/project-registry")));
|
|
170
|
+
const gc = readGlobalConfig();
|
|
171
|
+
registerProject({
|
|
172
|
+
root: projectRoot,
|
|
173
|
+
name: path.basename(projectRoot),
|
|
174
|
+
vectorDim: gc.vectorDim,
|
|
175
|
+
modelTier: gc.modelTier,
|
|
176
|
+
embedMode: gc.embedMode,
|
|
177
|
+
lastIndexed: new Date().toISOString(),
|
|
178
|
+
chunkCount: result.indexed,
|
|
179
|
+
status: "indexed",
|
|
180
|
+
});
|
|
181
|
+
const failedSuffix = result.failedFiles > 0 ? ` • ${result.failedFiles} failed` : "";
|
|
182
|
+
spinner.succeed(`${options.sync ? "Indexing" : "Initial indexing"} complete (${result.processed}/${result.total}) • indexed ${result.indexed}${failedSuffix}`);
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
spinner.fail("Indexing failed");
|
|
186
|
+
throw e;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Ensure a watcher is running for live reindexing
|
|
190
|
+
if (!process.env.VITEST && !((_b = process.env.NODE_ENV) === null || _b === void 0 ? void 0 : _b.includes("test"))) {
|
|
191
|
+
const { launchWatcher } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/watcher-launcher")));
|
|
192
|
+
const launched = yield launchWatcher(projectRoot);
|
|
193
|
+
if (!launched.ok && launched.reason === "spawn-failed") {
|
|
194
|
+
console.warn(`[search] ${launched.message}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const searcher = new searcher_1.Searcher(vectorDb);
|
|
198
|
+
searchResult = yield searcher.search(pattern, parseInt(options.m, 10), {
|
|
199
|
+
rerank: process.env.GMAX_RERANK === "1",
|
|
200
|
+
explain: options.explain,
|
|
201
|
+
seeds,
|
|
202
|
+
}, Object.keys(searchFilters).length > 0
|
|
203
|
+
? searchFilters
|
|
204
|
+
: undefined, pathFilter);
|
|
205
|
+
} // end if (!searchResult) — in-process fallback
|
|
206
|
+
return {
|
|
207
|
+
kind: "result",
|
|
208
|
+
vectorDb: openedDb,
|
|
209
|
+
searchResult,
|
|
210
|
+
precomputedSkeletons,
|
|
211
|
+
precomputedGraph,
|
|
212
|
+
indexState,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
// Mirror the original action's finally: close a DB opened mid-flight when
|
|
217
|
+
// indexing throws, before the error propagates to the caller.
|
|
218
|
+
if (openedDb) {
|
|
219
|
+
try {
|
|
220
|
+
yield openedDb.close();
|
|
221
|
+
}
|
|
222
|
+
catch (_c) { }
|
|
223
|
+
}
|
|
224
|
+
throw e;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.outputSkeletons = outputSkeletons;
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const skeleton_1 = require("../lib/skeleton");
|
|
49
|
+
const retriever_1 = require("../lib/skeleton/retriever");
|
|
50
|
+
// Reuse Skeletonizer instance
|
|
51
|
+
let globalSkeletonizer = null;
|
|
52
|
+
function outputSkeletons(results, projectRoot, limit, db, precomputed) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const seenPaths = new Set();
|
|
56
|
+
const filesToProcess = [];
|
|
57
|
+
for (const result of results) {
|
|
58
|
+
const p = (_a = result.metadata) === null || _a === void 0 ? void 0 : _a.path;
|
|
59
|
+
if (typeof p === "string" && !seenPaths.has(p)) {
|
|
60
|
+
seenPaths.add(p);
|
|
61
|
+
filesToProcess.push(p);
|
|
62
|
+
if (filesToProcess.length >= limit)
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (filesToProcess.length === 0) {
|
|
67
|
+
console.log("No skeleton matches found.");
|
|
68
|
+
console.log("\nTry: broaden your query, or use `gmax skeleton <path>` to view a specific file's structure.");
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Reuse or init skeletonizer for fallbacks
|
|
73
|
+
if (!globalSkeletonizer) {
|
|
74
|
+
globalSkeletonizer = new skeleton_1.Skeletonizer();
|
|
75
|
+
// Lazy init only if we actually fallback
|
|
76
|
+
}
|
|
77
|
+
const skeletonOpts = { includeSummary: true };
|
|
78
|
+
const skeletonResults = [];
|
|
79
|
+
for (const filePath of filesToProcess) {
|
|
80
|
+
// Paths from search results are now absolute (centralized index)
|
|
81
|
+
const absPath = path.isAbsolute(filePath)
|
|
82
|
+
? filePath
|
|
83
|
+
: path.resolve(projectRoot, filePath);
|
|
84
|
+
// 0. Daemon-supplied (preferred — already-warm DB lookup, no cold open)
|
|
85
|
+
const fromDaemon = (_b = precomputed === null || precomputed === void 0 ? void 0 : precomputed[absPath]) !== null && _b !== void 0 ? _b : precomputed === null || precomputed === void 0 ? void 0 : precomputed[filePath];
|
|
86
|
+
if (fromDaemon) {
|
|
87
|
+
skeletonResults.push({
|
|
88
|
+
file: filePath,
|
|
89
|
+
skeleton: fromDaemon,
|
|
90
|
+
tokens: Math.ceil(fromDaemon.length / 4),
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// 1. Try DB cache
|
|
95
|
+
if (db) {
|
|
96
|
+
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
97
|
+
if (cached) {
|
|
98
|
+
skeletonResults.push({
|
|
99
|
+
file: filePath,
|
|
100
|
+
skeleton: cached,
|
|
101
|
+
tokens: Math.ceil(cached.length / 4), // Rough estimate
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// 2. Fallback to fresh generation
|
|
107
|
+
yield globalSkeletonizer.init();
|
|
108
|
+
if (!fs.existsSync(absPath)) {
|
|
109
|
+
skeletonResults.push({
|
|
110
|
+
file: filePath,
|
|
111
|
+
skeleton: `// File not found: ${filePath}`,
|
|
112
|
+
tokens: 0,
|
|
113
|
+
error: "File not found",
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const content = fs.readFileSync(absPath, "utf-8");
|
|
118
|
+
const res = yield globalSkeletonizer.skeletonizeFile(absPath, content, skeletonOpts);
|
|
119
|
+
skeletonResults.push({
|
|
120
|
+
file: filePath,
|
|
121
|
+
skeleton: res.skeleton,
|
|
122
|
+
tokens: res.tokenEstimate,
|
|
123
|
+
error: res.error,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// Since search doesn't support --json explicitly yet, we just print text.
|
|
127
|
+
// But if we ever add it, we have the structure.
|
|
128
|
+
for (const res of skeletonResults) {
|
|
129
|
+
console.log(res.skeleton);
|
|
130
|
+
console.log(""); // Separator
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|