@shepai/cli 1.72.9-pr220.f17b60a → 1.73.0

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 (213) hide show
  1. package/dist/src/presentation/web/app/layout.js +1 -1
  2. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  3. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +45 -124
  4. package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.d.ts +13 -0
  5. package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.d.ts.map +1 -0
  6. package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.js +46 -0
  7. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.d.ts.map +1 -1
  8. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.js +7 -10
  9. package/dist/src/presentation/web/components/common/repository-node/use-repository-actions.d.ts.map +1 -1
  10. package/dist/src/presentation/web/components/common/repository-node/use-repository-actions.js +14 -39
  11. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +6 -7
  12. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +24 -8
  14. package/dist/src/presentation/web/hooks/agent-events-provider.d.ts.map +1 -1
  15. package/dist/src/presentation/web/hooks/agent-events-provider.js +3 -2
  16. package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -1
  17. package/dist/src/presentation/web/hooks/use-agent-events.js +12 -2
  18. package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
  19. package/dist/src/presentation/web/hooks/use-deploy-action.js +8 -3
  20. package/dist/src/presentation/web/lib/logger.d.ts +7 -0
  21. package/dist/src/presentation/web/lib/logger.d.ts.map +1 -0
  22. package/dist/src/presentation/web/lib/logger.js +11 -0
  23. package/dist/tsconfig.build.tsbuildinfo +1 -1
  24. package/package.json +1 -1
  25. package/web/.next/BUILD_ID +1 -1
  26. package/web/.next/build-manifest.json +2 -2
  27. package/web/.next/cache/.previewinfo +1 -1
  28. package/web/.next/cache/.rscinfo +1 -1
  29. package/web/.next/cache/.tsbuildinfo +1 -1
  30. package/web/.next/cache/config.json +3 -3
  31. package/web/.next/fallback-build-manifest.json +2 -2
  32. package/web/.next/prerender-manifest.json +3 -3
  33. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +15 -15
  34. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  35. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  36. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +18 -18
  37. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  38. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  39. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +13 -13
  40. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  41. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  42. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +15 -15
  43. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  44. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  45. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +18 -18
  46. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  47. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  48. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +13 -13
  49. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  50. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  51. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +13 -13
  52. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  53. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/_global-error.html +2 -2
  55. package/web/.next/server/app/_global-error.rsc +1 -1
  56. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  57. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  58. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  59. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  60. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  61. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  62. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  64. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  65. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  67. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  68. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  70. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  71. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  72. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  73. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +1 -1
  74. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js.map +1 -1
  75. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +1 -1
  76. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js.map +1 -1
  77. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  78. package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js.map +1 -1
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js.map +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js +1 -1
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  93. package/web/.next/server/chunks/ssr/_0e9d6bf7._.js.map +1 -1
  94. package/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  95. package/web/.next/server/chunks/ssr/_61351ed3._.js.map +1 -1
  96. package/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  97. package/web/.next/server/chunks/ssr/_8400c8bb._.js.map +1 -1
  98. package/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  99. package/web/.next/server/chunks/ssr/_9f940437._.js.map +1 -1
  100. package/web/.next/server/chunks/ssr/{_8e87c87b._.js → _dee726e3._.js} +2 -2
  101. package/web/.next/server/chunks/ssr/{_8e87c87b._.js.map → _dee726e3._.js.map} +1 -1
  102. package/web/.next/server/chunks/ssr/src_presentation_web_2b91e4ec._.js +1 -1
  103. package/web/.next/server/chunks/ssr/src_presentation_web_2b91e4ec._.js.map +1 -1
  104. package/web/.next/server/chunks/ssr/src_presentation_web_5bcc1537._.js +1 -1
  105. package/web/.next/server/chunks/ssr/src_presentation_web_5bcc1537._.js.map +1 -1
  106. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  107. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +1 -1
  109. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js.map +1 -1
  110. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  111. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +2 -2
  113. package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +2 -2
  115. package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js.map +1 -1
  116. package/web/.next/server/pages/500.html +2 -2
  117. package/web/.next/server/server-reference-manifest.js +1 -1
  118. package/web/.next/server/server-reference-manifest.json +21 -21
  119. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  120. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  121. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  122. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +15 -15
  123. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  124. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  125. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +18 -18
  126. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  127. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  128. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +13 -13
  129. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  130. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  131. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +15 -15
  132. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  133. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  134. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +18 -18
  135. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  136. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  137. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +13 -13
  138. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  139. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  140. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +13 -13
  141. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  142. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  143. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  144. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  145. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  146. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  147. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  148. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  149. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  150. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  151. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  152. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  153. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  154. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  155. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  156. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  157. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  158. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  159. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  160. 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
  161. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +1 -1
  162. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +1 -1
  163. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
  164. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
  165. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js +1 -1
  166. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  167. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
  168. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js +1 -1
  170. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
  172. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
  173. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
  174. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
  175. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_8e87c87b._.js → _dee726e3._.js} +2 -2
  176. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_2b91e4ec._.js +1 -1
  177. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5bcc1537._.js +1 -1
  178. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  179. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +1 -1
  180. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  181. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +2 -2
  182. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +2 -2
  183. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  184. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  185. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +21 -21
  186. package/web/.next/standalone/src/presentation/web/app/layout.tsx +1 -1
  187. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +69 -115
  188. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.ts +56 -0
  189. package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/use-feature-actions.ts +6 -7
  190. package/web/.next/standalone/src/presentation/web/components/common/repository-node/use-repository-actions.ts +29 -54
  191. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +7 -7
  192. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +25 -8
  193. package/web/.next/standalone/src/presentation/web/hooks/agent-events-provider.tsx +7 -2
  194. package/web/.next/standalone/src/presentation/web/hooks/use-agent-events.ts +12 -2
  195. package/web/.next/standalone/src/presentation/web/hooks/use-deploy-action.ts +11 -3
  196. package/web/.next/standalone/src/presentation/web/lib/logger.ts +12 -0
  197. package/web/.next/static/chunks/15ceb197ec725da2.js +1 -0
  198. package/web/.next/static/chunks/{792aac476adb5875.js → 6691ad02dc1efd1f.js} +1 -1
  199. package/web/.next/static/chunks/7bff5fbcbf157df5.js +1 -0
  200. package/web/.next/static/chunks/{d5e95c4482029a39.js → 8959112899679e25.js} +1 -1
  201. package/web/.next/static/chunks/{9b1770f05f1d95e5.js → 8f508ef906cbf0fa.js} +1 -1
  202. package/web/.next/static/chunks/{83463b63971f5723.js → 9cbb42a6bd516d3b.js} +1 -1
  203. package/web/.next/static/chunks/c5603ed8a95d460d.js +1 -0
  204. package/web/.next/static/chunks/{a7f52757dff0cea0.js → d7e0cd32364a8d76.js} +1 -1
  205. package/web/.next/static/chunks/{98ca891a5c156aa5.js → e69d79bd3f19dec8.js} +3 -3
  206. package/web/.next/trace +1 -1
  207. package/web/.next/trace-build +1 -1
  208. package/web/.next/static/chunks/76827713cb2b7333.js +0 -1
  209. package/web/.next/static/chunks/7c8adcfd920a8f74.js +0 -1
  210. package/web/.next/static/chunks/b6becf0de992842a.js +0 -1
  211. /package/web/.next/static/{eg0FTlsA4z_7CXbFfMgAr → 1AvkeBASQdtzZt9cm9UYg}/_buildManifest.js +0 -0
  212. /package/web/.next/static/{eg0FTlsA4z_7CXbFfMgAr → 1AvkeBASQdtzZt9cm9UYg}/_clientMiddlewareManifest.json +0 -0
  213. /package/web/.next/static/{eg0FTlsA4z_7CXbFfMgAr → 1AvkeBASQdtzZt9cm9UYg}/_ssgManifest.js +0 -0
