@shepai/cli 1.107.0 → 1.107.1-pr328.288e6e8

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 (170) hide show
  1. package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts +3 -0
  2. package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.js +23 -3
  4. package/dist/src/presentation/web/app/actions/delete-feature.d.ts +1 -1
  5. package/dist/src/presentation/web/app/actions/delete-feature.d.ts.map +1 -1
  6. package/dist/src/presentation/web/app/actions/delete-feature.js +8 -3
  7. package/dist/src/presentation/web/app/api/version/route.d.ts +3 -6
  8. package/dist/src/presentation/web/app/api/version/route.d.ts.map +1 -1
  9. package/dist/src/presentation/web/app/api/version/route.js +29 -15
  10. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  11. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +3 -3
  12. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog-config.d.ts +2 -1
  13. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog-config.d.ts.map +1 -1
  14. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.d.ts +1 -1
  15. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.d.ts.map +1 -1
  16. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.js +4 -2
  17. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.stories.d.ts +2 -0
  18. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.stories.d.ts.map +1 -1
  19. package/dist/src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.stories.js +11 -0
  20. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +2 -1
  21. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map +1 -1
  22. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  23. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +3 -3
  24. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +1 -1
  25. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  26. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +13 -10
  27. package/dist/src/presentation/web/lib/derive-graph.d.ts +1 -1
  28. package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -1
  29. package/dist/src/presentation/web/lib/derive-graph.js +13 -2
  30. package/dist/src/presentation/web/lib/version.d.ts +5 -5
  31. package/dist/src/presentation/web/lib/version.d.ts.map +1 -1
  32. package/dist/src/presentation/web/lib/version.js +26 -12
  33. package/dist/tsconfig.build.tsbuildinfo +1 -1
  34. package/package.json +1 -1
  35. package/web/.next/BUILD_ID +1 -1
  36. package/web/.next/build-manifest.json +2 -2
  37. package/web/.next/fallback-build-manifest.json +2 -2
  38. package/web/.next/prerender-manifest.json +3 -3
  39. package/web/.next/required-server-files.js +3 -3
  40. package/web/.next/required-server-files.json +3 -3
  41. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +20 -20
  42. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  43. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +26 -26
  45. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  46. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +26 -26
  48. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
  51. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  52. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +20 -20
  54. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  55. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +26 -26
  57. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  58. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +26 -26
  60. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  61. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +18 -18
  63. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  64. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  65. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
  66. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  67. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/_global-error.html +2 -2
  69. package/web/.next/server/app/_global-error.rsc +1 -1
  70. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  71. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  72. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  76. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  77. package/web/.next/server/app/api/version/route.js +1 -1
  78. package/web/.next/server/app/api/version/route.js.nft.json +1 -1
  79. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  80. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  81. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  82. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  83. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  84. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  85. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  86. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  87. package/web/.next/server/app/version/page.js +1 -1
  88. package/web/.next/server/app/version/page.js.nft.json +1 -1
  89. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  90. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +3 -0
  91. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js.map +1 -0
  92. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  93. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  94. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_a1104008.js +1 -1
  95. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_a1104008.js.map +1 -1
  96. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_92642200.js +1 -1
  97. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_92642200.js.map +1 -1
  98. package/web/.next/server/chunks/ssr/[root-of-the-server]__039967e3._.js +1 -1
  99. package/web/.next/server/chunks/ssr/[root-of-the-server]__039967e3._.js.map +1 -1
  100. package/web/.next/server/chunks/ssr/[root-of-the-server]__07a39ff2._.js +3 -0
  101. package/web/.next/server/chunks/ssr/[root-of-the-server]__07a39ff2._.js.map +1 -0
  102. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js +2 -2
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js.map +1 -1
  106. package/web/.next/server/chunks/ssr/[root-of-the-server]__2773312c._.js +1 -1
  107. package/web/.next/server/chunks/ssr/[root-of-the-server]__2773312c._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  109. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js +1 -1
  110. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
  112. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbd8062c._.js +1 -1
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbd8062c._.js.map +1 -1
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__e70c5627._.js +1 -1
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__e70c5627._.js.map +1 -1
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  120. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  121. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  122. package/web/.next/server/chunks/ssr/{_2f98781f._.js → _765f4eb3._.js} +2 -2
  123. package/web/.next/server/chunks/ssr/{_2f98781f._.js.map → _765f4eb3._.js.map} +1 -1
  124. package/web/.next/server/chunks/ssr/{_966a446d._.js → _80a470e7._.js} +2 -2
  125. package/web/.next/server/chunks/ssr/{_966a446d._.js.map → _80a470e7._.js.map} +1 -1
  126. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  127. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/{_fa69c532._.js → _bbbeafd1._.js} +2 -2
  129. package/web/.next/server/chunks/ssr/{_fa69c532._.js.map → _bbbeafd1._.js.map} +1 -1
  130. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  131. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  133. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_888633e1.js +1 -1
  135. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_888633e1.js.map +1 -1
  136. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  137. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  138. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  139. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  140. package/web/.next/server/chunks/ssr/src_presentation_web_ce7cbc3c._.js +2 -2
  141. package/web/.next/server/chunks/ssr/src_presentation_web_ce7cbc3c._.js.map +1 -1
  142. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  143. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  144. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  145. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  146. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  147. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  148. package/web/.next/server/chunks/ssr/src_presentation_web_e2604413._.js +2 -2
  149. package/web/.next/server/chunks/ssr/src_presentation_web_e2604413._.js.map +1 -1
  150. package/web/.next/server/pages/500.html +2 -2
  151. package/web/.next/server/server-reference-manifest.js +1 -1
  152. package/web/.next/server/server-reference-manifest.json +31 -31
  153. package/web/.next/static/chunks/{ab5b6ec6eeacc9bf.js → 171245d897faea3d.js} +1 -1
  154. package/web/.next/static/chunks/2ef4699ef5c60788.js +2 -0
  155. package/web/.next/static/chunks/{a1e6e3b5f8d5f78e.js → 33455fb3bc784ff7.js} +1 -1
  156. package/web/.next/static/chunks/{5899695755eaa473.js → 4bf02c8026557af4.js} +1 -1
  157. package/web/.next/static/chunks/{249bc4f5cd8521ea.js → 578d0b0ebe3a3e40.js} +2 -2
  158. package/web/.next/static/chunks/{a9a3c672ca96cd12.js → 69272ebd88407961.js} +1 -1
  159. package/web/.next/static/chunks/{742a6e0450bd20f3.js → 6d582f744c3f3ec1.js} +1 -1
  160. package/web/.next/static/chunks/{f6fafa215c01d77c.js → 8da51944bb9e031e.js} +1 -1
  161. package/web/.next/static/chunks/{81c04fca969aebf0.js → 98109291dea445ed.js} +1 -1
  162. package/web/.next/static/chunks/{69aab1a047573cb4.js → f87e3c9864fcbeb9.js} +1 -1
  163. package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js +0 -3
  164. package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js.map +0 -1
  165. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +0 -3
  166. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js.map +0 -1
  167. package/web/.next/static/chunks/7c026a8c682b1a16.js +0 -2
  168. /package/web/.next/static/{KzXiolX5g91WF6b-Yje6b → cvI6kxA93NQLNSEozqU8v}/_buildManifest.js +0 -0
  169. /package/web/.next/static/{KzXiolX5g91WF6b-Yje6b → cvI6kxA93NQLNSEozqU8v}/_clientMiddlewareManifest.json +0 -0
  170. /package/web/.next/static/{KzXiolX5g91WF6b-Yje6b → cvI6kxA93NQLNSEozqU8v}/_ssgManifest.js +0 -0
