@shepai/cli 1.72.9 → 1.73.0-pr222.ef53005

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 (449) hide show
  1. package/dist/packages/core/src/application/ports/output/agents/agent-executor-provider.interface.d.ts +1 -1
  2. package/dist/packages/core/src/application/ports/output/agents/agent-executor-provider.interface.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts +20 -1
  4. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
  5. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +68 -41
  6. package/dist/packages/core/src/application/use-cases/features/create/types.d.ts +10 -0
  7. package/dist/packages/core/src/application/use-cases/features/create/types.d.ts.map +1 -1
  8. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  9. package/dist/packages/core/src/infrastructure/di/container.js +2 -1
  10. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-provider.service.d.ts +4 -2
  11. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-provider.service.d.ts.map +1 -1
  12. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-provider.service.js +8 -4
  13. package/dist/packages/core/src/infrastructure/services/agents/common/agent-runner.service.js +1 -1
  14. package/dist/packages/core/src/infrastructure/services/agents/common/structured-agent-caller.service.js +1 -1
  15. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +1 -1
  16. package/dist/packages/core/src/infrastructure/services/notifications/notification.service.d.ts.map +1 -1
  17. package/dist/packages/core/src/infrastructure/services/notifications/notification.service.js +2 -4
  18. package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
  19. package/dist/src/presentation/web/app/actions/create-feature.js +20 -2
  20. package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts +10 -0
  21. package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts.map +1 -0
  22. package/dist/src/presentation/web/app/actions/get-feature-metadata.js +14 -0
  23. package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
  24. package/dist/src/presentation/web/app/api/agent-events/route.js +15 -0
  25. package/dist/src/presentation/web/app/build-feature-node-data.d.ts.map +1 -1
  26. package/dist/src/presentation/web/app/build-feature-node-data.js +2 -26
  27. package/dist/src/presentation/web/app/build-graph-nodes.d.ts.map +1 -1
  28. package/dist/src/presentation/web/app/build-graph-nodes.js +2 -26
  29. package/dist/src/presentation/web/app/layout.js +1 -1
  30. package/dist/src/presentation/web/components/common/add-repository-button/add-repository-button.d.ts.map +1 -0
  31. package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts +2 -0
  32. package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts.map +1 -0
  33. package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/index.js +0 -1
  34. package/dist/src/presentation/web/components/common/add-repository-button/pick-folder.d.ts.map +1 -0
  35. package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.d.ts.map +1 -1
  36. package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.js +13 -18
  37. package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts +2 -0
  38. package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts.map +1 -1
  39. package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.js +11 -11
  40. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  41. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +83 -144
  42. package/dist/src/presentation/web/components/common/control-center-drawer/index.d.ts +0 -1
  43. package/dist/src/presentation/web/components/common/control-center-drawer/index.d.ts.map +1 -1
  44. package/dist/src/presentation/web/components/common/control-center-drawer/index.js +0 -1
  45. package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.d.ts +13 -0
  46. package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.d.ts.map +1 -0
  47. package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.js +46 -0
  48. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.d.ts.map +1 -1
  49. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.js +7 -10
  50. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts +25 -0
  51. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
  52. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +45 -2
  53. package/dist/src/presentation/web/components/common/index.d.ts +2 -3
  54. package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
  55. package/dist/src/presentation/web/components/common/index.js +2 -3
  56. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +1 -1
  57. package/dist/src/presentation/web/components/common/repository-node/use-repository-actions.d.ts.map +1 -1
  58. package/dist/src/presentation/web/components/common/repository-node/use-repository-actions.js +14 -39
  59. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.d.ts +1 -3
  60. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.d.ts.map +1 -1
  61. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.js +3 -7
  62. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.d.ts +0 -1
  63. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.d.ts.map +1 -1
  64. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.js +0 -11
  65. package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.js +1 -1
  66. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  67. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +31 -14
  68. package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts +0 -11
  69. package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts.map +1 -1
  70. package/dist/src/presentation/web/components/features/control-center/control-center.stories.js +2 -37
  71. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +8 -0
  72. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  73. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +177 -350
  74. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +2 -10
  75. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
  76. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +11 -54
  77. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts.map +1 -1
  78. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.js +0 -46
  79. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +1 -1
  80. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts +1 -3
  81. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts.map +1 -1
  82. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.js +2 -2
  83. package/dist/src/presentation/web/components/ui/alert-dialog.d.ts.map +1 -1
  84. package/dist/src/presentation/web/components/ui/alert-dialog.js +2 -2
  85. package/dist/src/presentation/web/components/ui/dialog.d.ts.map +1 -1
  86. package/dist/src/presentation/web/components/ui/dialog.js +2 -2
  87. package/dist/src/presentation/web/components/ui/scroll-area.js +1 -1
  88. package/dist/src/presentation/web/hooks/agent-events-provider.d.ts.map +1 -1
  89. package/dist/src/presentation/web/hooks/agent-events-provider.js +3 -2
  90. package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -1
  91. package/dist/src/presentation/web/hooks/use-agent-events.js +19 -5
  92. package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
  93. package/dist/src/presentation/web/hooks/use-deploy-action.js +10 -13
  94. package/dist/src/presentation/web/hooks/use-graph-state.d.ts +42 -0
  95. package/dist/src/presentation/web/hooks/use-graph-state.d.ts.map +1 -0
  96. package/dist/src/presentation/web/hooks/use-graph-state.js +257 -0
  97. package/dist/src/presentation/web/hooks/use-notifications.d.ts +1 -5
  98. package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
  99. package/dist/src/presentation/web/hooks/use-notifications.js +1 -26
  100. package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
  101. package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -38
  102. package/dist/src/presentation/web/lib/derive-graph.d.ts +52 -0
  103. package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -0
  104. package/dist/src/presentation/web/lib/derive-graph.js +126 -0
  105. package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
  106. package/dist/src/presentation/web/lib/layout-with-dagre.js +1 -2
  107. package/dist/src/presentation/web/lib/logger.d.ts +7 -0
  108. package/dist/src/presentation/web/lib/logger.d.ts.map +1 -0
  109. package/dist/src/presentation/web/lib/logger.js +11 -0
  110. package/dist/tsconfig.build.tsbuildinfo +1 -1
  111. package/package.json +1 -1
  112. package/web/.next/BUILD_ID +1 -1
  113. package/web/.next/build-manifest.json +2 -2
  114. package/web/.next/cache/.previewinfo +1 -1
  115. package/web/.next/cache/.rscinfo +1 -1
  116. package/web/.next/cache/.tsbuildinfo +1 -1
  117. package/web/.next/cache/config.json +3 -3
  118. package/web/.next/fallback-build-manifest.json +2 -2
  119. package/web/.next/prerender-manifest.json +3 -3
  120. package/web/.next/required-server-files.js +1 -1
  121. package/web/.next/required-server-files.json +1 -1
  122. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +45 -30
  123. package/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
  124. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  125. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  126. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +51 -36
  127. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
  128. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  129. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  130. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +41 -26
  131. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  132. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  133. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  134. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +45 -30
  135. package/web/.next/server/app/(dashboard)/create/page.js +1 -1
  136. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  137. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  138. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +51 -36
  139. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
  140. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  141. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  142. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +41 -26
  143. package/web/.next/server/app/(dashboard)/page.js +1 -1
  144. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  145. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  146. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +41 -26
  147. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  148. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  149. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  150. package/web/.next/server/app/_global-error.html +2 -2
  151. package/web/.next/server/app/_global-error.rsc +1 -1
  152. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  153. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  154. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  155. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  156. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  157. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  158. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  159. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  160. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  161. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  162. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  163. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  164. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  165. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  166. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  167. package/web/.next/server/app/version/page.js.nft.json +1 -1
  168. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  169. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  170. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
  171. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  172. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  173. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +3 -0
  174. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js.map +1 -0
  175. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +3 -0
  176. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js.map +1 -0
  177. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  178. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js.map +1 -1
  179. package/web/.next/server/chunks/ssr/[root-of-the-server]__2018e25f._.js +1 -1
  180. package/web/.next/server/chunks/ssr/[root-of-the-server]__2018e25f._.js.map +1 -1
  181. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  182. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  183. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js.map +1 -1
  184. package/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
  185. package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -0
  186. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  187. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  188. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  189. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js.map +1 -1
  190. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  191. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  192. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js → server/chunks/ssr/[root-of-the-server]__94a6d275._.js} +2 -2
  193. package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -0
  194. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  195. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  196. package/web/.next/server/chunks/ssr/_07c2a32a._.js +1 -1
  197. package/web/.next/server/chunks/ssr/_07c2a32a._.js.map +1 -1
  198. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  199. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js.map +1 -1
  200. package/web/.next/server/chunks/ssr/_4f7c70c9._.js +6 -0
  201. package/web/.next/server/chunks/ssr/_4f7c70c9._.js.map +1 -0
  202. package/web/.next/server/chunks/ssr/{_1978f325._.js → _5ee4f5a7._.js} +2 -2
  203. package/web/.next/server/chunks/ssr/_5ee4f5a7._.js.map +1 -0
  204. package/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  205. package/web/.next/server/chunks/ssr/_61351ed3._.js.map +1 -1
  206. package/web/.next/server/chunks/ssr/_6e08ce21._.js +1 -1
  207. package/web/.next/server/chunks/ssr/_6e08ce21._.js.map +1 -1
  208. package/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  209. package/web/.next/server/chunks/ssr/_8400c8bb._.js.map +1 -1
  210. package/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  211. package/web/.next/server/chunks/ssr/_9f940437._.js.map +1 -1
  212. package/web/.next/server/chunks/ssr/src_presentation_web_163038ba._.js +8 -0
  213. package/web/.next/server/chunks/ssr/src_presentation_web_163038ba._.js.map +1 -0
  214. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5ea689db._.js → server/chunks/ssr/src_presentation_web_30d7eeda._.js} +2 -2
  215. package/web/.next/server/chunks/ssr/{src_presentation_web_5ea689db._.js.map → src_presentation_web_30d7eeda._.js.map} +1 -1
  216. package/web/.next/server/chunks/ssr/src_presentation_web_459cb687._.js +8 -0
  217. package/web/.next/server/chunks/ssr/src_presentation_web_459cb687._.js.map +1 -0
  218. package/web/.next/server/chunks/ssr/{src_presentation_web_5ea689db._.js → src_presentation_web_565a14f8._.js} +2 -2
  219. package/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js.map → src_presentation_web_565a14f8._.js.map} +1 -1
  220. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  221. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js.map +1 -1
  222. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +3 -0
  223. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js.map +1 -0
  224. package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +1 -1
  225. package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js.map +1 -1
  226. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  227. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  228. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  229. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  230. package/web/.next/server/pages/500.html +2 -2
  231. package/web/.next/server/server-reference-manifest.js +1 -1
  232. package/web/.next/server/server-reference-manifest.json +183 -126
  233. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  234. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  235. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  236. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  237. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +45 -30
  238. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
  239. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  240. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  241. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +51 -36
  242. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
  243. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  244. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  245. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +41 -26
  246. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  247. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  248. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  249. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +45 -30
  250. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js +1 -1
  251. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  252. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  253. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +51 -36
  254. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
  255. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  256. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  257. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +41 -26
  258. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js +1 -1
  259. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  260. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  261. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +41 -26
  262. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  263. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  264. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  265. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  266. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  267. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  268. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  269. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  270. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  271. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  272. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  273. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  274. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  275. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  276. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  277. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  278. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  279. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  280. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  281. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  282. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  283. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  284. package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  285. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  286. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +3 -0
  287. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +3 -0
  288. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  289. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2018e25f._.js +1 -1
  290. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  291. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  292. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
  293. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  294. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  295. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  296. package/web/.next/{server/chunks/ssr/[root-of-the-server]__e5e09f72._.js → standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js} +2 -2
  297. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  298. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_07c2a32a._.js +1 -1
  299. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  300. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_4f7c70c9._.js +6 -0
  301. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_1978f325._.js → _5ee4f5a7._.js} +2 -2
  302. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  303. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_6e08ce21._.js +1 -1
  304. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  305. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  306. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_163038ba._.js +8 -0
  307. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js → src_presentation_web_30d7eeda._.js} +2 -2
  308. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_459cb687._.js +8 -0
  309. package/web/.next/{server/chunks/ssr/src_presentation_web_7188a290._.js → standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_565a14f8._.js} +2 -2
  310. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  311. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +3 -0
  312. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +1 -1
  313. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  314. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  315. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  316. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  317. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +183 -126
  318. package/web/.next/standalone/src/presentation/web/CLAUDE.md +77 -33
  319. package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +27 -2
  320. package/web/.next/standalone/src/presentation/web/app/actions/get-feature-metadata.ts +18 -0
  321. package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +17 -0
  322. package/web/.next/standalone/src/presentation/web/app/build-feature-node-data.ts +3 -30
  323. package/web/.next/standalone/src/presentation/web/app/build-graph-nodes.ts +3 -30
  324. package/web/.next/standalone/src/presentation/web/app/layout.tsx +1 -1
  325. package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts +1 -0
  326. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +15 -21
  327. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/drawer-view.ts +12 -13
  328. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +116 -141
  329. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/index.ts +0 -1
  330. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.ts +56 -0
  331. package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/use-feature-actions.ts +6 -7
  332. package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +63 -2
  333. package/web/.next/standalone/src/presentation/web/components/common/index.ts +2 -12
  334. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +2 -2
  335. package/web/.next/standalone/src/presentation/web/components/common/repository-node/use-repository-actions.ts +29 -54
  336. package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.tsx +0 -13
  337. package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.tsx +1 -45
  338. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-empty-state.tsx +1 -1
  339. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +43 -36
  340. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +2 -41
  341. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +226 -438
  342. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +0 -48
  343. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +13 -76
  344. package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
  345. package/web/.next/standalone/src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx +2 -14
  346. package/web/.next/standalone/src/presentation/web/components/ui/alert-dialog.tsx +2 -5
  347. package/web/.next/standalone/src/presentation/web/components/ui/dialog.tsx +2 -5
  348. package/web/.next/standalone/src/presentation/web/components/ui/scroll-area.tsx +1 -1
  349. package/web/.next/standalone/src/presentation/web/hooks/agent-events-provider.tsx +7 -2
  350. package/web/.next/standalone/src/presentation/web/hooks/use-agent-events.ts +20 -5
  351. package/web/.next/standalone/src/presentation/web/hooks/use-deploy-action.ts +13 -13
  352. package/web/.next/standalone/src/presentation/web/hooks/use-graph-state.ts +376 -0
  353. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +2 -30
  354. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +2 -36
  355. package/web/.next/standalone/src/presentation/web/lib/derive-graph.ts +178 -0
  356. package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +1 -2
  357. package/web/.next/standalone/src/presentation/web/lib/logger.ts +12 -0
  358. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  359. package/web/.next/static/chunks/0cfed3c69c312c09.js +4 -0
  360. package/web/.next/static/chunks/0ffccd7be73305c8.css +2 -0
  361. package/web/.next/static/chunks/{c92e1360ada8ca94.js → 1f5c93a8b939416d.js} +2 -2
  362. package/web/.next/static/chunks/3f205fd6adbfd20a.js +7 -0
  363. package/web/.next/static/chunks/4c63764a2d06e45e.js +1 -0
  364. package/web/.next/static/chunks/{d4492cd12fdf0891.js → 89e74b1c3eb43b03.js} +1 -1
  365. package/web/.next/static/chunks/{c83897e9213881bf.js → 937d7d4d58942872.js} +1 -1
  366. package/web/.next/static/chunks/95e3f47abe8032e5.js +1 -0
  367. package/web/.next/static/chunks/b835e88cdc8c63d2.js +1 -0
  368. package/web/.next/static/chunks/cb41849bcfaa304e.js +1 -0
  369. package/web/.next/static/chunks/e2342af6b68c94c1.js +1 -0
  370. package/web/.next/static/chunks/ed69d6b172ae2bfe.js +1 -0
  371. package/web/.next/static/chunks/{64850cf4081a2950.js → fd46753cd16664a9.js} +1 -1
  372. package/web/.next/trace +1 -1
  373. package/web/.next/trace-build +1 -1
  374. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-button.d.ts.map +0 -1
  375. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts +0 -7
  376. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts.map +0 -1
  377. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.js +0 -1
  378. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts +0 -6
  379. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts.map +0 -1
  380. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.js +0 -23
  381. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts +0 -10
  382. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts.map +0 -1
  383. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.js +0 -33
  384. package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts +0 -4
  385. package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts.map +0 -1
  386. package/dist/src/presentation/web/components/common/add-repository-node/pick-folder.d.ts.map +0 -1
  387. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.d.ts +0 -12
  388. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.d.ts.map +0 -1
  389. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.js +0 -337
  390. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.d.ts +0 -33
  391. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.d.ts.map +0 -1
  392. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.js +0 -214
  393. package/dist/src/presentation/web/components/common/notification-permission-banner/index.d.ts +0 -2
  394. package/dist/src/presentation/web/components/common/notification-permission-banner/index.d.ts.map +0 -1
  395. package/dist/src/presentation/web/components/common/notification-permission-banner/index.js +0 -1
  396. package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.d.ts +0 -2
  397. package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.d.ts.map +0 -1
  398. package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.js +0 -33
  399. package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.d.ts +0 -6
  400. package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.d.ts.map +0 -1
  401. package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.js +0 -24
  402. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js +0 -3
  403. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js.map +0 -1
  404. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js +0 -3
  405. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js.map +0 -1
  406. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbae9863._.js.map +0 -1
  407. package/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js.map +0 -1
  408. package/web/.next/server/chunks/ssr/_1978f325._.js.map +0 -1
  409. package/web/.next/server/chunks/ssr/_38d24563._.js +0 -6
  410. package/web/.next/server/chunks/ssr/_38d24563._.js.map +0 -1
  411. package/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js +0 -8
  412. package/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js.map +0 -1
  413. package/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js +0 -8
  414. package/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js.map +0 -1
  415. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js +0 -3
  416. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js.map +0 -1
  417. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js +0 -3
  418. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js +0 -3
  419. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_38d24563._.js +0 -6
  420. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js +0 -8
  421. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js +0 -8
  422. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js +0 -3
  423. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node-config.ts +0 -8
  424. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.tsx +0 -70
  425. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.tsx +0 -49
  426. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/index.ts +0 -3
  427. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.tsx +0 -293
  428. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.tsx +0 -745
  429. package/web/.next/standalone/src/presentation/web/components/common/notification-permission-banner/index.ts +0 -1
  430. package/web/.next/standalone/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.tsx +0 -52
  431. package/web/.next/standalone/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.tsx +0 -55
  432. package/web/.next/static/chunks/0c54314c8c9c9739.js +0 -7
  433. package/web/.next/static/chunks/10cd08dae8c210ff.js +0 -1
  434. package/web/.next/static/chunks/15bf7ee1da91eb46.js +0 -4
  435. package/web/.next/static/chunks/3c79bca4d3f18f66.js +0 -1
  436. package/web/.next/static/chunks/67f2a7a5e8dc358a.css +0 -2
  437. package/web/.next/static/chunks/6cc840197626843b.js +0 -1
  438. package/web/.next/static/chunks/e3239b6c18cf8b96.js +0 -1
  439. package/web/.next/static/chunks/e9cc53ce8a7bdea6.js +0 -1
  440. package/web/.next/static/chunks/ef916db8c537211a.js +0 -1
  441. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.d.ts +0 -0
  442. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.js +0 -0
  443. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.d.ts +0 -0
  444. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.js +0 -0
  445. /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.tsx +0 -0
  446. /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.ts +0 -0
  447. /package/web/.next/static/{Atrl1oUfRQej2kgJ972uS → 0yTXyENV4ko7rpM4-3vV8}/_buildManifest.js +0 -0
  448. /package/web/.next/static/{Atrl1oUfRQej2kgJ972uS → 0yTXyENV4ko7rpM4-3vV8}/_clientMiddlewareManifest.json +0 -0
  449. /package/web/.next/static/{Atrl1oUfRQej2kgJ972uS → 0yTXyENV4ko7rpM4-3vV8}/_ssgManifest.js +0 -0
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { useState, useCallback, useEffect, useRef } from 'react';
3
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
4
4
  import { useRouter } from 'next/navigation';
