vibepulse 0.2.2 → 0.3.0-beta.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 (253) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/cache/.previewinfo +1 -1
  5. package/.next/cache/.rscinfo +1 -1
  6. package/.next/cache/.tsbuildinfo +1 -1
  7. package/.next/cache/config.json +3 -3
  8. package/.next/fallback-build-manifest.json +2 -2
  9. package/.next/prerender-manifest.json +3 -3
  10. package/.next/routes-manifest.json +8 -0
  11. package/.next/server/app/_global-error/page.js +1 -1
  12. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  13. package/.next/server/app/_global-error.html +2 -2
  14. package/.next/server/app/_global-error.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  17. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/_not-found/page.js +1 -1
  21. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/_not-found.html +1 -1
  24. package/.next/server/app/_not-found.rsc +2 -2
  25. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  26. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  27. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  28. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  29. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  30. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
  32. package/.next/server/app/api/node/sessions/route.js +5 -3
  33. package/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
  34. package/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
  35. package/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
  36. package/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
  37. package/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
  38. package/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
  39. package/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
  40. package/.next/server/app/api/profiles/route.js.nft.json +1 -1
  41. package/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
  42. package/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  43. package/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
  44. package/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
  45. package/.next/server/app/api/sessions/[id]/open-editor/route.js +1 -1
  46. package/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
  47. package/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
  48. package/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
  49. package/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
  50. package/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
  51. package/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
  52. package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
  53. package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
  54. package/.next/server/app/api/sessions/route.js +4 -2
  55. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  56. package/.next/server/app/index.html +1 -1
  57. package/.next/server/app/index.rsc +3 -3
  58. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  59. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  60. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  62. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app-paths-manifest.json +1 -0
  65. package/.next/server/chunks/[root-of-the-server]__31d19c5c._.js +3 -0
  66. package/.next/server/chunks/[root-of-the-server]__31d19c5c._.js.map +1 -0
  67. package/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
  68. package/.next/server/chunks/[root-of-the-server]__56f5f249._.js.map +1 -1
  69. package/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js +3 -0
  70. package/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js.map +1 -0
  71. package/.next/server/chunks/[root-of-the-server]__98073dd6._.js +3 -0
  72. package/.next/server/chunks/[root-of-the-server]__98073dd6._.js.map +1 -0
  73. package/.next/server/chunks/[root-of-the-server]__b7b717eb._.js +3 -0
  74. package/.next/server/chunks/[root-of-the-server]__b7b717eb._.js.map +1 -0
  75. package/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
  76. package/.next/server/chunks/[root-of-the-server]__d8e61048._.js.map +1 -1
  77. package/.next/server/chunks/[root-of-the-server]__f441109e._.js +3 -0
  78. package/.next/server/chunks/[root-of-the-server]__f441109e._.js.map +1 -0
  79. package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
  80. package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js.map +1 -0
  81. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js +3 -0
  82. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js.map +1 -0
  83. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js +3 -0
  84. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js.map +1 -0
  85. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
  86. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js.map +1 -1
  87. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
  88. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js.map +1 -1
  89. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js +3 -0
  90. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js.map +1 -0
  91. package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
  92. package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js.map +1 -0
  93. package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
  94. package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js.map → [root-of-the-server]__c91a8380._.js.map} +1 -1
  95. package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
  96. package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js.map +1 -1
  97. package/.next/server/pages/404.html +1 -1
  98. package/.next/server/pages/500.html +2 -2
  99. package/.next/server/server-reference-manifest.js +1 -1
  100. package/.next/server/server-reference-manifest.json +1 -1
  101. package/.next/standalone/.next/BUILD_ID +1 -1
  102. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  103. package/.next/standalone/.next/build-manifest.json +2 -2
  104. package/.next/standalone/.next/prerender-manifest.json +3 -3
  105. package/.next/standalone/.next/routes-manifest.json +8 -0
  106. package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
  107. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  109. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  110. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  111. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  112. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  113. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  114. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  115. package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  116. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  117. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  119. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  120. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  121. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  122. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  123. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  124. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  125. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  126. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
  127. package/.next/standalone/.next/server/app/api/node/sessions/route.js +5 -3
  128. package/.next/standalone/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
  129. package/.next/standalone/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
  131. package/.next/standalone/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
  132. package/.next/standalone/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
  133. package/.next/standalone/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
  134. package/.next/standalone/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
  135. package/.next/standalone/.next/server/app/api/profiles/route.js.nft.json +1 -1
  136. package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
  137. package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  138. package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
  139. package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
  140. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js +1 -1
  141. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
  143. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
  144. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
  145. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
  146. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
  147. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
  148. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
  149. package/.next/standalone/.next/server/app/api/sessions/route.js +4 -2
  150. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  151. package/.next/standalone/.next/server/app/index.html +1 -1
  152. package/.next/standalone/.next/server/app/index.rsc +3 -3
  153. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  154. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  155. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  156. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  157. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  158. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  159. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  160. package/.next/standalone/.next/server/chunks/[root-of-the-server]__31d19c5c._.js +3 -0
  161. package/.next/standalone/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
  162. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js +3 -0
  163. package/.next/standalone/.next/server/chunks/[root-of-the-server]__98073dd6._.js +3 -0
  164. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b7b717eb._.js +3 -0
  165. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
  166. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f441109e._.js +3 -0
  167. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
  168. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js +3 -0
  169. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js +3 -0
  170. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
  171. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
  172. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js +3 -0
  173. package/.next/standalone/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
  174. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
  175. package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
  176. package/.next/standalone/.next/server/pages/404.html +1 -1
  177. package/.next/standalone/.next/server/pages/500.html +2 -2
  178. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  179. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  180. package/.next/standalone/.next/static/chunks/b3bc362202331708.css +3 -0
  181. package/.next/standalone/.next/static/chunks/{65d5354ba0add961.js → c1294e057d8d4681.js} +3 -3
  182. package/.next/standalone/README.md +29 -5
  183. package/.next/standalone/docs/session-status-detection.md +36 -0
  184. package/.next/standalone/docs/superpowers/specs/2026-04-09-claude-capability-alignment-design.md +39 -0
  185. package/.next/standalone/package-lock.json +2 -2
  186. package/.next/standalone/package.json +1 -1
  187. package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.test.ts +60 -1
  188. package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.ts +77 -22
  189. package/.next/standalone/src/app/api/node/sessions/route.test.ts +282 -0
  190. package/.next/standalone/src/app/api/node/sessions/route.ts +141 -17
  191. package/.next/standalone/src/app/api/opencode-events/route.test.ts +3 -1
  192. package/.next/standalone/src/app/api/sessions/[id]/archive/route.test.ts +101 -0
  193. package/.next/standalone/src/app/api/sessions/[id]/archive/route.ts +47 -12
  194. package/.next/standalone/src/app/api/sessions/[id]/delete/route.test.ts +92 -0
  195. package/.next/standalone/src/app/api/sessions/[id]/delete/route.ts +45 -10
  196. package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.test.ts +74 -0
  197. package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.ts +22 -2
  198. package/.next/standalone/src/app/api/sessions/[id]/restore/route.test.ts +186 -0
  199. package/.next/standalone/src/app/api/sessions/[id]/restore/route.ts +184 -0
  200. package/.next/standalone/src/app/api/sessions/route.test.ts +1889 -107
  201. package/.next/standalone/src/app/api/sessions/route.ts +365 -981
  202. package/.next/standalone/src/components/KanbanBoard.test.tsx +307 -1
  203. package/.next/standalone/src/components/KanbanBoard.tsx +105 -18
  204. package/.next/standalone/src/components/ProjectCard.test.tsx +416 -2
  205. package/.next/standalone/src/components/ProjectCard.tsx +238 -86
  206. package/.next/standalone/src/components/SessionCard.test.tsx +253 -2
  207. package/.next/standalone/src/components/SessionCard.tsx +182 -76
  208. package/.next/standalone/src/hooks/useOpencodeSync.test.ts +321 -1
  209. package/.next/standalone/src/hooks/useOpencodeSync.ts +16 -12
  210. package/.next/standalone/src/lib/claudeSessionOverrides.test.ts +75 -0
  211. package/.next/standalone/src/lib/claudeSessionOverrides.ts +169 -0
  212. package/.next/standalone/src/lib/session-providers/claudeCode.test.ts +2288 -0
  213. package/.next/standalone/src/lib/session-providers/claudeCode.ts +1083 -0
  214. package/.next/standalone/src/lib/session-providers/localAggregator.test.ts +322 -0
  215. package/.next/standalone/src/lib/session-providers/localAggregator.ts +302 -0
  216. package/.next/standalone/src/lib/session-providers/opencodeProvider.ts +723 -0
  217. package/.next/standalone/src/lib/session-providers/providerIds.test.ts +337 -0
  218. package/.next/standalone/src/lib/session-providers/providerIds.ts +176 -0
  219. package/.next/standalone/src/lib/session-providers/types.ts +131 -0
  220. package/.next/standalone/src/lib/transform.test.ts +253 -0
  221. package/.next/standalone/src/lib/transform.ts +96 -37
  222. package/.next/standalone/src/types/index.ts +23 -17
  223. package/.next/static/chunks/b3bc362202331708.css +3 -0
  224. package/.next/static/chunks/{65d5354ba0add961.js → c1294e057d8d4681.js} +3 -3
  225. package/.next/trace +1 -1
  226. package/.next/trace-build +1 -1
  227. package/.next/types/routes.d.ts +2 -1
  228. package/.next/types/validator.ts +9 -0
  229. package/README.md +29 -5
  230. package/package.json +1 -1
  231. package/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
  232. package/.next/server/chunks/[root-of-the-server]__2f981540._.js.map +0 -1
  233. package/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
  234. package/.next/server/chunks/[root-of-the-server]__3745b314._.js.map +0 -1
  235. package/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
  236. package/.next/server/chunks/[root-of-the-server]__6c428a24._.js.map +0 -1
  237. package/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
  238. package/.next/server/chunks/[root-of-the-server]__73a00b88._.js.map +0 -1
  239. package/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
  240. package/.next/server/chunks/[root-of-the-server]__db285678._.js.map +0 -1
  241. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
  243. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
  244. package/.next/standalone/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
  246. package/.next/standalone/.next/static/chunks/f42202943f6742e5.css +0 -3
  247. package/.next/static/chunks/f42202943f6742e5.css +0 -3
  248. /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_buildManifest.js +0 -0
  249. /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_clientMiddlewareManifest.json +0 -0
  250. /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_ssgManifest.js +0 -0
  251. /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_buildManifest.js +0 -0
  252. /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_clientMiddlewareManifest.json +0 -0
  253. /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_ssgManifest.js +0 -0
