@shepai/cli 1.163.0 → 1.164.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 (275) hide show
  1. package/README.md +12 -0
  2. package/dist/src/presentation/web/app/actions/get-git-log.d.ts +44 -0
  3. package/dist/src/presentation/web/app/actions/get-git-log.d.ts.map +1 -0
  4. package/dist/src/presentation/web/app/actions/get-git-log.js +125 -0
  5. package/dist/src/presentation/web/app/actions/open-folder.d.ts.map +1 -1
  6. package/dist/src/presentation/web/app/actions/open-folder.js +5 -2
  7. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +1 -1
  8. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  9. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +46 -15
  10. package/dist/src/presentation/web/components/common/control-center-drawer/repository-drawer-client.d.ts.map +1 -1
  11. package/dist/src/presentation/web/components/common/control-center-drawer/repository-drawer-client.js +77 -5
  12. package/dist/src/presentation/web/components/common/control-center-drawer/use-drawer-sync.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/control-center-drawer/use-drawer-sync.js +11 -0
  14. package/dist/src/presentation/web/components/common/deployment-status-badge/deployment-status-badge.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/common/deployment-status-badge/deployment-status-badge.js +10 -9
  16. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts +5 -1
  17. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +1 -1
  18. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +24 -13
  19. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
  20. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +56 -81
  21. package/dist/src/presentation/web/components/common/open-action-menu/open-action-menu.d.ts.map +1 -1
  22. package/dist/src/presentation/web/components/common/open-action-menu/open-action-menu.js +11 -6
  23. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  24. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts.map +1 -1
  25. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.js +7 -2
  26. package/dist/src/presentation/web/hooks/use-animations-enabled.d.ts +6 -0
  27. package/dist/src/presentation/web/hooks/use-animations-enabled.d.ts.map +1 -0
  28. package/dist/src/presentation/web/hooks/use-animations-enabled.js +30 -0
  29. package/dist/tsconfig.build.tsbuildinfo +1 -1
  30. package/package.json +1 -1
  31. package/web/.next/BUILD_ID +1 -1
  32. package/web/.next/build-manifest.json +2 -2
  33. package/web/.next/fallback-build-manifest.json +2 -2
  34. package/web/.next/prerender-manifest.json +3 -3
  35. package/web/.next/required-server-files.js +2 -2
  36. package/web/.next/required-server-files.json +2 -2
  37. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +29 -29
  38. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/@drawer/chat/page/server-reference-manifest.json +27 -27
  41. package/web/.next/server/app/(dashboard)/@drawer/chat/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/@drawer/chat/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +30 -30
  44. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +37 -37
  47. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +37 -37
  50. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page/server-reference-manifest.json +69 -54
  53. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page.js +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page.js.nft.json +1 -1
  55. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +69 -54
  57. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
  58. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  59. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/(dashboard)/chat/page/server-reference-manifest.json +27 -27
  61. package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
  62. package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +30 -30
  64. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +37 -37
  67. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  68. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +37 -37
  70. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  71. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +27 -27
  73. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  74. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  75. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page/server-reference-manifest.json +69 -54
  76. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page.js +1 -1
  77. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page.js.nft.json +1 -1
  78. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page_client-reference-manifest.js +1 -1
  79. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +69 -54
  80. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
  81. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  82. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  83. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  84. package/web/.next/server/app/_global-error.html +2 -2
  85. package/web/.next/server/app/_global-error.rsc +1 -1
  86. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  87. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  88. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  89. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  90. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  91. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +6 -6
  92. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  93. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  94. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  95. package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
  96. package/web/.next/server/app/api/graph-data/route.js +1 -1
  97. package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
  98. package/web/.next/server/app/api/interactive/chat/[featureId]/messages/route.js.nft.json +1 -1
  99. package/web/.next/server/app/settings/page/server-reference-manifest.json +9 -9
  100. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  101. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  102. package/web/.next/server/app/skills/page/server-reference-manifest.json +11 -11
  103. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  104. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  105. package/web/.next/server/app/tools/page/server-reference-manifest.json +11 -11
  106. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  107. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  108. package/web/.next/server/app/version/page/server-reference-manifest.json +6 -6
  109. package/web/.next/server/app/version/page.js.nft.json +1 -1
  110. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  111. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  112. package/web/.next/server/chunks/{[root-of-the-server]__beda892a._.js → [root-of-the-server]__c78383b1._.js} +2 -2
  113. package/web/.next/server/chunks/{[root-of-the-server]__beda892a._.js.map → [root-of-the-server]__c78383b1._.js.map} +1 -1
  114. package/web/.next/server/chunks/ssr/403f9_next_dist_esm_ceb2fa1e._.js +1 -1
  115. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  116. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_feature-drawer-client_tsx_e9755fc8._.js +3 -3
  118. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_feature-drawer-client_tsx_e9755fc8._.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/{7f428_lucide-react_dist_esm_icons_4b319ae6._.js → 7f428_lucide-react_dist_esm_icons_281e0ef8._.js} +2 -2
  120. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_281e0ef8._.js.map +1 -0
  121. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_a593f310._.js +3 -0
  122. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_a593f310._.js.map +1 -0
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__1abe77bb._.js +2 -2
  124. package/web/.next/server/chunks/ssr/[root-of-the-server]__1abe77bb._.js.map +1 -1
  125. package/web/.next/server/chunks/ssr/[root-of-the-server]__1f389e5d._.js +1 -1
  126. package/web/.next/server/chunks/ssr/[root-of-the-server]__1f389e5d._.js.map +1 -1
  127. package/web/.next/server/chunks/ssr/[root-of-the-server]__23b5ca2c._.js +1 -1
  128. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  129. package/web/.next/server/chunks/ssr/[root-of-the-server]__563e4faf._.js +1 -1
  130. package/web/.next/server/chunks/ssr/[root-of-the-server]__563e4faf._.js.map +1 -1
  131. package/web/.next/server/chunks/ssr/[root-of-the-server]__7528eb6f._.js +1 -1
  132. package/web/.next/server/chunks/ssr/[root-of-the-server]__7562afc6._.js +2 -2
  133. package/web/.next/server/chunks/ssr/[root-of-the-server]__7562afc6._.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/[root-of-the-server]__821a11c1._.js +1 -1
  135. package/web/.next/server/chunks/ssr/[root-of-the-server]__821a11c1._.js.map +1 -1
  136. package/web/.next/server/chunks/ssr/[root-of-the-server]__86ff0bc5._.js +7 -0
  137. package/web/.next/server/chunks/ssr/[root-of-the-server]__86ff0bc5._.js.map +1 -0
  138. package/web/.next/server/chunks/ssr/[root-of-the-server]__8b0aac03._.js +1 -1
  139. package/web/.next/server/chunks/ssr/[root-of-the-server]__98740ee4._.js +1 -1
  140. package/web/.next/server/chunks/ssr/[root-of-the-server]__98740ee4._.js.map +1 -1
  141. package/web/.next/server/chunks/ssr/[root-of-the-server]__b7b96453._.js +1 -1
  142. package/web/.next/server/chunks/ssr/[root-of-the-server]__b7b96453._.js.map +1 -1
  143. package/web/.next/server/chunks/ssr/[root-of-the-server]__ba9f9e11._.js +1 -1
  144. package/web/.next/server/chunks/ssr/[root-of-the-server]__ba9f9e11._.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/[root-of-the-server]__c9777776._.js +1 -1
  146. package/web/.next/server/chunks/ssr/[root-of-the-server]__c9777776._.js.map +1 -1
  147. package/web/.next/server/chunks/ssr/[root-of-the-server]__e0be67c7._.js +3 -0
  148. package/web/.next/server/chunks/ssr/[root-of-the-server]__e0be67c7._.js.map +1 -0
  149. package/web/.next/server/chunks/ssr/_02e01240._.js +4 -0
  150. package/web/.next/server/chunks/ssr/_02e01240._.js.map +1 -0
  151. package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
  152. package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
  153. package/web/.next/server/chunks/ssr/_0727935d._.js +1 -1
  154. package/web/.next/server/chunks/ssr/_0727935d._.js.map +1 -1
  155. package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
  156. package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
  157. package/web/.next/server/chunks/ssr/_18886033._.js +4 -0
  158. package/web/.next/server/chunks/ssr/_18886033._.js.map +1 -0
  159. package/web/.next/server/chunks/ssr/_22e00a14._.js +1 -1
  160. package/web/.next/server/chunks/ssr/_22e00a14._.js.map +1 -1
  161. package/web/.next/server/chunks/ssr/_2f8f89fb._.js +3 -0
  162. package/web/.next/server/chunks/ssr/_2f8f89fb._.js.map +1 -0
  163. package/web/.next/server/chunks/ssr/_4d49a312._.js +3 -0
  164. package/web/.next/server/chunks/ssr/_4d49a312._.js.map +1 -0
  165. package/web/.next/server/chunks/ssr/{_def82237._.js → _52403a07._.js} +2 -2
  166. package/web/.next/server/chunks/ssr/{_def82237._.js.map → _52403a07._.js.map} +1 -1
  167. package/web/.next/server/chunks/ssr/_56b9d60f._.js +1 -1
  168. package/web/.next/server/chunks/ssr/_56b9d60f._.js.map +1 -1
  169. package/web/.next/server/chunks/ssr/_9215e9ec._.js +3 -0
  170. package/web/.next/server/chunks/ssr/_9215e9ec._.js.map +1 -0
  171. package/web/.next/server/chunks/ssr/_a5a5901d._.js +1 -1
  172. package/web/.next/server/chunks/ssr/_a5a5901d._.js.map +1 -1
  173. package/web/.next/server/chunks/ssr/_ad09f271._.js +4 -0
  174. package/web/.next/server/chunks/ssr/_ad09f271._.js.map +1 -0
  175. package/web/.next/server/chunks/ssr/_b7a43c05._.js +3 -0
  176. package/web/.next/server/chunks/ssr/_b7a43c05._.js.map +1 -0
  177. package/web/.next/server/chunks/ssr/_c3f595c6._.js +1 -1
  178. package/web/.next/server/chunks/ssr/_c3f595c6._.js.map +1 -1
  179. package/web/.next/server/chunks/ssr/_c45aee16._.js +3 -0
  180. package/web/.next/server/chunks/ssr/_c45aee16._.js.map +1 -0
  181. package/web/.next/server/chunks/ssr/{_b881a389._.js → _c9f903f2._.js} +2 -2
  182. package/web/.next/server/chunks/ssr/_c9f903f2._.js.map +1 -0
  183. package/web/.next/server/chunks/ssr/{_ce97386b._.js → _cc654b75._.js} +3 -3
  184. package/web/.next/server/chunks/ssr/_cc654b75._.js.map +1 -0
  185. package/web/.next/server/chunks/ssr/_ea9e1556._.js +4 -0
  186. package/web/.next/server/chunks/ssr/_ea9e1556._.js.map +1 -0
  187. package/web/.next/server/chunks/ssr/_f1ba9be6._.js +2 -2
  188. package/web/.next/server/chunks/ssr/_f1ba9be6._.js.map +1 -1
  189. package/web/.next/server/chunks/ssr/_f33cd07e._.js +2 -2
  190. package/web/.next/server/chunks/ssr/_f33cd07e._.js.map +1 -1
  191. package/web/.next/server/chunks/ssr/_f8b45233._.js +1 -1
  192. package/web/.next/server/chunks/ssr/_f8b45233._.js.map +1 -1
  193. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  194. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  195. package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_repository-drawer-client_tsx_39a00c03._.js +3 -0
  196. package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_repository-drawer-client_tsx_39a00c03._.js.map +1 -0
  197. package/web/.next/server/chunks/ssr/node_modules__pnpm_1300ae39._.js +3 -0
  198. package/web/.next/server/chunks/ssr/node_modules__pnpm_1300ae39._.js.map +1 -0
  199. package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
  200. package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js.map +1 -1
  201. package/web/.next/server/chunks/ssr/src_presentation_web_components_895e5bfa._.js +1 -1
  202. package/web/.next/server/chunks/ssr/src_presentation_web_components_895e5bfa._.js.map +1 -1
  203. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  204. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  205. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_tools_tools-page-client_tsx_3d0aa70c._.js +1 -1
  206. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_tools_tools-page-client_tsx_3d0aa70c._.js.map +1 -1
  207. package/web/.next/server/pages/500.html +2 -2
  208. package/web/.next/server/server-reference-manifest.js +1 -1
  209. package/web/.next/server/server-reference-manifest.json +190 -154
  210. package/web/.next/static/chunks/25e894a1de46b5fb.js +1 -0
  211. package/web/.next/static/chunks/2609c8fc6f14cb26.js +1 -0
  212. package/web/.next/static/chunks/29f0d874b1fde3d6.js +5 -0
  213. package/web/.next/static/chunks/400e93efac983a76.css +1 -0
  214. package/web/.next/static/chunks/498e45f8e05a5491.js +1 -0
  215. package/web/.next/static/chunks/{0d9e3aa7e10cf6e6.js → 56955fa252a9f3ed.js} +2 -2
  216. package/web/.next/static/chunks/779b5c49587b074e.js +1 -0
  217. package/web/.next/static/chunks/7f491899a2fe2fd7.js +1 -0
  218. package/web/.next/static/chunks/966eb2688300b7f5.js +1 -0
  219. package/web/.next/static/chunks/971e52f3f386ccfd.js +1 -0
  220. package/web/.next/static/chunks/a8243f8d06bdcef0.js +5 -0
  221. package/web/.next/static/chunks/b63e6727c84f30e2.js +1 -0
  222. package/web/.next/static/chunks/{0613d3829ce90fe9.js → ce0316338f7e01e6.js} +1 -1
  223. package/web/.next/static/chunks/ce87ded6cc38b4de.js +1 -0
  224. package/web/.next/static/chunks/cf000ba1a3f11439.js +7 -0
  225. package/web/.next/static/chunks/cf75fade434602c6.js +5 -0
  226. package/web/.next/static/chunks/{5f40b5c4f859b573.js → d9e4d90ef254da84.js} +1 -1
  227. package/web/.next/static/chunks/{780f688b8568331d.js → dc21cf85bfa262a7.js} +2 -2
  228. package/web/.next/static/chunks/e6398f94cffe9bc2.js +1 -0
  229. package/web/.next/static/chunks/ecfd93d61bf4d933.js +1 -0
  230. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_4b319ae6._.js.map +0 -1
  231. package/web/.next/server/chunks/ssr/[root-of-the-server]__e5ceba65._.js +0 -3
  232. package/web/.next/server/chunks/ssr/[root-of-the-server]__e5ceba65._.js.map +0 -1
  233. package/web/.next/server/chunks/ssr/[root-of-the-server]__e91ffd5e._.js +0 -7
  234. package/web/.next/server/chunks/ssr/[root-of-the-server]__e91ffd5e._.js.map +0 -1
  235. package/web/.next/server/chunks/ssr/_075fae20._.js +0 -4
  236. package/web/.next/server/chunks/ssr/_075fae20._.js.map +0 -1
  237. package/web/.next/server/chunks/ssr/_125c55af._.js +0 -4
  238. package/web/.next/server/chunks/ssr/_125c55af._.js.map +0 -1
  239. package/web/.next/server/chunks/ssr/_391e499a._.js +0 -3
  240. package/web/.next/server/chunks/ssr/_391e499a._.js.map +0 -1
  241. package/web/.next/server/chunks/ssr/_573caadf._.js +0 -3
  242. package/web/.next/server/chunks/ssr/_573caadf._.js.map +0 -1
  243. package/web/.next/server/chunks/ssr/_628621c7._.js +0 -4
  244. package/web/.next/server/chunks/ssr/_628621c7._.js.map +0 -1
  245. package/web/.next/server/chunks/ssr/_68996e5b._.js +0 -3
  246. package/web/.next/server/chunks/ssr/_68996e5b._.js.map +0 -1
  247. package/web/.next/server/chunks/ssr/_9412b27f._.js +0 -4
  248. package/web/.next/server/chunks/ssr/_9412b27f._.js.map +0 -1
  249. package/web/.next/server/chunks/ssr/_ac4a3873._.js +0 -3
  250. package/web/.next/server/chunks/ssr/_ac4a3873._.js.map +0 -1
  251. package/web/.next/server/chunks/ssr/_b881a389._.js.map +0 -1
  252. package/web/.next/server/chunks/ssr/_ce97386b._.js.map +0 -1
  253. package/web/.next/server/chunks/ssr/_cfbd1d7e._.js +0 -3
  254. package/web/.next/server/chunks/ssr/_cfbd1d7e._.js.map +0 -1
  255. package/web/.next/server/chunks/ssr/node_modules__pnpm_8ec2c790._.js +0 -3
  256. package/web/.next/server/chunks/ssr/node_modules__pnpm_8ec2c790._.js.map +0 -1
  257. package/web/.next/static/chunks/0a79dfbb8486b66e.js +0 -5
  258. package/web/.next/static/chunks/14d9d9181bb4e337.js +0 -1
  259. package/web/.next/static/chunks/4581eaf51543601d.js +0 -1
  260. package/web/.next/static/chunks/47477ed4c5871747.js +0 -1
  261. package/web/.next/static/chunks/74db65fa7bfb80bd.js +0 -1
  262. package/web/.next/static/chunks/8b64414cdb4a4dc7.js +0 -1
  263. package/web/.next/static/chunks/bf55fb50a69f7350.js +0 -5
  264. package/web/.next/static/chunks/c04ada25ace6f30c.js +0 -1
  265. package/web/.next/static/chunks/c3a67566f78b5bc3.js +0 -1
  266. package/web/.next/static/chunks/ca8752df0e809c1b.js +0 -7
  267. package/web/.next/static/chunks/d2cbeefbc8967b16.js +0 -1
  268. package/web/.next/static/chunks/d39ae882d080aa52.js +0 -1
  269. package/web/.next/static/chunks/d6e702c209c413ce.js +0 -5
  270. package/web/.next/static/chunks/dbfbd9d9ee5eb2d0.js +0 -1
  271. package/web/.next/static/chunks/dc39dd4276779dec.js +0 -1
  272. package/web/.next/static/chunks/e779296e738a812b.css +0 -1
  273. /package/web/.next/static/{qfCnoWSr4qmt2_dT28B-O → rav6zzO3q2NtCKwg9XZXP}/_buildManifest.js +0 -0
  274. /package/web/.next/static/{qfCnoWSr4qmt2_dT28B-O → rav6zzO3q2NtCKwg9XZXP}/_clientMiddlewareManifest.json +0 -0
  275. /package/web/.next/static/{qfCnoWSr4qmt2_dT28B-O → rav6zzO3q2NtCKwg9XZXP}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -370,6 +370,18 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for humans and [CONTRIBUTING-AGENTS.md]
