@stupidloud/codegraph 0.7.20 → 0.9.5

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 (155) hide show
  1. package/README.md +127 -106
  2. package/dist/bin/codegraph.d.ts +4 -0
  3. package/dist/bin/codegraph.d.ts.map +1 -1
  4. package/dist/bin/codegraph.js +327 -8
  5. package/dist/bin/codegraph.js.map +1 -1
  6. package/dist/bin/node-version-check.d.ts +17 -0
  7. package/dist/bin/node-version-check.d.ts.map +1 -1
  8. package/dist/bin/node-version-check.js +37 -0
  9. package/dist/bin/node-version-check.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +1 -11
  12. package/dist/config.js.map +1 -1
  13. package/dist/db/index.d.ts +30 -1
  14. package/dist/db/index.d.ts.map +1 -1
  15. package/dist/db/index.js +75 -25
  16. package/dist/db/index.js.map +1 -1
  17. package/dist/db/queries.d.ts +16 -0
  18. package/dist/db/queries.d.ts.map +1 -1
  19. package/dist/db/queries.js +80 -27
  20. package/dist/db/queries.js.map +1 -1
  21. package/dist/db/sqlite-adapter.d.ts +17 -23
  22. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  23. package/dist/db/sqlite-adapter.js +51 -174
  24. package/dist/db/sqlite-adapter.js.map +1 -1
  25. package/dist/extraction/grammars.d.ts +7 -1
  26. package/dist/extraction/grammars.d.ts.map +1 -1
  27. package/dist/extraction/grammars.js +42 -2
  28. package/dist/extraction/grammars.js.map +1 -1
  29. package/dist/extraction/index.d.ts +9 -14
  30. package/dist/extraction/index.d.ts.map +1 -1
  31. package/dist/extraction/index.js +131 -124
  32. package/dist/extraction/index.js.map +1 -1
  33. package/dist/extraction/languages/index.d.ts.map +1 -1
  34. package/dist/extraction/languages/index.js +4 -0
  35. package/dist/extraction/languages/index.js.map +1 -1
  36. package/dist/extraction/languages/lua.d.ts +3 -0
  37. package/dist/extraction/languages/lua.d.ts.map +1 -0
  38. package/dist/extraction/languages/lua.js +150 -0
  39. package/dist/extraction/languages/lua.js.map +1 -0
  40. package/dist/extraction/languages/luau.d.ts +3 -0
  41. package/dist/extraction/languages/luau.d.ts.map +1 -0
  42. package/dist/extraction/languages/luau.js +37 -0
  43. package/dist/extraction/languages/luau.js.map +1 -0
  44. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  45. package/dist/extraction/tree-sitter.js +38 -0
  46. package/dist/extraction/tree-sitter.js.map +1 -1
  47. package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
  48. package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
  49. package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
  50. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
  51. package/dist/extraction/wasm-runtime-flags.js +105 -0
  52. package/dist/extraction/wasm-runtime-flags.js.map +1 -0
  53. package/dist/graph/traversal.d.ts.map +1 -1
  54. package/dist/graph/traversal.js +71 -36
  55. package/dist/graph/traversal.js.map +1 -1
  56. package/dist/index.d.ts +11 -5
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +28 -18
  59. package/dist/index.js.map +1 -1
  60. package/dist/installer/config-writer.d.ts.map +1 -1
  61. package/dist/installer/config-writer.js +3 -1
  62. package/dist/installer/config-writer.js.map +1 -1
  63. package/dist/installer/index.d.ts +66 -2
  64. package/dist/installer/index.d.ts.map +1 -1
  65. package/dist/installer/index.js +195 -5
  66. package/dist/installer/index.js.map +1 -1
  67. package/dist/installer/instructions-template.d.ts +2 -2
  68. package/dist/installer/instructions-template.d.ts.map +1 -1
  69. package/dist/installer/instructions-template.js +4 -2
  70. package/dist/installer/instructions-template.js.map +1 -1
  71. package/dist/installer/targets/claude.d.ts +26 -6
  72. package/dist/installer/targets/claude.d.ts.map +1 -1
  73. package/dist/installer/targets/claude.js +165 -10
  74. package/dist/installer/targets/claude.js.map +1 -1
  75. package/dist/installer/targets/cursor.d.ts.map +1 -1
  76. package/dist/installer/targets/cursor.js +57 -3
  77. package/dist/installer/targets/cursor.js.map +1 -1
  78. package/dist/installer/targets/hermes.d.ts +18 -0
  79. package/dist/installer/targets/hermes.d.ts.map +1 -0
  80. package/dist/installer/targets/hermes.js +305 -0
  81. package/dist/installer/targets/hermes.js.map +1 -0
  82. package/dist/installer/targets/registry.d.ts.map +1 -1
  83. package/dist/installer/targets/registry.js +2 -0
  84. package/dist/installer/targets/registry.js.map +1 -1
  85. package/dist/installer/targets/types.d.ts +1 -1
  86. package/dist/installer/targets/types.d.ts.map +1 -1
  87. package/dist/mcp/index.d.ts +12 -0
  88. package/dist/mcp/index.d.ts.map +1 -1
  89. package/dist/mcp/index.js +213 -18
  90. package/dist/mcp/index.js.map +1 -1
  91. package/dist/mcp/server-instructions.d.ts +1 -1
  92. package/dist/mcp/server-instructions.d.ts.map +1 -1
  93. package/dist/mcp/server-instructions.js +15 -0
  94. package/dist/mcp/server-instructions.js.map +1 -1
  95. package/dist/mcp/tools.d.ts +25 -1
  96. package/dist/mcp/tools.d.ts.map +1 -1
  97. package/dist/mcp/tools.js +221 -30
  98. package/dist/mcp/tools.js.map +1 -1
  99. package/dist/mcp/transport.d.ts +17 -0
  100. package/dist/mcp/transport.d.ts.map +1 -1
  101. package/dist/mcp/transport.js +63 -0
  102. package/dist/mcp/transport.js.map +1 -1
  103. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  104. package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
  105. package/dist/resolution/frameworks/drupal.js +335 -0
  106. package/dist/resolution/frameworks/drupal.js.map +1 -0
  107. package/dist/resolution/frameworks/index.d.ts +2 -0
  108. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  109. package/dist/resolution/frameworks/index.js +9 -1
  110. package/dist/resolution/frameworks/index.js.map +1 -1
  111. package/dist/resolution/frameworks/nestjs.d.ts +26 -0
  112. package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
  113. package/dist/resolution/frameworks/nestjs.js +374 -0
  114. package/dist/resolution/frameworks/nestjs.js.map +1 -0
  115. package/dist/resolution/index.d.ts.map +1 -1
  116. package/dist/resolution/index.js +40 -7
  117. package/dist/resolution/index.js.map +1 -1
  118. package/dist/resolution/lru-cache.d.ts +24 -0
  119. package/dist/resolution/lru-cache.d.ts.map +1 -0
  120. package/dist/resolution/lru-cache.js +62 -0
  121. package/dist/resolution/lru-cache.js.map +1 -0
  122. package/dist/sync/git-hooks.d.ts +45 -0
  123. package/dist/sync/git-hooks.d.ts.map +1 -0
  124. package/dist/sync/git-hooks.js +223 -0
  125. package/dist/sync/git-hooks.js.map +1 -0
  126. package/dist/sync/index.d.ts +4 -0
  127. package/dist/sync/index.d.ts.map +1 -1
  128. package/dist/sync/index.js +12 -1
  129. package/dist/sync/index.js.map +1 -1
  130. package/dist/sync/watch-policy.d.ts +48 -0
  131. package/dist/sync/watch-policy.d.ts.map +1 -0
  132. package/dist/sync/watch-policy.js +124 -0
  133. package/dist/sync/watch-policy.js.map +1 -0
  134. package/dist/sync/watcher.d.ts +2 -4
  135. package/dist/sync/watcher.d.ts.map +1 -1
  136. package/dist/sync/watcher.js +14 -6
  137. package/dist/sync/watcher.js.map +1 -1
  138. package/dist/types.d.ts +1 -1
  139. package/dist/types.d.ts.map +1 -1
  140. package/dist/types.js +11 -0
  141. package/dist/types.js.map +1 -1
  142. package/dist/utils.js +1 -1
  143. package/package.json +4 -4
  144. package/scripts/add-lang/bench.sh +60 -0
  145. package/scripts/add-lang/check-grammar.mjs +75 -0
  146. package/scripts/add-lang/dump-ast.mjs +103 -0
  147. package/scripts/add-lang/verify-extraction.mjs +70 -0
  148. package/scripts/agent-eval/audit.sh +68 -0
  149. package/scripts/agent-eval/itrun.sh +1 -1
  150. package/scripts/agent-eval/run-all.sh +67 -0
  151. package/scripts/build-bundle.sh +118 -0
  152. package/scripts/npm-shim.js +246 -0
  153. package/scripts/pack-npm.sh +95 -0
  154. package/scripts/patch-tree-sitter-dart.js +0 -112
  155. package/scripts/release.sh +0 -68
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ # With/without A/B (and optional interactive) eval for a codegraph version on a
3
+ # repo. Codegraph is the ONLY variable: both arms launch claude with
4
+ # --strict-mcp-config — with = codegraph-only MCP (pointed at $CG_BIN),
5
+ # without = empty MCP. Built-in Read/Grep/Bash stay available in both arms.
6
+ #
7
+ # Usage: run-all.sh <repo-path> "<question>" [headless|tmux|all]
8
+ # Env: CG_BIN codegraph binary (default: command -v codegraph)
9
+ # AGENT_EVAL_OUT output dir (default: /tmp/agent-eval)
10
+ set -uo pipefail
11
+
12
+ REPO="${1:?usage: run-all.sh <repo-path> \"<question>\" [headless|tmux|all]}"
13
+ Q="${2:?question required}"
14
+ MODE="${3:-headless}"
15
+ CG_BIN="${CG_BIN:-$(command -v codegraph)}"
16
+ OUT="${AGENT_EVAL_OUT:-/tmp/agent-eval}"
17
+ HARNESS="$(cd "$(dirname "$0")" && pwd)"
18
+ mkdir -p "$OUT"
19
+
20
+ [ -n "$CG_BIN" ] || { echo "no codegraph binary on PATH (set CG_BIN)"; exit 1; }
21
+ [ -d "$REPO/.codegraph" ] || { echo "no .codegraph index at $REPO — index it first"; exit 1; }
22
+ case "$MODE" in headless|tmux|all) ;; *) echo "mode must be headless|tmux|all (got '$MODE')"; exit 1;; esac
23
+
24
+ # MCP config files (path form avoids inline-JSON quoting through tmux).
25
+ cat > "$OUT/mcp-codegraph.json" <<JSON
26
+ {"mcpServers":{"codegraph":{"command":"$CG_BIN","args":["serve","--mcp","--path","$REPO"]}}}
27
+ JSON
28
+ echo '{"mcpServers":{}}' > "$OUT/mcp-empty.json"
29
+
30
+ echo "###### codegraph: $CG_BIN"
31
+ echo "###### repo: $REPO"
32
+ echo "###### question: $Q"
33
+ echo
34
+
35
+ # Headless arm: claude -p with stream-json -> exact tool sequence + tokens/cost.
36
+ headless() {
37
+ local label="$1" cfg="$2"
38
+ echo "############################## HEADLESS [$label] ##############################"
39
+ ( cd "$REPO" && claude -p "$Q" \
40
+ --output-format stream-json --verbose \
41
+ --permission-mode bypassPermissions \
42
+ --model opus \
43
+ --max-budget-usd 4 \
44
+ --strict-mcp-config --mcp-config "$cfg" \
45
+ > "$OUT/run-$label.jsonl" 2>"$OUT/run-$label.err" )
46
+ echo "exit $? -> $OUT/run-$label.jsonl ($(wc -l < "$OUT/run-$label.jsonl" | tr -d ' ') lines)"
47
+ tail -2 "$OUT/run-$label.err" 2>/dev/null
48
+ node "$HARNESS/parse-run.mjs" "$OUT/run-$label.jsonl" 2>&1 || true
49
+ echo
50
+ }
51
+
52
+ if [ "$MODE" = headless ] || [ "$MODE" = all ]; then
53
+ headless "headless-with" "$OUT/mcp-codegraph.json"
54
+ headless "headless-without" "$OUT/mcp-empty.json"
55
+ fi
56
+
57
+ if [ "$MODE" = tmux ] || [ "$MODE" = all ]; then
58
+ echo "############################## INTERACTIVE [with] ##############################"
59
+ CLAUDE_EXTRA_ARGS="--model opus --strict-mcp-config --mcp-config $OUT/mcp-codegraph.json" \
60
+ bash "$HARNESS/itrun.sh" "$REPO" "int-with" "$Q" 2>&1 || echo "[itrun WITH failed]"
61
+ echo
62
+ echo "############################## INTERACTIVE [without] ##############################"
63
+ CLAUDE_EXTRA_ARGS="--model opus --strict-mcp-config --mcp-config $OUT/mcp-empty.json" \
64
+ bash "$HARNESS/itrun.sh" "$REPO" "int-without" "$Q" 2>&1 || echo "[itrun WITHOUT failed]"
65
+ echo
66
+ fi
67
+ echo "############################## RUN-ALL COMPLETE ##############################"
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Build a self-contained CodeGraph bundle: an official Node runtime + the
4
+ # compiled app + its production deps, so CodeGraph runs with NO system Node and
5
+ # NO native build — node:sqlite is built into the bundled Node. One archive per
6
+ # platform.
7
+ #
8
+ # Because dropping better-sqlite3 left zero native addons, the recipe is pure
9
+ # file-packaging (download the target's Node, copy the app, archive) — so any
10
+ # platform's bundle can be built on any OS. No cross-compile, no native runners.
11
+ #
12
+ # Usage:
13
+ # scripts/build-bundle.sh <target> [node-version]
14
+ # target: darwin-arm64 | darwin-x64 | linux-x64 | linux-arm64
15
+ # | win32-x64 | win32-arm64
16
+ # node-version: e.g. v24.16.0 (default below; pin for reproducible builds)
17
+ #
18
+ # Output:
19
+ # unix: release/codegraph-<target>.tar.gz (launcher: bin/codegraph)
20
+ # windows: release/codegraph-<target>.zip (launcher: bin/codegraph.cmd)
21
+ set -euo pipefail
22
+
23
+ TARGET="${1:?usage: build-bundle.sh <target> [node-version]}"
24
+ NODE_VERSION="${2:-v24.16.0}"
25
+
26
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
27
+ OUT="$ROOT/release"
28
+ WORK="$(mktemp -d)"
29
+ trap 'rm -rf "$WORK"' EXIT
30
+
31
+ ARCH="${TARGET##*-}" # x64 | arm64
32
+ OSFAM="${TARGET%-*}" # darwin | linux | win32
33
+
34
+ echo "[bundle] target=${TARGET} node=${NODE_VERSION}"
35
+
36
+ # 1. Download + extract the official Node runtime for the target platform.
37
+ if [ "$OSFAM" = "win32" ]; then
38
+ NODE_DIST="node-${NODE_VERSION}-win-${ARCH}"
39
+ NODE_URL="https://nodejs.org/dist/${NODE_VERSION}/${NODE_DIST}.zip"
40
+ echo "[bundle] downloading ${NODE_URL}"
41
+ curl -fsSL "$NODE_URL" -o "$WORK/node.zip"
42
+ if command -v unzip >/dev/null 2>&1; then
43
+ unzip -q "$WORK/node.zip" -d "$WORK"
44
+ else
45
+ tar -xf "$WORK/node.zip" -C "$WORK" # bsdtar can read zip
46
+ fi
47
+ NODE_BIN="$WORK/${NODE_DIST}/node.exe"
48
+ else
49
+ NODE_DIST="node-${NODE_VERSION}-${TARGET}"
50
+ NODE_URL="https://nodejs.org/dist/${NODE_VERSION}/${NODE_DIST}.tar.gz"
51
+ echo "[bundle] downloading ${NODE_URL}"
52
+ curl -fsSL "$NODE_URL" -o "$WORK/node.tar.gz"
53
+ tar -xzf "$WORK/node.tar.gz" -C "$WORK"
54
+ NODE_BIN="$WORK/${NODE_DIST}/bin/node"
55
+ fi
56
+ [ -f "$NODE_BIN" ] || { echo "[bundle] error: node binary not found ($NODE_BIN)" >&2; exit 1; }
57
+
58
+ # 2. Build the app (compiled JS + copied wasm/schema assets).
59
+ echo "[bundle] building app"
60
+ ( cd "$ROOT" && npm run build >/dev/null )
61
+
62
+ # 3. Stage: app + production-only deps (pure JS/wasm → portable across platforms).
63
+ STAGE="$WORK/codegraph-${TARGET}"
64
+ mkdir -p "$STAGE/lib" "$STAGE/bin"
65
+ cp -R "$ROOT/dist" "$STAGE/lib/dist"
66
+ cp "$ROOT/package.json" "$ROOT/package-lock.json" "$STAGE/lib/"
67
+ echo "[bundle] installing production dependencies"
68
+ ( cd "$STAGE/lib" && npm ci --omit=dev --ignore-scripts >/dev/null 2>&1 )
69
+ rm -f "$STAGE/lib/package-lock.json"
70
+
71
+ # 4. Vendored Node + launcher (the launcher uses the bundled Node by relative
72
+ # path, so no system Node is ever needed).
73
+ #
74
+ # `--liftoff-only`: keep tree-sitter's large WASM grammars on V8's Liftoff
75
+ # baseline compiler so they never reach the turboshaft optimizing tier, whose
76
+ # per-compilation Zone arena OOMs the whole process (`Fatal process out of
77
+ # memory: Zone`) on Node >= 22 — even with tens of GB free. The flag is read at
78
+ # V8 engine init so it must be on node's command line; the parse worker inherits
79
+ # it. See issues #293/#298 and src/extraction/wasm-runtime-flags.ts. (The CLI
80
+ # also self-relaunches with this flag when launched without it, so non-bundled
81
+ # runs are covered too; passing it here avoids that extra spawn.)
82
+ if [ "$OSFAM" = "win32" ]; then
83
+ cp "$NODE_BIN" "$STAGE/node.exe"
84
+ printf '@"%%~dp0..\\node.exe" --liftoff-only "%%~dp0..\\lib\\dist\\bin\\codegraph.js" %%*\r\n' \
85
+ > "$STAGE/bin/codegraph.cmd"
86
+ else
87
+ cp "$NODE_BIN" "$STAGE/node"
88
+ cat > "$STAGE/bin/codegraph" <<'LAUNCH'
89
+ #!/bin/sh
90
+ # Resolve symlinks (e.g. the ~/.local/bin/codegraph link install.sh creates) so
91
+ # we find the real bundle dir, not the symlink's location.
92
+ SELF="$0"
93
+ while [ -L "$SELF" ]; do
94
+ target="$(readlink "$SELF")"
95
+ case "$target" in
96
+ /*) SELF="$target" ;;
97
+ *) SELF="$(dirname "$SELF")/$target" ;;
98
+ esac
99
+ done
100
+ DIR="$(cd "$(dirname "$SELF")/.." && pwd)"
101
+ # --liftoff-only: avoid the V8 turboshaft WASM Zone OOM (issues #293/#298).
102
+ exec "$DIR/node" --liftoff-only "$DIR/lib/dist/bin/codegraph.js" "$@"
103
+ LAUNCH
104
+ chmod +x "$STAGE/bin/codegraph"
105
+ fi
106
+
107
+ # 5. Archive (.zip for Windows, .tar.gz otherwise).
108
+ mkdir -p "$OUT"
109
+ if [ "$OSFAM" = "win32" ]; then
110
+ ARCHIVE="$OUT/codegraph-${TARGET}.zip"
111
+ rm -f "$ARCHIVE"
112
+ ( cd "$WORK" && zip -rqX "$ARCHIVE" "codegraph-${TARGET}" )
113
+ else
114
+ ARCHIVE="$OUT/codegraph-${TARGET}.tar.gz"
115
+ # --no-xattrs: don't embed macOS xattrs that make GNU tar warn on Linux.
116
+ tar --no-xattrs -czf "$ARCHIVE" -C "$WORK" "codegraph-${TARGET}"
117
+ fi
118
+ echo "[bundle] wrote ${ARCHIVE} ($(du -h "$ARCHIVE" | cut -f1))"
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ //
4
+ // npm thin-installer launcher for CodeGraph.
5
+ //
6
+ // The heavy artifact (a vendored Node runtime + the app) ships as a per-platform
7
+ // optionalDependency: @colbymchenry/codegraph-<platform>-<arch>. npm installs
8
+ // only the one matching the host, via each package's `os`/`cpu` fields (the
9
+ // esbuild pattern). This shim — run by the user's OWN Node — locates that bundle
10
+ // and execs its launcher, so the real work always runs on the bundled Node 24
11
+ // (with node:sqlite), regardless of the user's Node version. The user's Node is
12
+ // only ever a launcher; even an ancient version can run this file.
13
+ //
14
+ // Self-heal (issue #303): some registries — notably the npmmirror/cnpm mirrors,
15
+ // and some corporate proxies — don't reliably mirror the per-platform
16
+ // optionalDependencies. npm treats an unfetchable optional dep as success and
17
+ // silently skips it, so the bundle goes missing and every command fails. When
18
+ // the installed bundle can't be resolved, this shim falls back to downloading
19
+ // the matching bundle straight from GitHub Releases — the very archive
20
+ // install.sh uses — into a cache dir, then runs that. Knobs:
21
+ // CODEGRAPH_NO_DOWNLOAD=1 disable the network fallback (print guidance)
22
+ // CODEGRAPH_INSTALL_DIR=DIR cache location (default: ~/.codegraph)
23
+ // CODEGRAPH_DOWNLOAD_BASE=URL release-download base (for mirrors/air-gapped)
24
+ //
25
+ // Wired up at release time as the main package's `bin`:
26
+ // "bin": { "codegraph": "npm-shim.js" }
27
+ // with the platform packages listed in `optionalDependencies`.
28
+
29
+ var childProcess = require('child_process');
30
+ var fs = require('fs');
31
+ var os = require('os');
32
+ var path = require('path');
33
+
34
+ var target = process.platform + '-' + process.arch; // e.g. darwin-arm64, linux-x64
35
+ var pkg = '@colbymchenry/codegraph-' + target;
36
+ var isWindows = process.platform === 'win32';
37
+ var REPO = 'colbymchenry/codegraph';
38
+
39
+ main().catch(function (e) {
40
+ process.stderr.write('codegraph: ' + (e && e.message ? e.message : String(e)) + '\n');
41
+ process.exit(1);
42
+ });
43
+
44
+ async function main() {
45
+ // Happy path: the npm-installed optional dependency. Fall back to a download
46
+ // when the registry didn't deliver it.
47
+ var resolved = resolveInstalledBundle() || (await selfHealBundle());
48
+ var res = childProcess.spawnSync(resolved.command, resolved.args, { stdio: 'inherit' });
49
+ if (res.error) {
50
+ process.stderr.write('codegraph: ' + res.error.message + '\n');
51
+ process.exit(1);
52
+ }
53
+ process.exit(res.status === null ? 1 : res.status);
54
+ }
55
+
56
+ // Resolve the launcher from the installed per-platform optionalDependency.
57
+ // Returns {command, args} or null if the package isn't installed.
58
+ function resolveInstalledBundle() {
59
+ try {
60
+ if (isWindows) {
61
+ // Modern Node refuses to spawn the bundle's .cmd directly (EINVAL, the
62
+ // CVE-2024-27980 hardening on Node 24), so invoke the bundled node.exe
63
+ // against the app entry point and pass --liftoff-only here.
64
+ var nodeExe = require.resolve(pkg + '/node.exe');
65
+ var entry = require.resolve(pkg + '/lib/dist/bin/codegraph.js');
66
+ return { command: nodeExe, args: liftoff(entry) };
67
+ }
68
+ return { command: require.resolve(pkg + '/bin/codegraph'), args: process.argv.slice(2) };
69
+ } catch (e) {
70
+ return null;
71
+ }
72
+ }
73
+
74
+ // Locate the launcher inside an extracted GitHub bundle directory (same
75
+ // node/lib/bin layout as the npm platform package). Returns {command, args} or
76
+ // null when the directory doesn't hold a usable bundle yet.
77
+ function launcherIn(dir) {
78
+ if (isWindows) {
79
+ var nodeExe = path.join(dir, 'node.exe');
80
+ var entry = path.join(dir, 'lib', 'dist', 'bin', 'codegraph.js');
81
+ if (fs.existsSync(nodeExe) && fs.existsSync(entry)) {
82
+ return { command: nodeExe, args: liftoff(entry) };
83
+ }
84
+ } else {
85
+ var launcher = path.join(dir, 'bin', 'codegraph');
86
+ if (fs.existsSync(launcher)) return { command: launcher, args: process.argv.slice(2) };
87
+ }
88
+ return null;
89
+ }
90
+
91
+ // --liftoff-only keeps tree-sitter's WASM grammars off V8's turboshaft tier to
92
+ // avoid the Zone OOM on Node >= 22 (issues #293/#298). The unix bin/codegraph
93
+ // launcher already passes it; on Windows we invoke node.exe directly so add it.
94
+ function liftoff(entry) {
95
+ return ['--liftoff-only', entry].concat(process.argv.slice(2));
96
+ }
97
+
98
+ // Download + cache the platform bundle from GitHub Releases. Returns
99
+ // {command, args}; exits the process with guidance if it can't.
100
+ async function selfHealBundle() {
101
+ var version = readVersion();
102
+ var bundlesDir = path.join(process.env.CODEGRAPH_INSTALL_DIR || path.join(os.homedir(), '.codegraph'), 'bundles');
103
+ var dest = path.join(bundlesDir, target + '-' + version);
104
+
105
+ // Already downloaded by a previous run? Use it even when downloads are
106
+ // disabled — CODEGRAPH_NO_DOWNLOAD blocks fetching, not a cached bundle.
107
+ var cached = launcherIn(dest);
108
+ if (cached) return cached;
109
+
110
+ if (process.env.CODEGRAPH_NO_DOWNLOAD) {
111
+ fail('the network fallback is disabled (CODEGRAPH_NO_DOWNLOAD is set).');
112
+ }
113
+
114
+ var asset = 'codegraph-' + target + (isWindows ? '.zip' : '.tar.gz');
115
+ var base = process.env.CODEGRAPH_DOWNLOAD_BASE || ('https://github.com/' + REPO + '/releases/download');
116
+ var url = base + '/v' + version + '/' + asset;
117
+
118
+ process.stderr.write(
119
+ 'codegraph: platform bundle missing (registry did not provide ' + pkg + ').\n' +
120
+ 'codegraph: downloading ' + asset + ' from GitHub Releases (' + version + ')...\n'
121
+ );
122
+
123
+ // Stage inside bundlesDir so the final rename is on the same filesystem (atomic,
124
+ // no EXDEV across tmpfs). Strip the archive's top-level codegraph-<target>/ dir.
125
+ fs.mkdirSync(bundlesDir, { recursive: true });
126
+ var stage = fs.mkdtempSync(path.join(bundlesDir, '.dl-'));
127
+ try {
128
+ var archivePath = path.join(stage, asset);
129
+ await download(url, archivePath, 6);
130
+ await verifyChecksum(archivePath, asset, base, version);
131
+ var extracted = path.join(stage, 'bundle');
132
+ fs.mkdirSync(extracted);
133
+ extract(archivePath, extracted);
134
+
135
+ var raced = launcherIn(dest); // another process may have finished meanwhile
136
+ if (raced) { rmrf(stage); return raced; }
137
+ try {
138
+ fs.renameSync(extracted, dest);
139
+ } catch (e) {
140
+ var other = launcherIn(dest); // lost the race but theirs is valid
141
+ if (other) { rmrf(stage); return other; }
142
+ throw e;
143
+ }
144
+ } catch (e) {
145
+ rmrf(stage);
146
+ fail('download failed (' + e.message + ').\n URL: ' + url);
147
+ }
148
+ rmrf(stage);
149
+
150
+ var ready = launcherIn(dest);
151
+ if (!ready) fail('downloaded bundle is missing its launcher under ' + dest + '.');
152
+ process.stderr.write('codegraph: bundle ready.\n');
153
+ return ready;
154
+ }
155
+
156
+ function readVersion() {
157
+ try {
158
+ return require(path.join(__dirname, 'package.json')).version;
159
+ } catch (e) {
160
+ fail('could not read this package\'s version to locate a matching release.');
161
+ }
162
+ }
163
+
164
+ // GET with manual redirect following (GitHub release URLs redirect to a CDN).
165
+ function download(url, dest, redirectsLeft) {
166
+ return new Promise(function (resolve, reject) {
167
+ var https = require('https');
168
+ // timeout is an idle/inactivity timeout — it won't kill a slow-but-progressing
169
+ // download, only a stalled connection (so a blocked mirror fails fast with
170
+ // guidance instead of hanging the user's command forever).
171
+ var req = https.get(url, { headers: { 'User-Agent': 'codegraph-npm-shim' }, timeout: 30000 }, function (res) {
172
+ var status = res.statusCode;
173
+ if (status >= 300 && status < 400 && res.headers.location) {
174
+ res.resume();
175
+ if (redirectsLeft <= 0) { reject(new Error('too many redirects')); return; }
176
+ download(new URL(res.headers.location, url).toString(), dest, redirectsLeft - 1).then(resolve, reject);
177
+ return;
178
+ }
179
+ if (status !== 200) { res.resume(); reject(new Error('HTTP ' + status)); return; }
180
+ var file = fs.createWriteStream(dest);
181
+ res.on('error', reject);
182
+ res.pipe(file);
183
+ file.on('error', reject);
184
+ file.on('finish', function () { file.close(function () { resolve(); }); });
185
+ });
186
+ req.on('timeout', function () { req.destroy(new Error('connection timed out')); });
187
+ req.on('error', reject);
188
+ });
189
+ }
190
+
191
+ // Best-effort integrity check. When the release publishes a SHA256SUMS file, the
192
+ // downloaded archive MUST match its listed hash or we abort. When that file is
193
+ // absent (older releases) or simply unreachable, we proceed — the archive still
194
+ // arrived from GitHub over TLS. So tampering/corruption is caught, while a
195
+ // missing checksum never breaks an install.
196
+ async function verifyChecksum(archivePath, asset, base, version) {
197
+ var sumsPath = archivePath + '.SHA256SUMS';
198
+ try {
199
+ await download(base + '/v' + version + '/SHA256SUMS', sumsPath, 6);
200
+ } catch (e) {
201
+ return; // not published / unreachable → skip
202
+ }
203
+ var expected = null;
204
+ var lines = fs.readFileSync(sumsPath, 'utf8').split('\n');
205
+ for (var i = 0; i < lines.length; i++) {
206
+ var m = lines[i].trim().match(/^([0-9a-fA-F]{64})\s+\*?(.+)$/);
207
+ if (m && path.basename(m[2].trim()) === asset) { expected = m[1].toLowerCase(); break; }
208
+ }
209
+ if (!expected) return; // asset not listed → nothing to check
210
+ var actual = require('crypto').createHash('sha256').update(fs.readFileSync(archivePath)).digest('hex');
211
+ if (actual !== expected) {
212
+ throw new Error('checksum mismatch for ' + asset +
213
+ ' (expected ' + expected.slice(0, 12) + '…, got ' + actual.slice(0, 12) + '…)');
214
+ }
215
+ process.stderr.write('codegraph: checksum verified.\n');
216
+ }
217
+
218
+ // Extract via the system tar — present on macOS, Linux, and Windows 10+
219
+ // (bsdtar reads .zip too). No third-party dependency in the shim.
220
+ function extract(archive, destDir) {
221
+ var args = isWindows
222
+ ? ['-xf', archive, '-C', destDir, '--strip-components=1']
223
+ : ['-xzf', archive, '-C', destDir, '--strip-components=1'];
224
+ var res = childProcess.spawnSync('tar', args, { stdio: 'ignore' });
225
+ if (res.error) throw new Error('tar unavailable: ' + res.error.message);
226
+ if (res.status !== 0) throw new Error('tar exited ' + res.status);
227
+ }
228
+
229
+ function rmrf(p) {
230
+ try { fs.rmSync(p, { recursive: true, force: true }); } catch (e) { /* best effort */ }
231
+ }
232
+
233
+ function fail(reason) {
234
+ process.stderr.write(
235
+ 'codegraph: no prebuilt bundle for ' + target + '.\n' +
236
+ (reason ? 'codegraph: ' + reason + '\n' : '') +
237
+ 'Expected the optional package ' + pkg + ' to be installed.\n' +
238
+ 'A registry mirror (e.g. npmmirror/cnpm) that did not mirror the per-platform\n' +
239
+ 'package is the usual cause. Fixes:\n' +
240
+ ' - install from the official registry:\n' +
241
+ ' npm i -g @colbymchenry/codegraph --registry=https://registry.npmjs.org\n' +
242
+ ' - or use the standalone installer (no Node required):\n' +
243
+ ' curl -fsSL https://raw.githubusercontent.com/' + REPO + '/main/install.sh | sh\n'
244
+ );
245
+ process.exit(1);
246
+ }
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Assemble the npm thin-installer packages from built bundles (esbuild pattern).
4
+ #
5
+ # Produces, under release/npm/:
6
+ # codegraph-<target>/ one per built bundle — the vendored Node + app, tagged
7
+ # with os/cpu so npm installs only the matching one.
8
+ # main/ the @colbymchenry/codegraph shim package: a tiny bin
9
+ # that execs the matching platform bundle, with every
10
+ # platform package in optionalDependencies.
11
+ #
12
+ # The release pipeline then `npm publish`es each dir. This does NOT touch the
13
+ # repo's package.json — the dev/from-source path keeps working; the *published*
14
+ # main package's shape is generated here.
15
+ #
16
+ # Prereq: run build-bundle.sh for each target first (release/codegraph-*.tar.gz).
17
+ # Usage: scripts/pack-npm.sh [version] (default: version from package.json)
18
+ set -euo pipefail
19
+
20
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
21
+ VERSION="${1:-$(node -p "require('$ROOT/package.json').version")}"
22
+ SCOPE="@colbymchenry"
23
+ REL="$ROOT/release"
24
+ NPM="$REL/npm"
25
+
26
+ rm -rf "$NPM"
27
+ mkdir -p "$NPM/main"
28
+
29
+ shopt -s nullglob
30
+ archives=("$REL"/codegraph-*.tar.gz "$REL"/codegraph-*.zip)
31
+ [ ${#archives[@]} -gt 0 ] || { echo "[pack-npm] no bundles in $REL — run build-bundle.sh first" >&2; exit 1; }
32
+
33
+ targets=()
34
+ for archive in "${archives[@]}"; do
35
+ fname="$(basename "$archive")"
36
+ case "$fname" in
37
+ *.tar.gz) base="${fname%.tar.gz}" ;; # codegraph-<target>
38
+ *.zip) base="${fname%.zip}" ;;
39
+ esac
40
+ target="${base#codegraph-}" # <target>, e.g. darwin-arm64 / win32-x64
41
+ os="${target%-*}" # darwin | linux | win32
42
+ arch="${target##*-}" # arm64 | x64
43
+ pkgdir="$NPM/$base"
44
+ mkdir -p "$pkgdir"
45
+ case "$fname" in
46
+ *.zip)
47
+ tmpx="$(mktemp -d)"
48
+ unzip -q "$archive" -d "$tmpx"
49
+ mv "$tmpx/codegraph-${target}"/* "$pkgdir"/
50
+ rm -rf "$tmpx"
51
+ nodefile="node.exe"
52
+ ;;
53
+ *)
54
+ tar -xzf "$archive" -C "$pkgdir" --strip-components=1
55
+ nodefile="node"
56
+ ;;
57
+ esac
58
+ VERSION="$VERSION" SCOPE="$SCOPE" TARGET="$target" OSV="$os" ARCHV="$arch" NODEFILE="$nodefile" \
59
+ node -e '
60
+ const fs=require("fs");
61
+ fs.writeFileSync(process.argv[1], JSON.stringify({
62
+ name: `${process.env.SCOPE}/codegraph-${process.env.TARGET}`,
63
+ version: process.env.VERSION,
64
+ description: `CodeGraph self-contained bundle for ${process.env.TARGET}`,
65
+ os: [process.env.OSV], cpu: [process.env.ARCHV],
66
+ files: [process.env.NODEFILE, "lib", "bin"],
67
+ license: "MIT"
68
+ }, null, 2) + "\n");
69
+ ' "$pkgdir/package.json"
70
+ targets+=("$target")
71
+ echo "[pack-npm] ${SCOPE}/codegraph-${target}@${VERSION}"
72
+ done
73
+
74
+ # Main shim package.
75
+ cp "$ROOT/scripts/npm-shim.js" "$NPM/main/npm-shim.js"
76
+ [ -f "$ROOT/README.md" ] && cp "$ROOT/README.md" "$NPM/main/README.md"
77
+ VERSION="$VERSION" SCOPE="$SCOPE" TARGETS="${targets[*]}" \
78
+ node -e '
79
+ const fs=require("fs");
80
+ const opt={};
81
+ for (const t of process.env.TARGETS.split(/\s+/).filter(Boolean))
82
+ opt[`${process.env.SCOPE}/codegraph-${t}`]=process.env.VERSION;
83
+ fs.writeFileSync(process.argv[1], JSON.stringify({
84
+ name: `${process.env.SCOPE}/codegraph`,
85
+ version: process.env.VERSION,
86
+ description: "Local-first code intelligence for AI agents (MCP). Self-contained — bundles its own runtime.",
87
+ bin: { codegraph: "npm-shim.js" },
88
+ optionalDependencies: opt,
89
+ files: ["npm-shim.js","README.md"],
90
+ license: "MIT"
91
+ }, null, 2) + "\n");
92
+ ' "$NPM/main/package.json"
93
+
94
+ echo "[pack-npm] ${SCOPE}/codegraph@${VERSION} (${#targets[@]} platform packages in optionalDependencies)"
95
+ echo "[pack-npm] output: $NPM"
@@ -1,112 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Patches tree-sitter-dart to use NAPI bindings compatible with tree-sitter 0.22+
4
- *
5
- * tree-sitter-dart v1.0.0 ships with NAN-style bindings that are incompatible
6
- * with tree-sitter 0.22+ which expects NAPI-style bindings with type-tagged
7
- * externals. This script rewrites the binding files and rebuilds.
8
- */
9
- const { writeFileSync, existsSync } = require('fs');
10
- const { join } = require('path');
11
- const { execSync } = require('child_process');
12
-
13
- const DART_DIR = join(__dirname, '..', 'node_modules', 'tree-sitter-dart');
14
-
15
- if (!existsSync(DART_DIR)) {
16
- // tree-sitter-dart not installed, skip
17
- process.exit(0);
18
- }
19
-
20
- // Check if already patched (look for NAPI-style binding)
21
- const bindingPath = join(DART_DIR, 'bindings', 'node', 'binding.cc');
22
- const { readFileSync } = require('fs');
23
- try {
24
- const existing = readFileSync(bindingPath, 'utf8');
25
- if (existing.includes('napi.h')) {
26
- // Already patched, check if build exists
27
- const buildPath = join(DART_DIR, 'build', 'Release', 'tree_sitter_dart_binding.node');
28
- if (existsSync(buildPath)) {
29
- console.log('tree-sitter-dart: already patched and built.');
30
- process.exit(0);
31
- }
32
- // Patched but not built, fall through to rebuild
33
- }
34
- } catch {
35
- // Can't read, continue with patch
36
- }
37
-
38
- console.log('Patching tree-sitter-dart for NAPI compatibility...');
39
-
40
- // Write NAPI-compatible binding.cc
41
- const bindingCC = `#include <napi.h>
42
-
43
- typedef struct TSLanguage TSLanguage;
44
-
45
- extern "C" TSLanguage *tree_sitter_dart();
46
-
47
- // "tree-sitter", "language" hashed with BLAKE2
48
- const napi_type_tag LANGUAGE_TYPE_TAG = {
49
- 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
50
- };
51
-
52
- Napi::Object Init(Napi::Env env, Napi::Object exports) {
53
- exports["name"] = Napi::String::New(env, "dart");
54
- auto language = Napi::External<TSLanguage>::New(env, tree_sitter_dart());
55
- language.TypeTag(&LANGUAGE_TYPE_TAG);
56
- exports["language"] = language;
57
- return exports;
58
- }
59
-
60
- NODE_API_MODULE(tree_sitter_dart_binding, Init)
61
- `;
62
- writeFileSync(bindingPath, bindingCC);
63
-
64
- // Write NAPI-compatible binding.gyp
65
- const bindingGyp = `{
66
- "targets": [
67
- {
68
- "target_name": "tree_sitter_dart_binding",
69
- "dependencies": [
70
- "<!(node -p \\"require('node-addon-api').targets\\"):node_addon_api_except"
71
- ],
72
- "include_dirs": [
73
- "src"
74
- ],
75
- "sources": [
76
- "src/parser.c",
77
- "bindings/node/binding.cc",
78
- "src/scanner.c"
79
- ],
80
- "conditions": [
81
- ["OS!='win'", {
82
- "cflags_c": [
83
- "-std=c99"
84
- ]
85
- }, {
86
- "cflags_c": [
87
- "/std:c11",
88
- "/utf-8"
89
- ]
90
- }]
91
- ]
92
- }
93
- ]
94
- }
95
- `;
96
- writeFileSync(join(DART_DIR, 'binding.gyp'), bindingGyp);
97
-
98
- // Rebuild native module
99
- try {
100
- execSync('npx node-gyp rebuild', {
101
- cwd: DART_DIR,
102
- stdio: 'pipe',
103
- timeout: 120000,
104
- });
105
- console.log('tree-sitter-dart: patched and rebuilt successfully.');
106
- } catch (error) {
107
- console.error('Warning: Failed to rebuild tree-sitter-dart native module.');
108
- console.error('Dart language support may not work.');
109
- if (process.env.DEBUG) {
110
- console.error(error.stderr?.toString());
111
- }
112
- }