@shepai/cli 1.90.1 → 1.91.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 (152) hide show
  1. package/dist/packages/core/src/application/ports/output/services/worktree-service.interface.d.ts +6 -0
  2. package/dist/packages/core/src/application/ports/output/services/worktree-service.interface.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/use-cases/features/cleanup-feature-worktree.use-case.d.ts.map +1 -1
  4. package/dist/packages/core/src/application/use-cases/features/cleanup-feature-worktree.use-case.js +8 -0
  5. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts +1 -0
  6. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
  7. package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +8 -0
  8. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts +1 -0
  9. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts.map +1 -1
  10. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.js +3 -0
  11. package/dist/src/presentation/web/app/api/attachments/preview/route.d.ts.map +1 -1
  12. package/dist/src/presentation/web/app/api/attachments/preview/route.js +47 -11
  13. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
  14. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +9 -6
  15. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
  16. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +2 -1
  17. package/dist/src/presentation/web/components/common/inline-attachments/index.d.ts +3 -0
  18. package/dist/src/presentation/web/components/common/inline-attachments/index.d.ts.map +1 -0
  19. package/dist/src/presentation/web/components/common/inline-attachments/index.js +1 -0
  20. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.d.ts +20 -0
  21. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.d.ts.map +1 -0
  22. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.js +72 -0
  23. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.stories.d.ts +16 -0
  24. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.stories.d.ts.map +1 -0
  25. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.stories.js +46 -0
  26. package/dist/tsconfig.build.tsbuildinfo +1 -1
  27. package/package.json +1 -1
  28. package/web/.next/BUILD_ID +1 -1
  29. package/web/.next/build-manifest.json +2 -2
  30. package/web/.next/fallback-build-manifest.json +2 -2
  31. package/web/.next/prerender-manifest.json +3 -3
  32. package/web/.next/required-server-files.js +1 -1
  33. package/web/.next/required-server-files.json +1 -1
  34. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +18 -18
  35. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  36. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  37. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +21 -21
  38. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +14 -14
  41. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +18 -18
  44. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +21 -21
  47. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +14 -14
  50. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +14 -14
  53. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  54. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/_global-error.html +2 -2
  56. package/web/.next/server/app/_global-error.rsc +1 -1
  57. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  58. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  59. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  60. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  61. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  62. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  63. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  65. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  66. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  67. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  69. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  70. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  71. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  72. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  73. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  75. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  76. package/web/.next/server/chunks/[root-of-the-server]__0b10355c._.js +1 -1
  77. package/web/.next/server/chunks/[root-of-the-server]__0b10355c._.js.map +1 -1
  78. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  79. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  80. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_9027d30a._.js +1 -1
  81. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_9027d30a._.js.map +1 -1
  82. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +1 -1
  83. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js.map +1 -1
  84. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +1 -1
  85. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js.map +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__17143953._.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__17143953._.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__4be9a9bb._.js +1 -1
  92. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__9de266fa._.js +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__9de266fa._.js.map +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  97. package/web/.next/server/chunks/ssr/[root-of-the-server]__b3a4cef4._.js +1 -1
  98. package/web/.next/server/chunks/ssr/[root-of-the-server]__b3a4cef4._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/[root-of-the-server]__c2173b6d._.js +1 -1
  100. package/web/.next/server/chunks/ssr/[root-of-the-server]__c2173b6d._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/[root-of-the-server]__f0d8d054._.js +2 -2
  102. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  104. package/web/.next/server/chunks/ssr/{_2fa756cd._.js → _05b3aacb._.js} +2 -2
  105. package/web/.next/server/chunks/ssr/{_2fa756cd._.js.map → _05b3aacb._.js.map} +1 -1
  106. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  107. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/_752e01fd._.js +1 -1
  109. package/web/.next/server/chunks/ssr/_752e01fd._.js.map +1 -1
  110. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  111. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  113. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  115. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  116. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +1 -1
  117. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js.map +1 -1
  118. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  119. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  120. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  121. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  122. package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js +2 -2
  123. package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  125. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  126. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  127. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  129. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  130. package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js +2 -2
  131. package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js.map +1 -1
  132. package/web/.next/server/pages/500.html +2 -2
  133. package/web/.next/server/server-reference-manifest.js +1 -1
  134. package/web/.next/server/server-reference-manifest.json +28 -28
  135. package/web/.next/static/chunks/{4906aba274bc6920.js → 0414e6b21f189456.js} +1 -1
  136. package/web/.next/static/chunks/205b12234277ba2f.js +1 -0
  137. package/web/.next/static/chunks/{bb3e32c60cb2e794.js → 24744618941b8239.js} +1 -1
  138. package/web/.next/static/chunks/2a09571d38368a18.css +2 -0
  139. package/web/.next/static/chunks/{54de5c2f98832b92.js → 2ea28eba896e489b.js} +1 -1
  140. package/web/.next/static/chunks/55e7ff9b53257d70.js +1 -0
  141. package/web/.next/static/chunks/{132ff681f7e12121.js → 7fb81216c7dd303e.js} +1 -1
  142. package/web/.next/static/chunks/{7c7da9e978216d2b.js → c0233da545286fe1.js} +1 -1
  143. package/web/.next/static/chunks/{4354b4b30513e7f0.js → d5c051b708c9eafd.js} +2 -2
  144. package/web/.next/static/chunks/{0cbb35f940c9feef.js → dcc6eeb6b6e4c578.js} +1 -1
  145. package/web/.next/static/chunks/{38bb02c07a427f1e.js → fb39605881b728b1.js} +1 -1
  146. package/web/.next/static/chunks/{c0f23bc23fd8b8ad.js → fbdf11fbbe42fcff.js} +1 -1
  147. package/web/.next/static/chunks/5c55da0e28e6cdc8.js +0 -1
  148. package/web/.next/static/chunks/cb5c85c5e16e82d3.js +0 -1
  149. package/web/.next/static/chunks/f98eb6cb17b61cb2.css +0 -2
  150. /package/web/.next/static/{o8F4OKh6WJmRpbTqaCZ67 → yZWy0Cxn_PmaNJkxer58a}/_buildManifest.js +0 -0
  151. /package/web/.next/static/{o8F4OKh6WJmRpbTqaCZ67 → yZWy0Cxn_PmaNJkxer58a}/_clientMiddlewareManifest.json +0 -0
  152. /package/web/.next/static/{o8F4OKh6WJmRpbTqaCZ67 → yZWy0Cxn_PmaNJkxer58a}/_ssgManifest.js +0 -0
