opencode-telos 1.0.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 (568) hide show
  1. package/README.md +927 -0
  2. package/dist/code-intelligence/analyzer.d.ts +9 -0
  3. package/dist/code-intelligence/analyzer.d.ts.map +1 -0
  4. package/dist/code-intelligence/analyzer.js +321 -0
  5. package/dist/code-intelligence/analyzer.js.map +1 -0
  6. package/dist/code-intelligence/ast/cache.d.ts +16 -0
  7. package/dist/code-intelligence/ast/cache.d.ts.map +1 -0
  8. package/dist/code-intelligence/ast/cache.js +44 -0
  9. package/dist/code-intelligence/ast/cache.js.map +1 -0
  10. package/dist/code-intelligence/ast/common.d.ts +8 -0
  11. package/dist/code-intelligence/ast/common.d.ts.map +1 -0
  12. package/dist/code-intelligence/ast/common.js +42 -0
  13. package/dist/code-intelligence/ast/common.js.map +1 -0
  14. package/dist/code-intelligence/ast/component.d.ts +10 -0
  15. package/dist/code-intelligence/ast/component.d.ts.map +1 -0
  16. package/dist/code-intelligence/ast/component.js +38 -0
  17. package/dist/code-intelligence/ast/component.js.map +1 -0
  18. package/dist/code-intelligence/ast/fallback.d.ts +9 -0
  19. package/dist/code-intelligence/ast/fallback.d.ts.map +1 -0
  20. package/dist/code-intelligence/ast/fallback.js +38 -0
  21. package/dist/code-intelligence/ast/fallback.js.map +1 -0
  22. package/dist/code-intelligence/ast/index.d.ts +5 -0
  23. package/dist/code-intelligence/ast/index.d.ts.map +1 -0
  24. package/dist/code-intelligence/ast/index.js +5 -0
  25. package/dist/code-intelligence/ast/index.js.map +1 -0
  26. package/dist/code-intelligence/ast/ir.d.ts +72 -0
  27. package/dist/code-intelligence/ast/ir.d.ts.map +1 -0
  28. package/dist/code-intelligence/ast/ir.js +2 -0
  29. package/dist/code-intelligence/ast/ir.js.map +1 -0
  30. package/dist/code-intelligence/ast/metrics.d.ts +25 -0
  31. package/dist/code-intelligence/ast/metrics.d.ts.map +1 -0
  32. package/dist/code-intelligence/ast/metrics.js +38 -0
  33. package/dist/code-intelligence/ast/metrics.js.map +1 -0
  34. package/dist/code-intelligence/ast/registry.d.ts +5 -0
  35. package/dist/code-intelligence/ast/registry.d.ts.map +1 -0
  36. package/dist/code-intelligence/ast/registry.js +22 -0
  37. package/dist/code-intelligence/ast/registry.js.map +1 -0
  38. package/dist/code-intelligence/ast/tree-sitter.d.ts +8 -0
  39. package/dist/code-intelligence/ast/tree-sitter.d.ts.map +1 -0
  40. package/dist/code-intelligence/ast/tree-sitter.js +247 -0
  41. package/dist/code-intelligence/ast/tree-sitter.js.map +1 -0
  42. package/dist/code-intelligence/ast/typescript.d.ts +8 -0
  43. package/dist/code-intelligence/ast/typescript.d.ts.map +1 -0
  44. package/dist/code-intelligence/ast/typescript.js +147 -0
  45. package/dist/code-intelligence/ast/typescript.js.map +1 -0
  46. package/dist/index.d.ts +10 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +18 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/mcp/server.d.ts +27 -0
  51. package/dist/mcp/server.d.ts.map +1 -0
  52. package/dist/mcp/server.js +67 -0
  53. package/dist/mcp/server.js.map +1 -0
  54. package/dist/opencode/hooks.d.ts +9 -0
  55. package/dist/opencode/hooks.d.ts.map +1 -0
  56. package/dist/opencode/hooks.js +583 -0
  57. package/dist/opencode/hooks.js.map +1 -0
  58. package/dist/opencode/router/categories.d.ts +31 -0
  59. package/dist/opencode/router/categories.d.ts.map +1 -0
  60. package/dist/opencode/router/categories.js +165 -0
  61. package/dist/opencode/router/categories.js.map +1 -0
  62. package/dist/opencode/router/embeddings.d.ts +41 -0
  63. package/dist/opencode/router/embeddings.d.ts.map +1 -0
  64. package/dist/opencode/router/embeddings.js +136 -0
  65. package/dist/opencode/router/embeddings.js.map +1 -0
  66. package/dist/opencode/router/graph-state-snapshot.d.ts +35 -0
  67. package/dist/opencode/router/graph-state-snapshot.d.ts.map +1 -0
  68. package/dist/opencode/router/graph-state-snapshot.js +129 -0
  69. package/dist/opencode/router/graph-state-snapshot.js.map +1 -0
  70. package/dist/opencode/router/index.d.ts +21 -0
  71. package/dist/opencode/router/index.d.ts.map +1 -0
  72. package/dist/opencode/router/index.js +23 -0
  73. package/dist/opencode/router/index.js.map +1 -0
  74. package/dist/opencode/router/intent-classifier.d.ts +35 -0
  75. package/dist/opencode/router/intent-classifier.d.ts.map +1 -0
  76. package/dist/opencode/router/intent-classifier.js +108 -0
  77. package/dist/opencode/router/intent-classifier.js.map +1 -0
  78. package/dist/opencode/router/semantic-nudge.d.ts +42 -0
  79. package/dist/opencode/router/semantic-nudge.d.ts.map +1 -0
  80. package/dist/opencode/router/semantic-nudge.js +73 -0
  81. package/dist/opencode/router/semantic-nudge.js.map +1 -0
  82. package/dist/opencode/router/state-gate.d.ts +25 -0
  83. package/dist/opencode/router/state-gate.d.ts.map +1 -0
  84. package/dist/opencode/router/state-gate.js +178 -0
  85. package/dist/opencode/router/state-gate.js.map +1 -0
  86. package/dist/opencode/router/tool-embeddings.d.ts +36 -0
  87. package/dist/opencode/router/tool-embeddings.d.ts.map +1 -0
  88. package/dist/opencode/router/tool-embeddings.js +140 -0
  89. package/dist/opencode/router/tool-embeddings.js.map +1 -0
  90. package/dist/opencode/router/tool-registry.d.ts +45 -0
  91. package/dist/opencode/router/tool-registry.d.ts.map +1 -0
  92. package/dist/opencode/router/tool-registry.js +103 -0
  93. package/dist/opencode/router/tool-registry.js.map +1 -0
  94. package/dist/opencode/router/tool-taxonomy.d.ts +51 -0
  95. package/dist/opencode/router/tool-taxonomy.d.ts.map +1 -0
  96. package/dist/opencode/router/tool-taxonomy.js +250 -0
  97. package/dist/opencode/router/tool-taxonomy.js.map +1 -0
  98. package/dist/opencode/router/tools-composite.d.ts +21 -0
  99. package/dist/opencode/router/tools-composite.d.ts.map +1 -0
  100. package/dist/opencode/router/tools-composite.js +684 -0
  101. package/dist/opencode/router/tools-composite.js.map +1 -0
  102. package/dist/opencode/shell-hooks.d.ts +7 -0
  103. package/dist/opencode/shell-hooks.d.ts.map +1 -0
  104. package/dist/opencode/shell-hooks.js +96 -0
  105. package/dist/opencode/shell-hooks.js.map +1 -0
  106. package/dist/opencode/system-prompt.d.ts +10 -0
  107. package/dist/opencode/system-prompt.d.ts.map +1 -0
  108. package/dist/opencode/system-prompt.js +600 -0
  109. package/dist/opencode/system-prompt.js.map +1 -0
  110. package/dist/opencode/tools.d.ts +8 -0
  111. package/dist/opencode/tools.d.ts.map +1 -0
  112. package/dist/opencode/tools.js +3731 -0
  113. package/dist/opencode/tools.js.map +1 -0
  114. package/dist/opencode/workflows/chains.d.ts +52 -0
  115. package/dist/opencode/workflows/chains.d.ts.map +1 -0
  116. package/dist/opencode/workflows/chains.js +170 -0
  117. package/dist/opencode/workflows/chains.js.map +1 -0
  118. package/dist/opencode/workflows/executor.d.ts +52 -0
  119. package/dist/opencode/workflows/executor.d.ts.map +1 -0
  120. package/dist/opencode/workflows/executor.js +130 -0
  121. package/dist/opencode/workflows/executor.js.map +1 -0
  122. package/dist/opencode/workflows/index.d.ts +14 -0
  123. package/dist/opencode/workflows/index.d.ts.map +1 -0
  124. package/dist/opencode/workflows/index.js +15 -0
  125. package/dist/opencode/workflows/index.js.map +1 -0
  126. package/dist/opencode/workflows/tools-workflow.d.ts +24 -0
  127. package/dist/opencode/workflows/tools-workflow.d.ts.map +1 -0
  128. package/dist/opencode/workflows/tools-workflow.js +85 -0
  129. package/dist/opencode/workflows/tools-workflow.js.map +1 -0
  130. package/dist/opencode/workflows/types.d.ts +32 -0
  131. package/dist/opencode/workflows/types.d.ts.map +1 -0
  132. package/dist/opencode/workflows/types.js +13 -0
  133. package/dist/opencode/workflows/types.js.map +1 -0
  134. package/dist/sdd/analysis/compliance.d.ts +30 -0
  135. package/dist/sdd/analysis/compliance.d.ts.map +1 -0
  136. package/dist/sdd/analysis/compliance.js +251 -0
  137. package/dist/sdd/analysis/compliance.js.map +1 -0
  138. package/dist/sdd/analysis/scalability.d.ts +15 -0
  139. package/dist/sdd/analysis/scalability.d.ts.map +1 -0
  140. package/dist/sdd/analysis/scalability.js +72 -0
  141. package/dist/sdd/analysis/scalability.js.map +1 -0
  142. package/dist/sdd/analysis/security.d.ts +25 -0
  143. package/dist/sdd/analysis/security.d.ts.map +1 -0
  144. package/dist/sdd/analysis/security.js +94 -0
  145. package/dist/sdd/analysis/security.js.map +1 -0
  146. package/dist/sdd/brownfield/scanner.d.ts +31 -0
  147. package/dist/sdd/brownfield/scanner.d.ts.map +1 -0
  148. package/dist/sdd/brownfield/scanner.js +228 -0
  149. package/dist/sdd/brownfield/scanner.js.map +1 -0
  150. package/dist/sdd/cache/manager.d.ts +237 -0
  151. package/dist/sdd/cache/manager.d.ts.map +1 -0
  152. package/dist/sdd/cache/manager.js +770 -0
  153. package/dist/sdd/cache/manager.js.map +1 -0
  154. package/dist/sdd/cache/snapshot-store.d.ts +39 -0
  155. package/dist/sdd/cache/snapshot-store.d.ts.map +1 -0
  156. package/dist/sdd/cache/snapshot-store.js +156 -0
  157. package/dist/sdd/cache/snapshot-store.js.map +1 -0
  158. package/dist/sdd/changes/manager.d.ts +58 -0
  159. package/dist/sdd/changes/manager.d.ts.map +1 -0
  160. package/dist/sdd/changes/manager.js +235 -0
  161. package/dist/sdd/changes/manager.js.map +1 -0
  162. package/dist/sdd/cicd/generators.d.ts +16 -0
  163. package/dist/sdd/cicd/generators.d.ts.map +1 -0
  164. package/dist/sdd/cicd/generators.js +1042 -0
  165. package/dist/sdd/cicd/generators.js.map +1 -0
  166. package/dist/sdd/code-quality/complexity.d.ts +38 -0
  167. package/dist/sdd/code-quality/complexity.d.ts.map +1 -0
  168. package/dist/sdd/code-quality/complexity.js +161 -0
  169. package/dist/sdd/code-quality/complexity.js.map +1 -0
  170. package/dist/sdd/code-quality/conventions.d.ts +33 -0
  171. package/dist/sdd/code-quality/conventions.d.ts.map +1 -0
  172. package/dist/sdd/code-quality/conventions.js +193 -0
  173. package/dist/sdd/code-quality/conventions.js.map +1 -0
  174. package/dist/sdd/code-quality/dependencies.d.ts +27 -0
  175. package/dist/sdd/code-quality/dependencies.d.ts.map +1 -0
  176. package/dist/sdd/code-quality/dependencies.js +142 -0
  177. package/dist/sdd/code-quality/dependencies.js.map +1 -0
  178. package/dist/sdd/code-quality/import-analyzer.d.ts +49 -0
  179. package/dist/sdd/code-quality/import-analyzer.d.ts.map +1 -0
  180. package/dist/sdd/code-quality/import-analyzer.js +213 -0
  181. package/dist/sdd/code-quality/import-analyzer.js.map +1 -0
  182. package/dist/sdd/code-quality/metrics.d.ts +35 -0
  183. package/dist/sdd/code-quality/metrics.d.ts.map +1 -0
  184. package/dist/sdd/code-quality/metrics.js +172 -0
  185. package/dist/sdd/code-quality/metrics.js.map +1 -0
  186. package/dist/sdd/code-quality/smells.d.ts +38 -0
  187. package/dist/sdd/code-quality/smells.d.ts.map +1 -0
  188. package/dist/sdd/code-quality/smells.js +319 -0
  189. package/dist/sdd/code-quality/smells.js.map +1 -0
  190. package/dist/sdd/code-quality/symbol-parser.d.ts +28 -0
  191. package/dist/sdd/code-quality/symbol-parser.d.ts.map +1 -0
  192. package/dist/sdd/code-quality/symbol-parser.js +230 -0
  193. package/dist/sdd/code-quality/symbol-parser.js.map +1 -0
  194. package/dist/sdd/code-quality/usage-tracker.d.ts +59 -0
  195. package/dist/sdd/code-quality/usage-tracker.d.ts.map +1 -0
  196. package/dist/sdd/code-quality/usage-tracker.js +402 -0
  197. package/dist/sdd/code-quality/usage-tracker.js.map +1 -0
  198. package/dist/sdd/code-quality/utils.d.ts +6 -0
  199. package/dist/sdd/code-quality/utils.d.ts.map +1 -0
  200. package/dist/sdd/code-quality/utils.js +14 -0
  201. package/dist/sdd/code-quality/utils.js.map +1 -0
  202. package/dist/sdd/codegen/generator.d.ts +42 -0
  203. package/dist/sdd/codegen/generator.d.ts.map +1 -0
  204. package/dist/sdd/codegen/generator.js +1023 -0
  205. package/dist/sdd/codegen/generator.js.map +1 -0
  206. package/dist/sdd/constitution/validator.d.ts +37 -0
  207. package/dist/sdd/constitution/validator.d.ts.map +1 -0
  208. package/dist/sdd/constitution/validator.js +192 -0
  209. package/dist/sdd/constitution/validator.js.map +1 -0
  210. package/dist/sdd/cost/estimator.d.ts +31 -0
  211. package/dist/sdd/cost/estimator.d.ts.map +1 -0
  212. package/dist/sdd/cost/estimator.js +92 -0
  213. package/dist/sdd/cost/estimator.js.map +1 -0
  214. package/dist/sdd/coverage/tracker.d.ts +47 -0
  215. package/dist/sdd/coverage/tracker.d.ts.map +1 -0
  216. package/dist/sdd/coverage/tracker.js +251 -0
  217. package/dist/sdd/coverage/tracker.js.map +1 -0
  218. package/dist/sdd/disaster/recovery.d.ts +11 -0
  219. package/dist/sdd/disaster/recovery.d.ts.map +1 -0
  220. package/dist/sdd/disaster/recovery.js +55 -0
  221. package/dist/sdd/disaster/recovery.js.map +1 -0
  222. package/dist/sdd/discovery/adaptive.d.ts +14 -0
  223. package/dist/sdd/discovery/adaptive.d.ts.map +1 -0
  224. package/dist/sdd/discovery/adaptive.js +145 -0
  225. package/dist/sdd/discovery/adaptive.js.map +1 -0
  226. package/dist/sdd/discovery/briefing-analyzer.d.ts +66 -0
  227. package/dist/sdd/discovery/briefing-analyzer.d.ts.map +1 -0
  228. package/dist/sdd/discovery/briefing-analyzer.js +672 -0
  229. package/dist/sdd/discovery/briefing-analyzer.js.map +1 -0
  230. package/dist/sdd/discovery/briefing.d.ts +109 -0
  231. package/dist/sdd/discovery/briefing.d.ts.map +1 -0
  232. package/dist/sdd/discovery/briefing.js +617 -0
  233. package/dist/sdd/discovery/briefing.js.map +1 -0
  234. package/dist/sdd/discovery/graph-builder.d.ts +10 -0
  235. package/dist/sdd/discovery/graph-builder.d.ts.map +1 -0
  236. package/dist/sdd/discovery/graph-builder.js +510 -0
  237. package/dist/sdd/discovery/graph-builder.js.map +1 -0
  238. package/dist/sdd/documentation/generator.d.ts +7 -0
  239. package/dist/sdd/documentation/generator.d.ts.map +1 -0
  240. package/dist/sdd/documentation/generator.js +119 -0
  241. package/dist/sdd/documentation/generator.js.map +1 -0
  242. package/dist/sdd/domain/types.d.ts +460 -0
  243. package/dist/sdd/domain/types.d.ts.map +1 -0
  244. package/dist/sdd/domain/types.js +25 -0
  245. package/dist/sdd/domain/types.js.map +1 -0
  246. package/dist/sdd/drift/detector.d.ts +28 -0
  247. package/dist/sdd/drift/detector.d.ts.map +1 -0
  248. package/dist/sdd/drift/detector.js +379 -0
  249. package/dist/sdd/drift/detector.js.map +1 -0
  250. package/dist/sdd/drift/exclusion.d.ts +54 -0
  251. package/dist/sdd/drift/exclusion.d.ts.map +1 -0
  252. package/dist/sdd/drift/exclusion.js +179 -0
  253. package/dist/sdd/drift/exclusion.js.map +1 -0
  254. package/dist/sdd/drift/signals.d.ts +52 -0
  255. package/dist/sdd/drift/signals.d.ts.map +1 -0
  256. package/dist/sdd/drift/signals.js +470 -0
  257. package/dist/sdd/drift/signals.js.map +1 -0
  258. package/dist/sdd/enforcement/interceptor.d.ts +47 -0
  259. package/dist/sdd/enforcement/interceptor.d.ts.map +1 -0
  260. package/dist/sdd/enforcement/interceptor.js +365 -0
  261. package/dist/sdd/enforcement/interceptor.js.map +1 -0
  262. package/dist/sdd/enforcement/workflow-tracker.d.ts +66 -0
  263. package/dist/sdd/enforcement/workflow-tracker.d.ts.map +1 -0
  264. package/dist/sdd/enforcement/workflow-tracker.js +229 -0
  265. package/dist/sdd/enforcement/workflow-tracker.js.map +1 -0
  266. package/dist/sdd/graph/engine.d.ts +44 -0
  267. package/dist/sdd/graph/engine.d.ts.map +1 -0
  268. package/dist/sdd/graph/engine.js +203 -0
  269. package/dist/sdd/graph/engine.js.map +1 -0
  270. package/dist/sdd/graph/index.d.ts +160 -0
  271. package/dist/sdd/graph/index.d.ts.map +1 -0
  272. package/dist/sdd/graph/index.js +624 -0
  273. package/dist/sdd/graph/index.js.map +1 -0
  274. package/dist/sdd/graph/integrity-guard.d.ts +129 -0
  275. package/dist/sdd/graph/integrity-guard.d.ts.map +1 -0
  276. package/dist/sdd/graph/integrity-guard.js +263 -0
  277. package/dist/sdd/graph/integrity-guard.js.map +1 -0
  278. package/dist/sdd/graph/integrity.d.ts +79 -0
  279. package/dist/sdd/graph/integrity.d.ts.map +1 -0
  280. package/dist/sdd/graph/integrity.js +462 -0
  281. package/dist/sdd/graph/integrity.js.map +1 -0
  282. package/dist/sdd/graph/pruner.d.ts +27 -0
  283. package/dist/sdd/graph/pruner.d.ts.map +1 -0
  284. package/dist/sdd/graph/pruner.js +203 -0
  285. package/dist/sdd/graph/pruner.js.map +1 -0
  286. package/dist/sdd/graph/traverse.d.ts +43 -0
  287. package/dist/sdd/graph/traverse.d.ts.map +1 -0
  288. package/dist/sdd/graph/traverse.js +265 -0
  289. package/dist/sdd/graph/traverse.js.map +1 -0
  290. package/dist/sdd/incidents/manager.d.ts +10 -0
  291. package/dist/sdd/incidents/manager.d.ts.map +1 -0
  292. package/dist/sdd/incidents/manager.js +63 -0
  293. package/dist/sdd/incidents/manager.js.map +1 -0
  294. package/dist/sdd/knowledge/transfer.d.ts +24 -0
  295. package/dist/sdd/knowledge/transfer.d.ts.map +1 -0
  296. package/dist/sdd/knowledge/transfer.js +94 -0
  297. package/dist/sdd/knowledge/transfer.js.map +1 -0
  298. package/dist/sdd/migrations/fixes.d.ts +2 -0
  299. package/dist/sdd/migrations/fixes.d.ts.map +1 -0
  300. package/dist/sdd/migrations/fixes.js +159 -0
  301. package/dist/sdd/migrations/fixes.js.map +1 -0
  302. package/dist/sdd/migrations/index.d.ts +2 -0
  303. package/dist/sdd/migrations/index.d.ts.map +1 -0
  304. package/dist/sdd/migrations/index.js +5 -0
  305. package/dist/sdd/migrations/index.js.map +1 -0
  306. package/dist/sdd/migrations/migration-runner.d.ts +41 -0
  307. package/dist/sdd/migrations/migration-runner.d.ts.map +1 -0
  308. package/dist/sdd/migrations/migration-runner.js +91 -0
  309. package/dist/sdd/migrations/migration-runner.js.map +1 -0
  310. package/dist/sdd/monitoring/setup.d.ts +40 -0
  311. package/dist/sdd/monitoring/setup.d.ts.map +1 -0
  312. package/dist/sdd/monitoring/setup.js +177 -0
  313. package/dist/sdd/monitoring/setup.js.map +1 -0
  314. package/dist/sdd/monitoring/telemetry.d.ts +18 -0
  315. package/dist/sdd/monitoring/telemetry.d.ts.map +1 -0
  316. package/dist/sdd/monitoring/telemetry.js +37 -0
  317. package/dist/sdd/monitoring/telemetry.js.map +1 -0
  318. package/dist/sdd/patterns/anti-patterns.d.ts +21 -0
  319. package/dist/sdd/patterns/anti-patterns.d.ts.map +1 -0
  320. package/dist/sdd/patterns/anti-patterns.js +286 -0
  321. package/dist/sdd/patterns/anti-patterns.js.map +1 -0
  322. package/dist/sdd/patterns/ast-clones.d.ts +23 -0
  323. package/dist/sdd/patterns/ast-clones.d.ts.map +1 -0
  324. package/dist/sdd/patterns/ast-clones.js +171 -0
  325. package/dist/sdd/patterns/ast-clones.js.map +1 -0
  326. package/dist/sdd/patterns/config-drift.d.ts +16 -0
  327. package/dist/sdd/patterns/config-drift.d.ts.map +1 -0
  328. package/dist/sdd/patterns/config-drift.js +322 -0
  329. package/dist/sdd/patterns/config-drift.js.map +1 -0
  330. package/dist/sdd/patterns/contradictions.d.ts +38 -0
  331. package/dist/sdd/patterns/contradictions.d.ts.map +1 -0
  332. package/dist/sdd/patterns/contradictions.js +237 -0
  333. package/dist/sdd/patterns/contradictions.js.map +1 -0
  334. package/dist/sdd/patterns/learner.d.ts +49 -0
  335. package/dist/sdd/patterns/learner.d.ts.map +1 -0
  336. package/dist/sdd/patterns/learner.js +226 -0
  337. package/dist/sdd/patterns/learner.js.map +1 -0
  338. package/dist/sdd/permissions/access.d.ts +69 -0
  339. package/dist/sdd/permissions/access.d.ts.map +1 -0
  340. package/dist/sdd/permissions/access.js +333 -0
  341. package/dist/sdd/permissions/access.js.map +1 -0
  342. package/dist/sdd/persistence/repository.d.ts +60 -0
  343. package/dist/sdd/persistence/repository.d.ts.map +1 -0
  344. package/dist/sdd/persistence/repository.js +96 -0
  345. package/dist/sdd/persistence/repository.js.map +1 -0
  346. package/dist/sdd/persistence/sqlite.d.ts +122 -0
  347. package/dist/sdd/persistence/sqlite.d.ts.map +1 -0
  348. package/dist/sdd/persistence/sqlite.js +602 -0
  349. package/dist/sdd/persistence/sqlite.js.map +1 -0
  350. package/dist/sdd/persistence/yaml.d.ts +41 -0
  351. package/dist/sdd/persistence/yaml.d.ts.map +1 -0
  352. package/dist/sdd/persistence/yaml.js +382 -0
  353. package/dist/sdd/persistence/yaml.js.map +1 -0
  354. package/dist/sdd/promises/classifier.d.ts +48 -0
  355. package/dist/sdd/promises/classifier.d.ts.map +1 -0
  356. package/dist/sdd/promises/classifier.js +215 -0
  357. package/dist/sdd/promises/classifier.js.map +1 -0
  358. package/dist/sdd/promises/tracker.d.ts +20 -0
  359. package/dist/sdd/promises/tracker.d.ts.map +1 -0
  360. package/dist/sdd/promises/tracker.js +204 -0
  361. package/dist/sdd/promises/tracker.js.map +1 -0
  362. package/dist/sdd/quality/scorer.d.ts +44 -0
  363. package/dist/sdd/quality/scorer.d.ts.map +1 -0
  364. package/dist/sdd/quality/scorer.js +216 -0
  365. package/dist/sdd/quality/scorer.js.map +1 -0
  366. package/dist/sdd/rollback/manager.d.ts +45 -0
  367. package/dist/sdd/rollback/manager.d.ts.map +1 -0
  368. package/dist/sdd/rollback/manager.js +266 -0
  369. package/dist/sdd/rollback/manager.js.map +1 -0
  370. package/dist/sdd/session/handoff.d.ts +52 -0
  371. package/dist/sdd/session/handoff.d.ts.map +1 -0
  372. package/dist/sdd/session/handoff.js +264 -0
  373. package/dist/sdd/session/handoff.js.map +1 -0
  374. package/dist/sdd/sla/tracker.d.ts +11 -0
  375. package/dist/sdd/sla/tracker.d.ts.map +1 -0
  376. package/dist/sdd/sla/tracker.js +52 -0
  377. package/dist/sdd/sla/tracker.js.map +1 -0
  378. package/dist/sdd/sync/git-sync.d.ts +61 -0
  379. package/dist/sdd/sync/git-sync.d.ts.map +1 -0
  380. package/dist/sdd/sync/git-sync.js +297 -0
  381. package/dist/sdd/sync/git-sync.js.map +1 -0
  382. package/dist/sdd/toggle/state.d.ts +8 -0
  383. package/dist/sdd/toggle/state.d.ts.map +1 -0
  384. package/dist/sdd/toggle/state.js +32 -0
  385. package/dist/sdd/toggle/state.js.map +1 -0
  386. package/dist/sdd/transactions/manager.d.ts +12 -0
  387. package/dist/sdd/transactions/manager.d.ts.map +1 -0
  388. package/dist/sdd/transactions/manager.js +80 -0
  389. package/dist/sdd/transactions/manager.js.map +1 -0
  390. package/dist/sdd/validation/coverage-index.d.ts +117 -0
  391. package/dist/sdd/validation/coverage-index.d.ts.map +1 -0
  392. package/dist/sdd/validation/coverage-index.js +251 -0
  393. package/dist/sdd/validation/coverage-index.js.map +1 -0
  394. package/dist/sdd/validation/executable.d.ts +26 -0
  395. package/dist/sdd/validation/executable.d.ts.map +1 -0
  396. package/dist/sdd/validation/executable.js +122 -0
  397. package/dist/sdd/validation/executable.js.map +1 -0
  398. package/dist/sdd/validation/smart-validator.d.ts +53 -0
  399. package/dist/sdd/validation/smart-validator.d.ts.map +1 -0
  400. package/dist/sdd/validation/smart-validator.js +347 -0
  401. package/dist/sdd/validation/smart-validator.js.map +1 -0
  402. package/dist/sdd/validation/validator.d.ts +39 -0
  403. package/dist/sdd/validation/validator.d.ts.map +1 -0
  404. package/dist/sdd/validation/validator.js +512 -0
  405. package/dist/sdd/validation/validator.js.map +1 -0
  406. package/dist/sdd/workflow/exporter.d.ts +36 -0
  407. package/dist/sdd/workflow/exporter.d.ts.map +1 -0
  408. package/dist/sdd/workflow/exporter.js +112 -0
  409. package/dist/sdd/workflow/exporter.js.map +1 -0
  410. package/dist/sdd/workflows/ab-testing.d.ts +18 -0
  411. package/dist/sdd/workflows/ab-testing.d.ts.map +1 -0
  412. package/dist/sdd/workflows/ab-testing.js +67 -0
  413. package/dist/sdd/workflows/ab-testing.js.map +1 -0
  414. package/dist/sdd/workflows/bug-fix.d.ts +13 -0
  415. package/dist/sdd/workflows/bug-fix.d.ts.map +1 -0
  416. package/dist/sdd/workflows/bug-fix.js +65 -0
  417. package/dist/sdd/workflows/bug-fix.js.map +1 -0
  418. package/dist/sdd/workflows/data-migration.d.ts +14 -0
  419. package/dist/sdd/workflows/data-migration.d.ts.map +1 -0
  420. package/dist/sdd/workflows/data-migration.js +68 -0
  421. package/dist/sdd/workflows/data-migration.js.map +1 -0
  422. package/dist/sdd/workflows/deprecation.d.ts +15 -0
  423. package/dist/sdd/workflows/deprecation.d.ts.map +1 -0
  424. package/dist/sdd/workflows/deprecation.js +68 -0
  425. package/dist/sdd/workflows/deprecation.js.map +1 -0
  426. package/dist/sdd/workflows/feature-flags.d.ts +14 -0
  427. package/dist/sdd/workflows/feature-flags.d.ts.map +1 -0
  428. package/dist/sdd/workflows/feature-flags.js +73 -0
  429. package/dist/sdd/workflows/feature-flags.js.map +1 -0
  430. package/dist/sdd/workflows/hotfix.d.ts +13 -0
  431. package/dist/sdd/workflows/hotfix.d.ts.map +1 -0
  432. package/dist/sdd/workflows/hotfix.js +67 -0
  433. package/dist/sdd/workflows/hotfix.js.map +1 -0
  434. package/dist/sdd/workflows/multi-tenancy.d.ts +13 -0
  435. package/dist/sdd/workflows/multi-tenancy.d.ts.map +1 -0
  436. package/dist/sdd/workflows/multi-tenancy.js +68 -0
  437. package/dist/sdd/workflows/multi-tenancy.js.map +1 -0
  438. package/dist/sdd/workflows/onboarding.d.ts +7 -0
  439. package/dist/sdd/workflows/onboarding.d.ts.map +1 -0
  440. package/dist/sdd/workflows/onboarding.js +43 -0
  441. package/dist/sdd/workflows/onboarding.js.map +1 -0
  442. package/dist/sdd/workflows/refactoring.d.ts +14 -0
  443. package/dist/sdd/workflows/refactoring.d.ts.map +1 -0
  444. package/dist/sdd/workflows/refactoring.js +73 -0
  445. package/dist/sdd/workflows/refactoring.js.map +1 -0
  446. package/dist/server/events.d.ts +64 -0
  447. package/dist/server/events.d.ts.map +1 -0
  448. package/dist/server/events.js +149 -0
  449. package/dist/server/events.js.map +1 -0
  450. package/dist/server/server.d.ts +33 -0
  451. package/dist/server/server.d.ts.map +1 -0
  452. package/dist/server/server.js +1019 -0
  453. package/dist/server/server.js.map +1 -0
  454. package/marketplace.json +39 -0
  455. package/package.json +65 -0
  456. package/src/code-intelligence/analyzer.ts +350 -0
  457. package/src/code-intelligence/ast/cache.ts +61 -0
  458. package/src/code-intelligence/ast/common.ts +62 -0
  459. package/src/code-intelligence/ast/component.ts +39 -0
  460. package/src/code-intelligence/ast/fallback.ts +44 -0
  461. package/src/code-intelligence/ast/index.ts +4 -0
  462. package/src/code-intelligence/ast/ir.ts +91 -0
  463. package/src/code-intelligence/ast/metrics.ts +65 -0
  464. package/src/code-intelligence/ast/registry.ts +28 -0
  465. package/src/code-intelligence/ast/tree-sitter.ts +226 -0
  466. package/src/code-intelligence/ast/typescript.ts +140 -0
  467. package/src/index.ts +22 -0
  468. package/src/mcp/server.ts +77 -0
  469. package/src/opencode/hooks.ts +728 -0
  470. package/src/opencode/router/categories.ts +192 -0
  471. package/src/opencode/router/embeddings.ts +149 -0
  472. package/src/opencode/router/graph-state-snapshot.ts +163 -0
  473. package/src/opencode/router/index.ts +44 -0
  474. package/src/opencode/router/intent-classifier.ts +140 -0
  475. package/src/opencode/router/semantic-nudge.ts +89 -0
  476. package/src/opencode/router/state-gate.ts +192 -0
  477. package/src/opencode/router/tool-embeddings.ts +162 -0
  478. package/src/opencode/router/tool-registry.ts +135 -0
  479. package/src/opencode/router/tool-taxonomy.ts +284 -0
  480. package/src/opencode/router/tools-composite.ts +685 -0
  481. package/src/opencode/shell-hooks.ts +115 -0
  482. package/src/opencode/system-prompt.ts +617 -0
  483. package/src/opencode/tools.ts +4102 -0
  484. package/src/opencode/workflows/chains.ts +205 -0
  485. package/src/opencode/workflows/executor.ts +172 -0
  486. package/src/opencode/workflows/index.ts +21 -0
  487. package/src/opencode/workflows/tools-workflow.ts +91 -0
  488. package/src/opencode/workflows/types.ts +42 -0
  489. package/src/sdd/analysis/compliance.ts +279 -0
  490. package/src/sdd/analysis/scalability.ts +97 -0
  491. package/src/sdd/analysis/security.ts +135 -0
  492. package/src/sdd/brownfield/scanner.ts +276 -0
  493. package/src/sdd/cache/manager.ts +899 -0
  494. package/src/sdd/cache/snapshot-store.ts +185 -0
  495. package/src/sdd/changes/manager.ts +326 -0
  496. package/src/sdd/cicd/generators.ts +1095 -0
  497. package/src/sdd/code-quality/complexity.ts +235 -0
  498. package/src/sdd/code-quality/conventions.ts +220 -0
  499. package/src/sdd/code-quality/dependencies.ts +203 -0
  500. package/src/sdd/code-quality/import-analyzer.ts +294 -0
  501. package/src/sdd/code-quality/metrics.ts +232 -0
  502. package/src/sdd/code-quality/smells.ts +415 -0
  503. package/src/sdd/code-quality/symbol-parser.ts +335 -0
  504. package/src/sdd/code-quality/usage-tracker.ts +527 -0
  505. package/src/sdd/code-quality/utils.ts +13 -0
  506. package/src/sdd/codegen/generator.ts +1181 -0
  507. package/src/sdd/constitution/validator.ts +253 -0
  508. package/src/sdd/cost/estimator.ts +147 -0
  509. package/src/sdd/coverage/tracker.ts +342 -0
  510. package/src/sdd/disaster/recovery.ts +79 -0
  511. package/src/sdd/discovery/adaptive.ts +167 -0
  512. package/src/sdd/discovery/briefing-analyzer.ts +802 -0
  513. package/src/sdd/discovery/briefing.ts +770 -0
  514. package/src/sdd/discovery/graph-builder.ts +598 -0
  515. package/src/sdd/documentation/generator.ts +158 -0
  516. package/src/sdd/domain/types.ts +658 -0
  517. package/src/sdd/drift/detector.ts +465 -0
  518. package/src/sdd/drift/exclusion.ts +223 -0
  519. package/src/sdd/drift/signals.ts +577 -0
  520. package/src/sdd/enforcement/interceptor.ts +444 -0
  521. package/src/sdd/enforcement/workflow-tracker.ts +263 -0
  522. package/src/sdd/graph/engine.ts +277 -0
  523. package/src/sdd/graph/index.ts +700 -0
  524. package/src/sdd/graph/integrity-guard.ts +348 -0
  525. package/src/sdd/graph/integrity.ts +628 -0
  526. package/src/sdd/graph/pruner.ts +221 -0
  527. package/src/sdd/graph/traverse.ts +331 -0
  528. package/src/sdd/incidents/manager.ts +87 -0
  529. package/src/sdd/knowledge/transfer.ts +140 -0
  530. package/src/sdd/migrations/fixes.ts +154 -0
  531. package/src/sdd/migrations/index.ts +14 -0
  532. package/src/sdd/migrations/migration-runner.ts +118 -0
  533. package/src/sdd/monitoring/setup.ts +237 -0
  534. package/src/sdd/monitoring/telemetry.ts +44 -0
  535. package/src/sdd/patterns/anti-patterns.ts +333 -0
  536. package/src/sdd/patterns/ast-clones.ts +214 -0
  537. package/src/sdd/patterns/config-drift.ts +346 -0
  538. package/src/sdd/patterns/contradictions.ts +323 -0
  539. package/src/sdd/patterns/learner.ts +266 -0
  540. package/src/sdd/permissions/access.ts +478 -0
  541. package/src/sdd/persistence/repository.ts +166 -0
  542. package/src/sdd/persistence/sqlite.ts +703 -0
  543. package/src/sdd/persistence/yaml.ts +409 -0
  544. package/src/sdd/promises/classifier.ts +253 -0
  545. package/src/sdd/promises/tracker.ts +258 -0
  546. package/src/sdd/quality/scorer.ts +263 -0
  547. package/src/sdd/rollback/manager.ts +361 -0
  548. package/src/sdd/session/handoff.ts +354 -0
  549. package/src/sdd/sla/tracker.ts +68 -0
  550. package/src/sdd/sync/git-sync.ts +401 -0
  551. package/src/sdd/toggle/state.ts +38 -0
  552. package/src/sdd/transactions/manager.ts +93 -0
  553. package/src/sdd/validation/coverage-index.ts +339 -0
  554. package/src/sdd/validation/executable.ts +127 -0
  555. package/src/sdd/validation/smart-validator.ts +470 -0
  556. package/src/sdd/validation/validator.ts +641 -0
  557. package/src/sdd/workflow/exporter.ts +169 -0
  558. package/src/sdd/workflows/ab-testing.ts +90 -0
  559. package/src/sdd/workflows/bug-fix.ts +81 -0
  560. package/src/sdd/workflows/data-migration.ts +86 -0
  561. package/src/sdd/workflows/deprecation.ts +87 -0
  562. package/src/sdd/workflows/feature-flags.ts +91 -0
  563. package/src/sdd/workflows/hotfix.ts +83 -0
  564. package/src/sdd/workflows/multi-tenancy.ts +84 -0
  565. package/src/sdd/workflows/onboarding.ts +82 -0
  566. package/src/sdd/workflows/refactoring.ts +90 -0
  567. package/src/server/events.ts +184 -0
  568. package/src/server/server.ts +1088 -0
