@shepai/cli 1.47.0 → 1.49.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 (235) hide show
  1. package/apis/json-schema/ApprovalGateDefaults.yaml +26 -0
  2. package/apis/json-schema/Feature.yaml +3 -0
  3. package/apis/json-schema/Repository.yaml +16 -0
  4. package/apis/json-schema/Settings.yaml +5 -0
  5. package/apis/json-schema/WorkflowConfig.yaml +4 -20
  6. package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts +17 -0
  7. package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts.map +1 -1
  8. package/dist/packages/core/src/application/ports/output/services/worktree-service.interface.d.ts +11 -1
  9. package/dist/packages/core/src/application/ports/output/services/worktree-service.interface.d.ts.map +1 -1
  10. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts +3 -1
  11. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
  12. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +11 -4
  13. package/dist/packages/core/src/application/use-cases/features/get-plan-artifact.use-case.d.ts +19 -0
  14. package/dist/packages/core/src/application/use-cases/features/get-plan-artifact.use-case.d.ts.map +1 -0
  15. package/dist/packages/core/src/application/use-cases/features/get-plan-artifact.use-case.js +52 -0
  16. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  17. package/dist/packages/core/src/infrastructure/di/container.js +8 -0
  18. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +1 -1
  19. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  20. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +2 -0
  21. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts +2 -0
  22. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts.map +1 -1
  23. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.js +66 -30
  24. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.d.ts.map +1 -1
  25. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.js +15 -7
  26. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts +2 -0
  27. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
  28. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +41 -0
  29. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts +2 -1
  30. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
  31. package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +29 -4
  32. package/dist/src/presentation/cli/ui/messages.d.ts.map +1 -1
  33. package/dist/src/presentation/cli/ui/messages.js +1 -7
  34. package/dist/src/presentation/web/app/actions/approve-feature.d.ts +2 -1
  35. package/dist/src/presentation/web/app/actions/approve-feature.d.ts.map +1 -1
  36. package/dist/src/presentation/web/app/actions/approve-feature.js +2 -2
  37. package/dist/src/presentation/web/app/actions/get-merge-review-data.d.ts +7 -0
  38. package/dist/src/presentation/web/app/actions/get-merge-review-data.d.ts.map +1 -0
  39. package/dist/src/presentation/web/app/actions/get-merge-review-data.js +49 -0
  40. package/dist/src/presentation/web/app/actions/reject-feature.d.ts +7 -0
  41. package/dist/src/presentation/web/app/actions/reject-feature.d.ts.map +1 -0
  42. package/dist/src/presentation/web/app/actions/reject-feature.js +34 -0
  43. package/dist/src/presentation/web/app/page.d.ts.map +1 -1
  44. package/dist/src/presentation/web/app/page.js +1 -0
  45. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.d.ts +24 -0
  46. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.d.ts.map +1 -0
  47. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.js +1 -0
  48. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts +3 -0
  49. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts.map +1 -0
  50. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.js +12 -0
  51. package/dist/src/presentation/web/components/common/drawer-action-bar/index.d.ts +3 -0
  52. package/dist/src/presentation/web/components/common/drawer-action-bar/index.d.ts.map +1 -0
  53. package/dist/src/presentation/web/components/common/drawer-action-bar/index.js +1 -0
  54. package/dist/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input-config.d.ts +11 -0
  55. package/dist/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input-config.d.ts.map +1 -0
  56. package/dist/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input-config.js +1 -0
  57. package/dist/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input.d.ts +3 -0
  58. package/dist/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input.d.ts.map +1 -0
  59. package/dist/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input.js +18 -0
  60. package/dist/src/presentation/web/components/common/drawer-revision-input/index.d.ts +3 -0
  61. package/dist/src/presentation/web/components/common/drawer-revision-input/index.d.ts.map +1 -0
  62. package/dist/src/presentation/web/components/common/drawer-revision-input/index.js +1 -0
  63. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  64. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +8 -1
  65. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +1 -0
  66. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
  67. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +22 -1
  68. package/dist/src/presentation/web/components/common/merge-review/index.d.ts +4 -0
  69. package/dist/src/presentation/web/components/common/merge-review/index.d.ts.map +1 -0
  70. package/dist/src/presentation/web/components/common/merge-review/index.js +2 -0
  71. package/dist/src/presentation/web/components/common/merge-review/merge-review-config.d.ts +77 -0
  72. package/dist/src/presentation/web/components/common/merge-review/merge-review-config.d.ts.map +1 -0
  73. package/dist/src/presentation/web/components/common/merge-review/merge-review-config.js +1 -0
  74. package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.d.ts +3 -0
  75. package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.d.ts.map +1 -0
  76. package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.js +7 -0
  77. package/dist/src/presentation/web/components/common/merge-review/merge-review.d.ts +3 -0
  78. package/dist/src/presentation/web/components/common/merge-review/merge-review.d.ts.map +1 -0
  79. package/dist/src/presentation/web/components/common/merge-review/merge-review.js +25 -0
  80. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts +32 -0
  81. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts.map +1 -0
  82. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.js +165 -0
  83. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire-config.d.ts +4 -2
  84. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire-config.d.ts.map +1 -1
  85. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.d.ts +1 -1
  86. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.d.ts.map +1 -1
  87. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.js +6 -15
  88. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts +4 -0
  89. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts.map +1 -1
  90. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.js +24 -3
  91. package/dist/src/presentation/web/components/common/reject-feedback-dialog/index.d.ts +3 -0
  92. package/dist/src/presentation/web/components/common/reject-feedback-dialog/index.d.ts.map +1 -0
  93. package/dist/src/presentation/web/components/common/reject-feedback-dialog/index.js +1 -0
  94. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog-config.d.ts +9 -0
  95. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog-config.d.ts.map +1 -0
  96. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog-config.js +1 -0
  97. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.d.ts +3 -0
  98. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.d.ts.map +1 -0
  99. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.js +19 -0
  100. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.stories.d.ts +14 -0
  101. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.stories.d.ts.map +1 -0
  102. package/dist/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.stories.js +46 -0
  103. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review-config.d.ts +4 -2
  104. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review-config.d.ts.map +1 -1
  105. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.d.ts +1 -1
  106. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.d.ts.map +1 -1
  107. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.js +4 -14
  108. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.d.ts +4 -0
  109. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.d.ts.map +1 -1
  110. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.js +24 -6
  111. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  112. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +88 -19
  113. package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts.map +1 -1
  114. package/dist/src/presentation/web/components/features/control-center/control-center.stories.js +15 -0
  115. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  116. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +37 -15
  117. package/dist/tsconfig.build.tsbuildinfo +1 -1
  118. package/package.json +1 -1
  119. package/web/.next/BUILD_ID +1 -1
  120. package/web/.next/build-manifest.json +2 -2
  121. package/web/.next/cache/.previewinfo +1 -1
  122. package/web/.next/cache/.rscinfo +1 -1
  123. package/web/.next/cache/.tsbuildinfo +1 -1
  124. package/web/.next/cache/config.json +3 -3
  125. package/web/.next/fallback-build-manifest.json +2 -2
  126. package/web/.next/prerender-manifest.json +3 -3
  127. package/web/.next/required-server-files.js +1 -1
  128. package/web/.next/required-server-files.json +1 -1
  129. package/web/.next/server/app/_global-error.html +2 -2
  130. package/web/.next/server/app/_global-error.rsc +1 -1
  131. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  132. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  133. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  134. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  135. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  136. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  137. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  138. package/web/.next/server/app/page/server-reference-manifest.json +54 -24
  139. package/web/.next/server/app/page.js +2 -1
  140. package/web/.next/server/app/page.js.nft.json +1 -1
  141. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  142. package/web/.next/server/app/version/page.js.nft.json +1 -1
  143. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  144. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +4 -4
  145. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  146. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  147. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +9 -0
  148. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js.map +1 -0
  149. package/web/.next/server/chunks/ssr/[root-of-the-server]__d8fbe735._.js +3 -0
  150. package/web/.next/server/chunks/ssr/[root-of-the-server]__d8fbe735._.js.map +1 -0
  151. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/_3ac3601c._.js → server/chunks/ssr/_1fafa9db._.js} +2 -2
  152. package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +1 -0
  153. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  154. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  155. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  156. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  157. package/web/.next/server/pages/500.html +2 -2
  158. package/web/.next/server/server-reference-manifest.js +1 -1
  159. package/web/.next/server/server-reference-manifest.json +55 -25
  160. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  161. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  162. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  163. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  164. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  165. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  166. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  167. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  168. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  170. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  172. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  173. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +54 -24
  174. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js +2 -1
  175. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  176. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  177. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  178. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  179. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +4 -4
  180. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  181. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +9 -0
  182. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__d8fbe735._.js +3 -0
  183. package/web/.next/{server/chunks/ssr/_3ac3601c._.js → standalone/src/presentation/web/.next/server/chunks/ssr/_1fafa9db._.js} +2 -2
  184. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  185. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  186. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  187. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  188. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +55 -25
  189. package/web/.next/standalone/src/presentation/web/app/actions/approve-feature.ts +4 -2
  190. package/web/.next/standalone/src/presentation/web/app/actions/get-merge-review-data.ts +64 -0
  191. package/web/.next/standalone/src/presentation/web/app/actions/reject-feature.ts +52 -0
  192. package/web/.next/standalone/src/presentation/web/app/page.tsx +1 -0
  193. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.ts +24 -0
  194. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.tsx +64 -0
  195. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/index.ts +2 -0
  196. package/web/.next/standalone/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input-config.ts +10 -0
  197. package/web/.next/standalone/src/presentation/web/components/common/drawer-revision-input/drawer-revision-input.tsx +41 -0
  198. package/web/.next/standalone/src/presentation/web/components/common/drawer-revision-input/index.ts +2 -0
  199. package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.stories.tsx +23 -1
  200. package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.tsx +6 -1
  201. package/web/.next/standalone/src/presentation/web/components/common/merge-review/index.ts +7 -0
  202. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review-config.ts +83 -0
  203. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review-drawer.tsx +34 -0
  204. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.stories.tsx +240 -0
  205. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.tsx +223 -0
  206. package/web/.next/standalone/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire-config.ts +4 -2
  207. package/web/.next/standalone/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.tsx +36 -8
  208. package/web/.next/standalone/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.tsx +16 -47
  209. package/web/.next/standalone/src/presentation/web/components/common/reject-feedback-dialog/index.ts +2 -0
  210. package/web/.next/standalone/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog-config.ts +8 -0
  211. package/web/.next/standalone/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.stories.tsx +63 -0
  212. package/web/.next/standalone/src/presentation/web/components/common/reject-feedback-dialog/reject-feedback-dialog.tsx +78 -0
  213. package/web/.next/standalone/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review-config.ts +4 -2
  214. package/web/.next/standalone/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.tsx +27 -6
  215. package/web/.next/standalone/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.tsx +14 -42
  216. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +157 -31
  217. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +15 -0
  218. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +47 -17
  219. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  220. package/web/.next/static/chunks/{9f8e71029fb04681.js → 0da7a9860fa6ae05.js} +2 -2
  221. package/web/.next/static/chunks/191ca1529c9868dd.js +10 -0
  222. package/web/.next/static/chunks/22f30ce91a3c79c1.js +1 -0
  223. package/web/.next/static/chunks/59b8bd47023e0249.css +2 -0
  224. package/web/.next/trace +1 -1
  225. package/web/.next/trace-build +1 -1
  226. package/web/.next/server/chunks/ssr/[root-of-the-server]__d6d75c22._.js +0 -9
  227. package/web/.next/server/chunks/ssr/[root-of-the-server]__d6d75c22._.js.map +0 -1
  228. package/web/.next/server/chunks/ssr/_3ac3601c._.js.map +0 -1
  229. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__d6d75c22._.js +0 -9
  230. package/web/.next/static/chunks/779c99975c85389e.js +0 -10
  231. package/web/.next/static/chunks/83ff3b6bbee54bce.js +0 -1
  232. package/web/.next/static/chunks/b7b7abe79e05b229.css +0 -2
  233. /package/web/.next/static/{wQ3vPV9I8JeacVNyp4HFg → UT60KjU8SOYIjqeqo2WT-}/_buildManifest.js +0 -0
  234. /package/web/.next/static/{wQ3vPV9I8JeacVNyp4HFg → UT60KjU8SOYIjqeqo2WT-}/_clientMiddlewareManifest.json +0 -0
  235. /package/web/.next/static/{wQ3vPV9I8JeacVNyp4HFg → UT60KjU8SOYIjqeqo2WT-}/_ssgManifest.js +0 -0
