gitnexus 1.6.7 → 1.6.8-rc.10

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 (82) hide show
  1. package/README.md +20 -0
  2. package/dist/cli/analyze-config.js +39 -0
  3. package/dist/cli/analyze.d.ts +16 -0
  4. package/dist/cli/analyze.js +34 -6
  5. package/dist/cli/clean.d.ts +1 -0
  6. package/dist/cli/clean.js +43 -1
  7. package/dist/cli/eval-server.js +43 -0
  8. package/dist/cli/help-i18n.js +6 -0
  9. package/dist/cli/i18n/en.d.ts +11 -0
  10. package/dist/cli/i18n/en.js +11 -0
  11. package/dist/cli/i18n/resources.d.ts +22 -0
  12. package/dist/cli/i18n/zh-CN.d.ts +11 -0
  13. package/dist/cli/i18n/zh-CN.js +11 -0
  14. package/dist/cli/index.js +9 -0
  15. package/dist/cli/list.js +12 -0
  16. package/dist/cli/optional-grammars.d.ts +6 -8
  17. package/dist/cli/optional-grammars.js +8 -11
  18. package/dist/cli/status.js +26 -5
  19. package/dist/cli/tool.d.ts +5 -0
  20. package/dist/cli/tool.js +5 -0
  21. package/dist/core/embeddings/embedder.js +4 -0
  22. package/dist/core/embeddings/embedding-pipeline.js +27 -16
  23. package/dist/core/embeddings/onnxruntime-common-resolver.d.ts +6 -0
  24. package/dist/core/embeddings/onnxruntime-common-resolver.js +130 -0
  25. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  26. package/dist/core/group/extractors/http-patterns/java.js +3 -50
  27. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  28. package/dist/core/group/extractors/include-extractor.js +7 -7
  29. package/dist/core/ingestion/language-provider.d.ts +25 -2
  30. package/dist/core/ingestion/languages/c-cpp.js +11 -4
  31. package/dist/core/ingestion/languages/java.js +3 -0
  32. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  33. package/dist/core/ingestion/languages/kotlin.js +5 -1
  34. package/dist/core/ingestion/parsing-processor.js +21 -0
  35. package/dist/core/ingestion/pipeline-phases/routes.js +64 -14
  36. package/dist/core/ingestion/pipeline.d.ts +9 -0
  37. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +50 -0
  38. package/dist/core/ingestion/route-extractors/spring-shared.js +80 -0
  39. package/dist/core/ingestion/route-extractors/spring.d.ts +35 -0
  40. package/dist/core/ingestion/route-extractors/spring.js +136 -0
  41. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  42. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  43. package/dist/core/ingestion/workers/parse-worker.d.ts +10 -0
  44. package/dist/core/ingestion/workers/parse-worker.js +27 -52
  45. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  46. package/dist/core/ingestion/workers/post-result.js +87 -0
  47. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  48. package/dist/core/ingestion/workers/result-merge.js +43 -0
  49. package/dist/core/ingestion/workers/worker-pool.d.ts +6 -0
  50. package/dist/core/ingestion/workers/worker-pool.js +28 -14
  51. package/dist/core/lbug/lbug-adapter.d.ts +19 -0
  52. package/dist/core/lbug/lbug-adapter.js +56 -1
  53. package/dist/core/run-analyze.d.ts +45 -0
  54. package/dist/core/run-analyze.js +175 -24
  55. package/dist/core/tree-sitter/parser-loader.js +5 -4
  56. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  57. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  58. package/dist/mcp/core/embedder.js +4 -0
  59. package/dist/mcp/local/local-backend.d.ts +55 -2
  60. package/dist/mcp/local/local-backend.js +363 -25
  61. package/dist/mcp/tools.js +32 -0
  62. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  63. package/dist/server/analyze-worker-ipc.js +16 -0
  64. package/dist/server/analyze-worker.js +7 -1
  65. package/dist/storage/branch-index.d.ts +52 -0
  66. package/dist/storage/branch-index.js +65 -0
  67. package/dist/storage/git.d.ts +11 -0
  68. package/dist/storage/git.js +28 -0
  69. package/dist/storage/parse-cache.js +3 -0
  70. package/dist/storage/repo-manager.d.ts +57 -2
  71. package/dist/storage/repo-manager.js +132 -18
  72. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +24 -4
  73. package/hooks/claude/gitnexus-hook.cjs +18 -3
  74. package/package.json +2 -2
  75. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  76. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  77. package/vendor/tree-sitter-c/package.json +1 -1
  78. package/vendor/tree-sitter-dart/package.json +1 -1
  79. package/vendor/tree-sitter-kotlin/package.json +1 -1
  80. package/vendor/tree-sitter-proto/package.json +1 -1
  81. package/vendor/tree-sitter-swift/package.json +1 -1
  82. package/scripts/materialize-vendor-grammars.cjs +0 -97
