@shepai/cli 1.106.4 → 1.107.0-pr330.e164f47

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 (311) hide show
  1. package/dist/packages/core/src/application/ports/output/agents/feature-agent-process.interface.d.ts +1 -0
  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/ports/output/services/git-pr-service.interface.d.ts +12 -1
  4. package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts.map +1 -1
  5. package/dist/packages/core/src/application/use-cases/features/resume-feature.use-case.d.ts.map +1 -1
  6. package/dist/packages/core/src/application/use-cases/features/resume-feature.use-case.js +2 -0
  7. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  8. package/dist/packages/core/src/infrastructure/di/container.js +3 -0
  9. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/fast-feature-agent-graph.d.ts +5 -0
  10. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/fast-feature-agent-graph.d.ts.map +1 -1
  11. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +17 -0
  12. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
  13. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts +1 -0
  14. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts.map +1 -1
  15. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.js +3 -0
  16. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts +1 -0
  17. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  18. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +8 -1
  19. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts +5 -1
  20. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts.map +1 -1
  21. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.js +4 -1
  22. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts +7 -0
  23. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
  24. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +23 -1
  25. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.d.ts +1 -0
  26. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.d.ts.map +1 -1
  27. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.js +5 -0
  28. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts +2 -1
  29. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
  30. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +22 -3
  31. package/dist/packages/core/src/infrastructure/services/process/is-process-alive.d.ts +8 -0
  32. package/dist/packages/core/src/infrastructure/services/process/is-process-alive.d.ts.map +1 -0
  33. package/dist/packages/core/src/infrastructure/services/process/is-process-alive.js +15 -0
  34. package/dist/src/presentation/cli/commands/status.command.d.ts +1 -1
  35. package/dist/src/presentation/cli/commands/status.command.d.ts.map +1 -1
  36. package/dist/src/presentation/cli/commands/status.command.js +2 -38
  37. package/dist/src/presentation/web/app/(dashboard)/@drawer/feature/[featureId]/page.d.ts.map +1 -1
  38. package/dist/src/presentation/web/app/(dashboard)/@drawer/feature/[featureId]/page.js +5 -48
  39. package/dist/src/presentation/web/app/actions/get-feature-drawer-data.d.ts +8 -0
  40. package/dist/src/presentation/web/app/actions/get-feature-drawer-data.d.ts.map +1 -0
  41. package/dist/src/presentation/web/app/actions/get-feature-drawer-data.js +67 -0
  42. package/dist/src/presentation/web/app/actions/resume-feature.d.ts +5 -0
  43. package/dist/src/presentation/web/app/actions/resume-feature.d.ts.map +1 -0
  44. package/dist/src/presentation/web/app/actions/resume-feature.js +16 -0
  45. package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
  46. package/dist/src/presentation/web/app/api/agent-events/route.js +17 -0
  47. package/dist/src/presentation/web/app/api/graph-data/route.d.ts +14 -0
  48. package/dist/src/presentation/web/app/api/graph-data/route.d.ts.map +1 -0
  49. package/dist/src/presentation/web/app/api/graph-data/route.js +14 -0
  50. package/dist/src/presentation/web/app/api/version/route.d.ts +3 -6
  51. package/dist/src/presentation/web/app/api/version/route.d.ts.map +1 -1
  52. package/dist/src/presentation/web/app/api/version/route.js +29 -15
  53. package/dist/src/presentation/web/app/build-feature-node-data.d.ts.map +1 -1
  54. package/dist/src/presentation/web/app/build-feature-node-data.js +6 -1
  55. package/dist/src/presentation/web/app/build-graph-nodes.d.ts.map +1 -1
  56. package/dist/src/presentation/web/app/build-graph-nodes.js +6 -1
  57. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  58. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +30 -26
  59. package/dist/src/presentation/web/components/common/control-center-drawer/use-drawer-sync.d.ts +12 -0
  60. package/dist/src/presentation/web/components/common/control-center-drawer/use-drawer-sync.d.ts.map +1 -0
  61. package/dist/src/presentation/web/components/common/control-center-drawer/use-drawer-sync.js +75 -0
  62. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +1 -1
  63. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +3 -1
  64. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
  65. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +2 -2
  66. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.d.ts +2 -0
  67. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.d.ts.map +1 -1
  68. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.js +9 -0
  69. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts +4 -2
  70. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
  71. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +5 -3
  72. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +1 -0
  73. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map +1 -1
  74. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  75. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +11 -8
  76. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +1 -0
  77. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
  78. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +13 -0
  79. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  80. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +3 -1
  81. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +1 -0
  82. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  83. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +27 -2
  84. package/dist/src/presentation/web/hooks/use-graph-state.d.ts.map +1 -1
  85. package/dist/src/presentation/web/hooks/use-graph-state.js +27 -0
  86. package/dist/src/presentation/web/hooks/use-sound.d.ts.map +1 -1
  87. package/dist/src/presentation/web/hooks/use-sound.js +13 -3
  88. package/dist/src/presentation/web/lib/derive-graph.d.ts +2 -0
  89. package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -1
  90. package/dist/src/presentation/web/lib/derive-graph.js +5 -0
  91. package/dist/src/presentation/web/lib/version.d.ts +5 -5
  92. package/dist/src/presentation/web/lib/version.d.ts.map +1 -1
  93. package/dist/src/presentation/web/lib/version.js +26 -12
  94. package/dist/tsconfig.build.tsbuildinfo +1 -1
  95. package/package.json +1 -1
  96. package/web/.next/BUILD_ID +1 -1
  97. package/web/.next/app-path-routes-manifest.json +1 -0
  98. package/web/.next/build-manifest.json +2 -2
  99. package/web/.next/fallback-build-manifest.json +2 -2
  100. package/web/.next/prerender-manifest.json +3 -3
  101. package/web/.next/required-server-files.js +3 -3
  102. package/web/.next/required-server-files.json +3 -3
  103. package/web/.next/routes-manifest.json +6 -0
  104. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +53 -53
  105. package/web/.next/server/app/(dashboard)/@drawer/create/page.js +3 -3
  106. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  107. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  108. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +78 -63
  109. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js +4 -4
  110. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  111. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  112. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +78 -63
  113. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +4 -4
  114. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  115. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  116. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +49 -49
  117. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +3 -3
  118. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  119. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  120. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +53 -53
  121. package/web/.next/server/app/(dashboard)/create/page.js +3 -3
  122. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  123. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  124. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +78 -63
  125. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js +4 -4
  126. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  127. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  128. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +78 -63
  129. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +4 -4
  130. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  131. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  132. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +49 -49
  133. package/web/.next/server/app/(dashboard)/page.js +3 -3
  134. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  135. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  136. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +49 -49
  137. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +3 -3
  138. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  139. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  140. package/web/.next/server/app/_global-error.html +2 -2
  141. package/web/.next/server/app/_global-error.rsc +1 -1
  142. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  143. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  144. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  145. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  146. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  147. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  148. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  149. package/web/.next/server/app/api/agent-events/route.js +1 -1
  150. package/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  151. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  152. package/web/.next/server/app/api/attachments/upload-from-path/route.js +1 -1
  153. package/web/.next/server/app/api/attachments/upload-from-path/route.js.nft.json +1 -1
  154. package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
  155. package/web/.next/server/app/api/feature-logs/route.js +1 -1
  156. package/web/.next/server/app/api/feature-logs/route.js.nft.json +1 -1
  157. package/web/.next/server/app/api/graph-data/route/app-paths-manifest.json +3 -0
  158. package/web/.next/server/app/api/graph-data/route/build-manifest.json +11 -0
  159. package/web/.next/server/app/api/graph-data/route/server-reference-manifest.json +4 -0
  160. package/web/.next/server/app/api/graph-data/route.js +8 -0
  161. package/web/.next/server/app/api/graph-data/route.js.map +5 -0
  162. package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -0
  163. package/web/.next/server/app/api/graph-data/route_client-reference-manifest.js +2 -0
  164. package/web/.next/server/app/api/version/route.js +1 -1
  165. package/web/.next/server/app/api/version/route.js.nft.json +1 -1
  166. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  167. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  168. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  169. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  170. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  171. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  172. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  173. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  174. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  175. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  176. package/web/.next/server/app/version/page.js +1 -1
  177. package/web/.next/server/app/version/page.js.nft.json +1 -1
  178. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  179. package/web/.next/server/app-paths-manifest.json +1 -0
  180. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js +3 -0
  181. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js.map +1 -0
  182. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
  183. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
  184. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_graph-data_route_actions_26df8a4e.js +3 -0
  185. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_graph-data_route_actions_26df8a4e.js.map +1 -0
  186. package/web/.next/server/chunks/[root-of-the-server]__7d5492a0._.js +3 -0
  187. package/web/.next/server/chunks/[root-of-the-server]__7d5492a0._.js.map +1 -0
  188. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +3 -0
  189. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js.map +1 -0
  190. package/web/.next/server/chunks/{[root-of-the-server]__db04ed44._.js → [root-of-the-server]__a5879003._.js} +2 -2
  191. package/web/.next/server/chunks/[root-of-the-server]__cd67a84c._.js +3 -0
  192. package/web/.next/server/chunks/[root-of-the-server]__cd67a84c._.js.map +1 -0
  193. package/web/.next/server/chunks/{[root-of-the-server]__91d44424._.js → [root-of-the-server]__d8037e14._.js} +2 -2
  194. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  195. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  196. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_a1104008.js +3 -0
  197. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_a1104008.js.map +1 -0
  198. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_92642200.js +3 -0
  199. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_92642200.js.map +1 -0
  200. package/web/.next/server/chunks/ssr/[root-of-the-server]__039967e3._.js +3 -0
  201. package/web/.next/server/chunks/ssr/[root-of-the-server]__039967e3._.js.map +1 -0
  202. package/web/.next/server/chunks/ssr/[root-of-the-server]__07a39ff2._.js +3 -0
  203. package/web/.next/server/chunks/ssr/[root-of-the-server]__07a39ff2._.js.map +1 -0
  204. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  205. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  206. package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js +2 -2
  207. package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js.map +1 -1
  208. package/web/.next/server/chunks/ssr/[root-of-the-server]__2773312c._.js +3 -0
  209. package/web/.next/server/chunks/ssr/[root-of-the-server]__2773312c._.js.map +1 -0
  210. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  211. package/web/.next/server/chunks/ssr/[root-of-the-server]__684a868c._.js +3 -0
  212. package/web/.next/server/chunks/ssr/[root-of-the-server]__684a868c._.js.map +1 -0
  213. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js +1 -1
  214. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js.map +1 -1
  215. package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
  216. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
  217. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  218. package/web/.next/server/chunks/ssr/[root-of-the-server]__c16bf5de._.js +3 -0
  219. package/web/.next/server/chunks/ssr/[root-of-the-server]__c16bf5de._.js.map +1 -0
  220. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbd8062c._.js +3 -0
  221. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbd8062c._.js.map +1 -0
  222. package/web/.next/server/chunks/ssr/[root-of-the-server]__e70c5627._.js +3 -0
  223. package/web/.next/server/chunks/ssr/[root-of-the-server]__e70c5627._.js.map +1 -0
  224. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  225. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  226. package/web/.next/server/chunks/ssr/{_a8d9cc2f._.js → _0f2b39f5._.js} +2 -2
  227. package/web/.next/server/chunks/ssr/{_a8d9cc2f._.js.map → _0f2b39f5._.js.map} +1 -1
  228. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  229. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  230. package/web/.next/server/chunks/ssr/_250a63ae._.js +3 -0
  231. package/web/.next/server/chunks/ssr/_250a63ae._.js.map +1 -0
  232. package/web/.next/server/chunks/ssr/{_4059434a._.js → _7cb5afad._.js} +2 -2
  233. package/web/.next/server/chunks/ssr/{_4059434a._.js.map → _7cb5afad._.js.map} +1 -1
  234. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  235. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  236. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  237. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  238. package/web/.next/server/chunks/ssr/_e6f2da4f._.js.map +1 -1
  239. package/web/.next/server/chunks/ssr/{_0619c65b._.js → _e8deff2b._.js} +2 -2
  240. package/web/.next/server/chunks/ssr/_e8deff2b._.js.map +1 -0
  241. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  242. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  243. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_888633e1.js +3 -0
  244. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_888633e1.js.map +1 -0
  245. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  246. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  247. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  248. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  249. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +3 -0
  250. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -0
  251. package/web/.next/server/chunks/ssr/{src_presentation_web_758dfa10._.js → src_presentation_web_ce7cbc3c._.js} +3 -3
  252. package/web/.next/server/chunks/ssr/{src_presentation_web_758dfa10._.js.map → src_presentation_web_ce7cbc3c._.js.map} +1 -1
  253. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  254. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  255. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  256. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  257. package/web/.next/server/chunks/ssr/{src_presentation_web_7275ce5d._.js → src_presentation_web_e2604413._.js} +3 -3
  258. package/web/.next/server/chunks/ssr/{src_presentation_web_7275ce5d._.js.map → src_presentation_web_e2604413._.js.map} +1 -1
  259. package/web/.next/server/pages/500.html +2 -2
  260. package/web/.next/server/server-reference-manifest.js +1 -1
  261. package/web/.next/server/server-reference-manifest.json +321 -285
  262. package/web/.next/static/chunks/{15e6c83214978e2a.js → 0822ebbebff2718d.js} +1 -1
  263. package/web/.next/static/chunks/{8a2e6fabcbb6720d.js → 14a2cef371a9f42d.js} +1 -1
  264. package/web/.next/static/chunks/{abb2cdec13050512.js → 262f024806123251.js} +1 -1
  265. package/web/.next/static/chunks/505ff1e0712e80f9.js +2 -0
  266. package/web/.next/static/chunks/5558a658fa82a01f.css +1 -0
  267. package/web/.next/static/chunks/{7f3823482bd3f594.js → 606cf866494330d2.js} +1 -1
  268. package/web/.next/static/chunks/{524307fe3f73c1cf.js → 7e2854d3352f7f50.js} +2 -2
  269. package/web/.next/static/chunks/{71733e636d34974a.js → bc638bb542a3718b.js} +1 -1
  270. package/web/.next/static/chunks/{9144d63a39898da5.js → e7cd8862950038a6.js} +1 -1
  271. package/web/.next/static/chunks/{229caf9bd06f0c45.js → ee097d181dc97a5d.js} +2 -2
  272. package/web/.next/static/chunks/{cacd384748cf0bd2.js → f20192b3d50908b7.js} +1 -1
  273. package/dist/src/presentation/web/app/actions/get-graph-data.d.ts +0 -10
  274. package/dist/src/presentation/web/app/actions/get-graph-data.d.ts.map +0 -1
  275. package/dist/src/presentation/web/app/actions/get-graph-data.js +0 -10
  276. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +0 -3
  277. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js.map +0 -1
  278. package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js +0 -3
  279. package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js.map +0 -1
  280. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js +0 -3
  281. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js.map +0 -1
  282. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js +0 -3
  283. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js.map +0 -1
  284. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +0 -3
  285. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js.map +0 -1
  286. package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js +0 -3
  287. package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js.map +0 -1
  288. package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js +0 -3
  289. package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js.map +0 -1
  290. package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js +0 -3
  291. package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js.map +0 -1
  292. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js +0 -3
  293. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js.map +0 -1
  294. package/web/.next/server/chunks/ssr/[root-of-the-server]__94128fa9._.js +0 -3
  295. package/web/.next/server/chunks/ssr/[root-of-the-server]__94128fa9._.js.map +0 -1
  296. package/web/.next/server/chunks/ssr/[root-of-the-server]__9bfb8e7c._.js +0 -3
  297. package/web/.next/server/chunks/ssr/[root-of-the-server]__9bfb8e7c._.js.map +0 -1
  298. package/web/.next/server/chunks/ssr/_0619c65b._.js.map +0 -1
  299. package/web/.next/server/chunks/ssr/_6e08ce21._.js +0 -3
  300. package/web/.next/server/chunks/ssr/_6e08ce21._.js.map +0 -1
  301. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js +0 -3
  302. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js.map +0 -1
  303. package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +0 -3
  304. package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js.map +0 -1
  305. package/web/.next/static/chunks/a5bcb76c9dcfaee6.js +0 -2
  306. package/web/.next/static/chunks/db122a6247f69104.css +0 -1
  307. /package/web/.next/server/chunks/{[root-of-the-server]__db04ed44._.js.map → [root-of-the-server]__a5879003._.js.map} +0 -0
  308. /package/web/.next/server/chunks/{[root-of-the-server]__91d44424._.js.map → [root-of-the-server]__d8037e14._.js.map} +0 -0
  309. /package/web/.next/static/{ORc-CcNcyS9ide_AZZ8ay → cQ78pXvGiKF4oj7-Lefwt}/_buildManifest.js +0 -0
  310. /package/web/.next/static/{ORc-CcNcyS9ide_AZZ8ay → cQ78pXvGiKF4oj7-Lefwt}/_clientMiddlewareManifest.json +0 -0
  311. /package/web/.next/static/{ORc-CcNcyS9ide_AZZ8ay → cQ78pXvGiKF4oj7-Lefwt}/_ssgManifest.js +0 -0