370
370
 
371
371
  ---
372
372
 
373
+ ## Star History
374
+
375
+ <a href="https://www.star-history.com/?repos=shep-ai%2Fshep&type=timeline&legend=top-left">
376
+ <picture>
377
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=shep-ai/shep&type=timeline&theme=dark&legend=top-left" />
378
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=shep-ai/shep&type=timeline&legend=top-left" />
379
+ <img alt="Star History Chart" src="https://api.star-history.com/image?repos=shep-ai/shep&type=timeline&legend=top-left" />
380
+ </picture>
381
+ </a>
382
+
383
+ ---
384
+
373
385
  ## License
374
386
 
375
387
  MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,44 @@
1
+ export interface GitLogEntry {
2
+ hash: string;
3
+ shortHash: string;
4
+ subject: string;
5
+ author: string;
6
+ relativeDate: string;
7
+ branch?: string;
8
+ }
9
+ export interface GitBranchInfo {
10
+ name: string;
11
+ isCurrent: boolean;
12
+ lastCommitDate: string;
13
+ }
14
+ export interface GitRemoteInfo {
15
+ name: string;
16
+ url: string;
17
+ }
18
+ export interface GitWorkingTreeStatus {
19
+ staged: number;
20
+ modified: number;
21
+ untracked: number;
22
+ }
23
+ export interface GitDiffStats {
24
+ filesChanged: number;
25
+ insertions: number;
26
+ deletions: number;
27
+ }
28
+ export interface GitRepoInfo {
29
+ commits: GitLogEntry[];
30
+ branches: GitBranchInfo[];
31
+ remotes: GitRemoteInfo[];
32
+ tags: string[];
33
+ stashCount: number;
34
+ currentBranch: string;
35
+ diffStats: GitDiffStats | null;
36
+ workingTree: GitWorkingTreeStatus;
37
+ error?: string;
38
+ }
39
+ export declare function getGitRepoInfo(repositoryPath: string, commitLimit?: number): Promise<GitRepoInfo>;
40
+ export declare function getGitLog(repositoryPath: string, limit?: number): Promise<{
41
+ entries: GitLogEntry[];
42
+ error?: string;
43
+ }>;
44
+ //# sourceMappingURL=get-git-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-git-log.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-git-log.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,oBAAoB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,cAAc,CAClC,cAAc,EAAE,MAAM,EACtB,WAAW,SAAI,GACd,OAAO,CAAC,WAAW,CAAC,CAwHtB;AAGD,wBAAsB,SAAS,CAC7B,cAAc,EAAE,MAAM,EACtB,KAAK,SAAK,GACT,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAGrD"}
@@ -0,0 +1,125 @@
1
+ 'use server';
2
+ import { execFile } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ const execFileAsync = promisify(execFile);
5
+ async function git(cwd, args) {
6
+ const { stdout } = await execFileAsync('git', args, { cwd, timeout: 5000 });
7
+ return stdout.trim();
8
+ }
9
+ export async function getGitRepoInfo(repositoryPath, commitLimit = 8) {
10
+ const empty = {
11
+ commits: [],
12
+ branches: [],
13
+ remotes: [],
14
+ tags: [],
15
+ stashCount: 0,
16
+ currentBranch: '',
17
+ diffStats: null,
18
+ workingTree: { staged: 0, modified: 0, untracked: 0 },
19
+ };
20
+ if (!repositoryPath.trim()) {
21
+ return { ...empty, error: 'Repository path is required' };
22
+ }
23
+ const results = await Promise.allSettled([
24
+ // 0: commits
25
+ git(repositoryPath, [
26
+ 'log',
27
+ `--max-count=${commitLimit}`,
28
+ '--format=%H%x00%h%x00%s%x00%an%x00%cr',
29
+ '--no-color',
30
+ ]),
31
+ // 1: current branch
32
+ git(repositoryPath, ['branch', '--show-current']),
33
+ // 2: branches with dates
34
+ git(repositoryPath, [
35
+ 'branch',
36
+ '--sort=-committerdate',
37
+ '--format=%(HEAD)%(refname:short)%00%(committerdate:relative)',
38
+ '--no-color',
39
+ ]),
40
+ // 3: remotes
41
+ git(repositoryPath, ['remote', '-v']),
42
+ // 4: tags (recent 5)
43
+ git(repositoryPath, ['tag', '--sort=-creatordate', '-l', '--format=%(refname:short)']),
44
+ // 5: stash count
45
+ git(repositoryPath, ['stash', 'list']),
46
+ // 6: working tree status
47
+ git(repositoryPath, ['status', '--porcelain']),
48
+ // 7: diff stats (uncommitted)
49
+ git(repositoryPath, ['diff', '--shortstat']),
50
+ ]);
51
+ const val = (i) => (results[i].status === 'fulfilled' ? results[i].value : '');
52
+ // Parse commits
53
+ const commits = val(0)
54
+ .split('\n')
55
+ .filter(Boolean)
56
+ .map((line) => {
57
+ const [hash, shortHash, subject, author, relativeDate] = line.split('\0');
58
+ return { hash, shortHash, subject, author, relativeDate };
59
+ });
60
+ const currentBranch = val(1);
61
+ if (commits.length > 0 && currentBranch) {
62
+ commits[0].branch = currentBranch;
63
+ }
64
+ // Parse branches
65
+ const branches = val(2)
66
+ .split('\n')
67
+ .filter(Boolean)
68
+ .slice(0, 10)
69
+ .map((line) => {
70
+ const isCurrent = line.startsWith('*');
71
+ const clean = isCurrent ? line.slice(1) : line;
72
+ const [name, lastCommitDate] = clean.split('\0');
73
+ return { name: name.trim(), isCurrent, lastCommitDate: lastCommitDate?.trim() ?? '' };
74
+ });
75
+ // Parse remotes (dedup fetch/push)
76
+ const remoteMap = new Map();
77
+ val(3)
78
+ .split('\n')
79
+ .filter(Boolean)
80
+ .forEach((line) => {
81
+ const match = line.match(/^(\S+)\s+(\S+)\s+\(fetch\)/);
82
+ if (match) {
83
+ // Strip credentials/tokens from URL before sending to client
84
+ const sanitized = match[2].replace(/\/\/[^@]+@/, '//').replace(/x-access-token:[^@]+@/, '');
85
+ remoteMap.set(match[1], sanitized);
86
+ }
87
+ });
88
+ const remotes = Array.from(remoteMap, ([name, url]) => ({ name, url }));
89
+ // Parse tags (top 5)
90
+ const tags = val(4).split('\n').filter(Boolean).slice(0, 5);
91
+ // Stash count
92
+ const stashCount = val(5).split('\n').filter(Boolean).length;
93
+ // Working tree status
94
+ const statusLines = val(6).split('\n').filter(Boolean);
95
+ const workingTree = { staged: 0, modified: 0, untracked: 0 };
96
+ for (const line of statusLines) {
97
+ const x = line[0];
98
+ const y = line[1];
99
+ if (x === '?' && y === '?')
100
+ workingTree.untracked++;
101
+ else if (x !== ' ' && x !== '?')
102
+ workingTree.staged++;
103
+ if (y !== ' ' && y !== '?')
104
+ workingTree.modified++;
105
+ }
106
+ // Diff stats
107
+ let diffStats = null;
108
+ const diffLine = val(7);
109
+ if (diffLine) {
110
+ const files = diffLine.match(/(\d+) file/);
111
+ const ins = diffLine.match(/(\d+) insertion/);
112
+ const del = diffLine.match(/(\d+) deletion/);
113
+ diffStats = {
114
+ filesChanged: files ? parseInt(files[1], 10) : 0,
115
+ insertions: ins ? parseInt(ins[1], 10) : 0,
116
+ deletions: del ? parseInt(del[1], 10) : 0,
117
+ };
118
+ }
119
+ return { commits, branches, remotes, tags, stashCount, currentBranch, diffStats, workingTree };
120
+ }
121
+ // Keep backward compat
122
+ export async function getGitLog(repositoryPath, limit = 10) {
123
+ const info = await getGitRepoInfo(repositoryPath, limit);
124
+ return { entries: info.commits, error: info.error };
125
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"open-folder.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/open-folder.ts"],"names":[],"mappings":"AAiBA,wBAAsB,UAAU,CAC9B,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B9D"}
1
+ {"version":3,"file":"open-folder.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/open-folder.ts"],"names":[],"mappings":"AAiBA,wBAAsB,UAAU,CAC9B,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkC9D"}
@@ -1,7 +1,7 @@
1
1
  'use server';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { platform } from 'node:os';