@@ -110,10 +110,20 @@ function hasGitNexusServerOwner(gitNexusDir) {
110
110
  return hasGitNexusDbLockedByGitNexusServer(path.join(gitNexusDir, 'lbug'), process.pid);
111
111
  }
112
112
 
113
+ /**
114
+ * Whether opt-in diagnostics should be written to the hook's stderr. Strict
115
+ * hook runners (e.g. Codex `PreToolUse`) validate hook output, so normal,
116
+ * non-error skip paths must stay silent unless the operator explicitly asks
117
+ * for diagnostics via GITNEXUS_DEBUG. See issue #1913.
118
+ */
119
+ function isDebugEnabled() {
120
+ return process.env.GITNEXUS_DEBUG === '1' || process.env.GITNEXUS_DEBUG === 'true';
121
+ }
122
+
113
123
  function extractAugmentContext(stderr) {
114
124
  const output = (stderr || '').trim();
115
125
  const marker = output.indexOf('[GitNexus]');
116
- const debug = process.env.GITNEXUS_DEBUG === '1' || process.env.GITNEXUS_DEBUG === 'true';
126
+ const debug = isDebugEnabled();
117
127
  if (debug && output.length > 0) {
118
128
  // Emit the FULL discarded prefix (everything before the marker, or all of
119
129
  // it when no marker is present) so suppressed diagnostics — KuzuDB lock
@@ -250,7 +260,12 @@ function handlePreToolUse(input) {
250
260
  const pattern = extractPattern(toolName, toolInput);
251
261
  if (!pattern || pattern.length < 3) return;
252
262
  if (hasGitNexusServerOwner(gitNexusDir)) {
253
- process.stderr.write('[GitNexus] augment skipped: MCP server owns DB\n');
263
+ // Normal skip path: the MCP server owns the DB, so the CLI augment would
264
+ // contend on the lock. Stay silent for strict hook runners (issue #1913);
265
+ // surface the reason only when diagnostics are explicitly requested.
266
+ if (isDebugEnabled()) {
267
+ process.stderr.write('[GitNexus] augment skipped: MCP server owns DB\n');
268
+ }
254
269
  return;
255
270
  }
256
271
 
@@ -361,7 +376,7 @@ function main() {
361
376
  const handler = handlers[input.hook_event_name || ''];
362
377
  if (handler) handler(input);
363
378
  } catch (err) {
364
- if (process.env.GITNEXUS_DEBUG) {
379
+ if (isDebugEnabled()) {
365
380
  console.error('GitNexus hook error:', (err.message || '').slice(0, 200));
366
381
  }
367
382
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.7",
3
+ "version": "1.6.8-rc.10",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",
@@ -49,7 +49,7 @@
49
49
  "test:watch": "vitest",
50
50
  "test:coverage": "vitest run --coverage",
51
51
  "test:cross-platform": "tsx scripts/run-cross-platform.ts",
52
- "postinstall": "node scripts/materialize-vendor-grammars.cjs && node scripts/build-tree-sitter-grammars.cjs",
52
+ "postinstall": "node scripts/build-tree-sitter-grammars.cjs",
53
53
  "assert-publish-coverage": "node scripts/assert-publish-grammar-coverage.cjs",
54
54
  "prepare": "node scripts/build.js",
55
55
  "prepack": "node scripts/assert-publish-grammar-coverage.cjs && node scripts/build.js"
@@ -112,6 +112,24 @@ function findCoverageProblems({ grammars }) {
112
112
  return problems;
113
113
  }
114
114
 
115
+ /**
116
+ * Stray local source-build outputs under `vendor/<name>/build/`. These would
117
+ * ship in the tarball (`files: ["vendor"]` overrides .gitignore/.npmignore) AND
118
+ * shadow the committed prebuilds — `node-gyp-build` resolves `build/Release`
119
+ * BEFORE `prebuilds/`, so a consumer on the publisher's platform would load the
120
+ * stray (possibly stale/wrong) binding instead of the curated prebuild. The
121
+ * build dir is gitignored and only appears if a maintainer source-built locally
122
+ * (e.g. on a no-prebuild platform); refuse to publish it. (#2144 review.)
123
+ */
124
+ function findStrayBuildArtifacts(vendorDir) {
125
+ if (!fs.existsSync(vendorDir)) return [];
126
+ return fs
127
+ .readdirSync(vendorDir)
128
+ .filter((d) => /^tree-sitter-/.test(d))
129
+ .filter((d) => fs.existsSync(path.join(vendorDir, d, 'build')))
130
+ .map((d) => `vendor/${d}/build`);
131
+ }
132
+
115
133
  function collectGrammars(vendorDir, shipsVendorSource) {
116
134
  if (!fs.existsSync(vendorDir)) return [];
117
135
  return fs
@@ -141,6 +159,18 @@ function main() {
141
159
  process.exit(1);
142
160
  }
143
161
 
162
+ const stray = findStrayBuildArtifacts(vendorDir);
163
+ if (stray.length > 0) {
164
+ console.error(
165
+ '[publish-guard] Refusing to publish — stray source-build output under vendor/ would\n' +
166
+ 'ship and shadow the committed prebuilds (node-gyp-build loads build/Release before\n' +
167
+ 'prebuilds/):',
168
+ );
169
+ for (const s of stray) console.error(` - ${s}`);
170
+ console.error('\nFix: remove it before packing, e.g. `rm -rf gitnexus/vendor/*/build`.');
171
+ process.exit(1);
172
+ }
173
+
144
174
  const problems = findCoverageProblems({ grammars });
145
175
  if (problems.length > 0) {
146
176
  console.error('[publish-guard] Refusing to publish — a vendored grammar would ship unusable:');
@@ -163,6 +193,7 @@ if (require.main === module) main();
163
193
 
164
194
  module.exports = {
165
195
  findCoverageProblems,
196
+ findStrayBuildArtifacts,
166
197
  filesShipsVendorSource,
167
198
  isBuildableFromSource,
168
199
  sourceBuildSet,
@@ -1,19 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Activate the vendored tree-sitter native bindings after
4
- * materialize-vendor-grammars.cjs. One registry-driven script replaces the
5
- * former per-grammar build-tree-sitter-<name>.cjs files (they were ~95%
6
- * identical).
3
+ * Activate the vendored tree-sitter native bindings IN PLACE under `vendor/`.
4
+ * One registry-driven script replaces the former per-grammar
5
+ * build-tree-sitter-<name>.cjs files (they were ~95% identical).
6
+ *
7
+ * The grammars (tree-sitter-c/dart/proto/swift/kotlin) are loaded from
8
+ * `vendor/<name>/` by absolute path at runtime (see
9
+ * src/core/tree-sitter/vendored-grammars.ts) and are NEVER copied into
10
+ * node_modules — an undeclared package under node_modules is "extraneous" to
11
+ * every subsequent npm/npx reify, which prunes/relocates it (Windows
12
+ * `EPERM: …, symlink` + a silent grammar deletion on the 2nd run; #2111/#1728).
7
13
  *
8
14
  * For each grammar the resolution order is identical:
9
- * 1. If the package isn't materialized (no binding.gyp) or the binding is
15
+ * 1. If the vendored source is absent (no binding.gyp) or the binding is
10
16
  * already built, do nothing.
11
17
  * 2. Prefer a committed prebuild for this platform-arch (toolchain-free) via
12
- * node-gyp-build — the goal once build-tree-sitter-prebuilds.yml has
13
- * populated all six tuples.
18
+ * node-gyp-build — `vendor/<name>/prebuilds/` ships all six tuples, so on a
19
+ * supported platform this returns immediately and writes nothing.
14
20
  * 3. Otherwise source-build from the vendored grammar source (binding.gyp +
15
- * src/) so parsing still works on any toolchain host — e.g. CI, before the
16
- * prebuilds land.
21
+ * src/) into `vendor/<name>/build/` (gitignored) so parsing still works on
22
+ * a toolchain host that lacks a matching prebuild.
17
23
  *
18
24
  * HARD INVARIANT: this runs in `gitnexus`'s postinstall, so it MUST NEVER throw
19
25
  * or exit non-zero — a failure for any single grammar must not break the install.
@@ -53,7 +59,7 @@ function buildGrammar(short) {
53
59
  return;
54
60
  }
55
61
 
56
- const dir = path.join(__dirname, '..', 'node_modules', `tree-sitter-${short}`);
62
+ const dir = path.join(__dirname, '..', 'vendor', `tree-sitter-${short}`);
57
63
  const bindingGyp = path.join(dir, 'binding.gyp');
58
64
  const bindingNode = path.join(dir, 'build', 'Release', `tree_sitter_${short}_binding.node`);
59
65
 
@@ -6,7 +6,7 @@
6
6
  "license": "MIT",
7
7
  "main": "bindings/node/index.js",
8
8
  "types": "bindings/node/index.d.ts",
9
- "_vendoredBy": "gitnexus - runtime package derived from tree-sitter-c@0.21.4 (tree-sitter/tree-sitter-c). HELD at 0.21.4 for ABI compatibility with the bundled tree-sitter@0.21.1 runtime (#1242/#858) — do not bump without the runtime upgrade. Vendored because upstream ships native prebuilds for only 4 of 6 platforms (no linux-arm64/win32-arm64, #2116), and tree-sitter-c is a REQUIRED grammar whose source build hard-fails `npm install` on a toolchain-less ARM host. GitNexus cross-builds all six prebuilds via .github/workflows/build-tree-sitter-prebuilds.yml; the C source (binding.gyp + src/) is ALSO vendored so build-tree-sitter-c.cjs can source-build the binding on a toolchain host when no prebuild matches (e.g. CI before prebuilds land). Copied to node_modules/ by materialize-vendor-grammars.cjs (no scripts.install here — #836/#1728).",
9
+ "_vendoredBy": "gitnexus - runtime package derived from tree-sitter-c@0.21.4 (tree-sitter/tree-sitter-c). HELD at 0.21.4 for ABI compatibility with the bundled tree-sitter@0.21.1 runtime (#1242/#858) — do not bump without the runtime upgrade. Vendored because upstream ships native prebuilds for only 4 of 6 platforms (no linux-arm64/win32-arm64, #2116), and tree-sitter-c is a REQUIRED grammar whose source build hard-fails `npm install` on a toolchain-less ARM host. GitNexus cross-builds all six prebuilds via .github/workflows/build-tree-sitter-prebuilds.yml; the C source (binding.gyp + src/) is ALSO vendored so build-tree-sitter-grammars.cjs can source-build the binding on a toolchain host when no prebuild matches (e.g. CI before prebuilds land). Loaded from vendor/ by absolute path at runtime (vendored-grammars.ts) — NEVER copied to node_modules (#2111) (no scripts.install here — #836/#1728).",
10
10
  "peerDependencies": {
11
11
  "tree-sitter": "^0.21.0"
12
12
  },
@@ -6,7 +6,7 @@
6
6
  "license": "ISC",
7
7
  "main": "bindings/node",
8
8
  "types": "bindings/node",
9
- "_vendoredBy": "gitnexus - pinned to UserNobody14/tree-sitter-dart commit 80e23c07b64494f7e21090bb3450223ef0b192f4. Copied to node_modules/ by materialize-vendor-grammars.cjs; native build via build-tree-sitter-dart.cjs (#1728, #836).",
9
+ "_vendoredBy": "gitnexus - pinned to UserNobody14/tree-sitter-dart commit 80e23c07b64494f7e21090bb3450223ef0b192f4. Loaded from vendor/ by absolute path at runtime (vendored-grammars.ts) — NEVER copied to node_modules (#2111); native build via build-tree-sitter-grammars.cjs (#1728, #836).",
10
10
  "peerDependencies": {
11
11
  "tree-sitter": "^0.21.0"
12
12
  },
@@ -6,7 +6,7 @@
6
6
  "license": "MIT",
7
7
  "main": "bindings/node/index.js",
8
8
  "types": "bindings/node/index.d.ts",
9
- "_vendoredBy": "gitnexus - runtime package derived from tree-sitter-kotlin@0.3.8 (fwcd). Unlike Swift's upstream-shipped prebuilds, upstream tree-sitter-kotlin ships SOURCE ONLY (no prebuilds/); the native prebuilds/ here are GitNexus-cross-built by .github/workflows/build-tree-sitter-prebuilds.yml. The grammar source (parser.c/scanner.c/binding.gyp + src/) is ALSO vendored so build-tree-sitter-kotlin.cjs can source-build the binding on a toolchain host when no prebuild matches (e.g. CI before prebuilds land). The generated parser.c is large (~23 MB on disk; it compresses heavily in git); once the prebuilds cover every platform-arch the source serves only as the fallback. Copied to node_modules/ by materialize-vendor-grammars.cjs (no scripts.install here — #836/#1728).",
9
+ "_vendoredBy": "gitnexus - runtime package derived from tree-sitter-kotlin@0.3.8 (fwcd). Unlike Swift's upstream-shipped prebuilds, upstream tree-sitter-kotlin ships SOURCE ONLY (no prebuilds/); the native prebuilds/ here are GitNexus-cross-built by .github/workflows/build-tree-sitter-prebuilds.yml. The grammar source (parser.c/scanner.c/binding.gyp + src/) is ALSO vendored so build-tree-sitter-grammars.cjs can source-build the binding on a toolchain host when no prebuild matches (e.g. CI before prebuilds land). The generated parser.c is large (~23 MB on disk; it compresses heavily in git); once the prebuilds cover every platform-arch the source serves only as the fallback. Loaded from vendor/ by absolute path at runtime (vendored-grammars.ts) — NEVER copied to node_modules (#2111) (no scripts.install here — #836/#1728).",
10
10
  "peerDependencies": {
11
11
  "tree-sitter": "^0.21.0"
12
12
  },
@@ -5,7 +5,7 @@
5
5
  "repository": "https://github.com/coder3101/tree-sitter-proto",
6
6
  "license": "MIT",
7
7
  "main": "bindings/node",
8
- "_vendoredBy": "gitnexus — materialized to node_modules/ by materialize-vendor-grammars.cjs; native build via build-tree-sitter-proto.cjs. Do NOT re-add dependencies or an install script (#836, #1728).",
8
+ "_vendoredBy": "gitnexus — loaded from vendor/ by absolute path at runtime (vendored-grammars.ts) — NEVER copied to node_modules (#2111); native build via build-tree-sitter-grammars.cjs. Do NOT re-add dependencies or an install script (#836, #1728).",
9
9
  "peerDependencies": {
10
10
  "tree-sitter": ">=0.21.0"
11
11
  }
@@ -9,7 +9,7 @@
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/alex-pinkus/tree-sitter-swift.git"
11
11
  },
12
- "_vendoredBy": "gitnexus - runtime package derived from official tree-sitter-swift@0.7.1 (gitHead 88bfd19a89be9d0481b14566fb6160cccea2fe0a). Unified with Dart/Proto/Kotlin/C: the grammar source (parser.c/scanner.c/binding.gyp + src/) is ALSO vendored so build-tree-sitter-grammars.cjs can source-build the binding on a toolchain host when no prebuild matches (e.g. CI before prebuilds land); src/parser.c is the ABI-14 default (~18 MB on disk, compresses heavily in git — the upstream parser_abi13.c alternate is not vendored). The native prebuilds/ are GitNexus-cross-built by .github/workflows/build-tree-sitter-prebuilds.yml (originally upstream-shipped). Build activation runs via gitnexus/scripts/build-tree-sitter-grammars.cjs after materialize-vendor-grammars.cjs (no scripts.install here — avoids #836 / #1728).",
12
+ "_vendoredBy": "gitnexus - runtime package derived from official tree-sitter-swift@0.7.1 (gitHead 88bfd19a89be9d0481b14566fb6160cccea2fe0a). Unified with Dart/Proto/Kotlin/C: the grammar source (parser.c/scanner.c/binding.gyp + src/) is ALSO vendored so build-tree-sitter-grammars.cjs can source-build the binding on a toolchain host when no prebuild matches (e.g. CI before prebuilds land); src/parser.c is the ABI-14 default (~18 MB on disk, compresses heavily in git — the upstream parser_abi13.c alternate is not vendored). The native prebuilds/ are GitNexus-cross-built by .github/workflows/build-tree-sitter-prebuilds.yml (originally upstream-shipped). Build activation runs via gitnexus/scripts/build-tree-sitter-grammars.cjs (no scripts.install here — avoids #836 / #1728).",
13
13
  "peerDependencies": {
14
14
  "tree-sitter": "^0.21.1 || ^0.22.1"
15
15
  },
@@ -1,97 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Copy vendored tree-sitter grammars into node_modules/ using real files (fs.cpSync).
4
- *
5
- * Published gitnexus used to declare these as optionalDependencies with
6
- * `file:./vendor/...`, which makes npm symlink/junction vendor → node_modules on
7
- * install. Windows without Developer Mode often fails with EPERM (#1728).
8
- *
9
- * Vendor trees stay read-only in gitnexus/vendor/; build artifacts must only
10
- * land under node_modules/ (see #836).
11
- */
12
- const fs = require('fs');
13
- const path = require('path');
14
-
15
- const ROOT = path.join(__dirname, '..');
16
- // tree-sitter-c is a REQUIRED grammar that we vendor prebuild-only purely to
17
- // close upstream's ARM prebuild gap (#2116) — it needs no toolchain and is not a
18
- // language the user opts out of, so it is always materialized, even under
19
- // GITNEXUS_SKIP_OPTIONAL_GRAMMARS. The rest are optional (user-skippable, and
20
- // Dart/Proto compile from source) and honor the skip flag.
21
- const REQUIRED_VENDORED = ['tree-sitter-c'];
22
- const OPTIONAL_VENDORED = [
23
- 'tree-sitter-dart',
24
- 'tree-sitter-proto',
25
- 'tree-sitter-swift',
26
- 'tree-sitter-kotlin',
27
- ];
28
-
29
- const skipOptional = process.env.GITNEXUS_SKIP_OPTIONAL_GRAMMARS === '1';
30
- if (skipOptional) {
31
- console.warn(
32
- '[gitnexus] GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1: skipping optional Dart/Proto/Swift/Kotlin materialize (required C is still materialized).',
33
- );
34
- }
35
- const VENDORED_GRAMMARS = skipOptional
36
- ? REQUIRED_VENDORED
37
- : [...REQUIRED_VENDORED, ...OPTIONAL_VENDORED];
38
-
39
- for (const name of VENDORED_GRAMMARS) {
40
- const src = path.join(ROOT, 'vendor', name);
41
- const dest = path.join(ROOT, 'node_modules', name);
42
-
43
- if (!fs.existsSync(src)) {
44
- console.warn(`[gitnexus] vendor/${name} missing; skipping materialize.`);
45
- continue;
46
- }
47
-
48
- // Sequence: copy src → partial; rename dest → backup; rename partial → dest;
49
- // remove backup. If any step fails, restore from backup so a previously-
50
- // materialized grammar is never lost. Targets the #1728 EPERM scenario plus
51
- // narrower failure modes (Windows AV scanner racing on rename, EBUSY mid-swap).
52
- const partial = `${dest}.materialize-tmp`;
53
- const backup = `${dest}.materialize-bak`;
54
- try {
55
- fs.mkdirSync(path.join(ROOT, 'node_modules'), { recursive: true });
56
- fs.rmSync(partial, { recursive: true, force: true });
57
- fs.rmSync(backup, { recursive: true, force: true });
58
- fs.cpSync(src, partial, { recursive: true, verbatim: true });
59
- if (fs.existsSync(dest)) {
60
- fs.renameSync(dest, backup);
61
- }
62
- try {
63
- fs.renameSync(partial, dest);
64
- } catch (renameErr) {
65
- // Best-effort rollback: restore the previous dest from backup.
66
- let restored = false;
67
- if (fs.existsSync(backup)) {
68
- try {
69
- fs.renameSync(backup, dest);
70
- restored = true;
71
- } catch {
72
- // Rollback also failed — dest is now missing. Leave the backup in
73
- // place (the catch below will NOT remove it) and surface where it is.
74
- }
75
- }
76
- if (!restored && fs.existsSync(backup)) {
77
- console.warn(
78
- `[gitnexus] CRITICAL: could not materialize vendor/${name} AND could not restore the ` +
79
- `previous node_modules/${name}. A recoverable copy remains at ${backup} — ` +
80
- `restore it (e.g. \`mv ${backup} ${dest}\`) or reinstall to recover ${name}.`,
81
- );
82
- }
83
- throw renameErr;
84
- }
85
- fs.rmSync(backup, { recursive: true, force: true });
86
- } catch (err) {
87
- // Fail-soft: a single locked/inaccessible file (common on Windows) must not
88
- // abort the whole gitnexus install. Matches build-tree-sitter-*.cjs pattern.
89
- // Only remove the scratch `partial`; never the `backup` (it may be the sole
90
- // recoverable copy after a failed rollback above).
91
- fs.rmSync(partial, { recursive: true, force: true });
92
- console.warn(`[gitnexus] Could not materialize vendor/${name}: ${err.message}`);
93
- console.warn(
94
- `[gitnexus] ${name} parsing will be unavailable. Other functionality is unaffected.`,
95
- );
96
- }
97
- }