project-librarian 0.5.4 → 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.
Files changed (46) hide show
  1. package/CONTRIBUTING.md +36 -0
  2. package/README.ko.md +57 -360
  3. package/README.md +56 -359
  4. package/dist/args.js +6 -1
  5. package/dist/code-index/extractors/light-languages.js +285 -0
  6. package/dist/code-index/extractors/registry.js +12 -0
  7. package/dist/code-index/extractors/shared.js +18 -1
  8. package/dist/code-index/extractors/typescript.js +30 -16
  9. package/dist/code-index/modes.js +136 -32
  10. package/dist/code-index/native-helper-matrix.js +99 -0
  11. package/dist/code-index/native-helper.js +292 -0
  12. package/dist/code-index/ownership.js +8 -6
  13. package/dist/code-index/schema.js +72 -13
  14. package/dist/code-index/search.js +1 -1
  15. package/dist/code-index-db.js +20 -12
  16. package/dist/code-index-file-policy.js +17 -11
  17. package/dist/code-index.js +365 -13
  18. package/dist/hooks.js +5 -5
  19. package/dist/init-project-wiki.js +7 -1
  20. package/dist/install-skill.js +99 -6
  21. package/dist/mcp-server.js +4 -4
  22. package/dist/migration.js +27 -2
  23. package/dist/native/darwin-arm64/project-librarian-indexer +0 -0
  24. package/dist/native/darwin-x64/project-librarian-indexer +0 -0
  25. package/dist/native/linux-arm64/project-librarian-indexer +0 -0
  26. package/dist/native/linux-arm64-musl/project-librarian-indexer +0 -0
  27. package/dist/native/linux-x64/project-librarian-indexer +0 -0
  28. package/dist/native/linux-x64-musl/project-librarian-indexer +0 -0
  29. package/dist/native/project-librarian-indexer-manifest.json +70 -0
  30. package/dist/native/win32-arm64/project-librarian-indexer.exe +0 -0
  31. package/dist/native/win32-x64/project-librarian-indexer.exe +0 -0
  32. package/dist/templates.js +4 -3
  33. package/dist/workspace.js +137 -10
  34. package/docs/README.md +11 -0
  35. package/docs/benchmarks.md +64 -0
  36. package/docs/cli-reference.md +60 -0
  37. package/docs/code-evidence.md +87 -0
  38. package/docs/ko/README.md +13 -0
  39. package/docs/ko/benchmarks.md +64 -0
  40. package/docs/ko/cli-reference.md +60 -0
  41. package/docs/ko/code-evidence.md +87 -0
  42. package/docs/ko/maintainer.md +76 -0
  43. package/docs/ko/usage.md +167 -0
  44. package/docs/maintainer.md +76 -0
  45. package/docs/usage.md +175 -0
  46. package/package.json +13 -2
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveCodeIndexEngine = resolveCodeIndexEngine;
36
37
  exports.runCodeIndexMode = runCodeIndexMode;
37
38
  exports.runCodeQueryMode = runCodeQueryMode;
38
39
  exports.runCodeReportMode = runCodeReportMode;
@@ -70,17 +71,75 @@ function requireCompatibleDatabase(database, runtime) {
70
71
  ].join("\n"));
71
72
  }
72
73
  }
