@unerr-ai/unerr 0.1.0 → 0.1.1

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 (553) hide show
  1. package/dist/__tests__/architecture-guard.test.js +122 -0
  2. package/dist/__tests__/arg-validator.test.js +205 -0
  3. package/dist/__tests__/ast-extractor.test.js +203 -0
  4. package/dist/__tests__/auto-bootstrap.test.js +280 -0
  5. package/dist/__tests__/background-indexer.test.js +228 -0
  6. package/dist/__tests__/blast-radius-engine.test.js +200 -0
  7. package/dist/__tests__/bridge-isolation.test.js +37 -0
  8. package/dist/__tests__/budget-enforcer.test.js +53 -0
  9. package/dist/__tests__/cfg-test-detection-perf.test.js +82 -0
  10. package/dist/__tests__/change-narrative.test.js +190 -0
  11. package/dist/__tests__/check-commit.test.js +258 -0
  12. package/dist/__tests__/checksum.test.js +34 -0
  13. package/dist/__tests__/commit-watcher.test.js +154 -0
  14. package/dist/__tests__/community-detection.test.js +179 -0
  15. package/dist/__tests__/community-tools.test.js +299 -0
  16. package/dist/__tests__/components.test.js +449 -0
  17. package/dist/__tests__/compression-log.test.js +174 -0
  18. package/dist/__tests__/compression-quality-monitor.test.js +40 -0
  19. package/dist/__tests__/config-healer.test.js +165 -0
  20. package/dist/__tests__/context-ledger.test.js +58 -0
  21. package/dist/__tests__/convention-detector.test.js +99 -0
  22. package/dist/__tests__/convention-learner.test.js +86 -0
  23. package/dist/__tests__/correction-detector.test.js +330 -0
  24. package/dist/__tests__/daemon-autostart-install.test.js +283 -0
  25. package/dist/__tests__/daemon-bridge.test.js +222 -0
  26. package/dist/__tests__/daemon-dashboard.test.js +202 -0
  27. package/dist/__tests__/daemon-registry.test.js +240 -0
  28. package/dist/__tests__/daemon-supervisor.test.js +318 -0
  29. package/dist/__tests__/daemon-version-check.test.js +275 -0
  30. package/dist/__tests__/decision-point-detector.test.js +98 -0
  31. package/dist/__tests__/deep-link.test.js +143 -0
  32. package/dist/__tests__/disallowed-tools.test.js +115 -0
  33. package/dist/__tests__/drift-tracker.test.js +582 -0
  34. package/dist/__tests__/durability-scorer.test.js +152 -0
  35. package/dist/__tests__/efficiency-tracker.test.js +65 -0
  36. package/dist/__tests__/enrich.test.js +144 -0
  37. package/dist/__tests__/entity-rewind.test.js +248 -0
  38. package/dist/__tests__/ephemeral.test.js +111 -0
  39. package/dist/__tests__/exploration-cost.test.js +93 -0
  40. package/dist/__tests__/fact-generator.test.js +197 -0
  41. package/dist/__tests__/file-l0-graph.test.js +244 -0
  42. package/dist/__tests__/file-logger.test.js +82 -0
  43. package/dist/__tests__/file-outline.test.js +141 -0
  44. package/dist/__tests__/file-read-protocol.test.js +188 -0
  45. package/dist/__tests__/format-encoder.test.js +233 -0
  46. package/dist/__tests__/git-attribution.test.js +259 -0
  47. package/dist/__tests__/graph-temporal-joiner.test.js +219 -0
  48. package/dist/__tests__/health-grade-enhanced.test.js +138 -0
  49. package/dist/__tests__/health-map-data.test.js +173 -0
  50. package/dist/__tests__/helpers/mcp-harness.js +45 -0
  51. package/dist/__tests__/helpers/mcp-harness.test.js +68 -0
  52. package/dist/__tests__/hook-dedup.test.js +112 -0
  53. package/dist/__tests__/hook-runner.test.js +253 -0
  54. package/dist/__tests__/indexer-cfg.test.js +185 -0
  55. package/dist/__tests__/indexer-cross-file.test.js +172 -0
  56. package/dist/__tests__/indexer-extraction.test.js +245 -0
  57. package/dist/__tests__/indexer-incremental.test.js +232 -0
  58. package/dist/__tests__/indexer-language-expansion.test.js +165 -0
  59. package/dist/__tests__/init-push.test.js +131 -0
  60. package/dist/__tests__/instruction-writer.test.js +179 -0
  61. package/dist/__tests__/intelligence-integration.test.js +217 -0
  62. package/dist/__tests__/intent-correlator.test.js +175 -0
  63. package/dist/__tests__/intent-detector.test.js +235 -0
  64. package/dist/__tests__/intent-encoder.test.js +167 -0
  65. package/dist/__tests__/java-build-tool-detection.test.js +174 -0
  66. package/dist/__tests__/layer3-sprint-q.test.js +160 -0
  67. package/dist/__tests__/layer3-sprint-r.test.js +91 -0
  68. package/dist/__tests__/layer3-sprint-s.test.js +183 -0
  69. package/dist/__tests__/layer3-sprint-t.test.js +201 -0
  70. package/dist/__tests__/layer3-sprint-u.test.js +174 -0
  71. package/dist/__tests__/layer4-sprint-ba2.test.js +354 -0
  72. package/dist/__tests__/layer4-sprint-ba4.test.js +84 -0
  73. package/dist/__tests__/layer4-sprint-vs.test.js +105 -0
  74. package/dist/__tests__/ledger-chains.test.js +162 -0
  75. package/dist/__tests__/lifecycle-machine.test.js +226 -0
  76. package/dist/__tests__/local-chat-provider.test.js +170 -0
  77. package/dist/__tests__/local-convention-detector.test.js +308 -0
  78. package/dist/__tests__/local-embeddings.test.js +422 -0
  79. package/dist/__tests__/local-graph.test.js +540 -0
  80. package/dist/__tests__/local-indexer.test.js +228 -0
  81. package/dist/__tests__/local-intelligence-l3.test.js +332 -0
  82. package/dist/__tests__/local-llm.test.js +253 -0
  83. package/dist/__tests__/local-mode-offline.test.js +187 -0
  84. package/dist/__tests__/local-mode-stats.test.js +273 -0
  85. package/dist/__tests__/local-mode-tui.test.js +343 -0
  86. package/dist/__tests__/local-parse.test.js +199 -0
  87. package/dist/__tests__/log-tailer.test.js +208 -0
  88. package/dist/__tests__/loop-breaker.test.js +276 -0
  89. package/dist/__tests__/loop-miner.test.js +226 -0
  90. package/dist/__tests__/mcp-config.test.js +126 -0
  91. package/dist/__tests__/mcp-content-json.test.js +10 -0
  92. package/dist/__tests__/mcp-envelope.test.js +124 -0
  93. package/dist/__tests__/metrics-store.test.js +223 -0
  94. package/dist/__tests__/native-watcher.test.js +191 -0
  95. package/dist/__tests__/navigation-hooks-agent-aware.test.js +145 -0
  96. package/dist/__tests__/negative-knowledge.test.js +116 -0
  97. package/dist/__tests__/network-boundary.test.js +190 -0
  98. package/dist/__tests__/network-firewall.test.js +112 -0
  99. package/dist/__tests__/nudge-invariants.test.js +160 -0
  100. package/dist/__tests__/nudge-v2.test.js +225 -0
  101. package/dist/__tests__/offline-rewind.test.js +251 -0
  102. package/dist/__tests__/open-threads.test.js +89 -0
  103. package/dist/__tests__/output-compressor.test.js +93 -0
  104. package/dist/__tests__/pending-violations.test.js +112 -0
  105. package/dist/__tests__/persistence-effectiveness.test.js +143 -0
  106. package/dist/__tests__/provider-factory.test.js +42 -0
  107. package/dist/__tests__/providers.test.js +24 -0
  108. package/dist/__tests__/proxy.test.js +314 -0
  109. package/dist/__tests__/query-router.test.js +1018 -0
  110. package/dist/__tests__/reasoning-quality-route.test.js +138 -0
  111. package/dist/__tests__/redactor.test.js +120 -0
  112. package/dist/__tests__/resource-monitor.test.js +57 -0
  113. package/dist/__tests__/response-envelope.test.js +100 -0
  114. package/dist/__tests__/risk-classifier.test.js +101 -0
  115. package/dist/__tests__/risk-signal-scope.test.js +75 -0
  116. package/dist/__tests__/rule-evaluator.test.js +280 -0
  117. package/dist/__tests__/scip-decoder.test.js +49 -0
  118. package/dist/__tests__/scip-downloader.test.js +201 -0
  119. package/dist/__tests__/scip-merger.test.js +103 -0
  120. package/dist/__tests__/search-index.test.js +422 -0
  121. package/dist/__tests__/semantic-enrichment.test.js +360 -0
  122. package/dist/__tests__/session-brief-builder.test.js +187 -0
  123. package/dist/__tests__/session-context.test.js +221 -0
  124. package/dist/__tests__/session-continuity.test.js +144 -0
  125. package/dist/__tests__/session-dedup.test.js +74 -0
  126. package/dist/__tests__/session-event-wiring.test.js +206 -0
  127. package/dist/__tests__/session-events.test.js +149 -0
  128. package/dist/__tests__/session-legend.test.js +20 -0
  129. package/dist/__tests__/session-persistence.test.js +131 -0
  130. package/dist/__tests__/session-resume-block.test.js +107 -0
  131. package/dist/__tests__/session-resume.test.js +97 -0
  132. package/dist/__tests__/session-summary-writer.test.js +134 -0
  133. package/dist/__tests__/shadow-ledger.test.js +203 -0
  134. package/dist/__tests__/shell-classifier.test.js +151 -0
  135. package/dist/__tests__/shell-compression-floor.test.js +189 -0
  136. package/dist/__tests__/shell-compression-v2.test.js +339 -0
  137. package/dist/__tests__/shell-compressor.test.js +35 -0
  138. package/dist/__tests__/shell-hooks.test.js +128 -0
  139. package/dist/__tests__/shell-strategies.test.js +644 -0
  140. package/dist/__tests__/shell-tee.test.js +133 -0
  141. package/dist/__tests__/signal-dedup.test.js +158 -0
  142. package/dist/__tests__/signal-reinforcer.test.js +77 -0
  143. package/dist/__tests__/signal-scorer.test.js +251 -0
  144. package/dist/__tests__/signal-show-store.test.js +108 -0
  145. package/dist/__tests__/smart-truncate.test.js +215 -0
  146. package/dist/__tests__/snapshot-v2.test.js +113 -0
  147. package/dist/__tests__/sprint-l1-local-mode.test.js +130 -0
  148. package/dist/__tests__/sprint-l10-boot.test.js +220 -0
  149. package/dist/__tests__/sprint-l9-offline-commands.test.js +189 -0
  150. package/dist/__tests__/sprint-q-persistent-context.test.js +198 -0
  151. package/dist/__tests__/sprint-s1-wiring.test.js +215 -0
  152. package/dist/__tests__/sprint-s2-wiring.test.js +256 -0
  153. package/dist/__tests__/sprint-s3-wiring.test.js +195 -0
  154. package/dist/__tests__/sprint-s4-wiring.test.js +213 -0
  155. package/dist/__tests__/sprint-s6-hooks.test.js +222 -0
  156. package/dist/__tests__/sprint-s7-persistent.test.js +263 -0
  157. package/dist/__tests__/sprint-s8-value.test.js +167 -0
  158. package/dist/__tests__/sprint-s9-behavioral.test.js +179 -0
  159. package/dist/__tests__/sprint3-intelligence.test.js +297 -0
  160. package/dist/__tests__/sprint5-mcp-server.test.js +136 -0
  161. package/dist/__tests__/startup-display.test.js +302 -0
  162. package/dist/__tests__/startup-log-file.test.js +97 -0
  163. package/dist/__tests__/stash-manager.test.js +229 -0
  164. package/dist/__tests__/state-detector.test.js +92 -0
  165. package/dist/__tests__/status-dashboard.test.js +142 -0
  166. package/dist/__tests__/temporal-facts.test.js +292 -0
  167. package/dist/__tests__/temporal-routes.test.js +142 -0
  168. package/dist/__tests__/test-detector.test.js +174 -0
  169. package/dist/__tests__/theme.test.js +72 -0
  170. package/dist/__tests__/timeline-agents.test.js +122 -0
  171. package/dist/__tests__/timeline-bootstrap.test.js +176 -0
  172. package/dist/__tests__/timeline-filters.test.js +193 -0
  173. package/dist/__tests__/timeline-markers.test.js +151 -0
  174. package/dist/__tests__/timeline-routes.test.js +156 -0
  175. package/dist/__tests__/timeline-store.test.js +171 -0
  176. package/dist/__tests__/token-counter.test.js +86 -0
  177. package/dist/__tests__/token-estimator.test.js +96 -0
  178. package/dist/__tests__/token-flow-api.test.js +239 -0
  179. package/dist/__tests__/token-flow-instrumentation.test.js +437 -0
  180. package/dist/__tests__/token-flow-persistence.test.js +356 -0
  181. package/dist/__tests__/token-flow-routes.test.js +199 -0
  182. package/dist/__tests__/token-flow.test.js +695 -0
  183. package/dist/__tests__/tool-clusters.test.js +177 -0
  184. package/dist/__tests__/transport-mux.test.js +283 -0
  185. package/dist/__tests__/turn-segmenter.test.js +166 -0
  186. package/dist/__tests__/uninstall.test.js +141 -0
  187. package/dist/__tests__/warm-start-policy.test.js +271 -0
  188. package/dist/__tests__/wire-cap-nudge.test.js +77 -0
  189. package/dist/__tests__/worker-pool.test.js +101 -0
  190. package/dist/behaviors/agent-llm-bridge.js +166 -0
  191. package/dist/behaviors/architecture-guard.js +256 -0
  192. package/dist/behaviors/auto-doc.js +247 -0
  193. package/dist/behaviors/cascade-guard.js +289 -0
  194. package/dist/behaviors/change-narrative.js +270 -0
  195. package/dist/behaviors/convention-drift.js +290 -0
  196. package/dist/behaviors/framework.js +235 -0
  197. package/dist/behaviors/guard-formatter.js +44 -0
  198. package/dist/behaviors/incomplete-work.js +270 -0
  199. package/dist/behaviors/loop-breaker.js +300 -0
  200. package/dist/behaviors/session-continuity.js +208 -0
  201. package/dist/cli.js +992 -706
  202. package/dist/commands/branches.js +97 -0
  203. package/dist/commands/check-commit.js +225 -0
  204. package/dist/commands/compress-output.js +64 -0
  205. package/dist/commands/config-verify.js +243 -0
  206. package/dist/commands/daemon.js +905 -0
  207. package/dist/commands/dashboard.js +52 -0
  208. package/dist/commands/debug.js +200 -0
  209. package/dist/commands/enrich.js +184 -0
  210. package/dist/commands/exec.js +233 -0
  211. package/dist/commands/gain.js +156 -0
  212. package/dist/commands/hook.js +88 -0
  213. package/dist/commands/index.js +88 -0
  214. package/dist/commands/init.js +74 -0
  215. package/dist/commands/install.js +505 -0
  216. package/dist/commands/learn.js +116 -0
  217. package/dist/commands/manifest.js +193 -0
  218. package/dist/commands/rewind.js +103 -0
  219. package/dist/commands/serve.js +19 -0
  220. package/dist/commands/setup-wizard.js +414 -0
  221. package/dist/commands/skills.js +64 -0
  222. package/dist/commands/stats.js +20 -0
  223. package/dist/commands/status.js +654 -0
  224. package/dist/commands/timeline.js +139 -0
  225. package/dist/commands/uninstall.js +230 -0
  226. package/dist/components/App.js +109 -0
  227. package/dist/components/Banner.js +12 -0
  228. package/dist/components/ConfirmPrompt.js +25 -0
  229. package/dist/components/DriftSummary.js +23 -0
  230. package/dist/components/GradeBadge.js +15 -0
  231. package/dist/components/HealthCard.js +18 -0
  232. package/dist/components/InkSpinner.js +22 -0
  233. package/dist/components/InputBox.js +17 -0
  234. package/dist/components/KeyValue.js +13 -0
  235. package/dist/components/MessageList.js +14 -0
  236. package/dist/components/ProgressBar.js +26 -0
  237. package/dist/components/Section.js +16 -0
  238. package/dist/components/SessionSummaryCard.js +73 -0
  239. package/dist/components/StartupDisplay.js +24 -0
  240. package/dist/components/StatusDashboard.js +57 -0
  241. package/dist/components/StatusLine.js +8 -0
  242. package/dist/components/StepLine.js +22 -0
  243. package/dist/components/Theme.js +20 -0
  244. package/dist/components/ToolProgress.js +8 -0
  245. package/dist/components/ViolationList.js +21 -0
  246. package/dist/components/render.js +13 -0
  247. package/dist/config/agent-registry.js +237 -0
  248. package/dist/config/claude-settings-hooks.js +304 -0
  249. package/dist/config/hook-installer.js +65 -0
  250. package/dist/config/instruction-writer.js +388 -0
  251. package/dist/config/mcp-config-writer.js +266 -0
  252. package/dist/config/settings.js +174 -0
  253. package/dist/config/tool-detector.js +42 -0
  254. package/dist/config/value-surfacing.js +119 -0
  255. package/dist/core/context-assembly.js +108 -0
  256. package/dist/core/conversation.js +33 -0
  257. package/dist/core/local-chat-provider.js +475 -0
  258. package/dist/core/provider-factory.js +55 -0
  259. package/dist/core/providers.js +90 -0
  260. package/dist/core/query-engine.js +174 -0
  261. package/dist/daemon/api.js +312 -0
  262. package/dist/daemon/autostart.js +119 -0
  263. package/dist/daemon/bootstrap.js +39 -0
  264. package/dist/daemon/client.js +164 -0
  265. package/dist/daemon/detect-ci.js +81 -0
  266. package/dist/daemon/platform-linux.js +146 -0
  267. package/dist/daemon/platform-macos.js +134 -0
  268. package/dist/daemon/platform-windows.js +116 -0
  269. package/dist/daemon/process-manager.js +299 -0
  270. package/dist/daemon/protocol.js +23 -0
  271. package/dist/daemon/registry.js +270 -0
  272. package/dist/daemon/settings-schema.js +72 -0
  273. package/dist/daemon/system-health.js +134 -0
  274. package/dist/daemon/version-checker.js +262 -0
  275. package/dist/daemon/warm-start.js +223 -0
  276. package/dist/entrypoints/cli.js +1043 -0
  277. package/dist/entrypoints/daemon.js +380 -0
  278. package/dist/entrypoints/repl.js +147 -0
  279. package/dist/hooks/adapters/claude-code.js +90 -0
  280. package/dist/hooks/adapters/cline.js +100 -0
  281. package/dist/hooks/adapters/cursor.js +98 -0
  282. package/dist/hooks/hook-dedup.js +79 -0
  283. package/dist/hooks/hook-runner.js +113 -0
  284. package/dist/hooks/navigation-hooks.js +175 -0
  285. package/dist/hooks/prompt-hooks.js +63 -0
  286. package/dist/hooks/shell-hooks.js +47 -0
  287. package/dist/ignore.js +111 -0
  288. package/dist/intelligence/approach-suggester.js +61 -0
  289. package/dist/intelligence/ast-extractor.js +2615 -0
  290. package/dist/intelligence/ast-worker.js +34 -0
  291. package/dist/intelligence/background-indexer.js +121 -0
  292. package/dist/intelligence/blast-radius.js +200 -0
  293. package/dist/intelligence/community-detection.js +691 -0
  294. package/dist/intelligence/community-detector.js +184 -0
  295. package/dist/intelligence/computation-scheduler.js +75 -0
  296. package/dist/intelligence/confidence-propagation.js +47 -0
  297. package/dist/intelligence/convention-detector.js +242 -0
  298. package/dist/intelligence/convention-learner.js +205 -0
  299. package/dist/intelligence/convention-matcher.js +205 -0
  300. package/dist/intelligence/cozo-schema.js +376 -0
  301. package/dist/intelligence/decision-point-detector.js +90 -0
  302. package/dist/intelligence/deep-dive-tools.js +586 -0
  303. package/dist/intelligence/durability-scorer.js +84 -0
  304. package/dist/intelligence/exploration-cost.js +204 -0
  305. package/dist/intelligence/exploration-pattern-tracker.js +61 -0
  306. package/dist/intelligence/fact-generator.js +322 -0
  307. package/dist/intelligence/facts-schema.js +90 -0
  308. package/dist/intelligence/file-intelligence.js +59 -0
  309. package/dist/intelligence/graph-holder.js +220 -0
  310. package/dist/intelligence/graph-temporal-joiner.js +238 -0
  311. package/dist/intelligence/health-grade.js +423 -0
  312. package/dist/intelligence/health-grader.js +200 -0
  313. package/dist/intelligence/health-map-data.js +259 -0
  314. package/dist/intelligence/import-symbols.js +136 -0
  315. package/dist/intelligence/incremental-indexer.js +658 -0
  316. package/dist/intelligence/indexer/centrality.js +62 -0
  317. package/dist/intelligence/indexer/cfg-context.js +95 -0
  318. package/dist/intelligence/indexer/confidence.js +34 -0
  319. package/dist/intelligence/indexer/cross-file-resolver.js +104 -0
  320. package/dist/intelligence/indexer/edge-repair.js +89 -0
  321. package/dist/intelligence/indexer/entity-key.js +17 -0
  322. package/dist/intelligence/indexer/export-map.js +132 -0
  323. package/dist/intelligence/indexer/git-cochange.js +128 -0
  324. package/dist/intelligence/indexer/graph-patch.js +147 -0
  325. package/dist/intelligence/indexer/incremental.js +78 -0
  326. package/dist/intelligence/indexer/ingest.js +160 -0
  327. package/dist/intelligence/indexer/language-detect.js +226 -0
  328. package/dist/intelligence/indexer/metadata.js +63 -0
  329. package/dist/intelligence/indexer/mutation-tracker.js +79 -0
  330. package/dist/intelligence/indexer/orchestrator.js +155 -0
  331. package/dist/intelligence/indexer/plugin-interface.js +31 -0
  332. package/dist/intelligence/indexer/plugins/csharp.js +440 -0
  333. package/dist/intelligence/indexer/plugins/go.js +335 -0
  334. package/dist/intelligence/indexer/plugins/java.js +370 -0
  335. package/dist/intelligence/indexer/plugins/python.js +358 -0
  336. package/dist/intelligence/indexer/plugins/regex-fallback.js +82 -0
  337. package/dist/intelligence/indexer/plugins/ruby.js +290 -0
  338. package/dist/intelligence/indexer/plugins/rust.js +484 -0
  339. package/dist/intelligence/indexer/plugins/tier2-generic.js +310 -0
  340. package/dist/intelligence/indexer/plugins/typescript.js +456 -0
  341. package/dist/intelligence/indexer/resource-monitor.js +93 -0
  342. package/dist/intelligence/indexer/scip/decoder.js +253 -0
  343. package/dist/intelligence/indexer/scip/detector.js +232 -0
  344. package/dist/intelligence/indexer/scip/downloader.js +427 -0
  345. package/dist/intelligence/indexer/scip/fallback.js +34 -0
  346. package/dist/intelligence/indexer/scip/merger.js +109 -0
  347. package/dist/intelligence/indexer/scip/orchestrator.js +433 -0
  348. package/dist/intelligence/indexer/scip/runner.js +98 -0
  349. package/dist/intelligence/indexer/snapshot.js +66 -0
  350. package/dist/intelligence/indexer/test-detector.js +196 -0
  351. package/dist/intelligence/indexer/watch-integration.js +61 -0
  352. package/dist/intelligence/indexer/worker.js +85 -0
  353. package/dist/intelligence/local-convention-detector.js +437 -0
  354. package/dist/intelligence/local-embeddings.js +190 -0
  355. package/dist/intelligence/local-graph.js +1946 -0
  356. package/dist/intelligence/local-indexer.js +1575 -0
  357. package/dist/intelligence/local-llm.js +163 -0
  358. package/dist/intelligence/local-rule-generator.js +154 -0
  359. package/dist/intelligence/local-snapshot.js +213 -0
  360. package/dist/intelligence/negative-knowledge.js +103 -0
  361. package/dist/intelligence/persistent-db.js +85 -0
  362. package/dist/intelligence/query-router.js +2556 -0
  363. package/dist/intelligence/risk-classifier.js +116 -0
  364. package/dist/intelligence/rule-evaluator.js +380 -0
  365. package/dist/intelligence/rule-generator.js +49 -0
  366. package/dist/intelligence/search-index.js +173 -0
  367. package/dist/intelligence/semantic/docstring-extractor.js +67 -0
  368. package/dist/intelligence/semantic/embedding-store.js +52 -0
  369. package/dist/intelligence/semantic/enrichment-orchestrator.js +48 -0
  370. package/dist/intelligence/semantic/git-message-miner.js +114 -0
  371. package/dist/intelligence/semantic/identifier-tokenizer.js +51 -0
  372. package/dist/intelligence/semantic/node2vec-embeddings.js +71 -0
  373. package/dist/intelligence/semantic/node2vec-walks.js +103 -0
  374. package/dist/intelligence/semantic/path-domain-inference.js +112 -0
  375. package/dist/intelligence/semantic/similarity-engine.js +60 -0
  376. package/dist/intelligence/semantic/tfidf-vectors.js +88 -0
  377. package/dist/intelligence/session-brief-builder.js +159 -0
  378. package/dist/intelligence/session-context.js +221 -0
  379. package/dist/intelligence/session-health-monitor.js +211 -0
  380. package/dist/intelligence/session-narrative.js +197 -0
  381. package/dist/intelligence/session-pattern-analyzer.js +218 -0
  382. package/dist/intelligence/signal-scorer.js +390 -0
  383. package/dist/intelligence/signal-show-store.js +182 -0
  384. package/dist/intelligence/smart-truncate.js +158 -0
  385. package/dist/intelligence/subgraph-cache.js +88 -0
  386. package/dist/intelligence/temporal-facts.js +494 -0
  387. package/dist/intelligence/token-estimator.js +100 -0
  388. package/dist/intelligence/tool-injector.js +87 -0
  389. package/dist/intelligence/tree-sitter-loader.js +71 -0
  390. package/dist/intelligence/worker-pool.js +116 -0
  391. package/dist/proxy/arg-validator.js +79 -0
  392. package/dist/proxy/auto-bootstrap.js +167 -0
  393. package/dist/proxy/bridge.js +147 -0
  394. package/dist/proxy/budget-enforcer.js +70 -0
  395. package/dist/proxy/compression-quality-monitor.js +160 -0
  396. package/dist/proxy/compression-stats.js +51 -0
  397. package/dist/proxy/context-rot-detector.js +137 -0
  398. package/dist/proxy/drift-detector.js +139 -0
  399. package/dist/proxy/efficiency-tracker.js +79 -0
  400. package/dist/proxy/fact-ranking.js +154 -0
  401. package/dist/proxy/format-encoder.js +266 -0
  402. package/dist/proxy/http-transport.js +90 -0
  403. package/dist/proxy/lifecycle-actor.js +55 -0
  404. package/dist/proxy/lifecycle-machine.js +187 -0
  405. package/dist/proxy/log-tailer.js +265 -0
  406. package/dist/proxy/model-pricing.js +98 -0
  407. package/dist/proxy/network-firewall.js +141 -0
  408. package/dist/proxy/nudge-state.js +93 -0
  409. package/dist/proxy/output-compressor.js +185 -0
  410. package/dist/proxy/pid-lock.js +291 -0
  411. package/dist/proxy/proxy-context.js +11 -0
  412. package/dist/proxy/proxy.js +2633 -0
  413. package/dist/proxy/response-enrichment.js +32 -0
  414. package/dist/proxy/response-envelope.js +313 -0
  415. package/dist/proxy/session-dedup.js +82 -0
  416. package/dist/proxy/session-legend.js +30 -0
  417. package/dist/proxy/session-persistence.js +210 -0
  418. package/dist/proxy/session-resume.js +94 -0
  419. package/dist/proxy/session-stats.js +513 -0
  420. package/dist/proxy/shell-classifier.js +1346 -0
  421. package/dist/proxy/shell-compression-log.js +93 -0
  422. package/dist/proxy/shell-compressor.js +390 -0
  423. package/dist/proxy/shell-graph-boost.js +202 -0
  424. package/dist/proxy/shell-monitor-map.js +18 -0
  425. package/dist/proxy/shell-stats.js +54 -0
  426. package/dist/proxy/shell-strategies/cloud.js +215 -0
  427. package/dist/proxy/shell-strategies/diff.js +159 -0
  428. package/dist/proxy/shell-strategies/error-diagnostic.js +796 -0
  429. package/dist/proxy/shell-strategies/filter-dsl.js +358 -0
  430. package/dist/proxy/shell-strategies/git-status.js +177 -0
  431. package/dist/proxy/shell-strategies/key-value.js +193 -0
  432. package/dist/proxy/shell-strategies/log-text.js +154 -0
  433. package/dist/proxy/shell-strategies/omni.js +188 -0
  434. package/dist/proxy/shell-strategies/progress.js +55 -0
  435. package/dist/proxy/shell-strategies/redact.js +76 -0
  436. package/dist/proxy/shell-strategies/structured.js +241 -0
  437. package/dist/proxy/shell-strategies/tabular.js +243 -0
  438. package/dist/proxy/shell-strategies/test-results-types.js +13 -0
  439. package/dist/proxy/shell-strategies/test-results.js +784 -0
  440. package/dist/proxy/shell-strategies/tree-paths.js +144 -0
  441. package/dist/proxy/shell-strategies/yaml.js +182 -0
  442. package/dist/proxy/shell-tee.js +111 -0
  443. package/dist/proxy/signal-dedup.js +171 -0
  444. package/dist/proxy/startup-renderer.js +158 -0
  445. package/dist/proxy/task-token-display.js +38 -0
  446. package/dist/proxy/token-counter.js +61 -0
  447. package/dist/proxy/tool-clusters.js +273 -0
  448. package/dist/proxy/tool-definitions.js +525 -0
  449. package/dist/proxy/transport-mux.js +229 -0
  450. package/dist/proxy/wire-cap.js +268 -0
  451. package/dist/schemas/api/skills.js +19 -0
  452. package/dist/schemas/common/errors.js +7 -0
  453. package/dist/schemas/common/headers.js +5 -0
  454. package/dist/schemas/entities/edge.js +25 -0
  455. package/dist/schemas/entities/entity.js +22 -0
  456. package/dist/schemas/entities/rule.js +18 -0
  457. package/dist/schemas/index.js +14 -0
  458. package/dist/server/event-bus.js +59 -0
  459. package/dist/server/http.js +156 -0
  460. package/dist/server/middleware.js +70 -0
  461. package/dist/server/routes/drift.js +97 -0
  462. package/dist/server/routes/intelligence.js +1217 -0
  463. package/dist/server/routes/reasoning-quality.js +444 -0
  464. package/dist/server/routes/session.js +86 -0
  465. package/dist/server/routes/stream.js +120 -0
  466. package/dist/server/routes/system.js +73 -0
  467. package/dist/server/routes/temporal.js +170 -0
  468. package/dist/server/routes/timeline.js +232 -0
  469. package/dist/server/routes/token-flow.js +403 -0
  470. package/dist/skills/effectiveness-tracker.js +93 -0
  471. package/dist/skills/local-pack.js +380 -0
  472. package/dist/skills/resolver.js +495 -0
  473. package/dist/state-detector.js +83 -0
  474. package/dist/timeline/intent-detector.js +263 -0
  475. package/dist/timeline/loop-miner.js +140 -0
  476. package/dist/timeline/open-threads.js +49 -0
  477. package/dist/timeline/signal-reinforcer.js +62 -0
  478. package/dist/timeline/timeline-bootstrap.js +151 -0
  479. package/dist/timeline/timeline-store.js +618 -0
  480. package/dist/tools/coding/bash.js +49 -0
  481. package/dist/tools/coding/file-edit.js +72 -0
  482. package/dist/tools/coding/file-outline.js +227 -0
  483. package/dist/tools/coding/file-read-protocol.js +425 -0
  484. package/dist/tools/coding/file-read.js +35 -0
  485. package/dist/tools/coding/file-write.js +43 -0
  486. package/dist/tools/coding/glob-tool.js +109 -0
  487. package/dist/tools/coding/grep.js +162 -0
  488. package/dist/tools/coding/index.js +27 -0
  489. package/dist/tools/intelligence/index.js +269 -0
  490. package/dist/tools/intelligence/record-fact.js +48 -0
  491. package/dist/tools/intelligence/timeline-markers.js +130 -0
  492. package/dist/tools/registry.js +47 -0
  493. package/dist/tools/types.js +8 -0
  494. package/dist/tracking/auto-snapshot-triggers.js +246 -0
  495. package/dist/tracking/branch-context.js +115 -0
  496. package/dist/tracking/branch-snapshot.js +217 -0
  497. package/dist/tracking/causal-bridge.js +317 -0
  498. package/dist/tracking/circuit-breaker.js +147 -0
  499. package/dist/tracking/commit-watcher.js +114 -0
  500. package/dist/tracking/context-ledger.js +119 -0
  501. package/dist/tracking/correction-detector.js +324 -0
  502. package/dist/tracking/drift-tracker.js +874 -0
  503. package/dist/tracking/durability-tracker.js +94 -0
  504. package/dist/tracking/entity-rewind.js +200 -0
  505. package/dist/tracking/file-hash-state.js +114 -0
  506. package/dist/tracking/git-attribution.js +132 -0
  507. package/dist/tracking/git-trailers.js +171 -0
  508. package/dist/tracking/intelligence-counter.js +46 -0
  509. package/dist/tracking/intent-correlator.js +202 -0
  510. package/dist/tracking/intent-encoder.js +52 -0
  511. package/dist/tracking/intent-token-tracker.js +159 -0
  512. package/dist/tracking/ledger-archiver.js +94 -0
  513. package/dist/tracking/ledger-chains.js +245 -0
  514. package/dist/tracking/metrics-store.js +361 -0
  515. package/dist/tracking/native-watcher.js +131 -0
  516. package/dist/tracking/offline-rewind.js +295 -0
  517. package/dist/tracking/pending-violations.js +74 -0
  518. package/dist/tracking/persistence-effectiveness.js +167 -0
  519. package/dist/tracking/prompt-durability.js +202 -0
  520. package/dist/tracking/quality-signals.js +213 -0
  521. package/dist/tracking/redactor.js +73 -0
  522. package/dist/tracking/rewind-engine.js +161 -0
  523. package/dist/tracking/session-history.js +128 -0
  524. package/dist/tracking/session-receipt.js +88 -0
  525. package/dist/tracking/session-summary-writer.js +157 -0
  526. package/dist/tracking/shadow-ledger.js +321 -0
  527. package/dist/tracking/stash-manager.js +258 -0
  528. package/dist/tracking/timeline-fork.js +213 -0
  529. package/dist/tracking/timeline.js +69 -0
  530. package/dist/tracking/token-flow.js +276 -0
  531. package/dist/tracking/turn-segmenter.js +122 -0
  532. package/dist/tracking/weekly-accumulator.js +179 -0
  533. package/dist/tracking/working-snapshots.js +188 -0
  534. package/dist/tracking/workspace-manifest.js +176 -0
  535. package/dist/transport/http.js +102 -0
  536. package/dist/ui/assets/index-BsMTQdhX.js +10 -0
  537. package/dist/ui/index.html +1 -1
  538. package/dist/utils/counterfactual.js +65 -0
  539. package/dist/utils/deep-link.js +34 -0
  540. package/dist/utils/detect.js +193 -0
  541. package/dist/utils/exec.js +73 -0
  542. package/dist/utils/file-logger.js +87 -0
  543. package/dist/utils/format-error.js +29 -0
  544. package/dist/utils/git.js +181 -0
  545. package/dist/utils/log.js +57 -0
  546. package/dist/utils/logger.js +35 -0
  547. package/dist/utils/mcp-content-json.js +8 -0
  548. package/dist/utils/session-logger.js +154 -0
  549. package/dist/utils/startup-log.js +512 -0
  550. package/dist/utils/ui.js +56 -0
  551. package/package.json +5 -3
  552. package/scripts/postinstall.mjs +299 -0
  553. package/dist/ui/assets/index-B-0HTtUR.js +0 -10
