@danielblomma/cortex-mcp 2.1.1 → 2.2.0

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 (150) hide show
  1. package/README.md +72 -24
  2. package/bin/cortex.mjs +50 -21
  3. package/mcp-registry-submission.json +63 -0
  4. package/package.json +19 -5
  5. package/scaffold/AGENTS.md +13 -9
  6. package/scaffold/CLAUDE.md +13 -9
  7. package/scaffold/docs/architecture.md +1 -1
  8. package/scaffold/mcp/build.mjs +92 -0
  9. package/scaffold/mcp/package.json +4 -4
  10. package/scaffold/mcp/src/cli/query.ts +455 -0
  11. package/scaffold/mcp/src/embed.ts +181 -77
  12. package/scaffold/mcp/src/embedScheduler.ts +509 -0
  13. package/scaffold/mcp/src/embeddings.ts +31 -30
  14. package/scaffold/mcp/src/graphCsv.ts +65 -0
  15. package/scaffold/mcp/src/jsonl.ts +69 -14
  16. package/scaffold/mcp/src/loadGraph.ts +276 -40
  17. package/scaffold/mcp/src/lruCache.ts +41 -0
  18. package/scaffold/mcp/src/searchCore.ts +1 -1
  19. package/scaffold/mcp/src/searchResults.ts +3 -3
  20. package/scaffold/mcp/src/types.ts +6 -1
  21. package/scaffold/mcp/tests/embed-scheduler.test.mjs +474 -0
  22. package/scaffold/mcp/tests/embedding-jsonl.test.mjs +39 -0
  23. package/scaffold/mcp/tests/graph-bulk-load.test.mjs +258 -0
  24. package/scaffold/mcp/tests/graph-csv.test.mjs +118 -0
  25. package/scaffold/mcp/tests/lru-cache.test.mjs +37 -0
  26. package/scaffold/mcp/tests/query-cli.test.mjs +115 -0
  27. package/scaffold/mcp/tests/vector-index.test.mjs +109 -0
  28. package/scaffold/mcp/tsconfig.json +3 -1
  29. package/scaffold/scripts/bootstrap.sh +37 -4
  30. package/scaffold/scripts/dashboard.mjs +1 -1
  31. package/scaffold/scripts/doctor.sh +9 -8
  32. package/scaffold/scripts/embed.sh +3 -2
  33. package/scaffold/scripts/ingest-parsers.mjs +387 -0
  34. package/scaffold/scripts/ingest-worker.mjs +52 -0
  35. package/scaffold/scripts/ingest.mjs +550 -378
  36. package/scaffold/scripts/load-ryu.sh +3 -2
  37. package/scaffold/scripts/memory-compile.mjs +5 -2
  38. package/scaffold/scripts/memory-lint.mjs +5 -2
  39. package/scaffold/scripts/parsers/csharp.mjs +2 -1
  40. package/scaffold/scripts/parsers/javascript/ast.mjs +160 -8
  41. package/scaffold/scripts/parsers/javascript/chunks.mjs +129 -22
  42. package/scaffold/scripts/parsers/javascript/imports.mjs +38 -4
  43. package/scaffold/scripts/parsers/vbnet.mjs +2 -1
  44. package/scaffold/scripts/status.sh +50 -14
  45. package/scaffold/scripts/parsers/node_modules/.package-lock.json +0 -56
  46. package/scaffold/scripts/parsers/node_modules/acorn/CHANGELOG.md +0 -972
  47. package/scaffold/scripts/parsers/node_modules/acorn/LICENSE +0 -21
  48. package/scaffold/scripts/parsers/node_modules/acorn/README.md +0 -301
  49. package/scaffold/scripts/parsers/node_modules/acorn/bin/acorn +0 -4
  50. package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.d.mts +0 -883
  51. package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.d.ts +0 -883
  52. package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.js +0 -6295
  53. package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.mjs +0 -6266
  54. package/scaffold/scripts/parsers/node_modules/acorn/dist/bin.js +0 -90
  55. package/scaffold/scripts/parsers/node_modules/acorn/package.json +0 -50
  56. package/scaffold/scripts/parsers/node_modules/acorn-typescript/CHANGELOG.md +0 -421
  57. package/scaffold/scripts/parsers/node_modules/acorn-typescript/LICENSE +0 -21
  58. package/scaffold/scripts/parsers/node_modules/acorn-typescript/README.md +0 -81
  59. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/error.d.ts +0 -103
  60. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/error.js +0 -78
  61. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/error.js.map +0 -1
  62. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/decorators.d.ts +0 -167
  63. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/decorators.js +0 -75
  64. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/decorators.js.map +0 -1
  65. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/import-assertions.d.ts +0 -177
  66. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/import-assertions.js +0 -56
  67. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/import-assertions.js.map +0 -1
  68. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/index.d.ts +0 -198
  69. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/index.js +0 -327
  70. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/index.js.map +0 -1
  71. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/xhtml.d.ts +0 -256
  72. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/xhtml.js +0 -256
  73. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/xhtml.js.map +0 -1
  74. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.d.ts +0 -472
  75. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.js +0 -1
  76. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.js.map +0 -1
  77. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.mjs +0 -1
  78. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/middleware.d.ts +0 -159
  79. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/middleware.js +0 -2
  80. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/middleware.js.map +0 -1
  81. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/parseutil.d.ts +0 -10
  82. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/parseutil.js +0 -38
  83. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/parseutil.js.map +0 -1
  84. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/scopeflags.d.ts +0 -12
  85. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/scopeflags.js +0 -29
  86. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/scopeflags.js.map +0 -1
  87. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/tokenType.d.ts +0 -2
  88. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/tokenType.js +0 -118
  89. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/tokenType.js.map +0 -1
  90. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/types.d.ts +0 -60
  91. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/types.js +0 -2
  92. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/types.js.map +0 -1
  93. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/whitespace.d.ts +0 -2
  94. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/whitespace.js +0 -19
  95. package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/whitespace.js.map +0 -1
  96. package/scaffold/scripts/parsers/node_modules/acorn-typescript/package.json +0 -53
  97. package/scaffold/scripts/parsers/node_modules/acorn-typescript/tsconfig.json +0 -19
  98. package/scaffold/scripts/parsers/node_modules/acorn-walk/CHANGELOG.md +0 -209
  99. package/scaffold/scripts/parsers/node_modules/acorn-walk/LICENSE +0 -21
  100. package/scaffold/scripts/parsers/node_modules/acorn-walk/README.md +0 -124
  101. package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.d.mts +0 -152
  102. package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.d.ts +0 -152
  103. package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.js +0 -485
  104. package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.mjs +0 -467
  105. package/scaffold/scripts/parsers/node_modules/acorn-walk/package.json +0 -50
  106. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/LICENSE +0 -24
  107. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/README.md +0 -23
  108. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-bash.wasm +0 -0
  109. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-c.wasm +0 -0
  110. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
  111. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-cpp.wasm +0 -0
  112. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-css.wasm +0 -0
  113. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-dart.wasm +0 -0
  114. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-elisp.wasm +0 -0
  115. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-elixir.wasm +0 -0
  116. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-elm.wasm +0 -0
  117. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-embedded_template.wasm +0 -0
  118. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
  119. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-html.wasm +0 -0
  120. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
  121. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
  122. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-json.wasm +0 -0
  123. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
  124. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-lua.wasm +0 -0
  125. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-objc.wasm +0 -0
  126. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-ocaml.wasm +0 -0
  127. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
  128. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
  129. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-ql.wasm +0 -0
  130. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-rescript.wasm +0 -0
  131. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
  132. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
  133. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
  134. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-solidity.wasm +0 -0
  135. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-swift.wasm +0 -0
  136. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-systemrdl.wasm +0 -0
  137. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-tlaplus.wasm +0 -0
  138. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-toml.wasm +0 -0
  139. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
  140. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
  141. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-vue.wasm +0 -0
  142. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-yaml.wasm +0 -0
  143. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-zig.wasm +0 -0
  144. package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/package.json +0 -64
  145. package/scaffold/scripts/parsers/node_modules/web-tree-sitter/LICENSE +0 -21
  146. package/scaffold/scripts/parsers/node_modules/web-tree-sitter/README.md +0 -198
  147. package/scaffold/scripts/parsers/node_modules/web-tree-sitter/package.json +0 -37
  148. package/scaffold/scripts/parsers/node_modules/web-tree-sitter/tree-sitter-web.d.ts +0 -242
  149. package/scaffold/scripts/parsers/node_modules/web-tree-sitter/tree-sitter.js +0 -1
  150. package/scaffold/scripts/parsers/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