@@ -5,6 +5,7 @@ import { useRouter, usePathname } from 'next/navigation';
5
5
  import { toast } from 'sonner';
6
6
  import { Loader2, Trash2, Play, Square, Copy, Check, Code2, ExternalLink } from 'lucide-react';
7
7
  import { approveFeature } from '../../../app/actions/approve-feature.js';
8
+ import { resumeFeature } from '../../../app/actions/resume-feature.js';
8
9
  import { rejectFeature } from '../../../app/actions/reject-feature.js';
9
10
  import { getFeatureArtifact } from '../../../app/actions/get-feature-artifact.js';
10
11
  import { getResearchArtifact } from '../../../app/actions/get-research-artifact.js';
@@ -28,6 +29,7 @@ import { useFeatureActions } from '../../common/feature-drawer/use-feature-actio
28
29
  import { resolveSseEventUpdates } from '../../common/feature-node/derive-feature-state.js';
29
30
  import { deriveInitialTab } from './drawer-view.js';
30
31
  import { useArtifactFetch } from './use-artifact-fetch.js';
32
+ import { useDrawerSync } from './use-drawer-sync.js';
31
33
  export function FeatureDrawerClient({ view: initialView, urlTab }) {
32
34
  const featureFlags = useFeatureFlags();
33
35
  const router = useRouter();
@@ -35,7 +37,7 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
35
37
  const rejectSound = useSoundAction('reject');
36
38
  // Track the view locally so SSE events can update the drawer type in real-time
37
39
  const [view, setView] = useState(initialView);
38
- // Sync when server re-renders with new props (e.g. after router.refresh())
40
+ // Sync when server re-renders with new props (e.g. after navigation)
39
41
  useEffect(() => {
40
42
  setView(initialView);
41
43
  }, [initialView]);
@@ -60,15 +62,9 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
60
62
  // fetches (which would fail with "not found" for soft-deleted features).
61
63
  if (featureNode.state === 'deleting')
62
64
  continue;
63
- // Trigger a server refresh to get the latest drawer view,
64
- // but skip when the drawer is closed, the user has unsaved changes,
65
- // or a rejection is in-flight to avoid unnecessary refreshes.
66
- if (isOpenRef.current && !isDirtyRef.current && !isRejectingRef.current)
67
- router.refresh();
68
65
  if (update.state !== undefined || update.lifecycle !== undefined) {
69
- // Optimistically update the node data AND re-derive the initial tab so the
70
- // drawer switches immediately (e.g. prd-review → overview when agent resumes)
71
- // without waiting for the router.refresh() round-trip.
66
+ // Update the node data AND re-derive the initial tab so the drawer
67
+ // switches immediately (e.g. prd-review → overview when agent resumes).
72
68
  setView((prev) => {
73
69
  if (prev.type !== 'feature')
74
70
  return prev;
@@ -81,24 +77,17 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
81
77
  });
82
78
  }
83
79
  }
