@shepai/cli 1.66.4 → 1.67.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 (272) hide show
  1. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts.map +1 -1
  2. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +21 -1
  3. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.d.ts +15 -1
  4. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.d.ts.map +1 -1
  5. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.js +16 -2
  6. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.d.ts +12 -0
  7. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.d.ts.map +1 -0
  8. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.js +284 -0
  9. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.d.ts +33 -0
  10. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.d.ts.map +1 -0
  11. package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.js +210 -0
  12. package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts +56 -0
  13. package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts.map +1 -0
  14. package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.js +32 -0
  15. package/dist/src/presentation/web/components/common/control-center-drawer/index.d.ts +3 -0
  16. package/dist/src/presentation/web/components/common/control-center-drawer/index.d.ts.map +1 -0
  17. package/dist/src/presentation/web/components/common/control-center-drawer/index.js +2 -0
  18. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.d.ts +0 -4
  19. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.d.ts.map +1 -1
  20. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts +1 -1
  21. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts.map +1 -1
  22. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.js +18 -12
  23. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.stories.d.ts +4 -6
  24. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.stories.d.ts.map +1 -1
  25. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.stories.js +5 -13
  26. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  27. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +2 -13
  28. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +2 -10
  29. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -1
  30. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +21 -1
  31. package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts +1 -1
  32. package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts.map +1 -1
  33. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts.map +1 -1
  34. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +14 -20
  35. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts +7 -1
  36. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts.map +1 -1
  37. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js +37 -2
  38. package/dist/src/presentation/web/components/common/index.d.ts +1 -0
  39. package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
  40. package/dist/src/presentation/web/components/common/index.js +1 -0
  41. package/dist/src/presentation/web/components/common/merge-review/merge-review.d.ts.map +1 -1
  42. package/dist/src/presentation/web/components/common/merge-review/merge-review.js +2 -2
  43. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts.map +1 -1
  44. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.js +1 -2
  45. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.js +1 -1
  46. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts.map +1 -1
  47. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.js +1 -2
  48. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.d.ts.map +1 -1
  49. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.js +5 -1
  50. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.d.ts +2 -0
  51. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.d.ts.map +1 -1
  52. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.js +13 -2
  53. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.d.ts.map +1 -1
  54. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.js +5 -2
  55. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.stories.js +1 -1
  56. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.d.ts.map +1 -1
  57. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.js +1 -1
  58. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.d.ts.map +1 -1
  59. package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.js +1 -2
  60. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  61. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +20 -215
  62. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  63. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +2 -1
  64. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
  65. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +1 -1
  66. package/dist/tsconfig.build.tsbuildinfo +1 -1
  67. package/package.json +1 -1
  68. package/web/.next/BUILD_ID +1 -1
  69. package/web/.next/build-manifest.json +5 -5
  70. package/web/.next/cache/.previewinfo +1 -1
  71. package/web/.next/cache/.rscinfo +1 -1
  72. package/web/.next/cache/.tsbuildinfo +1 -1
  73. package/web/.next/cache/config.json +3 -3
  74. package/web/.next/fallback-build-manifest.json +2 -2
  75. package/web/.next/prerender-manifest.json +3 -3
  76. package/web/.next/required-server-files.js +1 -1
  77. package/web/.next/required-server-files.json +1 -1
  78. package/web/.next/server/app/_global-error/page/build-manifest.json +3 -3
  79. package/web/.next/server/app/_global-error.html +2 -2
  80. package/web/.next/server/app/_global-error.rsc +1 -1
  81. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  82. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  83. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  84. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  85. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  86. package/web/.next/server/app/_not-found/page/build-manifest.json +3 -3
  87. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  88. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  89. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  90. package/web/.next/server/app/page/build-manifest.json +3 -3
  91. package/web/.next/server/app/page/server-reference-manifest.json +88 -88
  92. package/web/.next/server/app/page.js +1 -1
  93. package/web/.next/server/app/page.js.nft.json +1 -1
  94. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  95. package/web/.next/server/app/skills/page/build-manifest.json +3 -3
  96. package/web/.next/server/app/skills/page/server-reference-manifest.json +5 -5
  97. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  98. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  99. package/web/.next/server/app/tools/page/build-manifest.json +3 -3
  100. package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  101. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  102. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  103. package/web/.next/server/app/version/page/build-manifest.json +3 -3
  104. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  105. package/web/.next/server/app/version/page.js.nft.json +1 -1
  106. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  107. package/web/.next/server/chunks/ssr/403f9_next_dist_a53cb908._.js +3 -0
  108. package/web/.next/server/chunks/ssr/403f9_next_dist_a53cb908._.js.map +1 -0
  109. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  110. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  112. package/web/.next/server/chunks/ssr/{[root-of-the-server]__f5830fa9._.js → [root-of-the-server]__2ffb27f1._.js} +2 -2
  113. package/web/.next/server/chunks/ssr/{[root-of-the-server]__f5830fa9._.js.map → [root-of-the-server]__2ffb27f1._.js.map} +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js +1 -1
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js.map +1 -1
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__7f4180a1._.js +3 -0
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__7f4180a1._.js.map +1 -0
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +4 -4
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/[root-of-the-server]__e41b5eec._.js +9 -0
  125. package/web/.next/server/chunks/ssr/{[root-of-the-server]__f648005b._.js.map → [root-of-the-server]__e41b5eec._.js.map} +1 -1
  126. package/web/.next/server/chunks/ssr/[root-of-the-server]__eaf6100f._.js +3 -0
  127. package/web/.next/server/chunks/ssr/[root-of-the-server]__eaf6100f._.js.map +1 -0
  128. package/web/.next/server/chunks/ssr/_23c92688._.js +6 -0
  129. package/web/.next/server/chunks/ssr/_23c92688._.js.map +1 -0
  130. package/web/.next/server/chunks/ssr/_28993370._.js +1 -1
  131. package/web/.next/server/chunks/ssr/_28993370._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/_49bf495c._.js +3 -0
  133. package/web/.next/server/chunks/ssr/_49bf495c._.js.map +1 -0
  134. package/web/.next/server/chunks/ssr/_690ea95f._.js +3 -0
  135. package/web/.next/server/chunks/ssr/{_2900ed94._.js.map → _690ea95f._.js.map} +1 -1
  136. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/_c52cace8._.js → server/chunks/ssr/_725584e5._.js} +2 -2
  137. package/web/.next/server/chunks/ssr/_725584e5._.js.map +1 -0
  138. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  139. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  140. package/web/.next/server/middleware-build-manifest.js +3 -3
  141. package/web/.next/server/pages/500.html +2 -2
  142. package/web/.next/server/server-reference-manifest.js +1 -1
  143. package/web/.next/server/server-reference-manifest.json +115 -115
  144. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  145. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +5 -5
  146. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  147. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  148. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error/page/build-manifest.json +3 -3
  149. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  150. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  151. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  152. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  153. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  154. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  155. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  156. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/build-manifest.json +3 -3
  157. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  158. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  159. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  160. package/web/.next/standalone/src/presentation/web/.next/server/app/page/build-manifest.json +3 -3
  161. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +88 -88
  162. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js +1 -1
  163. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  164. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  165. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/build-manifest.json +3 -3
  166. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +5 -5
  167. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  168. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/build-manifest.json +3 -3
  170. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  172. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  173. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/build-manifest.json +3 -3
  174. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  175. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  176. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  177. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/403f9_next_dist_a53cb908._.js +3 -0
  178. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  179. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  180. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{[root-of-the-server]__f5830fa9._.js → [root-of-the-server]__2ffb27f1._.js} +2 -2
  181. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js +1 -1
  182. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  183. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__7f4180a1._.js +3 -0
  184. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  185. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +4 -4
  186. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__e41b5eec._.js +9 -0
  187. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__eaf6100f._.js +3 -0
  188. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_23c92688._.js +6 -0
  189. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_28993370._.js +1 -1
  190. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_49bf495c._.js +3 -0
  191. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_690ea95f._.js +3 -0
  192. package/web/.next/{server/chunks/ssr/_c52cace8._.js → standalone/src/presentation/web/.next/server/chunks/ssr/_725584e5._.js} +2 -2
  193. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  194. package/web/.next/standalone/src/presentation/web/.next/server/middleware-build-manifest.js +3 -3
  195. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  196. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  197. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +115 -115
  198. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.stories.tsx +16 -2
  199. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.tsx +35 -6
  200. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.tsx +285 -0
  201. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.tsx +650 -0
  202. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/drawer-view.ts +82 -0
  203. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/index.ts +2 -0
  204. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.ts +0 -4
  205. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.stories.tsx +5 -14
  206. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.tsx +58 -51
  207. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx +42 -1
  208. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +2 -16
  209. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/index.ts +1 -0
  210. package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.tsx +55 -2
  211. package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/feature-drawer.tsx +97 -106
  212. package/web/.next/standalone/src/presentation/web/components/common/index.ts +6 -0
  213. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.stories.tsx +1 -2
  214. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.tsx +1 -2
  215. package/web/.next/standalone/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.tsx +1 -2
  216. package/web/.next/standalone/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.tsx +1 -1
  217. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-drawer.stories.tsx +28 -2
  218. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-drawer.tsx +7 -2
  219. package/web/.next/standalone/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.stories.tsx +1 -1
  220. package/web/.next/standalone/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.tsx +7 -6
  221. package/web/.next/standalone/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.tsx +1 -2
  222. package/web/.next/standalone/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.tsx +1 -2
  223. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +30 -348
  224. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +2 -1
  225. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +5 -1
  226. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  227. package/web/.next/static/chunks/38ca82d1243738e3.js +1 -0
  228. package/web/.next/static/chunks/{35f41ba0a6d626d7.js → 491ae2bbae40857c.js} +1 -1
  229. package/web/.next/static/chunks/4b6cc9f65260f2bd.js +2 -0
  230. package/web/.next/static/chunks/67599679ca9ac863.js +1 -0
  231. package/web/.next/static/chunks/{e0073669b6bc24df.js → 6ece250c7d0ec203.js} +1 -1
  232. package/web/.next/static/chunks/a186bbb822ccb655.css +2 -0
  233. package/web/.next/static/chunks/a5c59952485e875e.js +1 -0
  234. package/web/.next/static/chunks/{8c60d1bd87239066.js → a9626385607910b3.js} +1 -1
  235. package/web/.next/static/chunks/c1c15470a7b058c8.js +1 -0
  236. package/web/.next/static/chunks/caa2e7e1618e2179.js +1 -0
  237. package/web/.next/static/chunks/ed799a306922f03e.js +10 -0
  238. package/web/.next/static/chunks/fb703cf73aba2eb8.js +1 -0
  239. package/web/.next/static/chunks/{turbopack-b6b5b4f015327a9a.js → turbopack-958ac34b879d0dce.js} +1 -1
  240. package/web/.next/trace +1 -1
  241. package/web/.next/trace-build +1 -1
  242. package/web/.next/server/chunks/ssr/403f9_next_dist_623b646a._.js +0 -3
  243. package/web/.next/server/chunks/ssr/403f9_next_dist_623b646a._.js.map +0 -1
  244. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb51fac._.js +0 -3
  245. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb51fac._.js.map +0 -1
  246. package/web/.next/server/chunks/ssr/[root-of-the-server]__c1f0f2a8._.js +0 -3
  247. package/web/.next/server/chunks/ssr/[root-of-the-server]__c1f0f2a8._.js.map +0 -1
  248. package/web/.next/server/chunks/ssr/[root-of-the-server]__f648005b._.js +0 -9
  249. package/web/.next/server/chunks/ssr/_2900ed94._.js +0 -3
  250. package/web/.next/server/chunks/ssr/_6978d868._.js +0 -3
  251. package/web/.next/server/chunks/ssr/_6978d868._.js.map +0 -1
  252. package/web/.next/server/chunks/ssr/_85965278._.js +0 -6
  253. package/web/.next/server/chunks/ssr/_85965278._.js.map +0 -1
  254. package/web/.next/server/chunks/ssr/_c52cace8._.js.map +0 -1
  255. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/403f9_next_dist_623b646a._.js +0 -3
  256. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6bb51fac._.js +0 -3
  257. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__c1f0f2a8._.js +0 -3
  258. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__f648005b._.js +0 -9
  259. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_2900ed94._.js +0 -3
  260. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_6978d868._.js +0 -3
  261. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_85965278._.js +0 -6
  262. package/web/.next/static/chunks/41a2adc09edfffaf.js +0 -1
  263. package/web/.next/static/chunks/5054c72b1c8f5912.js +0 -1
  264. package/web/.next/static/chunks/5f6d9f4647fc3093.js +0 -10
  265. package/web/.next/static/chunks/96f49affaceab206.css +0 -2
  266. package/web/.next/static/chunks/a6d1d774260fc927.js +0 -2
  267. package/web/.next/static/chunks/c0fdd832fc4ee8eb.js +0 -1
  268. package/web/.next/static/chunks/de70ba984363673f.js +0 -1
  269. package/web/.next/static/chunks/f5fb2f182ae9b015.js +0 -1
  270. /package/web/.next/static/{XGbKxw26cOCJQ-711YxBw → GdqfiIUVMj2wj_3lbrUqd}/_buildManifest.js +0 -0
  271. /package/web/.next/static/{XGbKxw26cOCJQ-711YxBw → GdqfiIUVMj2wj_3lbrUqd}/_clientMiddlewareManifest.json +0 -0
  272. /package/web/.next/static/{XGbKxw26cOCJQ-711YxBw → GdqfiIUVMj2wj_3lbrUqd}/_ssgManifest.js +0 -0