@@ -3,7 +3,8 @@ set -euo pipefail
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
6
- MCP_DIR="$REPO_ROOT/.context/mcp"
6
+ CONTEXT_RUNTIME_DIR="$REPO_ROOT/.context/mcp"
7
+ MCP_DIR="$CONTEXT_RUNTIME_DIR"
7
8
  TOTAL_STEPS=6
8
9
  STEP_INDEX=0
9
10
 
@@ -23,10 +24,42 @@ info "pipeline: deps -> ingest -> embeddings -> graph -> status"
23
24
 
24
25
  mkdir -p "$MCP_DIR/.npm-cache"
25
26
 
26
- step "Installing MCP dependencies"
27
+ # Runs `npm install` for a prefix only when its package-lock.json changed
28
+ # since the last successful install (or node_modules is missing). A warm
29
+ # no-op install still costs npm tens of seconds per prefix; the lockfile-hash
30
+ # marker makes repeat bootstraps and updates skip it entirely.
31
+ install_deps_if_changed() {
32
+ local prefix="$1" cache="$2"
33
+ local lock="$prefix/package-lock.json"
34
+ local marker="$prefix/node_modules/.cortex-lock-hash"
35
+ local current=""
36
+ if [ -f "$lock" ]; then
37
+ current=$(node -e '
38
+ const crypto = require("node:crypto");
39
+ const fs = require("node:fs");
40
+ // Node version is part of the key: native deps must reinstall after a
41
+ // runtime switch even when the lockfile is unchanged.
42
+ const hash = crypto.createHash("sha256");
43
+ hash.update(process.version);
44
+ hash.update(fs.readFileSync(process.argv[1]));
45
+ console.log(hash.digest("hex"));
46
+ ' "$lock" 2>/dev/null || true)
47
+ fi
48
+ if [ -n "$current" ] && [ -d "$prefix/node_modules" ] && [ -f "$marker" ] \
49
+ && [ "$(cat "$marker" 2>/dev/null)" = "$current" ]; then
50
+ info "dependencies up to date in $prefix (lockfile unchanged)"
51
+ return 0
52
+ fi
53
+ NPM_CONFIG_CACHE="$cache" npm --prefix "$prefix" install --no-fund --no-update-notifier --loglevel=warn
54
+ if [ -n "$current" ]; then
55
+ printf '%s' "$current" > "$marker" || true
56
+ fi
57
+ }
58
+
59
+ step "Installing context runtime dependencies"
27
60
  info "note: upstream RyuGraph dependencies may print deprecation warnings during install"
28
- NPM_CONFIG_CACHE="$MCP_DIR/.npm-cache" npm --prefix "$MCP_DIR" install --no-fund --no-update-notifier --loglevel=warn
29
- NPM_CONFIG_CACHE="$REPO_ROOT/.context/scripts/parsers/.npm-cache" npm --prefix "$REPO_ROOT/.context/scripts/parsers" install --no-fund --no-update-notifier --loglevel=warn
61
+ install_deps_if_changed "$MCP_DIR" "$MCP_DIR/.npm-cache"
62
+ install_deps_if_changed "$REPO_ROOT/.context/scripts/parsers" "$REPO_ROOT/.context/scripts/parsers/.npm-cache"
30
63
 
31
64
  source "$SCRIPT_DIR/lib/enterprise-check.sh"
32
65
 
@@ -17,7 +17,7 @@ const CONFIG_PATH = path.join(CONTEXT_DIR, "config.yaml");
17
17
  const SUPPORTED_TEXT_EXTENSIONS = new Set([
18
18
  ".md", ".mdx", ".txt", ".adoc", ".rst",
19
19
  ".yaml", ".yml", ".json", ".toml", ".csv",
20
- ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
20
+ ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs",
21
21
  ".py", ".go", ".java", ".cs", ".rb", ".rs", ".php", ".swift", ".kt",
22
22
  ".sql", ".sh", ".bash", ".zsh", ".ps1",
23
23
  ".c", ".h", ".cpp", ".hpp", ".cc", ".hh"
@@ -4,7 +4,8 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
6
6
  CONTEXT_DIR="$REPO_ROOT/.context"
7
- MCP_DIR="$CONTEXT_DIR/mcp"
7
+ CONTEXT_RUNTIME_DIR="$CONTEXT_DIR/mcp"
8
+ MCP_DIR="$CONTEXT_RUNTIME_DIR"
8
9
 
9
10
  PASS=0
10
11
  FAIL=0
@@ -160,24 +161,24 @@ if [[ -f "$INGEST_MANIFEST" ]] && command -v git &>/dev/null && git -C "$REPO_RO
160
161
  fi
161
162
  fi
162
163
 
163
- # ── MCP Server ──────────────────────────────────────────
164
+ # ── Context Runtime / MCP Server ─────────────────────────
164
165
 
165
166
  echo ""
166
- echo " MCP Server"
167
+ echo " Context Runtime"
167
168
 
168
169
  if [[ -f "$MCP_DIR/dist/server.js" ]]; then
169
- pass ".context/mcp/dist/server.js exists"
170
+ pass "runtime dist present (.context/mcp/dist/server.js)"
170
171
  else
171
- fail ".context/mcp/dist/server.js missing — run: cd .context/mcp && npm run build"
172
+ fail "runtime dist missing — run: cd .context/mcp && npm run build"
172
173
  fi
173
174
 
174
175
  if [[ -d "$MCP_DIR/node_modules" ]]; then
175
- pass ".context/mcp/node_modules present"
176
+ pass "runtime dependencies present (.context/mcp/node_modules)"
176
177
  else
177
- fail ".context/mcp/node_modules missing — run: cd .context/mcp && npm install"
178
+ fail "runtime dependencies missing — run: cd .context/mcp && npm install"
178
179
  fi
179
180
 
180
- # Quick MCP import check
181
+ # Quick runtime import check
181
182
  if [[ -f "$MCP_DIR/dist/server.js" ]] && [[ -d "$MCP_DIR/node_modules" ]]; then
182
183
  MCP_CHECK=$(cd "$REPO_ROOT" && timeout 10 node -e '
183
184
  const start = Date.now();
@@ -3,7 +3,8 @@ set -euo pipefail
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
6
- MCP_DIR="$REPO_ROOT/.context/mcp"
6
+ CONTEXT_RUNTIME_DIR="$REPO_ROOT/.context/mcp"
7
+ MCP_DIR="$CONTEXT_RUNTIME_DIR"
7
8
 
8
9
  if ! command -v npm >/dev/null 2>&1; then
9
10
  echo "[embed] npm is required but not found on PATH"
@@ -12,5 +13,5 @@ fi
12
13
 
13
14
  mkdir -p "$MCP_DIR/.npm-cache"
14
15
 
15
- echo "[embed] generating embeddings via .context/mcp/embed"
16
+ echo "[embed] generating embeddings via context runtime"
16
17
  CORTEX_PROJECT_ROOT="$REPO_ROOT" NPM_CONFIG_CACHE="$MCP_DIR/.npm-cache" npm --prefix "$MCP_DIR" run embed --silent -- "$@"
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Shared chunk-parser registry.
3
+ *
4
+ * Single source of truth for "which parser handles which extension", used by
5
+ * both the main ingest process and the worker-thread pool. Keeping one table
6
+ * guarantees a file parses identically whether it runs inline or in a worker.
7
+ *
8
+ * Parsers self-initialize lazily (tree-sitter grammars load WASM on first
9
+ * parse and cache per module instance), so a worker amortizes that cost over
10
+ * its lifetime.
11
+ */
12
+ import { parseCode } from "./parsers/javascript.mjs";
13
+
14
+ const parseJavaScriptCode = parseCode;
15
+ let parseVbNetCode = null;
16
+ let parseCSharpCode = null;
17
+ let parseCSharpProjectImpl = null;
18
+ let parseCppCode = null;
19
+ let parseConfigCode = null;
20
+ let parseResourcesCode = null;
21
+ let parseSqlCode = null;
22
+ let parseRustCode = null;
23
+ let parsePythonCode = null;
24
+ let parseGoCode = null;
25
+ let parseJavaCode = null;
26
+ let parseRubyCode = null;
27
+ let parseBashCode = null;
28
+ let parseVb6Code = null;
29
+ let parseMarkdownCode = null;
30
+ let isVbNetParserAvailable = () => false;
31
+ let isCSharpParserAvailableImpl = () => false;
32
+ let isCppParserAvailable = () => false;
33
+ let getCSharpParserRuntimeImpl = () => ({ available: false, reason: "parser module not loaded" });
34
+
35
+ let loadPromise = null;
36
+
37
+ // Languages whose parser runs purely in-process (JS or WASM) with no
38
+ // subprocess and no cross-file global state, so they are safe to run in a
39
+ // worker thread. csharp (Roslyn subprocess + project-wide batch parse in the
40
+ // main process), vbnet (subprocess), and cpp (clang-bridge fallback
41
+ // subprocess) stay on the main thread.
42
+ export const PARALLEL_SAFE_LANGUAGES = new Set([
43
+ "javascript",
44
+ "jsx",
45
+ "typescript",
46
+ "tsx",
47
+ "python",
48
+ "go",
49
+ "java",
50
+ "ruby",
51
+ "bash",
52
+ "sql",
53
+ "config",
54
+ "resource",
55
+ "settings",
56
+ "markdown",
57
+ "rust",
58
+ "vb6"
59
+ ]);
60
+
61
+ export async function loadParsers() {
62
+ if (loadPromise) {
63
+ return loadPromise;
64
+ }
65
+ const loaders = [
66
+ import("./parsers/vbnet.mjs").then((module) => {
67
+ parseVbNetCode = module.parseCode;
68
+ isVbNetParserAvailable =
69
+ typeof module.isVbNetParserAvailable === "function"
70
+ ? module.isVbNetParserAvailable
71
+ : () => typeof module.parseCode === "function";
72
+ }),
73
+ import("./parsers/csharp.mjs").then((module) => {
74
+ parseCSharpCode = module.parseCode;
75
+ parseCSharpProjectImpl = module.parseProject ?? null;
76
+ getCSharpParserRuntimeImpl =
77
+ typeof module.getCSharpParserRuntime === "function"
78
+ ? module.getCSharpParserRuntime
79
+ : () => ({ available: typeof module.parseCode === "function", reason: "runtime details unavailable" });
80
+ isCSharpParserAvailableImpl =
81
+ typeof module.isCSharpParserAvailable === "function"
82
+ ? module.isCSharpParserAvailable
83
+ : () => typeof module.parseCode === "function";
84
+ }),
85
+ import("./parsers/cpp-dispatch.mjs").then((module) => {
86
+ parseCppCode = module.parseCode;
87
+ isCppParserAvailable =
88
+ typeof module.isCppParserAvailable === "function"
89
+ ? module.isCppParserAvailable
90
+ : () => typeof module.parseCode === "function";
91
+ }),
92
+ import("./parsers/config.mjs").then((module) => {
93
+ parseConfigCode = module.parseCode;
94
+ }),
95
+ import("./parsers/resources.mjs").then((module) => {
96
+ parseResourcesCode = module.parseCode;
97
+ }),
98
+ import("./parsers/sql.mjs").then((module) => {
99
+ parseSqlCode = module.parseCode;
100
+ }),
101
+ import("./parsers/rust-dispatch.mjs").then((module) => {
102
+ parseRustCode = module.parseCode;
103
+ }),
104
+ import("./parsers/python-treesitter.mjs").then((module) => {
105
+ parsePythonCode = module.parseCode;
106
+ }),
107
+ import("./parsers/go-treesitter.mjs").then((module) => {
108
+ parseGoCode = module.parseCode;
109
+ }),
110
+ import("./parsers/java-treesitter.mjs").then((module) => {
111
+ parseJavaCode = module.parseCode;
112
+ }),
113
+ import("./parsers/ruby-treesitter.mjs").then((module) => {
114
+ parseRubyCode = module.parseCode;
115
+ }),
116
+ import("./parsers/bash-treesitter.mjs").then((module) => {
117
+ parseBashCode = module.parseCode;
118
+ }),
119
+ import("./parsers/vb6.mjs").then((module) => {
120
+ parseVb6Code = module.parseCode;
121
+ }),
122
+ import("./parsers/markdown.mjs").then((module) => {
123
+ parseMarkdownCode = module.parseCode;
124
+ })
125
+ ];
126
+
127
+ loadPromise = Promise.allSettled(loaders).then(() => undefined);
128
+ return loadPromise;
129
+ }
130
+
131
+ const CHUNK_PARSERS = new Map([
132
+ [".js", { language: "javascript", parse: parseJavaScriptCode }],
133
+ [".jsx", { language: "jsx", parse: parseJavaScriptCode }],
134
+ [".mjs", { language: "javascript", parse: parseJavaScriptCode }],
135
+ [".cjs", { language: "javascript", parse: parseJavaScriptCode }],
136
+ [".ts", { language: "typescript", parse: parseJavaScriptCode }],
137
+ [".tsx", { language: "tsx", parse: parseJavaScriptCode }],
138
+ [".mts", { language: "typescript", parse: parseJavaScriptCode }],
139
+ [".cts", { language: "typescript", parse: parseJavaScriptCode }],
140
+ [
141
+ ".vb",
142
+ {
143
+ language: "vbnet",
144
+ parse: (...args) => parseVbNetCode(...args),
145
+ isAvailable: () => typeof parseVbNetCode === "function" && isVbNetParserAvailable()
146
+ }
147
+ ],
148
+ [
149
+ ".cs",
150
+ {
151
+ language: "csharp",
152
+ parse: (...args) => parseCSharpCode(...args),
153
+ isAvailable: () => typeof parseCSharpCode === "function" && isCSharpParserAvailableImpl()
154
+ }
155
+ ],
156
+ [
157
+ ".sql",
158
+ {
159
+ language: "sql",
160
+ parse: (...args) => parseSqlCode(...args),
161
+ isAvailable: () => typeof parseSqlCode === "function"
162
+ }
163
+ ],
164
+ [
165
+ ".md",
166
+ {
167
+ language: "markdown",
168
+ parse: (...args) => parseMarkdownCode(...args),
169
+ isAvailable: () => typeof parseMarkdownCode === "function"
170
+ }
171
+ ],
172
+ [
173
+ ".mdx",
174
+ {
175
+ language: "markdown",
176
+ parse: (...args) => parseMarkdownCode(...args),
177
+ isAvailable: () => typeof parseMarkdownCode === "function"
178
+ }
179
+ ],
180
+ [
181
+ ".config",
182
+ {
183
+ language: "config",
184
+ parse: (...args) => parseConfigCode(...args),
185
+ isAvailable: () => typeof parseConfigCode === "function"
186
+ }
187
+ ],
188
+ [
189
+ ".resx",
190
+ {
191
+ language: "resource",
192
+ parse: (...args) => parseResourcesCode(...args),
193
+ isAvailable: () => typeof parseResourcesCode === "function"
194
+ }
195
+ ],
196
+ [
197
+ ".settings",
198
+ {
199
+ language: "settings",
200
+ parse: (...args) => parseResourcesCode(...args),
201
+ isAvailable: () => typeof parseResourcesCode === "function"
202
+ }
203
+ ],
204
+ [
205
+ ".c",
206
+ {
207
+ language: "c",
208
+ parse: (...args) => parseCppCode(...args),
209
+ isAvailable: () => typeof parseCppCode === "function" && isCppParserAvailable()
210
+ }
211
+ ],
212
+ [
213
+ ".h",
214
+ {
215
+ language: "c",
216
+ parse: (...args) => parseCppCode(...args),
217
+ isAvailable: () => typeof parseCppCode === "function" && isCppParserAvailable()
218
+ }
219
+ ],
220
+ [
221
+ ".cpp",
222
+ {
223
+ language: "cpp",
224
+ parse: (...args) => parseCppCode(...args),
225
+ isAvailable: () => typeof parseCppCode === "function" && isCppParserAvailable()
226
+ }
227
+ ],
228
+ [
229
+ ".cc",
230
+ {
231
+ language: "cpp",
232
+ parse: (...args) => parseCppCode(...args),
233
+ isAvailable: () => typeof parseCppCode === "function" && isCppParserAvailable()
234
+ }
235
+ ],
236
+ [
237
+ ".hpp",
238
+ {
239
+ language: "cpp",
240
+ parse: (...args) => parseCppCode(...args),
241
+ isAvailable: () => typeof parseCppCode === "function" && isCppParserAvailable()
242
+ }
243
+ ],
244
+ [
245
+ ".hh",
246
+ {
247
+ language: "cpp",
248
+ parse: (...args) => parseCppCode(...args),
249
+ isAvailable: () => typeof parseCppCode === "function" && isCppParserAvailable()
250
+ }
251
+ ],
252
+ [
253
+ ".rs",
254
+ {
255
+ language: "rust",
256
+ parse: (...args) => parseRustCode(...args),
257
+ isAvailable: () => typeof parseRustCode === "function"
258
+ }
259
+ ],
260
+ [
261
+ ".py",
262
+ {
263
+ language: "python",
264
+ parse: (...args) => parsePythonCode(...args),
265
+ isAvailable: () => typeof parsePythonCode === "function"
266
+ }
267
+ ],
268
+ [
269
+ ".go",
270
+ {
271
+ language: "go",
272
+ parse: (...args) => parseGoCode(...args),
273
+ isAvailable: () => typeof parseGoCode === "function"
274
+ }
275
+ ],
276
+ [
277
+ ".java",
278
+ {
279
+ language: "java",
280
+ parse: (...args) => parseJavaCode(...args),
281
+ isAvailable: () => typeof parseJavaCode === "function"
282
+ }
283
+ ],
284
+ [
285
+ ".rb",
286
+ {
287
+ language: "ruby",
288
+ parse: (...args) => parseRubyCode(...args),
289
+ isAvailable: () => typeof parseRubyCode === "function"
290
+ }
291
+ ],
292
+ [
293
+ ".sh",
294
+ {
295
+ language: "bash",
296
+ parse: (...args) => parseBashCode(...args),
297
+ isAvailable: () => typeof parseBashCode === "function"
298
+ }
299
+ ],
300
+ [
301
+ ".bash",
302
+ {
303
+ language: "bash",
304
+ parse: (...args) => parseBashCode(...args),
305
+ isAvailable: () => typeof parseBashCode === "function"
306
+ }
307
+ ],
308
+ [
309
+ ".zsh",
310
+ {
311
+ language: "bash",
312
+ parse: (...args) => parseBashCode(...args),
313
+ isAvailable: () => typeof parseBashCode === "function"
314
+ }
315
+ ],
316
+ [
317
+ ".bas",
318
+ {
319
+ language: "vb6",
320
+ parse: (...args) => parseVb6Code(...args),
321
+ isAvailable: () => typeof parseVb6Code === "function"
322
+ }
323
+ ],
324
+ [
325
+ ".cls",
326
+ {
327
+ language: "vb6",
328
+ parse: (...args) => parseVb6Code(...args),
329
+ isAvailable: () => typeof parseVb6Code === "function"
330
+ }
331
+ ],
332
+ [
333
+ ".frm",
334
+ {
335
+ language: "vb6",
336
+ parse: (...args) => parseVb6Code(...args),
337
+ isAvailable: () => typeof parseVb6Code === "function"
338
+ }
339
+ ],
340
+ [
341
+ ".ctl",
342
+ {
343
+ language: "vb6",
344
+ parse: (...args) => parseVb6Code(...args),
345
+ isAvailable: () => typeof parseVb6Code === "function"
346
+ }
347
+ ]
348
+ ]);
349
+
350
+ export function getChunkParserForExtension(ext) {
351
+ return CHUNK_PARSERS.get(ext) ?? null;
352
+ }
353
+
354
+ // Stable wrappers so callers can import these once; they delegate to whatever
355
+ // the dynamic import populated.
356
+ export function isCSharpParserAvailable() {
357
+ return isCSharpParserAvailableImpl();
358
+ }
359
+
360
+ export function getCSharpParserRuntime() {
361
+ return getCSharpParserRuntimeImpl();
362
+ }
363
+
364
+ export function parseCSharpProject(inputs) {
365
+ if (typeof parseCSharpProjectImpl !== "function") {
366
+ return null;
367
+ }
368
+ return parseCSharpProjectImpl(inputs);
369
+ }
370
+
371
+ export function hasCSharpProjectParser() {
372
+ return typeof parseCSharpProjectImpl === "function";
373
+ }
374
+
375
+ // Parse a single file with the registered parser for its extension. Used by
376
+ // the worker; returns the parser's { chunks, errors } result, or null if no
377
+ // parser is registered or it is unavailable in this process.
378
+ export async function parseFileContent(ext, content, filePath) {
379
+ const parser = getChunkParserForExtension(ext);
380
+ if (!parser) {
381
+ return null;
382
+ }
383
+ if (typeof parser.isAvailable === "function" && !(await parser.isAvailable())) {
384
+ return null;
385
+ }
386
+ return { language: parser.language, result: await parser.parse(content, filePath, parser.language) };
387
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Ingest worker thread.
3
+ *
4
+ * Runs the pure, parallel-safe parse step (tree-sitter / acorn / regex
5
+ * parsers) for one file at a time off the main thread. It does nothing
6
+ * stateful: no id allocation, windowing, checksums, or relation building —
7
+ * all of that stays on the main thread in deterministic order. The worker
8
+ * only turns (ext, file path) into a parse result.
9
+ *
10
+ * Parsers initialize lazily on first use and cache per module instance, so a
11
+ * long-lived worker pays each grammar's WASM init once.
12
+ */
13
+ import fs from "node:fs";
14
+ import { parentPort } from "node:worker_threads";
15
+ import { loadParsers, parseFileContent } from "./ingest-parsers.mjs";
16
+
17
+ if (!parentPort) {
18
+ throw new Error("ingest-worker.mjs must be run as a worker thread");
19
+ }
20
+
21
+ const ready = loadParsers();
22
+
23
+ parentPort.on("message", async (message) => {
24
+ if (message && message.type === "shutdown") {
25
+ process.exit(0);
26
+ }
27
+
28
+ const { taskId, ext, filePath } = message;
29
+ try {
30
+ await ready;
31
+ let content = typeof message.content === "string" ? message.content : null;
32
+ if (content === null) {
33
+ const limit = Number.isFinite(message.contentLimit) ? Math.max(0, Math.floor(message.contentLimit)) : null;
34
+ content = fs.readFileSync(message.absolutePath, "utf8");
35
+ if (limit !== null) {
36
+ content = content.slice(0, limit);
37
+ }
38
+ }
39
+ const parsed = await parseFileContent(ext, content, filePath);
40
+ if (!parsed) {
41
+ parentPort.postMessage({ taskId, ok: false, reason: "no parser available" });
42
+ return;
43
+ }
44
+ parentPort.postMessage({ taskId, ok: true, result: parsed.result });
45
+ } catch (error) {
46
+ parentPort.postMessage({
47
+ taskId,
48
+ ok: false,
49
+ reason: error instanceof Error ? error.message : String(error)
50
+ });
51
+ }
52
+ });