@optave/codegraph 3.11.2 → 3.13.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 (236) hide show
  1. package/README.md +73 -37
  2. package/dist/cli/commands/audit.d.ts.map +1 -1
  3. package/dist/cli/commands/audit.js +2 -1
  4. package/dist/cli/commands/audit.js.map +1 -1
  5. package/dist/cli/commands/batch.d.ts.map +1 -1
  6. package/dist/cli/commands/batch.js +1 -0
  7. package/dist/cli/commands/batch.js.map +1 -1
  8. package/dist/cli/commands/build.d.ts.map +1 -1
  9. package/dist/cli/commands/build.js +6 -1
  10. package/dist/cli/commands/build.js.map +1 -1
  11. package/dist/cli/commands/config.d.ts +3 -0
  12. package/dist/cli/commands/config.d.ts.map +1 -0
  13. package/dist/cli/commands/config.js +272 -0
  14. package/dist/cli/commands/config.js.map +1 -0
  15. package/dist/cli/commands/triage.js +1 -1
  16. package/dist/cli/commands/triage.js.map +1 -1
  17. package/dist/cli/index.d.ts.map +1 -1
  18. package/dist/cli/index.js +10 -0
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/shared/options.d.ts +2 -1
  21. package/dist/cli/shared/options.d.ts.map +1 -1
  22. package/dist/cli/shared/options.js +11 -1
  23. package/dist/cli/shared/options.js.map +1 -1
  24. package/dist/cli/types.d.ts +2 -0
  25. package/dist/cli/types.d.ts.map +1 -1
  26. package/dist/db/migrations.d.ts.map +1 -1
  27. package/dist/db/migrations.js +8 -1
  28. package/dist/db/migrations.js.map +1 -1
  29. package/dist/domain/analysis/module-map.d.ts +2 -0
  30. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  31. package/dist/domain/analysis/module-map.js +24 -2
  32. package/dist/domain/analysis/module-map.js.map +1 -1
  33. package/dist/domain/graph/builder/call-resolver.d.ts +16 -10
  34. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
  35. package/dist/domain/graph/builder/call-resolver.js +251 -34
  36. package/dist/domain/graph/builder/call-resolver.js.map +1 -1
  37. package/dist/domain/graph/builder/cha.d.ts +69 -0
  38. package/dist/domain/graph/builder/cha.d.ts.map +1 -0
  39. package/dist/domain/graph/builder/cha.js +158 -0
  40. package/dist/domain/graph/builder/cha.js.map +1 -0
  41. package/dist/domain/graph/builder/context.d.ts +3 -0
  42. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  43. package/dist/domain/graph/builder/context.js +2 -0
  44. package/dist/domain/graph/builder/context.js.map +1 -1
  45. package/dist/domain/graph/builder/helpers.d.ts +25 -1
  46. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  47. package/dist/domain/graph/builder/helpers.js +178 -5
  48. package/dist/domain/graph/builder/helpers.js.map +1 -1
  49. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  50. package/dist/domain/graph/builder/incremental.js +74 -2
  51. package/dist/domain/graph/builder/incremental.js.map +1 -1
  52. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  53. package/dist/domain/graph/builder/pipeline.js +37 -2
  54. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  55. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  56. package/dist/domain/graph/builder/stages/build-edges.js +704 -34
  57. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  58. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  59. package/dist/domain/graph/builder/stages/detect-changes.js +3 -2
  60. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  61. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  62. package/dist/domain/graph/builder/stages/finalize.js +4 -0
  63. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  64. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  65. package/dist/domain/graph/builder/stages/native-orchestrator.js +783 -37
  66. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  67. package/dist/domain/graph/builder/stages/resolve-imports.d.ts +1 -0
  68. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  69. package/dist/domain/graph/builder/stages/resolve-imports.js +10 -1
  70. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  71. package/dist/domain/graph/journal.js +1 -1
  72. package/dist/domain/graph/journal.js.map +1 -1
  73. package/dist/domain/graph/resolver/points-to.d.ts +53 -0
  74. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -0
  75. package/dist/domain/graph/resolver/points-to.js +213 -0
  76. package/dist/domain/graph/resolver/points-to.js.map +1 -0
  77. package/dist/domain/graph/resolver/ts-resolver.d.ts +9 -0
  78. package/dist/domain/graph/resolver/ts-resolver.d.ts.map +1 -0
  79. package/dist/domain/graph/resolver/ts-resolver.js +476 -0
  80. package/dist/domain/graph/resolver/ts-resolver.js.map +1 -0
  81. package/dist/domain/parser.d.ts +12 -4
  82. package/dist/domain/parser.d.ts.map +1 -1
  83. package/dist/domain/parser.js +83 -20
  84. package/dist/domain/parser.js.map +1 -1
  85. package/dist/domain/wasm-worker-entry.js +35 -2
  86. package/dist/domain/wasm-worker-entry.js.map +1 -1
  87. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  88. package/dist/domain/wasm-worker-pool.js +34 -0
  89. package/dist/domain/wasm-worker-pool.js.map +1 -1
  90. package/dist/domain/wasm-worker-protocol.d.ts +15 -1
  91. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  92. package/dist/extractors/c.js +3 -3
  93. package/dist/extractors/c.js.map +1 -1
  94. package/dist/extractors/clojure.js +1 -1
  95. package/dist/extractors/clojure.js.map +1 -1
  96. package/dist/extractors/cpp.d.ts.map +1 -1
  97. package/dist/extractors/cpp.js +45 -4
  98. package/dist/extractors/cpp.js.map +1 -1
  99. package/dist/extractors/csharp.d.ts.map +1 -1
  100. package/dist/extractors/csharp.js +37 -8
  101. package/dist/extractors/csharp.js.map +1 -1
  102. package/dist/extractors/cuda.d.ts.map +1 -1
  103. package/dist/extractors/cuda.js +45 -4
  104. package/dist/extractors/cuda.js.map +1 -1
  105. package/dist/extractors/elixir.js +6 -6
  106. package/dist/extractors/elixir.js.map +1 -1
  107. package/dist/extractors/fsharp.js +1 -1
  108. package/dist/extractors/fsharp.js.map +1 -1
  109. package/dist/extractors/go.js +5 -5
  110. package/dist/extractors/go.js.map +1 -1
  111. package/dist/extractors/haskell.js +1 -1
  112. package/dist/extractors/haskell.js.map +1 -1
  113. package/dist/extractors/helpers.d.ts +11 -0
  114. package/dist/extractors/helpers.d.ts.map +1 -1
  115. package/dist/extractors/helpers.js +40 -0
  116. package/dist/extractors/helpers.js.map +1 -1
  117. package/dist/extractors/java.d.ts.map +1 -1
  118. package/dist/extractors/java.js +10 -9
  119. package/dist/extractors/java.js.map +1 -1
  120. package/dist/extractors/javascript.d.ts +2 -0
  121. package/dist/extractors/javascript.d.ts.map +1 -1
  122. package/dist/extractors/javascript.js +1812 -71
  123. package/dist/extractors/javascript.js.map +1 -1
  124. package/dist/extractors/kotlin.js +5 -5
  125. package/dist/extractors/kotlin.js.map +1 -1
  126. package/dist/extractors/lua.js +1 -1
  127. package/dist/extractors/lua.js.map +1 -1
  128. package/dist/extractors/objc.js +3 -3
  129. package/dist/extractors/objc.js.map +1 -1
  130. package/dist/extractors/ocaml.js +1 -1
  131. package/dist/extractors/ocaml.js.map +1 -1
  132. package/dist/extractors/php.js +2 -2
  133. package/dist/extractors/php.js.map +1 -1
  134. package/dist/extractors/python.js +7 -7
  135. package/dist/extractors/python.js.map +1 -1
  136. package/dist/extractors/ruby.js +2 -2
  137. package/dist/extractors/ruby.js.map +1 -1
  138. package/dist/extractors/scala.js +1 -1
  139. package/dist/extractors/scala.js.map +1 -1
  140. package/dist/extractors/solidity.js +1 -1
  141. package/dist/extractors/solidity.js.map +1 -1
  142. package/dist/extractors/swift.js +4 -4
  143. package/dist/extractors/swift.js.map +1 -1
  144. package/dist/extractors/zig.js +4 -4
  145. package/dist/extractors/zig.js.map +1 -1
  146. package/dist/features/structure-query.d.ts +1 -1
  147. package/dist/features/structure-query.d.ts.map +1 -1
  148. package/dist/features/structure-query.js +6 -6
  149. package/dist/features/structure-query.js.map +1 -1
  150. package/dist/index.d.ts +1 -1
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +1 -1
  153. package/dist/index.js.map +1 -1
  154. package/dist/infrastructure/config.d.ts +85 -2
  155. package/dist/infrastructure/config.d.ts.map +1 -1
  156. package/dist/infrastructure/config.js +408 -19
  157. package/dist/infrastructure/config.js.map +1 -1
  158. package/dist/infrastructure/native.d.ts +11 -0
  159. package/dist/infrastructure/native.d.ts.map +1 -1
  160. package/dist/infrastructure/native.js +78 -5
  161. package/dist/infrastructure/native.js.map +1 -1
  162. package/dist/infrastructure/registry.d.ts +27 -0
  163. package/dist/infrastructure/registry.d.ts.map +1 -1
  164. package/dist/infrastructure/registry.js +59 -1
  165. package/dist/infrastructure/registry.js.map +1 -1
  166. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  167. package/dist/presentation/queries-cli/overview.js +5 -0
  168. package/dist/presentation/queries-cli/overview.js.map +1 -1
  169. package/dist/presentation/structure.d.ts +1 -1
  170. package/dist/presentation/structure.d.ts.map +1 -1
  171. package/dist/presentation/structure.js +2 -2
  172. package/dist/presentation/structure.js.map +1 -1
  173. package/dist/types.d.ts +221 -0
  174. package/dist/types.d.ts.map +1 -1
  175. package/grammars/tree-sitter-gleam.wasm +0 -0
  176. package/package.json +7 -8
  177. package/src/cli/commands/audit.ts +2 -1
  178. package/src/cli/commands/batch.ts +1 -0
  179. package/src/cli/commands/build.ts +6 -1
  180. package/src/cli/commands/config.ts +353 -0
  181. package/src/cli/commands/triage.ts +1 -1
  182. package/src/cli/index.ts +10 -0
  183. package/src/cli/shared/options.ts +11 -1
  184. package/src/cli/types.ts +2 -0
  185. package/src/db/migrations.ts +8 -1
  186. package/src/domain/analysis/module-map.ts +29 -1
  187. package/src/domain/graph/builder/call-resolver.ts +263 -35
  188. package/src/domain/graph/builder/cha.ts +192 -0
  189. package/src/domain/graph/builder/context.ts +3 -0
  190. package/src/domain/graph/builder/helpers.ts +195 -5
  191. package/src/domain/graph/builder/incremental.ts +80 -1
  192. package/src/domain/graph/builder/pipeline.ts +49 -2
  193. package/src/domain/graph/builder/stages/build-edges.ts +867 -32
  194. package/src/domain/graph/builder/stages/detect-changes.ts +4 -2
  195. package/src/domain/graph/builder/stages/finalize.ts +4 -0
  196. package/src/domain/graph/builder/stages/native-orchestrator.ts +910 -43
  197. package/src/domain/graph/builder/stages/resolve-imports.ts +15 -1
  198. package/src/domain/graph/journal.ts +1 -1
  199. package/src/domain/graph/resolver/points-to.ts +254 -0
  200. package/src/domain/graph/resolver/ts-resolver.ts +536 -0
  201. package/src/domain/parser.ts +86 -17
  202. package/src/domain/wasm-worker-entry.ts +35 -2
  203. package/src/domain/wasm-worker-pool.ts +22 -0
  204. package/src/domain/wasm-worker-protocol.ts +15 -0
  205. package/src/extractors/c.ts +3 -3
  206. package/src/extractors/clojure.ts +1 -1
  207. package/src/extractors/cpp.ts +47 -4
  208. package/src/extractors/csharp.ts +33 -9
  209. package/src/extractors/cuda.ts +47 -4
  210. package/src/extractors/elixir.ts +6 -6
  211. package/src/extractors/fsharp.ts +1 -1
  212. package/src/extractors/go.ts +5 -5
  213. package/src/extractors/haskell.ts +1 -1
  214. package/src/extractors/helpers.ts +43 -0
  215. package/src/extractors/java.ts +10 -9
  216. package/src/extractors/javascript.ts +1929 -72
  217. package/src/extractors/kotlin.ts +5 -5
  218. package/src/extractors/lua.ts +1 -1
  219. package/src/extractors/objc.ts +3 -3
  220. package/src/extractors/ocaml.ts +1 -1
  221. package/src/extractors/php.ts +2 -2
  222. package/src/extractors/python.ts +7 -7
  223. package/src/extractors/ruby.ts +2 -2
  224. package/src/extractors/scala.ts +1 -1
  225. package/src/extractors/solidity.ts +1 -1
  226. package/src/extractors/swift.ts +4 -4
  227. package/src/extractors/zig.ts +4 -4
  228. package/src/features/structure-query.ts +7 -7
  229. package/src/index.ts +5 -1
  230. package/src/infrastructure/config.ts +494 -20
  231. package/src/infrastructure/native.ts +87 -5
  232. package/src/infrastructure/registry.ts +82 -1
  233. package/src/presentation/queries-cli/overview.ts +15 -1
  234. package/src/presentation/structure.ts +3 -3
  235. package/src/types.ts +235 -0
  236. package/grammars/tree-sitter-erlang.wasm +0 -0
