@leanspec/ui 0.2.5-dev.20251120070726 → 0.2.5-dev.20251120093243

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 (361) hide show
  1. package/.next/standalone/packages/ui/.next/BUILD_ID +1 -1
  2. package/.next/standalone/packages/ui/.next/app-path-routes-manifest.json +12 -0
  3. package/.next/standalone/packages/ui/.next/build-manifest.json +7 -6
  4. package/.next/standalone/packages/ui/.next/prerender-manifest.json +28 -3
  5. package/.next/standalone/packages/ui/.next/routes-manifest.json +91 -0
  6. package/.next/standalone/packages/ui/.next/server/app/_global-error/page/build-manifest.json +5 -4
  7. package/.next/standalone/packages/ui/.next/server/app/_global-error/page.js +1 -1
  8. package/.next/standalone/packages/ui/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/packages/ui/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/packages/ui/.next/server/app/_global-error.html +2 -2
  11. package/.next/standalone/packages/ui/.next/server/app/_global-error.rsc +1 -1
  12. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  13. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/packages/ui/.next/server/app/_not-found/page/build-manifest.json +5 -4
  17. package/.next/standalone/packages/ui/.next/server/app/_not-found/page.js +2 -2
  18. package/.next/standalone/packages/ui/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/packages/ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/packages/ui/.next/server/app/_not-found.html +2 -2
  21. package/.next/standalone/packages/ui/.next/server/app/_not-found.rsc +20 -19
  22. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_full.segment.rsc +20 -19
  23. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_index.segment.rsc +26 -25
  24. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  25. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  26. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_tree.segment.rsc +5 -5
  27. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route/app-paths-manifest.json +3 -0
  28. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route/build-manifest.json +11 -0
  29. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route/server-reference-manifest.json +4 -0
  30. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route.js +6 -0
  31. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route.js.map +5 -0
  32. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route.js.nft.json +1 -0
  33. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route_client-reference-manifest.js +2 -0
  34. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route/app-paths-manifest.json +3 -0
  35. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route/build-manifest.json +11 -0
  36. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route/server-reference-manifest.json +4 -0
  37. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route.js +6 -0
  38. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route.js.map +5 -0
  39. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route.js.nft.json +1 -0
  40. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route_client-reference-manifest.js +2 -0
  41. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route/app-paths-manifest.json +3 -0
  42. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route/build-manifest.json +11 -0
  43. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route/server-reference-manifest.json +4 -0
  44. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route.js +6 -0
  45. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route.js.map +5 -0
  46. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route.js.nft.json +1 -0
  47. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route_client-reference-manifest.js +2 -0
  48. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route/app-paths-manifest.json +3 -0
  49. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route/build-manifest.json +11 -0
  50. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route/server-reference-manifest.json +4 -0
  51. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route.js +6 -0
  52. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route.js.map +5 -0
  53. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route.js.nft.json +1 -0
  54. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route_client-reference-manifest.js +2 -0
  55. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route/app-paths-manifest.json +3 -0
  56. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route/build-manifest.json +11 -0
  57. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route/server-reference-manifest.json +4 -0
  58. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route.js +8 -0
  59. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route.js.map +5 -0
  60. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route.js.nft.json +1 -0
  61. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route_client-reference-manifest.js +2 -0
  62. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route/app-paths-manifest.json +3 -0
  63. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route/build-manifest.json +11 -0
  64. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route/server-reference-manifest.json +4 -0
  65. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route.js +9 -0
  66. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route.js.map +5 -0
  67. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route.js.nft.json +1 -0
  68. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route_client-reference-manifest.js +2 -0
  69. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route/app-paths-manifest.json +3 -0
  70. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route/build-manifest.json +11 -0
  71. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route/server-reference-manifest.json +4 -0
  72. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route.js +8 -0
  73. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route.js.map +5 -0
  74. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route.js.nft.json +1 -0
  75. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route_client-reference-manifest.js +2 -0
  76. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/route.js +4 -2
  77. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/route.js.nft.json +1 -1
  78. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route/app-paths-manifest.json +3 -0
  79. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route/build-manifest.json +11 -0
  80. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route/server-reference-manifest.json +4 -0
  81. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route.js +8 -0
  82. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route.js.map +5 -0
  83. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route.js.nft.json +1 -0
  84. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route_client-reference-manifest.js +2 -0
  85. package/.next/standalone/packages/ui/.next/server/app/api/projects/route.js +3 -2
  86. package/.next/standalone/packages/ui/.next/server/app/api/projects/route.js.nft.json +1 -1
  87. package/.next/standalone/packages/ui/.next/server/app/api/revalidate/route.js +4 -2
  88. package/.next/standalone/packages/ui/.next/server/app/api/revalidate/route.js.nft.json +1 -1
  89. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/dependency-graph/route.js +4 -2
  90. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/dependency-graph/route.js.nft.json +1 -1
  91. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/route.js +4 -2
  92. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/route.js.nft.json +1 -1
  93. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/status/route.js +4 -3
  94. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/status/route.js.nft.json +1 -1
  95. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/subspecs/[file]/route.js +4 -2
  96. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/subspecs/[file]/route.js.nft.json +1 -1
  97. package/.next/standalone/packages/ui/.next/server/app/api/stats/route.js +4 -2
  98. package/.next/standalone/packages/ui/.next/server/app/api/stats/route.js.nft.json +1 -1
  99. package/.next/standalone/packages/ui/.next/server/app/page/build-manifest.json +5 -4
  100. package/.next/standalone/packages/ui/.next/server/app/page.js +2 -2
  101. package/.next/standalone/packages/ui/.next/server/app/page.js.nft.json +1 -1
  102. package/.next/standalone/packages/ui/.next/server/app/page_client-reference-manifest.js +1 -1
  103. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/app-paths-manifest.json +3 -0
  104. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/build-manifest.json +18 -0
  105. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/next-font-manifest.json +6 -0
  106. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/react-loadable-manifest.json +1 -0
  107. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/server-reference-manifest.json +4 -0
  108. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js +21 -0
  109. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js.map +5 -0
  110. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js.nft.json +1 -0
  111. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page_client-reference-manifest.js +2 -0
  112. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/app-paths-manifest.json +3 -0
  113. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/build-manifest.json +18 -0
  114. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/next-font-manifest.json +6 -0
  115. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/react-loadable-manifest.json +1 -0
  116. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/server-reference-manifest.json +4 -0
  117. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js +21 -0
  118. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js.map +5 -0
  119. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js.nft.json +1 -0
  120. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page_client-reference-manifest.js +2 -0
  121. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/app-paths-manifest.json +3 -0
  122. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/build-manifest.json +18 -0
  123. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/next-font-manifest.json +6 -0
  124. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/react-loadable-manifest.json +1 -0
  125. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/server-reference-manifest.json +4 -0
  126. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js +20 -0
  127. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js.map +5 -0
  128. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js.nft.json +1 -0
  129. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page_client-reference-manifest.js +2 -0
  130. package/.next/standalone/packages/ui/.next/server/app/projects/page/app-paths-manifest.json +3 -0
  131. package/.next/standalone/packages/ui/.next/server/app/projects/page/build-manifest.json +18 -0
  132. package/.next/standalone/packages/ui/.next/server/app/projects/page/next-font-manifest.json +6 -0
  133. package/.next/standalone/packages/ui/.next/server/app/projects/page/react-loadable-manifest.json +1 -0
  134. package/.next/standalone/packages/ui/.next/server/app/projects/page/server-reference-manifest.json +4 -0
  135. package/.next/standalone/packages/ui/.next/server/app/projects/page.js +19 -0
  136. package/.next/standalone/packages/ui/.next/server/app/projects/page.js.map +5 -0
  137. package/.next/standalone/packages/ui/.next/server/app/projects/page.js.nft.json +1 -0
  138. package/.next/standalone/packages/ui/.next/server/app/projects/page_client-reference-manifest.js +2 -0
  139. package/.next/standalone/packages/ui/.next/server/app/projects.html +2 -0
  140. package/.next/standalone/packages/ui/.next/server/app/projects.meta +14 -0
  141. package/.next/standalone/packages/ui/.next/server/app/projects.rsc +34 -0
  142. package/.next/standalone/packages/ui/.next/server/app/projects.segments/_full.segment.rsc +34 -0
  143. package/.next/standalone/packages/ui/.next/server/app/projects.segments/_index.segment.rsc +27 -0
  144. package/.next/standalone/packages/ui/.next/server/app/projects.segments/_tree.segment.rsc +9 -0
  145. package/.next/standalone/packages/ui/.next/server/app/projects.segments/projects/__PAGE__.segment.rsc +9 -0
  146. package/.next/standalone/packages/ui/.next/server/app/projects.segments/projects.segment.rsc +4 -0
  147. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page/build-manifest.json +5 -4
  148. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page.js +4 -4
  149. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page.js.nft.json +1 -1
  150. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page_client-reference-manifest.js +1 -1
  151. package/.next/standalone/packages/ui/.next/server/app/specs/page/build-manifest.json +5 -4
  152. package/.next/standalone/packages/ui/.next/server/app/specs/page.js +2 -2
  153. package/.next/standalone/packages/ui/.next/server/app/specs/page.js.nft.json +1 -1
  154. package/.next/standalone/packages/ui/.next/server/app/specs/page_client-reference-manifest.js +1 -1
  155. package/.next/standalone/packages/ui/.next/server/app/stats/page/build-manifest.json +5 -4
  156. package/.next/standalone/packages/ui/.next/server/app/stats/page.js +2 -2
  157. package/.next/standalone/packages/ui/.next/server/app/stats/page.js.nft.json +1 -1
  158. package/.next/standalone/packages/ui/.next/server/app/stats/page_client-reference-manifest.js +1 -1
  159. package/.next/standalone/packages/ui/.next/server/app-paths-manifest.json +12 -0
  160. package/.next/standalone/packages/ui/.next/server/chunks/6681a__next-internal_server_app_api_projects_[id]_specs_[spec]_route_actions_d018d9e0.js +3 -0
  161. package/.next/standalone/packages/ui/.next/server/chunks/730ea_ui__next-internal_server_app_api_local-projects_[id]_route_actions_664abe9c.js +3 -0
  162. package/.next/standalone/packages/ui/.next/server/chunks/730ea_ui__next-internal_server_app_api_local-projects_discover_route_actions_e6ec3fa7.js +3 -0
  163. package/.next/standalone/packages/ui/.next/server/chunks/730ea_ui__next-internal_server_app_api_projects_[id]_stats_route_actions_a0bb04a8.js +3 -0
  164. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__0fbfea15._.js +3 -0
  165. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__126dea7b._.js +3 -0
  166. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__1f037512._.js +3 -0
  167. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__3559376c._.js +21 -0
  168. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__548e27d4._.js +3 -0
  169. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__65667b70._.js +3 -0
  170. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__675a763e._.js +3 -0
  171. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__803d07f0._.js +3 -0
  172. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__8075fe29._.js +3 -0
  173. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__8233e09b._.js +3 -0
  174. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__84cdc14a._.js +3 -0
  175. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__9ce722b4._.js +3 -0
  176. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__a2d7cbed._.js +3 -0
  177. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__aa677b0d._.js +3 -0
  178. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__ac744182._.js +3 -0
  179. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__bb9fca84._.js +4 -0
  180. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__bdc3963a._.js +3 -0
  181. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__f5c6d6b8._.js +3 -0
  182. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__fb91986f._.js +3 -0
  183. package/.next/standalone/packages/ui/.next/server/chunks/f825c_server_app_api_local-projects_list-directory_route_actions_d620ec1a.js +3 -0
  184. package/.next/standalone/packages/ui/.next/server/chunks/f825c_server_app_api_projects_[id]_specs_[spec]_status_route_actions_e9f1bdc5.js +3 -0
  185. package/.next/standalone/packages/ui/.next/server/chunks/node_modules__pnpm_731b55aa._.js +3 -0
  186. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui__next-internal_server_app_api_local-projects_route_actions_9142d5ad.js +3 -0
  187. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui__next-internal_server_app_api_projects_[id]_route_actions_bff49675.js +3 -0
  188. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui_src_lib_db_index_ts_556307fa._.js +64 -5
  189. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui_src_lib_specs_sources_database-source_ts_0bd881ee._.js +3 -0
  190. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui_src_lib_specs_sources_database-source_ts_baa0160d._.js +3 -0
  191. package/.next/standalone/packages/ui/.next/server/chunks/ssr/38a22_sonner_dist_index_mjs_207730fe._.js +3 -0
  192. package/.next/standalone/packages/ui/.next/server/chunks/ssr/6e9bd_next_dist_7af75658._.js +3 -0
  193. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{_a5b58449._.js → 6e9bd_next_dist_df5dcbe9._.js} +2 -2
  194. package/.next/standalone/packages/ui/.next/server/chunks/ssr/730ea_ui__next-internal_server_app_projects_[projectId]_specs_page_actions_82e32fe4.js +3 -0
  195. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__01bfc8ff._.js +3 -0
  196. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__1d0c2012._.js +1 -1
  197. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__41f5b5c0._.js → [root-of-the-server]__4ec37765._.js} +2 -2
  198. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__1af33d49._.js → [root-of-the-server]__50122e7e._.js} +2 -2
  199. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__5382b397._.js +3 -0
  200. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__73f60f12._.js +7 -0
  201. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__88a691ee._.js +3 -0
  202. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__a7ae8552._.js +7 -0
  203. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__bdbbba01._.js +3 -0
  204. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__c592f5d1._.js +3 -0
  205. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__cc589a54._.js +3 -0
  206. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__ec5b96a5._.js +3 -0
  207. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__cbbbfb5d._.js → [root-of-the-server]__fd80e4dd._.js} +2 -2
  208. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_000dd317._.js +1 -1
  209. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_196b5a83._.js +3 -0
  210. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_39562b2a._.js +4 -0
  211. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_3b617676._.js +1 -1
  212. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_4129cc0f._.js +3 -0
  213. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_6174313a._.js +3 -0
  214. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_6e4dd8b6._.js +3 -0
  215. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_85effc18._.js +4 -0
  216. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_ad8c1e99._.js +1 -1
  217. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_bae6d2b8._.js +4 -0
  218. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_c2f54661._.js +5 -0
  219. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_fdd7780b._.js +4 -0
  220. package/.next/standalone/packages/ui/.next/server/chunks/ssr/f825c_server_app_projects_[projectId]_specs_[specId]_page_actions_22d9c35e.js +3 -0
  221. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_af9d3830._.js +3 -0
  222. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui__next-internal_server_app_projects_[projectId]_page_actions_f2986585.js +3 -0
  223. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui__next-internal_server_app_projects_page_actions_715940ea.js +3 -0
  224. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_projects_[projectId]_be242d6c._.js +3 -0
  225. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_projects_[projectId]_project-context-syncer_tsx_7954ea3b._.js +3 -0
  226. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_projects_page_tsx_6e166c8e._.js +3 -0
  227. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_specs_specs-client_tsx_0bb8f8f8._.js +1 -1
  228. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_components_ui_select_tsx_7e21d40f._.js +3 -0
  229. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_lib_specs_sources_database-source_ts_f7186c2a._.js +60 -1
  230. package/.next/standalone/packages/ui/.next/server/middleware-build-manifest.js +5 -4
  231. package/.next/standalone/packages/ui/.next/server/pages/404.html +2 -2
  232. package/.next/standalone/packages/ui/.next/server/pages/500.html +2 -2
  233. package/.next/standalone/packages/ui/.next/server/server-reference-manifest.js +1 -1
  234. package/.next/standalone/packages/ui/.next/server/server-reference-manifest.json +1 -1
  235. package/.next/standalone/packages/ui/.next/static/chunks/07f48b316e895080.js +1 -0
  236. package/.next/standalone/packages/ui/.next/static/chunks/0a7be410b438418a.js +1 -0
  237. package/.next/standalone/packages/ui/.next/static/chunks/1089ca88f6bef846.js +1 -0
  238. package/.next/standalone/packages/ui/.next/static/chunks/17471c87082c392c.js +5 -0
  239. package/.next/standalone/packages/ui/.next/static/chunks/220ba5cb8fe4dd69.js +1 -0
  240. package/.next/standalone/packages/ui/.next/static/chunks/35ccd3286e538bd2.js +1 -0
  241. package/.next/standalone/packages/ui/.next/static/chunks/571033caca823138.js +1 -0
  242. package/.next/standalone/packages/ui/.next/static/chunks/5a6fc30cd64085b4.css +1 -0
  243. package/.next/standalone/packages/ui/.next/static/chunks/5c2072ad938de8ed.js +1 -0
  244. package/.next/standalone/packages/ui/.next/static/chunks/843e1dfdc28e1385.js +1 -0
  245. package/.next/standalone/packages/ui/.next/static/chunks/9dad7fd31627df80.js +1 -0
  246. package/.next/standalone/packages/ui/.next/static/chunks/{c3d4d07de959ecf1.js → a055d4b2866ff8d4.js} +1 -1
  247. package/.next/standalone/packages/ui/.next/static/chunks/afb7c5a4255f4081.js +3 -0
  248. package/.next/{static/chunks/c557ac675be79771.js → standalone/packages/ui/.next/static/chunks/b760a3a9c2c8be28.js} +1 -1
  249. package/.next/standalone/packages/ui/.next/static/chunks/dcdd95180149fa10.js +1 -0
  250. package/.next/standalone/packages/ui/.next/static/chunks/ebd89051637b9a47.js +4 -0
  251. package/.next/standalone/packages/ui/.next/static/chunks/f9db24028d8c058e.js +1 -0
  252. package/.next/standalone/packages/ui/.next/static/chunks/fb640f50455ba34f.js +1 -0
  253. package/.next/standalone/packages/ui/.next/static/chunks/turbopack-b9690cacbf9fe48c.js +3 -0
  254. package/.next/standalone/packages/ui/bin/ui.js +17 -7
  255. package/.next/standalone/packages/ui/package.json +3 -1
  256. package/.next/standalone/packages/ui/src/app/api/local-projects/[id]/route.ts +117 -0
  257. package/.next/standalone/packages/ui/src/app/api/local-projects/discover/route.ts +41 -0
  258. package/.next/standalone/packages/ui/src/app/api/local-projects/list-directory/route.ts +38 -0
  259. package/.next/standalone/packages/ui/src/app/api/local-projects/route.ts +63 -0
  260. package/.next/standalone/packages/ui/src/app/api/projects/[id]/route.ts +97 -0
  261. package/.next/standalone/packages/ui/src/app/api/projects/[id]/specs/[spec]/route.ts +38 -0
  262. package/.next/standalone/packages/ui/src/app/api/projects/[id]/specs/[spec]/status/route.ts +97 -0
  263. package/.next/standalone/packages/ui/src/app/api/projects/[id]/specs/route.ts +7 -0
  264. package/.next/standalone/packages/ui/src/app/api/projects/[id]/stats/route.ts +19 -0
  265. package/.next/standalone/packages/ui/src/app/api/projects/route.ts +48 -1
  266. package/.next/standalone/packages/ui/src/app/dashboard-client.tsx +67 -6
  267. package/.next/standalone/packages/ui/src/app/layout.tsx +11 -8
  268. package/.next/standalone/packages/ui/src/app/not-found.tsx +1 -1
  269. package/.next/standalone/packages/ui/src/app/projects/[projectId]/layout.tsx +18 -0
  270. package/.next/standalone/packages/ui/src/app/projects/[projectId]/page.tsx +10 -0
  271. package/.next/standalone/packages/ui/src/app/projects/[projectId]/project-context-syncer.tsx +16 -0
  272. package/.next/standalone/packages/ui/src/app/projects/[projectId]/specs/[specId]/page.tsx +34 -0
  273. package/.next/standalone/packages/ui/src/app/projects/[projectId]/specs/page.tsx +20 -0
  274. package/.next/standalone/packages/ui/src/app/projects/page.tsx +156 -0
  275. package/.next/standalone/packages/ui/src/app/specs/specs-client.tsx +7 -2
  276. package/.next/standalone/packages/ui/src/components/create-project-dialog.tsx +139 -0
  277. package/.next/standalone/packages/ui/src/components/directory-picker.tsx +191 -0
  278. package/.next/standalone/packages/ui/src/components/main-sidebar.tsx +4 -0
  279. package/.next/standalone/packages/ui/src/components/project-switcher.tsx +139 -0
  280. package/.next/standalone/packages/ui/src/components/ui/dropdown-menu.tsx +200 -0
  281. package/.next/standalone/packages/ui/src/components/ui/popover.tsx +31 -0
  282. package/.next/standalone/packages/ui/src/contexts/project-context.tsx +229 -0
  283. package/.next/standalone/packages/ui/src/lib/db/index.ts +100 -0
  284. package/.next/standalone/packages/ui/src/lib/projects/index.ts +7 -0
  285. package/.next/standalone/packages/ui/src/lib/projects/registry.ts +467 -0
  286. package/.next/standalone/packages/ui/src/lib/projects/types.ts +37 -0
  287. package/.next/standalone/packages/ui/src/lib/specs/service.ts +13 -1
  288. package/.next/standalone/packages/ui/src/lib/specs/sources/multi-project-source.ts +258 -0
  289. package/.next/standalone/packages/ui/tsconfig.tsbuildinfo +1 -1
  290. package/.next/static/chunks/07f48b316e895080.js +1 -0
  291. package/.next/static/chunks/0a7be410b438418a.js +1 -0
  292. package/.next/static/chunks/1089ca88f6bef846.js +1 -0
  293. package/.next/static/chunks/17471c87082c392c.js +5 -0
  294. package/.next/static/chunks/220ba5cb8fe4dd69.js +1 -0
  295. package/.next/static/chunks/35ccd3286e538bd2.js +1 -0
  296. package/.next/static/chunks/571033caca823138.js +1 -0
  297. package/.next/static/chunks/5a6fc30cd64085b4.css +1 -0
  298. package/.next/static/chunks/5c2072ad938de8ed.js +1 -0
  299. package/.next/static/chunks/843e1dfdc28e1385.js +1 -0
  300. package/.next/static/chunks/9dad7fd31627df80.js +1 -0
  301. package/.next/static/chunks/{c3d4d07de959ecf1.js → a055d4b2866ff8d4.js} +1 -1
  302. package/.next/static/chunks/afb7c5a4255f4081.js +3 -0
  303. package/.next/{standalone/packages/ui/.next/static/chunks/c557ac675be79771.js → static/chunks/b760a3a9c2c8be28.js} +1 -1
  304. package/.next/static/chunks/dcdd95180149fa10.js +1 -0
  305. package/.next/static/chunks/ebd89051637b9a47.js +4 -0
  306. package/.next/static/chunks/f9db24028d8c058e.js +1 -0
  307. package/.next/static/chunks/fb640f50455ba34f.js +1 -0
  308. package/.next/static/chunks/turbopack-b9690cacbf9fe48c.js +3 -0
  309. package/bin/ui.js +17 -7
  310. package/package.json +3 -1
  311. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__175bef84._.js +0 -3
  312. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__3971eae5._.js +0 -3
  313. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__482b093a._.js +0 -21
  314. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__6bca1621._.js +0 -3
  315. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__87a3475a._.js +0 -3
  316. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__9f0f4c0b._.js +0 -3
  317. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__c1c9f5f5._.js +0 -3
  318. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__c6689757._.js +0 -3
  319. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__cd1fb0a2._.js +0 -4
  320. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__e2071b2e._.js +0 -3
  321. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__299c81cc._.js +0 -3
  322. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__5ca2e973._.js +0 -3
  323. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__b2fe773d._.js +0 -3
  324. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__e79a982a._.js +0 -7
  325. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__ff03fc1e._.js +0 -7
  326. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_66209e84._.js +0 -3
  327. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_6cd9a5e0._.js +0 -3
  328. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_b483c9fe._.js +0 -3
  329. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_b5040d31._.js +0 -5
  330. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_9710705b._.js +0 -3
  331. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_b8ff10b9._.js +0 -3
  332. package/.next/standalone/packages/ui/.next/static/chunks/093ea4b175adb770.js +0 -1
  333. package/.next/standalone/packages/ui/.next/static/chunks/5f97d665a4d9dc62.js +0 -3
  334. package/.next/standalone/packages/ui/.next/static/chunks/9a80c22382ddcfaf.css +0 -1
  335. package/.next/standalone/packages/ui/.next/static/chunks/9b7b7024c39c5f99.js +0 -1
  336. package/.next/standalone/packages/ui/.next/static/chunks/aa47b212786d8695.js +0 -1
  337. package/.next/standalone/packages/ui/.next/static/chunks/ae04dcd433be6dab.js +0 -1
  338. package/.next/standalone/packages/ui/.next/static/chunks/c0fe4c94c7282ac0.js +0 -4
  339. package/.next/standalone/packages/ui/.next/static/chunks/c61cbb6a0ba3b6ab.js +0 -1
  340. package/.next/standalone/packages/ui/.next/static/chunks/d79bf953f0dfb650.js +0 -1
  341. package/.next/standalone/packages/ui/.next/static/chunks/e50fb8d0b728cd35.js +0 -5
  342. package/.next/standalone/packages/ui/.next/static/chunks/e6e238dbf1d3e740.js +0 -1
  343. package/.next/standalone/packages/ui/.next/static/chunks/turbopack-9cc79aa1b34ffcbe.js +0 -3
  344. package/.next/static/chunks/093ea4b175adb770.js +0 -1
  345. package/.next/static/chunks/5f97d665a4d9dc62.js +0 -3
  346. package/.next/static/chunks/9a80c22382ddcfaf.css +0 -1
  347. package/.next/static/chunks/9b7b7024c39c5f99.js +0 -1
  348. package/.next/static/chunks/aa47b212786d8695.js +0 -1
  349. package/.next/static/chunks/ae04dcd433be6dab.js +0 -1
  350. package/.next/static/chunks/c0fe4c94c7282ac0.js +0 -4
  351. package/.next/static/chunks/c61cbb6a0ba3b6ab.js +0 -1
  352. package/.next/static/chunks/d79bf953f0dfb650.js +0 -1
  353. package/.next/static/chunks/e50fb8d0b728cd35.js +0 -5
  354. package/.next/static/chunks/e6e238dbf1d3e740.js +0 -1
  355. package/.next/static/chunks/turbopack-9cc79aa1b34ffcbe.js +0 -3
  356. /package/.next/standalone/packages/ui/.next/static/{C8sZuJV5DQETshOIgjsmH → T1wNN9JmkdwBabQsqL-tC}/_buildManifest.js +0 -0
  357. /package/.next/standalone/packages/ui/.next/static/{C8sZuJV5DQETshOIgjsmH → T1wNN9JmkdwBabQsqL-tC}/_clientMiddlewareManifest.json +0 -0
  358. /package/.next/standalone/packages/ui/.next/static/{C8sZuJV5DQETshOIgjsmH → T1wNN9JmkdwBabQsqL-tC}/_ssgManifest.js +0 -0
  359. /package/.next/static/{C8sZuJV5DQETshOIgjsmH → T1wNN9JmkdwBabQsqL-tC}/_buildManifest.js +0 -0
  360. /package/.next/static/{C8sZuJV5DQETshOIgjsmH → T1wNN9JmkdwBabQsqL-tC}/_clientMiddlewareManifest.json +0 -0
  361. /package/.next/static/{C8sZuJV5DQETshOIgjsmH → T1wNN9JmkdwBabQsqL-tC}/_ssgManifest.js +0 -0