84
- }, [events, featureNode, router]);
80
+ }, [events, featureNode]);
85
81
  // Derive open state from the URL. Next.js parallel routes preserve slot
86
82
  // content during soft navigation, so this component is NOT unmounted when
87
83
  // navigating to `/`. Instead, we watch the pathname and let Vaul handle
88
84
  // the close animation when the path no longer matches a feature route.
89
85
  const pathname = usePathname();
90
86
  const isOpen = pathname.startsWith('/feature/');
91
- const isOpenRef = useRef(isOpen);
92
- const wasOpenRef = useRef(isOpen);
93
- // When the drawer re-opens, force a server refresh so the view is always fresh.
94
- // SSE skips router.refresh() while the drawer is closed, so data can go stale.
95
- useEffect(() => {
96
- if (isOpen && !wasOpenRef.current) {
97
- router.refresh();
98
- }
99
- wasOpenRef.current = isOpen;
100
- }, [isOpen, router]);
101
- isOpenRef.current = isOpen;
87
+ // Targeted data sync: fetches fresh FeatureNodeData on drawer open and
88
+ // periodically while open. Replaces router.refresh() to avoid full-page
89
+ // re-renders that cause visible flashing and can reset form state.
90
+ useDrawerSync(isOpen, featureNode?.featureId ?? null, setView);
102
91
  const onClose = useCallback(() => {
103
92
  router.push('/');
104
93
  }, [router]);
