@shepai/cli 1.62.0 → 1.64.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 (221) hide show
  1. package/dist/packages/core/src/application/ports/output/agents/agent-executor-factory.interface.d.ts +18 -0
  2. package/dist/packages/core/src/application/ports/output/agents/agent-executor-factory.interface.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/ports/output/agents/index.d.ts +1 -1
  4. package/dist/packages/core/src/application/ports/output/agents/index.d.ts.map +1 -1
  5. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-factory.service.d.ts +2 -1
  6. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-factory.service.d.ts.map +1 -1
  7. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-factory.service.js +7 -0
  8. package/dist/packages/core/src/infrastructure/services/agents/common/executors/mock-executor-factory.service.d.ts +2 -1
  9. package/dist/packages/core/src/infrastructure/services/agents/common/executors/mock-executor-factory.service.d.ts.map +1 -1
  10. package/dist/packages/core/src/infrastructure/services/agents/common/executors/mock-executor-factory.service.js +3 -0
  11. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.d.ts +7 -0
  12. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.d.ts.map +1 -1
  13. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.js +9 -0
  14. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
  15. package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +18 -4
  16. package/dist/packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.js +1 -1
  17. package/dist/src/presentation/cli/commands/daemon/start-daemon.d.ts.map +1 -1
  18. package/dist/src/presentation/cli/commands/daemon/start-daemon.js +24 -15
  19. package/dist/src/presentation/cli/commands/status.command.d.ts +7 -0
  20. package/dist/src/presentation/cli/commands/status.command.d.ts.map +1 -1
  21. package/dist/src/presentation/cli/commands/status.command.js +167 -3
  22. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts +17 -0
  23. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts.map +1 -0
  24. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +23 -0
  25. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.d.ts +22 -0
  26. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.d.ts.map +1 -0
  27. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.js +56 -0
  28. package/dist/src/presentation/web/components/common/base-drawer/index.d.ts +2 -0
  29. package/dist/src/presentation/web/components/common/base-drawer/index.d.ts.map +1 -0
  30. package/dist/src/presentation/web/components/common/base-drawer/index.js +1 -0
  31. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  32. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +9 -16
  33. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +1 -1
  34. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +1 -1
  35. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts.map +1 -1
  36. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +14 -14
  37. package/dist/src/presentation/web/components/common/repository-node/index.d.ts +1 -0
  38. package/dist/src/presentation/web/components/common/repository-node/index.d.ts.map +1 -1
  39. package/dist/src/presentation/web/components/common/repository-node/index.js +1 -0
  40. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.d.ts +7 -0
  41. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.d.ts.map +1 -0
  42. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.js +12 -0
  43. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.d.ts +9 -0
  44. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.d.ts.map +1 -0
  45. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.js +34 -0
  46. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.d.ts.map +1 -1
  47. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.js +4 -6
  48. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  49. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +20 -2
  50. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +3 -1
  51. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
  52. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +4 -2
  53. package/dist/src/presentation/web/components/features/skills/skill-detail-drawer.d.ts.map +1 -1
  54. package/dist/src/presentation/web/components/features/skills/skill-detail-drawer.js +3 -6
  55. package/dist/tsconfig.build.tsbuildinfo +1 -1
  56. package/package.json +1 -1
  57. package/web/.next/BUILD_ID +1 -1
  58. package/web/.next/build-manifest.json +2 -2
  59. package/web/.next/cache/.previewinfo +1 -1
  60. package/web/.next/cache/.rscinfo +1 -1
  61. package/web/.next/cache/.tsbuildinfo +1 -1
  62. package/web/.next/cache/config.json +3 -3
  63. package/web/.next/fallback-build-manifest.json +2 -2
  64. package/web/.next/prerender-manifest.json +3 -3
  65. package/web/.next/required-server-files.js +1 -1
  66. package/web/.next/required-server-files.json +1 -1
  67. package/web/.next/server/app/_global-error.html +2 -2
  68. package/web/.next/server/app/_global-error.rsc +1 -1
  69. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  70. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  72. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  73. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  74. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  75. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  76. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  77. package/web/.next/server/app/page/server-reference-manifest.json +14 -14
  78. package/web/.next/server/app/page.js.nft.json +1 -1
  79. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  80. package/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
  81. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  82. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  83. package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  84. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  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.nft.json +1 -1
  88. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__5e0f14e9._.js +3 -0
  92. package/web/.next/server/chunks/ssr/[root-of-the-server]__5e0f14e9._.js.map +1 -0
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  97. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +4 -4
  98. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/[root-of-the-server]__ae251147._.js +3 -0
  100. package/web/.next/server/chunks/ssr/[root-of-the-server]__ae251147._.js.map +1 -0
  101. package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
  102. package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js.map +1 -1
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__dd5b62cb._.js +3 -0
  106. package/web/.next/server/chunks/ssr/[root-of-the-server]__dd5b62cb._.js.map +1 -0
  107. package/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
  108. package/web/.next/server/chunks/ssr/_73d14b70._.js +3 -0
  109. package/web/.next/server/chunks/ssr/_73d14b70._.js.map +1 -0
  110. package/web/.next/server/chunks/ssr/_7f386377._.js +3 -0
  111. package/web/.next/server/chunks/ssr/_7f386377._.js.map +1 -0
  112. package/web/.next/server/chunks/ssr/_d3711354._.js +2 -2
  113. package/web/.next/server/chunks/ssr/_d3711354._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/{_a64b7b24._.js → _d81184e2._.js} +2 -2
  115. package/web/.next/server/chunks/ssr/_d81184e2._.js.map +1 -0
  116. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/node_modules__pnpm_fe355030._.js → server/chunks/ssr/node_modules__pnpm_87f920e7._.js} +2 -2
  117. package/web/.next/server/chunks/ssr/{node_modules__pnpm_fe355030._.js.map → node_modules__pnpm_87f920e7._.js.map} +1 -1
  118. package/web/.next/server/chunks/ssr/src_presentation_web_components_7a0b09da._.js +3 -0
  119. package/web/.next/server/chunks/ssr/src_presentation_web_components_7a0b09da._.js.map +1 -0
  120. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +3 -0
  121. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -0
  122. package/web/.next/server/pages/500.html +2 -2
  123. package/web/.next/server/server-reference-manifest.js +1 -1
  124. package/web/.next/server/server-reference-manifest.json +15 -15
  125. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  126. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  127. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  128. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  129. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  130. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  131. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  132. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  133. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  134. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  135. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  136. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  137. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  138. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  139. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
  140. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  141. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  142. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
  143. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  144. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  145. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  146. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  147. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  148. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  149. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  150. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  151. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  152. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__5e0f14e9._.js +3 -0
  153. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  154. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  155. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +4 -4
  156. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__ae251147._.js +3 -0
  157. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
  158. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
  159. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__dd5b62cb._.js +3 -0
  160. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
  161. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_73d14b70._.js +3 -0
  162. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_7f386377._.js +3 -0
  163. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_d3711354._.js +2 -2
  164. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_a64b7b24._.js → _d81184e2._.js} +2 -2
  165. package/web/.next/{server/chunks/ssr/node_modules__pnpm_fe355030._.js → standalone/src/presentation/web/.next/server/chunks/ssr/node_modules__pnpm_87f920e7._.js} +2 -2
  166. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_7a0b09da._.js +3 -0
  167. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +3 -0
  168. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  169. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  170. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
  171. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.stories.tsx +207 -0
  172. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.tsx +90 -0
  173. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/index.ts +1 -0
  174. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx +1 -1
  175. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +143 -172
  176. package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/feature-drawer.tsx +119 -133
  177. package/web/.next/standalone/src/presentation/web/components/common/repository-node/index.ts +1 -0
  178. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-drawer.stories.tsx +58 -0
  179. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-drawer.tsx +82 -0
  180. package/web/.next/standalone/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.tsx +79 -96
  181. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +30 -2
  182. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +7 -0
  183. package/web/.next/standalone/src/presentation/web/components/features/skills/skill-detail-drawer.tsx +71 -77
  184. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  185. package/web/.next/static/chunks/04d22f0d67c6671b.js +10 -0
  186. package/web/.next/static/chunks/0c6654ec27f11c7e.js +1 -0
  187. package/web/.next/static/chunks/12c70bfd5951cf9b.js +1 -0
  188. package/web/.next/static/chunks/21541b346dd4dd28.js +1 -0
  189. package/web/.next/static/chunks/{cb1b27e4a21415d3.js → 3b941e59ac013e12.js} +2 -2
  190. package/web/.next/static/chunks/{09d898be63c54f20.js → 78919481e7c5ad4f.js} +1 -1
  191. package/web/.next/static/chunks/a5b6a22de303e877.css +2 -0
  192. package/web/.next/static/chunks/af7a5bcb7c49e46e.js +1 -0
  193. package/web/.next/static/chunks/bcd97add650ece51.js +1 -0
  194. package/web/.next/static/chunks/be784143669bb992.js +1 -0
  195. package/web/.next/trace +1 -1
  196. package/web/.next/trace-build +1 -1
  197. package/web/.next/server/chunks/ssr/[root-of-the-server]__2395adc6._.js +0 -3
  198. package/web/.next/server/chunks/ssr/[root-of-the-server]__2395adc6._.js.map +0 -1
  199. package/web/.next/server/chunks/ssr/[root-of-the-server]__87fda958._.js +0 -3
  200. package/web/.next/server/chunks/ssr/[root-of-the-server]__87fda958._.js.map +0 -1
  201. package/web/.next/server/chunks/ssr/[root-of-the-server]__ee7cffe1._.js +0 -3
  202. package/web/.next/server/chunks/ssr/[root-of-the-server]__ee7cffe1._.js.map +0 -1
  203. package/web/.next/server/chunks/ssr/_9915d2a7._.js +0 -3
  204. package/web/.next/server/chunks/ssr/_9915d2a7._.js.map +0 -1
  205. package/web/.next/server/chunks/ssr/_a64b7b24._.js.map +0 -1
  206. package/web/.next/server/chunks/ssr/_e7a4b0e4._.js +0 -3
  207. package/web/.next/server/chunks/ssr/_e7a4b0e4._.js.map +0 -1
  208. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2395adc6._.js +0 -3
  209. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__87fda958._.js +0 -3
  210. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__ee7cffe1._.js +0 -3
  211. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9915d2a7._.js +0 -3
  212. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_e7a4b0e4._.js +0 -3
  213. package/web/.next/static/chunks/13664c029245afc8.js +0 -1
  214. package/web/.next/static/chunks/25f42652257ff43d.js +0 -1
  215. package/web/.next/static/chunks/2934854f0378f815.js +0 -1
  216. package/web/.next/static/chunks/45f510f84c7c417d.css +0 -2
  217. package/web/.next/static/chunks/8e4c719503d9387e.js +0 -1
  218. package/web/.next/static/chunks/cd44f06f18e3425a.js +0 -10
  219. /package/web/.next/static/{2z-UoCok36_D1hh1-N88G → R37CbaEU-TK9xR6x4aI_Z}/_buildManifest.js +0 -0
  220. /package/web/.next/static/{2z-UoCok36_D1hh1-N88G → R37CbaEU-TK9xR6x4aI_Z}/_clientMiddlewareManifest.json +0 -0
  221. /package/web/.next/static/{2z-UoCok36_D1hh1-N88G → R37CbaEU-TK9xR6x4aI_Z}/_ssgManifest.js +0 -0
