@tyroneross/navgator 0.2.0 → 0.2.2

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 (362) hide show
  1. package/.claude-plugin/marketplace.json +23 -0
  2. package/.claude-plugin/plugin.json +10 -7
  3. package/CLAUDE.md +72 -0
  4. package/README.md +49 -80
  5. package/dist/cli/index.js +1 -1
  6. package/hooks/hooks.json +5 -5
  7. package/package.json +5 -3
  8. package/scripts/install-plugin.sh +98 -0
  9. package/skills/check/SKILL.md +64 -0
  10. package/skills/connections/SKILL.md +54 -0
  11. package/skills/diagram/SKILL.md +64 -0
  12. package/skills/export/SKILL.md +49 -0
  13. package/skills/impact/SKILL.md +58 -0
  14. package/skills/install/SKILL.md +94 -0
  15. package/skills/scan/SKILL.md +75 -0
  16. package/skills/status/SKILL.md +37 -0
  17. package/skills/ui/SKILL.md +43 -0
  18. package/skills/update/SKILL.md +43 -0
  19. package/web/.next/standalone/web/.next/BUILD_ID +1 -0
  20. package/web/.next/standalone/web/.next/app-path-routes-manifest.json +13 -0
  21. package/web/.next/standalone/web/.next/build-manifest.json +20 -0
  22. package/web/.next/standalone/web/.next/package.json +1 -0
  23. package/web/.next/standalone/web/.next/prerender-manifest.json +88 -0
  24. package/web/.next/standalone/web/.next/required-server-files.json +317 -0
  25. package/web/.next/standalone/web/.next/routes-manifest.json +110 -0
  26. package/web/.next/standalone/web/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
  27. package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +17 -0
  28. package/web/.next/standalone/web/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
  29. package/web/.next/standalone/web/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
  30. package/web/.next/standalone/web/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
  31. package/web/.next/standalone/web/.next/server/app/_global-error/page.js +10 -0
  32. package/web/.next/standalone/web/.next/server/app/_global-error/page.js.map +5 -0
  33. package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -0
  34. package/web/.next/standalone/web/.next/server/app/_global-error/page_client-reference-manifest.js +2 -0
  35. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -0
  36. package/web/.next/standalone/web/.next/server/app/_global-error.meta +15 -0
  37. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +12 -0
  38. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
  39. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +12 -0
  40. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +7 -0
  41. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +4 -0
  42. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  43. package/web/.next/standalone/web/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
  44. package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +17 -0
  45. package/web/.next/standalone/web/.next/server/app/_not-found/page/next-font-manifest.json +11 -0
  46. package/web/.next/standalone/web/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
  47. package/web/.next/standalone/web/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
  48. package/web/.next/standalone/web/.next/server/app/_not-found/page.js +13 -0
  49. package/web/.next/standalone/web/.next/server/app/_not-found/page.js.map +5 -0
  50. package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  51. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +2 -0
  52. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -0
  53. package/web/.next/standalone/web/.next/server/app/_not-found.meta +16 -0
  54. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +17 -0
  55. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +17 -0
  56. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +8 -0
  57. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +8 -0
  58. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
  59. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +4 -0
  60. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -0
  61. package/web/.next/standalone/web/.next/server/app/api/components/route/app-paths-manifest.json +3 -0
  62. package/web/.next/standalone/web/.next/server/app/api/components/route/build-manifest.json +11 -0
  63. package/web/.next/standalone/web/.next/server/app/api/components/route/server-reference-manifest.json +4 -0
  64. package/web/.next/standalone/web/.next/server/app/api/components/route.js +6 -0
  65. package/web/.next/standalone/web/.next/server/app/api/components/route.js.map +5 -0
  66. package/web/.next/standalone/web/.next/server/app/api/components/route.js.nft.json +1 -0
  67. package/web/.next/standalone/web/.next/server/app/api/components/route_client-reference-manifest.js +2 -0
  68. package/web/.next/standalone/web/.next/server/app/api/connections/route/app-paths-manifest.json +3 -0
  69. package/web/.next/standalone/web/.next/server/app/api/connections/route/build-manifest.json +11 -0
  70. package/web/.next/standalone/web/.next/server/app/api/connections/route/server-reference-manifest.json +4 -0
  71. package/web/.next/standalone/web/.next/server/app/api/connections/route.js +6 -0
  72. package/web/.next/standalone/web/.next/server/app/api/connections/route.js.map +5 -0
  73. package/web/.next/standalone/web/.next/server/app/api/connections/route.js.nft.json +1 -0
  74. package/web/.next/standalone/web/.next/server/app/api/connections/route_client-reference-manifest.js +2 -0
  75. package/web/.next/standalone/web/.next/server/app/api/graph/route/app-paths-manifest.json +3 -0
  76. package/web/.next/standalone/web/.next/server/app/api/graph/route/build-manifest.json +11 -0
  77. package/web/.next/standalone/web/.next/server/app/api/graph/route/server-reference-manifest.json +4 -0
  78. package/web/.next/standalone/web/.next/server/app/api/graph/route.js +6 -0
  79. package/web/.next/standalone/web/.next/server/app/api/graph/route.js.map +5 -0
  80. package/web/.next/standalone/web/.next/server/app/api/graph/route.js.nft.json +1 -0
  81. package/web/.next/standalone/web/.next/server/app/api/graph/route_client-reference-manifest.js +2 -0
  82. package/web/.next/standalone/web/.next/server/app/api/projects/route/app-paths-manifest.json +3 -0
  83. package/web/.next/standalone/web/.next/server/app/api/projects/route/build-manifest.json +11 -0
  84. package/web/.next/standalone/web/.next/server/app/api/projects/route/server-reference-manifest.json +4 -0
  85. package/web/.next/standalone/web/.next/server/app/api/projects/route.js +6 -0
  86. package/web/.next/standalone/web/.next/server/app/api/projects/route.js.map +5 -0
  87. package/web/.next/standalone/web/.next/server/app/api/projects/route.js.nft.json +1 -0
  88. package/web/.next/standalone/web/.next/server/app/api/projects/route_client-reference-manifest.js +2 -0
  89. package/web/.next/standalone/web/.next/server/app/api/prompts/route/app-paths-manifest.json +3 -0
  90. package/web/.next/standalone/web/.next/server/app/api/prompts/route/build-manifest.json +11 -0
  91. package/web/.next/standalone/web/.next/server/app/api/prompts/route/server-reference-manifest.json +4 -0
  92. package/web/.next/standalone/web/.next/server/app/api/prompts/route.js +7 -0
  93. package/web/.next/standalone/web/.next/server/app/api/prompts/route.js.map +5 -0
  94. package/web/.next/standalone/web/.next/server/app/api/prompts/route.js.nft.json +1 -0
  95. package/web/.next/standalone/web/.next/server/app/api/prompts/route_client-reference-manifest.js +2 -0
  96. package/web/.next/standalone/web/.next/server/app/api/scan/route/app-paths-manifest.json +3 -0
  97. package/web/.next/standalone/web/.next/server/app/api/scan/route/build-manifest.json +11 -0
  98. package/web/.next/standalone/web/.next/server/app/api/scan/route/server-reference-manifest.json +4 -0
  99. package/web/.next/standalone/web/.next/server/app/api/scan/route.js +6 -0
  100. package/web/.next/standalone/web/.next/server/app/api/scan/route.js.map +5 -0
  101. package/web/.next/standalone/web/.next/server/app/api/scan/route.js.nft.json +1 -0
  102. package/web/.next/standalone/web/.next/server/app/api/scan/route_client-reference-manifest.js +2 -0
  103. package/web/.next/standalone/web/.next/server/app/api/settings/route/app-paths-manifest.json +3 -0
  104. package/web/.next/standalone/web/.next/server/app/api/settings/route/build-manifest.json +11 -0
  105. package/web/.next/standalone/web/.next/server/app/api/settings/route/server-reference-manifest.json +4 -0
  106. package/web/.next/standalone/web/.next/server/app/api/settings/route.js +6 -0
  107. package/web/.next/standalone/web/.next/server/app/api/settings/route.js.map +5 -0
  108. package/web/.next/standalone/web/.next/server/app/api/settings/route.js.nft.json +1 -0
  109. package/web/.next/standalone/web/.next/server/app/api/settings/route_client-reference-manifest.js +2 -0
  110. package/web/.next/standalone/web/.next/server/app/api/status/route/app-paths-manifest.json +3 -0
  111. package/web/.next/standalone/web/.next/server/app/api/status/route/build-manifest.json +11 -0
  112. package/web/.next/standalone/web/.next/server/app/api/status/route/server-reference-manifest.json +4 -0
  113. package/web/.next/standalone/web/.next/server/app/api/status/route.js +6 -0
  114. package/web/.next/standalone/web/.next/server/app/api/status/route.js.map +5 -0
  115. package/web/.next/standalone/web/.next/server/app/api/status/route.js.nft.json +1 -0
  116. package/web/.next/standalone/web/.next/server/app/api/status/route_client-reference-manifest.js +2 -0
  117. package/web/.next/standalone/web/.next/server/app/index.html +1 -0
  118. package/web/.next/standalone/web/.next/server/app/index.meta +14 -0
  119. package/web/.next/standalone/web/.next/server/app/index.rsc +23 -0
  120. package/web/.next/standalone/web/.next/server/app/index.segments/__PAGE__.segment.rsc +9 -0
  121. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +23 -0
  122. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +8 -0
  123. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +8 -0
  124. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +5 -0
  125. package/web/.next/standalone/web/.next/server/app/page/app-paths-manifest.json +3 -0
  126. package/web/.next/standalone/web/.next/server/app/page/build-manifest.json +17 -0
  127. package/web/.next/standalone/web/.next/server/app/page/next-font-manifest.json +11 -0
  128. package/web/.next/standalone/web/.next/server/app/page/react-loadable-manifest.json +1 -0
  129. package/web/.next/standalone/web/.next/server/app/page/server-reference-manifest.json +4 -0
  130. package/web/.next/standalone/web/.next/server/app/page.js +15 -0
  131. package/web/.next/standalone/web/.next/server/app/page.js.map +5 -0
  132. package/web/.next/standalone/web/.next/server/app/page.js.nft.json +1 -0
  133. package/web/.next/standalone/web/.next/server/app/page_client-reference-manifest.js +2 -0
  134. package/web/.next/standalone/web/.next/server/app-paths-manifest.json +13 -0
  135. package/web/.next/standalone/web/.next/server/chunks/2374f_next_dist_esm_build_templates_app-route_0bb4e66a.js +19 -0
  136. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__006b837d._.js +3 -0
  137. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__1ab91221._.js +3 -0
  138. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__2e09fec9._.js +3 -0
  139. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__38d0390f._.js +3 -0
  140. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__539ef30d._.js +3 -0
  141. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__594bcf20._.js +3 -0
  142. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__b888fadf._.js +3 -0
  143. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__f3450c22._.js +21 -0
  144. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__fa2ec862._.js +3 -0
  145. package/web/.next/standalone/web/.next/server/chunks/[turbopack]_runtime.js +770 -0
  146. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_e6039567._.js +6 -0
  147. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_09351209._.js +6 -0
  148. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_222be7ae._.js +6 -0
  149. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_2346e1b3._.js +3 -0
  150. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_cec86455._.js +4 -0
  151. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_9c5d1a14._.js +3 -0
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_builtin_forbidden_8eae0c85.js +3 -0
  153. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_builtin_global-error_81159d60.js +3 -0
  154. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_builtin_unauthorized_7d34a31c.js +3 -0
  155. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_e03afa0e._.js +3 -0
  156. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_esm_build_templates_app-page_2c8d71b9.js +4 -0
  157. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_server_route-modules_app-page_vendored_ssr_react-dom_8910f04c.js +3 -0
  158. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__1a0663e6._.js +3 -0
  159. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__4306eafc._.js +3 -0
  160. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__44903626._.js +3 -0
  161. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__60278e3f._.js +3 -0
  162. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__61942f24._.js +4 -0
  163. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__8c45c3c9._.js +3 -0
  164. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9a2f110d._.js +3 -0
  165. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__ed07bd88._.js +3 -0
  166. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f2db61af._.js +3 -0
  167. package/web/.next/standalone/web/.next/server/chunks/ssr/[turbopack]_runtime.js +770 -0
  168. package/web/.next/standalone/web/.next/server/chunks/ssr/web_0103e631._.js +4 -0
  169. package/web/.next/standalone/web/.next/server/chunks/ssr/web_171de0df._.js +14 -0
  170. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app__global-error_page_actions_2a1e94d4.js +3 -0
  171. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app__not-found_page_actions_9eaa9845.js +3 -0
  172. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_page_actions_ec26bf28.js +3 -0
  173. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_components_route_actions_c88bf2a6.js +3 -0
  174. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_connections_route_actions_b1d5e95a.js +3 -0
  175. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_graph_route_actions_e2dd052c.js +3 -0
  176. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_projects_route_actions_3f671cbb.js +3 -0
  177. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_prompts_route_actions_85b56166.js +3 -0
  178. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_scan_route_actions_861cde8d.js +3 -0
  179. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_settings_route_actions_e19026ae.js +3 -0
  180. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_status_route_actions_8b8c6c89.js +3 -0
  181. package/web/.next/standalone/web/.next/server/functions-config-manifest.json +4 -0
  182. package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +21 -0
  183. package/web/.next/standalone/web/.next/server/middleware-manifest.json +6 -0
  184. package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -0
  185. package/web/.next/standalone/web/.next/server/next-font-manifest.json +15 -0
  186. package/web/.next/standalone/web/.next/server/pages/404.html +1 -0
  187. package/web/.next/standalone/web/.next/server/pages/500.html +2 -0
  188. package/web/.next/standalone/web/.next/server/pages-manifest.json +4 -0
  189. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -0
  190. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +5 -0
  191. package/web/.next/standalone/web/.next/static/P-ZMQO7_Wnj487ks3guqa/_buildManifest.js +11 -0
  192. package/web/.next/standalone/web/.next/static/P-ZMQO7_Wnj487ks3guqa/_clientMiddlewareManifest.json +1 -0
  193. package/web/.next/standalone/web/.next/static/P-ZMQO7_Wnj487ks3guqa/_ssgManifest.js +1 -0
  194. package/web/.next/standalone/web/.next/static/chunks/062ae79751df2759.js +1 -0
  195. package/web/.next/standalone/web/.next/static/chunks/159889e17b2cf1f8.js +2 -0
  196. package/web/.next/standalone/web/.next/static/chunks/458d6f37339fc069.js +1 -0
  197. package/web/.next/standalone/web/.next/static/chunks/6d3d39425a878d7f.js +1 -0
  198. package/web/.next/standalone/web/.next/static/chunks/8a80e7184ad3a13f.css +2 -0
  199. package/web/.next/standalone/web/.next/static/chunks/a6dad97d9634a72d.js +1 -0
  200. package/web/.next/standalone/web/.next/static/chunks/c056475f5f4424b6.css +1 -0
  201. package/web/.next/standalone/web/.next/static/chunks/c57fee8cce8d7cb9.js +1 -0
  202. package/web/.next/standalone/web/.next/static/chunks/cb3513192b63e480.js +12 -0
  203. package/web/.next/standalone/web/.next/static/chunks/dd22b5f2beb2cc31.js +1 -0
  204. package/web/.next/standalone/web/.next/static/chunks/e0affeef0ddb9a97.js +4 -0
  205. package/web/.next/standalone/web/.next/static/chunks/f74a6859e1c4d5e3.js +1 -0
  206. package/web/.next/standalone/web/.next/static/chunks/turbopack-c0c89f9e6f0a38c4.js +3 -0
  207. package/web/.next/standalone/web/.next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  208. package/web/.next/standalone/web/.next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  209. package/web/.next/standalone/web/.next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  210. package/web/.next/standalone/web/.next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  211. package/web/.next/standalone/web/.next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  212. package/web/.next/standalone/web/.next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  213. package/web/.next/standalone/web/app/api/components/route.ts +252 -0
  214. package/web/.next/standalone/web/app/api/connections/route.ts +319 -0
  215. package/web/.next/standalone/web/app/api/graph/route.ts +235 -0
  216. package/web/.next/standalone/web/app/api/projects/route.ts +221 -0
  217. package/web/.next/standalone/web/app/api/prompts/route.ts +328 -0
  218. package/web/.next/standalone/web/app/api/scan/route.ts +108 -0
  219. package/web/.next/standalone/web/app/api/settings/route.ts +198 -0
  220. package/web/.next/standalone/web/app/api/status/route.ts +169 -0
  221. package/web/.next/standalone/web/app/globals.css +99 -0
  222. package/web/.next/standalone/web/app/layout.tsx +49 -0
  223. package/web/.next/standalone/web/app/page.tsx +94 -0
  224. package/web/.next/standalone/web/components/components-panel.tsx +240 -0
  225. package/web/.next/standalone/web/components/connections-panel.tsx +220 -0
  226. package/web/.next/standalone/web/components/diagram-view.tsx +735 -0
  227. package/web/.next/standalone/web/components/header.tsx +225 -0
  228. package/web/.next/standalone/web/components/impact-analysis.tsx +358 -0
  229. package/web/.next/standalone/web/components/llm-tracking-panel.tsx +1483 -0
  230. package/web/.next/standalone/web/components/settings-panel.tsx +671 -0
  231. package/web/.next/standalone/web/components/sidebar.tsx +74 -0
  232. package/web/.next/standalone/web/components/status-overview.tsx +326 -0
  233. package/web/.next/standalone/web/components/theme-provider.tsx +11 -0
  234. package/web/.next/standalone/web/components/ui/accordion.tsx +66 -0
  235. package/web/.next/standalone/web/components/ui/alert-dialog.tsx +157 -0
  236. package/web/.next/standalone/web/components/ui/alert.tsx +66 -0
  237. package/web/.next/standalone/web/components/ui/aspect-ratio.tsx +11 -0
  238. package/web/.next/standalone/web/components/ui/avatar.tsx +53 -0
  239. package/web/.next/standalone/web/components/ui/badge.tsx +46 -0
  240. package/web/.next/standalone/web/components/ui/breadcrumb.tsx +109 -0
  241. package/web/.next/standalone/web/components/ui/button-group.tsx +83 -0
  242. package/web/.next/standalone/web/components/ui/button.tsx +60 -0
  243. package/web/.next/standalone/web/components/ui/calendar.tsx +213 -0
  244. package/web/.next/standalone/web/components/ui/card.tsx +92 -0
  245. package/web/.next/standalone/web/components/ui/carousel.tsx +241 -0
  246. package/web/.next/standalone/web/components/ui/chart.tsx +353 -0
  247. package/web/.next/standalone/web/components/ui/checkbox.tsx +32 -0
  248. package/web/.next/standalone/web/components/ui/collapsible.tsx +33 -0
  249. package/web/.next/standalone/web/components/ui/command.tsx +184 -0
  250. package/web/.next/standalone/web/components/ui/context-menu.tsx +252 -0
  251. package/web/.next/standalone/web/components/ui/dialog.tsx +143 -0
  252. package/web/.next/standalone/web/components/ui/drawer.tsx +135 -0
  253. package/web/.next/standalone/web/components/ui/dropdown-menu.tsx +257 -0
  254. package/web/.next/standalone/web/components/ui/empty.tsx +104 -0
  255. package/web/.next/standalone/web/components/ui/field.tsx +244 -0
  256. package/web/.next/standalone/web/components/ui/form.tsx +167 -0
  257. package/web/.next/standalone/web/components/ui/hover-card.tsx +44 -0
  258. package/web/.next/standalone/web/components/ui/input-group.tsx +169 -0
  259. package/web/.next/standalone/web/components/ui/input-otp.tsx +77 -0
  260. package/web/.next/standalone/web/components/ui/input.tsx +21 -0
  261. package/web/.next/standalone/web/components/ui/item.tsx +193 -0
  262. package/web/.next/standalone/web/components/ui/kbd.tsx +28 -0
  263. package/web/.next/standalone/web/components/ui/label.tsx +24 -0
  264. package/web/.next/standalone/web/components/ui/menubar.tsx +276 -0
  265. package/web/.next/standalone/web/components/ui/navigation-menu.tsx +166 -0
  266. package/web/.next/standalone/web/components/ui/pagination.tsx +127 -0
  267. package/web/.next/standalone/web/components/ui/popover.tsx +48 -0
  268. package/web/.next/standalone/web/components/ui/progress.tsx +31 -0
  269. package/web/.next/standalone/web/components/ui/radio-group.tsx +45 -0
  270. package/web/.next/standalone/web/components/ui/resizable.tsx +56 -0
  271. package/web/.next/standalone/web/components/ui/scroll-area.tsx +58 -0
  272. package/web/.next/standalone/web/components/ui/select.tsx +185 -0
  273. package/web/.next/standalone/web/components/ui/separator.tsx +28 -0
  274. package/web/.next/standalone/web/components/ui/sheet.tsx +139 -0
  275. package/web/.next/standalone/web/components/ui/sidebar.tsx +726 -0
  276. package/web/.next/standalone/web/components/ui/skeleton.tsx +13 -0
  277. package/web/.next/standalone/web/components/ui/slider.tsx +63 -0
  278. package/web/.next/standalone/web/components/ui/sonner.tsx +25 -0
  279. package/web/.next/standalone/web/components/ui/spinner.tsx +16 -0
  280. package/web/.next/standalone/web/components/ui/switch.tsx +31 -0
  281. package/web/.next/standalone/web/components/ui/table.tsx +116 -0
  282. package/web/.next/standalone/web/components/ui/tabs.tsx +66 -0
  283. package/web/.next/standalone/web/components/ui/textarea.tsx +18 -0
  284. package/web/.next/standalone/web/components/ui/toast.tsx +129 -0
  285. package/web/.next/standalone/web/components/ui/toaster.tsx +35 -0
  286. package/web/.next/standalone/web/components/ui/toggle-group.tsx +73 -0
  287. package/web/.next/standalone/web/components/ui/toggle.tsx +47 -0
  288. package/web/.next/standalone/web/components/ui/tooltip.tsx +61 -0
  289. package/web/.next/standalone/web/components/ui/use-mobile.tsx +19 -0
  290. package/web/.next/standalone/web/components/ui/use-toast.ts +191 -0
  291. package/web/.next/standalone/web/components.json +21 -0
  292. package/web/.next/standalone/web/hooks/use-mobile.ts +19 -0
  293. package/web/.next/standalone/web/hooks/use-toast.ts +191 -0
  294. package/web/.next/standalone/web/lib/hooks/index.ts +8 -0
  295. package/web/.next/standalone/web/lib/hooks/use-components.ts +83 -0
  296. package/web/.next/standalone/web/lib/hooks/use-connections.ts +83 -0
  297. package/web/.next/standalone/web/lib/hooks/use-projects.ts +116 -0
  298. package/web/.next/standalone/web/lib/hooks/use-prompts.ts +122 -0
  299. package/web/.next/standalone/web/lib/hooks/use-settings.ts +135 -0
  300. package/web/.next/standalone/web/lib/hooks/use-status.ts +80 -0
  301. package/web/.next/standalone/web/lib/project-context.tsx +46 -0
  302. package/web/.next/standalone/web/lib/transform.ts +625 -0
  303. package/web/.next/standalone/web/lib/types.ts +198 -0
  304. package/web/.next/standalone/web/lib/utils.ts +6 -0
  305. package/web/.next/standalone/web/next.config.mjs +12 -0
  306. package/web/.next/standalone/web/package-lock.json +3987 -0
  307. package/web/.next/standalone/web/package.json +73 -0
  308. package/web/.next/standalone/web/pnpm-lock.yaml +5 -0
  309. package/web/.next/standalone/web/postcss.config.mjs +8 -0
  310. package/web/.next/standalone/web/public/apple-icon.png +0 -0
  311. package/web/.next/standalone/web/public/icon-dark-32x32.png +0 -0
  312. package/web/.next/standalone/web/public/icon-light-32x32.png +0 -0
  313. package/web/.next/standalone/web/public/icon.svg +26 -0
  314. package/web/.next/standalone/web/public/navgator-logo.png +0 -0
  315. package/web/.next/standalone/web/public/placeholder-logo.png +0 -0
  316. package/web/.next/standalone/web/public/placeholder-logo.svg +1 -0
  317. package/web/.next/standalone/web/public/placeholder-user.jpg +0 -0
  318. package/web/.next/standalone/web/public/placeholder.jpg +0 -0
  319. package/web/.next/standalone/web/public/placeholder.svg +1 -0
  320. package/web/.next/standalone/web/public/public/apple-icon.png +0 -0
  321. package/web/.next/standalone/web/public/public/icon-dark-32x32.png +0 -0
  322. package/web/.next/standalone/web/public/public/icon-light-32x32.png +0 -0
  323. package/web/.next/standalone/web/public/public/icon.svg +26 -0
  324. package/web/.next/standalone/web/public/public/navgator-logo.png +0 -0
  325. package/web/.next/standalone/web/public/public/placeholder-logo.png +0 -0
  326. package/web/.next/standalone/web/public/public/placeholder-logo.svg +1 -0
  327. package/web/.next/standalone/web/public/public/placeholder-user.jpg +0 -0
  328. package/web/.next/standalone/web/public/public/placeholder.jpg +0 -0
  329. package/web/.next/standalone/web/public/public/placeholder.svg +1 -0
  330. package/web/.next/standalone/web/server.js +38 -0
  331. package/web/.next/standalone/web/styles/globals.css +125 -0
  332. package/web/.next/standalone/web/tsconfig.json +41 -0
  333. package/web/.next/static/P-ZMQO7_Wnj487ks3guqa/_buildManifest.js +11 -0
  334. package/web/.next/static/P-ZMQO7_Wnj487ks3guqa/_clientMiddlewareManifest.json +1 -0
  335. package/web/.next/static/P-ZMQO7_Wnj487ks3guqa/_ssgManifest.js +1 -0
  336. package/web/.next/static/chunks/062ae79751df2759.js +1 -0
  337. package/web/.next/static/chunks/159889e17b2cf1f8.js +2 -0
  338. package/web/.next/static/chunks/458d6f37339fc069.js +1 -0
  339. package/web/.next/static/chunks/6d3d39425a878d7f.js +1 -0
  340. package/web/.next/static/chunks/8a80e7184ad3a13f.css +2 -0
  341. package/web/.next/static/chunks/a6dad97d9634a72d.js +1 -0
  342. package/web/.next/static/chunks/c056475f5f4424b6.css +1 -0
  343. package/web/.next/static/chunks/c57fee8cce8d7cb9.js +1 -0
  344. package/web/.next/static/chunks/cb3513192b63e480.js +12 -0
  345. package/web/.next/static/chunks/dd22b5f2beb2cc31.js +1 -0
  346. package/web/.next/static/chunks/e0affeef0ddb9a97.js +4 -0
  347. package/web/.next/static/chunks/f74a6859e1c4d5e3.js +1 -0
  348. package/web/.next/static/chunks/turbopack-c0c89f9e6f0a38c4.js +3 -0
  349. package/web/.next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  350. package/web/.next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  351. package/web/.next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  352. package/web/.next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  353. package/web/.next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  354. package/web/.next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  355. package/commands/nav-check.md +0 -64
  356. package/commands/nav-connections.md +0 -58
  357. package/commands/nav-diagram.md +0 -106
  358. package/commands/nav-export.md +0 -71
  359. package/commands/nav-impact.md +0 -58
  360. package/commands/nav-scan.md +0 -46
  361. package/commands/nav-status.md +0 -44
  362. package/skills/architecture-awareness/SKILL.md +0 -141