74
+ function elapsedMs(started) {
75
+ return Number(process.hrtime.bigint() - started) / 1_000_000;
76
+ }
77
+ function measurePhase(timings, key, fn) {
78
+ const started = process.hrtime.bigint();
79
+ try {
80
+ return fn();
81
+ }
82
+ finally {
83
+ timings[key] = Number(((timings[key] ?? 0) + elapsedMs(started)).toFixed(3));
84
+ }
85
+ }
86
+ function emitCodeIndexPhaseTimings(timings) {
87
+ if (process.env.PROJECT_LIBRARIAN_CODE_INDEX_TIMINGS !== "1")
88
+ return;
89
+ console.error(`code_index_phase_timings ${JSON.stringify(timings)}`);
90
+ }
91
+ function configureBulkWriteConnection(database) {
92
+ database.exec(`
93
+ PRAGMA synchronous = OFF;
94
+ PRAGMA temp_store = MEMORY;
95
+ PRAGMA cache_size = -20000;
96
+ `);
97
+ }
98
+ function shouldUseNativeIncrementalForAuto(requestedEngine, runtime, staleFileCount) {
99
+ if (requestedEngine !== "auto" || !args_1.codeIndexIncrementalMode)
100
+ return false;
101
+ if (staleFileCount <= 0 || !runtime.nativeCodeIndexAvailable())
102
+ return false;
103
+ return true;
104
+ }
105
+ function resolveCodeIndexEngine(requestedEngine, context, shouldUseNativeAuto, incrementalMode = args_1.codeIndexIncrementalMode) {
106
+ if (requestedEngine !== "auto")
107
+ return requestedEngine;
108
+ if (incrementalMode)
109
+ return "typescript";
110
+ return shouldUseNativeAuto(context) ? "native-rust" : "typescript";
111
+ }
73
112
  function runCodeIndexMode(runtime) {
113
+ const totalStarted = process.hrtime.bigint();
114
+ const phaseTimings = {};
74
115
  const databasePath = runtime.codeEvidenceDatabasePath();
75
116
  const scopes = runtime.codeScopes();
76
117
  const parserMode = runtime.selectedCodeParserMode();
118
+ const requestedEngine = runtime.selectedCodeIndexEngine();
77
119
  // Scale gate before ANY write or database work: below the measured threshold
78
120
  // the build halts with the evidence-citing warning unless --acknowledge-small-repo
79
121
  // was passed (2026-06-12 scale-aware guidance decision).
80
- const discoveredFiles = (0, code_index_file_policy_1.discoverCodeFiles)(scopes);
122
+ const discoveredFiles = measurePhase(phaseTimings, "discover_files_ms", () => (0, code_index_file_policy_1.discoverCodeFiles)(scopes));
81
123
  const scaleGate = (0, code_index_file_policy_1.smallRepoCodeIndexGate)(discoveredFiles.length, args_1.acknowledgeSmallRepoMode);
82
124
  if (!scaleGate.proceed)
83
125
  runtime.fail(scaleGate.warning);
126
+ const engineSelectionContext = runtime.codeIndexEngineSelectionContext(discoveredFiles, parserMode);
127
+ const engine = resolveCodeIndexEngine(requestedEngine, engineSelectionContext, runtime.shouldUseNativeCodeIndexAuto);
128
+ if (engine === "native-rust") {
129
+ if (!args_1.codeIndexIncrementalMode) {
130
+ try {
131
+ measurePhase(phaseTimings, "native_helper_ms", () => runtime.runNativeCodeIndexMode({ databasePath, discoveredFiles, parserMode, requestedEngine, scopes }));
132
+ phaseTimings.total_ms = Number(elapsedMs(totalStarted).toFixed(3));
133
+ emitCodeIndexPhaseTimings(phaseTimings);
134
+ return;
135
+ }
136
+ catch (error) {
137
+ phaseTimings.total_ms = Number(elapsedMs(totalStarted).toFixed(3));
138
+ emitCodeIndexPhaseTimings(phaseTimings);
139
+ throw error;
140
+ }
141
+ }
142
+ }
84
143
  const existingIndex = fs.existsSync(databasePath.absolutePath);
85
144
  if (args_1.codeIndexIncrementalMode && !existingIndex) {
86
145
  runtime.fail(`--incremental requires an existing compatible code evidence index: ${databasePath.relativePath}`);
@@ -88,27 +147,30 @@ function runCodeIndexMode(runtime) {
88
147
  let incremental = false;
89
148
  if (existingIndex && !args_1.codeIndexFullMode) {
90
149
  let compatibility = { compatible: false, reason: "compatibility was not checked" };
91
- const existingDatabase = runtime.openDatabase(databasePath.absolutePath);
92
- try {
93
- compatibility = (0, schema_1.incrementalCompatibility)(existingDatabase, scopes, parserMode);
94
- }
95
- finally {
96
- existingDatabase.close();
97
- }
150
+ measurePhase(phaseTimings, "compatibility_ms", () => {
151
+ const existingDatabase = runtime.openDatabase(databasePath.absolutePath);
152
+ try {
153
+ compatibility = (0, schema_1.incrementalCompatibility)(existingDatabase, scopes, parserMode);
154
+ }
155
+ finally {
156
+ existingDatabase.close();
157
+ }
158
+ });
98
159
  incremental = !args_1.codeIndexFullMode && compatibility.compatible;
99
160
  if (args_1.codeIndexIncrementalMode && !compatibility.compatible)
100
161
  runtime.fail(`--incremental cannot update ${databasePath.relativePath}: ${compatibility.reason}`);
101
162
  }
102
- runtime.prepareOutputPath();
103
- if (!incremental)
104
- runtime.removeDatabaseFiles(databasePath.absolutePath);
105
- const database = runtime.openDatabase(databasePath.absolutePath);
163
+ measurePhase(phaseTimings, "prepare_output_ms", () => {
164
+ runtime.prepareOutputPath();
165
+ if (!incremental)
166
+ runtime.removeDatabaseFiles(databasePath.absolutePath);
167
+ });
168
+ let database = runtime.openDatabase(databasePath.absolutePath);
106
169
  try {
107
170
  if (!incremental)
108
- (0, schema_1.setupDatabase)(database);
109
- const statements = (0, schema_1.createIndexStatements)(database);
110
- const currentFingerprints = discoveredFiles.map((filePath) => runtime.readCodeFileFingerprint(filePath));
111
- let reindexedFiles;
171
+ (0, schema_1.setupDatabase)(database, { secondaryIndexes: false });
172
+ const currentFingerprints = measurePhase(phaseTimings, "fingerprints_ms", () => discoveredFiles.map((filePath) => runtime.readCodeFileFingerprint(filePath)));
173
+ let reindexedFingerprints;
112
174
  let deletedPaths;
113
175
  let indexedPaths = new Set();
114
176
  let unchangedFiles = 0;
@@ -122,45 +184,87 @@ function runCodeIndexMode(runtime) {
122
184
  }]));
123
185
  const currentPaths = new Set(currentFingerprints.map((file) => file.path));
124
186
  deletedPaths = indexedRows.map((row) => String(row.path)).filter((filePath) => !currentPaths.has(filePath));
125
- reindexedFiles = [];
187
+ reindexedFingerprints = [];
126
188
  for (const file of currentFingerprints) {
127
189
  const existing = indexed.get(file.path);
128
190
  if (existing && existing.mtimeMs === file.mtimeMs && existing.size === file.size) {
129
191
  unchangedFiles += 1;
130
192
  continue;
131
193
  }
132
- reindexedFiles.push(runtime.readCodeFile(file.path, parserMode));
194
+ reindexedFingerprints.push(file);
133
195
  }
134
196
  }