@@ -0,0 +1,46 @@
1
+ import { fn } from '@storybook/test';
2
+ import { RejectFeedbackDialog } from './reject-feedback-dialog.js';
3
+ const meta = {
4
+ title: 'Composed/RejectFeedbackDialog',
5
+ component: RejectFeedbackDialog,
6
+ tags: ['autodocs'],
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ args: {
11
+ open: true,
12
+ onOpenChange: fn().mockName('onOpenChange'),
13
+ onConfirm: fn().mockName('onConfirm'),
14
+ isSubmitting: false,
15
+ },
16
+ };
17
+ export default meta;
18
+ /** Default — dialog open with empty feedback; confirm button disabled. */
19
+ export const Default = {};
20
+ /** With feedback entered — confirm button enabled. */
21
+ export const WithFeedback = {
22
+ play: async ({ canvasElement }) => {
23
+ // Type feedback into the textarea to show enabled state
24
+ const textarea = canvasElement.ownerDocument.querySelector('textarea[aria-label="Rejection feedback"]');
25
+ if (textarea) {
26
+ // Simulate React-compatible input
27
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
28
+ nativeInputValueSetter?.call(textarea, 'The requirements are missing error handling for edge cases. Please add validation for empty inputs.');
29
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
30
+ textarea.dispatchEvent(new Event('change', { bubbles: true }));
31
+ }
32
+ },
33
+ };
34
+ /** Submitting state — spinner shown, buttons disabled. */
35
+ export const Submitting = {
36
+ args: {
37
+ isSubmitting: true,
38
+ },
39
+ };
40
+ /** Custom title and description for tech decisions context. */
41
+ export const CustomTitle = {
42
+ args: {
43
+ title: 'Reject Plan',
44
+ description: 'Provide feedback on the technical plan for the agent to revise. Be specific about which decisions need changes.',
45
+ },
46
+ };
@@ -9,12 +9,14 @@ export interface TechDecisionsReviewData {
9
9
  export interface TechDecisionsReviewProps {
10
10
  /** Tech decisions data from the research artifact */
11
11
  data: TechDecisionsReviewData;
12
- /** Chat refinement callback — sends user feedback to the agent */
13
- onRefine: (text: string) => void;
14
12
  /** Approve plan callback */
15
13
  onApprove: () => void;
14
+ /** Reject plan callback — opens feedback dialog when provided; also used for inline text rejection */
15
+ onReject?: (feedback: string) => void;
16
16
  /** Controls disabled state during loading */
17
17
  isProcessing?: boolean;
18
+ /** Whether a reject operation is in flight */
19
+ isRejecting?: boolean;
18
20
  }
19
21
  export interface TechDecisionsDrawerProps extends TechDecisionsReviewProps {
20
22
  /** Whether the drawer is open */
@@ -1 +1 @@
1
- {"version":3,"file":"tech-decisions-review-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/tech-decisions-review/tech-decisions-review-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEzE,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,qDAAqD;IACrD,IAAI,EAAE,uBAAuB,CAAC;IAC9B,kEAAkE;IAClE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,4BAA4B;IAC5B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,iCAAiC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,wCAAwC;IACxC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB"}
1
+ {"version":3,"file":"tech-decisions-review-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/tech-decisions-review/tech-decisions-review-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEzE,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,qDAAqD;IACrD,IAAI,EAAE,uBAAuB,CAAC;IAC9B,4BAA4B;IAC5B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,sGAAsG;IACtG,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,6CAA6C;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,iCAAiC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,wCAAwC;IACxC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB"}
@@ -1,3 +1,3 @@
1
1
  import type { TechDecisionsReviewProps } from './tech-decisions-review-config.js';
2
- export declare function TechDecisionsReview({ data, onRefine, onApprove, isProcessing, }: TechDecisionsReviewProps): import("react/jsx-runtime").JSX.Element | null;
2
+ export declare function TechDecisionsReview({ data, onApprove, onReject, isProcessing, isRejecting, }: TechDecisionsReviewProps): import("react/jsx-runtime").JSX.Element | null;
3
3
  //# sourceMappingURL=tech-decisions-review.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tech-decisions-review.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,wBAAwB,EAAgB,MAAM,gCAAgC,CAAC;AAoG7F,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,YAAoB,GACrB,EAAE,wBAAwB,kDAyE1B"}
1
+ {"version":3,"file":"tech-decisions-review.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,wBAAwB,EAAgB,MAAM,gCAAgC,CAAC;AAoG7F,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,YAAoB,EACpB,WAAmB,GACpB,EAAE,wBAAwB,kDA6C1B"}
@@ -2,10 +2,9 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from 'react';
4
4
  import Markdown from 'react-markdown';
5
- import { Check, ChevronRight, GitCompareArrows, Layers, Send } from 'lucide-react';
5
+ import { Check, ChevronRight, GitCompareArrows, Layers } from 'lucide-react';
6
6
  import { Badge } from '../../ui/badge.js';
7
- import { Button } from '../../ui/button.js';
8
- import { Input } from '../../ui/input.js';
7
+ import { DrawerActionBar } from '../../common/drawer-action-bar/index.js';
9
8
  const markdownComponents = {
10
9
  p: ({ children }) => (_jsx("p", { className: "text-muted-foreground mb-2 text-xs leading-relaxed last:mb-0", children: children })),
11
10
  strong: ({ children }) => _jsx("strong", { className: "text-foreground font-semibold", children: children }),
@@ -22,18 +21,9 @@ function DecisionCard({ decision, index }) {
22
21
  return (_jsxs("div", { className: "border-border rounded-lg border", children: [_jsxs("div", { className: "space-y-3 px-4 py-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "bg-primary text-primary-foreground flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-bold", children: index + 1 }), _jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-foreground text-sm leading-tight font-semibold", children: decision.title }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-xs", children: decision.chosen })] })] }), 'decisionName' in decision &&
23
22
  decision.decisionName ? (_jsx(Badge, { variant: "secondary", className: "bg-primary/10 text-primary shrink-0", children: decision.decisionName })) : null] }), decision.rationale ? (_jsx(Markdown, { components: markdownComponents, children: decision.rationale })) : null] }), decision.rejected.length > 0 ? (_jsxs("div", { className: "border-border border-t", children: [_jsxs("button", { type: "button", onClick: () => setAlternativesOpen((prev) => !prev), className: "text-muted-foreground hover:bg-muted/50 flex w-full items-center gap-1.5 px-4 py-3 text-xs font-medium transition-colors", children: [_jsx(ChevronRight, { className: `h-3.5 w-3.5 transition-transform ${alternativesOpen ? 'rotate-90' : ''}` }), _jsx(Layers, { className: "h-3.5 w-3.5" }), "Other Options Considered (", decision.rejected.length, ")"] }), alternativesOpen ? (_jsx("div", { className: "space-y-1.5 px-4 pb-3", children: decision.rejected.map((alt) => (_jsx("div", { className: "bg-primary/5 rounded-md px-3 py-2", children: _jsx("span", { className: "text-foreground text-xs", children: alt }) }, alt))) })) : null] })) : null] }));
