@shepai/cli 1.72.8 → 1.72.9-pr220.99e5e13

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 (375) 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 -2
  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 -2
  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 +25 -7
  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 +145 -256
  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 +42 -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 +257 -0
  77. package/dist/src/presentation/web/hooks/use-notifications.d.ts +1 -5
  78. package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
  79. package/dist/src/presentation/web/hooks/use-notifications.js +1 -26
  80. package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
  81. package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -38
  82. package/dist/src/presentation/web/lib/derive-graph.d.ts +52 -0
  83. package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -0
  84. package/dist/src/presentation/web/lib/derive-graph.js +126 -0
  85. package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
  86. package/dist/src/presentation/web/lib/layout-with-dagre.js +1 -2
  87. package/dist/tsconfig.build.tsbuildinfo +1 -1
  88. package/package.json +1 -1
  89. package/web/.next/BUILD_ID +1 -1
  90. package/web/.next/build-manifest.json +2 -2
  91. package/web/.next/cache/.previewinfo +1 -1
  92. package/web/.next/cache/.rscinfo +1 -1
  93. package/web/.next/cache/.tsbuildinfo +1 -1
  94. package/web/.next/cache/config.json +3 -3
  95. package/web/.next/fallback-build-manifest.json +2 -2
  96. package/web/.next/prerender-manifest.json +3 -3
  97. package/web/.next/required-server-files.js +1 -1
  98. package/web/.next/required-server-files.json +1 -1
  99. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +43 -28
  100. package/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
  101. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  102. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  103. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +49 -34
  104. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
  105. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  106. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  107. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  108. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  109. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  110. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  111. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +43 -28
  112. package/web/.next/server/app/(dashboard)/create/page.js +1 -1
  113. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  114. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  115. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +49 -34
  116. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
  117. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  118. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  119. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +39 -24
  120. package/web/.next/server/app/(dashboard)/page.js +1 -1
  121. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  122. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  123. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  124. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  125. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  126. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  127. package/web/.next/server/app/_global-error.html +2 -2
  128. package/web/.next/server/app/_global-error.rsc +1 -1
  129. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  130. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  131. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  132. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  133. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  134. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  135. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  136. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  137. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  138. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  139. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  140. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  141. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  142. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  143. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  144. package/web/.next/server/app/version/page.js.nft.json +1 -1
  145. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  146. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  147. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
  148. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  149. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  150. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +3 -0
  151. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js.map +1 -0
  152. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
  153. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js.map +1 -0
  154. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  155. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js.map +1 -1
  156. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  157. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  158. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js.map +1 -1
  159. package/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
  160. package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -0
  161. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  162. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  163. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  164. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js.map +1 -1
  165. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  166. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  167. 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
  168. package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -0
  169. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  170. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  171. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  172. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js.map +1 -1
  173. package/web/.next/server/chunks/ssr/_250a63ae._.js.map +1 -1
  174. package/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  175. package/web/.next/server/chunks/ssr/_61351ed3._.js.map +1 -1
  176. package/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  177. package/web/.next/server/chunks/ssr/_8400c8bb._.js.map +1 -1
  178. package/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  179. package/web/.next/server/chunks/ssr/_9f940437._.js.map +1 -1
  180. package/web/.next/server/chunks/ssr/_9fd79ac5._.js +6 -0
  181. package/web/.next/server/chunks/ssr/_9fd79ac5._.js.map +1 -0
  182. 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
  183. package/web/.next/server/chunks/ssr/{src_presentation_web_5c87e8e4._.js.map → src_presentation_web_2b91e4ec._.js.map} +1 -1
  184. 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
  185. package/web/.next/server/chunks/ssr/{src_presentation_web_398553b0._.js.map → src_presentation_web_5bcc1537._.js.map} +1 -1
  186. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
  187. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js.map +1 -0
  188. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  189. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
  190. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  191. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  192. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  193. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  194. package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
  195. package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js.map +1 -0
  196. package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
  197. package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js.map +1 -0
  198. package/web/.next/server/pages/500.html +2 -2
  199. package/web/.next/server/server-reference-manifest.js +1 -1
  200. package/web/.next/server/server-reference-manifest.json +175 -118
  201. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  202. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  203. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  204. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  205. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +43 -28
  206. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
  207. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  208. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  209. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +49 -34
  210. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
  211. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  212. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  213. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  214. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  215. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  216. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  217. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +43 -28
  218. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js +1 -1
  219. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  220. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  221. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +49 -34
  222. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
  223. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  224. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  225. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +39 -24
  226. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js +1 -1
  227. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  228. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  229. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +39 -24
  230. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  231. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  232. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  233. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  234. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  235. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  236. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  237. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  238. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  239. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  240. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  241. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  242. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  243. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  244. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  245. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  246. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  247. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  248. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  249. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  250. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  251. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  252. package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  253. 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
  254. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +3 -0
  255. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
  256. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  257. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  258. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  259. 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
  260. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  261. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  262. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  263. 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
  264. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  265. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  266. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  267. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  268. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  269. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9fd79ac5._.js +6 -0
  270. 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
  271. 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
  272. 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
  273. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  274. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  275. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  276. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
  277. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
  278. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  279. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  280. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +175 -118
  281. package/web/.next/standalone/src/presentation/web/CLAUDE.md +191 -0
  282. package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +27 -2
  283. package/web/.next/standalone/src/presentation/web/app/actions/get-feature-metadata.ts +18 -0
  284. package/web/.next/standalone/src/presentation/web/app/actions/get-graph-data.ts +12 -0
  285. package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +21 -1
  286. package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts +1 -0
  287. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +23 -26
  288. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +44 -18
  289. package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +2 -0
  290. package/web/.next/standalone/src/presentation/web/components/common/index.ts +1 -6
  291. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +2 -2
  292. package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.tsx +0 -13
  293. package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.tsx +1 -45
  294. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-empty-state.tsx +1 -1
  295. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +36 -29
  296. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +2 -41
  297. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +178 -325
  298. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +0 -48
  299. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +34 -82
  300. package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
  301. package/web/.next/standalone/src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx +2 -14
  302. package/web/.next/standalone/src/presentation/web/components/ui/scroll-area.tsx +1 -1
  303. package/web/.next/standalone/src/presentation/web/hooks/use-graph-state.ts +376 -0
  304. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +2 -30
  305. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +2 -36
  306. package/web/.next/standalone/src/presentation/web/lib/derive-graph.ts +178 -0
  307. package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +1 -2
  308. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  309. package/web/.next/static/chunks/{1c07b21eb8b32f25.js → 0b409f42a82665ec.js} +1 -1
  310. package/web/.next/static/chunks/0e035a073754dde7.js +1 -0
  311. package/web/.next/static/chunks/109d6898972d7b78.js +1 -0
  312. package/web/.next/static/chunks/{fcafb841e3860687.js → 5238c7e71fc81e4d.js} +1 -1
  313. package/web/.next/static/chunks/6b4899963367ea96.js +4 -0
  314. package/web/.next/static/chunks/71c9ecdceac1939f.css +2 -0
  315. package/web/.next/static/chunks/752846e217931a38.js +1 -0
  316. package/web/.next/static/chunks/9f9e6f5083600784.js +1 -0
  317. package/web/.next/static/chunks/{8682dcb376de62de.js → ae00da647a8354b9.js} +1 -1
  318. package/web/.next/static/chunks/cb41849bcfaa304e.js +1 -0
  319. package/web/.next/static/chunks/dc23a5773f5cd610.js +1 -0
  320. package/web/.next/trace +1 -1
  321. package/web/.next/trace-build +1 -1
  322. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-button.d.ts.map +0 -1
  323. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts +0 -7
  324. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts.map +0 -1
  325. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.js +0 -1
  326. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts +0 -6
  327. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts.map +0 -1
  328. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.js +0 -23
  329. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts +0 -10
  330. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts.map +0 -1
  331. package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.js +0 -33
  332. package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts +0 -4
  333. package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts.map +0 -1
  334. package/dist/src/presentation/web/components/common/add-repository-node/pick-folder.d.ts.map +0 -1
  335. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +0 -3
  336. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js.map +0 -1
  337. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +0 -3
  338. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js.map +0 -1
  339. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbae9863._.js.map +0 -1
  340. package/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js.map +0 -1
  341. package/web/.next/server/chunks/ssr/_47ef589d._.js +0 -6
  342. package/web/.next/server/chunks/ssr/_47ef589d._.js.map +0 -1
  343. package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +0 -8
  344. package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js.map +0 -1
  345. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +0 -3
  346. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js.map +0 -1
  347. package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +0 -8
  348. package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js.map +0 -1
  349. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +0 -3
  350. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +0 -3
  351. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_47ef589d._.js +0 -6
  352. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +0 -8
  353. 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
  354. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +0 -8
  355. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node-config.ts +0 -8
  356. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.tsx +0 -70
  357. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.tsx +0 -49
  358. package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/index.ts +0 -3
  359. package/web/.next/static/chunks/3c79bca4d3f18f66.js +0 -1
  360. package/web/.next/static/chunks/5cd24ca47061a21d.js +0 -4
  361. package/web/.next/static/chunks/67f2a7a5e8dc358a.css +0 -2
  362. package/web/.next/static/chunks/71cf72022f08a8c9.js +0 -1
  363. package/web/.next/static/chunks/991d4490d7a338d8.js +0 -1
  364. package/web/.next/static/chunks/c71589df79020ecb.js +0 -1
  365. package/web/.next/static/chunks/ebf172a84c9596ed.js +0 -1
  366. package/web/.next/static/chunks/fabfa54940973cc7.js +0 -1
  367. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.d.ts +0 -0
  368. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.js +0 -0
  369. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.d.ts +0 -0
  370. /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.js +0 -0
  371. /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.tsx +0 -0
  372. /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.ts +0 -0
  373. /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → JYBKfZ0ROsNn817Id2TJ7}/_buildManifest.js +0 -0
  374. /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → JYBKfZ0ROsNn817Id2TJ7}/_clientMiddlewareManifest.json +0 -0
  375. /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → JYBKfZ0ROsNn817Id2TJ7}/_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, 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,12 +15,16 @@ 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 { useAgentEventsContext } from '@/hooks/agent-events-provider';