4
- import { isAbsolute } from 'node:path';
4
+ import { isAbsolute, normalize } from 'node:path';
5
5
  import { spawn } from 'node:child_process';
6
6
  // Use a record lookup instead of if/else to prevent the bundler from
7
7
  // tree-shaking platform branches at build time. Turbopack evaluates
@@ -27,7 +27,10 @@ export async function openFolder(repositoryPath) {
27
27
  error: `Unsupported platform: ${platform()}`,
28
28
  };
29
29
  }
30
- const child = spawn(entry.cmd, entry.args(repositoryPath), {
30
+ // Normalize to platform-native separators — explorer.exe on Windows
31
+ // does not understand forward-slash paths and falls back to Documents.
32
+ const nativePath = normalize(repositoryPath);
33
+ const child = spawn(entry.cmd, entry.args(nativePath), {
31
34
  detached: true,
32
35
  stdio: 'ignore',
33
36
  });
@@ -58,7 +58,7 @@ export function BaseDrawer({ open, onClose, modal = false, dismissOnOutsideClick
58
58
  return (_jsxs(Drawer, { direction: drawerDirection, modal: modal, handleOnly: true, open: open, onOpenChange: (isOpen) => {
59
59
  if (!isOpen)
60
60
  onClose();
61
- }, children: [modal ? _jsx(DrawerOverlay, {}) : null, _jsxs(DrawerContent, { ref: contentRef, direction: drawerDirection, showCloseButton: false, className: cn(drawerVariants({ size }), 'bg-white/85 backdrop-blur-xl dark:bg-neutral-800/85', className), "data-testid": testId, onInteractOutside: modal ? undefined : (e) => e.preventDefault(), children: [_jsx(DrawerTitle, { asChild: true, children: _jsx("span", { className: "sr-only", children: title }) }), _jsx(DrawerDescription, { asChild: true, children: _jsx("span", { className: "sr-only", children: title }) }), _jsxs("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute end-4 top-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", "data-testid": testId ? `${testId}-close-button` : undefined, children: [_jsx(XIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] }), header ? _jsx(DrawerHeader, { className: "shrink-0", children: header }) : null, header ? _jsx(Separator, {}) : null, featureFlags.envDeploy && deployTarget ? _jsx(DeployBar, { deployTarget: deployTarget }) : null, _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: children }), footer ? _jsx(DrawerFooter, { className: "shrink-0", children: footer }) : null] })] }));
61
+ }, children: [modal ? _jsx(DrawerOverlay, {}) : null, _jsxs(DrawerContent, { ref: contentRef, direction: drawerDirection, showCloseButton: false, className: cn(drawerVariants({ size }), 'bg-white/85 backdrop-blur-xl dark:bg-neutral-800/85', className), "data-testid": testId, onInteractOutside: modal ? undefined : (e) => e.preventDefault(), children: [_jsx(DrawerTitle, { asChild: true, children: _jsx("span", { className: "sr-only", children: title }) }), _jsx(DrawerDescription, { asChild: true, children: _jsx("span", { className: "sr-only", children: title }) }), _jsxs("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute end-3 top-2 z-50 rounded-sm p-1 opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", "data-testid": testId ? `${testId}-close-button` : undefined, children: [_jsx(XIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] }), header ? _jsx(DrawerHeader, { className: "shrink-0", children: header }) : null, header ? _jsx(Separator, {}) : null, featureFlags.envDeploy && deployTarget ? _jsx(DeployBar, { deployTarget: deployTarget }) : null, _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: children }), footer ? _jsx(DrawerFooter, { className: "shrink-0", children: footer }) : null] })] }));
62
62
  }
