@mndrk/agx 2.4.5 → 2.4.7

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 (360) hide show
  1. package/cloud-runtime/standalone/apps/local/.next/BUILD_ID +1 -1
  2. package/cloud-runtime/standalone/apps/local/.next/app-path-routes-manifest.json +4 -0
  3. package/cloud-runtime/standalone/apps/local/.next/build-manifest.json +2 -2
  4. package/cloud-runtime/standalone/apps/local/.next/prerender-manifest.json +3 -3
  5. package/cloud-runtime/standalone/apps/local/.next/routes-manifest.json +33 -0
  6. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.html +2 -2
  7. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.rsc +1 -1
  8. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.html +2 -2
  15. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.rsc +2 -2
  16. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/cloud-runtime/standalone/apps/local/.next/server/app/agents/[id]/page.js.nft.json +1 -1
  23. package/cloud-runtime/standalone/apps/local/.next/server/app/agents/[id]/page_client-reference-manifest.js +1 -1
  24. package/cloud-runtime/standalone/apps/local/.next/server/app/agents/page.js.nft.json +1 -1
  25. package/cloud-runtime/standalone/apps/local/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  26. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.html +2 -2
  27. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.rsc +3 -3
  28. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_full.segment.rsc +3 -3
  29. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  30. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_index.segment.rsc +2 -2
  31. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
  32. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/agents/__PAGE__.segment.rsc +2 -2
  33. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/agents.segment.rsc +1 -1
  34. package/cloud-runtime/standalone/apps/local/.next/server/app/api/agent-specs/route.js.nft.json +1 -1
  35. package/cloud-runtime/standalone/apps/local/.next/server/app/api/agents/[id]/profile/route.js.nft.json +1 -1
  36. package/cloud-runtime/standalone/apps/local/.next/server/app/api/agents/export/route.js.nft.json +1 -1
  37. package/cloud-runtime/standalone/apps/local/.next/server/app/api/chat/route.js.nft.json +1 -1
  38. package/cloud-runtime/standalone/apps/local/.next/server/app/api/file-search/route.js.nft.json +1 -1
  39. package/cloud-runtime/standalone/apps/local/.next/server/app/api/participants/route.js.nft.json +1 -1
  40. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/activities/route.js +1 -1
  41. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/activities/route.js.nft.json +1 -1
  42. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/notes/[noteId]/route.js.nft.json +1 -1
  43. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/notes/route.js.nft.json +1 -1
  44. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route/app-paths-manifest.json +3 -0
  45. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route/build-manifest.json +11 -0
  46. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route/server-reference-manifest.json +4 -0
  47. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route.js +13 -0
  48. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route.js.map +5 -0
  49. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route.js.nft.json +1 -0
  50. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route_client-reference-manifest.js +2 -0
  51. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/route.js.nft.json +1 -1
  52. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/scheduled-tasks/route.js.nft.json +1 -1
  53. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/validate/route.js.nft.json +1 -1
  54. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/worker/route.js.nft.json +1 -1
  55. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  56. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/search/route.js.nft.json +1 -1
  57. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/teams/route.js.nft.json +1 -1
  58. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route/app-paths-manifest.json +3 -0
  59. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route/build-manifest.json +11 -0
  60. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route/server-reference-manifest.json +4 -0
  61. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route.js +11 -0
  62. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route.js.map +5 -0
  63. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route.js.nft.json +1 -0
  64. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route_client-reference-manifest.js +2 -0
  65. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route/app-paths-manifest.json +3 -0
  66. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route/build-manifest.json +11 -0
  67. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route/server-reference-manifest.json +4 -0
  68. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route.js +11 -0
  69. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route.js.map +5 -0
  70. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route.js.nft.json +1 -0
  71. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route_client-reference-manifest.js +2 -0
  72. package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/route.js.nft.json +1 -1
  73. package/cloud-runtime/standalone/apps/local/.next/server/app/api/prompt-jobs/poll/route.js.nft.json +1 -1
  74. package/cloud-runtime/standalone/apps/local/.next/server/app/api/providers/check/[id]/route.js.nft.json +1 -1
  75. package/cloud-runtime/standalone/apps/local/.next/server/app/api/providers/route.js.nft.json +1 -1
  76. package/cloud-runtime/standalone/apps/local/.next/server/app/api/queue/route.js.nft.json +1 -1
  77. package/cloud-runtime/standalone/apps/local/.next/server/app/api/schedules/debug/route.js.nft.json +1 -1
  78. package/cloud-runtime/standalone/apps/local/.next/server/app/api/schedules/poll/route.js.nft.json +1 -1
  79. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/assign/route.js.nft.json +1 -1
  80. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/available/route.js.nft.json +1 -1
  81. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/detail/route.js.nft.json +1 -1
  82. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/history/route.js.nft.json +1 -1
  83. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/learn/route.js.nft.json +1 -1
  84. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/route.js.nft.json +1 -1
  85. package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/unlearn/route.js.nft.json +1 -1
  86. package/cloud-runtime/standalone/apps/local/.next/server/app/api/status/route.js.nft.json +1 -1
  87. package/cloud-runtime/standalone/apps/local/.next/server/app/api/summarize/route.js.nft.json +1 -1
  88. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/comments/[commentId]/route.js.nft.json +1 -1
  89. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/comments/route.js.nft.json +1 -1
  90. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/nodes/[nodeId]/verify/route.js.nft.json +1 -1
  91. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/route.js.nft.json +1 -1
  92. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/route.js.nft.json +1 -1
  93. package/cloud-runtime/standalone/apps/local/.next/server/app/api/threads/knowledge/route.js.nft.json +1 -1
  94. package/cloud-runtime/standalone/apps/local/.next/server/app/api/threads/route.js.nft.json +1 -1
  95. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/recap/route.js.nft.json +1 -1
  96. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/scripted/route.js.nft.json +1 -1
  97. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/status/route.js.nft.json +1 -1
  98. package/cloud-runtime/standalone/apps/local/.next/server/app/api/update-check/route.js.nft.json +1 -1
  99. package/cloud-runtime/standalone/apps/local/.next/server/app/index.html +2 -2
  100. package/cloud-runtime/standalone/apps/local/.next/server/app/index.rsc +3 -3
  101. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  102. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_full.segment.rsc +3 -3
  103. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_head.segment.rsc +1 -1
  104. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_index.segment.rsc +2 -2
  105. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  106. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos/page_client-reference-manifest.js +1 -1
  107. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.html +2 -2
  108. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.rsc +2 -2
  109. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_full.segment.rsc +2 -2
  110. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_head.segment.rsc +1 -1
  111. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_index.segment.rsc +2 -2
  112. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_tree.segment.rsc +2 -2
  113. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations/github/select-repos/__PAGE__.segment.rsc +1 -1
  114. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations/github/select-repos.segment.rsc +1 -1
  115. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations/github.segment.rsc +1 -1
  116. package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations.segment.rsc +1 -1
  117. package/cloud-runtime/standalone/apps/local/.next/server/app/page_client-reference-manifest.js +1 -1
  118. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/automations/page.js.nft.json +1 -1
  119. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/automations/page_client-reference-manifest.js +1 -1
  120. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/env-vars/page.js.nft.json +1 -1
  121. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/env-vars/page_client-reference-manifest.js +1 -1
  122. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/folders/page.js.nft.json +1 -1
  123. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/folders/page_client-reference-manifest.js +1 -1
  124. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/graph/[taskId]/page.js.nft.json +1 -1
  125. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/graph/[taskId]/page_client-reference-manifest.js +1 -1
  126. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/notifications/page.js.nft.json +1 -1
  127. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/notifications/page_client-reference-manifest.js +1 -1
  128. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/[objectiveId]/page.js.nft.json +1 -1
  129. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/[objectiveId]/page_client-reference-manifest.js +1 -1
  130. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/page.js.nft.json +1 -1
  131. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/page_client-reference-manifest.js +1 -1
  132. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  133. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  134. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/prs/page.js.nft.json +1 -1
  135. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/prs/page_client-reference-manifest.js +1 -1
  136. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/agents/[agentId]/page.js.nft.json +1 -1
  137. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/agents/[agentId]/page_client-reference-manifest.js +1 -1
  138. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/page.js.nft.json +1 -1
  139. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/page_client-reference-manifest.js +1 -1
  140. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/adopt/page.js.nft.json +1 -1
  141. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/adopt/page_client-reference-manifest.js +1 -1
  142. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/new/page.js.nft.json +1 -1
  143. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/new/page_client-reference-manifest.js +1 -1
  144. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/page.js.nft.json +1 -1
  145. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/page_client-reference-manifest.js +1 -1
  146. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/replace/page.js.nft.json +1 -1
  147. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/replace/page_client-reference-manifest.js +1 -1
  148. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/terminal/page.js.nft.json +1 -1
  149. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/terminal/page_client-reference-manifest.js +1 -1
  150. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/[threadId]/page.js.nft.json +1 -1
  151. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/[threadId]/page_client-reference-manifest.js +1 -1
  152. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/app-paths-manifest.json +3 -0
  153. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/build-manifest.json +18 -0
  154. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/next-font-manifest.json +11 -0
  155. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/react-loadable-manifest.json +1 -0
  156. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/server-reference-manifest.json +4 -0
  157. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page.js +16 -0
  158. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page.js.map +5 -0
  159. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page.js.nft.json +1 -0
  160. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page_client-reference-manifest.js +2 -0
  161. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/[tracker]/page.js.nft.json +1 -1
  162. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/[tracker]/page_client-reference-manifest.js +1 -1
  163. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/connect/page.js.nft.json +1 -1
  164. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/connect/page_client-reference-manifest.js +1 -1
  165. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/page.js.nft.json +1 -1
  166. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/page_client-reference-manifest.js +1 -1
  167. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/page.js.nft.json +1 -1
  168. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  169. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.html +2 -2
  170. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.rsc +3 -3
  171. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_full.segment.rsc +3 -3
  172. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_head.segment.rsc +1 -1
  173. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_index.segment.rsc +2 -2
  174. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
  175. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/projects/__PAGE__.segment.rsc +2 -2
  176. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/projects.segment.rsc +1 -1
  177. package/cloud-runtime/standalone/apps/local/.next/server/app/setup/page.js.nft.json +1 -1
  178. package/cloud-runtime/standalone/apps/local/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  179. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.html +2 -2
  180. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.rsc +3 -3
  181. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_full.segment.rsc +3 -3
  182. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_head.segment.rsc +1 -1
  183. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_index.segment.rsc +2 -2
  184. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  185. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +2 -2
  186. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/setup.segment.rsc +1 -1
  187. package/cloud-runtime/standalone/apps/local/.next/server/app/status/page_client-reference-manifest.js +1 -1
  188. package/cloud-runtime/standalone/apps/local/.next/server/app/status.html +2 -2
  189. package/cloud-runtime/standalone/apps/local/.next/server/app/status.rsc +2 -2
  190. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_full.segment.rsc +2 -2
  191. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_head.segment.rsc +1 -1
  192. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_index.segment.rsc +2 -2
  193. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_tree.segment.rsc +2 -2
  194. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/status/__PAGE__.segment.rsc +1 -1
  195. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/status.segment.rsc +1 -1
  196. package/cloud-runtime/standalone/apps/local/.next/server/app-paths-manifest.json +4 -0
  197. package/cloud-runtime/standalone/apps/local/.next/server/chunks/30bdd_server_app_api_projects_[id]_workspace_export_route_actions_e161f108.js +3 -0
  198. package/cloud-runtime/standalone/apps/local/.next/server/chunks/30bdd_server_app_api_projects_[id]_workspace_import_route_actions_10d2bce2.js +3 -0
  199. package/cloud-runtime/standalone/apps/local/.next/server/chunks/7255d_app_api_projects_[id]_objectives_[objectiveId]_program_route_actions_e6cdbc76.js +3 -0
  200. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__1563ebff._.js +2 -2
  201. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__3fdd57cb._.js +1 -1
  202. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__45af4365._.js +1 -1
  203. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__683b7ad7._.js +1 -1
  204. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__736d5859._.js +1 -1
  205. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__96b5ea56._.js +3 -0
  206. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__ba72d342._.js +5 -5
  207. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__c3188470._.js +1 -1
  208. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__eb69343e._.js +3 -0
  209. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_1d99e7a5._.js +1 -1
  210. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_4d88e4dc._.js +91 -0
  211. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_4dec65a2._.js +1 -1
  212. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_50e3a6e3._.js +1 -1
  213. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_56a2544c._.js +1 -1
  214. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_5843a1a1._.js +1 -1
  215. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_a15e73a6._.js +1 -1
  216. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_bd72806a._.js +1 -1
  217. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_c101ba9e._.js +1 -1
  218. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_c530bd77._.js +11 -0
  219. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_d69937f2._.js +91 -0
  220. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_e2867b32._.js +3 -0
  221. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_e32b0cab._.js +91 -0
  222. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_lib_5ab8f104._.js +1 -1
  223. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_lib_db_ts_77c23d6c._.js +1 -1
  224. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/0da96_local__next-internal_server_app_projects_[slug]_thread_page_actions_3ceeb59b.js +3 -0
  225. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__1e0fd816._.js +3 -0
  226. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__42499ecf._.js +3 -0
  227. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__5f31e40b._.js +3 -0
  228. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__636799fb._.js +3 -0
  229. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{[root-of-the-server]__4bf7d5b9._.js → [root-of-the-server]__74436f61._.js} +2 -2
  230. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__9434d6d7._.js +3 -0
  231. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__bd339da0._.js +3 -0
  232. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__d08d7899._.js +3 -0
  233. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__e02a457c._.js +3 -0
  234. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__e3a1fef0._.js +3 -0
  235. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__f802e7d1._.js +3 -0
  236. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__ff96bb77._.js +3 -0
  237. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_6a2ba8f0._.js +3 -0
  238. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_7cb6b08a._.js +3 -0
  239. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_a0f42923._.js +7 -0
  240. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_a93c1059._.js +3 -0
  241. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{apps_local_components_PromptJobBoard_tsx_281b2873._.js → apps_local_04cb9fd5._.js} +3 -3
  242. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_15502af8._.js +3 -0
  243. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_16eaf7ae._.js +3 -0
  244. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_3c11c068._.js +8 -0
  245. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_89b49aad._.js +7 -0
  246. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_988d29c0._.js +1 -1
  247. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_ae1a134f._.js +7 -0
  248. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_agents_page_tsx_2a02508d._.js +1 -1
  249. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_folders_page_tsx_72fb68e5._.js +1 -1
  250. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_layout_tsx_3bb31889._.js +1 -1
  251. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_page_tsx_76330306._.js +1 -1
  252. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_teams_[teamId]_page_tsx_6dcfdd52._.js +1 -1
  253. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_b8e580cc._.js +4 -0
  254. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_TrackerBoard_tsx_98970bab._.js +2 -2
  255. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{apps_local_app_projects_[slug]_thread_[threadId]_page_tsx_2a1d1d5e._.js → apps_local_components_chat-ui_ChatContainer_tsx_47371955._.js} +2 -2
  256. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_projects_ProjectObjectivesWorkspace_tsx_751ab3d3._.js +3 -3
  257. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_eb223411._.js +18 -0
  258. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{apps_local_7d1abfbf._.js → apps_local_f1c9872a._.js} +2 -2
  259. package/cloud-runtime/standalone/apps/local/.next/server/functions-config-manifest.json +3 -0
  260. package/cloud-runtime/standalone/apps/local/.next/server/middleware-manifest.json +5 -5
  261. package/cloud-runtime/standalone/apps/local/.next/server/next-font-manifest.js +1 -1
  262. package/cloud-runtime/standalone/apps/local/.next/server/next-font-manifest.json +4 -0
  263. package/cloud-runtime/standalone/apps/local/.next/server/pages/404.html +2 -2
  264. package/cloud-runtime/standalone/apps/local/.next/server/pages/500.html +2 -2
  265. package/cloud-runtime/standalone/apps/local/.next/server/server-reference-manifest.js +1 -1
  266. package/cloud-runtime/standalone/apps/local/.next/server/server-reference-manifest.json +1 -1
  267. package/cloud-runtime/standalone/apps/local/.next/static/chunks/08e75b35d6c52269.js +1 -0
  268. package/cloud-runtime/standalone/apps/local/.next/static/chunks/0d22a723b9b96e65.js +1 -0
  269. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{cb19d71dd7ffb935.js → 1585dd5f9ec4866a.js} +1 -1
  270. package/cloud-runtime/standalone/apps/local/.next/static/chunks/1bc8cc9cbb99c1ed.js +1 -0
  271. package/cloud-runtime/standalone/apps/local/.next/static/chunks/2586c0baa16e9a0d.js +16 -0
  272. package/cloud-runtime/standalone/apps/local/.next/static/chunks/2aa24a251622fd06.js +5 -0
  273. package/cloud-runtime/standalone/apps/local/.next/static/chunks/39513207112d6828.js +1 -0
  274. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{74e3a3e93be78db8.js → 40857295bb9226c4.js} +7 -7
  275. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{8b5d6f5bade8c4ea.js → 41ae15220fc19bf7.js} +2 -2
  276. package/cloud-runtime/standalone/apps/local/.next/static/chunks/5092f14c2bf43204.js +16 -0
  277. package/cloud-runtime/standalone/apps/local/.next/static/chunks/5159169343ce6d2f.css +1 -0
  278. package/cloud-runtime/standalone/apps/local/.next/static/chunks/67b36c70b72e0e10.js +1 -0
  279. package/cloud-runtime/standalone/apps/local/.next/static/chunks/7bfbfcd072f8d61e.js +20 -0
  280. package/cloud-runtime/standalone/apps/local/.next/static/chunks/7cf26dd04166f32f.js +6 -0
  281. package/cloud-runtime/standalone/apps/local/.next/static/chunks/90dcb743d23feeca.js +16 -0
  282. package/cloud-runtime/standalone/apps/local/.next/static/chunks/93749b45e2700808.js +16 -0
  283. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{48feca51cf97c8a7.js → 9bb8f1406365af81.js} +1 -1
  284. package/cloud-runtime/standalone/apps/local/.next/static/chunks/b4aa71020acae46f.js +16 -0
  285. package/cloud-runtime/standalone/apps/local/.next/static/chunks/c352706a0ad7e35d.js +1 -0
  286. package/cloud-runtime/standalone/apps/local/.next/static/chunks/c5436b2b7e8f0ca9.js +16 -0
  287. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{1600dd6ff8607d8f.js → c9a2c4b55bb76f4f.js} +6 -6
  288. package/cloud-runtime/standalone/apps/local/.next/static/chunks/d6a9cc66254693b8.js +1 -0
  289. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{9e703dd5d95e9557.js → e3bdd1e0f385114e.js} +1 -1
  290. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{93b2b6aa0c9593f6.js → e6d4118b7c32d11c.js} +1 -1
  291. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{6ffcd12eabd7c65e.js → f71d73895d228547.js} +1 -1
  292. package/cloud-runtime/standalone/apps/local/.next/static/chunks/f750d2f1882d31a8.js +5 -0
  293. package/cloud-runtime/standalone/apps/local/app/api/automations/route.ts +2 -10
  294. package/cloud-runtime/standalone/apps/local/app/api/projects/[id]/objectives/[objectiveId]/program/route.ts +77 -0
  295. package/cloud-runtime/standalone/apps/local/app/api/projects/[id]/workspace/export/route.ts +35 -0
  296. package/cloud-runtime/standalone/apps/local/app/api/projects/[id]/workspace/import/route.ts +91 -0
  297. package/cloud-runtime/standalone/apps/local/app/api/prompt-jobs/route.ts +1 -8
  298. package/cloud-runtime/standalone/apps/local/app/projects/[slug]/layout.tsx +1 -1
  299. package/cloud-runtime/standalone/apps/local/app/projects/[slug]/teams/[teamId]/page.tsx +58 -2
  300. package/cloud-runtime/standalone/apps/local/app/projects/[slug]/thread/page.tsx +14 -0
  301. package/cloud-runtime/standalone/apps/local/components/AutomationsBoard.tsx +15 -15
  302. package/cloud-runtime/standalone/apps/local/components/PromptJobBoard.tsx +80 -45
  303. package/cloud-runtime/standalone/apps/local/components/projects/FoldersSummaryCard.tsx +51 -196
  304. package/cloud-runtime/standalone/apps/local/components/projects/FoldersView.tsx +662 -784
  305. package/cloud-runtime/standalone/apps/local/components/projects/ObjectiveScheduledTasksPanel.tsx +85 -249
  306. package/cloud-runtime/standalone/apps/local/components/projects/ProjectHome.tsx +4 -4
  307. package/cloud-runtime/standalone/apps/local/components/projects/ProjectObjectivesWorkspace.tsx +118 -0
  308. package/cloud-runtime/standalone/apps/local/components/projects/ProjectSettings.tsx +81 -7
  309. package/cloud-runtime/standalone/apps/local/components/thread/WorkspaceSidebar.tsx +15 -5
  310. package/cloud-runtime/standalone/apps/local/hooks/useProjectWorkspace.ts +117 -0
  311. package/cloud-runtime/standalone/apps/local/lib/db/projects.ts +14 -10
  312. package/cloud-runtime/standalone/apps/local/lib/project-workspace.ts +80 -0
  313. package/cloud-runtime/standalone/apps/local/lib/task-context.ts +39 -4
  314. package/cloud-runtime/standalone/apps/local/lib/workspace-yaml.ts +164 -0
  315. package/cloud-runtime/standalone/apps/local/src/objectives/program/index.ts +7 -0
  316. package/cloud-runtime/standalone/apps/local/src/objectives/program/repository.ts +60 -0
  317. package/cloud-runtime/standalone/apps/local/src/prompt-scheduler/objective-worker.ts +12 -0
  318. package/cloud-runtime/standalone/apps/local/src/scheduling/status.ts +42 -0
  319. package/cloud-runtime/standalone/apps/local/worker/index.js +15 -10
  320. package/lib/commands/workspace.js +145 -7
  321. package/package.json +1 -1
  322. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_09d85861._.js +0 -91
  323. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_25136564._.js +0 -11
  324. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_2caf4398._.js +0 -91
  325. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_31ca7a35._.js +0 -91
  326. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/9fb8c_lucide-react_dist_esm_icons_8c8ee1bc._.js +0 -3
  327. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__3b4708d5._.js +0 -3
  328. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__49cbf624._.js +0 -3
  329. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__49cdd178._.js +0 -3
  330. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__55ed4ff9._.js +0 -3
  331. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__7a31a76d._.js +0 -3
  332. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__7e3c2ea1._.js +0 -7
  333. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__bb42490f._.js +0 -3
  334. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__dbda910b._.js +0 -7
  335. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__ee9351f9._.js +0 -3
  336. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_347d036d._.js +0 -7
  337. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_917d90b4._.js +0 -3
  338. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_f99c22fd._.js +0 -3
  339. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_3b90cb76._.js +0 -18
  340. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_9f36c765._.js +0 -3
  341. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_a2835b49._.js +0 -3
  342. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_8cc1a335._.js +0 -3
  343. package/cloud-runtime/standalone/apps/local/.next/static/chunks/1d8b2726979134e5.js +0 -16
  344. package/cloud-runtime/standalone/apps/local/.next/static/chunks/55a31773f8c2833f.js +0 -20
  345. package/cloud-runtime/standalone/apps/local/.next/static/chunks/6952b44e247d594a.js +0 -16
  346. package/cloud-runtime/standalone/apps/local/.next/static/chunks/6bfdc40a24e65ca2.js +0 -1
  347. package/cloud-runtime/standalone/apps/local/.next/static/chunks/7c8515a24aeea102.js +0 -1
  348. package/cloud-runtime/standalone/apps/local/.next/static/chunks/7ff071e8a33d2fd8.js +0 -16
  349. package/cloud-runtime/standalone/apps/local/.next/static/chunks/80446e085a3aad56.css +0 -1
  350. package/cloud-runtime/standalone/apps/local/.next/static/chunks/87e4fb11329a358d.js +0 -1
  351. package/cloud-runtime/standalone/apps/local/.next/static/chunks/941f7f3626ec442c.js +0 -16
  352. package/cloud-runtime/standalone/apps/local/.next/static/chunks/94a66d2d19268d25.js +0 -5
  353. package/cloud-runtime/standalone/apps/local/.next/static/chunks/9c29ef447bef1576.js +0 -5
  354. package/cloud-runtime/standalone/apps/local/.next/static/chunks/a9111ccc979d4933.js +0 -5
  355. package/cloud-runtime/standalone/apps/local/.next/static/chunks/b35865f64b56d6f4.js +0 -1
  356. package/cloud-runtime/standalone/apps/local/.next/static/chunks/d2b34ade19d9d8da.js +0 -1
  357. package/cloud-runtime/standalone/apps/local/.next/static/chunks/f154e2ed7753f8ee.js +0 -16
  358. /package/cloud-runtime/standalone/apps/local/.next/static/{C3-tP4djbU34_g7VoIFac → xVMVGrzDjtD1H0bGL76NJ}/_buildManifest.js +0 -0
  359. /package/cloud-runtime/standalone/apps/local/.next/static/{C3-tP4djbU34_g7VoIFac → xVMVGrzDjtD1H0bGL76NJ}/_clientMiddlewareManifest.json +0 -0
  360. /package/cloud-runtime/standalone/apps/local/.next/static/{C3-tP4djbU34_g7VoIFac → xVMVGrzDjtD1H0bGL76NJ}/_ssgManifest.js +0 -0