24
23
  }
25
- export function TechDecisionsReview({ data, onRefine, onApprove, isProcessing = false, }) {
24
+ export function TechDecisionsReview({ data, onApprove, onReject, isProcessing = false, isRejecting = false, }) {
26
25
  const { summary, decisions } = data;
27
- const [chatInput, setChatInput] = useState('');
28
- function handleSubmit(e) {
29
- e.preventDefault();
30
- const text = chatInput.trim();
31
- if (!text)
32
- return;
33
- onRefine(text);
34
- setChatInput('');
35
- }
36
26
  if (decisions.length === 0)
37
27
  return null;
38
- return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1.5 h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-bold", children: "Technical Implementation Plan Review" }), summary ? (_jsx("p", { className: "text-muted-foreground mt-1 text-xs leading-relaxed", children: summary })) : null] })] }), _jsxs("div", { className: "flex items-center gap-2 pt-1", children: [_jsx(GitCompareArrows, { className: "text-primary h-4 w-4" }), _jsx("h3", { className: "text-foreground text-sm font-bold", children: "Technical Decisions" })] }), decisions.map((decision, i) => (_jsx(DecisionCard, { decision: decision, index: i }, decision.title)))] }), _jsxs("div", { className: "border-border shrink-0 border-t", children: [_jsxs("form", { onSubmit: handleSubmit, className: "flex items-center gap-2 p-4 pb-2", children: [_jsx(Input, { type: "text", placeholder: "Ask AI to revise the plan...", "aria-label": "Ask AI to revise the plan", disabled: isProcessing, value: chatInput, onChange: (e) => setChatInput(e.target.value), className: "flex-1" }), _jsx(Button, { type: "submit", variant: "secondary", size: "icon", "aria-label": "Send", disabled: isProcessing, children: _jsx(Send, {}) })] }), _jsx("div", { className: "px-4 pt-2 pb-4", children: _jsxs(Button, { type: "button", className: "w-full", disabled: isProcessing, onClick: onApprove, children: [_jsx(Check, { className: "mr-1.5 h-4 w-4" }), "Approve Plan"] }) })] })] }));
28
+ return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1.5 h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-bold", children: "Technical Implementation Plan Review" }), summary ? (_jsx("p", { className: "text-muted-foreground mt-1 text-xs leading-relaxed", children: summary })) : null] })] }), _jsxs("div", { className: "flex items-center gap-2 pt-1", children: [_jsx(GitCompareArrows, { className: "text-primary h-4 w-4" }), _jsx("h3", { className: "text-foreground text-sm font-bold", children: "Technical Decisions" })] }), decisions.map((decision, i) => (_jsx(DecisionCard, { decision: decision, index: i }, decision.title)))] }), _jsx(DrawerActionBar, { onReject: onReject, onApprove: onApprove, approveLabel: "Approve Plan", approveIcon: _jsx(Check, { className: "mr-1.5 h-4 w-4" }), revisionPlaceholder: "Ask AI to revise the plan...", rejectDialogTitle: "Reject Plan", isProcessing: isProcessing, isRejecting: isRejecting })] }));
39
29
  }
@@ -19,4 +19,8 @@ export declare const InDrawer: DrawerStory;
19
19
  export declare const WithDeleteButton: DrawerStory;
20
20
  /** Drawer with delete in progress (button disabled). */
21
21
  export declare const DeletingState: DrawerStory;
22
+ /** With reject button visible next to approve. */
23
+ export declare const WithRejectButton: Story;
24
+ /** Rejecting state — reject button disabled while reject action is in flight. */
25
+ export declare const RejectingState: Story;
22
26
  //# sourceMappingURL=tech-decisions-review.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tech-decisions-review.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AA0C9D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAc1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAElD,oDAAoD;AACpD,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,uBAAuB;AACvB,eAAO,MAAM,cAAc,EAAE,KAM5B,CAAC;AAEF,8BAA8B;AAC9B,eAAO,MAAM,cAAc,EAAE,KAM5B,CAAC;AAIF,KAAK,WAAW,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA8BxD,uCAAuC;AACvC,eAAO,MAAM,QAAQ,EAAE,WAWtB,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,EAAE,WAY9B,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,WAa3B,CAAC"}
1
+ {"version":3,"file":"tech-decisions-review.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AA0C9D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAc1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAElD,oDAAoD;AACpD,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,uBAAuB;AACvB,eAAO,MAAM,cAAc,EAAE,KAM5B,CAAC;AAEF,8BAA8B;AAC9B,eAAO,MAAM,cAAc,EAAE,KAM5B,CAAC;AAIF,KAAK,WAAW,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA8BxD,uCAAuC;AACvC,eAAO,MAAM,QAAQ,EAAE,WAWtB,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,EAAE,WAY9B,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,WAa3B,CAAC;AAIF,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,EAAE,KAM9B,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,cAAc,EAAE,KAO5B,CAAC"}
@@ -51,7 +51,7 @@ export default meta;
51
51
  export const Default = {
52
52
  args: {
53
53
  data: mockData,
54
- onRefine: fn().mockName('onRefine'),
54
+ onReject: fn().mockName('onReject'),
55
55
  onApprove: fn().mockName('onApprove'),
56
56
  },
57
57
  };
@@ -67,7 +67,7 @@ export const Processing = {
67
67
  export const SingleDecision = {
68
68
  args: {
69
69
  data: { ...mockData, decisions: [mockData.decisions[0]] },
70
- onRefine: fn().mockName('onRefine'),
70
+ onReject: fn().mockName('onReject'),
71
71
  onApprove: fn().mockName('onApprove'),
72
72
  },
73
73
  };
@@ -75,7 +75,7 @@ export const SingleDecision = {
75
75
  export const NoTechnologies = {
76
76
  args: {
77
77
  data: { ...mockData, technologies: [] },
78
- onRefine: fn().mockName('onRefine'),
78
+ onReject: fn().mockName('onReject'),
79
79
  onApprove: fn().mockName('onApprove'),
80
80
  },
81
81
  };
@@ -94,15 +94,33 @@ function DrawerTemplate(props) {
94
94
  /** Drawer with tech decisions list. */
95
95
  export const InDrawer = {
96
96
  ...drawerMeta,
97
- render: () => (_jsx(DrawerTemplate, { featureName: "Agent Executor Abstraction", featureId: "FEAT-099", data: mockData, onRefine: fn().mockName('onRefine'), onApprove: fn().mockName('onApprove') })),
97
+ render: () => (_jsx(DrawerTemplate, { featureName: "Agent Executor Abstraction", featureId: "FEAT-099", data: mockData, onApprove: fn().mockName('onApprove'), onReject: fn().mockName('onReject') })),
98
98
  };
99
99
  /** Drawer with delete button visible. */
100
100
  export const WithDeleteButton = {
101
101
  ...drawerMeta,
102
- render: () => (_jsx(DrawerTemplate, { featureName: "Agent Executor Abstraction", featureId: "FEAT-099", data: mockData, onRefine: fn().mockName('onRefine'), onApprove: fn().mockName('onApprove'), onDelete: fn().mockName('onDelete') })),
102
+ render: () => (_jsx(DrawerTemplate, { featureName: "Agent Executor Abstraction", featureId: "FEAT-099", data: mockData, onApprove: fn().mockName('onApprove'), onReject: fn().mockName('onReject'), onDelete: fn().mockName('onDelete') })),
103
103
  };
104
104
  /** Drawer with delete in progress (button disabled). */
105
105
  export const DeletingState = {
106
106
  ...drawerMeta,
107
- render: () => (_jsx(DrawerTemplate, { featureName: "Agent Executor Abstraction", featureId: "FEAT-099", data: mockData, onRefine: fn().mockName('onRefine'), onApprove: fn().mockName('onApprove'), onDelete: fn().mockName('onDelete'), isDeleting: true })),
107
+ render: () => (_jsx(DrawerTemplate, { featureName: "Agent Executor Abstraction", featureId: "FEAT-099", data: mockData, onApprove: fn().mockName('onApprove'), onReject: fn().mockName('onReject'), onDelete: fn().mockName('onDelete'), isDeleting: true })),
108
+ };
109
+ /* ─── Reject Variants ─── */
110
+ /** With reject button visible next to approve. */
111
+ export const WithRejectButton = {
112
+ args: {
113
+ data: mockData,
114
+ onApprove: fn().mockName('onApprove'),
115
+ onReject: fn().mockName('onReject'),
116
+ },
117
+ };
118
+ /** Rejecting state — reject button disabled while reject action is in flight. */
119
+ export const RejectingState = {
120
+ args: {
121
+ data: mockData,
122
+ onApprove: fn().mockName('onApprove'),
123
+ onReject: fn().mockName('onReject'),
124
+ isRejecting: true,
125
+ },
108
126
  };
@@ -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":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAS1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAS5E,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,2CA2QzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAgB1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAU5E,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,2CAiYzF"}
@@ -3,13 +3,16 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  import { useCallback, useEffect, useState } from 'react';
4
4
  import { toast } from 'sonner';
5
5
  import { approveFeature } from '../../../app/actions/approve-feature.js';
6
+ import { rejectFeature } from '../../../app/actions/reject-feature.js';
6
7
  import { getFeatureArtifact } from '../../../app/actions/get-feature-artifact.js';
7
8
  import { getResearchArtifact } from '../../../app/actions/get-research-artifact.js';
8
9
  import { getWorkflowDefaults } from '../../../app/actions/get-workflow-defaults.js';
10
+ import { getMergeReviewData } from '../../../app/actions/get-merge-review-data.js';
9
11
  import { FeaturesCanvas } from '../../features/features-canvas/index.js';
10
12
  import { FeatureDrawer, FeatureCreateDrawer } from '../../common/index.js';
11
13
  import { PrdQuestionnaireDrawer } from '../../common/prd-questionnaire/index.js';
12
14
  import { TechDecisionsDrawer } from '../../common/tech-decisions-review/index.js';
15
+ import { MergeReviewDrawer } from '../../common/merge-review/index.js';
13
16
  import { NotificationPermissionBanner } from '../../common/notification-permission-banner/index.js';
14
17
  import { ControlCenterEmptyState } from './control-center-empty-state.js';
15
18
  import { useControlCenterState } from './use-control-center-state.js';
@@ -26,29 +29,42 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
26
29
  }, []);
27
30
  // PRD questionnaire drawer state
28
31
  const [prdSelections, setPrdSelections] = useState({});
29
- const [isPrdProcessing, setIsPrdProcessing] = useState(false);
30
32
  const [questionnaireData, setQuestionnaireData] = useState(null);
31
33
  const [isLoadingQuestionnaire, setIsLoadingQuestionnaire] = useState(false);
34
+ // Reject state (shared by both drawers)
35
+ const [isRejecting, setIsRejecting] = useState(false);
32
36
  // Tech decisions drawer state
33
37
  const [techDecisionsData, setTechDecisionsData] = useState(null);
34
38
  const [isLoadingTechDecisions, setIsLoadingTechDecisions] = useState(false);
39
+ // Merge review drawer state
40
+ const [mergeReviewData, setMergeReviewData] = useState(null);
41
+ const [isLoadingMergeReview, setIsLoadingMergeReview] = useState(false);
35
42
  const showPrdDrawer = selectedNode?.lifecycle === 'requirements' && selectedNode?.state === 'action-required';
36
43
  const showTechDecisionsDrawer = selectedNode?.lifecycle === 'implementation' && selectedNode?.state === 'action-required';
44
+ const showMergeReviewDrawer = selectedNode?.lifecycle === 'review' && selectedNode?.state === 'action-required';
37
45
  const handlePrdSelect = useCallback((questionId, optionId) => {
38
46
  setPrdSelections((prev) => ({ ...prev, [questionId]: optionId }));
39
47
  }, []);
40
- const handlePrdRefine = useCallback(async (_text) => {
41
- setIsPrdProcessing(true);
42
- // TODO: Call API to refine requirements
43
- // For now simulate a delay
44
- await new Promise((resolve) => setTimeout(resolve, 1500));
45
- setIsPrdProcessing(false);
46
- }, []);
47
48
  const handlePrdApprove = useCallback(async (_actionId) => {
48
49
  const featureId = selectedNode?.featureId;
49
50
  if (!featureId)
50
51
  return;
51
- const result = await approveFeature(featureId);
52
+ let payload;
53
+ if (questionnaireData) {
54
+ const changedSelections = [];
55
+ for (const [questionId, optionId] of Object.entries(prdSelections)) {
56
+ const question = questionnaireData.questions.find((q) => q.id === questionId);
57
+ const option = question?.options.find((o) => o.id === optionId);
58
+ if (question && option) {
59
+ changedSelections.push({
60
+ questionId: question.question,
61
+ selectedOption: option.label,
62
+ });
63
+ }
64
+ }
65
+ payload = { approved: true, changedSelections };
66
+ }
67
+ const result = await approveFeature(featureId, payload);
52
68
  if (!result.approved) {
53
69
  toast.error(result.error ?? 'Failed to approve requirements');
54
70
  return;
@@ -56,25 +72,48 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
56
72
  toast.success('Requirements approved — agent resuming');
57
73
  clearSelection();
58
74
  setPrdSelections({});
75
+ }, [selectedNode?.featureId, clearSelection, questionnaireData, prdSelections]);
76
+ // Shared reject handler — all drawers use the same rejectFeature flow
77
+ const handleReject = useCallback(async (feedback, label, onDone) => {
78
+ const featureId = selectedNode?.featureId;
79
+ if (!featureId)
80
+ return;
81
+ setIsRejecting(true);
82
+ try {
83
+ const result = await rejectFeature(featureId, feedback);
84
+ if (!result.rejected) {
85
+ toast.error(result.error ?? `Failed to reject ${label.toLowerCase()}`);
86
+ return;
87
+ }
88
+ toast.success(`${label} rejected — agent re-iterating (iteration ${result.iteration})`);
89
+ if (result.iterationWarning) {
90
+ toast.warning(`Iteration ${result.iteration} — consider approving or adjusting feedback to avoid excessive iterations`);
91
+ }
92
+ clearSelection();
93
+ onDone?.();
94
+ }
95
+ finally {
96
+ setIsRejecting(false);
97
+ }
59
98
  }, [selectedNode?.featureId, clearSelection]);
60
- const handleTechDecisionsRefine = useCallback(async (_text) => {
61
- setIsLoadingTechDecisions(true);
62
- // TODO: Call API to refine tech decisions
63
- await new Promise((resolve) => setTimeout(resolve, 1500));
64
- setIsLoadingTechDecisions(false);
65
- }, []);
66
- const handleTechDecisionsApprove = useCallback(async () => {
99
+ const handlePrdReject = useCallback((feedback) => handleReject(feedback, 'Requirements', () => setPrdSelections({})), [handleReject]);
100
+ const handleTechDecisionsReject = useCallback((feedback) => handleReject(feedback, 'Plan'), [handleReject]);
101
+ const handleMergeReject = useCallback((feedback) => handleReject(feedback, 'Merge'), [handleReject]);
102
+ // Shared approve handler tech decisions and merge use identical approve flows
103
+ const handleSimpleApprove = useCallback(async (label) => {
67
104
  const featureId = selectedNode?.featureId;
68
105
  if (!featureId)
69
106
  return;
70
107
  const result = await approveFeature(featureId);
71
108
  if (!result.approved) {
72
- toast.error(result.error ?? 'Failed to approve plan');
109
+ toast.error(result.error ?? `Failed to approve ${label.toLowerCase()}`);
73
110
  return;
74
111
  }
75
- toast.success('Plan approved — agent resuming');
112
+ toast.success(`${label} approved — agent resuming`);
76
113
  clearSelection();
77
114
  }, [selectedNode?.featureId, clearSelection]);
115
+ const handleTechDecisionsApprove = useCallback(() => handleSimpleApprove('Plan'), [handleSimpleApprove]);
116
+ const handleMergeApprove = useCallback(() => handleSimpleApprove('Merge'), [handleSimpleApprove]);
78
117
  // Fetch questionnaire data and reset selections when a different feature is selected
79
118
  const prdFeatureId = showPrdDrawer ? selectedNode?.featureId : null;
80
119
  useEffect(() => {
@@ -148,6 +187,36 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
148
187
  cancelled = true;
149
188
  };
150
189
  }, [techDecisionsFeatureId]);
190
+ // Fetch merge review data when a feature is in review + action-required
191
+ const mergeFeatureId = showMergeReviewDrawer ? selectedNode?.featureId : null;
192
+ useEffect(() => {
193
+ setMergeReviewData(null);
194
+ if (!mergeFeatureId)
195
+ return;
196
+ let cancelled = false;
197
+ setIsLoadingMergeReview(true);
198
+ getMergeReviewData(mergeFeatureId)
199
+ .then((result) => {
200
+ if (cancelled)
201
+ return;
202
+ if ('error' in result) {
203
+ toast.error(result.error);
204
+ return;
205
+ }
206
+ setMergeReviewData(result);
207
+ })
208
+ .catch(() => {
209
+ if (!cancelled)
210
+ toast.error('Failed to load merge review data');
211
+ })
212
+ .finally(() => {
213
+ if (!cancelled)
214
+ setIsLoadingMergeReview(false);
215
+ });
216
+ return () => {
217
+ cancelled = true;
218
+ };
219
+ }, [mergeFeatureId]);
151
220
  const hasRepositories = nodes.some((n) => n.type === 'repositoryNode');
152
221
  // Listen for global "open create drawer" events from the sidebar
153
222
  useEffect(() => {
@@ -158,5 +227,5 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
158
227
  if (!hasRepositories) {
159
228
  return (_jsxs(_Fragment, { children: [_jsx(NotificationPermissionBanner, {}), _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath, workflowDefaults: workflowDefaults })] }));
160
229
  }
161
- return (_jsxs(_Fragment, { children: [_jsx(NotificationPermissionBanner, {}), _jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositoryDelete: handleDeleteRepository, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: showPrdDrawer || showTechDecisionsDrawer ? null : selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), questionnaireData ? (_jsx(PrdQuestionnaireDrawer, { open: showPrdDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureDescription: selectedNode?.description, featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: questionnaireData, selections: prdSelections, onSelect: handlePrdSelect, onRefine: handlePrdRefine, onApprove: handlePrdApprove, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isPrdProcessing || isLoadingQuestionnaire })) : null, techDecisionsData ? (_jsx(TechDecisionsDrawer, { open: showTechDecisionsDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: techDecisionsData, onRefine: handleTechDecisionsRefine, onApprove: handleTechDecisionsApprove, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isLoadingTechDecisions })) : null, _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath, workflowDefaults: workflowDefaults })] }));
230
+ return (_jsxs(_Fragment, { children: [_jsx(NotificationPermissionBanner, {}), _jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositoryDelete: handleDeleteRepository, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: showPrdDrawer || showTechDecisionsDrawer || showMergeReviewDrawer ? null : selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), questionnaireData ? (_jsx(PrdQuestionnaireDrawer, { open: showPrdDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureDescription: selectedNode?.description, featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: questionnaireData, selections: prdSelections, onSelect: handlePrdSelect, onApprove: handlePrdApprove, onReject: handlePrdReject, isRejecting: isRejecting, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isLoadingQuestionnaire })) : null, techDecisionsData ? (_jsx(TechDecisionsDrawer, { open: showTechDecisionsDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: techDecisionsData, onApprove: handleTechDecisionsApprove, onReject: handleTechDecisionsReject, isRejecting: isRejecting, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isLoadingTechDecisions })) : null, mergeReviewData ? (_jsx(MergeReviewDrawer, { open: showMergeReviewDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: mergeReviewData, onApprove: handleMergeApprove, onReject: handleMergeReject, isRejecting: isRejecting, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isLoadingMergeReview })) : null, _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath, workflowDefaults: workflowDefaults })] }));
162
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"control-center.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAQjD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,CAgBpC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAkEnC,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAQ1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAS7B,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAYzB,CAAC;AAwIF;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC"}
1
+ {"version":3,"file":"control-center.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAQjD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,CAgBpC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAiFnC,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAQ1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAS7B,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAYzB,CAAC;AAwIF;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC"}
@@ -60,6 +60,21 @@ const featureNodes = [
60
60
  branch: 'feat/api-gateway',
61
61
  },