5
5
  import { toast } from 'sonner';
6
- import { applyNodeChanges } from '@xyflow/react';
7
6
  import type { Connection, Edge, NodeChange } from '@xyflow/react';
8
7
  import type { FeatureNodeData } from '@/components/common/feature-node';
8
+ import type { RepositoryNodeData } from '@/components/common/repository-node';
9
9
  import type { CanvasNodeType } from '@/components/features/features-canvas';
10
10
  import {
11
11
  layoutWithDagre,
@@ -15,13 +15,21 @@ import {
15
15
  import { deleteFeature } from '@/app/actions/delete-feature';
16
16
  import { addRepository } from '@/app/actions/add-repository';
17
17
  import { deleteRepository } from '@/app/actions/delete-repository';
18
+ import { getFeatureMetadata } from '@/app/actions/get-feature-metadata';
18
19
  import { fetchGraphData } from '@/app/actions/get-graph-data';
19
20
  import { useAgentEventsContext } from '@/hooks/agent-events-provider';
20
21
  import { useSoundAction } from '@/hooks/use-sound-action';
22
+ import { createLogger } from '@/lib/logger';
23
+
21
24
  import {
22
25
  mapEventTypeToState,
23
- mapPhaseNameToLifecycle,
26
+ resolveSseEventUpdates,
24
27
  } from '@/components/common/feature-node/derive-feature-state';
28
+ import { useGraphState, type GraphCallbacks } from '@/hooks/use-graph-state';
29
+ import type { FeatureEntry } from '@/lib/derive-graph';
30
+
31
+ const log = createLogger('[Polling]');
32
+ const POLL_INTERVAL_MS = 3_000;
25
33
 
26
34
  export interface ControlCenterState {
27
35
  nodes: CanvasNodeType[];
@@ -37,8 +45,17 @@ export interface ControlCenterState {
37
45
  dataOverride?: Partial<FeatureNodeData>,
38
46
  edgeType?: string
39
47
  ) => string;
48
+ /** Stable lookup: repositoryPath for a feature node. */
49
+ getFeatureRepositoryPath: (featureNodeId: string) => string | undefined;
50
+ /** Stable lookup: repository data by nodeId. */
51
+ getRepositoryData: (nodeId: string) => RepositoryNodeData | undefined;
52
+ /** Sync callbacks into derived node data (does not trigger re-render). */
53
+ setCallbacks: (callbacks: GraphCallbacks) => void;
40
54
  }
41
55
 
56
+ /** Must match the message string emitted by the SSE route in agent-events/route.ts */
57
+ const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
58
+
42
59
  let nextFeatureId = 0;
43
60
 
44
61
  export function useControlCenterState(
@@ -46,123 +63,81 @@ export function useControlCenterState(
46
63
  initialEdges: Edge[]
47
64
  ): ControlCenterState {
48
65
  const router = useRouter();
49
- const [nodes, setNodes] = useState<CanvasNodeType[]>(initialNodes);
50
- // eslint-disable-next-line react/hook-use-state -- raw setter renamed; public setEdges wrapper keeps edgesRef in sync
51
- const [edges, setEdgesRaw] = useState<Edge[]>(initialEdges);
52
- const edgesRef = useRef<Edge[]>(initialEdges);
53
-
54
- // Wrapper that keeps edgesRef in sync with edges state, allowing
55
- // createFeatureNode to read current edges without a closure dependency.
56
- const setEdges = useCallback((update: Edge[] | ((prev: Edge[]) => Edge[])) => {
57
- setEdgesRaw((prev) => {
58
- const next = typeof update === 'function' ? update(prev) : update;
59
- edgesRef.current = next;
60
- return next;
61
- });
62
- }, []);
63
66
  const deleteSound = useSoundAction('delete');
64
67
  const createSound = useSoundAction('create');
65
68
 
66
- // Sync server props into local state when parent re-renders with new data
67
- const initialNodeKey = initialNodes
68
- .map((n) => n.id)
69
- .sort()
70
- .join(',');
71
- const initialEdgeKey = initialEdges
72
- .map((e) => e.id)
73
- .sort()
74
- .join(',');
75
-
76
- // Track previous feature states so we can detect transitions on server refresh
77
- const prevFeatureStatesRef = useRef<Map<string, FeatureNodeData['state']>>(new Map());
78
-
79
- // Stable key that changes when feature DATA changes (state/lifecycle), not just IDs.
80
- const initialDataKey = initialNodes
81
- .filter((n) => n.type === 'featureNode')
82
- .map((n) => {
83
- const d = n.data as FeatureNodeData;
84
- return `${n.id}:${d.state}:${d.lifecycle}`;
85
- })
86
- .sort()
87
- .join(',');
88
-
89
- // Sync server props into local state — keyed by derived strings (initialNodeKey/initialEdgeKey)
90
- // to avoid infinite re-renders from unstable array references.
69
+ const {
70
+ nodes,
71
+ edges,
72
+ reconcile,
73
+ updateFeature,
74
+ addPendingFeature,
75
+ removeFeature,
76
+ restoreFeature,
77
+ addRepository: addRepositoryToMap,
78
+ removeRepository,
79
+ replaceRepository,
80
+ getFeatureRepositoryPath,
81
+ getRepositoryData,
82
+ setCallbacks,
83
+ } = useGraphState(initialNodes, initialEdges);
84
+
85
+ // Refs for stable access to latest nodes/edges without callback recreation
86
+ const nodesRef = useRef(nodes);
87
+ const edgesRef = useRef(edges);
91
88
  useEffect(() => {
92
- setNodes((currentNodes) => {
93
- // Build a lookup of current node positions by ID
94
- const currentById = new Map(currentNodes.map((n) => [n.id, n]));
95
-
96
- // Identify optimistic "creating" nodes (they have temp IDs not in server data)
97
- const serverIds = new Set(initialNodes.map((n) => n.id));
98
- const creatingNodes = currentNodes.filter(
99
- (n) =>
100
- n.type === 'featureNode' &&
101
- (n.data as FeatureNodeData).state === 'creating' &&
102
- !serverIds.has(n.id)
103
- );
104
-
105
- // Merge server nodes with client positions, tracking if relayout is needed
106
- let needsRelayout = false;
107
- const merged = initialNodes.map((serverNode) => {
108
- // Node already exists on canvas — keep its position, update data
109
- const existing = currentById.get(serverNode.id);
110
- if (existing) {
111
- return { ...serverNode, position: existing.position };
112
- }
113
-
114
- // New server node — inherit position from an optimistic creating node if available
115
- if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
116
- const donor = creatingNodes.shift()!;
117
- return { ...serverNode, position: donor.position };
118
- }
119
-
120
- // Truly new node with no optimistic counterpart — needs relayout
121
- needsRelayout = true;
122
- return serverNode;
123
- });
124
-
125
- // Also check if non-creating nodes were removed (graph got smaller)
126
- if (!needsRelayout) {
127
- needsRelayout = currentNodes.some(
128
- (cn) =>
129
- !serverIds.has(cn.id) &&
130
- !(cn.type === 'featureNode' && (cn.data as FeatureNodeData).state === 'creating')
131
- );
132
- }
133
-
134
- if (needsRelayout) {
135
- const layoutResult = layoutWithDagre(merged, initialEdges, CANVAS_LAYOUT_DEFAULTS);
136
- setEdges(layoutResult.edges);
137
- return layoutResult.nodes;
138
- }
89
+ nodesRef.current = nodes;
90
+ edgesRef.current = edges;
91
+ }, [nodes, edges]);
92
+
93
+ // Sync server props into domain Maps when initialNodes/initialEdges change.
94
+ // Keyed by a stable string so we don't re-reconcile on every render.
95
+ const initialNodeKey = useMemo(
96
+ () =>
97
+ initialNodes
98
+ .map((n) => n.id)
99
+ .sort()
100
+ .join(','),
101
+ [initialNodes]
102
+ );
103
+ const initialDataKey = useMemo(
104
+ () =>
105
+ initialNodes
106
+ .filter((n) => n.type === 'featureNode')
107
+ .map((n) => {
108
+ const d = n.data as FeatureNodeData;
109
+ return `${n.id}:${d.state}:${d.lifecycle}`;
110
+ })
111
+ .sort()
112
+ .join(','),
113
+ [initialNodes]
114
+ );
139
115
 
140
- return merged;
141
- });
142
- }, [initialNodeKey, initialNodes, initialEdges, setEdges]);
116
+ const prevReconcileKey = useRef('');
143
117
 
144
118
  useEffect(() => {
145
- setEdges(initialEdges);
146
- }, [initialEdgeKey, initialEdges, setEdges]);
119
+ const key = `${initialNodeKey}|${initialDataKey}`;
120
+ if (key !== prevReconcileKey.current) {
121
+ prevReconcileKey.current = key;
122
+ reconcile(initialNodes, initialEdges);
123
+ }
124
+ }, [initialNodeKey, initialDataKey, initialNodes, initialEdges, reconcile]);
125
+
126
+ // Track previous feature states for fallback notifications
127
+ const prevFeatureStatesRef = useRef<Map<string, FeatureNodeData['state']>>(new Map());
147
128
 