@@ -1,855 +1,733 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
3
+ import type { ChangeEvent } from "react";
4
+ import { useCallback, useRef, useState } from "react";
4
5
  import {
5
- ChevronRight,
6
- Copy,
7
- Check,
8
- ExternalLink,
6
+ Download,
9
7
  FileText,
8
+ Folder,
10
9
  FolderGit2,
11
- FolderOpen,
12
- FolderSearch,
13
- GitBranch,
14
- HardDrive,
10
+ FolderPlus,
11
+ Loader2,
15
12
  Pencil,
16
13
  Plus,
17
14
  Save,
15
+ Settings,
16
+ TerminalSquare,
18
17
  Trash2,
18
+ Upload,
19
19
  X,
20
20
  } from "lucide-react";
21
- import { useProjectsWithAgents, type ProjectRepoInput, type ProjectRepo } from "@/hooks/useProjects";
22
- import DirectoryBrowser from "@/components/DirectoryBrowser";
21
+ import type { WorkspaceEntry } from "@/lib/db/types";
22
+ import { useProjectWorkspace } from "@/hooks/useProjectWorkspace";
23
+ import { buildWorkspaceCategoryGroups } from "@/lib/project-workspace";
24
+ import { DEFAULT_WORKSPACE_CATEGORIES } from "@/lib/workspace-categories";
23
25
 