@@ -0,0 +1,650 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback, useEffect } from 'react';
4
+ import { toast } from 'sonner';
5
+ import {
6
+ Loader2,
7
+ Trash2,
8
+ ExternalLink,
9
+ GitCommitHorizontal,
10
+ Code2,
11
+ Terminal,
12
+ FolderOpen,
13
+ } from 'lucide-react';
14
+ import type {
15
+ PrdApprovalPayload,
16
+ QuestionSelectionChange,
17
+ } from '@shepai/core/domain/generated/output';
18
+ import { PrStatus } from '@shepai/core/domain/generated/output';
19
+ import { approveFeature } from '@/app/actions/approve-feature';
20
+ import { rejectFeature } from '@/app/actions/reject-feature';
21
+ import { getFeatureArtifact } from '@/app/actions/get-feature-artifact';
22
+ import { getResearchArtifact } from '@/app/actions/get-research-artifact';
23
+ import { getMergeReviewData } from '@/app/actions/get-merge-review-data';
24
+ import { cn } from '@/lib/utils';
25
+ import { useSoundAction } from '@/hooks/use-sound-action';
26
+ import { BaseDrawer } from '@/components/common/base-drawer';
27
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
28
+ import { Button } from '@/components/ui/button';
29
+ import { Badge } from '@/components/ui/badge';
30
+ import { Separator } from '@/components/ui/separator';
31
+ import { CometSpinner } from '@/components/ui/comet-spinner';
32
+ import {
33
+ AlertDialog,
34
+ AlertDialogAction,
35
+ AlertDialogCancel,
36
+ AlertDialogContent,
37
+ AlertDialogDescription,
38
+ AlertDialogFooter,
39
+ AlertDialogHeader,
40
+ AlertDialogTitle,
41
+ AlertDialogTrigger,
42
+ } from '@/components/ui/alert-dialog';
43
+ import { CiStatusBadge } from '@/components/common/ci-status-badge';
44
+ import { ActionButton } from '@/components/common/action-button';
45
+ import { OpenActionMenu } from '@/components/common/open-action-menu';
46
+ import { FeatureCreateDrawer } from '@/components/common/feature-create-drawer';
47
+ import { PrdQuestionnaire } from '@/components/common/prd-questionnaire';
48
+ import { TechDecisionsReview } from '@/components/common/tech-decisions-review';
49
+ import { MergeReview } from '@/components/common/merge-review';
50
+ import { featureNodeStateConfig, lifecycleDisplayLabels } from '@/components/common/feature-node';
51
+ import type { FeatureNodeData } from '@/components/common/feature-node';
52
+ import { useFeatureActions } from '@/components/common/feature-drawer/use-feature-actions';
53
+ import { useRepositoryActions } from '@/components/common/repository-node/use-repository-actions';
54
+ import type { PrdQuestionnaireData } from '@/components/common/prd-questionnaire';
55
+ import type { TechDecisionsReviewData } from '@/components/common/tech-decisions-review';
56
+ import type { MergeReviewData } from '@/components/common/merge-review';
57
+ import type { FeatureCreatePayload } from '@/components/common/feature-create-drawer';
58
+ import type { DrawerView } from './drawer-view';
59
+
60
+ export interface ControlCenterDrawerProps {
61
+ view: DrawerView | null;
62
+ onClose: () => void;
63
+ onDelete?: (featureId: string) => void;
64
+ isDeleting?: boolean;
65
+ onCreateSubmit: (data: FeatureCreatePayload) => void;
66
+ isSubmitting?: boolean;
67
+ }
68
+
69
+ export function ControlCenterDrawer({
70
+ view,
71
+ onClose,
72
+ onDelete,
73
+ isDeleting,
74
+ onCreateSubmit,
75
+ isSubmitting,
76
+ }: ControlCenterDrawerProps) {
77
+ // ── PRD questionnaire state ─────────────────────────────────────────────
78
+ const [prdData, setPrdData] = useState<PrdQuestionnaireData | null>(null);
79
+ const [prdSelections, setPrdSelections] = useState<Record<string, string>>({});
80
+ const [isLoadingPrd, setIsLoadingPrd] = useState(false);
81
+
82
+ // ── Tech decisions state ────────────────────────────────────────────────
83
+ const [techData, setTechData] = useState<TechDecisionsReviewData | null>(null);
84
+ const [isLoadingTech, setIsLoadingTech] = useState(false);
85
+
86
+ // ── Merge review state ──────────────────────────────────────────────────
87
+ const [mergeData, setMergeData] = useState<MergeReviewData | null>(null);
88
+ const [isLoadingMerge, setIsLoadingMerge] = useState(false);
89
+
90
+ // ── Shared reject state ─────────────────────────────────────────────────
91
+ const [isRejecting, setIsRejecting] = useState(false);
92
+ const rejectSound = useSoundAction('reject');
93
+
94
+ // ── Data fetching ───────────────────────────────────────────────────────
95
+
96
+ const prdFeatureId = view?.type === 'prd-review' ? view.node.featureId : null;
97
+ useEffect(() => {
98
+ setPrdSelections({});
99
+ setPrdData(null);
100
+ if (!prdFeatureId) return;
101
+
102
+ let cancelled = false;
103
+ setIsLoadingPrd(true);
104
+ getFeatureArtifact(prdFeatureId)
105
+ .then((result) => {
106
+ if (cancelled) return;
107
+ if (result.error) {
108
+ toast.error(result.error);
109
+ return;
110
+ }
111
+ if (result.questionnaire) {
112
+ setPrdData(result.questionnaire);
113
+ const defaults: Record<string, string> = {};
114
+ for (const q of result.questionnaire.questions) {
115
+ const recommended = q.options.find((o) => o.recommended);
116
+ if (recommended) defaults[q.id] = recommended.id;
117
+ }
118
+ setPrdSelections(defaults);
119
+ }
120
+ })
121
+ .catch(() => {
122
+ if (!cancelled) toast.error('Failed to load questionnaire');
123
+ })
124
+ .finally(() => {
125
+ if (!cancelled) setIsLoadingPrd(false);
126
+ });
127
+ return () => {
128
+ cancelled = true;
129
+ };
130
+ }, [prdFeatureId]);
131
+
132
+ const techFeatureId = view?.type === 'tech-review' ? view.node.featureId : null;
133
+ useEffect(() => {
134
+ setTechData(null);
135
+ if (!techFeatureId) return;
136
+
137
+ let cancelled = false;
138
+ setIsLoadingTech(true);
139
+ getResearchArtifact(techFeatureId)
140
+ .then((result) => {
141
+ if (cancelled) return;
142
+ if (result.error) {
143
+ toast.error(result.error);
144
+ return;
145
+ }
146
+ if (result.techDecisions) setTechData(result.techDecisions);
147
+ })
148
+ .catch(() => {
149
+ if (!cancelled) toast.error('Failed to load tech decisions');
150
+ })
151
+ .finally(() => {
152
+ if (!cancelled) setIsLoadingTech(false);
153
+ });
154
+ return () => {
155
+ cancelled = true;
156
+ };
157
+ }, [techFeatureId]);
158
+
159
+ const mergeFeatureId = view?.type === 'merge-review' ? view.node.featureId : null;
160
+ useEffect(() => {
161
+ setMergeData(null);
162
+ if (!mergeFeatureId) return;
163
+
164
+ let cancelled = false;
165
+ setIsLoadingMerge(true);
166
+ getMergeReviewData(mergeFeatureId)
167
+ .then((result) => {
168
+ if (cancelled) return;
169
+ if ('error' in result) {
170
+ toast.error(result.error);
171
+ return;
172
+ }
173
+ setMergeData(result);
174
+ })
175
+ .catch(() => {
176
+ if (!cancelled) toast.error('Failed to load merge review data');
177
+ })
178
+ .finally(() => {
179
+ if (!cancelled) setIsLoadingMerge(false);
180
+ });
181
+ return () => {
182
+ cancelled = true;
183
+ };
184
+ }, [mergeFeatureId]);
185
+
186
+ // ── Approve / reject handlers ───────────────────────────────────────────
187
+
188
+ const reviewNode =
189
+ view?.type === 'prd-review' || view?.type === 'tech-review' || view?.type === 'merge-review'
190
+ ? view.node
191
+ : null;
192
+
193
+ const handleReject = useCallback(
194
+ async (feedback: string, label: string, onDone?: () => void) => {
195
+ if (!reviewNode?.featureId) return;
196
+ setIsRejecting(true);
197
+ try {
198
+ const result = await rejectFeature(reviewNode.featureId, feedback);
199
+ if (!result.rejected) {
200
+ toast.error(result.error ?? `Failed to reject ${label.toLowerCase()}`);
201
+ return;
202
+ }
203
+ rejectSound.play();
204
+ toast.success(`${label} rejected — agent re-iterating (iteration ${result.iteration})`);
205
+ if (result.iterationWarning) {
206
+ toast.warning(
207
+ `Iteration ${result.iteration} — consider approving or adjusting feedback to avoid excessive iterations`
208
+ );
209
+ }
210
+ onClose();
211
+ onDone?.();
212
+ } finally {
213
+ setIsRejecting(false);
214
+ }
215
+ },
216
+ [reviewNode, onClose, rejectSound]
217
+ );
218
+
219
+ const handlePrdReject = useCallback(
220
+ (feedback: string) => handleReject(feedback, 'Requirements', () => setPrdSelections({})),
221
+ [handleReject]
222
+ );
223
+ const handleTechReject = useCallback(
224
+ (feedback: string) => handleReject(feedback, 'Plan'),
225
+ [handleReject]
226
+ );
227
+ const handleMergeReject = useCallback(
228
+ (feedback: string) => handleReject(feedback, 'Merge'),
229
+ [handleReject]
230
+ );
231
+
232
+ const handleSimpleApprove = useCallback(
233
+ async (label: string) => {
234
+ if (!reviewNode?.featureId) return;
235
+ const result = await approveFeature(reviewNode.featureId);
236
+ if (!result.approved) {
237
+ toast.error(result.error ?? `Failed to approve ${label.toLowerCase()}`);
238
+ return;
239
+ }
240
+ toast.success(`${label} approved — agent resuming`);
241
+ onClose();
242
+ },
243
+ [reviewNode, onClose]
244
+ );
245
+
246
+ const handlePrdApprove = useCallback(
247
+ async (_actionId: string) => {
248
+ if (view?.type !== 'prd-review') return;
249
+ let payload: PrdApprovalPayload | undefined;
250
+ if (prdData) {
251
+ const changedSelections: QuestionSelectionChange[] = [];
252
+ for (const [questionId, optionId] of Object.entries(prdSelections)) {
253
+ const question = prdData.questions.find((q) => q.id === questionId);
254
+ const option = question?.options.find((o) => o.id === optionId);
255
+ if (question && option) {
256
+ changedSelections.push({ questionId: question.question, selectedOption: option.label });
257
+ }
258
+ }
259
+ payload = { approved: true, changedSelections };
260
+ }
261
+ const result = await approveFeature(view.node.featureId, payload);
262
+ if (!result.approved) {
263
+ toast.error(result.error ?? 'Failed to approve requirements');
264
+ return;
265
+ }
266
+ toast.success('Requirements approved — agent resuming');
267
+ setPrdSelections({});
268
+ onClose();
269
+ },
270
+ [view, prdData, prdSelections, onClose]
271
+ );
272
+
273
+ const handleTechApprove = useCallback(() => handleSimpleApprove('Plan'), [handleSimpleApprove]);
274
+ const handleMergeApprove = useCallback(() => handleSimpleApprove('Merge'), [handleSimpleApprove]);
275
+
276
+ // ── Derived view data ───────────────────────────────────────────────────
277
+
278
+ const isCreateView = view?.type === 'feature-create';
279
+ const createView = isCreateView ? view : null;
280
+
281
+ const featureNode =
282
+ view?.type === 'feature' ||
283
+ view?.type === 'prd-review' ||
284
+ view?.type === 'tech-review' ||
285
+ view?.type === 'merge-review'
286
+ ? view.node
287
+ : null;
288
+
289
+ const repoData = view?.type === 'repository' ? view.data : null;
290
+
291
+ // ── Hooks (always called unconditionally per Rules of Hooks) ────────────
292
+
293
+ const featureActionsInput =
294
+ featureNode?.repositoryPath && featureNode?.branch
295
+ ? {
296
+ repositoryPath: featureNode.repositoryPath,
297
+ branch: featureNode.branch,
298
+ specPath: featureNode.specPath,
299
+ }
300
+ : null;
301
+ const featureActions = useFeatureActions(featureActionsInput);
302
+ const repoActions = useRepositoryActions(
303
+ repoData?.repositoryPath ? { repositoryPath: repoData.repositoryPath } : null
304
+ );
305
+
306
+ // ── Header ──────────────────────────────────────────────────────────────
307
+
308
+ let header: React.ReactNode = undefined;
309
+
310
+ if (featureNode) {
311
+ header = (
312
+ <>
313
+ <div data-testid="feature-drawer-header">
314
+ <DrawerTitle>{featureNode.name}</DrawerTitle>
315
+ {featureNode.description ? (
316
+ <DrawerDescription>{featureNode.description}</DrawerDescription>
317
+ ) : featureNode.featureId ? (
318
+ <DrawerDescription className="sr-only">{featureNode.featureId}</DrawerDescription>
319
+ ) : null}
320
+ </div>
321
+
322
+ {featureActionsInput ? (
323
+ <div className="flex items-center gap-2 pt-2">
324
+ <OpenActionMenu
325
+ actions={featureActions}
326
+ repositoryPath={featureActionsInput.repositoryPath}
327
+ showSpecs={!!featureActionsInput.specPath}
328
+ />
329
+ {onDelete && featureNode.featureId ? (
330
+ <>
331
+ <div className="bg-border mx-1 h-4 w-px" />
332
+ <AlertDialog>
333
+ <AlertDialogTrigger asChild>
334
+ <Button
335
+ variant="ghost"
336
+ size="icon-sm"
337
+ aria-label="Delete feature"
338
+ disabled={isDeleting}
339
+ className="text-muted-foreground hover:text-destructive"
340
+ data-testid="feature-drawer-delete"
341
+ >
342
+ {isDeleting ? (
343
+ <Loader2 className="size-4 animate-spin" />
344
+ ) : (
345
+ <Trash2 className="size-4" />
346
+ )}
347
+ </Button>
348
+ </AlertDialogTrigger>
349
+ <AlertDialogContent>
350
+ <AlertDialogHeader>
351
+ <AlertDialogTitle>Delete feature?</AlertDialogTitle>
352
+ <AlertDialogDescription>
353
+ This will permanently delete <strong>{featureNode.name}</strong> (
354
+ {featureNode.featureId}). This action cannot be undone.
355
+ {featureNode.state === 'running' ? (
356
+ <> This feature has a running agent that will be stopped.</>
357
+ ) : null}
358
+ </AlertDialogDescription>
359
+ </AlertDialogHeader>
360
+ <AlertDialogFooter>
361
+ <AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
362
+ <AlertDialogAction
363
+ variant="destructive"
364
+ disabled={isDeleting}
365
+ onClick={() => onDelete(featureNode.featureId)}
366
+ >
367
+ {isDeleting ? (
368
+ <>
369
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
370
+ Deleting…
371
+ </>
372
+ ) : (
373
+ 'Delete'
374
+ )}
375
+ </AlertDialogAction>
376
+ </AlertDialogFooter>
377
+ </AlertDialogContent>
378
+ </AlertDialog>
379
+ </>
380
+ ) : null}
381
+ </div>
382
+ ) : null}
383
+ </>
384
+ );
385
+ } else if (repoData) {
386
+ header = (
387
+ <div data-testid="repository-drawer-header">
388
+ <DrawerTitle>{repoData.name}</DrawerTitle>
389
+ {repoData.repositoryPath ? (
390
+ <DrawerDescription className="truncate font-mono text-xs">
391
+ {repoData.repositoryPath}
392
+ </DrawerDescription>
393
+ ) : null}
394
+ </div>
395
+ );
396
+ }
397
+
398
+ // ── Body ────────────────────────────────────────────────────────────────
399
+
400
+ let body: React.ReactNode = null;
401
+
402
+ if (view?.type === 'feature' && featureNode) {
403
+ body = (
404
+ <div className="flex-1 overflow-y-auto">
405
+ <div data-testid="feature-drawer-status" className="flex flex-col gap-3 p-4">
406
+ <div className="text-muted-foreground text-xs font-semibold tracking-wider">
407
+ {lifecycleDisplayLabels[featureNode.lifecycle]}
408
+ </div>
409
+ <FeatureStateBadge data={featureNode} />
410
+ {featureNode.progress > 0 ? (
411
+ <div data-testid="feature-drawer-progress" className="flex flex-col gap-1">
412
+ <div className="text-muted-foreground flex items-center justify-between text-xs">
413
+ <span>Progress</span>
414
+ <span>{featureNode.progress}%</span>
415
+ </div>
416
+ <div className="bg-muted h-2 w-full overflow-hidden rounded-full">
417
+ <div
418
+ className={cn(
419
+ 'h-full rounded-full transition-all',
420
+ featureNodeStateConfig[featureNode.state].progressClass
421
+ )}
422
+ style={{ width: `${featureNode.progress}%` }}
423
+ />
424
+ </div>
425
+ </div>
426
+ ) : null}
427
+ </div>
428
+ {featureNode.pr ? (
429
+ <>
430
+ <Separator />
431
+ <FeaturePrInfo pr={featureNode.pr} />
432
+ </>
433
+ ) : null}
434
+ <FeatureDetails data={featureNode} />
435
+ </div>
436
+ );
437
+ } else if (view?.type === 'prd-review') {
438
+ body = prdData ? (
439
+ <PrdQuestionnaire
440
+ data={prdData}
441
+ selections={prdSelections}
442
+ onSelect={(qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId }))}
443
+ onApprove={handlePrdApprove}
444
+ onReject={handlePrdReject}
445
+ isProcessing={isLoadingPrd}
446
+ isRejecting={isRejecting}
447
+ />
448
+ ) : (
449
+ <div className="flex items-center justify-center p-8">
450
+ <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
451
+ </div>
452
+ );
453
+ } else if (view?.type === 'tech-review') {
454
+ body = techData ? (
455
+ <TechDecisionsReview
456
+ data={techData}
457
+ onApprove={handleTechApprove}
458
+ onReject={handleTechReject}
459
+ isProcessing={isLoadingTech}
460
+ isRejecting={isRejecting}
461
+ />
462
+ ) : (
463
+ <div className="flex items-center justify-center p-8">
464
+ <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
465
+ </div>
466
+ );
467
+ } else if (view?.type === 'merge-review') {
468
+ body = mergeData ? (
469
+ <MergeReview
470
+ data={mergeData}
471
+ onApprove={handleMergeApprove}
472
+ onReject={handleMergeReject}
473
+ isProcessing={isLoadingMerge}
474
+ isRejecting={isRejecting}
475
+ />
476
+ ) : (
477
+ <div className="flex items-center justify-center p-8">
478
+ <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
479
+ </div>
480
+ );
481
+ } else if (view?.type === 'repository' && repoData?.repositoryPath) {
482
+ body = (
483
+ <>
484
+ <Separator />
485
+ <div className="flex flex-col gap-3 p-4">
486
+ <div className="text-muted-foreground text-xs font-semibold tracking-wider">
487
+ OPEN WITH
488
+ </div>
489
+ <div className="flex flex-col gap-2">
490
+ <ActionButton
491
+ label="Open in IDE"
492
+ onClick={repoActions.openInIde}
493
+ loading={repoActions.ideLoading}
494
+ error={!!repoActions.ideError}
495
+ icon={Code2}
496
+ variant="outline"
497
+ size="sm"
498
+ />
499
+ <ActionButton
500
+ label="Open in Shell"
501
+ onClick={repoActions.openInShell}
502
+ loading={repoActions.shellLoading}
503
+ error={!!repoActions.shellError}
504
+ icon={Terminal}
505
+ variant="outline"
506
+ size="sm"
507
+ />
508
+ <ActionButton
509
+ label="Open Folder"
510
+ onClick={repoActions.openFolder}
511
+ loading={repoActions.folderLoading}
512
+ error={!!repoActions.folderError}
513
+ icon={FolderOpen}
514
+ variant="outline"
515
+ size="sm"
516
+ />
517
+ </div>
518
+ </div>
519
+ </>
520
+ );
521
+ }
522
+
523
+ // ── Render ──────────────────────────────────────────────────────────────
524
+
525
+ return (
526
+ <>
527
+ {/* Single BaseDrawer for all node-selection views */}
528
+ <BaseDrawer
529
+ open={view !== null && !isCreateView}
530
+ onClose={onClose}
531
+ size="md"
532
+ modal={false}
533
+ header={header}
534
+ data-testid={
535
+ view?.type === 'feature'
536
+ ? 'feature-drawer'
537
+ : view?.type === 'repository'
538
+ ? 'repository-drawer'
539
+ : 'review-drawer'
540
+ }
541
+ >
542
+ {body}
543
+ </BaseDrawer>
544
+
545
+ {/* Feature create drawer — rendered as sibling since it owns its own complex form state */}
546
+ <FeatureCreateDrawer
547
+ open={isCreateView}
548
+ onClose={onClose}
549
+ onSubmit={onCreateSubmit}
550
+ repositoryPath={createView?.repositoryPath ?? ''}
551
+ features={createView?.features ?? []}
552
+ workflowDefaults={createView?.workflowDefaults}
553
+ initialParentId={createView?.initialParentId}
554
+ isSubmitting={isSubmitting}
555
+ />
556
+ </>
557
+ );
558
+ }
559
+
560
+ // ── Private sub-components ────────────────────────────────────────────────────
561
+
562
+ function FeatureStateBadge({ data }: { data: FeatureNodeData }) {
563
+ const config = featureNodeStateConfig[data.state];
564
+ const Icon = config.icon;
565
+ return (
566
+ <div
567
+ className={cn(
568
+ 'flex items-center gap-2 rounded-full px-3 py-1.5 text-sm font-medium',
569
+ config.badgeBgClass,
570
+ config.badgeClass
571
+ )}
572
+ >
573
+ {data.state === 'running' ? (
574
+ <CometSpinner size="sm" className="shrink-0" />
575
+ ) : (
576
+ <Icon className="h-4 w-4 shrink-0" />
577
+ )}
578
+ <span>{config.label}</span>
579
+ </div>
580
+ );
581
+ }
582
+
583
+ const prStatusStyles: Record<PrStatus, string> = {
584
+ [PrStatus.Open]: 'border-transparent bg-blue-50 text-blue-700 hover:bg-blue-50',
585
+ [PrStatus.Merged]: 'border-transparent bg-purple-50 text-purple-700 hover:bg-purple-50',
586
+ [PrStatus.Closed]: 'border-transparent bg-red-50 text-red-700 hover:bg-red-50',
587
+ };
588
+
589
+ function FeaturePrInfo({ pr }: { pr: NonNullable<FeatureNodeData['pr']> }) {
590
+ return (
591
+ <div data-testid="feature-drawer-pr" className="border-border mx-4 rounded-lg border">
592
+ <div className="space-y-3 px-4 py-3">
593
+ <div className="flex items-center justify-between">
594
+ <a
595
+ href={pr.url}
596
+ target="_blank"
597
+ rel="noopener noreferrer"
598
+ className="text-primary flex items-center gap-1.5 text-sm font-semibold underline underline-offset-2"
599
+ >
600
+ PR #{pr.number}
601
+ <ExternalLink className="h-3.5 w-3.5" />
602
+ </a>
603
+ <Badge className={prStatusStyles[pr.status]}>{pr.status}</Badge>
604
+ </div>
605
+ {pr.ciStatus ? (
606
+ <div className="flex items-center justify-between">
607
+ <span className="text-muted-foreground text-xs font-medium">CI Status</span>
608
+ <CiStatusBadge status={pr.ciStatus} />
609
+ </div>
610
+ ) : null}
611
+ {pr.commitHash ? (
612
+ <div className="flex items-center justify-between">
613
+ <span className="text-muted-foreground text-xs font-medium">Commit</span>
614
+ <div className="flex items-center gap-1.5">
615
+ <GitCommitHorizontal className="text-muted-foreground h-3.5 w-3.5" />
616
+ <code className="bg-muted text-foreground rounded-md px-1.5 py-0.5 font-mono text-[11px]">
617
+ {pr.commitHash.slice(0, 7)}
618
+ </code>
619
+ </div>
620
+ </div>
621
+ ) : null}
622
+ </div>
623
+ </div>
624
+ );
625
+ }
626
+
627
+ function FeatureDetails({ data }: { data: FeatureNodeData }) {
628
+ const hasAnyDetail = data.agentType ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
629
+ if (!hasAnyDetail) return null;
630
+ return (
631
+ <>
632
+ <Separator />
633
+ <div data-testid="feature-drawer-details" className="flex flex-col gap-3 p-4">
634
+ {data.agentType ? <DetailRow label="Agent" value={data.agentType} /> : null}
635
+ {data.runtime ? <DetailRow label="Runtime" value={data.runtime} /> : null}
636
+ {data.blockedBy ? <DetailRow label="Blocked by" value={data.blockedBy} /> : null}
637
+ {data.errorMessage ? <DetailRow label="Error" value={data.errorMessage} /> : null}
638
+ </div>
639
+ </>
640
+ );
641
+ }
642
+
643
+ function DetailRow({ label, value }: { label: string; value: string }) {
644
+ return (
645
+ <div className="flex flex-col gap-0.5">
646
+ <span className="text-muted-foreground text-xs font-medium">{label}</span>
647
+ <span className="text-sm">{value}</span>
648
+ </div>
649
+ );
650
+ }