63
63
  function DeployBar({ deployTarget }) {
64
64
  const deployAction = useDeployAction(deployTarget);
@@ -1 +1 @@
1
- {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,WAAW,EACjB,MAAM,EACN,uBAA8B,GAC/B,EAAE,wBAAwB,2CA0uB1B"}
1
+ {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,WAAW,EACjB,MAAM,EACN,uBAA8B,GAC/B,EAAE,wBAAwB,2CAoxB1B"}
@@ -1,13 +1,15 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useCallback, useEffect, useRef } from 'react';
4
+ import { cn } from '../../../lib/utils.js';
4
5
  import { useTranslation } from 'react-i18next';
5
6
  import { useRouter, usePathname } from 'next/navigation';
6
7
  import { toast } from 'sonner';
7
- import { Loader2, Trash2, Play, Square, Copy, Check, Code2, ExternalLink, Archive, ArchiveRestore, } from 'lucide-react';
8
+ import { Loader2, Trash2, Play, Square, Copy, Check, Archive, ArchiveRestore } from 'lucide-react';
8
9
  import { approveFeature } from '../../../app/actions/approve-feature.js';
9
10
  import { resumeFeature } from '../../../app/actions/resume-feature.js';
10
11
  import { startFeature } from '../../../app/actions/start-feature.js';
12
+ import { stopFeature } from '../../../app/actions/stop-feature.js';
11
13
  import { rejectFeature } from '../../../app/actions/reject-feature.js';
12
14
  import { getFeatureArtifact } from '../../../app/actions/get-feature-artifact.js';
13
15
  import { getResearchArtifact } from '../../../app/actions/get-research-artifact.js';
@@ -19,10 +21,8 @@ import { useDeployAction } from '../../../hooks/use-deploy-action.js';
19
21
  import { useAgentEventsContext } from '../../../hooks/agent-events-provider.js';
20
22
  import { BaseDrawer } from '../../common/base-drawer/index.js';
21
23
  import { DeploymentStatusBadge } from '../../common/deployment-status-badge/index.js';
22
- import { Button } from '../../ui/button.js';
23
24
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
24
25
  import { DeleteFeatureDialog } from '../../common/delete-feature-dialog/index.js';
25
- import { ActionButton } from '../../common/action-button/index.js';
26
26
  import { OpenActionMenu } from '../../common/open-action-menu/index.js';
27
27
  import { FeatureDrawerTabs } from '../../common/feature-drawer-tabs/index.js';
28
28
  import { useFeatureActions } from '../../common/feature-drawer/use-feature-actions.js';
@@ -38,9 +38,22 @@ export function FeatureDrawerClient({ view: initialView, urlTab, interactiveAgen
38
38
  const rejectSound = useSoundAction('reject');
39
39
  // Track the view locally so SSE events can update the drawer type in real-time
40
40
  const [view, setView] = useState(initialView);
41
- // Sync when server re-renders with new props (e.g. after navigation)
41
+ // Sync when server re-renders with new props (e.g. after navigation).
42
+ // When reopening the SAME feature, merge server data into the existing view
43
+ // to preserve enriched fields (repositoryName, baseBranch, oneLiner, remoteUrl)
44
+ // that useDrawerSync added but the minimal server component doesn't provide.
42
45
  useEffect(() => {
43
- setView(initialView);
46
+ setView((prev) => {
47
+ if (prev.type === 'feature' &&
48
+ initialView.type === 'feature' &&
49
+ prev.node.featureId === initialView.node.featureId) {
50
+ return {
51
+ ...initialView,
52
+ node: { ...prev.node, ...initialView.node },
53
+ };
54
+ }
55
+ return initialView;
56
+ });
44
57
  }, [initialView]);
45
58
  const featureNode = view.type === 'feature' ? view.node : null;
46
59
  // SSE: update drawer view when feature state changes
@@ -325,17 +338,28 @@ export function FeatureDrawerClient({ view: initialView, urlTab, interactiveAgen
325
338
  return;
326
339
  }
327
340
  toast.success('Feature resumed — agent restarting');
328
- // Optimistically update canvas node
329
341
  window.dispatchEvent(new CustomEvent('shep:feature-approved', {
330
342
  detail: { featureId },
331
343
  }));
332
- // Optimistically update the drawer view
333
344
  setView((prev) => {
334
345
  if (prev.type !== 'feature')
335
346
  return prev;
336
347
  return { ...prev, node: { ...prev.node, state: 'running' } };
337
348
  });
338
349
  }, []);
350
+ const handleStop = useCallback(async (featureId) => {
351
+ const result = await stopFeature(featureId);
352
+ if (!result.stopped) {
353
+ toast.error(result.error ?? 'Failed to stop');
354
+ return;
355
+ }
356
+ toast.success('Agent stopped');
357
+ setView((prev) => {
358
+ if (prev.type !== 'feature')
359
+ return prev;
360
+ return { ...prev, node: { ...prev.node, state: 'error' } };
361
+ });
362
+ }, []);
339
363
  const handleStart = useCallback(async (featureId) => {
340
364
  const result = await startFeature(featureId);
341
365
  if (result.error) {
@@ -401,14 +425,21 @@ export function FeatureDrawerClient({ view: initialView, urlTab, interactiveAgen
401
425
  let headerContent = undefined;
402
426
  if (featureNode) {
403
427
  const shortId = featureNode.featureId.slice(0, 8);
404
- const repoName = featureNode.repositoryName ??
405
- featureNode.repositoryPath.split('/').filter(Boolean).at(-1) ??
406
- '';
407
- headerContent = (_jsxs(_Fragment, { children: [repoName ? (_jsxs("div", { className: "flex items-center gap-1.5 pt-0.5", children: [_jsx(Code2, { className: "text-muted-foreground size-3.5 shrink-0" }), featureNode.remoteUrl ? (_jsxs("a", { href: featureNode.remoteUrl, target: "_blank", rel: "noopener noreferrer", className: "text-muted-foreground hover:text-foreground inline-flex items-center gap-1 text-xs transition-colors", "data-testid": "feature-drawer-repo-link", children: [repoName, _jsx(ExternalLink, { className: "size-3" })] })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: repoName }))] })) : null, featureActionsInput ? (_jsxs("div", { className: "flex items-center gap-2 pt-1", "data-testid": "feature-drawer-actions", children: [featureNode?.state !== 'done' ? (_jsx(OpenActionMenu, { actions: featureActions, repositoryPath: featureActionsInput.repositoryPath, worktreePath: featureActionsInput.worktreePath, showSpecs: !!featureActionsInput.specPath })) : null, featureNode?.state !== 'done' && featureFlags.envDeploy && featureDeployTarget ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(ActionButton, { label: isFeatureDeployActive
408
- ? t('featureDrawer.stopDevServer')
409
- : t('featureDrawer.startDevServer'), onClick: isFeatureDeployActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isFeatureDeployActive ? Square : Play, iconOnly: true, variant: "outline", size: "icon-sm" }) }) }), _jsx(TooltipContent, { children: isFeatureDeployActive
428
+ {
429
+ /* Reusable toolbar icon button classes */
430
+ }
431
+ const tbBtn = 'text-muted-foreground hover:bg-foreground/8 hover:text-foreground inline-flex size-8 items-center justify-center rounded-[3px] disabled:opacity-40';
432
+ const tbSep = 'bg-border/60 mx-1.5 h-5 w-px shrink-0';
433
+ headerContent = (_jsx("div", { "data-testid": "feature-drawer-toolbar", children: _jsxs("div", { className: "flex h-10 items-center px-2", "data-testid": "feature-drawer-actions", children: [featureActionsInput && featureNode?.state !== 'done' ? (_jsx(OpenActionMenu, { actions: featureActions, repositoryPath: featureActionsInput.repositoryPath, worktreePath: featureActionsInput.worktreePath, showSpecs: !!featureActionsInput.specPath })) : null, featureActionsInput &&
434
+ featureNode?.state !== 'done' &&
435
+ featureFlags.envDeploy &&
436
+ featureDeployTarget ? (_jsxs(_Fragment, { children: [_jsx("div", { className: tbSep }), _jsx(TooltipProvider, { delayDuration: 300, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: deployAction.deployLoading || deployAction.stopLoading, onClick: isFeatureDeployActive ? deployAction.stop : deployAction.deploy, className: cn('inline-flex size-7 items-center justify-center rounded-[3px] disabled:opacity-40', isFeatureDeployActive
437
+ ? 'text-red-500 hover:bg-red-500/10 hover:text-red-400'
438
+ : 'text-green-600 hover:bg-green-500/10 dark:text-green-500'), "aria-label": isFeatureDeployActive
410
439
  ? t('featureDrawer.stopDevServer')
411
- : t('featureDrawer.startDevServer') })] }) }), isFeatureDeployActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: featureDeployTarget?.targetId })) : null] })) : null, _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [_jsx("code", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs", children: shortId }), _jsx("button", { type: "button", onClick: handleCopyId, className: "text-muted-foreground hover:text-foreground inline-flex items-center rounded p-0.5 transition-colors", "aria-label": t('featureDrawer.copyFeatureId'), "data-testid": "feature-drawer-copy-id", children: idCopied ? (_jsx(Check, { className: "size-3.5 text-green-600" })) : (_jsx(Copy, { className: "size-3.5" })) })] }), featureNode.featureId ? (_jsxs(_Fragment, { children: [featureNode.state === 'archived' ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": t('featureDrawer.unarchiveFeature'), disabled: isArchiving, className: "text-muted-foreground hover:text-primary", "data-testid": "feature-drawer-unarchive", onClick: () => handleUnarchive(featureNode.featureId), children: isArchiving ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(ArchiveRestore, { className: "size-4" })) }) }), _jsx(TooltipContent, { children: t('featureDrawer.unarchiveFeature') })] }) })) : featureNode.state !== 'deleting' ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": t('featureDrawer.archiveFeature'), disabled: isArchiving, className: "text-muted-foreground hover:text-foreground", "data-testid": "feature-drawer-archive", onClick: () => handleArchive(featureNode.featureId), children: isArchiving ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Archive, { className: "size-4" })) }) }), _jsx(TooltipContent, { children: t('featureDrawer.archiveFeature') })] }) })) : null, _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": t('featureDrawer.deleteFeature'), disabled: isDeleting, className: "text-muted-foreground hover:text-destructive", "data-testid": "feature-drawer-delete", onClick: () => setDeleteDialogOpen(true), children: isDeleting ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Trash2, { className: "size-4" })) }), _jsx(DeleteFeatureDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, onConfirm: (cleanup, cascadeDelete, closePr) => handleDelete(featureNode.featureId, cleanup, cascadeDelete, closePr), isDeleting: isDeleting, featureName: featureNode.name, featureId: featureNode.featureId, hasChildren: featureNode.hasChildren, hasOpenPr: !!featureNode.pr && featureNode.pr.status === 'Open' })] })) : null] })) : null] }));
440
+ : t('featureDrawer.startDevServer'), children: deployAction.deployLoading || deployAction.stopLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : isFeatureDeployActive ? (_jsx(Square, { className: "size-4" })) : (_jsx(Play, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: isFeatureDeployActive
441
+ ? t('featureDrawer.stopDevServer')
442
+ : t('featureDrawer.startDevServer') })] }) }), isFeatureDeployActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: featureDeployTarget?.targetId })) : null] })) : null, _jsx("div", { className: "flex-1" }), _jsxs("div", { className: "flex items-center", children: [_jsx("code", { className: "text-muted-foreground/70 font-mono text-[10px] tracking-wide select-none", children: shortId }), _jsx(TooltipProvider, { delayDuration: 300, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: handleCopyId, className: tbBtn, "aria-label": t('featureDrawer.copyFeatureId'), "data-testid": "feature-drawer-copy-id", children: idCopied ? (_jsx(Check, { className: "size-4 text-green-500" })) : (_jsx(Copy, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: t('featureDrawer.copyFeatureId') })] }) }), featureNode.featureId ? (_jsxs(_Fragment, { children: [_jsx("div", { className: tbSep }), _jsxs(TooltipProvider, { delayDuration: 300, children: [featureNode.state === 'archived' ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: isArchiving, className: tbBtn, "data-testid": "feature-drawer-unarchive", onClick: () => handleUnarchive(featureNode.featureId), children: isArchiving ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(ArchiveRestore, { className: "size-3" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: t('featureDrawer.unarchiveFeature') })] })) : featureNode.state !== 'deleting' ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: isArchiving, className: tbBtn, "data-testid": "feature-drawer-archive", onClick: () => handleArchive(featureNode.featureId), children: isArchiving ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Archive, { className: "size-3" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: t('featureDrawer.archiveFeature') })] })) : null, _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: isDeleting, className: cn(tbBtn, 'hover:bg-destructive/10 hover:text-destructive'), "data-testid": "feature-drawer-delete", onClick: () => setDeleteDialogOpen(true), children: isDeleting ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Trash2, { className: "size-3" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: t('featureDrawer.deleteFeature') })] })] }), _jsx(DeleteFeatureDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, onConfirm: (cleanup, cascadeDelete, closePr) => handleDelete(featureNode.featureId, cleanup, cascadeDelete, closePr), isDeleting: isDeleting, featureName: featureNode.name, featureId: featureNode.featureId, hasChildren: featureNode.hasChildren, hasOpenPr: !!featureNode.pr && featureNode.pr.status === 'Open' })] })) : null] })] }) }));
412
443
  }
