@next-friday/create-app 9.1.1 → 9.2.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/README.md +5 -4
- package/dist/main.mjs +135 -96
- package/package.json +14 -10
- package/templates/base/.agents/hooks/agent-mcp-gate.sh +7 -2
- package/templates/base/.agents/hooks/agent-production-gate.sh +6 -1
- package/templates/base/.agents/hooks/agent-shell-gate.sh +1 -181
- package/templates/base/.agents/hooks/agent-test-edit-gate.sh +12 -3
- package/templates/base/.agents/review.md +2 -2
- package/templates/base/.agents/rules/no-suppression.md +1 -1
- package/templates/base/.agents/skill-evals/README.md +42 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/01-the-reply-explains-why-pnpm.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/02-no-file-in-the-project.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/03-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/05-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/06-everything-the-reply-states-as.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/01-work-happens-on-the-plan-branch.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/02-the-plan-is-implemented-and-checked.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/03-the-branch-is-pushed.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/04-the-reply-lists-three-playbooks.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/05-no-approval-and-no-merge.md +7 -0
- 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
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/07-the-plan-names-the-engineer.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/case.yaml +70 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/01-the-reply-asks-numbered-questions.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/02-no-plan-before-the-answers.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/03-no-question-depends-on-another.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/04-no-file-or-commit.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/case.yaml +67 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/fixture.md +1 -0
- 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
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/02-no-source-file-or-commit.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/03-the-reply-names-the-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/prompt.md +10 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/case.yaml +7 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/01-no-deployment-is-started.md +7 -0
- 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
- 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
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/case.yaml +12 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/01-a-file-intentkebabcasenameintentmd-exists-with.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/02-the-reply-shows-the-draft.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/03-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/04-reply-lists-the-playbook-steps.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/06-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/07-the-drafts-open-questions-section.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/prompt.md +10 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/case.yaml +33 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/fixture.md +1 -0
- 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
- 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
- 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
- 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
- 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
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/01-the-first-message-restates-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/02-the-reply-asks-numbered-questions.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/03-no-intentmd-is-written-before.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/04-reply-lists-the-playbook-steps.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/05-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/07-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/08-no-question-in-the-round.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/case.yaml +14 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/01-the-intent-has-the-five-headings.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/02-problem-holds-the-error-and-where.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/03-unverified-cause-is-an-open-question.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/04-no-source-file-is-changed.md +7 -0
- 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
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/06-the-person-is-asked-no-question.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/01-the-reply-follows-the-plan.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/02-the-reply-does-not-tell.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/03-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/05-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/case.yaml +10 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/fixture.md +1 -0
- 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
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/02-the-reply-names-the-closest-row.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/03-the-reply-is-short.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/case.yaml +79 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/01-findings-have-pass-severity-and-location.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/02-no-approval-and-no-merge.md +7 -0
- 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
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/04-nothing-is-pushed-or-changed.md +7 -0
- 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
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/case.yaml +41 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/01-no-spec-is-written.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/02-no-commit-is-made.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/03-the-reply-says-not-merged.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/05-the-reply-is-short.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/01-no-intentmd-or-any-other.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/02-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/04-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/05-the-reply-says-that-work.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/case.yaml +41 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/01-the-spec-has-the-five-headings.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/02-policies-has-one-line-per-policy.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/03-no-policy-is-called-unwritten.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/04-a-branch-is-pushed-for-review.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/05-the-reply-names-the-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/07-no-merge-is-made.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/08-the-four-policies-are-read-before-writing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/case.yaml +10 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/01-the-reply-says-that-work.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/02-the-reply-gives-one-example.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/03-the-reply-lists-the-project.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/04-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/06-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/case.yaml +9 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/01-the-reply-says-that-this.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/02-the-reply-does-not-describe.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/03-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/05-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/case.yaml +10 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/01-the-reply-says-to-send.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/02-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/04-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/prompt.md +8 -0
- package/templates/base/.agents/skills/sdlc/SKILL.md +25 -21
- package/templates/base/.agents/skills/sdlc/playbooks/build.md +14 -9
- package/templates/base/.agents/skills/sdlc/playbooks/deploy.md +2 -2
- package/templates/base/.agents/skills/sdlc/playbooks/design.md +3 -3
- package/templates/base/.agents/skills/sdlc/playbooks/fix.md +23 -0
- package/templates/base/.agents/skills/sdlc/playbooks/maintain.md +3 -4
- package/templates/base/.agents/skills/sdlc/playbooks/open-pr.md +3 -3
- package/templates/base/.agents/skills/sdlc/playbooks/plan.md +3 -3
- package/templates/base/.agents/skills/sdlc/playbooks/review.md +2 -2
- package/templates/base/.agents/skills/sdlc-help/SKILL.md +4 -2
- package/templates/base/.claude/managed-settings.example.json +2 -2
- package/templates/base/.claude/settings.json +0 -13
- package/templates/base/.claude/skills/sdlc/SKILL.md +25 -21
- package/templates/base/.claude/skills/sdlc/playbooks/build.md +14 -9
- package/templates/base/.claude/skills/sdlc/playbooks/deploy.md +2 -2
- package/templates/base/.claude/skills/sdlc/playbooks/design.md +3 -3
- package/templates/base/.claude/skills/sdlc/playbooks/fix.md +23 -0
- package/templates/base/.claude/skills/sdlc/playbooks/maintain.md +3 -4
- package/templates/base/.claude/skills/sdlc/playbooks/open-pr.md +3 -3
- package/templates/base/.claude/skills/sdlc/playbooks/plan.md +3 -3
- package/templates/base/.claude/skills/sdlc/playbooks/review.md +2 -2
- package/templates/base/.claude/skills/sdlc-help/SKILL.md +4 -2
- package/templates/base/.cursor/hooks.json +0 -6
- package/templates/base/.cursor/rules/no-suppression.mdc +1 -1
- package/templates/base/.cursor/skills/sdlc/SKILL.md +25 -21
- package/templates/base/.cursor/skills/sdlc/playbooks/build.md +14 -9
- package/templates/base/.cursor/skills/sdlc/playbooks/deploy.md +2 -2
- package/templates/base/.cursor/skills/sdlc/playbooks/design.md +3 -3
- package/templates/base/.cursor/skills/sdlc/playbooks/fix.md +23 -0
- package/templates/base/.cursor/skills/sdlc/playbooks/maintain.md +3 -4
- package/templates/base/.cursor/skills/sdlc/playbooks/open-pr.md +3 -3
- package/templates/base/.cursor/skills/sdlc/playbooks/plan.md +3 -3
- package/templates/base/.cursor/skills/sdlc/playbooks/review.md +2 -2
- package/templates/base/.cursor/skills/sdlc-help/SKILL.md +4 -2
- package/templates/base/.github/ISSUE_TEMPLATE/documentation.yml +1 -1
- package/templates/base/.github/dependabot.yml +1 -10
- package/templates/base/.github/file-filters.yml +3 -6
- package/templates/base/.github/workflows/agent-evals.yml +46 -0
- package/templates/base/.github/workflows/claude-fix-loop.yml +45 -0
- package/templates/base/.github/workflows/control-band.yml +115 -0
- package/templates/base/.github/workflows/dependency-review.yml +1 -0
- package/templates/base/.github/workflows/deploy.yml +4 -2
- package/templates/base/.github/workflows/osv-scanner.yml +0 -5
- package/templates/base/.github/workflows/pr-triage.yml +1 -1
- package/templates/base/.github/workflows/secret-scan.yml +4 -1
- package/templates/base/.github/workflows/spec-on-intent-merge.yml +108 -0
- package/templates/base/.github/workflows/validate-pr-title.yml +3 -2
- package/templates/base/.husky/pre-push +1 -8
- package/templates/base/REVIEW.md +2 -2
- package/templates/base/SETUP.md +44 -8
- package/templates/base/bands.yaml +3 -3
- package/templates/base/intent/README.md +1 -0
- package/templates/base/knip.json +2 -1
- package/templates/base/pnpm-workspace.yaml +1 -22
- package/templates/base/runbooks/rollback-deploy.md +27 -0
- package/templates/base/scripts/check-agent-gates.ts +265 -226
- package/templates/base/scripts/check-bands.ts +665 -0
- package/templates/base/scripts/check-plan.ts +1047 -0
- package/templates/base/scripts/measure-loop.ts +293 -0
- package/templates/base/scripts/run-agent-evals.ts +1197 -0
- package/templates/base/scripts/sync-agent-rules.ts +0 -8
- package/templates/nestjs/.dependency-cruiser.json +1 -1
- package/templates/nestjs/.github/ISSUE_TEMPLATE/bug.yml +5 -5
- package/templates/nestjs/.github/workflows/ci.yml +58 -9
- package/templates/nestjs/.github/workflows/sast.yml +0 -5
- package/templates/nestjs/AGENTS.md +17 -13
- package/templates/nestjs/CONTRIBUTING.md +3 -2
- package/templates/nestjs/Dockerfile +4 -4
- package/templates/nestjs/README.md +4 -6
- package/templates/nestjs/_gitignore +0 -1
- package/templates/nestjs/package.json +21 -19
- package/templates/nestjs/src/app.controller.test.ts +1 -0
- package/templates/nestjs/src/health/health.controller.test.ts +1 -0
- package/templates/nestjs/src/port.test.ts +1 -0
- package/templates/nestjs/test/app.e2e.test.ts +1 -0
- package/templates/nestjs/tsconfig.build.json +2 -3
- package/templates/nestjs/tsconfig.json +1 -3
- package/templates/nestjs/vitest.config.ts +0 -1
- package/templates/nestjs/vitest.e2e.config.ts +0 -1
- package/templates/nextjs/.dependency-cruiser.json +1 -1
- package/templates/nextjs/.github/ISSUE_TEMPLATE/bug.yml +1 -1
- package/templates/nextjs/.github/workflows/ci.yml +58 -9
- package/templates/nextjs/.github/workflows/sast.yml +0 -5
- package/templates/nextjs/AGENTS.md +17 -13
- package/templates/nextjs/CONTRIBUTING.md +3 -2
- package/templates/nextjs/Dockerfile +2 -2
- package/templates/nextjs/README.md +5 -3
- package/templates/nextjs/app/layout.tsx +2 -2
- package/templates/nextjs/app/page.test.ts +1 -1
- package/templates/nextjs/app/page.tsx +1 -1
- package/templates/nextjs/package.json +15 -14
- package/templates/nextjs/test/home.e2e.test.ts +3 -7
- package/templates/nextjs/tsconfig.json +0 -3
- package/templates/nodejs/.dependency-cruiser.json +1 -1
- package/templates/nodejs/.github/ISSUE_TEMPLATE/bug.yml +5 -5
- package/templates/nodejs/.github/workflows/ci.yml +58 -9
- package/templates/nodejs/.github/workflows/sast.yml +0 -5
- package/templates/nodejs/AGENTS.md +17 -12
- package/templates/nodejs/CONTRIBUTING.md +4 -3
- package/templates/nodejs/Dockerfile +3 -3
- package/templates/nodejs/README.md +4 -4
- package/templates/nodejs/_gitignore +0 -1
- package/templates/nodejs/package.json +13 -11
- package/templates/nodejs/src/server.test.ts +23 -0
- package/templates/nodejs/src/server.ts +2 -2
- package/templates/nodejs/tsconfig.build.json +2 -3
- package/templates/nodejs/tsconfig.json +0 -4
- package/templates/payload/.agents/agents/verifier.md +1 -1
- package/templates/payload/.agents/review.md +21 -0
- package/templates/payload/.claude/agents/verifier.md +1 -1
- package/templates/payload/.cursor/agents/verifier.md +1 -1
- package/templates/payload/.dependency-cruiser.json +1 -1
- package/templates/payload/.github/ISSUE_TEMPLATE/bug.yml +1 -1
- package/templates/payload/.github/workflows/ci.yml +75 -10
- package/templates/payload/.github/workflows/sast.yml +0 -5
- package/templates/payload/AGENTS.md +22 -18
- package/templates/payload/CONTRIBUTING.md +4 -3
- package/templates/payload/Dockerfile +2 -2
- package/templates/payload/README.md +8 -4
- package/templates/payload/REVIEW.md +21 -0
- package/templates/payload/package.json +17 -15
- package/templates/payload/src/app/(frontend)/layout.tsx +2 -2
- package/templates/payload/src/app/(frontend)/page.test.ts +1 -1
- package/templates/payload/src/app/(frontend)/page.tsx +1 -1
- package/templates/payload/src/app/favicon.ico +0 -0
- package/templates/payload/src/app/health/route.test.ts +20 -2
- package/templates/payload/src/app/health/route.ts +9 -2
- package/templates/payload/src/collections/media.test.ts +36 -1
- package/templates/payload/src/collections/media.ts +1 -1
- package/templates/payload/src/payload.config.test.ts +15 -0
- package/templates/payload/test/home.e2e.test.ts +3 -7
- package/templates/payload/tsconfig.json +0 -1
- package/templates/turborepo/.agents/agents/verifier.md +1 -1
- package/templates/turborepo/.agents/review.md +21 -0
- package/templates/turborepo/.claude/agents/verifier.md +1 -1
- package/templates/turborepo/.cursor/agents/verifier.md +1 -1
- package/templates/turborepo/.dockerignore +9 -4
- package/templates/turborepo/.github/ISSUE_TEMPLATE/bug.yml +16 -1
- package/templates/turborepo/.github/dependabot.yml +1 -10
- package/templates/turborepo/.github/scripts/affected-apps.sh +5 -23
- package/templates/turborepo/.github/workflows/ci.yml +140 -103
- package/templates/turborepo/.github/workflows/deploy.yml +24 -23
- package/templates/turborepo/.github/workflows/sast.yml +0 -5
- package/templates/turborepo/AGENTS.md +22 -18
- package/templates/turborepo/CONTRIBUTING.md +6 -4
- package/templates/turborepo/README.md +8 -8
- package/templates/turborepo/REVIEW.md +21 -0
- package/templates/turborepo/apps/app/.dependency-cruiser.json +1 -1
- package/templates/turborepo/apps/app/AGENTS.md +9 -0
- package/templates/turborepo/apps/app/Dockerfile +2 -2
- package/templates/turborepo/apps/app/app/layout.tsx +2 -2
- package/templates/turborepo/apps/app/app/page.test.ts +1 -1
- package/templates/turborepo/apps/app/app/page.tsx +1 -1
- package/templates/turborepo/apps/app/package.json +9 -9
- package/templates/turborepo/apps/app/test/home.e2e.test.ts +3 -7
- package/templates/turborepo/apps/app/tsconfig.json +0 -3
- package/templates/turborepo/apps/backoffice/.dependency-cruiser.json +1 -1
- package/templates/turborepo/apps/backoffice/AGENTS.md +9 -0
- package/templates/turborepo/apps/backoffice/Dockerfile +2 -2
- package/templates/turborepo/apps/backoffice/package.json +12 -11
- package/templates/turborepo/apps/backoffice/src/app/(frontend)/layout.tsx +2 -2
- package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.test.ts +1 -1
- package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.tsx +1 -1
- package/templates/turborepo/apps/backoffice/src/app/favicon.ico +0 -0
- package/templates/turborepo/apps/backoffice/src/app/health/route.test.ts +20 -2
- package/templates/turborepo/apps/backoffice/src/app/health/route.ts +10 -2
- package/templates/turborepo/apps/backoffice/src/collections/media.test.ts +36 -1
- package/templates/turborepo/apps/backoffice/src/collections/media.ts +1 -1
- package/templates/turborepo/apps/backoffice/src/environment.test.ts +19 -0
- package/templates/turborepo/apps/backoffice/src/payload.config.test.ts +15 -0
- package/templates/turborepo/apps/backoffice/test/home.e2e.test.ts +3 -7
- package/templates/turborepo/apps/backoffice/tsconfig.json +0 -1
- package/templates/turborepo/knip.json +4 -2
- package/templates/turborepo/package.json +16 -16
- package/templates/turborepo/pnpm-workspace.yaml +1 -22
- package/templates/turborepo/turbo.json +32 -11
- package/templates/base/.agents/hooks/agent-suppression-gate.sh +0 -45
- package/templates/base/.claude/rules/agents-guide.md +0 -1
- package/templates/base/scripts/check-human-gates.ts +0 -383
- package/templates/base/scripts/check-intent.ts +0 -391
- package/templates/nextjs/knip.json +0 -4
- package/templates/payload/src/collections/users.test.ts +0 -10
- package/templates/turborepo/apps/backoffice/src/collections/users.test.ts +0 -10
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import {execFileSync} from "node:child_process";
|
|
2
|
+
import {existsSync, readdirSync} from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
type CommitDates = {
|
|
6
|
+
intent: string[];
|
|
7
|
+
plan: string[];
|
|
8
|
+
spec: string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type LoopFigures = {
|
|
12
|
+
daysIntentToSpec?: number;
|
|
13
|
+
firstIntent?: string;
|
|
14
|
+
firstPlan?: string;
|
|
15
|
+
firstSpec?: string;
|
|
16
|
+
intentCommitsAfterSpec: number;
|
|
17
|
+
specCommitsAfterPlan: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type SelfTestCase = {
|
|
21
|
+
dates: CommitDates;
|
|
22
|
+
expected: LoopFigures;
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const intentDirectory = "intent";
|
|
27
|
+
const millisecondsPerDay = 86_400_000;
|
|
28
|
+
const secondDay = "2026-01-02T00:00:00+00:00";
|
|
29
|
+
const thirdDay = "2026-01-03T00:00:00+00:00";
|
|
30
|
+
const firstDay = "2026-01-01T00:00:00+00:00";
|
|
31
|
+
|
|
32
|
+
const tableHeader = [
|
|
33
|
+
"change",
|
|
34
|
+
"intent",
|
|
35
|
+
"spec",
|
|
36
|
+
"plan",
|
|
37
|
+
"days intent to spec",
|
|
38
|
+
"intent commits after spec",
|
|
39
|
+
"spec commits after plan",
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Find the oldest commit date.
|
|
44
|
+
* @param dates Commit dates in any order.
|
|
45
|
+
* @returns The earliest instant, or undefined when there is no commit.
|
|
46
|
+
*/
|
|
47
|
+
function findFirstDate(dates: string[]): string | undefined {
|
|
48
|
+
return dates.toSorted((left, right) => Date.parse(left) - Date.parse(right)).at(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Read the change name of a folder that is a direct child of `intent/`.
|
|
53
|
+
* @param changePath Path given on the command line.
|
|
54
|
+
* @returns The folder name, or undefined when the path is not `intent/<change>`.
|
|
55
|
+
*/
|
|
56
|
+
function readChangeName(changePath: string): string | undefined {
|
|
57
|
+
const segments = path.relative(intentDirectory, changePath).split(path.sep);
|
|
58
|
+
|
|
59
|
+
return segments.length === 1 && segments.at(0) !== "" && segments.at(0) !== ".."
|
|
60
|
+
? segments.at(0)
|
|
61
|
+
: undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Count the commits made after a moment.
|
|
66
|
+
* @param dates Commit dates.
|
|
67
|
+
* @param moment The date the commits must follow.
|
|
68
|
+
* @returns The number of later commits, or 0 when there is no moment.
|
|
69
|
+
*/
|
|
70
|
+
function countCommitsAfter(dates: string[], moment: string | undefined): number {
|
|
71
|
+
return moment === undefined
|
|
72
|
+
? 0
|
|
73
|
+
: dates.filter(date => Date.parse(date) > Date.parse(moment)).length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Count the days between two moments.
|
|
78
|
+
* @param from The earlier moment.
|
|
79
|
+
* @param to The later moment.
|
|
80
|
+
* @returns The days rounded to one decimal.
|
|
81
|
+
*/
|
|
82
|
+
function countDaysBetween(from: string, to: string): number {
|
|
83
|
+
return Math.round(((Date.parse(to) - Date.parse(from)) / millisecondsPerDay) * 10) / 10;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Compute the loop figures of one change from its commit dates.
|
|
88
|
+
* @param dates Commit dates of `intent.md`, `spec.md` and `plan.md`.
|
|
89
|
+
* @returns The first commit of each file, the days from intent to spec, and the two rework counts.
|
|
90
|
+
*/
|
|
91
|
+
function summarizeLoop(dates: CommitDates): LoopFigures {
|
|
92
|
+
const firstIntent = findFirstDate(dates.intent);
|
|
93
|
+
const firstSpec = findFirstDate(dates.spec);
|
|
94
|
+
const firstPlan = findFirstDate(dates.plan);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
firstIntent,
|
|
98
|
+
firstPlan,
|
|
99
|
+
firstSpec,
|
|
100
|
+
...(firstIntent !== undefined &&
|
|
101
|
+
firstSpec !== undefined && {
|
|
102
|
+
daysIntentToSpec: countDaysBetween(firstIntent, firstSpec),
|
|
103
|
+
}),
|
|
104
|
+
intentCommitsAfterSpec: countCommitsAfter(dates.intent, firstSpec),
|
|
105
|
+
specCommitsAfterPlan: countCommitsAfter(dates.spec, firstPlan),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Read the dates of the commits that changed a file.
|
|
111
|
+
* @param filePath Path of the file from the repository root.
|
|
112
|
+
* @returns Commit dates in ISO 8601, newest first; empty when git has no commit for the file.
|
|
113
|
+
*/
|
|
114
|
+
function readCommitDates(filePath: string): string[] {
|
|
115
|
+
return execFileSync("git", ["log", "--format=%cI", "--", filePath], {
|
|
116
|
+
encoding: "utf8",
|
|
117
|
+
})
|
|
118
|
+
.split("\n")
|
|
119
|
+
.filter(line => line !== "");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Read the commit dates of the three files of a change.
|
|
124
|
+
* @param change Folder name under `intent/`.
|
|
125
|
+
* @returns The commit dates of `intent.md`, `spec.md` and `plan.md`.
|
|
126
|
+
*/
|
|
127
|
+
function readChangeDates(change: string): CommitDates {
|
|
128
|
+
const readDates = (fileName: string): string[] =>
|
|
129
|
+
readCommitDates(path.join(intentDirectory, change, fileName));
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
intent: readDates("intent.md"),
|
|
133
|
+
plan: readDates("plan.md"),
|
|
134
|
+
spec: readDates("spec.md"),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Format one change as a table row.
|
|
140
|
+
* @param change Folder name under `intent/`.
|
|
141
|
+
* @param figures The figures of the change.
|
|
142
|
+
* @returns The cells of the row; a figure that does not exist yet is blank.
|
|
143
|
+
*/
|
|
144
|
+
function formatRow(change: string, figures: LoopFigures): string[] {
|
|
145
|
+
return [
|
|
146
|
+
change,
|
|
147
|
+
figures.firstIntent?.slice(0, 10) ?? "",
|
|
148
|
+
figures.firstSpec?.slice(0, 10) ?? "",
|
|
149
|
+
figures.firstPlan?.slice(0, 10) ?? "",
|
|
150
|
+
String(figures.daysIntentToSpec ?? ""),
|
|
151
|
+
figures.firstSpec === undefined ? "" : String(figures.intentCommitsAfterSpec),
|
|
152
|
+
figures.firstPlan === undefined ? "" : String(figures.specCommitsAfterPlan),
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Pad the cells of every row to the width of their column.
|
|
158
|
+
* @param rows The header and the rows.
|
|
159
|
+
* @returns One line per row.
|
|
160
|
+
*/
|
|
161
|
+
function formatTable(rows: string[][]): string[] {
|
|
162
|
+
const widths = tableHeader.map((_, column) =>
|
|
163
|
+
Math.max(...rows.map(row => row.at(column)?.length ?? 0)),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return rows.map(row =>
|
|
167
|
+
row
|
|
168
|
+
.map((cell, column) => cell.padEnd(widths.at(column) ?? 0))
|
|
169
|
+
.join(" ")
|
|
170
|
+
.trimEnd(),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const noFigures: LoopFigures = {
|
|
175
|
+
intentCommitsAfterSpec: 0,
|
|
176
|
+
specCommitsAfterPlan: 0,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const selfTestCases: SelfTestCase[] = [
|
|
180
|
+
{
|
|
181
|
+
expected: noFigures,
|
|
182
|
+
name: "a change with no commit",
|
|
183
|
+
dates: {
|
|
184
|
+
intent: [],
|
|
185
|
+
plan: [],
|
|
186
|
+
spec: [],
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "an intent without a spec",
|
|
191
|
+
dates: {
|
|
192
|
+
intent: [secondDay],
|
|
193
|
+
plan: [],
|
|
194
|
+
spec: [],
|
|
195
|
+
},
|
|
196
|
+
expected: {
|
|
197
|
+
...noFigures,
|
|
198
|
+
firstIntent: secondDay,
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "rework after the spec and after the plan",
|
|
203
|
+
dates: {
|
|
204
|
+
intent: ["2026-01-05T00:00:00+00:00", "2026-01-01T12:00:00+00:00"],
|
|
205
|
+
plan: ["2026-01-09T00:00:00+00:00", "2026-01-04T00:00:00+00:00"],
|
|
206
|
+
spec: ["2026-01-10T00:00:00+00:00", "2026-01-06T00:00:00+00:00", thirdDay],
|
|
207
|
+
},
|
|
208
|
+
expected: {
|
|
209
|
+
firstSpec: thirdDay,
|
|
210
|
+
firstIntent: "2026-01-01T12:00:00+00:00",
|
|
211
|
+
firstPlan: "2026-01-04T00:00:00+00:00",
|
|
212
|
+
daysIntentToSpec: 1.5,
|
|
213
|
+
intentCommitsAfterSpec: 1,
|
|
214
|
+
specCommitsAfterPlan: 2,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "dates that are not in time order",
|
|
219
|
+
dates: {
|
|
220
|
+
intent: [firstDay, thirdDay, secondDay],
|
|
221
|
+
plan: [],
|
|
222
|
+
spec: [],
|
|
223
|
+
},
|
|
224
|
+
expected: {
|
|
225
|
+
...noFigures,
|
|
226
|
+
firstIntent: firstDay,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: "offsets are compared as instants",
|
|
231
|
+
dates: {
|
|
232
|
+
intent: ["2026-01-01T00:00:00+07:00"],
|
|
233
|
+
plan: [],
|
|
234
|
+
spec: ["2026-01-01T05:00:00+00:00"],
|
|
235
|
+
},
|
|
236
|
+
expected: {
|
|
237
|
+
...noFigures,
|
|
238
|
+
firstIntent: "2026-01-01T00:00:00+07:00",
|
|
239
|
+
firstSpec: "2026-01-01T05:00:00+00:00",
|
|
240
|
+
daysIntentToSpec: 0.5,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
const selfTestFailures = selfTestCases
|
|
246
|
+
.filter(({dates, expected}) => {
|
|
247
|
+
const actualRow = formatRow("", summarizeLoop(dates));
|
|
248
|
+
|
|
249
|
+
return actualRow.join("|") !== formatRow("", expected).join("|");
|
|
250
|
+
})
|
|
251
|
+
.map(({name}) => name);
|
|
252
|
+
|
|
253
|
+
if (readChangeName("other/dir/change") !== undefined) {
|
|
254
|
+
selfTestFailures.push("a folder outside intent/ is not a change");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (readChangeName("intent/change") !== "change") {
|
|
258
|
+
selfTestFailures.push("a direct child of intent/ is a change");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const changePaths = process.argv.slice(2);
|
|
262
|
+
|
|
263
|
+
if (selfTestFailures.length > 0) {
|
|
264
|
+
const failureLines = selfTestFailures.map(name => `- ${name}`);
|
|
265
|
+
|
|
266
|
+
process.stderr.write(`Loop measure self-tests failed:\n${failureLines.join("\n")}\n`);
|
|
267
|
+
process.exitCode = 1;
|
|
268
|
+
} else {
|
|
269
|
+
const missingLines = changePaths
|
|
270
|
+
.filter(changePath => !existsSync(changePath) || readChangeName(changePath) === undefined)
|
|
271
|
+
.map(
|
|
272
|
+
changePath =>
|
|
273
|
+
`${changePath}: the folder does not exist or is not directly under ${intentDirectory}/; pass the path of an existing intent/<change> folder`,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (missingLines.length > 0) {
|
|
277
|
+
process.stderr.write(`${missingLines.join("\n")}\n`);
|
|
278
|
+
process.exitCode = 1;
|
|
279
|
+
} else {
|
|
280
|
+
const changes =
|
|
281
|
+
changePaths.length > 0
|
|
282
|
+
? changePaths.map(changePath => readChangeName(changePath) ?? "")
|
|
283
|
+
: readdirSync(intentDirectory, {
|
|
284
|
+
withFileTypes: true,
|
|
285
|
+
})
|
|
286
|
+
.filter(entry => entry.isDirectory())
|
|
287
|
+
.map(({name}) => name);
|
|
288
|
+
|
|
289
|
+
const rows = changes.map(change => formatRow(change, summarizeLoop(readChangeDates(change))));
|
|
290
|
+
|
|
291
|
+
process.stdout.write(`${formatTable([tableHeader, ...rows]).join("\n")}\n`);
|
|
292
|
+
}
|
|
293
|
+
}
|