148
- // Fallback notifications from server-prop state transitions.
149
- // Fires only when SSE didn't already deliver the matching event for a feature.
150
129
  const { events } = useAgentEventsContext();
151
130
 
152
131
  useEffect(() => {
153
132
  const prevStates = prevFeatureStatesRef.current;
154
-
155
133
  for (const node of initialNodes) {
156
134
  if (node.type !== 'featureNode') continue;
157
135
  const data = node.data as FeatureNodeData;
158
136
  const prev = prevStates.get(node.id);
159
-
160
137
  if (prev !== undefined && prev !== data.state) {
161
- // Check if SSE already delivered a matching event for this feature
162
138
  const sseAlreadyCovered = events.some(
163
139
  (e) => e.featureId === data.featureId && mapEventTypeToState(e.eventType) === data.state
164
140
  );
165
-
166
141
  if (!sseAlreadyCovered) {
167
142
  if (data.state === 'done') {
168
143
  toast.success(data.name, { description: 'Feature completed!' });
@@ -171,9 +146,7 @@ export function useControlCenterState(
171
146
  description: 'Waiting for your approval',
172
147
  action: {
173
148
  label: 'Review',
174
- onClick: () => {
175
- router.push(`/feature/${data.featureId}`);
176
- },
149
+ onClick: () => router.push(`/feature/${data.featureId}`),
177
150
  },
178
151
  });
179
152
  } else if (data.state === 'error') {
@@ -181,186 +154,102 @@ export function useControlCenterState(
181
154
  }
182
155
  }
183
156
  }
184
-
185
157
  prevStates.set(node.id, data.state);
186
158
  }
187
159
  }, [initialDataKey, initialNodes, events, router]);