@@ -0,0 +1,1019 @@
1
+ import { createRepository } from "../sdd/persistence/repository.js";
2
+ import { getGraphStats, getNode } from "../sdd/graph/engine.js";
3
+ import { computeImpact } from "../sdd/graph/traverse.js";
4
+ import { validateGraph } from "../sdd/validation/validator.js";
5
+ import { detectDrift } from "../sdd/drift/detector.js";
6
+ import { getPendingChanges } from "../sdd/changes/manager.js";
7
+ import { progressEmitter } from "./events.js";
8
+ export class SddDashboardServer {
9
+ repo;
10
+ projectDir;
11
+ port = 0;
12
+ constructor(projectDir) {
13
+ this.projectDir = projectDir;
14
+ this.repo = createRepository(projectDir);
15
+ }
16
+ async start() {
17
+ if (!this.repo.isInitialized()) {
18
+ throw new Error("SDD not initialized");
19
+ }
20
+ const server = Bun.serve({
21
+ port: 0,
22
+ hostname: "127.0.0.1",
23
+ fetch: async (req) => {
24
+ const url = new URL(req.url);
25
+ return this.handleRequest(url, req);
26
+ },
27
+ });
28
+ this.port = server.port ?? 0;
29
+ return this.port;
30
+ }
31
+ getPort() {
32
+ return this.port;
33
+ }
34
+ getUrl() {
35
+ return `http://127.0.0.1:${this.port}`;
36
+ }
37
+ async handleRequest(url, req) {
38
+ const corsHeaders = {
39
+ "Access-Control-Allow-Origin": "*",
40
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
41
+ "Access-Control-Allow-Headers": "Content-Type",
42
+ };
43
+ if (req.method === "OPTIONS") {
44
+ return new Response(null, { status: 204, headers: corsHeaders });
45
+ }
46
+ try {
47
+ const path = url.pathname;
48
+ if (path === "/api/health") {
49
+ return this.jsonResponse({ status: "ok", timestamp: new Date().toISOString() }, corsHeaders);
50
+ }
51
+ if (path === "/api/project") {
52
+ return this.jsonResponse(this.getProjectInfo(), corsHeaders);
53
+ }
54
+ if (path === "/api/graph") {
55
+ return this.jsonResponse(this.getGraphData(), corsHeaders);
56
+ }
57
+ if (path === "/api/graph/counts") {
58
+ return this.jsonResponse(this.getGraphCounts(), corsHeaders);
59
+ }
60
+ if (path === "/api/graph/summary") {
61
+ const offset = parseInt(url.searchParams.get("offset") || "0");
62
+ const limit = parseInt(url.searchParams.get("limit") || "500");
63
+ const type = url.searchParams.get("type") || undefined;
64
+ return this.jsonResponse(this.getGraphSummary(offset, limit, type), corsHeaders);
65
+ }
66
+ if (path === "/api/graph/types") {
67
+ return this.jsonResponse(this.getNodeTypeCounts(), corsHeaders);
68
+ }
69
+ if (path === "/api/nodes") {
70
+ const type = url.searchParams.get("type");
71
+ return this.jsonResponse(this.getNodes(type), corsHeaders);
72
+ }
73
+ if (path.startsWith("/api/nodes/") && path.endsWith("/relationships")) {
74
+ const nodeId = path.split("/")[3];
75
+ return this.jsonResponse(this.getNodeRelationships(nodeId), corsHeaders);
76
+ }
77
+ if (path.startsWith("/api/nodes/") && path.endsWith("/impact")) {
78
+ const nodeId = path.split("/")[3];
79
+ const depth = parseInt(url.searchParams.get("depth") || "3");
80
+ return this.jsonResponse(this.getNodeImpact(nodeId, depth), corsHeaders);
81
+ }
82
+ if (path.startsWith("/api/nodes/")) {
83
+ const nodeId = path.split("/")[3];
84
+ return this.jsonResponse(this.getNodeDetail(nodeId), corsHeaders);
85
+ }
86
+ if (path === "/api/status") {
87
+ return this.jsonResponse(this.getStatus(), corsHeaders);
88
+ }
89
+ if (path === "/api/validation") {
90
+ return this.jsonResponse(this.getValidation(), corsHeaders);
91
+ }
92
+ if (path === "/api/drift") {
93
+ return this.jsonResponse(this.getDrift(), corsHeaders);
94
+ }
95
+ if (path === "/api/changes") {
96
+ return this.jsonResponse(this.getChanges(), corsHeaders);
97
+ }
98
+ if (path === "/api/events") {
99
+ return this.streamEvents(req);
100
+ }
101
+ if (path === "/api/progress") {
102
+ return this.getProgress();
103
+ }
104
+ if (path === "/") {
105
+ return this.serveDashboard(corsHeaders);
106
+ }
107
+ if (path === "/favicon.ico") {
108
+ return new Response(null, { status: 204, headers: corsHeaders });
109
+ }
110
+ return this.jsonResponse({ error: "Not found" }, corsHeaders, 404);
111
+ }
112
+ catch (error) {
113
+ return this.jsonResponse({ error: error instanceof Error ? error.message : String(error) }, corsHeaders, 500);
114
+ }
115
+ }
116
+ jsonResponse(data, headers = {}, status = 200) {
117
+ return new Response(JSON.stringify(data), {
118
+ status,
119
+ headers: { "Content-Type": "application/json", ...headers },
120
+ });
121
+ }
122
+ getProjectInfo() {
123
+ if (!this.repo.isInitialized())
124
+ return { initialized: false };
125
+ const graph = this.repo.loadGraph();
126
+ return {
127
+ initialized: true,
128
+ project_id: graph.project_id,
129
+ version: graph.version,
130
+ sdd_version: graph.metadata.sdd_version,
131
+ created_at: graph.metadata.created_at,
132
+ updated_at: graph.metadata.updated_at,
133
+ };
134
+ }
135
+ getGraphData() {
136
+ if (!this.repo.isInitialized())
137
+ return { nodes: [], relationships: [], updated_at: "" };
138
+ // Use optimized SQLite queries when available for large graphs
139
+ const sqliteRepo = this.repo;
140
+ if (sqliteRepo.getNodesSummary) {
141
+ const counts = sqliteRepo.getGraphCounts();
142
+ const updatedAt = sqliteRepo.getUpdatedAt();
143
+ // For graphs under 2000 nodes, send all data at once
144
+ if (counts.nodeCount <= 2000) {
145
+ const nodes = sqliteRepo.getAllNodesSummary();
146
+ const rels = sqliteRepo.getAllRelationshipsSummary();
147
+ return { nodes, relationships: rels, updated_at: updatedAt, nodeCount: counts.nodeCount, relCount: counts.relCount };
148
+ }
149
+ // For larger graphs, send all nodes summary (lightweight) but only first 2000 relationships
150
+ const nodes = sqliteRepo.getAllNodesSummary();
151
+ const relsResult = sqliteRepo.getRelationshipsSummary(0, 2000);
152
+ return {
153
+ nodes,
154
+ relationships: relsResult.relationships,
155
+ updated_at: updatedAt,
156
+ nodeCount: counts.nodeCount,
157
+ relCount: relsResult.total,
158
+ hasMoreRelationships: relsResult.total > 2000,
159
+ };
160
+ }
161
+ // Fallback for YAML
162
+ const graph = this.repo.loadGraph();
163
+ return {
164
+ nodes: graph.nodes.map((n) => ({
165
+ id: n.id,
166
+ type: n.type,
167
+ name: n.name,
168
+ status: n.status,
169
+ version: n.version,
170
+ })),
171
+ relationships: graph.relationships.map((r) => ({
172
+ id: r.id,
173
+ from: r.from,
174
+ to: r.to,
175
+ type: r.type,
176
+ })),
177
+ updated_at: graph.metadata.updated_at,
178
+ };
179
+ }
180
+ getGraphCounts() {
181
+ if (!this.repo.isInitialized())
182
+ return { nodeCount: 0, relCount: 0, updated_at: "" };
183
+ const sqliteRepo = this.repo;
184
+ if (sqliteRepo.getGraphCounts) {
185
+ const counts = sqliteRepo.getGraphCounts();
186
+ const updatedAt = sqliteRepo.getUpdatedAt();
187
+ return { ...counts, updated_at: updatedAt };
188
+ }
189
+ const graph = this.repo.loadGraph();
190
+ return {
191
+ nodeCount: graph.nodes.length,
192
+ relCount: graph.relationships.length,
193
+ updated_at: graph.metadata.updated_at,
194
+ };
195
+ }
196
+ getGraphSummary(offset, limit, type) {
197
+ if (!this.repo.isInitialized())
198
+ return { nodes: [], relationships: [], total: 0 };
199
+ const sqliteRepo = this.repo;
200
+ if (sqliteRepo.getNodesSummary) {
201
+ const nodesResult = sqliteRepo.getNodesSummary(offset, limit, type);
202
+ return {
203
+ nodes: nodesResult.nodes,
204
+ total: nodesResult.total,
205
+ offset,
206
+ limit,
207
+ hasMore: offset + limit < nodesResult.total,
208
+ };
209
+ }
210
+ // Fallback for YAML — load full graph and slice
211
+ const graph = this.repo.loadGraph();
212
+ let nodes = graph.nodes.map((n) => ({
213
+ id: n.id,
214
+ type: n.type,
215
+ name: n.name,
216
+ status: n.status,
217
+ version: n.version,
218
+ }));
219
+ if (type)
220
+ nodes = nodes.filter((n) => n.type === type);
221
+ return {
222
+ nodes: nodes.slice(offset, offset + limit),
223
+ total: nodes.length,
224
+ offset,
225
+ limit,
226
+ hasMore: offset + limit < nodes.length,
227
+ };
228
+ }
229
+ getNodeTypeCounts() {
230
+ if (!this.repo.isInitialized())
231
+ return [];
232
+ const sqliteRepo = this.repo;
233
+ if (sqliteRepo.getNodeTypeCounts) {
234
+ return sqliteRepo.getNodeTypeCounts();
235
+ }
236
+ const graph = this.repo.loadGraph();
237
+ const counts = {};
238
+ for (const n of graph.nodes) {
239
+ counts[n.type] = (counts[n.type] || 0) + 1;
240
+ }
241
+ return Object.entries(counts)
242
+ .map(([type, count]) => ({ type, count }))
243
+ .sort((a, b) => b.count - a.count);
244
+ }
245
+ getNodes(type) {
246
+ if (!this.repo.isInitialized())
247
+ return [];
248
+ const sqliteRepo = this.repo;
249
+ if (type && sqliteRepo.getNodesByType) {
250
+ return sqliteRepo.getNodesByType(type);
251
+ }
252
+ const graph = this.repo.loadGraph();
253
+ if (type) {
254
+ return graph.nodes.filter((n) => n.type === type);
255
+ }
256
+ return graph.nodes;
257
+ }
258
+ getNodeDetail(nodeId) {
259
+ if (!this.repo.isInitialized())
260
+ return { error: "Not initialized" };
261
+ const sqliteRepo = this.repo;
262
+ if (sqliteRepo.getNodeById) {
263
+ const node = sqliteRepo.getNodeById(nodeId);
264
+ return node || { error: "Node not found" };
265
+ }
266
+ const graph = this.repo.loadGraph();
267
+ const node = getNode(graph, nodeId);
268
+ if (!node)
269
+ return { error: "Node not found" };
270
+ return node;
271
+ }
272
+ getNodeRelationships(nodeId) {
273
+ if (!this.repo.isInitialized())
274
+ return [];
275
+ const sqliteRepo = this.repo;
276
+ if (sqliteRepo.getRelationshipsForNode) {
277
+ return sqliteRepo.getRelationshipsForNode(nodeId);
278
+ }
279
+ const graph = this.repo.loadGraph();
280
+ return graph.relationships.filter((r) => r.from === nodeId || r.to === nodeId);
281
+ }
282
+ getNodeImpact(nodeId, depth) {
283
+ if (!this.repo.isInitialized())
284
+ return { error: "Not initialized" };
285
+ const graph = this.repo.loadGraph();
286
+ return computeImpact(graph, nodeId, depth);
287
+ }
288
+ getStatus() {
289
+ if (!this.repo.isInitialized())
290
+ return { initialized: false };
291
+ const sqliteRepo = this.repo;
292
+ if (sqliteRepo.getGraphCounts) {
293
+ const counts = sqliteRepo.getGraphCounts();
294
+ return {
295
+ initialized: true,
296
+ stats: {
297
+ total_nodes: counts.nodeCount,
298
+ total_relationships: counts.relCount,
299
+ },
300
+ pending_changes: 0,
301
+ validation_errors: 0,
302
+ validation_warnings: 0,
303
+ };
304
+ }
305
+ const graph = this.repo.loadGraph();
306
+ const stats = getGraphStats(graph);
307
+ const pending = getPendingChanges(graph);
308
+ const validation = validateGraph(graph);
309
+ return {
310
+ initialized: true,
311
+ stats,
312
+ pending_changes: pending.length,
313
+ validation_errors: validation.errors.length,
314
+ validation_warnings: validation.warnings.length,
315
+ };
316
+ }
317
+ getValidation() {
318
+ if (!this.repo.isInitialized())
319
+ return { error: "Not initialized" };
320
+ const graph = this.repo.loadGraph();
321
+ return validateGraph(graph);
322
+ }
323
+ getDrift() {
324
+ if (!this.repo.isInitialized())
325
+ return { error: "Not initialized" };
326
+ const graph = this.repo.loadGraph();
327
+ return detectDrift(graph, this.projectDir);
328
+ }
329
+ getChanges() {
330
+ if (!this.repo.isInitialized())
331
+ return [];
332
+ const sqliteRepo = this.repo;
333
+ if (sqliteRepo.getNodesByType) {
334
+ return sqliteRepo.getNodesByType("change");
335
+ }
336
+ const graph = this.repo.loadGraph();
337
+ return graph.nodes
338
+ .filter((n) => n.type === "change")
339
+ .sort((a, b) => b.created_at.localeCompare(a.created_at));
340
+ }
341
+ streamEvents(_req) {
342
+ let unsubscribe = null;
343
+ const stream = new ReadableStream({
344
+ start(controller) {
345
+ const encoder = new TextEncoder();
346
+ const send = (event) => {
347
+ try {
348
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
349
+ }
350
+ catch {
351
+ // Stream might be closed
352
+ }
353
+ };
354
+ // Send initial connection event
355
+ send({ type: "connected", timestamp: new Date().toISOString(), data: {} });
356
+ // Send current build status if any
357
+ const currentBuild = progressEmitter.getCurrentBuild();
358
+ if (currentBuild) {
359
+ send({
360
+ type: "build_status",
361
+ timestamp: new Date().toISOString(),
362
+ data: currentBuild,
363
+ });
364
+ }
365
+ // Subscribe to progress events
366
+ unsubscribe = progressEmitter.on((event) => {
367
+ send(event);
368
+ });
369
+ // Send heartbeat every 30 seconds to keep connection alive
370
+ const heartbeat = setInterval(() => {
371
+ try {
372
+ controller.enqueue(encoder.encode(`:heartbeat\n\n`));
373
+ }
374
+ catch {
375
+ clearInterval(heartbeat);
376
+ }
377
+ }, 30000);
378
+ // Cleanup on close
379
+ const originalCancel = stream.cancel?.bind(stream);
380
+ stream.cancel = () => {
381
+ clearInterval(heartbeat);
382
+ if (unsubscribe)
383
+ unsubscribe();
384
+ return originalCancel?.() ?? Promise.resolve();
385
+ };
386
+ },
387
+ cancel() {
388
+ if (unsubscribe)
389
+ unsubscribe();
390
+ },
391
+ });
392
+ return new Response(stream, {
393
+ headers: {
394
+ "Content-Type": "text/event-stream",
395
+ "Cache-Control": "no-cache",
396
+ Connection: "keep-alive",
397
+ "Access-Control-Allow-Origin": "*",
398
+ },
399
+ });
400
+ }
401
+ getProgress() {
402
+ const currentBuild = progressEmitter.getCurrentBuild();
403
+ return new Response(JSON.stringify({
404
+ building: progressEmitter.isBuilding(),
405
+ currentBuild,
406
+ subscribers: progressEmitter.getSubscriberCount(),
407
+ }), {
408
+ headers: { "Content-Type": "application/json" },
409
+ });
410
+ }
411
+ serveDashboard(headers) {
412
+ const html = `<!DOCTYPE html>
413
+ <html lang="en">
414
+ <head>
415
+ <meta charset="UTF-8">
416
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
417
+ <title>SDD Knowledge Graph Dashboard</title>
418
+ <style>
419
+ * { margin: 0; padding: 0; box-sizing: border-box; }
420
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0d1117; color: #c9d1d9; overflow: hidden; }
421
+ .container { display: grid; grid-template-columns: 280px 1fr 320px; height: 100vh; }
422
+ .sidebar { background: #161b22; border-right: 1px solid #30363d; padding: 16px; overflow-y: auto; display: flex; flex-direction: column; }
423
+ .main { position: relative; overflow: hidden; }
424
+ .details { background: #161b22; border-left: 1px solid #30363d; padding: 16px; overflow-y: auto; }
425
+ h1 { font-size: 18px; margin-bottom: 16px; color: #58a6ff; }
426
+ h2 { font-size: 14px; margin-bottom: 8px; color: #8b949e; text-transform: uppercase; letter-spacing: 1px; }
427
+ .stat { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #21262d; }
428
+ .stat-label { color: #8b949e; }
429
+ .stat-value { font-weight: 600; }
430
+ .node-list { list-style: none; flex: 1; overflow-y: auto; }
431
+ .node-item { padding: 8px 12px; margin: 2px 0; border-radius: 6px; cursor: pointer; transition: background 0.2s; }
432
+ .node-item:hover { background: #21262d; }
433
+ .node-item.selected { background: #1f6feb33; border-left: 3px solid #58a6ff; }
434
+ .node-id { font-size: 11px; color: #8b949e; }
435
+ .node-name { font-size: 13px; font-weight: 500; }
436
+ .node-status { font-size: 10px; padding: 2px 6px; border-radius: 10px; margin-left: 8px; }
437
+ .status-DRAFT { background: #30363d; color: #8b949e; }
438
+ .status-APPROVED { background: #238636; color: #fff; }
439
+ .status-IMPLEMENTING { background: #9e6a03; color: #fff; }
440
+ .status-IMPLEMENTED { background: #1f6feb; color: #fff; }
441
+ .status-BLOCKED { background: #da3633; color: #fff; }
442
+ .filter-bar { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
443
+ .filter-btn { padding: 4px 10px; border-radius: 12px; border: 1px solid #30363d; background: transparent; color: #c9d1d9; cursor: pointer; font-size: 11px; transition: all 0.2s; }
444
+ .filter-btn:hover { border-color: #58a6ff; }
445
+ .filter-btn.active { background: #1f6feb; border-color: #1f6feb; color: #fff; }
446
+ .search { width: 100%; padding: 8px 12px; border-radius: 6px; border: 1px solid #30363d; background: #0d1117; color: #c9d1d9; margin-bottom: 12px; font-size: 13px; outline: none; }
447
+ .search:focus { border-color: #58a6ff; }
448
+ .detail-section { margin-bottom: 16px; }
449
+ .detail-section h3 { font-size: 12px; color: #58a6ff; margin-bottom: 8px; }
450
+ .detail-field { font-size: 12px; padding: 4px 0; }
451
+ .detail-field .label { color: #8b949e; }
452
+ #graph-container { width: 100%; height: 100%; }
453
+ .graph-overlay { position: absolute; bottom: 12px; left: 12px; background: #161b22ee; border: 1px solid #30363d; border-radius: 8px; padding: 10px 14px; font-size: 11px; color: #8b949e; pointer-events: none; }
454
+ .graph-controls { position: absolute; top: 12px; right: 12px; display: flex; flex-direction: column; gap: 6px; }
455
+ .graph-btn { width: 32px; height: 32px; border-radius: 6px; border: 1px solid #30363d; background: #161b22; color: #c9d1d9; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
456
+ .graph-btn:hover { border-color: #58a6ff; color: #58a6ff; }
457
+ .legend { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
458
+ .legend-item { display: flex; align-items: center; gap: 4px; font-size: 11px; color: #8b949e; }
459
+ .legend-dot { width: 10px; height: 10px; border-radius: 50%; }
460
+ .progress-panel { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 12px; margin-bottom: 16px; display: none; }
461
+ .progress-panel.active { display: block; }
462
+ .progress-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
463
+ .progress-title { font-size: 12px; font-weight: 600; color: #58a6ff; }
464
+ .progress-percent { font-size: 11px; color: #3fb950; }
465
+ .progress-bar-bg { height: 6px; background: #21262d; border-radius: 3px; overflow: hidden; margin-bottom: 8px; }
466
+ .progress-bar { height: 100%; background: linear-gradient(90deg, #1f6feb, #3fb950); border-radius: 3px; transition: width 0.3s ease; }
467
+ .progress-step { font-size: 11px; color: #8b949e; }
468
+ .progress-log { max-height: 120px; overflow-y: auto; font-size: 10px; font-family: monospace; color: #8b949e; margin-top: 8px; }
469
+ .progress-log-entry { padding: 2px 0; border-bottom: 1px solid #21262d; }
470
+ .progress-log-entry.error { color: #f85149; }
471
+ .progress-log-entry.complete { color: #3fb950; }
472
+ .sse-status { display: flex; align-items: center; gap: 4px; font-size: 10px; color: #8b949e; margin-bottom: 8px; }
473
+ .sse-dot { width: 6px; height: 6px; border-radius: 50%; }
474
+ .sse-dot.connected { background: #3fb950; }
475
+ .sse-dot.disconnected { background: #f85149; }
476
+ .large-graph-notice { background: #1c2333; border: 1px solid #30363d; border-radius: 6px; padding: 8px 12px; margin-bottom: 12px; font-size: 11px; color: #d29922; display: none; }
477
+ </style>
478
+ </head>
479
+ <body>
480
+ <div class="container">
481
+ <div class="sidebar">
482
+ <h1>SDD Knowledge Graph</h1>
483
+ <div class="sse-status" id="sse-status">
484
+ <span class="sse-dot disconnected" id="sse-dot"></span>
485
+ <span id="sse-text">Disconnected</span>
486
+ </div>
487
+ <div class="progress-panel" id="progress-panel">
488
+ <div class="progress-header">
489
+ <span class="progress-title" id="progress-title">Building...</span>
490
+ <span class="progress-percent" id="progress-percent">0%</span>
491
+ </div>
492
+ <div class="progress-bar-bg">
493
+ <div class="progress-bar" id="progress-bar" style="width: 0%"></div>
494
+ </div>
495
+ <div class="progress-step" id="progress-step">Initializing...</div>
496
+ <div class="progress-log" id="progress-log"></div>
497
+ </div>
498
+ <input id="search" class="search" type="text" placeholder="Search nodes...">
499
+ <div id="filters" class="filter-bar"></div>
500
+ <div id="legend" class="legend"></div>
501
+ <h2>Nodes (<span id="node-count">0</span>)</h2>
502
+ <ul id="node-list" class="node-list"></ul>
503
+ </div>
504
+ <div class="main">
505
+ <div id="graph-container"></div>
506
+ <div class="graph-controls">
507
+ <button class="graph-btn" onclick="resetView()" title="Reset view">&#x21bb;</button>
508
+ <button class="graph-btn" onclick="zoomIn()" title="Zoom in">+</button>
509
+ <button class="graph-btn" onclick="zoomOut()" title="Zoom out">&minus;</button>
510
+ </div>
511
+ <div class="graph-overlay" id="graph-overlay">Loading...</div>
512
+ </div>
513
+ <div class="details" id="details">
514
+ <h2>Node Details</h2>
515
+ <p style="color: #8b949e; font-size: 13px;">Click a node to view details</p>
516
+ </div>
517
+ </div>
518
+ <script src="https://unpkg.com/3d-force-graph@1.73.4/dist/3d-force-graph.min.js"></script>
519
+ <script>
520
+ var TYPE_COLORS = {
521
+ project: "#58a6ff", feature: "#3fb950", requirement: "#d29922",
522
+ entity: "#bc8cff", task: "#f85149", change: "#f0883e",
523
+ architecture_component: "#79c0ff", test: "#56d364", business_rule: "#f778ba",
524
+ actor: "#d2a8ff", flow: "#79c0ff", use_case: "#56d364", module: "#d29922",
525
+ api: "#79c0ff", endpoint: "#58a6ff", database: "#bc8cff", table: "#d2a8ff",
526
+ field: "#8b949e", file: "#8b949e", symbol: "#8b949e",
527
+ decision: "#f0883e", constraint: "#da3633", assumption: "#d29922",
528
+ value_object: "#d2a8ff", domain: "#58a6ff",
529
+ };
530
+
531
+ var graphData = { nodes: [], links: [] };
532
+ var allNodes = [];
533
+ var allLinks = [];
534
+ var selectedNodeId = null;
535
+ var activeFilter = null;
536
+ var searchQuery = "";
537
+ var fg = null;
538
+ // Use counter-based hash instead of string concatenation (O(1) for large graphs)
539
+ var lastNodeCount = 0;
540
+ var lastRelCount = 0;
541
+ var lastUpdatedAt = "";
542
+ var isLargeGraph = false;
543
+ var LARGE_GRAPH_THRESHOLD = 2000;
544
+
545
+ function getColor(type) { return TYPE_COLORS[type] || "#8b949e"; }
546
+
547
+ // Helper to safely extract id from node ref (may be object or string after graphData())
548
+ function linkSourceId(l) { return (typeof l.source === "object" && l.source !== null) ? l.source.id : l.source; }
549
+ function linkTargetId(l) { return (typeof l.target === "object" && l.target !== null) ? l.target.id : l.target; }
550
+
551
+ function loadGraph() {
552
+ fetch("/api/graph")
553
+ .then(function(res) { return res.json(); })
554
+ .then(function(data) {
555
+ var nodeCount = data.nodeCount || data.nodes.length;
556
+ var relCount = data.relCount || data.relationships.length;
557
+ var updatedAt = data.updated_at || "";
558
+
559
+ // O(1) change detection: compare counters + timestamp
560
+ var changed = nodeCount !== lastNodeCount || relCount !== lastRelCount || updatedAt !== lastUpdatedAt;
561
+ lastNodeCount = nodeCount;
562
+ lastRelCount = relCount;
563
+ lastUpdatedAt = updatedAt;
564
+
565
+ isLargeGraph = nodeCount > LARGE_GRAPH_THRESHOLD;
566
+
567
+ allNodes = data.nodes;
568
+ allLinks = data.relationships;
569
+
570
+ if (changed) {
571
+ applyFilters();
572
+ }
573
+ renderFilters();
574
+ renderLegend();
575
+ renderNodeList();
576
+
577
+ var overlayText = nodeCount + " nodes, " + relCount + " links";
578
+ if (isLargeGraph) overlayText += " (large graph mode)";
579
+ document.getElementById("graph-overlay").textContent = overlayText;
580
+ })
581
+ .catch(function(e) { console.error("Failed to load graph:", e); });
582
+ }
583
+
584
+ function applyFilters() {
585
+ var filteredNodes = allNodes;
586
+ if (activeFilter) filteredNodes = filteredNodes.filter(function(n) { return n.type === activeFilter; });
587
+ if (searchQuery) {
588
+ var q = searchQuery.toLowerCase();
589
+ filteredNodes = filteredNodes.filter(function(n) {
590
+ return n.name.toLowerCase().indexOf(q) !== -1 || n.id.toLowerCase().indexOf(q) !== -1 || n.type.toLowerCase().indexOf(q) !== -1;
591
+ });
592
+ }
593
+ var nodeIds = new Set(filteredNodes.map(function(n) { return n.id; }));
594
+ var filteredLinks = allLinks.filter(function(l) { return nodeIds.has(l.from) && nodeIds.has(l.to); });
595
+ var newData = {
596
+ nodes: filteredNodes,
597
+ links: filteredLinks.map(function(r) { return { id: r.id, source: r.from, target: r.to, type: r.type }; })
598
+ };
599
+
600
+ // For large graphs, limit nodes rendered in 3D to keep performance smooth
601
+ if (isLargeGraph && newData.nodes.length > 500) {
602
+ var renderLimit = 500;
603
+ // Prioritize nodes connected to selected node, then by type diversity
604
+ if (selectedNodeId) {
605
+ var connected = [];
606
+ var notConnected = [];
607
+ newData.nodes.forEach(function(n) {
608
+ if (n.id === selectedNodeId) { connected.unshift(n); return; }
609
+ var isConn = newData.links.some(function(l) {
610
+ return (l.source === selectedNodeId && l.target === n.id) || (l.target === selectedNodeId && l.source === n.id);
611
+ });
612
+ if (isConn) connected.push(n);
613
+ else notConnected.push(n);
614
+ });
615
+ newData.nodes = connected.concat(notConnected).slice(0, renderLimit);
616
+ } else {
617
+ // Sample: keep first N to maintain type diversity
618
+ newData.nodes = newData.nodes.slice(0, renderLimit);
619
+ }
620
+ // Only keep links between visible nodes
621
+ var visIds = new Set(newData.nodes.map(function(n) { return n.id; }));
622
+ newData.links = newData.links.filter(function(l) { return visIds.has(l.source) && visIds.has(l.target); });
623
+ }
624
+
625
+ if (fg) {
626
+ updateGraphData(newData);
627
+ } else {
628
+ graphData = newData;
629
+ }
630
+ }
631
+
632
+ function updateGraphData(newData) {
633
+ if (!fg) return;
634
+ // Preserve positions of existing nodes to avoid restarting the simulation
635
+ var posMap = {};
636
+ graphData.nodes.forEach(function(n) {
637
+ if (n.x !== undefined) {
638
+ posMap[n.id] = { x: n.x, y: n.y, z: n.z, vx: n.vx, vy: n.vy, vz: n.vz };
639
+ }
640
+ });
641
+ newData.nodes.forEach(function(n) {
642
+ var pos = posMap[n.id];
643
+ if (pos) {
644
+ n.x = pos.x; n.y = pos.y; n.z = pos.z;
645
+ n.vx = pos.vx; n.vy = pos.vy; n.vz = pos.vz;
646
+ }
647
+ });
648
+ graphData = newData;
649
+ fg.graphData(graphData);
650
+ // Re-apply highlight if a node is selected
651
+ if (selectedNodeId) {
652
+ highlightNode(selectedNodeId);
653
+ }
654
+ }
655
+
656
+ function renderFilters() {
657
+ // Build type counts in a single pass (O(n) instead of O(n*types))
658
+ var typeCounts = {};
659
+ var types = [];
660
+ allNodes.forEach(function(n) {
661
+ if (!typeCounts[n.type]) { typeCounts[n.type] = 0; types.push(n.type); }
662
+ typeCounts[n.type]++;
663
+ });
664
+ var el = document.getElementById("filters");
665
+ var html = '<button class="filter-btn active" data-filter="all">All (' + allNodes.length + ')</button>';
666
+ types.forEach(function(t) {
667
+ html += '<button class="filter-btn" data-filter="' + t + '">' + t + " (" + typeCounts[t] + ")</button>";
668
+ });
669
+ el.innerHTML = html;
670
+ el.querySelectorAll(".filter-btn").forEach(function(btn) {
671
+ btn.addEventListener("click", function() {
672
+ var f = this.getAttribute("data-filter");
673
+ activeFilter = f === "all" ? null : f;
674
+ el.querySelectorAll(".filter-btn").forEach(function(b) { b.classList.remove("active"); });
675
+ this.classList.add("active");
676
+ applyFilters();
677
+ renderNodeList();
678
+ });
679
+ });
680
+ }
681
+
682
+ function renderLegend() {
683
+ // Build unique types in a single pass
684
+ var seen = {};
685
+ var types = [];
686
+ allNodes.forEach(function(n) { if (!seen[n.type]) { seen[n.type] = true; types.push(n.type); } });
687
+ var el = document.getElementById("legend");
688
+ el.innerHTML = types.slice(0, 10).map(function(t) {
689
+ return '<span class="legend-item"><span class="legend-dot" style="background:' + getColor(t) + '"></span>' + t + "</span>";
690
+ }).join("");
691
+ }
692
+
693
+ function renderNodeList() {
694
+ var nodes = graphData.nodes;
695
+ document.getElementById("node-count").textContent = isLargeGraph ? (lastNodeCount + "+") : nodes.length;
696
+ var el = document.getElementById("node-list");
697
+ // For large graphs, limit sidebar list to 200 items
698
+ var listLimit = isLargeGraph ? 200 : nodes.length;
699
+ var html = "";
700
+ var displayNodes = nodes.slice(0, listLimit);
701
+ displayNodes.forEach(function(n) {
702
+ var sel = selectedNodeId === n.id ? " selected" : "";
703
+ html += '<li class="node-item' + sel + '" data-id="' + n.id + '">' +
704
+ '<div class="node-id">' + n.id + "</div>" +
705
+ '<div class="node-name">' + n.name + '<span class="node-status status-' + n.status + '">' + n.status + "</span></div>" +
706
+ "</li>";
707
+ });
708
+ if (isLargeGraph && nodes.length > listLimit) {
709
+ html += '<li style="padding:8px;color:#8b949e;font-size:11px;text-align:center">... ' + (nodes.length - listLimit) + " more (use search to filter)</li>";
710
+ }
711
+ el.innerHTML = html;
712
+ el.querySelectorAll(".node-item").forEach(function(item) {
713
+ item.addEventListener("click", function() {
714
+ selectNode(this.getAttribute("data-id"));
715
+ });
716
+ });
717
+ }
718
+
719
+ function selectNode(id) {
720
+ selectedNodeId = id;
721
+ renderNodeList();
722
+ highlightNode(id);
723
+ fetch("/api/nodes/" + id)
724
+ .then(function(res) { return res.json(); })
725
+ .then(function(node) {
726
+ return fetch("/api/nodes/" + id + "/relationships")
727
+ .then(function(res2) { return res2.json(); })
728
+ .then(function(rels) { renderDetails(node, rels); centerOnNode(id); });
729
+ })
730
+ .catch(function(e) { console.error("Failed to load node:", e); });
731
+ }
732
+
733
+ function renderDetails(node, rels) {
734
+ var el = document.getElementById("details");
735
+ var outgoing = rels.filter(function(r) { return r.from === node.id; });
736
+ var incoming = rels.filter(function(r) { return r.to === node.id; });
737
+ var html = "<h2>Node Details</h2>" +
738
+ '<div class="detail-section"><h3>Info</h3>' +
739
+ '<div class="detail-field"><span class="label">ID:</span> ' + node.id + "</div>" +
740
+ '<div class="detail-field"><span class="label">Type:</span> <span class="legend-dot" style="background:' + getColor(node.type) + ';display:inline-block;vertical-align:middle;margin-right:4px"></span>' + node.type + "</div>" +
741
+ '<div class="detail-field"><span class="label">Name:</span> ' + node.name + "</div>" +
742
+ '<div class="detail-field"><span class="label">Status:</span> <span class="node-status status-' + node.status + '">' + node.status + "</span></div>" +
743
+ '<div class="detail-field"><span class="label">Version:</span> ' + node.version + "</div>";
744
+ if (node.description) html += '<div class="detail-field"><span class="label">Description:</span> ' + node.description + "</div>";
745
+ if (node.metadata && Object.keys(node.metadata).length > 0) {
746
+ html += '<div class="detail-field"><span class="label">Metadata:</span> <pre style="font-size:11px;color:#8b949e;white-space:pre-wrap;margin-top:4px">' + JSON.stringify(node.metadata, null, 2) + "</pre></div>";
747
+ }
748
+ html += "</div>";
749
+ if (outgoing.length) {
750
+ html += '<div class="detail-section"><h3>Outgoing (' + outgoing.length + ')</h3>';
751
+ outgoing.forEach(function(r) { html += '<div class="detail-field" style="cursor:pointer" data-goto="' + r.to + '">' + r.type + " &rarr; " + r.to + "</div>"; });
752
+ html += "</div>";
753
+ }
754
+ if (incoming.length) {
755
+ html += '<div class="detail-section"><h3>Incoming (' + incoming.length + ')</h3>';
756
+ incoming.forEach(function(r) { html += '<div class="detail-field" style="cursor:pointer" data-goto="' + r.from + '">' + r.type + " &larr; " + r.from + "</div>"; });
757
+ html += "</div>";
758
+ }
759
+ el.innerHTML = html;
760
+ el.querySelectorAll("[data-goto]").forEach(function(el) {
761
+ el.addEventListener("click", function() { selectNode(this.getAttribute("data-goto")); });
762
+ });
763
+ }
764
+
765
+ function highlightNode(id) {
766
+ if (!fg) return;
767
+ var connectedIds = new Set();
768
+ if (id) {
769
+ connectedIds.add(id);
770
+ allLinks.forEach(function(l) {
771
+ var from = l.from || l.source;
772
+ var to = l.to || l.target;
773
+ if (from === id) connectedIds.add(to);
774
+ if (to === id) connectedIds.add(from);
775
+ });
776
+ }
777
+ fg.nodeColor(function(n) {
778
+ if (!id) return getColor(n.type);
779
+ return connectedIds.has(n.id) ? getColor(n.type) : getColor(n.type);
780
+ });
781
+ fg.nodeOpacity(function(n) {
782
+ if (!id) return 0.9;
783
+ return connectedIds.has(n.id) ? 1.0 : 0.6;
784
+ });
785
+ fg.linkOpacity(function(l) {
786
+ var sid = linkSourceId(l);
787
+ var tid = linkTargetId(l);
788
+ if (!id) return 0.6;
789
+ return (sid === id || tid === id) ? 0.9 : 0.15;
790
+ });
791
+ fg.linkWidth(function(l) {
792
+ var sid = linkSourceId(l);
793
+ var tid = linkTargetId(l);
794
+ if (!id) return 1;
795
+ return (sid === id || tid === id) ? 2.5 : 0.5;
796
+ });
797
+ }
798
+
799
+ function clearHighlight() {
800
+ selectedNodeId = null;
801
+ if (!fg) return;
802
+ fg.nodeColor(function(n) { return getColor(n.type); });
803
+ fg.nodeOpacity(0.9);
804
+ fg.linkOpacity(0.6);
805
+ fg.linkWidth(1);
806
+ fg.linkDirectionalParticles(0);
807
+ }
808
+
809
+ function centerOnNode(id) {
810
+ if (!fg) return;
811
+ var node = graphData.nodes.find(function(n) { return n.id === id; });
812
+ if (node && node.x !== undefined) {
813
+ var distance = 120;
814
+ var distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z);
815
+ fg.cameraPosition(
816
+ { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio },
817
+ { x: node.x, y: node.y, z: node.z },
818
+ 600
819
+ );
820
+ }
821
+ }
822
+
823
+ function resetView() {
824
+ clearHighlight();
825
+ selectedNodeId = null;
826
+ renderNodeList();
827
+ document.getElementById("details").innerHTML = '<h2>Node Details</h2><p style="color:#8b949e;font-size:13px">Click a node to view details</p>';
828
+ if (fg) fg.cameraPosition({ x: 0, y: 0, z: 500 }, { x: 0, y: 0, z: 0 }, 600);
829
+ }
830
+
831
+ function zoomIn() { if (fg) fg.cameraPosition({ z: fg.cameraPosition().z * 0.7 }); }
832
+ function zoomOut() { if (fg) fg.cameraPosition({ z: fg.cameraPosition().z * 1.4 }); }
833
+
834
+ function initGraph() {
835
+ var container = document.getElementById("graph-container");
836
+ fg = ForceGraph3D()(container)
837
+ .graphData(graphData)
838
+ .backgroundColor("#0d1117")
839
+ .width(container.offsetWidth)
840
+ .height(container.offsetHeight)
841
+ .nodeVal(function(n) { return selectedNodeId === n.id ? 12 : 6; })
842
+ .nodeColor(function(n) { return getColor(n.type); })
843
+ .nodeOpacity(0.9)
844
+ .nodeResolution(isLargeGraph ? 8 : 24)
845
+ .nodeLabel(function(n) { return "<b>" + n.name + "</b><br/>" + n.type + " [" + n.status + "]"; })
846
+ .onNodeClick(function(node) { selectNode(node.id); })
847
+ .onBackgroundClick(function() {
848
+ clearHighlight();
849
+ renderNodeList();
850
+ document.getElementById("details").innerHTML = '<h2>Node Details</h2><p style="color:#8b949e;font-size:13px">Click a node to view details</p>';
851
+ })
852
+ .linkColor(function() { return "#30363d"; })
853
+ .linkOpacity(0.6)
854
+ .linkWidth(1)
855
+ .linkLabel(function(l) { return l.type; })
856
+ .linkDirectionalParticles(0)
857
+ .d3AlphaDecay(isLargeGraph ? 0.05 : 0.02)
858
+ .d3VelocityDecay(isLargeGraph ? 0.5 : 0.3)
859
+ .enablePointerInteraction(true)
860
+ .showNavInfo(false);
861
+
862
+ window.addEventListener("resize", function() {
863
+ if (fg && container) {
864
+ fg.width(container.offsetWidth);
865
+ fg.height(container.offsetHeight);
866
+ }
867
+ });
868
+ }
869
+
870
+ document.getElementById("search").addEventListener("input", function(e) {
871
+ searchQuery = e.target.value;
872
+ applyFilters();
873
+ renderNodeList();
874
+ });
875
+
876
+ initGraph();
877
+ loadGraph();
878
+ // Poll every 10s using lightweight counter-based change detection
879
+ setInterval(loadGraph, 10000);
880
+
881
+ // ─── SSE Real-time Progress ──────────────────────────────────────
882
+ var progressPanel = document.getElementById("progress-panel");
883
+ var progressTitle = document.getElementById("progress-title");
884
+ var progressPercent = document.getElementById("progress-percent");
885
+ var progressBar = document.getElementById("progress-bar");
886
+ var progressStep = document.getElementById("progress-step");
887
+ var progressLog = document.getElementById("progress-log");
888
+ var sseDot = document.getElementById("sse-dot");
889
+ var sseText = document.getElementById("sse-text");
890
+ var eventSource = null;
891
+
892
+ function connectSSE() {
893
+ if (eventSource) {
894
+ eventSource.close();
895
+ }
896
+ eventSource = new EventSource("/api/events");
897
+
898
+ eventSource.onopen = function() {
899
+ sseDot.className = "sse-dot connected";
900
+ sseText.textContent = "Connected";
901
+ };
902
+
903
+ eventSource.onerror = function() {
904
+ var readyState = eventSource ? eventSource.readyState : -1;
905
+ // 0=CONNECTING, 1=OPEN, 2=CLOSED
906
+ if (readyState === 2) {
907
+ // Connection permanently closed — manual reconnect needed
908
+ sseDot.className = "sse-dot disconnected";
909
+ sseText.textContent = "Disconnected";
910
+ setTimeout(connectSSE, 3000);
911
+ } else if (readyState === 0) {
912
+ // Browser is auto-reconnecting, show status but don't create new instance
913
+ sseDot.className = "sse-dot disconnected";
914
+ sseText.textContent = "Reconnecting...";
915
+ }
916
+ // readyState === 1 (OPEN): transient error, connection still alive — do nothing
917
+ };
918
+
919
+ eventSource.onmessage = function(e) {
920
+ try {
921
+ var event = JSON.parse(e.data);
922
+ handleProgressEvent(event);
923
+ } catch (err) {
924
+ console.error("Failed to parse SSE event:", err);
925
+ }
926
+ };
927
+ }
928
+
929
+ function handleProgressEvent(event) {
930
+ // Update progress panel for build events
931
+ if (event.type === "info" && event.step === "build_start") {
932
+ progressPanel.classList.add("active");
933
+ progressTitle.textContent = "Building Graph...";
934
+ progressPercent.textContent = "0%";
935
+ progressBar.style.width = "0%";
936
+ progressStep.textContent = event.message;
937
+ progressLog.innerHTML = "";
938
+ addLogEntry("Build started: " + (event.details ? event.details.buildId : ""));
939
+ return;
940
+ }
941
+
942
+ if (event.type === "step") {
943
+ progressPanel.classList.add("active");
944
+ progressTitle.textContent = "Building Graph...";
945
+ if (event.progress !== undefined) {
946
+ progressPercent.textContent = event.progress + "%";
947
+ progressBar.style.width = event.progress + "%";
948
+ }
949
+ progressStep.textContent = event.message;
950
+ addLogEntry("[STEP] " + event.message);
951
+ return;
952
+ }
953
+
954
+ if (event.type === "progress") {
955
+ if (event.progress !== undefined) {
956
+ progressPercent.textContent = event.progress + "%";
957
+ progressBar.style.width = event.progress + "%";
958
+ }
959
+ progressStep.textContent = event.message;
960
+ return;
961
+ }
962
+
963
+ if (event.type === "error") {
964
+ addLogEntry("[ERROR] " + event.message, "error");
965
+ return;
966
+ }
967
+
968
+ if (event.type === "complete") {
969
+ progressTitle.textContent = "Build Complete";
970
+ progressPercent.textContent = "100%";
971
+ progressBar.style.width = "100%";
972
+ progressStep.textContent = event.message;
973
+ addLogEntry("[DONE] " + event.message, "complete");
974
+ // Refresh graph after build completes
975
+ setTimeout(loadGraph, 500);
976
+ // Hide panel after 5 seconds
977
+ setTimeout(function() {
978
+ progressPanel.classList.remove("active");
979
+ }, 5000);
980
+ return;
981
+ }
982
+
983
+ // Handle other events
984
+ if (event.type === "connected") {
985
+ addLogEntry("SSE connected");
986
+ }
987
+ if (event.type === "build_status") {
988
+ progressPanel.classList.add("active");
989
+ progressTitle.textContent = "Build in Progress...";
990
+ if (event.data && event.data.progress !== undefined) {
991
+ progressPercent.textContent = event.data.progress + "%";
992
+ progressBar.style.width = event.data.progress + "%";
993
+ }
994
+ if (event.data && event.data.currentStep) {
995
+ progressStep.textContent = event.data.currentStep;
996
+ }
997
+ }
998
+ }
999
+
1000
+ function addLogEntry(text, className) {
1001
+ var entry = document.createElement("div");
1002
+ entry.className = "progress-log-entry" + (className ? " " + className : "");
1003
+ var time = new Date().toLocaleTimeString();
1004
+ entry.textContent = "[" + time + "] " + text;
1005
+ progressLog.appendChild(entry);
1006
+ progressLog.scrollTop = progressLog.scrollHeight;
1007
+ }
1008
+
1009
+ // Start SSE connection
1010
+ connectSSE();
1011
+ </script>
1012
+ </body>
1013
+ </html>`;
1014
+ return new Response(html, {
1015
+ headers: { "Content-Type": "text/html", ...headers },
1016
+ });
1017
+ }
1018
+ }
1019
+ //# sourceMappingURL=server.js.map