@@ -0,0 +1,1483 @@
1
+ "use client";
2
+
3
+ import { useState, useMemo } from "react";
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Input } from "@/components/ui/input";
8
+ import { ScrollArea } from "@/components/ui/scroll-area";
9
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
10
+ import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
11
+ import {
12
+ Search,
13
+ Brain,
14
+ MessageSquare,
15
+ FileText,
16
+ Copy,
17
+ Check,
18
+ ChevronDown,
19
+ ChevronRight,
20
+ Zap,
21
+ AlertTriangle,
22
+ Filter,
23
+ RefreshCw,
24
+ Loader2,
25
+ Info,
26
+ GitBranch,
27
+ ArrowDown,
28
+ Circle,
29
+ Layers,
30
+ X,
31
+ } from "lucide-react";
32
+ import { usePrompts } from "@/lib/hooks";
33
+ import type { LLMCall, Prompt } from "@/lib/types";
34
+
35
+ const categoryColors: Record<string, string> = {
36
+ chat: "bg-info/20 text-info border-info/30",
37
+ completion: "bg-primary/20 text-primary border-primary/30",
38
+ embedding: "bg-chart-3/20 text-chart-3 border-chart-3/30",
39
+ function: "bg-chart-4/20 text-chart-4 border-chart-4/30",
40
+ agent: "bg-warning/20 text-warning border-warning/30",
41
+ image: "bg-chart-3/20 text-chart-3 border-chart-3/30",
42
+ audio: "bg-chart-4/20 text-chart-4 border-chart-4/30",
43
+ };
44
+
45
+ const typeColors: Record<Prompt["type"], string> = {
46
+ system: "bg-info/20 text-info border-info/30",
47
+ user: "bg-primary/20 text-primary border-primary/30",
48
+ assistant: "bg-chart-3/20 text-chart-3 border-chart-3/30",
49
+ function: "bg-chart-4/20 text-chart-4 border-chart-4/30",
50
+ };
51
+
52
+ export function LLMTrackingPanel() {
53
+ // Fetch data from API (defaults to demo mode until real scan data exists)
54
+ const { calls, prompts, summary, isLoading, error, source, refresh, scan } = usePrompts({
55
+ autoFetch: true,
56
+ });
57
+
58
+ const [searchQuery, setSearchQuery] = useState("");
59
+ const [selectedCall, setSelectedCall] = useState<LLMCall | null>(null);
60
+ const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
61
+ const [expandedCalls, setExpandedCalls] = useState<Set<string>>(new Set());
62
+ const [copiedId, setCopiedId] = useState<string | null>(null);
63
+ const [categoryFilter, setCategoryFilter] = useState<string>("all");
64
+ const [providerFilter, setProviderFilter] = useState<string>("all");
65
+ const [activeTab, setActiveTab] = useState<string>("calls");
66
+ const [sheetType, setSheetType] = useState<"calls" | "prompts" | null>(null);
67
+ const [expandedProviders, setExpandedProviders] = useState<Set<string>>(new Set());
68
+ const [expandedModels, setExpandedModels] = useState<Set<string>>(new Set());
69
+ const [fileFilter, setFileFilter] = useState<string | null>(null);
70
+
71
+ // Navigate to a specific file's prompts
72
+ const navigateToFile = (file: string) => {
73
+ setFileFilter(file);
74
+ setActiveTab("prompts");
75
+ // Auto-select first prompt in this file
76
+ const firstPrompt = prompts.find((p) => p.file === file);
77
+ if (firstPrompt) {
78
+ setSelectedPrompt(firstPrompt);
79
+ }
80
+ };
81
+
82
+ const filteredCalls = calls.filter((call) => {
83
+ const matchesSearch =
84
+ call.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
85
+ call.model.toLowerCase().includes(searchQuery.toLowerCase()) ||
86
+ call.file.toLowerCase().includes(searchQuery.toLowerCase());
87
+ const matchesCategory = categoryFilter === "all" || call.category === categoryFilter;
88
+ const matchesProvider = providerFilter === "all" || call.provider === providerFilter;
89
+ return matchesSearch && matchesCategory && matchesProvider;
90
+ });
91
+
92
+ const filteredPrompts = prompts.filter((prompt) => {
93
+ const matchesSearch =
94
+ prompt.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
95
+ prompt.content.toLowerCase().includes(searchQuery.toLowerCase()) ||
96
+ prompt.file.toLowerCase().includes(searchQuery.toLowerCase());
97
+ const matchesFile = !fileFilter || prompt.file === fileFilter;
98
+ return matchesSearch && matchesFile;
99
+ });
100
+
101
+ const toggleExpanded = (id: string) => {
102
+ const next = new Set(expandedCalls);
103
+ if (next.has(id)) {
104
+ next.delete(id);
105
+ } else {
106
+ next.add(id);
107
+ }
108
+ setExpandedCalls(next);
109
+ };
110
+
111
+ const toggleProvider = (provider: string) => {
112
+ const next = new Set(expandedProviders);
113
+ if (next.has(provider)) {
114
+ next.delete(provider);
115
+ } else {
116
+ next.add(provider);
117
+ }
118
+ setExpandedProviders(next);
119
+ };
120
+
121
+ const toggleModel = (key: string) => {
122
+ const next = new Set(expandedModels);
123
+ if (next.has(key)) {
124
+ next.delete(key);
125
+ } else {
126
+ next.add(key);
127
+ }
128
+ setExpandedModels(next);
129
+ };
130
+
131
+ const copyToClipboard = (text: string, id: string) => {
132
+ navigator.clipboard.writeText(text);
133
+ setCopiedId(id);
134
+ setTimeout(() => setCopiedId(null), 2000);
135
+ };
136
+
137
+ // Calculate stats from real data
138
+ const providers = [...new Set(calls.map((c) => c.provider))];
139
+ const categories = [...new Set(calls.map((c) => c.category))];
140
+ const filesWithAI = new Set(calls.map((c) => c.file)).size;
141
+
142
+ // Enhancement 3: Compute issues
143
+ const issues = useMemo(() => {
144
+ const items: Array<{ type: string; message: string; id: string; tab: "calls" | "prompts" }> = [];
145
+ for (const call of calls) {
146
+ if (call.provider === "unknown") items.push({ type: "unknown-provider", message: `${call.name}: unknown provider`, id: call.id, tab: "calls" });
147
+ if (call.model === "unknown") items.push({ type: "unknown-model", message: `${call.name}: unknown model`, id: call.id, tab: "calls" });
148
+ if (!call.promptTemplate && !call.systemPrompt) items.push({ type: "no-prompt", message: `${call.name}: no visible prompt`, id: call.id, tab: "calls" });
149
+ }
150
+ for (const prompt of prompts) {
151
+ if (prompt.usedBy.length === 0) items.push({ type: "orphan", message: `${prompt.name}: defined but never called`, id: prompt.id, tab: "prompts" });
152
+ }
153
+ return items;
154
+ }, [calls, prompts]);
155
+
156
+ const issuesByType = useMemo(() => {
157
+ const counts: Record<string, number> = {};
158
+ for (const issue of issues) {
159
+ counts[issue.type] = (counts[issue.type] || 0) + 1;
160
+ }
161
+ return counts;
162
+ }, [issues]);
163
+
164
+ const issueTextSummary = useMemo(() => {
165
+ const parts: string[] = [];
166
+ if (issuesByType["unknown-provider"]) parts.push(`${issuesByType["unknown-provider"]} unknown provider${issuesByType["unknown-provider"] > 1 ? "s" : ""}`);
167
+ if (issuesByType["unknown-model"]) parts.push(`${issuesByType["unknown-model"]} unknown model${issuesByType["unknown-model"] > 1 ? "s" : ""}`);
168
+ if (issuesByType["no-prompt"]) parts.push(`${issuesByType["no-prompt"]} call${issuesByType["no-prompt"] > 1 ? "s" : ""} without prompt`);
169
+ if (issuesByType["orphan"]) parts.push(`${issuesByType["orphan"]} orphan prompt${issuesByType["orphan"] > 1 ? "s" : ""}`);
170
+ return parts.join(", ");
171
+ }, [issuesByType]);
172
+
173
+ // Enhancement 1: Provider tree computation
174
+ const providerTree = useMemo(() => {
175
+ const tree: Record<string, Record<string, LLMCall[]>> = {};
176
+ for (const call of calls) {
177
+ if (!tree[call.provider]) tree[call.provider] = {};
178
+ if (!tree[call.provider][call.model]) tree[call.provider][call.model] = [];
179
+ tree[call.provider][call.model].push(call);
180
+ }
181
+ return tree;
182
+ }, [calls]);
183
+
184
+ const providerStats = useMemo(() => {
185
+ const stats: Record<string, { modelCount: number; callSiteCount: number }> = {};
186
+ for (const provider in providerTree) {
187
+ const models = providerTree[provider];
188
+ const modelCount = Object.keys(models).length;
189
+ const callSiteCount = Object.values(models).flat().length;
190
+ stats[provider] = { modelCount, callSiteCount };
191
+ }
192
+ return stats;
193
+ }, [providerTree]);
194
+
195
+ const modelStats = useMemo(() => {
196
+ const stats: Record<string, number> = {};
197
+ for (const provider in providerTree) {
198
+ for (const model in providerTree[provider]) {
199
+ const key = `${provider}:${model}`;
200
+ stats[key] = providerTree[provider][model].length;
201
+ }
202
+ }
203
+ return stats;
204
+ }, [providerTree]);
205
+
206
+ // Find provider and model for a call
207
+ const getCallProviderModel = (callId: string) => {
208
+ const call = calls.find(c => c.id === callId);
209
+ return call ? { provider: call.provider, model: call.model } : null;
210
+ };
211
+
212
+ // Sheet handlers
213
+ const openSheet = (type: "calls" | "prompts") => {
214
+ setSheetType(type);
215
+ };
216
+
217
+ const closeSheet = () => {
218
+ setSheetType(null);
219
+ };
220
+
221
+ const handleSheetItemClick = (type: "calls" | "prompts", item: LLMCall | Prompt) => {
222
+ if (type === "calls") {
223
+ setSelectedCall(item as LLMCall);
224
+ setActiveTab("calls");
225
+ } else {
226
+ setSelectedPrompt(item as Prompt);
227
+ setActiveTab("prompts");
228
+ }
229
+ closeSheet();
230
+ };
231
+
232
+ // Helper to check if call has issue
233
+ const callHasIssue = (callId: string) => {
234
+ return issues.some(issue => issue.id === callId && issue.tab === "calls");
235
+ };
236
+
237
+ const promptHasIssue = (promptId: string) => {
238
+ return issues.some(issue => issue.id === promptId && issue.tab === "prompts");
239
+ };
240
+
241
+ return (
242
+ <div className="flex flex-col gap-6">
243
+ {/* Data Source Indicator */}
244
+ {source === "mock" && (
245
+ <div className="flex items-center gap-2 rounded-lg border border-info/30 bg-info/10 p-3">
246
+ <Info className="h-4 w-4 text-info" />
247
+ <p className="text-sm text-info">
248
+ Showing demo data. Run <code className="rounded bg-info/20 px-1">navgator scan --prompts</code> to scan your project.
249
+ </p>
250
+ <Button
251
+ variant="outline"
252
+ size="sm"
253
+ className="ml-auto"
254
+ onClick={() => scan()}
255
+ disabled={isLoading}
256
+ >
257
+ {isLoading ? (
258
+ <Loader2 className="h-4 w-4 animate-spin" />
259
+ ) : (
260
+ <RefreshCw className="h-4 w-4" />
261
+ )}
262
+ <span className="ml-2">Scan Now</span>
263
+ </Button>
264
+ </div>
265
+ )}
266
+
267
+ {error && (
268
+ <div className="flex items-center gap-2 rounded-lg border border-destructive/30 bg-destructive/10 p-3">
269
+ <AlertTriangle className="h-4 w-4 text-destructive" />
270
+ <p className="text-sm text-destructive">{error}</p>
271
+ </div>
272
+ )}
273
+
274
+ {/* Summary Stats - Enhancement 2: Make clickable */}
275
+ <div className="grid grid-cols-3 gap-4">
276
+ <Card
277
+ className="border-border bg-card cursor-pointer transition-colors hover:bg-secondary/50"
278
+ onClick={() => openSheet("calls")}
279
+ >
280
+ <CardContent className="p-4">
281
+ <div className="flex items-center gap-3">
282
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
283
+ {isLoading ? (
284
+ <Loader2 className="h-5 w-5 animate-spin text-primary" />
285
+ ) : (
286
+ <Brain className="h-5 w-5 text-primary" />
287
+ )}
288
+ </div>
289
+ <div>
290
+ <p className="text-sm text-muted-foreground">LLM Call Sites</p>
291
+ <p className="text-2xl font-semibold text-foreground">
292
+ {calls.length}
293
+ </p>
294
+ </div>
295
+ </div>
296
+ </CardContent>
297
+ </Card>
298
+
299
+ <Card
300
+ className="border-border bg-card cursor-pointer transition-colors hover:bg-secondary/50"
301
+ onClick={() => openSheet("prompts")}
302
+ >
303
+ <CardContent className="p-4">
304
+ <div className="flex items-center gap-3">
305
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-info/10">
306
+ <FileText className="h-5 w-5 text-info" />
307
+ </div>
308
+ <div>
309
+ <p className="text-sm text-muted-foreground">Prompts</p>
310
+ <p className="text-2xl font-semibold text-foreground">
311
+ {prompts.length}
312
+ </p>
313
+ </div>
314
+ </div>
315
+ </CardContent>
316
+ </Card>
317
+
318
+ <Card className="border-border bg-card">
319
+ <CardContent className="p-4">
320
+ <div className="flex items-center gap-3">
321
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-warning/10">
322
+ <Layers className="h-5 w-5 text-warning" />
323
+ </div>
324
+ <div>
325
+ <p className="text-sm text-muted-foreground">Providers</p>
326
+ <p className="text-2xl font-semibold text-foreground">
327
+ {providers.length}
328
+ </p>
329
+ <p className="text-xs text-muted-foreground">
330
+ {filesWithAI} file{filesWithAI !== 1 ? "s" : ""} with AI
331
+ </p>
332
+ </div>
333
+ </div>
334
+ </CardContent>
335
+ </Card>
336
+ </div>
337
+
338
+ {/* Enhancement 3: Issues summary bar */}
339
+ {issues.length > 0 && (
340
+ <div className="flex items-center gap-2">
341
+ <AlertTriangle className="h-4 w-4 text-warning" />
342
+ <p className="text-sm text-warning">
343
+ {issues.length} item{issues.length !== 1 ? "s" : ""} need attention: {issueTextSummary}
344
+ </p>
345
+ </div>
346
+ )}
347
+
348
+ {/* Enhancement 2: Sheet overlay */}
349
+ <Sheet open={sheetType !== null} onOpenChange={(open) => !open && closeSheet()}>
350
+ <SheetContent side="right" className="w-[400px] sm:w-[540px]">
351
+ <SheetHeader>
352
+ <SheetTitle>
353
+ {sheetType === "calls" && "All LLM Call Sites"}
354
+ {sheetType === "prompts" && "All Prompts"}
355
+ </SheetTitle>
356
+ </SheetHeader>
357
+ <ScrollArea className="h-[calc(100vh-80px)] mt-6">
358
+ <div className="rounded-lg border border-border">
359
+ {sheetType === "calls" && calls.map((call, idx) => (
360
+ <button
361
+ key={`${call.id}-sheet-${idx}`}
362
+ type="button"
363
+ onClick={() => handleSheetItemClick("calls", call)}
364
+ className={`w-full text-left p-4 transition-colors hover:bg-secondary/50 ${
365
+ idx !== calls.length - 1 ? "border-b border-border" : ""
366
+ }`}
367
+ >
368
+ <div className="font-medium text-sm">{call.name}</div>
369
+ <div className="text-xs text-muted-foreground mt-1">
370
+ {call.model} · {call.provider}
371
+ </div>
372
+ <div className="font-mono text-xs text-muted-foreground mt-1">
373
+ {call.file}:{call.line}
374
+ </div>
375
+ </button>
376
+ ))}
377
+ {sheetType === "prompts" && prompts.map((prompt, idx) => (
378
+ <button
379
+ key={`${prompt.id}-sheet-${idx}`}
380
+ type="button"
381
+ onClick={() => handleSheetItemClick("prompts", prompt)}
382
+ className={`w-full text-left p-4 transition-colors hover:bg-secondary/50 ${
383
+ idx !== prompts.length - 1 ? "border-b border-border" : ""
384
+ }`}
385
+ >
386
+ <div className="font-medium text-sm">{prompt.name}</div>
387
+ <div className="text-xs text-muted-foreground mt-1">{prompt.type}</div>
388
+ <div className="font-mono text-xs text-muted-foreground mt-1">
389
+ {prompt.file}:{prompt.line}
390
+ </div>
391
+ </button>
392
+ ))}
393
+ </div>
394
+ </ScrollArea>
395
+ </SheetContent>
396
+ </Sheet>
397
+
398
+ {/* Main Content - Enhancement 1: Add activeTab control */}
399
+ <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
400
+ <div className="flex items-center justify-between gap-4">
401
+ <TabsList className="bg-secondary">
402
+ <TabsTrigger value="calls" className="gap-2">
403
+ <Zap className="h-4 w-4" />
404
+ LLM Call Sites
405
+ </TabsTrigger>
406
+ <TabsTrigger value="prompts" className="gap-2">
407
+ <MessageSquare className="h-4 w-4" />
408
+ Prompts
409
+ </TabsTrigger>
410
+ <TabsTrigger value="flow" className="gap-2">
411
+ <GitBranch className="h-4 w-4" />
412
+ AI Flow
413
+ </TabsTrigger>
414
+ <TabsTrigger value="providers" className="gap-2">
415
+ <Layers className="h-4 w-4" />
416
+ By Provider
417
+ </TabsTrigger>
418
+ </TabsList>
419
+
420
+ <div className="flex items-center gap-2">
421
+ <div className="relative">
422
+ <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
423
+ <Input
424
+ placeholder="Search..."
425
+ value={searchQuery}
426
+ onChange={(e) => setSearchQuery(e.target.value)}
427
+ className="w-64 bg-secondary pl-9"
428
+ />
429
+ </div>
430
+ <Button
431
+ variant="outline"
432
+ size="sm"
433
+ onClick={() => refresh()}
434
+ disabled={isLoading}
435
+ title="Refresh data"
436
+ >
437
+ {isLoading ? (
438
+ <Loader2 className="h-4 w-4 animate-spin" />
439
+ ) : (
440
+ <RefreshCw className="h-4 w-4" />
441
+ )}
442
+ </Button>
443
+ </div>
444
+ </div>
445
+
446
+ <TabsContent value="calls" className="mt-4">
447
+ {/* Filters */}
448
+ <div className="mb-4 flex items-center gap-3">
449
+ <Filter className="h-4 w-4 text-muted-foreground" />
450
+ <div className="flex gap-2">
451
+ <Button
452
+ variant={categoryFilter === "all" ? "default" : "outline"}
453
+ size="sm"
454
+ onClick={() => setCategoryFilter("all")}
455
+ >
456
+ All Types
457
+ </Button>
458
+ {categories.map((cat) => (
459
+ <Button
460
+ key={cat}
461
+ variant={categoryFilter === cat ? "default" : "outline"}
462
+ size="sm"
463
+ onClick={() => setCategoryFilter(cat)}
464
+ className="capitalize"
465
+ >
466
+ {cat}
467
+ </Button>
468
+ ))}
469
+ </div>
470
+ <div className="ml-4 flex gap-2">
471
+ <Button
472
+ variant={providerFilter === "all" ? "default" : "outline"}
473
+ size="sm"
474
+ onClick={() => setProviderFilter("all")}
475
+ >
476
+ All Providers
477
+ </Button>
478
+ {providers.map((prov) => (
479
+ <Button
480
+ key={prov}
481
+ variant={providerFilter === prov ? "default" : "outline"}
482
+ size="sm"
483
+ onClick={() => setProviderFilter(prov)}
484
+ className="capitalize"
485
+ >
486
+ {prov}
487
+ </Button>
488
+ ))}
489
+ </div>
490
+ </div>
491
+
492
+ <div className="grid grid-cols-2 gap-4">
493
+ {/* LLM Calls List - Enhancement 3: Add issue indicators */}
494
+ <Card className="border-border bg-card">
495
+ <CardHeader className="pb-3">
496
+ <CardTitle className="text-base font-medium">
497
+ Detected Call Sites ({filteredCalls.length})
498
+ </CardTitle>
499
+ </CardHeader>
500
+ <CardContent className="p-0">
501
+ <ScrollArea className="h-[500px]">
502
+ <div className="flex flex-col">
503
+ {filteredCalls.map((call, idx) => (
504
+ <div key={`${call.id}-${idx}`}>
505
+ <button
506
+ type="button"
507
+ onClick={() => {
508
+ setSelectedCall(call);
509
+ toggleExpanded(call.id);
510
+ }}
511
+ className={`flex w-full items-start gap-3 border-b border-border p-4 text-left transition-colors hover:bg-secondary/50 ${
512
+ selectedCall?.id === call.id ? "bg-secondary" : ""
513
+ }`}
514
+ >
515
+ <div className="mt-0.5">
516
+ {expandedCalls.has(call.id) ? (
517
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
518
+ ) : (
519
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
520
+ )}
521
+ </div>
522
+ <div className="flex-1">
523
+ <div className="flex items-center gap-2">
524
+ <span className="font-mono text-sm text-foreground">
525
+ {call.name}
526
+ </span>
527
+ {callHasIssue(call.id) && (
528
+ <AlertTriangle className="h-3.5 w-3.5 text-warning" />
529
+ )}
530
+ <Badge
531
+ variant="outline"
532
+ className={`text-xs ${categoryColors[call.category]}`}
533
+ >
534
+ {call.category}
535
+ </Badge>
536
+ </div>
537
+ <div className="mt-1 flex items-center gap-3 text-xs text-muted-foreground">
538
+ <span>{call.model}</span>
539
+ <span>·</span>
540
+ <span>{call.provider}</span>
541
+ {call.method && (
542
+ <>
543
+ <span>·</span>
544
+ <span className="font-mono">{call.method}</span>
545
+ </>
546
+ )}
547
+ </div>
548
+ <span
549
+ role="button"
550
+ tabIndex={0}
551
+ onClick={(e) => {
552
+ e.stopPropagation();
553
+ navigateToFile(call.file);
554
+ }}
555
+ onKeyDown={(e) => {
556
+ if (e.key === "Enter") {
557
+ e.stopPropagation();
558
+ navigateToFile(call.file);
559
+ }
560
+ }}
561
+ className="mt-1 font-mono text-xs text-muted-foreground hover:text-primary hover:underline transition-colors cursor-pointer"
562
+ >
563
+ {call.file}:{call.line}
564
+ </span>
565
+ </div>
566
+ </button>
567
+
568
+ {expandedCalls.has(call.id) && (
569
+ <div className="border-b border-border bg-secondary/30 p-4">
570
+ <div className="grid grid-cols-3 gap-4 text-sm">
571
+ {call.method && (
572
+ <div>
573
+ <p className="text-muted-foreground">SDK Method</p>
574
+ <p className="font-mono text-foreground">{call.method}</p>
575
+ </div>
576
+ )}
577
+ {call.sdk && (
578
+ <div>
579
+ <p className="text-muted-foreground">SDK</p>
580
+ <p className="font-mono text-foreground">{call.sdk}</p>
581
+ </div>
582
+ )}
583
+ <div>
584
+ <p className="text-muted-foreground">Confidence</p>
585
+ <p className="font-mono text-foreground">
586
+ {(call.confidence * 100).toFixed(0)}%
587
+ </p>
588
+ </div>
589
+ </div>
590
+ {call.configExtracted && (
591
+ <div className="mt-3 grid grid-cols-3 gap-4 text-sm">
592
+ {call.configExtracted.temperature !== undefined && (
593
+ <div>
594
+ <p className="text-muted-foreground">Temperature</p>
595
+ <p className="font-mono text-foreground">{call.configExtracted.temperature}</p>
596
+ </div>
597
+ )}
598
+ {call.configExtracted.maxTokens !== undefined && (
599
+ <div>
600
+ <p className="text-muted-foreground">Max Tokens</p>
601
+ <p className="font-mono text-foreground">{call.configExtracted.maxTokens}</p>
602
+ </div>
603
+ )}
604
+ {call.configExtracted.stream !== undefined && (
605
+ <div>
606
+ <p className="text-muted-foreground">Streaming</p>
607
+ <p className="font-mono text-foreground">{call.configExtracted.stream ? "Yes" : "No"}</p>
608
+ </div>
609
+ )}
610
+ </div>
611
+ )}
612
+ {call.promptVariables.length > 0 && (
613
+ <div className="mt-3">
614
+ <p className="text-xs text-muted-foreground">
615
+ Variables:{" "}
616
+ {call.promptVariables.map((v) => (
617
+ <code
618
+ key={v}
619
+ className="mx-1 rounded bg-secondary px-1 py-0.5"
620
+ >
621
+ {`{{${v}}}`}
622
+ </code>
623
+ ))}
624
+ </p>
625
+ </div>
626
+ )}
627
+ </div>
628
+ )}
629
+ </div>
630
+ ))}
631
+ </div>
632
+ </ScrollArea>
633
+ </CardContent>
634
+ </Card>
635
+
636
+ {/* Call Details */}
637
+ <Card className="border-border bg-card">
638
+ <CardHeader className="pb-3">
639
+ <CardTitle className="text-base font-medium">Call Site Details</CardTitle>
640
+ </CardHeader>
641
+ <CardContent>
642
+ {selectedCall ? (
643
+ <div className="flex flex-col gap-4">
644
+ <div className="flex items-center justify-between">
645
+ <div>
646
+ <h3 className="font-mono text-lg text-foreground">
647
+ {selectedCall.name}
648
+ </h3>
649
+ <button
650
+ type="button"
651
+ onClick={() => navigateToFile(selectedCall.file)}
652
+ className="text-sm text-muted-foreground hover:text-primary hover:underline transition-colors text-left"
653
+ >
654
+ {selectedCall.file}:{selectedCall.line}
655
+ </button>
656
+ </div>
657
+ <Badge
658
+ variant="outline"
659
+ className={categoryColors[selectedCall.category]}
660
+ >
661
+ {selectedCall.category}
662
+ </Badge>
663
+ </div>
664
+
665
+ <div className="grid grid-cols-2 gap-4">
666
+ <div className="rounded-lg bg-secondary p-3">
667
+ <p className="text-xs text-muted-foreground">Model</p>
668
+ <p className="font-mono text-sm text-foreground">
669
+ {selectedCall.model}
670
+ </p>
671
+ {selectedCall.model === "unknown" && (
672
+ <p className="text-xs text-warning mt-1">Model name not detected</p>
673
+ )}
674
+ </div>
675
+ <div className="rounded-lg bg-secondary p-3">
676
+ <p className="text-xs text-muted-foreground">Provider</p>
677
+ <p className="font-mono text-sm capitalize text-foreground">
678
+ {selectedCall.provider}
679
+ </p>
680
+ {selectedCall.provider === "unknown" && (
681
+ <p className="text-xs text-warning mt-1">Provider could not be identified from code patterns</p>
682
+ )}
683
+ </div>
684
+ </div>
685
+
686
+ {(selectedCall.method || selectedCall.sdk) && (
687
+ <div className="grid grid-cols-2 gap-4">
688
+ {selectedCall.method && (
689
+ <div className="rounded-lg bg-secondary p-3">
690
+ <p className="text-xs text-muted-foreground">SDK Method</p>
691
+ <p className="font-mono text-sm text-foreground">{selectedCall.method}</p>
692
+ </div>
693
+ )}
694
+ {selectedCall.sdk && (
695
+ <div className="rounded-lg bg-secondary p-3">
696
+ <p className="text-xs text-muted-foreground">SDK Package</p>
697
+ <p className="font-mono text-sm text-foreground">{selectedCall.sdk}</p>
698
+ </div>
699
+ )}
700
+ </div>
701
+ )}
702
+
703
+ <div className="rounded-lg bg-secondary p-3">
704
+ <p className="text-xs text-muted-foreground">Confidence</p>
705
+ <p className="font-mono text-sm text-foreground">
706
+ {(selectedCall.confidence * 100).toFixed(0)}%
707
+ </p>
708
+ </div>
709
+
710
+ {selectedCall.systemPrompt && (
711
+ <div>
712
+ <div className="mb-2 flex items-center justify-between">
713
+ <p className="text-sm font-medium text-foreground">
714
+ System Prompt
715
+ </p>
716
+ <Button
717
+ variant="ghost"
718
+ size="sm"
719
+ onClick={() =>
720
+ copyToClipboard(
721
+ selectedCall.systemPrompt || "",
722
+ `sys-${selectedCall.id}`
723
+ )
724
+ }
725
+ >
726
+ {copiedId === `sys-${selectedCall.id}` ? (
727
+ <Check className="h-4 w-4 text-primary" />
728
+ ) : (
729
+ <Copy className="h-4 w-4" />
730
+ )}
731
+ </Button>
732
+ </div>
733
+ <pre className="rounded-lg bg-secondary p-3 font-mono text-xs text-muted-foreground">
734
+ {selectedCall.systemPrompt}
735
+ </pre>
736
+ </div>
737
+ )}
738
+
739
+ <div>
740
+ <div className="mb-2 flex items-center justify-between">
741
+ <p className="text-sm font-medium text-foreground">
742
+ Prompt Template
743
+ </p>
744
+ <Button
745
+ variant="ghost"
746
+ size="sm"
747
+ onClick={() =>
748
+ copyToClipboard(
749
+ selectedCall.promptTemplate,
750
+ `tpl-${selectedCall.id}`
751
+ )
752
+ }
753
+ >
754
+ {copiedId === `tpl-${selectedCall.id}` ? (
755
+ <Check className="h-4 w-4 text-primary" />
756
+ ) : (
757
+ <Copy className="h-4 w-4" />
758
+ )}
759
+ </Button>
760
+ </div>
761
+ <pre className="rounded-lg bg-secondary p-3 font-mono text-xs text-muted-foreground">
762
+ {selectedCall.promptTemplate}
763
+ </pre>
764
+ </div>
765
+
766
+ </div>
767
+ ) : (
768
+ <div className="flex h-[400px] items-center justify-center text-muted-foreground">
769
+ <p>Select an LLM call to view details</p>
770
+ </div>
771
+ )}
772
+ </CardContent>
773
+ </Card>
774
+ </div>
775
+ </TabsContent>
776
+
777
+ <TabsContent value="prompts" className="mt-4">
778
+ {fileFilter && (
779
+ <div className="mb-3 flex items-center gap-2">
780
+ <span className="text-xs text-muted-foreground">Filtered by file:</span>
781
+ <button
782
+ type="button"
783
+ onClick={() => {
784
+ setFileFilter(null);
785
+ setSelectedPrompt(null);
786
+ }}
787
+ className="inline-flex items-center gap-1.5 rounded-md bg-secondary px-2.5 py-1 text-xs font-mono text-foreground hover:bg-secondary/80 transition-colors"
788
+ >
789
+ {fileFilter.split("/").pop()}
790
+ <X className="h-3 w-3 text-muted-foreground" />
791
+ </button>
792
+ </div>
793
+ )}
794
+ <div className="grid grid-cols-2 gap-4">
795
+ {/* Prompts List - Enhancement 1 & 3: Add provider/model info and issue indicators */}
796
+ <Card className="border-border bg-card">
797
+ <CardHeader className="pb-3">
798
+ <CardTitle className="text-base font-medium">
799
+ Prompt Library ({filteredPrompts.length})
800
+ </CardTitle>
801
+ </CardHeader>
802
+ <CardContent className="p-0">
803
+ <ScrollArea className="h-[500px]">
804
+ <div className="flex flex-col">
805
+ {filteredPrompts.map((prompt, idx) => {
806
+ // Find calls that use this prompt
807
+ const relatedCall = calls.find(call =>
808
+ call.promptTemplate?.includes(prompt.name) ||
809
+ call.systemPrompt?.includes(prompt.name) ||
810
+ prompt.usedBy.includes(call.name)
811
+ );
812
+ return (
813
+ <button
814
+ key={`${prompt.id}-${idx}`}
815
+ type="button"
816
+ onClick={() => setSelectedPrompt(prompt)}
817
+ className={`flex w-full items-start gap-3 border-b border-border p-4 text-left transition-colors hover:bg-secondary/50 ${
818
+ selectedPrompt?.id === prompt.id ? "bg-secondary" : ""
819
+ }`}
820
+ >
821
+ <div className="flex-1">
822
+ <div className="flex items-center gap-2">
823
+ <span className="font-mono text-sm text-foreground">
824
+ {prompt.name}
825
+ </span>
826
+ {promptHasIssue(prompt.id) && (
827
+ <AlertTriangle className="h-3.5 w-3.5 text-warning" />
828
+ )}
829
+ <Badge
830
+ variant="outline"
831
+ className={`text-xs ${typeColors[prompt.type]}`}
832
+ >
833
+ {prompt.type}
834
+ </Badge>
835
+ {relatedCall && (
836
+ <span className="text-xs text-muted-foreground">
837
+ {relatedCall.provider} · {relatedCall.model}
838
+ </span>
839
+ )}
840
+ </div>
841
+ <p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
842
+ {prompt.content}
843
+ </p>
844
+ <div className="mt-2 flex items-center gap-3 text-xs text-muted-foreground">
845
+ <span>{prompt.tokenCount} tokens</span>
846
+ <span>·</span>
847
+ <span>v{prompt.version}</span>
848
+ <span>·</span>
849
+ <span>{prompt.lastModified}</span>
850
+ </div>
851
+ </div>
852
+ </button>
853
+ );
854
+ })}
855
+ </div>
856
+ </ScrollArea>
857
+ </CardContent>
858
+ </Card>
859
+
860
+ {/* Prompt Details - Enhancement 1: Add provider/model and clickable provider */}
861
+ <Card className="border-border bg-card">
862
+ <CardHeader className="pb-3">
863
+ <CardTitle className="text-base font-medium">Prompt Details</CardTitle>
864
+ </CardHeader>
865
+ <CardContent>
866
+ {selectedPrompt ? (
867
+ <div className="flex flex-col gap-4">
868
+ <div className="flex items-center justify-between">
869
+ <div>
870
+ <h3 className="font-mono text-lg text-foreground">
871
+ {selectedPrompt.name}
872
+ </h3>
873
+ <button
874
+ type="button"
875
+ onClick={() => navigateToFile(selectedPrompt.file)}
876
+ className="text-sm text-muted-foreground hover:text-primary hover:underline transition-colors text-left"
877
+ >
878
+ {selectedPrompt.file}:{selectedPrompt.line}
879
+ </button>
880
+ </div>
881
+ <div className="flex items-center gap-2">
882
+ <Badge variant="outline" className="text-xs">
883
+ v{selectedPrompt.version}
884
+ </Badge>
885
+ <Badge
886
+ variant="outline"
887
+ className={typeColors[selectedPrompt.type]}
888
+ >
889
+ {selectedPrompt.type}
890
+ </Badge>
891
+ </div>
892
+ </div>
893
+
894
+ {(() => {
895
+ const relatedCall = calls.find(call =>
896
+ call.promptTemplate?.includes(selectedPrompt.name) ||
897
+ call.systemPrompt?.includes(selectedPrompt.name) ||
898
+ selectedPrompt.usedBy.includes(call.name)
899
+ );
900
+ return relatedCall ? (
901
+ <div className="grid grid-cols-2 gap-4">
902
+ <div className="rounded-lg bg-secondary p-3">
903
+ <p className="text-xs text-muted-foreground">Provider</p>
904
+ <button
905
+ type="button"
906
+ onClick={() => {
907
+ setActiveTab("providers");
908
+ setExpandedProviders(new Set([relatedCall.provider]));
909
+ }}
910
+ className={`font-mono text-sm text-foreground hover:text-primary transition-colors ${
911
+ relatedCall.provider === "unknown" ? "text-warning" : ""
912
+ }`}
913
+ >
914
+ {relatedCall.provider}
915
+ </button>
916
+ </div>
917
+ <div className="rounded-lg bg-secondary p-3">
918
+ <p className="text-xs text-muted-foreground">Model</p>
919
+ <p className="font-mono text-sm text-foreground">
920
+ {relatedCall.model}
921
+ </p>
922
+ </div>
923
+ </div>
924
+ ) : null;
925
+ })()}
926
+
927
+ <div className="grid grid-cols-2 gap-4">
928
+ <div className="rounded-lg bg-secondary p-3">
929
+ <p className="text-xs text-muted-foreground">Token Count</p>
930
+ <p className="font-mono text-sm text-foreground">
931
+ {selectedPrompt.tokenCount}
932
+ </p>
933
+ </div>
934
+ <div className="rounded-lg bg-secondary p-3">
935
+ <p className="text-xs text-muted-foreground">Last Modified</p>
936
+ <p className="font-mono text-sm text-foreground">
937
+ {selectedPrompt.lastModified}
938
+ </p>
939
+ </div>
940
+ </div>
941
+
942
+ <div>
943
+ <div className="mb-2 flex items-center justify-between">
944
+ <p className="text-sm font-medium text-foreground">Content</p>
945
+ <Button
946
+ variant="ghost"
947
+ size="sm"
948
+ onClick={() =>
949
+ copyToClipboard(
950
+ selectedPrompt.content,
951
+ `content-${selectedPrompt.id}`
952
+ )
953
+ }
954
+ >
955
+ {copiedId === `content-${selectedPrompt.id}` ? (
956
+ <Check className="h-4 w-4 text-primary" />
957
+ ) : (
958
+ <Copy className="h-4 w-4" />
959
+ )}
960
+ </Button>
961
+ </div>
962
+ <pre className="max-h-[200px] overflow-auto rounded-lg bg-secondary p-3 font-mono text-xs text-muted-foreground">
963
+ {selectedPrompt.content}
964
+ </pre>
965
+ </div>
966
+
967
+ {selectedPrompt.variables.length > 0 && (
968
+ <div>
969
+ <p className="mb-2 text-sm font-medium text-foreground">
970
+ Variables
971
+ </p>
972
+ <div className="flex flex-wrap gap-2">
973
+ {selectedPrompt.variables.map((v) => (
974
+ <code
975
+ key={v}
976
+ className="rounded bg-secondary px-2 py-1 font-mono text-xs text-muted-foreground"
977
+ >
978
+ {`{{${v}}}`}
979
+ </code>
980
+ ))}
981
+ </div>
982
+ </div>
983
+ )}
984
+
985
+ <div>
986
+ <p className="mb-2 text-sm font-medium text-foreground">Used By</p>
987
+ <div className="flex flex-wrap gap-2">
988
+ {selectedPrompt.usedBy.length > 0 ? (
989
+ selectedPrompt.usedBy.map((fn) => (
990
+ <Badge key={fn} variant="outline" className="font-mono">
991
+ {fn}()
992
+ </Badge>
993
+ ))
994
+ ) : (
995
+ <p className="text-xs text-warning">Not used by any detected calls</p>
996
+ )}
997
+ </div>
998
+ </div>
999
+ </div>
1000
+ ) : (
1001
+ <div className="flex h-[400px] items-center justify-center text-muted-foreground">
1002
+ <p>Select a prompt to view details</p>
1003
+ </div>
1004
+ )}
1005
+ </CardContent>
1006
+ </Card>
1007
+ </div>
1008
+ </TabsContent>
1009
+
1010
+ <TabsContent value="flow" className="mt-4">
1011
+ <AIFlowDiagram prompts={prompts} calls={calls} onFileClick={navigateToFile} />
1012
+ </TabsContent>
1013
+
1014
+ {/* Enhancement 1: By Provider tab */}
1015
+ <TabsContent value="providers" className="mt-4">
1016
+ <div className="grid grid-cols-2 gap-4">
1017
+ <Card className="border-border bg-card">
1018
+ <CardHeader className="pb-3">
1019
+ <CardTitle className="text-base font-medium">
1020
+ By Provider ({Object.keys(providerTree).length})
1021
+ </CardTitle>
1022
+ </CardHeader>
1023
+ <CardContent className="p-0">
1024
+ <ScrollArea className="h-[500px]">
1025
+ <div className="flex flex-col">
1026
+ {Object.entries(providerTree).map(([provider, models], providerIdx) => (
1027
+ <div key={provider}>
1028
+ <button
1029
+ type="button"
1030
+ onClick={() => toggleProvider(provider)}
1031
+ className="flex w-full items-start gap-3 border-b border-border p-4 text-left transition-colors hover:bg-secondary/50"
1032
+ >
1033
+ <div className="mt-0.5">
1034
+ {expandedProviders.has(provider) ? (
1035
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
1036
+ ) : (
1037
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
1038
+ )}
1039
+ </div>
1040
+ <div className="flex-1">
1041
+ <div className="flex items-center gap-2">
1042
+ <span className={`font-medium text-sm ${provider === "unknown" ? "text-warning" : "text-foreground"}`}>
1043
+ {provider}
1044
+ </span>
1045
+ </div>
1046
+ <div className="mt-1 text-xs text-muted-foreground">
1047
+ {providerStats[provider].modelCount} model{providerStats[provider].modelCount !== 1 ? "s" : ""} · {providerStats[provider].callSiteCount} call site{providerStats[provider].callSiteCount !== 1 ? "s" : ""}
1048
+ </div>
1049
+ </div>
1050
+ </button>
1051
+
1052
+ {expandedProviders.has(provider) && (
1053
+ <div className="border-b border-border bg-secondary/30">
1054
+ {Object.entries(models).map(([model, modelCalls]) => {
1055
+ const modelKey = `${provider}:${model}`;
1056
+ return (
1057
+ <div key={modelKey}>
1058
+ <button
1059
+ type="button"
1060
+ onClick={() => toggleModel(modelKey)}
1061
+ className="flex w-full items-start gap-3 border-b border-border p-4 pl-12 text-left transition-colors hover:bg-secondary/50"
1062
+ >
1063
+ <div className="mt-0.5">
1064
+ {expandedModels.has(modelKey) ? (
1065
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
1066
+ ) : (
1067
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
1068
+ )}
1069
+ </div>
1070
+ <div className="flex-1">
1071
+ <div className="flex items-center gap-2">
1072
+ <span className="font-mono text-sm text-foreground">
1073
+ {model}
1074
+ </span>
1075
+ </div>
1076
+ <div className="mt-1 text-xs text-muted-foreground">
1077
+ {modelStats[modelKey]} call site{modelStats[modelKey] !== 1 ? "s" : ""}
1078
+ </div>
1079
+ </div>
1080
+ </button>
1081
+
1082
+ {expandedModels.has(modelKey) && (
1083
+ <div className="border-b border-border bg-secondary/50">
1084
+ {modelCalls.map((call, callIdx) => (
1085
+ <button
1086
+ key={`${call.id}-prov-${callIdx}`}
1087
+ type="button"
1088
+ onClick={() => {
1089
+ setSelectedCall(call);
1090
+ }}
1091
+ className={`flex w-full items-start gap-3 border-b border-border p-4 pl-20 text-left transition-colors hover:bg-secondary/70 ${
1092
+ selectedCall?.id === call.id ? "bg-secondary" : ""
1093
+ }`}
1094
+ >
1095
+ <div className="flex-1">
1096
+ <div className="flex items-center gap-2">
1097
+ <span className="font-mono text-sm text-foreground">
1098
+ {call.name}
1099
+ </span>
1100
+ </div>
1101
+ <p className="mt-1 font-mono text-xs text-muted-foreground">
1102
+ {call.file}:{call.line}
1103
+ </p>
1104
+ </div>
1105
+ </button>
1106
+ ))}
1107
+ </div>
1108
+ )}
1109
+ </div>
1110
+ );
1111
+ })}
1112
+ </div>
1113
+ )}
1114
+ </div>
1115
+ ))}
1116
+ </div>
1117
+ </ScrollArea>
1118
+ </CardContent>
1119
+ </Card>
1120
+
1121
+ {/* Call Site Details (providers tab) */}
1122
+ <Card className="border-border bg-card">
1123
+ <CardHeader className="pb-3">
1124
+ <CardTitle className="text-base font-medium">Call Site Details</CardTitle>
1125
+ </CardHeader>
1126
+ <CardContent>
1127
+ {selectedCall ? (
1128
+ <div className="flex flex-col gap-4">
1129
+ <div className="flex items-center justify-between">
1130
+ <div>
1131
+ <h3 className="font-mono text-lg text-foreground">
1132
+ {selectedCall.name}
1133
+ </h3>
1134
+ <button
1135
+ type="button"
1136
+ onClick={() => navigateToFile(selectedCall.file)}
1137
+ className="text-sm text-muted-foreground hover:text-primary hover:underline transition-colors text-left"
1138
+ >
1139
+ {selectedCall.file}:{selectedCall.line}
1140
+ </button>
1141
+ </div>
1142
+ <Badge
1143
+ variant="outline"
1144
+ className={categoryColors[selectedCall.category]}
1145
+ >
1146
+ {selectedCall.category}
1147
+ </Badge>
1148
+ </div>
1149
+
1150
+ <div className="grid grid-cols-2 gap-4">
1151
+ <div className="rounded-lg bg-secondary p-3">
1152
+ <p className="text-xs text-muted-foreground">Model</p>
1153
+ <p className="font-mono text-sm text-foreground">{selectedCall.model}</p>
1154
+ </div>
1155
+ <div className="rounded-lg bg-secondary p-3">
1156
+ <p className="text-xs text-muted-foreground">Provider</p>
1157
+ <p className="font-mono text-sm capitalize text-foreground">{selectedCall.provider}</p>
1158
+ </div>
1159
+ </div>
1160
+
1161
+ {(selectedCall.method || selectedCall.sdk) && (
1162
+ <div className="grid grid-cols-2 gap-4">
1163
+ {selectedCall.method && (
1164
+ <div className="rounded-lg bg-secondary p-3">
1165
+ <p className="text-xs text-muted-foreground">SDK Method</p>
1166
+ <p className="font-mono text-sm text-foreground">{selectedCall.method}</p>
1167
+ </div>
1168
+ )}
1169
+ {selectedCall.sdk && (
1170
+ <div className="rounded-lg bg-secondary p-3">
1171
+ <p className="text-xs text-muted-foreground">SDK Package</p>
1172
+ <p className="font-mono text-sm text-foreground">{selectedCall.sdk}</p>
1173
+ </div>
1174
+ )}
1175
+ </div>
1176
+ )}
1177
+
1178
+ {selectedCall.systemPrompt && (
1179
+ <div>
1180
+ <p className="mb-2 text-sm font-medium text-foreground">System Prompt</p>
1181
+ <pre className="rounded-lg bg-secondary p-3 font-mono text-xs text-muted-foreground">
1182
+ {selectedCall.systemPrompt}
1183
+ </pre>
1184
+ </div>
1185
+ )}
1186
+
1187
+ <div>
1188
+ <p className="mb-2 text-sm font-medium text-foreground">Prompt Template</p>
1189
+ <pre className="rounded-lg bg-secondary p-3 font-mono text-xs text-muted-foreground">
1190
+ {selectedCall.promptTemplate}
1191
+ </pre>
1192
+ </div>
1193
+
1194
+ </div>
1195
+ ) : (
1196
+ <div className="flex h-[400px] items-center justify-center text-muted-foreground">
1197
+ <p>Select a call site to view details</p>
1198
+ </div>
1199
+ )}
1200
+ </CardContent>
1201
+ </Card>
1202
+ </div>
1203
+ </TabsContent>
1204
+ </Tabs>
1205
+ </div>
1206
+ );
1207
+ }
1208
+
1209
+ // =============================================================================
1210
+ // AI FLOW DIAGRAM COMPONENT
1211
+ // =============================================================================
1212
+
1213
+ interface AIFlowDiagramProps {
1214
+ prompts: Prompt[];
1215
+ calls: LLMCall[];
1216
+ onFileClick?: (file: string) => void;
1217
+ }
1218
+
1219
+ interface FlowNode {
1220
+ id: string;
1221
+ name: string;
1222
+ file: string;
1223
+ type: "input" | "process" | "output";
1224
+ category?: string;
1225
+ purpose?: string;
1226
+ prompts: Prompt[];
1227
+ }
1228
+
1229
+ function AIFlowDiagram({ prompts, calls, onFileClick }: AIFlowDiagramProps) {
1230
+ // Group prompts by file and categorize them
1231
+ const groupedByFile = prompts.reduce((acc, prompt) => {
1232
+ const file = prompt.file;
1233
+ if (!acc[file]) {
1234
+ acc[file] = [];
1235
+ }
1236
+ acc[file].push(prompt);
1237
+ return acc;
1238
+ }, {} as Record<string, Prompt[]>);
1239
+
1240
+ // Categorize files into flow stages based on naming patterns and prompt types
1241
+ const categorizeFile = (file: string, filePrompts: Prompt[]): "input" | "process" | "output" => {
1242
+ const lowerFile = file.toLowerCase();
1243
+
1244
+ // Input/routing patterns
1245
+ if (
1246
+ lowerFile.includes("router") ||
1247
+ lowerFile.includes("query") ||
1248
+ lowerFile.includes("classify") ||
1249
+ lowerFile.includes("input") ||
1250
+ lowerFile.includes("parse")
1251
+ ) {
1252
+ return "input";
1253
+ }
1254
+
1255
+ // Output patterns
1256
+ if (
1257
+ lowerFile.includes("summar") ||
1258
+ lowerFile.includes("output") ||
1259
+ lowerFile.includes("response") ||
1260
+ lowerFile.includes("format") ||
1261
+ lowerFile.includes("render")
1262
+ ) {
1263
+ return "output";
1264
+ }
1265
+
1266
+ // Check prompt purposes/categories
1267
+ const hasInput = filePrompts.some(p =>
1268
+ p.purpose?.toLowerCase().includes("classif") ||
1269
+ p.purpose?.toLowerCase().includes("rout") ||
1270
+ p.category?.toLowerCase().includes("input")
1271
+ );
1272
+ if (hasInput) return "input";
1273
+
1274
+ const hasOutput = filePrompts.some(p =>
1275
+ p.purpose?.toLowerCase().includes("summar") ||
1276
+ p.purpose?.toLowerCase().includes("format") ||
1277
+ p.category?.toLowerCase().includes("output")
1278
+ );
1279
+ if (hasOutput) return "output";
1280
+
1281
+ return "process";
1282
+ };
1283
+
1284
+ // Build flow nodes
1285
+ const flowNodes: FlowNode[] = Object.entries(groupedByFile).map(([file, filePrompts]) => {
1286
+ const type = categorizeFile(file, filePrompts);
1287
+ const fileName = file.split("/").pop() || file;
1288
+
1289
+ return {
1290
+ id: file,
1291
+ name: fileName.replace(/\.(ts|js|tsx|jsx|py)$/, ""),
1292
+ file,
1293
+ type,
1294
+ category: filePrompts[0]?.category,
1295
+ purpose: filePrompts[0]?.purpose,
1296
+ prompts: filePrompts,
1297
+ };
1298
+ });
1299
+
1300
+ // Sort by type order: input -> process -> output
1301
+ const typeOrder = { input: 0, process: 1, output: 2 };
1302
+ flowNodes.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);
1303
+
1304
+ // Group by type for display
1305
+ const inputNodes = flowNodes.filter(n => n.type === "input");
1306
+ const processNodes = flowNodes.filter(n => n.type === "process");
1307
+ const outputNodes = flowNodes.filter(n => n.type === "output");
1308
+
1309
+ const typeColors = {
1310
+ input: "border-info bg-info/10 text-info",
1311
+ process: "border-primary bg-primary/10 text-primary",
1312
+ output: "border-chart-3 bg-chart-3/10 text-chart-3",
1313
+ };
1314
+
1315
+ const typeLabels = {
1316
+ input: "Input / Routing",
1317
+ process: "Processing / Analysis",
1318
+ output: "Output / Summary",
1319
+ };
1320
+
1321
+ if (prompts.length === 0) {
1322
+ return (
1323
+ <Card className="border-border bg-card">
1324
+ <CardContent className="flex flex-col items-center justify-center py-16">
1325
+ <GitBranch className="h-12 w-12 text-muted-foreground/50" />
1326
+ <p className="mt-4 text-muted-foreground">No AI prompts detected</p>
1327
+ <p className="mt-1 text-xs text-muted-foreground/80">
1328
+ Run <code className="rounded bg-secondary px-1.5">navgator scan --prompts</code> to detect AI flows
1329
+ </p>
1330
+ </CardContent>
1331
+ </Card>
1332
+ );
1333
+ }
1334
+
1335
+ return (
1336
+ <div className="space-y-6">
1337
+ {/* Flow Legend */}
1338
+ <div className="flex items-center gap-6 text-xs">
1339
+ <div className="flex items-center gap-2">
1340
+ <div className="h-3 w-3 rounded-full border-2 border-info bg-info/30" />
1341
+ <span className="text-muted-foreground">Input/Routing</span>
1342
+ </div>
1343
+ <div className="flex items-center gap-2">
1344
+ <div className="h-3 w-3 rounded-full border-2 border-primary bg-primary/30" />
1345
+ <span className="text-muted-foreground">Processing</span>
1346
+ </div>
1347
+ <div className="flex items-center gap-2">
1348
+ <div className="h-3 w-3 rounded-full border-2 border-chart-3 bg-chart-3/30" />
1349
+ <span className="text-muted-foreground">Output/Summary</span>
1350
+ </div>
1351
+ </div>
1352
+
1353
+ {/* Flow Diagram */}
1354
+ <Card className="border-border bg-card">
1355
+ <CardContent className="p-6">
1356
+ <div className="relative flex flex-col items-center gap-2">
1357
+ {/* User Input */}
1358
+ <div className="flex items-center gap-2 rounded-lg border border-border bg-secondary px-4 py-2">
1359
+ <Circle className="h-4 w-4 text-muted-foreground" />
1360
+ <span className="text-sm font-medium text-foreground">User Input</span>
1361
+ </div>
1362
+
1363
+ {inputNodes.length > 0 && (
1364
+ <>
1365
+ <ArrowDown className="h-5 w-5 text-muted-foreground" />
1366
+ <FlowStage
1367
+ label={typeLabels.input}
1368
+ nodes={inputNodes}
1369
+ colorClass={typeColors.input}
1370
+ onNodeClick={onFileClick}
1371
+ />
1372
+ </>
1373
+ )}
1374
+
1375
+ {processNodes.length > 0 && (
1376
+ <>
1377
+ <ArrowDown className="h-5 w-5 text-muted-foreground" />
1378
+ <FlowStage
1379
+ label={typeLabels.process}
1380
+ nodes={processNodes}
1381
+ colorClass={typeColors.process}
1382
+ onNodeClick={onFileClick}
1383
+ />
1384
+ </>
1385
+ )}
1386
+
1387
+ {outputNodes.length > 0 && (
1388
+ <>
1389
+ <ArrowDown className="h-5 w-5 text-muted-foreground" />
1390
+ <FlowStage
1391
+ label={typeLabels.output}
1392
+ nodes={outputNodes}
1393
+ colorClass={typeColors.output}
1394
+ onNodeClick={onFileClick}
1395
+ />
1396
+ </>
1397
+ )}
1398
+
1399
+ <ArrowDown className="h-5 w-5 text-muted-foreground" />
1400
+
1401
+ {/* Response */}
1402
+ <div className="flex items-center gap-2 rounded-lg border border-border bg-secondary px-4 py-2">
1403
+ <Circle className="h-4 w-4 text-muted-foreground" />
1404
+ <span className="text-sm font-medium text-foreground">Response</span>
1405
+ </div>
1406
+ </div>
1407
+ </CardContent>
1408
+ </Card>
1409
+
1410
+ {/* Detailed File List */}
1411
+ <Card className="border-border bg-card">
1412
+ <CardHeader className="pb-3">
1413
+ <CardTitle className="text-base font-medium">
1414
+ Files with AI Prompts ({Object.keys(groupedByFile).length})
1415
+ </CardTitle>
1416
+ </CardHeader>
1417
+ <CardContent>
1418
+ <ScrollArea className="h-[300px]">
1419
+ <div className="space-y-2">
1420
+ {flowNodes.map((node) => (
1421
+ <button
1422
+ type="button"
1423
+ key={node.id}
1424
+ onClick={() => onFileClick?.(node.file)}
1425
+ className={`w-full text-left rounded-lg border p-3 transition-colors hover:opacity-80 ${typeColors[node.type]}`}
1426
+ >
1427
+ <div className="flex items-center justify-between">
1428
+ <div className="flex items-center gap-2">
1429
+ <span className="font-mono text-sm font-medium">{node.name}</span>
1430
+ <Badge variant="outline" className="text-xs">
1431
+ {node.prompts.length} prompt{node.prompts.length !== 1 ? "s" : ""}
1432
+ </Badge>
1433
+ </div>
1434
+ <Badge variant="outline" className="text-xs capitalize">
1435
+ {node.type}
1436
+ </Badge>
1437
+ </div>
1438
+ <p className="mt-1 font-mono text-xs opacity-70">{node.file}</p>
1439
+ {node.purpose && (
1440
+ <p className="mt-2 text-xs">{node.purpose}</p>
1441
+ )}
1442
+ </button>
1443
+ ))}
1444
+ </div>
1445
+ </ScrollArea>
1446
+ </CardContent>
1447
+ </Card>
1448
+ </div>
1449
+ );
1450
+ }
1451
+
1452
+ interface FlowStageProps {
1453
+ label: string;
1454
+ nodes: FlowNode[];
1455
+ colorClass: string;
1456
+ onNodeClick?: (file: string) => void;
1457
+ }
1458
+
1459
+ function FlowStage({ label, nodes, colorClass, onNodeClick }: FlowStageProps) {
1460
+ return (
1461
+ <div className="w-full max-w-2xl">
1462
+ <div className="mb-2 text-center text-xs font-medium text-muted-foreground">
1463
+ {label}
1464
+ </div>
1465
+ <div className={`rounded-lg border-2 p-4 ${colorClass}`}>
1466
+ <div className="flex flex-wrap justify-center gap-3">
1467
+ {nodes.map((node) => (
1468
+ <button
1469
+ type="button"
1470
+ key={node.id}
1471
+ onClick={() => onNodeClick?.(node.file)}
1472
+ className="flex items-center gap-2 rounded-md bg-background/50 px-3 py-1.5 transition-colors hover:bg-background/80"
1473
+ >
1474
+ <FileText className="h-3.5 w-3.5" />
1475
+ <span className="font-mono text-xs">{node.name}</span>
1476
+ <span className="text-xs opacity-60">({node.prompts.length})</span>
1477
+ </button>
1478
+ ))}
1479
+ </div>
1480
+ </div>
1481
+ </div>
1482
+ );
1483
+ }