@@ -0,0 +1,467 @@
1
+ /**
2
+ * Project registry for local filesystem projects
3
+ * Manages discovery, storage, and retrieval of local LeanSpec projects
4
+ */
5
+
6
+ import * as fs from 'node:fs/promises';
7
+ import * as path from 'node:path';
8
+ import { createHash } from 'node:crypto';
9
+ import { homedir } from 'node:os';
10
+ import yaml from 'js-yaml';
11
+ import type { LocalProject, ProjectsConfig, ProjectValidation } from './types';
12
+
13
+ const PROJECTS_CONFIG_FILE = path.join(homedir(), '.lean-spec', 'projects.yaml');
14
+
15
+ /**
16
+ * Project Registry - manages local filesystem projects
17
+ */
18
+ export class ProjectRegistry {
19
+ private config: ProjectsConfig | null = null;
20
+
21
+ /**
22
+ * Generate a unique ID for a project based on its path
23
+ */
24
+ private generateProjectId(projectPath: string): string {
25
+ return createHash('sha256')
26
+ .update(projectPath)
27
+ .digest('hex')
28
+ .substring(0, 12);
29
+ }
30
+
31
+ /**
32
+ * Load projects configuration from disk
33
+ */
34
+ private async loadConfig(): Promise<ProjectsConfig> {
35
+ if (this.config) {
36
+ return this.config;
37
+ }
38
+
39
+ try {
40
+ const configDir = path.dirname(PROJECTS_CONFIG_FILE);
41
+ await fs.mkdir(configDir, { recursive: true });
42
+
43
+ const fileContent = await fs.readFile(PROJECTS_CONFIG_FILE, 'utf-8');
44
+ const parsed = yaml.load(fileContent) as any;
45
+
46
+ // Convert date strings back to Date objects
47
+ const projects = (parsed.projects || []).map((p: any) => ({
48
+ ...p,
49
+ lastAccessed: p.lastAccessed ? new Date(p.lastAccessed) : new Date(),
50
+ }));
51
+
52
+ this.config = {
53
+ projects,
54
+ recentProjects: parsed.recentProjects || [],
55
+ };
56
+ } catch (error: any) {
57
+ if (error.code === 'ENOENT') {
58
+ // File doesn't exist, create empty config
59
+ this.config = {
60
+ projects: [],
61
+ recentProjects: [],
62
+ };
63
+ } else {
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ return this.config;
69
+ }
70
+
71
+ /**
72
+ * Save projects configuration to disk
73
+ */
74
+ private async saveConfig(): Promise<void> {
75
+ if (!this.config) {
76
+ return;
77
+ }
78
+
79
+ const configDir = path.dirname(PROJECTS_CONFIG_FILE);
80
+ await fs.mkdir(configDir, { recursive: true });
81
+
82
+ // Convert Date objects to ISO strings for serialization
83
+ const serializable = {
84
+ projects: this.config.projects.map((p) => ({
85
+ ...p,
86
+ lastAccessed: p.lastAccessed.toISOString(),
87
+ })),
88
+ recentProjects: this.config.recentProjects,
89
+ };
90
+
91
+ const yamlContent = yaml.dump(serializable, {
92
+ indent: 2,
93
+ lineWidth: 100,
94
+ });
95
+
96
+ await fs.writeFile(PROJECTS_CONFIG_FILE, yamlContent, 'utf-8');
97
+ }
98
+
99
+ /**
100
+ * Validate a project path and extract metadata
101
+ */
102
+ async validateProject(projectPath: string): Promise<ProjectValidation> {
103
+ try {
104
+ const normalizedPath = path.resolve(projectPath);
105
+
106
+ // Check if path exists
107
+ const stats = await fs.stat(normalizedPath);
108
+ if (!stats.isDirectory()) {
109
+ return {
110
+ isValid: false,
111
+ path: normalizedPath,
112
+ error: 'Path is not a directory',
113
+ };
114
+ }
115
+
116
+ // Look for .lean-spec directory or specs directory
117
+ let specsDir: string | undefined;
118
+ const leanSpecDir = path.join(normalizedPath, '.lean-spec');
119
+ const specsOnlyDir = path.join(normalizedPath, 'specs');
120
+
121
+ try {
122
+ await fs.access(leanSpecDir);
123
+ // If .lean-spec exists, check for specs inside or alongside
124
+ const specsInLeanSpec = path.join(leanSpecDir, '../specs');
125
+ try {
126
+ await fs.access(specsInLeanSpec);
127
+ specsDir = specsInLeanSpec;
128
+ } catch {
129
+ specsDir = specsOnlyDir;
130
+ }
131
+ } catch {
132
+ // No .lean-spec, check for specs directory
133
+ try {
134
+ await fs.access(specsOnlyDir);
135
+ specsDir = specsOnlyDir;
136
+ } catch {
137
+ return {
138
+ isValid: false,
139
+ path: normalizedPath,
140
+ error: 'No .lean-spec directory or specs directory found',
141
+ };
142
+ }
143
+ }
144
+
145
+ // Try to read project name and description from various sources
146
+ let name = path.basename(normalizedPath);
147
+ let description: string | undefined;
148
+
149
+ // Try leanspec.yaml
150
+ try {
151
+ const leanspecYaml = path.join(normalizedPath, 'leanspec.yaml');
152
+ const content = await fs.readFile(leanspecYaml, 'utf-8');
153
+ const config = yaml.load(content) as any;
154
+ if (config.name) name = config.name;
155
+ if (config.description) description = config.description;
156
+ } catch {
157
+ // Try package.json
158
+ try {
159
+ const packageJson = path.join(normalizedPath, 'package.json');
160
+ const content = await fs.readFile(packageJson, 'utf-8');
161
+ const pkg = JSON.parse(content);
162
+ if (pkg.name) name = pkg.name;
163
+ if (pkg.description) description = pkg.description;
164
+ } catch {
165
+ // Use directory name as fallback
166
+ }
167
+ }
168
+
169
+ return {
170
+ isValid: true,
171
+ path: normalizedPath,
172
+ specsDir: path.resolve(specsDir),
173
+ name,
174
+ description,
175
+ };
176
+ } catch (error) {
177
+ return {
178
+ isValid: false,
179
+ path: projectPath,
180
+ error: error instanceof Error ? error.message : 'Unknown error',
181
+ };
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Add a project to the registry
187
+ */
188
+ async addProject(projectPath: string, options?: { favorite?: boolean; color?: string }): Promise<LocalProject> {
189
+ const validation = await this.validateProject(projectPath);
190
+
191
+ if (!validation.isValid) {
192
+ throw new Error(`Invalid project: ${validation.error}`);
193
+ }
194
+
195
+ const config = await this.loadConfig();
196
+ const projectId = this.generateProjectId(validation.path);
197
+
198
+ // Check if project already exists
199
+ const existingIndex = config.projects.findIndex((p) => p.id === projectId);
200
+
201
+ const project: LocalProject = {
202
+ id: projectId,
203
+ name: validation.name!,
204
+ path: validation.path,
205
+ specsDir: validation.specsDir!,
206
+ lastAccessed: new Date(),
207
+ favorite: options?.favorite || false,
208
+ color: options?.color,
209
+ description: validation.description,
210
+ };
211
+
212
+ if (existingIndex >= 0) {
213
+ // Update existing project
214
+ config.projects[existingIndex] = project;
215
+ } else {
216
+ // Add new project
217
+ config.projects.push(project);
218
+ }
219
+
220
+ // Update recent projects
221
+ this.updateRecentProjects(config, projectId);
222
+
223
+ await this.saveConfig();
224
+ return project;
225
+ }
226
+
227
+ /**
228
+ * Remove a project from the registry
229
+ */
230
+ async removeProject(projectId: string): Promise<void> {
231
+ const config = await this.loadConfig();
232
+
233
+ config.projects = config.projects.filter((p) => p.id !== projectId);
234
+ config.recentProjects = config.recentProjects.filter((id) => id !== projectId);
235
+
236
+ await this.saveConfig();
237
+ }
238
+
239
+ /**
240
+ * Get a project by ID
241
+ */
242
+ async getProject(projectId: string): Promise<LocalProject | null> {
243
+ const config = await this.loadConfig();
244
+ return config.projects.find((p) => p.id === projectId) || null;
245
+ }
246
+
247
+ /**
248
+ * Find the closest project root with .lean-spec directory
249
+ */
250
+ private async findProjectRoot(startDir: string): Promise<string | null> {
251
+ let currentDir = path.resolve(startDir);
252
+ const { root } = path.parse(currentDir);
253
+
254
+ while (true) {
255
+ const leanSpecDir = path.join(currentDir, '.lean-spec');
256
+ try {
257
+ await fs.access(leanSpecDir);
258
+ return currentDir;
259
+ } catch {
260
+ // Not found
261
+ }
262
+
263
+ if (currentDir === root) {
264
+ break;
265
+ }
266
+ currentDir = path.dirname(currentDir);
267
+ }
268
+
269
+ return null;
270
+ }
271
+
272
+ /**
273
+ * Get all projects
274
+ */
275
+ async getProjects(): Promise<LocalProject[]> {
276
+ const config = await this.loadConfig();
277
+
278
+ // Auto-discover if no projects
279
+ if (config.projects.length === 0) {
280
+ const projectRoot = await this.findProjectRoot(process.cwd());
281
+ if (projectRoot) {
282
+ try {
283
+ const project = await this.addProject(projectRoot);
284
+ return [project];
285
+ } catch (e) {
286
+ // Ignore error if auto-add fails
287
+ console.warn('Failed to auto-add default project:', e);
288
+ }
289
+ }
290
+ }
291
+
292
+ return config.projects;
293
+ }
294
+
295
+ /**
296
+ * Update project lastAccessed timestamp and add to recent projects
297
+ */
298
+ async touchProject(projectId: string): Promise<void> {
299
+ const config = await this.loadConfig();
300
+
301
+ const project = config.projects.find((p) => p.id === projectId);
302
+ if (!project) {
303
+ return;
304
+ }
305
+
306
+ project.lastAccessed = new Date();
307
+ this.updateRecentProjects(config, projectId);
308
+
309
+ await this.saveConfig();
310
+ }
311
+
312
+ /**
313
+ * Toggle favorite status
314
+ */
315
+ async toggleFavorite(projectId: string): Promise<boolean> {
316
+ const config = await this.loadConfig();
317
+
318
+ const project = config.projects.find((p) => p.id === projectId);
319
+ if (!project) {
320
+ throw new Error('Project not found');
321
+ }
322
+
323
+ project.favorite = !project.favorite;
324
+ await this.saveConfig();
325
+
326
+ return project.favorite;
327
+ }
328
+
329
+ /**
330
+ * Update project metadata
331
+ */
332
+ async updateProject(projectId: string, updates: Partial<Pick<LocalProject, 'name' | 'color' | 'description'>>): Promise<LocalProject> {
333
+ const config = await this.loadConfig();
334
+
335
+ const project = config.projects.find((p) => p.id === projectId);
336
+ if (!project) {
337
+ throw new Error('Project not found');
338
+ }
339
+
340
+ Object.assign(project, updates);
341
+ await this.saveConfig();
342
+
343
+ return project;
344
+ }
345
+
346
+ /**
347
+ * Discover projects in a directory tree
348
+ */
349
+ async discoverProjects(rootDir: string, maxDepth: number = 3): Promise<ProjectValidation[]> {
350
+ const discovered: ProjectValidation[] = [];
351
+
352
+ const self = this;
353
+ async function scan(dir: string, depth: number) {
354
+ if (depth > maxDepth) {
355
+ return;
356
+ }
357
+
358
+ try {
359
+ const entries = await fs.readdir(dir, { withFileTypes: true });
360
+
361
+ for (const entry of entries) {
362
+ if (!entry.isDirectory()) {
363
+ continue;
364
+ }
365
+
366
+ // Skip common ignore patterns
367
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name.startsWith('.')) {
368
+ continue;
369
+ }
370
+
371
+ const fullPath = path.join(dir, entry.name);
372
+
373
+ // Check if this directory is a LeanSpec project
374
+ const validation = await self.validateProject(fullPath);
375
+ if (validation.isValid) {
376
+ discovered.push(validation);
377
+ } else {
378
+ // Recurse into subdirectories
379
+ await scan(fullPath, depth + 1);
380
+ }
381
+ }
382
+ } catch (error) {
383
+ // Skip directories we can't read
384
+ }
385
+ }
386
+
387
+ await scan(rootDir, 0);
388
+ return discovered;
389
+ }
390
+
391
+ /**
392
+ * List directories in a given path
393
+ */
394
+ async listDirectory(dirPath: string = homedir()): Promise<{ name: string; path: string; isDirectory: boolean }[]> {
395
+ try {
396
+ const normalizedPath = path.resolve(dirPath);
397
+ const entries = await fs.readdir(normalizedPath, { withFileTypes: true });
398
+
399
+ const items = entries
400
+ .filter(entry => entry.isDirectory() && !entry.name.startsWith('.'))
401
+ .map(entry => ({
402
+ name: entry.name,
403
+ path: path.join(normalizedPath, entry.name),
404
+ isDirectory: true
405
+ }))
406
+ .sort((a, b) => a.name.localeCompare(b.name));
407
+
408
+ // Add parent directory entry if not at root
409
+ const parentDir = path.dirname(normalizedPath);
410
+ if (parentDir !== normalizedPath) {
411
+ items.unshift({
412
+ name: '..',
413
+ path: parentDir,
414
+ isDirectory: true
415
+ });
416
+ }
417
+
418
+ return items;
419
+ } catch (error: any) {
420
+ throw new Error(`Failed to list directory: ${error.message}`);
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Get recent projects
426
+ */
427
+ async getRecentProjects(limit: number = 10): Promise<LocalProject[]> {
428
+ const config = await this.loadConfig();
429
+
430
+ return config.recentProjects
431
+ .slice(0, limit)
432
+ .map((id) => config.projects.find((p) => p.id === id))
433
+ .filter((p): p is LocalProject => p !== undefined);
434
+ }
435
+
436
+ /**
437
+ * Get favorite projects
438
+ */
439
+ async getFavoriteProjects(): Promise<LocalProject[]> {
440
+ const config = await this.loadConfig();
441
+ return config.projects.filter((p) => p.favorite);
442
+ }
443
+
444
+ /**
445
+ * Update recent projects list
446
+ */
447
+ private updateRecentProjects(config: ProjectsConfig, projectId: string): void {
448
+ // Remove if already in list
449
+ config.recentProjects = config.recentProjects.filter((id) => id !== projectId);
450
+
451
+ // Add to front
452
+ config.recentProjects.unshift(projectId);
453
+
454
+ // Keep only last 10
455
+ config.recentProjects = config.recentProjects.slice(0, 10);
456
+ }
457
+
458
+ /**
459
+ * Invalidate cached config
460
+ */
461
+ invalidateCache(): void {
462
+ this.config = null;
463
+ }
464
+ }
465
+
466
+ // Singleton instance
467
+ export const projectRegistry = new ProjectRegistry();
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Types for local multi-project support
3
+ */
4
+
5
+ /**
6
+ * Local project metadata
7
+ */
8
+ export interface LocalProject {
9
+ id: string; // Unique identifier (hash of path)
10
+ name: string; // Display name (from config or folder name)
11
+ path: string; // Absolute path to project root
12
+ specsDir: string; // Path to specs/ directory
13
+ lastAccessed: Date; // For sorting recent projects
14
+ favorite: boolean; // User can pin favorites
15
+ color?: string; // Optional color coding
16
+ description?: string; // From project README or config
17
+ }
18
+
19
+ /**
20
+ * Projects configuration file structure
21
+ */
22
+ export interface ProjectsConfig {
23
+ projects: LocalProject[];
24
+ recentProjects: string[]; // Project IDs in order (max 10)
25
+ }
26
+
27
+ /**
28
+ * Project validation result
29
+ */
30
+ export interface ProjectValidation {
31
+ isValid: boolean;
32
+ path: string;
33
+ specsDir?: string;
34
+ name?: string;
35
+ description?: string;
36
+ error?: string;
37
+ }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { FilesystemSource } from './sources/filesystem-source';
7
+ import { MultiProjectFilesystemSource } from './sources/multi-project-source';
7
8
  import type { SpecSource } from './types';
8
9
  import type { Spec } from '../db/schema';
9
10
 
@@ -14,10 +15,11 @@ let DatabaseSourceCtor: DatabaseSourceConstructor | null = null;
14
15
  /**
15
16
  * Service modes
16
17
  * - filesystem: Read from local filesystem only (LeanSpec's own specs)
18
+ * - multi-project: Read from multiple local filesystem projects
17
19
  * - database: Read from database only (external repos)
18
20
  * - both: Support both modes (route based on projectId)
19
21
  */
20
- type SpecsMode = 'filesystem' | 'database' | 'both';
22
+ type SpecsMode = 'filesystem' | 'multi-project' | 'database' | 'both';
21
23
 
22
24
  /**
23
25
  * Unified specs service
@@ -25,6 +27,7 @@ type SpecsMode = 'filesystem' | 'database' | 'both';
25
27
  */
26
28
  export class SpecsService {
27
29
  private filesystemSource?: FilesystemSource;
30
+ private multiProjectSource?: MultiProjectFilesystemSource;
28
31
  private databaseSource?: SpecSource;
29
32
  private mode: SpecsMode;
30
33
 
@@ -36,6 +39,10 @@ export class SpecsService {
36
39
  this.filesystemSource = new FilesystemSource(specsDir);
37
40
  }
38
41
 
42
+ if (this.mode === 'multi-project') {
43
+ this.multiProjectSource = new MultiProjectFilesystemSource();
44
+ }
45
+
39
46
  // Don't instantiate database source here - do it lazily
40
47
  }
41
48
 
@@ -102,6 +109,11 @@ export class SpecsService {
102
109
  * Get the appropriate source based on projectId and mode
103
110
  */
104
111
  private async getSource(projectId?: string): Promise<SpecSource> {
112
+ // Multi-project mode: use multi-project source
113
+ if (this.mode === 'multi-project' && this.multiProjectSource) {
114
+ return this.multiProjectSource;
115
+ }
116
+
105
117
  // If projectId provided, use database (external repo)
106
118
  if (projectId && (this.mode === 'database' || this.mode === 'both')) {
107
119
  return await this.getDatabaseSource();