@@ -0,0 +1,207 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { BaseDrawer } from './base-drawer';
4
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
5
+ import { Button } from '@/components/ui/button';
6
+
7
+ const meta: Meta<typeof BaseDrawer> = {
8
+ title: 'Common/BaseDrawer',
9
+ component: BaseDrawer,
10
+ tags: ['autodocs'],
11
+ parameters: {
12
+ layout: 'fullscreen',
13
+ },
14
+ };
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof BaseDrawer>;
18
+
19
+ /* ---------------------------------------------------------------------------
20
+ * Trigger wrapper — starts closed, click to open
21
+ * ------------------------------------------------------------------------- */
22
+
23
+ function DrawerTrigger(props: Omit<React.ComponentProps<typeof BaseDrawer>, 'open' | 'onClose'>) {
24
+ const [open, setOpen] = useState(false);
25
+
26
+ return (
27
+ <div className="flex h-screen items-start p-4">
28
+ <Button variant="outline" onClick={() => setOpen(true)}>
29
+ Open Drawer
30
+ </Button>
31
+ <BaseDrawer {...props} open={open} onClose={() => setOpen(false)} />
32
+ </div>
33
+ );
34
+ }
35
+
36
+ /* ---------------------------------------------------------------------------
37
+ * Stories
38
+ * ------------------------------------------------------------------------- */
39
+
40
+ /** Default drawer with sm size, non-modal, children only. */
41
+ export const Default: Story = {
42
+ render: () => (
43
+ <DrawerTrigger>
44
+ <div className="p-4">
45
+ <p className="text-muted-foreground text-sm">Default drawer content</p>
46
+ </div>
47
+ </DrawerTrigger>
48
+ ),
49
+ };
50
+
51
+ /** Explicit sm size with inspector-style content. */
52
+ export const SmallSize: Story = {
53
+ render: () => (
54
+ <DrawerTrigger size="sm">
55
+ <div className="flex flex-col gap-3 p-4">
56
+ <h3 className="text-sm font-semibold">Feature Inspector</h3>
57
+ <div className="flex flex-col gap-1">
58
+ <span className="text-muted-foreground text-xs">Status</span>
59
+ <span className="text-sm">Running</span>
60
+ </div>
61
+ <div className="flex flex-col gap-1">
62
+ <span className="text-muted-foreground text-xs">Progress</span>
63
+ <div className="bg-muted h-2 w-full overflow-hidden rounded-full">
64
+ <div className="bg-primary h-full w-[45%] rounded-full" />
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </DrawerTrigger>
69
+ ),
70
+ };
71
+
72
+ /** Medium size with content-rich review-style content. */
73
+ export const MediumSize: Story = {
74
+ render: () => (
75
+ <DrawerTrigger size="md">
76
+ <div className="flex flex-col gap-4 p-4">
77
+ <h3 className="text-sm font-semibold">Review Content</h3>
78
+ <p className="text-muted-foreground text-sm">
79
+ This drawer uses size=&quot;md&quot; (w-xl / 576px) for content-rich review panels that
80
+ need more horizontal space for diff summaries, tables, and code blocks.
81
+ </p>
82
+ <div className="bg-muted rounded-md p-3">
83
+ <pre className="text-xs">
84
+ {`+ Added authentication middleware\n- Removed legacy session handler\n Updated user model schema`}
85
+ </pre>
86
+ </div>
87
+ </div>
88
+ </DrawerTrigger>
89
+ ),
90
+ };
91
+
92
+ /** Modal drawer with overlay. */
93
+ export const Modal: Story = {
94
+ render: () => (
95
+ <DrawerTrigger modal>
96
+ <div className="p-4">
97
+ <p className="text-muted-foreground text-sm">
98
+ This drawer is modal — an overlay blocks background interaction and focus is trapped.
99
+ </p>
100
+ </div>
101
+ </DrawerTrigger>
102
+ ),
103
+ };
104
+
105
+ /** Drawer with header slot containing DrawerTitle and DrawerDescription. */
106
+ export const WithHeader: Story = {
107
+ render: () => (
108
+ <DrawerTrigger
109
+ header={
110
+ <>
111
+ <DrawerTitle>Feature Details</DrawerTitle>
112
+ <DrawerDescription>FEAT-042 — Authentication Flow</DrawerDescription>
113
+ </>
114
+ }
115
+ >
116
+ <div className="p-4">
117
+ <p className="text-muted-foreground text-sm">Content below the header.</p>
118
+ </div>
119
+ </DrawerTrigger>
120
+ ),
121
+ };
122
+
123
+ /** Drawer with footer slot containing action buttons. */
124
+ export const WithFooter: Story = {
125
+ render: () => (
126
+ <DrawerTrigger
127
+ footer={
128
+ <div className="flex gap-2">
129
+ <Button variant="outline" className="flex-1">
130
+ Cancel
131
+ </Button>
132
+ <Button className="flex-1">Save</Button>
133
+ </div>
134
+ }
135
+ >
136
+ <div className="p-4">
137
+ <p className="text-muted-foreground text-sm">Content above the footer.</p>
138
+ </div>
139
+ </DrawerTrigger>
140
+ ),
141
+ };
142
+
143
+ /** Drawer with both header and footer slots populated. */
144
+ export const WithHeaderAndFooter: Story = {
145
+ render: () => (
146
+ <DrawerTrigger
147
+ header={
148
+ <>
149
+ <DrawerTitle>Create Feature</DrawerTitle>
150
+ <DrawerDescription>Fill in the details below</DrawerDescription>
151
+ </>
152
+ }
153
+ footer={
154
+ <div className="flex gap-2">
155
+ <Button variant="outline" className="flex-1">
156
+ Cancel
157
+ </Button>
158
+ <Button className="flex-1">Create</Button>
159
+ </div>
160
+ }
161
+ >
162
+ <div className="flex flex-col gap-4 p-4">
163
+ <div className="flex flex-col gap-1">
164
+ <label className="text-sm font-medium">Name</label>
165
+ <input
166
+ type="text"
167
+ className="border-input bg-background rounded-md border px-3 py-2 text-sm"
168
+ placeholder="Feature name"
169
+ />
170
+ </div>
171
+ <div className="flex flex-col gap-1">
172
+ <label className="text-sm font-medium">Description</label>
173
+ <textarea
174
+ className="border-input bg-background rounded-md border px-3 py-2 text-sm"
175
+ rows={3}
176
+ placeholder="Optional description"
177
+ />
178
+ </div>
179
+ </div>
180
+ </DrawerTrigger>
181
+ ),
182
+ };
183
+
184
+ /** Drawer with content that exceeds viewport to demonstrate scroll behavior. */
185
+ export const ScrollableContent: Story = {
186
+ render: () => (
187
+ <DrawerTrigger
188
+ header={
189
+ <>
190
+ <DrawerTitle>Scrollable Content</DrawerTitle>
191
+ <DrawerDescription>Content below overflows and scrolls</DrawerDescription>
192
+ </>
193
+ }
194
+ >
195
+ <div className="flex flex-col gap-4 p-4">
196
+ {Array.from({ length: 30 }, (_, i) => (
197
+ <div key={i} className="border-border rounded-md border p-3">
198
+ <h4 className="text-sm font-medium">Item {i + 1}</h4>
199
+ <p className="text-muted-foreground text-xs">
200
+ This is a scrollable content item to demonstrate overflow handling.
201
+ </p>
202
+ </div>
203
+ ))}
204
+ </div>
205
+ </DrawerTrigger>
206
+ ),
207
+ };
@@ -0,0 +1,90 @@
1
+ 'use client';
2
+
3
+ import { XIcon } from 'lucide-react';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+ import { cn } from '@/lib/utils';
6
+ import {
7
+ Drawer,
8
+ DrawerContent,
9
+ DrawerHeader,
10
+ DrawerFooter,
11
+ DrawerOverlay,
12
+ } from '@/components/ui/drawer';
13
+
14
+ const drawerVariants = cva('', {
15
+ variants: {
16
+ size: {
17
+ sm: 'w-96',
18
+ md: 'w-xl',
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ size: 'sm',
23
+ },
24
+ });
25
+
26
+ export interface BaseDrawerProps extends VariantProps<typeof drawerVariants> {
27
+ open: boolean;
28
+ onClose: () => void;
29
+ modal?: boolean;
30
+ header?: React.ReactNode;
31
+ children: React.ReactNode;
32
+ footer?: React.ReactNode;
33
+ className?: string;
34
+ 'data-testid'?: string;
35
+ }
36
+
37
+ export function BaseDrawer({
38
+ open,
39
+ onClose,
40
+ modal = false,
41
+ size,
42
+ header,
43
+ children,
44
+ footer,
45
+ className,
46
+ 'data-testid': testId,
47
+ }: BaseDrawerProps) {
48
+ return (
49
+ <Drawer
50
+ direction="right"
51
+ modal={modal}
52
+ handleOnly
53
+ open={open}
54
+ onOpenChange={(isOpen) => {
55
+ if (!isOpen) onClose();
56
+ }}
57
+ >
58
+ {modal ? <DrawerOverlay /> : null}
59
+ <DrawerContent
60
+ direction="right"
61
+ showCloseButton={false}
62
+ className={cn(drawerVariants({ size }), className)}
63
+ data-testid={testId}
64
+ >
65
+ {/* Close button */}
66
+ <button
67
+ type="button"
68
+ aria-label="Close"
69
+ onClick={onClose}
70
+ className="ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden"
71
+ data-testid={testId ? `${testId}-close-button` : undefined}
72
+ >
73
+ <XIcon className="size-4" />
74
+ <span className="sr-only">Close</span>
75
+ </button>
76
+
77
+ {/* Header slot */}
78
+ {header ? <DrawerHeader>{header}</DrawerHeader> : null}
79
+
80
+ {/* Scrollable content area */}
81
+ <div className="flex-1 overflow-y-auto">
82
+ <div className="flex flex-col">{children}</div>
83
+ </div>
84
+
85
+ {/* Footer slot */}
86
+ {footer ? <DrawerFooter>{footer}</DrawerFooter> : null}
87
+ </DrawerContent>
88
+ </Drawer>
89
+ );
90
+ }
@@ -0,0 +1 @@
1
+ export { BaseDrawer, type BaseDrawerProps } from './base-drawer';
@@ -36,7 +36,7 @@ import { Button } from '@/components/ui/button';
36
36
  * - Submit button is disabled when name is empty
37
37
  * - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
38
38
  * - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
39
- * - Fixed width: 384px (`w-96`)
39
+ * - Fixed width: 448px (`w-xl`) — matches review drawers (PRD, Plan, Merge)
40
40
  * - Attachments use native OS file picker via `pickFiles()` — returns `FileAttachment[]`
41
41
  * with full absolute paths, filenames, and sizes
42
42
  */
@@ -3,7 +3,6 @@
3
3
  import { useState, useCallback, useEffect, useRef } from 'react';
4
4
  import type { LucideIcon } from 'lucide-react';
5
5
  import {
6
- XIcon,
7
6
  PlusIcon,
8
7
  FileIcon,
9
8
  FileTextIcon,
@@ -15,14 +14,8 @@ import {
15
14
  } from 'lucide-react';
16
15
  import { cn } from '@/lib/utils';
17
16
  import { useSoundAction } from '@/hooks/use-sound-action';
18
- import {
19
- Drawer,
20
- DrawerContent,
21
- DrawerHeader,
22
- DrawerTitle,
23
- DrawerDescription,
24
- DrawerFooter,
25
- } from '@/components/ui/drawer';
17
+ import { BaseDrawer } from '@/components/common/base-drawer';
18
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
26
19
  import { Button } from '@/components/ui/button';
27
20
  import { Input } from '@/components/ui/input';
28
21
  import { Textarea } from '@/components/ui/textarea';
@@ -142,16 +135,11 @@ export function FeatureCreateDrawer({
142
135
  setParentId(undefined);
143
136
  }, [defaultGates, defaultPush, defaultOpenPr]);
144
137
 
145
- const handleOpenChange = useCallback(
146
- (nextOpen: boolean) => {
147
- if (!nextOpen) {
148
- drawerCloseSound.play();
149
- resetForm();
150
- onClose();
151
- }
152
- },
153
- [onClose, resetForm, drawerCloseSound]
154
- );
138
+ const handleClose = useCallback(() => {
139
+ drawerCloseSound.play();
140
+ resetForm();
141
+ onClose();
142
+ }, [onClose, resetForm, drawerCloseSound]);
155
143
 
156
144
  const handleSubmit = useCallback(
157
145
  (e: React.FormEvent) => {
@@ -208,23 +196,14 @@ export function FeatureCreateDrawer({
208
196
  const hasFeatures = features && features.length > 0;
209
197
 
210
198
  return (
211
- <Drawer direction="right" modal={false} handleOnly open={open} onOpenChange={handleOpenChange}>
212
- <DrawerContent direction="right" className="w-96" showCloseButton={false}>
213
- {/* Close button */}
214
- <button
215
- type="button"
216
- aria-label="Close"
217
- onClick={() => {
218
- drawerCloseSound.play();
219
- onClose();
220
- }}
221
- className="ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden"
222
- >
223
- <XIcon className="size-4" />
224
- </button>
225
-
226
- {/* Header */}
227
- <DrawerHeader>
199
+ <BaseDrawer
200
+ open={open}
201
+ onClose={handleClose}
202
+ size="md"
203
+ modal={false}
204
+ data-testid="feature-create-drawer"
205
+ header={
206
+ <>
228
207
  <div className="flex items-center gap-2">
229
208
  <div className="h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" />
230
209
  <DrawerTitle>NEW FEATURE</DrawerTitle>
@@ -234,164 +213,156 @@ export function FeatureCreateDrawer({
234
213
  <Badge variant="secondary">Creating...</Badge>
235
214
  </div>
236
215
  </DrawerDescription>
237
- </DrawerHeader>
216
+ </>
217
+ }
218
+ footer={
219
+ <div className="flex flex-row justify-end gap-2">
220
+ <Button variant="outline" onClick={handleClose} disabled={isSubmitting}>
221
+ Cancel
222
+ </Button>
223
+ <Button type="submit" form="create-feature-form" disabled={!name.trim() || isSubmitting}>
224
+ {isSubmitting ? 'Creating...' : '+ Create Feature'}
225
+ </Button>
226
+ </div>
227
+ }
228
+ >
229
+ <Separator />
230
+
231
+ {/* Form body */}
232
+ <div className="p-4">
233
+ <form id="create-feature-form" onSubmit={handleSubmit} className="flex flex-col gap-4">
234
+ {/* Feature name */}
235
+ <div className="flex flex-col gap-1.5">
236
+ <Label
237
+ htmlFor="feature-name"
238
+ className="text-muted-foreground text-xs font-semibold tracking-wider"
239
+ >
240
+ FEATURE NAME
241
+ </Label>
242
+ <Input
243
+ id="feature-name"
244
+ placeholder="e.g. GitHub OAuth Login"
245
+ value={name}
246
+ onChange={(e) => setName(e.target.value)}
247
+ required
248
+ disabled={isSubmitting}
249
+ />
250
+ </div>
238
251
 
239
- <Separator />
252
+ {/* Description */}
253
+ <div className="flex flex-col gap-1.5">
254
+ <Label
255
+ htmlFor="feature-description"
256
+ className="text-muted-foreground text-xs font-semibold tracking-wider"
257
+ >
258
+ DESCRIPTION
259
+ </Label>
260
+ <Textarea
261
+ id="feature-description"
262
+ placeholder="Describe what this feature does..."
263
+ value={description}
264
+ onChange={(e) => setDescription(e.target.value)}
265
+ rows={4}
266
+ disabled={isSubmitting}
267
+ />
268
+ </div>
240
269
 
241
- {/* Form body */}
242
- <div className="flex-1 overflow-y-auto p-4">
243
- <form id="create-feature-form" onSubmit={handleSubmit} className="flex flex-col gap-4">
244
- {/* Feature name */}
270
+ {/* Parent feature selector (only when features are available) */}
271
+ {hasFeatures ? (
245
272
  <div className="flex flex-col gap-1.5">
246
273
  <Label
247
- htmlFor="feature-name"
274
+ htmlFor="parent-feature"
248
275
  className="text-muted-foreground text-xs font-semibold tracking-wider"
249
276
  >
250
- FEATURE NAME
277
+ PARENT FEATURE
251
278
  </Label>
252
- <Input
253
- id="feature-name"
254
- placeholder="e.g. GitHub OAuth Login"
255
- value={name}
256
- onChange={(e) => setName(e.target.value)}
257
- required
279
+ <ParentFeatureCombobox
280
+ features={features}
281
+ value={parentId}
282
+ onChange={setParentId}
258
283
  disabled={isSubmitting}
259
284
  />
260
285
  </div>
286
+ ) : null}
287
+
288
+ {/* Auto-approve checkboxes */}
289
+ <div className="flex flex-col gap-1.5">
290
+ <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
291
+ APPROVE
292
+ </Label>
293
+ <CheckboxGroup
294
+ label="Autonomous Mode"
295
+ description="YOLO!"
296
+ parentAriaLabel="Auto approve all"
297
+ options={AUTO_APPROVE_OPTIONS}
298
+ value={approvalGates}
299
+ onValueChange={setApprovalGates}
300
+ disabled={isSubmitting}
301
+ />
302
+ </div>
261
303
 
262
- {/* Description */}
263
- <div className="flex flex-col gap-1.5">
264
- <Label
265
- htmlFor="feature-description"
266
- className="text-muted-foreground text-xs font-semibold tracking-wider"
267
- >
268
- DESCRIPTION
269
- </Label>
270
- <Textarea
271
- id="feature-description"
272
- placeholder="Describe what this feature does..."
273
- value={description}
274
- onChange={(e) => setDescription(e.target.value)}
275
- rows={4}
304
+ {/* Git options */}
305
+ <div className="flex flex-col gap-1.5">
306
+ <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
307
+ GIT
308
+ </Label>
309
+ <div className="flex flex-col gap-2">
310
+ <CheckboxGroupItem
311
+ id="push"
312
+ label="Push"
313
+ description="Push branch to remote after implementation."
314
+ checked={push || openPr}
315
+ onCheckedChange={setPush}
316
+ disabled={openPr || isSubmitting}
317
+ />
318
+ <CheckboxGroupItem
319
+ id="open-pr"
320
+ label="Create PR"
321
+ description="Open a pull request after pushing."
322
+ checked={openPr}
323
+ onCheckedChange={setOpenPr}
276
324
  disabled={isSubmitting}
277
325
  />
278
326
  </div>
327
+ </div>
279
328
 
280
- {/* Parent feature selector (only when features are available) */}
281
- {hasFeatures ? (
282
- <div className="flex flex-col gap-1.5">
283
- <Label
284
- htmlFor="parent-feature"
285
- className="text-muted-foreground text-xs font-semibold tracking-wider"
286
- >
287
- PARENT FEATURE
288
- </Label>
289
- <ParentFeatureCombobox
290
- features={features}
291
- value={parentId}
292
- onChange={setParentId}
293
- disabled={isSubmitting}
294
- />
295
- </div>
296
- ) : null}
297
-
298
- {/* Auto-approve checkboxes */}
299
- <div className="flex flex-col gap-1.5">
329
+ {/* Attachments */}
330
+ <div className="flex flex-col gap-2">
331
+ <div className="flex items-center justify-between">
300
332
  <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
301
- APPROVE
333
+ ATTACHMENTS
334
+ {attachments.length > 0 && (
335
+ <span className="text-muted-foreground/60 ml-1.5">({attachments.length})</span>
336
+ )}
302
337
  </Label>
303
- <CheckboxGroup
304
- label="Autonomous Mode"
305
- description="YOLO!"
306
- parentAriaLabel="Auto approve all"
307
- options={AUTO_APPROVE_OPTIONS}
308
- value={approvalGates}
309
- onValueChange={setApprovalGates}
338
+ <Button
339
+ type="button"
340
+ variant="outline"
341
+ size="xs"
342
+ onClick={handleAddFiles}
310
343
  disabled={isSubmitting}
311
- />
344
+ >
345
+ <PlusIcon className="size-3" />
346
+ Add Files
347
+ </Button>
312
348
  </div>
313
349
 
314
- {/* Git options */}
315
- <div className="flex flex-col gap-1.5">
316
- <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
317
- GIT
318
- </Label>
350
+ {attachments.length > 0 && (
319
351
  <div className="flex flex-col gap-2">
320
- <CheckboxGroupItem
321
- id="push"
322
- label="Push"
323
- description="Push branch to remote after implementation."
324
- checked={push || openPr}
325
- onCheckedChange={setPush}
326
- disabled={openPr || isSubmitting}
327
- />
328
- <CheckboxGroupItem
329
- id="open-pr"
330
- label="Create PR"
331
- description="Open a pull request after pushing."
332
- checked={openPr}
333
- onCheckedChange={setOpenPr}
334
- disabled={isSubmitting}
335
- />
352
+ {attachments.map((file) => (
353
+ <AttachmentCard
354
+ key={file.path}
355
+ file={file}
356
+ onRemove={() => handleRemoveFile(file.path)}
357
+ disabled={isSubmitting}
358
+ />
359
+ ))}
336
360
  </div>
337
- </div>
338
-
339
- {/* Attachments */}
340
- <div className="flex flex-col gap-2">
341
- <div className="flex items-center justify-between">
342
- <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
343
- ATTACHMENTS
344
- {attachments.length > 0 && (
345
- <span className="text-muted-foreground/60 ml-1.5">({attachments.length})</span>
346
- )}
347
- </Label>
348
- <Button
349
- type="button"
350
- variant="outline"
351
- size="xs"
352
- onClick={handleAddFiles}
353
- disabled={isSubmitting}
354
- >
355
- <PlusIcon className="size-3" />
356
- Add Files
357
- </Button>
358
- </div>
359
-
360
- {attachments.length > 0 && (
361
- <div className="flex flex-col gap-2">
362
- {attachments.map((file) => (
363
- <AttachmentCard
364
- key={file.path}
365
- file={file}
366
- onRemove={() => handleRemoveFile(file.path)}
367
- disabled={isSubmitting}
368
- />
369
- ))}
370
- </div>
371
- )}
372
- </div>
373
- </form>
374
- </div>
375
-
376
- {/* Footer */}
377
- <Separator />
378
- <DrawerFooter className="flex-row justify-end gap-2">
379
- <Button
380
- variant="outline"
381
- onClick={() => {
382
- drawerCloseSound.play();
383
- onClose();
384
- }}
385
- disabled={isSubmitting}
386
- >
387
- Cancel
388
- </Button>
389
- <Button type="submit" form="create-feature-form" disabled={!name.trim() || isSubmitting}>
390
- {isSubmitting ? 'Creating...' : '+ Create Feature'}
391
- </Button>
392
- </DrawerFooter>
393
- </DrawerContent>
394
- </Drawer>
361
+ )}
362
+ </div>
363
+ </form>
364
+ </div>
365
+ </BaseDrawer>
395
366
  );
396
367
  }
397
368