19
20
  import { useSoundAction } from '@/hooks/use-sound-action';
20
21
  import {
21
22
  mapEventTypeToState,
22
23
  mapPhaseNameToLifecycle,
23
24
  } from '@/components/common/feature-node/derive-feature-state';
25
+ import { NotificationEventType } from '@shepai/core/domain/generated/output';
26
+ import { useGraphState, type GraphCallbacks } from '@/hooks/use-graph-state';
27
+ import type { FeatureEntry } from '@/lib/derive-graph';
24
28
 
25
29
  export interface ControlCenterState {
26
30
  nodes: CanvasNodeType[];
@@ -36,8 +40,17 @@ export interface ControlCenterState {
36
40
  dataOverride?: Partial<FeatureNodeData>,
37
41
  edgeType?: string
38
42
  ) => string;
43
+ /** Stable lookup: repositoryPath for a feature node. */
44
+ getFeatureRepositoryPath: (featureNodeId: string) => string | undefined;
45
+ /** Stable lookup: repository data by nodeId. */
46
+ getRepositoryData: (nodeId: string) => RepositoryNodeData | undefined;
47
+ /** Sync callbacks into derived node data (does not trigger re-render). */
48
+ setCallbacks: (callbacks: GraphCallbacks) => void;
39
49
  }