413
444
  // ── Body ──────────────────────────────────────────────────────────────
414
445
  let body = null;
@@ -418,7 +449,7 @@ export function FeatureDrawerClient({ view: initialView, urlTab, interactiveAgen
418
449
  ...(featureNode.state === 'error' && { onRetry: handleRetry }),
419
450
  ...(featureNode.state === 'pending' && { onStart: handleStart }),
420
451
  };
421
- body = (_jsx(FeatureDrawerTabs, { featureName: featureNode.name, headerContent: headerContent, featureNode: enrichedNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, sseEvents: events, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, syncStatus: syncFeatureId ? syncData : undefined, syncLoading: syncLoading, syncError: syncError, onRefreshSync: syncFeatureId ? refreshSync : undefined, onRebaseOnMain: syncFeatureId ? featureActions.rebaseOnMain : undefined, rebaseLoading: featureActions.rebaseLoading, rebaseError: featureActions.rebaseError, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput, interactiveAgentEnabled: interactiveAgentEnabled }));
452
+ body = (_jsx(FeatureDrawerTabs, { featureName: featureNode.name, headerContent: headerContent, featureNode: enrichedNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, sseEvents: events, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, syncStatus: syncFeatureId ? syncData : undefined, syncLoading: syncLoading, syncError: syncError, onRefreshSync: syncFeatureId ? refreshSync : undefined, onRebaseOnMain: syncFeatureId ? featureActions.rebaseOnMain : undefined, rebaseLoading: featureActions.rebaseLoading, rebaseError: featureActions.rebaseError, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput, interactiveAgentEnabled: interactiveAgentEnabled, onRetry: handleRetry, onStop: handleStop, onStart: handleStart }));
422
453
  }