@@ -97,6 +97,12 @@ export interface IWorktreeService {
97
97
  * @returns Computed worktree path (e.g., /repo/.worktrees/branch-name)
98
98
  */
99
99
  getWorktreePath(repoPath: string, branch: string): string;
100
+ /**
101
+ * Prune stale worktree entries whose directories no longer exist.
102
+ *
103
+ * @param repoPath - Path to the git repository
104
+ */
105
+ prune(repoPath: string): Promise<void>;
100
106
  /**
101
107
  * Ensure a directory is a valid git repository with at least one commit.
102
108
  * If the directory is not a git repo, initializes it with `git init`
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-service.interface.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/ports/output/services/worktree-service.interface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,oBAAY,iBAAiB;IAC3B,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,SAAS,cAAc;IACvB,cAAc,mBAAmB;IACjC,SAAS,cAAc;CACxB;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAAE,iBAAiB;aACvB,KAAK,CAAC,EAAE,KAAK;gBAF7B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,iBAAiB,EACvB,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;OASG;IACH,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB;;;;;;OAMG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;;;;OAKG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE3D;;;;;;OAMG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjE;;;;;;OAMG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvE;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1D;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD"}
1
+ {"version":3,"file":"worktree-service.interface.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/ports/output/services/worktree-service.interface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,oBAAY,iBAAiB;IAC3B,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,SAAS,cAAc;IACvB,cAAc,mBAAmB;IACjC,SAAS,cAAc;CACxB;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAAE,iBAAiB;aACvB,KAAK,CAAC,EAAE,KAAK;gBAF7B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,iBAAiB,EACvB,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;OASG;IACH,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB;;;;;;OAMG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;;;;OAKG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE3D;;;;;;OAMG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjE;;;;;;OAMG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvE;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1D;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup-feature-worktree.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/features/cleanup-feature-worktree.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yDAAyD,CAAC;AAE7F,qBACa,6BAA6B;IAER,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAFP,WAAW,EAAE,kBAAkB,EACjC,eAAe,EAAE,gBAAgB,EACpC,YAAY,EAAE,aAAa;IAGjE,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAqChD"}
1
+ {"version":3,"file":"cleanup-feature-worktree.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/features/cleanup-feature-worktree.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yDAAyD,CAAC;AAE7F,qBACa,6BAA6B;IAER,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAFP,WAAW,EAAE,kBAAkB,EACjC,eAAe,EAAE,gBAAgB,EACpC,YAAY,EAAE,aAAa;IAGjE,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4ChD"}
@@ -44,6 +44,14 @@ let CleanupFeatureWorktreeUseCase = class CleanupFeatureWorktreeUseCase {
44
44
  catch (err) {
45
45
  // eslint-disable-next-line no-console
46
46
  console.warn('[CleanupFeatureWorktreeUseCase] worktree remove failed:', err);
47
+ // Prune stale worktree entries so the branch is no longer considered "in use"
48
+ try {
49
+ await this.worktreeService.prune(feature.repositoryPath);
50
+ }
51
+ catch (pruneErr) {
52
+ // eslint-disable-next-line no-console
53
+ console.warn('[CleanupFeatureWorktreeUseCase] worktree prune failed:', pruneErr);
54
+ }
47
55
  }
48
56
  // Step 2: Delete the local feature branch
49
57
  try {
@@ -19,6 +19,7 @@ export declare class WorktreeService implements IWorktreeService {
19
19
  constructor(execFile: ExecFunction);
20
20
  create(repoPath: string, branch: string, worktreePath: string, startPoint?: string): Promise<WorktreeInfo>;
21
21
  remove(worktreePath: string, force?: boolean): Promise<void>;
22
+ prune(repoPath: string): Promise<void>;
22
23
  list(repoPath: string): Promise<WorktreeInfo[]>;
23
24
  exists(repoPath: string, branch: string): Promise<boolean>;
24
25
  branchExists(repoPath: string, branch: string): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"worktree.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/worktree.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,0EAA0E,CAAC;AAOlF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,qBACa,eAAgB,YAAW,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC;IAqBlB,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1D,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtE,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC1D,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzD,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;CAU9B"}
1
+ {"version":3,"file":"worktree.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/worktree.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,0EAA0E,CAAC;AAOlF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,qBACa,eAAgB,YAAW,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC;IAqBlB,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1D,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtE,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC1D,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzD,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;CAU9B"}
@@ -60,6 +60,14 @@ let WorktreeService = class WorktreeService {
60
60
  throw this.parseGitError(error);
61
61
  }
62
62
  }
63
+ async prune(repoPath) {
64
+ try {
65
+ await this.execFile('git', ['worktree', 'prune'], { cwd: repoPath });
66
+ }
67
+ catch (error) {
68
+ throw this.parseGitError(error);
69
+ }
70
+ }
63
71
  async list(repoPath) {
64
72
  const { stdout } = await this.execFile('git', ['worktree', 'list', '--porcelain'], {
65
73
  cwd: repoPath,
@@ -11,6 +11,7 @@ export interface RejectionFeedbackData {
11
11
  iteration: number;
12
12
  message: string;
13
13
  phase?: string;
14
+ attachments?: string[];
14
15
  }
15
16
  type GetPhaseTimingsResult = {
16
17
  timings: PhaseTimingData[];
@@ -1 +1 @@
1
- {"version":3,"file":"get-feature-phase-timings.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-phase-timings.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,qBAAqB,GACtB;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC1E;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2B9F"}
1
+ {"version":3,"file":"get-feature-phase-timings.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-phase-timings.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,KAAK,qBAAqB,GACtB;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC1E;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2B9F"}
@@ -42,6 +42,9 @@ async function readRejectionFeedback(featureId) {
42
42
  iteration: Number(entry.iteration ?? 1),
43
43
  message: String(entry.message ?? ''),
44
44
  phase: entry.phase ? String(entry.phase) : undefined,
45
+ attachments: Array.isArray(entry.attachments)
46
+ ? entry.attachments.map(String)
47
+ : undefined,
45
48
  }));
46
49
  }
47
50
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/attachments/preview/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsB3C,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA6BrE"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/attachments/preview/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8D3C,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA0BrE"}
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from 'next/server';
2
- import { readFile, stat } from 'fs/promises';
3
- import { extname, resolve } from 'path';
2
+ import { readFile, stat, readdir } from 'fs/promises';
3
+ import { basename, extname, join, resolve } from 'path';
4
4
  import { getShepHomeDir } from '../../../../../../../packages/core/src/infrastructure/services/filesystem/shep-directory.service.js';
5
5
  const MIME_MAP = {
6
6
  '.png': 'image/png',
@@ -18,6 +18,44 @@ const MIME_MAP = {
18
18
  '.yaml': 'text/yaml',
19
19
  '.yml': 'text/yaml',
20
20
  };
21
+ /**
22
+ * When the exact path doesn't exist (e.g. pending-<id> was renamed to <slug>),
23
+ * search all sibling attachment directories for the same filename.
24
+ */
25
+ async function findAttachmentFallback(requestedPath, attachmentsRoot) {
26
+ const filename = basename(requestedPath);
27
+ try {
28
+ const dirs = await readdir(attachmentsRoot, { withFileTypes: true });
29
+ for (const entry of dirs) {
30
+ if (!entry.isDirectory())
31
+ continue;
32
+ const candidate = join(attachmentsRoot, entry.name, filename);
33
+ try {
34
+ await stat(candidate);
35
+ return candidate;
36
+ }
37
+ catch {
38
+ // not in this directory
39
+ }
40
+ }
41
+ }
42
+ catch {
43
+ // attachments root doesn't exist
44
+ }
45
+ return null;
46
+ }
47
+ function serveFile(filePath, mimeTypeHint) {
48
+ return readFile(filePath).then((buffer) => {
49
+ const ext = extname(filePath).toLowerCase();
50
+ const contentType = MIME_MAP[ext] ?? mimeTypeHint ?? 'application/octet-stream';
51
+ return new NextResponse(buffer, {
52
+ headers: {
53
+ 'Content-Type': contentType,
54
+ 'Cache-Control': 'private, max-age=3600',
55
+ },
56
+ });
57
+ });
58
+ }
21
59
  export async function GET(request) {
22
60
  const path = request.nextUrl.searchParams.get('path');
23
61
  const mimeTypeHint = request.nextUrl.searchParams.get('mimeType');
@@ -31,17 +69,15 @@ export async function GET(request) {
31
69
  }
32
70
  try {
33
71
  await stat(path);
34
- const buffer = await readFile(path);
35
- const ext = extname(path).toLowerCase();
36
- const contentType = MIME_MAP[ext] ?? mimeTypeHint ?? 'application/octet-stream';
37
- return new NextResponse(buffer, {
38
- headers: {
39
- 'Content-Type': contentType,
40
- 'Cache-Control': 'private, max-age=3600',
41
- },
42
- });
72
+ return await serveFile(path, mimeTypeHint);
43
73
  }
44
74
  catch {
75
+ // File not found at exact path — try fallback search
76
+ // This handles the case where pending-<sessionId>/ was renamed to <featureSlug>/
77
+ const fallbackPath = await findAttachmentFallback(path, attachmentsRoot);
78
+ if (fallbackPath) {
79
+ return await serveFile(fallbackPath, mimeTypeHint);
80
+ }
45
81
  return NextResponse.json({ error: 'File not found' }, { status: 404 });
46
82
  }
47
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AAsED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CA8D3F"}
1
+ {"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAGjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AA4ED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CA8D3F"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Loader2, AlertCircle, Clock } from 'lucide-react';
4
+ import { InlineAttachments } from '../../common/inline-attachments/index.js';
4
5
  const NODE_TO_PHASE = {
5
6
  analyze: 'Analyzing',
6
7
  requirements: 'Requirements',
@@ -38,7 +39,7 @@ function groupTimingsByRun(timings) {
38
39
  return groups;
39
40
  }
40
41
  /**
41
- * Map each run:rejected timing entry to its rejection feedback message.
42
+ * Map each run:rejected timing entry to its rejection feedback message and attachments.
42
43
  * Matches by sequential order: 1st rejected event → 1st feedback entry, etc.
43
44
  */
44
45
  function buildRejectionMessageMap(timings, feedback) {
@@ -49,7 +50,8 @@ function buildRejectionMessageMap(timings, feedback) {
49
50
  for (const t of timings) {
50
51
  if (t.phase === 'run:rejected' && rejectionIdx < feedback.length) {
51
52
  const key = `${t.agentRunId}-${t.phase}-${t.startedAt}`;
52
- map.set(key, feedback[rejectionIdx].message);
53
+ const entry = feedback[rejectionIdx];
54
+ map.set(key, { message: entry.message, attachments: entry.attachments });
53
55
  rejectionIdx++;
54
56
  }
55
57
  }
@@ -79,17 +81,18 @@ function RunGroup({ group, groupIdx, multiRun, maxDurationMs, rejectionMessages,
79
81
  return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [multiRun ? (_jsxs("div", { className: "text-muted-foreground text-xs font-medium", children: ["Run #", groupIdx + 1] })) : null, group.timings.map((t) => {
80
82
  if (isLifecycleEvent(t.phase)) {
81
83
  const key = `${t.agentRunId}-${t.phase}-${t.startedAt}`;
82
- const message = t.phase === 'run:rejected' ? rejectionMessages.get(key) : undefined;
83
- return _jsx(LifecycleEventRow, { timing: t, message: message }, key);
84
+ const rejection = t.phase === 'run:rejected' ? rejectionMessages.get(key) : undefined;
85
+ return (_jsx(LifecycleEventRow, { timing: t, message: rejection?.message, attachments: rejection?.attachments }, key));
84
86
  }
85
87
  return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
86
88
  })] }));
87
89
  }
88
- function LifecycleEventRow({ timing, message }) {
90
+ function LifecycleEventRow({ timing, message, attachments, }) {
89
91
  const event = LIFECYCLE_EVENTS[timing.phase];
90
92
  if (!event)
91
93
  return null;
92
- return (_jsxs("div", { className: `text-xs ${event.colorClass}`, children: [_jsx("span", { children: event.label }), message ? (_jsxs("span", { "data-testid": "rejection-feedback-text", className: "text-muted-foreground ml-2 font-normal italic", children: ["\u2014 ", message] })) : null] }));
94
+ const hasAttachments = attachments && attachments.length > 0;
95
+ return (_jsxs("div", { className: `text-xs ${event.colorClass}`, children: [_jsx("span", { children: event.label }), message ? (_jsxs("span", { "data-testid": "rejection-feedback-text", className: "text-muted-foreground ml-2 font-normal italic", children: ["\u2014 ", message] })) : null, hasAttachments ? (_jsx("div", { "data-testid": "rejection-feedback-attachments", className: "mt-1.5 ml-2", children: _jsx(InlineAttachments, { text: "", attachmentPaths: attachments }) })) : null] }));
93
96
  }
94
97
  function NodeTimingRow({ timing, maxDurationMs, }) {
95
98
  const label = timing.phase.includes(':')
@@ -1 +1 @@
1
- {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
1
+ {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { ExternalLink, GitBranch, GitCommitHorizontal, Zap } from 'lucide-react';
4
+ import { InlineAttachments } from '../../common/inline-attachments/index.js';
4
5
  import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
5
6
  import { cn } from '../../../lib/utils.js';
6
7
  import { Badge } from '../../ui/badge.js';
@@ -50,7 +51,7 @@ function FeatureInfo({ data }) {
50
51
  Boolean(data.createdAt);
51
52
  if (!hasInfo)
52
53
  return null;
53
- return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-info", className: "flex flex-col gap-3 p-4", children: [data.branch ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Branch" }), _jsxs("span", { className: "flex items-center gap-1.5 text-sm", children: [_jsx(GitBranch, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx("code", { className: "bg-muted rounded px-1 py-0.5 font-mono text-xs", children: data.branch }), data.baseBranch ? (_jsxs("span", { className: "text-muted-foreground text-xs", children: ["from ", data.baseBranch] })) : null] })] })) : null, data.oneLiner ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "One-Liner" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.oneLiner })] })) : null, data.userQuery ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "User Query" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.userQuery })] })) : null, data.summary ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Summary" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.summary })] })) : null, data.createdAt ? (_jsx(DetailRow, { label: "Created", value: formatRelativeTime(data.createdAt) })) : null] })] }));
54
+ return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-info", className: "flex flex-col gap-3 p-4", children: [data.branch ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Branch" }), _jsxs("span", { className: "flex items-center gap-1.5 text-sm", children: [_jsx(GitBranch, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx("code", { className: "bg-muted rounded px-1 py-0.5 font-mono text-xs", children: data.branch }), data.baseBranch ? (_jsxs("span", { className: "text-muted-foreground text-xs", children: ["from ", data.baseBranch] })) : null] })] })) : null, data.oneLiner ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "One-Liner" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.oneLiner })] })) : null, data.userQuery ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "User Query" }), _jsx(InlineAttachments, { text: data.userQuery })] })) : null, data.summary ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Summary" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.summary })] })) : null, data.createdAt ? (_jsx(DetailRow, { label: "Created", value: formatRelativeTime(data.createdAt) })) : null] })] }));
54
55
  }
