@shepai/cli 1.124.0 → 1.126.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 (227) hide show
  1. package/dist/packages/core/src/application/ports/output/services/deployment-service.interface.d.ts +13 -2
  2. package/dist/packages/core/src/application/ports/output/services/deployment-service.interface.d.ts.map +1 -1
  3. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  4. package/dist/packages/core/src/infrastructure/di/container.js +2 -0
  5. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.d.ts +21 -0
  6. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.d.ts.map +1 -0
  7. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.js +32 -0
  8. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.d.ts.map +1 -1
  9. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.js +9 -0
  10. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts +24 -14
  11. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts.map +1 -1
  12. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.js +203 -31
  13. package/dist/packages/core/src/infrastructure/services/deployment/parse-port.d.ts +0 -6
  14. package/dist/packages/core/src/infrastructure/services/deployment/parse-port.d.ts.map +1 -1
  15. package/dist/packages/core/src/infrastructure/services/deployment/parse-port.js +6 -1
  16. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
  17. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +7 -2
  18. package/dist/src/presentation/web/app/actions/deploy-feature.js +1 -1
  19. package/dist/src/presentation/web/app/actions/deploy-repository.js +1 -1
  20. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  21. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +7 -4
  22. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  23. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +12 -3
  24. package/dist/src/presentation/web/components/features/settings/notification-settings-section.d.ts.map +1 -1
  25. package/dist/src/presentation/web/components/features/settings/notification-settings-section.js +4 -8
  26. package/dist/src/presentation/web/components/features/settings/notification-settings-section.stories.js +3 -3
  27. package/dist/src/presentation/web/components/features/settings/settings-page-client.d.ts.map +1 -1
  28. package/dist/src/presentation/web/components/features/settings/settings-page-client.js +12 -37
  29. package/dist/src/presentation/web/components/features/settings/settings-page-client.stories.d.ts.map +1 -1
  30. package/dist/src/presentation/web/components/features/settings/settings-page-client.stories.js +2 -3
  31. package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
  32. package/dist/src/presentation/web/hooks/use-deploy-action.js +69 -10
  33. package/dist/src/presentation/web/lib/layout-with-dagre.js +3 -3
  34. package/dist/tsconfig.build.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/web/.next/BUILD_ID +1 -1
  37. package/web/.next/build-manifest.json +2 -2
  38. package/web/.next/fallback-build-manifest.json +2 -2
  39. package/web/.next/prerender-manifest.json +3 -3
  40. package/web/.next/required-server-files.js +2 -2
  41. package/web/.next/required-server-files.json +2 -2
  42. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +57 -57
  43. package/web/.next/server/app/(dashboard)/@drawer/create/page.js +2 -2
  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 +69 -69
  47. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js +2 -2
  48. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +69 -69
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +2 -2
  52. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  53. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +53 -53
  55. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +2 -2
  56. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  57. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +57 -57
  59. package/web/.next/server/app/(dashboard)/create/page.js +2 -2
  60. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  61. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +69 -69
  63. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js +2 -2
  64. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +69 -69
  67. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +2 -2
  68. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  69. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +53 -53
  71. package/web/.next/server/app/(dashboard)/page.js +2 -2
  72. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  73. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +53 -53
  75. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +2 -2
  76. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  77. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  78. package/web/.next/server/app/_global-error.html +2 -2
  79. package/web/.next/server/app/_global-error.rsc +1 -1
  80. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  81. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  82. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  83. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  84. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  85. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  86. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  87. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  88. package/web/.next/server/app/settings/page/server-reference-manifest.json +6 -6
  89. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  90. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  91. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  92. package/web/.next/server/app/skills/page.js +1 -2
  93. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  94. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  95. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  96. package/web/.next/server/app/tools/page.js +1 -2
  97. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  98. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  99. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  100. package/web/.next/server/app/version/page.js.nft.json +1 -1
  101. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  102. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js +1 -1
  103. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js.map +1 -1
  104. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  105. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  106. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  107. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_fdfbf5cc.js +3 -0
  108. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_fdfbf5cc.js.map +1 -0
  109. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_d8fa0b5f.js +3 -0
  110. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_d8fa0b5f.js.map +1 -0
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__06fef644._.js +1 -1
  112. package/web/.next/server/chunks/ssr/{[root-of-the-server]__ce6057f6._.js → [root-of-the-server]__248b9fc3._.js} +2 -2
  113. package/web/.next/server/chunks/ssr/{[root-of-the-server]__9e8b25ad._.js.map → [root-of-the-server]__248b9fc3._.js.map} +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__357de47c._.js +4 -0
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__357de47c._.js.map +1 -0
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__56ecbe40._.js +3 -0
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__56ecbe40._.js.map +1 -0
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__67fe07fe._.js +4 -0
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__67fe07fe._.js.map +1 -0
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__68c2d48e._.js +3 -0
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__68c2d48e._.js.map +1 -0
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
  125. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  126. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  127. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/[root-of-the-server]__8c553503._.js +2 -2
  129. package/web/.next/server/chunks/ssr/[root-of-the-server]__a50a7e39._.js +4 -0
  130. package/web/.next/server/chunks/ssr/[root-of-the-server]__a50a7e39._.js.map +1 -0
  131. package/web/.next/server/chunks/ssr/{[root-of-the-server]__7cbda1ea._.js → [root-of-the-server]__dfc367f2._.js} +2 -2
  132. package/web/.next/server/chunks/ssr/{[root-of-the-server]__ce6057f6._.js.map → [root-of-the-server]__dfc367f2._.js.map} +1 -1
  133. package/web/.next/server/chunks/ssr/{[root-of-the-server]__9e8b25ad._.js → [root-of-the-server]__e7f61cc6._.js} +2 -2
  134. package/web/.next/server/chunks/ssr/{[root-of-the-server]__7cbda1ea._.js.map → [root-of-the-server]__e7f61cc6._.js.map} +1 -1
  135. package/web/.next/server/chunks/ssr/{[root-of-the-server]__aed1d6f8._.js → [root-of-the-server]__fe48d2dc._.js} +2 -2
  136. package/web/.next/server/chunks/ssr/{[root-of-the-server]__aed1d6f8._.js.map → [root-of-the-server]__fe48d2dc._.js.map} +1 -1
  137. package/web/.next/server/chunks/ssr/_243e3685._.js +3 -0
  138. package/web/.next/server/chunks/ssr/_243e3685._.js.map +1 -0
  139. package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
  140. package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
  141. package/web/.next/server/chunks/ssr/_7dca1882._.js +3 -0
  142. package/web/.next/server/chunks/ssr/_7dca1882._.js.map +1 -0
  143. package/web/.next/server/chunks/ssr/{_f84c16ab._.js → _821bbcc5._.js} +2 -2
  144. package/web/.next/server/chunks/ssr/{_f84c16ab._.js.map → _821bbcc5._.js.map} +1 -1
  145. package/web/.next/server/chunks/ssr/{_0e506641._.js → _918887c5._.js} +2 -2
  146. package/web/.next/server/chunks/ssr/_918887c5._.js.map +1 -0
  147. package/web/.next/server/chunks/ssr/{_1bb131c4._.js → _d4b20e29._.js} +2 -2
  148. package/web/.next/server/chunks/ssr/_d4b20e29._.js.map +1 -0
  149. package/web/.next/server/chunks/ssr/_e9703421._.js +1 -1
  150. package/web/.next/server/chunks/ssr/_e9703421._.js.map +1 -1
  151. package/web/.next/server/chunks/ssr/_f39a1adb._.js +3 -0
  152. package/web/.next/server/chunks/ssr/{_135edb9c._.js.map → _f39a1adb._.js.map} +1 -1
  153. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  154. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  155. package/web/.next/server/chunks/ssr/src_presentation_web_9db47db7._.js +5 -0
  156. package/web/.next/server/chunks/ssr/{src_presentation_web_5f039db1._.js.map → src_presentation_web_9db47db7._.js.map} +1 -1
  157. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f66cd328.js +3 -0
  158. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f66cd328.js.map +1 -0
  159. package/web/.next/server/chunks/ssr/src_presentation_web_aeddd007._.js +3 -0
  160. package/web/.next/server/chunks/ssr/{src_presentation_web_164fa933._.js.map → src_presentation_web_aeddd007._.js.map} +1 -1
  161. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  162. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
  163. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  164. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  165. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  166. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  167. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  168. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  169. package/web/.next/server/chunks/ssr/src_presentation_web_ede4a227._.js +5 -0
  170. package/web/.next/server/chunks/ssr/{src_presentation_web_c4337169._.js.map → src_presentation_web_ede4a227._.js.map} +1 -1
  171. package/web/.next/server/pages/500.html +2 -2
  172. package/web/.next/server/server-reference-manifest.js +1 -1
  173. package/web/.next/server/server-reference-manifest.json +325 -325
  174. package/web/.next/static/chunks/01ba448cf76c6014.js +1 -0
  175. package/web/.next/static/chunks/044f4665499a9656.js +1 -0
  176. package/web/.next/static/chunks/112cf5891d1ca558.js +1 -0
  177. package/web/.next/static/chunks/1be7ac7a24009333.css +1 -0
  178. package/web/.next/static/chunks/37ada233462bcaec.js +11 -0
  179. package/web/.next/static/chunks/4ac11847f73b50d6.js +1 -0
  180. package/web/.next/static/chunks/{5b7726ff31c25338.js → 4ac4a43b7eb441e1.js} +2 -2
  181. package/web/.next/static/chunks/5fb66ee5a353a6c6.js +1 -0
  182. package/web/.next/static/chunks/6cfc75060a243290.js +1 -0
  183. package/web/.next/static/chunks/b70dd4985a814b27.js +1 -0
  184. package/web/.next/static/chunks/{9db7f24ddea15a4c.js → d60f93a1a25ab09d.js} +1 -1
  185. package/web/.next/static/chunks/dcfed2c9b7cd1cb4.js +1 -0
  186. package/web/.next/static/chunks/{ad076c0b68dd9971.js → de66f8d6a67c2361.js} +1 -1
  187. package/web/.next/static/chunks/{74fa59dbd04fdfcb.js → de8b0265f780e212.js} +2 -2
  188. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js +0 -3
  189. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js.map +0 -1
  190. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js +0 -3
  191. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js.map +0 -1
  192. package/web/.next/server/chunks/ssr/[root-of-the-server]__14664e9b._.js +0 -4
  193. package/web/.next/server/chunks/ssr/[root-of-the-server]__14664e9b._.js.map +0 -1
  194. package/web/.next/server/chunks/ssr/[root-of-the-server]__4993a47c._.js +0 -3
  195. package/web/.next/server/chunks/ssr/[root-of-the-server]__4993a47c._.js.map +0 -1
  196. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js +0 -3
  197. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js.map +0 -1
  198. package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js +0 -4
  199. package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js.map +0 -1
  200. package/web/.next/server/chunks/ssr/_0e506641._.js.map +0 -1
  201. package/web/.next/server/chunks/ssr/_135edb9c._.js +0 -3
  202. package/web/.next/server/chunks/ssr/_1bb131c4._.js.map +0 -1
  203. package/web/.next/server/chunks/ssr/_b89059c0._.js +0 -3
  204. package/web/.next/server/chunks/ssr/_b89059c0._.js.map +0 -1
  205. package/web/.next/server/chunks/ssr/_cc77a143._.js +0 -3
  206. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +0 -1
  207. package/web/.next/server/chunks/ssr/src_presentation_web_164fa933._.js +0 -3
  208. package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js +0 -5
  209. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js +0 -3
  210. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js.map +0 -1
  211. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +0 -3
  212. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +0 -1
  213. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +0 -3
  214. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +0 -1
  215. package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js +0 -5
  216. package/web/.next/static/chunks/0dc102ef519487f5.js +0 -1
  217. package/web/.next/static/chunks/10fc8c2e5ff44bb2.js +0 -1
  218. package/web/.next/static/chunks/3925626b99dcd57a.css +0 -1
  219. package/web/.next/static/chunks/76c038a9d55ca717.js +0 -1
  220. package/web/.next/static/chunks/9d7e0bb6dc2f88ae.js +0 -1
  221. package/web/.next/static/chunks/ac7251026c1e2cdf.js +0 -1
  222. package/web/.next/static/chunks/d72044b2826292fe.js +0 -1
  223. package/web/.next/static/chunks/e2ef95d904c360f3.js +0 -1
  224. package/web/.next/static/chunks/eaeabda870a35a9a.js +0 -11
  225. /package/web/.next/static/{Ap_0BUaFHzD8WxrlWsbMg → 4hN7Gtw--ruMbDn0FRI2-}/_buildManifest.js +0 -0
  226. /package/web/.next/static/{Ap_0BUaFHzD8WxrlWsbMg → 4hN7Gtw--ruMbDn0FRI2-}/_clientMiddlewareManifest.json +0 -0
  227. /package/web/.next/static/{Ap_0BUaFHzD8WxrlWsbMg → 4hN7Gtw--ruMbDn0FRI2-}/_ssgManifest.js +0 -0