@@ -20,6 +20,7 @@ import type { IAgentRunRepository } from '../../ports/output/agents/agent-run-re
20
20
  import type { IGitPrService } from '../../ports/output/services/git-pr-service.interface.js';
21
21
  export interface DeleteFeatureOptions {
22
22
  cleanup?: boolean;
23
+ cascadeDelete?: boolean;
23
24
  }
24
25
  export declare class DeleteFeatureUseCase {
25
26
  private readonly featureRepo;
@@ -29,6 +30,8 @@ export declare class DeleteFeatureUseCase {
29
30
  private readonly gitPrService;
30
31
  constructor(featureRepo: IFeatureRepository, worktreeService: IWorktreeService, processService: IFeatureAgentProcessService, runRepo: IAgentRunRepository, gitPrService: IGitPrService);
31
32
  execute(featureId: string, options?: DeleteFeatureOptions): Promise<Feature>;
33
+ /** Relocate direct children one level up (set their parentId to the deleted feature's parentId). */
34
+ private relocateChildren;
32
35
  /** Recursively soft-delete all children (depth-first). */
33
36
  private cascadeSoftDelete;
34
37
  /** Set lifecycle to Deleting and soft-delete the feature. */
@@ -1 +1 @@
1
- {"version":3,"file":"delete-feature.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/features/delete-feature.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qCAAqC,CAAC;AAEnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8DAA8D,CAAC;AAChH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yDAAyD,CAAC;AAE7F,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBACa,oBAAoB;IAEC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAE5D,OAAO,CAAC,QAAQ,CAAC,cAAc;IACA,OAAO,CAAC,QAAQ,CAAC,OAAO;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBALP,WAAW,EAAE,kBAAkB,EACjC,eAAe,EAAE,gBAAgB,EAE7D,cAAc,EAAE,2BAA2B,EACZ,OAAO,EAAE,mBAAmB,EAClC,YAAY,EAAE,aAAa;IAGjE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBlF,0DAA0D;YAC5C,iBAAiB;IAQ/B,6DAA6D;YAC/C,yBAAyB;IAWvC,sDAAsD;YACxC,sBAAsB;IAYpC,6EAA6E;YAC/D,oBAAoB;CAsDnC"}
1
+ {"version":3,"file":"delete-feature.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/features/delete-feature.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qCAAqC,CAAC;AAEnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8DAA8D,CAAC;AAChH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yDAAyD,CAAC;AAE7F,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,qBACa,oBAAoB;IAEC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAE5D,OAAO,CAAC,QAAQ,CAAC,cAAc;IACA,OAAO,CAAC,QAAQ,CAAC,OAAO;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBALP,WAAW,EAAE,kBAAkB,EACjC,eAAe,EAAE,gBAAgB,EAE7D,cAAc,EAAE,2BAA2B,EACZ,OAAO,EAAE,mBAAmB,EAClC,YAAY,EAAE,aAAa;IAGjE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA8BlF,oGAAoG;YACtF,gBAAgB;IAW9B,0DAA0D;YAC5C,iBAAiB;IAQ/B,6DAA6D;YAC/C,yBAAyB;IAWvC,sDAAsD;YACxC,sBAAsB;IAYpC,6EAA6E;YAC/D,oBAAoB;CAsDnC"}
@@ -46,15 +46,35 @@ let DeleteFeatureUseCase = class DeleteFeatureUseCase {
46
46
  if (!feature) {
47
47
  throw new Error(`Feature not found: "${featureId}"`);
48
48
  }
49
- // 2. Immediately soft-delete the feature and all children
49
+ const cascadeDelete = options?.cascadeDelete === true;
50
+ // 2. Immediately soft-delete the feature (and children if cascading)
50
51
  // This makes them vanish from all queries right away (no reappear bug)
51
- await this.cascadeSoftDelete(feature.id);
52
+ if (cascadeDelete) {
53
+ await this.cascadeSoftDelete(feature.id);
54
+ }
55
+ else {
56
+ // Relocate direct children one level up in the hierarchy
57
+ await this.relocateChildren(feature.id, feature.parentId);
58
+ }
52
59
  await this.markDeletingAndSoftDelete(feature);
53
60
  // 3. Then perform cleanup (best-effort, feature is already hidden)
54
- await this.cascadeCleanupChildren(feature.id, options);
61
+ if (cascadeDelete) {
62
+ await this.cascadeCleanupChildren(feature.id, options);
63
+ }
55
64
  await this.cleanupSingleFeature(feature, options);
56
65
  return feature;
57
66
  }
67
+ /** Relocate direct children one level up (set their parentId to the deleted feature's parentId). */
68
+ async relocateChildren(parentId, newParentId) {
69
+ const children = await this.featureRepo.findByParentId(parentId);
70
+ for (const child of children) {
71
+ await this.featureRepo.update({
72
+ ...child,
73
+ parentId: newParentId,
74
+ updatedAt: new Date(),
75
+ });
76
+ }
77
+ }
58
78
  /** Recursively soft-delete all children (depth-first). */
59
79
  async cascadeSoftDelete(parentId) {
60
80
  const children = await this.featureRepo.findByParentId(parentId);
@@ -1,5 +1,5 @@
1
1
  import type { Feature } from '../../../../../packages/core/src/domain/generated/output.js';
2
- export declare function deleteFeature(featureId: string, cleanup?: boolean): Promise<{
2
+ export declare function deleteFeature(featureId: string, cleanup?: boolean, cascadeDelete?: boolean): Promise<{
3
3
  feature?: Feature;
4
4
  error?: string;
5
5
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"delete-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/delete-feature.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAEpE,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBhD"}
1
+ {"version":3,"file":"delete-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/delete-feature.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAEpE,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,GACtB,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBhD"}
@@ -1,13 +1,18 @@
1
1
  'use server';
2
2
  import { resolve } from '../../lib/server-container.js';
3
- export async function deleteFeature(featureId, cleanup) {
3
+ export async function deleteFeature(featureId, cleanup, cascadeDelete) {
4
4
  if (!featureId?.trim()) {
5
5
  return { error: 'id is required' };
6
6
  }
7
7
  try {
8
8
  const deleteFeatureUseCase = resolve('DeleteFeatureUseCase');
9
- const feature = cleanup !== undefined
10
- ? await deleteFeatureUseCase.execute(featureId, { cleanup })
9
+ const options = {};
10
+ if (cleanup !== undefined)
11
+ options.cleanup = cleanup;
12
+ if (cascadeDelete !== undefined)
13
+ options.cascadeDelete = cascadeDelete;
14
+ const feature = Object.keys(options).length > 0
15
+ ? await deleteFeatureUseCase.execute(featureId, options)
11
16
  : await deleteFeatureUseCase.execute(featureId);
12
17
  return { feature };
13
18
  }
@@ -4,12 +4,9 @@ export declare const dynamic = "force-dynamic";
4
4
  /**
5
5
  * Returns runtime version info.
6
6
  *
7
- * NEXT_PUBLIC_* env vars are inlined at build time for client components,
8
- * so client-side code sees the version from when the bundle was built
9
- * not the currently running CLI version.
10
- *
11
- * This API route reads the env vars at runtime on the server, ensuring
12
- * the client always gets the correct version.
7
+ * Reads the version from the DI container's VersionService the same source
8
+ * used by `shep --version`. This avoids stale values from NEXT_PUBLIC_* env
9
+ * vars which Next.js inlines at build time.
13
10
  */
14
11
  export declare function GET(): NextResponse;
15
12
  //# sourceMappingURL=route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/app/api/version/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,6EAA6E;AAC7E,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,GAAG,IAAI,YAAY,CAUlC"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/app/api/version/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,6EAA6E;AAC7E,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,GAAG,IAAI,YAAY,CA0BlC"}
@@ -1,24 +1,38 @@
1
1
  import { NextResponse } from 'next/server';
2
+ import { resolve } from '../../../lib/server-container.js';
2
3
  /** Prevent Next.js from statically prerendering this route at build time. */
3
4
  export const dynamic = 'force-dynamic';
4
5
  /**
5
6
  * Returns runtime version info.
6
7
  *
7
- * NEXT_PUBLIC_* env vars are inlined at build time for client components,
8
- * so client-side code sees the version from when the bundle was built
9
- * not the currently running CLI version.
10
- *
11
- * This API route reads the env vars at runtime on the server, ensuring
12
- * the client always gets the correct version.
8
+ * Reads the version from the DI container's VersionService the same source
9
+ * used by `shep --version`. This avoids stale values from NEXT_PUBLIC_* env
10
+ * vars which Next.js inlines at build time.
13
11
  */
14
12
  export function GET() {
15
- return NextResponse.json({
16
- version: process.env.NEXT_PUBLIC_SHEP_VERSION ?? 'unknown',
17
- packageName: process.env.NEXT_PUBLIC_SHEP_PACKAGE_NAME ?? '@shepai/cli',
18
- description: process.env.NEXT_PUBLIC_SHEP_DESCRIPTION ?? 'Autonomous AI Native SDLC Platform',
19
- branch: process.env.NEXT_PUBLIC_SHEP_BRANCH ?? '',
20
- commitHash: process.env.NEXT_PUBLIC_SHEP_COMMIT ?? '',
21
- instancePath: process.env.NEXT_PUBLIC_SHEP_INSTANCE_PATH ?? '',
22
- isDev: process.env.NODE_ENV === 'development',
23
- });
13
+ try {
14
+ const versionService = resolve('IVersionService');
15
+ const { version, name, description } = versionService.getVersion();
16
+ return NextResponse.json({
17
+ version,
18
+ packageName: name,
19
+ description,
20
+ branch: process.env.NEXT_PUBLIC_SHEP_BRANCH ?? '',
21
+ commitHash: process.env.NEXT_PUBLIC_SHEP_COMMIT ?? '',
22
+ instancePath: process.env.NEXT_PUBLIC_SHEP_INSTANCE_PATH ?? '',
23
+ isDev: process.env.NODE_ENV === 'development',
24
+ });
25
+ }
26
+ catch {
27
+ // DI container not available (e.g. during build) — fall back to env vars
28
+ return NextResponse.json({
29
+ version: process.env.NEXT_PUBLIC_SHEP_VERSION ?? 'unknown',
30
+ packageName: process.env.NEXT_PUBLIC_SHEP_PACKAGE_NAME ?? '@shepai/cli',
31
+ description: process.env.NEXT_PUBLIC_SHEP_DESCRIPTION ?? 'Autonomous AI Native SDLC Platform',
32
+ branch: process.env.NEXT_PUBLIC_SHEP_BRANCH ?? '',
33
+ commitHash: process.env.NEXT_PUBLIC_SHEP_COMMIT ?? '',
34
+ instancePath: process.env.NEXT_PUBLIC_SHEP_INSTANCE_PATH ?? '',
35
+ isDev: process.env.NODE_ENV === 'development',
36
+ });
37
+ }
24
38
  }
@@ -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":"AAuCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAI/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAgjB1F"}
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;AAI/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAmjB1F"}
@@ -253,10 +253,10 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
253
253
  }, [view, featureNode, prdData, prdSelections, onClose]);
254
254
  const handleTechApprove = useCallback(() => handleSimpleApprove('Plan'), [handleSimpleApprove]);
255
255
  const handleMergeApprove = useCallback(() => handleSimpleApprove('Merge'), [handleSimpleApprove]);
256
- const handleDelete = useCallback(async (featureId, cleanup) => {
256
+ const handleDelete = useCallback(async (featureId, cleanup, cascadeDelete) => {
257
257
  setIsDeleting(true);
258
258
  try {
259
- const result = await deleteFeature(featureId, cleanup);
259
+ const result = await deleteFeature(featureId, cleanup, cascadeDelete);
260
260
  if (result.error) {
261
261
  toast.error(result.error);
262
262
  return;
@@ -327,7 +327,7 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
327
327
  const repoName = featureNode.repositoryName ??
328
328
  featureNode.repositoryPath.split('/').filter(Boolean).at(-1) ??
329
329
  '';
330
- header = (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-header", children: [_jsx(DrawerTitle, { children: featureNode.name }), 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, _jsx(DrawerDescription, { className: "sr-only", children: featureNode.name })] }), featureActionsInput ? (_jsxs("div", { className: "flex items-center gap-2 pt-2", "data-testid": "feature-drawer-actions", children: [_jsx(OpenActionMenu, { actions: featureActions, repositoryPath: featureActionsInput.repositoryPath, showSpecs: !!featureActionsInput.specPath }), featureFlags.envDeploy && featureDeployTarget ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(ActionButton, { label: isFeatureDeployActive ? 'Stop Dev Server' : 'Start Dev Server', 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 ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), 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": "Copy feature ID", "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: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Delete feature", 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) => handleDelete(featureNode.featureId, cleanup), isDeleting: isDeleting, featureName: featureNode.name, featureId: featureNode.featureId })] })) : null] })) : null] }));
330
+ header = (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-header", children: [_jsx(DrawerTitle, { children: featureNode.name }), 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, _jsx(DrawerDescription, { className: "sr-only", children: featureNode.name })] }), featureActionsInput ? (_jsxs("div", { className: "flex items-center gap-2 pt-2", "data-testid": "feature-drawer-actions", children: [_jsx(OpenActionMenu, { actions: featureActions, repositoryPath: featureActionsInput.repositoryPath, showSpecs: !!featureActionsInput.specPath }), featureFlags.envDeploy && featureDeployTarget ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(ActionButton, { label: isFeatureDeployActive ? 'Stop Dev Server' : 'Start Dev Server', 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 ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), 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": "Copy feature ID", "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: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Delete feature", 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) => handleDelete(featureNode.featureId, cleanup, cascadeDelete), isDeleting: isDeleting, featureName: featureNode.name, featureId: featureNode.featureId, hasChildren: featureNode.hasChildren })] })) : null] })) : null] }));
331
331
  }