@@ -182,10 +171,6 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
182
171
  featureNode?.state === 'action-required' &&
183
172
  Object.keys(prdDefaultSelections).some((k) => prdDefaultSelections[k] !== prdSelections[k]);
184
173
  const isDirty = isChatDirty || isPrdDirty;
185
- // Keep dirty state in a ref so the SSE handler can read the latest value
186
- // without re-running the effect on every keystroke.
187
- const isDirtyRef = useRef(isDirty);
188
- isDirtyRef.current = isDirty;
189
174
  const onReset = useCallback(() => {
190
175
  setChatInput('');
191
176
  setPrdSelections({ ...prdDefaultSelections });
@@ -288,6 +273,24 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
288
273
  setIsDeleting(false);
289
274
  }
290
275
  }, [router, deleteSound]);
276
+ const handleRetry = useCallback(async (featureId) => {
277
+ const result = await resumeFeature(featureId);
278
+ if (result.error) {
279
+ toast.error(result.error);
280
+ return;
281
+ }
282
+ toast.success('Feature resumed — agent restarting');
283
+ // Optimistically update canvas node
284
+ window.dispatchEvent(new CustomEvent('shep:feature-approved', {
285
+ detail: { featureId },
286
+ }));
287
+ // Optimistically update the drawer view
288
+ setView((prev) => {
289
+ if (prev.type !== 'feature')
290
+ return prev;
291
+ return { ...prev, node: { ...prev.node, state: 'running' } };
292
+ });
293
+ }, []);
291
294
  // ── Hooks (always called unconditionally per Rules of Hooks) ──────────
292
295
  const featureActionsInput = featureNode?.repositoryPath && featureNode?.branch
293
296
  ? {
@@ -329,7 +332,8 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
329
332
  // ── Body ──────────────────────────────────────────────────────────────
330
333
  let body = null;
331
334
  if (view.type === 'feature' && featureNode) {
332
- body = (_jsx(FeatureDrawerTabs, { featureNode: featureNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, sseEvents: events, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput }));
335
+ const enrichedNode = featureNode.state === 'error' ? { ...featureNode, onRetry: handleRetry } : featureNode;
336
+ body = (_jsx(FeatureDrawerTabs, { featureNode: enrichedNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, sseEvents: events, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput }));
333
337
  }
334
338
  return (_jsx(BaseDrawer, { open: isOpen, onClose: attemptClose, size: "md", modal: false, header: header, "data-testid": view.type === 'feature' ? 'feature-drawer' : 'repository-drawer', children: body }));
335
339
  }