@@ -38,12 +38,17 @@ const PORT_PATTERNS = [
38
38
  * @param line - A single line of process output
39
39
  * @returns The detected URL string, or null if no URL/port was found
40
40
  */
41
+ // Strip ANSI escape codes (colors, cursor movement, etc.)
42
+ // eslint-disable-next-line no-control-regex
43
+ const ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]/g;
41
44
  export function parsePort(line) {
42
45
  if (!line?.trim()) {
43
46
  return null;
44
47
  }
48
+ // Strip ANSI escape codes before matching — dev servers often emit colored output
49
+ const clean = line.replace(ANSI_REGEX, '');
45
50
  for (const { regex, extract } of PORT_PATTERNS) {
46
- const match = line.match(regex);
51
+ const match = clean.match(regex);
47
52
  if (match) {
48
53
  return extract(match);
49
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EAGd,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,EACd,YAAY,EACb,MAAM,wEAAwE,CAAC;AAUhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,qBACa,YAAa,YAAW,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgBjD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgF9C,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKnD,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;YA6BjF,eAAe;IASvB,gBAAgB,CACpB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,UAAQ,GAChB,OAAO,CAAC,IAAI,CAAC;IA4EV,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAsBjE,OAAO,CACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,cAAc,CAAC;IA4DpB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhF,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAevE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAaxE,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,aAAa;IAmFf,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAoCpD,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAc/E,WAAW,CACf,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC;IA6CnB;;;OAGG;YACW,UAAU;IAmBlB,cAAc,CAClB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,WAAW,SAAS,GACnB,OAAO,CAAC,MAAM,CAAC;IAWlB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,aAAa;CAiBtB"}
1
+ {"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"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"}
@@ -308,15 +308,19 @@ let GitPrService = class GitPrService {
308
308
  }
309
309
  }
310
310
  async watchCi(cwd, branch, timeoutMs, intervalSeconds) {
311
+ // Resolve the latest run for the branch BEFORE the try/catch so the
312
+ // runUrl is available in both success and failure return paths.
313
+ let runUrl;
311
314
  try {
312
315
  // gh run watch requires a run ID — it does not support --branch.
313
316
  // First, resolve the latest run ID for the branch via gh run list.
314
- const { stdout: listOut } = await this.execFile('gh', ['run', 'list', '--branch', branch, '--json', 'databaseId', '--limit', '1'], { cwd });
317
+ const { stdout: listOut } = await this.execFile('gh', ['run', 'list', '--branch', branch, '--json', 'databaseId,url', '--limit', '1'], { cwd });
315
318
  const runs = JSON.parse(listOut);
316
319
  if (runs.length === 0 || !runs[0].databaseId) {
317
320
  return { status: 'pending' };
318
321
  }
319
322
  const runId = String(runs[0].databaseId);
323
+ runUrl = runs[0].url;
320
324
  const interval = intervalSeconds ?? 30;
321
325
  const args = [
322
326
  'run',
@@ -335,6 +339,7 @@ let GitPrService = class GitPrService {
335
339
  // If we reach here (no exception), CI passed — no need for fragile stdout parsing.
336
340
  return {
337
341
  status: 'success',
342
+ runUrl,
338
343
  logExcerpt: stdout.trim(),
339
344
  };
340
345
  }
@@ -356,7 +361,7 @@ let GitPrService = class GitPrService {
356
361
  // Build a useful log excerpt from stdout/stderr if available
357
362
  const errObj = error;
358
363
  const parts = [errObj.stdout, errObj.stderr, message].filter(Boolean);
359
- return { status: 'failure', logExcerpt: parts.join('\n').trim() };
364
+ return { status: 'failure', runUrl, logExcerpt: parts.join('\n').trim() };
360
365
  }
361
366
  throw new GitPrError(message, GitPrErrorCode.GIT_ERROR, cause);
362
367
  }
@@ -27,7 +27,7 @@ export async function deployFeature(featureId) {
27
27
  }
28
28
  log.info('worktree path exists, calling deploymentService.start()');
29
29
  const deploymentService = resolve('IDeploymentService');
30
- deploymentService.start(featureId, worktreePath);
30
+ deploymentService.start(featureId, worktreePath, 'feature');
31
31
  log.info('start() returned successfully — state=Booting');
32
32
  return { success: true, state: DeploymentState.Booting };
33
33
  }
@@ -18,7 +18,7 @@ export async function deployRepository(repositoryPath) {
18
18
  }
19
19
  log.info('directory exists, calling deploymentService.start()');
20
20
  const deploymentService = resolve('IDeploymentService');
21
- deploymentService.start(repositoryPath, repositoryPath);
21
+ deploymentService.start(repositoryPath, repositoryPath, 'repository');
22
22
  log.info('start() returned successfully — state=Booting');
23
23
  return { success: true, state: DeploymentState.Booting };
24
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"repository-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,2CA+U5F"}
1
+ {"version":3,"file":"repository-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,2CAkX5F"}
@@ -3,13 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback } from 'react';
4
4
  import { Handle, Position } from '@xyflow/react';
5
5
  import { useRouter } from 'next/navigation';
6
- import { Github, Plus, Code2, Terminal, FolderOpen, Trash2, Play, Square, GitBranch, GitCommitHorizontal, ArrowDown, User, } from 'lucide-react';
6
+ import { Github, Plus, Code2, Terminal, FolderOpen, Trash2, Play, Square, GitBranch, GitCommitHorizontal, ArrowDown, User, RotateCcw, } from 'lucide-react';
7
7
  import { cn } from '../../../lib/utils.js';
8
8
  import { ActionButton } from '../../common/action-button/index.js';
9
9
  import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '../../ui/dialog.js';
10
10
  import { Button } from '../../ui/button.js';
11
11
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
12
- import { DeploymentStatusBadge } from '../../common/deployment-status-badge/index.js';
13
12
  import { useDeployAction } from '../../../hooks/use-deploy-action.js';
14
13
  import { useFeatureFlags } from '../../../hooks/feature-flags-context.js';
15
14
  import { useRepositoryActions } from './use-repository-actions.js';
@@ -72,9 +71,13 @@ export function RepositoryNode({ data }) {
72
71
  e.stopPropagation();
73
72
  data.onClick?.();
74
73
  }
75
- }, className: "nodrag bg-card flex w-[26rem] cursor-pointer flex-col overflow-hidden rounded-xl border shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [_jsx(Github, { className: "text-muted-foreground h-5 w-5 shrink-0" }), _jsx("span", { "data-testid": "repository-node-name", className: "min-w-0 truncate text-sm font-medium", children: data.name }), _jsxs("div", { className: cn('flex shrink-0 items-center gap-2', (data.repositoryPath ?? data.onAdd) && 'ml-auto'), onClick: (e) => e.stopPropagation(), children: [data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in IDE", onClick: actions.openInIde, loading: actions.ideLoading, error: !!actions.ideError, icon: Code2, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in IDE" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in Shell", onClick: actions.openInShell, loading: actions.shellLoading, error: !!actions.shellError, icon: Terminal, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in Shell" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open Folder", onClick: actions.openFolder, loading: actions.folderLoading, error: !!actions.folderError, icon: FolderOpen, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open Folder" })] }) }), _jsx(FeatureSessionsDropdown, { repositoryPath: data.repositoryPath, onCreateFromSession: handleCreateFromSession }), featureFlags.envDeploy ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isDeploymentActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isDeploymentActive ? Square : Play, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isDeploymentActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] })) : null, data.onAdd ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Add feature", "data-testid": "repository-node-add-button", onClick: (e) => {
74
+ }, className: "nodrag bg-card flex w-[26rem] cursor-pointer flex-col overflow-hidden rounded-xl border shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [_jsx(Github, { className: "text-muted-foreground h-5 w-5 shrink-0" }), _jsx("span", { "data-testid": "repository-node-name", className: "min-w-0 truncate text-sm font-medium", children: data.name }), _jsxs("div", { className: cn('flex shrink-0 items-center gap-2', (data.repositoryPath ?? data.onAdd) && 'ml-auto'), onClick: (e) => e.stopPropagation(), children: [data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in IDE", onClick: actions.openInIde, loading: actions.ideLoading, error: !!actions.ideError, icon: Code2, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in IDE" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in Shell", onClick: actions.openInShell, loading: actions.shellLoading, error: !!actions.shellError, icon: Terminal, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in Shell" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open Folder", onClick: actions.openFolder, loading: actions.folderLoading, error: !!actions.folderError, icon: FolderOpen, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open Folder" })] }) }), _jsx(FeatureSessionsDropdown, { repositoryPath: data.repositoryPath, onCreateFromSession: handleCreateFromSession })] })) : null, data.onAdd ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Add feature", "data-testid": "repository-node-add-button", onClick: (e) => {
76
75
  e.stopPropagation();
77
76
  data.onAdd?.();
78
77
  }, className: cn('text-muted-foreground hover:bg-accent dark:hover:bg-accent/50 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:text-blue-500', data.pulseAdd &&
79
- 'animate-pulse-cta bg-blue-100 text-blue-500 dark:bg-blue-900/40'), children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Add feature" })] }) })) : null] })] }), data.branch ? (_jsx("div", { "data-testid": "repository-node-git-info", className: "text-muted-foreground border-t px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-3 text-xs", children: [_jsxs("span", { className: "flex items-center gap-1 truncate", "data-testid": "repository-node-branch", children: [_jsx(GitBranch, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: data.branch })] }), data.behindCount != null && data.behindCount > 0 ? (_jsxs("span", { className: "flex shrink-0 items-center gap-1 whitespace-nowrap text-amber-500", "data-testid": "repository-node-behind", children: [_jsx(ArrowDown, { className: "h-3 w-3 shrink-0" }), data.behindCount, " behind"] })) : null] }) })) : null, data.commitMessage ? (_jsx("div", { "data-testid": "repository-node-commit-info", className: "text-muted-foreground border-t px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsx(GitCommitHorizontal, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "min-w-0 truncate", "data-testid": "repository-node-commit-message", children: data.commitMessage }), data.committer ? (_jsxs("span", { className: "text-muted-foreground/70 ml-auto flex shrink-0 items-center gap-1", "data-testid": "repository-node-committer", children: [_jsx(User, { className: "h-3 w-3 shrink-0" }), _jsx("span", { children: data.committer })] })) : null] }) })) : null] }), data.onAdd || data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: !data.showHandles, className: "opacity-0!" })) : null] }));
78
+ 'animate-pulse-cta bg-blue-100 text-blue-500 dark:bg-blue-900/40'), children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Add feature" })] }) })) : null] })] }), data.branch ? (_jsx("div", { "data-testid": "repository-node-git-info", className: "text-muted-foreground border-t px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-3 text-xs", children: [_jsxs("span", { className: "flex items-center gap-1 truncate", "data-testid": "repository-node-branch", children: [_jsx(GitBranch, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: data.branch })] }), data.behindCount != null && data.behindCount > 0 ? (_jsxs("span", { className: "flex shrink-0 items-center gap-1 whitespace-nowrap text-amber-500", "data-testid": "repository-node-behind", children: [_jsx(ArrowDown, { className: "h-3 w-3 shrink-0" }), data.behindCount, " behind"] })) : null] }) })) : null, data.commitMessage ? (_jsx("div", { "data-testid": "repository-node-commit-info", className: "text-muted-foreground border-t px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsx(GitCommitHorizontal, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "min-w-0 truncate", "data-testid": "repository-node-commit-message", children: data.commitMessage }), data.committer ? (_jsxs("span", { className: "text-muted-foreground/70 ml-auto flex shrink-0 items-center gap-1", "data-testid": "repository-node-committer", children: [_jsx(User, { className: "h-3 w-3 shrink-0" }), _jsx("span", { children: data.committer })] })) : null] }) })) : null, featureFlags.envDeploy && data.repositoryPath ? (_jsx("div", { "data-testid": "repository-node-dev-preview", className: "border-t px-4 py-2", onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "flex items-center gap-2 text-xs", children: [deployAction.deployError ? (_jsx("span", { className: "truncate text-xs text-red-500", children: deployAction.deployError })) : isDeploymentActive ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "mr-0.5 inline-block h-2 w-2 shrink-0 rounded-full bg-green-500" }), deployAction.url ? (_jsx("a", { href: deployAction.url, target: "_blank", rel: "noopener noreferrer", className: "truncate text-green-700 hover:underline dark:text-green-400", children: deployAction.url })) : (_jsx("span", { className: "text-muted-foreground", children: "Starting..." }))] })) : (_jsxs("span", { className: "text-muted-foreground", children: ["Run", _jsx("span", { className: "text-muted-foreground/50 ml-2 text-[10px]", children: "start local environment" })] })), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "ml-auto flex items-center", children: _jsx(ActionButton, { label: deployAction.deployError
79
+ ? 'Retry'
80
+ : isDeploymentActive
81
+ ? 'Stop Dev Server'
82
+ : 'Start Dev Server', onClick: isDeploymentActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: false, icon: deployAction.deployError ? RotateCcw : isDeploymentActive ? Square : Play, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) })] }) })) : null] }), data.onAdd || data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: !data.showHandles, className: "opacity-0!" })) : null] }));
80
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAgBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7F,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA6gBpB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAgBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7F,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAuhBpB"}
@@ -102,11 +102,20 @@ export function useControlCenterState(initialNodes, initialEdges) {
102
102
  for (const { featureId, state, lifecycle } of resolveSseEventUpdates(newEvents)) {
103
103
  if (state !== undefined || lifecycle !== undefined) {
104
104
  const nodeId = `feat-${featureId}`;
105
+ const existingNode = nodesRef.current.find((n) => n.id === nodeId);
106
+ const existingState = existingNode
107
+ ? existingNode.data.state
108
+ : undefined;
105
109
  // Skip SSE updates for features in 'deleting' state — the optimistic
106
110
  // delete state must not be overwritten by stale SSE events (e.g. agent
107
111
  // cancellation emitting AgentFailed during the soft-delete window).
108
- const existingNode = nodesRef.current.find((n) => n.id === nodeId);
109
- if (existingNode && existingNode.data.state === 'deleting')
112
+ if (existingState === 'deleting')
113
+ continue;
114
+ // Skip SSE error events during mutation cooldown — after approve/reject
115
+ // the agent may still report AgentFailed from the previous run before
116
+ // the resume kicks in. The optimistic 'running' state must not be
117
+ // overwritten by stale error events during this transition window.
118
+ if (state === 'error' && isMutating() && existingState === 'running')
110
119
  continue;
111
120
  updateFeature(nodeId, {
112
121
  ...(state !== undefined && { state }),
@@ -114,7 +123,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
114
123
  });
115
124
  }
116
125
  }
117
- }, [events, updateFeature]);
126
+ }, [events, updateFeature, isMutating]);
118
127
  // Listen for optimistic approval/rejection events from the drawer (fires before SSE arrives).