62
62
  },
63
+ {
64
+ id: 'feat-4',
65
+ type: 'featureNode',
66
+ position: { x: 800, y: 345 },
67
+ data: {
68
+ name: 'Merge Review',
69
+ description: 'PR ready for merge approval',
70
+ featureId: '#f4',
71
+ lifecycle: 'review',
72
+ state: 'action-required',
73
+ progress: 90,
74
+ repositoryPath: '/home/user/my-repo',
75
+ branch: 'feat/merge-review',
76
+ },
77
+ },
63
78
  ];
64
79
  const repoNode = {
65
80
  id: 'repo-1',
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAOhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,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,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KACpC,MAAM,CAAC;CACb;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAwepB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAOhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,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,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KACpC,MAAM,CAAC;CACb;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAsgBpB"}
@@ -273,8 +273,17 @@ export function useControlCenterState(initialNodes, initialEdges) {
273
273
  return;
274
274
  }
275
275
  setSelectedNode(null);
276
- setNodes((prev) => prev.filter((n) => n.id !== featureId));
277
- setEdges((prev) => prev.filter((e) => e.source !== featureId && e.target !== featureId));
276
+ setNodes((currentNodes) => {
277
+ const remainingNodes = currentNodes.filter((n) => n.id !== featureId);
278
+ const remainingEdges = edges.filter((e) => e.source !== featureId && e.target !== featureId);
279
+ const result = layoutWithDagre(remainingNodes, remainingEdges, {
280
+ direction: 'LR',
281
+ ranksep: 200,
282
+ nodesep: 60,
283
+ });
284
+ setEdges(result.edges);
285
+ return result.nodes;
286
+ });
278
287
  toast.success('Feature deleted successfully');
279
288
  router.refresh();
280
289
  }