@@ -0,0 +1,111 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ describe("Ephemeral Sandbox (P5.6-ADV-02)", () => {
6
+ let tmpDir;
7
+ beforeEach(() => {
8
+ tmpDir = path.join(os.tmpdir(), `unerr-ephemeral-${Date.now()}`);
9
+ fs.mkdirSync(tmpDir, { recursive: true });
10
+ });
11
+ afterEach(() => {
12
+ fs.rmSync(tmpDir, { recursive: true, force: true });
13
+ });
14
+ it("connect --ephemeral creates ephemeral repo config", () => {
15
+ // Simulate ephemeral repo creation: a config with ephemeral: true and a TTL
16
+ const unerrDir = path.join(tmpDir, ".unerr");
17
+ fs.mkdirSync(unerrDir, { recursive: true });
18
+ const ttlHours = 24;
19
+ const expiresAt = new Date(Date.now() + ttlHours * 60 * 60 * 1000).toISOString();
20
+ const ephemeralConfig = {
21
+ repoId: "eph-repo-123",
22
+ orgId: "org-1",
23
+ branch: "main",
24
+ ephemeral: true,
25
+ expiresAt,
26
+ };
27
+ fs.writeFileSync(path.join(unerrDir, "config.json"), `${JSON.stringify(ephemeralConfig, null, 2)}\n`);
28
+ const raw = fs.readFileSync(path.join(unerrDir, "config.json"), "utf-8");
29
+ const config = JSON.parse(raw);
30
+ expect(config.ephemeral).toBe(true);
31
+ expect(config.repoId).toBe("eph-repo-123");
32
+ expect(new Date(config.expiresAt).getTime()).toBeGreaterThan(Date.now());
33
+ });
34
+ it("promote converts ephemeral to permanent config", () => {
35
+ const unerrDir = path.join(tmpDir, ".unerr");
36
+ fs.mkdirSync(unerrDir, { recursive: true });
37
+ // Start with ephemeral config
38
+ const ephemeralConfig = {
39
+ repoId: "eph-repo-456",
40
+ orgId: "org-1",
41
+ branch: "main",
42
+ ephemeral: true,
43
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
44
+ };
45
+ const configPath = path.join(unerrDir, "config.json");
46
+ fs.writeFileSync(configPath, JSON.stringify(ephemeralConfig, null, 2));
47
+ // Simulate "promote" — remove ephemeral flag and expiry
48
+ const raw = fs.readFileSync(configPath, "utf-8");
49
+ const config = JSON.parse(raw);
50
+ config.ephemeral = undefined;
51
+ config.expiresAt = undefined;
52
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
53
+ const promoted = JSON.parse(fs.readFileSync(configPath, "utf-8"));
54
+ expect(promoted.repoId).toBe("eph-repo-456");
55
+ expect(promoted.ephemeral).toBeUndefined();
56
+ expect(promoted.expiresAt).toBeUndefined();
57
+ });
58
+ it("ephemeral repo has TTL that can be checked", () => {
59
+ const ttlHours = 2;
60
+ const expiresAt = new Date(Date.now() + ttlHours * 60 * 60 * 1000);
61
+ // Check if expired
62
+ const isExpired = expiresAt.getTime() < Date.now();
63
+ expect(isExpired).toBe(false);
64
+ // Simulate an already-expired ephemeral repo
65
+ const pastExpiry = new Date(Date.now() - 1000);
66
+ const isPastExpired = pastExpiry.getTime() < Date.now();
67
+ expect(isPastExpired).toBe(true);
68
+ });
69
+ it("ephemeral config includes all required fields", () => {
70
+ const ephemeralConfig = {
71
+ repoId: "eph-repo-789",
72
+ orgId: "org-1",
73
+ branch: "main",
74
+ ephemeral: true,
75
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
76
+ };
77
+ // Validate required fields
78
+ expect(ephemeralConfig.repoId).toBeTruthy();
79
+ expect(ephemeralConfig.orgId).toBeTruthy();
80
+ expect(ephemeralConfig.branch).toBeTruthy();
81
+ expect(ephemeralConfig.ephemeral).toBe(true);
82
+ expect(ephemeralConfig.expiresAt).toBeTruthy();
83
+ });
84
+ it("expired ephemeral repos are detectable for cleanup", () => {
85
+ // Simulate multiple ephemeral configs, some expired
86
+ const configs = [
87
+ {
88
+ repoId: "eph-1",
89
+ expiresAt: new Date(Date.now() + 1000).toISOString(),
90
+ ephemeral: true,
91
+ },
92
+ {
93
+ repoId: "eph-2",
94
+ expiresAt: new Date(Date.now() - 1000).toISOString(),
95
+ ephemeral: true,
96
+ },
97
+ {
98
+ repoId: "eph-3",
99
+ expiresAt: new Date(Date.now() - 5000).toISOString(),
100
+ ephemeral: true,
101
+ },
102
+ { repoId: "perm-1", ephemeral: false, expiresAt: undefined },
103
+ ];
104
+ const expired = configs.filter((c) => c.ephemeral &&
105
+ c.expiresAt &&
106
+ new Date(c.expiresAt).getTime() < Date.now());
107
+ expect(expired.length).toBe(2);
108
+ expect(expired.map((c) => c.repoId)).toContain("eph-2");
109
+ expect(expired.map((c) => c.repoId)).toContain("eph-3");
110
+ });
111
+ });
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { estimateExplorationCost } from "../intelligence/exploration-cost.js";
3
+ describe("exploration-cost — MCP tool alias resolution", () => {
4
+ it("get_references resolves to find_callers rule (not DEFAULT_RULE)", () => {
5
+ const aliased = estimateExplorationCost("get_references", 5);
6
+ const target = estimateExplorationCost("find_callers", 5);
7
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
8
+ expect(aliased.counterfactualMethod).toBe(target.counterfactualMethod);
9
+ });
10
+ it("search_code resolves to search_entities rule", () => {
11
+ const aliased = estimateExplorationCost("search_code", 8);
12
+ const target = estimateExplorationCost("search_entities", 8);
13
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
14
+ expect(aliased.counterfactualMethod).toBe(target.counterfactualMethod);
15
+ });
16
+ it("get_conventions resolves to show_conventions rule", () => {
17
+ const aliased = estimateExplorationCost("get_conventions", 0, 12);
18
+ const target = estimateExplorationCost("show_conventions", 0, 12);
19
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
20
+ });
21
+ it("get_critical_nodes resolves to risk_assessment rule", () => {
22
+ const aliased = estimateExplorationCost("get_critical_nodes", 4);
23
+ const target = estimateExplorationCost("risk_assessment", 4);
24
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
25
+ });
26
+ it("get_cross_boundary_links resolves to risk_assessment rule", () => {
27
+ const aliased = estimateExplorationCost("get_cross_boundary_links", 6);
28
+ const target = estimateExplorationCost("risk_assessment", 6);
29
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
30
+ });
31
+ it("file_connections resolves to risk_assessment rule", () => {
32
+ const aliased = estimateExplorationCost("file_connections", 3);
33
+ const target = estimateExplorationCost("risk_assessment", 3);
34
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
35
+ });
36
+ it("file_outline falls through to DEFAULT_RULE (returns N entities, not 1)", () => {
37
+ // Intentionally NOT aliased: get_entity assumes 1 returned entity and yields
38
+ // negative savings as resultSize grows. DEFAULT_RULE scales positively with N.
39
+ const result = estimateExplorationCost("file_outline", 15);
40
+ // DEFAULT_RULE: 150 + ceil(15/2)*400 = 150 + 8*400 = 3350
41
+ expect(result.tokensWithout).toBe(150 + 8 * 400);
42
+ expect(result.counterfactualMethod).toBe("generic file exploration");
43
+ });
44
+ it("get_imports resolves to find_callers rule", () => {
45
+ const aliased = estimateExplorationCost("get_imports", 10);
46
+ const target = estimateExplorationCost("find_callers", 10);
47
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
48
+ });
49
+ it("get_test_coverage resolves to find_callers rule", () => {
50
+ const aliased = estimateExplorationCost("get_test_coverage", 7);
51
+ const target = estimateExplorationCost("find_callers", 7);
52
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
53
+ });
54
+ it("get_file resolves to get_entity rule", () => {
55
+ const aliased = estimateExplorationCost("get_file", 1);
56
+ const target = estimateExplorationCost("get_entity", 1);
57
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
58
+ });
59
+ it("file_read falls through to DEFAULT_RULE (has its own dedicated mechanism)", () => {
60
+ // file_read is tracked via mechanism="file_read" with full-file counterfactual.
61
+ // Letting it also alias graph_query would double-count. Keep it on DEFAULT_RULE
62
+ // so graph_query events fire (when positive) without inheriting get_entity math.
63
+ const result = estimateExplorationCost("file_read", 4);
64
+ // DEFAULT_RULE: 150 + ceil(4/2)*400 = 150 + 2*400 = 950
65
+ expect(result.tokensWithout).toBe(150 + 2 * 400);
66
+ expect(result.counterfactualMethod).toBe("generic file exploration");
67
+ });
68
+ it("get_project_stats resolves to health_grade rule", () => {
69
+ const aliased = estimateExplorationCost("get_project_stats", 0, 50);
70
+ const target = estimateExplorationCost("health_grade", 0, 50);
71
+ expect(aliased.tokensWithout).toBe(target.tokensWithout);
72
+ });
73
+ it("legacy keys still resolve directly (not regressed by alias layer)", () => {
74
+ // blast_radius and find_callers are still used internally — must remain stable
75
+ const blast = estimateExplorationCost("blast_radius", 4);
76
+ expect(blast.counterfactualMethod).toContain("read each caller file");
77
+ const callers = estimateExplorationCost("find_callers", 4);
78
+ expect(callers.counterfactualMethod).toContain("grep + read");
79
+ });
80
+ it("unknown tool name still falls through to DEFAULT_RULE", () => {
81
+ const result = estimateExplorationCost("totally_unknown_tool", 4);
82
+ // DEFAULT_RULE: fileMultiplier = ceil(resultSize / 2) = 2, tokensPerFile = 400, baseTokens = 150
83
+ expect(result.tokensWithout).toBe(150 + 2 * 400);
84
+ expect(result.counterfactualMethod).toBe("generic file exploration");
85
+ });
86
+ it("aliased tools beat DEFAULT_RULE on at least one shape", () => {
87
+ // search_code with 10 results: search_entities gives 200 + min(10,20)*500 = 5200
88
+ // DEFAULT_RULE would give 150 + ceil(10/2)*400 = 2150
89
+ // Confirms the alias is actually changing the calculation
90
+ const aliased = estimateExplorationCost("search_code", 10);
91
+ expect(aliased.tokensWithout).toBe(200 + 10 * 500);
92
+ });
93
+ });
@@ -0,0 +1,197 @@
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { generateFromCausalBridge, generateFromConventions, generateFromNegativeKnowledge, generateFromSessionAnalysis, } from "../intelligence/fact-generator.js";
6
+ import { initFactsSchema } from "../intelligence/facts-schema.js";
7
+ import { TemporalFactStore } from "../intelligence/temporal-facts.js";
8
+ async function createTestDb() {
9
+ const cozoModule = await import("cozo-node");
10
+ const CozoDbConstructor = cozoModule.default
11
+ ? cozoModule.default.CozoDb
12
+ : cozoModule.CozoDb;
13
+ return new CozoDbConstructor("mem", "");
14
+ }
15
+ function makeSessionRecord(overrides = {}) {
16
+ return {
17
+ session_id: `sess-${Math.random().toString(36).slice(2, 8)}`,
18
+ written_at: new Date().toISOString(),
19
+ started_at: new Date(Date.now() - 3600000).toISOString(),
20
+ ended_at: new Date().toISOString(),
21
+ duration_ms: 3600000,
22
+ tool_calls: 20,
23
+ chains: 5,
24
+ files_modified: ["src/proxy.ts", "src/auth.ts"],
25
+ entities_touched: ["src/proxy.ts::startProxy"],
26
+ tools_used: { get_function: 10, file_read: 10 },
27
+ feature_areas: ["src/proxy"],
28
+ facts_recorded: 0,
29
+ facts_surfaced: [],
30
+ revert_count: 0,
31
+ rot_score: 0.1,
32
+ token_estimate: 10000,
33
+ branch: "main",
34
+ ...overrides,
35
+ };
36
+ }
37
+ describe("fact-generator", () => {
38
+ let db;
39
+ let store;
40
+ let testDir;
41
+ beforeEach(async () => {
42
+ db = await createTestDb();
43
+ await initFactsSchema(db);
44
+ store = TemporalFactStore.fromDb(db);
45
+ testDir = join(tmpdir(), `unerr-factgen-${Date.now()}`);
46
+ mkdirSync(testDir, { recursive: true });
47
+ });
48
+ afterEach(() => {
49
+ try {
50
+ rmSync(testDir, { recursive: true, force: true });
51
+ }
52
+ catch {
53
+ // best-effort
54
+ }
55
+ });
56
+ describe("generateFromConventions", () => {
57
+ it("creates facts for conventions with >70% adherence", async () => {
58
+ const conventions = [
59
+ {
60
+ key: "conv-1",
61
+ kind: "naming",
62
+ name: "camelCase functions",
63
+ detail: "^[a-z][a-zA-Z]*$",
64
+ exemplarKeys: ["src/proxy/startProxy.ts"],
65
+ frequency: 20,
66
+ confidence: 0.85,
67
+ },
68
+ ];
69
+ const result = await generateFromConventions(store, conventions);
70
+ expect(result.created).toBe(1);
71
+ expect(result.source).toBe("convention_detector");
72
+ expect(result.details[0]).toContain("camelCase functions");
73
+ });
74
+ it("skips conventions below 70% adherence", async () => {
75
+ const conventions = [
76
+ {
77
+ key: "conv-low",
78
+ kind: "naming",
79
+ name: "snake_case",
80
+ detail: ".*",
81
+ exemplarKeys: [],
82
+ frequency: 10,
83
+ confidence: 0.5,
84
+ },
85
+ ];
86
+ const result = await generateFromConventions(store, conventions);
87
+ expect(result.created).toBe(0);
88
+ });
89
+ it("skips conventions with too few entities", async () => {
90
+ const conventions = [
91
+ {
92
+ key: "conv-few",
93
+ kind: "structure",
94
+ name: "rare pattern",
95
+ detail: ".*",
96
+ exemplarKeys: [],
97
+ frequency: 2,
98
+ confidence: 0.95,
99
+ },
100
+ ];
101
+ const result = await generateFromConventions(store, conventions);
102
+ expect(result.created).toBe(0);
103
+ });
104
+ });
105
+ describe("generateFromNegativeKnowledge", () => {
106
+ it("creates negative facts from correction entries", async () => {
107
+ const corrections = [
108
+ {
109
+ id: "corr-1",
110
+ entityKey: "src/auth.ts",
111
+ pattern: "modified-then-reverted",
112
+ reason: "File src/auth.ts was modified and reverted. The approach was incorrect.",
113
+ detectedAt: new Date().toISOString(),
114
+ rewindEntryId: "rewind-001",
115
+ confidence: 0.7,
116
+ },
117
+ ];
118
+ const result = await generateFromNegativeKnowledge(store, corrections);
119
+ expect(result.created).toBe(1);
120
+ expect(result.source).toBe("negative_knowledge");
121
+ const recalled = await store.recallByScope("src/auth.ts", 0);
122
+ expect(recalled.length).toBe(1);
123
+ expect(recalled[0].fact_type).toBe("negative");
124
+ });
125
+ });
126
+ describe("generateFromCausalBridge", () => {
127
+ it("creates episodic facts for survived changes", async () => {
128
+ const events = [
129
+ {
130
+ session_id: "sess-1",
131
+ entity_key: "src/proxy.ts::startProxy",
132
+ action: "survived",
133
+ branch: "main",
134
+ timestamp: Date.now() - 86400000,
135
+ },
136
+ ];
137
+ const result = await generateFromCausalBridge(store, events);
138
+ expect(result.created).toBe(1);
139
+ expect(result.details[0]).toContain("survived");
140
+ });
141
+ it("creates negative facts for reverted changes", async () => {
142
+ const events = [
143
+ {
144
+ session_id: "sess-2",
145
+ entity_key: "src/config.ts",
146
+ action: "reverted",
147
+ branch: "main",
148
+ timestamp: Date.now() - 86400000,
149
+ },
150
+ ];
151
+ const result = await generateFromCausalBridge(store, events);
152
+ expect(result.created).toBe(1);
153
+ expect(result.details[0]).toContain("reverted");
154
+ const recalled = await store.recallByScope("src/config.ts", 0);
155
+ expect(recalled[0].fact_type).toBe("negative");
156
+ });
157
+ });
158
+ describe("generateFromSessionAnalysis", () => {
159
+ it("detects hot files from multiple sessions", async () => {
160
+ const sessionsDir = join(testDir, "sessions");
161
+ mkdirSync(sessionsDir, { recursive: true });
162
+ for (let i = 0; i < 5; i++) {
163
+ const record = makeSessionRecord({
164
+ session_id: `s${i}`,
165
+ files_modified: ["src/proxy.ts", "src/auth.ts", `src/other${i}.ts`],
166
+ });
167
+ writeFileSync(join(sessionsDir, `s${i}.jsonl`), `${JSON.stringify(record)}\n`, "utf-8");
168
+ }
169
+ const result = await generateFromSessionAnalysis(store, testDir);
170
+ expect(result.created).toBeGreaterThan(0);
171
+ expect(result.details.some((d) => d.includes("src/proxy.ts"))).toBe(true);
172
+ });
173
+ it("detects high-revert files", async () => {
174
+ const sessionsDir = join(testDir, "sessions");
175
+ mkdirSync(sessionsDir, { recursive: true });
176
+ for (let i = 0; i < 5; i++) {
177
+ const record = makeSessionRecord({
178
+ session_id: `s${i}`,
179
+ files_modified: ["src/fragile.ts"],
180
+ revert_count: i < 3 ? 1 : 0, // 3 out of 5 sessions have reverts
181
+ });
182
+ writeFileSync(join(sessionsDir, `s${i}.jsonl`), `${JSON.stringify(record)}\n`, "utf-8");
183
+ }
184
+ const result = await generateFromSessionAnalysis(store, testDir);
185
+ const fragileDetail = result.details.find((d) => d.includes("src/fragile.ts"));
186
+ expect(fragileDetail).toBeDefined();
187
+ });
188
+ it("returns empty when fewer than 3 sessions", async () => {
189
+ const sessionsDir = join(testDir, "sessions");
190
+ mkdirSync(sessionsDir, { recursive: true });
191
+ const record = makeSessionRecord();
192
+ writeFileSync(join(sessionsDir, "s1.jsonl"), `${JSON.stringify(record)}\n`, "utf-8");
193
+ const result = await generateFromSessionAnalysis(store, testDir);
194
+ expect(result.created).toBe(0);
195
+ });
196
+ });
197
+ });
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Sprint R.10: File-as-L0 Graph Enrichment Tests
3
+ *
4
+ * Verifies:
5
+ * - File entities created for every indexed file
6
+ * - Contains edges from file→entity guarantee zero orphans
7
+ * - File→file import edges created from cross-file resolution
8
+ * - Co-change edge computation from git history
9
+ * - Community detection handles weighted contains edges
10
+ * - Blast radius falls back to file siblings when no callers exist
11
+ * - File-level queries (getFileEntities, getFileNeighbors) work correctly
12
+ */
13
+ import { describe, expect, it } from "vitest";
14
+ import { resolveCrossFileEdges } from "../intelligence/indexer/cross-file-resolver.js";
15
+ import { computeCoChangeEdges } from "../intelligence/indexer/git-cochange.js";
16
+ import { resolveImportSourceToFile } from "../intelligence/local-indexer.js";
17
+ describe("Sprint R: File-as-L0 Graph Enrichment", () => {
18
+ describe("R.3: Cross-file resolver emits file import edges", () => {
19
+ it("produces file→file import edges alongside entity resolution", () => {
20
+ const fileResults = new Map([
21
+ [
22
+ "src/a.ts",
23
+ {
24
+ entities: [
25
+ {
26
+ key: "src/a.ts::fnA",
27
+ name: "fnA",
28
+ exported: true,
29
+ kind: "function",
30
+ file_path: "src/a.ts",
31
+ },
32
+ ],
33
+ edges: [
34
+ {
35
+ from_key: "src/a.ts::fnA",
36
+ to_key: "unresolved:fnB",
37
+ type: "calls",
38
+ file_path: "src/a.ts",
39
+ line: 5,
40
+ },
41
+ ],
42
+ imports: [
43
+ {
44
+ source: "src/b.ts",
45
+ symbols: ["fnB"],
46
+ isDefault: false,
47
+ isNamespace: false,
48
+ localName: undefined,
49
+ line: 1,
50
+ },
51
+ ],
52
+ },
53
+ ],
54
+ [
55
+ "src/b.ts",
56
+ {
57
+ entities: [
58
+ {
59
+ key: "src/b.ts::fnB",
60
+ name: "fnB",
61
+ exported: true,
62
+ kind: "function",
63
+ file_path: "src/b.ts",
64
+ },
65
+ ],
66
+ edges: [],
67
+ imports: [],
68
+ },
69
+ ],
70
+ ]);
71
+ const result = resolveCrossFileEdges(fileResults);
72
+ // Should have file→file import edges
73
+ expect(result.fileImportEdges.length).toBeGreaterThan(0);
74
+ const fileEdge = result.fileImportEdges.find((e) => e.from_key === "file:src/a.ts" && e.to_key === "file:src/b.ts");
75
+ expect(fileEdge).toBeDefined();
76
+ expect(fileEdge.type).toBe("imports");
77
+ });
78
+ it("deduplicates file import edges (multiple imports from same file)", () => {
79
+ const fileResults = new Map([
80
+ [
81
+ "src/a.ts",
82
+ {
83
+ entities: [
84
+ {
85
+ key: "src/a.ts::fnA",
86
+ name: "fnA",
87
+ exported: true,
88
+ kind: "function",
89
+ file_path: "src/a.ts",
90
+ },
91
+ ],
92
+ edges: [],
93
+ imports: [
94
+ {
95
+ source: "src/b.ts",
96
+ symbols: ["fnB"],
97
+ isDefault: false,
98
+ isNamespace: false,
99
+ localName: undefined,
100
+ line: 1,
101
+ },
102
+ {
103
+ source: "src/b.ts",
104
+ symbols: ["fnC"],
105
+ isDefault: false,
106
+ isNamespace: false,
107
+ localName: undefined,
108
+ line: 1,
109
+ },
110
+ ],
111
+ },
112
+ ],
113
+ [
114
+ "src/b.ts",
115
+ {
116
+ entities: [
117
+ {
118
+ key: "src/b.ts::fnB",
119
+ name: "fnB",
120
+ exported: true,
121
+ kind: "function",
122
+ file_path: "src/b.ts",
123
+ },
124
+ {
125
+ key: "src/b.ts::fnC",
126
+ name: "fnC",
127
+ exported: true,
128
+ kind: "function",
129
+ file_path: "src/b.ts",
130
+ },
131
+ ],
132
+ edges: [],
133
+ imports: [],
134
+ },
135
+ ],
136
+ ]);
137
+ const result = resolveCrossFileEdges(fileResults);
138
+ // Only one file→file edge despite two import declarations
139
+ const fileEdges = result.fileImportEdges.filter((e) => e.from_key === "file:src/a.ts" && e.to_key === "file:src/b.ts");
140
+ expect(fileEdges).toHaveLength(1);
141
+ });
142
+ });
143
+ describe("R.4: Git co-change computation", () => {
144
+ it("computes co-change edges from the current repo", () => {
145
+ // This test runs against the actual unerr-cli repo
146
+ const edges = computeCoChangeEdges(process.cwd(), 50, 10, 2);
147
+ // Should find at least some co-change pairs in a real repo
148
+ expect(edges.length).toBeGreaterThanOrEqual(0);
149
+ // If there are results, verify structure
150
+ if (edges.length > 0) {
151
+ const first = edges[0];
152
+ expect(first.from_file).toBeTruthy();
153
+ expect(first.to_file).toBeTruthy();
154
+ expect(first.co_occurrences).toBeGreaterThanOrEqual(2);
155
+ expect(first.correlation).toBeGreaterThan(0);
156
+ expect(first.correlation).toBeLessThanOrEqual(1);
157
+ }
158
+ });
159
+ it("returns empty for non-git directory", () => {
160
+ const edges = computeCoChangeEdges("/tmp", 50, 10, 2);
161
+ expect(edges).toHaveLength(0);
162
+ });
163
+ });
164
+ describe("R.1/R.2: File entity and contains edge verification (integration)", () => {
165
+ it("ingestIndexResult creates file entities for each unique file path", async () => {
166
+ // This is a unit-level check of the ingest function's contract
167
+ // Full integration test requires CozoDB instance
168
+ const { basename } = await import("node:path");
169
+ // Verify the file key convention
170
+ const filePath = "src/utils/exec.ts";
171
+ const fileKey = `file:${filePath}`;
172
+ expect(fileKey).toBe("file:src/utils/exec.ts");
173
+ expect(basename(filePath)).toBe("exec.ts");
174
+ });
175
+ it("file entity keys use file: prefix to avoid collisions", () => {
176
+ // Entity keys use filepath::name format (e.g., "src/a.ts::fnA")
177
+ // File keys use file:filepath format (e.g., "file:src/a.ts")
178
+ const entityKey = "src/a.ts::fnA";
179
+ const fileKey = "file:src/a.ts";
180
+ // They must not collide
181
+ expect(entityKey).not.toBe(fileKey);
182
+ expect(fileKey.startsWith("file:")).toBe(true);
183
+ expect(entityKey.startsWith("file:")).toBe(false);
184
+ });
185
+ });
186
+ describe("R.3 Multi-language: resolveImportSourceToFile", () => {
187
+ const projectFiles = new Set([
188
+ "src/utils/helper.ts",
189
+ "src/utils/index.ts",
190
+ "src/commands/status.ts",
191
+ "app/models/user.py",
192
+ "app/models/__init__.py",
193
+ "app/services/auth.py",
194
+ "pkg/handlers/auth.go",
195
+ "pkg/models/user.go",
196
+ "internal/db/connection.go",
197
+ "com/example/service/UserService.java",
198
+ "com/example/models/User.java",
199
+ "src/module/sub.rs",
200
+ "src/module/mod.rs",
201
+ "src/lib.rs",
202
+ "lib/models/user.rb",
203
+ "lib/services/auth.rb",
204
+ "Services/UserService.cs",
205
+ "Models/User.cs",
206
+ ]);
207
+ it("resolves TS/JS relative imports", () => {
208
+ expect(resolveImportSourceToFile("./helper", "src/utils/status.ts", projectFiles)).toBe("src/utils/helper.ts");
209
+ expect(resolveImportSourceToFile("../commands/status", "src/utils/helper.ts", projectFiles)).toBe("src/commands/status.ts");
210
+ // Index file resolution (../utils from commands/ → src/utils/index.ts)
211
+ expect(resolveImportSourceToFile("../utils", "src/commands/status.ts", projectFiles)).toBe("src/utils/index.ts");
212
+ });
213
+ it("resolves Python dotted module paths", () => {
214
+ expect(resolveImportSourceToFile("models.user", "app/services/auth.py", projectFiles)).toBe("app/models/user.py");
215
+ // __init__.py as directory index
216
+ expect(resolveImportSourceToFile("models", "app/services/auth.py", projectFiles)).toBe("app/models/__init__.py");
217
+ });
218
+ it("resolves Go package paths by suffix matching", () => {
219
+ expect(resolveImportSourceToFile("github.com/myapp/pkg/handlers", "cmd/main.go", projectFiles)).toBe("pkg/handlers/auth.go");
220
+ });
221
+ it("resolves Java dot-separated package paths", () => {
222
+ expect(resolveImportSourceToFile("com.example.models.User", "com/example/service/UserService.java", projectFiles)).toBe("com/example/models/User.java");
223
+ });
224
+ it("resolves Rust crate paths", () => {
225
+ expect(resolveImportSourceToFile("crate::module::sub", "src/lib.rs", projectFiles)).toBe("src/module/sub.rs");
226
+ // Rust mod.rs as directory index
227
+ expect(resolveImportSourceToFile("crate::module", "src/lib.rs", projectFiles)).toBe("src/module/mod.rs");
228
+ });
229
+ it("resolves Ruby require paths", () => {
230
+ expect(resolveImportSourceToFile("models/user", "lib/services/auth.rb", projectFiles)).toBe("lib/models/user.rb");
231
+ });
232
+ it("resolves C# namespace paths", () => {
233
+ expect(resolveImportSourceToFile("Models.User", "Services/UserService.cs", projectFiles)).toBe("Models/User.cs");
234
+ });
235
+ it("returns null for external/unresolvable imports", () => {
236
+ // npm package
237
+ expect(resolveImportSourceToFile("lodash", "src/utils/helper.ts", projectFiles)).toBeNull();
238
+ // Python stdlib
239
+ expect(resolveImportSourceToFile("os.path", "app/services/auth.py", projectFiles)).toBeNull();
240
+ // Go stdlib
241
+ expect(resolveImportSourceToFile("fmt", "pkg/handlers/auth.go", projectFiles)).toBeNull();
242
+ });
243
+ });
244
+ });