project-librarian 0.5.5 → 0.5.6
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/CONTRIBUTING.md +36 -0
- package/README.ko.md +57 -364
- package/README.md +56 -363
- package/dist/args.js +6 -1
- package/dist/code-index/extractors/light-languages.js +285 -0
- package/dist/code-index/extractors/registry.js +12 -0
- package/dist/code-index/extractors/shared.js +18 -1
- package/dist/code-index/extractors/typescript.js +30 -16
- package/dist/code-index/modes.js +136 -32
- package/dist/code-index/native-helper-matrix.js +99 -0
- package/dist/code-index/native-helper.js +292 -0
- package/dist/code-index/schema.js +72 -13
- package/dist/code-index/search.js +1 -1
- package/dist/code-index-file-policy.js +9 -1
- package/dist/code-index.js +363 -12
- package/dist/init-project-wiki.js +5 -0
- package/dist/native/darwin-arm64/project-librarian-indexer +0 -0
- package/dist/native/darwin-x64/project-librarian-indexer +0 -0
- package/dist/native/linux-arm64/project-librarian-indexer +0 -0
- package/dist/native/linux-arm64-musl/project-librarian-indexer +0 -0
- package/dist/native/linux-x64/project-librarian-indexer +0 -0
- package/dist/native/linux-x64-musl/project-librarian-indexer +0 -0
- package/dist/native/project-librarian-indexer-manifest.json +70 -0
- package/dist/native/win32-arm64/project-librarian-indexer.exe +0 -0
- package/dist/native/win32-x64/project-librarian-indexer.exe +0 -0
- package/docs/README.md +11 -0
- package/docs/benchmarks.md +64 -0
- package/docs/cli-reference.md +60 -0
- package/docs/code-evidence.md +87 -0
- package/docs/ko/README.md +13 -0
- package/docs/ko/benchmarks.md +64 -0
- package/docs/ko/cli-reference.md +60 -0
- package/docs/ko/code-evidence.md +87 -0
- package/docs/ko/maintainer.md +76 -0
- package/docs/ko/usage.md +167 -0
- package/docs/maintainer.md +76 -0
- package/docs/usage.md +175 -0
- package/package.json +11 -1
|
@@ -1,6 +1,41 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.codeIndexSchemaVersion = void 0;
|
|
37
|
+
exports.fileFtsRowid = fileFtsRowid;
|
|
38
|
+
exports.createSecondaryIndexes = createSecondaryIndexes;
|
|
4
39
|
exports.setupDatabase = setupDatabase;
|
|
5
40
|
exports.createIndexStatements = createIndexStatements;
|
|
6
41
|
exports.removeIndexedFile = removeIndexedFile;
|
|
@@ -10,14 +45,44 @@ exports.indexedScopes = indexedScopes;
|
|
|
10
45
|
exports.indexedParserMode = indexedParserMode;
|
|
11
46
|
exports.incrementalCompatibility = incrementalCompatibility;
|
|
12
47
|
exports.codeIndexSnapshot = codeIndexSnapshot;
|
|
48
|
+
const crypto = __importStar(require("node:crypto"));
|
|
13
49
|
const workspace_1 = require("../workspace");
|
|
14
|
-
exports.codeIndexSchemaVersion = "
|
|
15
|
-
function
|
|
50
|
+
exports.codeIndexSchemaVersion = "5";
|
|
51
|
+
function stableFtsRowid(parts) {
|
|
52
|
+
const hash = crypto.createHash("sha256");
|
|
53
|
+
for (const part of parts) {
|
|
54
|
+
hash.update(part);
|
|
55
|
+
hash.update("\0");
|
|
56
|
+
}
|
|
57
|
+
const digest = hash.digest();
|
|
58
|
+
let value = 0;
|
|
59
|
+
for (let index = 0; index < 6; index += 1)
|
|
60
|
+
value = value * 256 + digest[index];
|
|
61
|
+
return value + 1;
|
|
62
|
+
}
|
|
63
|
+
function fileFtsRowid(filePath) {
|
|
64
|
+
return stableFtsRowid(["file", filePath]);
|
|
65
|
+
}
|
|
66
|
+
const secondaryIndexSql = `
|
|
67
|
+
CREATE INDEX idx_symbols_file ON symbols(file_path);
|
|
68
|
+
CREATE INDEX idx_symbols_name ON symbols(name);
|
|
69
|
+
CREATE INDEX idx_imports_from ON imports(from_file);
|
|
70
|
+
CREATE INDEX idx_routes_path ON routes(route);
|
|
71
|
+
CREATE INDEX idx_configs_file ON configs(file_path);
|
|
72
|
+
CREATE INDEX idx_edges_source ON edges(source_kind, source);
|
|
73
|
+
CREATE INDEX idx_edges_target ON edges(target_kind, target);
|
|
74
|
+
CREATE INDEX idx_edges_kind ON edges(kind);
|
|
75
|
+
`;
|
|
76
|
+
function createSecondaryIndexes(database) {
|
|
77
|
+
database.exec(secondaryIndexSql);
|
|
78
|
+
}
|
|
79
|
+
function setupDatabase(database, options = {}) {
|
|
16
80
|
database.exec(`
|
|
17
81
|
PRAGMA journal_mode = WAL;
|
|
18
82
|
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
19
83
|
CREATE TABLE files (
|
|
20
84
|
path TEXT PRIMARY KEY,
|
|
85
|
+
fts_rowid INTEGER NOT NULL UNIQUE,
|
|
21
86
|
language TEXT NOT NULL,
|
|
22
87
|
profile TEXT NOT NULL,
|
|
23
88
|
kind TEXT NOT NULL,
|
|
@@ -71,30 +136,24 @@ function setupDatabase(database) {
|
|
|
71
136
|
);
|
|
72
137
|
CREATE VIRTUAL TABLE files_fts USING fts5(path, language, profile, content);
|
|
73
138
|
CREATE VIRTUAL TABLE symbols_fts USING fts5(name, kind, file_path, signature);
|
|
74
|
-
CREATE INDEX idx_symbols_file ON symbols(file_path);
|
|
75
|
-
CREATE INDEX idx_symbols_name ON symbols(name);
|
|
76
|
-
CREATE INDEX idx_imports_from ON imports(from_file);
|
|
77
|
-
CREATE INDEX idx_routes_path ON routes(route);
|
|
78
|
-
CREATE INDEX idx_configs_file ON configs(file_path);
|
|
79
|
-
CREATE INDEX idx_edges_source ON edges(source_kind, source);
|
|
80
|
-
CREATE INDEX idx_edges_target ON edges(target_kind, target);
|
|
81
|
-
CREATE INDEX idx_edges_kind ON edges(kind);
|
|
82
139
|
`);
|
|
140
|
+
if (options.secondaryIndexes ?? true)
|
|
141
|
+
createSecondaryIndexes(database);
|
|
83
142
|
}
|
|
84
143
|
function createIndexStatements(database) {
|
|
85
144
|
return {
|
|
86
145
|
deleteConfig: database.prepare("DELETE FROM configs WHERE file_path = ?"),
|
|
87
146
|
deleteEdge: database.prepare("DELETE FROM edges WHERE file_path = ?"),
|
|
88
147
|
deleteFile: database.prepare("DELETE FROM files WHERE path = ?"),
|
|
89
|
-
deleteFileFts: database.prepare("DELETE FROM files_fts WHERE path = ?"),
|
|
148
|
+
deleteFileFts: database.prepare("DELETE FROM files_fts WHERE rowid = (SELECT fts_rowid FROM files WHERE path = ?)"),
|
|
90
149
|
deleteImport: database.prepare("DELETE FROM imports WHERE from_file = ?"),
|
|
91
150
|
deleteRoute: database.prepare("DELETE FROM routes WHERE file_path = ?"),
|
|
92
151
|
deleteSymbol: database.prepare("DELETE FROM symbols WHERE file_path = ?"),
|
|
93
152
|
deleteSymbolFts: database.prepare("DELETE FROM symbols_fts WHERE file_path = ?"),
|
|
94
153
|
insertConfig: database.prepare("INSERT INTO configs (key, value, file_path, line) VALUES (?, ?, ?, ?)"),
|
|
95
154
|
insertEdge: database.prepare("INSERT INTO edges (kind, source_kind, source, target_kind, target, file_path, line, evidence) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"),
|
|
96
|
-
insertFile: database.prepare("INSERT INTO files (path, language, profile, kind, bytes, lines, hash, mtime_ms, size) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
|
97
|
-
insertFileFts: database.prepare("INSERT INTO files_fts (path, language, profile, content) VALUES (?, ?, ?, ?)"),
|
|
155
|
+
insertFile: database.prepare("INSERT INTO files (path, fts_rowid, language, profile, kind, bytes, lines, hash, mtime_ms, size) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
|
156
|
+
insertFileFts: database.prepare("INSERT INTO files_fts (rowid, path, language, profile, content) VALUES (?, ?, ?, ?, ?)"),
|
|
98
157
|
insertImport: database.prepare("INSERT INTO imports (from_file, to_ref, imported, line, raw) VALUES (?, ?, ?, ?, ?)"),
|
|
99
158
|
insertMeta: database.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"),
|
|
100
159
|
insertRoute: database.prepare("INSERT INTO routes (method, route, file_path, line, handler) VALUES (?, ?, ?, ?, ?)"),
|
|
@@ -82,7 +82,7 @@ function searchFiles(database, term, limit = 25) {
|
|
|
82
82
|
const ftsRows = database.prepare(`
|
|
83
83
|
SELECT files.path, files.language, files.profile, files.lines, files.bytes
|
|
84
84
|
FROM files_fts
|
|
85
|
-
JOIN files ON files.
|
|
85
|
+
JOIN files ON files.fts_rowid = files_fts.rowid
|
|
86
86
|
WHERE files_fts MATCH ?
|
|
87
87
|
ORDER BY bm25(files_fts, 8.0, 1.0, 1.0, 0.25), files.path
|
|
88
88
|
LIMIT ?
|
|
@@ -38,6 +38,7 @@ exports.fileLanguage = fileLanguage;
|
|
|
38
38
|
exports.isJavaScriptLike = isJavaScriptLike;
|
|
39
39
|
exports.shouldIndexFile = shouldIndexFile;
|
|
40
40
|
exports.isIgnoredCodePath = isIgnoredCodePath;
|
|
41
|
+
exports.cachedDiscoveredCodeFileStat = cachedDiscoveredCodeFileStat;
|
|
41
42
|
exports.discoverCodeFiles = discoverCodeFiles;
|
|
42
43
|
exports.smallRepoCodeIndexGate = smallRepoCodeIndexGate;
|
|
43
44
|
const childProcess = __importStar(require("node:child_process"));
|
|
@@ -76,6 +77,7 @@ const languageByExtension = {
|
|
|
76
77
|
};
|
|
77
78
|
const configExtensions = new Set([".json", ".yaml", ".yml", ".toml"]);
|
|
78
79
|
exports.maxIndexedBytes = 1024 * 1024;
|
|
80
|
+
const discoveredCodeFileStats = new Map();
|
|
79
81
|
function fileLanguage(relativePath) {
|
|
80
82
|
if (path.basename(relativePath) === ".env.example")
|
|
81
83
|
return "config";
|
|
@@ -170,7 +172,11 @@ function gitTrackedAndUnignoredFiles(scopes) {
|
|
|
170
172
|
function indexableFileStat(file) {
|
|
171
173
|
return (0, workspace_1.containedProjectFileStat)(file);
|
|
172
174
|
}
|
|
175
|
+
function cachedDiscoveredCodeFileStat(relativePath) {
|
|
176
|
+
return discoveredCodeFileStats.get(relativePath);
|
|
177
|
+
}
|
|
173
178
|
function discoverCodeFiles(scopes) {
|
|
179
|
+
discoveredCodeFileStats.clear();
|
|
174
180
|
const gitFiles = gitTrackedAndUnignoredFiles(scopes);
|
|
175
181
|
const candidates = gitFiles ?? scopes.flatMap((scope) => walkCodeFiles(scope));
|
|
176
182
|
const files = [];
|
|
@@ -178,8 +184,10 @@ function discoverCodeFiles(scopes) {
|
|
|
178
184
|
if (isIgnoredCodePath(file) || !shouldIndexFile(file))
|
|
179
185
|
continue;
|
|
180
186
|
const stat = indexableFileStat(file);
|
|
181
|
-
if (stat && stat.size <= exports.maxIndexedBytes)
|
|
187
|
+
if (stat && stat.size <= exports.maxIndexedBytes) {
|
|
188
|
+
discoveredCodeFileStats.set(file, { absolutePath: (0, workspace_1.abs)(file), stat });
|
|
182
189
|
files.push(file);
|
|
190
|
+
}
|
|
183
191
|
}
|
|
184
192
|
return files.sort();
|
|
185
193
|
}
|
package/dist/code-index.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.CodeEvidenceIndexUnavailableError = exports.codeContextPackTruncationNotice = exports.codeContextPackCharCap = exports.codeIndexSnapshot = exports.workspaceSummary = exports.workspaceDependencyGraph = exports.searchSymbols = exports.ownershipInfo = exports.ownershipContext = exports.matchedCodeownerRules = exports.evidenceCoverage = exports.codeownerRules = void 0;
|
|
36
|
+
exports.CodeEvidenceIndexUnavailableError = exports.nativeCodeIndexAutoFileThreshold = exports.codeContextPackTruncationNotice = exports.codeContextPackCharCap = exports.codeIndexSnapshot = exports.workspaceSummary = exports.workspaceDependencyGraph = exports.searchSymbols = exports.ownershipInfo = exports.ownershipContext = exports.matchedCodeownerRules = exports.evidenceCoverage = exports.codeownerRules = void 0;
|
|
37
37
|
exports.codeIndexStaleness = codeIndexStaleness;
|
|
38
38
|
exports.codeIndexHealth = codeIndexHealth;
|
|
39
39
|
exports.codeImpact = codeImpact;
|
|
@@ -61,6 +61,7 @@ const registry_1 = require("./code-index/extractors/registry");
|
|
|
61
61
|
const shared_1 = require("./code-index/extractors/shared");
|
|
62
62
|
const index_health_1 = require("./code-index/index-health");
|
|
63
63
|
const modes_1 = require("./code-index/modes");
|
|
64
|
+
const native_helper_1 = require("./code-index/native-helper");
|
|
64
65
|
const ownership_1 = require("./code-index/ownership");
|
|
65
66
|
Object.defineProperty(exports, "codeownerRules", { enumerable: true, get: function () { return ownership_1.codeownerRules; } });
|
|
66
67
|
Object.defineProperty(exports, "matchedCodeownerRules", { enumerable: true, get: function () { return ownership_1.matchedCodeownerRules; } });
|
|
@@ -77,6 +78,8 @@ Object.defineProperty(exports, "searchSymbols", { enumerable: true, get: functio
|
|
|
77
78
|
const workspace_1 = require("./workspace");
|
|
78
79
|
exports.codeContextPackCharCap = 4000;
|
|
79
80
|
exports.codeContextPackTruncationNotice = "[truncated - refine the query]";
|
|
81
|
+
exports.nativeCodeIndexAutoFileThreshold = 1;
|
|
82
|
+
const codeFileFingerprintPaths = new Map();
|
|
80
83
|
function fail(message) {
|
|
81
84
|
console.error(message);
|
|
82
85
|
process.exit(1);
|
|
@@ -110,31 +113,82 @@ function selectedCodeParserMode() {
|
|
|
110
113
|
return "tree-sitter";
|
|
111
114
|
fail(`invalid --code-parser: ${args_1.codeParser}; expected one of: default, tree-sitter`);
|
|
112
115
|
}
|
|
116
|
+
function selectedCodeIndexEngine() {
|
|
117
|
+
const requested = args_1.codeIndexEngine.trim().toLowerCase();
|
|
118
|
+
if (!requested || requested === "auto")
|
|
119
|
+
return "auto";
|
|
120
|
+
if (requested === "typescript")
|
|
121
|
+
return "typescript";
|
|
122
|
+
if (requested === "native-rust")
|
|
123
|
+
return "native-rust";
|
|
124
|
+
fail(`invalid --code-index-engine: ${args_1.codeIndexEngine}; expected one of: auto, typescript, native-rust`);
|
|
125
|
+
}
|
|
126
|
+
function codeIndexEngineSelectionContext(discoveredFiles, parserMode) {
|
|
127
|
+
let nativeEligibleFileCount = 0;
|
|
128
|
+
for (const filePath of discoveredFiles) {
|
|
129
|
+
const language = (0, code_index_file_policy_1.fileLanguage)(filePath) || "config";
|
|
130
|
+
if (nativeAutoEligibleProfile((0, registry_1.extractionProfile)(filePath, language, parserMode)))
|
|
131
|
+
nativeEligibleFileCount += 1;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
discoveredFileCount: discoveredFiles.length,
|
|
135
|
+
nativeEligibleFileCount,
|
|
136
|
+
nativeIneligibleFileCount: discoveredFiles.length - nativeEligibleFileCount,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function shouldUseNativeCodeIndexAuto(context) {
|
|
140
|
+
return context.nativeEligibleFileCount >= exports.nativeCodeIndexAutoFileThreshold
|
|
141
|
+
&& (0, native_helper_1.nativeCodeIndexHelperAvailable)();
|
|
142
|
+
}
|
|
143
|
+
function nativeCodeIndexAvailable() {
|
|
144
|
+
return (0, native_helper_1.nativeCodeIndexHelperAvailable)();
|
|
145
|
+
}
|
|
113
146
|
function normalizedMtimeMs(stat) {
|
|
114
147
|
return Number(stat.mtimeMs.toFixed(3));
|
|
115
148
|
}
|
|
116
149
|
function readCodeFileFingerprint(relativePath) {
|
|
117
|
-
const
|
|
150
|
+
const discovered = (0, code_index_file_policy_1.cachedDiscoveredCodeFileStat)(relativePath);
|
|
151
|
+
const { absolutePath, stat } = discovered ?? (0, workspace_1.requireContainedProjectFile)(relativePath, "code-index file");
|
|
152
|
+
codeFileFingerprintPaths.set(relativePath, absolutePath);
|
|
118
153
|
return {
|
|
119
154
|
mtimeMs: normalizedMtimeMs(stat),
|
|
120
155
|
path: relativePath,
|
|
121
156
|
size: stat.size,
|
|
122
157
|
};
|
|
123
158
|
}
|
|
124
|
-
function
|
|
125
|
-
|
|
159
|
+
function lineCount(text) {
|
|
160
|
+
if (text.length === 0)
|
|
161
|
+
return 0;
|
|
162
|
+
let lines = 1;
|
|
163
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
164
|
+
if (text.charCodeAt(index) === 10)
|
|
165
|
+
lines += 1;
|
|
166
|
+
}
|
|
167
|
+
return lines;
|
|
168
|
+
}
|
|
169
|
+
function readCodeFile(relativePath, parserMode = "default", fingerprint) {
|
|
170
|
+
let effectiveFingerprint = fingerprint;
|
|
171
|
+
let absolutePath = effectiveFingerprint ? codeFileFingerprintPaths.get(relativePath) : undefined;
|
|
172
|
+
if (!absolutePath || !effectiveFingerprint) {
|
|
173
|
+
const contained = (0, workspace_1.requireContainedProjectFile)(relativePath, "code-index file");
|
|
174
|
+
absolutePath = contained.absolutePath;
|
|
175
|
+
effectiveFingerprint ??= {
|
|
176
|
+
mtimeMs: normalizedMtimeMs(contained.stat),
|
|
177
|
+
path: relativePath,
|
|
178
|
+
size: contained.stat.size,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
126
181
|
const text = fs.readFileSync(absolutePath, "utf8");
|
|
127
|
-
const fingerprint = readCodeFileFingerprint(relativePath);
|
|
128
182
|
const language = (0, code_index_file_policy_1.fileLanguage)(relativePath) || "config";
|
|
129
183
|
return {
|
|
130
|
-
bytes:
|
|
184
|
+
bytes: effectiveFingerprint.size,
|
|
131
185
|
hash: crypto.createHash("sha256").update(text).digest("hex"),
|
|
132
186
|
language,
|
|
133
|
-
lines: text
|
|
134
|
-
mtimeMs:
|
|
187
|
+
lines: lineCount(text),
|
|
188
|
+
mtimeMs: effectiveFingerprint.mtimeMs,
|
|
135
189
|
path: relativePath,
|
|
136
190
|
profile: (0, registry_1.extractionProfile)(relativePath, language, parserMode),
|
|
137
|
-
size:
|
|
191
|
+
size: effectiveFingerprint.size,
|
|
138
192
|
text,
|
|
139
193
|
};
|
|
140
194
|
}
|
|
@@ -143,8 +197,9 @@ function extractionBackendForProfile(profile) {
|
|
|
143
197
|
return extractionBackendRegistry.backendForProfile(profile);
|
|
144
198
|
}
|
|
145
199
|
function indexCodeFile(file, statements) {
|
|
146
|
-
|
|
147
|
-
statements.
|
|
200
|
+
const ftsRowid = (0, schema_1.fileFtsRowid)(file.path);
|
|
201
|
+
statements.insertFile.run(file.path, ftsRowid, file.language, file.profile, file.language === "config" ? "config" : "source", file.bytes, file.lines, file.hash, file.mtimeMs, file.size);
|
|
202
|
+
statements.insertFileFts.run(ftsRowid, file.path, file.language, file.profile, file.text);
|
|
148
203
|
extractionBackendForProfile(file.profile).index(file, statements);
|
|
149
204
|
}
|
|
150
205
|
function printRows(rows) {
|
|
@@ -173,6 +228,295 @@ function removeDatabaseFiles(databasePath) {
|
|
|
173
228
|
fs.unlinkSync(filePath);
|
|
174
229
|
}
|
|
175
230
|
}
|
|
231
|
+
function moveDatabaseFiles(sourcePath, targetPath) {
|
|
232
|
+
const backupPath = `${targetPath}.backup-${process.pid}-${Date.now()}`;
|
|
233
|
+
removeDatabaseFiles(backupPath);
|
|
234
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
235
|
+
const target = `${targetPath}${suffix}`;
|
|
236
|
+
if (fs.existsSync(target))
|
|
237
|
+
fs.renameSync(target, `${backupPath}${suffix}`);
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
241
|
+
const source = `${sourcePath}${suffix}`;
|
|
242
|
+
if (fs.existsSync(source))
|
|
243
|
+
fs.renameSync(source, `${targetPath}${suffix}`);
|
|
244
|
+
}
|
|
245
|
+
removeDatabaseFiles(backupPath);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
removeDatabaseFiles(targetPath);
|
|
249
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
250
|
+
const backup = `${backupPath}${suffix}`;
|
|
251
|
+
if (fs.existsSync(backup))
|
|
252
|
+
fs.renameSync(backup, `${targetPath}${suffix}`);
|
|
253
|
+
}
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function removeTemporaryDatabaseFiles(databasePath) {
|
|
258
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
259
|
+
const filePath = `${databasePath}${suffix}`;
|
|
260
|
+
if (fs.existsSync(filePath))
|
|
261
|
+
fs.unlinkSync(filePath);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function nativeCodeIndexFileFor(filePath, parserMode) {
|
|
265
|
+
const fingerprint = readCodeFileFingerprint(filePath);
|
|
266
|
+
const language = (0, code_index_file_policy_1.fileLanguage)(filePath) || "config";
|
|
267
|
+
return {
|
|
268
|
+
...fingerprint,
|
|
269
|
+
language,
|
|
270
|
+
profile: (0, registry_1.extractionProfile)(filePath, language, parserMode),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function nativeCodeIndexFileFromCodeFile(file) {
|
|
274
|
+
return {
|
|
275
|
+
language: file.language,
|
|
276
|
+
mtimeMs: file.mtimeMs,
|
|
277
|
+
path: file.path,
|
|
278
|
+
profile: file.profile,
|
|
279
|
+
size: file.size,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function nativeCodeIndexFileFromFingerprint(file, parserMode) {
|
|
283
|
+
const language = (0, code_index_file_policy_1.fileLanguage)(file.path) || "config";
|
|
284
|
+
return {
|
|
285
|
+
language,
|
|
286
|
+
mtimeMs: file.mtimeMs,
|
|
287
|
+
path: file.path,
|
|
288
|
+
profile: (0, registry_1.extractionProfile)(file.path, language, parserMode),
|
|
289
|
+
size: file.size,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function nativeEligibleProfile(profile) {
|
|
293
|
+
return nativeAutoEligibleProfile(profile) || profile === "config" || profile === "inventory-only";
|
|
294
|
+
}
|
|
295
|
+
function nativeCodeIndexIncrementalEligible(files, parserMode) {
|
|
296
|
+
return files.every((file) => nativeEligibleProfile(nativeCodeIndexFileFromFingerprint(file, parserMode).profile));
|
|
297
|
+
}
|
|
298
|
+
function nativeAutoEligibleProfile(profile) {
|
|
299
|
+
return [
|
|
300
|
+
"typescript-ast",
|
|
301
|
+
"python-light",
|
|
302
|
+
"go-light",
|
|
303
|
+
"c-light",
|
|
304
|
+
"cpp-light",
|
|
305
|
+
"csharp-light",
|
|
306
|
+
"java-light",
|
|
307
|
+
"kotlin-light",
|
|
308
|
+
"php-light",
|
|
309
|
+
"rust-light",
|
|
310
|
+
"swift-light",
|
|
311
|
+
].includes(profile);
|
|
312
|
+
}
|
|
313
|
+
function nativeCodeIndexOutputMode() {
|
|
314
|
+
const requested = (process.env.PROJECT_LIBRARIAN_NATIVE_INDEXER_STRATEGY ?? "sqlite-direct").trim();
|
|
315
|
+
if (requested === "row-stream" || requested === "sqlite-bridge" || requested === "sqlite-direct")
|
|
316
|
+
return requested;
|
|
317
|
+
fail(`invalid PROJECT_LIBRARIAN_NATIVE_INDEXER_STRATEGY: ${requested}; expected row-stream, sqlite-bridge, or sqlite-direct`);
|
|
318
|
+
}
|
|
319
|
+
function appendTypeScriptPartitionToNativeDatabase(databasePath, files, parserMode) {
|
|
320
|
+
if (files.length === 0)
|
|
321
|
+
return 0;
|
|
322
|
+
const database = openDatabase(databasePath);
|
|
323
|
+
try {
|
|
324
|
+
const statements = (0, schema_1.createIndexStatements)(database);
|
|
325
|
+
database.exec("BEGIN");
|
|
326
|
+
for (const file of files)
|
|
327
|
+
indexCodeFile(readCodeFile(file.path, parserMode, file), statements);
|
|
328
|
+
database.exec("COMMIT");
|
|
329
|
+
return files.length;
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
try {
|
|
333
|
+
database.exec("ROLLBACK");
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// Ignore rollback failures after helper-created database errors.
|
|
337
|
+
}
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
database.close();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function writeNativeRowsToDatabase(databasePath, rows, scopes, parserMode) {
|
|
345
|
+
removeDatabaseFiles(databasePath);
|
|
346
|
+
const database = openDatabase(databasePath);
|
|
347
|
+
try {
|
|
348
|
+
(0, schema_1.setupDatabase)(database, { secondaryIndexes: false });
|
|
349
|
+
const statements = (0, schema_1.createIndexStatements)(database);
|
|
350
|
+
database.exec("BEGIN");
|
|
351
|
+
statements.insertMeta.run("created_at", new Date().toISOString());
|
|
352
|
+
(0, schema_1.writeIndexMetadata)(scopes, parserMode, statements);
|
|
353
|
+
for (const file of rows.files) {
|
|
354
|
+
const ftsRowid = (0, schema_1.fileFtsRowid)(file.path);
|
|
355
|
+
statements.insertFile.run(file.path, ftsRowid, file.language, file.profile, file.kind, file.bytes, file.lines, file.hash, file.mtime_ms, file.size);
|
|
356
|
+
statements.insertFileFts.run(ftsRowid, file.path, file.language, file.profile, file.content);
|
|
357
|
+
}
|
|
358
|
+
for (const symbol of rows.symbols) {
|
|
359
|
+
statements.insertSymbol.run(symbol.name, symbol.kind, symbol.file_path, symbol.line, symbol.signature);
|
|
360
|
+
statements.insertSymbolFts.run(symbol.name, symbol.kind, symbol.file_path, symbol.signature);
|
|
361
|
+
}
|
|
362
|
+
for (const imported of rows.imports) {
|
|
363
|
+
statements.insertImport.run(imported.from_file, imported.to_ref, imported.imported, imported.line, imported.raw);
|
|
364
|
+
}
|
|
365
|
+
for (const route of rows.routes) {
|
|
366
|
+
statements.insertRoute.run(route.method, route.route, route.file_path, route.line, route.handler);
|
|
367
|
+
}
|
|
368
|
+
for (const config of rows.configs) {
|
|
369
|
+
statements.insertConfig.run(config.key, config.value, config.file_path, config.line);
|
|
370
|
+
}
|
|
371
|
+
for (const edge of rows.edges) {
|
|
372
|
+
statements.insertEdge.run(edge.kind, edge.source_kind, edge.source, edge.target_kind, edge.target, edge.file_path, edge.line, edge.evidence);
|
|
373
|
+
}
|
|
374
|
+
(0, schema_1.createSecondaryIndexes)(database);
|
|
375
|
+
database.exec("COMMIT");
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
try {
|
|
379
|
+
database.exec("ROLLBACK");
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// Ignore rollback failures after row-stream setup errors.
|
|
383
|
+
}
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
finally {
|
|
387
|
+
database.close();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function runNativeCodeIndexMode(request) {
|
|
391
|
+
let helperPath = "";
|
|
392
|
+
try {
|
|
393
|
+
helperPath = (0, native_helper_1.requireNativeCodeIndexHelperPath)();
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
397
|
+
}
|
|
398
|
+
prepareOutputPath();
|
|
399
|
+
const tempDatabasePath = `${request.databasePath.absolutePath}.native-${process.pid}-${Date.now()}.tmp`;
|
|
400
|
+
removeDatabaseFiles(tempDatabasePath);
|
|
401
|
+
const manifestFiles = [];
|
|
402
|
+
const nativeFiles = [];
|
|
403
|
+
const typescriptFiles = [];
|
|
404
|
+
for (const filePath of request.discoveredFiles) {
|
|
405
|
+
const file = nativeCodeIndexFileFor(filePath, request.parserMode);
|
|
406
|
+
manifestFiles.push(file);
|
|
407
|
+
if (nativeEligibleProfile(file.profile))
|
|
408
|
+
nativeFiles.push(file);
|
|
409
|
+
else
|
|
410
|
+
typescriptFiles.push(file);
|
|
411
|
+
}
|
|
412
|
+
const typescriptProfiles = [...new Set(typescriptFiles.map((file) => file.profile))].sort();
|
|
413
|
+
const outputMode = nativeCodeIndexOutputMode();
|
|
414
|
+
const rowsPath = `${tempDatabasePath}.rows.json`;
|
|
415
|
+
const job = (0, native_helper_1.buildNativeCodeIndexJob)({
|
|
416
|
+
database_path: tempDatabasePath,
|
|
417
|
+
files: nativeFiles,
|
|
418
|
+
output_mode: outputMode,
|
|
419
|
+
parser_mode: request.parserMode,
|
|
420
|
+
schema_version: schema_1.codeIndexSchemaVersion,
|
|
421
|
+
scopes: request.scopes,
|
|
422
|
+
...(outputMode === "row-stream" ? { rows_path: rowsPath } : {}),
|
|
423
|
+
});
|
|
424
|
+
let summary;
|
|
425
|
+
let typescriptIndexedFiles = 0;
|
|
426
|
+
try {
|
|
427
|
+
if (outputMode === "row-stream") {
|
|
428
|
+
const result = (0, native_helper_1.runNativeCodeIndexRowsHelper)(job, { helperPath });
|
|
429
|
+
summary = result.summary;
|
|
430
|
+
writeNativeRowsToDatabase(tempDatabasePath, result.rows, request.scopes, request.parserMode);
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
summary = (0, native_helper_1.runNativeCodeIndexHelper)(job, { helperPath });
|
|
434
|
+
}
|
|
435
|
+
if (!fs.existsSync(tempDatabasePath)) {
|
|
436
|
+
fail(`native code index helper did not create database: ${tempDatabasePath}`);
|
|
437
|
+
}
|
|
438
|
+
typescriptIndexedFiles = appendTypeScriptPartitionToNativeDatabase(tempDatabasePath, typescriptFiles, request.parserMode);
|
|
439
|
+
moveDatabaseFiles(tempDatabasePath, request.databasePath.absolutePath);
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
removeTemporaryDatabaseFiles(tempDatabasePath);
|
|
443
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
if (fs.existsSync(rowsPath))
|
|
447
|
+
fs.unlinkSync(rowsPath);
|
|
448
|
+
}
|
|
449
|
+
console.log("Project wiki code evidence index complete.");
|
|
450
|
+
console.log(`database: ${request.databasePath.relativePath}`);
|
|
451
|
+
console.log("mode: full");
|
|
452
|
+
console.log(`parser_mode: ${request.parserMode}`);
|
|
453
|
+
console.log(`engine: ${typescriptIndexedFiles > 0 ? "mixed-native-rust" : "native-rust"}`);
|
|
454
|
+
if (request.requestedEngine === "auto")
|
|
455
|
+
console.log("engine_selection: auto");
|
|
456
|
+
console.log(`native_strategy: ${outputMode}`);
|
|
457
|
+
console.log(`scopes: ${request.scopes.join(", ")}`);
|
|
458
|
+
console.log(`files: ${manifestFiles.length}`);
|
|
459
|
+
console.log(`native_files: ${nativeFiles.length}`);
|
|
460
|
+
console.log(`typescript_files: ${typescriptIndexedFiles}`);
|
|
461
|
+
if (typescriptProfiles.length > 0)
|
|
462
|
+
console.log(`typescript_profiles: ${typescriptProfiles.join(", ")}`);
|
|
463
|
+
console.log(`reindexed_files: ${manifestFiles.length}`);
|
|
464
|
+
console.log(`deleted_files: ${summary.deleted_files ?? 0}`);
|
|
465
|
+
console.log(`unchanged_files: ${summary.unchanged_files ?? 0}`);
|
|
466
|
+
}
|
|
467
|
+
function runNativeCodeIndexIncrementalMode(request) {
|
|
468
|
+
let helperPath = "";
|
|
469
|
+
try {
|
|
470
|
+
helperPath = (0, native_helper_1.requireNativeCodeIndexHelperPath)();
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
474
|
+
}
|
|
475
|
+
const nativeFiles = request.reindexedFiles.map((file) => nativeCodeIndexFileFromFingerprint(file, request.parserMode));
|
|
476
|
+
const ineligibleProfiles = [...new Set(nativeFiles
|
|
477
|
+
.filter((file) => !nativeEligibleProfile(file.profile))
|
|
478
|
+
.map((file) => file.profile))].sort();
|
|
479
|
+
if (ineligibleProfiles.length > 0) {
|
|
480
|
+
fail(`native incremental writer does not support parser profiles: ${ineligibleProfiles.join(", ")}`);
|
|
481
|
+
}
|
|
482
|
+
prepareOutputPath();
|
|
483
|
+
const outputMode = "sqlite-direct";
|
|
484
|
+
const job = (0, native_helper_1.buildNativeCodeIndexJob)({
|
|
485
|
+
database_path: request.databasePath.absolutePath,
|
|
486
|
+
deleted_paths: request.deletedPaths,
|
|
487
|
+
files: nativeFiles,
|
|
488
|
+
mode: "incremental",
|
|
489
|
+
output_mode: outputMode,
|
|
490
|
+
parser_mode: request.parserMode,
|
|
491
|
+
schema_version: schema_1.codeIndexSchemaVersion,
|
|
492
|
+
scopes: request.scopes,
|
|
493
|
+
});
|
|
494
|
+
let summary;
|
|
495
|
+
try {
|
|
496
|
+
summary = (0, native_helper_1.runNativeCodeIndexHelper)(job, { helperPath });
|
|
497
|
+
if (!fs.existsSync(request.databasePath.absolutePath)) {
|
|
498
|
+
fail(`native incremental writer did not preserve database: ${request.databasePath.absolutePath}`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
503
|
+
}
|
|
504
|
+
console.log("Project wiki code evidence index complete.");
|
|
505
|
+
console.log(`database: ${request.databasePath.relativePath}`);
|
|
506
|
+
console.log("mode: incremental");
|
|
507
|
+
console.log(`parser_mode: ${request.parserMode}`);
|
|
508
|
+
console.log("engine: native-rust");
|
|
509
|
+
if (request.requestedEngine === "auto")
|
|
510
|
+
console.log("engine_selection: auto");
|
|
511
|
+
console.log(`native_strategy: ${outputMode}`);
|
|
512
|
+
console.log(`scopes: ${request.scopes.join(", ")}`);
|
|
513
|
+
console.log(`files: ${request.discoveredFiles.length}`);
|
|
514
|
+
console.log(`native_files: ${nativeFiles.length}`);
|
|
515
|
+
console.log("typescript_files: 0");
|
|
516
|
+
console.log(`reindexed_files: ${summary.reindexed_files ?? nativeFiles.length}`);
|
|
517
|
+
console.log(`deleted_files: ${summary.deleted_files ?? request.deletedPaths.length}`);
|
|
518
|
+
console.log(`unchanged_files: ${request.unchangedFiles}`);
|
|
519
|
+
}
|
|
176
520
|
function codeIndexStaleness(database) {
|
|
177
521
|
const scopes = (0, schema_1.indexedScopes)(database);
|
|
178
522
|
const parserMode = (0, schema_1.indexedParserMode)(database);
|
|
@@ -194,7 +538,7 @@ function codeIndexStaleness(database) {
|
|
|
194
538
|
}
|
|
195
539
|
if (existing.mtimeMs === file.mtimeMs && existing.size === file.size)
|
|
196
540
|
continue;
|
|
197
|
-
if (readCodeFile(file.path, parserMode).hash !== existing.hash)
|
|
541
|
+
if (readCodeFile(file.path, parserMode, file).hash !== existing.hash)
|
|
198
542
|
changed += 1;
|
|
199
543
|
}
|
|
200
544
|
const deleted = indexedRows.filter((row) => !currentPaths.has(String(row.path))).length;
|
|
@@ -397,13 +741,20 @@ function codeIndexModeRuntime() {
|
|
|
397
741
|
codeScopes,
|
|
398
742
|
fail,
|
|
399
743
|
indexCodeFile,
|
|
744
|
+
nativeCodeIndexAvailable,
|
|
745
|
+
nativeCodeIndexIncrementalEligible,
|
|
400
746
|
openDatabase,
|
|
401
747
|
prepareOutputPath,
|
|
402
748
|
readCodeFileFingerprint,
|
|
403
749
|
readCodeFile,
|
|
404
750
|
removeDatabaseFiles,
|
|
405
751
|
requireExistingIndex,
|
|
752
|
+
runNativeCodeIndexIncrementalMode,
|
|
753
|
+
runNativeCodeIndexMode,
|
|
754
|
+
selectedCodeIndexEngine,
|
|
406
755
|
selectedCodeParserMode,
|
|
756
|
+
codeIndexEngineSelectionContext,
|
|
757
|
+
shouldUseNativeCodeIndexAuto,
|
|
407
758
|
warnIfCodeIndexStale,
|
|
408
759
|
};
|
|
409
760
|
}
|
|
@@ -62,6 +62,7 @@ Options:
|
|
|
62
62
|
--acknowledge-small-repo With --code-index, proceed below the small-repo scale gate after its cost warning.
|
|
63
63
|
--incremental With --code-index, require an existing compatible index and update only changes.
|
|
64
64
|
--code-index-full With --code-index, force a full rebuild even when incremental update is possible.
|
|
65
|
+
--code-index-engine <engine> With --code-index, override default auto engine: typescript or native-rust.
|
|
65
66
|
--code-parser <mode> With --code-index, use parser mode default or tree-sitter.
|
|
66
67
|
--code-query <sql> Run conservative read-only SQL over the code evidence index.
|
|
67
68
|
--code-status, --code-files Inspect the code evidence index.
|
|
@@ -165,6 +166,10 @@ if (args_1.codeIndexFullMode && !args_1.codeIndexMode) {
|
|
|
165
166
|
console.error("--code-index-full is only supported with --code-index.");
|
|
166
167
|
process.exit(1);
|
|
167
168
|
}
|
|
169
|
+
if (args_1.codeIndexEngineMode && !args_1.codeIndexMode) {
|
|
170
|
+
console.error("--code-index-engine is only supported with --code-index.");
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
168
173
|
if (args_1.codeParserMode && !args_1.codeIndexMode) {
|
|
169
174
|
console.error("--code-parser is only supported with --code-index.");
|
|
170
175
|
process.exit(1);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|