kirograph 0.16.1 → 0.19.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 (365) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +103 -1605
  3. package/dist/architecture/layers/dart.js +112 -0
  4. package/dist/architecture/layers/dart.js.map +7 -0
  5. package/dist/architecture/layers/index.js +3 -1
  6. package/dist/architecture/layers/index.js.map +2 -2
  7. package/dist/bin/banner.js +74 -6
  8. package/dist/bin/banner.js.map +2 -2
  9. package/dist/bin/commands/affected.js +40 -5
  10. package/dist/bin/commands/affected.js.map +2 -2
  11. package/dist/bin/commands/attack-surface.js +157 -0
  12. package/dist/bin/commands/attack-surface.js.map +7 -0
  13. package/dist/bin/commands/benchmark.js +206 -0
  14. package/dist/bin/commands/benchmark.js.map +7 -0
  15. package/dist/bin/commands/budget.js +70 -0
  16. package/dist/bin/commands/budget.js.map +7 -0
  17. package/dist/bin/commands/communities.js +77 -0
  18. package/dist/bin/commands/communities.js.map +7 -0
  19. package/dist/bin/commands/data.js +665 -0
  20. package/dist/bin/commands/data.js.map +7 -0
  21. package/dist/bin/commands/dep-confusion.js +119 -0
  22. package/dist/bin/commands/dep-confusion.js.map +7 -0
  23. package/dist/bin/commands/export.js +304 -10
  24. package/dist/bin/commands/export.js.map +2 -2
  25. package/dist/bin/commands/flows.js +97 -0
  26. package/dist/bin/commands/flows.js.map +7 -0
  27. package/dist/bin/commands/help.js +386 -100
  28. package/dist/bin/commands/help.js.map +2 -2
  29. package/dist/bin/commands/install.js +90 -8
  30. package/dist/bin/commands/install.js.map +2 -2
  31. package/dist/bin/commands/licenses.js +201 -0
  32. package/dist/bin/commands/licenses.js.map +7 -0
  33. package/dist/bin/commands/reachability.js +160 -0
  34. package/dist/bin/commands/reachability.js.map +7 -0
  35. package/dist/bin/commands/read.js +100 -0
  36. package/dist/bin/commands/read.js.map +7 -0
  37. package/dist/bin/commands/refactor.js +116 -0
  38. package/dist/bin/commands/refactor.js.map +7 -0
  39. package/dist/bin/commands/remediation.js +133 -0
  40. package/dist/bin/commands/remediation.js.map +7 -0
  41. package/dist/bin/commands/sbom.js +76 -0
  42. package/dist/bin/commands/sbom.js.map +7 -0
  43. package/dist/bin/commands/security-ci-report.js +267 -0
  44. package/dist/bin/commands/security-ci-report.js.map +7 -0
  45. package/dist/bin/commands/security-export.js +1349 -0
  46. package/dist/bin/commands/security-export.js.map +7 -0
  47. package/dist/bin/commands/security-flows.js +126 -0
  48. package/dist/bin/commands/security-flows.js.map +7 -0
  49. package/dist/bin/commands/security-secrets.js +170 -0
  50. package/dist/bin/commands/security-secrets.js.map +7 -0
  51. package/dist/bin/commands/security.js +193 -0
  52. package/dist/bin/commands/security.js.map +7 -0
  53. package/dist/bin/commands/staleness.js +144 -0
  54. package/dist/bin/commands/staleness.js.map +7 -0
  55. package/dist/bin/commands/status.js +102 -2
  56. package/dist/bin/commands/status.js.map +2 -2
  57. package/dist/bin/commands/supply-chain.js +138 -0
  58. package/dist/bin/commands/supply-chain.js.map +7 -0
  59. package/dist/bin/commands/uninit.js +109 -60
  60. package/dist/bin/commands/uninit.js.map +2 -2
  61. package/dist/bin/commands/vex.js +76 -0
  62. package/dist/bin/commands/vex.js.map +7 -0
  63. package/dist/bin/commands/vuln-suppress.js +95 -0
  64. package/dist/bin/commands/vuln-suppress.js.map +7 -0
  65. package/dist/bin/commands/vulns.js +382 -0
  66. package/dist/bin/commands/vulns.js.map +7 -0
  67. package/dist/bin/installer/auto-detect.js +125 -0
  68. package/dist/bin/installer/auto-detect.js.map +7 -0
  69. package/dist/bin/installer/cli-agent.js +28 -5
  70. package/dist/bin/installer/cli-agent.js.map +2 -2
  71. package/dist/bin/installer/common.js +47 -0
  72. package/dist/bin/installer/common.js.map +2 -2
  73. package/dist/bin/installer/config-prompt.js +39 -1
  74. package/dist/bin/installer/config-prompt.js.map +2 -2
  75. package/dist/bin/installer/detect.js +309 -0
  76. package/dist/bin/installer/detect.js.map +7 -0
  77. package/dist/bin/installer/index.js +36 -1
  78. package/dist/bin/installer/index.js.map +2 -2
  79. package/dist/bin/installer/instructions.js +195 -3
  80. package/dist/bin/installer/instructions.js.map +2 -2
  81. package/dist/bin/installer/steering.js +440 -0
  82. package/dist/bin/installer/steering.js.map +2 -2
  83. package/dist/bin/installer/targets/aider.js +3 -3
  84. package/dist/bin/installer/targets/aider.js.map +2 -2
  85. package/dist/bin/installer/targets/amp.js +3 -3
  86. package/dist/bin/installer/targets/amp.js.map +2 -2
  87. package/dist/bin/installer/targets/antigravity.js +35 -6
  88. package/dist/bin/installer/targets/antigravity.js.map +2 -2
  89. package/dist/bin/installer/targets/augment.js +3 -3
  90. package/dist/bin/installer/targets/augment.js.map +2 -2
  91. package/dist/bin/installer/targets/claude.js +49 -5
  92. package/dist/bin/installer/targets/claude.js.map +2 -2
  93. package/dist/bin/installer/targets/cline.js +32 -6
  94. package/dist/bin/installer/targets/cline.js.map +2 -2
  95. package/dist/bin/installer/targets/codex.js +64 -12
  96. package/dist/bin/installer/targets/codex.js.map +2 -2
  97. package/dist/bin/installer/targets/continue.js +2 -2
  98. package/dist/bin/installer/targets/continue.js.map +2 -2
  99. package/dist/bin/installer/targets/copilot-cli.js +101 -0
  100. package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
  101. package/dist/bin/installer/targets/copilot.js +31 -5
  102. package/dist/bin/installer/targets/copilot.js.map +2 -2
  103. package/dist/bin/installer/targets/cursor.js +4 -4
  104. package/dist/bin/installer/targets/cursor.js.map +2 -2
  105. package/dist/bin/installer/targets/devin.js +2 -2
  106. package/dist/bin/installer/targets/devin.js.map +2 -2
  107. package/dist/bin/installer/targets/gemini-cli.js +2 -2
  108. package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
  109. package/dist/bin/installer/targets/generic.js +2 -14
  110. package/dist/bin/installer/targets/generic.js.map +2 -2
  111. package/dist/bin/installer/targets/goose.js +3 -3
  112. package/dist/bin/installer/targets/goose.js.map +2 -2
  113. package/dist/bin/installer/targets/index.js +250 -0
  114. package/dist/bin/installer/targets/index.js.map +2 -2
  115. package/dist/bin/installer/targets/junie.js +2 -2
  116. package/dist/bin/installer/targets/junie.js.map +2 -2
  117. package/dist/bin/installer/targets/kilo.js +2 -2
  118. package/dist/bin/installer/targets/kilo.js.map +2 -2
  119. package/dist/bin/installer/targets/kiro.js +3 -3
  120. package/dist/bin/installer/targets/kiro.js.map +2 -2
  121. package/dist/bin/installer/targets/opencode.js +2 -2
  122. package/dist/bin/installer/targets/opencode.js.map +2 -2
  123. package/dist/bin/installer/targets/openhands.js +3 -3
  124. package/dist/bin/installer/targets/openhands.js.map +2 -2
  125. package/dist/bin/installer/targets/qoder.js +91 -0
  126. package/dist/bin/installer/targets/qoder.js.map +7 -0
  127. package/dist/bin/installer/targets/qwen.js +96 -0
  128. package/dist/bin/installer/targets/qwen.js.map +7 -0
  129. package/dist/bin/installer/targets/replit.js +3 -3
  130. package/dist/bin/installer/targets/replit.js.map +2 -2
  131. package/dist/bin/installer/targets/roo.js +2 -2
  132. package/dist/bin/installer/targets/roo.js.map +2 -2
  133. package/dist/bin/installer/targets/tabnine.js +3 -3
  134. package/dist/bin/installer/targets/tabnine.js.map +2 -2
  135. package/dist/bin/installer/targets/trae.js +5 -5
  136. package/dist/bin/installer/targets/trae.js.map +2 -2
  137. package/dist/bin/installer/targets/warp.js +2 -2
  138. package/dist/bin/installer/targets/warp.js.map +2 -2
  139. package/dist/bin/installer/targets/windsurf.js +35 -6
  140. package/dist/bin/installer/targets/windsurf.js.map +2 -2
  141. package/dist/bin/kirograph.js +74 -6
  142. package/dist/bin/kirograph.js.map +3 -3
  143. package/dist/bin/progress.js +30 -0
  144. package/dist/bin/progress.js.map +2 -2
  145. package/dist/compression/naive-cost.js +25 -0
  146. package/dist/compression/naive-cost.js.map +2 -2
  147. package/dist/compression/tracker.js +91 -2
  148. package/dist/compression/tracker.js.map +2 -2
  149. package/dist/compression/types.js.map +1 -1
  150. package/dist/config.js +126 -3
  151. package/dist/config.js.map +2 -2
  152. package/dist/core/pipeline.js +92 -4
  153. package/dist/core/pipeline.js.map +2 -2
  154. package/dist/data/filters.js +105 -0
  155. package/dist/data/filters.js.map +7 -0
  156. package/dist/data/indexer.js +225 -0
  157. package/dist/data/indexer.js.map +7 -0
  158. package/dist/data/linker.js +150 -0
  159. package/dist/data/linker.js.map +7 -0
  160. package/dist/data/lint.js +109 -0
  161. package/dist/data/lint.js.map +7 -0
  162. package/dist/data/parsers/csv.js +132 -0
  163. package/dist/data/parsers/csv.js.map +7 -0
  164. package/dist/data/parsers/excel.js +88 -0
  165. package/dist/data/parsers/excel.js.map +7 -0
  166. package/dist/data/parsers/index.js +79 -0
  167. package/dist/data/parsers/index.js.map +7 -0
  168. package/dist/data/parsers/json-array.js +89 -0
  169. package/dist/data/parsers/json-array.js.map +7 -0
  170. package/dist/data/parsers/jsonl.js +84 -0
  171. package/dist/data/parsers/jsonl.js.map +7 -0
  172. package/dist/data/parsers/parquet.js +95 -0
  173. package/dist/data/parsers/parquet.js.map +7 -0
  174. package/dist/data/profiler.js +182 -0
  175. package/dist/data/profiler.js.map +7 -0
  176. package/dist/data/queries.js +512 -0
  177. package/dist/data/queries.js.map +7 -0
  178. package/dist/data/types.js +17 -0
  179. package/dist/data/types.js.map +7 -0
  180. package/dist/db/data-schema.sql +61 -0
  181. package/dist/db/database.js +67 -5
  182. package/dist/db/database.js.map +2 -2
  183. package/dist/db/memory-schema.sql +5 -1
  184. package/dist/db/schema.sql +3 -1
  185. package/dist/db/security-schema.sql +72 -0
  186. package/dist/extraction/extractor.js +81 -0
  187. package/dist/extraction/extractor.js.map +2 -2
  188. package/dist/extraction/grammars.js +32 -8
  189. package/dist/extraction/grammars.js.map +2 -2
  190. package/dist/extraction/languages.js +43 -1
  191. package/dist/extraction/languages.js.map +2 -2
  192. package/dist/extraction/notebook.js +300 -0
  193. package/dist/extraction/notebook.js.map +7 -0
  194. package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
  195. package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
  196. package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
  197. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  198. package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
  199. package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
  200. package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
  201. package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
  202. package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
  203. package/dist/frameworks/flutter.js +130 -0
  204. package/dist/frameworks/flutter.js.map +7 -0
  205. package/dist/frameworks/index.js +7 -1
  206. package/dist/frameworks/index.js.map +3 -3
  207. package/dist/graph/communities.js +317 -0
  208. package/dist/graph/communities.js.map +7 -0
  209. package/dist/graph/flows.js +138 -0
  210. package/dist/graph/flows.js.map +7 -0
  211. package/dist/graph/refactor.js +155 -0
  212. package/dist/graph/refactor.js.map +7 -0
  213. package/dist/index.js +1 -1
  214. package/dist/index.js.map +2 -2
  215. package/dist/mcp/cache.js +172 -0
  216. package/dist/mcp/cache.js.map +7 -0
  217. package/dist/mcp/read-modes.js +295 -0
  218. package/dist/mcp/read-modes.js.map +7 -0
  219. package/dist/mcp/server.js +2 -1
  220. package/dist/mcp/server.js.map +2 -2
  221. package/dist/mcp/tool-names.js +29 -1
  222. package/dist/mcp/tool-names.js.map +2 -2
  223. package/dist/mcp/tools.js +1588 -6
  224. package/dist/mcp/tools.js.map +3 -3
  225. package/dist/memory/database.js +32 -2
  226. package/dist/memory/database.js.map +2 -2
  227. package/dist/memory/types.js.map +1 -1
  228. package/dist/resolution/bridges/android-rn.js +298 -0
  229. package/dist/resolution/bridges/android-rn.js.map +7 -0
  230. package/dist/resolution/bridges/expo-modules.js +155 -0
  231. package/dist/resolution/bridges/expo-modules.js.map +7 -0
  232. package/dist/resolution/bridges/flutter-channel.js +385 -0
  233. package/dist/resolution/bridges/flutter-channel.js.map +7 -0
  234. package/dist/resolution/bridges/index.js +80 -0
  235. package/dist/resolution/bridges/index.js.map +7 -0
  236. package/dist/resolution/bridges/native-events.js +188 -0
  237. package/dist/resolution/bridges/native-events.js.map +7 -0
  238. package/dist/resolution/bridges/native-views.js +244 -0
  239. package/dist/resolution/bridges/native-views.js.map +7 -0
  240. package/dist/resolution/bridges/react-native.js +161 -0
  241. package/dist/resolution/bridges/react-native.js.map +7 -0
  242. package/dist/resolution/bridges/swift-objc.js +227 -0
  243. package/dist/resolution/bridges/swift-objc.js.map +7 -0
  244. package/dist/resolution/bridges/turbomodules.js +216 -0
  245. package/dist/resolution/bridges/turbomodules.js.map +7 -0
  246. package/dist/resolution/index.js +61 -2
  247. package/dist/resolution/index.js.map +2 -2
  248. package/dist/security/attack-surface.js +164 -0
  249. package/dist/security/attack-surface.js.map +7 -0
  250. package/dist/security/context-warnings.js +123 -0
  251. package/dist/security/context-warnings.js.map +7 -0
  252. package/dist/security/context-warnings.test.js +300 -0
  253. package/dist/security/context-warnings.test.js.map +7 -0
  254. package/dist/security/data-flows.js +228 -0
  255. package/dist/security/data-flows.js.map +7 -0
  256. package/dist/security/dep-confusion.js +255 -0
  257. package/dist/security/dep-confusion.js.map +7 -0
  258. package/dist/security/errors.js +51 -0
  259. package/dist/security/errors.js.map +7 -0
  260. package/dist/security/export/fix-suggestions.js +58 -0
  261. package/dist/security/export/fix-suggestions.js.map +7 -0
  262. package/dist/security/export/fix-suggestions.test.js +74 -0
  263. package/dist/security/export/fix-suggestions.test.js.map +7 -0
  264. package/dist/security/export/sbom.js +230 -0
  265. package/dist/security/export/sbom.js.map +7 -0
  266. package/dist/security/export/sbom.test.js +288 -0
  267. package/dist/security/export/sbom.test.js.map +7 -0
  268. package/dist/security/export/serialization.js +142 -0
  269. package/dist/security/export/serialization.js.map +7 -0
  270. package/dist/security/export/serialization.test.js +310 -0
  271. package/dist/security/export/serialization.test.js.map +7 -0
  272. package/dist/security/export/vex.js +220 -0
  273. package/dist/security/export/vex.js.map +7 -0
  274. package/dist/security/export/vex.test.js +278 -0
  275. package/dist/security/export/vex.test.js.map +7 -0
  276. package/dist/security/index.js +67 -0
  277. package/dist/security/index.js.map +7 -0
  278. package/dist/security/integrator-transitives.test.js +359 -0
  279. package/dist/security/integrator-transitives.test.js.map +7 -0
  280. package/dist/security/integrator.js +491 -0
  281. package/dist/security/integrator.js.map +7 -0
  282. package/dist/security/integrator.test.js +237 -0
  283. package/dist/security/integrator.test.js.map +7 -0
  284. package/dist/security/license.js +70 -0
  285. package/dist/security/license.js.map +7 -0
  286. package/dist/security/manifest/adapter.js +243 -0
  287. package/dist/security/manifest/adapter.js.map +7 -0
  288. package/dist/security/manifest/adapter.test.js +272 -0
  289. package/dist/security/manifest/adapter.test.js.map +7 -0
  290. package/dist/security/manifest/parser.js +376 -0
  291. package/dist/security/manifest/parser.js.map +7 -0
  292. package/dist/security/manifest/parser.test.js +110 -0
  293. package/dist/security/manifest/parser.test.js.map +7 -0
  294. package/dist/security/manifest/plugins/cargo.js +233 -0
  295. package/dist/security/manifest/plugins/cargo.js.map +7 -0
  296. package/dist/security/manifest/plugins/cargo.test.js +304 -0
  297. package/dist/security/manifest/plugins/cargo.test.js.map +7 -0
  298. package/dist/security/manifest/plugins/composer.js +152 -0
  299. package/dist/security/manifest/plugins/composer.js.map +7 -0
  300. package/dist/security/manifest/plugins/go.js +146 -0
  301. package/dist/security/manifest/plugins/go.js.map +7 -0
  302. package/dist/security/manifest/plugins/go.test.js +341 -0
  303. package/dist/security/manifest/plugins/go.test.js.map +7 -0
  304. package/dist/security/manifest/plugins/gradle.js +159 -0
  305. package/dist/security/manifest/plugins/gradle.js.map +7 -0
  306. package/dist/security/manifest/plugins/hex.js +159 -0
  307. package/dist/security/manifest/plugins/hex.js.map +7 -0
  308. package/dist/security/manifest/plugins/maven.js +128 -0
  309. package/dist/security/manifest/plugins/maven.js.map +7 -0
  310. package/dist/security/manifest/plugins/maven.test.js +389 -0
  311. package/dist/security/manifest/plugins/maven.test.js.map +7 -0
  312. package/dist/security/manifest/plugins/npm.js +284 -0
  313. package/dist/security/manifest/plugins/npm.js.map +7 -0
  314. package/dist/security/manifest/plugins/npm.test.js +298 -0
  315. package/dist/security/manifest/plugins/npm.test.js.map +7 -0
  316. package/dist/security/manifest/plugins/nuget.js +186 -0
  317. package/dist/security/manifest/plugins/nuget.js.map +7 -0
  318. package/dist/security/manifest/plugins/pip.js +123 -0
  319. package/dist/security/manifest/plugins/pip.js.map +7 -0
  320. package/dist/security/manifest/plugins/pip.test.js +208 -0
  321. package/dist/security/manifest/plugins/pip.test.js.map +7 -0
  322. package/dist/security/manifest/plugins/pubspec.js +212 -0
  323. package/dist/security/manifest/plugins/pubspec.js.map +7 -0
  324. package/dist/security/manifest/plugins/pyproject.js +261 -0
  325. package/dist/security/manifest/plugins/pyproject.js.map +7 -0
  326. package/dist/security/manifest/plugins/rubygems.js +185 -0
  327. package/dist/security/manifest/plugins/rubygems.js.map +7 -0
  328. package/dist/security/manifest/plugins/swift.js +200 -0
  329. package/dist/security/manifest/plugins/swift.js.map +7 -0
  330. package/dist/security/owasp.js +104 -0
  331. package/dist/security/owasp.js.map +7 -0
  332. package/dist/security/pipeline.js +125 -0
  333. package/dist/security/pipeline.js.map +7 -0
  334. package/dist/security/reachability.js +276 -0
  335. package/dist/security/reachability.js.map +7 -0
  336. package/dist/security/reachability.test.js +394 -0
  337. package/dist/security/reachability.test.js.map +7 -0
  338. package/dist/security/remediation.js +126 -0
  339. package/dist/security/remediation.js.map +7 -0
  340. package/dist/security/secrets.js +176 -0
  341. package/dist/security/secrets.js.map +7 -0
  342. package/dist/security/staleness.js +231 -0
  343. package/dist/security/staleness.js.map +7 -0
  344. package/dist/security/supply-chain.js +301 -0
  345. package/dist/security/supply-chain.js.map +7 -0
  346. package/dist/security/suppressions.js +106 -0
  347. package/dist/security/suppressions.js.map +7 -0
  348. package/dist/security/types.js +17 -0
  349. package/dist/security/types.js.map +7 -0
  350. package/dist/security/vuln/client.js +355 -0
  351. package/dist/security/vuln/client.js.map +7 -0
  352. package/dist/security/vuln/client.test.js +288 -0
  353. package/dist/security/vuln/client.test.js.map +7 -0
  354. package/dist/security/vuln/epss-client.js +95 -0
  355. package/dist/security/vuln/epss-client.js.map +7 -0
  356. package/dist/security/vuln/index.js +29 -0
  357. package/dist/security/vuln/index.js.map +7 -0
  358. package/dist/security/vuln/osv-adapter.js +321 -0
  359. package/dist/security/vuln/osv-adapter.js.map +7 -0
  360. package/dist/security/vuln/osv-adapter.test.js +391 -0
  361. package/dist/security/vuln/osv-adapter.test.js.map +7 -0
  362. package/dist/security/vuln/types.js +17 -0
  363. package/dist/security/vuln/types.js.map +7 -0
  364. package/dist/types.js.map +2 -2
  365. package/package.json +5 -2