@@ -8,8 +8,12 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
8
8
  type RenderFn = (ui: React.ReactElement) => { rerender: (ui: React.ReactElement) => void };
9
9
  type Screen = {
10
10
  getByText: (text: string | RegExp) => HTMLElement;
11
+ getAllByText: (text: string | RegExp) => HTMLElement[];
12
+ queryByText: (text: string | RegExp) => HTMLElement | null;
11
13
  getByTitle: (title: string | RegExp) => HTMLElement;
14
+ getAllByTitle: (title: string | RegExp) => HTMLElement[];
12
15
  queryByTitle: (title: string | RegExp) => HTMLElement | null;
16
+ queryAllByTitle: (title: string | RegExp) => HTMLElement[];
13
17
  };
14
18
 
15
19
  const tlReact = TestingLibraryReact as unknown as {
@@ -116,6 +120,12 @@ describe('ProjectCard', () => {
116
120
  hostLabel: 'Node 1',
117
121
  hostKind: 'remote',
118
122
  hostBaseUrl: 'https://node-1.test',
123
+ capabilities: {
124
+ openProject: true,
125
+ openEditor: true,
126
+ archive: true,
127
+ delete: true,
128
+ },
119
129
  };
120
130
  const queryClient = createQueryClient();
121
131
  queryClient.setQueryData(['opencode-config'], { vibepulse: { openEditorTargetMode: 'remote' } });
@@ -146,6 +156,94 @@ describe('ProjectCard', () => {
146
156
  });
147
157
  });