@@ -6,11 +6,13 @@
6
6
  * to the existing WASM pipeline.
7
7
  */
8
8
 
9
+ import { existsSync } from 'node:fs';
9
10
  import { createRequire } from 'node:module';
10
11
  import os from 'node:os';
12
+ import { fileURLToPath } from 'node:url';
11
13
  import { EngineError, toErrorMessage } from '../shared/errors.js';
12
14
  import type { NativeAddon } from '../types.js';
13
- import { debug } from './logger.js';
15
+ import { debug, warn } from './logger.js';
14
16
 
15
17
  let _cached: NativeAddon | null | undefined; // undefined = not yet tried, null = failed, NativeAddon = module
16
18
  let _loadError: Error | null = null;
@@ -44,31 +46,107 @@ const PLATFORM_PACKAGES: Record<string, string> = {
44
46
  'win32-x64': '@optave/codegraph-win32-x64-msvc',
45
47
  };
46
48
 
49
+ /**
50
+ * Map of (platform-arch[-libc]) → locally compiled binary filename.
51
+ * Checked before the npm package so that locally compiled Rust changes
52
+ * are picked up immediately in development without publishing a new release.
53
+ */
54
+ const PLATFORM_LOCAL_BINARIES: Record<string, string> = {
55
+ 'linux-x64-gnu': 'codegraph-core.linux-x64-gnu.node',
56
+ 'linux-x64-musl': 'codegraph-core.linux-x64-musl.node',
57
+ 'linux-arm64-gnu': 'codegraph-core.linux-arm64-gnu.node',
58
+ 'linux-arm64-musl': 'codegraph-core.linux-arm64-musl.node',
59
+ 'darwin-arm64': 'codegraph-core.darwin-arm64.node',
60
+ 'darwin-x64': 'codegraph-core.darwin-x64.node',
61
+ 'win32-x64': 'codegraph-core.win32-x64-msvc.node',
62
+ };
63
+
64
+ /** Compute the platform key used to index PLATFORM_PACKAGES / PLATFORM_LOCAL_BINARIES. */
65
+ function resolvePlatformKey(): string {
66
+ const platform = os.platform();
67
+ const arch = os.arch();
68
+ return platform === 'linux' ? `${platform}-${arch}-${detectLibc()}` : `${platform}-${arch}`;
69
+ }
70
+
47
71
  /**
48
72
  * Resolve the platform-specific npm package name for the native addon.
49
73
  * Returns null if the current platform is not supported.
50
74
  */
