@shepai/cli 1.72.8 → 1.72.9-pr220.e1bfec0

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 (365) hide show
  1. package/dist/packages/core/src/application/ports/output/agents/feature-agent-process.interface.d.ts +2 -1
  2. package/dist/packages/core/src/application/ports/output/agents/feature-agent-process.interface.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/use-cases/agents/approve-agent-run.use-case.d.ts.map +1 -1
  4. package/dist/packages/core/src/application/use-cases/agents/approve-agent-run.use-case.js +1 -0
  5. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts +20 -1
  6. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
  7. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +68 -41
  8. package/dist/packages/core/src/application/use-cases/features/create/types.d.ts +10 -0
  9. package/dist/packages/core/src/application/use-cases/features/create/types.d.ts.map +1 -1
  10. package/dist/packages/core/src/application/use-cases/features/resume-feature.use-case.d.ts.map +1 -1
  11. package/dist/packages/core/src/application/use-cases/features/resume-feature.use-case.js +1 -0
  12. package/dist/packages/core/src/domain/factories/settings-defaults.factory.js +1 -1
  13. package/dist/packages/core/src/infrastructure/services/agents/common/executors/dev-executor.service.d.ts.map +1 -1
  14. package/dist/packages/core/src/infrastructure/services/agents/common/executors/dev-executor.service.js +88 -13
  15. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +1 -1
  16. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts +2 -1
  17. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts.map +1 -1
  18. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.js +3 -0
  19. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts +2 -0
  20. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  21. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +16 -3
  22. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts.map +1 -1
  23. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.js +5 -1
  24. package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
  25. package/dist/src/presentation/web/app/actions/create-feature.js +20 -2
  26. package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts +10 -0
  27. package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts.map +1 -0
  28. package/dist/src/presentation/web/app/actions/get-feature-metadata.js +14 -0
  29. package/dist/src/presentation/web/app/actions/get-graph-data.d.ts +10 -0
  30. package/dist/src/presentation/web/app/actions/get-graph-data.d.ts.map +1 -0
  31. package/dist/src/presentation/web/app/actions/get-graph-data.js +10 -0
  32. package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
  33. package/dist/src/presentation/web/app/api/agent-events/route.js +18 -0
  34. package/dist/src/presentation/web/components/common/add-repository-button/add-repository-button.d.ts.map +1 -0
  35. package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts +2 -0
  36. package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts.map +1 -0
  37. package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/index.js +0 -1
  38. package/dist/src/presentation/web/components/common/add-repository-button/pick-folder.d.ts.map +1 -0
  39. package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.d.ts.map +1 -1
  40. package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.js +21 -23
  41. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  42. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +35 -16
  43. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
  44. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +2 -0
  45. package/dist/src/presentation/web/components/common/index.d.ts +1 -1
  46. package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
  47. package/dist/src/presentation/web/components/common/index.js +1 -1
  48. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +1 -1
  49. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.d.ts +1 -3
  50. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.d.ts.map +1 -1
  51. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.js +3 -7
  52. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.d.ts +0 -1
  53. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.d.ts.map +1 -1
  54. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.js +0 -11
  55. package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.js +1 -1
  56. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  57. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +23 -4
  58. package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts +0 -11
  59. package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts.map +1 -1
  60. package/dist/src/presentation/web/components/features/control-center/control-center.stories.js +2 -37
  61. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +8 -0
  62. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  63. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +154 -258
  64. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +2 -10
  65. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
  66. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +12 -44
  67. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts.map +1 -1
  68. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.js +0 -46
  69. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +1 -1
  70. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts +1 -3
  71. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts.map +1 -1
  72. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.js +2 -2
  73. package/dist/src/presentation/web/components/ui/scroll-area.js +1 -1
  74. package/dist/src/presentation/web/hooks/use-graph-state.d.ts +41 -0
  75. package/dist/src/presentation/web/hooks/use-graph-state.d.ts.map +1 -0
  76. package/dist/src/presentation/web/hooks/use-graph-state.js +230 -0
  77. package/dist/src/presentation/web/lib/derive-graph.d.ts +52 -0
  78. package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -0
  79. package/dist/src/presentation/web/lib/derive-graph.js +126 -0
  80. package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
  81. package/dist/src/presentation/web/lib/layout-with-dagre.js +1 -2
  82. package/dist/tsconfig.build.tsbuildinfo +1 -1
  83. package/package.json +1 -1
  84. package/web/.next/BUILD_ID +1 -1
  85. package/web/.next/build-manifest.json +2 -2
  86. package/web/.next/cache/.previewinfo +1 -1
  87. package/web/.next/cache/.rscinfo +1 -1
  88. package/web/.next/cache/.tsbuildinfo +1 -1
  89. package/web/.next/cache/config.json +3 -3
  90. package/web/.next/fallback-build-manifest.json +2 -2
  91. package/web/.next/prerender-manifest.json +3 -3
  92. package/web/.next/required-server-files.js +1 -1
  93. package/web/.next/required-server-files.json +1 -1
  94. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +43 -28
  95. package/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
  96. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  97. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  98. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +49 -34
  99. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
  100. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  101. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  102. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  103. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  104. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  105. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  106. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +43 -28
  107. package/web/.next/server/app/(dashboard)/create/page.js +1 -1
  108. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  109. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  110. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +49 -34
  111. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
  112. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  113. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  114. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +39 -24
  115. package/web/.next/server/app/(dashboard)/page.js +1 -1
  116. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  117. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  118. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  119. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  120. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  121. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  122. package/web/.next/server/app/_global-error.html +2 -2
  123. package/web/.next/server/app/_global-error.rsc +1 -1
  124. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  125. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  126. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  127. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  128. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  129. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  130. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  131. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  132. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  133. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  134. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  135. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  136. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  137. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  138. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  139. package/web/.next/server/app/version/page.js.nft.json +1 -1
  140. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  141. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  142. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
  143. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  144. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +3 -0
  146. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js.map +1 -0
  147. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
  148. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js.map +1 -0
  149. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  150. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js.map +1 -1
  151. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  152. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  153. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js.map +1 -1
  154. package/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
  155. package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -0
  156. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  157. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  158. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  159. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js.map +1 -1
  160. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  161. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  162. 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
  163. package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -0
  164. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  165. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  166. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  167. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js.map +1 -1
  168. package/web/.next/server/chunks/ssr/_250a63ae._.js.map +1 -1
  169. package/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  170. package/web/.next/server/chunks/ssr/_61351ed3._.js.map +1 -1
  171. package/web/.next/server/chunks/ssr/_63688261._.js +6 -0
  172. package/web/.next/server/chunks/ssr/_63688261._.js.map +1 -0
  173. package/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  174. package/web/.next/server/chunks/ssr/_8400c8bb._.js.map +1 -1
  175. package/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  176. package/web/.next/server/chunks/ssr/_9f940437._.js.map +1 -1
  177. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js → server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
  178. package/web/.next/server/chunks/ssr/{src_presentation_web_5c87e8e4._.js.map → src_presentation_web_2b91e4ec._.js.map} +1 -1
  179. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js → server/chunks/ssr/src_presentation_web_5bcc1537._.js} +2 -2
  180. package/web/.next/server/chunks/ssr/{src_presentation_web_398553b0._.js.map → src_presentation_web_5bcc1537._.js.map} +1 -1
  181. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
  182. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js.map +1 -0
  183. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  184. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
  185. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  186. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  187. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  188. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  189. package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
  190. package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js.map +1 -0
  191. package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
  192. package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js.map +1 -0
  193. package/web/.next/server/pages/500.html +2 -2
  194. package/web/.next/server/server-reference-manifest.js +1 -1
  195. package/web/.next/server/server-reference-manifest.json +175 -118
  196. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  197. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  198. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  199. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  200. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +43 -28
  201. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
  202. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  203. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  204. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +49 -34
  205. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
  206. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  207. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  208. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  209. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  210. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  211. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  212. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +43 -28
  213. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js +1 -1
  214. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  215. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  216. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +49 -34
  217. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
  218. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  219. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  220. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +39 -24
  221. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js +1 -1
  222. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  223. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  224. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  225. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  226. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  227. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  228. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  229. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  230. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  231. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  232. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  233. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  234. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  235. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  236. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  237. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  238. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  239. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  240. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  241. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  242. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  243. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  244. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  245. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  246. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  247. package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  248. 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
  249. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +3 -0
  250. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
  251. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  252. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  253. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  254. 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
  255. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  256. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  257. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  258. 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
  259. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  260. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  261. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  262. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_63688261._.js +6 -0
  263. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  264. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  265. package/web/.next/{server/chunks/ssr/src_presentation_web_398553b0._.js → standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
  266. package/web/.next/{server/chunks/ssr/src_presentation_web_5c87e8e4._.js → standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5bcc1537._.js} +2 -2
  267. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
  268. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  269. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  270. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  271. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
  272. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
  273. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  274. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  275. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +175 -118
  276. package/web/.next/standalone/src/presentation/web/CLAUDE.md +191 -0
  277. package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +27 -2
  278. package/web/.next/standalone/src/presentation/web/app/actions/get-feature-metadata.ts +18 -0
  279. package/web/.next/standalone/src/presentation/web/app/actions/get-graph-data.ts +12 -0
  280. package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +21 -1
  281. package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts +1 -0
  282. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +23 -26
  283. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +44 -18
  284. package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +2 -0
  285. package/web/.next/standalone/src/presentation/web/components/common/index.ts +1 -5
  286. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +2 -2
  287. package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.tsx +0 -13
  288. package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.tsx +1 -45
  289. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-empty-state.tsx +1 -1
  290. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +24 -8
  291. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +2 -41
  292. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +183 -326
  293. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +0 -48
  294. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +34 -82
  295. package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
  296. package/web/.next/standalone/src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx +2 -14
  297. package/web/.next/standalone/src/presentation/web/components/ui/scroll-area.tsx +1 -1
  298. package/web/.next/standalone/src/presentation/web/hooks/use-graph-state.ts +343 -0
  299. package/web/.next/standalone/src/presentation/web/lib/derive-graph.ts +178 -0
  300. package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +1 -2
  301. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  302. package/web/.next/static/chunks/{71cf72022f08a8c9.js → 0a883e2efecfd563.js} +1 -1
  303. package/web/.next/static/chunks/{ebf172a84c9596ed.js → 596ca28d78baae25.js} +1 -1
  304. package/web/.next/static/chunks/{1c07b21eb8b32f25.js → 638824249d103905.js} +1 -1
  305. package/web/.next/static/chunks/63a80e9d4c4b0253.js +4 -0
  306. package/web/.next/static/chunks/71c9ecdceac1939f.css +2 -0
  307. package/web/.next/static/chunks/a040b058d79f4ade.js +1 -0
  308. package/web/.next/static/chunks/{fcafb841e3860687.js → c575ab63c138645b.js} +1 -1
  309. package/web/.next/static/chunks/cdc92cdf81005c84.js +1 -0
  310. package/web/.next/static/chunks/{fabfa54940973cc7.js → e05eb1cc84ee254a.js} +1 -1
  311. package/web/.next/static/chunks/{8682dcb376de62de.js → ef8f8940eac6c542.js} +1 -1
  312. package/web/.next/static/chunks/f32c98a709949c5b.js +1 -0
  313. package/web/.next/trace +1 -1
  314. package/web/.next/trace-build +1 -1
  315. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-button.d.ts.map +0 -1
  316. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts +0 -7
  317. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts.map +0 -1
  318. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.js +0 -1
  319. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts +0 -6
  320. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts.map +0 -1
  321. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.js +0 -23
  322. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts +0 -10
  323. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts.map +0 -1
  324. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.js +0 -33
  325. package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts +0 -4
  326. package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts.map +0 -1
  327. package/dist/src/presentation/web/components/common/add-repository-node/pick-folder.d.ts.map +0 -1
  328. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +0 -3
  329. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js.map +0 -1
  330. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +0 -3
  331. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js.map +0 -1
  332. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbae9863._.js.map +0 -1
  333. package/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js.map +0 -1
  334. package/web/.next/server/chunks/ssr/_47ef589d._.js +0 -6
  335. package/web/.next/server/chunks/ssr/_47ef589d._.js.map +0 -1
  336. package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +0 -8
  337. package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js.map +0 -1
  338. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +0 -3
  339. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js.map +0 -1
  340. package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +0 -8
  341. package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js.map +0 -1
  342. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +0 -3
  343. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +0 -3
  344. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_47ef589d._.js +0 -6
  345. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +0 -8
  346. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +0 -3
  347. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +0 -8
  348. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node-config.ts +0 -8
  349. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.tsx +0 -70
  350. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.tsx +0 -49
  351. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/index.ts +0 -3
  352. package/web/.next/static/chunks/3c79bca4d3f18f66.js +0 -1
  353. package/web/.next/static/chunks/5cd24ca47061a21d.js +0 -4
  354. package/web/.next/static/chunks/67f2a7a5e8dc358a.css +0 -2
  355. package/web/.next/static/chunks/991d4490d7a338d8.js +0 -1
  356. package/web/.next/static/chunks/c71589df79020ecb.js +0 -1
  357. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.d.ts +0 -0
  358. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.js +0 -0
  359. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.d.ts +0 -0
  360. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.js +0 -0
  361. /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.tsx +0 -0
  362. /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.ts +0 -0
  363. /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → uMxPSDPwZCludF5408rWJ}/_buildManifest.js +0 -0
  364. /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → uMxPSDPwZCludF5408rWJ}/_clientMiddlewareManifest.json +0 -0
  365. /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → uMxPSDPwZCludF5408rWJ}/_ssgManifest.js +0 -0