@@ -0,0 +1,12 @@
1
+ import type { DrawerView } from './drawer-view.js';
2
+ /**
3
+ * Targeted drawer data synchronization — replaces router.refresh() for
4
+ * keeping the feature drawer in sync with the server.
5
+ *
6
+ * - On drawer open: fetches fresh FeatureNodeData via server action
7
+ * - Background sync: every 15s while open, merges fresh data into view
8
+ * - SSE-driven state/lifecycle updates are handled separately (not here)
9
+ * - Never affects form state (chatInput, prdSelections, attachments)
10
+ */
11
+ export declare function useDrawerSync(isOpen: boolean, featureId: string | null, setView: React.Dispatch<React.SetStateAction<DrawerView>>): void;
12
+ //# sourceMappingURL=use-drawer-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-drawer-sync.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/use-drawer-sync.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,GACxD,IAAI,CAwCN"}
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+ import { useEffect, useRef, useCallback } from 'react';
3
+ import { getFeatureDrawerData } from '../../../app/actions/get-feature-drawer-data.js';
4
+ import { deriveInitialTab } from './drawer-view.js';
5
+ /** How often (ms) to run a background sync while the drawer is open. */
6
+ const BACKGROUND_SYNC_INTERVAL_MS = 15_000;
7
+ /**
8
+ * Targeted drawer data synchronization — replaces router.refresh() for
9
+ * keeping the feature drawer in sync with the server.
10
+ *
11
+ * - On drawer open: fetches fresh FeatureNodeData via server action
12
+ * - Background sync: every 15s while open, merges fresh data into view
13
+ * - SSE-driven state/lifecycle updates are handled separately (not here)
14
+ * - Never affects form state (chatInput, prdSelections, attachments)
15
+ */
16
+ export function useDrawerSync(isOpen, featureId, setView) {
17
+ const wasOpenRef = useRef(isOpen);
18
+ const isFetchingRef = useRef(false);
19
+ const syncFromServer = useCallback(async () => {
20
+ if (!featureId || isFetchingRef.current)
21
+ return;
22
+ isFetchingRef.current = true;
23
+ try {
24
+ const data = await getFeatureDrawerData(featureId);
25
+ if (!data)
26
+ return;
27
+ setView((prev) => mergeFeatureData(prev, data));
28
+ }
29
+ catch {
30
+ // Silent — background sync failure is non-critical
31
+ }
32
+ finally {
33
+ isFetchingRef.current = false;
34
+ }
35
+ }, [featureId, setView]);
36
+ // Fetch full data when drawer opens (either on mount or closed→open transition).
37
+ // The server component only provides minimal data (feature + agent run);
38
+ // expensive fields (repo name, remote URL, CI status, etc.) are loaded here.
39
+ const hasFetchedOnMountRef = useRef(false);
40
+ useEffect(() => {
41
+ if (isOpen && (!wasOpenRef.current || !hasFetchedOnMountRef.current)) {
42
+ hasFetchedOnMountRef.current = true;
43
+ void syncFromServer();
44
+ }
45
+ wasOpenRef.current = isOpen;
46
+ }, [isOpen, syncFromServer]);
47
+ // Background sync: throttled interval while drawer is open
48
+ useEffect(() => {
49
+ if (!isOpen || !featureId)
50
+ return;
51
+ const timer = setInterval(() => {
52
+ void syncFromServer();
53
+ }, BACKGROUND_SYNC_INTERVAL_MS);
54
+ return () => clearInterval(timer);
55
+ }, [isOpen, featureId, syncFromServer]);
56
+ }
57
+ /**
58
+ * Merges fresh server data into the current drawer view.
59
+ * Preserves the view type and only updates the feature node data,
60
+ * keeping all client-side state (form inputs, selections) untouched.
61
+ */
62
+ function mergeFeatureData(prev, freshData) {
63
+ if (prev.type !== 'feature')
64
+ return prev;
65
+ const merged = { ...prev.node, ...freshData };
66
+ // Only re-derive initialTab when state or lifecycle actually changed.
67
+ // Re-deriving on every background sync would reset the chat input
68
+ // (which clears on initialTab change) even when nothing meaningful changed.
69
+ const tabChanged = merged.state !== prev.node.state || merged.lifecycle !== prev.node.lifecycle;
70
+ return {
71
+ ...prev,
72
+ node: merged,
73
+ initialTab: tabChanged ? deriveInitialTab(merged) : prev.initialTab,
74
+ };
75
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"feature-drawer-tabs.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAQ9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAiD3F,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,eAAe,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,gFAAgF;IAChF,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,mFAAmF;IACnF,SAAS,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAGzC,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,QAAQ,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,WAAW,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAGjD,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;IAGzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAgB,iBAAiB,CAAC,EAChC,WAAW,EACX,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,SAAS,GACV,EAAE,sBAAsB,2CAwTxB"}
1
+ {"version":3,"file":"feature-drawer-tabs.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAQ9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAiD3F,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,eAAe,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,gFAAgF;IAChF,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,mFAAmF;IACnF,SAAS,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAGzC,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,QAAQ,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,WAAW,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAGjD,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;IAGzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAgB,iBAAiB,CAAC,EAChC,WAAW,EACX,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,SAAS,GACV,EAAE,sBAAsB,2CA2TxB"}
@@ -66,7 +66,6 @@ const TAB_FETCHERS = {
66
66
  };
67
67
  export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, sseEvents, }) {
68
68
  const pathname = usePathname();
69
- const featureLogs = useFeatureLogs(featureId);
70
69
  const visibleTabs = useMemo(() => computeVisibleTabs(featureNode), [featureNode]);
71
70
  const visibleTabDefs = useMemo(() => ALL_TABS.filter((t) => visibleTabs.includes(t.key)), [visibleTabs]);
72
71
  // Derive the base path (without tab segment) from the current pathname.
@@ -86,6 +85,9 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
86
85
  // eslint-disable-next-line react-hooks/exhaustive-deps
87
86
  }, []);
88
87
  const [activeTab, setActiveTab] = useState(effectiveInitial);
88
+ // Only subscribe to log SSE when the log tab is active to avoid
89
+ // opening an EventSource connection on every drawer open.
90
+ const featureLogs = useFeatureLogs(activeTab === 'log' ? featureId : null);
89
91
  const { tabs, fetchTab, refreshTab } = useTabDataFetch(featureId, TAB_FETCHERS);
90
92
  // Sync URL when active tab changes via user interaction.
91
93
  // Use window.history.pushState instead of router.push to avoid triggering a