135
197
  else {
136
198
  deletedPaths = [];
137
- reindexedFiles = discoveredFiles.map((filePath) => runtime.readCodeFile(filePath, parserMode));
199
+ reindexedFingerprints = currentFingerprints;
138
200
  }
139
- database.exec("BEGIN");
140
- if (!incremental)
141
- statements.insertMeta.run("created_at", new Date().toISOString());
142
- (0, schema_1.writeIndexMetadata)(scopes, parserMode, statements);
143
- for (const filePath of deletedPaths)
144
- (0, schema_1.removeIndexedFile)(filePath, statements);
145
- for (const file of reindexedFiles) {
146
- if (incremental && indexedPaths.has(file.path))
147
- (0, schema_1.removeIndexedFile)(file.path, statements);
148
- runtime.indexCodeFile(file, statements);
201
+ const nativeIncrementalRequested = engine === "native-rust"
202
+ || shouldUseNativeIncrementalForAuto(requestedEngine, runtime, reindexedFingerprints.length + deletedPaths.length);
203
+ if (nativeIncrementalRequested) {
204
+ if (!runtime.nativeCodeIndexAvailable()) {
205
+ runtime.fail("--code-index-engine native-rust --incremental requires PROJECT_LIBRARIAN_NATIVE_INDEXER or a packaged native helper.");
206
+ }
207
+ if (!runtime.nativeCodeIndexIncrementalEligible(reindexedFingerprints, parserMode)) {
208
+ if (engine === "native-rust") {
209
+ runtime.fail("--code-index-engine native-rust --incremental only supports native-eligible parser profiles; use --code-index-engine typescript for this incremental update.");
210
+ }
211
+ }
212
+ else {
213
+ database.close();
214
+ database = undefined;
215
+ measurePhase(phaseTimings, "native_helper_ms", () => runtime.runNativeCodeIndexIncrementalMode({
216
+ databasePath,
217
+ deletedPaths,
218
+ discoveredFiles,
219
+ parserMode,
220
+ requestedEngine,
221
+ reindexedFiles: reindexedFingerprints,
222
+ scopes,
223
+ unchangedFiles,
224
+ }));
225
+ phaseTimings.total_ms = Number(elapsedMs(totalStarted).toFixed(3));
226
+ emitCodeIndexPhaseTimings(phaseTimings);
227
+ return;
228
+ }
149
229
  }
150
- database.exec("COMMIT");
230
+ const reindexedFiles = measurePhase(phaseTimings, "read_files_ms", () => reindexedFingerprints.map((file) => runtime.readCodeFile(file.path, parserMode, file)));
231
+ const activeDatabase = database;
232
+ const statements = (0, schema_1.createIndexStatements)(activeDatabase);
233
+ measurePhase(phaseTimings, "sqlite_write_ms", () => {
234
+ configureBulkWriteConnection(activeDatabase);
235
+ activeDatabase.exec("BEGIN");
236
+ if (!incremental)
237
+ statements.insertMeta.run("created_at", new Date().toISOString());
238
+ (0, schema_1.writeIndexMetadata)(scopes, parserMode, statements);
239
+ for (const filePath of deletedPaths)
240
+ (0, schema_1.removeIndexedFile)(filePath, statements);
241
+ for (const file of reindexedFiles) {
242
+ if (incremental && indexedPaths.has(file.path))
243
+ (0, schema_1.removeIndexedFile)(file.path, statements);
244
+ runtime.indexCodeFile(file, statements);
245
+ }
246
+ if (!incremental)
247
+ (0, schema_1.createSecondaryIndexes)(activeDatabase);
248
+ activeDatabase.exec("COMMIT");
249
+ });
250
+ phaseTimings.total_ms = Number(elapsedMs(totalStarted).toFixed(3));
151
251
  console.log("Project wiki code evidence index complete.");
152
252
  console.log(`database: ${databasePath.relativePath}`);
153
253
  console.log(`mode: ${incremental ? "incremental" : "full"}`);
254
+ console.log(`engine: ${engine}`);
255
+ if (requestedEngine === "auto")
256
+ console.log("engine_selection: auto");
154
257
  console.log(`parser_mode: ${parserMode}`);
155
258
  console.log(`scopes: ${scopes.join(", ")}`);
156
259
  console.log(`files: ${currentFingerprints.length}`);
157
260
  console.log(`reindexed_files: ${reindexedFiles.length}`);
158
261
  console.log(`deleted_files: ${deletedPaths.length}`);
159
262
  console.log(`unchanged_files: ${unchangedFiles}`);
263
+ emitCodeIndexPhaseTimings(phaseTimings);
160
264
  }
