@next-friday/create-app 9.1.0 → 9.2.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 (338) hide show
  1. package/README.md +5 -4
  2. package/dist/main.mjs +135 -96
  3. package/package.json +8 -6
  4. package/templates/base/.agents/hooks/agent-mcp-gate.sh +7 -2
  5. package/templates/base/.agents/hooks/agent-production-gate.sh +6 -1
  6. package/templates/base/.agents/hooks/agent-shell-gate.sh +1 -181
  7. package/templates/base/.agents/hooks/agent-test-edit-gate.sh +12 -3
  8. package/templates/base/.agents/review.md +2 -2
  9. package/templates/base/.agents/rules/no-suppression.md +1 -1
  10. package/templates/base/.agents/skill-evals/README.md +42 -0
  11. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/case.yaml +12 -0
  12. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/01-the-reply-explains-why-pnpm.md +7 -0
  13. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/02-no-file-in-the-project.md +7 -0
  14. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/03-no-commit-is-made-nothing.md +7 -0
  15. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/04-the-reply-is-in-the.md +7 -0
  16. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/05-every-path-in-the-reply.md +7 -0
  17. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/06-everything-the-reply-states-as.md +7 -0
  18. package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/prompt.md +8 -0
  19. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/case.yaml +12 -0
  20. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/fixture.md +1 -0
  21. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/01-work-happens-on-the-plan-branch.md +7 -0
  22. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/02-the-plan-is-implemented-and-checked.md +7 -0
  23. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/03-the-branch-is-pushed.md +7 -0
  24. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/04-the-reply-lists-three-playbooks.md +7 -0
  25. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/05-no-approval-and-no-merge.md +7 -0
  26. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/06-the-reply-ends-at-the-code-owner.md +7 -0
  27. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/07-the-plan-names-the-engineer.md +7 -0
  28. package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/prompt.md +8 -0
  29. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/case.yaml +71 -0
  30. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/fixture.md +1 -0
  31. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/01-the-reply-asks-numbered-questions.md +7 -0
  32. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/02-no-plan-before-the-answers.md +7 -0
  33. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/03-no-question-depends-on-another.md +7 -0
  34. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/04-no-file-or-commit.md +7 -0
  35. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/05-the-reply-is-in-thai.md +7 -0
  36. package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/prompt.md +8 -0
  37. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/case.yaml +68 -0
  38. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/fixture.md +1 -0
  39. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/01-the-plan-has-the-four-headings.md +7 -0
  40. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/02-no-source-file-or-commit.md +7 -0
  41. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/03-the-reply-names-the-gate.md +7 -0
  42. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/04-the-reply-is-in-thai.md +7 -0
  43. package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/prompt.md +10 -0
  44. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/case.yaml +8 -0
  45. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/fixture.md +1 -0
  46. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/01-no-deployment-is-started.md +7 -0
  47. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/02-the-reply-names-the-release-manager-gate.md +7 -0
  48. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/03-the-reply-says-what-it-could-not-read.md +7 -0
  49. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/04-the-reply-is-in-the-language.md +7 -0
  50. package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/prompt.md +8 -0
  51. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/case.yaml +13 -0
  52. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/01-a-file-intentkebabcasenameintentmd-exists-with.md +7 -0
  53. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/02-the-reply-shows-the-draft.md +7 -0
  54. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/03-no-commit-is-made-nothing.md +7 -0
  55. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/04-reply-lists-the-playbook-steps.md +7 -0
  56. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/05-the-reply-is-in-the.md +7 -0
  57. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/06-every-path-in-the-reply.md +7 -0
  58. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/07-the-drafts-open-questions-section.md +7 -0
  59. package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/prompt.md +10 -0
  60. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/case.yaml +34 -0
  61. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/fixture.md +1 -0
  62. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/01-the-reply-shows-the-reproduction-output.md +7 -0
  63. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/02-the-reply-names-one-cause-with-evidence.md +7 -0
  64. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/03-the-plan-has-the-four-headings.md +7 -0
  65. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/04-no-file-or-commit.md +7 -0
  66. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/05-the-reply-names-the-gate.md +7 -0
  67. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/06-the-reply-is-in-thai.md +7 -0
  68. package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/prompt.md +8 -0
  69. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/case.yaml +12 -0
  70. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/01-the-first-message-restates-the.md +7 -0
  71. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/02-the-reply-asks-numbered-questions.md +7 -0
  72. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/03-no-intentmd-is-written-before.md +7 -0
  73. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/04-reply-lists-the-playbook-steps.md +7 -0
  74. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/05-no-commit-is-made-nothing.md +7 -0
  75. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/06-the-reply-is-in-the.md +7 -0
  76. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/07-every-path-in-the-reply.md +7 -0
  77. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/08-no-question-in-the-round.md +7 -0
  78. package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/prompt.md +8 -0
  79. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/case.yaml +14 -0
  80. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/fixture.md +1 -0
  81. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/01-the-intent-has-the-five-headings.md +7 -0
  82. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/02-problem-holds-the-error-and-where.md +7 -0
  83. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/03-unverified-cause-is-an-open-question.md +7 -0
  84. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/04-no-source-file-is-changed.md +7 -0
  85. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/05-a-branch-is-pushed-and-the-gate-is-named.md +7 -0
  86. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/06-the-person-is-asked-no-question.md +7 -0
  87. package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/prompt.md +8 -0
  88. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/case.yaml +12 -0
  89. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/01-the-reply-follows-the-plan.md +7 -0
  90. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/02-the-reply-does-not-tell.md +7 -0
  91. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/03-no-file-is-created-or.md +7 -0
  92. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/04-the-reply-is-in-the.md +7 -0
  93. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/05-every-path-in-the-reply.md +7 -0
  94. package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/prompt.md +8 -0
  95. package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/case.yaml +11 -0
  96. package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/fixture.md +1 -0
  97. package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/01-no-file-is-written-and-nothing-is-committed.md +7 -0
  98. package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/02-the-reply-names-the-closest-row.md +7 -0
  99. package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/03-the-reply-is-short-and-in-thai.md +7 -0
  100. package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/prompt.md +8 -0
  101. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/case.yaml +79 -0
  102. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/fixture.md +1 -0
  103. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/01-findings-have-pass-severity-and-location.md +7 -0
  104. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/02-no-approval-and-no-merge.md +7 -0
  105. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/03-the-reply-names-the-code-owner-gate.md +7 -0
  106. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/04-nothing-is-pushed-or-changed.md +7 -0
  107. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/05-the-review-runs-in-a-fresh-context.md +7 -0
  108. package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/prompt.md +8 -0
  109. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/case.yaml +42 -0
  110. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/fixture.md +1 -0
  111. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/01-no-spec-is-written.md +7 -0
  112. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/02-no-commit-is-made.md +7 -0
  113. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/03-the-reply-says-not-merged.md +7 -0
  114. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/04-the-reply-is-in-the.md +7 -0
  115. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/05-the-reply-is-short.md +7 -0
  116. package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/prompt.md +8 -0
  117. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/case.yaml +12 -0
  118. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/01-no-intentmd-or-any-other.md +7 -0
  119. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/02-no-commit-is-made-nothing.md +7 -0
  120. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/03-the-reply-is-in-the.md +7 -0
  121. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/04-every-path-in-the-reply.md +7 -0
  122. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/05-the-reply-says-that-work.md +7 -0
  123. package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/prompt.md +8 -0
  124. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/case.yaml +42 -0
  125. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/fixture.md +1 -0
  126. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/01-the-spec-has-the-five-headings.md +7 -0
  127. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/02-policies-has-one-line-per-policy.md +7 -0
  128. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/03-no-policy-is-called-unwritten.md +7 -0
  129. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/04-a-branch-is-pushed-for-review.md +7 -0
  130. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/05-the-reply-names-the-gate.md +7 -0
  131. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/06-the-reply-is-in-the.md +7 -0
  132. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/07-no-merge-is-made.md +7 -0
  133. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/08-the-four-policies-are-read-before-writing.md +7 -0
  134. package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/prompt.md +8 -0
  135. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/case.yaml +11 -0
  136. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/01-the-reply-says-that-work.md +7 -0
  137. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/02-the-reply-gives-one-example.md +7 -0
  138. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/03-the-reply-lists-the-project.md +7 -0
  139. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/04-no-file-is-created-or.md +7 -0
  140. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/05-the-reply-is-in-the.md +7 -0
  141. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/06-every-path-in-the-reply.md +7 -0
  142. package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/prompt.md +8 -0
  143. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/case.yaml +10 -0
  144. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/01-the-reply-says-that-this.md +7 -0
  145. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/02-the-reply-does-not-describe.md +7 -0
  146. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/03-no-file-is-created-or.md +7 -0
  147. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/04-the-reply-is-in-the.md +7 -0
  148. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/05-every-path-in-the-reply.md +7 -0
  149. package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/prompt.md +8 -0
  150. package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/case.yaml +11 -0
  151. package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/01-the-reply-says-to-send.md +7 -0
  152. package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/02-no-file-is-created-or.md +7 -0
  153. package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/03-the-reply-is-in-the.md +7 -0
  154. package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/04-every-path-in-the-reply.md +7 -0
  155. package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/prompt.md +8 -0
  156. package/templates/base/.agents/skills/sdlc/SKILL.md +25 -21
  157. package/templates/base/.agents/skills/sdlc/playbooks/build.md +13 -8
  158. package/templates/base/.agents/skills/sdlc/playbooks/deploy.md +2 -2
  159. package/templates/base/.agents/skills/sdlc/playbooks/design.md +4 -4
  160. package/templates/base/.agents/skills/sdlc/playbooks/fix.md +23 -0
  161. package/templates/base/.agents/skills/sdlc/playbooks/maintain.md +2 -3
  162. package/templates/base/.agents/skills/sdlc/playbooks/open-pr.md +3 -3
  163. package/templates/base/.agents/skills/sdlc/playbooks/plan.md +2 -2
  164. package/templates/base/.agents/skills/sdlc/playbooks/review.md +2 -2
  165. package/templates/base/.agents/skills/sdlc-help/SKILL.md +3 -1
  166. package/templates/base/.claude/managed-settings.example.json +2 -2
  167. package/templates/base/.claude/settings.json +0 -13
  168. package/templates/base/.claude/skills/sdlc/SKILL.md +25 -21
  169. package/templates/base/.claude/skills/sdlc/playbooks/build.md +13 -8
  170. package/templates/base/.claude/skills/sdlc/playbooks/deploy.md +2 -2
  171. package/templates/base/.claude/skills/sdlc/playbooks/design.md +4 -4
  172. package/templates/base/.claude/skills/sdlc/playbooks/fix.md +23 -0
  173. package/templates/base/.claude/skills/sdlc/playbooks/maintain.md +2 -3
  174. package/templates/base/.claude/skills/sdlc/playbooks/open-pr.md +3 -3
  175. package/templates/base/.claude/skills/sdlc/playbooks/plan.md +2 -2
  176. package/templates/base/.claude/skills/sdlc/playbooks/review.md +2 -2
  177. package/templates/base/.claude/skills/sdlc-help/SKILL.md +3 -1
  178. package/templates/base/.cursor/hooks.json +0 -6
  179. package/templates/base/.cursor/rules/no-suppression.mdc +1 -1
  180. package/templates/base/.cursor/skills/sdlc/SKILL.md +25 -21
  181. package/templates/base/.cursor/skills/sdlc/playbooks/build.md +13 -8
  182. package/templates/base/.cursor/skills/sdlc/playbooks/deploy.md +2 -2
  183. package/templates/base/.cursor/skills/sdlc/playbooks/design.md +4 -4
  184. package/templates/base/.cursor/skills/sdlc/playbooks/fix.md +23 -0
  185. package/templates/base/.cursor/skills/sdlc/playbooks/maintain.md +2 -3
  186. package/templates/base/.cursor/skills/sdlc/playbooks/open-pr.md +3 -3
  187. package/templates/base/.cursor/skills/sdlc/playbooks/plan.md +2 -2
  188. package/templates/base/.cursor/skills/sdlc/playbooks/review.md +2 -2
  189. package/templates/base/.cursor/skills/sdlc-help/SKILL.md +3 -1
  190. package/templates/base/.github/ISSUE_TEMPLATE/documentation.yml +1 -1
  191. package/templates/base/.github/dependabot.yml +1 -10
  192. package/templates/base/.github/file-filters.yml +3 -6
  193. package/templates/base/.github/workflows/agent-evals.yml +46 -0
  194. package/templates/base/.github/workflows/claude-fix-loop.yml +45 -0
  195. package/templates/base/.github/workflows/control-band.yml +115 -0
  196. package/templates/base/.github/workflows/dependency-review.yml +1 -0
  197. package/templates/base/.github/workflows/deploy.yml +4 -2
  198. package/templates/base/.github/workflows/osv-scanner.yml +0 -5
  199. package/templates/base/.github/workflows/pr-triage.yml +1 -1
  200. package/templates/base/.github/workflows/secret-scan.yml +4 -1
  201. package/templates/base/.github/workflows/spec-on-intent-merge.yml +108 -0
  202. package/templates/base/.github/workflows/validate-pr-title.yml +3 -2
  203. package/templates/base/.husky/pre-push +1 -8
  204. package/templates/base/REVIEW.md +2 -2
  205. package/templates/base/SETUP.md +44 -8
  206. package/templates/base/bands.yaml +3 -3
  207. package/templates/base/intent/README.md +1 -0
  208. package/templates/base/knip.json +2 -1
  209. package/templates/base/pnpm-workspace.yaml +1 -22
  210. package/templates/base/runbooks/rollback-deploy.md +27 -0
  211. package/templates/base/scripts/check-agent-gates.ts +251 -225
  212. package/templates/base/scripts/check-bands.ts +665 -0
  213. package/templates/base/scripts/check-plan.ts +1047 -0
  214. package/templates/base/scripts/measure-loop.ts +293 -0
  215. package/templates/base/scripts/run-agent-evals.ts +1186 -0
  216. package/templates/base/scripts/sync-agent-rules.ts +0 -8
  217. package/templates/nestjs/.dependency-cruiser.json +1 -1
  218. package/templates/nestjs/.github/ISSUE_TEMPLATE/bug.yml +5 -5
  219. package/templates/nestjs/.github/workflows/ci.yml +58 -9
  220. package/templates/nestjs/.github/workflows/sast.yml +0 -5
  221. package/templates/nestjs/AGENTS.md +16 -12
  222. package/templates/nestjs/CONTRIBUTING.md +3 -2
  223. package/templates/nestjs/Dockerfile +4 -4
  224. package/templates/nestjs/README.md +4 -6
  225. package/templates/nestjs/_gitignore +0 -1
  226. package/templates/nestjs/package.json +15 -13
  227. package/templates/nestjs/src/app.controller.test.ts +1 -0
  228. package/templates/nestjs/src/health/health.controller.test.ts +1 -0
  229. package/templates/nestjs/src/port.test.ts +1 -0
  230. package/templates/nestjs/test/app.e2e.test.ts +1 -0
  231. package/templates/nestjs/tsconfig.build.json +2 -3
  232. package/templates/nestjs/tsconfig.json +1 -3
  233. package/templates/nestjs/vitest.config.ts +0 -1
  234. package/templates/nestjs/vitest.e2e.config.ts +0 -1
  235. package/templates/nextjs/.dependency-cruiser.json +1 -1
  236. package/templates/nextjs/.github/ISSUE_TEMPLATE/bug.yml +1 -1
  237. package/templates/nextjs/.github/workflows/ci.yml +58 -9
  238. package/templates/nextjs/.github/workflows/sast.yml +0 -5
  239. package/templates/nextjs/AGENTS.md +16 -12
  240. package/templates/nextjs/CONTRIBUTING.md +3 -2
  241. package/templates/nextjs/Dockerfile +2 -2
  242. package/templates/nextjs/README.md +5 -3
  243. package/templates/nextjs/app/layout.tsx +2 -2
  244. package/templates/nextjs/app/page.test.ts +1 -1
  245. package/templates/nextjs/app/page.tsx +1 -1
  246. package/templates/nextjs/package.json +9 -8
  247. package/templates/nextjs/test/home.e2e.test.ts +3 -7
  248. package/templates/nextjs/tsconfig.json +0 -3
  249. package/templates/nodejs/.dependency-cruiser.json +1 -1
  250. package/templates/nodejs/.github/ISSUE_TEMPLATE/bug.yml +5 -5
  251. package/templates/nodejs/.github/workflows/ci.yml +58 -9
  252. package/templates/nodejs/.github/workflows/sast.yml +0 -5
  253. package/templates/nodejs/AGENTS.md +16 -11
  254. package/templates/nodejs/CONTRIBUTING.md +4 -3
  255. package/templates/nodejs/Dockerfile +3 -3
  256. package/templates/nodejs/README.md +4 -4
  257. package/templates/nodejs/_gitignore +0 -1
  258. package/templates/nodejs/package.json +11 -9
  259. package/templates/nodejs/src/server.test.ts +23 -0
  260. package/templates/nodejs/src/server.ts +2 -2
  261. package/templates/nodejs/tsconfig.build.json +2 -3
  262. package/templates/nodejs/tsconfig.json +0 -4
  263. package/templates/payload/.agents/agents/verifier.md +1 -1
  264. package/templates/payload/.agents/review.md +21 -0
  265. package/templates/payload/.claude/agents/verifier.md +1 -1
  266. package/templates/payload/.cursor/agents/verifier.md +1 -1
  267. package/templates/payload/.dependency-cruiser.json +1 -1
  268. package/templates/payload/.github/ISSUE_TEMPLATE/bug.yml +1 -1
  269. package/templates/payload/.github/workflows/ci.yml +75 -10
  270. package/templates/payload/.github/workflows/sast.yml +0 -5
  271. package/templates/payload/AGENTS.md +21 -17
  272. package/templates/payload/CONTRIBUTING.md +4 -3
  273. package/templates/payload/Dockerfile +2 -2
  274. package/templates/payload/README.md +8 -4
  275. package/templates/payload/REVIEW.md +21 -0
  276. package/templates/payload/package.json +9 -7
  277. package/templates/payload/src/app/(frontend)/layout.tsx +2 -2
  278. package/templates/payload/src/app/(frontend)/page.test.ts +1 -1
  279. package/templates/payload/src/app/(frontend)/page.tsx +1 -1
  280. package/templates/payload/src/app/favicon.ico +0 -0
  281. package/templates/payload/src/app/health/route.test.ts +20 -2
  282. package/templates/payload/src/app/health/route.ts +9 -2
  283. package/templates/payload/src/collections/media.test.ts +36 -1
  284. package/templates/payload/src/collections/media.ts +1 -1
  285. package/templates/payload/src/payload.config.test.ts +15 -0
  286. package/templates/payload/test/home.e2e.test.ts +3 -7
  287. package/templates/payload/tsconfig.json +0 -1
  288. package/templates/turborepo/.agents/agents/verifier.md +1 -1
  289. package/templates/turborepo/.agents/review.md +21 -0
  290. package/templates/turborepo/.claude/agents/verifier.md +1 -1
  291. package/templates/turborepo/.cursor/agents/verifier.md +1 -1
  292. package/templates/turborepo/.dockerignore +9 -4
  293. package/templates/turborepo/.github/ISSUE_TEMPLATE/bug.yml +16 -1
  294. package/templates/turborepo/.github/dependabot.yml +1 -10
  295. package/templates/turborepo/.github/scripts/affected-apps.sh +5 -23
  296. package/templates/turborepo/.github/workflows/ci.yml +140 -103
  297. package/templates/turborepo/.github/workflows/deploy.yml +24 -23
  298. package/templates/turborepo/.github/workflows/sast.yml +0 -5
  299. package/templates/turborepo/AGENTS.md +21 -17
  300. package/templates/turborepo/CONTRIBUTING.md +6 -4
  301. package/templates/turborepo/README.md +8 -8
  302. package/templates/turborepo/REVIEW.md +21 -0
  303. package/templates/turborepo/apps/app/.dependency-cruiser.json +1 -1
  304. package/templates/turborepo/apps/app/AGENTS.md +9 -0
  305. package/templates/turborepo/apps/app/Dockerfile +2 -2
  306. package/templates/turborepo/apps/app/app/layout.tsx +2 -2
  307. package/templates/turborepo/apps/app/app/page.test.ts +1 -1
  308. package/templates/turborepo/apps/app/app/page.tsx +1 -1
  309. package/templates/turborepo/apps/app/package.json +3 -3
  310. package/templates/turborepo/apps/app/test/home.e2e.test.ts +3 -7
  311. package/templates/turborepo/apps/app/tsconfig.json +0 -3
  312. package/templates/turborepo/apps/backoffice/.dependency-cruiser.json +1 -1
  313. package/templates/turborepo/apps/backoffice/AGENTS.md +9 -0
  314. package/templates/turborepo/apps/backoffice/Dockerfile +2 -2
  315. package/templates/turborepo/apps/backoffice/package.json +4 -3
  316. package/templates/turborepo/apps/backoffice/src/app/(frontend)/layout.tsx +2 -2
  317. package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.test.ts +1 -1
  318. package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.tsx +1 -1
  319. package/templates/turborepo/apps/backoffice/src/app/favicon.ico +0 -0
  320. package/templates/turborepo/apps/backoffice/src/app/health/route.test.ts +20 -2
  321. package/templates/turborepo/apps/backoffice/src/app/health/route.ts +10 -2
  322. package/templates/turborepo/apps/backoffice/src/collections/media.test.ts +36 -1
  323. package/templates/turborepo/apps/backoffice/src/collections/media.ts +1 -1
  324. package/templates/turborepo/apps/backoffice/src/environment.test.ts +19 -0
  325. package/templates/turborepo/apps/backoffice/src/payload.config.test.ts +15 -0
  326. package/templates/turborepo/apps/backoffice/test/home.e2e.test.ts +3 -7
  327. package/templates/turborepo/apps/backoffice/tsconfig.json +0 -1
  328. package/templates/turborepo/knip.json +4 -2
  329. package/templates/turborepo/package.json +15 -15
  330. package/templates/turborepo/pnpm-workspace.yaml +1 -22
  331. package/templates/turborepo/turbo.json +32 -11
  332. package/templates/base/.agents/hooks/agent-suppression-gate.sh +0 -45
  333. package/templates/base/.claude/rules/agents-guide.md +0 -1
  334. package/templates/base/scripts/check-human-gates.ts +0 -383
  335. package/templates/base/scripts/check-intent.ts +0 -391
  336. package/templates/nextjs/knip.json +0 -4
  337. package/templates/payload/src/collections/users.test.ts +0 -10
  338. package/templates/turborepo/apps/backoffice/src/collections/users.test.ts +0 -10