24
26
  interface FoldersViewProps {
25
27
  projectId: string;
26
28
  }
27
29
 
28
- type SystemNote = {
29
- id: string;
30
- content: string;
31
- created_at?: string;
32
- updated_at?: string;
33
- };
34
-
35
- interface RepoAnalysis {
36
- isGit: boolean;
37
- branch?: string;
38
- status?: { modified: number; untracked: number; staged: number };
39
- languages: Record<string, number>;
30
+ type StatusTone = "success" | "error" | "neutral";
31
+
32
+ interface EntryDraft {
33
+ category: string;
34
+ name: string;
35
+ path: string;
36
+ purpose: string;
37
+ error: string | null;
38
+ isSaving: boolean;
40
39
  }
41
40
 
42
- function mapNote(note: { id: string; content: string; createdAt?: string; updatedAt?: string } | null | undefined): SystemNote | null {
43
- if (!note) return null;
44
- return { id: note.id, content: note.content, created_at: note.createdAt, updated_at: note.updatedAt };
41
+ function getCategoryEntryLabel(categoryId: string, label: string): string {
42
+ switch (categoryId) {
43
+ case "repositories":
44
+ return "Repository";
45
+ case "docs":
46
+ return "Doc";
47
+ case "config":
48
+ return "Config";
49
+ case "scripts":
50
+ return "Script";
51
+ default:
52
+ return label;
53
+ }
45
54
  }
46
55
 
47
- function CopyPathButton({ path }: { path: string }) {
48
- const [copied, setCopied] = useState(false);
49
- return (
50
- <button
51
- type="button"
52
- onClick={(e) => {
53
- e.stopPropagation();
54
- void navigator.clipboard.writeText(path);
55
- setCopied(true);
56
- setTimeout(() => setCopied(false), 1500);
57
- }}
58
- className="rounded p-1 text-[var(--app-shell-soft-text)] transition-colors hover:text-[var(--foreground)]"
59
- title="Copy path"
60
- >
61
- {copied ? <Check className="w-3 h-3 text-green-400" /> : <Copy className="w-3 h-3" />}
62
- </button>
63
- );
56
+ function createEntryDraft(category: string, entry?: WorkspaceEntry): EntryDraft {
57
+ return {
58
+ category,
59
+ name: entry?.name ?? "",
60
+ path: entry?.path ?? "",
61
+ purpose: entry?.purpose ?? "",
62
+ error: null,
63
+ isSaving: false,
64
+ };
64
65
  }
65
66
 
66
- function LanguageBadge({ lang, count }: { lang: string; count: number }) {
67
- return (
68
- <span className="inline-flex items-center gap-1 rounded-md bg-[var(--secondary)] px-2 py-0.5 text-xs text-[var(--muted-foreground)]">
69
- {lang}
70
- <span className="text-[var(--app-shell-soft-text)]">{count}</span>
71
- </span>
72
- );
67
+ function getCategoryIcon(categoryId: string) {
68
+ switch (categoryId) {
69
+ case "repositories":
70
+ return FolderGit2;
71
+ case "docs":
72
+ return FileText;
73
+ case "config":
74
+ return Settings;
75
+ case "scripts":
76
+ return TerminalSquare;
77
+ default:
78
+ return Folder;
79
+ }
73
80
  }
74
81
 
75
- function AnalysisSummary({ analysis }: { analysis: RepoAnalysis }) {
76
- const topLangs = Object.entries(analysis.languages)
77
- .sort(([, a], [, b]) => b - a)
78
- .slice(0, 5);
82
+ export function FoldersView({ projectId }: FoldersViewProps) {
83
+ const { workspace, entryCount, isLoading, error, refetch, createEntry, updateEntry, deleteEntry } = useProjectWorkspace(projectId);
84
+ const [customCategories, setCustomCategories] = useState<string[]>([]);
85
+ const [addingCategoryId, setAddingCategoryId] = useState<string | null>(null);
86
+ const [addDraft, setAddDraft] = useState<EntryDraft>(createEntryDraft("repositories"));
87
+ const [editingEntryId, setEditingEntryId] = useState<string | null>(null);
88
+ const [editDraft, setEditDraft] = useState<EntryDraft | null>(null);
89
+ const [isCreatingCategory, setIsCreatingCategory] = useState(false);
90
+ const [categoryDraft, setCategoryDraft] = useState("");
91
+ const [status, setStatus] = useState<{ tone: StatusTone; message: string } | null>(null);
92
+ const importInputRef = useRef<HTMLInputElement>(null);
93
+
94
+ const groups = buildWorkspaceCategoryGroups(workspace, customCategories);
95
+ const showEmptyState = entryCount === 0 && customCategories.length === 0 && !addingCategoryId;
96
+
97
+ const setFeedback = useCallback((message: string, tone: StatusTone = "neutral") => {
98
+ setStatus({ tone, message });
99
+ window.setTimeout(() => setStatus(null), 3500);
100
+ }, []);
79
101
 
80
- return (
81
- <div className="overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--card-bg)]">
82
- <div className="flex items-center gap-2 border-b border-[var(--border)] px-4 py-2.5">
83
- <HardDrive className="h-3.5 w-3.5 text-[var(--muted-foreground)]" />
84
- <span className="text-xs font-medium uppercase tracking-wider text-[var(--muted-foreground)]">Analysis</span>
85
- </div>
86
- <div className="px-4 py-3 space-y-3">
87
- {/* Git info */}
88
- <div className="flex items-center gap-3">
89
- <div className="flex items-center gap-1.5">
90
- <GitBranch className="h-3.5 w-3.5 text-[var(--muted-foreground)]" />
91
- {analysis.isGit ? (
92
- <span className="text-xs text-[var(--foreground)]">
93
- {analysis.branch ?? "detached"}
94
- </span>
95
- ) : (
96
- <span className="text-xs italic text-[var(--muted-foreground)]">Not a git repo</span>
97
- )}
98
- </div>
99
- {analysis.isGit && analysis.status && (
100
- <div className="flex items-center gap-2 text-xs text-[var(--muted-foreground)]">
101
- {analysis.status.staged > 0 && (
102
- <span className="text-[var(--status-completed-text)]">{analysis.status.staged} staged</span>
103
- )}
104
- {analysis.status.modified > 0 && (
105
- <span className="text-[var(--status-blocked-text)]">{analysis.status.modified} modified</span>
106
- )}
107
- {analysis.status.untracked > 0 && (
108
- <span className="text-[var(--muted-foreground)]">{analysis.status.untracked} untracked</span>
109
- )}
110
- {analysis.status.staged === 0 && analysis.status.modified === 0 && analysis.status.untracked === 0 && (
111
- <span className="text-[var(--app-shell-soft-text)]">clean</span>
112
- )}
113
- </div>
114
- )}
115
- </div>
116
-
117
- {/* Languages */}
118
- {topLangs.length > 0 && (
119
- <div className="flex flex-wrap gap-1.5">
120
- {topLangs.map(([lang, count]) => (
121
- <LanguageBadge key={lang} lang={lang} count={count} />
122
- ))}
123
- </div>
124
- )}
125
- </div>
126
- </div>
127
- );
128
- }
102
+ const pickFolder = useCallback(async () => {
103
+ const response = await fetch("/api/filesystem/pick-folder", { method: "POST" });
104
+ const data = await response.json().catch(() => ({}));
105
+ if (!response.ok) {
106
+ throw new Error(typeof data.error === "string" ? data.error : "Failed to open folder picker");
107
+ }
108
+ return typeof data.path === "string" ? data.path : null;
109
+ }, []);
129
110
 
130
- function FolderRow({
131
- repo,
132
- isSelected,
133
- onSelect,
134
- }: {
135
- repo: ProjectRepo;
136
- isSelected: boolean;
137
- onSelect: () => void;
138
- }) {
139
- return (
140
- <button
141
- type="button"
142
- onClick={onSelect}
143
- className={`w-full text-left flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors group ${
144
- isSelected
145
- ? "border border-blue-500/20 bg-blue-500/10"
146
- : "border border-transparent hover:bg-[var(--secondary)]"
147
- }`}
148
- >
149
- {isSelected ? (
150
- <FolderOpen className="w-4 h-4 text-blue-400 flex-shrink-0" />
151
- ) : (
152
- <FolderGit2 className="h-4 w-4 flex-shrink-0 text-[var(--muted-foreground)]" />
153
- )}
154
- <div className="min-w-0 flex-1">
155
- <div className={`truncate text-sm font-medium ${isSelected ? "text-blue-500" : "text-[var(--foreground)]"}`}>
156
- {repo.name}
157
- </div>
158
- <div className="truncate text-xs text-[var(--muted-foreground)]">
159
- {repo.path || repo.git_url || "No path set"}
160
- </div>
161
- </div>
162
- <ChevronRight
163
- className={`w-3.5 h-3.5 flex-shrink-0 transition-colors ${
164
- isSelected ? "text-blue-500" : "text-[var(--app-shell-soft-text)] group-hover:text-[var(--muted-foreground)]"
165
- }`}
166
- />
167
- </button>
168
- );
169
- }
111
+ const closeAddForm = useCallback(() => {
112
+ setAddingCategoryId(null);
113
+ setAddDraft(createEntryDraft("repositories"));
114
+ }, []);
170
115
 
171
- function FolderDetail({
172
- repo,
173
- systemNote,
174
- analysis,
175
- analysisLoading,
176
- onNoteSave,
177
- noteSaving,
178
- onEdit,
179
- onDelete,
180
- }: {
181
- repo: ProjectRepo;
182
- systemNote: SystemNote | null;
183
- analysis: RepoAnalysis | null;
184
- analysisLoading: boolean;
185
- onNoteSave: (repoId: string, content: string) => Promise<void>;
186
- noteSaving: boolean;
187
- onEdit: (repo: ProjectRepo) => void;
188
- onDelete: (repoId: string) => void;
189
- }) {
190
- const [notes, setNotes] = useState(repo.notes ?? "");
191
- const [editingNotes, setEditingNotes] = useState(false);
192
- const [localNotes, setLocalNotes] = useState(notes);
193
-
194
- useEffect(() => {
195
- setNotes(repo.notes ?? "");
196
- setLocalNotes(repo.notes ?? "");
197
- setEditingNotes(false);
198
- }, [repo.id, repo.notes]);
116
+ const openAddForm = useCallback((category: string) => {
117
+ setAddingCategoryId(category);
118
+ setAddDraft(createEntryDraft(category));
119
+ setEditingEntryId(null);
120
+ setEditDraft(null);
121
+ }, []);
199
122
 
200
- return (
201
- <div className="space-y-5">
202
- {/* Header */}
203
- <div className="flex items-start justify-between gap-3">
204
- <div className="min-w-0 flex-1">
205
- <div className="flex items-center gap-2 mb-1">
206
- <FolderOpen className="w-5 h-5 text-blue-400 flex-shrink-0" />
207
- <h3 className="truncate text-lg font-semibold text-[var(--foreground)]">{repo.name}</h3>
208
- </div>
209
- {repo.path && (
210
- <div className="flex items-center gap-1.5 ml-7">
211
- <code className="truncate font-mono text-xs text-[var(--muted-foreground)]">{repo.path}</code>
212
- <CopyPathButton path={repo.path} />
213
- </div>
214
- )}
215
- {repo.git_url && (
216
- <div className="flex items-center gap-1.5 ml-7 mt-1">
217
- <ExternalLink className="h-3 w-3 flex-shrink-0 text-[var(--app-shell-soft-text)]" />
218
- <a
219
- href={repo.git_url}
220
- target="_blank"
221
- rel="noopener noreferrer"
222
- className="text-xs text-blue-400/70 hover:text-blue-400 truncate"
223
- >
224
- {repo.git_url}
225
- </a>
226
- </div>
227
- )}
228
- </div>
229
- <div className="flex items-center gap-1 flex-shrink-0">
230
- <button
231
- type="button"
232
- onClick={() => onEdit(repo)}
233
- className="rounded-lg p-1.5 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--foreground)]"
234
- title="Edit folder"
235
- >
236
- <Pencil className="w-3.5 h-3.5" />
237
- </button>
238
- <button
239
- type="button"
240
- onClick={() => onDelete(repo.id)}
241
- className="rounded-lg p-1.5 text-[var(--app-shell-soft-text)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--destructive)]"
242
- title="Delete folder"
243
- >
244
- <Trash2 className="w-3.5 h-3.5" />
245
- </button>
246
- </div>
247
- </div>
123
+ const openEditForm = useCallback((entry: WorkspaceEntry) => {
124
+ setEditingEntryId(entry.id);
125
+ setEditDraft(createEntryDraft(entry.category, entry));
126
+ setAddingCategoryId(null);
127
+ setAddDraft(createEntryDraft(entry.category));
128
+ }, []);
248
129
 
249
- {/* Analysis */}
250
- {analysisLoading && (
251
- <div className="rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-4 py-3">
252
- <span className="animate-pulse text-xs text-[var(--muted-foreground)]">Analyzing folder...</span>
253
- </div>
254
- )}
255
- {!analysisLoading && analysis && <AnalysisSummary analysis={analysis} />}
256
-
257
- {/* Notes section */}
258
- <div className="overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--card-bg)]">
259
- <div className="flex items-center justify-between border-b border-[var(--border)] px-4 py-2.5">
260
- <div className="flex items-center gap-2">
261
- <FileText className="h-3.5 w-3.5 text-[var(--muted-foreground)]" />
262
- <span className="text-xs font-medium uppercase tracking-wider text-[var(--muted-foreground)]">Notes</span>
263
- </div>
264
- {!editingNotes ? (
265
- <button
266
- type="button"
267
- onClick={() => {
268
- setLocalNotes(notes);
269
- setEditingNotes(true);
270
- }}
271
- className="flex items-center gap-1 text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
272
- >
273
- <Pencil className="w-3 h-3" />
274
- Edit
275
- </button>
276
- ) : (
277
- <div className="flex items-center gap-2">
278
- <span className="text-[10px] text-[var(--app-shell-soft-text)]">Cmd+Enter to save</span>
279
- <button
280
- type="button"
281
- onClick={() => {
282
- setEditingNotes(false);
283
- setLocalNotes(notes);
284
- }}
285
- className="flex items-center gap-1 text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
286
- >
287
- <X className="w-3 h-3" />
288
- Cancel
289
- </button>
290
- <button
291
- type="button"
292
- disabled={noteSaving}
293
- onClick={async () => {
294
- await onNoteSave(repo.id, localNotes);
295
- setNotes(localNotes);
296
- setEditingNotes(false);
297
- }}
298
- className="flex items-center gap-1 text-xs text-blue-500 transition-colors hover:text-blue-600 disabled:opacity-50"
299
- >
300
- <Save className="w-3 h-3" />
301
- Save
302
- </button>
303
- </div>
304
- )}
305
- </div>
306
- <div className="px-4 py-3 min-h-[80px]">
307
- {editingNotes ? (
308
- <textarea
309
- value={localNotes}
310
- onChange={(e) => setLocalNotes(e.target.value)}
311
- onKeyDown={(e) => {
312
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
313
- e.preventDefault();
314
- void (async () => {
315
- await onNoteSave(repo.id, localNotes);
316
- setNotes(localNotes);
317
- setEditingNotes(false);
318
- })();
319
- } else if (e.key === "Escape") {
320
- setEditingNotes(false);
321
- setLocalNotes(notes);
322
- }
323
- }}
324
- className="min-h-[120px] w-full resize-none bg-transparent text-sm text-[var(--foreground)] outline-none placeholder:text-[var(--app-shell-soft-text)]"
325
- placeholder="Add notes about this folder... (architecture, conventions, important files, etc.)"
326
- autoFocus
327
- />
328
- ) : notes ? (
329
- <p className="whitespace-pre-wrap text-sm text-[var(--foreground)]">{notes}</p>
330
- ) : (
331
- <p className="text-sm italic text-[var(--muted-foreground)]">No notes yet. Click edit to describe this folder.</p>
332
- )}
333
- </div>
334
- </div>
130
+ const closeEditForm = useCallback(() => {
131
+ setEditingEntryId(null);
132
+ setEditDraft(null);
133
+ }, []);
335
134
 
336
- {/* System-generated knowledge */}
337
- {systemNote && (
338
- <div className="overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--secondary)]">
339
- <div className="flex items-center gap-2 border-b border-[var(--border)] px-4 py-2.5">
340
- <span className="w-1.5 h-1.5 rounded-full bg-emerald-500/60" />
341
- <span className="text-xs font-medium uppercase tracking-wider text-[var(--muted-foreground)]">
342
- Generated Knowledge
343
- </span>
344
- {systemNote.updated_at && (
345
- <span className="ml-auto text-xs text-[var(--app-shell-soft-text)]">
346
- {new Date(systemNote.updated_at).toLocaleDateString()}
347
- </span>
348
- )}
349
- </div>
350
- <div className="whitespace-pre-wrap px-4 py-3 text-sm leading-relaxed text-[var(--muted-foreground)]">
351
- {systemNote.content}
352
- </div>
353
- </div>
354
- )}
355
- </div>
356
- );
357
- }
135
+ const handleAddEntry = useCallback(async () => {
136
+ const name = addDraft.name.trim();
137
+ if (!name) {
138
+ setAddDraft((current) => ({ ...current, error: "Name is required" }));
139
+ return;
140
+ }
358
141
 
359
- /** Add-folder flow: name input + directory browser + native picker option */
360
- function AddFolderPanel({
361
- onAdd,
362
- onCancel,
363
- }: {
364
- onAdd: (name: string, path: string) => Promise<void>;
365
- onCancel: () => void;
366
- }) {
367
- const [name, setName] = useState("");
368
- const [selectedPath, setSelectedPath] = useState<string | null>(null);
369
- const [saving, setSaving] = useState(false);
370
- const [pickingNative, setPickingNative] = useState(false);
371
- const [analysis, setAnalysis] = useState<RepoAnalysis | null>(null);
372
- const [analyzing, setAnalyzing] = useState(false);
373
-
374
- const analyzePath = useCallback(async (dirPath: string) => {
375
- setAnalyzing(true);
142
+ setAddDraft((current) => ({ ...current, isSaving: true, error: null }));
376
143
  try {
377
- const res = await fetch("/api/filesystem/analyze", {
378
- method: "POST",
379
- headers: { "Content-Type": "application/json" },
380
- body: JSON.stringify({ path: dirPath }),
144
+ await createEntry({
145
+ category: addDraft.category,
146
+ name,
147
+ path: addDraft.path.trim() || null,
148
+ purpose: addDraft.purpose.trim() || null,
381
149
  });
382
- if (res.ok) {
383
- const data = await res.json();
384
- setAnalysis(data.analysis);
385
- }
386
- } catch { /* ignore */ }
387
- setAnalyzing(false);
388
- }, []);
150
+ closeAddForm();
151
+ setFeedback(`Added ${name} to ${groups.find((group) => group.id === addDraft.category)?.label ?? "workspace map"}`, "success");
152
+ } catch (err) {
153
+ setAddDraft((current) => ({
154
+ ...current,
155
+ isSaving: false,
156
+ error: err instanceof Error ? err.message : "Failed to add workspace entry",
157
+ }));
158
+ }
159
+ }, [addDraft, closeAddForm, createEntry, groups, setFeedback]);
160
+
161
+ const handleSaveEdit = useCallback(async () => {
162
+ if (!editingEntryId || !editDraft) return;
163
+ const name = editDraft.name.trim();
164
+ if (!name) {
165
+ setEditDraft((current) => (current ? { ...current, error: "Name is required" } : current));
166
+ return;
167
+ }
389
168
 
390
- const handleSelectPath = useCallback((path: string) => {
391
- setSelectedPath(path);
392
- // Auto-fill name from last path segment if empty
393
- if (!name.trim()) {
394
- const segment = path.split("/").filter(Boolean).pop() ?? "";
395
- setName(segment);
169
+ setEditDraft((current) => (current ? { ...current, isSaving: true, error: null } : current));
170
+ try {
171
+ await updateEntry(editingEntryId, {
172
+ name,
173
+ path: editDraft.path.trim() || null,
174
+ purpose: editDraft.purpose.trim() || null,
175
+ });
176
+ closeEditForm();
177
+ setFeedback("Workspace entry updated", "success");
178
+ } catch (err) {
179
+ setEditDraft((current) => ({
180
+ ...(current ?? createEntryDraft("")),
181
+ isSaving: false,
182
+ error: err instanceof Error ? err.message : "Failed to update workspace entry",
183
+ }));
396
184
  }
397
- void analyzePath(path);
398
- }, [name, analyzePath]);
185
+ }, [closeEditForm, editDraft, editingEntryId, setFeedback, updateEntry]);
399
186
 
400
- const handleNativePick = useCallback(async () => {
401
- setPickingNative(true);
187
+ const handleDeleteEntry = useCallback(async (entry: WorkspaceEntry) => {
188
+ if (!window.confirm(`Delete ${entry.name} from ${entry.category}?`)) return;
402
189
  try {
403
- const res = await fetch("/api/filesystem/pick-folder", { method: "POST" });
404
- const data = await res.json();
405
- if (data.path) {
406
- handleSelectPath(data.path);
190
+ await deleteEntry(entry.id);
191
+ if (editingEntryId === entry.id) {
192
+ closeEditForm();
407
193
  }
408
- } catch { /* ignore */ }
409
- setPickingNative(false);
410
- }, [handleSelectPath]);
194
+ setFeedback(`Deleted ${entry.name}`, "success");
195
+ } catch (err) {
196
+ setFeedback(err instanceof Error ? err.message : "Failed to delete workspace entry", "error");
197
+ }
198
+ }, [closeEditForm, deleteEntry, editingEntryId, setFeedback]);
411
199
 
412
- const handleSave = useCallback(async () => {
413
- if (!selectedPath || !name.trim()) return;
414
- setSaving(true);
200
+ const handleBrowseForAdd = useCallback(async () => {
415
201
  try {
416
- await onAdd(name.trim(), selectedPath);
417
- } finally {
418
- setSaving(false);
202
+ const path = await pickFolder();
203
+ if (path) {
204
+ setAddDraft((current) => ({ ...current, path, error: null }));
205
+ }
206
+ } catch (err) {
207
+ setAddDraft((current) => ({
208
+ ...current,
209
+ error: err instanceof Error ? err.message : "Failed to pick folder",
210
+ }));
419
211
  }
420
- }, [name, selectedPath, onAdd]);
421
-
422
- return (
423
- <div className="space-y-4">
424
- <div className="flex items-center justify-between">
425
- <h3 className="text-sm font-medium text-[var(--foreground)]">Add Folder</h3>
426
- <button
427
- type="button"
428
- onClick={onCancel}
429
- className="rounded-lg p-1 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--foreground)]"
430
- >
431
- <X className="w-4 h-4" />
432
- </button>
433
- </div>
434
-
435
- {/* Name input */}
436
- <div className="space-y-1.5">
437
- <label className="text-xs font-medium text-[var(--muted-foreground)]">Name</label>
438
- <input
439
- value={name}
440
- onChange={(e) => setName(e.target.value)}
441
- className="input w-full text-sm"
442
- placeholder="Auto-detected from path"
443
- />
444
- </div>
212
+ }, [pickFolder]);
445
213
 
446
- {/* Selected path preview */}
447
- {selectedPath && (
448
- <div className="rounded-lg border border-blue-500/20 bg-blue-500/5 px-3 py-2 flex items-center gap-2">
449
- <FolderOpen className="w-4 h-4 text-blue-400 flex-shrink-0" />
450
- <code className="text-xs text-blue-300 font-mono truncate flex-1">{selectedPath}</code>
451
- <button
452
- type="button"
453
- onClick={() => { setSelectedPath(null); setAnalysis(null); }}
454
- className="p-0.5 rounded text-blue-400/50 hover:text-blue-300 transition-colors flex-shrink-0"
455
- >
456
- <X className="w-3 h-3" />
457
- </button>
458
- </div>
459
- )}
460
-
461
- {/* Analysis preview */}
462
- {analyzing && (
463
- <div className="px-1 text-xs text-[var(--muted-foreground)] animate-pulse">Analyzing...</div>
464
- )}
465
- {!analyzing && analysis && <AnalysisSummary analysis={analysis} />}
466
-
467
- {/* Directory browser or native picker */}
468
- {!selectedPath && (
469
- <div className="space-y-2">
470
- <div className="flex items-center gap-2">
471
- <span className="text-xs font-medium text-[var(--muted-foreground)]">Choose a folder</span>
472
- <button
473
- type="button"
474
- onClick={() => void handleNativePick()}
475
- disabled={pickingNative}
476
- className="ml-auto inline-flex items-center gap-1.5 rounded-lg border border-[var(--border)] bg-[var(--secondary)] px-2.5 py-1 text-xs font-medium text-[var(--muted-foreground)] transition-colors hover:border-[var(--card-hover-border)] hover:text-[var(--foreground)] disabled:opacity-50"
477
- >
478
- <FolderSearch className="w-3 h-3" />
479
- {pickingNative ? "Opening..." : "System Picker"}
480
- </button>
481
- </div>
482
- <DirectoryBrowser
483
- initialPath=""
484
- onSelect={handleSelectPath}
485
- onCancel={onCancel}
486
- />
487
- </div>
488
- )}
489
-
490
- {/* Save action */}
491
- {selectedPath && (
492
- <div className="flex items-center gap-2 pt-1">
493
- <button
494
- type="button"
495
- onClick={() => void handleSave()}
496
- disabled={!name.trim() || saving}
497
- className="btn-primary px-4 py-1.5 text-sm disabled:opacity-40"
498
- >
499
- {saving ? "Adding..." : "Add Folder"}
500
- </button>
501
- <button
502
- type="button"
503
- onClick={onCancel}
504
- className="px-4 py-1.5 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
505
- >
506
- Cancel
507
- </button>
508
- </div>
509
- )}
510
- </div>
511
- );
512
- }
513
-
514
- /** Edit-folder panel: name + path with directory browser */
515
- function EditFolderPanel({
516
- repo,
517
- onSave,
518
- onCancel,
519
- }: {
520
- repo: ProjectRepo;
521
- onSave: (name: string, path: string) => Promise<void>;
522
- onCancel: () => void;
523
- }) {
524
- const [name, setName] = useState(repo.name);
525
- const [selectedPath, setSelectedPath] = useState(repo.path ?? "");
526
- const [showBrowser, setShowBrowser] = useState(false);
527
- const [saving, setSaving] = useState(false);
528
- const [pickingNative, setPickingNative] = useState(false);
529
-
530
- const handleNativePick = useCallback(async () => {
531
- setPickingNative(true);
214
+ const handleBrowseForEdit = useCallback(async () => {
215
+ if (!editDraft) return;
532
216
  try {
533
- const res = await fetch("/api/filesystem/pick-folder", { method: "POST" });
534
- const data = await res.json();
535
- if (data.path) {
536
- setSelectedPath(data.path);
537
- setShowBrowser(false);
217
+ const path = await pickFolder();
218
+ if (path) {
219
+ setEditDraft((current) => (current ? { ...current, path, error: null } : current));
538
220
  }
539
- } catch { /* ignore */ }
540
- setPickingNative(false);
541
- }, []);
542
-
543
- return (
544
- <div className="space-y-4">
545
- <div className="flex items-center justify-between">
546
- <h3 className="text-sm font-medium text-[var(--foreground)]">Edit Folder</h3>
547
- <button
548
- type="button"
549
- onClick={onCancel}
550
- className="rounded-lg p-1 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--foreground)]"
551
- >
552
- <X className="w-4 h-4" />
553
- </button>
554
- </div>
221
+ } catch (err) {
222
+ setEditDraft((current) => (
223
+ current
224
+ ? { ...current, error: err instanceof Error ? err.message : "Failed to pick folder" }
225
+ : current
226
+ ));
227
+ }
228
+ }, [editDraft, pickFolder]);
555
229
 
556
- <div className="space-y-1.5">
557
- <label className="text-xs font-medium text-[var(--muted-foreground)]">Name</label>
558
- <input
559
- value={name}
560
- onChange={(e) => setName(e.target.value)}
561
- className="input w-full text-sm"
562
- placeholder="Folder name"
563
- autoFocus
564
- />
565
- </div>
230
+ const handleQuickBrowse = useCallback(async (entry: WorkspaceEntry) => {
231
+ try {
232
+ const path = await pickFolder();
233
+ if (!path) return;
234
+ await updateEntry(entry.id, { path });
235
+ setFeedback(`Updated path for ${entry.name}`, "success");
236
+ } catch (err) {
237
+ setFeedback(err instanceof Error ? err.message : "Failed to update path", "error");
238
+ }
239
+ }, [pickFolder, setFeedback, updateEntry]);
566
240
 
567
- <div className="space-y-1.5">
568
- <div className="flex items-center justify-between">
569
- <label className="text-xs font-medium text-[var(--muted-foreground)]">Path</label>
570
- <div className="flex items-center gap-1.5">
571
- <button
572
- type="button"
573
- onClick={() => setShowBrowser(!showBrowser)}
574
- className="text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
575
- >
576
- {showBrowser ? "Type path" : "Browse"}
577
- </button>
578
- <button
579
- type="button"
580
- onClick={() => void handleNativePick()}
581
- disabled={pickingNative}
582
- className="inline-flex items-center gap-1 text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)] disabled:opacity-50"
583
- >
584
- <FolderSearch className="w-3 h-3" />
585
- {pickingNative ? "Opening..." : "System Picker"}
586
- </button>
587
- </div>
588
- </div>
589
- {showBrowser ? (
590
- <DirectoryBrowser
591
- initialPath={selectedPath}
592
- onSelect={(path) => { setSelectedPath(path); setShowBrowser(false); }}
593
- onCancel={() => setShowBrowser(false)}
594
- />
595
- ) : (
596
- <input
597
- value={selectedPath}
598
- onChange={(e) => setSelectedPath(e.target.value)}
599
- className="input w-full text-sm font-mono"
600
- placeholder="/path/to/folder"
601
- />
602
- )}
603
- </div>
241
+ const handleCreateCategory = useCallback(() => {
242
+ const nextCategory = categoryDraft.trim();
243
+ if (!nextCategory) return;
604
244
 
605
- <div className="flex items-center gap-2 pt-1">
606
- <button
607
- type="button"
608
- onClick={async () => {
609
- if (!name.trim() || !selectedPath.trim()) return;
610
- setSaving(true);
611
- try { await onSave(name.trim(), selectedPath.trim()); }
612
- finally { setSaving(false); }
613
- }}
614
- disabled={!name.trim() || !selectedPath.trim() || saving}
615
- className="btn-primary px-4 py-1.5 text-sm disabled:opacity-40"
616
- >
617
- {saving ? "Saving..." : "Save"}
618
- </button>
619
- <button
620
- type="button"
621
- onClick={onCancel}
622
- className="px-4 py-1.5 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
623
- >
624
- Cancel
625
- </button>
626
- </div>
627
- </div>
628
- );
629
- }
245
+ const existingGroup = groups.find(
246
+ (group) =>
247
+ group.id.toLowerCase() === nextCategory.toLowerCase() ||
248
+ group.label.toLowerCase() === nextCategory.toLowerCase(),
249
+ );
630
250
 
631
- export function FoldersView({ projectId }: FoldersViewProps) {
632
- const { projects, updateProject } = useProjectsWithAgents();
633
- const project = projects.find((p) => p.id === projectId);
634
-
635
- const [selectedRepoId, setSelectedRepoId] = useState<string | null>(null);
636
- const [addingRepo, setAddingRepo] = useState(false);
637
- const [editingRepo, setEditingRepo] = useState<ProjectRepo | null>(null);
638
- const [repoStatus, setRepoStatus] = useState<string | null>(null);
639
- const [noteSaving, setNoteSaving] = useState(false);
640
- const [systemNotes, setSystemNotes] = useState<Record<string, SystemNote | null>>({});
641
- const [analyses, setAnalyses] = useState<Record<string, RepoAnalysis>>({});
642
- const [analysisLoading, setAnalysisLoading] = useState<Record<string, boolean>>({});
643
-
644
- const repos = project?.repos ?? [];
645
- const selectedRepo = repos.find((r) => r.id === selectedRepoId) ?? null;
646
-
647
- // Auto-select first folder
648
- useEffect(() => {
649
- if (!selectedRepoId && repos.length > 0) {
650
- setSelectedRepoId(repos[0].id);
651
- }
652
- if (selectedRepoId && !repos.find((r) => r.id === selectedRepoId) && repos.length > 0) {
653
- setSelectedRepoId(repos[0].id);
251
+ if (existingGroup) {
252
+ setCategoryDraft("");
253
+ setIsCreatingCategory(false);
254
+ openAddForm(existingGroup.id);
255
+ return;
654
256
  }
655
- }, [repos, selectedRepoId]);
656
-
657
- // Load system notes for all repos
658
- useEffect(() => {
659
- let cancelled = false;
660
- async function load() {
661
- if (repos.length === 0) { setSystemNotes({}); return; }
662
- try {
663
- const entries = await Promise.all(
664
- repos.map(async (repo) => {
665
- const res = await fetch(`/api/knowledge-notes?scope=repo&subjectId=${encodeURIComponent(repo.id)}`);
666
- const data = res.ok ? await res.json() : { note: null };
667
- return [repo.id, mapNote(data.note)] as const;
668
- })
669
- );
670
- if (!cancelled) setSystemNotes(Object.fromEntries(entries));
671
- } catch {
672
- if (!cancelled) setSystemNotes({});
673
- }
257
+
258
+ setCustomCategories((current) => [...current, nextCategory]);
259
+ setCategoryDraft("");
260
+ setIsCreatingCategory(false);
261
+ openAddForm(nextCategory);
262
+ }, [categoryDraft, groups, openAddForm]);
263
+
264
+ const handleRemoveCustomCategory = useCallback((categoryId: string) => {
265
+ setCustomCategories((current) => current.filter((category) => category !== categoryId));
266
+ if (addingCategoryId === categoryId) {
267
+ closeAddForm();
674
268
  }
675
- void load();
676
- return () => { cancelled = true; };
677
- }, [repos]);
678
-
679
- // Analyze selected repo on selection
680
- useEffect(() => {
681
- if (!selectedRepo?.path || analyses[selectedRepo.id]) return;
682
- const repoId = selectedRepo.id;
683
- const repoPath = selectedRepo.path;
684
- setAnalysisLoading((prev) => ({ ...prev, [repoId]: true }));
685
- fetch("/api/filesystem/analyze", {
686
- method: "POST",
687
- headers: { "Content-Type": "application/json" },
688
- body: JSON.stringify({ path: repoPath }),
689
- })
690
- .then((res) => (res.ok ? res.json() : null))
691
- .then((data) => {
692
- if (data?.analysis) {
693
- setAnalyses((prev) => ({ ...prev, [repoId]: data.analysis }));
694
- }
695
- })
696
- .catch(() => {})
697
- .finally(() => setAnalysisLoading((prev) => ({ ...prev, [repoId]: false })));
698
- }, [selectedRepo?.id, selectedRepo?.path, analyses]);
699
-
700
- const setFeedback = useCallback((msg: string) => {
701
- setRepoStatus(msg);
702
- setTimeout(() => setRepoStatus(null), 2500);
703
- }, []);
269
+ }, [addingCategoryId, closeAddForm]);
704
270
 
705
- const toInputs = useCallback((): ProjectRepoInput[] =>
706
- repos.map((r) => ({ id: r.id, name: r.name, path: r.path, git_url: r.git_url, notes: r.notes })),
707
- [repos]
708
- );
271
+ const handleExportYaml = useCallback(() => {
272
+ window.open(`/api/projects/${projectId}/workspace/export`, "_blank");
273
+ }, [projectId]);
709
274
 
710
- const handleAdd = useCallback(async (name: string, path: string) => {
711
- try {
712
- await updateProject(projectId, { repos: [...toInputs(), { name, path }] });
713
- setAddingRepo(false);
714
- setFeedback("Folder added");
715
- } catch { setFeedback("Failed to add"); }
716
- }, [updateProject, projectId, toInputs, setFeedback]);
717
-
718
- const handleEditSave = useCallback(async (name: string, path: string) => {
719
- if (!editingRepo) return;
720
- const updated = toInputs().map((r) =>
721
- r.id === editingRepo.id ? { ...r, name, path } : r
722
- );
723
- try {
724
- await updateProject(projectId, { repos: updated });
725
- setEditingRepo(null);
726
- // Invalidate analysis cache for this repo since path may have changed
727
- setAnalyses((prev) => {
728
- const next = { ...prev };
729
- delete next[editingRepo.id];
730
- return next;
731
- });
732
- setFeedback("Folder updated");
733
- } catch { setFeedback("Failed to update"); }
734
- }, [editingRepo, updateProject, projectId, toInputs, setFeedback]);
275
+ const handleImportYaml = useCallback(async (event: ChangeEvent<HTMLInputElement>) => {
276
+ const file = event.target.files?.[0];
277
+ if (!file) return;
735
278
 
736
- const handleDelete = useCallback(async (repoId: string) => {
737
279
  try {
738
- await updateProject(projectId, { repos: toInputs().filter((r) => r.id !== repoId) });
739
- if (selectedRepoId === repoId) setSelectedRepoId(null);
740
- setFeedback("Folder removed");
741
- } catch { setFeedback("Failed to remove"); }
742
- }, [updateProject, projectId, toInputs, selectedRepoId, setFeedback]);
743
-
744
- const handleNoteSave = useCallback(async (repoId: string, content: string) => {
745
- setNoteSaving(true);
746
- const updated = toInputs().map((r) =>
747
- r.id === repoId ? { ...r, notes: content } : r
748
- );
749
- try {
750
- await updateProject(projectId, { repos: updated });
751
- setFeedback("Notes saved");
752
- } catch {
753
- setFeedback("Failed to save notes");
280
+ const content = await file.text();
281
+ const response = await fetch(`/api/projects/${projectId}/workspace/import`, {
282
+ method: "POST",
283
+ headers: { "Content-Type": "application/x-yaml" },
284
+ body: content,
285
+ });
286
+ if (!response.ok) {
287
+ const data = await response.json().catch(() => ({}));
288
+ throw new Error(typeof data.error === "string" ? data.error : "Failed to import workspace YAML");
289
+ }
290
+ await refetch();
291
+ setFeedback("Imported workspace YAML", "success");
292
+ } catch (err) {
293
+ setFeedback(err instanceof Error ? err.message : "Failed to import workspace YAML", "error");
754
294
  } finally {
755
- setNoteSaving(false);
295
+ if (importInputRef.current) importInputRef.current.value = "";
756
296
  }
757
- }, [updateProject, projectId, toInputs, setFeedback]);
758
-
759
- if (!project) {
760
- return (
761
- <div className="flex h-full items-center justify-center text-sm text-[var(--muted-foreground)]">
762
- Loading...
763
- </div>
764
- );
765
- }
297
+ }, [projectId, refetch, setFeedback]);
766
298
 
767
299
  return (
768
- <div className="h-full flex overflow-hidden">
769
- {/* Left panel: folder list */}
770
- <div className="flex w-72 flex-shrink-0 flex-col border-r border-[var(--border)] bg-[var(--app-shell-pane)]">
771
- <div className="flex items-center justify-between border-b border-[var(--border)] px-4 py-3">
772
- <div className="flex items-center gap-2">
773
- <FolderGit2 className="h-4 w-4 text-[var(--muted-foreground)]" />
774
- <span className="text-sm font-medium text-[var(--foreground)]">Folders</span>
775
- {repos.length > 0 && (
776
- <span className="rounded-full bg-[var(--secondary)] px-1.5 py-0.5 text-xs text-[var(--muted-foreground)]">
777
- {repos.length}
778
- </span>
779
- )}
780
- </div>
781
- <button
782
- type="button"
783
- onClick={() => { setAddingRepo(true); setEditingRepo(null); }}
784
- className="rounded-lg p-1 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-blue-500"
785
- title="Add folder"
786
- >
787
- <Plus className="w-4 h-4" />
788
- </button>
789
- </div>
790
-
791
- <div className="flex-1 overflow-y-auto px-2 py-2 space-y-0.5">
792
- {repos.length === 0 && !addingRepo && (
793
- <div className="px-3 py-8 text-center">
794
- <FolderGit2 className="mx-auto mb-3 h-8 w-8 text-[var(--app-shell-soft-text)]" />
795
- <p className="mb-1 text-sm text-[var(--muted-foreground)]">No folders yet</p>
796
- <p className="mb-4 text-xs text-[var(--app-shell-soft-text)]">Link local repos or project directories</p>
300
+ <div className="h-full overflow-y-auto">
301
+ <div className="mx-auto flex max-w-5xl flex-col gap-6 px-6 py-8">
302
+ <section className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-6 shadow-sm">
303
+ <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
304
+ <div className="max-w-2xl">
305
+ <div className="mb-3 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.2em] text-[var(--muted-foreground)]">
306
+ <FolderGit2 className="h-3.5 w-3.5" />
307
+ Project Setup
308
+ </div>
309
+ <h1 className="text-2xl font-semibold text-[var(--foreground)]">Workspace Map</h1>
310
+ <p className="mt-2 text-sm leading-6 text-[var(--muted-foreground)]">
311
+ Map your project&apos;s folders so agents know where things live. Group repositories, docs, config, and
312
+ scripts into a workspace that people and automation can navigate without guesswork.
313
+ </p>
314
+ </div>
315
+
316
+ <div className="flex flex-wrap items-center gap-2">
797
317
  <button
798
318
  type="button"
799
- onClick={() => setAddingRepo(true)}
800
- className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-blue-400 border border-blue-500/20 hover:bg-blue-500/10 transition-colors"
319
+ onClick={() => setIsCreatingCategory((current) => !current)}
320
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
801
321
  >
802
- <Plus className="w-3 h-3" />
803
- Add Folder
322
+ <FolderPlus className="h-4 w-4" />
323
+ Add Category
804
324
  </button>
325
+ <button
326
+ type="button"
327
+ onClick={handleExportYaml}
328
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
329
+ >
330
+ <Download className="h-4 w-4" />
331
+ Export YAML
332
+ </button>
333
+ <label className="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]">
334
+ <Upload className="h-4 w-4" />
335
+ Import YAML
336
+ <input
337
+ ref={importInputRef}
338
+ type="file"
339
+ accept=".yaml,.yml"
340
+ onChange={handleImportYaml}
341
+ className="hidden"
342
+ />
343
+ </label>
344
+ </div>
345
+ </div>
346
+
347
+ {isCreatingCategory && (
348
+ <div className="mt-4 flex flex-col gap-2 rounded-2xl border border-[var(--border)] bg-[var(--secondary)]/30 p-4 md:flex-row md:items-center">
349
+ <input
350
+ value={categoryDraft}
351
+ onChange={(event) => setCategoryDraft(event.target.value)}
352
+ placeholder="Custom category name"
353
+ className="flex-1 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)]"
354
+ />
355
+ <div className="flex items-center gap-2">
356
+ <button
357
+ type="button"
358
+ onClick={handleCreateCategory}
359
+ disabled={!categoryDraft.trim()}
360
+ className="rounded-xl bg-[var(--foreground)] px-4 py-2 text-sm font-medium text-[var(--background)] disabled:opacity-40"
361
+ >
362
+ Create Category
363
+ </button>
364
+ <button
365
+ type="button"
366
+ onClick={() => {
367
+ setIsCreatingCategory(false);
368
+ setCategoryDraft("");
369
+ }}
370
+ className="rounded-xl px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
371
+ >
372
+ Cancel
373
+ </button>
374
+ </div>
805
375
  </div>
806
376
  )}
807
377
 
808
- {repos.map((repo) => (
809
- <FolderRow
810
- key={repo.id}
811
- repo={repo}
812
- isSelected={selectedRepoId === repo.id}
813
- onSelect={() => { setSelectedRepoId(repo.id); setAddingRepo(false); setEditingRepo(null); }}
814
- />
815
- ))}
816
- </div>
817
-
818
- {repoStatus && (
819
- <div className="border-t border-[var(--border)] px-4 py-2">
820
- <span className="text-xs text-[var(--muted-foreground)]">{repoStatus}</span>
821
- </div>
378
+ {status && (
379
+ <p
380
+ className={`mt-4 text-sm ${
381
+ status.tone === "error"
382
+ ? "text-[var(--destructive)]"
383
+ : status.tone === "success"
384
+ ? "text-emerald-500"
385
+ : "text-[var(--muted-foreground)]"
386
+ }`}
387
+ >
388
+ {status.message}
389
+ </p>
390
+ )}
391
+ </section>
392
+
393
+ {isLoading && (
394
+ <section className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-8">
395
+ <div className="flex items-center gap-3 text-sm text-[var(--muted-foreground)]">
396
+ <Loader2 className="h-4 w-4 animate-spin" />
397
+ Loading workspace map...
398
+ </div>
399
+ </section>
400
+ )}
401
+
402
+ {!isLoading && error && (
403
+ <section className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-8">
404
+ <div className="space-y-3">
405
+ <p className="text-sm text-[var(--destructive)]">{error.message || "Failed to load workspace map"}</p>
406
+ <button
407
+ type="button"
408
+ onClick={() => void refetch()}
409
+ className="rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
410
+ >
411
+ Try Again
412
+ </button>
413
+ </div>
414
+ </section>
822
415
  )}
823
- </div>
824
416
 
825
- {/* Right panel: detail / form */}
826
- <div className="flex-1 overflow-y-auto">
827
- <div className="max-w-2xl mx-auto px-6 py-6">
828
- {addingRepo ? (
829
- <AddFolderPanel onAdd={handleAdd} onCancel={() => setAddingRepo(false)} />
830
- ) : editingRepo ? (
831
- <EditFolderPanel
832
- repo={editingRepo}
833
- onSave={handleEditSave}
834
- onCancel={() => setEditingRepo(null)}
835
- />
836
- ) : selectedRepo ? (
837
- <FolderDetail
838
- repo={selectedRepo}
839
- systemNote={systemNotes[selectedRepo.id] ?? null}
840
- analysis={analyses[selectedRepo.id] ?? null}
841
- analysisLoading={analysisLoading[selectedRepo.id] ?? false}
842
- onNoteSave={handleNoteSave}
843
- noteSaving={noteSaving}
844
- onEdit={(r) => setEditingRepo(r)}
845
- onDelete={handleDelete}
846
- />
847
- ) : repos.length > 0 ? (
848
- <div className="flex h-64 items-center justify-center text-sm text-[var(--muted-foreground)]">
849
- Select a folder to view details
417
+ {!isLoading && !error && showEmptyState && (
418
+ <section className="rounded-3xl border border-dashed border-[var(--border)] bg-[var(--card-bg)] p-10 text-center">
419
+ <div className="mx-auto max-w-2xl">
420
+ <h2 className="text-xl font-semibold text-[var(--foreground)]">Tell your agents where things live.</h2>
421
+ <p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)]">
422
+ Add folders to your workspace so agents can find and work in the right places. Start with a repository,
423
+ docs, or config, or import a shared workspace definition.
424
+ </p>
425
+ <div className="mt-6 flex flex-wrap items-center justify-center gap-3">
426
+ {DEFAULT_WORKSPACE_CATEGORIES.slice(0, 3).map((category) => {
427
+ const Icon = getCategoryIcon(category.id);
428
+ return (
429
+ <button
430
+ key={category.id}
431
+ type="button"
432
+ onClick={() => openAddForm(category.id)}
433
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-4 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
434
+ >
435
+ <Icon className="h-4 w-4" />
436
+ Add {getCategoryEntryLabel(category.id, category.label)}
437
+ </button>
438
+ );
439
+ })}
440
+ <label className="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-4 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]">
441
+ <Upload className="h-4 w-4" />
442
+ Import YAML
443
+ <input
444
+ type="file"
445
+ accept=".yaml,.yml"
446
+ onChange={handleImportYaml}
447
+ className="hidden"
448
+ />
449
+ </label>
450
+ </div>
850
451
  </div>
851
- ) : null}
852
- </div>
452
+ </section>
453
+ )}
454
+
455
+ {!isLoading && !error && !showEmptyState && (
456
+ <div className="space-y-4">
457
+ {groups.map((group) => {
458
+ const Icon = getCategoryIcon(group.id);
459
+
460
+ return (
461
+ <section
462
+ key={group.id}
463
+ className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-6"
464
+ >
465
+ <div className="flex flex-col gap-4 border-b border-[var(--border)] pb-4 md:flex-row md:items-start md:justify-between">
466
+ <div className="flex items-start gap-3">
467
+ <div className="rounded-2xl border border-[var(--border)] bg-[var(--secondary)]/40 p-3">
468
+ <Icon className="h-5 w-5 text-[var(--muted-foreground)]" />
469
+ </div>
470
+ <div>
471
+ <h2 className="text-lg font-semibold text-[var(--foreground)]">{group.label}</h2>
472
+ <p className="text-sm text-[var(--muted-foreground)]">
473
+ {group.entries.length > 0
474
+ ? `${group.entries.length} location${group.entries.length === 1 ? "" : "s"} mapped`
475
+ : group.isPreset
476
+ ? `No ${group.label.toLowerCase()} mapped yet`
477
+ : "Custom category ready for entries"}
478
+ </p>
479
+ </div>
480
+ </div>
481
+ <div className="flex flex-wrap items-center gap-2">
482
+ {!group.isPreset && group.isEmpty && (
483
+ <button
484
+ type="button"
485
+ onClick={() => handleRemoveCustomCategory(group.id)}
486
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--destructive)]"
487
+ >
488
+ <Trash2 className="h-4 w-4" />
489
+ Remove Empty Category
490
+ </button>
491
+ )}
492
+ <button
493
+ type="button"
494
+ onClick={() => openAddForm(group.id)}
495
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
496
+ >
497
+ <Plus className="h-4 w-4" />
498
+ Add to {group.label}
499
+ </button>
500
+ </div>
501
+ </div>
502
+
503
+ <div className="mt-4 space-y-3">
504
+ {group.entries.length === 0 && addingCategoryId !== group.id && (
505
+ <p className="rounded-2xl border border-dashed border-[var(--border)] px-4 py-5 text-sm text-[var(--muted-foreground)]">
506
+ {group.isPreset
507
+ ? `Start this section by adding a ${getCategoryEntryLabel(group.id, group.label).toLowerCase()} and selecting a folder on disk.`
508
+ : "This custom category is empty until you add its first folder."}
509
+ </p>
510
+ )}
511
+
512
+ {group.entries.map((entry) => {
513
+ const isEditing = editingEntryId === entry.id && editDraft;
514
+ return (
515
+ <div
516
+ key={entry.id}
517
+ className="rounded-2xl border border-[var(--border)] bg-[var(--secondary)]/20 p-4"
518
+ >
519
+ {!isEditing && (
520
+ <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
521
+ <div className="min-w-0 flex-1">
522
+ <div className="flex items-center gap-2">
523
+ <p className="text-base font-medium text-[var(--foreground)]">{entry.name}</p>
524
+ </div>
525
+ <p className="mt-2 break-all font-mono text-xs text-[var(--muted-foreground)]">
526
+ {entry.path || "No path selected yet"}
527
+ </p>
528
+ <p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)]">
529
+ {entry.purpose || "No purpose documented yet."}
530
+ </p>
531
+ </div>
532
+ <div className="flex flex-wrap items-center gap-2">
533
+ <button
534
+ type="button"
535
+ onClick={() => void handleQuickBrowse(entry)}
536
+ className="rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
537
+ >
538
+ Browse
539
+ </button>
540
+ <button
541
+ type="button"
542
+ onClick={() => openEditForm(entry)}
543
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
544
+ >
545
+ <Pencil className="h-4 w-4" />
546
+ Edit
547
+ </button>
548
+ <button
549
+ type="button"
550
+ onClick={() => void handleDeleteEntry(entry)}
551
+ className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--destructive)]"
552
+ >
553
+ <Trash2 className="h-4 w-4" />
554
+ Delete
555
+ </button>
556
+ </div>
557
+ </div>
558
+ )}
559
+
560
+ {isEditing && editDraft && (
561
+ <div className="space-y-3">
562
+ <div className="grid gap-3 md:grid-cols-2">
563
+ <label className="block">
564
+ <span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
565
+ Name
566
+ </span>
567
+ <input
568
+ value={editDraft.name}
569
+ onChange={(event) =>
570
+ setEditDraft((current) =>
571
+ current ? { ...current, name: event.target.value, error: null } : current,
572
+ )
573
+ }
574
+ className="w-full rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)]"
575
+ />
576
+ </label>
577
+ <label className="block">
578
+ <span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
579
+ Path
580
+ </span>
581
+ <div className="flex items-center gap-2">
582
+ <input
583
+ value={editDraft.path}
584
+ onChange={(event) =>
585
+ setEditDraft((current) =>
586
+ current ? { ...current, path: event.target.value, error: null } : current,
587
+ )
588
+ }
589
+ className="w-full rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)]"
590
+ placeholder="/Users/you/Projects/repo"
591
+ />
592
+ <button
593
+ type="button"
594
+ onClick={() => void handleBrowseForEdit()}
595
+ className="rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
596
+ >
597
+ Browse
598
+ </button>
599
+ </div>
600
+ </label>
601
+ </div>
602
+ <label className="block">
603
+ <span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
604
+ Purpose
605
+ </span>
606
+ <textarea
607
+ value={editDraft.purpose}
608
+ onChange={(event) =>
609
+ setEditDraft((current) =>
610
+ current ? { ...current, purpose: event.target.value, error: null } : current,
611
+ )
612
+ }
613
+ className="min-h-24 w-full rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-3 text-sm text-[var(--foreground)]"
614
+ placeholder="What should agents or teammates expect in this folder?"
615
+ />
616
+ </label>
617
+ {editDraft.error && (
618
+ <p className="text-sm text-[var(--destructive)]">{editDraft.error}</p>
619
+ )}
620
+ <div className="flex flex-wrap items-center gap-2">
621
+ <button
622
+ type="button"
623
+ onClick={() => void handleSaveEdit()}
624
+ disabled={editDraft.isSaving}
625
+ className="inline-flex items-center gap-2 rounded-xl bg-[var(--foreground)] px-4 py-2 text-sm font-medium text-[var(--background)] disabled:opacity-40"
626
+ >
627
+ {editDraft.isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
628
+ Save Changes
629
+ </button>
630
+ <button
631
+ type="button"
632
+ onClick={closeEditForm}
633
+ className="inline-flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
634
+ >
635
+ <X className="h-4 w-4" />
636
+ Cancel
637
+ </button>
638
+ </div>
639
+ </div>
640
+ )}
641
+ </div>
642
+ );
643
+ })}
644
+
645
+ {addingCategoryId === group.id && (
646
+ <div className="rounded-2xl border border-[var(--border)] bg-[var(--background)] p-4">
647
+ <div className="mb-3 flex items-center gap-2">
648
+ <Plus className="h-4 w-4 text-[var(--muted-foreground)]" />
649
+ <p className="text-sm font-medium text-[var(--foreground)]">Add to {group.label}</p>
650
+ </div>
651
+ <div className="grid gap-3 md:grid-cols-2">
652
+ <label className="block">
653
+ <span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
654
+ Name
655
+ </span>
656
+ <input
657
+ value={addDraft.name}
658
+ onChange={(event) =>
659
+ setAddDraft((current) => ({ ...current, name: event.target.value, error: null }))
660
+ }
661
+ className="w-full rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-2 text-sm text-[var(--foreground)]"
662
+ placeholder="backend, handbook, deploy"
663
+ />
664
+ </label>
665
+ <label className="block">
666
+ <span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
667
+ Path
668
+ </span>
669
+ <div className="flex items-center gap-2">
670
+ <input
671
+ value={addDraft.path}
672
+ onChange={(event) =>
673
+ setAddDraft((current) => ({ ...current, path: event.target.value, error: null }))
674
+ }
675
+ className="w-full rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-2 text-sm text-[var(--foreground)]"
676
+ placeholder="/Users/you/Projects/repo"
677
+ />
678
+ <button
679
+ type="button"
680
+ onClick={() => void handleBrowseForAdd()}
681
+ className="rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
682
+ >
683
+ Browse
684
+ </button>
685
+ </div>
686
+ </label>
687
+ </div>
688
+ <label className="mt-3 block">
689
+ <span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
690
+ Purpose
691
+ </span>
692
+ <textarea
693
+ value={addDraft.purpose}
694
+ onChange={(event) =>
695
+ setAddDraft((current) => ({ ...current, purpose: event.target.value, error: null }))
696
+ }
697
+ className="min-h-24 w-full rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-3 text-sm text-[var(--foreground)]"
698
+ placeholder="Describe what lives here and why it matters."
699
+ />
700
+ </label>
701
+ {addDraft.error && (
702
+ <p className="mt-3 text-sm text-[var(--destructive)]">{addDraft.error}</p>
703
+ )}
704
+ <div className="mt-4 flex flex-wrap items-center gap-2">
705
+ <button
706
+ type="button"
707
+ onClick={() => void handleAddEntry()}
708
+ disabled={addDraft.isSaving}
709
+ className="inline-flex items-center gap-2 rounded-xl bg-[var(--foreground)] px-4 py-2 text-sm font-medium text-[var(--background)] disabled:opacity-40"
710
+ >
711
+ {addDraft.isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
712
+ Save Entry
713
+ </button>
714
+ <button
715
+ type="button"
716
+ onClick={closeAddForm}
717
+ className="inline-flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
718
+ >
719
+ <X className="h-4 w-4" />
720
+ Cancel
721
+ </button>
722
+ </div>
723
+ </div>
724
+ )}
725
+ </div>
726
+ </section>
727
+ );
728
+ })}
729
+ </div>
730
+ )}
853
731
  </div>
854
732
  </div>
855
733
  );