@@ -1 +1 @@
1
- {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAOxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
1
+ {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAOxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAoDrD"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useRef } from 'react';
4
- import { AlertTriangle, ExternalLink, GitBranch, GitCommitHorizontal, Zap } from 'lucide-react';
4
+ import { AlertTriangle, ExternalLink, GitBranch, GitCommitHorizontal, RotateCcw, Zap, } from 'lucide-react';
5
5
  import { InlineAttachments } from '../../common/inline-attachments/index.js';
6
6
  import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
7
7
  import { cn } from '../../../lib/utils.js';
@@ -14,7 +14,7 @@ import { getAgentTypeIcon, agentTypeLabels, } from '../../common/feature-node/ag
14
14
  import { formatDuration } from '../../../lib/format-duration.js';
15
15
  export function OverviewTab({ data }) {
16
16
  const isCompleted = data.lifecycle === 'maintain';
17
- return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-status", className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: lifecycleDisplayLabels[data.lifecycle] }), _jsx(FeatureStateBadge, { data: data }), !isCompleted && data.progress > 0 ? (_jsxs("div", { "data-testid": "feature-drawer-progress", className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-xs", children: [_jsx("span", { children: "Progress" }), _jsxs("span", { children: [data.progress, "%"] })] }), _jsx("div", { className: "bg-muted h-2 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', featureNodeStateConfig[data.state].progressClass), style: { width: `${data.progress}%` } }) })] })) : null, isCompleted && data.pr ? _jsx(FeaturePrInfo, { pr: data.pr }) : null] }), _jsx(FeatureInfo, { data: data }), !isCompleted && data.pr ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx("div", { className: "p-4", children: _jsx(FeaturePrInfo, { pr: data.pr }) })] })) : null, _jsx(FeatureDetails, { data: data })] }));
17
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-status", className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: lifecycleDisplayLabels[data.lifecycle] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FeatureStateBadge, { data: data }), data.state === 'error' && data.onRetry ? (_jsxs("button", { "data-testid": "feature-drawer-retry-button", onClick: () => data.onRetry(data.featureId), className: "flex shrink-0 cursor-pointer items-center gap-1.5 rounded-full bg-red-50 px-3 py-1.5 text-sm font-medium text-red-700 transition-colors hover:bg-red-100", children: [_jsx(RotateCcw, { className: "h-3.5 w-3.5" }), "Retry"] })) : null] }), !isCompleted && data.progress > 0 ? (_jsxs("div", { "data-testid": "feature-drawer-progress", className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-xs", children: [_jsx("span", { children: "Progress" }), _jsxs("span", { children: [data.progress, "%"] })] }), _jsx("div", { className: "bg-muted h-2 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', featureNodeStateConfig[data.state].progressClass), style: { width: `${data.progress}%` } }) })] })) : null, isCompleted && data.pr ? _jsx(FeaturePrInfo, { pr: data.pr }) : null] }), _jsx(FeatureInfo, { data: data }), !isCompleted && data.pr ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx("div", { className: "p-4", children: _jsx(FeaturePrInfo, { pr: data.pr }) })] })) : null, _jsx(FeatureDetails, { data: data })] }));
18
18
  }
19
19
  function FeatureStateBadge({ data }) {
20
20
  const config = featureNodeStateConfig[data.state];
@@ -15,6 +15,8 @@ export declare const DoneWithMergedPr: Story;
15
15
  export declare const Blocked: Story;
16
16
  /** Error state showing error message detail. */
17
17
  export declare const Error: Story;
18
+ /** Error state with retry button visible. */
19
+ export declare const ErrorWithRetry: Story;
18
20
  /** Minimal data — only required fields, no optional details or PR. */
19
21
  export declare const Minimal: Story;
20
22
  /** Fast mode feature — shows lightning icon in details section. */
@@ -1 +1 @@
1
- {"version":3,"file":"overview-tab.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAclC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,WAAW,CAAC,CAAC;AA0G1C,uEAAuE;AACvE,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,IAAI,EAAE,KAElB,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,UAAU,EAAE,KAExB,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAwBF,mEAAmE;AACnE,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC;AAMF,0BAA0B;AAC1B,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC;AAEF,sBAAsB;AACtB,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,oBAAoB;AACpB,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,sBAAsB;AACtB,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,oBAAoB,EAAE,KAclC,CAAC;AAqBF,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAE,KAEpC,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,eAAe,EAAE,KAgB7B,CAAC"}
1
+ {"version":3,"file":"overview-tab.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAclC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,WAAW,CAAC,CAAC;AA0G1C,uEAAuE;AACvE,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,IAAI,EAAE,KAElB,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,UAAU,EAAE,KAExB,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC;AAOF,6CAA6C;AAC7C,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAwBF,mEAAmE;AACnE,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC;AAMF,0BAA0B;AAC1B,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC;AAEF,sBAAsB;AACtB,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,oBAAoB;AACpB,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,sBAAsB;AACtB,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,oBAAoB,EAAE,KAclC,CAAC;AAqBF,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAE,KAEpC,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,eAAe,EAAE,KAgB7B,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { fn } from '@storybook/test';
2
3
  import { PrStatus, CiStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
3
4
  import { OverviewTab } from './overview-tab.js';
4
5
  const meta = {
@@ -132,6 +133,14 @@ export const Blocked = {
132
133
  export const Error = {
133
134
  args: { data: errorData },
134
135
  };
136
+ const errorWithRetryData = {
137
+ ...errorData,
138
+ onRetry: fn(),
139
+ };
140
+ /** Error state with retry button visible. */
141
+ export const ErrorWithRetry = {
142
+ args: { data: errorWithRetryData },
143
+ };
135
144
  /** Minimal data — only required fields, no optional details or PR. */
136
145
  export const Minimal = {
137
146
  args: { data: minimalData },
@@ -13,12 +13,14 @@ import type { FeatureNodeState, FeatureLifecyclePhase } from './feature-node-sta
13
13
  * Priority (mirrors CLI formatStatus):
14
14
  * 1. Agent waiting_approval → action-required
15
15
  * 2. Agent failed → error
16
- * 3. Agent interrupted/cancelled → blocked
16
+ * 3. Agent interrupted/cancelled → error
17
17
  * 4. Agent completed + Maintain lifecycle → done
18
18
  * 5. Agent running/pending → running
19
19
  * 6. No agent run → fall back to plan tasks / lifecycle
20
20
  */
21
- export declare function deriveNodeState(feature: Feature, agentRun?: AgentRun | null): FeatureNodeState;
21
+ export declare function deriveNodeState(feature: Feature, agentRun?: AgentRun | null, options?: {
22
+ isPidAlive?: boolean;
23
+ }): FeatureNodeState;
22
24
  /**
23
25
  * Derives progress percentage from Feature.plan.tasks.
24
26
  *
@@ -1 +1 @@
1
- {"version":3,"file":"derive-feature-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/derive-feature-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAIL,qBAAqB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAE3F;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,gBAAgB,CAiD9F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAYvD;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,qBAAqB,GAAG,gBAAgB,CAoBtF;AAED,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAOlE,CAAC;AAcF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,GAAG,qBAAqB,CAQ7F;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC7C,SAAS,EAAE,qBAAqB,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,qBAAqB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAC7F,cAAc,EAAE,CAmBlB;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,qBAAqB,GAAG,SAAS,CAGnC"}
1
+ {"version":3,"file":"derive-feature-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/derive-feature-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAIL,qBAAqB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAE3F;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,EAC1B,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,gBAAgB,CAkDlB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAYvD;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,qBAAqB,GAAG,gBAAgB,CAoBtF;AAED,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAOlE,CAAC;AAcF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,GAAG,qBAAqB,CAQ7F;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC7C,SAAS,EAAE,qBAAqB,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,qBAAqB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAC7F,cAAc,EAAE,CAmBlB;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,qBAAqB,GAAG,SAAS,CAGnC"}
@@ -11,12 +11,12 @@ import { SdlcLifecycle, AgentRunStatus, TaskState, NotificationEventType, } from
11
11
  * Priority (mirrors CLI formatStatus):
12
12
  * 1. Agent waiting_approval → action-required
13
13
  * 2. Agent failed → error
14
- * 3. Agent interrupted/cancelled → blocked
14
+ * 3. Agent interrupted/cancelled → error
15
15
  * 4. Agent completed + Maintain lifecycle → done
16
16
  * 5. Agent running/pending → running
17
17
  * 6. No agent run → fall back to plan tasks / lifecycle
18
18
  */
19
- export function deriveNodeState(feature, agentRun) {
19
+ export function deriveNodeState(feature, agentRun, options) {
20
20
  // Deleting lifecycle takes top priority — feature is being removed
21
21
  if (feature.lifecycle === SdlcLifecycle.Deleting) {
22
22
  return 'deleting';
@@ -33,11 +33,13 @@ export function deriveNodeState(feature, agentRun) {
33
33
  return 'error';
34
34
  case AgentRunStatus.interrupted:
35
35
  case AgentRunStatus.cancelled:
36
- return 'blocked';
36
+ return 'error';
37
37
  case AgentRunStatus.completed:
38
38
  return 'done';
39
39
  case AgentRunStatus.running:
40
40
  case AgentRunStatus.pending:
41
+ if (options?.isPidAlive === false)
42
+ return 'error';
41
43
  return 'running';
42
44
  }
43
45
  }
@@ -67,6 +67,7 @@ export interface FeatureNodeData {
67
67
  onAction?: () => void;
68
68
  onSettings?: () => void;
69
69
  onDelete?: (featureId: string, cleanup?: boolean) => void;
70
+ onRetry?: (featureId: string) => void;
70
71
  showHandles?: boolean;
71
72
  }
72
73
  export type FeatureNodeType = Node<FeatureNodeData, 'featureNode'>;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA0DnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAoSA"}
1
+ {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA0DnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAqTA"}
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState } from 'react';
4
4
  import { Handle, Position } from '@xyflow/react';
5
- import { Plus, FileText, Wrench, GitMerge, Trash2, Zap, Loader2, Globe, } from 'lucide-react';
5
+ import { Plus, FileText, Wrench, GitMerge, Trash2, Zap, Loader2, Globe, RotateCcw, } from 'lucide-react';
6
6
  import { cn } from '../../../lib/utils.js';
7
7
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
8
8
  import { DeleteFeatureDialog } from '../../common/delete-feature-dialog/index.js';
@@ -82,13 +82,16 @@ export function FeatureNode({ data, selected, }) {
82
82
  ? 'bg-blue-50 text-blue-700'
83
83
  : 'bg-green-50 text-green-700'), children: [data.deployment.status === DeploymentState.Booting ? (_jsx(Loader2, { className: "h-3 w-3 animate-spin" })) : (_jsx(Globe, { className: "h-3 w-3" })), _jsx("span", { className: "truncate", children: data.deployment.status === DeploymentState.Booting
84
84
  ? 'Deploying...'
85
- : (data.deployment.url ?? 'Live') })] })) : null, _jsx("div", { className: "mt-auto pt-2", children: data.state === 'deleting' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Trash2, { className: "h-3.5 w-3.5 shrink-0 text-gray-400" }), _jsx("span", { className: "text-muted-foreground", children: "Deleting\u2026" })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'creating' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _jsxs("span", { children: [data.progress, "%"] }) }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { "data-testid": "feature-node-badge", className: (() => {
86
- const override = getActionRequiredBadgeClasses(data);
87
- return cn('mt-1.5 flex items-center justify-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium', override?.badgeBgClass ?? config.badgeBgClass, override?.badgeClass ?? config.badgeClass);
88
- })(), children: [(() => {
89
- const BadgeIcon = getBadgeIcon(data);
90
- return _jsx(BadgeIcon, { className: "h-3.5 w-3.5 shrink-0" });
91
- })(), _jsx("span", { className: "truncate", children: getBadgeText(data) })] })] })) })] }), data.onAction && data.state !== 'deleting' ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
85
+ : (data.deployment.url ?? 'Live') })] })) : null, _jsx("div", { className: "mt-auto pt-2", children: data.state === 'deleting' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Trash2, { className: "h-3.5 w-3.5 shrink-0 text-gray-400" }), _jsx("span", { className: "text-muted-foreground", children: "Deleting\u2026" })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'creating' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _jsxs("span", { children: [data.progress, "%"] }) }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { className: "mt-1.5 flex items-center gap-1.5", children: [_jsxs("div", { "data-testid": "feature-node-badge", className: (() => {
86
+ const override = getActionRequiredBadgeClasses(data);
87
+ return cn('flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium', override?.badgeBgClass ?? config.badgeBgClass, override?.badgeClass ?? config.badgeClass);
88
+ })(), children: [(() => {
89
+ const BadgeIcon = getBadgeIcon(data);
90
+ return _jsx(BadgeIcon, { className: "h-3.5 w-3.5 shrink-0" });
91
+ })(), _jsx("span", { className: "truncate", children: getBadgeText(data) })] }), data.state === 'error' && data.onRetry ? (_jsxs("button", { type: "button", "aria-label": "Retry feature", "data-testid": "feature-node-retry-button", onClick: (e) => {
92
+ e.stopPropagation();
93
+ data.onRetry(data.featureId);
94
+ }, className: "nodrag flex shrink-0 cursor-pointer items-center gap-1 rounded-full bg-red-100 px-2.5 py-1 text-[11px] font-medium text-red-700 transition-colors hover:bg-red-200", children: [_jsx(RotateCcw, { className: "h-3 w-3" }), "Retry"] })) : null] })] })) })] }), data.onAction && data.state !== 'deleting' ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
92
95
  e.stopPropagation();