161
265
  catch (error) {
162
266
  try {
163
- database.exec("ROLLBACK");
267
+ database?.exec("ROLLBACK");
164
268
  }
165
269
  catch {
166
270
  // Ignore rollback failures after setup errors.
@@ -168,7 +272,7 @@ function runCodeIndexMode(runtime) {
168
272
  throw error;
169
273
  }
170
274
  finally {
171
- database.close();
275
+ database?.close();
172
276
  }
173
277
  }
174
278
  function runCodeQueryMode(runtime) {
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.supportedNativeHelperTriples = exports.nativeCodeIndexHelperTargets = void 0;
4
+ exports.nativeCodeIndexHelperTargetForTriple = nativeCodeIndexHelperTargetForTriple;
5
+ exports.nativeCodeIndexHelperPlatformFromTriple = nativeCodeIndexHelperPlatformFromTriple;
6
+ exports.nativeCodeIndexHelperTripleForPlatform = nativeCodeIndexHelperTripleForPlatform;
7
+ exports.nativeCodeIndexHelperTargets = [
8
+ {
9
+ arch: "arm64",
10
+ architecture: "arm64",
11
+ format: "mach-o",
12
+ libc: "",
13
+ platform: "darwin",
14
+ runner: "macos-14",
15
+ rustTarget: "aarch64-apple-darwin",
16
+ triple: "darwin-arm64",
17
+ },
18
+ {
19
+ arch: "x64",
20
+ architecture: "x64",
21
+ format: "mach-o",
22
+ libc: "",
23
+ platform: "darwin",
24
+ runner: "macos-15-intel",
25
+ rustTarget: "x86_64-apple-darwin",
26
+ triple: "darwin-x64",
27
+ },
28
+ {
29
+ arch: "arm64",
30
+ architecture: "arm64",
31
+ format: "elf",
32
+ libc: "",
33
+ platform: "linux",
34
+ runner: "ubuntu-24.04-arm",
35
+ rustTarget: "aarch64-unknown-linux-gnu",
36
+ triple: "linux-arm64",
37
+ },
38
+ {
39
+ arch: "arm64",
40
+ architecture: "arm64",
41
+ format: "elf",
42
+ libc: "musl",
43
+ platform: "linux",
44
+ runner: "ubuntu-24.04-arm",
45
+ rustTarget: "aarch64-unknown-linux-musl",
46
+ triple: "linux-arm64-musl",
47
+ },
48
+ {
49
+ arch: "x64",
50
+ architecture: "x64",
51
+ format: "elf",
52
+ libc: "",
53
+ platform: "linux",
54
+ runner: "ubuntu-latest",
55
+ rustTarget: "x86_64-unknown-linux-gnu",
56
+ triple: "linux-x64",
57
+ },
58
+ {
59
+ arch: "x64",
60
+ architecture: "x64",
61
+ format: "elf",
62
+ libc: "musl",
63
+ platform: "linux",
64
+ runner: "ubuntu-latest",
65
+ rustTarget: "x86_64-unknown-linux-musl",
66
+ triple: "linux-x64-musl",
67
+ },
68
+ {
69
+ arch: "arm64",
70
+ architecture: "arm64",
71
+ format: "pe",
72
+ libc: "",
73
+ platform: "win32",
74
+ runner: "windows-11-arm",
75
+ rustTarget: "aarch64-pc-windows-msvc",
76
+ triple: "win32-arm64",
77
+ },
78
+ {
79
+ arch: "x64",
80
+ architecture: "x64",
81
+ format: "pe",
82
+ libc: "",
83
+ platform: "win32",
84
+ runner: "windows-latest",
85
+ rustTarget: "x86_64-pc-windows-msvc",
86
+ triple: "win32-x64",
87
+ },
88
+ ];
89
+ exports.supportedNativeHelperTriples = exports.nativeCodeIndexHelperTargets.map((target) => target.triple);
90
+ function nativeCodeIndexHelperTargetForTriple(triple) {
91
+ return exports.nativeCodeIndexHelperTargets.find((target) => target.triple === triple);
92
+ }
93
+ function nativeCodeIndexHelperPlatformFromTriple(triple) {
94
+ return nativeCodeIndexHelperTargetForTriple(triple)?.platform ?? String(triple).split("-")[0] ?? "";
95
+ }
96
+ function nativeCodeIndexHelperTripleForPlatform(platform, arch, libc = "") {
97
+ const normalizedLibc = platform === "linux" && libc === "musl" ? "musl" : "";
98
+ return exports.nativeCodeIndexHelperTargets.find((target) => (target.platform === platform && target.arch === arch && target.libc === normalizedLibc))?.triple ?? `${platform}-${arch}`;
99
+ }
@@ -0,0 +1,292 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.nativeCodeIndexHelperPlatformTriple = nativeCodeIndexHelperPlatformTriple;
37
+ exports.nativeCodeIndexHelperBinaryName = nativeCodeIndexHelperBinaryName;
38
+ exports.packagedNativeCodeIndexHelperPath = packagedNativeCodeIndexHelperPath;
39
+ exports.requireNativeCodeIndexHelperPath = requireNativeCodeIndexHelperPath;
40
+ exports.nativeCodeIndexHelperAvailability = nativeCodeIndexHelperAvailability;
41
+ exports.nativeCodeIndexHelperAvailable = nativeCodeIndexHelperAvailable;
42
+ exports.buildNativeCodeIndexJob = buildNativeCodeIndexJob;
43
+ exports.runNativeCodeIndexHelper = runNativeCodeIndexHelper;
44
+ exports.runNativeCodeIndexRowsHelper = runNativeCodeIndexRowsHelper;
45
+ const childProcess = __importStar(require("node:child_process"));
46
+ const fs = __importStar(require("node:fs"));
47
+ const os = __importStar(require("node:os"));
48
+ const path = __importStar(require("node:path"));
49
+ const native_helper_matrix_1 = require("./native-helper-matrix");
50
+ const workspace_1 = require("../workspace");
51
+ const supportedNativeHelperTripleSet = new Set(native_helper_matrix_1.supportedNativeHelperTriples);
52
+ function nativeCodeIndexLinuxLibcVariant(platform = process.platform) {
53
+ if (platform !== "linux")
54
+ return "";
55
+ const report = process.report;
56
+ if (!report || typeof report.getReport !== "function")
57
+ return "";
58
+ try {
59
+ return report.getReport().header?.glibcVersionRuntime ? "glibc" : "musl";
60
+ }
61
+ catch {
62
+ return "";
63
+ }
64
+ }
65
+ function nativeCodeIndexHelperPlatformTriple(platform = process.platform, arch = process.arch, libc = nativeCodeIndexLinuxLibcVariant(platform)) {
66
+ return (0, native_helper_matrix_1.nativeCodeIndexHelperTripleForPlatform)(platform, arch, libc);
67
+ }
68
+ function nativeCodeIndexHelperBinaryName(platform = process.platform) {
69
+ return platform === "win32" ? "project-librarian-indexer.exe" : "project-librarian-indexer";
70
+ }
71
+ function nativeCodeIndexHelperPackageRoot(options = {}) {
72
+ return path.resolve(options.packageRoot ?? path.join(__dirname, ".."));
73
+ }
74
+ function packagedNativeCodeIndexHelperPath(options = {}) {
75
+ const platform = options.platform ?? process.platform;
76
+ const arch = options.arch ?? process.arch;
77
+ return path.join(nativeCodeIndexHelperPackageRoot(options), "native", nativeCodeIndexHelperPlatformTriple(platform, arch, options.libc), nativeCodeIndexHelperBinaryName(platform));
78
+ }
79
+ function configuredHelperPath(options = {}) {
80
+ const optionPath = (options.helperPath ?? "").trim();
81
+ if (optionPath)
82
+ return { helperPath: optionPath, source: "option" };
83
+ const envPath = ((options.env ?? process.env).PROJECT_LIBRARIAN_NATIVE_INDEXER ?? "").trim();
84
+ if (envPath)
85
+ return { helperPath: envPath, source: "environment" };
86
+ return { helperPath: "", source: "missing" };
87
+ }
88
+ function helperPathLabel(source) {
89
+ if (source === "option")
90
+ return "native helper path";
91
+ if (source === "environment")
92
+ return "PROJECT_LIBRARIAN_NATIVE_INDEXER";
93
+ return "packaged native helper";
94
+ }
95
+ function requireUsableHelperPath(helperPath, source) {
96
+ const label = helperPathLabel(source);
97
+ if (!path.isAbsolute(helperPath)) {
98
+ throw new Error(`${label} must be an absolute path: ${helperPath}`);
99
+ }
100
+ const resolved = path.resolve(helperPath);
101
+ if (!fs.existsSync(resolved)) {
102
+ throw new Error(`${label} does not exist: ${resolved}`);
103
+ }
104
+ const stat = fs.statSync(resolved);
105
+ if (!stat.isFile()) {
106
+ throw new Error(`${label} must point to an executable file: ${resolved}`);
107
+ }
108
+ fs.accessSync(resolved, fs.constants.X_OK);
109
+ return resolved;
110
+ }
111
+ function requireNativeCodeIndexHelperPath(options = {}) {
112
+ const availability = nativeCodeIndexHelperAvailability(options);
113
+ if (availability.available)
114
+ return availability.helperPath;
115
+ throw new Error(availability.reason);
116
+ }
117
+ function nativeCodeIndexHelperAvailability(options = {}) {
118
+ const platform = options.platform ?? process.platform;
119
+ const arch = options.arch ?? process.arch;
120
+ const platformTriple = nativeCodeIndexHelperPlatformTriple(platform, arch, options.libc);
121
+ const packagedHelperPath = packagedNativeCodeIndexHelperPath({ ...options, arch, platform });
122
+ const configured = configuredHelperPath(options);
123
+ if (configured.helperPath) {
124
+ try {
125
+ return {
126
+ available: true,
127
+ helperPath: requireUsableHelperPath(configured.helperPath, configured.source),
128
+ packagedHelperPath,
129
+ platformTriple,
130
+ reason: "",
131
+ source: configured.source,
132
+ };
133
+ }
134
+ catch (error) {
135
+ return {
136
+ available: false,
137
+ helperPath: "",
138
+ packagedHelperPath,
139
+ platformTriple,
140
+ reason: error instanceof Error ? error.message : String(error),
141
+ source: configured.source,
142
+ };
143
+ }
144
+ }
145
+ if (!supportedNativeHelperTripleSet.has(platformTriple)) {
146
+ return {
147
+ available: false,
148
+ helperPath: "",
149
+ packagedHelperPath,
150
+ platformTriple,
151
+ reason: `packaged native code index helper does not support this platform: ${platformTriple}; set PROJECT_LIBRARIAN_NATIVE_INDEXER to a compatible helper path`,
152
+ source: "missing",
153
+ };
154
+ }
155
+ if (fs.existsSync(packagedHelperPath)) {
156
+ try {
157
+ return {
158
+ available: true,
159
+ helperPath: requireUsableHelperPath(packagedHelperPath, "packaged"),
160
+ packagedHelperPath,
161
+ platformTriple,
162
+ reason: "",
163
+ source: "packaged",
164
+ };
165
+ }
166
+ catch (error) {
167
+ return {
168
+ available: false,
169
+ helperPath: "",
170
+ packagedHelperPath,
171
+ platformTriple,
172
+ reason: error instanceof Error ? error.message : String(error),
173
+ source: "packaged",
174
+ };
175
+ }
176
+ }
177
+ return {
178
+ available: false,
179
+ helperPath: "",
180
+ packagedHelperPath,
181
+ platformTriple,
182
+ reason: `--code-index-engine native-rust requires PROJECT_LIBRARIAN_NATIVE_INDEXER or a packaged native helper at ${packagedHelperPath}`,
183
+ source: "missing",
184
+ };
185
+ }
186
+ function nativeCodeIndexHelperAvailable(options = {}) {
187
+ return nativeCodeIndexHelperAvailability(options).available;
188
+ }
189
+ function writeJobManifest(job) {
190
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "project-librarian-native-indexer-"));
191
+ const manifestPath = path.join(tmpDir, "job.json");
192
+ fs.writeFileSync(manifestPath, `${JSON.stringify(job)}\n`);
193
+ return manifestPath;
194
+ }
195
+ function buildNativeCodeIndexJob(input) {
196
+ const { mode = "full", ...rest } = input;
197
+ return {
198
+ abi_version: 1,
199
+ engine: "native-rust",
200
+ mode,
201
+ project_root: (0, workspace_1.normalizePath)(workspace_1.root),
202
+ ...rest,
203
+ };
204
+ }
205
+ function validateNativeCodeIndexSummary(job, summary) {
206
+ if (summary.engine !== job.engine) {
207
+ throw new Error(`native code index helper summary engine mismatch: expected ${job.engine}, got ${summary.engine ?? "(missing)"}`);
208
+ }
209
+ if (summary.schema_version !== job.schema_version) {
210
+ throw new Error(`native code index helper summary schema mismatch: expected ${job.schema_version}, got ${summary.schema_version ?? "(missing)"}`);
211
+ }
212
+ if (summary.mode !== job.mode) {
213
+ throw new Error(`native code index helper summary mode mismatch: expected ${job.mode}, got ${summary.mode ?? "(missing)"}`);
214
+ }
215
+ const database = summary.database ?? summary.database_path ?? "";
216
+ if (path.resolve(database) !== path.resolve(job.database_path)) {
217
+ throw new Error(`native code index helper summary database mismatch: expected ${job.database_path}, got ${database || "(missing)"}`);
218
+ }
219
+ if (!Number.isInteger(summary.files) || (summary.files ?? -1) < 0) {
220
+ throw new Error("native code index helper summary files must be a non-negative integer");
221
+ }
222
+ if (summary.native_files !== undefined && summary.native_files !== job.files.length) {
223
+ throw new Error(`native code index helper summary native_files mismatch: expected ${job.files.length}, got ${summary.native_files}`);
224
+ }
225
+ if ((summary.unsupported_profiles ?? []).length > 0) {
226
+ throw new Error(`native code index helper reported unsupported profiles: ${(summary.unsupported_profiles ?? []).join(", ")}`);
227
+ }
228
+ return summary;
229
+ }
230
+ function runNativeCodeIndexHelper(job, options = {}) {
231
+ const helperPath = requireNativeCodeIndexHelperPath(options);
232
+ const manifestPath = writeJobManifest(job);
233
+ try {
234
+ const result = childProcess.spawnSync(helperPath, ["--manifest", manifestPath], {
235
+ cwd: workspace_1.root,
236
+ encoding: "utf8",
237
+ stdio: ["ignore", "pipe", "pipe"],
238
+ });
239
+ if (result.status !== 0) {
240
+ const detail = (result.stderr || result.stdout || "").trim();
241
+ throw new Error(`native code index helper failed (${result.status ?? "signal"}): ${detail || helperPath}`);
242
+ }
243
+ let summary;
244
+ try {
245
+ summary = JSON.parse(result.stdout || "{}");
246
+ }
247
+ catch (error) {
248
+ const message = error instanceof Error ? error.message : String(error);
249
+ throw new Error(`native code index helper returned invalid JSON: ${message}`);
250
+ }
251
+ return validateNativeCodeIndexSummary(job, summary);
252
+ }
253
+ finally {
254
+ fs.rmSync(path.dirname(manifestPath), { recursive: true, force: true });
255
+ }
256
+ }
257
+ function validateNativeRows(value) {
258
+ if (typeof value !== "object" || value === null) {
259
+ throw new Error("native code index helper row stream must be an object");
260
+ }
261
+ const rows = value;
262
+ for (const key of ["configs", "edges", "files", "imports", "routes", "symbols"]) {
263
+ if (!Array.isArray(rows[key])) {
264
+ throw new Error(`native code index helper row stream missing array: ${key}`);
265
+ }
266
+ }
267
+ return rows;
268
+ }
269
+ function runNativeCodeIndexRowsHelper(job, options = {}) {
270
+ if (job.output_mode !== "row-stream") {
271
+ throw new Error("native row helper requires output_mode row-stream");
272
+ }
273
+ if (!job.rows_path) {
274
+ throw new Error("native row helper requires rows_path");
275
+ }
276
+ const summary = runNativeCodeIndexHelper(job, options);
277
+ let rowsJson = "";
278
+ try {
279
+ rowsJson = fs.readFileSync(job.rows_path, "utf8");
280
+ }
281
+ catch (error) {
282
+ const message = error instanceof Error ? error.message : String(error);
283
+ throw new Error(`native code index helper did not write rows: ${message}`);
284
+ }
285
+ try {
286
+ return { rows: validateNativeRows(JSON.parse(rowsJson)), summary };
287
+ }
288
+ catch (error) {
289
+ const message = error instanceof Error ? error.message : String(error);
290
+ throw new Error(`native code index helper returned invalid row stream: ${message}`);
291
+ }
292
+ }