40
50
 
51
+ /** Must match the message string emitted by the SSE route in agent-events/route.ts */
52
+ const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
53
+
41
54
  let nextFeatureId = 0;
42
55
 
43
56
  export function useControlCenterState(
@@ -45,37 +58,31 @@ export function useControlCenterState(
45
58
  initialEdges: Edge[]
46
59
  ): ControlCenterState {
47
60
  const router = useRouter();
48
- const [nodes, setNodes] = useState<CanvasNodeType[]>(initialNodes);
49
- // eslint-disable-next-line react/hook-use-state -- raw setter renamed; public setEdges wrapper keeps edgesRef in sync
50
- const [edges, setEdgesRaw] = useState<Edge[]>(initialEdges);
51
- const edgesRef = useRef<Edge[]>(initialEdges);
52
-
53
- // Wrapper that keeps edgesRef in sync with edges state, allowing
54
- // createFeatureNode to read current edges without a closure dependency.
55
- const setEdges = useCallback((update: Edge[] | ((prev: Edge[]) => Edge[])) => {
56
- setEdgesRaw((prev) => {
57
- const next = typeof update === 'function' ? update(prev) : update;
58
- edgesRef.current = next;
59
- return next;
60
- });
61
- }, []);
62
61
  const deleteSound = useSoundAction('delete');
63
62
  const createSound = useSoundAction('create');
64
63
 
65
- // Sync server props into local state when parent re-renders with new data
64
+ const {
65
+ nodes,
66
+ edges,
67
+ reconcile,
68
+ updateFeature,
69
+ addPendingFeature,
70
+ removeFeature,
71
+ restoreFeature,
72
+ addRepository: addRepositoryToMap,
73
+ removeRepository,
74
+ replaceRepository,
75
+ getFeatureRepositoryPath,
76
+ getRepositoryData,
77
+ setCallbacks,
78
+ } = useGraphState(initialNodes, initialEdges);
79
+
80
+ // Sync server props into domain Maps when initialNodes/initialEdges change.
81
+ // Keyed by a stable string so we don't re-reconcile on every render.
66
82
  const initialNodeKey = initialNodes
67
83
  .map((n) => n.id)
68
84
  .sort()
69
85
  .join(',');
70
- const initialEdgeKey = initialEdges
71
- .map((e) => e.id)
72
- .sort()
73
- .join(',');
74
-
75
- // Track previous feature states so we can detect transitions on server refresh
76
- const prevFeatureStatesRef = useRef<Map<string, FeatureNodeData['state']>>(new Map());
77
-
78
- // Stable key that changes when feature DATA changes (state/lifecycle), not just IDs.
79
86
  const initialDataKey = initialNodes
80
87
  .filter((n) => n.type === 'featureNode')
81
88
  .map((n) => {
@@ -85,83 +92,31 @@ export function useControlCenterState(
85
92
  .sort()
86
93
  .join(',');
87
94
 
88
- // Sync server props into local state — keyed by derived strings (initialNodeKey/initialEdgeKey)
89
- // to avoid infinite re-renders from unstable array references.
90
- useEffect(() => {
91
- setNodes((currentNodes) => {
92
- // Build a lookup of current node positions by ID
93
- const currentById = new Map(currentNodes.map((n) => [n.id, n]));
94
-
95
- // Identify optimistic "creating" nodes (they have temp IDs not in server data)
96
- const serverIds = new Set(initialNodes.map((n) => n.id));
97
- const creatingNodes = currentNodes.filter(
98
- (n) =>
99
- n.type === 'featureNode' &&
100
- (n.data as FeatureNodeData).state === 'creating' &&
101
- !serverIds.has(n.id)
102
- );
103
-
104
- // Merge server nodes with client positions, tracking if relayout is needed
105
- let needsRelayout = false;
106
- const merged = initialNodes.map((serverNode) => {
107
- // Node already exists on canvas — keep its position, update data
108
- const existing = currentById.get(serverNode.id);
109
- if (existing) {
110
- return { ...serverNode, position: existing.position };
111
- }
112
-
113
- // New server node — inherit position from an optimistic creating node if available
114
- if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
115
- const donor = creatingNodes.shift()!;
116
- return { ...serverNode, position: donor.position };
117
- }
118
-
119
- // Truly new node with no optimistic counterpart — needs relayout
120
- needsRelayout = true;
121
- return serverNode;
122
- });
123
-
124
- // Also check if non-creating nodes were removed (graph got smaller)
125
- if (!needsRelayout) {
126
- needsRelayout = currentNodes.some(
127
- (cn) =>
128
- !serverIds.has(cn.id) &&
129
- !(cn.type === 'featureNode' && (cn.data as FeatureNodeData).state === 'creating')
130
- );
131
- }
132
-
133
- if (needsRelayout) {
134
- const layoutResult = layoutWithDagre(merged, initialEdges, CANVAS_LAYOUT_DEFAULTS);
135
- setEdges(layoutResult.edges);
136
- return layoutResult.nodes;
137
- }
138
-
139
- return merged;
140
- });
141
- }, [initialNodeKey, initialNodes, initialEdges, setEdges]);
95
+ const prevReconcileKey = useRef('');
142
96
 
143
97
  useEffect(() => {
144
- setEdges(initialEdges);
145
- }, [initialEdgeKey, initialEdges, setEdges]);
98
+ const key = `${initialNodeKey}|${initialDataKey}`;
99
+ if (key !== prevReconcileKey.current) {
100
+ prevReconcileKey.current = key;
101
+ reconcile(initialNodes, initialEdges);
102
+ }
103
+ }, [initialNodeKey, initialDataKey, initialNodes, initialEdges, reconcile]);
104
+
105
+ // Track previous feature states for fallback notifications
106
+ const prevFeatureStatesRef = useRef<Map<string, FeatureNodeData['state']>>(new Map());
146
107
 
147
- // Fallback notifications from server-prop state transitions.
148
- // Fires only when SSE didn't already deliver the matching event for a feature.
149
108
  const { events } = useAgentEventsContext();
150
109
 
151
110
  useEffect(() => {
152
111
  const prevStates = prevFeatureStatesRef.current;
153
-
154
112
  for (const node of initialNodes) {
155
113
  if (node.type !== 'featureNode') continue;
156
114
  const data = node.data as FeatureNodeData;
157
115
  const prev = prevStates.get(node.id);
158
-
159
116
  if (prev !== undefined && prev !== data.state) {
160
- // Check if SSE already delivered a matching event for this feature
161
117
  const sseAlreadyCovered = events.some(
162
118
  (e) => e.featureId === data.featureId && mapEventTypeToState(e.eventType) === data.state
163
119
  );
164
-
165
120
  if (!sseAlreadyCovered) {
166
121
  if (data.state === 'done') {
167
122
  toast.success(data.name, { description: 'Feature completed!' });
@@ -170,9 +125,7 @@ export function useControlCenterState(
170
125
  description: 'Waiting for your approval',
171
126
  action: {
172
127
  label: 'Review',
173
- onClick: () => {
174
- router.push(`/feature/${data.featureId}`);
175
- },
128
+ onClick: () => router.push(`/feature/${data.featureId}`),
176
129
  },
177
130
  });
178
131
  } else if (data.state === 'error') {
@@ -180,81 +133,83 @@ export function useControlCenterState(
180
133
  }
181
134
  }
182
135
  }
183
-
184
136
  prevStates.set(node.id, data.state);
185
137
  }
186
138
  }, [initialDataKey, initialNodes, events, router]);
187
139
 
188
- // Targeted optimistic updates from SSE agent events.
189
- // Uses `events` array (not `lastEvent`) so that React batching cannot silently
190
- // drop events when multiple SSE messages arrive in the same tick.
140
+ // SSE effect: only state + lifecycle updates
191
141
  const processedEventCountRef = useRef(0);
192
142
 
193
143
  useEffect(() => {
194
144
  if (events.length <= processedEventCountRef.current) return;
195
-
196
145
  const newEvents = events.slice(processedEventCountRef.current);
197
146
  processedEventCountRef.current = events.length;
198
147
 
148
+ // PhaseCompleted must NOT set state when WaitingApproval is in the same poll batch —
149
+ // they arrive together and PhaseCompleted → 'running' would revert 'action-required'.
150
+ // But when PhaseCompleted arrives WITHOUT WaitingApproval (e.g. after approval), we
151
+ // must allow it to update state as a fallback for any missed AgentStarted event.
152
+ const waitingApprovalFeatures = new Set(
153
+ newEvents
154
+ .filter((e) => e.eventType === NotificationEventType.WaitingApproval)
155
+ .map((e) => e.featureId)
156
+ );
157
+
199
158
  for (const event of newEvents) {
200
- const newState = mapEventTypeToState(event.eventType);
159
+ const newState =
160
+ event.eventType === NotificationEventType.PhaseCompleted &&
161
+ waitingApprovalFeatures.has(event.featureId)
162
+ ? undefined
163
+ : mapEventTypeToState(event.eventType);
201
164
  const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
202
165
 
203
- // Targeted node update — only clone the matched node
204
- setNodes((prev) =>
205
- prev.map((node) => {
206
- if (node.type !== 'featureNode') return node;
207
- const data = node.data as FeatureNodeData;
208
- if (data.featureId !== event.featureId) return node;
209
- return {
210
- ...node,
211
- data: {
212
- ...data,
213
- state: newState,
214
- ...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
215
- },
216
- };
217
- })
218
- );
166
+ const nodeId = `feat-${event.featureId}`;
167
+ if (newState !== undefined || newLifecycle !== undefined) {
168
+ updateFeature(nodeId, {
169
+ ...(newState !== undefined && { state: newState }),
170
+ ...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
171
+ });
172
+ }
219
173
  }
220
- }, [events]);
174
+ }, [events, updateFeature]);
221
175
 
