indexer-cli 0.2.1
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/README.md +156 -0
- package/bin/indexer-cli.js +97 -0
- package/dist/_temp_test.d.ts +1 -0
- package/dist/_temp_test.js +4 -0
- package/dist/_temp_test.js.map +1 -0
- package/dist/chunking/adaptive.d.ts +15 -0
- package/dist/chunking/adaptive.js +43 -0
- package/dist/chunking/adaptive.js.map +1 -0
- package/dist/chunking/function.d.ts +6 -0
- package/dist/chunking/function.js +96 -0
- package/dist/chunking/function.js.map +1 -0
- package/dist/chunking/index.d.ts +5 -0
- package/dist/chunking/index.js +22 -0
- package/dist/chunking/index.js.map +1 -0
- package/dist/chunking/module.d.ts +6 -0
- package/dist/chunking/module.js +33 -0
- package/dist/chunking/module.js.map +1 -0
- package/dist/chunking/single.d.ts +4 -0
- package/dist/chunking/single.js +19 -0
- package/dist/chunking/single.js.map +1 -0
- package/dist/chunking/types.d.ts +17 -0
- package/dist/chunking/types.js +3 -0
- package/dist/chunking/types.js.map +1 -0
- package/dist/cli/commands/architecture.d.ts +2 -0
- package/dist/cli/commands/architecture.js +162 -0
- package/dist/cli/commands/architecture.js.map +1 -0
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +241 -0
- package/dist/cli/commands/context.js.map +1 -0
- package/dist/cli/commands/deps.d.ts +2 -0
- package/dist/cli/commands/deps.js +129 -0
- package/dist/cli/commands/deps.js.map +1 -0
- package/dist/cli/commands/enrich.d.ts +2 -0
- package/dist/cli/commands/ensure-indexed.d.ts +4 -0
- package/dist/cli/commands/ensure-indexed.js +168 -0
- package/dist/cli/commands/ensure-indexed.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +2 -0
- package/dist/cli/commands/explain.js +165 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.js +271 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +132 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/search.d.ts +2 -0
- package/dist/cli/commands/search.js +206 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.js +425 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/skill-template.d.ts +6 -0
- package/dist/cli/commands/skill-template.js +72 -0
- package/dist/cli/commands/skill-template.js.map +1 -0
- package/dist/cli/commands/structure.d.ts +2 -0
- package/dist/cli/commands/structure.js +243 -0
- package/dist/cli/commands/structure.js.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +2 -0
- package/dist/cli/commands/uninstall.js +138 -0
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/entry.d.ts +1 -0
- package/dist/cli/entry.js +55 -0
- package/dist/cli/entry.js.map +1 -0
- package/dist/cli/help-text.d.ts +2 -0
- package/dist/cli/help-text.js +9 -0
- package/dist/cli/help-text.js.map +1 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +9 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/core/config.d.ts +21 -0
- package/dist/core/config.js +77 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/logger.d.ts +19 -0
- package/dist/core/logger.js +116 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/types.d.ts +194 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/update-check.d.ts +1 -0
- package/dist/core/update-check.js +61 -0
- package/dist/core/update-check.js.map +1 -0
- package/dist/embedding/ollama.d.ts +29 -0
- package/dist/embedding/ollama.js +264 -0
- package/dist/embedding/ollama.js.map +1 -0
- package/dist/engine/architecture.d.ts +55 -0
- package/dist/engine/architecture.js +359 -0
- package/dist/engine/architecture.js.map +1 -0
- package/dist/engine/dependency-resolver.d.ts +4 -0
- package/dist/engine/dependency-resolver.js +69 -0
- package/dist/engine/dependency-resolver.js.map +1 -0
- package/dist/engine/git.d.ts +11 -0
- package/dist/engine/git.js +246 -0
- package/dist/engine/git.js.map +1 -0
- package/dist/engine/indexer.d.ts +86 -0
- package/dist/engine/indexer.js +933 -0
- package/dist/engine/indexer.js.map +1 -0
- package/dist/engine/scanner.d.ts +1 -0
- package/dist/engine/scanner.js +42 -0
- package/dist/engine/scanner.js.map +1 -0
- package/dist/engine/searcher.d.ts +26 -0
- package/dist/engine/searcher.js +70 -0
- package/dist/engine/searcher.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/csharp.d.ts +25 -0
- package/dist/languages/csharp.js +311 -0
- package/dist/languages/csharp.js.map +1 -0
- package/dist/languages/gdscript.d.ts +25 -0
- package/dist/languages/gdscript.js +382 -0
- package/dist/languages/gdscript.js.map +1 -0
- package/dist/languages/plugin.d.ts +73 -0
- package/dist/languages/plugin.js +35 -0
- package/dist/languages/plugin.js.map +1 -0
- package/dist/languages/python.d.ts +24 -0
- package/dist/languages/python.js +292 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/ruby.d.ts +25 -0
- package/dist/languages/ruby.js +328 -0
- package/dist/languages/ruby.js.map +1 -0
- package/dist/languages/typescript.d.ts +21 -0
- package/dist/languages/typescript.js +439 -0
- package/dist/languages/typescript.js.map +1 -0
- package/dist/storage/sqlite.d.ts +51 -0
- package/dist/storage/sqlite.js +726 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/storage/vectors.d.ts +39 -0
- package/dist/storage/vectors.js +450 -0
- package/dist/storage/vectors.js.map +1 -0
- package/dist/utils/gitignore.d.ts +4 -0
- package/dist/utils/gitignore.js +85 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +12 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/token-estimator.d.ts +3 -0
- package/dist/utils/token-estimator.js +13 -0
- package/dist/utils/token-estimator.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ensureIndexed = ensureIndexed;
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_path_2 = require("node:path");
|
|
10
|
+
const config_js_1 = require("../../core/config.js");
|
|
11
|
+
const types_js_1 = require("../../core/types.js");
|
|
12
|
+
const ollama_js_1 = require("../../embedding/ollama.js");
|
|
13
|
+
const git_js_1 = require("../../engine/git.js");
|
|
14
|
+
const git_js_2 = require("../../engine/git.js");
|
|
15
|
+
const indexer_js_1 = require("../../engine/indexer.js");
|
|
16
|
+
const hash_js_1 = require("../../utils/hash.js");
|
|
17
|
+
const vectors_js_1 = require("../../storage/vectors.js");
|
|
18
|
+
// Extensions that the indexer actually processes (from language plugins).
|
|
19
|
+
// Non-code files in workspace changes are irrelevant for re-index decisions.
|
|
20
|
+
const INDEXED_EXTENSIONS = new Set([
|
|
21
|
+
".ts", ".tsx", ".mts", ".cts",
|
|
22
|
+
".js", ".jsx", ".mjs", ".cjs",
|
|
23
|
+
".py", ".pyi",
|
|
24
|
+
".cs",
|
|
25
|
+
".gd",
|
|
26
|
+
".rb",
|
|
27
|
+
]);
|
|
28
|
+
/**
|
|
29
|
+
* Returns true if every workspace-modified/added file in workspaceChanges
|
|
30
|
+
* is already captured in `snapshot` with the same sha256 as on disk.
|
|
31
|
+
* This prevents repeated reindexing of persistent uncommitted changes.
|
|
32
|
+
*/
|
|
33
|
+
async function workspaceAlreadyIndexed(metadata, repoRoot, snapshot, workspaceChanges) {
|
|
34
|
+
// Deleted files can't be verified as "already indexed"
|
|
35
|
+
if (workspaceChanges.deleted.length > 0)
|
|
36
|
+
return false;
|
|
37
|
+
const filesToCheck = [
|
|
38
|
+
...workspaceChanges.modified,
|
|
39
|
+
...workspaceChanges.added,
|
|
40
|
+
];
|
|
41
|
+
if (filesToCheck.length === 0)
|
|
42
|
+
return true;
|
|
43
|
+
for (const filePath of filesToCheck) {
|
|
44
|
+
if (!INDEXED_EXTENSIONS.has((0, node_path_2.extname)(filePath).toLowerCase())) {
|
|
45
|
+
continue; // Non-code file — not indexed, irrelevant for re-index decision
|
|
46
|
+
}
|
|
47
|
+
const record = await metadata.getFile(types_js_1.DEFAULT_PROJECT_ID, snapshot.id, filePath);
|
|
48
|
+
if (!record)
|
|
49
|
+
return false; // Code file not in snapshot
|
|
50
|
+
let content;
|
|
51
|
+
try {
|
|
52
|
+
content = await (0, promises_1.readFile)(node_path_1.default.join(repoRoot, filePath), "utf8");
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false; // Unreadable
|
|
56
|
+
}
|
|
57
|
+
if ((0, hash_js_1.computeHash)(content) !== record.sha256)
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
async function getIndexPlan(git, repoRoot, metadata, snapshot) {
|
|
63
|
+
if (!snapshot) {
|
|
64
|
+
return { isFullReindex: true, changedFiles: undefined };
|
|
65
|
+
}
|
|
66
|
+
if (!snapshot.meta.headCommit) {
|
|
67
|
+
return { isFullReindex: true, changedFiles: undefined };
|
|
68
|
+
}
|
|
69
|
+
const headCommit = await git.getHeadCommit(repoRoot);
|
|
70
|
+
const workspaceChanges = await git.getWorkingTreeChanges(repoRoot);
|
|
71
|
+
const committedChanges = headCommit && headCommit !== snapshot.meta.headCommit
|
|
72
|
+
? await git.getChangedFiles(repoRoot, snapshot.meta.headCommit)
|
|
73
|
+
: { added: [], modified: [], deleted: [] };
|
|
74
|
+
const changedFiles = (0, git_js_2.mergeGitDiffs)(committedChanges, workspaceChanges);
|
|
75
|
+
const hasChanges = changedFiles.added.length > 0 ||
|
|
76
|
+
changedFiles.modified.length > 0 ||
|
|
77
|
+
changedFiles.deleted.length > 0;
|
|
78
|
+
if (!hasChanges)
|
|
79
|
+
return null;
|
|
80
|
+
// Optimisation: if the only "changes" are workspace changes that were
|
|
81
|
+
// already indexed in the latest snapshot (same sha256 on disk), skip.
|
|
82
|
+
const noCommittedChanges = committedChanges.added.length === 0 &&
|
|
83
|
+
committedChanges.modified.length === 0 &&
|
|
84
|
+
committedChanges.deleted.length === 0;
|
|
85
|
+
if (noCommittedChanges) {
|
|
86
|
+
const alreadyIndexed = await workspaceAlreadyIndexed(metadata, repoRoot, snapshot, workspaceChanges);
|
|
87
|
+
if (alreadyIndexed)
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return { isFullReindex: false, changedFiles };
|
|
91
|
+
}
|
|
92
|
+
async function ensureIndexed(metadata, repoRoot, options) {
|
|
93
|
+
const silent = options?.silent ?? false;
|
|
94
|
+
const git = new git_js_1.SimpleGitOperations();
|
|
95
|
+
const snapshot = (await metadata.getLatestCompletedSnapshot(types_js_1.DEFAULT_PROJECT_ID)) ??
|
|
96
|
+
undefined;
|
|
97
|
+
const indexPlan = await getIndexPlan(git, repoRoot, metadata, snapshot);
|
|
98
|
+
if (!indexPlan) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const dataDir = node_path_1.default.join(repoRoot, ".indexer-cli");
|
|
102
|
+
const vectorsPath = node_path_1.default.join(dataDir, "vectors");
|
|
103
|
+
const vectors = new vectors_js_1.LanceDbVectorStore({
|
|
104
|
+
dbPath: vectorsPath,
|
|
105
|
+
vectorSize: config_js_1.config.get("vectorSize"),
|
|
106
|
+
});
|
|
107
|
+
const startedAt = Date.now();
|
|
108
|
+
const embedder = new ollama_js_1.OllamaEmbeddingProvider(config_js_1.config.get("ollamaBaseUrl"), config_js_1.config.get("embeddingModel"), config_js_1.config.get("indexBatchSize"), config_js_1.config.get("indexConcurrency"), config_js_1.config.get("ollamaNumCtx"));
|
|
109
|
+
let engine = null;
|
|
110
|
+
try {
|
|
111
|
+
await vectors.initialize();
|
|
112
|
+
await embedder.initialize();
|
|
113
|
+
engine = new indexer_js_1.IndexerEngine({
|
|
114
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
115
|
+
repoRoot,
|
|
116
|
+
metadata,
|
|
117
|
+
vectors,
|
|
118
|
+
embedder,
|
|
119
|
+
git,
|
|
120
|
+
languagePlugins: (0, indexer_js_1.createDefaultLanguagePlugins)(),
|
|
121
|
+
});
|
|
122
|
+
const headCommit = await git.getHeadCommit(repoRoot);
|
|
123
|
+
// metadata is already initialized by the caller — skip engine.initialize()
|
|
124
|
+
// to avoid redundant schema/migration checks.
|
|
125
|
+
const mode = indexPlan.isFullReindex ? "full" : "incremental";
|
|
126
|
+
if (!silent) {
|
|
127
|
+
console.log(`Indexing (${mode})...`);
|
|
128
|
+
}
|
|
129
|
+
const result = await engine.indexProject({
|
|
130
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
131
|
+
repoRoot,
|
|
132
|
+
gitRef: headCommit ?? "unknown",
|
|
133
|
+
isFullReindex: indexPlan.isFullReindex,
|
|
134
|
+
changedFiles: indexPlan.changedFiles,
|
|
135
|
+
onProgress: silent
|
|
136
|
+
? undefined
|
|
137
|
+
: (processed, total) => {
|
|
138
|
+
console.log(` ${processed}/${total} files...`);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
const elapsedMs = Date.now() - startedAt;
|
|
142
|
+
const chunkCount = await vectors.countVectors({
|
|
143
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
144
|
+
snapshotId: result.snapshotId,
|
|
145
|
+
});
|
|
146
|
+
if (!silent) {
|
|
147
|
+
console.log("Index updated.");
|
|
148
|
+
console.log(` Snapshot: ${result.snapshotId}`);
|
|
149
|
+
console.log(` Files indexed: ${result.filesIndexed}`);
|
|
150
|
+
console.log(` Chunks created: ${chunkCount}`);
|
|
151
|
+
console.log(` Time elapsed: ${(elapsedMs / 1000).toFixed(2)}s`);
|
|
152
|
+
}
|
|
153
|
+
if (!silent && result.errors.length > 0) {
|
|
154
|
+
for (const error of result.errors) {
|
|
155
|
+
console.error(` Error: ${error}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (indexError) {
|
|
160
|
+
const message = indexError instanceof Error ? indexError.message : String(indexError);
|
|
161
|
+
throw new Error(`Auto-indexing failed: ${message}`);
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
// metadata is borrowed from caller — do NOT close it here
|
|
165
|
+
await Promise.allSettled([vectors.close(), embedder.close()]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=ensure-indexed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-indexed.js","sourceRoot":"","sources":["../../../src/cli/commands/ensure-indexed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerExplainCommand = registerExplainCommand;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const config_js_1 = require("../../core/config.js");
|
|
9
|
+
const logger_js_1 = require("../../core/logger.js");
|
|
10
|
+
const types_js_1 = require("../../core/types.js");
|
|
11
|
+
const sqlite_js_1 = require("../../storage/sqlite.js");
|
|
12
|
+
const help_text_js_1 = require("../help-text.js");
|
|
13
|
+
const ensure_indexed_js_1 = require("./ensure-indexed.js");
|
|
14
|
+
function registerExplainCommand(program) {
|
|
15
|
+
program
|
|
16
|
+
.command("explain <symbol>")
|
|
17
|
+
.description("Show context for a symbol: signature, callers, and module")
|
|
18
|
+
.addHelpText("after", `\n${help_text_js_1.PROJECT_ROOT_COMMAND_HELP}\n`)
|
|
19
|
+
.option("--json", "output as JSON")
|
|
20
|
+
.action(async (symbolArg, options) => {
|
|
21
|
+
const resolvedProjectPath = process.cwd();
|
|
22
|
+
const dataDir = node_path_1.default.join(resolvedProjectPath, ".indexer-cli");
|
|
23
|
+
const dbPath = node_path_1.default.join(dataDir, "db.sqlite");
|
|
24
|
+
const isJson = Boolean(options?.json);
|
|
25
|
+
const rankSymbolMatch = (candidateName, query) => {
|
|
26
|
+
if (candidateName === query) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
const candidateNameLower = candidateName.toLowerCase();
|
|
30
|
+
const queryLower = query.toLowerCase();
|
|
31
|
+
if (candidateNameLower === queryLower) {
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
if (candidateNameLower.startsWith(queryLower)) {
|
|
35
|
+
return 2;
|
|
36
|
+
}
|
|
37
|
+
return 3;
|
|
38
|
+
};
|
|
39
|
+
(0, logger_js_1.initLogger)(dataDir);
|
|
40
|
+
config_js_1.config.load(dataDir);
|
|
41
|
+
const metadata = new sqlite_js_1.SqliteMetadataStore(dbPath);
|
|
42
|
+
try {
|
|
43
|
+
await metadata.initialize();
|
|
44
|
+
await (0, ensure_indexed_js_1.ensureIndexed)(metadata, resolvedProjectPath, { silent: isJson });
|
|
45
|
+
const snapshot = await metadata.getLatestCompletedSnapshot(types_js_1.DEFAULT_PROJECT_ID);
|
|
46
|
+
if (!snapshot) {
|
|
47
|
+
throw new Error("No completed snapshot found.");
|
|
48
|
+
}
|
|
49
|
+
// Parse "file::symbol" or just "symbol"
|
|
50
|
+
let symbolName;
|
|
51
|
+
let filterFilePath;
|
|
52
|
+
if (symbolArg.includes("::")) {
|
|
53
|
+
const parts = symbolArg.split("::");
|
|
54
|
+
filterFilePath = parts[0];
|
|
55
|
+
symbolName = parts[1] ?? symbolArg;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
symbolName = symbolArg;
|
|
59
|
+
}
|
|
60
|
+
// Search symbols by name
|
|
61
|
+
let symbols = await metadata.searchSymbols(types_js_1.DEFAULT_PROJECT_ID, snapshot.id, symbolName);
|
|
62
|
+
if (symbols.length === 0) {
|
|
63
|
+
const camelCaseParts = symbolName
|
|
64
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
65
|
+
.split(/\s+/)
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
const fallbackQuery = camelCaseParts[0];
|
|
68
|
+
if (fallbackQuery && fallbackQuery !== symbolName) {
|
|
69
|
+
symbols = await metadata.searchSymbols(types_js_1.DEFAULT_PROJECT_ID, snapshot.id, fallbackQuery);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const matches = filterFilePath
|
|
73
|
+
? symbols.filter((s) => s.filePath === filterFilePath)
|
|
74
|
+
: [...symbols].sort((a, b) => {
|
|
75
|
+
const rankDiff = rankSymbolMatch(a.name, symbolName) -
|
|
76
|
+
rankSymbolMatch(b.name, symbolName);
|
|
77
|
+
if (rankDiff !== 0) {
|
|
78
|
+
return rankDiff;
|
|
79
|
+
}
|
|
80
|
+
return a.name.localeCompare(b.name);
|
|
81
|
+
});
|
|
82
|
+
if (matches.length === 0) {
|
|
83
|
+
const fuzzy = symbols.slice(0, 5).map((s) => ({
|
|
84
|
+
name: s.name,
|
|
85
|
+
kind: s.kind,
|
|
86
|
+
filePath: s.filePath,
|
|
87
|
+
}));
|
|
88
|
+
if (isJson) {
|
|
89
|
+
console.log(JSON.stringify({ error: "Symbol not found", suggestions: fuzzy }));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.error(`Symbol "${symbolName}" not found.`);
|
|
93
|
+
if (fuzzy.length > 0) {
|
|
94
|
+
console.error(`Did you mean: ${fuzzy.map((s) => `${s.name} (${s.kind}) in ${s.filePath}`).join(", ")}?`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const results = await Promise.all(matches.map(async (sym) => {
|
|
101
|
+
const [deps, dependents] = await Promise.all([
|
|
102
|
+
metadata.listDependencies(types_js_1.DEFAULT_PROJECT_ID, snapshot.id, sym.filePath),
|
|
103
|
+
metadata.getDependents(types_js_1.DEFAULT_PROJECT_ID, snapshot.id, sym.filePath),
|
|
104
|
+
]);
|
|
105
|
+
return {
|
|
106
|
+
name: sym.name,
|
|
107
|
+
kind: sym.kind,
|
|
108
|
+
file: sym.filePath,
|
|
109
|
+
lines: {
|
|
110
|
+
start: sym.range.start.line,
|
|
111
|
+
end: sym.range.end.line,
|
|
112
|
+
},
|
|
113
|
+
exported: sym.exported,
|
|
114
|
+
signature: sym.signature,
|
|
115
|
+
docComment: sym.docComment ?? null,
|
|
116
|
+
callers: dependents
|
|
117
|
+
.map((d) => d.fromPath)
|
|
118
|
+
.filter((v, i, arr) => arr.indexOf(v) === i),
|
|
119
|
+
callees: deps
|
|
120
|
+
.filter((d) => d.dependencyType === "internal" && d.toPath)
|
|
121
|
+
.map((d) => d.toPath)
|
|
122
|
+
.filter((v, i, arr) => arr.indexOf(v) === i),
|
|
123
|
+
};
|
|
124
|
+
}));
|
|
125
|
+
if (isJson) {
|
|
126
|
+
console.log(JSON.stringify(results.length === 1 ? results[0] : results, null, 2));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
for (const result of results) {
|
|
130
|
+
console.log(`Symbol: ${result.name}`);
|
|
131
|
+
console.log(`File: ${result.file} (lines ${result.lines.start}-${result.lines.end})`);
|
|
132
|
+
console.log(`Kind: ${result.kind}${result.exported ? " (exported)" : ""}`);
|
|
133
|
+
if (result.signature) {
|
|
134
|
+
console.log(`Signature: ${result.signature}`);
|
|
135
|
+
}
|
|
136
|
+
if (result.docComment) {
|
|
137
|
+
console.log(`Docs: ${result.docComment.split("\n")[0]}`);
|
|
138
|
+
}
|
|
139
|
+
if (result.callers.length > 0) {
|
|
140
|
+
console.log(`\nCallers (${result.callers.length}):`);
|
|
141
|
+
for (const caller of result.callers) {
|
|
142
|
+
console.log(` ${caller}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (result.callees.length > 0) {
|
|
146
|
+
console.log(`\nCallees (${result.callees.length}):`);
|
|
147
|
+
for (const callee of result.callees) {
|
|
148
|
+
console.log(` ${callee}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (results.length > 1)
|
|
152
|
+
console.log("");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
157
|
+
console.error(`Explain failed: ${message}`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
await metadata.close().catch(() => undefined);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerIndexCommand = registerIndexCommand;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const config_js_1 = require("../../core/config.js");
|
|
9
|
+
const types_js_1 = require("../../core/types.js");
|
|
10
|
+
const logger_js_1 = require("../../core/logger.js");
|
|
11
|
+
const ollama_js_1 = require("../../embedding/ollama.js");
|
|
12
|
+
const git_js_1 = require("../../engine/git.js");
|
|
13
|
+
const indexer_js_1 = require("../../engine/indexer.js");
|
|
14
|
+
const scanner_js_1 = require("../../engine/scanner.js");
|
|
15
|
+
const sqlite_js_1 = require("../../storage/sqlite.js");
|
|
16
|
+
const vectors_js_1 = require("../../storage/vectors.js");
|
|
17
|
+
const help_text_js_1 = require("../help-text.js");
|
|
18
|
+
function countChangedFiles(diff) {
|
|
19
|
+
return diff.added.length + diff.modified.length + diff.deleted.length;
|
|
20
|
+
}
|
|
21
|
+
function buildFileTree(filePaths) {
|
|
22
|
+
const root = { dirs: new Map(), files: new Set() };
|
|
23
|
+
for (const filePath of filePaths) {
|
|
24
|
+
const parts = filePath.split("/");
|
|
25
|
+
let node = root;
|
|
26
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
27
|
+
const dir = parts[i];
|
|
28
|
+
let child = node.dirs.get(dir);
|
|
29
|
+
if (!child) {
|
|
30
|
+
child = { dirs: new Map(), files: new Set() };
|
|
31
|
+
node.dirs.set(dir, child);
|
|
32
|
+
}
|
|
33
|
+
node = child;
|
|
34
|
+
}
|
|
35
|
+
node.files.add(parts[parts.length - 1]);
|
|
36
|
+
}
|
|
37
|
+
return root;
|
|
38
|
+
}
|
|
39
|
+
function printFileTree(node, indent) {
|
|
40
|
+
const dirs = Array.from(node.dirs.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
41
|
+
const files = Array.from(node.files).sort((a, b) => a.localeCompare(b));
|
|
42
|
+
for (const [name, child] of dirs) {
|
|
43
|
+
console.log(`${indent}${name}/`);
|
|
44
|
+
printFileTree(child, `${indent} `);
|
|
45
|
+
}
|
|
46
|
+
for (const name of files) {
|
|
47
|
+
console.log(`${indent}${name}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function registerIndexCommand(program) {
|
|
51
|
+
program
|
|
52
|
+
.command("index")
|
|
53
|
+
.description("Index project files for semantic search")
|
|
54
|
+
.addHelpText("after", `\n${help_text_js_1.PROJECT_ROOT_COMMAND_HELP}\n`)
|
|
55
|
+
.option("--full", "force a full reindex")
|
|
56
|
+
.option("--dry-run", "show what would change without indexing")
|
|
57
|
+
.option("--status", "show indexing status for the current project")
|
|
58
|
+
.option("--tree", "show indexed file tree (use with --status)")
|
|
59
|
+
.option("--json", "output status as JSON (use with --status)")
|
|
60
|
+
.action(async (options) => {
|
|
61
|
+
const resolvedProjectPath = process.cwd();
|
|
62
|
+
const dataDir = node_path_1.default.join(resolvedProjectPath, ".indexer-cli");
|
|
63
|
+
const dbPath = node_path_1.default.join(dataDir, "db.sqlite");
|
|
64
|
+
const vectorsPath = node_path_1.default.join(dataDir, "vectors");
|
|
65
|
+
(0, logger_js_1.initLogger)(dataDir);
|
|
66
|
+
config_js_1.config.load(dataDir);
|
|
67
|
+
const metadata = new sqlite_js_1.SqliteMetadataStore(dbPath);
|
|
68
|
+
try {
|
|
69
|
+
await metadata.initialize();
|
|
70
|
+
if (options?.status) {
|
|
71
|
+
const snapshot = await metadata.getLatestCompletedSnapshot(types_js_1.DEFAULT_PROJECT_ID);
|
|
72
|
+
if (!snapshot) {
|
|
73
|
+
if (options?.json) {
|
|
74
|
+
console.log(JSON.stringify({ indexed: false }, null, 2));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log("No completed snapshot found. Run `indexer-cli index` first.");
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const [files, symbols, dependencies] = await Promise.all([
|
|
82
|
+
metadata.listFiles(types_js_1.DEFAULT_PROJECT_ID, snapshot.id, {}),
|
|
83
|
+
metadata.listSymbols(types_js_1.DEFAULT_PROJECT_ID, snapshot.id),
|
|
84
|
+
metadata.listDependencies(types_js_1.DEFAULT_PROJECT_ID, snapshot.id),
|
|
85
|
+
]);
|
|
86
|
+
const vectors = new vectors_js_1.LanceDbVectorStore({
|
|
87
|
+
dbPath: vectorsPath,
|
|
88
|
+
vectorSize: config_js_1.config.get("vectorSize"),
|
|
89
|
+
});
|
|
90
|
+
let vectorCount = 0;
|
|
91
|
+
try {
|
|
92
|
+
await vectors.initialize();
|
|
93
|
+
vectorCount = await vectors.countVectors({
|
|
94
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
95
|
+
snapshotId: snapshot.id,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await vectors.close().catch(() => undefined);
|
|
100
|
+
}
|
|
101
|
+
const languages = new Map();
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const lang = file.languageId || "unknown";
|
|
104
|
+
languages.set(lang, (languages.get(lang) ?? 0) + 1);
|
|
105
|
+
}
|
|
106
|
+
const symbolKinds = new Map();
|
|
107
|
+
for (const symbol of symbols) {
|
|
108
|
+
symbolKinds.set(symbol.kind, (symbolKinds.get(symbol.kind) ?? 0) + 1);
|
|
109
|
+
}
|
|
110
|
+
if (options?.json) {
|
|
111
|
+
const output = {
|
|
112
|
+
indexed: true,
|
|
113
|
+
snapshot: {
|
|
114
|
+
id: snapshot.id,
|
|
115
|
+
status: snapshot.status,
|
|
116
|
+
createdAt: snapshot.createdAt,
|
|
117
|
+
gitRef: snapshot.meta.headCommit ?? null,
|
|
118
|
+
},
|
|
119
|
+
stats: {
|
|
120
|
+
files: files.length,
|
|
121
|
+
symbols: symbols.length,
|
|
122
|
+
chunks: vectorCount,
|
|
123
|
+
dependencies: dependencies.length,
|
|
124
|
+
},
|
|
125
|
+
languages: Object.fromEntries(languages),
|
|
126
|
+
symbolKinds: Object.fromEntries(symbolKinds),
|
|
127
|
+
};
|
|
128
|
+
if (options?.tree) {
|
|
129
|
+
output.files = files.map((f) => f.path);
|
|
130
|
+
}
|
|
131
|
+
console.log(JSON.stringify(output, null, 2));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.log(`Snapshot: ${snapshot.id} (${snapshot.status})`);
|
|
135
|
+
console.log(`Created: ${snapshot.createdAt} | Git ref: ${snapshot.meta.headCommit ?? "unknown"}`);
|
|
136
|
+
console.log(`Files: ${files.length} | Symbols: ${symbols.length} | Chunks: ${vectorCount} | Dependencies: ${dependencies.length}`);
|
|
137
|
+
if (languages.size > 0) {
|
|
138
|
+
const langEntries = Array.from(languages.entries())
|
|
139
|
+
.sort((a, b) => b[1] - a[1])
|
|
140
|
+
.map(([lang, count]) => `${lang}: ${count}`)
|
|
141
|
+
.join(", ");
|
|
142
|
+
console.log(`Languages: ${langEntries}`);
|
|
143
|
+
}
|
|
144
|
+
if (symbolKinds.size > 0) {
|
|
145
|
+
const kindEntries = Array.from(symbolKinds.entries())
|
|
146
|
+
.sort((a, b) => b[1] - a[1])
|
|
147
|
+
.map(([kind, count]) => `${kind}: ${count}`)
|
|
148
|
+
.join(", ");
|
|
149
|
+
console.log(`Symbol kinds: ${kindEntries}`);
|
|
150
|
+
}
|
|
151
|
+
if (options?.tree) {
|
|
152
|
+
console.log("");
|
|
153
|
+
const filePaths = files.map((f) => f.path);
|
|
154
|
+
printFileTree(buildFileTree(filePaths), "");
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const startedAt = Date.now();
|
|
159
|
+
console.log("Preparing indexer...");
|
|
160
|
+
const vectors = new vectors_js_1.LanceDbVectorStore({
|
|
161
|
+
dbPath: vectorsPath,
|
|
162
|
+
vectorSize: config_js_1.config.get("vectorSize"),
|
|
163
|
+
});
|
|
164
|
+
const embedder = new ollama_js_1.OllamaEmbeddingProvider(config_js_1.config.get("ollamaBaseUrl"), config_js_1.config.get("embeddingModel"), config_js_1.config.get("indexBatchSize"), config_js_1.config.get("indexConcurrency"), config_js_1.config.get("ollamaNumCtx"));
|
|
165
|
+
const git = new git_js_1.SimpleGitOperations();
|
|
166
|
+
let engine = null;
|
|
167
|
+
try {
|
|
168
|
+
engine = new indexer_js_1.IndexerEngine({
|
|
169
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
170
|
+
repoRoot: resolvedProjectPath,
|
|
171
|
+
metadata,
|
|
172
|
+
vectors,
|
|
173
|
+
embedder,
|
|
174
|
+
git,
|
|
175
|
+
languagePlugins: (0, indexer_js_1.createDefaultLanguagePlugins)(),
|
|
176
|
+
});
|
|
177
|
+
const latestSnapshot = await metadata.getLatestCompletedSnapshot(types_js_1.DEFAULT_PROJECT_ID);
|
|
178
|
+
const headCommit = await git.getHeadCommit(resolvedProjectPath);
|
|
179
|
+
const changedFiles = !options?.full && latestSnapshot?.meta.headCommit
|
|
180
|
+
? (0, git_js_1.mergeGitDiffs)(await git.getChangedFiles(resolvedProjectPath, latestSnapshot.meta.headCommit), await git.getWorkingTreeChanges(resolvedProjectPath))
|
|
181
|
+
: undefined;
|
|
182
|
+
if (options?.dryRun) {
|
|
183
|
+
if (options.full || !latestSnapshot) {
|
|
184
|
+
const plannedFiles = await (0, scanner_js_1.scanProjectFiles)(resolvedProjectPath, [
|
|
185
|
+
".ts",
|
|
186
|
+
".tsx",
|
|
187
|
+
".mts",
|
|
188
|
+
".cts",
|
|
189
|
+
".js",
|
|
190
|
+
".jsx",
|
|
191
|
+
".mjs",
|
|
192
|
+
".cjs",
|
|
193
|
+
".py",
|
|
194
|
+
".pyi",
|
|
195
|
+
".cs",
|
|
196
|
+
".gd",
|
|
197
|
+
]);
|
|
198
|
+
console.log("Dry run complete.");
|
|
199
|
+
console.log("Mode: full reindex");
|
|
200
|
+
console.log(`Files to index: ${plannedFiles.length}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const diff = changedFiles ?? {
|
|
204
|
+
added: [],
|
|
205
|
+
modified: [],
|
|
206
|
+
deleted: [],
|
|
207
|
+
};
|
|
208
|
+
console.log("Dry run complete.");
|
|
209
|
+
console.log("Mode: incremental");
|
|
210
|
+
console.log(`Added: ${diff.added.length}`);
|
|
211
|
+
console.log(`Modified: ${diff.modified.length}`);
|
|
212
|
+
console.log(`Deleted: ${diff.deleted.length}`);
|
|
213
|
+
console.log(`Changed total: ${countChangedFiles(diff)}`);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
await engine.initialize();
|
|
218
|
+
const mode = options?.full
|
|
219
|
+
? "Running full reindex..."
|
|
220
|
+
: "Running incremental index...";
|
|
221
|
+
console.log(mode);
|
|
222
|
+
const result = await engine.indexProject({
|
|
223
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
224
|
+
repoRoot: resolvedProjectPath,
|
|
225
|
+
gitRef: headCommit ?? "unknown",
|
|
226
|
+
isFullReindex: Boolean(options?.full),
|
|
227
|
+
changedFiles,
|
|
228
|
+
onProgress: (processed, total) => {
|
|
229
|
+
console.log(` ${processed}/${total} files...`);
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
const snapshot = await metadata.getSnapshot(result.snapshotId);
|
|
233
|
+
const elapsedMs = Date.now() - startedAt;
|
|
234
|
+
const totalFiles = snapshot?.totalFiles
|
|
235
|
+
? ` / ${snapshot.totalFiles}`
|
|
236
|
+
: "";
|
|
237
|
+
console.log("Index completed successfully.");
|
|
238
|
+
console.log(` Snapshot: ${result.snapshotId}`);
|
|
239
|
+
console.log(` Files indexed: ${result.filesIndexed}${totalFiles}`);
|
|
240
|
+
console.log(` Chunks created: ${await vectors.countVectors({
|
|
241
|
+
projectId: types_js_1.DEFAULT_PROJECT_ID,
|
|
242
|
+
snapshotId: result.snapshotId
|
|
243
|
+
})}`);
|
|
244
|
+
console.log(` Time elapsed: ${(elapsedMs / 1000).toFixed(2)}s`);
|
|
245
|
+
console.log(` Errors: ${result.errors.length}`);
|
|
246
|
+
if (result.errors.length > 0) {
|
|
247
|
+
for (const error of result.errors) {
|
|
248
|
+
console.error(` - ${error}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
254
|
+
console.error(`Indexing failed: ${message}`);
|
|
255
|
+
process.exitCode = 1;
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
if (engine) {
|
|
259
|
+
await engine.close().catch(() => undefined);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
await Promise.allSettled([vectors.close(), embedder.close()]);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
await metadata.close().catch(() => undefined);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=index.js.map
|