148
158
 
159
+ it('falls back to file-based open for remote projects when openEditor capability is unsupported', async () => {
160
+ const remoteCard: KanbanCard = {
161
+ ...mockCard,
162
+ id: 'node-1:123',
163
+ hostId: 'node-1',
164
+ hostLabel: 'Node 1',
165
+ hostKind: 'remote',
166
+ hostBaseUrl: 'https://node-1.test',
167
+ provider: 'claude-code',
168
+ readOnly: true,
169
+ capabilities: {
170
+ openProject: true,
171
+ openEditor: false,
172
+ archive: false,
173
+ delete: false,
174
+ },
175
+ };
176
+ const queryClient = createQueryClient();
177
+ queryClient.setQueryData(['opencode-config'], { vibepulse: { openEditorTargetMode: 'remote' } });
178
+ const fetchMock = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
179
+ return new Response(JSON.stringify({ success: true }), {
180
+ status: 200,
181
+ headers: { 'Content-Type': 'application/json' },
182
+ });
183
+ });
184
+ Object.defineProperty(globalThis, 'fetch', { value: fetchMock, configurable: true });
185
+
186
+ render(
187
+ <QueryClientProvider client={queryClient}>
188
+ <ProjectCard projectName="TestProject" cards={[remoteCard]} />
189
+ </QueryClientProvider>
190
+ );
191
+
192
+ const { fireEvent } = TestingLibraryReact;
193
+ await TestingLibraryReact.waitFor(() => {
194
+ expect(screen.getByTitle('Open project')).not.toBeDisabled();
195
+ });
196
+ fireEvent.click(screen.getByTitle('Open project'));
197
+
198
+ expect(window.location.assign).toHaveBeenCalledWith('vscode://vscode-remote/ssh-remote+node-1.test/path/to/project');
199
+ expect((fetchMock.mock.calls as unknown as Array<[RequestInfo | URL, RequestInit | undefined]>).filter(([, init]) => init?.method === 'POST')).toHaveLength(0);
200
+ });
201
+
202
+ it('shows actionable error when Antigravity is selected for remote fallback without openEditor support', async () => {
203
+ window.localStorage.setItem('vibepulse:open-tool', 'antigravity');
204
+ const remoteCard: KanbanCard = {
205
+ ...mockCard,
206
+ id: 'node-1:123',
207
+ hostId: 'node-1',
208
+ hostLabel: 'Node 1',
209
+ hostKind: 'remote',
210
+ hostBaseUrl: 'https://node-1.test',
211
+ provider: 'claude-code',
212
+ readOnly: true,
213
+ capabilities: {
214
+ openProject: true,
215
+ openEditor: false,
216
+ archive: false,
217
+ delete: false,
218
+ },
219
+ };
220
+ const queryClient = createQueryClient();
221
+ queryClient.setQueryData(['opencode-config'], { vibepulse: { openEditorTargetMode: 'remote' } });
222
+ const fetchMock = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
223
+ return new Response(JSON.stringify({ success: true }), {
224
+ status: 200,
225
+ headers: { 'Content-Type': 'application/json' },
226
+ });
227
+ });
228
+ Object.defineProperty(globalThis, 'fetch', { value: fetchMock, configurable: true });
229
+
230
+ render(
231
+ <QueryClientProvider client={queryClient}>
232
+ <ProjectCard projectName="TestProject" cards={[remoteCard]} />
233
+ </QueryClientProvider>
234
+ );
235
+
236
+ const { fireEvent } = TestingLibraryReact;
237
+ await TestingLibraryReact.waitFor(() => {
238
+ expect(screen.getByTitle('Open project')).not.toBeDisabled();
239
+ });
240
+ fireEvent.click(screen.getByTitle('Open project'));
241
+
242
+ expect(await TestingLibraryReact.screen.findByText('Antigravity cannot open remote sessions without remote editor support. Use VS Code.')).toBeTruthy();
243
+ expect(window.location.assign).not.toHaveBeenCalled();
244
+ expect((fetchMock.mock.calls as unknown as Array<[RequestInfo | URL, RequestInit | undefined]>).filter(([, init]) => init?.method === 'POST')).toHaveLength(0);
245
+ });
246
+
149
247
  it('shows an explicit loading state while a remote project open request is in flight', async () => {
150
248
  const remoteCard: KanbanCard = {
151
249
  ...mockCard,
@@ -684,14 +782,107 @@ describe('ProjectCard', () => {
684
782
 
685
783
  expect(screen.getByText('TestProject')).toBeTruthy();
686
784
  expect(screen.getByTitle('Source: Remote 1')).toBeTruthy();
687
- expect(screen.queryByTitle('Open project')).toBeNull();
785
+ expect(screen.getByTitle('Open project')).toBeTruthy();
688
786
  });
689
787
 
690
788
  it('respects readOnly prop when explicitly passed', () => {
691
789
  renderWithProviders(
692
790
  <ProjectCard projectName="TestProject" cards={[mockCard]} readOnly={true} />
693
791
  );
694
- expect(screen.queryByTitle('Open project')).toBeNull();
792
+ expect(screen.getByTitle('Open project')).toBeDisabled();
793
+ expect(screen.queryByTitle('Batch actions')).toBeNull();
794
+ expect(screen.queryByTitle('Actions')).toBeNull();
795
+ });
796
+
797
+ it('renders Claude-backed cards as read-only but with footer controls', () => {
798
+ const claudeCard: KanbanCard = {
799
+ ...mockCard,
800
+ provider: 'claude-code',
801
+ readOnly: true,
802
+ id: 'local:claude~550e8400-e29b-41d4-a716-446655440000',
803
+ capabilities: {
804
+ openProject: true,
805
+ openEditor: false,
806
+ archive: true,
807
+ delete: true,
808
+ },
809
+ };
810
+
811
+ renderWithProviders(
812
+ <ProjectCard projectName="TestProject" cards={[claudeCard]} />
813
+ );
814
+
815
+ expect(screen.getByText('TestProject')).toBeTruthy();
816
+ expect(screen.getByTitle('Open project')).toBeTruthy();
817
+ expect(screen.getByTitle('Batch actions')).toBeTruthy();
818
+ expect(screen.getByTitle('Actions')).toBeTruthy();
819
+ });
820
+
821
+ it('keeps writable OpenCode controls in mixed local Claude groups while hiding Claude row actions', async () => {
822
+ const claudeCard: KanbanCard = {
823
+ ...mockCard,
824
+ id: 'local:claude~550e8400-e29b-41d4-a716-446655440000',
825
+ sessionSlug: '550e8400-e29b-41d4-a716-446655440000',
826
+ title: 'Claude Code Session',
827
+ provider: 'claude-code',
828
+ providerRawId: '550e8400-e29b-41d4-a716-446655440000',
829
+ rawSessionId: '550e8400-e29b-41d4-a716-446655440000',
830
+ readOnly: true,
831
+ sortOrder: 1,
832
+ capabilities: {
833
+ openProject: true,
834
+ openEditor: false,
835
+ archive: true,
836
+ delete: true,
837
+ },
838
+ };
839
+
840
+ renderWithProviders(
841
+ <ProjectCard projectName="TestProject" cards={[mockCard, claudeCard]} />
842
+ );
843
+
844
+ expect(screen.getByTitle('Open project')).toBeTruthy();
845
+ expect(screen.getByTitle('Batch actions')).toBeTruthy();
846
+ expect(screen.queryAllByTitle('Actions')).toHaveLength(2);
847
+
848
+ const { fireEvent, waitFor } = TestingLibraryReact;
849
+ fireEvent.click(screen.getByTitle('Batch actions'));
850
+ fireEvent.click(screen.getByText('Archive all'));
851
+
852
+ await waitFor(() => {
853
+ expect(globalThis.fetch).toHaveBeenCalledWith('/api/sessions/local:123/archive', expect.objectContaining({
854
+ method: 'POST',
855
+ }));
856
+ expect(globalThis.fetch).toHaveBeenCalledWith('/api/sessions/local:claude~550e8400-e29b-41d4-a716-446655440000/archive', expect.objectContaining({
857
+ method: 'POST',
858
+ }));
859
+ });
860
+ });
861
+
862
+ it('respects capabilities for action visibility regardless of readOnly status', () => {
863
+ const capabilityCard: KanbanCard = {
864
+ ...mockCard,
865
+ readOnly: true,
866
+ capabilities: {
867
+ openProject: true,
868
+ openEditor: true,
869
+ archive: true,
870
+ delete: false
871
+ }
872
+ };
873
+
874
+ renderWithProviders(
875
+ <ProjectCard projectName="TestProject" cards={[capabilityCard]} />
876
+ );
877
+
878
+ expect(screen.getByTitle('Batch actions')).toBeTruthy();
879
+ expect(screen.getByTitle('Actions')).toBeTruthy();
880
+
881
+ const { fireEvent } = TestingLibraryReact;
882
+ fireEvent.click(screen.getByTitle('Batch actions'));
883
+
884
+ expect(TestingLibraryReact.screen.getByText('Archive all')).toBeTruthy();
885
+ expect(TestingLibraryReact.screen.queryByText('Delete all')).toBeNull();
695
886
  });
696
887
 
697
888
  it('distinguishes same project name on different hosts via badges', () => {
@@ -764,6 +955,56 @@ describe('ProjectCard Host Badges', () => {
764
955
  expect(screen.getByTitle('main')).toBeTruthy();
765
956
  });
766
957
 
958
+ it('renders branch metadata and Open project for pure Claude read-only projects', () => {
959
+ const claudeCard: KanbanCard = {
960
+ ...mockCard,
961
+ provider: 'claude-code',
962
+ readOnly: true,
963
+ id: 'local:claude~550e8400-e29b-41d4-a716-446655440000',
964
+ };
965
+
966
+ renderWithProviders(
967
+ <ProjectCard projectName="TestProject" branch="claude-branch" cards={[claudeCard]} />
968
+ );
969
+
970
+ expect(screen.getByTitle('claude-branch')).toBeTruthy();
971
+ expect(screen.getByTitle('Open project')).toBeTruthy();
972
+ });
973
+
974
+ it('shows restore actions for archived Claude sessions', async () => {
975
+ const fetchMock = vi.fn(async () => new Response(JSON.stringify({ success: true }), {
976
+ status: 200,
977
+ headers: { 'Content-Type': 'application/json' },
978
+ }));
979
+ Object.defineProperty(globalThis, 'fetch', { value: fetchMock, configurable: true });
980
+
981
+ const archivedClaudeCard: KanbanCard = {
982
+ ...mockCard,
983
+ provider: 'claude-code',
984
+ readOnly: true,
985
+ status: 'done',
986
+ id: 'local:claude~550e8400-e29b-41d4-a716-446655440000',
987
+ capabilities: {
988
+ openProject: true,
989
+ openEditor: false,
990
+ archive: true,
991
+ delete: true,
992
+ },
993
+ };
994
+
995
+ renderWithProviders(
996
+ <ProjectCard projectName="TestProject" cards={[archivedClaudeCard]} />
997
+ );
998
+
999
+ const { fireEvent, waitFor } = TestingLibraryReact;
1000
+ fireEvent.click(screen.getByTitle('Batch actions'));
1001
+ fireEvent.click(screen.getByText('Restore all'));
1002
+
1003
+ await waitFor(() => {
1004
+ expect(fetchMock).toHaveBeenCalledWith('/api/sessions/local:claude~550e8400-e29b-41d4-a716-446655440000/restore', expect.objectContaining({ method: 'POST' }));
1005
+ });
1006
+ });
1007
+
767
1008
  it('hides Local badge when multipleHostsEnabled is false', () => {
768
1009
  renderWithProviders(
769
1010
  <ProjectCard projectName="TestProject" cards={[mockCard]} multipleHostsEnabled={false} />
@@ -771,3 +1012,176 @@ describe('ProjectCard Host Badges', () => {
771
1012
  expect(screen.queryByTitle('Source: Local')).toBeNull();
772
1013
  });
773
1014
  });
1015
+
1016
+ describe('ProjectCard Provider Visuals', () => {
1017
+ const mockCard: KanbanCard = {
1018
+ id: 'local:123',
1019
+ sessionSlug: 'session_123',
1020
+ title: 'Session',
1021
+ directory: '/',
1022
+ projectName: 'Prj',
1023
+ agents: [],
1024
+ messageCount: 0,
1025
+ status: 'idle',
1026
+ opencodeStatus: 'idle',
1027
+ waitingForUser: false,
1028
+ todosTotal: 0,
1029
+ todosCompleted: 0,
1030
+ createdAt: 0,
1031
+ updatedAt: 0,
1032
+ sortOrder: 0,
1033
+ provider: 'opencode',
1034
+ };
1035
+
1036
+ it('does not show a provider badge at the project-card level for opencode-only projects', () => {
1037
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[{ ...mockCard, provider: 'opencode' }]} />);
1038
+ expect(screen.queryByTitle('Provider: OpenCode')).toBeNull();
1039
+ });
1040
+
1041
+ it('renders Claude row status as a diamond instead of a round dot', () => {
1042
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[{ ...mockCard, provider: 'claude-code' }]} />);
1043
+ const status = screen.getByTitle('Idle');
1044
+ expect(status.className).toContain('rotate-45');
1045
+ expect(status.className).toContain('h-[7px]');
1046
+ expect(screen.queryByTitle('Provider: Claude Code')).toBeNull();
1047
+ });
1048
+
1049
+ it('does not render a mixed provider badge at the project-card level', () => {
1050
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[
1051
+ { ...mockCard, provider: 'opencode' },
1052
+ { ...mockCard, id: '2', provider: 'claude-code' }
1053
+ ]} />);
1054
+ expect(screen.queryByTitle('Provider: Mixed (OpenCode & Claude)')).toBeNull();
1055
+ expect(screen.getAllByTitle('Idle').some((node) => node.className.includes('rotate-45'))).toBe(true);
1056
+ });
1057
+
1058
+ it('renders verified Claude child rows nested under parent card', () => {
1059
+ const parentCard: KanbanCard = {
1060
+ ...mockCard,
1061
+ id: 'parent-1',
1062
+ provider: 'claude-code',
1063
+ children: [{
1064
+ id: 'child-1',
1065
+ title: 'Nested Claude Child',
1066
+ realTimeStatus: 'busy',
1067
+ waitingForUser: false,
1068
+ createdAt: 1000,
1069
+ updatedAt: 2000
1070
+ }]
1071
+ };
1072
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[parentCard]} />);
1073
+
1074
+ expect(screen.getByText('Nested Claude Child')).toBeTruthy();
1075
+
1076
+ // Find the wrapper with Running title, and check its inner child for the shape class
1077
+ const statusNode = screen.getByTitle('Running');
1078
+ expect(statusNode.innerHTML).toContain('rotate-45');
1079
+ });
1080
+
1081
+ it('suppresses duplicate standalone Claude child cards if they are also passed as top-level cards', () => {
1082
+ const childCard: KanbanCard = {
1083
+ ...mockCard,
1084
+ id: 'child-1',
1085
+ title: 'Standalone Child',
1086
+ provider: 'claude-code'
1087
+ };
1088
+ const parentCard: KanbanCard = {
1089
+ ...mockCard,
1090
+ id: 'parent-1',
1091
+ title: 'Parent Card',
1092
+ provider: 'claude-code',
1093
+ children: [{
1094
+ id: 'child-1',
1095
+ title: 'Nested Claude Child',
1096
+ realTimeStatus: 'busy',
1097
+ waitingForUser: false,
1098
+ createdAt: 1000,
1099
+ updatedAt: 2000
1100
+ }]
1101
+ };
1102
+
1103
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[parentCard, childCard]} />);
1104
+
1105
+ expect(screen.queryByText('Standalone Child')).toBeNull();
1106
+ expect(screen.getByText('Parent Card')).toBeTruthy();
1107
+ expect(screen.getByText('Nested Claude Child')).toBeTruthy();
1108
+ });
1109
+
1110
+ it('keeps duplicate top-level cards when they carry their own descendants', () => {
1111
+ const intermediateCard: KanbanCard = {
1112
+ ...mockCard,
1113
+ id: 'child-1',
1114
+ title: 'Intermediate Top-level Card',
1115
+ provider: 'claude-code',
1116
+ children: [{
1117
+ id: 'grandchild-1',
1118
+ title: 'Nested Claude Grandchild',
1119
+ realTimeStatus: 'busy',
1120
+ waitingForUser: false,
1121
+ createdAt: 1200,
1122
+ updatedAt: 2200,
1123
+ }],
1124
+ };
1125
+ const parentCard: KanbanCard = {
1126
+ ...mockCard,
1127
+ id: 'parent-1',
1128
+ title: 'Parent Card',
1129
+ provider: 'claude-code',
1130
+ children: [{
1131
+ id: 'child-1',
1132
+ title: 'Nested Claude Child',
1133
+ realTimeStatus: 'busy',
1134
+ waitingForUser: false,
1135
+ createdAt: 1000,
1136
+ updatedAt: 2000,
1137
+ }],
1138
+ };
1139
+
1140
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[parentCard, intermediateCard]} />);
1141
+
1142
+ expect(screen.getByText('Intermediate Top-level Card')).toBeTruthy();
1143
+ expect(screen.getByText('Nested Claude Grandchild')).toBeTruthy();
1144
+ });
1145
+
1146
+ it('shows recently updated idle children to avoid delegated-session disappearance', () => {
1147
+ const now = Date.now();
1148
+ const parentCard: KanbanCard = {
1149
+ ...mockCard,
1150
+ id: 'parent-1',
1151
+ provider: 'claude-code',
1152
+ children: [{
1153
+ id: 'child-recent-idle',
1154
+ title: 'Recent Idle Child',
1155
+ realTimeStatus: 'idle',
1156
+ waitingForUser: false,
1157
+ createdAt: now - 90_000,
1158
+ updatedAt: now - 15_000,
1159
+ }],
1160
+ };
1161
+
1162
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[parentCard]} />);
1163
+
1164
+ expect(screen.getByText('Recent Idle Child')).toBeTruthy();
1165
+ });
1166
+
1167
+ it('hides stale idle children that are older than the recent visibility window', () => {
1168
+ const now = Date.now();
1169
+ const parentCard: KanbanCard = {
1170
+ ...mockCard,
1171
+ id: 'parent-1',
1172
+ provider: 'claude-code',
1173
+ children: [{
1174
+ id: 'child-stale-idle',
1175
+ title: 'Stale Idle Child',
1176
+ realTimeStatus: 'idle',
1177
+ waitingForUser: false,
1178
+ createdAt: now - 180_000,
1179
+ updatedAt: now - 120_000,
1180
+ }],
1181
+ };
1182
+
1183
+ renderWithProviders(<ProjectCard projectName="Prj" cards={[parentCard]} />);
1184
+
1185
+ expect(TestingLibraryReact.screen.queryByText('Stale Idle Child')).toBeNull();
1186
+ });
1187
+ });