93
96
  data.onAction?.();
94
97
  }, className: "nodrag absolute top-1/2 left-1/2 flex h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white opacity-0 shadow-md transition-opacity group-hover:opacity-100 hover:bg-blue-600", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) })) : data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: false, className: "opacity-0!" })) : null] }));
@@ -15,6 +15,7 @@ export declare const Creating: Story;
15
15
  export declare const MergeReviewActionRequired: Story;
16
16
  export declare const Deleting: Story;
17
17
  export declare const ErrorWithMessage: Story;
18
+ export declare const ErrorWithRetry: Story;
18
19
  export declare const WithDeleteButton: Story;
19
20
  export declare const FastMode: Story;
20
21
  export declare const FastModeDone: Story;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAc/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAgHF,eAAO,MAAM,SAAS,EAAE,KAQvB,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,KAqB3B,CAAC;AA2EF,eAAO,MAAM,aAAa,EAAE,KAQ3B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAW7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,KAUvC,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAatB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAc1B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAY/B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAa7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAYlC,CAAC;AA4EF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC"}
1
+ {"version":3,"file":"feature-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAc/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAgHF,eAAO,MAAM,SAAS,EAAE,KAQvB,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,KAqB3B,CAAC;AA2EF,eAAO,MAAM,aAAa,EAAE,KAQ3B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAW7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,KAUvC,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAY5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAatB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAc1B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAY/B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAa7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAYlC,CAAC;AA4EF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC"}
@@ -319,6 +319,19 @@ export const ErrorWithMessage = {
319
319
  },