@@ -53,8 +53,27 @@ class MemoryDatabase {
53
53
  const schemaPath = path.join(__dirname, "../db/memory-schema.sql");
54
54
  const sql = fs.readFileSync(schemaPath, "utf8");
55
55
  this.db.exec(sql);
56
+ this.migrateTemporalColumns();
56
57
  this.initialized = true;
57
58
  }
59
+ /**
60
+ * Add temporal fact columns to mem_observations if missing.
61
+ * Safe to run multiple times (uses IF NOT EXISTS pattern via try/catch).
62
+ */
63
+ migrateTemporalColumns() {
64
+ const columnsToAdd = [
65
+ { name: "valid_from", type: "INTEGER" },
66
+ { name: "valid_until", type: "INTEGER" },
67
+ { name: "superseded_by", type: "TEXT" },
68
+ { name: "fact_type", type: "TEXT DEFAULT 'observation'" }
69
+ ];
70
+ for (const col of columnsToAdd) {
71
+ try {
72
+ this.db.exec(`ALTER TABLE mem_observations ADD COLUMN ${col.name} ${col.type}`);
73
+ } catch {
74
+ }
75
+ }
76
+ }
58
77
  // ── Sessions ─────────────────────────────────────────────────────────────
59
78
  /**
60
79
  * Get or create an active session. Auto-creates if no active session exists
@@ -143,7 +162,7 @@ class MemoryDatabase {
143
162
  }
144
163
  // ── FTS Search ───────────────────────────────────────────────────────────
145
164
  searchFTS(query, opts = {}) {
146
- const { limit = 10, kind, sessionId } = opts;
165
+ const { limit = 10, kind, sessionId, asOf } = opts;
147
166
  const safe = query.replace(/\b(AND|OR|NOT)\b/g, " ").replace(/['"*()?\-+^~:{}\\\.\/,]/g, " ").replace(/\s+/g, " ").trim();
148
167
  if (!safe) return [];
149
168
  const ftsQuery = safe + "*";
@@ -158,6 +177,13 @@ class MemoryDatabase {
158
177
  conditions.push("o.session_id = ?");
159
178
  params.push(sessionId);
160
179
  }
180
+ if (asOf) {
181
+ conditions.push("(o.valid_from IS NULL OR o.valid_from <= ?)");
182
+ params.push(asOf);
183
+ conditions.push("(o.valid_until IS NULL OR o.valid_until > ?)");
184
+ params.push(asOf);
185
+ conditions.push("o.superseded_by IS NULL");
186
+ }
161
187
  const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
162
188
  const rows = this.db.all(
163
189
  `SELECT o.*, rank FROM mem_fts f
@@ -364,7 +390,11 @@ class MemoryDatabase {
364
390
  kind: row.kind,
365
391
  source: row.source,
366
392
  tags: row.tags ? JSON.parse(row.tags) : void 0,
367
- createdAt: row.created_at
393
+ createdAt: row.created_at,
394
+ validFrom: row.valid_from ?? void 0,
395
+ validUntil: row.valid_until ?? void 0,
396
+ supersededBy: row.superseded_by ?? void 0,
397
+ factType: row.fact_type ?? void 0
368
398
  };
369
399
  }
370
400
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/database.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Database layer\n *\n * Operates on mem_* tables within the same SQLite database as the code graph.\n * Completely isolated: no FK to core nodes/edges tables.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport type {\n MemSession,\n MemObservation,\n MemObservationInput,\n MemLink,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n} from './types';\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash('sha256').update(content).digest('hex');\n}\n\n// \u2500\u2500 MemoryDatabase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryDatabase {\n private db: any;\n private initialized = false;\n\n constructor(db: any) {\n this.db = db;\n }\n\n /**\n * Apply memory schema. Called once when enableMemory is true.\n */\n initialize(): void {\n if (this.initialized) return;\n const schemaPath = path.join(__dirname, '../db/memory-schema.sql');\n const sql = fs.readFileSync(schemaPath, 'utf8');\n this.db.exec(sql);\n this.initialized = true;\n }\n\n // \u2500\u2500 Sessions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get or create an active session. Auto-creates if no active session exists\n * within the timeout window.\n */\n getOrCreateSession(ide: string, cwd: string, timeoutSeconds = 7200): string {\n const cutoff = Date.now() - (timeoutSeconds * 1000);\n\n // Look for an active session (no ended_at, same ide, started within timeout)\n const existing = this.db.get(\n `SELECT id FROM mem_sessions\n WHERE ended_at IS NULL AND ide = ? AND started_at > ?\n ORDER BY started_at DESC LIMIT 1`,\n [ide, cutoff]\n );\n\n if (existing) return existing.id;\n\n // Auto-close any stale sessions for this IDE\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND ide = ?`,\n [Date.now(), ide]\n );\n\n // Create new session\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_sessions (id, ide, cwd, started_at) VALUES (?, ?, ?, ?)`,\n [id, ide, cwd, Date.now()]\n );\n return id;\n }\n\n endSession(sessionId: string): void {\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE id = ? AND ended_at IS NULL`,\n [Date.now(), sessionId]\n );\n }\n\n getSession(sessionId: string): MemSession | null {\n const row = this.db.get('SELECT * FROM mem_sessions WHERE id = ?', [sessionId]);\n return row ? this.rowToSession(row) : null;\n }\n\n listSessions(limit = 10): MemSession[] {\n return this.db.all(\n 'SELECT * FROM mem_sessions ORDER BY started_at DESC LIMIT ?',\n [limit]\n ).map(this.rowToSession);\n }\n\n // \u2500\u2500 Observations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert an observation. Returns the ID, or null if a duplicate (same content_hash).\n */\n insertObservation(\n content: string,\n opts: {\n contentRaw?: string;\n kind?: string;\n source?: string;\n tags?: string[];\n sessionId?: string;\n } = {}\n ): string | null {\n const id = generateId();\n const contentHash = hashContent(content);\n const kind = opts.kind ?? 'note';\n const source = opts.source ?? 'manual';\n const tags = opts.tags ? JSON.stringify(opts.tags) : null;\n const now = Date.now();\n\n try {\n this.db.run(\n `INSERT INTO mem_observations (id, session_id, content, content_raw, content_hash, kind, source, tags, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, opts.sessionId ?? null, content, opts.contentRaw ?? null, contentHash, kind, source, tags, now]\n );\n } catch (err: any) {\n // Unique constraint on content_hash \u2014 duplicate, skip silently\n if (err.message?.includes('UNIQUE constraint failed') || err.message?.includes('unique')) {\n return null;\n }\n throw err;\n }\n\n // Keep FTS in sync\n this.db.run(\n `INSERT INTO mem_fts (id, content, kind) VALUES (?, ?, ?)`,\n [id, content, kind]\n );\n\n return id;\n }\n\n getObservation(id: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE id = ?', [id]);\n return row ? this.rowToObservation(row) : null;\n }\n\n getObservationsBySession(sessionId: string, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE session_id = ? ORDER BY created_at DESC LIMIT ?',\n [sessionId, limit]\n ).map(this.rowToObservation);\n }\n\n getObservationsByTimeRange(from: number, to: number, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?',\n [from, to, limit]\n ).map(this.rowToObservation);\n }\n\n // \u2500\u2500 FTS Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n searchFTS(query: string, opts: MemSearchOptions = {}): ScoredObservation[] {\n const { limit = 10, kind, sessionId } = opts;\n\n // Sanitize for FTS5\n const safe = query\n .replace(/\\b(AND|OR|NOT)\\b/g, ' ')\n .replace(/['\"*()?\\-+^~:{}\\\\\\.\\/,]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (!safe) return [];\n\n const ftsQuery = safe + '*';\n const safeLimit = Math.max(1, Math.floor(Number(limit)));\n\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (kind) {\n conditions.push('o.kind = ?');\n params.push(kind);\n }\n if (sessionId) {\n conditions.push('o.session_id = ?');\n params.push(sessionId);\n }\n\n const where = conditions.length > 0 ? `AND ${conditions.join(' AND ')}` : '';\n\n const rows = this.db.all(\n `SELECT o.*, rank FROM mem_fts f\n JOIN mem_observations o ON o.id = f.id\n WHERE mem_fts MATCH '${ftsQuery}' ${where}\n ORDER BY rank\n LIMIT ${safeLimit}`,\n params\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: Math.abs(row.rank ?? 0),\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n linkToSymbol(observationId: string, qualifiedName: string, relevance = 1.0): void {\n this.db.run(\n `INSERT OR IGNORE INTO mem_links (observation_id, qualified_name, relevance)\n VALUES (?, ?, ?)`,\n [observationId, qualifiedName, relevance]\n );\n }\n\n linkToSymbols(observationId: string, qualifiedNames: string[]): void {\n for (const qn of qualifiedNames) {\n this.linkToSymbol(observationId, qn);\n }\n }\n\n /**\n * Get observations linked to a symbol by qualified_name.\n * Resolves through mem_links \u2014 no dependency on node IDs.\n */\n getLinkedObservations(qualifiedName: string, limit = 10): ScoredObservation[] {\n const rows = this.db.all(\n `SELECT o.*, l.relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name = ?\n ORDER BY l.relevance DESC, o.created_at DESC\n LIMIT ?`,\n [qualifiedName, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n /**\n * Get observations linked to any of the given qualified names.\n * Used by kirograph_context and kirograph_impact to surface memory.\n */\n getLinkedObservationsForSymbols(qualifiedNames: string[], limit = 5): ScoredObservation[] {\n if (qualifiedNames.length === 0) return [];\n const placeholders = qualifiedNames.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT DISTINCT o.*, MAX(l.relevance) as relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name IN (${placeholders})\n GROUP BY o.id\n ORDER BY relevance DESC, o.created_at DESC\n LIMIT ?`,\n [...qualifiedNames, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Vectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertVector(observationId: string, embedding: Buffer, model: string): void {\n this.db.run(\n `INSERT OR REPLACE INTO mem_vectors (observation_id, embedding, model, created_at)\n VALUES (?, ?, ?, ?)`,\n [observationId, embedding, model, Date.now()]\n );\n }\n\n getVector(observationId: string): { embedding: Buffer; model: string } | null {\n const row = this.db.get(\n 'SELECT embedding, model FROM mem_vectors WHERE observation_id = ?',\n [observationId]\n );\n return row ? { embedding: row.embedding, model: row.model } : null;\n }\n\n getVectorCount(): number {\n const row = this.db.get('SELECT COUNT(*) as count FROM mem_vectors');\n return row?.count ?? 0;\n }\n\n getVectorModelMismatch(currentModel: string): number {\n const row = this.db.get(\n 'SELECT COUNT(*) as count FROM mem_vectors WHERE model != ?',\n [currentModel]\n );\n return row?.count ?? 0;\n }\n\n getAllVectors(model?: string): Array<{ observationId: string; embedding: Buffer }> {\n const sql = model\n ? 'SELECT observation_id, embedding FROM mem_vectors WHERE model = ?'\n : 'SELECT observation_id, embedding FROM mem_vectors';\n const params = model ? [model] : [];\n return this.db.all(sql, params).map((row: any) => ({\n observationId: row.observation_id,\n embedding: row.embedding,\n }));\n }\n\n deleteVectors(): void {\n this.db.run('DELETE FROM mem_vectors');\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(currentModel?: string): MemStats {\n const sessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions')?.c ?? 0;\n const activeSessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions WHERE ended_at IS NULL')?.c ?? 0;\n const observations = this.db.get('SELECT COUNT(*) as c FROM mem_observations')?.c ?? 0;\n const links = this.db.get('SELECT COUNT(*) as c FROM mem_links')?.c ?? 0;\n const vectors = this.db.get('SELECT COUNT(*) as c FROM mem_vectors')?.c ?? 0;\n const mismatch = currentModel ? this.getVectorModelMismatch(currentModel) : 0;\n\n return {\n sessions,\n activeSessions,\n observations,\n links,\n vectors,\n embeddableCount: observations,\n modelMismatch: mismatch > 0,\n currentModel,\n };\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Delete observations older than the given timestamp.\n * Cascades to mem_links and mem_vectors via FK.\n */\n prune(olderThan: number): number {\n // Get IDs to delete (for FTS cleanup)\n const ids = this.db.all(\n 'SELECT id FROM mem_observations WHERE created_at < ?',\n [olderThan]\n ).map((r: any) => r.id);\n\n if (ids.length === 0) return 0;\n\n const placeholders = ids.map(() => '?').join(',');\n this.db.run(`DELETE FROM mem_fts WHERE id IN (${placeholders})`, ids);\n this.db.run(`DELETE FROM mem_observations WHERE created_at < ?`, [olderThan]);\n\n // Clean up empty sessions\n this.db.run(\n `DELETE FROM mem_sessions WHERE id NOT IN (SELECT DISTINCT session_id FROM mem_observations WHERE session_id IS NOT NULL)`\n );\n\n return ids.length;\n }\n\n /**\n * Find stale links where qualified_name no longer exists in the nodes table.\n */\n findStaleLinks(): MemLink[] {\n const rows = this.db.all(\n `SELECT l.* FROM mem_links l\n LEFT JOIN nodes n ON n.qualified_name = l.qualified_name\n WHERE n.id IS NULL`\n );\n return rows.map((row: any) => ({\n observationId: row.observation_id,\n qualifiedName: row.qualified_name,\n relevance: row.relevance,\n }));\n }\n\n /**\n * Remove stale links (where qualified_name no longer resolves).\n */\n removeStaleLinks(): number {\n const result = this.db.run(\n `DELETE FROM mem_links WHERE qualified_name NOT IN (SELECT DISTINCT qualified_name FROM nodes)`\n );\n return result?.changes ?? 0;\n }\n\n /**\n * Close stale sessions (started but never ended, older than timeout).\n */\n closeStaleSessionsOlderThan(timeoutMs: number): number {\n const cutoff = Date.now() - timeoutMs;\n const result = this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND started_at < ?`,\n [Date.now(), cutoff]\n );\n return result?.changes ?? 0;\n }\n\n // \u2500\u2500 Privacy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Strip <private>...</private> blocks from text.\n */\n static stripPrivate(text: string): string {\n return text.replace(/<private>[\\s\\S]*?<\\/private>/gi, '').trim();\n }\n\n // \u2500\u2500 Row mappers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private rowToSession(row: any): MemSession {\n return {\n id: row.id,\n ide: row.ide ?? undefined,\n cwd: row.cwd ?? undefined,\n startedAt: row.started_at,\n endedAt: row.ended_at ?? undefined,\n };\n }\n\n private rowToObservation(row: any): MemObservation {\n return {\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n };\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Database layer\n *\n * Operates on mem_* tables within the same SQLite database as the code graph.\n * Completely isolated: no FK to core nodes/edges tables.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport type {\n MemSession,\n MemObservation,\n MemObservationInput,\n MemLink,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n} from './types';\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash('sha256').update(content).digest('hex');\n}\n\n// \u2500\u2500 MemoryDatabase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryDatabase {\n private db: any;\n private initialized = false;\n\n constructor(db: any) {\n this.db = db;\n }\n\n /**\n * Apply memory schema. Called once when enableMemory is true.\n */\n initialize(): void {\n if (this.initialized) return;\n const schemaPath = path.join(__dirname, '../db/memory-schema.sql');\n const sql = fs.readFileSync(schemaPath, 'utf8');\n this.db.exec(sql);\n\n // Migration: add temporal columns if they don't exist (for existing databases)\n this.migrateTemporalColumns();\n\n this.initialized = true;\n }\n\n /**\n * Add temporal fact columns to mem_observations if missing.\n * Safe to run multiple times (uses IF NOT EXISTS pattern via try/catch).\n */\n private migrateTemporalColumns(): void {\n const columnsToAdd = [\n { name: 'valid_from', type: 'INTEGER' },\n { name: 'valid_until', type: 'INTEGER' },\n { name: 'superseded_by', type: 'TEXT' },\n { name: 'fact_type', type: \"TEXT DEFAULT 'observation'\" },\n ];\n\n for (const col of columnsToAdd) {\n try {\n this.db.exec(`ALTER TABLE mem_observations ADD COLUMN ${col.name} ${col.type}`);\n } catch {\n // Column already exists \u2014 ignore\n }\n }\n }\n\n // \u2500\u2500 Sessions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get or create an active session. Auto-creates if no active session exists\n * within the timeout window.\n */\n getOrCreateSession(ide: string, cwd: string, timeoutSeconds = 7200): string {\n const cutoff = Date.now() - (timeoutSeconds * 1000);\n\n // Look for an active session (no ended_at, same ide, started within timeout)\n const existing = this.db.get(\n `SELECT id FROM mem_sessions\n WHERE ended_at IS NULL AND ide = ? AND started_at > ?\n ORDER BY started_at DESC LIMIT 1`,\n [ide, cutoff]\n );\n\n if (existing) return existing.id;\n\n // Auto-close any stale sessions for this IDE\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND ide = ?`,\n [Date.now(), ide]\n );\n\n // Create new session\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_sessions (id, ide, cwd, started_at) VALUES (?, ?, ?, ?)`,\n [id, ide, cwd, Date.now()]\n );\n return id;\n }\n\n endSession(sessionId: string): void {\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE id = ? AND ended_at IS NULL`,\n [Date.now(), sessionId]\n );\n }\n\n getSession(sessionId: string): MemSession | null {\n const row = this.db.get('SELECT * FROM mem_sessions WHERE id = ?', [sessionId]);\n return row ? this.rowToSession(row) : null;\n }\n\n listSessions(limit = 10): MemSession[] {\n return this.db.all(\n 'SELECT * FROM mem_sessions ORDER BY started_at DESC LIMIT ?',\n [limit]\n ).map(this.rowToSession);\n }\n\n // \u2500\u2500 Observations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert an observation. Returns the ID, or null if a duplicate (same content_hash).\n */\n insertObservation(\n content: string,\n opts: {\n contentRaw?: string;\n kind?: string;\n source?: string;\n tags?: string[];\n sessionId?: string;\n } = {}\n ): string | null {\n const id = generateId();\n const contentHash = hashContent(content);\n const kind = opts.kind ?? 'note';\n const source = opts.source ?? 'manual';\n const tags = opts.tags ? JSON.stringify(opts.tags) : null;\n const now = Date.now();\n\n try {\n this.db.run(\n `INSERT INTO mem_observations (id, session_id, content, content_raw, content_hash, kind, source, tags, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, opts.sessionId ?? null, content, opts.contentRaw ?? null, contentHash, kind, source, tags, now]\n );\n } catch (err: any) {\n // Unique constraint on content_hash \u2014 duplicate, skip silently\n if (err.message?.includes('UNIQUE constraint failed') || err.message?.includes('unique')) {\n return null;\n }\n throw err;\n }\n\n // Keep FTS in sync\n this.db.run(\n `INSERT INTO mem_fts (id, content, kind) VALUES (?, ?, ?)`,\n [id, content, kind]\n );\n\n return id;\n }\n\n getObservation(id: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE id = ?', [id]);\n return row ? this.rowToObservation(row) : null;\n }\n\n getObservationsBySession(sessionId: string, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE session_id = ? ORDER BY created_at DESC LIMIT ?',\n [sessionId, limit]\n ).map(this.rowToObservation);\n }\n\n getObservationsByTimeRange(from: number, to: number, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?',\n [from, to, limit]\n ).map(this.rowToObservation);\n }\n\n // \u2500\u2500 FTS Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n searchFTS(query: string, opts: MemSearchOptions = {}): ScoredObservation[] {\n const { limit = 10, kind, sessionId, asOf } = opts;\n\n // Sanitize for FTS5\n const safe = query\n .replace(/\\b(AND|OR|NOT)\\b/g, ' ')\n .replace(/['\"*()?\\-+^~:{}\\\\\\.\\/,]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (!safe) return [];\n\n const ftsQuery = safe + '*';\n const safeLimit = Math.max(1, Math.floor(Number(limit)));\n\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (kind) {\n conditions.push('o.kind = ?');\n params.push(kind);\n }\n if (sessionId) {\n conditions.push('o.session_id = ?');\n params.push(sessionId);\n }\n\n // Temporal filtering: exclude expired and superseded facts\n if (asOf) {\n conditions.push('(o.valid_from IS NULL OR o.valid_from <= ?)');\n params.push(asOf);\n conditions.push('(o.valid_until IS NULL OR o.valid_until > ?)');\n params.push(asOf);\n conditions.push('o.superseded_by IS NULL');\n }\n\n const where = conditions.length > 0 ? `AND ${conditions.join(' AND ')}` : '';\n\n const rows = this.db.all(\n `SELECT o.*, rank FROM mem_fts f\n JOIN mem_observations o ON o.id = f.id\n WHERE mem_fts MATCH '${ftsQuery}' ${where}\n ORDER BY rank\n LIMIT ${safeLimit}`,\n params\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: Math.abs(row.rank ?? 0),\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n linkToSymbol(observationId: string, qualifiedName: string, relevance = 1.0): void {\n this.db.run(\n `INSERT OR IGNORE INTO mem_links (observation_id, qualified_name, relevance)\n VALUES (?, ?, ?)`,\n [observationId, qualifiedName, relevance]\n );\n }\n\n linkToSymbols(observationId: string, qualifiedNames: string[]): void {\n for (const qn of qualifiedNames) {\n this.linkToSymbol(observationId, qn);\n }\n }\n\n /**\n * Get observations linked to a symbol by qualified_name.\n * Resolves through mem_links \u2014 no dependency on node IDs.\n */\n getLinkedObservations(qualifiedName: string, limit = 10): ScoredObservation[] {\n const rows = this.db.all(\n `SELECT o.*, l.relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name = ?\n ORDER BY l.relevance DESC, o.created_at DESC\n LIMIT ?`,\n [qualifiedName, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n /**\n * Get observations linked to any of the given qualified names.\n * Used by kirograph_context and kirograph_impact to surface memory.\n */\n getLinkedObservationsForSymbols(qualifiedNames: string[], limit = 5): ScoredObservation[] {\n if (qualifiedNames.length === 0) return [];\n const placeholders = qualifiedNames.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT DISTINCT o.*, MAX(l.relevance) as relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name IN (${placeholders})\n GROUP BY o.id\n ORDER BY relevance DESC, o.created_at DESC\n LIMIT ?`,\n [...qualifiedNames, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Vectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertVector(observationId: string, embedding: Buffer, model: string): void {\n this.db.run(\n `INSERT OR REPLACE INTO mem_vectors (observation_id, embedding, model, created_at)\n VALUES (?, ?, ?, ?)`,\n [observationId, embedding, model, Date.now()]\n );\n }\n\n getVector(observationId: string): { embedding: Buffer; model: string } | null {\n const row = this.db.get(\n 'SELECT embedding, model FROM mem_vectors WHERE observation_id = ?',\n [observationId]\n );\n return row ? { embedding: row.embedding, model: row.model } : null;\n }\n\n getVectorCount(): number {\n const row = this.db.get('SELECT COUNT(*) as count FROM mem_vectors');\n return row?.count ?? 0;\n }\n\n getVectorModelMismatch(currentModel: string): number {\n const row = this.db.get(\n 'SELECT COUNT(*) as count FROM mem_vectors WHERE model != ?',\n [currentModel]\n );\n return row?.count ?? 0;\n }\n\n getAllVectors(model?: string): Array<{ observationId: string; embedding: Buffer }> {\n const sql = model\n ? 'SELECT observation_id, embedding FROM mem_vectors WHERE model = ?'\n : 'SELECT observation_id, embedding FROM mem_vectors';\n const params = model ? [model] : [];\n return this.db.all(sql, params).map((row: any) => ({\n observationId: row.observation_id,\n embedding: row.embedding,\n }));\n }\n\n deleteVectors(): void {\n this.db.run('DELETE FROM mem_vectors');\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(currentModel?: string): MemStats {\n const sessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions')?.c ?? 0;\n const activeSessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions WHERE ended_at IS NULL')?.c ?? 0;\n const observations = this.db.get('SELECT COUNT(*) as c FROM mem_observations')?.c ?? 0;\n const links = this.db.get('SELECT COUNT(*) as c FROM mem_links')?.c ?? 0;\n const vectors = this.db.get('SELECT COUNT(*) as c FROM mem_vectors')?.c ?? 0;\n const mismatch = currentModel ? this.getVectorModelMismatch(currentModel) : 0;\n\n return {\n sessions,\n activeSessions,\n observations,\n links,\n vectors,\n embeddableCount: observations,\n modelMismatch: mismatch > 0,\n currentModel,\n };\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Delete observations older than the given timestamp.\n * Cascades to mem_links and mem_vectors via FK.\n */\n prune(olderThan: number): number {\n // Get IDs to delete (for FTS cleanup)\n const ids = this.db.all(\n 'SELECT id FROM mem_observations WHERE created_at < ?',\n [olderThan]\n ).map((r: any) => r.id);\n\n if (ids.length === 0) return 0;\n\n const placeholders = ids.map(() => '?').join(',');\n this.db.run(`DELETE FROM mem_fts WHERE id IN (${placeholders})`, ids);\n this.db.run(`DELETE FROM mem_observations WHERE created_at < ?`, [olderThan]);\n\n // Clean up empty sessions\n this.db.run(\n `DELETE FROM mem_sessions WHERE id NOT IN (SELECT DISTINCT session_id FROM mem_observations WHERE session_id IS NOT NULL)`\n );\n\n return ids.length;\n }\n\n /**\n * Find stale links where qualified_name no longer exists in the nodes table.\n */\n findStaleLinks(): MemLink[] {\n const rows = this.db.all(\n `SELECT l.* FROM mem_links l\n LEFT JOIN nodes n ON n.qualified_name = l.qualified_name\n WHERE n.id IS NULL`\n );\n return rows.map((row: any) => ({\n observationId: row.observation_id,\n qualifiedName: row.qualified_name,\n relevance: row.relevance,\n }));\n }\n\n /**\n * Remove stale links (where qualified_name no longer resolves).\n */\n removeStaleLinks(): number {\n const result = this.db.run(\n `DELETE FROM mem_links WHERE qualified_name NOT IN (SELECT DISTINCT qualified_name FROM nodes)`\n );\n return result?.changes ?? 0;\n }\n\n /**\n * Close stale sessions (started but never ended, older than timeout).\n */\n closeStaleSessionsOlderThan(timeoutMs: number): number {\n const cutoff = Date.now() - timeoutMs;\n const result = this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND started_at < ?`,\n [Date.now(), cutoff]\n );\n return result?.changes ?? 0;\n }\n\n // \u2500\u2500 Privacy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Strip <private>...</private> blocks from text.\n */\n static stripPrivate(text: string): string {\n return text.replace(/<private>[\\s\\S]*?<\\/private>/gi, '').trim();\n }\n\n // \u2500\u2500 Row mappers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private rowToSession(row: any): MemSession {\n return {\n id: row.id,\n ide: row.ide ?? undefined,\n cwd: row.cwd ?? undefined,\n startedAt: row.started_at,\n endedAt: row.ended_at ?? undefined,\n };\n }\n\n private rowToObservation(row: any): MemObservation {\n return {\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n validFrom: row.valid_from ?? undefined,\n validUntil: row.valid_until ?? undefined,\n supersededBy: row.superseded_by ?? undefined,\n factType: row.fact_type ?? undefined,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/types.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Type definitions\n */\n\n// \u2500\u2500 Session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemSession {\n id: string;\n ide?: string;\n cwd?: string;\n startedAt: number;\n endedAt?: number;\n}\n\n// \u2500\u2500 Observation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ObservationKind = 'decision' | 'error' | 'pattern' | 'architecture' | 'summary' | 'note';\nexport type ObservationSource = 'hook' | 'manual' | 'agent';\n\nexport interface MemObservation {\n id: string;\n sessionId?: string;\n content: string;\n contentRaw?: string;\n contentHash: string;\n kind: ObservationKind;\n source: ObservationSource;\n tags?: string[];\n createdAt: number;\n}\n\nexport interface MemObservationInput {\n content: string;\n kind?: ObservationKind;\n source?: ObservationSource;\n tags?: string[];\n}\n\n// \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemLink {\n observationId: string;\n qualifiedName: string;\n relevance: number;\n}\n\n// \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ScoredObservation {\n observation: MemObservation;\n score: number;\n /** Source of the score: 'fts', 'vector', or 'hybrid' */\n scoreSource: 'fts' | 'vector' | 'hybrid';\n}\n\nexport interface MemSearchOptions {\n limit?: number;\n kind?: ObservationKind;\n sessionId?: string;\n /** FTS/vector blend: 0 = FTS only, 1 = vector only, 0.5 = equal blend */\n alpha?: number;\n}\n\nexport interface MemTimelineOptions {\n limit?: number;\n sessionId?: string;\n}\n\n// \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemStats {\n sessions: number;\n activeSessions: number;\n observations: number;\n links: number;\n vectors: number;\n embeddableCount: number;\n modelMismatch: boolean;\n currentModel?: string;\n}\n\n// \u2500\u2500 Compress \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface CompressResult {\n compressed: string;\n originalLength: number;\n compressedLength: number;\n detectedSymbols: string[];\n}\n"],
4
+ "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Type definitions\n */\n\n// \u2500\u2500 Session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemSession {\n id: string;\n ide?: string;\n cwd?: string;\n startedAt: number;\n endedAt?: number;\n}\n\n// \u2500\u2500 Observation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ObservationKind = 'decision' | 'error' | 'pattern' | 'architecture' | 'summary' | 'note';\nexport type ObservationSource = 'hook' | 'manual' | 'agent';\n\nexport interface MemObservation {\n id: string;\n sessionId?: string;\n content: string;\n contentRaw?: string;\n contentHash: string;\n kind: ObservationKind;\n source: ObservationSource;\n tags?: string[];\n createdAt: number;\n /** Temporal validity: when this fact became valid (epoch ms). */\n validFrom?: number;\n /** Temporal validity: when this fact expires (epoch ms). Null = no expiry. */\n validUntil?: number;\n /** ID of the observation that supersedes this one. */\n supersededBy?: string;\n /** Fact type for temporal classification. */\n factType?: 'observation' | 'decision' | 'procedure' | 'constraint';\n}\n\nexport interface MemObservationInput {\n content: string;\n kind?: ObservationKind;\n source?: ObservationSource;\n tags?: string[];\n}\n\n// \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemLink {\n observationId: string;\n qualifiedName: string;\n relevance: number;\n}\n\n// \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ScoredObservation {\n observation: MemObservation;\n score: number;\n /** Source of the score: 'fts', 'vector', or 'hybrid' */\n scoreSource: 'fts' | 'vector' | 'hybrid';\n}\n\nexport interface MemSearchOptions {\n limit?: number;\n kind?: ObservationKind;\n sessionId?: string;\n /** FTS/vector blend: 0 = FTS only, 1 = vector only, 0.5 = equal blend */\n alpha?: number;\n /** Query facts valid at this timestamp (epoch ms). Filters out expired/superseded observations. */\n asOf?: number;\n}\n\nexport interface MemTimelineOptions {\n limit?: number;\n sessionId?: string;\n}\n\n// \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemStats {\n sessions: number;\n activeSessions: number;\n observations: number;\n links: number;\n vectors: number;\n embeddableCount: number;\n modelMismatch: boolean;\n currentModel?: string;\n}\n\n// \u2500\u2500 Compress \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface CompressResult {\n compressed: string;\n originalLength: number;\n compressedLength: number;\n detectedSymbols: string[];\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var android_rn_exports = {};
20
+ __export(android_rn_exports, {
21
+ androidRnBridge: () => androidRnBridge
22
+ });
23
+ module.exports = __toCommonJS(android_rn_exports);
24
+ function hasAndroidNativeFiles(context) {
25
+ const files = context.getAllFiles();
26
+ for (const f of files) {
27
+ if (!f.endsWith(".kt") && !f.endsWith(".java")) continue;
28
+ const content = context.readFile(f);
29
+ if (!content) continue;
30
+ if (content.includes("ReactContextBaseJavaModule") || content.includes("@ReactMethod") || content.includes("@ReactProp")) {
31
+ return true;
32
+ }
33
+ }
34
+ return false;
35
+ }
36
+ function extractModuleName(content, filePath) {
37
+ const ktSingleLine = content.match(/override\s+fun\s+getName\s*\(\s*\)\s*=\s*"(\w+)"/);
38
+ if (ktSingleLine) return ktSingleLine[1];
39
+ const blockStyle = content.match(
40
+ /(?:override\s+)?fun\s+getName\s*\(\s*\)\s*(?::\s*String\s*)?\{[^}]*?return\s*"(\w+)"/s
41
+ );
42
+ if (blockStyle) return blockStyle[1];
43
+ const javaStyle = content.match(
44
+ /(?:@Override\s+)?(?:public\s+)?String\s+getName\s*\(\s*\)\s*\{[^}]*?return\s*"(\w+)"/s
45
+ );
46
+ if (javaStyle) return javaStyle[1];
47
+ const classMatch = content.match(/class\s+(\w+)/);
48
+ if (classMatch) {
49
+ return classMatch[1].replace(/Module$/, "").replace(/Manager$/, "").replace(/Package$/, "") || null;
50
+ }
51
+ return null;
52
+ }
53
+ function findReactMethodDecls(context) {
54
+ const decls = [];
55
+ const files = context.getAllFiles();
56
+ for (const f of files) {
57
+ if (!f.endsWith(".kt") && !f.endsWith(".java")) continue;
58
+ const content = context.readFile(f);
59
+ if (!content) continue;
60
+ if (!content.includes("ReactContextBaseJavaModule") && !content.includes("ReactMethod")) {
61
+ continue;
62
+ }
63
+ const moduleName = extractModuleName(content, f);
64
+ if (!moduleName) continue;
65
+ const nodes = context.getNodesInFile(f);
66
+ const reactMethodRegex = /@ReactMethod(?:\s*\(\s*isBlockingSynchronousMethod\s*=\s*(true|false)\s*\))?\s*\n?\s*(?:@\w+\s*)*(?:fun|public\s+\w+)\s+(\w+)\s*\(/g;
67
+ let match;
68
+ while ((match = reactMethodRegex.exec(content)) !== null) {
69
+ const isBlocking = match[1] === "true";
70
+ const methodName = match[2];
71
+ const lineNum = content.slice(0, match.index).split("\n").length;
72
+ const methodNode = nodes.find(
73
+ (n) => (n.kind === "method" || n.kind === "function") && n.name === methodName && Math.abs(n.startLine - lineNum) < 8
74
+ );
75
+ if (methodNode) {
76
+ decls.push({
77
+ moduleName,
78
+ methodName,
79
+ nodeId: methodNode.id,
80
+ isBlocking,
81
+ filePath: f
82
+ });
83
+ }
84
+ }
85
+ }
86
+ return decls;
87
+ }
88
+ function findReactPropDecls(context) {
89
+ const decls = [];
90
+ const files = context.getAllFiles();
91
+ for (const f of files) {
92
+ if (!f.endsWith(".kt") && !f.endsWith(".java")) continue;
93
+ const content = context.readFile(f);
94
+ if (!content) continue;
95
+ if (!content.includes("@ReactProp")) continue;
96
+ const moduleName = extractModuleName(content, f);
97
+ if (!moduleName) continue;
98
+ const nodes = context.getNodesInFile(f);
99
+ const reactPropRegex = /@ReactProp\s*\(\s*name\s*=\s*"(\w+)"[^)]*\)\s*\n?\s*(?:@\w+\s*)*(?:fun|public\s+void)\s+(\w+)\s*\(/g;
100
+ let match;
101
+ while ((match = reactPropRegex.exec(content)) !== null) {
102
+ const propName = match[1];
103
+ const setterName = match[2];
104
+ const lineNum = content.slice(0, match.index).split("\n").length;
105
+ const methodNode = nodes.find(
106
+ (n) => (n.kind === "method" || n.kind === "function") && n.name === setterName && Math.abs(n.startLine - lineNum) < 8
107
+ );
108
+ if (methodNode) {
109
+ decls.push({ moduleName, propName, nodeId: methodNode.id, filePath: f });
110
+ }
111
+ }
112
+ }
113
+ return decls;
114
+ }
115
+ function findAndroidNativeModuleCalls(context) {
116
+ const calls = [];
117
+ const files = context.getAllFiles();
118
+ for (const f of files) {
119
+ if (!f.endsWith(".ts") && !f.endsWith(".tsx") && !f.endsWith(".js") && !f.endsWith(".jsx")) {
120
+ continue;
121
+ }
122
+ const content = context.readFile(f);
123
+ if (!content) continue;
124
+ if (!content.includes("NativeModules")) continue;
125
+ const nodes = context.getNodesInFile(f);
126
+ const enclosingNode = (offset) => {
127
+ const lineNum = content.slice(0, offset).split("\n").length;
128
+ return nodes.find(
129
+ (n) => (n.kind === "function" || n.kind === "method") && n.startLine <= lineNum && n.endLine >= lineNum
130
+ );
131
+ };
132
+ const directRegex = /NativeModules\.(\w+)\.(\w+)\s*\(/g;
133
+ let match;
134
+ while ((match = directRegex.exec(content)) !== null) {
135
+ const moduleName = match[1];
136
+ const methodName = match[2];
137
+ const caller = enclosingNode(match.index);
138
+ if (caller) {
139
+ calls.push({ moduleName, methodName, callerNodeId: caller.id, via: "NativeModules" });
140
+ }
141
+ }
142
+ const destructureRegex = /const\s*\{([^}]+)\}\s*=\s*NativeModules/g;
143
+ while ((match = destructureRegex.exec(content)) !== null) {
144
+ const moduleNames = match[1].split(",").map((s) => s.trim()).filter(Boolean);
145
+ for (const moduleName of moduleNames) {
146
+ const callRegex = new RegExp(`\\b${moduleName}\\.(\\w+)\\s*\\(`, "g");
147
+ let callMatch;
148
+ while ((callMatch = callRegex.exec(content)) !== null) {
149
+ const methodName = callMatch[1];
150
+ const caller = enclosingNode(callMatch.index);
151
+ if (caller) {
152
+ calls.push({
153
+ moduleName,
154
+ methodName,
155
+ callerNodeId: caller.id,
156
+ via: "NativeModules"
157
+ });
158
+ }
159
+ }
160
+ }
161
+ }
162
+ const emitterRegex = /new\s+NativeEventEmitter\s*\(\s*NativeModules\.(\w+)\s*\)/g;
163
+ while ((match = emitterRegex.exec(content)) !== null) {
164
+ const moduleName = match[1];
165
+ const caller = enclosingNode(match.index);
166
+ if (caller) {
167
+ calls.push({
168
+ moduleName,
169
+ methodName: "__eventEmitterAttach__",
170
+ callerNodeId: caller.id,
171
+ via: "NativeEventEmitter"
172
+ });
173
+ }
174
+ }
175
+ }
176
+ return calls;
177
+ }
178
+ function findJsxPropUsages(context, propNames) {
179
+ const result = /* @__PURE__ */ new Map();
180
+ const files = context.getAllFiles();
181
+ for (const f of files) {
182
+ if (!f.endsWith(".tsx") && !f.endsWith(".jsx")) continue;
183
+ const content = context.readFile(f);
184
+ if (!content) continue;
185
+ const nodes = context.getNodesInFile(f);
186
+ for (const propName of propNames) {
187
+ const attrRegex = new RegExp(`\\b${propName}\\s*=\\s*(?:\\{|")`, "g");
188
+ let match;
189
+ while ((match = attrRegex.exec(content)) !== null) {
190
+ const lineNum = content.slice(0, match.index).split("\n").length;
191
+ const enclosing = nodes.find(
192
+ (n) => (n.kind === "function" || n.kind === "method") && n.startLine <= lineNum && n.endLine >= lineNum
193
+ );
194
+ if (enclosing) {
195
+ const existing = result.get(propName) ?? [];
196
+ if (!existing.includes(enclosing.id)) {
197
+ existing.push(enclosing.id);
198
+ }
199
+ result.set(propName, existing);
200
+ }
201
+ }
202
+ }
203
+ }
204
+ return result;
205
+ }
206
+ const androidRnBridge = {
207
+ name: "android-rn-bridge",
208
+ detect(context) {
209
+ const packageJson = context.readFile("package.json");
210
+ if (!packageJson) return false;
211
+ try {
212
+ const pkg = JSON.parse(packageJson);
213
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
214
+ if (!("react-native" in deps)) return false;
215
+ } catch {
216
+ return false;
217
+ }
218
+ return hasAndroidNativeFiles(context);
219
+ },
220
+ resolve(context) {
221
+ const edges = [];
222
+ const methodDecls = findReactMethodDecls(context);
223
+ const jsCalls = findAndroidNativeModuleCalls(context);
224
+ for (const call of jsCalls) {
225
+ if (call.via === "NativeEventEmitter") continue;
226
+ for (const decl of methodDecls) {
227
+ if (call.moduleName === decl.moduleName && call.methodName === decl.methodName) {
228
+ edges.push({
229
+ source: call.callerNodeId,
230
+ target: decl.nodeId,
231
+ kind: "calls",
232
+ confidence: "inferred",
233
+ confidenceScore: decl.isBlocking ? 0.75 : 0.7,
234
+ metadata: {
235
+ synthesizedBy: "android-rn-bridge",
236
+ provenance: "heuristic",
237
+ moduleName: call.moduleName,
238
+ methodName: call.methodName,
239
+ nativeLanguage: decl.filePath.endsWith(".kt") ? "kotlin" : "java",
240
+ isBlockingSynchronousMethod: decl.isBlocking
241
+ }
242
+ });
243
+ }
244
+ }
245
+ }
246
+ const emitterCalls = jsCalls.filter((c) => c.via === "NativeEventEmitter");
247
+ for (const call of emitterCalls) {
248
+ const moduleDecls = methodDecls.filter((d) => d.moduleName === call.moduleName);
249
+ for (const decl of moduleDecls) {
250
+ edges.push({
251
+ source: call.callerNodeId,
252
+ target: decl.nodeId,
253
+ kind: "references",
254
+ confidence: "inferred",
255
+ confidenceScore: 0.65,
256
+ metadata: {
257
+ synthesizedBy: "android-rn-bridge",
258
+ provenance: "heuristic",
259
+ moduleName: call.moduleName,
260
+ via: "NativeEventEmitter",
261
+ nativeLanguage: decl.filePath.endsWith(".kt") ? "kotlin" : "java"
262
+ }
263
+ });
264
+ break;
265
+ }
266
+ }
267
+ const propDecls = findReactPropDecls(context);
268
+ if (propDecls.length > 0) {
269
+ const propNameSet = new Set(propDecls.map((p) => p.propName));
270
+ const jsxUsages = findJsxPropUsages(context, propNameSet);
271
+ for (const propDecl of propDecls) {
272
+ const callerIds = jsxUsages.get(propDecl.propName) ?? [];
273
+ for (const callerId of callerIds) {
274
+ edges.push({
275
+ source: callerId,
276
+ target: propDecl.nodeId,
277
+ kind: "references",
278
+ confidence: "inferred",
279
+ confidenceScore: 0.7,
280
+ metadata: {
281
+ synthesizedBy: "android-rn-bridge",
282
+ provenance: "heuristic",
283
+ moduleName: propDecl.moduleName,
284
+ propName: propDecl.propName,
285
+ nativeLanguage: propDecl.filePath.endsWith(".kt") ? "kotlin" : "java"
286
+ }
287
+ });
288
+ }
289
+ }
290
+ }
291
+ return edges;
292
+ }
293
+ };
294
+ // Annotate the CommonJS export names for ESM import in node:
295
+ 0 && (module.exports = {
296
+ androidRnBridge
297
+ });
298
+ //# sourceMappingURL=android-rn.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/resolution/bridges/android-rn.ts"],
4
+ "sourcesContent": ["/**\n * Android React Native Bridge\n *\n * Synthesizes edges between JavaScript/TypeScript calls to NativeModules.X.method()\n * (and NativeEventEmitter) and their Kotlin/Java implementations declared via\n * @ReactMethod or @ReactProp in classes extending ReactContextBaseJavaModule.\n *\n * Covers the Android/Kotlin/Java side of the React Native bridge, closing the\n * gap with the iOS/ObjC coverage provided by react-native-legacy-bridge.\n */\n\nimport type { ResolutionContext } from '../../frameworks/types';\nimport type { BridgeResolver, SynthesizedEdge } from './index';\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface AndroidNativeModuleCall {\n moduleName: string;\n methodName: string;\n callerNodeId: string;\n via: 'NativeModules' | 'NativeEventEmitter';\n}\n\ninterface AndroidReactMethodDecl {\n moduleName: string;\n methodName: string;\n nodeId: string;\n isBlocking: boolean;\n filePath: string;\n}\n\ninterface AndroidReactPropDecl {\n moduleName: string;\n propName: string;\n nodeId: string;\n filePath: string;\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Detect Android project root presence by checking for android/ directory\n * marker files or Kotlin/Java files that import react-native packages.\n */\nfunction hasAndroidNativeFiles(context: ResolutionContext): boolean {\n const files = context.getAllFiles();\n for (const f of files) {\n if (!f.endsWith('.kt') && !f.endsWith('.java')) continue;\n const content = context.readFile(f);\n if (!content) continue;\n if (\n content.includes('ReactContextBaseJavaModule') ||\n content.includes('@ReactMethod') ||\n content.includes('@ReactProp')\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Extract the module name exposed to JS from a Kotlin/Java native module file.\n *\n * Priority:\n * 1. Explicit string in getName() return: `return \"ModuleName\"`\n * 2. Class name stripping common suffixes (Module, Manager)\n */\nfunction extractModuleName(content: string, filePath: string): string | null {\n // Kotlin: override fun getName() = \"Name\" or fun getName(): String { return \"Name\" }\n const ktSingleLine = content.match(/override\\s+fun\\s+getName\\s*\\(\\s*\\)\\s*=\\s*\"(\\w+)\"/);\n if (ktSingleLine) return ktSingleLine[1];\n\n // Java/Kotlin block-style getName()\n const blockStyle = content.match(\n /(?:override\\s+)?fun\\s+getName\\s*\\(\\s*\\)\\s*(?::\\s*String\\s*)?\\{[^}]*?return\\s*\"(\\w+)\"/s\n );\n if (blockStyle) return blockStyle[1];\n\n // Java getName()\n const javaStyle = content.match(\n /(?:@Override\\s+)?(?:public\\s+)?String\\s+getName\\s*\\(\\s*\\)\\s*\\{[^}]*?return\\s*\"(\\w+)\"/s\n );\n if (javaStyle) return javaStyle[1];\n\n // Fallback: derive from class name\n const classMatch = content.match(/class\\s+(\\w+)/);\n if (classMatch) {\n return classMatch[1]\n .replace(/Module$/, '')\n .replace(/Manager$/, '')\n .replace(/Package$/, '') || null;\n }\n\n return null;\n}\n\n/**\n * Scan all Kotlin/Java files for @ReactMethod annotations and collect the\n * corresponding method declarations with their graph node IDs.\n */\nfunction findReactMethodDecls(context: ResolutionContext): AndroidReactMethodDecl[] {\n const decls: AndroidReactMethodDecl[] = [];\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (!f.endsWith('.kt') && !f.endsWith('.java')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n // Only process files that are React Native modules\n if (\n !content.includes('ReactContextBaseJavaModule') &&\n !content.includes('ReactMethod')\n ) {\n continue;\n }\n\n const moduleName = extractModuleName(content, f);\n if (!moduleName) continue;\n\n const nodes = context.getNodesInFile(f);\n\n // Match @ReactMethod (and @ReactMethod(isBlockingSynchronousMethod = true))\n // followed by fun/void/public <methodName>(\n const reactMethodRegex =\n /@ReactMethod(?:\\s*\\(\\s*isBlockingSynchronousMethod\\s*=\\s*(true|false)\\s*\\))?\\s*\\n?\\s*(?:@\\w+\\s*)*(?:fun|public\\s+\\w+)\\s+(\\w+)\\s*\\(/g;\n\n let match: RegExpExecArray | null;\n while ((match = reactMethodRegex.exec(content)) !== null) {\n const isBlocking = match[1] === 'true';\n const methodName = match[2];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n\n const methodNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function') &&\n n.name === methodName &&\n Math.abs(n.startLine - lineNum) < 8\n );\n\n if (methodNode) {\n decls.push({\n moduleName,\n methodName,\n nodeId: methodNode.id,\n isBlocking,\n filePath: f,\n });\n }\n }\n }\n\n return decls;\n}\n\n/**\n * Scan Kotlin/Java ViewManager files for @ReactProp annotations and collect\n * the corresponding setter method declarations.\n */\nfunction findReactPropDecls(context: ResolutionContext): AndroidReactPropDecl[] {\n const decls: AndroidReactPropDecl[] = [];\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (!f.endsWith('.kt') && !f.endsWith('.java')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n if (!content.includes('@ReactProp')) continue;\n\n const moduleName = extractModuleName(content, f);\n if (!moduleName) continue;\n\n const nodes = context.getNodesInFile(f);\n\n // @ReactProp(name = \"propName\") followed by fun/void setPropName(...)\n const reactPropRegex =\n /@ReactProp\\s*\\(\\s*name\\s*=\\s*\"(\\w+)\"[^)]*\\)\\s*\\n?\\s*(?:@\\w+\\s*)*(?:fun|public\\s+void)\\s+(\\w+)\\s*\\(/g;\n\n let match: RegExpExecArray | null;\n while ((match = reactPropRegex.exec(content)) !== null) {\n const propName = match[1];\n const setterName = match[2];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n\n const methodNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function') &&\n n.name === setterName &&\n Math.abs(n.startLine - lineNum) < 8\n );\n\n if (methodNode) {\n decls.push({ moduleName, propName, nodeId: methodNode.id, filePath: f });\n }\n }\n }\n\n return decls;\n}\n\n/**\n * Scan JS/TS files for:\n * - NativeModules.ModuleName.methodName(...)\n * - const { ModuleName } = NativeModules; ... ModuleName.methodName(...)\n * - new NativeEventEmitter(NativeModules.ModuleName)\n */\nfunction findAndroidNativeModuleCalls(context: ResolutionContext): AndroidNativeModuleCall[] {\n const calls: AndroidNativeModuleCall[] = [];\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (\n !f.endsWith('.ts') &&\n !f.endsWith('.tsx') &&\n !f.endsWith('.js') &&\n !f.endsWith('.jsx')\n ) {\n continue;\n }\n\n const content = context.readFile(f);\n if (!content) continue;\n\n // Skip if no NativeModules reference at all\n if (!content.includes('NativeModules')) continue;\n\n const nodes = context.getNodesInFile(f);\n\n /**\n * Helper: find the enclosing function/method node for a character offset.\n */\n const enclosingNode = (offset: number) => {\n const lineNum = content.slice(0, offset).split('\\n').length;\n return nodes.find(\n n =>\n (n.kind === 'function' || n.kind === 'method') &&\n n.startLine <= lineNum &&\n n.endLine >= lineNum\n );\n };\n\n // 1. Direct: NativeModules.ModuleName.methodName(\n const directRegex = /NativeModules\\.(\\w+)\\.(\\w+)\\s*\\(/g;\n let match: RegExpExecArray | null;\n while ((match = directRegex.exec(content)) !== null) {\n const moduleName = match[1];\n const methodName = match[2];\n const caller = enclosingNode(match.index);\n if (caller) {\n calls.push({ moduleName, methodName, callerNodeId: caller.id, via: 'NativeModules' });\n }\n }\n\n // 2. Destructured: const { ModuleName } = NativeModules (then ModuleName.method)\n const destructureRegex = /const\\s*\\{([^}]+)\\}\\s*=\\s*NativeModules/g;\n while ((match = destructureRegex.exec(content)) !== null) {\n const moduleNames = match[1]\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n for (const moduleName of moduleNames) {\n // Find all calls to moduleName.methodName(\n const callRegex = new RegExp(`\\\\b${moduleName}\\\\.(\\\\w+)\\\\s*\\\\(`, 'g');\n let callMatch: RegExpExecArray | null;\n while ((callMatch = callRegex.exec(content)) !== null) {\n const methodName = callMatch[1];\n const caller = enclosingNode(callMatch.index);\n if (caller) {\n calls.push({\n moduleName,\n methodName,\n callerNodeId: caller.id,\n via: 'NativeModules',\n });\n }\n }\n }\n }\n\n // 3. NativeEventEmitter usage: new NativeEventEmitter(NativeModules.ModuleName)\n const emitterRegex = /new\\s+NativeEventEmitter\\s*\\(\\s*NativeModules\\.(\\w+)\\s*\\)/g;\n while ((match = emitterRegex.exec(content)) !== null) {\n const moduleName = match[1];\n const caller = enclosingNode(match.index);\n if (caller) {\n // Synthesize a synthetic \"attach\" method call to represent the binding\n calls.push({\n moduleName,\n methodName: '__eventEmitterAttach__',\n callerNodeId: caller.id,\n via: 'NativeEventEmitter',\n });\n }\n }\n }\n\n return calls;\n}\n\n/**\n * Scan JS/TS files for JSX prop usages that match @ReactProp names.\n * e.g. <MyView color=\"red\" /> \u2192 color prop setter in ViewManager\n *\n * Returns a map of propName \u2192 callerNodeId[]\n */\nfunction findJsxPropUsages(\n context: ResolutionContext,\n propNames: Set<string>\n): Map<string, string[]> {\n const result = new Map<string, string[]>();\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (!f.endsWith('.tsx') && !f.endsWith('.jsx')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n const nodes = context.getNodesInFile(f);\n\n for (const propName of propNames) {\n // Match JSX attribute: propName={...} or propName=\"...\"\n const attrRegex = new RegExp(`\\\\b${propName}\\\\s*=\\\\s*(?:\\\\{|\")`, 'g');\n let match: RegExpExecArray | null;\n while ((match = attrRegex.exec(content)) !== null) {\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const enclosing = nodes.find(\n n =>\n (n.kind === 'function' || n.kind === 'method') &&\n n.startLine <= lineNum &&\n n.endLine >= lineNum\n );\n if (enclosing) {\n const existing = result.get(propName) ?? [];\n if (!existing.includes(enclosing.id)) {\n existing.push(enclosing.id);\n }\n result.set(propName, existing);\n }\n }\n }\n }\n\n return result;\n}\n\n// \u2500\u2500 Bridge Implementation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const androidRnBridge: BridgeResolver = {\n name: 'android-rn-bridge',\n\n detect(context: ResolutionContext): boolean {\n // Must be a React Native project\n const packageJson = context.readFile('package.json');\n if (!packageJson) return false;\n\n try {\n const pkg = JSON.parse(packageJson);\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (!('react-native' in deps)) return false;\n } catch {\n return false;\n }\n\n // Must have Android native files with RN annotations\n return hasAndroidNativeFiles(context);\n },\n\n resolve(context: ResolutionContext): SynthesizedEdge[] {\n const edges: SynthesizedEdge[] = [];\n\n // \u2500\u2500 1. @ReactMethod edges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const methodDecls = findReactMethodDecls(context);\n const jsCalls = findAndroidNativeModuleCalls(context);\n\n for (const call of jsCalls) {\n // NativeEventEmitter attach \u2014 skip method matching, handled below\n if (call.via === 'NativeEventEmitter') continue;\n\n for (const decl of methodDecls) {\n if (call.moduleName === decl.moduleName && call.methodName === decl.methodName) {\n edges.push({\n source: call.callerNodeId,\n target: decl.nodeId,\n kind: 'calls',\n confidence: 'inferred',\n confidenceScore: decl.isBlocking ? 0.75 : 0.7,\n metadata: {\n synthesizedBy: 'android-rn-bridge',\n provenance: 'heuristic',\n moduleName: call.moduleName,\n methodName: call.methodName,\n nativeLanguage: decl.filePath.endsWith('.kt') ? 'kotlin' : 'java',\n isBlockingSynchronousMethod: decl.isBlocking,\n },\n });\n }\n }\n }\n\n // \u2500\u2500 2. NativeEventEmitter \u2192 module edges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Synthesize a \"references\" edge from the JS emitter attachment to the\n // module class node so the call graph surfaces the Android \u2194 JS binding.\n\n const emitterCalls = jsCalls.filter(c => c.via === 'NativeEventEmitter');\n for (const call of emitterCalls) {\n // Find any @ReactMethod in the referenced module as a representative target\n const moduleDecls = methodDecls.filter(d => d.moduleName === call.moduleName);\n for (const decl of moduleDecls) {\n edges.push({\n source: call.callerNodeId,\n target: decl.nodeId,\n kind: 'references',\n confidence: 'inferred',\n confidenceScore: 0.65,\n metadata: {\n synthesizedBy: 'android-rn-bridge',\n provenance: 'heuristic',\n moduleName: call.moduleName,\n via: 'NativeEventEmitter',\n nativeLanguage: decl.filePath.endsWith('.kt') ? 'kotlin' : 'java',\n },\n });\n // Only one representative edge per emitter attachment\n break;\n }\n }\n\n // \u2500\u2500 3. @ReactProp edges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const propDecls = findReactPropDecls(context);\n if (propDecls.length > 0) {\n const propNameSet = new Set(propDecls.map(p => p.propName));\n const jsxUsages = findJsxPropUsages(context, propNameSet);\n\n for (const propDecl of propDecls) {\n const callerIds = jsxUsages.get(propDecl.propName) ?? [];\n for (const callerId of callerIds) {\n edges.push({\n source: callerId,\n target: propDecl.nodeId,\n kind: 'references',\n confidence: 'inferred',\n confidenceScore: 0.7,\n metadata: {\n synthesizedBy: 'android-rn-bridge',\n provenance: 'heuristic',\n moduleName: propDecl.moduleName,\n propName: propDecl.propName,\n nativeLanguage: propDecl.filePath.endsWith('.kt') ? 'kotlin' : 'java',\n },\n });\n }\n }\n }\n\n return edges;\n },\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }