@chorus-aidlc/chorus 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +215 -215
  3. package/.next/standalone/.next/app-path-routes-manifest.json +44 -44
  4. package/.next/standalone/.next/build-manifest.json +2 -2
  5. package/.next/standalone/.next/prerender-manifest.json +33 -33
  6. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js +1 -1
  7. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js +1 -1
  9. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js +1 -1
  11. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js +2 -2
  13. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js.nft.json +1 -1
  14. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js +1 -1
  16. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page_client-reference-manifest.js +1 -1
  17. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js +2 -2
  18. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/page.js +1 -1
  20. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/page.js +1 -1
  23. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/page.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/page_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js +2 -2
  26. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js +1 -1
  29. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page_client-reference-manifest.js +1 -1
  30. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js +2 -2
  31. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page_client-reference-manifest.js +1 -1
  32. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js +1 -1
  33. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page_client-reference-manifest.js +1 -1
  35. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js +1 -1
  36. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page_client-reference-manifest.js +1 -1
  38. package/.next/standalone/.next/server/app/(dashboard)/projects/page.js +2 -2
  39. package/.next/standalone/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/(dashboard)/settings/page.js +2 -2
  41. package/.next/standalone/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  45. package/.next/standalone/.next/server/app/_not-found.rsc +6 -6
  46. package/.next/standalone/.next/server/app/admin/companies/[uuid]/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/admin/companies/new/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/admin/companies/new.html +1 -1
  49. package/.next/standalone/.next/server/app/admin/companies/new.rsc +6 -6
  50. package/.next/standalone/.next/server/app/admin/companies/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/admin/companies.html +1 -1
  52. package/.next/standalone/.next/server/app/admin/companies.rsc +6 -6
  53. package/.next/standalone/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/admin.html +1 -1
  55. package/.next/standalone/.next/server/app/admin.rsc +6 -6
  56. package/.next/standalone/.next/server/app/api/admin/companies/[uuid]/route_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/api/admin/companies/route_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/api/admin/login/route_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/api/admin/session/route_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/api/agents/[uuid]/route_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/api/agents/route_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/api/api-keys/[uuid]/route_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/api/api-keys/route_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/api/auth/callback/route_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/api/auth/check-default/route_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/api/auth/company-oidc/route_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/api/auth/default-login/route_client-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/app/api/auth/identify/route_client-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/api/auth/refresh/route_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/api/auth/session/route_client-reference-manifest.js +1 -1
  74. package/.next/standalone/.next/server/app/api/auth/sync-token/route_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/app/api/comments/route.js +1 -1
  76. package/.next/standalone/.next/server/app/api/comments/route_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/api/documents/[uuid]/route.js +1 -1
  78. package/.next/standalone/.next/server/app/api/documents/[uuid]/route_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/api/events/notifications/route_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
  81. package/.next/standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  82. package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route_client-reference-manifest.js +1 -1
  85. package/.next/standalone/.next/server/app/api/ideas/[uuid]/route_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/api/mcp/route.js +3 -3
  87. package/.next/standalone/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/api/me/assignments/route.js +1 -1
  89. package/.next/standalone/.next/server/app/api/me/assignments/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/me/assignments/route_client-reference-manifest.js +1 -1
  91. package/.next/standalone/.next/server/app/api/mentionables/route.js +1 -1
  92. package/.next/standalone/.next/server/app/api/mentionables/route_client-reference-manifest.js +1 -1
  93. package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route_client-reference-manifest.js +1 -1
  94. package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route_client-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/app/api/notifications/preferences/route_client-reference-manifest.js +1 -1
  96. package/.next/standalone/.next/server/app/api/notifications/read-all/route_client-reference-manifest.js +1 -1
  97. package/.next/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  98. package/.next/standalone/.next/server/app/api/notifications/unread-count/route_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/api/project-groups/[uuid]/dashboard/route_client-reference-manifest.js +1 -1
  100. package/.next/standalone/.next/server/app/api/project-groups/[uuid]/route_client-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/app/api/project-groups/route_client-reference-manifest.js +1 -1
  102. package/.next/standalone/.next/server/app/api/projects/[uuid]/activity/route_client-reference-manifest.js +1 -1
  103. package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route.js +1 -1
  104. package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route_client-reference-manifest.js +1 -1
  106. package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route.js +1 -1
  107. package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/api/projects/[uuid]/group/route_client-reference-manifest.js +1 -1
  109. package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route_client-reference-manifest.js +1 -1
  110. package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route_client-reference-manifest.js +1 -1
  111. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route.js +1 -1
  112. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route_client-reference-manifest.js +1 -1
  113. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route.js +1 -1
  114. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route_client-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route.js +1 -1
  116. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route_client-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/app/api/projects/[uuid]/route_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route.js +1 -1
  119. package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route_client-reference-manifest.js +1 -1
  120. package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route_client-reference-manifest.js +1 -1
  121. package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route_client-reference-manifest.js +1 -1
  122. package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route.js +1 -1
  124. package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route_client-reference-manifest.js +1 -1
  125. package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route.js +1 -1
  126. package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route_client-reference-manifest.js +1 -1
  127. package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route.js +1 -1
  128. package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route_client-reference-manifest.js +1 -1
  129. package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route.js +1 -1
  130. package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route_client-reference-manifest.js +1 -1
  131. package/.next/standalone/.next/server/app/api/proposals/[uuid]/route.js +1 -1
  132. package/.next/standalone/.next/server/app/api/proposals/[uuid]/route_client-reference-manifest.js +1 -1
  133. package/.next/standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/app/api/sessions/[uuid]/route_client-reference-manifest.js +1 -1
  135. package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route_client-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route_client-reference-manifest.js +1 -1
  137. package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route_client-reference-manifest.js +1 -1
  138. package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route_client-reference-manifest.js +1 -1
  139. package/.next/standalone/.next/server/app/api/tasks/[uuid]/route_client-reference-manifest.js +1 -1
  140. package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route_client-reference-manifest.js +1 -1
  141. package/.next/standalone/.next/server/app/index.html +1 -1
  142. package/.next/standalone/.next/server/app/index.rsc +6 -6
  143. package/.next/standalone/.next/server/app/login/admin/page_client-reference-manifest.js +1 -1
  144. package/.next/standalone/.next/server/app/login/admin.html +1 -1
  145. package/.next/standalone/.next/server/app/login/admin.rsc +6 -6
  146. package/.next/standalone/.next/server/app/login/callback/page_client-reference-manifest.js +1 -1
  147. package/.next/standalone/.next/server/app/login/callback.html +1 -1
  148. package/.next/standalone/.next/server/app/login/callback.rsc +6 -6
  149. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  150. package/.next/standalone/.next/server/app/login/pick-workspace/page_client-reference-manifest.js +1 -1
  151. package/.next/standalone/.next/server/app/login/pick-workspace.html +1 -1
  152. package/.next/standalone/.next/server/app/login/pick-workspace.rsc +6 -6
  153. package/.next/standalone/.next/server/app/login/silent-refresh/page_client-reference-manifest.js +1 -1
  154. package/.next/standalone/.next/server/app/login/silent-refresh.html +1 -1
  155. package/.next/standalone/.next/server/app/login/silent-refresh.rsc +6 -6
  156. package/.next/standalone/.next/server/app/login.html +1 -1
  157. package/.next/standalone/.next/server/app/login.rsc +6 -6
  158. package/.next/standalone/.next/server/app/onboarding/page.js +2 -2
  159. package/.next/standalone/.next/server/app/onboarding/page.js.nft.json +1 -1
  160. package/.next/standalone/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/app/onboarding.html +1 -1
  162. package/.next/standalone/.next/server/app/onboarding.rsc +7 -7
  163. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  164. package/.next/standalone/.next/server/app/projects.html +1 -1
  165. package/.next/standalone/.next/server/app/projects.rsc +7 -7
  166. package/.next/standalone/.next/server/app/settings.html +1 -1
  167. package/.next/standalone/.next/server/app/settings.rsc +8 -8
  168. package/.next/standalone/.next/server/app-paths-manifest.json +44 -44
  169. package/.next/standalone/.next/server/chunks/1002.js +1 -1
  170. package/.next/standalone/.next/server/chunks/1596.js +2 -2
  171. package/.next/standalone/.next/server/chunks/1673.js +1 -0
  172. package/.next/standalone/.next/server/chunks/1871.js +1 -1
  173. package/.next/standalone/.next/server/chunks/2570.js +1 -1
  174. package/.next/standalone/.next/server/chunks/2618.js +1 -1
  175. package/.next/standalone/.next/server/chunks/{2675.js → 3139.js} +1 -1
  176. package/.next/standalone/.next/server/chunks/3405.js +1 -0
  177. package/.next/standalone/.next/server/chunks/4294.js +1 -0
  178. package/.next/standalone/.next/server/chunks/{8251.js → 5044.js} +1 -1
  179. package/.next/standalone/.next/server/chunks/6207.js +1 -1
  180. package/.next/standalone/.next/server/chunks/6836.js +1 -0
  181. package/.next/standalone/.next/server/chunks/8322.js +2 -2
  182. package/.next/standalone/.next/server/chunks/8881.js +1 -1
  183. package/.next/standalone/.next/server/chunks/937.js +2 -2
  184. package/.next/standalone/.next/server/chunks/9508.js +2 -2
  185. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  186. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  187. package/.next/standalone/.next/server/next-font-manifest.json +1 -1
  188. package/.next/standalone/.next/server/pages/404.html +1 -1
  189. package/.next/standalone/.next/server/pages/500.html +1 -1
  190. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  191. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  192. package/.next/standalone/.next/static/chunks/10739-a14d0dc7271f247e.js +1 -0
  193. package/.next/standalone/.next/static/chunks/19126-f1bc396538752bce.js +1 -0
  194. package/.next/standalone/.next/static/chunks/{53664-a2be2eaacce03f0f.js → 23588-66d80958224a8544.js} +1 -1
  195. package/.next/standalone/.next/static/chunks/32190-a110356fb68a4333.js +1 -0
  196. package/.next/standalone/.next/static/chunks/65371-d3686d82abca6495.js +1 -0
  197. package/.next/standalone/.next/static/chunks/70317-49653c6dce23f1d9.js +1 -0
  198. package/.next/standalone/.next/static/chunks/8731-7869a6ed671c63a3.js +1 -0
  199. package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-719f0815a37f19be.js +1 -0
  200. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-bc8c3c0362de377c.js +1 -0
  201. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-c1c4b5798c494341.js +1 -0
  202. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/page-29ac4172940780e0.js +1 -0
  203. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/{page-2d820769aa7bcaf6.js → page-e59232665c3a0412.js} +1 -1
  204. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/ideas/{page-dec0e71221d77928.js → page-9b1e02b5e2cfb410.js} +1 -1
  205. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-567e98e9e90a7ef1.js +1 -0
  206. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/new/{page-bc66567cb9c40bf0.js → page-31f4ffd9778a39ed.js} +1 -1
  207. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/{page-86cfeb421fa97830.js → page-8eded17e1e44522d.js} +1 -1
  208. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/{page-7fe70579023330da.js → page-e5c99f2020b904f5.js} +1 -1
  209. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/{page-8c51993bb3866a55.js → page-7947010e9684fbcf.js} +1 -1
  210. package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/page-a01b45dbe80aeecf.js +1 -0
  211. package/.next/standalone/.next/static/chunks/app/{layout-22f1a931323bbb60.js → layout-482dee7dace72c88.js} +1 -1
  212. package/.next/standalone/.next/static/chunks/app/onboarding/{page-8d755192f0f67938.js → page-ac0593c045c64013.js} +1 -1
  213. package/.next/standalone/.next/static/css/84edd78de243cde6.css +1 -0
  214. package/.next/standalone/.next/static/css/{ca9796ac54f96058.css → a577d38ad6d7491b.css} +1 -1
  215. package/.next/standalone/.next/static/media/0f1bdadaf30e2d5f-s.woff2 +0 -0
  216. package/.next/standalone/.next/static/media/22a5144ee8d83bca-s.p.woff2 +0 -0
  217. package/.next/standalone/.next/static/media/2c34d62a75506231-s.woff2 +0 -0
  218. package/.next/standalone/.next/static/media/601f5c280d60caca-s.woff2 +0 -0
  219. package/.next/standalone/.next/static/media/9766a7e9e2e0ad5a-s.woff2 +0 -0
  220. package/.next/standalone/.next/static/media/a115172161b307bb-s.woff2 +0 -0
  221. package/.next/standalone/.next/static/media/aa016aab0e6d1295-s.woff2 +0 -0
  222. package/.next/standalone/.next/static/media/b66cf8e69499582a-s.woff2 +0 -0
  223. package/.next/standalone/.next/static/media/d100b2a099e34044-s.woff2 +0 -0
  224. package/.next/standalone/.next/static/media/f5271587012faf78-s.p.woff2 +0 -0
  225. package/.next/standalone/.next/static/media/f639721981034f88-s.woff2 +0 -0
  226. package/.next/standalone/package.json +1 -1
  227. package/.next/standalone/public/chorus-plugin/.claude-plugin/plugin.json +7 -1
  228. package/.next/standalone/public/chorus-plugin/agents/proposal-reviewer.md +1 -1
  229. package/.next/standalone/public/chorus-plugin/agents/task-reviewer.md +1 -1
  230. package/.next/standalone/public/chorus-plugin/bin/on-post-verify-task.sh +190 -0
  231. package/.next/standalone/public/chorus-plugin/bin/on-session-start.sh +58 -0
  232. package/.next/standalone/public/chorus-plugin/bin/test-syntax.sh +1 -0
  233. package/.next/standalone/public/chorus-plugin/bin/tests/test-on-post-verify-task.sh +365 -0
  234. package/.next/standalone/public/chorus-plugin/hooks/hooks.json +9 -0
  235. package/.next/standalone/public/chorus-plugin/skills/chorus/SKILL.md +2 -1
  236. package/.next/standalone/public/chorus-plugin/skills/develop/SKILL.md +9 -1
  237. package/.next/standalone/public/chorus-plugin/skills/idea/SKILL.md +1 -1
  238. package/.next/standalone/public/chorus-plugin/skills/openspec-aware/SKILL.md +436 -0
  239. package/.next/standalone/public/chorus-plugin/skills/proposal/SKILL.md +11 -1
  240. package/.next/standalone/public/chorus-plugin/skills/quick-dev/SKILL.md +1 -1
  241. package/.next/standalone/public/chorus-plugin/skills/review/SKILL.md +1 -1
  242. package/.next/standalone/public/chorus-plugin/skills/yolo/SKILL.md +26 -4
  243. package/README.md +34 -7
  244. package/README.zh.md +15 -8
  245. package/chorus.mjs +56 -12
  246. package/package.json +1 -1
  247. package/.next/standalone/.next/server/chunks/1639.js +0 -1
  248. package/.next/standalone/.next/server/chunks/3300.js +0 -1
  249. package/.next/standalone/.next/server/chunks/8584.js +0 -1
  250. package/.next/standalone/.next/server/chunks/9051.js +0 -1
  251. package/.next/standalone/.next/static/chunks/10739-5c185aa0432f5d56.js +0 -1
  252. package/.next/standalone/.next/static/chunks/19126-858a24cab6fd338a.js +0 -1
  253. package/.next/standalone/.next/static/chunks/29445-04c4ab36da85b7da.js +0 -1
  254. package/.next/standalone/.next/static/chunks/39611-cc227784ed679a6d.js +0 -1
  255. package/.next/standalone/.next/static/chunks/96919-8cbf6310c703e7af.js +0 -1
  256. package/.next/standalone/.next/static/chunks/99513-24849ff68a8098e1.js +0 -1
  257. package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-acddb9c84f5d1607.js +0 -1
  258. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-b4c6141dd26cc506.js +0 -1
  259. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-4bf7bff8d68509f9.js +0 -1
  260. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/page-37a0cea0ba40c31b.js +0 -1
  261. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-355321010dd82e56.js +0 -1
  262. package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/page-8db25385236a9bbf.js +0 -1
  263. package/.next/standalone/.next/static/css/de70bee13400563f.css +0 -1
  264. package/.next/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  265. package/.next/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  266. package/.next/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  267. package/.next/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  268. package/.next/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  269. package/.next/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  270. /package/.next/standalone/.next/static/{TJ6qFSxjQGUSajvwbI2qu → CqUAJ6h-gVfCxU6FvVekP}/_buildManifest.js +0 -0
  271. /package/.next/standalone/.next/static/{TJ6qFSxjQGUSajvwbI2qu → CqUAJ6h-gVfCxU6FvVekP}/_ssgManifest.js +0 -0
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env bash
2
+ # on-post-verify-task.sh — PostToolUse hook for chorus_admin_verify_task
3
+ #
4
+ # Detects the "last task verified for an OpenSpec-mode idea" condition and
5
+ # injects an additionalContext reminder telling the main agent to run
6
+ # `openspec archive <slug>` and mirror the resulting
7
+ # openspec/specs/<capability>/spec.md files back to the matching Chorus
8
+ # Documents.
9
+ #
10
+ # Detection contract (all signals must hold to fire):
11
+ # 1. CHORUS_OPENSPEC_MODE != "off" — explicit opt-out wins.
12
+ # 2. The project root contains an `openspec/` directory — this repo is
13
+ # OpenSpec-init'd. Probed via $CLAUDE_PROJECT_DIR/openspec.
14
+ # 3. `openspec` CLI is on PATH — needed for the agent's later
15
+ # `openspec archive` step. Both (2) and (3) are required because
16
+ # either alone leaves the archive workflow unrunnable.
17
+ # 4. The verified task's proposal description contains a line matching
18
+ # ^OpenSpec change slug: <slug>$ (slug provenance from §3.5).
19
+ # 5. Every task under the same proposal has status === "done".
20
+ #
21
+ # If any signal fails, the hook exits 0 silently (strict opt-in).
22
+ #
23
+ # Bash 3.2 compatible (per CLAUDE.md pitfall #10): no ${VAR,,}, no
24
+ # ${VAR^^}, no `declare -A`, no `mapfile`, no `readarray`, no `|&`,
25
+ # no `&>>`.
26
+ #
27
+ # All shell variable parsing of captured JSON uses
28
+ # `printf '%s' "$VAR" | jq ...` rather than `echo "$VAR" | jq ...` —
29
+ # echo corrupts multi-line content on `\n` (canonical §6 warning).
30
+
31
+ set -euo pipefail
32
+
33
+ # Check userConfig toggle — default enabled. The same `enableOpenSpec`
34
+ # switch gates SessionStart detection; if it's off, we skip the archive
35
+ # reminder too. (Env-level CHORUS_OPENSPEC_MODE=off is checked separately
36
+ # at step 4 for symmetry with the SessionStart gate.)
37
+ if [ "${CLAUDE_PLUGIN_OPTION_ENABLEOPENSPEC:-true}" != "true" ]; then
38
+ exit 0
39
+ fi
40
+
41
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
42
+ API="${SCRIPT_DIR}/chorus-api.sh"
43
+
44
+ # Read event JSON from stdin (PostToolUse hook input)
45
+ EVENT=""
46
+ if [ ! -t 0 ]; then
47
+ EVENT=$(cat)
48
+ fi
49
+
50
+ if [ -z "$EVENT" ]; then
51
+ exit 0
52
+ fi
53
+
54
+ # Step 2: Extract taskUuid from .tool_input
55
+ TASK_UUID=$(printf '%s' "$EVENT" | jq -r '.tool_input.taskUuid // empty' 2>/dev/null) || true
56
+
57
+ # Step 3: If taskUuid is empty -> exit 0 silently
58
+ if [ -z "$TASK_UUID" ]; then
59
+ exit 0
60
+ fi
61
+
62
+ # Step 4: OpenSpec gate — explicit opt-out, then folder + CLI both required.
63
+ # Both folder-presence and CLI-presence are necessary: the agent will run
64
+ # `openspec archive <slug>` on receipt of the injected reminder, and that
65
+ # command needs the openspec/ working directory AND the CLI on PATH. If
66
+ # either is missing, the reminder would point the agent at a dead end, so
67
+ # we silently skip injection and let the verify pass through unannotated.
68
+ if [ "${CHORUS_OPENSPEC_MODE:-}" = "off" ]; then
69
+ exit 0
70
+ fi
71
+ PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
72
+ if [ ! -d "${PROJECT_ROOT}/openspec" ]; then
73
+ exit 0
74
+ fi
75
+ if ! command -v openspec >/dev/null 2>&1; then
76
+ exit 0
77
+ fi
78
+ if ! openspec --version >/dev/null 2>&1; then
79
+ exit 0
80
+ fi
81
+
82
+ # Step 5: chorus_get_task -> proposalUuid
83
+ TASK_JSON=$("$API" mcp-tool chorus_get_task "$(printf '{"taskUuid":"%s"}' "$TASK_UUID")" 2>/dev/null) || exit 0
84
+ if [ -z "$TASK_JSON" ]; then
85
+ exit 0
86
+ fi
87
+
88
+ PROPOSAL_UUID=$(printf '%s' "$TASK_JSON" | jq -r '.proposalUuid // empty' 2>/dev/null) || true
89
+ PROJECT_UUID=$(printf '%s' "$TASK_JSON" | jq -r '.project.uuid // empty' 2>/dev/null) || true
90
+
91
+ # Step 6: If proposalUuid empty -> exit 0 silently (Quick Tasks have no proposal)
92
+ if [ -z "$PROPOSAL_UUID" ]; then
93
+ exit 0
94
+ fi
95
+
96
+ # Step 7: chorus_get_proposal -> description (for slug grep)
97
+ PROPOSAL_JSON=$("$API" mcp-tool chorus_get_proposal "$(printf '{"proposalUuid":"%s"}' "$PROPOSAL_UUID")" 2>/dev/null) || exit 0
98
+ if [ -z "$PROPOSAL_JSON" ]; then
99
+ exit 0
100
+ fi
101
+
102
+ PROPOSAL_DESC=$(printf '%s' "$PROPOSAL_JSON" | jq -r '.description // empty' 2>/dev/null) || true
103
+
104
+ # Step 9: grep description for ^OpenSpec change slug: (.+)$ — first match wins
105
+ SLUG=""
106
+ if [ -n "$PROPOSAL_DESC" ]; then
107
+ # POSIX-portable: print description with embedded newlines via printf %s
108
+ # then grep line-anchored for the slug marker.
109
+ SLUG_LINE=$(printf '%s\n' "$PROPOSAL_DESC" | grep -E '^OpenSpec change slug: .+' | head -1 || true)
110
+ if [ -n "$SLUG_LINE" ]; then
111
+ # Strip the prefix; trim trailing whitespace.
112
+ SLUG=$(printf '%s' "$SLUG_LINE" | sed -e 's/^OpenSpec change slug:[[:space:]]*//' -e 's/[[:space:]]*$//')
113
+ fi
114
+ fi
115
+
116
+ # Step 10: If $SLUG is empty -> exit 0 silently (free-form proposal)
117
+ if [ -z "$SLUG" ]; then
118
+ exit 0
119
+ fi
120
+
121
+ # Step 8: chorus_list_tasks (filtered by proposalUuid) -> verify every task is "done".
122
+ # (The proposal-design assumed chorus_get_idea returned tasks[], but the
123
+ # actual MCP tool only returns IdeaResponse; chorus_list_tasks with the
124
+ # proposalUuids filter is the closest equivalent and is the documented
125
+ # contract.)
126
+ #
127
+ # We page through up to 200 tasks (pageSize=200) — far above the design
128
+ # expectation of ~10-20 tasks per proposal. If a proposal exceeds 200
129
+ # tasks the hook conservatively exits 0 silently, matching the
130
+ # Tech Design "Risk: chorus_get_idea is paginated" mitigation.
131
+ TASKS_JSON=$("$API" mcp-tool chorus_list_tasks "$(printf '{"projectUuid":"%s","proposalUuids":["%s"],"pageSize":200}' "$PROJECT_UUID" "$PROPOSAL_UUID")" 2>/dev/null) || exit 0
132
+ if [ -z "$TASKS_JSON" ]; then
133
+ exit 0
134
+ fi
135
+
136
+ TOTAL_TASKS=$(printf '%s' "$TASKS_JSON" | jq -r '.total // 0' 2>/dev/null) || true
137
+ RETURNED_TASKS=$(printf '%s' "$TASKS_JSON" | jq -r '.tasks | length' 2>/dev/null) || true
138
+
139
+ # Pagination guard: if total exceeds what we fetched, exit silently (better
140
+ # safe than fire prematurely).
141
+ if [ -n "$TOTAL_TASKS" ] && [ -n "$RETURNED_TASKS" ] && [ "$TOTAL_TASKS" -gt "$RETURNED_TASKS" ]; then
142
+ exit 0
143
+ fi
144
+
145
+ # Defensive: zero-task proposal would otherwise fall through with
146
+ # NOT_DONE_COUNT=0. Proposal-submit validation prevents this in practice;
147
+ # this guard makes the gate complete.
148
+ if [ -z "$RETURNED_TASKS" ] || [ "$RETURNED_TASKS" -eq 0 ]; then
149
+ exit 0
150
+ fi
151
+
152
+ NOT_DONE_COUNT=$(printf '%s' "$TASKS_JSON" | jq -r '[.tasks[] | select(.status != "done")] | length' 2>/dev/null) || true
153
+
154
+ if [ -z "$NOT_DONE_COUNT" ] || [ "$NOT_DONE_COUNT" != "0" ]; then
155
+ exit 0
156
+ fi
157
+
158
+ # Step 11: Build CONTEXT — instruct the agent to run `openspec archive <SLUG>`
159
+ # and mirror the resulting specs/<capability>/spec.md files back to the
160
+ # matching Chorus Documents.
161
+ #
162
+ # Note on chorus_get_documents: the canonical §3.8 mirror-back step uses
163
+ # `chorus_get_documents(projectUuid)` (the only filter the tool supports
164
+ # server-side is `type` — there is NO server-side title-prefix filter).
165
+ # The agent must list all `type:"spec"` documents and filter client-side
166
+ # by title prefix `Spec:` to find the matching capability.
167
+
168
+ CONTEXT="[Chorus Plugin — OpenSpec Archive Trigger]
169
+ The last task of OpenSpec-mode proposal ${PROPOSAL_UUID} (slug \`${SLUG}\`) has been admin-verified.
170
+
171
+ ACTION REQUIRED: archive the OpenSpec change locally and mirror updated specs back to the Chorus Documents. Run the steps below in order; HALT immediately on any error (canonical §6 — no silent errors).
172
+
173
+ 1. Run \`openspec archive ${SLUG}\` in the repo root. This moves \`openspec/changes/${SLUG}/\` under \`openspec/changes/archive/<date>-${SLUG}/\` and emits/updates one \`openspec/specs/<capability>/spec.md\` per capability. If the CLI prompts interactively and a \`--yes\`/\`--no-confirm\` flag is available, use it (verify with \`openspec archive --help\`).
174
+
175
+ 2. For EACH newly-emitted \`openspec/specs/<capability>/spec.md\`:
176
+ - List all spec-type Documents in this project: \`chorus_get_documents({projectUuid: \"${PROJECT_UUID}\", type: \"spec\"})\`. The \`type\` filter is the only server-side filter — do client-side title matching against the documents you get back.
177
+ - Find the Document whose title matches the capability (canonical §3.8 mirror-back contract; typical title shape \`Spec: <capability>\`).
178
+ - Call \`chorus_pm_update_document\` with the new content from the on-disk spec.md.
179
+
180
+ 3. On any error from \`openspec archive\` or \`chorus_pm_update_document\`: print stderr verbatim, post a comment on the proposal recording the failure (\`chorus_add_comment({targetType: \"proposal\", targetUuid: \"${PROPOSAL_UUID}\", content: \"...\"})\`), and HALT. No retry, no silent skip.
181
+
182
+ 4. Confirm success by listing \`openspec/specs/<capability>/spec.md\` files and verifying they round-trip byte-equal (modulo trailing newline) with their Chorus Document counterparts.
183
+
184
+ References: canonical openspec-aware §3.8 (mirror-back contract), §3.9 (this archive trigger), §6 (no silent errors)."
185
+
186
+ # Step 12: Inject CONTEXT as additionalContext.
187
+ "$API" hook-output "" "$CONTEXT" "PostToolUse"
188
+
189
+ # Step 13: exit 0 (set -e already enforces this on success).
190
+ exit 0
@@ -61,6 +61,36 @@ if command -v jq >/dev/null 2>&1; then
61
61
 
62
62
  fi
63
63
 
64
+ # Detect OpenSpec mode for this repo, once per session.
65
+ # Both conditions are required for OpenSpec mode to be usable:
66
+ # (a) an openspec/ directory at the project root (this repo was inited via `openspec init`), AND
67
+ # (b) the `openspec` CLI on PATH (so we can `openspec new change`, `validate`, `archive`).
68
+ # Overrides (precedence high -> low, first match wins):
69
+ # 1. enableOpenSpec userConfig toggle (default true) — UI-level switch.
70
+ # 2. CHORUS_OPENSPEC_MODE=off env var — env-level explicit opt-out.
71
+ # When the folder is present but the CLI is missing, surface that as a
72
+ # specific reason so the user-visible toast can hint at the install step
73
+ # instead of silently falling back.
74
+ PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
75
+ OPENSPEC_HINT=""
76
+ if [ "${CLAUDE_PLUGIN_OPTION_ENABLEOPENSPEC:-true}" != "true" ]; then
77
+ CHORUS_OPENSPEC_ACTIVE=0
78
+ OPENSPEC_REASON="enableOpenSpec userConfig=false (plugin-level opt-out)"
79
+ elif [ "${CHORUS_OPENSPEC_MODE:-}" = "off" ]; then
80
+ CHORUS_OPENSPEC_ACTIVE=0
81
+ OPENSPEC_REASON="CHORUS_OPENSPEC_MODE=off (explicit opt-out)"
82
+ elif [ ! -d "${PROJECT_ROOT}/openspec" ]; then
83
+ CHORUS_OPENSPEC_ACTIVE=0
84
+ OPENSPEC_REASON="no openspec/ directory at ${PROJECT_ROOT}/openspec"
85
+ elif ! command -v openspec >/dev/null 2>&1; then
86
+ CHORUS_OPENSPEC_ACTIVE=0
87
+ OPENSPEC_REASON="openspec/ directory present but \`openspec\` CLI not on PATH"
88
+ OPENSPEC_HINT="install with: npm i -g @fission-ai/openspec"
89
+ else
90
+ CHORUS_OPENSPEC_ACTIVE=1
91
+ OPENSPEC_REASON="openspec/ directory + openspec CLI both present"
92
+ fi
93
+
64
94
  # Build context for Claude (additionalContext)
65
95
  CONTEXT="# Chorus Plugin — Active
66
96
 
@@ -70,6 +100,29 @@ Chorus is connected at ${CHORUS_URL}. Session lifecycle hooks are enabled.
70
100
 
71
101
  ${CHECKIN_RESULT}
72
102
 
103
+ ## OpenSpec Mode
104
+
105
+ CHORUS_OPENSPEC_ACTIVE=${CHORUS_OPENSPEC_ACTIVE} (${OPENSPEC_REASON})"
106
+
107
+ if [ "$CHORUS_OPENSPEC_ACTIVE" = "1" ]; then
108
+ CONTEXT="${CONTEXT}
109
+
110
+ OpenSpec mode is **active** for this session. When the proposal / develop / yolo skills reach an OpenSpec-aware step, load the openspec-aware skill at \`.claude/skills/openspec-aware/SKILL.md\` and follow §3 (OpenSpec authoring) — do NOT re-run the §1 detection block, the answer is already known.
111
+
112
+ Critical rule (openspec-aware §2 Rule 1): document mirror calls (\`chorus_pm_add_document_draft\` / \`chorus_pm_update_document_draft\` / \`chorus_pm_update_document\`) MUST go through \`chorus-api.sh mcp-tool\` with \`content\` produced by \`json_encode_file\`. Do NOT invoke these MCP tools directly with hand-typed \`content\` in OpenSpec mode."
113
+ else
114
+ CONTEXT="${CONTEXT}
115
+
116
+ OpenSpec mode is **inactive** for this session. The proposal / develop / yolo skills follow their free-form path; do NOT scaffold \`openspec/changes/\`, do NOT add an \`OpenSpec change slug:\` line to proposal descriptions, and do NOT route document mirror calls through \`chorus-api.sh\`."
117
+ if [ -n "$OPENSPEC_HINT" ]; then
118
+ CONTEXT="${CONTEXT}
119
+
120
+ Note: this repo has an \`openspec/\` directory, so the user likely intends to use OpenSpec mode but the \`openspec\` CLI is not installed. Surface this to the user (e.g. \"This repo is OpenSpec-init'd but the \\\`openspec\\\` CLI isn't installed locally — ${OPENSPEC_HINT}\") before authoring documents so they can install it and re-launch the session if they want spec-driven mode."
121
+ fi
122
+ fi
123
+
124
+ CONTEXT="${CONTEXT}
125
+
73
126
  ## Quick Reference
74
127
 
75
128
  - **Idea Tracker**: Shows up to 10 most recently updated ideas. Use chorus_get_ideas() for full list.
@@ -88,6 +141,11 @@ fi
88
141
 
89
142
  # Build user-visible message
90
143
  USER_MSG="Chorus connected at ${CHORUS_URL}"
144
+ if [ "$CHORUS_OPENSPEC_ACTIVE" = "1" ]; then
145
+ USER_MSG="${USER_MSG} (OpenSpec Enabled)"
146
+ elif [ -n "$OPENSPEC_HINT" ]; then
147
+ USER_MSG="${USER_MSG} (OpenSpec repo detected — ${OPENSPEC_HINT})"
148
+ fi
91
149
  if [ -n "$MAIN_SESSION" ]; then
92
150
  USER_MSG="${USER_MSG} (resumed session)"
93
151
  fi
@@ -72,6 +72,7 @@ run_test "on-task-completed.sh" '{"task_id":"task-001"}'
72
72
  # --- PostToolUse hooks ---
73
73
  run_test "on-post-submit-proposal.sh" '{"tool_input":{"proposalUuid":"test-uuid"},"tool_response":{"uuid":"test-uuid","status":"pending","title":"Test proposal"}}'
74
74
  run_test "on-post-submit-for-verify.sh" '{"tool_input":{"taskUuid":"test-uuid"},"tool_response":{"uuid":"test-uuid","status":"to_verify","title":"Test task"}}'
75
+ run_test "on-post-verify-task.sh" '{"tool_input":{"taskUuid":"test-uuid"},"tool_response":{"uuid":"test-uuid","status":"done","title":"Test task"}}'
75
76
 
76
77
  # --- Session hooks ---
77
78
  run_test "on-session-start.sh" '{}'
@@ -0,0 +1,365 @@
1
+ #!/usr/bin/env bash
2
+ # test-on-post-verify-task.sh — Fixture-based shell test for the
3
+ # on-post-verify-task.sh hooks (Claude Code + Codex variants).
4
+ #
5
+ # We can't talk to a real Chorus backend in CI, so we shim out the MCP
6
+ # wrapper and the openspec CLI: the test creates a sandbox PATH and a
7
+ # replacement "chorus-api.sh" / "chorus-mcp-call.sh" that echo canned JSON
8
+ # responses keyed off the (tool_name, fixture_id) pair carried via the
9
+ # CHORUS_FIXTURE env var. The replacement scripts are invoked because the
10
+ # hook resolves them by directory next to itself (Claude Code) or sources
11
+ # hook-output.sh by directory (Codex). We exploit this by COPYING the
12
+ # hook into a sandbox dir alongside our shim wrapper — the hook's
13
+ # `dirname "$0"` lookup then picks up our shim, not the real wrapper.
14
+ #
15
+ # Fixtures cover all gating branches:
16
+ # positive — last task verified, slug present, openspec/ folder + CLI both present
17
+ # -> stdout MUST contain `openspec archive my-feature`
18
+ # not-last-task — slug present, both signals present, but one task still in_progress
19
+ # -> stdout MUST NOT contain `openspec archive`
20
+ # no-slug — proposal description has no `OpenSpec change slug:` line
21
+ # -> stdout MUST NOT contain `openspec archive`
22
+ # no-cli — openspec/ folder present but `openspec` CLI not on PATH
23
+ # -> stdout MUST NOT contain `openspec archive`
24
+ # no-folder — CLI on PATH but no openspec/ directory in project root
25
+ # -> stdout MUST NOT contain `openspec archive`
26
+ # mode-off — CHORUS_OPENSPEC_MODE=off (explicit opt-out), even with both signals present
27
+ # -> stdout MUST NOT contain `openspec archive`
28
+ #
29
+ # All fixtures must end with exit 0.
30
+ #
31
+ # Bash 3.2 compatible. Run:
32
+ # /bin/bash public/chorus-plugin/bin/tests/test-on-post-verify-task.sh
33
+
34
+ set -euo pipefail
35
+
36
+ REPO_ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"
37
+ CLAUDE_HOOK="$REPO_ROOT/public/chorus-plugin/bin/on-post-verify-task.sh"
38
+ CODEX_HOOK="$REPO_ROOT/plugins/chorus/hooks/on-post-verify-task.sh"
39
+ CODEX_HOOK_OUTPUT="$REPO_ROOT/plugins/chorus/hooks/hook-output.sh"
40
+
41
+ [ -x "$CLAUDE_HOOK" ] || { echo "FAIL: $CLAUDE_HOOK is not executable" >&2; exit 1; }
42
+ [ -x "$CODEX_HOOK" ] || { echo "FAIL: $CODEX_HOOK is not executable" >&2; exit 1; }
43
+
44
+ PASS=0
45
+ FAIL=0
46
+ FAILED=""
47
+
48
+ # ----- Build sandbox -----
49
+
50
+ SANDBOX=$(mktemp -d)
51
+ cleanup() { rm -rf "$SANDBOX"; }
52
+ trap cleanup EXIT
53
+
54
+ # Sandbox layout:
55
+ # $SANDBOX/claude/ — copy of Claude hook + shim chorus-api.sh
56
+ # $SANDBOX/codex/ — copy of Codex hook + shim chorus-mcp-call.sh + real hook-output.sh
57
+ # $SANDBOX/bin/ — fake openspec CLI on PATH
58
+ # $SANDBOX/project/ — synthetic project root (contains openspec/ for fixtures
59
+ # that need the folder signal to be present)
60
+ mkdir -p "$SANDBOX/claude" "$SANDBOX/codex" "$SANDBOX/bin" "$SANDBOX/project/openspec"
61
+
62
+ cp "$CLAUDE_HOOK" "$SANDBOX/claude/on-post-verify-task.sh"
63
+ cp "$CODEX_HOOK" "$SANDBOX/codex/on-post-verify-task.sh"
64
+ cp "$CODEX_HOOK_OUTPUT" "$SANDBOX/codex/hook-output.sh"
65
+ chmod +x "$SANDBOX/claude/on-post-verify-task.sh" "$SANDBOX/codex/on-post-verify-task.sh"
66
+
67
+ # ----- Shim: chorus-api.sh (Claude Code shape) -----
68
+ # Subcommand interface:
69
+ # chorus-api.sh mcp-tool <tool> <json_args> -> echo canned JSON
70
+ # chorus-api.sh hook-output "" "$CONTEXT" "PostToolUse"
71
+ # We delegate hook-output to a real jq invocation so the emitted JSON
72
+ # matches the production wrapper's shape.
73
+ cat > "$SANDBOX/claude/chorus-api.sh" <<'SHIM_CLAUDE'
74
+ #!/usr/bin/env bash
75
+ set -euo pipefail
76
+ cmd="${1:-}"; shift || true
77
+ case "$cmd" in
78
+ mcp-tool)
79
+ tool="$1"
80
+ # CHORUS_FIXTURE picks the canned response set. Some fixtures
81
+ # (mode-off / no-folder / no-cli) test the OpenSpec gates that fire
82
+ # BEFORE any MCP call; we route them to the same canned data as
83
+ # "positive" so that if a gate is silently broken the hook reaches
84
+ # MCP, gets a positive result, and emits an archive reminder —
85
+ # failing the "no archive reminder" assertion. Without this aliasing
86
+ # the test would pass for the wrong reason (silent-skip at empty
87
+ # TASK_JSON instead of at the gate).
88
+ fixture="${CHORUS_FIXTURE:-positive}"
89
+ case "$fixture" in
90
+ mode-off|no-folder|no-cli) fixture=positive ;;
91
+ esac
92
+ case "${fixture}:${tool}" in
93
+ positive:chorus_get_task)
94
+ echo '{"uuid":"task-3","title":"Task 3","status":"done","proposalUuid":"prop-1","project":{"uuid":"proj-1","name":"P"}}'
95
+ ;;
96
+ positive:chorus_get_proposal)
97
+ printf '%s' '{"uuid":"prop-1","title":"P1","description":"Some intro line.\nOpenSpec change slug: my-feature\nMore body.","inputUuids":["idea-1"]}'
98
+ ;;
99
+ positive:chorus_list_tasks)
100
+ echo '{"tasks":[{"uuid":"task-1","status":"done"},{"uuid":"task-2","status":"done"},{"uuid":"task-3","status":"done"}],"total":3,"page":1,"pageSize":200}'
101
+ ;;
102
+ not-last-task:chorus_get_task)
103
+ echo '{"uuid":"task-2","title":"Task 2","status":"done","proposalUuid":"prop-1","project":{"uuid":"proj-1","name":"P"}}'
104
+ ;;
105
+ not-last-task:chorus_get_proposal)
106
+ printf '%s' '{"uuid":"prop-1","title":"P1","description":"Intro.\nOpenSpec change slug: my-feature\nMore.","inputUuids":["idea-1"]}'
107
+ ;;
108
+ not-last-task:chorus_list_tasks)
109
+ echo '{"tasks":[{"uuid":"task-1","status":"done"},{"uuid":"task-2","status":"done"},{"uuid":"task-3","status":"in_progress"}],"total":3,"page":1,"pageSize":200}'
110
+ ;;
111
+ no-slug:chorus_get_task)
112
+ echo '{"uuid":"task-3","title":"Task 3","status":"done","proposalUuid":"prop-2","project":{"uuid":"proj-1","name":"P"}}'
113
+ ;;
114
+ no-slug:chorus_get_proposal)
115
+ printf '%s' '{"uuid":"prop-2","title":"P2","description":"Free-form proposal description with no slug marker.","inputUuids":["idea-2"]}'
116
+ ;;
117
+ no-slug:chorus_list_tasks)
118
+ echo '{"tasks":[{"uuid":"task-1","status":"done"},{"uuid":"task-2","status":"done"},{"uuid":"task-3","status":"done"}],"total":3,"page":1,"pageSize":200}'
119
+ ;;
120
+ *)
121
+ # Unhandled (tool, fixture) — return empty, hook should silent-skip.
122
+ echo ""
123
+ ;;
124
+ esac
125
+ ;;
126
+ hook-output)
127
+ sm="${1:-}"; ac="${2:-}"; hen="${3:-}"
128
+ if [ -n "$ac" ]; then
129
+ jq -n --arg sm "$sm" --arg ac "$ac" --arg hen "$hen" \
130
+ '{systemMessage:$sm, hookSpecificOutput:{hookEventName:$hen, additionalContext:$ac}}'
131
+ else
132
+ jq -n --arg sm "$sm" '{systemMessage:$sm}'
133
+ fi
134
+ ;;
135
+ *)
136
+ echo "shim chorus-api.sh: unknown cmd $cmd" >&2; exit 2 ;;
137
+ esac
138
+ SHIM_CLAUDE
139
+ chmod +x "$SANDBOX/claude/chorus-api.sh"
140
+
141
+ # ----- Shim: chorus-mcp-call.sh (Codex shape) -----
142
+ # Codex wrapper interface:
143
+ # chorus-mcp-call.sh <tool> <json_args> -> echo canned JSON
144
+ cat > "$SANDBOX/codex/chorus-mcp-call.sh" <<'SHIM_CODEX'
145
+ #!/usr/bin/env bash
146
+ set -euo pipefail
147
+ tool="${1:-}"
148
+ # Alias gate-test fixtures to "positive" canned data so a broken gate
149
+ # would let the hook proceed and emit an archive reminder, failing the
150
+ # "no archive reminder" assertion. See claude shim for full rationale.
151
+ fixture="${CHORUS_FIXTURE:-positive}"
152
+ case "$fixture" in
153
+ mode-off|no-folder|no-cli) fixture=positive ;;
154
+ esac
155
+ case "${fixture}:${tool}" in
156
+ positive:chorus_get_task)
157
+ echo '{"uuid":"task-3","title":"Task 3","status":"done","proposalUuid":"prop-1","project":{"uuid":"proj-1","name":"P"}}'
158
+ ;;
159
+ positive:chorus_get_proposal)
160
+ printf '%s' '{"uuid":"prop-1","title":"P1","description":"Some intro line.\nOpenSpec change slug: my-feature\nMore body.","inputUuids":["idea-1"]}'
161
+ ;;
162
+ positive:chorus_list_tasks)
163
+ echo '{"tasks":[{"uuid":"task-1","status":"done"},{"uuid":"task-2","status":"done"},{"uuid":"task-3","status":"done"}],"total":3,"page":1,"pageSize":200}'
164
+ ;;
165
+ not-last-task:chorus_get_task)
166
+ echo '{"uuid":"task-2","title":"Task 2","status":"done","proposalUuid":"prop-1","project":{"uuid":"proj-1","name":"P"}}'
167
+ ;;
168
+ not-last-task:chorus_get_proposal)
169
+ printf '%s' '{"uuid":"prop-1","title":"P1","description":"Intro.\nOpenSpec change slug: my-feature\nMore.","inputUuids":["idea-1"]}'
170
+ ;;
171
+ not-last-task:chorus_list_tasks)
172
+ echo '{"tasks":[{"uuid":"task-1","status":"done"},{"uuid":"task-2","status":"done"},{"uuid":"task-3","status":"in_progress"}],"total":3,"page":1,"pageSize":200}'
173
+ ;;
174
+ no-slug:chorus_get_task)
175
+ echo '{"uuid":"task-3","title":"Task 3","status":"done","proposalUuid":"prop-2","project":{"uuid":"proj-1","name":"P"}}'
176
+ ;;
177
+ no-slug:chorus_get_proposal)
178
+ printf '%s' '{"uuid":"prop-2","title":"P2","description":"Free-form proposal description with no slug marker.","inputUuids":["idea-2"]}'
179
+ ;;
180
+ no-slug:chorus_list_tasks)
181
+ echo '{"tasks":[{"uuid":"task-1","status":"done"},{"uuid":"task-2","status":"done"},{"uuid":"task-3","status":"done"}],"total":3,"page":1,"pageSize":200}'
182
+ ;;
183
+ *)
184
+ echo "" ;;
185
+ esac
186
+ SHIM_CODEX
187
+ chmod +x "$SANDBOX/codex/chorus-mcp-call.sh"
188
+
189
+ # ----- Fake `openspec` on PATH -----
190
+ # Default behavior: `openspec --version` exits 0. The "no-openspec"
191
+ # fixture rebuilds PATH without this dir to simulate CLI absence.
192
+ cat > "$SANDBOX/bin/openspec" <<'OPENSPEC_FAKE'
193
+ #!/usr/bin/env bash
194
+ case "${1:-}" in
195
+ --version) echo "openspec 0.0.0-fixture"; exit 0 ;;
196
+ *) echo "openspec fixture: ignoring $*"; exit 0 ;;
197
+ esac
198
+ OPENSPEC_FAKE
199
+ chmod +x "$SANDBOX/bin/openspec"
200
+
201
+ # ----- Synthetic PostToolUse event JSON -----
202
+ EVENT_JSON='{"tool_name":"chorus_admin_verify_task","tool_input":{"taskUuid":"task-3"},"tool_response":{"uuid":"task-3","status":"done","title":"Task 3"}}'
203
+
204
+ # ----- Test runner -----
205
+
206
+ # Args: name fixture variant expected_substring should_contain
207
+ # variant in {claude, codex}
208
+ # should_contain: "yes" -> stdout MUST contain expected_substring
209
+ # should_contain: "no" -> stdout MUST NOT contain "openspec archive"
210
+ run_one() {
211
+ local name="$1"
212
+ local fixture="$2"
213
+ local variant="$3"
214
+ local should_contain="$4"
215
+ local expected="$5"
216
+
217
+ local hook_dir
218
+ if [ "$variant" = "claude" ]; then
219
+ hook_dir="$SANDBOX/claude"
220
+ else
221
+ hook_dir="$SANDBOX/codex"
222
+ fi
223
+
224
+ # Build PATH:
225
+ # - default fixtures get $SANDBOX/bin (where the fake openspec lives)
226
+ # prepended in front of the inherited PATH;
227
+ # - "no-cli" needs to actually have NO openspec on PATH. Just removing
228
+ # $SANDBOX/bin isn't enough — the dev machine often has a real
229
+ # openspec under ~/.nvm/.../bin or similar. So for no-cli we strip
230
+ # every PATH entry that contains an `openspec` executable.
231
+ local PATH_FOR_RUN
232
+ if [ "$fixture" = "no-cli" ]; then
233
+ local _filtered=""
234
+ local _entry
235
+ for _entry in $(printf '%s' "$PATH" | tr ':' '\n'); do
236
+ if [ -z "$_entry" ]; then continue; fi
237
+ if [ -x "$_entry/openspec" ]; then continue; fi
238
+ if [ -n "$_filtered" ]; then
239
+ _filtered="$_filtered:$_entry"
240
+ else
241
+ _filtered="$_entry"
242
+ fi
243
+ done
244
+ PATH_FOR_RUN="$_filtered"
245
+ else
246
+ PATH_FOR_RUN="$SANDBOX/bin:$PATH"
247
+ fi
248
+
249
+ # Pick the working directory + CLAUDE_PROJECT_DIR. The "no-folder"
250
+ # fixture points the hook at $SANDBOX (which contains no openspec/),
251
+ # everything else uses $SANDBOX/project (which has openspec/).
252
+ local PROJECT_DIR_FOR_RUN
253
+ if [ "$fixture" = "no-folder" ]; then
254
+ PROJECT_DIR_FOR_RUN="$SANDBOX"
255
+ else
256
+ PROJECT_DIR_FOR_RUN="$SANDBOX/project"
257
+ fi
258
+
259
+ # The "mode-off" fixture sets the explicit-opt-out env var; everything
260
+ # else leaves it unset.
261
+ local MODE_VAR_FOR_RUN=""
262
+ if [ "$fixture" = "mode-off" ]; then
263
+ MODE_VAR_FOR_RUN="off"
264
+ fi
265
+
266
+ local stdout_file
267
+ stdout_file=$(mktemp)
268
+ local stderr_file
269
+ stderr_file=$(mktemp)
270
+ local rc=0
271
+
272
+ printf '%s' "$EVENT_JSON" \
273
+ | env -i \
274
+ HOME="$HOME" \
275
+ CHORUS_FIXTURE="$fixture" \
276
+ PATH="$PATH_FOR_RUN" \
277
+ CLAUDE_PROJECT_DIR="$PROJECT_DIR_FOR_RUN" \
278
+ CHORUS_OPENSPEC_MODE="$MODE_VAR_FOR_RUN" \
279
+ /bin/bash -c "cd \"$PROJECT_DIR_FOR_RUN\" && /bin/bash \"$hook_dir/on-post-verify-task.sh\"" \
280
+ >"$stdout_file" 2>"$stderr_file" || rc=$?
281
+
282
+ if [ "$rc" -ne 0 ]; then
283
+ echo " FAIL $name [$variant] exit=$rc"
284
+ sed 's/^/ /' "$stderr_file"
285
+ FAIL=$((FAIL + 1))
286
+ FAILED="$FAILED $name[$variant]"
287
+ rm -f "$stdout_file" "$stderr_file"
288
+ return
289
+ fi
290
+
291
+ local stdout
292
+ stdout=$(cat "$stdout_file")
293
+ rm -f "$stdout_file" "$stderr_file"
294
+
295
+ if [ "$should_contain" = "yes" ]; then
296
+ if printf '%s' "$stdout" | grep -q "$expected"; then
297
+ echo " PASS $name [$variant]"
298
+ PASS=$((PASS + 1))
299
+ else
300
+ echo " FAIL $name [$variant]: stdout missing '$expected'"
301
+ printf '%s\n' "$stdout" | sed 's/^/ /'
302
+ FAIL=$((FAIL + 1))
303
+ FAILED="$FAILED $name[$variant]"
304
+ fi
305
+ else
306
+ # should_contain == "no": archive reminder MUST be absent.
307
+ if printf '%s' "$stdout" | grep -q "openspec archive"; then
308
+ echo " FAIL $name [$variant]: stdout unexpectedly contains 'openspec archive'"
309
+ printf '%s\n' "$stdout" | sed 's/^/ /'
310
+ FAIL=$((FAIL + 1))
311
+ FAILED="$FAILED $name[$variant]"
312
+ else
313
+ echo " PASS $name [$variant]"
314
+ PASS=$((PASS + 1))
315
+ fi
316
+ fi
317
+ }
318
+
319
+ echo "Sandbox: $SANDBOX"
320
+ echo ""
321
+
322
+ # ----- Branch 1: positive (slug + last task + CLI) -----
323
+ run_one "positive" "positive" "claude" "yes" "openspec archive my-feature"
324
+ run_one "positive" "positive" "codex" "yes" "openspec archive my-feature"
325
+
326
+ # ----- Branch 2: not-last-task -----
327
+ run_one "not-last-task" "not-last-task" "claude" "no" ""
328
+ run_one "not-last-task" "not-last-task" "codex" "no" ""
329
+
330
+ # ----- Branch 3: no-slug (free-form proposal) -----
331
+ run_one "no-slug" "no-slug" "claude" "no" ""
332
+ run_one "no-slug" "no-slug" "codex" "no" ""
333
+
334
+ # ----- Branch 4: no-cli (folder present, CLI absent) -----
335
+ # Strip $SANDBOX/bin from PATH so `command -v openspec` fails. Folder is
336
+ # still there in $SANDBOX/project/openspec. Hook must short-circuit at
337
+ # step 4 (CLI gate) and emit no archive reminder.
338
+ run_one "no-cli" "no-cli" "claude" "no" ""
339
+ run_one "no-cli" "no-cli" "codex" "no" ""
340
+
341
+ # ----- Branch 5: no-folder (CLI present, folder absent) -----
342
+ # CLAUDE_PROJECT_DIR points at $SANDBOX (no openspec/ dir there). Hook
343
+ # must short-circuit at step 4 (folder gate) before trying any MCP calls.
344
+ # We don't actually exercise any MCP fixture here — the hook should bail
345
+ # before reaching the wrappers. We pass "positive" only because the
346
+ # unhandled-fixture branch in the shim returns an empty body and the hook
347
+ # would then silent-skip on get_task. Either way the assertion ("no
348
+ # archive reminder") holds.
349
+ run_one "no-folder" "no-folder" "claude" "no" ""
350
+ run_one "no-folder" "no-folder" "codex" "no" ""
351
+
352
+ # ----- Branch 6: mode-off (explicit opt-out via env) -----
353
+ # Both folder and CLI are present, but CHORUS_OPENSPEC_MODE=off. Hook
354
+ # must honor the opt-out and exit silently.
355
+ run_one "mode-off" "mode-off" "claude" "no" ""
356
+ run_one "mode-off" "mode-off" "codex" "no" ""
357
+
358
+ echo ""
359
+ echo "Results: $PASS passed, $FAIL failed"
360
+
361
+ if [ "$FAIL" -gt 0 ]; then
362
+ echo "Failed:$FAILED"
363
+ exit 1
364
+ fi
365
+ exit 0
@@ -39,6 +39,15 @@
39
39
  "command": "${CLAUDE_PLUGIN_ROOT}/bin/on-post-submit-for-verify.sh"
40
40
  }
41
41
  ]
42
+ },
43
+ {
44
+ "matcher": ".*chorus_admin_verify_task",
45
+ "hooks": [
46
+ {
47
+ "type": "command",
48
+ "command": "${CLAUDE_PLUGIN_ROOT}/bin/on-post-verify-task.sh"
49
+ }
50
+ ]
42
51
  }
43
52
  ],
44
53
  "PreToolUse": [