188
160
 
189
- // Targeted optimistic updates from SSE agent events.
190
- // Uses `events` array (not `lastEvent`) so that React batching cannot silently
191
- // drop events when multiple SSE messages arrive in the same tick.
161
+ // SSE effect: only state + lifecycle updates
192
162
  const processedEventCountRef = useRef(0);
193
163
 
194
164
  useEffect(() => {
165
+ // Clamp cursor if events were pruned
166
+ if (processedEventCountRef.current > events.length) {
167
+ processedEventCountRef.current = 0;
168
+ }
195
169
  if (events.length <= processedEventCountRef.current) return;
196
-
197
170
  const newEvents = events.slice(processedEventCountRef.current);
198
171
  processedEventCountRef.current = events.length;
199
172
 
200
- for (const event of newEvents) {
201
- const newState = mapEventTypeToState(event.eventType);
202
- const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
203
-
204
- // Targeted node update only clone the matched node
205
- setNodes((prev) =>
206
- prev.map((node) => {
207
- if (node.type !== 'featureNode') return node;
208
- const data = node.data as FeatureNodeData;
209
- if (data.featureId !== event.featureId) return node;
210
-
211
- return {
212
- ...node,
213
- data: {
214
- ...data,
215
- state: newState,
216
- ...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
217
- },
218
- };
219
- })
220
- );
173
+ for (const { featureId, state, lifecycle } of resolveSseEventUpdates(newEvents)) {
174
+ if (state !== undefined || lifecycle !== undefined) {
175
+ updateFeature(`feat-${featureId}`, {
176
+ ...(state !== undefined && { state }),
177
+ ...(lifecycle !== undefined && { lifecycle }),
178
+ });
179
+ }
221
180
  }
222
- }, [events]);
223
-
224
- // Lightweight server-action polling fallback: fetches fresh graph data every 5s
225
- // when any feature is in an active state. This avoids full router.refresh() while
226
- // ensuring the UI stays in sync even if SSE events are missed.
227
- //
228
- // We derive a stable boolean so the interval isn't torn down on every node change.
229
- const hasActiveFeature = nodes.some((n) => {
230
- if (n.type !== 'featureNode') return false;
231
- const data = n.data as FeatureNodeData;
232
- return (
233
- data.state === 'running' || data.state === 'action-required' || data.state === 'creating'
234
- );
235
- });
181
+ }, [events, updateFeature]);
236
182
 