320
320
  render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
321
321
  };
322
+ export const ErrorWithRetry = {
323
+ args: {
324
+ name: 'Email Service',
325
+ description: 'Transactional email with SendGrid',
326
+ featureId: '#f5',
327
+ lifecycle: 'review',
328
+ state: 'error',
329
+ progress: 30,
330
+ errorMessage: 'Build failed: type mismatch',
331
+ onRetry: fn(),
332
+ },
333
+ render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
334
+ };
322
335
  export const WithDeleteButton = {
323
336
  args: {
324
337
  name: 'Auth Module',
@@ -1 +1 @@
1
- {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAsB5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAoTzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAsB5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAuTzF"}
@@ -26,7 +26,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
26
26
  const { fitView } = useReactFlow();
27
27
  const drawerTimerRef = useRef(null);
28
28
  const { defaultViewport, onMoveEnd: handleViewportChange, resetViewport, } = useViewportPersistence();
29
- const { nodes, edges, onNodesChange, handleConnect, handleAddRepository, handleDeleteFeature, handleDeleteRepository, createFeatureNode, setCallbacks, } = useControlCenterState(initialNodes, initialEdges);
29
+ const { nodes, edges, onNodesChange, handleConnect, handleAddRepository, handleDeleteFeature, handleRetryFeature, handleDeleteRepository, createFeatureNode, setCallbacks, } = useControlCenterState(initialNodes, initialEdges);
30
30
  // Publish sidebar features to context whenever feature node data changes
31
31
  const { setFeatures: setSidebarFeatures } = useSidebarFeaturesContext();
32
32
  const featureNodes = useMemo(() => nodes.filter((n) => n.type === 'featureNode'), [nodes]);
@@ -179,6 +179,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
179
179
  setCallbacks({
180
180
  onNodeAction: handleAddFeatureToFeature,
181
181
  onFeatureDelete: handleDeleteFeature,
182
+ onRetryFeature: handleRetryFeature,
182
183
  onRepositoryAdd: handleAddFeatureToRepo,
183
184
  onRepositoryClick: handleRepositoryClick,
184
185
  onRepositoryDelete: handleDeleteRepository,
@@ -187,6 +188,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
187
188
  setCallbacks,
188
189
  handleAddFeatureToFeature,
189
190
  handleDeleteFeature,
191
+ handleRetryFeature,
190
192
  handleAddFeatureToRepo,
191
193
  handleRepositoryClick,
192
194
  handleDeleteRepository,
@@ -15,6 +15,7 @@ export interface ControlCenterState {
15
15
  };
16
16
  handleLayout: (direction: LayoutDirection) => void;
17
17
  handleDeleteFeature: (featureId: string, cleanup?: boolean) => void;
18
+ handleRetryFeature: (featureId: string) => void;
18
19
  handleDeleteRepository: (repositoryId: string) => Promise<void>;
19
20
  createFeatureNode: (sourceNodeId: string | null, dataOverride?: Partial<FeatureNodeData>, edgeType?: string) => string;
20
21
  /** Stable lookup: repositoryPath for a feature node. */
@@ -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;AAcjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,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;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpE,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;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAiapB"}
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;AAcjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,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;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpE,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,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;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA6bpB"}
@@ -4,10 +4,10 @@ import { useRouter } from 'next/navigation';
4
4
  import { toast } from 'sonner';
5
5
  import { layoutWithDagre, CANVAS_LAYOUT_DEFAULTS, } from '../../../lib/layout-with-dagre.js';
6
6
  import { deleteFeature } from '../../../app/actions/delete-feature.js';
7
+ import { resumeFeature } from '../../../app/actions/resume-feature.js';
7
8
  import { addRepository } from '../../../app/actions/add-repository.js';
8
9
  import { deleteRepository } from '../../../app/actions/delete-repository.js';
9
10
  import { getFeatureMetadata } from '../../../app/actions/get-feature-metadata.js';
10
- import { fetchGraphData } from '../../../app/actions/get-graph-data.js';
11
11
  import { useAgentEventsContext } from '../../../hooks/agent-events-provider.js';
12
12
  import { useSoundAction } from '../../../hooks/use-sound-action.js';
13
13
  import { createLogger } from '../../../lib/logger.js';
@@ -157,7 +157,12 @@ export function useControlCenterState(initialNodes, initialEdges) {
157
157
  log.debug(`polling enabled (${POLL_INTERVAL_MS}ms interval)`);
158
158
  const timer = setInterval(async () => {
159
159
  try {
160
- const { nodes: freshNodes, edges: freshEdges } = await fetchGraphData();
160
+ // Use a plain fetch instead of a server action so the poll
161
+ // doesn't trigger the Next.js "Rendering…" indicator.
162
+ const res = await fetch('/api/graph-data');
163
+ if (!res.ok)
164
+ throw new Error(`status ${res.status}`);
165
+ const { nodes: freshNodes, edges: freshEdges } = await res.json();
161
166
  reconcile(freshNodes, freshEdges);
162
167
  }
163
168
  catch {
@@ -197,6 +202,25 @@ export function useControlCenterState(initialNodes, initialEdges) {
197
202
  addPendingFeature(id, newFeatureData, parentNodeId);
198
203
  return id;
199
204
  }, [addPendingFeature]);
205
+ const handleRetryFeature = useCallback((featureId) => {
206
+ const nodeId = `feat-${featureId}`;
207
+ // Optimistic: switch to running state
208
+ updateFeature(nodeId, { state: 'running' });
209
+ resumeFeature(featureId)
210
+ .then((result) => {
211
+ if (result.error) {
212
+ updateFeature(nodeId, { state: 'error' });
213
+ toast.error(result.error);
214
+ }
215
+ else {
216
+ toast.success('Feature resumed');
217
+ }
218
+ })
219
+ .catch(() => {
220
+ updateFeature(nodeId, { state: 'error' });
221
+ toast.error('Failed to resume feature');
222
+ });
223
+ }, [updateFeature]);
200
224
  const handleDeleteFeature = useCallback((featureId, cleanup) => {
201
225
  const nodeId = `feat-${featureId}`;
202
226
  // Collect all descendant feature node IDs (children, grandchildren, etc.)
@@ -353,6 +377,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
353
377
  handleAddRepository,
354
378
  handleLayout,
355
379
  handleDeleteFeature,
380
+ handleRetryFeature,
356
381
  handleDeleteRepository,
357
382
  createFeatureNode,
358
383
  getFeatureRepositoryPath,