@@ -36,5 +36,5 @@ export default function RootLayout({ children, }) {
36
36
  } catch (e) {}
37
37
  })();
38
38
  `,
39
- } }) }), _jsxs("body", { className: "min-h-screen antialiased", children: [_jsx(AppShell, { children: children }), _jsx(Toaster, {})] })] }));
39
+ } }) }), _jsxs("body", { className: "min-h-screen antialiased", children: [_jsx(AppShell, { children: children }), _jsx(Toaster, { position: "bottom-center" })] })] }));
40
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,wBAAwB,2CA6mBlF"}
1
+ {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,wBAAwB,2CA8jBlF"}
@@ -36,6 +36,7 @@ import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/fea
36
36
  import { useFeatureActions } from '../../common/feature-drawer/use-feature-actions.js';
37
37
  import { resolveSseEventUpdates } from '../../common/feature-node/derive-feature-state.js';
38
38
  import { deriveFeatureViewType } from './drawer-view.js';
39
+ import { useArtifactFetch } from './use-artifact-fetch.js';
39
40
  export function FeatureDrawerClient({ view: initialView }) {
40
41
  const router = useRouter();
41
42
  const deleteSound = useSoundAction('delete');
@@ -56,6 +57,10 @@ export function FeatureDrawerClient({ view: initialView }) {
56
57
  const { events } = useAgentEventsContext();
57
58
  const processedCountRef = useRef(0);
58
59
  useEffect(() => {
60
+ // Clamp cursor if events were pruned
61
+ if (processedCountRef.current > events.length) {
62
+ processedCountRef.current = 0;
63
+ }
59
64
  if (!featureNode || events.length <= processedCountRef.current)
60
65
  return;
61
66
  const newEvents = events.slice(processedCountRef.current);
@@ -64,9 +69,9 @@ export function FeatureDrawerClient({ view: initialView }) {
64
69
  if (update.featureId !== featureNode.featureId)
65
70
  continue;
66
71
  // Trigger a server refresh to get the latest drawer view,
67
- // but skip when the user has unsaved changes or a rejection is in-flight
68
- // to avoid the refresh interfering with the reject handler's navigation.
69
- if (!isDirtyRef.current && !isRejectingRef.current)
72
+ // but skip when the drawer is closed, the user has unsaved changes,
73
+ // or a rejection is in-flight to avoid unnecessary refreshes.
74
+ if (isOpenRef.current && !isDirtyRef.current && !isRejectingRef.current)
70
75
  router.refresh();
71
76
  if (update.state !== undefined || update.lifecycle !== undefined) {
72
77
  // Optimistically update the node data AND re-derive the view type so the
@@ -94,6 +99,8 @@ export function FeatureDrawerClient({ view: initialView }) {
94
99
  // the close animation when the path no longer matches a feature route.
95
100
  const pathname = usePathname();
96
101
  const isOpen = pathname.startsWith('/feature/');
102
+ const isOpenRef = useRef(isOpen);
103
+ isOpenRef.current = isOpen;
97
104
  const onClose = useCallback(() => {
98
105
  router.push('/');
99
106
  }, [router]);
@@ -103,16 +110,12 @@ export function FeatureDrawerClient({ view: initialView }) {
103
110
  const [prdData, setPrdData] = useState(null);
104
111
  const [prdSelections, setPrdSelections] = useState({});
105
112
  const [prdDefaultSelections, setPrdDefaultSelections] = useState({});
106
- const [isLoadingPrd, setIsLoadingPrd] = useState(false);
107
113
  // ── Tech state ─────────────────────────────────────────────────────────
108
114
  const [techData, setTechData] = useState(null);
109
- const [isLoadingTech, setIsLoadingTech] = useState(false);
110
115
  // ── Product decisions state (for tech review Product tab) ─────────────
111
116
  const [techProductData, setTechProductData] = useState(undefined);
112
- const [isLoadingTechProduct, setIsLoadingTechProduct] = useState(false);
113
117
  // ── Merge state ────────────────────────────────────────────────────────
114
118
  const [mergeData, setMergeData] = useState(null);
115
- const [isLoadingMerge, setIsLoadingMerge] = useState(false);
116
119
  // ── Delete state ───────────────────────────────────────────────────────
117
120
  const [isDeleting, setIsDeleting] = useState(false);
118
121
  // ── Shared reject state ────────────────────────────────────────────────
@@ -125,130 +128,48 @@ export function FeatureDrawerClient({ view: initialView }) {
125
128
  }, [viewType]);
126
129
  // ── Data fetching ─────────────────────────────────────────────────────
127
130
  const prdFeatureId = view.type === 'prd-review' ? view.node.featureId : null;
128
- useEffect(() => {
131
+ const isLoadingPrd = useArtifactFetch(prdFeatureId, getFeatureArtifact, (result) => {
132
+ if (result.error) {
133
+ toast.error(result.error);
134
+ return;
135
+ }
136
+ if (result.questionnaire) {
137
+ setPrdData(result.questionnaire);
138
+ const defaults = {};
139
+ for (const q of result.questionnaire.questions) {
140
+ const recommended = q.options.find((o) => o.recommended);
141
+ if (recommended)
142
+ defaults[q.id] = recommended.id;
143
+ }
144
+ setPrdSelections(defaults);
145
+ setPrdDefaultSelections(defaults);
146
+ }
147
+ }, () => {
129
148
  setPrdSelections({});
130
149
  setPrdDefaultSelections({});
131
150
  setPrdData(null);
132
- if (!prdFeatureId)
133
- return;
134
- let cancelled = false;
135
- setIsLoadingPrd(true);
136
- getFeatureArtifact(prdFeatureId)
137
- .then((result) => {
138
- if (cancelled)
139
- return;
140
- if (result.error) {
141
- toast.error(result.error);
142
- return;
143
- }
144
- if (result.questionnaire) {
145
- setPrdData(result.questionnaire);
146
- const defaults = {};
147
- for (const q of result.questionnaire.questions) {
148
- const recommended = q.options.find((o) => o.recommended);
149
- if (recommended)
150
- defaults[q.id] = recommended.id;
151
- }
152
- setPrdSelections(defaults);
153
- setPrdDefaultSelections(defaults);
154
- }
155
- })
156
- .catch(() => {
157
- if (!cancelled)
158
- toast.error('Failed to load questionnaire');
159
- })
160
- .finally(() => {
161
- if (!cancelled)
162
- setIsLoadingPrd(false);
163
- });
164
- return () => {
165
- cancelled = true;
166
- };
167
- }, [prdFeatureId]);
151
+ }, 'Failed to load questionnaire');
168
152
  const techFeatureId = view.type === 'tech-review' ? view.node.featureId : null;
169
- useEffect(() => {
170
- setTechData(null);
171
- if (!techFeatureId)
153
+ const isLoadingTech = useArtifactFetch(techFeatureId, getResearchArtifact, (result) => {
154
+ if (result.error) {
155
+ toast.error(result.error);
172
156
  return;
173
- let cancelled = false;
174
- setIsLoadingTech(true);
175
- getResearchArtifact(techFeatureId)
176
- .then((result) => {
177
- if (cancelled)
178
- return;
179
- if (result.error) {
180
- toast.error(result.error);
181
- return;
182
- }
183
- if (result.techDecisions)
184
- setTechData(result.techDecisions);
185
- })
186
- .catch(() => {
187
- if (!cancelled)
188
- toast.error('Failed to load tech decisions');
189
- })
190
- .finally(() => {
191
- if (!cancelled)
192
- setIsLoadingTech(false);
193
- });
194
- return () => {
195
- cancelled = true;
196
- };
197
- }, [techFeatureId]);
198
- useEffect(() => {
199
- setTechProductData(undefined);
200
- if (!techFeatureId)
201
- return;
202
- let cancelled = false;
203
- setIsLoadingTechProduct(true);
204
- getFeatureArtifact(techFeatureId)
205
- .then((result) => {
206
- if (cancelled)
207
- return;
208
- if (result.productDecisions) {
209
- setTechProductData(result.productDecisions);
210
- }
211
- })
212
- .catch(() => {
213
- // Silent failure — the product tab is supplementary
214
- })
215
- .finally(() => {
216
- if (!cancelled)
217
- setIsLoadingTechProduct(false);
218
- });
219
- return () => {
220
- cancelled = true;
221
- };
222
- }, [techFeatureId]);
157
+ }
158
+ if (result.techDecisions)
159
+ setTechData(result.techDecisions);
160
+ }, () => setTechData(null), 'Failed to load tech decisions');
161
+ const isLoadingTechProduct = useArtifactFetch(techFeatureId, getFeatureArtifact, (result) => {
162
+ if (result.productDecisions)
163
+ setTechProductData(result.productDecisions);
164
+ }, () => setTechProductData(undefined));
223
165
  const mergeFeatureId = view.type === 'merge-review' ? view.node.featureId : null;
224
- useEffect(() => {
225
- setMergeData(null);
226
- if (!mergeFeatureId)
166
+ const isLoadingMerge = useArtifactFetch(mergeFeatureId, getMergeReviewData, (result) => {
167
+ if ('error' in result) {
168
+ toast.error(result.error);
227
169
  return;
228
- let cancelled = false;
229
- setIsLoadingMerge(true);
230
- getMergeReviewData(mergeFeatureId)
231
- .then((result) => {
232
- if (cancelled)
233
- return;
234
- if ('error' in result) {
235
- toast.error(result.error);
236
- return;
237
- }
238
- setMergeData(result);
239
- })
240
- .catch(() => {
241
- if (!cancelled)
242
- toast.error('Failed to load merge review data');
243
- })
244
- .finally(() => {
245
- if (!cancelled)
246
- setIsLoadingMerge(false);
247
- });
248
- return () => {
249
- cancelled = true;
250
- };
251
- }, [mergeFeatureId]);
170
+ }
171
+ setMergeData(result);
172
+ }, () => setMergeData(null), 'Failed to load merge review data');
252
173
  // ── Close guard ──────────────────────────────────────────────────────
253
174
  const isChatDirty = chatInput.trim().length > 0;
254
175
  const isPrdDirty = view.type === 'prd-review' &&
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Generic hook for fetching drawer artifact data when a feature ID changes.
3
+ * Handles loading state, cancellation on unmount/re-fetch, and error toasting.
4
+ *
5
+ * @param featureId - The feature ID to fetch for, or null to reset.
6
+ * @param fetcher - Async function that fetches the artifact data.
7
+ * @param onSuccess - Called with the fetch result when successful and not cancelled.
8
+ * @param onReset - Called immediately when featureId changes (before fetching).
9
+ * @param errorMessage - Optional toast message shown on fetch failure.
10
+ * @returns Whether the fetch is currently in progress.
11
+ */
12
+ export declare function useArtifactFetch<TResult>(featureId: string | null, fetcher: (id: string) => Promise<TResult>, onSuccess: (result: TResult) => void, onReset: () => void, errorMessage?: string): boolean;
13
+ //# sourceMappingURL=use-artifact-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-artifact-fetch.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EACtC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EACzC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,EACpC,OAAO,EAAE,MAAM,IAAI,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAiCT"}
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { useState, useEffect, useRef } from 'react';
3
+ import { toast } from 'sonner';
4
+ /**
5
+ * Generic hook for fetching drawer artifact data when a feature ID changes.
6
+ * Handles loading state, cancellation on unmount/re-fetch, and error toasting.
7
+ *
8
+ * @param featureId - The feature ID to fetch for, or null to reset.
9
+ * @param fetcher - Async function that fetches the artifact data.
10
+ * @param onSuccess - Called with the fetch result when successful and not cancelled.
11
+ * @param onReset - Called immediately when featureId changes (before fetching).
12
+ * @param errorMessage - Optional toast message shown on fetch failure.
13
+ * @returns Whether the fetch is currently in progress.
14
+ */
15
+ export function useArtifactFetch(featureId, fetcher, onSuccess, onReset, errorMessage) {
16
+ const [isLoading, setIsLoading] = useState(false);
17
+ // Use refs to avoid stale callbacks in the effect without triggering re-runs.
18
+ const onSuccessRef = useRef(onSuccess);
19
+ const onResetRef = useRef(onReset);
20
+ onSuccessRef.current = onSuccess;
21
+ onResetRef.current = onReset;
22
+ useEffect(() => {
23
+ onResetRef.current();
24
+ if (!featureId)
25
+ return;
26
+ let cancelled = false;
27
+ setIsLoading(true);
28
+ fetcher(featureId)
29
+ .then((result) => {
30
+ if (!cancelled)
31
+ onSuccessRef.current(result);
32
+ })
33
+ .catch(() => {
34
+ if (!cancelled && errorMessage)
35
+ toast.error(errorMessage);
36
+ })
37
+ .finally(() => {
38
+ if (!cancelled)
39
+ setIsLoading(false);
40
+ });
41
+ return () => {
42
+ cancelled = true;
43
+ };
44
+ }, [featureId, fetcher, errorMessage]);
45
+ return isLoading;
46
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"use-feature-actions.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/use-feature-actions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,GAAG,mBAAmB,CA+GxF"}
1
+ {"version":3,"file":"use-feature-actions.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/use-feature-actions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,GAAG,mBAAmB,CA8GxF"}
@@ -14,18 +14,15 @@ export function useFeatureActions(input) {
14
14
  const ideTimerRef = useRef(null);
15
15
  const shellTimerRef = useRef(null);
16
16
  const specsTimerRef = useRef(null);
17
- // Clear timers on unmount
17
+ // Clear timers on unmount — read .current inside cleanup so we get the
18
+ // actual timer value at teardown time, not the always-null value at mount.
18
19
  useEffect(() => {
19
- const ideTimer = ideTimerRef.current;
20
- const shellTimer = shellTimerRef.current;
21
- const specsTimer = specsTimerRef.current;
20
+ const refs = [ideTimerRef, shellTimerRef, specsTimerRef];
22
21
  return () => {
23
- if (ideTimer)
24
- clearTimeout(ideTimer);
25
- if (shellTimer)
26
- clearTimeout(shellTimer);
27
- if (specsTimer)
28
- clearTimeout(specsTimer);
22
+ for (const ref of refs) {
23
+ if (ref.current)
24
+ clearTimeout(ref.current);
25
+ }
29
26
  };
30
27
  }, []);
31
28
  const performAction = useCallback(async (action, setLoading, setError, timerRef, isLoading) => {
@@ -1 +1 @@
1
- {"version":3,"file":"use-repository-actions.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/use-repository-actions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,GAAG,sBAAsB,CAkIjG"}
1
+ {"version":3,"file":"use-repository-actions.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/use-repository-actions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,GAAG,sBAAsB,CAyGjG"}
@@ -16,28 +16,27 @@ export function useRepositoryActions(input) {
16
16
  const folderTimerRef = useRef(null);
17
17
  // Clear timers on unmount
18
18
  useEffect(() => {
19
- const ideTimer = ideTimerRef.current;
20
- const shellTimer = shellTimerRef.current;
21
- const folderTimer = folderTimerRef.current;
19
+ const ideRef = ideTimerRef;
20
+ const shellRef = shellTimerRef;
21
+ const folderRef = folderTimerRef;
22
22
  return () => {
23
- if (ideTimer)
24
- clearTimeout(ideTimer);
25
- if (shellTimer)
26
- clearTimeout(shellTimer);
27
- if (folderTimer)
28
- clearTimeout(folderTimer);
23
+ if (ideRef.current)
24
+ clearTimeout(ideRef.current);
25
+ if (shellRef.current)
26
+ clearTimeout(shellRef.current);
27
+ if (folderRef.current)
28
+ clearTimeout(folderRef.current);
29
29
  };
30
30
  }, []);
31
- const performToolbarAction = useCallback(async (action, setLoading, setError, timerRef, isLoading) => {
31
+ const performAction = useCallback(async (action, setLoading, setError, timerRef, isLoading) => {
32
32
  if (!input || isLoading)
33
33
  return;
34
- // Clear any existing timer
35
34
  if (timerRef.current)
36
35
  clearTimeout(timerRef.current);
37
36
  setLoading(true);
38
37
  setError(null);
39
38
  try {
40
- const result = await action({ repositoryPath: input.repositoryPath });
39
+ const result = await action();
41
40
  if (!result.success) {
42
41
  const errorMessage = result.error ?? 'An unexpected error occurred';
43
42
  setError(errorMessage);
@@ -53,33 +52,9 @@ export function useRepositoryActions(input) {
53
52
  setLoading(false);
54
53
  }
55
54
  }, [input]);
56
- const performFolderAction = useCallback(async (action, setLoading, setError, timerRef, isLoading) => {
57
- if (!input || isLoading)
58
- return;
59
- if (timerRef.current)
60
- clearTimeout(timerRef.current);
61
- setLoading(true);
62
- setError(null);
63
- try {
64
- const result = await action(input.repositoryPath);
65
- if (!result.success) {
66
- const errorMessage = result.error ?? 'An unexpected error occurred';
67
- setError(errorMessage);
68
- timerRef.current = setTimeout(() => setError(null), ERROR_CLEAR_DELAY);
69
- }
70
- }
71
- catch (err) {
72
- const errorMessage = err instanceof Error ? err.message : 'An unexpected error occurred';
73
- setError(errorMessage);
74
- timerRef.current = setTimeout(() => setError(null), ERROR_CLEAR_DELAY);
75
- }
76
- finally {
77
- setLoading(false);
78
- }
79
- }, [input]);
80
- const handleOpenIde = useCallback(() => performToolbarAction(openIde, setIdeLoading, setIdeError, ideTimerRef, ideLoading), [performToolbarAction, ideLoading]);
81
- const handleOpenShell = useCallback(() => performToolbarAction(openShell, setShellLoading, setShellError, shellTimerRef, shellLoading), [performToolbarAction, shellLoading]);
82
- const handleOpenFolder = useCallback(() => performFolderAction(openFolder, setFolderLoading, setFolderError, folderTimerRef, folderLoading), [performFolderAction, folderLoading]);
55
+ const handleOpenIde = useCallback(() => performAction(() => openIde({ repositoryPath: input.repositoryPath }), setIdeLoading, setIdeError, ideTimerRef, ideLoading), [performAction, ideLoading, input]);
56
+ const handleOpenShell = useCallback(() => performAction(() => openShell({ repositoryPath: input.repositoryPath }), setShellLoading, setShellError, shellTimerRef, shellLoading), [performAction, shellLoading, input]);
57
+ const handleOpenFolder = useCallback(() => performAction(() => openFolder(input.repositoryPath), setFolderLoading, setFolderError, folderTimerRef, folderLoading), [performAction, folderLoading, input]);
83
58
  return {
84
59
  openInIde: handleOpenIde,
85
60
  openInShell: handleOpenShell,
@@ -18,19 +18,18 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
18
18
  const { nodes, edges, onNodesChange, handleConnect, handleAddRepository, handleDeleteFeature, handleDeleteRepository, createFeatureNode, setCallbacks, } = useControlCenterState(initialNodes, initialEdges);
19
19
  // Publish sidebar features to context whenever feature node data changes
20
20
  const { setFeatures: setSidebarFeatures } = useSidebarFeaturesContext();
21
+ const featureNodes = useMemo(() => nodes.filter((n) => n.type === 'featureNode'), [nodes]);
21
22
  const sidebarKey = useMemo(() => {
22
- return nodes
23
- .filter((n) => n.type === 'featureNode')
23
+ return featureNodes
24
24
  .map((n) => {
25
25
  const d = n.data;
26
- return `${d.featureId}:${d.state}:${d.runtime ?? ''}:${d.startedAt ?? ''}`;
26
+ return `${d.featureId}:${d.state}:${d.name}`;
27
27
  })
28
28
  .sort()
29
29
  .join(',');
30
- }, [nodes]);
30
+ }, [featureNodes]);
31
31
  useEffect(() => {
32
- const sidebarItems = nodes
33
- .filter((n) => n.type === 'featureNode')
32
+ const sidebarItems = featureNodes
34
33
  .map((n) => {
35
34
  const d = n.data;
36
35
  const status = mapNodeStateToSidebarStatus(d.state);
@@ -46,7 +45,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
46
45
  })
47
46
  .filter(Boolean);
48
47
  setSidebarFeatures(sidebarItems);
49
- }, [sidebarKey, nodes, setSidebarFeatures]);
48
+ }, [sidebarKey, featureNodes, setSidebarFeatures]);
50
49
  // ── URL-based navigation handlers ────────────────────────────────────
51
50
  const handleNodeClick = useCallback((_event, node) => {
52
51
  if (node.type === 'featureNode') {
@@ -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":"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;AAWjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG7E,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;AAOD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA+UpB"}
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;AAWjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG7E,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;AAOD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAgWpB"}
@@ -19,6 +19,13 @@ export function useControlCenterState(initialNodes, initialEdges) {
19
19
  const deleteSound = useSoundAction('delete');
20
20
  const createSound = useSoundAction('create');
21
21
  const { nodes, edges, reconcile, updateFeature, addPendingFeature, removeFeature, restoreFeature, addRepository: addRepositoryToMap, removeRepository, replaceRepository, getFeatureRepositoryPath, getRepositoryData, setCallbacks, } = useGraphState(initialNodes, initialEdges);
22
+ // Refs for stable access to latest nodes/edges without callback recreation
23
+ const nodesRef = useRef(nodes);
24
+ const edgesRef = useRef(edges);
25
+ useEffect(() => {
26
+ nodesRef.current = nodes;
27
+ edgesRef.current = edges;
28
+ }, [nodes, edges]);
22
29
  // Sync server props into domain Maps when initialNodes/initialEdges change.
23
30
  // Keyed by a stable string so we don't re-reconcile on every render.
24
31
  const initialNodeKey = useMemo(() => initialNodes
@@ -77,6 +84,10 @@ export function useControlCenterState(initialNodes, initialEdges) {
77
84
  // SSE effect: only state + lifecycle updates
78
85
  const processedEventCountRef = useRef(0);
79
86
  useEffect(() => {
87
+ // Clamp cursor if events were pruned
88
+ if (processedEventCountRef.current > events.length) {
89
+ processedEventCountRef.current = 0;
90
+ }
80
91
  if (events.length <= processedEventCountRef.current)
81
92
  return;
82
93
  const newEvents = events.slice(processedEventCountRef.current);
@@ -103,6 +114,10 @@ export function useControlCenterState(initialNodes, initialEdges) {
103
114
  const metadataFetchedRef = useRef(new Set());
104
115
  const processedMetadataCountRef = useRef(0);
105
116
  useEffect(() => {
117
+ // Clamp cursor if events were pruned
118
+ if (processedMetadataCountRef.current > events.length) {
119
+ processedMetadataCountRef.current = 0;
120
+ }
106
121
  if (events.length <= processedMetadataCountRef.current)
107
122
  return;
108
123
  const newEvents = events.slice(processedMetadataCountRef.current);
@@ -156,7 +171,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
156
171
  const handleDeleteFeature = useCallback((featureId) => {
157
172
  const nodeId = `feat-${featureId}`;
158
173
  // Find the current entry for rollback
159
- const prevEntry = nodes
174
+ const prevEntry = nodesRef.current
160
175
  .filter((n) => n.id === nodeId)
161
176
  .map((n) => ({
162
177
  nodeId: n.id,
@@ -180,16 +195,16 @@ export function useControlCenterState(initialNodes, initialEdges) {
180
195
  restoreFeature(nodeId, prevEntry);
181
196
  toast.error('Failed to delete feature');
182
197
  });
183
- }, [nodes, router, deleteSound, removeFeature, restoreFeature]);
198
+ }, [router, deleteSound, removeFeature, restoreFeature]);
184
199
  const handleDeleteRepository = useCallback(async (repositoryId) => {
185
200
  const repoNodeId = `repo-${repositoryId}`;
186
201
  // Find children of this repo via edges
187
- const childFeatureIds = new Set(edges.filter((e) => e.source === repoNodeId).map((e) => e.target));
202
+ const childFeatureIds = new Set(edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target));
188
203
  // Snapshot for rollback
189
204
  const prevRepoData = getRepositoryData(repoNodeId);
190
205
  const childSnapshots = new Map();
191
206
  for (const childId of childFeatureIds) {
192
- const childNode = nodes.find((n) => n.id === childId);
207
+ const childNode = nodesRef.current.find((n) => n.id === childId);
193
208
  if (childNode) {
194
209
  childSnapshots.set(childId, { nodeId: childId, data: childNode.data });
195
210
  }
@@ -221,8 +236,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
221
236
  rollback();
222
237
  }
223
238
  }, [
224
- edges,
225
- nodes,
226
239
  deleteSound,
227
240
  removeRepository,
228
241
  removeFeature,
@@ -233,9 +246,12 @@ export function useControlCenterState(initialNodes, initialEdges) {
233
246
  const handleLayout = useCallback((direction) => {
234
247
  // Layout is applied via reconcile on next server prop update.
235
248
  // For immediate re-layout, we apply dagre and trigger a reconcile-like update.
236
- const result = layoutWithDagre(nodes, edges, { ...CANVAS_LAYOUT_DEFAULTS, direction });
249
+ const result = layoutWithDagre(nodesRef.current, edgesRef.current, {
250
+ ...CANVAS_LAYOUT_DEFAULTS,
251
+ direction,
252
+ });
237
253
  reconcile(result.nodes, result.edges);
238
- }, [nodes, edges, reconcile]);
254
+ }, [reconcile]);
239
255
  const handleAddRepository = useCallback((path) => {
240
256
  const tempId = `repo-temp-${Date.now()}`;
241
257
  const repoName = path
@@ -1 +1 @@
1
- {"version":3,"file":"agent-events-provider.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/agent-events-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAI5B,UAAU,wBAAyB,SAAQ,qBAAqB;IAC9D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,wBAAwB,2CAIhF;AAED,wBAAgB,qBAAqB,IAAI,oBAAoB,CAM5D"}
1
+ {"version":3,"file":"agent-events-provider.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/agent-events-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAI5B,UAAU,wBAAyB,SAAQ,qBAAqB;IAC9D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,wBAAwB,2CAShF;AAED,wBAAgB,qBAAqB,IAAI,oBAAoB,CAM5D"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useContext } from 'react';
3
+ import { createContext, useContext, useMemo } from 'react';
4
4
  import { useAgentEvents, } from './use-agent-events.js';
5
5
  const AgentEventsContext = createContext(null);
6
6
  /**
@@ -8,7 +8,8 @@ const AgentEventsContext = createContext(null);
8
8
  * Wrap the app once; use `useAgentEventsContext()` to read.
9
9
  */
10
10
  export function AgentEventsProvider({ children, runId }) {
11
- const value = useAgentEvents({ runId });
11
+ const { events, lastEvent, connectionStatus } = useAgentEvents({ runId });
12
+ const value = useMemo(() => ({ events, lastEvent, connectionStatus }), [events, lastEvent, connectionStatus]);
12
13
  return _jsx(AgentEventsContext.Provider, { value: value, children: children });
13
14
  }
14
15
  export function useAgentEventsContext() {
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-events.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-agent-events.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,oBAAoB,CA+GpF"}
1
+ {"version":3,"file":"use-agent-events.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-agent-events.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAMD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,oBAAoB,CAoHpF"}
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
  import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  const SW_PATH = '/agent-events-sw.js';
4
+ const MAX_EVENTS = 500;
5
+ const PRUNE_KEEP = 250;
4
6
  /**
5
7
  * Hook that receives real-time agent notification events via a Service Worker.
6
8
  *
@@ -20,7 +22,12 @@ export function useAgentEvents(options) {
20
22
  return;
21
23
  if (msg.type === 'notification') {
22
24
  const parsed = msg.data;
23
- setEvents((prev) => [...prev, parsed]);
25
+ // eslint-disable-next-line no-console
26
+ console.log('[SSE] event received:', parsed.eventType, parsed);
27
+ setEvents((prev) => {
28
+ const next = [...prev, parsed];
29
+ return next.length > MAX_EVENTS ? next.slice(-PRUNE_KEEP) : next;
30
+ });
24
31
  setLastEvent(parsed);
25
32
  }
26
33
  else if (msg.type === 'status') {
@@ -146,7 +153,10 @@ function connectDirectEventSource(runId, setEvents, setLastEvent, setConnectionS
146
153
  };
147
154
  es.addEventListener('notification', ((event) => {
148
155
  const parsed = JSON.parse(event.data);
149
- setEvents((prev) => [...prev, parsed]);
156
+ setEvents((prev) => {
157
+ const next = [...prev, parsed];
158
+ return next.length > MAX_EVENTS ? next.slice(-PRUNE_KEEP) : next;
159
+ });
150
160
  setLastEvent(parsed);
151
161
  }));
152
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAeD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAgKlF"}
1
+ {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAeD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAwKlF"}