423
454
  return (_jsx(BaseDrawer, { open: isOpen, onClose: attemptClose, size: "lg", modal: false, "data-testid": view.type === 'feature' ? 'feature-drawer' : 'repository-drawer', children: body }));
424
455
  }
@@ -1 +1 @@
1
- {"version":3,"file":"repository-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/repository-drawer-client.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAG9E,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,kBAAkB,CAAC;IACzB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAClC;AAED,wBAAgB,sBAAsB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,2BAA2B,2CA6KvF"}
1
+ {"version":3,"file":"repository-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/repository-drawer-client.tsx"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAW9E,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,kBAAkB,CAAC;IACzB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAClC;AAED,wBAAgB,sBAAsB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,2BAA2B,2CAqQvF"}
@@ -1,24 +1,28 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback } from 'react';
3
+ import { useState, useCallback, useEffect } from 'react';
4
4
  import { useRouter, usePathname } from 'next/navigation';
5
- import { Code2, Terminal, FolderOpen, RefreshCw, Play, Square } from 'lucide-react';
5
+ import { Code2, Terminal, FolderOpen, RefreshCw, Play, Square, Copy, Check, Loader2, LayoutDashboard, MessageSquare, GitBranch, GitCommitHorizontal, AlertTriangle, Globe, Tag, Archive, FileEdit, FilePlus, FileCheck2, } from 'lucide-react';
6
+ import { cn } from '../../../lib/utils.js';
6
7
  import { BaseDrawer } from '../../common/base-drawer/index.js';
7
- import { ActionButton } from '../../common/action-button/index.js';
8
8
  import { DeploymentStatusBadge } from '../../common/deployment-status-badge/index.js';
9
- import { Separator } from '../../ui/separator.js';
10
9
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
11
10
  import { Tabs, TabsList, TabsTrigger, TabsContent } from '../../ui/tabs.js';
12
11
  import { useRepositoryActions } from '../../common/repository-node/use-repository-actions.js';
13
12
  import { useDeployAction } from '../../../hooks/use-deploy-action.js';
14
13
  import { useFeatureFlags } from '../../../hooks/feature-flags-context.js';
15
14
  import { ChatTab } from '../../features/chat/ChatTab.js';