@@ -284,7 +293,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
284
293
  finally {
285
294
  setIsDeleting(false);
286
295
  }
287
- }, [router]);
296
+ }, [router, edges]);
288
297
  const handleDeleteRepository = useCallback(async (repositoryId) => {
289
298
  const repoNodeId = `repo-${repositoryId}`;
290
299
  // Optimistic: remove node and its edges immediately
@@ -332,27 +341,31 @@ export function useControlCenterState(initialNodes, initialEdges) {
332
341
  .split(/[\\/]/)
333
342
  .pop() ?? path;
334
343
  // Optimistic UI: add node immediately
344
+ let savedAddRepoY = 0;
335
345
  setNodes((currentNodes) => {
336
346
  const repoNodes = currentNodes.filter((n) => n.type === 'repositoryNode');
337
347
  const addRepoNode = currentNodes.find((n) => n.type === 'addRepositoryNode');
338
- // Place in the repo column, below the last existing repo node
348
+ // Save addRepoNode's original Y for rollback on error
349
+ if (addRepoNode)
350
+ savedAddRepoY = addRepoNode.position.y;
351
+ // Place in the repo column, at the addRepoNode's current position
339
352
  const repoX = repoNodes[0]?.position.x ?? addRepoNode?.position.x ?? 50;
340
353
  const repoHeight = 50; // repositoryNode height
341
354
  const gap = 15; // match dagre nodesep
342
- const lastRepoBottomY = repoNodes.length > 0
343
- ? Math.max(...repoNodes.map((n) => n.position.y)) + repoHeight
344
- : addRepoNode
345
- ? addRepoNode.position.y
346
- : 0;
347
- const position = { x: repoX, y: lastRepoBottomY + gap };
355
+ const position = {
356
+ x: repoX,
357
+ y: addRepoNode ? addRepoNode.position.y : 0,
358
+ };
348
359
  const newNode = {
349
360
  id: tempId,
350
361
  type: 'repositoryNode',
351
362
  position,
352
363
  data: { name: repoName, repositoryPath: path, id: tempId },
353
364
  };
354
- // Move addRepo button below the new node
355
- const addRepoY = position.y + repoHeight + gap;
365
+ // Shift addRepo button down by exactly one slot
366
+ const addRepoY = addRepoNode
367
+ ? addRepoNode.position.y + repoHeight + gap
368
+ : position.y + repoHeight + gap;
356
369
  return currentNodes
357
370
  .map((n) => n.type === 'addRepositoryNode' ? { ...n, position: { ...n.position, y: addRepoY } } : n)
358
371
  .concat(newNode);
@@ -361,8 +374,12 @@ export function useControlCenterState(initialNodes, initialEdges) {
361
374
  addRepository({ path, name: repoName })
362
375
  .then((result) => {
363
376
  if (result.error) {
364
- // Rollback optimistic node
365
- setNodes((prev) => prev.filter((n) => n.id !== tempId));
377
+ // Rollback optimistic node and restore addRepoNode position
378
+ setNodes((prev) => prev
379
+ .filter((n) => n.id !== tempId)
380
+ .map((n) => n.type === 'addRepositoryNode'
381
+ ? { ...n, position: { ...n.position, y: savedAddRepoY } }
382
+ : n));
366
383
  toast.error(result.error);
367
384
  return;
368
385
  }
@@ -385,7 +402,12 @@ export function useControlCenterState(initialNodes, initialEdges) {
385
402
  router.refresh();
386
403
  })
387
404
  .catch(() => {
388
- setNodes((prev) => prev.filter((n) => n.id !== tempId));
405
+ // Rollback optimistic node and restore addRepoNode position
406
+ setNodes((prev) => prev
407
+ .filter((n) => n.id !== tempId)
408
+ .map((n) => n.type === 'addRepositoryNode'
409
+ ? { ...n, position: { ...n.position, y: savedAddRepoY } }
410
+ : n));
389
411
  toast.error('Failed to add repository');
390
412
  });
391
413
  }, [router]);