119
128
  // Uses beginMutation + endMutation to prevent stale poll/reconcile data from overwriting
120
129
  // the optimistic 'running' state during the transition window (avoids brief "Something went wrong" flash).
@@ -1 +1 @@
1
- {"version":3,"file":"notification-settings-section.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/settings/notification-settings-section.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAmBpF,MAAM,WAAW,gCAAgC;IAC/C,aAAa,EAAE,uBAAuB,CAAC;CACxC;AAED,wBAAgB,2BAA2B,CAAC,EAAE,aAAa,EAAE,EAAE,gCAAgC,2CAkJ9F"}
1
+ {"version":3,"file":"notification-settings-section.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/settings/notification-settings-section.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAmBpF,MAAM,WAAW,gCAAgC;IAC/C,aAAa,EAAE,uBAAuB,CAAC;CACxC;AAED,wBAAgB,2BAA2B,CAAC,EAAE,aAAa,EAAE,EAAE,gCAAgC,2CAsH9F"}
@@ -25,8 +25,6 @@ const PR_EVENT_TOGGLES = [
25
25
  ];
26
26
  export function NotificationSettingsSection({ notifications }) {
27
27
  const [inApp, setInApp] = useState(notifications.inApp.enabled);
28
- const [browser, setBrowser] = useState(notifications.browser.enabled);
29
- const [desktop, setDesktop] = useState(notifications.desktop.enabled);
30
28
  const [events, setEvents] = useState({ ...notifications.events });
31
29
  const [isPending, startTransition] = useTransition();
32
30
  const [showSaved, setShowSaved] = useState(false);
@@ -51,20 +49,18 @@ export function NotificationSettingsSection({ notifications }) {
51
49
  return {
52
50
  notifications: {
53
51
  inApp: { enabled: overrides.inApp ?? inApp },
54
- browser: { enabled: overrides.browser ?? browser },
55
- desktop: { enabled: overrides.desktop ?? desktop },
56
52
  events: overrides.events ?? events,
57
53
  },
58
54
  };
59
55
  }
60
- function handleChannelChange(channel, setter, value) {
61
- setter(value);
62
- save(buildFullPayload({ [channel]: value }));
56
+ function handleInAppChange(value) {
57
+ setInApp(value);
58
+ save(buildFullPayload({ inApp: value }));
63
59
  }
64
60
  function handleEventChange(key, value) {
65
61
  const newEvents = { ...events, [key]: value };
66
62
  setEvents(newEvents);
67
63
  save(buildFullPayload({ events: newEvents }));
68
64
  }
69
- return (_jsxs(Card, { id: "notifications", className: "scroll-mt-6", "data-testid": "notification-settings-section", children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Bell, { className: "text-muted-foreground h-4 w-4" }), _jsx(CardTitle, { children: "Notifications" })] }), isPending ? _jsx("span", { className: "text-muted-foreground text-xs", children: "Saving..." }) : null, showSaved && !isPending ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-green-600", children: [_jsx(Check, { className: "h-3 w-3" }), "Saved"] })) : null] }), _jsx(CardDescription, { children: "Configure notification channels and event preferences" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Channels" }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "notif-in-app", children: "In-App" }), _jsx(Switch, { id: "notif-in-app", "data-testid": "switch-in-app", checked: inApp, onCheckedChange: (v) => handleChannelChange('inApp', setInApp, v) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "notif-browser", children: "Browser" }), _jsx(Switch, { id: "notif-browser", "data-testid": "switch-browser", checked: browser, onCheckedChange: (v) => handleChannelChange('browser', setBrowser, v) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "notif-desktop", children: "Desktop" }), _jsx(Switch, { id: "notif-desktop", "data-testid": "switch-desktop", checked: desktop, onCheckedChange: (v) => handleChannelChange('desktop', setDesktop, v) })] })] }), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Agent Events" }), AGENT_EVENT_TOGGLES.map(({ key, label }) => (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: `notif-event-${key}`, children: label }), _jsx(Switch, { id: `notif-event-${key}`, "data-testid": `switch-event-${key}`, checked: events[key], onCheckedChange: (v) => handleEventChange(key, v) })] }, key)))] }), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold", children: "PR Events" }), PR_EVENT_TOGGLES.map(({ key, label }) => (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: `notif-event-${key}`, children: label }), _jsx(Switch, { id: `notif-event-${key}`, "data-testid": `switch-event-${key}`, checked: events[key], onCheckedChange: (v) => handleEventChange(key, v) })] }, key)))] })] })] }));
65
+ return (_jsxs(Card, { id: "notifications", className: "scroll-mt-6", "data-testid": "notification-settings-section", children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Bell, { className: "text-muted-foreground h-4 w-4" }), _jsx(CardTitle, { children: "Notifications" })] }), isPending ? _jsx("span", { className: "text-muted-foreground text-xs", children: "Saving..." }) : null, showSaved && !isPending ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-green-600", children: [_jsx(Check, { className: "h-3 w-3" }), "Saved"] })) : null] }), _jsx(CardDescription, { children: "Configure notification channels and event preferences" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Channels" }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "notif-in-app", children: "In-App" }), _jsx(Switch, { id: "notif-in-app", "data-testid": "switch-in-app", checked: inApp, onCheckedChange: handleInAppChange })] })] }), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Agent Events" }), AGENT_EVENT_TOGGLES.map(({ key, label }) => (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: `notif-event-${key}`, children: label }), _jsx(Switch, { id: `notif-event-${key}`, "data-testid": `switch-event-${key}`, checked: events[key], onCheckedChange: (v) => handleEventChange(key, v) })] }, key)))] }), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold", children: "PR Events" }), PR_EVENT_TOGGLES.map(({ key, label }) => (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: `notif-event-${key}`, children: label }), _jsx(Switch, { id: `notif-event-${key}`, "data-testid": `switch-event-${key}`, checked: events[key], onCheckedChange: (v) => handleEventChange(key, v) })] }, key)))] })] })] }));
70
66
  }