183
+ // Listen for optimistic approval events from the drawer (fires before SSE arrives)
237
184
  useEffect(() => {
238
- if (!hasActiveFeature) return;
239
-
240
- const interval = setInterval(async () => {
241
- try {
242
- const { nodes: serverNodes } = await fetchGraphData();
243
- setNodes((currentNodes) => {
244
- const serverById = new Map(serverNodes.map((n) => [n.id, n]));
245
- let changed = false;
246
-
247
- const merged = currentNodes.map((node) => {
248
- const serverNode = serverById.get(node.id);
249
- if (!serverNode || node.type !== 'featureNode') return node;
250
- const currentData = node.data as FeatureNodeData;
251
- const serverData = serverNode.data as FeatureNodeData;
252
-
253
- // Only update if server data has different state or lifecycle
254
- if (
255
- currentData.state === serverData.state &&
256
- currentData.lifecycle === serverData.lifecycle
257
- ) {
258
- return node;
259
- }
260
-
261
- changed = true;
262
- return {
263
- ...node,
264
- data: {
265
- ...currentData,
266
- state: serverData.state,
267
- lifecycle: serverData.lifecycle,
268
- progress: serverData.progress,
269
- ...(serverData.errorMessage && { errorMessage: serverData.errorMessage }),
270
- ...(serverData.runtime && { runtime: serverData.runtime }),
271
- ...(serverData.agentType && { agentType: serverData.agentType }),
272
- ...(serverData.pr && { pr: serverData.pr }),
273
- ...(serverData.blockedBy && { blockedBy: serverData.blockedBy }),
274
- },
275
- };
276
- });
185
+ const handler = (e: Event) => {
186
+ const { featureId } = (e as CustomEvent<{ featureId: string }>).detail;
187
+ updateFeature(`feat-${featureId}`, { state: 'running' });
188
+ };
189
+ window.addEventListener('shep:feature-approved', handler);
190
+ return () => window.removeEventListener('shep:feature-approved', handler);
191
+ }, [updateFeature]);
192
+
193
+ // Separate effect: fetch metadata (name + description) when SSE reports it changed
194
+ const metadataFetchedRef = useRef<Set<string>>(new Set());
195
+ const processedMetadataCountRef = useRef(0);
277
196
 
278
- // Find new server nodes not yet on the client
279
- const clientIds = new Set(currentNodes.map((n) => n.id));
280
- const newServerNodes = serverNodes.filter((n) => !clientIds.has(n.id));
281
-
282
- // Replace optimistic "creating" temp nodes with their real server
283
- // counterparts. Temp nodes have IDs like "feature-<ts>" while real
284
- // nodes use "feat-<uuid>". Match by repositoryPath + name.
285
- if (newServerNodes.length > 0) {
286
- const creatingIndices: number[] = [];
287
- merged.forEach((n, i) => {
288
- if (n.type === 'featureNode' && (n.data as FeatureNodeData).state === 'creating') {
289
- creatingIndices.push(i);
290
- }
291
- });
197
+ useEffect(() => {
198
+ // Clamp cursor if events were pruned
199
+ if (processedMetadataCountRef.current > events.length) {
200
+ processedMetadataCountRef.current = 0;
201
+ }
202
+ if (events.length <= processedMetadataCountRef.current) return;
203
+ const newEvents = events.slice(processedMetadataCountRef.current);
204
+ processedMetadataCountRef.current = events.length;
292
205
 
293
- for (const serverNode of newServerNodes) {
294
- if (serverNode.type !== 'featureNode' || creatingIndices.length === 0) continue;
295
- const idx = creatingIndices.shift()!;
296
- // Replace the temp node in-place, keeping its position
297
- merged[idx] = { ...serverNode, position: merged[idx].position };
298
- changed = true;
299
- }
300
-
301
- // Any remaining new server nodes that didn't replace a temp node
302
- const replacedIds = new Set(merged.map((n) => n.id));
303
- const trueNewNodes = newServerNodes.filter((n) => !replacedIds.has(n.id));
304
- if (trueNewNodes.length > 0) {
305
- changed = true;
306
- return [...merged, ...trueNewNodes];
307
- }
206
+ for (const event of newEvents) {
207
+ if (event.message !== METADATA_UPDATED_MESSAGE) continue;
208
+ if (metadataFetchedRef.current.has(event.featureId)) continue;
209
+ metadataFetchedRef.current.add(event.featureId);
210
+
211
+ const nodeId = `feat-${event.featureId}`;
212
+ getFeatureMetadata(event.featureId)
213
+ .then((meta) => {
214
+ if (meta) {
215
+ updateFeature(nodeId, { name: meta.name, description: meta.description });
308
216
  }
309
-
310
- return changed ? merged : currentNodes;
217
+ })
218
+ .catch(() => {
219
+ // Silent: metadata fetch failure is non-critical
311
220
  });
312
- } catch {
313
- // Silently ignore polling errors — SSE is the primary mechanism
314
- }
315
- }, 5_000);
316
-
317
- return () => clearInterval(interval);
318
- }, [hasActiveFeature]);
319
-
320
- const onNodesChange = useCallback((changes: NodeChange<CanvasNodeType>[]) => {
321
- // Filter out 'replace' changes — in controlled mode these come from
322
- // React Flow's StoreUpdater syncing the nodes prop back into state.
323
- // Applying them creates a circular update loop (nodes → enrichedNodes →
324
- // StoreUpdater → onNodesChange → setNodes → nodes → …) that both causes
325
- // excessive re-renders and overwrites SSE-driven state changes with stale data.
326
- const applicable = changes.filter((c) => c.type !== 'replace');
327
- if (applicable.length > 0) {
328
- setNodes((ns) => applyNodeChanges(applicable, ns));
329
221
  }
330
- }, []);
331
-
332
- const handleConnect = useCallback(
333
- (connection: Connection) => {
334
- if (!connection.source || !connection.target) return;
222
+ }, [events, updateFeature]);
335
223
 
336
- setNodes((currentNodes) => {
337
- const sourceNode = currentNodes.find((n) => n.id === connection.source);
338
- if (sourceNode?.type !== 'repositoryNode') return currentNodes;
224
+ // --- Polling fallback: catch any SSE events that were missed ---
225
+ useEffect(() => {
226
+ log.debug(`polling enabled (${POLL_INTERVAL_MS}ms interval)`);
339
227
 
340
- // Block if target feature already has a repo source
341
- setEdges((currentEdges) => {
342
- const targetAlreadyHasRepo = currentEdges.some((e) => {
343
- const edgeSource = currentNodes.find((n) => n.id === e.source);
344
- return edgeSource?.type === 'repositoryNode' && e.target === connection.target;
345
- });
346
- if (targetAlreadyHasRepo) return currentEdges;
347
-
348
- return [
349
- ...currentEdges,
350
- {
351
- id: `edge-${connection.source}-${connection.target}`,
352
- source: connection.source,
353
- target: connection.target,
354
- style: { strokeDasharray: '5 5' },
355
- },
356
- ];
357
- });
228
+ const timer = setInterval(async () => {
229
+ try {
230
+ const { nodes: freshNodes, edges: freshEdges } = await fetchGraphData();
231
+ reconcile(freshNodes, freshEdges);
232
+ } catch {
233
+ log.warn('poll fetch failed — will retry next interval');
234
+ }
235
+ }, POLL_INTERVAL_MS);
236
+
237
+ return () => {
238
+ log.debug('polling disabled');
239
+ clearInterval(timer);
240
+ };
241
+ }, [reconcile]);
242
+
243
+ // onNodesChange is a no-op: nodes are derived from domain Maps.
244
+ // Since nodesDraggable=false and elementsSelectable=false, only React Flow's
245
+ // internal replace/dimensions changes come through — we ignore them all.
246
+ const onNodesChange = useCallback((_changes: NodeChange<CanvasNodeType>[]) => {
247
+ // Intentional no-op: domain Maps are the source of truth.
248
+ }, []);
358
249
 
359
- return currentNodes;
360
- });
361
- },
362
- [setEdges]
363
- );
250
+ const handleConnect = useCallback((_connection: Connection) => {
251
+ // Connections are managed via domain operations, not direct edge manipulation.
252
+ }, []);
364
253
 
365
254
  const createFeatureNode = useCallback(
366
255
  (
@@ -368,11 +257,14 @@ export function useControlCenterState(
368
257
  dataOverride?: Partial<FeatureNodeData>,
369
258
  edgeType?: string
370
259
  ): string => {
371
- const id = `feature-${Date.now()}-${nextFeatureId++}`;
260
+ // Use real feature ID when available (from server), otherwise temp ID
261
+ const id = dataOverride?.featureId
262
+ ? `feat-${dataOverride.featureId}`
263
+ : `feature-${Date.now()}-${nextFeatureId++}`;
372
264
  const newFeatureData: FeatureNodeData = {
373
265
  name: dataOverride?.name ?? 'New Feature',
374
266
  description: dataOverride?.description ?? 'Describe what this feature does',
375
- featureId: `#${id.slice(-4)}`,
267
+ featureId: dataOverride?.featureId ?? `#${id.slice(-4)}`,
376
268
  lifecycle: 'requirements',
377
269
  state: dataOverride?.state ?? 'running',
378
270
  progress: 0,
@@ -380,171 +272,115 @@ export function useControlCenterState(
380
272
  branch: dataOverride?.branch ?? '',
381
273
  };
382
274
 
383
- setNodes((currentNodes) => {
384
- const newNode = {
385
- id,
386
- type: 'featureNode' as const,
387
- position: { x: 0, y: 0 },
388
- data: newFeatureData,
389
- } as CanvasNodeType;
390
-
391
- const newEdge = sourceNodeId
392
- ? {
393
- id:
394
- edgeType === 'dependencyEdge'
395
- ? `dep-${sourceNodeId}-${id}`
396
- : `edge-${sourceNodeId}-${id}`,
397
- source: sourceNodeId,
398
- target: id,
399
- ...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
400
- }
401
- : null;
402
-
403
- const allEdges = newEdge
404
- ? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
405
- : edgesRef.current;
406
-
407
- // Insert the new node after the last sibling connected to the same source,
408
- // so dagre's input-order preservation keeps repo groups together.
409
- let insertIndex = currentNodes.length;
410
- if (sourceNodeId) {
411
- const siblingIds = new Set(
412
- allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target)
413
- );
414
- for (let i = currentNodes.length - 1; i >= 0; i--) {
415
- if (siblingIds.has(currentNodes[i].id)) {
416
- insertIndex = i + 1;
417
- break;
418
- }
419
- }
420
- }
421
- const allNodes = [
422
- ...currentNodes.slice(0, insertIndex),
423
- newNode,
424
- ...currentNodes.slice(insertIndex),
425
- ];
426
-
427
- // Run dagre layout on the full graph so the new node is positioned consistently
428
- const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
429
- setEdges(layoutResult.edges);
430
- return layoutResult.nodes;
431
- });
275
+ const parentNodeId = edgeType === 'dependencyEdge' && sourceNodeId ? sourceNodeId : undefined;
276
+
277
+ addPendingFeature(id, newFeatureData, parentNodeId);
432
278
 
433
279
  return id;
434
280
  },
435
- [setEdges]
281
+ [addPendingFeature]
436
282
  );
437
283
 
438
284
  const handleDeleteFeature = useCallback(
439
285
  (featureId: string) => {
440
286
  const nodeId = `feat-${featureId}`;
441
287
 
442
- // Snapshot current state for rollback
443
- const prevNodes = nodes;
444
- const prevEdges = edgesRef.current;
288
+ // Find the current entry for rollback
289
+ const prevEntry = nodesRef.current
290
+ .filter((n) => n.id === nodeId)
291
+ .map((n) => ({
292
+ nodeId: n.id,
293
+ data: n.data as FeatureNodeData,
294
+ }))[0];
445
295
 
446
- // Optimistic removal — update UI immediately
447
- setNodes((currentNodes) => {
448
- const remainingNodes = currentNodes.filter((n) => n.id !== nodeId);
449
- const remainingEdges = edgesRef.current.filter(
450
- (e) => e.source !== nodeId && e.target !== nodeId
451
- );
452
- const layoutResult = layoutWithDagre(
453
- remainingNodes,
454
- remainingEdges,
455
- CANVAS_LAYOUT_DEFAULTS
456
- );
457
- setEdges(layoutResult.edges);
458
- return layoutResult.nodes;
459
- });
296
+ // Optimistic removal
297
+ removeFeature(nodeId);
460
298
  deleteSound.play();
461
299
  toast.success('Feature deleted successfully');
462
300
  router.push('/');
463
301
 
464
- // Persist in background — rollback on failure
465
302
  deleteFeature(featureId)
466
303
  .then((result) => {
467
304
  if (result.error) {
468
- setNodes(prevNodes);
469
- setEdges(prevEdges);
305
+ if (prevEntry) restoreFeature(nodeId, prevEntry);
470
306
  toast.error(result.error);
471
307
  }
472
308
  })
473
309
  .catch(() => {
474
- setNodes(prevNodes);
475
- setEdges(prevEdges);
310
+ if (prevEntry) restoreFeature(nodeId, prevEntry);
476
311
  toast.error('Failed to delete feature');
477
312
  });
478
313
  },
479
- [nodes, router, deleteSound, setEdges]
314
+ [router, deleteSound, removeFeature, restoreFeature]
480
315
  );
481
316
 
482
317
  const handleDeleteRepository = useCallback(
483
318
  async (repositoryId: string) => {
484
319
  const repoNodeId = `repo-${repositoryId}`;
485
320
 
321
+ // Find children of this repo via edges
322
+ const childFeatureIds = new Set(
323
+ edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target)
324
+ );
325
+
486
326
  // Snapshot for rollback
487
- const prevNodes = nodes;
488
- const prevEdges = edgesRef.current;
489
-
490
- // Optimistic: remove repo node, its child feature nodes, and all related edges
491
- setNodes((currentNodes) => {
492
- // Find feature node IDs connected to this repo via edges
493
- const childFeatureIds = new Set(
494
- edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target)
495
- );
496
- const remainingNodes = currentNodes.filter(
497
- (n) => n.id !== repoNodeId && !childFeatureIds.has(n.id)
498
- );
499
- const remainingEdges = edgesRef.current.filter(
500
- (e) =>
501
- e.source !== repoNodeId &&
502
- e.target !== repoNodeId &&
503
- !childFeatureIds.has(e.source) &&
504
- !childFeatureIds.has(e.target)
505
- );
506
- const layoutResult = layoutWithDagre(
507
- remainingNodes,
508
- remainingEdges,
509
- CANVAS_LAYOUT_DEFAULTS
510
- );
511
- setEdges(layoutResult.edges);
512
- return layoutResult.nodes;
513
- });
327
+ const prevRepoData = getRepositoryData(repoNodeId);
328
+ const childSnapshots = new Map<string, FeatureEntry>();
329
+ for (const childId of childFeatureIds) {
330
+ const childNode = nodesRef.current.find((n) => n.id === childId);
331
+ if (childNode) {
332
+ childSnapshots.set(childId, { nodeId: childId, data: childNode.data as FeatureNodeData });
333
+ }
334
+ }
335
+
336
+ const rollback = () => {
337
+ if (prevRepoData) addRepositoryToMap(repoNodeId, prevRepoData);
338
+ for (const [childId, entry] of childSnapshots) {
339
+ restoreFeature(childId, entry);
340
+ }
341
+ };
342
+
343
+ // Optimistic: remove repo + children
344
+ removeRepository(repoNodeId);
345
+ for (const childId of childFeatureIds) {
346
+ removeFeature(childId);
347
+ }
514
348
 
515
349
  try {
516
350
  const result = await deleteRepository(repositoryId);
517
-
518
351
  if (!result.success) {
519
352
  toast.error(result.error ?? 'Failed to remove repository');
520
- setNodes(prevNodes);
521
- setEdges(prevEdges);
353
+ rollback();
522
354
  return;
523
355
  }
524
-
525
356
  deleteSound.play();
526
357
  toast.success('Repository removed');
527
358
  } catch {
528
359
  toast.error('Failed to remove repository');
529
- setNodes(prevNodes);
530
- setEdges(prevEdges);
360
+ rollback();
531
361
  }
532
362
  },
533
- [nodes, deleteSound, setEdges]
363
+ [
364
+ deleteSound,
365
+ removeRepository,
366
+ removeFeature,
367
+ addRepositoryToMap,
368
+ restoreFeature,
369
+ getRepositoryData,
370
+ ]
534
371
  );
535
372
 
536
373
  const handleLayout = useCallback(
537
374
  (direction: LayoutDirection) => {
538
- setNodes((currentNodes) => {
539
- const result = layoutWithDagre(currentNodes, edgesRef.current, {
540
- ...CANVAS_LAYOUT_DEFAULTS,
541
- direction,
542
- });
543
- setEdges(result.edges);
544
- return result.nodes;
375
+ // Layout is applied via reconcile on next server prop update.
376
+ // For immediate re-layout, we apply dagre and trigger a reconcile-like update.
377
+ const result = layoutWithDagre(nodesRef.current, edgesRef.current, {
378
+ ...CANVAS_LAYOUT_DEFAULTS,
379
+ direction,
545
380
  });
381
+ reconcile(result.nodes, result.edges);
546
382
  },
547
- [setEdges]
383
+ [reconcile]
548
384
  );
549
385
 
550
386
  const handleAddRepository = useCallback(
@@ -556,81 +392,30 @@ export function useControlCenterState(
556
392
  .split(/[\\/]/)
557
393
  .pop() ?? path;
558
394
 
559
- // Optimistic UI: add node and re-layout immediately
560
- setNodes((currentNodes) => {
561
- const newNode = {
562
- id: tempId,
563
- type: 'repositoryNode' as const,
564
- position: { x: 0, y: 0 },
565
- data: { name: repoName, repositoryPath: path, id: tempId },
566
- } as CanvasNodeType;
567
-
568
- const allNodes = [...currentNodes, newNode];
569
- const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
570
- setEdges(layoutResult.edges);
571
- return layoutResult.nodes;
572
- });
395
+ addRepositoryToMap(tempId, { name: repoName, repositoryPath: path, id: tempId });
573
396
 
574
- // Persist via server action
575
397
  addRepository({ path, name: repoName })
576
398
  .then((result) => {
577
399
  if (result.error) {
578
- // Rollback optimistic node and re-layout
579
- setNodes((prev) => {
580
- const remaining = prev.filter((n) => n.id !== tempId);
581
- const layoutResult = layoutWithDagre(
582
- remaining,
583
- edgesRef.current,
584
- CANVAS_LAYOUT_DEFAULTS
585
- );
586
- setEdges(layoutResult.edges);
587
- return layoutResult.nodes;
588
- });
400
+ removeRepository(tempId);
589
401
  toast.error(result.error);
590
402
  return;
591
403
  }
592
-
593
- // Replace temp ID with real repository ID
594
404
  const repo = result.repository!;
595
405
  const realId = `repo-${repo.id}`;
596
- setNodes((prev) =>
597
- prev.map((n) =>
598
- n.id === tempId
599
- ? ({
600
- ...n,
601
- id: realId,
602
- data: { ...n.data, id: repo.id, repositoryPath: repo.path },
603
- } as CanvasNodeType)
604
- : n
605
- )
606
- );
607
- setEdges((prev) =>
608
- prev.map((e) => ({
609
- ...e,
610
- source: e.source === tempId ? realId : e.source,
611
- target: e.target === tempId ? realId : e.target,
612
- id: e.id.replace(tempId, realId),
613
- }))
614
- );
615
-
406
+ replaceRepository(tempId, realId, {
407
+ name: repo.name,
408
+ repositoryPath: repo.path,
409
+ id: repo.id,
410
+ });
616
411
  createSound.play();
617
412
  })
618
413
  .catch(() => {
619
- // Rollback optimistic node and re-layout
620
- setNodes((prev) => {
621
- const remaining = prev.filter((n) => n.id !== tempId);
622
- const layoutResult = layoutWithDagre(
623
- remaining,
624
- edgesRef.current,
625
- CANVAS_LAYOUT_DEFAULTS
626
- );
627
- setEdges(layoutResult.edges);
628
- return layoutResult.nodes;
629
- });
414
+ removeRepository(tempId);
630
415
  toast.error('Failed to add repository');
631
416
  });
632
417
  },
633
- [createSound, setEdges]
418
+ [addRepositoryToMap, removeRepository, replaceRepository, createSound]
634
419
  );
635
420
 
636
421
  return {
@@ -643,5 +428,8 @@ export function useControlCenterState(
643
428
  handleDeleteFeature,
644
429
  handleDeleteRepository,
645
430
  createFeatureNode,
431
+ getFeatureRepositoryPath,
432
+ getRepositoryData,
433
+ setCallbacks,
646
434
  };
647
435
  }