@@ -83,12 +83,6 @@ const repoNode = {
83
83
  position: { x: 50, y: 115 },
84
84
  data: { name: 'shep-ai/cli' },
85
85
  };
86
- const addRepoNode = {
87
- id: 'add-repo',
88
- type: 'addRepositoryNode',
89
- position: { x: 50, y: 302 },
90
- data: {},
91
- };
92
86
  const dashedEdge = { style: { strokeDasharray: '5 5' } };
93
87
  export const Empty = {
94
88
  args: {
@@ -113,7 +107,7 @@ export const WithToolbar = {
113
107
  };
114
108
  export const WithNodeActions = {
115
109
  args: {
116
- initialNodes: [repoNode, addRepoNode, ...featureNodes],
110
+ initialNodes: [repoNode, ...featureNodes],
117
111
  initialEdges: [
118
112
  { id: 'e-repo-f1', source: 'repo-1', target: 'feat-1', ...dashedEdge },
119
113
  { id: 'e-repo-f2', source: 'repo-1', target: 'feat-2', ...dashedEdge },
@@ -121,29 +115,6 @@ export const WithNodeActions = {
121
115
  ],
122
116
  },
123
117
  };
124
- /**
125
- * Interactive story — starts with just an "Add Repository" button.
126
- *
127
- * User flow:
128
- * 1. Click "Add Repository" → native folder picker → select a folder → repo node appears
129
- * 2. Hover the repo node → click (+) → a new feature node appears connected to the repo
130
- * 3. Hover the feature node → click (+) → another feature appears connected to the first
131
- *
132
- * You can also use the toolbar "Add Feature" button to create unconnected features.
133
- */
134
- export const Interactive = {
135
- args: {
136
- initialNodes: [
137
- {
138
- id: 'add-repo',
139
- type: 'addRepositoryNode',
140
- position: { x: 50, y: 50 },
141
- data: {},
142
- },
143
- ],
144
- initialEdges: [],
145
- },
146
- };
147
118
  // --- Dagre-layouted story: multiple repos with feature trees ---
148
119
  const dagreRepoNodes = [
149
120
  {
@@ -237,13 +208,7 @@ const dagreFeatureNodes = [
237
208
  },
238
209
  },
239
210
  ];
240
- const dagreAddRepo = {
241
- id: 'add-repo',
242
- type: 'addRepositoryNode',
243
- position: { x: 0, y: 0 },
244
- data: {},
245
- };
246
- const dagreNodesRaw = [...dagreRepoNodes, ...dagreFeatureNodes, dagreAddRepo];
211
+ const dagreNodesRaw = [...dagreRepoNodes, ...dagreFeatureNodes];
247
212
  const dagreEdges = [
248
213
  // cli repo → Auth, SSO
249
214
  { id: 'e-cli-auth', source: 'repo-cli', target: 'feat-auth', style: { strokeDasharray: '5 5' } },
@@ -1,7 +1,9 @@
1
1
  import type { Connection, Edge, NodeChange } from '@xyflow/react';
2
2
  import type { FeatureNodeData } from '../../common/feature-node/index.js';
3
+ import type { RepositoryNodeData } from '../../common/repository-node/index.js';
3
4
  import type { CanvasNodeType } from '../../features/features-canvas/index.js';
4
5
  import { type LayoutDirection } from '../../../lib/layout-with-dagre.js';
6
+ import { type GraphCallbacks } from '../../../hooks/use-graph-state.js';
5
7
  export interface ControlCenterState {
6
8
  nodes: CanvasNodeType[];
7
9
  edges: Edge[];
@@ -12,6 +14,12 @@ export interface ControlCenterState {
12
14
  handleDeleteFeature: (featureId: string) => void;
13
15
  handleDeleteRepository: (repositoryId: string) => Promise<void>;
14
16
  createFeatureNode: (sourceNodeId: string | null, dataOverride?: Partial<FeatureNodeData>, edgeType?: string) => string;
17
+ /** Stable lookup: repositoryPath for a feature node. */
18
+ getFeatureRepositoryPath: (featureNodeId: string) => string | undefined;
19
+ /** Stable lookup: repository data by nodeId. */
20
+ getRepositoryData: (nodeId: string) => RepositoryNodeData | undefined;
21
+ /** Sync callbacks into derived node data (does not trigger re-render). */
22
+ setCallbacks: (callbacks: GraphCallbacks) => void;
15
23
  }
16
24
  export declare function useControlCenterState(initialNodes: CanvasNodeType[], initialEdges: Edge[]): ControlCenterState;
17
25
  //# sourceMappingURL=use-control-center-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAWjC,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;CACb;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA+epB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAYjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAuVpB"}
@@ -1,45 +1,29 @@
1
1
  'use client';
2
- import { useState, useCallback, useEffect, useRef } from 'react';
2
+ import { useCallback, useEffect, useRef } from 'react';
3
3
  import { useRouter } from 'next/navigation';
4
4
  import { toast } from 'sonner';
5
- import { applyNodeChanges } from '@xyflow/react';
6
5
  import { layoutWithDagre, CANVAS_LAYOUT_DEFAULTS, } from '../../../lib/layout-with-dagre.js';
7
6
  import { deleteFeature } from '../../../app/actions/delete-feature.js';
8
7
  import { addRepository } from '../../../app/actions/add-repository.js';
9
8
  import { deleteRepository } from '../../../app/actions/delete-repository.js';
9
+ import { getFeatureMetadata } from '../../../app/actions/get-feature-metadata.js';
10
10
  import { useAgentEventsContext } from '../../../hooks/agent-events-provider.js';
11
11
  import { useSoundAction } from '../../../hooks/use-sound-action.js';
12
12
  import { mapEventTypeToState, mapPhaseNameToLifecycle, } from '../../common/feature-node/derive-feature-state.js';
13
+ import { NotificationEventType } from '../../../../../../packages/core/src/domain/generated/output.js';
14
+ import { useGraphState } from '../../../hooks/use-graph-state.js';
13
15
  let nextFeatureId = 0;
14
16
  export function useControlCenterState(initialNodes, initialEdges) {
15
17
  const router = useRouter();
16
- const [nodes, setNodes] = useState(initialNodes);
17
- // eslint-disable-next-line react/hook-use-state -- raw setter renamed; public setEdges wrapper keeps edgesRef in sync
18
- const [edges, setEdgesRaw] = useState(initialEdges);
19
- const edgesRef = useRef(initialEdges);
20
- // Wrapper that keeps edgesRef in sync with edges state, allowing
21
- // createFeatureNode to read current edges without a closure dependency.
22
- const setEdges = useCallback((update) => {
23
- setEdgesRaw((prev) => {
24
- const next = typeof update === 'function' ? update(prev) : update;
25
- edgesRef.current = next;
26
- return next;
27
- });
28
- }, []);
29
18
  const deleteSound = useSoundAction('delete');
30
19
  const createSound = useSoundAction('create');
31
- // Sync server props into local state when parent re-renders with new data
20
+ const { nodes, edges, reconcile, updateFeature, addPendingFeature, removeFeature, restoreFeature, addRepository: addRepositoryToMap, removeRepository, replaceRepository, getFeatureRepositoryPath, getRepositoryData, setCallbacks, } = useGraphState(initialNodes, initialEdges);
21
+ // Sync server props into domain Maps when initialNodes/initialEdges change.
22
+ // Keyed by a stable string so we don't re-reconcile on every render.
32
23
  const initialNodeKey = initialNodes
33
24
  .map((n) => n.id)
34
25
  .sort()
35
26
  .join(',');
36
- const initialEdgeKey = initialEdges
37
- .map((e) => e.id)
38
- .sort()
39
- .join(',');
40
- // Track previous feature states so we can detect transitions on server refresh
41
- const prevFeatureStatesRef = useRef(new Map());
42
- // Stable key that changes when feature DATA changes (state/lifecycle), not just IDs.
43
27
  const initialDataKey = initialNodes
44
28
  .filter((n) => n.type === 'featureNode')
45
29
  .map((n) => {
@@ -48,52 +32,24 @@ export function useControlCenterState(initialNodes, initialEdges) {
48
32
  })
49
33
  .sort()
50
34
  .join(',');
51
- // Sync server props into local state — keyed by derived strings (initialNodeKey/initialEdgeKey)
52
- // to avoid infinite re-renders from unstable array references.
53
- useEffect(() => {
54
- setNodes((currentNodes) => {
55
- // Build a lookup of current node positions by ID
56
- const currentById = new Map(currentNodes.map((n) => [n.id, n]));
57
- // Identify optimistic "creating" nodes (they have temp IDs not in server data)
58
- const serverIds = new Set(initialNodes.map((n) => n.id));
59
- const creatingNodes = currentNodes.filter((n) => n.type === 'featureNode' &&
60
- n.data.state === 'creating' &&
61
- !serverIds.has(n.id));
62
- // Merge server nodes with client positions, tracking if relayout is needed
63
- let needsRelayout = false;
64
- const merged = initialNodes.map((serverNode) => {
65
- // Node already exists on canvas — keep its position, update data
66
- const existing = currentById.get(serverNode.id);
67
- if (existing) {
68
- return { ...serverNode, position: existing.position };
69
- }
70
- // New server node — inherit position from an optimistic creating node if available
71
- if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
72
- const donor = creatingNodes.shift();
73
- return { ...serverNode, position: donor.position };
74
- }
75
- // Truly new node with no optimistic counterpart — needs relayout
76
- needsRelayout = true;
77
- return serverNode;
78
- });
79
- // Also check if non-creating nodes were removed (graph got smaller)
80
- if (!needsRelayout) {
81
- needsRelayout = currentNodes.some((cn) => !serverIds.has(cn.id) &&
82
- !(cn.type === 'featureNode' && cn.data.state === 'creating'));
83
- }
84
- if (needsRelayout) {
85
- const layoutResult = layoutWithDagre(merged, initialEdges, CANVAS_LAYOUT_DEFAULTS);
86
- setEdges(layoutResult.edges);
87
- return layoutResult.nodes;
88
- }
89
- return merged;
90
- });
91
- }, [initialNodeKey, initialNodes, initialEdges, setEdges]);
35
+ const prevReconcileKey = useRef('');
92
36
  useEffect(() => {
93
- setEdges(initialEdges);
94
- }, [initialEdgeKey, initialEdges, setEdges]);
95
- // Fallback notifications from server-prop state transitions.
96
- // Fires only when SSE didn't already deliver the matching event for a feature.
37
+ const key = `${initialNodeKey}|${initialDataKey}`;
38
+ if (key !== prevReconcileKey.current) {
39
+ prevReconcileKey.current = key;
40
+ reconcile(initialNodes, initialEdges);
41
+ }
42
+ }, [initialNodeKey, initialDataKey, initialNodes, initialEdges, reconcile]);
43
+ // Track previous feature states for fallback notifications
44
+ const prevFeatureStatesRef = useRef(new Map());
45
+ const initialFeatureDataKey = initialNodes
46
+ .filter((n) => n.type === 'featureNode')
47
+ .map((n) => {
48
+ const d = n.data;
49
+ return `${n.id}:${d.state}`;
50
+ })
51
+ .sort()
52
+ .join(',');
97
53
  const { events } = useAgentEventsContext();
98
54
  useEffect(() => {
99
55
  const prevStates = prevFeatureStatesRef.current;
@@ -103,7 +59,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
103
59
  const data = node.data;
104
60
  const prev = prevStates.get(node.id);
105
61
  if (prev !== undefined && prev !== data.state) {
106
- // Check if SSE already delivered a matching event for this feature
107
62
  const sseAlreadyCovered = events.some((e) => e.featureId === data.featureId && mapEventTypeToState(e.eventType) === data.state);
108
63
  if (!sseAlreadyCovered) {
109
64
  if (data.state === 'done') {
@@ -114,9 +69,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
114
69
  description: 'Waiting for your approval',
115
70
  action: {
116
71
  label: 'Review',
117
- onClick: () => {
118
- router.push(`/feature/${data.featureId}`);
119
- },
72
+ onClick: () => router.push(`/feature/${data.featureId}`),
120
73
  },
121
74
  });
122
75
  }
@@ -127,179 +80,139 @@ export function useControlCenterState(initialNodes, initialEdges) {
127
80
  }
128
81
  prevStates.set(node.id, data.state);
129
82
  }
130
- }, [initialDataKey, initialNodes, events, router]);
131
- // Targeted optimistic updates from SSE agent events.
132
- // Uses `events` array (not `lastEvent`) so that React batching cannot silently
133
- // drop events when multiple SSE messages arrive in the same tick.
83
+ }, [initialFeatureDataKey, initialNodes, events, router]);
84
+ // SSE effect: only state + lifecycle updates
134
85
  const processedEventCountRef = useRef(0);
135
86
  useEffect(() => {
136
87
  if (events.length <= processedEventCountRef.current)
137
88
  return;
138
89
  const newEvents = events.slice(processedEventCountRef.current);
139
90
  processedEventCountRef.current = events.length;
91
+ // PhaseCompleted must NOT set state when WaitingApproval is in the same poll batch —
92
+ // they arrive together and PhaseCompleted → 'running' would revert 'action-required'.
93
+ // But when PhaseCompleted arrives WITHOUT WaitingApproval (e.g. after approval), we
94
+ // must allow it to update state as a fallback for any missed AgentStarted event.
95
+ const waitingApprovalFeatures = new Set(newEvents
96
+ .filter((e) => e.eventType === NotificationEventType.WaitingApproval)
97
+ .map((e) => e.featureId));
140
98
  for (const event of newEvents) {
141
- const newState = mapEventTypeToState(event.eventType);
99
+ const newState = event.eventType === NotificationEventType.PhaseCompleted &&
100
+ waitingApprovalFeatures.has(event.featureId)
101
+ ? undefined
102
+ : mapEventTypeToState(event.eventType);
142
103
  const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
143
- // Targeted node update — only clone the matched node
144
- setNodes((prev) => prev.map((node) => {
145
- if (node.type !== 'featureNode')
146
- return node;
147
- const data = node.data;
148
- if (data.featureId !== event.featureId)
149
- return node;
150
- return {
151
- ...node,
152
- data: {
153
- ...data,
154
- state: newState,
155
- ...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
156
- },
157
- };
158
- }));
159
- }
160
- }, [events]);
161
- const onNodesChange = useCallback((changes) => {
162
- setNodes((ns) => applyNodeChanges(changes, ns));
163
- }, []);
164
- const handleConnect = useCallback((connection) => {
165
- if (!connection.source || !connection.target)
166
- return;
167
- setNodes((currentNodes) => {
168
- const sourceNode = currentNodes.find((n) => n.id === connection.source);
169
- if (sourceNode?.type !== 'repositoryNode')
170
- return currentNodes;
171
- // Block if target feature already has a repo source
172
- setEdges((currentEdges) => {
173
- const targetAlreadyHasRepo = currentEdges.some((e) => {
174
- const edgeSource = currentNodes.find((n) => n.id === e.source);
175
- return edgeSource?.type === 'repositoryNode' && e.target === connection.target;
104
+ const nodeId = `feat-${event.featureId}`;
105
+ if (newState !== undefined || newLifecycle !== undefined) {
106
+ updateFeature(nodeId, {
107
+ ...(newState !== undefined && { state: newState }),
108
+ ...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
176
109
  });
177
- if (targetAlreadyHasRepo)
178
- return currentEdges;
179
- return [
180
- ...currentEdges,
181
- {
182
- id: `edge-${connection.source}-${connection.target}`,
183
- source: connection.source,
184
- target: connection.target,
185
- style: { strokeDasharray: '5 5' },
186
- },
187
- ];
110
+ }
111
+ }
112
+ }, [events, updateFeature]);
113
+ // Separate effect: fetch metadata (name + description) when SSE reports it changed
114
+ const metadataFetchedRef = useRef(new Set());
115
+ useEffect(() => {
116
+ for (const event of events) {
117
+ if (event.message !== 'Feature metadata updated')
118
+ continue;
119
+ if (metadataFetchedRef.current.has(event.featureId))
120
+ continue;
121
+ metadataFetchedRef.current.add(event.featureId);
122
+ const nodeId = `feat-${event.featureId}`;
123
+ getFeatureMetadata(event.featureId)
124
+ .then((meta) => {
125
+ if (meta) {
126
+ updateFeature(nodeId, { name: meta.name, description: meta.description });
127
+ }
128
+ })
129
+ .catch(() => {
130
+ // Silent: metadata fetch failure is non-critical
188
131
  });
189
- return currentNodes;
190
- });
191
- }, [setEdges]);
132
+ }
133
+ }, [events, updateFeature]);
134
+ // onNodesChange is a no-op: nodes are derived from domain Maps.
135
+ // Since nodesDraggable=false and elementsSelectable=false, only React Flow's
136
+ // internal replace/dimensions changes come through — we ignore them all.
137
+ const onNodesChange = useCallback((_changes) => {
138
+ // Intentional no-op: domain Maps are the source of truth.
139
+ }, []);
140
+ const handleConnect = useCallback((_connection) => {
141
+ // Connections are managed via domain operations, not direct edge manipulation.
142
+ }, []);
192
143
  const createFeatureNode = useCallback((sourceNodeId, dataOverride, edgeType) => {
193
- const id = `feature-${Date.now()}-${nextFeatureId++}`;
144
+ // Use real feature ID when available (from server), otherwise temp ID
145
+ const id = dataOverride?.featureId
146
+ ? `feat-${dataOverride.featureId}`
147
+ : `feature-${Date.now()}-${nextFeatureId++}`;
194
148
  const newFeatureData = {
195
149
  name: dataOverride?.name ?? 'New Feature',
196
150
  description: dataOverride?.description ?? 'Describe what this feature does',
197
- featureId: `#${id.slice(-4)}`,
151
+ featureId: dataOverride?.featureId ?? `#${id.slice(-4)}`,
198
152
  lifecycle: 'requirements',
199
153
  state: dataOverride?.state ?? 'running',
200
154
  progress: 0,
201
155
  repositoryPath: dataOverride?.repositoryPath ?? '',
202
156
  branch: dataOverride?.branch ?? '',
203
157
  };
204
- setNodes((currentNodes) => {
205
- const newNode = {
206
- id,
207
- type: 'featureNode',
208
- position: { x: 0, y: 0 },
209
- data: newFeatureData,
210
- };
211
- const newEdge = sourceNodeId
212
- ? {
213
- id: edgeType === 'dependencyEdge'
214
- ? `dep-${sourceNodeId}-${id}`
215
- : `edge-${sourceNodeId}-${id}`,
216
- source: sourceNodeId,
217
- target: id,
218
- ...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
219
- }
220
- : null;
221
- const allEdges = newEdge
222
- ? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
223
- : edgesRef.current;
224
- // Insert the new node after the last sibling connected to the same source,
225
- // so dagre's input-order preservation keeps repo groups together.
226
- let insertIndex = currentNodes.length;
227
- if (sourceNodeId) {
228
- const siblingIds = new Set(allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target));
229
- for (let i = currentNodes.length - 1; i >= 0; i--) {
230
- if (siblingIds.has(currentNodes[i].id)) {
231
- insertIndex = i + 1;
232
- break;
233
- }
234
- }
235
- }
236
- const allNodes = [
237
- ...currentNodes.slice(0, insertIndex),
238
- newNode,
239
- ...currentNodes.slice(insertIndex),
240
- ];
241
- // Run dagre layout on the full graph so the new node is positioned consistently
242
- const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
243
- setEdges(layoutResult.edges);
244
- return layoutResult.nodes;
245
- });
158
+ const parentNodeId = edgeType === 'dependencyEdge' && sourceNodeId ? sourceNodeId : undefined;
159
+ addPendingFeature(id, newFeatureData, parentNodeId);
246
160
  return id;
247
- }, [setEdges]);
161
+ }, [addPendingFeature]);
248
162
  const handleDeleteFeature = useCallback((featureId) => {
249
163
  const nodeId = `feat-${featureId}`;
250
- // Snapshot current state for rollback
251
- const prevNodes = nodes;
252
- const prevEdges = edgesRef.current;
253
- // Optimistic removal — update UI immediately
254
- setNodes((currentNodes) => {
255
- const remainingNodes = currentNodes.filter((n) => n.id !== nodeId);
256
- const remainingEdges = edgesRef.current.filter((e) => e.source !== nodeId && e.target !== nodeId);
257
- const layoutResult = layoutWithDagre(remainingNodes, remainingEdges, CANVAS_LAYOUT_DEFAULTS);
258
- setEdges(layoutResult.edges);
259
- return layoutResult.nodes;
260
- });
164
+ // Find the current entry for rollback
165
+ const prevEntry = nodes
166
+ .filter((n) => n.id === nodeId)
167
+ .map((n) => ({
168
+ nodeId: n.id,
169
+ data: n.data,
170
+ }))[0];
171
+ // Optimistic removal
172
+ removeFeature(nodeId);
261
173
  deleteSound.play();
262
174
  toast.success('Feature deleted successfully');
263
175
  router.push('/');
264
- // Persist in background — rollback on failure
265
176
  deleteFeature(featureId)
266
177
  .then((result) => {
267
178
  if (result.error) {
268
- setNodes(prevNodes);
269
- setEdges(prevEdges);
179
+ if (prevEntry)
180
+ restoreFeature(nodeId, prevEntry);
270
181
  toast.error(result.error);
271
182
  }
272
183
  })
273
184
  .catch(() => {
274
- setNodes(prevNodes);
275
- setEdges(prevEdges);
185
+ if (prevEntry)
186
+ restoreFeature(nodeId, prevEntry);
276
187
  toast.error('Failed to delete feature');
277
188
  });
278
- }, [nodes, router, deleteSound, setEdges]);
189
+ }, [nodes, router, deleteSound, removeFeature, restoreFeature]);
279
190
  const handleDeleteRepository = useCallback(async (repositoryId) => {
280
191
  const repoNodeId = `repo-${repositoryId}`;
192
+ // Find children of this repo via edges
193
+ const childFeatureIds = new Set(edges.filter((e) => e.source === repoNodeId).map((e) => e.target));
281
194
  // Snapshot for rollback
282
- const prevNodes = nodes;
283
- const prevEdges = edgesRef.current;
284
- // Optimistic: remove repo node, its child feature nodes, and all related edges
285
- setNodes((currentNodes) => {
286
- // Find feature node IDs connected to this repo via edges
287
- const childFeatureIds = new Set(edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target));
288
- const remainingNodes = currentNodes.filter((n) => n.id !== repoNodeId && !childFeatureIds.has(n.id));
289
- const remainingEdges = edgesRef.current.filter((e) => e.source !== repoNodeId &&
290
- e.target !== repoNodeId &&
291
- !childFeatureIds.has(e.source) &&
292
- !childFeatureIds.has(e.target));
293
- const layoutResult = layoutWithDagre(remainingNodes, remainingEdges, CANVAS_LAYOUT_DEFAULTS);
294
- setEdges(layoutResult.edges);
295
- return layoutResult.nodes;
296
- });
195
+ const prevRepoData = getRepositoryData(repoNodeId);
196
+ // Optimistic: remove repo + children
197
+ removeRepository(repoNodeId);
198
+ for (const childId of childFeatureIds) {
199
+ removeFeature(childId);
200
+ }
297
201
  try {
298
202
  const result = await deleteRepository(repositoryId);
299
203
  if (!result.success) {
300
204
  toast.error(result.error ?? 'Failed to remove repository');
301
- setNodes(prevNodes);
302
- setEdges(prevEdges);
205
+ if (prevRepoData)
206
+ addRepositoryToMap(repoNodeId, prevRepoData);
207
+ for (const childId of childFeatureIds) {
208
+ const childNode = nodes.find((n) => n.id === childId);
209
+ if (childNode) {
210
+ restoreFeature(childId, {
211
+ nodeId: childId,
212
+ data: childNode.data,
213
+ });
214
+ }
215
+ }
303
216
  return;
304
217
  }
305
218
  deleteSound.play();
@@ -307,82 +220,62 @@ export function useControlCenterState(initialNodes, initialEdges) {
307
220
  }
308
221
  catch {
309
222
  toast.error('Failed to remove repository');
310
- setNodes(prevNodes);
311
- setEdges(prevEdges);
223
+ if (prevRepoData)
224
+ addRepositoryToMap(repoNodeId, prevRepoData);
225
+ for (const childId of childFeatureIds) {
226
+ const childNode = nodes.find((n) => n.id === childId);
227
+ if (childNode) {
228
+ restoreFeature(childId, {
229
+ nodeId: childId,
230
+ data: childNode.data,
231
+ });
232
+ }
233
+ }
312
234
  }
313
- }, [nodes, deleteSound, setEdges]);
235
+ }, [
236
+ edges,
237
+ nodes,
238
+ deleteSound,
239
+ removeRepository,
240
+ removeFeature,
241
+ addRepositoryToMap,
242
+ restoreFeature,
243
+ getRepositoryData,
244
+ ]);
314
245
  const handleLayout = useCallback((direction) => {
315
- setNodes((currentNodes) => {
316
- const result = layoutWithDagre(currentNodes, edgesRef.current, {
317
- ...CANVAS_LAYOUT_DEFAULTS,
318
- direction,
319
- });
320
- setEdges(result.edges);
321
- return result.nodes;
322
- });
323
- }, [setEdges]);
246
+ // Layout is applied via reconcile on next server prop update.
247
+ // For immediate re-layout, we apply dagre and trigger a reconcile-like update.
248
+ const result = layoutWithDagre(nodes, edges, { ...CANVAS_LAYOUT_DEFAULTS, direction });
249
+ reconcile(result.nodes, result.edges);
250
+ }, [nodes, edges, reconcile]);
324
251
  const handleAddRepository = useCallback((path) => {
325
252
  const tempId = `repo-temp-${Date.now()}`;
326
253
  const repoName = path
327
254
  .replace(/[\\/]+$/, '')
328
255
  .split(/[\\/]/)
329
256
  .pop() ?? path;
330
- // Optimistic UI: add node and re-layout immediately
331
- setNodes((currentNodes) => {
332
- const newNode = {
333
- id: tempId,
334
- type: 'repositoryNode',
335
- position: { x: 0, y: 0 },
336
- data: { name: repoName, repositoryPath: path, id: tempId },
337
- };
338
- const allNodes = [...currentNodes, newNode];
339
- const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
340
- setEdges(layoutResult.edges);
341
- return layoutResult.nodes;
342
- });
343
- // Persist via server action
257
+ addRepositoryToMap(tempId, { name: repoName, repositoryPath: path, id: tempId });
344
258
  addRepository({ path, name: repoName })
345
259
  .then((result) => {
346
260
  if (result.error) {
347
- // Rollback optimistic node and re-layout
348
- setNodes((prev) => {
349
- const remaining = prev.filter((n) => n.id !== tempId);
350
- const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
351
- setEdges(layoutResult.edges);
352
- return layoutResult.nodes;
353
- });
261
+ removeRepository(tempId);
354
262
  toast.error(result.error);
355
263
  return;
356
264
  }
357
- // Replace temp ID with real repository ID
358
265
  const repo = result.repository;
359
266
  const realId = `repo-${repo.id}`;
360
- setNodes((prev) => prev.map((n) => n.id === tempId
361
- ? {
362
- ...n,
363
- id: realId,
364
- data: { ...n.data, id: repo.id, repositoryPath: repo.path },
365
- }
366
- : n));
367
- setEdges((prev) => prev.map((e) => ({
368
- ...e,
369
- source: e.source === tempId ? realId : e.source,
370
- target: e.target === tempId ? realId : e.target,
371
- id: e.id.replace(tempId, realId),
372
- })));
267
+ replaceRepository(tempId, realId, {
268
+ name: repo.name,
269
+ repositoryPath: repo.path,
270
+ id: repo.id,
271
+ });
373
272
  createSound.play();
374
273
  })
375
274
  .catch(() => {
376
- // Rollback optimistic node and re-layout
377
- setNodes((prev) => {
378
- const remaining = prev.filter((n) => n.id !== tempId);
379
- const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
380
- setEdges(layoutResult.edges);
381
- return layoutResult.nodes;
382
- });
275
+ removeRepository(tempId);
383
276
  toast.error('Failed to add repository');
384
277
  });
385
- }, [createSound, setEdges]);
278
+ }, [addRepositoryToMap, removeRepository, replaceRepository, createSound]);
386
279
  return {
387
280
  nodes,
388
281
  edges,
@@ -393,5 +286,8 @@ export function useControlCenterState(initialNodes, initialEdges) {
393
286
  handleDeleteFeature,
394
287
  handleDeleteRepository,
395
288
  createFeatureNode,
289
+ getFeatureRepositoryPath,
290
+ getRepositoryData,
291
+ setCallbacks,
396
292
  };
397
293
  }