@@ -1,44 +1,65 @@
1
1
  {
2
- "$schema": "https://turbo.build/schema.json",
2
+ "$schema": "https://turborepo.dev/schema.json",
3
+ "futureFlags": {
4
+ "affectedUsingTaskInputs": true
5
+ },
6
+ "globalDependencies": [
7
+ ".dockerignore",
8
+ ".env",
9
+ "package.json",
10
+ "pnpm-workspace.yaml",
11
+ "railway.json"
12
+ ],
3
13
  "globalPassThroughEnv": [
4
14
  "APP_URL",
5
15
  "CI",
6
16
  "DATABASE_URL",
7
17
  "NEXT_TELEMETRY_DISABLED",
8
- "PAYLOAD_SECRET",
9
- "PORT"
18
+ "PAYLOAD_SECRET"
10
19
  ],
11
20
  "tasks": {
12
21
  "build": {
13
22
  "dependsOn": ["^build"],
14
- "outputs": [".next/**", "!.next/cache/**"]
23
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"],
24
+ "outputs": [".next/**", "!.next/cache/**", "!.next/dev/**"]
25
+ },
26
+ "depcruise": {
27
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
15
28
  },
16
- "depcruise": {},
17
29
  "dev": {
18
30
  "cache": false,
19
- "persistent": true
31
+ "persistent": true,
32
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
20
33
  },
21
34
  "start": {
22
35
  "cache": false,
23
- "persistent": true
36
+ "persistent": true,
37
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
38
+ },
39
+ "test": {
40
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
24
41
  },
25
- "test": {},
26
42
  "test:changed": {
27
- "cache": false
43
+ "cache": false,
44
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
28
45
  },
29
46
  "test:coverage": {
47
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"],
30
48
  "outputs": ["coverage/**"]
31
49
  },
32
50
  "test:e2e": {
33
51
  "cache": false,
34
- "dependsOn": ["build"]
52
+ "dependsOn": ["build"],
53
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
35
54
  },
36
55
  "test:watch": {
37
56
  "cache": false,
38
- "persistent": true
57
+ "persistent": true,
58
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"]
39
59
  },
40
60
  "typecheck": {
41
61
  "dependsOn": ["^typecheck"],
62
+ "inputs": ["$TURBO_DEFAULT$", "!README.md"],
42
63
  "outputs": [".next/types/**", "next-env.d.ts"]
43
64
  }
44
65
  }
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -uo pipefail
3
-
4
- directives='eslint-disable|eslint-enable|@ts-ignore|@ts-expect-error|@ts-nocheck|zizmor: ?ignore|nosemgrep|gitleaks:allow|trufflehog:ignore|prettier-ignore|istanbul ignore|c8 ignore|v8 ignore|knip-ignore|NOSONAR|editorconfig-checker-disable|hadolint ignore|trivy:ignore|(^|[^[:alnum:]_])@public([^[:alnum:]_]|$)|(^|[^[:alnum:]_])noqa([^[:alnum:]_]|$)'
5
- exempt='^(\.agents/rules/|\.agents/hooks/|\.claude/rules/|\.cursor/rules/|\.cursor/hooks/|scripts/check-agent-gates\.ts(:|$))'
6
-
7
- allow() {
8
- printf '%s\n' '{"permission":"allow"}'
9
- exit 0
10
- }
11
-
12
- block() {
13
- jq -n --arg message "$1" '{permission:"deny",user_message:$message,agent_message:$message}'
14
- printf 'agent-suppression-gate: %s\n' "$1" >&2
15
- exit 2
16
- }
17
-
18
- if [[ ${1:-} == --repo ]]; then
19
- matches=$(git grep -nIE "$directives" -- . ":(exclude)pnpm-lock.yaml" | grep -vE "$exempt" || true)
20
- if [[ -n $matches ]]; then
21
- printf '%s\n' "$matches" >&2
22
- printf 'suppressions:check: remove these suppression directives and fix the findings they hide.\n' >&2
23
- exit 1
24
- fi
25
- exit 0
26
- fi
27
-
28
- trap 'block ".agents/hooks/agent-suppression-gate.sh failed. Stop and report this error to the user; agents may not edit the gate."' ERR
29
- command -v jq > /dev/null || block "jq is required to read the hook input. Install it: https://jqlang.org/download/"
30
- input=$(cat)
31
- path=$(jq -r '.tool_input.file_path // .tool_input.path // .tool_input.notebook_path // .file_path // empty' <<< "$input")
32
- [[ -n $path ]] || allow
33
-
34
- project=$(jq -r '.workspace_roots[0] // .cwd // empty' <<< "$input")
35
- project=${CLAUDE_PROJECT_DIR:-${project:-$PWD}}
36
- [[ $path == /* ]] || path=$project/$path
37
- [[ $path == "$project"/* ]] || allow
38
- relative=${path#"$project"/}
39
- [[ ! $relative =~ (^|/)\.\.?(/|$) && $relative =~ $exempt ]] && allow
40
-
41
- content=$(jq -r '[.tool_input.content, .tool_input.contents, .tool_input.new_string, .tool_input.new_source, (.tool_input.edits // [] | .[].new_string)] | map(select(. != null)) | join("\n")' <<< "$input")
42
- if grep -qE "$directives" <<< "$content"; then
43
- block "The edit to $relative adds a suppression directive. Fix what the tool reports instead; if you believe the finding is wrong, stop and ask the user."
44
- fi
45
- allow
@@ -1 +0,0 @@
1
- @../../AGENTS.md
@@ -1,383 +0,0 @@
1
- import {existsSync, readdirSync, readFileSync, statSync} from "node:fs";
2
- import path from "node:path";
3
-
4
- type Problem = {
5
- line: number;
6
- message: string;
7
- rule: string;
8
- };
9
-
10
- type Step = {
11
- line: number;
12
- text: string;
13
- };
14
-
15
- type SelfTestCase = {
16
- expectedRule?: string;
17
- name: string;
18
- text: string;
19
- };
20
-
21
- const playbookDirectory = ".agents/skills/sdlc/playbooks";
22
- const agentOwnedWorkRule = ".agents/rules/agent-owned-work.md";
23
- const nextMarker = "**Next:**";
24
-
25
- const humanGateRoles = [
26
- "originator",
27
- "product owner",
28
- "engineer",
29
- "code owner",
30
- "release manager",
31
- "service owner",
32
- ];
33
-
34
- const roleWords = new RegExp(String.raw`\b(?:${humanGateRoles.join("|")})s?\b`, "i");
35
-
36
- const waitingWordList = [
37
- "wait",
38
- "waits",
39
- "approve",
40
- "approves",
41
- "approval",
42
- "approved",
43
- "approving",
44
- "confirm",
45
- "confirms",
46
- "confirmation",
47
- "permission",
48
- "sign off",
49
- ];
50
-
51
- const waitingWords = new RegExp(String.raw`\b(?:${waitingWordList.join("|")})\b`, "i");
52
- const roleList = humanGateRoles.join(", ");
53
-
54
- /**
55
- * Blank every line of a fenced code block, keeping line numbers.
56
- * @param text Playbook content with LF line endings.
57
- * @returns The content with fenced lines emptied.
58
- */
59
- function maskFencedBlocks(text: string): string {
60
- let isFenced = false;
61
-
62
- return text
63
- .split("\n")
64
- .map(line => {
65
- const isFence = /^\s*(?:```|~~~)/.test(line);
66
- const isMasked = isFenced || isFence;
67
-
68
- isFenced = isFence ? !isFenced : isFenced;
69
-
70
- return isMasked ? "" : line;
71
- })
72
- .join("\n");
73
- }
74
-
75
- /**
76
- * Split a playbook into its numbered steps, each with its indented continuation lines.
77
- * @param text Playbook content.
78
- * @returns The steps with the line each starts on.
79
- */
80
- function splitSteps(text: string): Step[] {
81
- return Array.from(text.matchAll(/^\d+\. .*(?:\n[\t ]+\S.*)*/gm), match => ({
82
- line: text.slice(0, match.index).split("\n").length,
83
- text: match[0],
84
- }));
85
- }
86
-
87
- /**
88
- * Find gate lines whose bold sentence names no human gate role.
89
- * @param lines Playbook lines.
90
- * @returns One problem per such line.
91
- */
92
- function findGateProblems(lines: string[]): Problem[] {
93
- return lines
94
- .map((text, index) => ({
95
- text,
96
- line: index + 1,
97
- }))
98
- .filter(({text}) => text.includes("**Gate:"))
99
- .filter(({text}) => !roleWords.test(/\*\*Gate:(.*?)\*\*/.exec(text)?.[1] ?? ""))
100
- .map(({line}) => ({
101
- line,
102
- rule: "A",
103
- message: `a gate must name one of ${roleList} inside its bold sentence; write "**Gate: the <role> <does what>.**"`,
104
- }));
105
- }
106
-
107
- /**
108
- * Find steps that wait, approve, or confirm without naming a human gate role.
109
- * @param text Playbook content.
110
- * @returns One problem per such step.
111
- */
112
- function findWaitingStepProblems(text: string): Problem[] {
113
- return splitSteps(text)
114
- .filter(step => waitingWords.test(step.text) && !roleWords.test(step.text))
115
- .map(step => ({
116
- rule: "B",
117
- line: step.line,
118
- message: `a step that waits, approves, or confirms must name who does it: one of ${roleList}`,
119
- }));
120
- }
121
-
122
- /**
123
- * Tell whether a path is a relative path without `..` to an existing regular file.
124
- * @param targetPath Path from a `**Next:**` target.
125
- * @returns True when the path is usable.
126
- */
127
- function isProjectFile(targetPath: string): boolean {
128
- const segments = targetPath.split(/[\\/]/);
129
-
130
- return (
131
- !path.isAbsolute(targetPath) &&
132
- !segments.includes("..") &&
133
- statSync(targetPath, {
134
- throwIfNoEntry: false,
135
- })?.isFile() === true
136
- );
137
- }
138
-
139
- /**
140
- * Describe what is wrong with the text after a `**Next:**` marker.
141
- * @param text The text after the marker.
142
- * @returns The problem, or an empty string when the line is valid.
143
- */
144
- function describeNextProblem(text: string): string {
145
- const parts = text.replace(/\.\s*$/, "").split(";");
146
-
147
- if (parts.length !== 3 || parts.some(part => part.trim() === "")) {
148
- return `"${nextMarker}" needs exactly three non-empty parts separated by ";" and no other ";": the target, who runs it, and what starts it`;
149
- }
150
-
151
- const target = parts.at(0)?.trim() ?? "";
152
- const targetPath = /^`([^`\s]+)`$/.exec(target)?.[1];
153
-
154
- return target !== "none" && (targetPath === undefined || !isProjectFile(targetPath))
155
- ? `the "${nextMarker}" target must be "none" or one backticked relative path to an existing file in the project, with no ".." segment, found ${target}`
156
- : "";
157
- }
158
-
159
- /**
160
- * Find a missing, repeated, or malformed `**Next:**` line.
161
- * @param lines Playbook lines.
162
- * @returns The problems with the next-step line.
163
- */
164
- function findNextProblems(lines: string[]): Problem[] {
165
- const nextLines = lines
166
- .map((text, index) => ({
167
- text,
168
- line: index + 1,
169
- }))
170
- .filter(({text}) => text.startsWith(nextMarker));
171
-
172
- const next = nextLines.at(0);
173
-
174
- if (next === undefined || nextLines.length !== 1) {
175
- return [
176
- {
177
- rule: "C",
178
- line: next?.line ?? 1,
179
- message: `a playbook needs exactly one line starting with "${nextMarker}", found ${nextLines.length}; write "${nextMarker} <target>; <who runs it>; <what starts it>."`,
180
- },
181
- ];
182
- }
183
-
184
- const message = describeNextProblem(next.text.slice(nextMarker.length));
185
-
186
- return message === ""
187
- ? []
188
- : [
189
- {
190
- message,
191
- rule: "C",
192
- line: next.line,
193
- },
194
- ];
195
- }
196
-
197
- /**
198
- * Check one playbook against rules A, B, and C.
199
- * @param text Playbook content.
200
- * @returns Every problem found.
201
- */
202
- function checkPlaybook(text: string): Problem[] {
203
- const unfenced = maskFencedBlocks(text.replaceAll("\r\n", "\n"));
204
- const lines = unfenced.split("\n");
205
-
206
- return [
207
- ...findGateProblems(lines),
208
- ...findWaitingStepProblems(unfenced),
209
- ...findNextProblems(lines),
210
- ];
211
- }
212
-
213
- /**
214
- * List the human gate roles a rule text does not mention.
215
- * @param ruleText Content of the agent-owned work rule.
216
- * @returns The missing roles.
217
- */
218
- function findMissingRoles(ruleText: string): string[] {
219
- return humanGateRoles.filter(role => !ruleText.toLowerCase().includes(role));
220
- }
221
-
222
- const validNext = "**Next:** none; the person; when they write.";
223
-
224
- const selfTestCases: SelfTestCase[] = [
225
- {
226
- name: "gate names a role",
227
- text: `**Gate: the originator corrects the draft.**\n\n${validNext}`,
228
- },
229
- {
230
- expectedRule: "A",
231
- name: "gate names no role",
232
- text: `**Gate: the manager corrects the draft.**\n\n${validNext}`,
233
- },
234
- {
235
- expectedRule: "A",
236
- name: "role outside the bold gate sentence",
237
- text: `**Gate: the draft is corrected.** The originator does it.\n\n${validNext}`,
238
- },
239
- {
240
- name: "waiting step names a role",
241
- text: `1. Wait for the engineer.\n\n${validNext}`,
242
- },
243
- ...waitingWordList.map(word => ({
244
- expectedRule: "B",
245
- name: `waiting word "${word}" names no role`,
246
- text: `1. Ask, then ${word} for the answer.\n\n${validNext}`,
247
- })),
248
- {
249
- name: "plural role",
250
- text: `1. Wait for the engineers and the code owners.\n\n${validNext}`,
251
- },
252
- {
253
- expectedRule: "B",
254
- name: "longer word that starts with a role",
255
- text: `1. Wait for the engineering team.\n\n${validNext}`,
256
- },
257
- {
258
- name: "CRLF line endings",
259
- text: `1. Ask, then wait.\r\n - The code owner answers.\r\n\r\n${validNext}\r\n`,
260
- },
261
- {
262
- name: "gate inside a fenced block",
263
- text: `\`\`\`\n**Gate: the manager corrects the draft.**\n\`\`\`\n\n${validNext}`,
264
- },
265
- {
266
- name: "next inside a fenced block",
267
- text: `\`\`\`\n**Next:** none; ; x\n\`\`\`\n\n${validNext}`,
268
- },
269
- {
270
- expectedRule: "B",
271
- name: "waiting word on a continuation line",
272
- text: `1. Ask.\n - Wait for the answer.\n\n${validNext}`,
273
- },
274
- {
275
- name: "role on a continuation line covers the step",
276
- text: `1. Ask, then wait.\n - The code owner answers.\n\n${validNext}`,
277
- },
278
- {
279
- name: "target is an existing path",
280
- text: "**Next:** `package.json`; an agent; when asked.",
281
- },
282
- {
283
- expectedRule: "C",
284
- name: "target path does not exist",
285
- text: "**Next:** `missing/file.md`; an agent; when asked.",
286
- },
287
- {
288
- expectedRule: "C",
289
- name: "target is an absolute path",
290
- text: "**Next:** `/etc/hostname`; an agent; when asked.",
291
- },
292
- {
293
- expectedRule: "C",
294
- name: "target has a .. segment",
295
- text: "**Next:** `.agents/../package.json`; an agent; when asked.",
296
- },
297
- {
298
- expectedRule: "C",
299
- name: "target is a directory",
300
- text: "**Next:** `.agents`; an agent; when asked.",
301
- },
302
- {
303
- expectedRule: "C",
304
- name: "target is not backticked",
305
- text: "**Next:** package.json; an agent; when asked.",
306
- },
307
- {
308
- expectedRule: "C",
309
- name: "next has two parts",
310
- text: "**Next:** none; the person.",
311
- },
312
- {
313
- expectedRule: "C",
314
- name: "next has an empty part",
315
- text: "**Next:** none; ; when they write.",
316
- },
317
- {
318
- expectedRule: "C",
319
- name: "next is missing",
320
- text: "1. Answer.",
321
- },
322
- {
323
- expectedRule: "C",
324
- name: "next appears twice",
325
- text: `${validNext}\n${validNext}`,
326
- },
327
- ];
328
-
329
- const selfTestFailures = selfTestCases
330
- .filter(({expectedRule, text}) => {
331
- const rules = checkPlaybook(text).map(problem => problem.rule);
332
-
333
- return expectedRule === undefined ? rules.length > 0 : rules.join(",") !== expectedRule;
334
- })
335
- .map(({name}) => name);
336
-
337
- if (findMissingRoles(humanGateRoles.join(" ")).length > 0) {
338
- selfTestFailures.push("a rule naming every role has no missing role");
339
- }
340
-
341
- if (findMissingRoles("no role").length !== humanGateRoles.length) {
342
- selfTestFailures.push("a rule naming no role is missing every role");
343
- }
344
-
345
- if (existsSync(playbookDirectory) && existsSync(agentOwnedWorkRule)) {
346
- if (selfTestFailures.length > 0) {
347
- const failureLines = selfTestFailures.map(name => `- ${name}`);
348
-
349
- process.stderr.write(`Human gate self-tests failed:\n${failureLines.join("\n")}\n`);
350
- process.exitCode = 1;
351
- } else {
352
- const playbooks = readdirSync(playbookDirectory)
353
- .filter(name => name.endsWith(".md"))
354
- .map(name => `${playbookDirectory}/${name}`);
355
-
356
- const problemLines = [
357
- ...playbooks.flatMap(playbook =>
358
- checkPlaybook(readFileSync(playbook, "utf8")).map(
359
- ({line, message}) => `${playbook}:${line}: ${message}`,
360
- ),
361
- ),
362
- ...findMissingRoles(readFileSync(agentOwnedWorkRule, "utf8")).map(
363
- role =>
364
- `${agentOwnedWorkRule}:1: the human gate role "${role}" is missing; name it in the rule or remove it from scripts/check-human-gates.ts`,
365
- ),
366
- ];
367
-
368
- if (problemLines.length > 0) {
369
- process.stderr.write(`${problemLines.join("\n")}\n`);
370
- process.exitCode = 1;
371
- } else {
372
- process.stdout.write(
373
- `Checked ${playbooks.length} playbooks against ${humanGateRoles.length} human gate roles.\n`,
374
- );
375
- }
376
- }
377
- } else {
378
- process.stderr.write(
379
- `Run from the project root: ${playbookDirectory} and ${agentOwnedWorkRule} must exist.\n`,
380
- );
381
-
382
- process.exitCode = 1;
383
- }