222
- const onNodesChange = useCallback((changes: NodeChange<CanvasNodeType>[]) => {
223
- setNodes((ns) => applyNodeChanges(changes, ns));
224
- }, []);
225
-
226
- const handleConnect = useCallback(
227
- (connection: Connection) => {
228
- if (!connection.source || !connection.target) return;
176
+ // Separate effect: fetch metadata (name + description) when SSE reports it changed
177
+ const metadataFetchedRef = useRef<Set<string>>(new Set());
178
+ const processedMetadataCountRef = useRef(0);
229
179
 
230
- setNodes((currentNodes) => {
231
- const sourceNode = currentNodes.find((n) => n.id === connection.source);
232
- if (sourceNode?.type !== 'repositoryNode') return currentNodes;
180
+ useEffect(() => {
181
+ if (events.length <= processedMetadataCountRef.current) return;
182
+ const newEvents = events.slice(processedMetadataCountRef.current);
183
+ processedMetadataCountRef.current = events.length;
233
184
 
234
- // Block if target feature already has a repo source
235
- setEdges((currentEdges) => {
236
- const targetAlreadyHasRepo = currentEdges.some((e) => {
237
- const edgeSource = currentNodes.find((n) => n.id === e.source);
238
- return edgeSource?.type === 'repositoryNode' && e.target === connection.target;
239
- });
240
- if (targetAlreadyHasRepo) return currentEdges;
241
-
242
- return [
243
- ...currentEdges,
244
- {
245
- id: `edge-${connection.source}-${connection.target}`,
246
- source: connection.source,
247
- target: connection.target,
248
- style: { strokeDasharray: '5 5' },
249
- },
250
- ];
185
+ for (const event of newEvents) {
186
+ if (event.message !== METADATA_UPDATED_MESSAGE) continue;
187
+ if (metadataFetchedRef.current.has(event.featureId)) continue;
188
+ metadataFetchedRef.current.add(event.featureId);
189
+
190
+ const nodeId = `feat-${event.featureId}`;
191
+ getFeatureMetadata(event.featureId)
192
+ .then((meta) => {
193
+ if (meta) {
194
+ updateFeature(nodeId, { name: meta.name, description: meta.description });
195
+ }
196
+ })
197
+ .catch(() => {
198
+ // Silent: metadata fetch failure is non-critical
251
199
  });
200
+ }
201
+ }, [events, updateFeature]);
252
202
 
253
- return currentNodes;
254
- });
255
- },
256
- [setEdges]
257
- );
203
+ // onNodesChange is a no-op: nodes are derived from domain Maps.
204
+ // Since nodesDraggable=false and elementsSelectable=false, only React Flow's
205
+ // internal replace/dimensions changes come through — we ignore them all.
206
+ const onNodesChange = useCallback((_changes: NodeChange<CanvasNodeType>[]) => {
207
+ // Intentional no-op: domain Maps are the source of truth.
208
+ }, []);
209
+
210
+ const handleConnect = useCallback((_connection: Connection) => {
211
+ // Connections are managed via domain operations, not direct edge manipulation.
212
+ }, []);
258
213
 
259
214
  const createFeatureNode = useCallback(
260
215
  (
@@ -262,11 +217,14 @@ export function useControlCenterState(
262
217
  dataOverride?: Partial<FeatureNodeData>,
263
218
  edgeType?: string
264
219
  ): string => {
265
- const id = `feature-${Date.now()}-${nextFeatureId++}`;
220
+ // Use real feature ID when available (from server), otherwise temp ID
221
+ const id = dataOverride?.featureId
222
+ ? `feat-${dataOverride.featureId}`
223
+ : `feature-${Date.now()}-${nextFeatureId++}`;
266
224
  const newFeatureData: FeatureNodeData = {
267
225
  name: dataOverride?.name ?? 'New Feature',
268
226
  description: dataOverride?.description ?? 'Describe what this feature does',
269
- featureId: `#${id.slice(-4)}`,
227
+ featureId: dataOverride?.featureId ?? `#${id.slice(-4)}`,
270
228
  lifecycle: 'requirements',
271
229
  state: dataOverride?.state ?? 'running',
272
230
  progress: 0,
@@ -274,171 +232,114 @@ export function useControlCenterState(
274
232
  branch: dataOverride?.branch ?? '',
275
233
  };
276
234
 
277
- setNodes((currentNodes) => {
278
- const newNode = {
279
- id,
280
- type: 'featureNode' as const,
281
- position: { x: 0, y: 0 },
282
- data: newFeatureData,
283
- } as CanvasNodeType;
284
-
285
- const newEdge = sourceNodeId
286
- ? {
287
- id:
288
- edgeType === 'dependencyEdge'
289
- ? `dep-${sourceNodeId}-${id}`
290
- : `edge-${sourceNodeId}-${id}`,
291
- source: sourceNodeId,
292
- target: id,
293
- ...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
294
- }
295
- : null;
296
-
297
- const allEdges = newEdge
298
- ? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
299
- : edgesRef.current;
300
-
301
- // Insert the new node after the last sibling connected to the same source,
302
- // so dagre's input-order preservation keeps repo groups together.
303
- let insertIndex = currentNodes.length;
304
- if (sourceNodeId) {
305
- const siblingIds = new Set(
306
- allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target)
307
- );
308
- for (let i = currentNodes.length - 1; i >= 0; i--) {
309
- if (siblingIds.has(currentNodes[i].id)) {
310
- insertIndex = i + 1;
311
- break;
312
- }
313
- }
314
- }
315
- const allNodes = [
316
- ...currentNodes.slice(0, insertIndex),
317
- newNode,
318
- ...currentNodes.slice(insertIndex),
319
- ];
320
-
321
- // Run dagre layout on the full graph so the new node is positioned consistently
322
- const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
323
- setEdges(layoutResult.edges);
324
- return layoutResult.nodes;
325
- });
235
+ const parentNodeId = edgeType === 'dependencyEdge' && sourceNodeId ? sourceNodeId : undefined;
236
+
237
+ addPendingFeature(id, newFeatureData, parentNodeId);
326
238
 
327
239
  return id;
328
240
  },
329
- [setEdges]
241
+ [addPendingFeature]
330
242
  );
331
243
 
332
244
  const handleDeleteFeature = useCallback(
333
245
  (featureId: string) => {
334
246
  const nodeId = `feat-${featureId}`;
335
247
 
336
- // Snapshot current state for rollback
337
- const prevNodes = nodes;
338
- const prevEdges = edgesRef.current;
248
+ // Find the current entry for rollback
249
+ const prevEntry = nodes
250
+ .filter((n) => n.id === nodeId)
251
+ .map((n) => ({
252
+ nodeId: n.id,
253
+ data: n.data as FeatureNodeData,
254
+ }))[0];
339
255
 
340
- // Optimistic removal — update UI immediately
341
- setNodes((currentNodes) => {
342
- const remainingNodes = currentNodes.filter((n) => n.id !== nodeId);
343
- const remainingEdges = edgesRef.current.filter(
344
- (e) => e.source !== nodeId && e.target !== nodeId
345
- );
346
- const layoutResult = layoutWithDagre(
347
- remainingNodes,
348
- remainingEdges,
349
- CANVAS_LAYOUT_DEFAULTS
350
- );
351
- setEdges(layoutResult.edges);
352
- return layoutResult.nodes;
353
- });
256
+ // Optimistic removal
257
+ removeFeature(nodeId);
354
258
  deleteSound.play();
355
259
  toast.success('Feature deleted successfully');
356
260
  router.push('/');
357
261
 
358
- // Persist in background — rollback on failure
359
262
  deleteFeature(featureId)
360
263
  .then((result) => {
361
264
  if (result.error) {
362
- setNodes(prevNodes);
363
- setEdges(prevEdges);
265
+ if (prevEntry) restoreFeature(nodeId, prevEntry);
364
266
  toast.error(result.error);
365
267
  }
366
268
  })
367
269
  .catch(() => {
368
- setNodes(prevNodes);
369
- setEdges(prevEdges);
270
+ if (prevEntry) restoreFeature(nodeId, prevEntry);
370
271
  toast.error('Failed to delete feature');
371
272
  });
372
273
  },
373
- [nodes, router, deleteSound, setEdges]
274
+ [nodes, router, deleteSound, removeFeature, restoreFeature]
374
275
  );
375
276
 
376
277
  const handleDeleteRepository = useCallback(
377
278
  async (repositoryId: string) => {
378
279
  const repoNodeId = `repo-${repositoryId}`;
379
280
 
281
+ // Find children of this repo via edges
282
+ const childFeatureIds = new Set(
283
+ edges.filter((e) => e.source === repoNodeId).map((e) => e.target)
284
+ );
285
+
380
286
  // Snapshot for rollback
381
- const prevNodes = nodes;
382
- const prevEdges = edgesRef.current;
383
-
384
- // Optimistic: remove repo node, its child feature nodes, and all related edges
385
- setNodes((currentNodes) => {
386
- // Find feature node IDs connected to this repo via edges
387
- const childFeatureIds = new Set(
388
- edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target)
389
- );
390
- const remainingNodes = currentNodes.filter(
391
- (n) => n.id !== repoNodeId && !childFeatureIds.has(n.id)
392
- );
393
- const remainingEdges = edgesRef.current.filter(
394
- (e) =>
395
- e.source !== repoNodeId &&
396
- e.target !== repoNodeId &&
397
- !childFeatureIds.has(e.source) &&
398
- !childFeatureIds.has(e.target)
399
- );
400
- const layoutResult = layoutWithDagre(
401
- remainingNodes,
402
- remainingEdges,
403
- CANVAS_LAYOUT_DEFAULTS
404
- );
405
- setEdges(layoutResult.edges);
406
- return layoutResult.nodes;
407
- });
287
+ const prevRepoData = getRepositoryData(repoNodeId);
288
+ const childSnapshots = new Map<string, FeatureEntry>();
289
+ for (const childId of childFeatureIds) {
290
+ const childNode = nodes.find((n) => n.id === childId);
291
+ if (childNode) {
292
+ childSnapshots.set(childId, { nodeId: childId, data: childNode.data as FeatureNodeData });
293
+ }
294
+ }
295
+
296
+ const rollback = () => {
297
+ if (prevRepoData) addRepositoryToMap(repoNodeId, prevRepoData);
298
+ for (const [childId, entry] of childSnapshots) {
299
+ restoreFeature(childId, entry);
300
+ }
301
+ };
302
+
303
+ // Optimistic: remove repo + children
304
+ removeRepository(repoNodeId);
305
+ for (const childId of childFeatureIds) {
306
+ removeFeature(childId);
307
+ }
408
308
 
409
309
  try {
410
310
  const result = await deleteRepository(repositoryId);
411
-
412
311
  if (!result.success) {
413
312
  toast.error(result.error ?? 'Failed to remove repository');
414
- setNodes(prevNodes);
415
- setEdges(prevEdges);
313
+ rollback();
416
314
  return;
417
315
  }
418
-
419
316
  deleteSound.play();
420
317
  toast.success('Repository removed');
421
318
  } catch {
422
319
  toast.error('Failed to remove repository');
423
- setNodes(prevNodes);
424
- setEdges(prevEdges);
320
+ rollback();
425
321
  }
426
322
  },
427
- [nodes, deleteSound, setEdges]
323
+ [
324
+ edges,
325
+ nodes,
326
+ deleteSound,
327
+ removeRepository,
328
+ removeFeature,
329
+ addRepositoryToMap,
330
+ restoreFeature,
331
+ getRepositoryData,
332
+ ]
428
333
  );
429
334
 
430
335
  const handleLayout = useCallback(
431
336
  (direction: LayoutDirection) => {
432
- setNodes((currentNodes) => {
433
- const result = layoutWithDagre(currentNodes, edgesRef.current, {
434
- ...CANVAS_LAYOUT_DEFAULTS,
435
- direction,
436
- });
437
- setEdges(result.edges);
438
- return result.nodes;
439
- });
337
+ // Layout is applied via reconcile on next server prop update.
338
+ // For immediate re-layout, we apply dagre and trigger a reconcile-like update.
339
+ const result = layoutWithDagre(nodes, edges, { ...CANVAS_LAYOUT_DEFAULTS, direction });
340
+ reconcile(result.nodes, result.edges);
440
341
  },
441
- [setEdges]
342
+ [nodes, edges, reconcile]
442
343
  );
443
344
 
444
345
  const handleAddRepository = useCallback(
@@ -450,81 +351,30 @@ export function useControlCenterState(
450
351
  .split(/[\\/]/)
451
352
  .pop() ?? path;
452
353
 
453
- // Optimistic UI: add node and re-layout immediately
454
- setNodes((currentNodes) => {
455
- const newNode = {
456
- id: tempId,
457
- type: 'repositoryNode' as const,
458
- position: { x: 0, y: 0 },
459
- data: { name: repoName, repositoryPath: path, id: tempId },
460
- } as CanvasNodeType;
461
-
462
- const allNodes = [...currentNodes, newNode];
463
- const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
464
- setEdges(layoutResult.edges);
465
- return layoutResult.nodes;
466
- });
467
-
468
- // Persist via server action
354
+ addRepositoryToMap(tempId, { name: repoName, repositoryPath: path, id: tempId });
355
+
469
356
  addRepository({ path, name: repoName })
470
357
  .then((result) => {
471
358
  if (result.error) {
472
- // Rollback optimistic node and re-layout
473
- setNodes((prev) => {
474
- const remaining = prev.filter((n) => n.id !== tempId);
475
- const layoutResult = layoutWithDagre(
476
- remaining,
477
- edgesRef.current,
478
- CANVAS_LAYOUT_DEFAULTS
479
- );
480
- setEdges(layoutResult.edges);
481
- return layoutResult.nodes;
482
- });
359
+ removeRepository(tempId);
483
360
  toast.error(result.error);
484
361
  return;
485
362
  }
486
-
487
- // Replace temp ID with real repository ID
488
363
  const repo = result.repository!;
489
364
  const realId = `repo-${repo.id}`;
490
- setNodes((prev) =>
491
- prev.map((n) =>
492
- n.id === tempId
493
- ? ({
494
- ...n,
495
- id: realId,
496
- data: { ...n.data, id: repo.id, repositoryPath: repo.path },
497
- } as CanvasNodeType)
498
- : n
499
- )
500
- );
501
- setEdges((prev) =>
502
- prev.map((e) => ({
503
- ...e,
504
- source: e.source === tempId ? realId : e.source,
505
- target: e.target === tempId ? realId : e.target,
506
- id: e.id.replace(tempId, realId),
507
- }))
508
- );
509
-
365
+ replaceRepository(tempId, realId, {
366
+ name: repo.name,
367
+ repositoryPath: repo.path,
368
+ id: repo.id,
369
+ });
510
370
  createSound.play();
511
371
  })
512
372
  .catch(() => {
513
- // Rollback optimistic node and re-layout
514
- setNodes((prev) => {
515
- const remaining = prev.filter((n) => n.id !== tempId);
516
- const layoutResult = layoutWithDagre(
517
- remaining,
518
- edgesRef.current,
519
- CANVAS_LAYOUT_DEFAULTS
520
- );
521
- setEdges(layoutResult.edges);
522
- return layoutResult.nodes;
523
- });
373
+ removeRepository(tempId);
524
374
  toast.error('Failed to add repository');
525
375
  });
526
376
  },
527
- [createSound, setEdges]
377
+ [addRepositoryToMap, removeRepository, replaceRepository, createSound]
528
378
  );
529
379
 
530
380
  return {
@@ -537,5 +387,8 @@ export function useControlCenterState(
537
387
  handleDeleteFeature,
538
388
  handleDeleteRepository,
539
389
  createFeatureNode,
390
+ getFeatureRepositoryPath,
391
+ getRepositoryData,
392
+ setCallbacks,
540
393
  };
541
394
  }