332
332
  // ── Body ──────────────────────────────────────────────────────────────
333
333
  let body = null;
@@ -1,9 +1,10 @@
1
1
  export interface DeleteFeatureDialogProps {
2
2
  open: boolean;
3
3
  onOpenChange: (open: boolean) => void;
4
- onConfirm: (cleanup: boolean) => void;
4
+ onConfirm: (cleanup: boolean, cascadeDelete: boolean) => void;
5
5
  isDeleting: boolean;
6
6
  featureName: string;
7
7
  featureId: string;
8
+ hasChildren?: boolean;
8
9
  }
9
10
  //# sourceMappingURL=delete-feature-dialog-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-feature-dialog-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog-config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"delete-feature-dialog-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog-config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
@@ -1,3 +1,3 @@
1
1
  import type { DeleteFeatureDialogProps } from './delete-feature-dialog-config.js';
2
- export declare function DeleteFeatureDialog({ open, onOpenChange, onConfirm, isDeleting, featureName, featureId, }: DeleteFeatureDialogProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function DeleteFeatureDialog({ open, onOpenChange, onConfirm, isDeleting, featureName, featureId, hasChildren, }: DeleteFeatureDialogProps): import("react/jsx-runtime").JSX.Element;
3
3
  //# sourceMappingURL=delete-feature-dialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-feature-dialog.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,WAAW,EACX,SAAS,GACV,EAAE,wBAAwB,2CAqD1B"}
1
+ {"version":3,"file":"delete-feature-dialog.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,GACZ,EAAE,wBAAwB,2CAuE1B"}
@@ -5,12 +5,14 @@ import { Loader2 } from 'lucide-react';
5
5
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../../ui/alert-dialog.js';
6
6
  import { Checkbox } from '../../ui/checkbox.js';
7
7
  import { Label } from '../../ui/label.js';
8
- export function DeleteFeatureDialog({ open, onOpenChange, onConfirm, isDeleting, featureName, featureId, }) {
8
+ export function DeleteFeatureDialog({ open, onOpenChange, onConfirm, isDeleting, featureName, featureId, hasChildren, }) {
9
9
  const [cleanup, setCleanup] = useState(true);
10
+ const [cascadeDelete, setCascadeDelete] = useState(false);
10
11
  useEffect(() => {
11
12
  if (open) {
12
13
  setCleanup(true);
14
+ setCascadeDelete(false);
13
15
  }
14
16
  }, [open]);
15
- return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete feature?" }), _jsxs(AlertDialogDescription, { children: ["This will permanently delete ", _jsx("strong", { children: featureName }), " (", featureId, "). This action cannot be undone."] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "cleanup-checkbox", checked: cleanup, onCheckedChange: (checked) => setCleanup(checked === true), disabled: isDeleting, "aria-label": "Clean up worktree and branches" }), _jsx(Label, { htmlFor: "cleanup-checkbox", className: "cursor-pointer text-sm", children: "Clean up worktree and branches" })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isDeleting, onClick: () => onOpenChange(false), children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", disabled: isDeleting, onClick: () => onConfirm(cleanup), children: isDeleting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Deleting\u2026"] })) : ('Delete') })] })] }) }));
17
+ return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete feature?" }), _jsxs(AlertDialogDescription, { children: ["This will permanently delete ", _jsx("strong", { children: featureName }), " (", featureId, "). This action cannot be undone."] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "cleanup-checkbox", checked: cleanup, onCheckedChange: (checked) => setCleanup(checked === true), disabled: isDeleting, "aria-label": "Clean up worktree and branches" }), _jsx(Label, { htmlFor: "cleanup-checkbox", className: "cursor-pointer text-sm", children: "Clean up worktree and branches" })] }), hasChildren ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "cascade-delete-checkbox", checked: cascadeDelete, onCheckedChange: (checked) => setCascadeDelete(checked === true), disabled: isDeleting, "aria-label": "Delete sub-features" }), _jsx(Label, { htmlFor: "cascade-delete-checkbox", className: "cursor-pointer text-sm", children: "Delete sub-features" })] })) : null] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isDeleting, onClick: () => onOpenChange(false), children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", disabled: isDeleting, onClick: () => onConfirm(cleanup, cascadeDelete), children: isDeleting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Deleting\u2026"] })) : ('Delete') })] })] }) }));
16
18
  }
@@ -4,5 +4,7 @@ declare const meta: Meta<typeof DeleteFeatureDialog>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof DeleteFeatureDialog>;
6
6
  export declare const Default: Story;
7
+ export declare const WithChildren: Story;
7
8
  export declare const Deleting: Story;
9
+ export declare const DeletingWithChildren: Story;
8
10
  //# sourceMappingURL=delete-feature-dialog.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-feature-dialog.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAe1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAElD,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC;AAEjC,eAAO,MAAM,QAAQ,EAAE,KAItB,CAAC"}
1
+ {"version":3,"file":"delete-feature-dialog.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/delete-feature-dialog/delete-feature-dialog.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAe1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAElD,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC;AAEjC,eAAO,MAAM,YAAY,EAAE,KAI1B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAItB,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAKlC,CAAC"}
@@ -18,8 +18,19 @@ const meta = {
18
18
  };
19
19
  export default meta;
20
20
  export const Default = {};
21
+ export const WithChildren = {
22
+ args: {
23
+ hasChildren: true,
24
+ },
25
+ };
21
26
  export const Deleting = {
22
27
  args: {
23
28
  isDeleting: true,
24
29
  },
25
30
  };
31
+ export const DeletingWithChildren = {
32
+ args: {
33
+ isDeleting: true,
34
+ hasChildren: true,
35
+ },
36
+ };
@@ -66,7 +66,8 @@ export interface FeatureNodeData {
66
66
  };
67
67
  onAction?: () => void;
68
68
  onSettings?: () => void;
69
- onDelete?: (featureId: string, cleanup?: boolean) => void;
69
+ hasChildren?: boolean;
70
+ onDelete?: (featureId: string, cleanup?: boolean, cascadeDelete?: boolean) => void;
70
71
  onRetry?: (featureId: string) => void;
71
72
  showHandles?: boolean;
72
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,SAAS,GACT,iBAAiB,GACjB,MAAM,GACN,SAAS,GACT,OAAO,GACP,UAAU,CAAC;AAEf,MAAM,MAAM,qBAAqB,GAC7B,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,UAAU,CAAC;AAEf,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOxE,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,EAAE,CAAC,EAAE;QACH,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,QAAQ,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,+DAA+D;IAC/D,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,eAAe,CAAC;QACxB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAuEnF,CAAC"}
1
+ {"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA0DnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAqTA"}
1
+ {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA0DnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAsTA"}
@@ -70,10 +70,10 @@ export function FeatureNode({ data, selected, }) {
70
70
  return (_jsxs("div", { className: "animate-in fade-in group relative duration-300", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, data.onDelete && data.featureId && data.state !== 'deleting' ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute top-1/2 -left-10 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100", onPointerDown: (e) => e.stopPropagation(), children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Delete feature", "data-testid": "feature-node-delete-button", onClick: (e) => {
71
71
  e.stopPropagation();
72
72
  setConfirmOpen(true);
73
- }, className: "bg-card text-muted-foreground hover:border-destructive hover:text-destructive flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border shadow-sm transition-colors", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Delete feature" })] }) }) }), _jsx(DeleteFeatureDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, onConfirm: (cleanup) => {
73
+ }, className: "bg-card text-muted-foreground hover:border-destructive hover:text-destructive flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border shadow-sm transition-colors", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Delete feature" })] }) }) }), _jsx(DeleteFeatureDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, onConfirm: (cleanup, cascadeDelete) => {
74
74
  setConfirmOpen(false);
75
- data.onDelete?.(data.featureId, cleanup);
76
- }, isDeleting: false, featureName: data.name ?? 'this feature', featureId: data.featureId })] })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' || data.state === 'deleting' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 cursor-pointer flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2', data.state === 'deleting' && 'opacity-60'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: data.state === 'blocked' ? 'BLOCKED' : lifecycleDisplayLabels[data.lifecycle] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [data.fastMode ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { "data-testid": "feature-node-fast-mode-badge", className: "-mt-1 p-1", children: _jsx(Zap, { className: "h-3.5 w-3.5 text-amber-500" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Fast Mode" })] }) })) : null, data.agentType ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": agentTypeLabels[data.agentType] ?? data.agentType, "data-testid": "feature-node-agent-badge", onClick: (e) => {
75
+ data.onDelete?.(data.featureId, cleanup, cascadeDelete);
76
+ }, isDeleting: false, featureName: data.name ?? 'this feature', featureId: data.featureId, hasChildren: data.hasChildren })] })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' || data.state === 'deleting' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 cursor-pointer flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2', data.state === 'deleting' && 'opacity-60'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: data.state === 'blocked' ? 'BLOCKED' : lifecycleDisplayLabels[data.lifecycle] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [data.fastMode ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { "data-testid": "feature-node-fast-mode-badge", className: "-mt-1 p-1", children: _jsx(Zap, { className: "h-3.5 w-3.5 text-amber-500" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Fast Mode" })] }) })) : null, data.agentType ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": agentTypeLabels[data.agentType] ?? data.agentType, "data-testid": "feature-node-agent-badge", onClick: (e) => {
77
77
  e.stopPropagation();
78
78
  data.onSettings?.();
79
79
  }, className: cn('nodrag -mt-1 -mr-1 p-1', data.onSettings
@@ -14,7 +14,7 @@ export interface ControlCenterState {
14
14
  repoPath: string;
15
15
  };
16
16
  handleLayout: (direction: LayoutDirection) => void;
17
- handleDeleteFeature: (featureId: string, cleanup?: boolean) => void;
17
+ handleDeleteFeature: (featureId: string, cleanup?: boolean, cascadeDelete?: boolean) => void;
18
18
  handleRetryFeature: (featureId: string) => void;
19
19
  handleDeleteRepository: (repositoryId: string) => Promise<void>;
20
20
  createFeatureNode: (sourceNodeId: string | null, dataOverride?: Partial<FeatureNodeData>, edgeType?: string) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAcjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpE,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA6bpB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAcjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7F,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAgcpB"}
@@ -221,17 +221,20 @@ export function useControlCenterState(initialNodes, initialEdges) {
221
221
  toast.error('Failed to resume feature');
222
222
  });
223
223
  }, [updateFeature]);
224
- const handleDeleteFeature = useCallback((featureId, cleanup) => {
224
+ const handleDeleteFeature = useCallback((featureId, cleanup, cascadeDelete) => {
225
225
  const nodeId = `feat-${featureId}`;
226
+ const shouldCascade = cascadeDelete === true;
226
227
  // Collect all descendant feature node IDs (children, grandchildren, etc.)
227
228
  const descendants = [];
228
- const queue = [nodeId];
229
- while (queue.length > 0) {
230
- const current = queue.shift();
231
- for (const edge of edgesRef.current) {
232
- if (edge.type === 'dependencyEdge' && edge.source === current) {
233
- descendants.push(edge.target);
234
- queue.push(edge.target);
229
+ if (shouldCascade) {
230
+ const queue = [nodeId];
231
+ while (queue.length > 0) {
232
+ const current = queue.shift();
233
+ for (const edge of edgesRef.current) {
234
+ if (edge.type === 'dependencyEdge' && edge.source === current) {
235
+ descendants.push(edge.target);
236
+ queue.push(edge.target);
237
+ }
235
238
  }
236
239
  }
237
240
  }
@@ -243,14 +246,14 @@ export function useControlCenterState(initialNodes, initialEdges) {
243
246
  prevStates.set(nid, node.data.state);
244
247
  }
245
248
  }
246
- // Optimistic: show "deleting" state on parent AND all descendants
249
+ // Optimistic: show "deleting" state on parent (and descendants if cascading)
247
250
  updateFeature(nodeId, { state: 'deleting' });
248
251
  for (const childId of descendants) {
249
252
  updateFeature(childId, { state: 'deleting' });
250
253
  }
251
254
  deleteSound.play();
252
255
  router.push('/');
253
- deleteFeature(featureId, cleanup)
256
+ deleteFeature(featureId, cleanup, cascadeDelete)
254
257
  .then((result) => {
255
258
  if (result.error) {
256
259
  // Rollback all to previous states
@@ -28,7 +28,7 @@ export interface GraphCallbacks {
28
28
  /** Called when the user opens settings/details on a feature node. */
29
29
  onNodeSettings?: (nodeId: string) => void;
30
30
  /** Called when the user deletes a feature. */
31
- onFeatureDelete?: (featureId: string, cleanup?: boolean) => void;
31
+ onFeatureDelete?: (featureId: string, cleanup?: boolean, cascadeDelete?: boolean) => void;
32
32
  /** Called when the user clicks the "+" add-feature button on a repo node. */
33
33
  onRepositoryAdd?: (repoNodeId: string) => void;
34
34
  /** Called when the user clicks a repo node to navigate to its detail page. */
@@ -1 +1 @@
1
- {"version":3,"file":"derive-graph.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/derive-graph.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,4FAA4F;IAC5F,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,qEAAqE;IACrE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,8CAA8C;IAC9C,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,6EAA6E;IAC7E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,qDAAqD;IACrD,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACrC,SAAS,CAAC,EAAE,cAAc,GACzB;IAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAqI5C"}
1
+ {"version":3,"file":"derive-graph.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/derive-graph.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,4FAA4F;IAC5F,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,qEAAqE;IACrE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,8CAA8C;IAC9C,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1F,6EAA6E;IAC7E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,qDAAqD;IACrD,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACrC,SAAS,CAAC,EAAE,cAAc,GACzB;IAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAiJ5C"}
@@ -129,10 +129,21 @@ export function deriveGraph(featureMap, repoMap, pendingMap, callbacks) {
129
129
  });
130
130
  }
131
131
  }
132
- // Inject showHandles based on whether there are any edges
132
+ // Build set of feature node IDs that have children (are dependency edge sources)
133
+ const parentNodeIds = new Set();
134
+ for (const edge of edges) {
135
+ if (edge.type === 'dependencyEdge') {
136
+ parentNodeIds.add(edge.source);
137
+ }
138
+ }
139
+ // Inject showHandles and hasChildren based on edges
133
140
  const hasEdges = edges.length > 0;
134
141
  for (const node of nodes) {
135
- node.data.showHandles = hasEdges;
142
+ const data = node.data;
143
+ data.showHandles = hasEdges;
144
+ if (node.type === 'featureNode') {
145
+ data.hasChildren = parentNodeIds.has(node.id);
146
+ }
136
147
  }
137
148
  return { nodes, edges };
138
149
  }
@@ -2,9 +2,9 @@
2
2
  * Version Information Helpers
3
3
  *
4
4
  * Provides version and system info for the Web UI.
5
- * Reads from NEXT_PUBLIC_SHEP_* environment variables which are set by:
6
- * 1. The CLI (`shep ui`) via setVersionEnvVars() before starting Next.js
7
- * 2. next.config.ts dev fallback for standalone `pnpm dev:web` mode
5
+ * Reads the version from the DI container's VersionService the same source
6
+ * used by `shep --version`. Falls back to NEXT_PUBLIC_* env vars if the
7
+ * container is unavailable.
8
8
  *
9
9
  * The VersionInfo shape mirrors src/domain/value-objects/version-info.ts
10
10
  * from the CLI domain layer.
@@ -24,8 +24,8 @@ export interface SystemInfo {
24
24
  arch: string;
25
25
  }
26
26
  /**
27
- * Get version info from environment variables.
28
- * Falls back to defaults if not set.
27
+ * Get version info from the DI container's VersionService.
28
+ * Falls back to env vars if the container is unavailable.
29
29
  */
30
30
  export declare function getVersionInfo(): VersionInfo;
31
31
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,iCAAiC;AACjC,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAM1C"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,iCAAiC;AACjC,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAoB5C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAM1C"}
@@ -2,26 +2,40 @@
2
2
  * Version Information Helpers
3
3
  *
4
4
  * Provides version and system info for the Web UI.
5
- * Reads from NEXT_PUBLIC_SHEP_* environment variables which are set by:
6
- * 1. The CLI (`shep ui`) via setVersionEnvVars() before starting Next.js
7
- * 2. next.config.ts dev fallback for standalone `pnpm dev:web` mode
5
+ * Reads the version from the DI container's VersionService the same source
6
+ * used by `shep --version`. Falls back to NEXT_PUBLIC_* env vars if the
7
+ * container is unavailable.
8
8
  *
9
9
  * The VersionInfo shape mirrors src/domain/value-objects/version-info.ts
10
10
  * from the CLI domain layer.
11
11
  */
12
12
  import { arch, platform } from 'node:os';
13
+ import { resolve } from './server-container.js';
13
14
  /**
14
- * Get version info from environment variables.
15
- * Falls back to defaults if not set.
15
+ * Get version info from the DI container's VersionService.
16
+ * Falls back to env vars if the container is unavailable.
16
17
  */
17
18
  export function getVersionInfo() {
18
- return {
19
- version: process.env.NEXT_PUBLIC_SHEP_VERSION ?? 'unknown',
20
- name: process.env.NEXT_PUBLIC_SHEP_PACKAGE_NAME ?? '@shepai/cli',
21
- description: process.env.NEXT_PUBLIC_SHEP_DESCRIPTION ?? 'Autonomous AI Native SDLC Platform',
22
- branch: process.env.NEXT_PUBLIC_SHEP_BRANCH ?? '',
23
- commitHash: process.env.NEXT_PUBLIC_SHEP_COMMIT ?? '',
24
- };
19
+ try {
20
+ const versionService = resolve('IVersionService');
21
+ const { version, name, description } = versionService.getVersion();
22
+ return {
23
+ version,
24
+ name,
25
+ description,
26
+ branch: process.env.NEXT_PUBLIC_SHEP_BRANCH ?? '',
27
+ commitHash: process.env.NEXT_PUBLIC_SHEP_COMMIT ?? '',
28
+ };
29
+ }
30
+ catch {
31
+ return {
32
+ version: process.env.NEXT_PUBLIC_SHEP_VERSION ?? 'unknown',
33
+ name: process.env.NEXT_PUBLIC_SHEP_PACKAGE_NAME ?? '@shepai/cli',
34
+ description: process.env.NEXT_PUBLIC_SHEP_DESCRIPTION ?? 'Autonomous AI Native SDLC Platform',
35
+ branch: process.env.NEXT_PUBLIC_SHEP_BRANCH ?? '',
36
+ commitHash: process.env.NEXT_PUBLIC_SHEP_COMMIT ?? '',
37
+ };
38
+ }
25
39
  }
26
40
  /**
27
41
  * Get system runtime information.