15
+ import { getGitRepoInfo } from '../../../app/actions/get-git-log.js';
16
+ const COPY_FEEDBACK_DELAY = 2000;
17
+ const tbBtn = 'text-muted-foreground hover:bg-foreground/8 hover:text-foreground inline-flex size-8 items-center justify-center rounded-[3px] disabled:opacity-40';
18
+ const tbSep = 'bg-border/60 mx-1.5 h-5 w-px shrink-0';
16
19
  export function RepositoryDrawerClient({ data, initialTab }) {
17
20
  const featureFlags = useFeatureFlags();
18
21
  const router = useRouter();
19
22
  const pathname = usePathname();
20
23
  const isOpen = pathname.startsWith('/repository/');
21
24
  const [activeTab, setActiveTab] = useState(initialTab ?? 'overview');
25
+ const [pathCopied, setPathCopied] = useState(false);
22
26
  const repoActions = useRepositoryActions(data.repositoryPath ? { repositoryId: data.id, repositoryPath: data.repositoryPath } : null);
23
27
  const onClose = useCallback(() => {
24
28
  router.push('/');
@@ -31,7 +35,75 @@ export function RepositoryDrawerClient({ data, initialTab }) {
31
35
  }
32
36
  : null);
33
37
  const isDeployActive = deployAction.status === 'Booting' || deployAction.status === 'Ready';
38
+ const handleCopyPath = useCallback(() => {
39
+ if (!data.repositoryPath)
40
+ return;
41
+ void navigator.clipboard.writeText(data.repositoryPath);
42
+ setPathCopied(true);
43
+ setTimeout(() => setPathCopied(false), COPY_FEEDBACK_DELAY);
44
+ }, [data.repositoryPath]);
34
45
  // Session ID for repo chat — deterministic per repo
35
46
  const repoSessionId = data.id ? `repo-${data.id}` : `repo-${data.name}`;
36
- return (_jsx(BaseDrawer, { open: isOpen, onClose: onClose, size: "lg", modal: false, "data-testid": "repository-drawer", children: _jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "shrink-0 px-4 pt-4 pb-3", "data-testid": "repository-drawer-header", children: [_jsxs("div", { className: "flex items-baseline gap-4 pe-6", children: [_jsx("h2", { className: "text-foreground min-w-0 shrink truncate text-base font-semibold tracking-tight", children: data.name }), _jsxs(TabsList, { className: "h-auto shrink-0 gap-0.5 rounded-none border-0 bg-transparent p-0", children: [_jsx(TabsTrigger, { value: "overview", className: "text-muted-foreground hover:text-foreground data-[state=active]:text-foreground data-[state=active]:border-primary h-auto rounded-none border-b-2 border-transparent bg-transparent px-2 py-0.5 text-[12px] font-medium shadow-none transition-colors data-[state=active]:bg-transparent data-[state=active]:shadow-none", children: "Overview" }), _jsx(TabsTrigger, { value: "chat", className: "text-muted-foreground hover:text-foreground data-[state=active]:text-foreground data-[state=active]:border-primary h-auto rounded-none border-b-2 border-transparent bg-transparent px-2 py-0.5 text-[12px] font-medium shadow-none transition-colors data-[state=active]:bg-transparent data-[state=active]:shadow-none", children: "Chat" })] })] }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [data.repositoryPath ? (_jsx("p", { className: "text-muted-foreground min-w-0 truncate font-mono text-xs", children: data.repositoryPath })) : null, featureFlags.envDeploy && data.repositoryPath ? (_jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(ActionButton, { label: isDeployActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isDeployActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isDeployActive ? Square : Play, iconOnly: true, variant: "outline", size: "icon-sm" }) }) }), _jsx(TooltipContent, { children: isDeployActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isDeployActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] })] }), _jsx(Separator, {}), _jsx(TabsContent, { value: "overview", className: "mt-0 flex-1 overflow-y-auto", children: data.repositoryPath ? (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "OPEN WITH" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(ActionButton, { label: "Open in IDE", onClick: repoActions.openInIde, loading: repoActions.ideLoading, error: !!repoActions.ideError, icon: Code2, variant: "outline", size: "sm" }), _jsx(ActionButton, { label: "Open in Shell", onClick: repoActions.openInShell, loading: repoActions.shellLoading, error: !!repoActions.shellError, icon: Terminal, variant: "outline", size: "sm" }), _jsx(ActionButton, { label: "Open Folder", onClick: repoActions.openFolder, loading: repoActions.folderLoading, error: !!repoActions.folderError, icon: FolderOpen, variant: "outline", size: "sm" })] })] }), data.id && featureFlags.gitRebaseSync ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "GIT OPERATIONS" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(ActionButton, { label: "Sync Main", onClick: repoActions.syncMain, loading: repoActions.syncLoading, error: !!repoActions.syncError, icon: RefreshCw, variant: "outline", size: "sm" }), repoActions.syncError ? (_jsx("p", { className: "text-destructive text-xs", children: repoActions.syncError })) : null] })] })] })) : null] })) : null }), _jsx(TabsContent, { value: "chat", className: "mt-0 flex min-h-0 flex-1 flex-col overflow-hidden", children: _jsx(ChatTab, { featureId: repoSessionId, worktreePath: data.repositoryPath }) })] }) }));
47
+ return (_jsx(BaseDrawer, { open: isOpen, onClose: onClose, size: "lg", modal: false, "data-testid": "repository-drawer", children: _jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex min-h-0 flex-1 flex-col", children: [_jsxs(TabsList, { className: "bg-muted/50 h-auto w-full shrink-0 justify-start gap-0 rounded-none border-b p-0", children: [_jsxs(TabsTrigger, { value: "overview", className: "text-muted-foreground hover:bg-muted hover:text-foreground data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:border-t-primary [&:not([data-state=active])]:border-r-border relative h-auto rounded-none border-t-2 border-r border-t-transparent border-r-transparent bg-transparent px-3.5 py-2.5 text-[13px] font-normal shadow-none transition-none last:border-r-transparent data-[state=active]:shadow-none", children: [_jsx(LayoutDashboard, { className: "mr-1.5 size-4" }), "Overview"] }), _jsxs(TabsTrigger, { value: "chat", className: "text-muted-foreground hover:bg-muted hover:text-foreground data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:border-t-primary [&:not([data-state=active])]:border-r-border relative h-auto rounded-none border-t-2 border-r border-t-transparent border-r-transparent bg-transparent px-3.5 py-2.5 text-[13px] font-normal shadow-none transition-none last:border-r-transparent data-[state=active]:shadow-none", children: [_jsx(MessageSquare, { className: "mr-1.5 size-4" }), "Chat"] })] }), _jsxs("div", { className: "bg-muted/40 shrink-0 border-b", children: [_jsxs("div", { className: "flex items-center gap-2 px-4 pe-10 pt-2.5 pb-2", "data-testid": "repository-drawer-header", children: [_jsx("h2", { className: "text-foreground min-w-0 truncate text-base font-semibold tracking-tight", children: data.name }), data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground/40 text-xs", children: "/" }), _jsx("span", { className: "text-muted-foreground min-w-0 truncate font-mono text-[11px]", children: data.repositoryPath })] })) : null] }), data.repositoryPath ? (_jsx("div", { "data-testid": "repository-drawer-toolbar", children: _jsxs("div", { className: "flex h-9 items-center px-1.5", children: [_jsxs(TooltipProvider, { delayDuration: 300, children: [_jsxs("div", { className: "flex items-center", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: tbBtn, onClick: repoActions.openInIde, disabled: repoActions.ideLoading, "aria-label": "Open in IDE", children: repoActions.ideLoading ? (_jsx(Loader2, { className: "size-3.5 animate-spin" })) : (_jsx(Code2, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: "Open in IDE" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: tbBtn, onClick: repoActions.openInShell, disabled: repoActions.shellLoading, "aria-label": "Open terminal", children: repoActions.shellLoading ? (_jsx(Loader2, { className: "size-3.5 animate-spin" })) : (_jsx(Terminal, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: "Open terminal" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: tbBtn, onClick: repoActions.openFolder, disabled: repoActions.folderLoading, "aria-label": "Open folder", children: repoActions.folderLoading ? (_jsx(Loader2, { className: "size-3.5 animate-spin" })) : (_jsx(FolderOpen, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: "Open folder" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: tbBtn, onClick: handleCopyPath, "aria-label": "Copy path", children: pathCopied ? (_jsx(Check, { className: "size-3.5 text-green-500" })) : (_jsx(Copy, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: pathCopied ? 'Copied!' : 'Copy path' })] })] }), data.id && featureFlags.gitRebaseSync ? (_jsxs(_Fragment, { children: [_jsx("div", { className: tbSep }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: tbBtn, onClick: repoActions.syncMain, disabled: repoActions.syncLoading, "aria-label": "Sync main", children: repoActions.syncLoading ? (_jsx(Loader2, { className: "size-3.5 animate-spin" })) : (_jsx(RefreshCw, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: "Sync main" })] })] })) : null, featureFlags.envDeploy ? (_jsxs(_Fragment, { children: [_jsx("div", { className: tbSep }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: deployAction.deployLoading || deployAction.stopLoading, onClick: isDeployActive ? deployAction.stop : deployAction.deploy, className: cn('inline-flex size-7 items-center justify-center rounded-[3px] disabled:opacity-40', isDeployActive
48
+ ? 'text-red-500 hover:bg-red-500/10 hover:text-red-400'
49
+ : 'text-green-500 hover:bg-green-500/10 hover:text-green-400'), "aria-label": isDeployActive ? 'Stop dev server' : 'Start dev server', children: deployAction.deployLoading || deployAction.stopLoading ? (_jsx(Loader2, { className: "size-3.5 animate-spin" })) : isDeployActive ? (_jsx(Square, { className: "size-4" })) : (_jsx(Play, { className: "size-4" })) }) }), _jsx(TooltipContent, { side: "bottom", className: "text-xs", children: isDeployActive ? 'Stop dev server' : 'Start dev server' })] }), isDeployActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] }), _jsx("div", { className: "flex-1" })] }) })) : null] }), _jsx(TabsContent, { value: "overview", className: "mt-0 flex-1 overflow-y-auto", children: _jsx(RepoOverview, { data: data, syncError: repoActions.syncError }) }), _jsx(TabsContent, { value: "chat", className: "mt-0 flex min-h-0 flex-1 flex-col overflow-hidden", children: _jsx(ChatTab, { featureId: repoSessionId, worktreePath: data.repositoryPath }) })] }) }));
50
+ }
51
+ // ── Repo Overview ───────────────────────────────────────────────────
52
+ function Section({ icon: Icon, title, children, }) {
53
+ return (_jsxs("div", { className: "px-3 pt-4 pb-1", children: [_jsxs("div", { className: "text-foreground mb-2 flex items-center gap-1.5 text-sm font-semibold tracking-wider uppercase", children: [_jsx(Icon, { className: "size-4 opacity-50" }), title] }), children] }));
54
+ }
55
+ function Card({ children, className }) {
56
+ return (_jsx("div", { className: cn('bg-muted/60 rounded-md border border-transparent p-3', className), children: children }));
57
+ }
58
+ function KV({ label, children }) {
59
+ return (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-foreground/40 text-[11px] font-medium tracking-wider uppercase", children: label }), _jsx("span", { className: "text-sm leading-snug", children: children })] }));
60
+ }
61
+ function RepoOverview({ data, syncError, }) {
62
+ const [repoInfo, setRepoInfo] = useState(null);
63
+ const [loading, setLoading] = useState(false);
64
+ useEffect(() => {
65
+ if (!data.repositoryPath)
66
+ return;
67
+ let cancelled = false;
68
+ setLoading(true);
69
+ getGitRepoInfo(data.repositoryPath, 8)
70
+ .then((result) => {
71
+ if (!cancelled)
72
+ setRepoInfo(result);
73
+ })
74
+ .catch(() => {
75
+ // Silently handle — repo info is non-critical
76
+ })
77
+ .finally(() => {
78
+ if (!cancelled)
79
+ setLoading(false);
80
+ });
81
+ return () => {
82
+ cancelled = true;
83
+ };
84
+ }, [data.repositoryPath]);
85
+ if (!data.repositoryPath)
86
+ return null;
87
+ const wt = repoInfo?.workingTree;
88
+ const isDirty = wt && (wt.staged > 0 || wt.modified > 0 || wt.untracked > 0);
89
+ const ds = repoInfo?.diffStats;
90
+ return (_jsxs("div", { className: "pb-4", children: [loading ? (_jsxs("div", { className: "text-foreground/40 flex items-center gap-2 px-4 py-8 text-sm", children: [_jsx(Loader2, { className: "size-4 animate-spin" }), " Loading repository info..."] })) : null, repoInfo ? (_jsxs("div", { className: "grid grid-cols-2 gap-2 px-3 pt-3", children: [_jsx(Card, { children: _jsxs(KV, { label: "Branch", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx(GitBranch, { className: "text-foreground/30 size-3.5 shrink-0" }), _jsx("code", { className: "font-mono text-sm", children: repoInfo.currentBranch ?? data.branch ?? '—' })] }), data.behindCount != null && data.behindCount > 0 ? (_jsxs("span", { className: "mt-0.5 block text-xs text-orange-600 dark:text-orange-400", children: [data.behindCount, " behind default"] })) : null] }) }), _jsx(Card, { children: _jsx(KV, { label: "Working Tree", children: isDirty ? (_jsxs("div", { className: "flex flex-wrap gap-x-3 gap-y-0.5", children: [wt.staged > 0 ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-sm text-green-600 dark:text-green-400", children: [_jsx(FileCheck2, { className: "size-3.5" }), " ", wt.staged, " staged"] })) : null, wt.modified > 0 ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-sm text-amber-600 dark:text-amber-400", children: [_jsx(FileEdit, { className: "size-3.5" }), " ", wt.modified, " modified"] })) : null, wt.untracked > 0 ? (_jsxs("span", { className: "text-foreground/50 inline-flex items-center gap-1 text-sm", children: [_jsx(FilePlus, { className: "size-3.5" }), " ", wt.untracked, " untracked"] })) : null] })) : (_jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm text-green-600 dark:text-green-400", children: [_jsx(Check, { className: "size-3.5" }), " Clean"] })) }) }), ds ? (_jsx(Card, { children: _jsx(KV, { label: "Uncommitted Changes", children: _jsxs("div", { className: "flex items-center gap-3 text-sm", children: [_jsxs("span", { children: [ds.filesChanged, " file", ds.filesChanged !== 1 ? 's' : ''] }), _jsxs("span", { className: "text-green-600 dark:text-green-400", children: ["+", ds.insertions] }), _jsxs("span", { className: "text-red-500 dark:text-red-400", children: ["-", ds.deletions] })] }) }) })) : null, repoInfo.remotes.length > 0 ? (_jsx(Card, { children: _jsx(KV, { label: "Remote", children: repoInfo.remotes.map((r) => (_jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm", children: [_jsx(Globe, { className: "text-foreground/30 size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: r.url
91
+ .replace(/\.git$/, '')
92
+ .replace(/^https?:\/\/([^@]+@)?/, '')
93
+ .replace(/x-access-token:[^@]+@/, '') })] }, r.name))) }) })) : null, repoInfo.stashCount > 0 ? (_jsx(Card, { children: _jsx(KV, { label: "Stashes", children: _jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm", children: [_jsx(Archive, { className: "text-foreground/30 size-3.5 shrink-0" }), repoInfo.stashCount, " stash", repoInfo.stashCount !== 1 ? 'es' : ''] }) }) })) : null, repoInfo.tags.length > 0 ? (_jsx(Card, { children: _jsx(KV, { label: "Tags", children: _jsx("div", { className: "flex flex-wrap gap-1.5", children: repoInfo.tags.map((t) => (_jsxs("span", { className: "bg-foreground/[0.04] inline-flex items-center gap-0.5 rounded px-1.5 py-0.5 text-xs", children: [_jsx(Tag, { className: "text-foreground/30 size-2.5" }), t] }, t))) }) }) })) : null] })) : null, repoInfo && repoInfo.commits.length > 0 ? (_jsx(Section, { icon: GitCommitHorizontal, title: "Recent Commits", children: _jsxs("div", { className: "relative ml-3", children: [_jsx("div", { className: "bg-border absolute top-2 bottom-2 left-[5px] w-px" }), repoInfo.commits.map((c, i) => (_jsxs("div", { className: "group relative flex gap-3 py-1.5", children: [_jsx("div", { className: cn('relative z-10 mt-1.5 size-[11px] shrink-0 rounded-full border-2', i === 0
94
+ ? 'border-primary bg-primary'
95
+ : 'border-border bg-background group-hover:border-foreground/30') }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("p", { className: "min-w-0 truncate text-sm leading-snug", children: c.subject }), _jsx("code", { className: "text-foreground/30 shrink-0 font-mono text-[11px]", children: c.shortHash })] }), _jsxs("div", { className: "text-foreground/40 mt-0.5 flex items-center gap-2 text-xs", children: [_jsx("span", { children: c.author }), _jsx("span", { children: "\u00B7" }), _jsx("span", { children: c.relativeDate }), c.branch ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "\u00B7" }), _jsxs("span", { className: "inline-flex items-center gap-0.5", children: [_jsx(GitBranch, { className: "size-3" }), c.branch] })] })) : null] })] })] }, c.hash)))] }) })) : null, repoInfo && repoInfo.branches.length > 1 ? (_jsx(Section, { icon: GitBranch, title: "Branches", children: _jsx("div", { className: "flex flex-col", children: [...repoInfo.branches]
96
+ .sort((a, b) => {
97
+ const isDefault = (n) => /^(main|master)$/.test(n);
98
+ if (a.isCurrent && !b.isCurrent)
99
+ return -1;
100
+ if (!a.isCurrent && b.isCurrent)
101
+ return 1;
102
+ if (isDefault(a.name) && !isDefault(b.name))
103
+ return -1;
104
+ if (!isDefault(a.name) && isDefault(b.name))
105
+ return 1;
106
+ return 0;
107
+ })
108
+ .map((b) => (_jsxs("div", { className: cn('flex items-center justify-between rounded px-2 py-1.5', b.isCurrent && 'bg-muted/60'), children: [_jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm", children: [b.isCurrent ? (_jsx("span", { className: "size-2 shrink-0 rounded-full bg-green-500" })) : /^(main|master)$/.test(b.name) ? (_jsx("span", { className: "size-2 shrink-0 rounded-full bg-blue-500" })) : (_jsx("span", { className: "bg-foreground/10 size-2 shrink-0 rounded-full" })), _jsx("code", { className: "font-mono text-[13px]", children: b.name }), b.isCurrent ? (_jsx("span", { className: "text-foreground/40 text-[10px]", children: "current" })) : null] }), _jsx("span", { className: "text-foreground/40 text-xs", children: b.lastCommitDate })] }, b.name))) }) })) : null, syncError ? (_jsx(Section, { icon: AlertTriangle, title: "Issues", children: _jsx(Card, { className: "bg-destructive/5 border-transparent", children: _jsx("p", { className: "text-destructive text-sm", children: syncError }) }) })) : null] }));
37
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-drawer-sync.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/use-drawer-sync.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,GACxD,IAAI,CAwCN"}
1
+ {"version":3,"file":"use-drawer-sync.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/use-drawer-sync.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,GACxD,IAAI,CAkDN"}
@@ -16,14 +16,21 @@ const BACKGROUND_SYNC_INTERVAL_MS = 15_000;
16
16
  export function useDrawerSync(isOpen, featureId, setView) {
17
17
  const wasOpenRef = useRef(isOpen);
18
18
  const isFetchingRef = useRef(false);
19
+ // Incremented on every open transition — stale fetches from a previous
20
+ // open/close cycle are discarded so they can't overwrite fresh data.
21
+ const generationRef = useRef(0);
19
22
  const syncFromServer = useCallback(async () => {
20
23
  if (!featureId || isFetchingRef.current)
21
24
  return;
22
25
  isFetchingRef.current = true;
26
+ const gen = generationRef.current;
23
27
  try {
24
28
  const data = await getFeatureDrawerData(featureId);
25
29
  if (!data)
26
30
  return;
31
+ // Discard result if the drawer was closed and reopened while fetching
32
+ if (gen !== generationRef.current)
33
+ return;
27
34
  setView((prev) => mergeFeatureData(prev, data));
28
35
  }
29
36
  catch {
@@ -40,6 +47,10 @@ export function useDrawerSync(isOpen, featureId, setView) {
40
47
  useEffect(() => {
41
48
  if (isOpen && (!wasOpenRef.current || !hasFetchedOnMountRef.current)) {
42
49
  hasFetchedOnMountRef.current = true;
50
+ // New open transition — bump generation to invalidate any in-flight fetch
51
+ // from a previous cycle and reset the fetching guard.
52
+ generationRef.current += 1;
53
+ isFetchingRef.current = false;
43
54
  void syncFromServer();
44
55
  }
45
56
  wasOpenRef.current = isOpen;
@@ -1 +1 @@
1
- {"version":3,"file":"deployment-status-badge.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/deployment-status-badge/deployment-status-badge.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAIvE,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,0BAA0B,kDAgF1F"}
1
+ {"version":3,"file":"deployment-status-badge.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/deployment-status-badge/deployment-status-badge.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAIvE,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,0BAA0B,kDAkG1F"}