51
75
  function resolvePlatformPackage(): string | null {
52
- const platform = os.platform();
53
- const arch = os.arch();
54
- const key = platform === 'linux' ? `${platform}-${arch}-${detectLibc()}` : `${platform}-${arch}`;
55
- return PLATFORM_PACKAGES[key] || null;
76
+ return PLATFORM_PACKAGES[resolvePlatformKey()] ?? null;
56
77
  }
57
78
 
58
79
  /**
59
80
  * Try to load the native napi addon.
60
81
  * Returns the module on success, null on failure.
82
+ *
83
+ * Load order:
84
+ * 1. NAPI_RS_NATIVE_LIBRARY_PATH env var (explicit override)
85
+ * 2. locally compiled binary in crates/codegraph-core/ (dev mode — preferred
86
+ * over the npm package so that Rust changes are picked up immediately
87
+ * without publishing a new release)
88
+ * 3. npm platform package (production path)
61
89
  */
62
90
  export function loadNative(): NativeAddon | null {
63
91
  if (_cached !== undefined) return _cached;
64
92
 
93
+ const platformKey = resolvePlatformKey();
94
+
95
+ // 1. Explicit path override — highest priority. Failure is fatal: if the
96
+ // operator set this variable, silently loading a different binary would
97
+ // be harder to diagnose than an explicit error.
98
+ const envPath = process.env.NAPI_RS_NATIVE_LIBRARY_PATH;
99
+ if (envPath) {
100
+ try {
101
+ _cached = _require(envPath) as NativeAddon;
102
+ debug(`loadNative: loaded from NAPI_RS_NATIVE_LIBRARY_PATH: ${envPath}`);
103
+ return _cached;
104
+ } catch (err) {
105
+ _loadError = err as Error;
106
+ warn(
107
+ `loadNative: NAPI_RS_NATIVE_LIBRARY_PATH is set but failed to load "${envPath}": ${toErrorMessage(err as Error)}`,
108
+ );
109
+ _cached = null;
110
+ return null;
111
+ }
112
+ }
113
+
114
+ // 2. Locally compiled dev binary — preferred over npm package so that Rust
115
+ // changes are visible without publishing. Only used when the file exists.
116
+ // If the file exists but fails to load (e.g. stale ABI), we warn and halt
117
+ // rather than silently falling through to the npm package — that would
118
+ // defeat the purpose of this priority order.
119
+ const localFile = PLATFORM_LOCAL_BINARIES[platformKey];
120
+ if (localFile) {
121
+ const localPath = fileURLToPath(
122
+ new URL(`../../crates/codegraph-core/${localFile}`, import.meta.url),
123
+ );
124
+ if (existsSync(localPath)) {
125
+ try {
126
+ _cached = _require(localPath) as NativeAddon;
127
+ debug(`loadNative: loaded local dev binary: ${localPath}`);
128
+ return _cached;
129
+ } catch (err) {
130
+ _loadError = err as Error;
131
+ warn(
132
+ `loadNative: local dev binary exists but failed to load "${localPath}": ${toErrorMessage(err as Error)}`,
133
+ );
134
+ _cached = null;
135
+ return null;
136
+ }
137
+ }
138
+ }
139
+
140
+ // 3. Published npm platform package — production path.
65
141
  const pkg = resolvePlatformPackage();
66
142
  if (pkg) {
67
143
  try {
68
144
  _cached = _require(pkg) as NativeAddon;
145
+ debug(`loadNative: loaded npm package: ${pkg}`);
69
146
  return _cached;
70
147
  } catch (err) {
71
148
  _loadError = err as Error;
149
+ debug(`loadNative: npm package ${pkg} not available: ${toErrorMessage(err as Error)}`);
72
150
  }
73
151
  } else {
74
152
  _loadError = new Error(`Unsupported platform: ${os.platform()}-${os.arch()}`);
@@ -88,6 +166,10 @@ export function isNativeAvailable(): boolean {
88
166
  /**
89
167
  * Read the version from the platform-specific npm package.json.
90
168
  * Returns null if the package is not installed or has no version.
169
+ *
170
+ * Note: always reports the npm package version. When the local dev binary or
171
+ * NAPI_RS_NATIVE_LIBRARY_PATH is loaded instead, this version may not match
172
+ * the running binary.
91
173
  */
92
174
  export function getNativePackageVersion(): string | null {
93
175
  const pkg = resolvePlatformPackage();
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
+ import type { ConsentDecision } from '../types.js';
4
5
  import { debug, warn } from './logger.js';
5
6
 
6
7
  export const REGISTRY_PATH: string =
@@ -16,8 +17,15 @@ interface RegistryEntry {
16
17
  lastAccessedAt?: string;
17
18
  }
18
19
 
20
+ interface UserConfigSection {
21
+ /** Per-repo consent decisions keyed by absolute repo path. */
22
+ consent: Record<string, ConsentDecision>;
23
+ }
24
+
19
25
  interface Registry {
20
26
  repos: Record<string, RegistryEntry>;
27
+ /** User-level global config consent store — separate from MCP repo listings. */
28
+ userConfig?: UserConfigSection;
21
29
  }
22
30
 
23
31
  /**
@@ -160,6 +168,67 @@ export function resolveRepoDbPath(
160
168
  return entry.dbPath;
161
169
  }
162
170
 
171
+ // ── User-config consent ────────────────────────────────────────────────
172
+
173
+ /**
174
+ * Read the per-repo consent decision for the global user config.
175
+ * Returns `undefined` when the repo is undecided (no recorded decision).
176
+ */
177
+ export function getUserConfigConsent(
178
+ rootDir: string,
179
+ registryPath: string = REGISTRY_PATH,
180
+ ): ConsentDecision | undefined {
181
+ const registry = loadRegistry(registryPath);
182
+ const absRoot = path.resolve(rootDir);
183
+ return registry.userConfig?.consent?.[absRoot];
184
+ }
185
+
186
+ /**
187
+ * Persist a per-repo consent decision. Atomic write via temp+rename.
188
+ */
189
+ export function setUserConfigConsent(
190
+ rootDir: string,
191
+ decision: ConsentDecision,
192
+ registryPath: string = REGISTRY_PATH,
193
+ ): void {
194
+ const registry = loadRegistry(registryPath);
195
+ const absRoot = path.resolve(rootDir);
196
+ if (!registry.userConfig) registry.userConfig = { consent: {} };
197
+ if (!registry.userConfig.consent) registry.userConfig.consent = {};
198
+ registry.userConfig.consent[absRoot] = decision;
199
+ saveRegistry(registry, registryPath);
200
+ debug(`User-config consent for "${absRoot}" set to "${decision}"`);
201
+ }
202
+
203
+ /**
204
+ * List every repo with a recorded consent decision, sorted by path.
205
+ */
206
+ export function listUserConfigConsent(
207
+ registryPath: string = REGISTRY_PATH,
208
+ ): Array<{ path: string; decision: ConsentDecision }> {
209
+ const registry = loadRegistry(registryPath);
210
+ const consent = registry.userConfig?.consent ?? {};
211
+ return Object.entries(consent)
212
+ .map(([p, decision]) => ({ path: p, decision }))
213
+ .sort((a, b) => a.path.localeCompare(b.path));
214
+ }
215
+
216
+ /**
217
+ * Revert a repo to undecided state. Returns true if a decision was removed.
218
+ */
219
+ export function clearUserConfigConsent(
220
+ rootDir: string,
221
+ registryPath: string = REGISTRY_PATH,
222
+ ): boolean {
223
+ const registry = loadRegistry(registryPath);
224
+ const absRoot = path.resolve(rootDir);
225
+ const consent = registry.userConfig?.consent;
226
+ if (!consent || !(absRoot in consent)) return false;
227
+ delete consent[absRoot];
228
+ saveRegistry(registry, registryPath);
229
+ return true;
230
+ }
231
+
163
232
  interface PrunedEntry {
164
233
  name: string;
165
234
  path: string;
@@ -200,7 +269,19 @@ export function pruneRegistry(
200
269
  }
201
270
  }
202
271
 
203
- if (!dryRun && pruned.length > 0) {
272
+ // Prune consent entries whose repo paths no longer exist on disk.
273
+ // Consent entries are TTL-exempt — only the missing-path rule applies.
274
+ let consentChanged = false;
275
+ if (!dryRun && registry.userConfig?.consent) {
276
+ for (const p of Object.keys(registry.userConfig.consent)) {
277
+ if (!fs.existsSync(p)) {
278
+ delete registry.userConfig.consent[p];
279
+ consentChanged = true;
280
+ }
281
+ }
282
+ }
283
+
284
+ if (!dryRun && (pruned.length > 0 || consentChanged)) {
204
285
  saveRegistry(registry, registryPath);
205
286
  }
206
287
 
@@ -54,7 +54,13 @@ interface EmbeddingsInfo {
54
54
 
55
55
  interface QualityInfo {
56
56
  score: number;
57
- callerCoverage: { ratio: number; covered: number; total: number };
57
+ callerCoverage: {
58
+ ratio: number;
59
+ percentage: number;
60
+ covered: number;
61
+ total: number;
62
+ byTechnique?: Record<string, number>;
63
+ };
58
64
  callConfidence: { ratio: number; highConf: number; total: number };
59
65
  falsePositiveWarnings: { name: string; callerCount: number; file: string; line: number }[];
60
66
  }
@@ -189,6 +195,14 @@ function printQuality(data: StatsData): void {
189
195
  console.log(
190
196
  ` Caller coverage: ${(cc.ratio * 100).toFixed(1)}% (${cc.covered}/${cc.total} functions have >=1 caller)`,
191
197
  );
198
+ if (cc.byTechnique && Object.keys(cc.byTechnique).length > 0) {
199
+ const entries = Object.entries(cc.byTechnique).sort((a, b) => b[1] - a[1]) as [
200
+ string,
201
+ number,
202
+ ][];
203
+ const parts = entries.map(([k, v]) => `${k} ${v}`).join(' ');
204
+ console.log(` by technique: ${parts}`);
205
+ }
192
206
  console.log(
193
207
  ` Call confidence: ${(cf.ratio * 100).toFixed(1)}% (${cf.highConf}/${cf.total} call edges are high-confidence)`,
194
208
  );
@@ -53,15 +53,15 @@ interface HotspotsResult {
53
53
  metric: string;
54
54
  level: string;
55
55
  limit: number;
56
- hotspots: any[];
56
+ items: any[];
57
57
  }
58
58
 
59
59
  export function formatHotspots(data: HotspotsResult): string {
60
- if (data.hotspots.length === 0) return 'No hotspots found. Run "codegraph build" first.';
60
+ if (data.items.length === 0) return 'No hotspots found. Run "codegraph build" first.';
61
61
 
62
62
  const lines = [`\nHotspots by ${data.metric} (${data.level}-level, top ${data.limit}):\n`];
63
63
  let rank = 1;
64
- for (const h of data.hotspots) {
64
+ for (const h of data.items) {
65
65
  const extra =
66
66
  h.kind === 'directory'
67
67
  ? `${h.fileCount} files, cohesion=${h.cohesion !== null ? h.cohesion!.toFixed(2) : 'n/a'}`
package/src/types.ts CHANGED
@@ -523,6 +523,125 @@ export interface TypeMapEntry {
523
523
  confidence: number;
524
524
  }
525
525
 
526
+ /**
527
+ * A variable assignment from a call expression, recorded during extraction for
528
+ * cross-file return-type propagation (Phase 8.2).
529
+ */
530
+ export interface CallAssignment {
531
+ /** Variable being assigned to. */
532
+ varName: string;
533
+ /** Name of the function or method being called. */
534
+ calleeName: string;
535
+ /** Resolved receiver type, if the call is a method call (e.g. service.getRepo()). */
536
+ receiverTypeName?: string;
537
+ }
538
+
539
+ /**
540
+ * A function-reference binding recorded during extraction for points-to analysis (Phase 8.3).
541
+ * Captures `const fn = handler` or `const fn = obj.method` patterns where the right-hand
542
+ * side is a named function reference (not a call expression or literal).
543
+ */
544
+ export interface FnRefBinding {
545
+ /** Variable being assigned (the left-hand side identifier). */
546
+ lhs: string;
547
+ /** Named function/property on the right-hand side. */
548
+ rhs: string;
549
+ /** If rhs is a member expression (obj.method), the receiver object name. */
550
+ rhsReceiver?: string;
551
+ }
552
+
553
+ /**
554
+ * An argument-to-parameter binding at a call site, recorded for parameter-flow
555
+ * points-to analysis (Phase 8.3c). Captures `f(x)` where `x` is an identifier
556
+ * that may carry a function reference into `f`'s parameter.
557
+ */
558
+ export interface ParamBinding {
559
+ /** The function being called at the call site. */
560
+ callee: string;
561
+ /** Zero-based index of the argument. */
562
+ argIndex: number;
563
+ /** Identifier name of the argument being passed. */
564
+ argName: string;
565
+ }
566
+
567
+ /**
568
+ * A this-context binding recorded when `fn.call(namedCtx, ...)` or
569
+ * `fn.apply(namedCtx, ...)` is seen. Seeds `fn::this → namedCtx` in the
570
+ * points-to map so that `this()` calls inside `fn` resolve to `namedCtx`.
571
+ */
572
+ export interface ThisCallBinding {
573
+ /** The function being invoked via .call() or .apply(). */
574
+ callee: string;
575
+ /** The identifier passed as the `this` context (first argument). */
576
+ thisArg: string;
577
+ }
578
+
579
+ /**
580
+ * An array-element binding: `const arr = [fn1, fn2]` records each named function
581
+ * stored at a specific index. Phase 8.3e: array-element pts tracking.
582
+ */
583
+ export interface ArrayElemBinding {
584
+ arrayName: string;
585
+ index: number;
586
+ elemName: string;
587
+ }
588
+
589
+ /**
590
+ * A spread-argument binding: `f(...arr)` records that `arr` is spread into `f`'s
591
+ * parameter list starting at `startIndex`. Phase 8.3e.
592
+ */
593
+ export interface SpreadArgBinding {
594
+ callee: string;
595
+ arrayName: string;
596
+ startIndex: number;
597
+ }
598
+
599
+ /**
600
+ * A for-of iteration binding: `for (const x of arr)` records that `x` receives
601
+ * each element of `arr` within `enclosingFunc`. Phase 8.3e.
602
+ */
603
+ export interface ForOfBinding {
604
+ varName: string;
605
+ sourceName: string;
606
+ enclosingFunc: string;
607
+ }
608
+
609
+ /**
610
+ * An array-callback binding: `Array.from(arr, cb)` records that `cb`'s first
611
+ * parameter receives each element of `arr`. Phase 8.3e.
612
+ */
613
+ export interface ArrayCallbackBinding {
614
+ sourceName: string;
615
+ calleeName: string;
616
+ }
617
+
618
+ /**
619
+ * An object-rest parameter binding: `function f({ a, ...rest })` records that
620
+ * `rest` is the rest of the object passed as argument `argIndex` to `f`.
621
+ * Phase 8.3f: object destructuring rest dispatch.
622
+ */
623
+ export interface ObjectRestParamBinding {
624
+ /** Function that owns this rest parameter, e.g. "f3" */
625
+ callee: string;
626
+ /** Name of the rest binding, e.g. "eerest" */
627
+ restName: string;
628
+ /** Zero-based index of the argument whose rest is bound, e.g. 0 */
629
+ argIndex: number;
630
+ }
631
+
632
+ /**
633
+ * An object-property binding: `const obj = { e4 }` or `const obj = { e4: fn }` records
634
+ * that `obj.e4` points to the named function `fn`. Phase 8.3f.
635
+ */
636
+ export interface ObjectPropBinding {
637
+ /** Variable holding the object, e.g. "obj" */
638
+ objectName: string;
639
+ /** Property name, e.g. "e4" */
640
+ propName: string;
641
+ /** Named function value, e.g. "e4" or "fn" */
642
+ valueName: string;
643
+ }
644
+
526
645
  /** The normalized output shape returned by every language extractor. */
527
646
  export interface ExtractorOutput {
528
647
  definitions: Definition[];
@@ -531,6 +650,63 @@ export interface ExtractorOutput {
531
650
  classes: ClassRelation[];
532
651
  exports: Export[];
533
652
  typeMap: Map<string, TypeMapEntry>;
653
+ /**
654
+ * Maps function/method names to their declared or inferred return types.
655
+ * Keys: plain name (e.g. "createUser") or qualified name (e.g. "UserService.getUser").
656
+ * Populated by JS/TS extractor; used for inter-procedural type propagation (Phase 8.2).
657
+ */
658
+ returnTypeMap?: Map<string, TypeMapEntry>;
659
+ /**
660
+ * Variable assignments from call expressions that could not be resolved from the
661
+ * per-file returnTypeMap. Consumed by build-edges.ts to propagate cross-file return types.
662
+ */
663
+ callAssignments?: CallAssignment[];
664
+ /**
665
+ * Function-reference bindings for points-to analysis (Phase 8.3).
666
+ * Records `const fn = handler` and `const fn = obj.method` patterns so the
667
+ * edge builder can follow aliases when a call target has no direct definition.
668
+ */
669
+ fnRefBindings?: FnRefBinding[];
670
+ /**
671
+ * Argument-to-parameter bindings for parameter-flow points-to analysis (Phase 8.3c).
672
+ * Records `f(x)` call sites where `x` is an identifier, enabling the pts solver
673
+ * to propagate function references through function parameters.
674
+ */
675
+ paramBindings?: ParamBinding[];
676
+ /** Phase 8.3e: array-element bindings from `const arr = [fn1, fn2]` patterns. */
677
+ arrayElemBindings?: ArrayElemBinding[];
678
+ /** Phase 8.3e: spread-argument bindings from `f(...arr)` call sites. */
679
+ spreadArgBindings?: SpreadArgBinding[];
680
+ /** Phase 8.3e: for-of iteration variable bindings. */
681
+ forOfBindings?: ForOfBinding[];
682
+ /** Phase 8.3e: array callback bindings from Array.from/forEach/etc. */
683
+ arrayCallbackBindings?: ArrayCallbackBinding[];
684
+ /** Phase 8.3f: object-rest parameter bindings from `function f({ ...rest })` patterns. */
685
+ objectRestParamBindings?: ObjectRestParamBinding[];
686
+ /** Phase 8.3f: object-property bindings from `const obj = { fn }` patterns. */
687
+ objectPropBindings?: ObjectPropBinding[];
688
+ /**
689
+ * This-context bindings from `fn.call(namedCtx, ...)` / `fn.apply(namedCtx, ...)`.
690
+ * Seeds `fn::this → namedCtx` in the points-to map so that `this()` calls inside
691
+ * `fn` resolve to `namedCtx` when `fn` is invoked via `.call()`/`.apply()`.
692
+ */
693
+ thisCallBindings?: ThisCallBinding[];
694
+ /**
695
+ * Phase 8.5 (RTA): constructor names from all `new X()` expressions in the file,
696
+ * including unassigned ones (e.g. `doSomething(new Foo())`). Used to build the
697
+ * project-wide instantiated-types set for Rapid Type Analysis filtering.
698
+ */
699
+ newExpressions?: readonly string[];
700
+ /**
701
+ * Object.defineProperty receiver bindings: maps function name → target object name.
702
+ * Records `Object.defineProperty(obj, "bar", { get: getter })` so the edge builder
703
+ * can resolve `this.X()` calls inside `getter` as `obj.X()` (this === obj when the
704
+ * accessor is invoked through the property).
705
+ *
706
+ * Example: `Object.defineProperty(obj, "bar", { get: getter })` emits
707
+ * `definePropertyReceivers.set("getter", "obj")`.
708
+ */
709
+ definePropertyReceivers?: Map<string, string>;
534
710
  /** WASM tree retained for downstream analysis (complexity, CFG, dataflow). */
535
711
  _tree?: TreeSitterTree;
536
712
  /** Language identifier. */
@@ -1078,6 +1254,20 @@ export interface BuildGraphOpts {
1078
1254
  * `findDbPath` for every other DB-scoped command.
1079
1255
  */
1080
1256
  dbPath?: string;
1257
+ /**
1258
+ * User-config override for this build (from --user-config / --no-user-config).
1259
+ * - false → --no-user-config: skip global layer for this run
1260
+ * - string → --user-config <path>: apply the given file
1261
+ * - true → --user-config (bare): apply the default global file
1262
+ * - undefined → honour per-repo consent from the registry (normal)
1263
+ */
1264
+ userConfig?: string | boolean;
1265
+ /**
1266
+ * When true, an interactive consent prompt may fire on first build for
1267
+ * repos whose global-config consent is undecided. Only set by the CLI
1268
+ * build command when stdin/stdout are TTYs and CI is not set.
1269
+ */
1270
+ promptForConsent?: boolean;
1081
1271
  }
1082
1272
 
1083
1273
  /** Build timing result from buildGraph. */
@@ -1092,6 +1282,16 @@ export interface BuildResult {
1092
1282
  edgesMs: number;
1093
1283
  structureMs: number;
1094
1284
  rolesMs: number;
1285
+ /** Wall-clock time for the CHA expansion post-pass (native path only). */
1286
+ chaMs?: number;
1287
+ /** Wall-clock time for the this/super dispatch WASM post-pass (native path only). */
1288
+ thisDispatchMs?: number;
1289
+ /** Wall-clock time for the dropped-language gap detection + backfill (native path only). */
1290
+ gapDetectMs?: number;
1291
+ /** Wall-clock time for role re-classification after JS edge-writing post-passes (native path only). */
1292
+ reclassifyMs?: number;
1293
+ /** Wall-clock time for the technique-column backfill on native-written edges (native path only). */
1294
+ techniqueBackfillMs?: number;
1095
1295
  astMs: number;
1096
1296
  complexityMs: number;
1097
1297
  cfgMs: number;
@@ -1119,6 +1319,14 @@ export interface CodegraphConfig {
1119
1319
  dbPath: string;
1120
1320
  driftThreshold: number;
1121
1321
  smallFilesThreshold: number;
1322
+ /**
1323
+ * Use the TypeScript compiler API to enrich typeMap for .ts/.tsx files.
1324
+ * Improves method-call edge accuracy for patterns like `const svc = container.get<MyService>()`.
1325
+ * Disabled by default because `ts.createProgram` adds ~1s overhead per build;
1326
+ * enable in `.codegraphrc.json` when you need accurate type-resolved call edges.
1327
+ * Default: false.
1328
+ */
1329
+ typescriptResolver: boolean;
1122
1330
  };
1123
1331
 
1124
1332
  query: {
@@ -1189,6 +1397,14 @@ export interface CodegraphConfig {
1189
1397
  briefImporterDepth: number;
1190
1398
  briefHighRiskCallers: number;
1191
1399
  briefMediumRiskCallers: number;
1400
+ /** Maximum chain depth for inter-procedural return-type propagation (Phase 8.2). */
1401
+ typePropagationDepth: number;
1402
+ /**
1403
+ * Maximum fixed-point iterations for the Phase 8.3 points-to solver.
1404
+ * @reserved — currently not wired to either solver; both use a hardcoded
1405
+ * constant of 50. See TODO in `src/infrastructure/config.ts`.
1406
+ */
1407
+ pointsToMaxIterations: number;
1192
1408
  };
1193
1409
 
1194
1410
  community: {
@@ -1275,6 +1491,25 @@ export interface McpDefaults {
1275
1491
  interfaces: number;
1276
1492
  }
1277
1493
 
1494
+ /** Per-repo consent decision for the user-level global config. */
1495
+ export type ConsentDecision = 'enabled' | 'disabled';
1496
+
1497
+ /** Which layer contributed a resolved config value. */
1498
+ export type ConfigSource = 'default' | 'user' | 'project' | 'env';
1499
+
1500
+ /** Maps config key paths (dot notation) to the layer that supplied the value. */
1501
+ export type ConfigProvenance = Record<string, ConfigSource>;
1502
+
1503
+ /** Result of loadConfigWithProvenance. */
1504
+ export interface ConfigWithProvenance {
1505
+ config: CodegraphConfig;
1506
+ provenance: ConfigProvenance;
1507
+ /** Absolute path of the applied global file, or null if none was applied. */
1508
+ appliedGlobalPath: string | null;
1509
+ /** This repo's recorded consent decision (may be undefined if undecided). */
1510
+ consentDecision: ConsentDecision | undefined;
1511
+ }
1512
+
1278
1513
  // ════════════════════════════════════════════════════════════════════════
1279
1514
  // §12 Pagination
1280
1515
  // ════════════════════════════════════════════════════════════════════════
Binary file