@@ -38,7 +38,7 @@ export const Default = {
38
38
  args: {
39
39
  notifications: {
40
40
  inApp: { enabled: true },
41
- browser: { enabled: true },
41
+ browser: { enabled: false },
42
42
  desktop: { enabled: false },
43
43
  events: allEvents,
44
44
  },
@@ -48,8 +48,8 @@ export const AllEnabled = {
48
48
  args: {
49
49
  notifications: {
50
50
  inApp: { enabled: true },
51
- browser: { enabled: true },
52
- desktop: { enabled: true },
51
+ browser: { enabled: false },
52
+ desktop: { enabled: false },
53
53
  events: allEvents,
54
54
  },
55
55
  },
@@ -1 +1 @@
1
- {"version":3,"file":"settings-page-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/settings/settings-page-client.tsx"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EACV,QAAQ,EAGT,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAgC/E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC1C;AA6QD,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,kBAAkB,GACnB,EAAE,uBAAuB,2CAyqCzB"}
1
+ {"version":3,"file":"settings-page-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/settings/settings-page-client.tsx"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EACV,QAAQ,EAGT,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AA2B/E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC1C;AA6QD,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,kBAAkB,GACnB,EAAE,uBAAuB,2CAulCzB"}
@@ -1,24 +1,17 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useTransition, useRef, useEffect, useCallback } from 'react';
4
- import { Eye, EyeOff, Check, Bot, Terminal, GitBranch, Activity, Bell, Flag, Database, Minus, Plus, ExternalLink, Settings2, Timer, } from 'lucide-react';
4
+ import { Check, Bot, Terminal, GitBranch, Activity, Bell, Flag, Database, Minus, Plus, ExternalLink, Settings2, Timer, } from 'lucide-react';
5
5
  import { toast } from 'sonner';
6
6
  import { cn } from '../../../lib/utils.js';
7
7
  import { Label } from '../../ui/label.js';
8
- import { Input } from '../../ui/input.js';
9
- import { Button } from '../../ui/button.js';
10
8
  import { Switch } from '../../ui/switch.js';
11
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
12
9
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../ui/select.js';
13
10
  import { updateSettingsAction } from '../../../app/actions/update-settings.js';
14
- import { AgentAuthMethod, EditorType, TerminalType, } from '../../../../../../packages/core/src/domain/generated/output.js';
11
+ import { EditorType, TerminalType } from '../../../../../../packages/core/src/domain/generated/output.js';
15
12
  import { getEditorTypeIcon } from '../../common/editor-type-icons.js';
16
13
  import { AgentModelPicker } from '../../features/settings/AgentModelPicker/index.js';
17
14
  import { TimeoutSlider } from '../../features/settings/timeout-slider.js';
18
- const AUTH_METHOD_OPTIONS = [
19
- { value: AgentAuthMethod.Session, label: 'Session' },
20
- { value: AgentAuthMethod.Token, label: 'Token' },
21
- ];
22
15
  const EDITOR_OPTIONS = [
23
16
  { value: EditorType.VsCode, label: 'VS Code' },
24
17
  { value: EditorType.Cursor, label: 'Cursor' },
@@ -125,9 +118,6 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
125
118
  const featureFlags = settings.featureFlags ?? { skills: false, envDeploy: false, debug: false };
126
119
  // Agent state
127
120
  const [agentType, setAgentType] = useState(settings.agent.type);
128
- const authMethod = settings.agent.authMethod;
129
- const [token, setToken] = useState(settings.agent.token ?? '');
130
- const [showToken, setShowToken] = useState(false);
131
121
  // Environment state
132
122
  const [editor, setEditor] = useState(settings.environment.defaultEditor);
133
123
  const [shell, setShell] = useState(settings.environment.shellPreference);
@@ -167,8 +157,6 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
167
157
  const [analyzeRepoTimeout, setAnalyzeRepoTimeout] = useState(String(Math.round((analyzeRepoConfig?.analyzeMs ?? 600_000) / 1000)));
168
158
  // Notification state
169
159
  const [inApp, setInApp] = useState(settings.notifications.inApp.enabled);
170
- const [browser, setBrowser] = useState(settings.notifications.browser.enabled);
171
- const [desktop, setDesktop] = useState(settings.notifications.desktop.enabled);
172
160
  const [events, setEvents] = useState({ ...settings.notifications.events });
173
161
  // Feature flags state
174
162
  const [flags, setFlags] = useState({ ...featureFlags });
@@ -208,15 +196,6 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
208
196
  const n = parseInt(value, 10);
209
197
  return Number.isNaN(n) || n <= 0 ? undefined : n;
210
198
  }
211
- // Agent helpers
212
- function buildAgentPayload(overrides = {}) {
213
- const merged = { type: agentType, authMethod, ...overrides };
214
- const result = { type: merged.type, authMethod: merged.authMethod };
215
- if (merged.authMethod === AgentAuthMethod.Token) {
216
- result.token = overrides.token ?? token;
217
- }
218
- return { agent: result };
219
- }
220
199
  function secondsToMs(val) {
221
200
  if (val === undefined)
222
201
  return undefined;
@@ -261,8 +240,6 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
261
240
  return {
262
241
  notifications: {
263
242
  inApp: { enabled: overrides.inApp ?? inApp },
264
- browser: { enabled: overrides.browser ?? browser },
265
- desktop: { enabled: overrides.desktop ?? desktop },
266
243
  events: overrides.events ?? events,
267
244
  },
268
245
  };
@@ -301,11 +278,7 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
301
278
  return (_jsxs("button", { type: "button", onClick: () => scrollToSection(s.id), className: cn('flex cursor-pointer items-center gap-1 rounded-md px-1.5 py-1 text-[11px] transition-all', isActive
302
279
  ? 'bg-accent text-foreground font-medium'
303
280
  : 'text-muted-foreground/60 hover:text-foreground hover:bg-accent/50'), children: [_jsx(SectionIcon, { className: "h-3 w-3" }), _jsx("span", { className: "hidden sm:inline", children: s.label })] }, s.id));
304
- }) })] }) }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { id: "section-agent", className: "grid scroll-mt-18 grid-cols-1 gap-x-5 rounded-lg lg:grid-cols-[1fr_280px]", children: [_jsxs(SettingsSection, { icon: Bot, title: "Agent", description: "AI coding agent and authentication", testId: "agent-settings-section", children: [_jsx(SettingsRow, { label: "Agent & Model", description: "Provider and model for all operations", htmlFor: "agent-model-picker", children: _jsx(AgentModelPicker, { initialAgentType: agentType, initialModel: settings.models.default, mode: "settings", onAgentModelChange: (newAgent) => setAgentType(newAgent), className: "w-55" }) }), _jsx(SettingsRow, { label: "Authentication", description: "How the agent authenticates", htmlFor: "auth-method", children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { children: _jsxs(Select, { value: authMethod, disabled: true, children: [_jsx(SelectTrigger, { id: "auth-method", "data-testid": "auth-method-select", className: "w-55 cursor-not-allowed text-xs opacity-60", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: AUTH_METHOD_OPTIONS.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }) }) }), _jsx(TooltipContent, { children: "Coming soon" })] }) }) }), authMethod === AgentAuthMethod.Token && (_jsx(SettingsRow, { label: "API Token", htmlFor: "agent-token", children: _jsxs("div", { className: "relative w-45", children: [_jsx(Input, { id: "agent-token", "data-testid": "agent-token-input", type: showToken ? 'text' : 'password', value: token, onChange: (e) => setToken(e.target.value), onBlur: () => {
305
- if (token !== (settings.agent.token ?? '')) {
306
- save(buildAgentPayload({ token }));
307
- }
308
- }, placeholder: "Enter token", className: "pr-8" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "absolute top-0 right-0 h-full px-2 hover:bg-transparent", onClick: () => setShowToken(!showToken), "data-testid": "toggle-token-visibility", "aria-label": showToken ? 'Hide token' : 'Show token', children: showToken ? _jsx(EyeOff, { className: "h-3 w-3" }) : _jsx(Eye, { className: "h-3 w-3" }) })] }) }))] }), _jsx(SectionHint, { links: [
281
+ }) })] }) }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { id: "section-agent", className: "grid scroll-mt-18 grid-cols-1 gap-x-5 rounded-lg lg:grid-cols-[1fr_280px]", children: [_jsx(SettingsSection, { icon: Bot, title: "Agent", description: "AI coding agent and authentication", testId: "agent-settings-section", children: _jsx(SettingsRow, { label: "Agent & Model", description: "Provider and model for all operations", htmlFor: "agent-model-picker", children: _jsx(AgentModelPicker, { initialAgentType: agentType, initialModel: settings.models.default, mode: "settings", onAgentModelChange: (newAgent) => setAgentType(newAgent), className: "w-55" }) }) }), _jsx(SectionHint, { links: [
309
282
  {
310
283
  label: 'Agent system',
311
284
  href: 'https://github.com/shep-ai/cli/blob/main/docs/architecture/agent-system.md',
@@ -443,12 +416,6 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
443
416
  }, defaultSeconds: 600 }) })] }), _jsx(SectionHint, { children: "Each agent has independently configurable stage timeouts. When a stage exceeds its timeout, the agent is terminated. Longer timeouts are useful for complex implementations. Feature agent defaults to 30 minutes per stage. Analyze repository agent defaults to 10 minutes." })] }), _jsxs("div", { id: "section-notifications", className: "grid scroll-mt-18 grid-cols-1 gap-x-5 rounded-lg lg:grid-cols-[1fr_280px]", children: [_jsxs(SettingsSection, { icon: Bell, title: "Notifications", description: "How and when you get notified", testId: "notification-settings-section", children: [_jsx(SubsectionLabel, { children: "Channels" }), _jsx(SwitchRow, { label: "In-app", description: "Notifications inside the Shep UI", id: "notif-in-app", testId: "switch-in-app", checked: inApp, onChange: (v) => {
444
417
  setInApp(v);
445
418
  save(buildNotificationPayload({ inApp: v }));
446
- } }), _jsx(SwitchRow, { label: "Browser", description: "Push notifications via the browser", id: "notif-browser", testId: "switch-browser", checked: browser, onChange: (v) => {
447
- setBrowser(v);
448
- save(buildNotificationPayload({ browser: v }));
449
- } }), _jsx(SwitchRow, { label: "Desktop", description: "Native OS notifications", id: "notif-desktop", testId: "switch-desktop", checked: desktop, onChange: (v) => {
450
- setDesktop(v);
451
- save(buildNotificationPayload({ desktop: v }));
452
419
  } }), _jsx(SubsectionLabel, { children: "Agent Events" }), _jsx(SwitchRow, { label: "Agent started", id: "notif-event-agentStarted", testId: "switch-event-agentStarted", checked: events.agentStarted, onChange: (v) => {
453
420
  const newEvents = { ...events, agentStarted: v };
454
421
  setEvents(newEvents);
@@ -485,12 +452,20 @@ export function SettingsPageClient({ settings, shepHome, dbFileSize, availableTe
485
452
  const newEvents = { ...events, prChecksFailed: v };
486
453
  setEvents(newEvents);
487
454
  save(buildNotificationPayload({ events: newEvents }));
455
+ } }), _jsx(SwitchRow, { label: "PR blocked", id: "notif-event-prBlocked", testId: "switch-event-prBlocked", checked: events.prBlocked, onChange: (v) => {
456
+ const newEvents = { ...events, prBlocked: v };
457
+ setEvents(newEvents);
458
+ save(buildNotificationPayload({ events: newEvents }));
459
+ } }), _jsx(SwitchRow, { label: "Merge review ready", id: "notif-event-mergeReviewReady", testId: "switch-event-mergeReviewReady", checked: events.mergeReviewReady, onChange: (v) => {
460
+ const newEvents = { ...events, mergeReviewReady: v };
461
+ setEvents(newEvents);
462
+ save(buildNotificationPayload({ events: newEvents }));
488
463
  } })] }), _jsx(SectionHint, { links: [
489
464
  {
490
465
  label: 'Notification system',
491
466
  href: 'https://github.com/shep-ai/cli/blob/main/specs/021-agent-notifications/spec.yaml',
492
467
  },
493
- ], children: "Three notification channels keep you in the loop: in-app toasts, browser push notifications, and native desktop alerts. Fine-tune which agent lifecycle events trigger a notification." })] }), _jsxs("div", { id: "section-feature-flags", className: "grid scroll-mt-18 grid-cols-1 gap-x-5 rounded-lg lg:grid-cols-[1fr_280px]", children: [_jsxs(SettingsSection, { icon: Flag, title: "Feature Flags", description: "Enable or disable experimental features", badge: "Experimental", testId: "feature-flags-settings-section", children: [_jsx(SwitchRow, { label: "Skills", description: "Enable the skills system for agent capabilities", id: "flag-skills", testId: "switch-flag-skills", checked: flags.skills, onChange: (v) => {
468
+ ], children: "In-app toast notifications keep you in the loop. Fine-tune which agent lifecycle events trigger a notification." })] }), _jsxs("div", { id: "section-feature-flags", className: "grid scroll-mt-18 grid-cols-1 gap-x-5 rounded-lg lg:grid-cols-[1fr_280px]", children: [_jsxs(SettingsSection, { icon: Flag, title: "Feature Flags", description: "Enable or disable experimental features", badge: "Experimental", testId: "feature-flags-settings-section", children: [_jsx(SwitchRow, { label: "Skills", description: "Enable the skills system for agent capabilities", id: "flag-skills", testId: "switch-flag-skills", checked: flags.skills, onChange: (v) => {
494
469
  const newFlags = { ...flags, skills: v };
495
470
  setFlags(newFlags);
496
471
  save({ featureFlags: newFlags });
@@ -1 +1 @@
1
- {"version":3,"file":"settings-page-client.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/settings/settings-page-client.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAM5D,QAAA,MAAM,IAAI;;;;;;;CAOiC,CAAC;AAE5C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAkBzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAa7B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAsB5B,CAAC"}
1
+ {"version":3,"file":"settings-page-client.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/settings/settings-page-client.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAM5D,QAAA,MAAM,IAAI;;;;;;;CAOiC,CAAC;AAE5C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAiBzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAa7B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAsB5B,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { SettingsPageClient } from './settings-page-client.js';
2
2
  import { createDefaultSettings } from '../../../../../../packages/core/src/domain/factories/settings-defaults.factory.js';
3
- import { AgentType, AgentAuthMethod } from '../../../../../../packages/core/src/domain/generated/output.js';
3
+ import { AgentType } from '../../../../../../packages/core/src/domain/generated/output.js';
4
4
  const defaultSettings = createDefaultSettings();
5
5
  const meta = {
6
6
  title: 'Features/Settings/SettingsPageClient',
@@ -23,9 +23,8 @@ export const AllSections = {
23
23
  settings: {
24
24
  ...defaultSettings,
25
25
  agent: {
26
+ ...defaultSettings.agent,
26
27
  type: AgentType.GeminiCli,
27
- authMethod: AgentAuthMethod.Token,
28
- token: 'sk-test-token-12345',
29
28
  },
30
29
  featureFlags: {
31
30
  skills: true,
@@ -1 +1 @@
1
- {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAOD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAwKlF"}
1
+ {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAmOlF"}
@@ -6,7 +6,6 @@ import { deployRepository } from '../app/actions/deploy-repository.js';
6
6
  import { stopDeployment } from '../app/actions/stop-deployment.js';
7
7
  import { getDeploymentStatus } from '../app/actions/get-deployment-status.js';
8
8
  const log = createLogger('[useDeployAction]');
9
- const ERROR_CLEAR_DELAY = 5000;
10
9
  const POLL_INTERVAL = 3000;
11
10
  export function useDeployAction(input) {
12
11
  const [deployLoading, setDeployLoading] = useState(false);
@@ -14,7 +13,6 @@ export function useDeployAction(input) {
14
13
  const [deployError, setDeployError] = useState(null);
15
14
  const [status, setStatus] = useState(null);
16
15
  const [url, setUrl] = useState(null);
17
- const errorTimerRef = useRef(null);
18
16
  const pollIntervalRef = useRef(null);
19
17
  const mountedRef = useRef(true);
20
18
  const statusRef = useRef(status);
@@ -29,11 +27,37 @@ export function useDeployAction(input) {
29
27
  mountedRef.current = false;
30
28
  };
31
29
  }, []);
30
+ // Fetch status on mount — recover running dev servers after page reload
31
+ useEffect(() => {
32
+ if (!input)
33
+ return;
34
+ let cancelled = false;
35
+ (async () => {
36
+ try {
37
+ const result = await getDeploymentStatus(input.targetId);
38
+ if (cancelled || !mountedRef.current)
39
+ return;
40
+ if (result && result.state !== 'Stopped') {
41
+ log.info(`mount recovery: "${input.targetId}" state=${result.state}, url=${result.url}`);
42
+ setStatus(result.state);
43
+ setUrl(result.url);
44
+ startPolling(input.targetId);
45
+ }
46
+ }
47
+ catch {
48
+ // Server container may not be available (e.g., in tests)
49
+ log.debug(`mount recovery failed for "${input.targetId}" — ignoring`);
50
+ }
51
+ })();
52
+ return () => {
53
+ cancelled = true;
54
+ };
55
+ // Only run on mount (input identity is stable from the caller)
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ }, [input?.targetId]);
32
58
  // Cleanup on unmount
33
59
  useEffect(() => {
34
60
  return () => {
35
- if (errorTimerRef.current)
36
- clearTimeout(errorTimerRef.current);
37
61
  if (pollIntervalRef.current)
38
62
  clearInterval(pollIntervalRef.current);
39
63
  };
@@ -53,7 +77,20 @@ export function useDeployAction(input) {
53
77
  stopPolling();
54
78
  return;
55
79
  }
56
- const result = await getDeploymentStatus(targetId);
80
+ let result;
81
+ try {
82
+ result = await getDeploymentStatus(targetId);
83
+ }
84
+ catch (err) {
85
+ log.warn(`poll fetch failed for "${targetId}":`, err);
86
+ // Treat fetch failure as deployment gone — clear UI
87
+ if (mountedRef.current) {
88
+ setStatus(null);
89
+ setUrl(null);
90
+ stopPolling();
91
+ }
92
+ return;
93
+ }
57
94
  if (!mountedRef.current)
58
95
  return;
59
96
  if (!result || result.state === 'Stopped') {
@@ -71,6 +108,32 @@ export function useDeployAction(input) {
71
108
  }
72
109
  }, POLL_INTERVAL);
73
110
  }, [stopPolling]);
111
+ // Idle poll — when no deployment is known, periodically check if one was
112
+ // started externally (e.g., from the drawer while the repo node is mounted).
113
+ const IDLE_POLL_INTERVAL = 5000;
114
+ useEffect(() => {
115
+ if (!input || status !== null)
116
+ return;
117
+ const timer = setInterval(async () => {
118
+ if (!mountedRef.current)
119
+ return;
120
+ try {
121
+ const result = await getDeploymentStatus(input.targetId);
122
+ if (!mountedRef.current)
123
+ return;
124
+ if (result && result.state !== 'Stopped') {
125
+ log.info(`idle poll: "${input.targetId}" state=${result.state}, url=${result.url}`);
126
+ setStatus(result.state);
127
+ setUrl(result.url);
128
+ startPolling(input.targetId);
129
+ }
130
+ }
131
+ catch {
132
+ // ignore
133
+ }
134
+ }, IDLE_POLL_INTERVAL);
135
+ return () => clearInterval(timer);
136
+ }, [input, status, startPolling]);
74
137
  const handleDeploy = useCallback(async () => {
75
138
  if (!input) {
76
139
  log.warn('deploy() called but input is null — no-op');
@@ -81,8 +144,6 @@ export function useDeployAction(input) {
81
144
  return;
82
145
  }
83
146
  log.info(`deploy() — targetType="${input.targetType}", targetId="${input.targetId}", repositoryPath="${input.repositoryPath}"`);
84
- if (errorTimerRef.current)
85
- clearTimeout(errorTimerRef.current);
86
147
  setDeployLoading(true);
87
148
  setDeployError(null);
88
149
  try {
@@ -96,9 +157,8 @@ export function useDeployAction(input) {
96
157
  }
97
158
  if (!result.success) {
98
159
  const errorMessage = result.error ?? 'An unexpected error occurred';
99
- log.error(`deploy failed: ${errorMessage}`);
160
+ log.warn(`deploy failed: ${errorMessage}`);
100
161
  setDeployError(errorMessage);
101
- errorTimerRef.current = setTimeout(() => setDeployError(null), ERROR_CLEAR_DELAY);
102
162
  }
103
163
  else {
104
164
  log.info(`deploy succeeded — initial state=${result.state}, starting polling`);
@@ -113,7 +173,6 @@ export function useDeployAction(input) {
113
173
  const errorMessage = err instanceof Error ? err.message : 'An unexpected error occurred';
114
174
  log.error(`deploy threw exception: ${errorMessage}`, err);
115
175
  setDeployError(errorMessage);
116
- errorTimerRef.current = setTimeout(() => setDeployError(null), ERROR_CLEAR_DELAY);
117
176
  }
118
177
  finally {
119
178
  if (mountedRef.current) {
@@ -3,12 +3,12 @@ import dagre from '@dagrejs/dagre';
3
3
  export const CANVAS_LAYOUT_DEFAULTS = {
4
4
  direction: 'LR',
5
5
  ranksep: 200,
6
- nodesep: 15,
6
+ nodesep: 30,
7
7
  };
8
8
  /** Known node-type dimensions for the canvas node types */
9
9
  const NODE_DIMENSIONS = {
10
- featureNode: { width: 288, height: 140 },
11
- repositoryNode: { width: 400, height: 110 },
10
+ featureNode: { width: 288, height: 160 },
11
+ repositoryNode: { width: 400, height: 160 },
12
12
  };
13
13
  function getNodeSize(node, defaultSize) {
14
14
  // Prefer per-node size from data, then type-based lookup, then default