55
56
  // ── PR Info section ──────────────────────────────────────────────────
56
57
  const prStatusStyles = {
@@ -0,0 +1,3 @@
1
+ export { InlineAttachments, parseAttachmentRefs } from './inline-attachments.js';
2
+ export type { InlineAttachmentsProps } from './inline-attachments.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/inline-attachments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1 @@
1
+ export { InlineAttachments, parseAttachmentRefs } from './inline-attachments.js';
@@ -0,0 +1,20 @@
1
+ export interface InlineAttachmentsProps {
2
+ /** Text that may contain `@/path/to/file` attachment references. */
3
+ text: string;
4
+ /** Additional attachment paths to render (e.g. from rejection feedback). */
5
+ attachmentPaths?: string[];
6
+ }
7
+ interface TextSegment {
8
+ type: 'text';
9
+ value: string;
10
+ }
11
+ interface AttachmentSegment {
12
+ type: 'attachment';
13
+ path: string;
14
+ }
15
+ type Segment = TextSegment | AttachmentSegment;
16
+ /** Parse text into segments of plain text and attachment references. */
17
+ export declare function parseAttachmentRefs(text: string): Segment[];
18
+ export declare function InlineAttachments({ text, attachmentPaths }: InlineAttachmentsProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
20
+ //# sourceMappingURL=inline-attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-attachments.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/inline-attachments/inline-attachments.tsx"],"names":[],"mappings":"AA6BA,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,KAAK,OAAO,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAE/C,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAwB3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,sBAAsB,2CA4BlF"}
@@ -0,0 +1,72 @@
1
+ /* eslint-disable @next/next/no-img-element -- Local file preview requires raw <img>, not next/image */
2
+ 'use client';
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ import { useState } from 'react';
5
+ import { Dialog, DialogTrigger, DialogContent, DialogTitle } from '../../ui/dialog.js';
6
+ import { VisuallyHidden } from 'radix-ui';
7
+ import { DownloadIcon, ImageOff } from 'lucide-react';
8
+ const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp']);
9
+ function isImagePath(path) {
10
+ const dot = path.lastIndexOf('.');
11
+ return dot >= 0 && IMAGE_EXTS.has(path.slice(dot).toLowerCase());
12
+ }
13
+ function previewUrl(path) {
14
+ const ext = path.lastIndexOf('.') >= 0 ? path.slice(path.lastIndexOf('.')) : '';
15
+ const mimeGuess = isImagePath(path) ? `image/${ext.slice(1).replace('jpg', 'jpeg')}` : '';
16
+ const params = new URLSearchParams({ path, ...(mimeGuess && { mimeType: mimeGuess }) });
17
+ return `/api/attachments/preview?${params.toString()}`;
18
+ }
19
+ function getFilename(path) {
20
+ return path.split('/').pop() ?? path;
21
+ }
22
+ /** Regex matching `@/absolute/path` attachment references in text. */
23
+ const ATTACHMENT_REF_RE = /(?:^|\s)@(\/[^\s]+)/g;
24
+ /** Parse text into segments of plain text and attachment references. */
25
+ export function parseAttachmentRefs(text) {
26
+ const segments = [];
27
+ let lastIndex = 0;
28
+ for (const match of text.matchAll(ATTACHMENT_REF_RE)) {
29
+ const fullMatchStart = match.index;
30
+ // The path capture starts after the optional leading whitespace
31
+ const leadingWs = match[0].length - match[0].trimStart().length;
32
+ const refStart = fullMatchStart + leadingWs;
33
+ // Add text before this reference (including leading whitespace)
34
+ if (refStart > lastIndex) {
35
+ segments.push({ type: 'text', value: text.slice(lastIndex, refStart) });
36
+ }
37
+ segments.push({ type: 'attachment', path: match[1] });
38
+ lastIndex = fullMatchStart + match[0].length;
39
+ }
40
+ if (lastIndex < text.length) {
41
+ segments.push({ type: 'text', value: text.slice(lastIndex) });
42
+ }
43
+ return segments;
44
+ }
45
+ export function InlineAttachments({ text, attachmentPaths }) {
46
+ const segments = parseAttachmentRefs(text);
47
+ const hasInlineAttachments = segments.some((s) => s.type === 'attachment');
48
+ const extraPaths = attachmentPaths?.filter(Boolean) ?? [];
49
+ if (!hasInlineAttachments && extraPaths.length === 0) {
50
+ return _jsx("span", { className: "text-sm leading-relaxed", children: text });
51
+ }
52
+ return (_jsxs("div", { className: "flex flex-col gap-2", children: [segments.map((segment) => {
53
+ if (segment.type === 'text') {
54
+ const trimmed = segment.value.trim();
55
+ if (!trimmed)
56
+ return null;
57
+ return (_jsx("span", { className: "text-sm leading-relaxed", children: trimmed }, `text-${trimmed.slice(0, 40)}`));
58
+ }
59
+ return _jsx(AttachmentPreview, { path: segment.path }, `att-${segment.path}`);
60
+ }), extraPaths.map((path) => (_jsx(AttachmentPreview, { path: path }, `extra-${path}`)))] }));
61
+ }
62
+ function AttachmentPreview({ path }) {
63
+ const filename = getFilename(path);
64
+ const [loadError, setLoadError] = useState(false);
65
+ if (isImagePath(path)) {
66
+ if (loadError) {
67
+ return (_jsxs("div", { "data-testid": "inline-attachment-image-error", className: "text-muted-foreground flex items-center gap-2 rounded-md border px-3 py-2 text-sm", children: [_jsx(ImageOff, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "truncate", children: filename })] }));
68
+ }
69
+ return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx("img", { src: previewUrl(path), alt: filename, "data-testid": "inline-attachment-image", onError: () => setLoadError(true), className: "max-h-48 max-w-full cursor-pointer rounded-md border object-contain transition-opacity hover:opacity-80" }) }), _jsxs(DialogContent, { className: "max-w-3xl gap-0 overflow-hidden border-0 p-0 [&>button:last-child]:!cursor-pointer [&>button:last-child]:!rounded-full [&>button:last-child]:!bg-black/70 [&>button:last-child]:!p-1.5 [&>button:last-child]:!text-white [&>button:last-child]:!opacity-100 [&>button:last-child]:!shadow-lg [&>button:last-child]:!backdrop-blur-md [&>button:last-child]:hover:!bg-black/90", children: [_jsx(VisuallyHidden.Root, { children: _jsxs(DialogTitle, { children: ["Preview: ", filename] }) }), _jsx("div", { className: "relative bg-black/90", children: _jsx("img", { src: previewUrl(path), alt: filename, className: "h-auto max-h-[70vh] w-full object-contain" }) }), _jsxs("div", { className: "bg-background flex items-center gap-3 px-4 py-3", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: _jsx("span", { className: "truncate text-sm font-medium", children: filename }) }), _jsx("a", { href: previewUrl(path), download: filename, className: "text-muted-foreground hover:text-foreground shrink-0 cursor-pointer rounded p-1.5 transition-colors", "aria-label": `Download ${filename}`, children: _jsx(DownloadIcon, { className: "h-4 w-4" }) })] })] })] }));
70
+ }
71
+ return (_jsx("a", { href: previewUrl(path), download: filename, "data-testid": "inline-attachment-file", className: "text-primary text-sm underline underline-offset-2", children: filename }));
72
+ }
@@ -0,0 +1,16 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { InlineAttachments } from './inline-attachments.js';
3
+ declare const meta: Meta<typeof InlineAttachments>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof InlineAttachments>;
6
+ /** Plain text with no attachment references. */
7
+ export declare const PlainText: Story;
8
+ /** Text with an image attachment reference. */
9
+ export declare const WithImageAttachment: Story;
10
+ /** Text with multiple attachment references. */
11
+ export declare const MultipleAttachments: Story;
12
+ /** Text with a non-image file attachment. */
13
+ export declare const FileAttachment: Story;
14
+ /** Extra attachment paths passed via prop (e.g. rejection feedback). */
15
+ export declare const ExtraAttachmentPaths: Story;
16
+ //# sourceMappingURL=inline-attachments.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-attachments.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/inline-attachments/inline-attachments.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,iBAAiB,CAYxC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEhD,gDAAgD;AAChD,eAAO,MAAM,SAAS,EAAE,KAIvB,CAAC;AAEF,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,EAAE,KAIjC,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,mBAAmB,EAAE,KAIjC,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,cAAc,EAAE,KAI5B,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,EAAE,KAQlC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { InlineAttachments } from './inline-attachments.js';
3
+ const meta = {
4
+ title: 'Common/InlineAttachments',
5
+ component: InlineAttachments,
6
+ tags: ['autodocs'],
7
+ parameters: { layout: 'padded' },
8
+ decorators: [
9
+ (Story) => (_jsx("div", { style: { maxWidth: '400px', border: '1px solid var(--color-border)', padding: '16px' }, children: _jsx(Story, {}) })),
10
+ ],
11
+ };
12
+ export default meta;
13
+ /** Plain text with no attachment references. */
14
+ export const PlainText = {
15
+ args: {
16
+ text: 'Fix the login bug where users cannot sign in with Google OAuth.',
17
+ },
18
+ };
19
+ /** Text with an image attachment reference. */
20
+ export const WithImageAttachment = {
21
+ args: {
22
+ text: 'The button is misaligned, see screenshot:\n@/home/user/.shep/attachments/pending-abc/screenshot.png',
23
+ },
24
+ };
25
+ /** Text with multiple attachment references. */
26
+ export const MultipleAttachments = {
27
+ args: {
28
+ text: 'Before: @/home/user/.shep/attachments/pending-abc/before.png\nAfter: @/home/user/.shep/attachments/pending-abc/after.png',
29
+ },
30
+ };
31
+ /** Text with a non-image file attachment. */
32
+ export const FileAttachment = {
33
+ args: {
34
+ text: 'See the log file:\n@/home/user/.shep/attachments/pending-abc/error.log',
35
+ },
36
+ };
37
+ /** Extra attachment paths passed via prop (e.g. rejection feedback). */
38
+ export const ExtraAttachmentPaths = {
39
+ args: {
40
+ text: 'Please fix the layout issues',
41
+ attachmentPaths: [
42
+ '/home/user/.shep/attachments/pending-abc/screenshot1.png',
43
+ '/home/user/.shep/attachments/pending-abc/screenshot2.png',
44
+ ],
45
+ },
46
+ };