@chorus-aidlc/chorus 0.7.1 → 0.8.1
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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +251 -251
- package/.next/standalone/.next/app-path-routes-manifest.json +42 -42
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +48 -48
- package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/ideas/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +6 -6
- package/.next/standalone/.next/server/app/admin/companies/[uuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin/companies/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin/companies/new.html +1 -1
- package/.next/standalone/.next/server/app/admin/companies/new.rsc +6 -6
- package/.next/standalone/.next/server/app/admin/companies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin/companies.html +1 -1
- package/.next/standalone/.next/server/app/admin/companies.rsc +6 -6
- package/.next/standalone/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin.html +1 -1
- package/.next/standalone/.next/server/app/admin.rsc +6 -6
- package/.next/standalone/.next/server/app/api/admin/companies/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/companies/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/login/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/session/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/api-keys/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/api-keys/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/callback/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/check-default/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/company-oidc/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/default-login/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/identify/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/refresh/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/session/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/sync-token/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/comments/route.js +1 -1
- package/.next/standalone/.next/server/app/api/comments/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/documents/[uuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/documents/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/events/notifications/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/mcp/route.js +5 -5
- package/.next/standalone/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/me/assignments/route.js +1 -1
- package/.next/standalone/.next/server/app/api/me/assignments/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/me/assignments/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/mentionables/route.js +1 -1
- package/.next/standalone/.next/server/app/api/mentionables/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/preferences/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/read-all/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/unread-count/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-groups/[uuid]/dashboard/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-groups/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-groups/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/activity/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/group/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +6 -6
- package/.next/standalone/.next/server/app/login/admin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/admin.html +1 -1
- package/.next/standalone/.next/server/app/login/admin.rsc +6 -6
- package/.next/standalone/.next/server/app/login/callback/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/callback.html +1 -1
- package/.next/standalone/.next/server/app/login/callback.rsc +6 -6
- package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/pick-workspace/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/pick-workspace.html +1 -1
- package/.next/standalone/.next/server/app/login/pick-workspace.rsc +6 -6
- package/.next/standalone/.next/server/app/login/silent-refresh/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/silent-refresh.html +1 -1
- package/.next/standalone/.next/server/app/login/silent-refresh.rsc +6 -6
- package/.next/standalone/.next/server/app/login.html +1 -1
- package/.next/standalone/.next/server/app/login.rsc +6 -6
- package/.next/standalone/.next/server/app/onboarding/page.js +2 -2
- package/.next/standalone/.next/server/app/onboarding/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/onboarding.html +1 -1
- package/.next/standalone/.next/server/app/onboarding.rsc +7 -7
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects.html +1 -1
- package/.next/standalone/.next/server/app/projects.rsc +8 -8
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +8 -8
- package/.next/standalone/.next/server/app-paths-manifest.json +42 -42
- package/.next/standalone/.next/server/chunks/1002.js +1 -1
- package/.next/standalone/.next/server/chunks/1596.js +2 -2
- package/.next/standalone/.next/server/chunks/1673.js +1 -0
- package/.next/standalone/.next/server/chunks/1871.js +1 -1
- package/.next/standalone/.next/server/chunks/2570.js +1 -1
- package/.next/standalone/.next/server/chunks/2618.js +1 -1
- package/.next/standalone/.next/server/chunks/{2675.js → 3139.js} +1 -1
- package/.next/standalone/.next/server/chunks/3405.js +1 -0
- package/.next/standalone/.next/server/chunks/4294.js +1 -0
- package/.next/standalone/.next/server/chunks/5044.js +1 -0
- package/.next/standalone/.next/server/chunks/6207.js +1 -1
- package/.next/standalone/.next/server/chunks/6320.js +2 -2
- package/.next/standalone/.next/server/chunks/6836.js +1 -0
- package/.next/standalone/.next/server/chunks/8322.js +2 -2
- package/.next/standalone/.next/server/chunks/8844.js +1 -1
- package/.next/standalone/.next/server/chunks/8881.js +1 -1
- package/.next/standalone/.next/server/chunks/937.js +2 -2
- package/.next/standalone/.next/server/chunks/9508.js +2 -2
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/next-font-manifest.js +1 -1
- package/.next/standalone/.next/server/next-font-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/10739-a14d0dc7271f247e.js +1 -0
- package/.next/standalone/.next/static/chunks/19126-f1bc396538752bce.js +1 -0
- package/.next/standalone/.next/static/chunks/{53664-a2be2eaacce03f0f.js → 23588-66d80958224a8544.js} +1 -1
- package/.next/standalone/.next/static/chunks/32190-a110356fb68a4333.js +1 -0
- package/.next/standalone/.next/static/chunks/65371-d3686d82abca6495.js +1 -0
- package/.next/standalone/.next/static/chunks/70317-49653c6dce23f1d9.js +1 -0
- package/.next/standalone/.next/static/chunks/8731-7869a6ed671c63a3.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-719f0815a37f19be.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-bc8c3c0362de377c.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-c1c4b5798c494341.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/page-29ac4172940780e0.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/ideas/[ideaUuid]/{page-2d820769aa7bcaf6.js → page-e59232665c3a0412.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/ideas/{page-dec0e71221d77928.js → page-9b1e02b5e2cfb410.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-567e98e9e90a7ef1.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/new/{page-bc66567cb9c40bf0.js → page-31f4ffd9778a39ed.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/{page-86cfeb421fa97830.js → page-8eded17e1e44522d.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/{page-7fe70579023330da.js → page-e5c99f2020b904f5.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/{page-8c51993bb3866a55.js → page-7947010e9684fbcf.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/{page-80566dcc1cd42bee.js → page-5326f2b34b200e50.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/page-a01b45dbe80aeecf.js +1 -0
- package/.next/standalone/.next/static/chunks/app/{layout-22f1a931323bbb60.js → layout-482dee7dace72c88.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/onboarding/{page-8d755192f0f67938.js → page-ac0593c045c64013.js} +1 -1
- package/.next/standalone/.next/static/css/84edd78de243cde6.css +1 -0
- package/.next/standalone/.next/static/css/{ca9796ac54f96058.css → a577d38ad6d7491b.css} +1 -1
- package/.next/standalone/.next/static/media/0f1bdadaf30e2d5f-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/22a5144ee8d83bca-s.p.woff2 +0 -0
- package/.next/standalone/.next/static/media/2c34d62a75506231-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/601f5c280d60caca-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/9766a7e9e2e0ad5a-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/a115172161b307bb-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/aa016aab0e6d1295-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/b66cf8e69499582a-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/d100b2a099e34044-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/f5271587012faf78-s.p.woff2 +0 -0
- package/.next/standalone/.next/static/media/f639721981034f88-s.woff2 +0 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/public/chorus-plugin/.claude-plugin/plugin.json +7 -1
- package/.next/standalone/public/chorus-plugin/agents/proposal-reviewer.md +1 -1
- package/.next/standalone/public/chorus-plugin/agents/task-reviewer.md +1 -1
- package/.next/standalone/public/chorus-plugin/bin/on-post-verify-task.sh +190 -0
- package/.next/standalone/public/chorus-plugin/bin/on-session-start.sh +58 -0
- package/.next/standalone/public/chorus-plugin/bin/test-syntax.sh +1 -0
- package/.next/standalone/public/chorus-plugin/bin/tests/test-on-post-verify-task.sh +365 -0
- package/.next/standalone/public/chorus-plugin/hooks/hooks.json +9 -0
- package/.next/standalone/public/chorus-plugin/skills/chorus/SKILL.md +2 -1
- package/.next/standalone/public/chorus-plugin/skills/develop/SKILL.md +9 -1
- package/.next/standalone/public/chorus-plugin/skills/idea/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/openspec-aware/SKILL.md +436 -0
- package/.next/standalone/public/chorus-plugin/skills/proposal/SKILL.md +11 -1
- package/.next/standalone/public/chorus-plugin/skills/quick-dev/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/review/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/yolo/SKILL.md +26 -4
- package/README.md +34 -7
- package/README.zh.md +15 -8
- package/chorus.mjs +56 -12
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/1639.js +0 -1
- package/.next/standalone/.next/server/chunks/3300.js +0 -1
- package/.next/standalone/.next/server/chunks/8251.js +0 -1
- package/.next/standalone/.next/server/chunks/8584.js +0 -1
- package/.next/standalone/.next/server/chunks/9051.js +0 -1
- package/.next/standalone/.next/static/chunks/10739-5c185aa0432f5d56.js +0 -1
- package/.next/standalone/.next/static/chunks/19126-858a24cab6fd338a.js +0 -1
- package/.next/standalone/.next/static/chunks/29445-04c4ab36da85b7da.js +0 -1
- package/.next/standalone/.next/static/chunks/39611-cc227784ed679a6d.js +0 -1
- package/.next/standalone/.next/static/chunks/96919-8cbf6310c703e7af.js +0 -1
- package/.next/standalone/.next/static/chunks/99513-24849ff68a8098e1.js +0 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-acddb9c84f5d1607.js +0 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-b4c6141dd26cc506.js +0 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-4bf7bff8d68509f9.js +0 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/page-37a0cea0ba40c31b.js +0 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-355321010dd82e56.js +0 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/page-8db25385236a9bbf.js +0 -1
- package/.next/standalone/.next/static/css/de70bee13400563f.css +0 -1
- package/.next/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/.next/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/.next/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/.next/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- /package/.next/standalone/.next/static/{TJ6qFSxjQGUSajvwbI2qu → bLmYoXAXta69K0xmICa8D}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{TJ6qFSxjQGUSajvwbI2qu → bLmYoXAXta69K0xmICa8D}/_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": [
|