@bonesofspring/ai-rules 0.2.11 → 0.2.12
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/CHANGELOG.md +30 -0
- package/README.md +5 -1
- package/bin/cli.js +4 -0
- package/package.json +1 -1
- package/presets/_shared/README.md +1 -1
- package/presets/_shared/core/agent-team/agent-task-metrics.md +30 -0
- package/presets/_shared/core/agent-team/agent-team-orchestrator.md +9 -1
- package/presets/_shared/core/meta/preset-layering.md +1 -1
- package/presets/_shared/core/meta/preset-no-cross-stack-leakage.md +5 -5
- package/presets/_shared/core/meta/preset-pr-checklist.md +1 -1
- package/presets/_shared/core/meta/preset-twin-sync.md +4 -0
- package/presets/_shared/core/review/technical-retro.md +2 -0
- package/presets/claude/go/agents/build-verifier.md +2 -2
- package/presets/claude/go/agents/task-router.md +4 -0
- package/presets/claude/go/commands/task-continue.md +17 -16
- package/presets/claude/go/commands/task.md +4 -0
- package/presets/claude/go/commands/technical-retro.md +4 -0
- package/presets/claude/go/hooks/chain-team-phases.sh +226 -26
- package/presets/claude/go/rules/README.md +6 -0
- package/presets/claude/go/rules/tooling-and-review/agent-team-orchestrator.md +4 -0
- package/presets/claude/go/rules/tooling-and-review/preset-layering.md +1 -1
- package/presets/claude/go/rules/tooling-and-review/preset-no-cross-stack-leakage.md +5 -5
- package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +1 -1
- package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +4 -0
- package/presets/claude/go/rules/tooling-and-review/technical-retro.md +2 -0
- package/presets/claude/go/team/README.md +8 -0
- package/presets/claude/ios-swift/agents/build-verifier.md +3 -3
- package/presets/claude/ios-swift/agents/task-router.md +4 -0
- package/presets/claude/ios-swift/commands/task-continue.md +17 -16
- package/presets/claude/ios-swift/commands/task.md +4 -0
- package/presets/claude/ios-swift/commands/technical-retro.md +4 -0
- package/presets/claude/ios-swift/hooks/chain-team-phases.sh +226 -26
- package/presets/claude/ios-swift/rules/README.md +6 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/agent-team-orchestrator.md +4 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-layering.md +1 -1
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-no-cross-stack-leakage.md +5 -5
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +1 -1
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +4 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/technical-retro.md +2 -0
- package/presets/claude/ios-swift/team/README.md +8 -0
- package/presets/claude/java/agents/build-verifier.md +3 -3
- package/presets/claude/java/agents/task-router.md +4 -0
- package/presets/claude/java/commands/task-continue.md +17 -16
- package/presets/claude/java/commands/task.md +4 -0
- package/presets/claude/java/commands/technical-retro.md +4 -0
- package/presets/claude/java/hooks/chain-team-phases.sh +226 -26
- package/presets/claude/java/rules/README.md +6 -0
- package/presets/claude/java/rules/tooling-and-review/agent-team-orchestrator.md +4 -0
- package/presets/claude/java/rules/tooling-and-review/preset-layering.md +1 -1
- package/presets/claude/java/rules/tooling-and-review/preset-no-cross-stack-leakage.md +5 -5
- package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +1 -1
- package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +4 -0
- package/presets/claude/java/rules/tooling-and-review/technical-retro.md +2 -0
- package/presets/claude/java/team/README.md +8 -0
- package/presets/claude/mcp-ts/agents/build-verifier.md +2 -2
- package/presets/claude/mcp-ts/agents/task-router.md +4 -0
- package/presets/claude/mcp-ts/commands/task-continue.md +21 -20
- package/presets/claude/mcp-ts/commands/task.md +4 -0
- package/presets/claude/mcp-ts/commands/technical-retro.md +4 -0
- package/presets/claude/mcp-ts/hooks/chain-team-phases.sh +226 -26
- package/presets/claude/mcp-ts/rules/README.md +6 -0
- package/presets/claude/mcp-ts/rules/tooling-and-review/agent-team-orchestrator.md +4 -0
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-layering.md +1 -1
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-no-cross-stack-leakage.md +5 -5
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +1 -1
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +4 -0
- package/presets/claude/mcp-ts/rules/tooling-and-review/technical-retro.md +2 -4
- package/presets/claude/mcp-ts/team/README.md +8 -0
- package/presets/claude/next/agents/build-verifier.md +3 -3
- package/presets/claude/next/agents/task-router.md +4 -0
- package/presets/claude/next/commands/task-continue.md +17 -16
- package/presets/claude/next/commands/task.md +4 -0
- package/presets/claude/next/commands/technical-retro.md +4 -0
- package/presets/claude/next/hooks/chain-team-phases.sh +226 -26
- package/presets/claude/next/rules/README.md +6 -0
- package/presets/claude/next/rules/tooling-and-review/agent-team-orchestrator.md +4 -0
- package/presets/claude/next/rules/tooling-and-review/preset-layering.md +1 -1
- package/presets/claude/next/rules/tooling-and-review/preset-no-cross-stack-leakage.md +5 -5
- package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +1 -1
- package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +4 -0
- package/presets/claude/next/rules/tooling-and-review/technical-retro.md +2 -0
- package/presets/claude/next/team/README.md +19 -0
- package/presets/claude/php-hexagonal/CLAUDE.md +31 -0
- package/presets/claude/php-hexagonal/MCP.md +15 -0
- package/presets/claude/php-hexagonal/README.md +26 -0
- package/presets/claude/php-hexagonal/REPO_AGENTS.md +46 -0
- package/presets/claude/php-hexagonal/agents/README.md +32 -0
- package/presets/claude/php-hexagonal/agents/api-contract-reviewer.md +23 -0
- package/presets/claude/php-hexagonal/agents/build-verifier.md +45 -0
- package/presets/claude/php-hexagonal/agents/ci-investigator.md +21 -0
- package/presets/claude/php-hexagonal/agents/code-reviewer.md +40 -0
- package/presets/claude/php-hexagonal/agents/codebase-analyzer.md +28 -0
- package/presets/claude/php-hexagonal/agents/debugger.md +29 -0
- package/presets/claude/php-hexagonal/agents/feature-developer.md +47 -0
- package/presets/claude/php-hexagonal/agents/integration-test-generator.md +22 -0
- package/presets/claude/php-hexagonal/agents/integration-test-healer.md +22 -0
- package/presets/claude/php-hexagonal/agents/integration-test-planner.md +22 -0
- package/presets/claude/php-hexagonal/agents/migration-specialist.md +31 -0
- package/presets/claude/php-hexagonal/agents/performance-auditor.md +22 -0
- package/presets/claude/php-hexagonal/agents/qa-tester.md +24 -0
- package/presets/claude/php-hexagonal/agents/security-reviewer.md +20 -0
- package/presets/claude/php-hexagonal/agents/solution-architect.md +28 -0
- package/presets/claude/php-hexagonal/agents/task-analyst.md +25 -0
- package/presets/claude/php-hexagonal/agents/task-router.md +73 -0
- package/presets/claude/php-hexagonal/agents/tech-writer.md +15 -0
- package/presets/claude/php-hexagonal/agents/unit-test-generator.md +21 -0
- package/presets/claude/php-hexagonal/agents/unit-test-healer.md +13 -0
- package/presets/claude/php-hexagonal/agents/unit-test-planner.md +26 -0
- package/presets/claude/php-hexagonal/commands/README.md +13 -0
- package/presets/claude/php-hexagonal/commands/feature-continue.md +51 -0
- package/presets/claude/php-hexagonal/commands/feature-start.md +30 -0
- package/presets/claude/php-hexagonal/commands/task-continue.md +50 -0
- package/presets/claude/php-hexagonal/commands/task.md +53 -0
- package/presets/claude/php-hexagonal/commands/technical-retro.md +62 -0
- package/presets/claude/php-hexagonal/hooks/README.md +16 -0
- package/presets/claude/php-hexagonal/hooks/chain-team-phases.sh +582 -0
- package/presets/claude/php-hexagonal/hooks/examples/README.md +18 -0
- package/presets/claude/php-hexagonal/hooks/examples/format-edited.example.sh +11 -0
- package/presets/claude/php-hexagonal/hooks/examples/secret-guard.example.sh +10 -0
- package/presets/claude/php-hexagonal/hooks/examples/test-on-save.example.sh +19 -0
- package/presets/claude/php-hexagonal/hooks/guard-shell-command.sh +80 -0
- package/presets/claude/php-hexagonal/mcp.json +11 -0
- package/presets/claude/php-hexagonal/rules/README.md +67 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/README.md +14 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/adapters-driven.md +38 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/adapters-driving.md +38 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/api-grpc.md +29 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/api-http.md +36 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/messaging-adapters.md +37 -0
- package/presets/claude/php-hexagonal/rules/api-and-data/persistence-adapters.md +33 -0
- package/presets/claude/php-hexagonal/rules/architecture/README.md +16 -0
- package/presets/claude/php-hexagonal/rules/architecture/application-usecases.md +31 -0
- package/presets/claude/php-hexagonal/rules/architecture/boundaries.md +34 -0
- package/presets/claude/php-hexagonal/rules/architecture/composition-root.md +33 -0
- package/presets/claude/php-hexagonal/rules/architecture/domain-layer.md +37 -0
- package/presets/claude/php-hexagonal/rules/architecture/feature-delivery.md +53 -0
- package/presets/claude/php-hexagonal/rules/architecture/module-public-api.md +31 -0
- package/presets/claude/php-hexagonal/rules/architecture/ports-interfaces.md +34 -0
- package/presets/claude/php-hexagonal/rules/architecture/reference-features.md +39 -0
- package/presets/claude/php-hexagonal/rules/stack/README.md +10 -0
- package/presets/claude/php-hexagonal/rules/stack/php-app-core.md +35 -0
- package/presets/claude/php-hexagonal/rules/stack/php-conventions.md +51 -0
- package/presets/claude/php-hexagonal/rules/testing/README.md +11 -0
- package/presets/claude/php-hexagonal/rules/testing/e2e.md +35 -0
- package/presets/claude/php-hexagonal/rules/testing/integration.md +31 -0
- package/presets/claude/php-hexagonal/rules/testing/unit.md +32 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/README.md +21 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/agent-team-intake.md +26 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/agent-team-orchestrator.md +183 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/anti-sycophancy-discipline.md +29 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/code-quality.md +53 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/code-review.md +60 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/design-guidance.md +111 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/php-tooling.md +39 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/post-change-test.md +31 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-layering.md +36 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-no-cross-stack-leakage.md +69 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +39 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-token-budget.md +41 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +50 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/security-php.md +29 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/technical-retro.md +22 -0
- package/presets/claude/php-hexagonal/rules/ui-and-accessibility/README.md +5 -0
- package/presets/claude/php-hexagonal/skills/README.md +5 -0
- package/presets/claude/php-hexagonal/skills/ci-investigation/SKILL.md +11 -0
- package/presets/claude/php-hexagonal/skills/code-review/SKILL.md +25 -0
- package/presets/claude/php-hexagonal/skills/debug-investigation/SKILL.md +15 -0
- package/presets/claude/php-hexagonal/skills/feature-delivery/SKILL.md +30 -0
- package/presets/claude/php-hexagonal/skills/integration-testing/SKILL.md +15 -0
- package/presets/claude/php-hexagonal/skills/technical-retro/SKILL.md +54 -0
- package/presets/claude/php-hexagonal/skills/unit-testing/SKILL.md +14 -0
- package/presets/claude/php-hexagonal/skills/write-adr/SKILL.md +41 -0
- package/presets/claude/php-hexagonal/team/README.md +37 -0
- package/presets/claude/php-hexagonal/team/conventions.md +21 -0
- package/presets/claude/php-hexagonal/team/fixtures/bugfix-standard.json +37 -0
- package/presets/claude/php-hexagonal/team/fixtures/feature-full.json +25 -0
- package/presets/claude/php-hexagonal/team/fixtures/feature-light.json +17 -0
- package/presets/claude/php-laravel/CLAUDE.md +20 -0
- package/presets/claude/php-laravel/MCP.md +5 -0
- package/presets/claude/php-laravel/README.md +30 -0
- package/presets/claude/php-laravel/REPO_AGENTS.md +47 -0
- package/presets/claude/php-laravel/agents/README.md +32 -0
- package/presets/claude/php-laravel/agents/api-contract-reviewer.md +23 -0
- package/presets/claude/php-laravel/agents/build-verifier.md +45 -0
- package/presets/claude/php-laravel/agents/ci-investigator.md +21 -0
- package/presets/claude/php-laravel/agents/code-reviewer.md +40 -0
- package/presets/claude/php-laravel/agents/codebase-analyzer.md +28 -0
- package/presets/claude/php-laravel/agents/debugger.md +29 -0
- package/presets/claude/php-laravel/agents/feature-developer.md +47 -0
- package/presets/claude/php-laravel/agents/integration-test-generator.md +22 -0
- package/presets/claude/php-laravel/agents/integration-test-healer.md +22 -0
- package/presets/claude/php-laravel/agents/integration-test-planner.md +22 -0
- package/presets/claude/php-laravel/agents/migration-specialist.md +31 -0
- package/presets/claude/php-laravel/agents/performance-auditor.md +22 -0
- package/presets/claude/php-laravel/agents/qa-tester.md +30 -0
- package/presets/claude/php-laravel/agents/security-reviewer.md +32 -0
- package/presets/claude/php-laravel/agents/solution-architect.md +29 -0
- package/presets/claude/php-laravel/agents/task-analyst.md +25 -0
- package/presets/claude/php-laravel/agents/task-router.md +92 -0
- package/presets/claude/php-laravel/agents/tech-writer.md +15 -0
- package/presets/claude/php-laravel/agents/unit-test-generator.md +21 -0
- package/presets/claude/php-laravel/agents/unit-test-healer.md +13 -0
- package/presets/claude/php-laravel/agents/unit-test-planner.md +26 -0
- package/presets/claude/php-laravel/commands/README.md +13 -0
- package/presets/claude/php-laravel/commands/feature-continue.md +51 -0
- package/presets/claude/php-laravel/commands/feature-start.md +30 -0
- package/presets/claude/php-laravel/commands/task-continue.md +50 -0
- package/presets/claude/php-laravel/commands/task.md +53 -0
- package/presets/claude/php-laravel/commands/technical-retro.md +62 -0
- package/presets/claude/php-laravel/hooks/README.md +16 -0
- package/presets/claude/php-laravel/hooks/chain-team-phases.sh +582 -0
- package/presets/claude/php-laravel/hooks/examples/README.md +18 -0
- package/presets/claude/php-laravel/hooks/examples/format-edited.example.sh +11 -0
- package/presets/claude/php-laravel/hooks/examples/secret-guard.example.sh +10 -0
- package/presets/claude/php-laravel/hooks/examples/test-on-save.example.sh +19 -0
- package/presets/claude/php-laravel/hooks/guard-shell-command.sh +80 -0
- package/presets/claude/php-laravel/mcp.json +11 -0
- package/presets/claude/php-laravel/rules/README.md +67 -0
- package/presets/claude/php-laravel/rules/api-and-data/README.md +3 -0
- package/presets/claude/php-laravel/rules/api-and-data/eloquent-persistence.md +31 -0
- package/presets/claude/php-laravel/rules/api-and-data/events-listeners.md +31 -0
- package/presets/claude/php-laravel/rules/api-and-data/form-requests.md +29 -0
- package/presets/claude/php-laravel/rules/api-and-data/http-layer.md +32 -0
- package/presets/claude/php-laravel/rules/api-and-data/jobs-queues.md +41 -0
- package/presets/claude/php-laravel/rules/api-and-data/policies-authorization.md +29 -0
- package/presets/claude/php-laravel/rules/api-and-data/routes-api.md +29 -0
- package/presets/claude/php-laravel/rules/architecture/README.md +3 -0
- package/presets/claude/php-laravel/rules/architecture/actions-services.md +30 -0
- package/presets/claude/php-laravel/rules/architecture/boundaries.md +34 -0
- package/presets/claude/php-laravel/rules/architecture/domain-layer.md +24 -0
- package/presets/claude/php-laravel/rules/architecture/feature-delivery.md +51 -0
- package/presets/claude/php-laravel/rules/architecture/module-public-api.md +29 -0
- package/presets/claude/php-laravel/rules/architecture/reference-features.md +40 -0
- package/presets/claude/php-laravel/rules/architecture/service-providers.md +28 -0
- package/presets/claude/php-laravel/rules/stack/README.md +3 -0
- package/presets/claude/php-laravel/rules/stack/laravel-app-core.md +32 -0
- package/presets/claude/php-laravel/rules/stack/laravel-conventions.md +38 -0
- package/presets/claude/php-laravel/rules/testing/README.md +3 -0
- package/presets/claude/php-laravel/rules/testing/e2e.md +34 -0
- package/presets/claude/php-laravel/rules/testing/integration.md +30 -0
- package/presets/claude/php-laravel/rules/testing/unit.md +30 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/README.md +3 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/agent-team-intake.md +21 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/agent-team-orchestrator.md +180 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/anti-sycophancy-discipline.md +29 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/code-quality.md +52 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/code-review.md +58 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/design-guidance.md +111 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/php-tooling.md +37 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/post-change-test.md +29 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-layering.md +33 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-no-cross-stack-leakage.md +66 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +36 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-token-budget.md +38 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +47 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/security-php.md +58 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/technical-retro.md +17 -0
- package/presets/claude/php-laravel/rules/ui-and-accessibility/README.md +3 -0
- package/presets/claude/php-laravel/skills/README.md +5 -0
- package/presets/claude/php-laravel/skills/ci-investigation/SKILL.md +11 -0
- package/presets/claude/php-laravel/skills/code-review/SKILL.md +25 -0
- package/presets/claude/php-laravel/skills/debug-investigation/SKILL.md +15 -0
- package/presets/claude/php-laravel/skills/feature-delivery/SKILL.md +30 -0
- package/presets/claude/php-laravel/skills/integration-testing/SKILL.md +15 -0
- package/presets/claude/php-laravel/skills/technical-retro/SKILL.md +54 -0
- package/presets/claude/php-laravel/skills/unit-testing/SKILL.md +14 -0
- package/presets/claude/php-laravel/skills/write-adr/SKILL.md +41 -0
- package/presets/claude/php-laravel/team/README.md +17 -0
- package/presets/claude/php-laravel/team/conventions.md +21 -0
- package/presets/claude/php-laravel/team/fixtures/bugfix-standard.json +12 -0
- package/presets/claude/php-laravel/team/fixtures/feature-full.json +15 -0
- package/presets/claude/php-laravel/team/fixtures/feature-light.json +8 -0
- package/presets/cursor/go/agents/build-verifier.md +2 -2
- package/presets/cursor/go/agents/task-router.md +4 -0
- package/presets/cursor/go/commands/task-continue.md +17 -16
- package/presets/cursor/go/commands/task.md +4 -0
- package/presets/cursor/go/commands/technical-retro.md +4 -0
- package/presets/cursor/go/hooks/chain-team-phases.sh +226 -26
- package/presets/cursor/go/rules/README.md +6 -0
- package/presets/cursor/go/rules/agent-team-orchestrator.mdc +4 -0
- package/presets/cursor/go/rules/preset-layering.mdc +1 -1
- package/presets/cursor/go/rules/preset-no-cross-stack-leakage.mdc +5 -5
- package/presets/cursor/go/rules/preset-pr-checklist.mdc +1 -1
- package/presets/cursor/go/rules/preset-twin-sync.mdc +4 -0
- package/presets/cursor/go/rules/technical-retro.mdc +2 -0
- package/presets/cursor/go/team/README.md +8 -0
- package/presets/cursor/ios-swift/agents/build-verifier.md +3 -3
- package/presets/cursor/ios-swift/agents/task-router.md +4 -0
- package/presets/cursor/ios-swift/commands/task-continue.md +17 -16
- package/presets/cursor/ios-swift/commands/task.md +4 -0
- package/presets/cursor/ios-swift/commands/technical-retro.md +4 -0
- package/presets/cursor/ios-swift/hooks/chain-team-phases.sh +226 -26
- package/presets/cursor/ios-swift/rules/README.md +6 -0
- package/presets/cursor/ios-swift/rules/agent-team-orchestrator.mdc +4 -0
- package/presets/cursor/ios-swift/rules/preset-layering.mdc +1 -1
- package/presets/cursor/ios-swift/rules/preset-no-cross-stack-leakage.mdc +5 -5
- package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +1 -1
- package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +4 -0
- package/presets/cursor/ios-swift/rules/technical-retro.mdc +2 -0
- package/presets/cursor/ios-swift/team/README.md +8 -0
- package/presets/cursor/java/agents/build-verifier.md +3 -3
- package/presets/cursor/java/agents/task-router.md +4 -0
- package/presets/cursor/java/commands/task-continue.md +17 -16
- package/presets/cursor/java/commands/task.md +4 -0
- package/presets/cursor/java/commands/technical-retro.md +4 -0
- package/presets/cursor/java/hooks/chain-team-phases.sh +226 -26
- package/presets/cursor/java/rules/README.md +6 -0
- package/presets/cursor/java/rules/agent-team-orchestrator.mdc +4 -0
- package/presets/cursor/java/rules/preset-layering.mdc +1 -1
- package/presets/cursor/java/rules/preset-no-cross-stack-leakage.mdc +5 -5
- package/presets/cursor/java/rules/preset-pr-checklist.mdc +1 -1
- package/presets/cursor/java/rules/preset-twin-sync.mdc +4 -0
- package/presets/cursor/java/rules/technical-retro.mdc +2 -0
- package/presets/cursor/java/team/README.md +8 -0
- package/presets/cursor/mcp-ts/agents/build-verifier.md +2 -2
- package/presets/cursor/mcp-ts/agents/task-router.md +4 -0
- package/presets/cursor/mcp-ts/commands/task-continue.md +19 -18
- package/presets/cursor/mcp-ts/commands/task.md +4 -0
- package/presets/cursor/mcp-ts/commands/technical-retro.md +4 -0
- package/presets/cursor/mcp-ts/hooks/chain-team-phases.sh +226 -26
- package/presets/cursor/mcp-ts/rules/README.md +6 -0
- package/presets/cursor/mcp-ts/rules/agent-team-orchestrator.mdc +4 -0
- package/presets/cursor/mcp-ts/rules/preset-layering.mdc +1 -1
- package/presets/cursor/mcp-ts/rules/preset-no-cross-stack-leakage.mdc +5 -5
- package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +1 -1
- package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +4 -0
- package/presets/cursor/mcp-ts/rules/technical-retro.mdc +2 -4
- package/presets/cursor/mcp-ts/team/README.md +8 -0
- package/presets/cursor/next/agents/build-verifier.md +3 -3
- package/presets/cursor/next/agents/task-router.md +4 -0
- package/presets/cursor/next/commands/task-continue.md +17 -16
- package/presets/cursor/next/commands/task.md +4 -0
- package/presets/cursor/next/commands/technical-retro.md +4 -0
- package/presets/cursor/next/hooks/chain-team-phases.sh +230 -26
- package/presets/cursor/next/rules/README.md +6 -0
- package/presets/cursor/next/rules/agent-team-orchestrator.mdc +4 -0
- package/presets/cursor/next/rules/preset-layering.mdc +1 -1
- package/presets/cursor/next/rules/preset-no-cross-stack-leakage.mdc +5 -5
- package/presets/cursor/next/rules/preset-pr-checklist.mdc +1 -1
- package/presets/cursor/next/rules/preset-twin-sync.mdc +4 -0
- package/presets/cursor/next/rules/technical-retro.mdc +2 -0
- package/presets/cursor/next/team/README.md +19 -0
- package/presets/cursor/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
- package/presets/cursor/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
- package/presets/cursor/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
- package/presets/cursor/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
- package/presets/cursor/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
- package/presets/cursor/next/team/fixtures/metrics.example.json +54 -0
- package/presets/cursor/php-hexagonal/AGENTS.md +39 -0
- package/presets/cursor/php-hexagonal/BUGBOT.md +11 -0
- package/presets/cursor/php-hexagonal/MCP.md +15 -0
- package/presets/cursor/php-hexagonal/README.md +26 -0
- package/presets/cursor/php-hexagonal/REPO_AGENTS.md +46 -0
- package/presets/cursor/php-hexagonal/agents/README.md +36 -0
- package/presets/cursor/php-hexagonal/agents/api-contract-reviewer.md +23 -0
- package/presets/cursor/php-hexagonal/agents/build-verifier.md +45 -0
- package/presets/cursor/php-hexagonal/agents/ci-investigator.md +21 -0
- package/presets/cursor/php-hexagonal/agents/code-reviewer.md +40 -0
- package/presets/cursor/php-hexagonal/agents/codebase-analyzer.md +28 -0
- package/presets/cursor/php-hexagonal/agents/debugger.md +29 -0
- package/presets/cursor/php-hexagonal/agents/feature-developer.md +47 -0
- package/presets/cursor/php-hexagonal/agents/integration-test-generator.md +22 -0
- package/presets/cursor/php-hexagonal/agents/integration-test-healer.md +22 -0
- package/presets/cursor/php-hexagonal/agents/integration-test-planner.md +22 -0
- package/presets/cursor/php-hexagonal/agents/migration-specialist.md +31 -0
- package/presets/cursor/php-hexagonal/agents/performance-auditor.md +22 -0
- package/presets/cursor/php-hexagonal/agents/qa-tester.md +24 -0
- package/presets/cursor/php-hexagonal/agents/security-reviewer.md +20 -0
- package/presets/cursor/php-hexagonal/agents/solution-architect.md +28 -0
- package/presets/cursor/php-hexagonal/agents/task-analyst.md +25 -0
- package/presets/cursor/php-hexagonal/agents/task-router.md +73 -0
- package/presets/cursor/php-hexagonal/agents/tech-writer.md +15 -0
- package/presets/cursor/php-hexagonal/agents/unit-test-generator.md +21 -0
- package/presets/cursor/php-hexagonal/agents/unit-test-healer.md +13 -0
- package/presets/cursor/php-hexagonal/agents/unit-test-planner.md +26 -0
- package/presets/cursor/php-hexagonal/commands/README.md +54 -0
- package/presets/cursor/php-hexagonal/commands/feature-continue.md +19 -0
- package/presets/cursor/php-hexagonal/commands/feature-start.md +33 -0
- package/presets/cursor/php-hexagonal/commands/task-continue.md +50 -0
- package/presets/cursor/php-hexagonal/commands/task.md +53 -0
- package/presets/cursor/php-hexagonal/commands/technical-retro.md +85 -0
- package/presets/cursor/php-hexagonal/hooks/README.md +12 -0
- package/presets/cursor/php-hexagonal/hooks/chain-team-phases.sh +582 -0
- package/presets/cursor/php-hexagonal/hooks/examples/README.md +18 -0
- package/presets/cursor/php-hexagonal/hooks/examples/format-edited.example.sh +11 -0
- package/presets/cursor/php-hexagonal/hooks/examples/secret-guard.example.sh +10 -0
- package/presets/cursor/php-hexagonal/hooks/examples/test-on-save.example.sh +19 -0
- package/presets/cursor/php-hexagonal/hooks/guard-shell-command.sh +80 -0
- package/presets/cursor/php-hexagonal/hooks.json +17 -0
- package/presets/cursor/php-hexagonal/mcp.json +11 -0
- package/presets/cursor/php-hexagonal/rules/README.md +106 -0
- package/presets/cursor/php-hexagonal/rules/adapters-driven.mdc +41 -0
- package/presets/cursor/php-hexagonal/rules/adapters-driving.mdc +42 -0
- package/presets/cursor/php-hexagonal/rules/agent-team-intake.mdc +20 -0
- package/presets/cursor/php-hexagonal/rules/agent-team-orchestrator.mdc +177 -0
- package/presets/cursor/php-hexagonal/rules/anti-sycophancy-discipline.mdc +30 -0
- package/presets/cursor/php-hexagonal/rules/api-grpc.mdc +31 -0
- package/presets/cursor/php-hexagonal/rules/api-http.mdc +40 -0
- package/presets/cursor/php-hexagonal/rules/application-usecases.mdc +33 -0
- package/presets/cursor/php-hexagonal/rules/architecture-boundaries.mdc +36 -0
- package/presets/cursor/php-hexagonal/rules/code-quality-and-refactoring.mdc +54 -0
- package/presets/cursor/php-hexagonal/rules/code-review-mr.mdc +54 -0
- package/presets/cursor/php-hexagonal/rules/composition-root.mdc +37 -0
- package/presets/cursor/php-hexagonal/rules/design-guidance.mdc +112 -0
- package/presets/cursor/php-hexagonal/rules/domain-layer.mdc +39 -0
- package/presets/cursor/php-hexagonal/rules/feature-delivery-workflow.mdc +47 -0
- package/presets/cursor/php-hexagonal/rules/messaging-adapters.mdc +38 -0
- package/presets/cursor/php-hexagonal/rules/module-public-api.mdc +32 -0
- package/presets/cursor/php-hexagonal/rules/persistence-adapters.mdc +35 -0
- package/presets/cursor/php-hexagonal/rules/php-app-core.mdc +37 -0
- package/presets/cursor/php-hexagonal/rules/php-conventions.mdc +53 -0
- package/presets/cursor/php-hexagonal/rules/php-tooling.mdc +38 -0
- package/presets/cursor/php-hexagonal/rules/ports-interfaces.mdc +37 -0
- package/presets/cursor/php-hexagonal/rules/post-change-test.mdc +30 -0
- package/presets/cursor/php-hexagonal/rules/preset-layering.mdc +36 -0
- package/presets/cursor/php-hexagonal/rules/preset-no-cross-stack-leakage.mdc +69 -0
- package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +39 -0
- package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +41 -0
- package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +50 -0
- package/presets/cursor/php-hexagonal/rules/reference-features.mdc +40 -0
- package/presets/cursor/php-hexagonal/rules/security-php.mdc +33 -0
- package/presets/cursor/php-hexagonal/rules/technical-retro.mdc +16 -0
- package/presets/cursor/php-hexagonal/rules/tests-e2e.mdc +36 -0
- package/presets/cursor/php-hexagonal/rules/tests-integration.mdc +33 -0
- package/presets/cursor/php-hexagonal/rules/tests-unit.mdc +35 -0
- package/presets/cursor/php-hexagonal/skills/README.md +5 -0
- package/presets/cursor/php-hexagonal/skills/ci-investigation/SKILL.md +11 -0
- package/presets/cursor/php-hexagonal/skills/code-review/SKILL.md +25 -0
- package/presets/cursor/php-hexagonal/skills/debug-investigation/SKILL.md +15 -0
- package/presets/cursor/php-hexagonal/skills/feature-delivery/SKILL.md +30 -0
- package/presets/cursor/php-hexagonal/skills/integration-testing/SKILL.md +15 -0
- package/presets/cursor/php-hexagonal/skills/technical-retro/SKILL.md +54 -0
- package/presets/cursor/php-hexagonal/skills/unit-testing/SKILL.md +14 -0
- package/presets/cursor/php-hexagonal/skills/write-adr/SKILL.md +41 -0
- package/presets/cursor/php-hexagonal/team/README.md +41 -0
- package/presets/cursor/php-hexagonal/team/conventions.md +21 -0
- package/presets/cursor/php-hexagonal/team/fixtures/bugfix-standard.json +37 -0
- package/presets/cursor/php-hexagonal/team/fixtures/feature-full.json +25 -0
- package/presets/cursor/php-hexagonal/team/fixtures/feature-light.json +17 -0
- package/presets/cursor/php-laravel/AGENTS.md +39 -0
- package/presets/cursor/php-laravel/BUGBOT.md +9 -0
- package/presets/cursor/php-laravel/MCP.md +5 -0
- package/presets/cursor/php-laravel/README.md +33 -0
- package/presets/cursor/php-laravel/REPO_AGENTS.md +47 -0
- package/presets/cursor/php-laravel/agents/README.md +36 -0
- package/presets/cursor/php-laravel/agents/api-contract-reviewer.md +23 -0
- package/presets/cursor/php-laravel/agents/build-verifier.md +45 -0
- package/presets/cursor/php-laravel/agents/ci-investigator.md +21 -0
- package/presets/cursor/php-laravel/agents/code-reviewer.md +40 -0
- package/presets/cursor/php-laravel/agents/codebase-analyzer.md +28 -0
- package/presets/cursor/php-laravel/agents/debugger.md +29 -0
- package/presets/cursor/php-laravel/agents/feature-developer.md +47 -0
- package/presets/cursor/php-laravel/agents/integration-test-generator.md +22 -0
- package/presets/cursor/php-laravel/agents/integration-test-healer.md +22 -0
- package/presets/cursor/php-laravel/agents/integration-test-planner.md +22 -0
- package/presets/cursor/php-laravel/agents/migration-specialist.md +31 -0
- package/presets/cursor/php-laravel/agents/performance-auditor.md +22 -0
- package/presets/cursor/php-laravel/agents/qa-tester.md +30 -0
- package/presets/cursor/php-laravel/agents/security-reviewer.md +32 -0
- package/presets/cursor/php-laravel/agents/solution-architect.md +29 -0
- package/presets/cursor/php-laravel/agents/task-analyst.md +25 -0
- package/presets/cursor/php-laravel/agents/task-router.md +92 -0
- package/presets/cursor/php-laravel/agents/tech-writer.md +15 -0
- package/presets/cursor/php-laravel/agents/unit-test-generator.md +21 -0
- package/presets/cursor/php-laravel/agents/unit-test-healer.md +13 -0
- package/presets/cursor/php-laravel/agents/unit-test-planner.md +26 -0
- package/presets/cursor/php-laravel/commands/README.md +54 -0
- package/presets/cursor/php-laravel/commands/feature-continue.md +19 -0
- package/presets/cursor/php-laravel/commands/feature-start.md +33 -0
- package/presets/cursor/php-laravel/commands/task-continue.md +50 -0
- package/presets/cursor/php-laravel/commands/task.md +53 -0
- package/presets/cursor/php-laravel/commands/technical-retro.md +85 -0
- package/presets/cursor/php-laravel/hooks/README.md +12 -0
- package/presets/cursor/php-laravel/hooks/chain-team-phases.sh +582 -0
- package/presets/cursor/php-laravel/hooks/examples/README.md +18 -0
- package/presets/cursor/php-laravel/hooks/examples/format-edited.example.sh +11 -0
- package/presets/cursor/php-laravel/hooks/examples/secret-guard.example.sh +10 -0
- package/presets/cursor/php-laravel/hooks/examples/test-on-save.example.sh +19 -0
- package/presets/cursor/php-laravel/hooks/guard-shell-command.sh +80 -0
- package/presets/cursor/php-laravel/hooks.json +17 -0
- package/presets/cursor/php-laravel/mcp.json +11 -0
- package/presets/cursor/php-laravel/rules/README.md +111 -0
- package/presets/cursor/php-laravel/rules/actions-services.mdc +33 -0
- package/presets/cursor/php-laravel/rules/agent-team-intake.mdc +20 -0
- package/presets/cursor/php-laravel/rules/agent-team-orchestrator.mdc +178 -0
- package/presets/cursor/php-laravel/rules/anti-sycophancy-discipline.mdc +30 -0
- package/presets/cursor/php-laravel/rules/architecture-boundaries.mdc +37 -0
- package/presets/cursor/php-laravel/rules/code-quality-and-refactoring.mdc +54 -0
- package/presets/cursor/php-laravel/rules/code-review-mr.mdc +56 -0
- package/presets/cursor/php-laravel/rules/design-guidance.mdc +112 -0
- package/presets/cursor/php-laravel/rules/domain-layer.mdc +27 -0
- package/presets/cursor/php-laravel/rules/eloquent-persistence.mdc +34 -0
- package/presets/cursor/php-laravel/rules/events-listeners.mdc +34 -0
- package/presets/cursor/php-laravel/rules/feature-delivery-workflow.mdc +50 -0
- package/presets/cursor/php-laravel/rules/form-requests.mdc +32 -0
- package/presets/cursor/php-laravel/rules/http-layer.mdc +35 -0
- package/presets/cursor/php-laravel/rules/jobs-queues.mdc +44 -0
- package/presets/cursor/php-laravel/rules/laravel-app-core.mdc +35 -0
- package/presets/cursor/php-laravel/rules/laravel-conventions.mdc +41 -0
- package/presets/cursor/php-laravel/rules/module-public-api.mdc +31 -0
- package/presets/cursor/php-laravel/rules/php-tooling.mdc +39 -0
- package/presets/cursor/php-laravel/rules/policies-authorization.mdc +32 -0
- package/presets/cursor/php-laravel/rules/post-change-test.mdc +31 -0
- package/presets/cursor/php-laravel/rules/preset-layering.mdc +36 -0
- package/presets/cursor/php-laravel/rules/preset-no-cross-stack-leakage.mdc +69 -0
- package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +39 -0
- package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +41 -0
- package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +50 -0
- package/presets/cursor/php-laravel/rules/reference-features.mdc +42 -0
- package/presets/cursor/php-laravel/rules/routes-api.mdc +32 -0
- package/presets/cursor/php-laravel/rules/security-php.mdc +61 -0
- package/presets/cursor/php-laravel/rules/service-providers.mdc +31 -0
- package/presets/cursor/php-laravel/rules/technical-retro.mdc +16 -0
- package/presets/cursor/php-laravel/rules/tests-e2e.mdc +36 -0
- package/presets/cursor/php-laravel/rules/tests-integration.mdc +33 -0
- package/presets/cursor/php-laravel/rules/tests-unit.mdc +33 -0
- package/presets/cursor/php-laravel/skills/README.md +5 -0
- package/presets/cursor/php-laravel/skills/ci-investigation/SKILL.md +11 -0
- package/presets/cursor/php-laravel/skills/code-review/SKILL.md +25 -0
- package/presets/cursor/php-laravel/skills/debug-investigation/SKILL.md +15 -0
- package/presets/cursor/php-laravel/skills/feature-delivery/SKILL.md +30 -0
- package/presets/cursor/php-laravel/skills/integration-testing/SKILL.md +15 -0
- package/presets/cursor/php-laravel/skills/technical-retro/SKILL.md +54 -0
- package/presets/cursor/php-laravel/skills/unit-testing/SKILL.md +14 -0
- package/presets/cursor/php-laravel/skills/write-adr/SKILL.md +41 -0
- package/presets/cursor/php-laravel/team/README.md +17 -0
- package/presets/cursor/php-laravel/team/conventions.md +21 -0
- package/presets/cursor/php-laravel/team/fixtures/bugfix-standard.json +12 -0
- package/presets/cursor/php-laravel/team/fixtures/feature-full.json +15 -0
- package/presets/cursor/php-laravel/team/fixtures/feature-light.json +8 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# preset: php-laravel (laravel) — chain handoffs use integration-test-* + post-change-test + php-tooling
|
|
3
|
+
# php-laravel-verify-gate: php artisan test | composer test | pint/phpstan if configured — never yarn lint / playwright / browser e2e
|
|
4
|
+
# Chains agent team steps from pipeline.json after subagentStop.
|
|
5
|
+
# Falls back to legacy phase-based flow when pipeline.json is absent.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
input=$(cat)
|
|
10
|
+
|
|
11
|
+
INPUT="$input" node <<'NODE'
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const TEAM_ROOT = path.join(process.cwd(), '.cursor', 'team');
|
|
16
|
+
|
|
17
|
+
const inputRaw = process.env.INPUT || '';
|
|
18
|
+
let hookInput;
|
|
19
|
+
try {
|
|
20
|
+
hookInput = JSON.parse(inputRaw);
|
|
21
|
+
} catch {
|
|
22
|
+
process.stdout.write('{}');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (hookInput.status !== 'completed') {
|
|
27
|
+
process.stdout.write('{}');
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const activePath = path.join(TEAM_ROOT, 'active-task.json');
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(activePath)) {
|
|
34
|
+
process.stdout.write('{}');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let active;
|
|
39
|
+
try {
|
|
40
|
+
active = JSON.parse(fs.readFileSync(activePath, 'utf8'));
|
|
41
|
+
} catch {
|
|
42
|
+
process.stdout.write('{}');
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const slug = active.slug;
|
|
47
|
+
if (!slug) {
|
|
48
|
+
process.stdout.write('{}');
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const taskDir = path.join(TEAM_ROOT, 'tasks', slug);
|
|
53
|
+
const statusPath = path.join(taskDir, 'status.json');
|
|
54
|
+
const pipelinePath = path.join(taskDir, 'pipeline.json');
|
|
55
|
+
const METRICS_PATH = path.join(taskDir, 'metrics.json');
|
|
56
|
+
const METRICS_LOCK = `${METRICS_PATH}.lock`;
|
|
57
|
+
|
|
58
|
+
if (!fs.existsSync(statusPath)) {
|
|
59
|
+
process.stdout.write('{}');
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let status;
|
|
64
|
+
const now = new Date().toISOString();
|
|
65
|
+
const STACK = 'php-laravel';
|
|
66
|
+
const PLATFORM = 'cursor';
|
|
67
|
+
|
|
68
|
+
function waitForMetricsLock() {
|
|
69
|
+
for (let attempt = 0; attempt < 200; attempt += 1) {
|
|
70
|
+
try { fs.mkdirSync(METRICS_LOCK); return; } catch (error) {
|
|
71
|
+
if (error && error.code !== 'EEXIST') throw error;
|
|
72
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new Error('metrics lock timeout');
|
|
76
|
+
}
|
|
77
|
+
let taskLockHeld = false;
|
|
78
|
+
function releaseTaskLock() {
|
|
79
|
+
if (!taskLockHeld) return;
|
|
80
|
+
taskLockHeld = false;
|
|
81
|
+
try { fs.rmdirSync(METRICS_LOCK); } catch {}
|
|
82
|
+
}
|
|
83
|
+
waitForMetricsLock();
|
|
84
|
+
taskLockHeld = true;
|
|
85
|
+
process.on('exit', releaseTaskLock);
|
|
86
|
+
try {
|
|
87
|
+
status = JSON.parse(fs.readFileSync(statusPath, 'utf8'));
|
|
88
|
+
} catch {
|
|
89
|
+
releaseTaskLock();
|
|
90
|
+
process.stdout.write('{}');
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
function mutateMetrics(mutator) {
|
|
94
|
+
const acquiredHere = !taskLockHeld;
|
|
95
|
+
if (acquiredHere) waitForMetricsLock();
|
|
96
|
+
try {
|
|
97
|
+
let metrics;
|
|
98
|
+
try { metrics = JSON.parse(fs.readFileSync(METRICS_PATH, 'utf8')); } catch {
|
|
99
|
+
const runId = status.metricsRunId || `${slug}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
100
|
+
status.metricsRunId = runId;
|
|
101
|
+
metrics = { schemaVersion: 1, task: { runId, slug, preset: 'ai-rules', stack: STACK, platform: PLATFORM, intent: status.intent || 'unknown', profile: 'unknown' }, events: [] };
|
|
102
|
+
metrics.events.push({ eventId: `${runId}:task_started`, type: 'task_started', at: now, source: 'hook', attributionConfidence: 'high' });
|
|
103
|
+
}
|
|
104
|
+
mutator(metrics);
|
|
105
|
+
const temporary = `${METRICS_PATH}.${process.pid}.${Date.now()}.tmp`;
|
|
106
|
+
fs.writeFileSync(temporary, JSON.stringify(metrics, null, 2) + '\n');
|
|
107
|
+
fs.renameSync(temporary, METRICS_PATH);
|
|
108
|
+
} finally {
|
|
109
|
+
if (acquiredHere) { try { fs.rmdirSync(METRICS_LOCK); } catch {} }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function appendMetric(event) { mutateMetrics((metrics) => metrics.events.push({ eventId: `${metrics.task.runId}:${Date.now()}:${Math.random().toString(16).slice(2)}`, ...event })); }
|
|
113
|
+
function nextAttemptOrdinal(stepIndex, agent) {
|
|
114
|
+
try {
|
|
115
|
+
const metrics = JSON.parse(fs.readFileSync(METRICS_PATH, 'utf8'));
|
|
116
|
+
return metrics.events.reduce((highest, event) => event.type === 'attempt_started' && event.stepIndex === stepIndex && event.agent === agent ? Math.max(highest, Number(event.attempt) || 0) : highest, 0) + 1;
|
|
117
|
+
} catch { return 1; }
|
|
118
|
+
}
|
|
119
|
+
function roleFor(agent) {
|
|
120
|
+
if (/analyst|architect|router|migration/.test(agent || '')) return 'planning';
|
|
121
|
+
if (/verifier/.test(agent || '')) return 'validation';
|
|
122
|
+
if (/reviewer|auditor/.test(agent || '')) return 'review';
|
|
123
|
+
if (/test|qa/.test(agent || '')) return 'testing';
|
|
124
|
+
if (/writer/.test(agent || '')) return 'docs';
|
|
125
|
+
return 'implementation';
|
|
126
|
+
}
|
|
127
|
+
function activateAttempts(step, stepIndex) {
|
|
128
|
+
const active = { ...(status.activeAttempts || {}) }; const agents = getStepAgents(step);
|
|
129
|
+
const parallelGroupId = step.parallel && agents.length > 1 ? `${status.metricsRunId || slug}:step-${stepIndex}` : null;
|
|
130
|
+
for (const agent of agents) {
|
|
131
|
+
const attempt = nextAttemptOrdinal(stepIndex, agent); const runId = status.metricsRunId || `${slug}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
132
|
+
status.metricsRunId = runId; const attemptId = `${runId}:step-${stepIndex}:${agent}:attempt-${attempt}`;
|
|
133
|
+
if (active[attemptId]) continue;
|
|
134
|
+
active[attemptId] = { agent, stepIndex, attempt, startedAt: now, parallelGroupId };
|
|
135
|
+
appendMetric({ type: 'attempt_started', at: now, attemptId, stepIndex, stepLabel: step.label || null, agent, role: roleFor(agent), attempt, parallelGroupId, source: 'hook', attributionConfidence: 'high' });
|
|
136
|
+
}
|
|
137
|
+
status.activeAttempts = active;
|
|
138
|
+
}
|
|
139
|
+
function resolvedAttempt() {
|
|
140
|
+
const active = status.activeAttempts || {}; const entries = Object.entries(active);
|
|
141
|
+
const stableAttemptId = hookInput.attemptId;
|
|
142
|
+
if (stableAttemptId) {
|
|
143
|
+
return active[stableAttemptId] ? { attemptId: stableAttemptId, entry: active[stableAttemptId], confidence: 'high' } : null;
|
|
144
|
+
}
|
|
145
|
+
if (entries.length === 1) return { attemptId: entries[0][0], entry: entries[0][1], confidence: 'high' };
|
|
146
|
+
try { const receipts = JSON.parse(fs.readFileSync(path.join(taskDir, '.metrics-receipts.json'), 'utf8')); const receipt = Array.isArray(receipts) ? receipts.find((item) => active[item.attemptId]) : null; if (receipt) return { attemptId: receipt.attemptId, entry: active[receipt.attemptId], confidence: 'fallback', source: 'agent' }; } catch {}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
function finishActiveAttempt() {
|
|
150
|
+
const resolved = resolvedAttempt(); if (!resolved) return null;
|
|
151
|
+
const durationMs = Date.parse(now) - Date.parse(resolved.entry.startedAt);
|
|
152
|
+
appendMetric({ type: 'attempt_finished', at: now, attemptId: resolved.attemptId, outcome: ['completed', 'changes_requested', 'validation_failed'].includes(status.state) ? status.state : 'unknown', durationMs: Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : null, retryAfterFix: status.retryAfterFix || null, source: resolved.source || 'hook', attributionConfidence: resolved.confidence });
|
|
153
|
+
const active = { ...(status.activeAttempts || {}) }; delete active[resolved.attemptId]; status.activeAttempts = active; status.lastCompletedAttempt = { attemptId: resolved.attemptId, completedAt: now }; return resolved;
|
|
154
|
+
}
|
|
155
|
+
function beginHumanGate() { const eventId = `${status.metricsRunId || slug}:gate:${Date.now()}`; appendMetric({ eventId, type: 'human_gate_started', at: now, source: 'hook', attributionConfidence: 'high' }); status.activeHumanGateEventId = eventId; }
|
|
156
|
+
function endHumanGateIfResumed() {
|
|
157
|
+
if (!status.activeHumanGateEventId || status.awaitingHumanGate || status.state === 'awaiting_approval') return;
|
|
158
|
+
appendMetric({ type: 'human_gate_ended', at: now, gateStartedEventId: status.activeHumanGateEventId, source: 'hook', attributionConfidence: 'high' });
|
|
159
|
+
delete status.activeHumanGateEventId;
|
|
160
|
+
}
|
|
161
|
+
function completeTaskMetrics() { appendMetric({ type: 'task_completed', at: now, outcome: status.state === 'completed' ? 'completed' : 'unknown', source: 'hook', attributionConfidence: 'high' }); }
|
|
162
|
+
function recordSkippedStep(step, stepIndex) {
|
|
163
|
+
for (const agent of getStepAgents(step)) {
|
|
164
|
+
const runId = status.metricsRunId || `${slug}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
165
|
+
status.metricsRunId = runId;
|
|
166
|
+
const attempt = nextAttemptOrdinal(stepIndex, agent);
|
|
167
|
+
appendMetric({ type: 'attempt_skipped', at: now, attemptId: `${runId}:step-${stepIndex}:${agent}:attempt-${attempt}`, stepIndex, stepLabel: step.label || null, agent, role: roleFor(agent), attempt, outcome: 'skipped', source: 'hook', attributionConfidence: 'high' });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
function writeStatus(next) {
|
|
173
|
+
status = { ...status, ...next, slug, updatedAt: now };
|
|
174
|
+
const temporary = `${statusPath}.${process.pid}.${Date.now()}.tmp`;
|
|
175
|
+
fs.writeFileSync(temporary, JSON.stringify(status, null, 2) + '\n');
|
|
176
|
+
fs.renameSync(temporary, statusPath);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function out(message) {
|
|
180
|
+
process.stdout.write(JSON.stringify({ followup_message: message }));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function readArtifact(name) {
|
|
184
|
+
const p = path.join(taskDir, name);
|
|
185
|
+
if (!fs.existsSync(p)) return '';
|
|
186
|
+
try {
|
|
187
|
+
return fs.readFileSync(p, 'utf8');
|
|
188
|
+
} catch {
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function getStepAgents(step) {
|
|
194
|
+
if (!step) return [];
|
|
195
|
+
return Array.isArray(step.agent) ? step.agent : [step.agent];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function skipIfMatches(skipIf, dir) {
|
|
199
|
+
if (!skipIf) return false;
|
|
200
|
+
if (skipIf === 'debugger.fixed') {
|
|
201
|
+
const text = readArtifact('debug-report.md');
|
|
202
|
+
return /fixApplied:\s*true/i.test(text) || /\*\*Fix applied:\*\*\s*true/i.test(text);
|
|
203
|
+
}
|
|
204
|
+
if (skipIf === 'ci-investigator.resolved') {
|
|
205
|
+
const text = readArtifact('ci-report.md');
|
|
206
|
+
return /fixApplied:\s*true/i.test(text) || /## Verdict\s*\nPASS/i.test(text);
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function findNextExecutableStep(steps, startIdx, dir) {
|
|
212
|
+
for (let i = startIdx; i < steps.length; i++) {
|
|
213
|
+
if (!skipIfMatches(steps[i].skipIf, dir)) return { step: steps[i], index: i };
|
|
214
|
+
recordSkippedStep(steps[i], i);
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const AGENT_HANDOFF = {
|
|
220
|
+
'task-analyst': (s) =>
|
|
221
|
+
`Invoke task-analyst for slug "${s}". Write brief.md and decomposition.md under .cursor/team/tasks/${s}/. ` +
|
|
222
|
+
`Set status.json: phase executing, state awaiting_approval if human gate applies. Do not use legacy phase analysis.`,
|
|
223
|
+
debugger: (s) =>
|
|
224
|
+
`Invoke debugger subagent for slug "${s}". Investigate root cause with php artisan test / composer test / logs; write debug-report.md with fixApplied: true/false. ` +
|
|
225
|
+
`Update status.json when done.`,
|
|
226
|
+
'solution-architect': (s) =>
|
|
227
|
+
`Invoke solution-architect for slug "${s}". Read brief.md; write architecture.md for Laravel layers. Update status.json when done.`,
|
|
228
|
+
'migration-specialist': (s) =>
|
|
229
|
+
`Invoke migration-specialist for slug "${s}". Read brief.md; write migration-plan.md for PHP build/toolchain. Update status.json when done.`,
|
|
230
|
+
'api-contract-reviewer': (s) =>
|
|
231
|
+
`Invoke api-contract-reviewer (readonly) for slug "${s}". Review OpenAPI (if present) vs routes/Resources/Form Requests; write api-contract-review.md. Update status.json when done.`,
|
|
232
|
+
'ci-investigator': (s) =>
|
|
233
|
+
`Invoke ci-investigator for slug "${s}". Diagnose CI failure (php artisan test / composer test / phpstan or preset-structure); write ci-report.md; apply minimal fix. ` +
|
|
234
|
+
`Use ci-investigation skill. Update status.json when done.`,
|
|
235
|
+
'feature-developer': (s) =>
|
|
236
|
+
`Invoke feature-developer for slug "${s}". Read brief.md, decomposition.md, validation-report.md, review.md, architecture.md, migration-plan.md, api-contract-review.md, debug-report.md if present. ` +
|
|
237
|
+
`Follow feature-delivery-workflow.mdc and reference-features.mdc. Prefer Laravel paths: routes → Form Request → Action/Service → Eloquent → Feature test (app/, routes/, tests/). ` +
|
|
238
|
+
`Include PHPUnit/Pest via php artisan test / composer test (Unit + Feature) before handoff. Update status.json when done.`,
|
|
239
|
+
'build-verifier': (s) =>
|
|
240
|
+
`Invoke build-verifier (readonly) for slug "${s}". Follow php-tooling.mdc and post-change-test.mdc. ` +
|
|
241
|
+
`Run php artisan test / composer test and configured static analysis if present in repo/CI — never yarn/ESLint/Stylelint. ` +
|
|
242
|
+
`For preset packaging: yarn check:preset-structure. Write validation-report.md. Set state completed or validation_failed. Do not edit production code.`,
|
|
243
|
+
'performance-auditor': (s) =>
|
|
244
|
+
`Invoke performance-auditor (readonly) for slug "${s}". Audit Laravel hot paths, Eloquent N+1, queue backlog; Blackfire/Xdebug/Telescope/query log if present; write perf-report.md. Update status.json when done.`,
|
|
245
|
+
'code-reviewer': (s) =>
|
|
246
|
+
`Invoke code-reviewer subagent (readonly) for slug "${s}". Review git diff vs brief.md per code-review-mr.mdc (Laravel boundaries). ` +
|
|
247
|
+
`Update status.json when done.`,
|
|
248
|
+
'security-reviewer': (s) =>
|
|
249
|
+
`Invoke security-reviewer (readonly) for slug "${s}". Follow security-php.mdc; write security-review.md. Update status.json when done.`,
|
|
250
|
+
'qa-tester': (s, scope) =>
|
|
251
|
+
`Invoke qa-tester for slug "${s}"${scope ? ` (scope: ${scope})` : ''}. ` +
|
|
252
|
+
`Add/update PHPUnit unit and integration tests per brief. Follow tests-unit.mdc, tests-integration.mdc, and skill integration-testing. Update status.json when done.`,
|
|
253
|
+
'unit-test-planner': (s) =>
|
|
254
|
+
`Invoke unit-test-planner for slug "${s}". Write unit-test-plan.md for PHPUnit/Pest via composer test. Use unit-testing skill. Update status.json when done.`,
|
|
255
|
+
'unit-test-generator': (s) =>
|
|
256
|
+
`Invoke unit-test-generator for slug "${s}". Implement *Test.php / *Tests.php from unit-test-plan.md under tests. Use unit-testing skill. Update status.json when done.`,
|
|
257
|
+
'unit-test-healer': (s) =>
|
|
258
|
+
`Invoke unit-test-healer for slug "${s}". Fix failing unit tests. Use unit-testing and debug-investigation skills. Update status.json when done.`,
|
|
259
|
+
'integration-test-planner': (s) =>
|
|
260
|
+
`Invoke integration-test-planner for slug "${s}". Create/update Feature/integration test plans (HTTP Feature tests / DB fixtures / external HTTP fakes / Bus::fake Event::fake). ` +
|
|
261
|
+
`Follow tests-integration.mdc and the integration-testing skill. Do not use browser e2e tooling. Update status.json when done.`,
|
|
262
|
+
'integration-test-generator': (s) =>
|
|
263
|
+
`Invoke integration-test-generator for slug "${s}". Generate/update Feature tests (*Test.php under tests/Feature) from plans. ` +
|
|
264
|
+
`Follow tests-integration.mdc and the integration-testing skill. Do not use browser e2e tooling. Update status.json when done.`,
|
|
265
|
+
'integration-test-healer': (s) =>
|
|
266
|
+
`Invoke integration-test-healer for slug "${s}". Fix failing/flaky Feature/integration tests while preserving plan assertions. ` +
|
|
267
|
+
`Follow tests-integration.mdc, integration-testing, and debug-investigation. Do not use browser e2e tooling. Update status.json when done.`,
|
|
268
|
+
'tech-writer': (s) =>
|
|
269
|
+
`Invoke tech-writer for slug "${s}". Write documentation.md from task artifacts. Update status.json when done.`,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const REVIEW_AGENTS = new Set([
|
|
273
|
+
'code-reviewer',
|
|
274
|
+
'security-reviewer',
|
|
275
|
+
'api-contract-reviewer',
|
|
276
|
+
]);
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
function metricsHandoff(agent) {
|
|
280
|
+
const matches = Object.entries(status.activeAttempts || {}).filter(([, entry]) => entry.agent === agent);
|
|
281
|
+
if (matches.length === 1) {
|
|
282
|
+
return ` Metrics attemptId: ${matches[0][0]}; update normal status only. If the hook cannot identify a parallel stop, append a minimal { attemptId, outcome } receipt to .metrics-receipts.json; do not calculate duration.`;
|
|
283
|
+
}
|
|
284
|
+
return ' Metrics: use the supplied attemptId when available; for a parallel completion, write a minimal { attemptId, outcome } receipt to .metrics-receipts.json rather than relying on currentAgent. Do not calculate duration.';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function handoffForAgent(agent, scope) {
|
|
288
|
+
const fn = AGENT_HANDOFF[agent];
|
|
289
|
+
const handoff = fn ? fn(slug, scope) : `Invoke ${agent} subagent for slug "${slug}". Update status.json when done.`;
|
|
290
|
+
return handoff + metricsHandoff(agent);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function handoffForParallelStep(step) {
|
|
294
|
+
const agents = getStepAgents(step);
|
|
295
|
+
const attemptIds = Object.entries(status.activeAttempts || {})
|
|
296
|
+
.filter(([, entry]) => agents.includes(entry.agent))
|
|
297
|
+
.map(([attemptId, entry]) => `${entry.agent}=${attemptId}`);
|
|
298
|
+
return (
|
|
299
|
+
`Invoke IN PARALLEL (one message, multiple Task calls): ${agents.join(' AND ')} for slug "${slug}". ` +
|
|
300
|
+
`Each agent writes its artifact and appends itself to status.json parallelCompleted when done. ` +
|
|
301
|
+
`Metrics attemptIds: ${attemptIds.join('; ')}. If the hook cannot identify a parallel stop, append a minimal { attemptId, outcome } receipt to .metrics-receipts.json; do not calculate duration.`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function hasGateAfter(agent, humanGates) {
|
|
306
|
+
return Array.isArray(humanGates) && humanGates.includes(`after:${agent}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function hasGateAfterStep(step, humanGates) {
|
|
310
|
+
return getStepAgents(step).some((a) => hasGateAfter(a, humanGates));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function legacyFlow() {
|
|
314
|
+
if (status.phase === 'analysis') {
|
|
315
|
+
process.stdout.write('{}');
|
|
316
|
+
process.exit(0);
|
|
317
|
+
}
|
|
318
|
+
if (status.phase === 'development' && (status.state === 'completed' || status.state === 'in_progress')) {
|
|
319
|
+
writeStatus({ phase: 'executing', state: 'in_progress', currentAgent: 'build-verifier' });
|
|
320
|
+
out(handoffForAgent('build-verifier'));
|
|
321
|
+
process.exit(0);
|
|
322
|
+
}
|
|
323
|
+
if (status.currentAgent === 'build-verifier' && status.state === 'completed') {
|
|
324
|
+
writeStatus({ phase: 'executing', state: 'in_progress', currentAgent: 'code-reviewer' });
|
|
325
|
+
out(handoffForAgent('code-reviewer'));
|
|
326
|
+
process.exit(0);
|
|
327
|
+
}
|
|
328
|
+
if (status.phase === 'review' && status.state === 'completed') {
|
|
329
|
+
writeStatus({ phase: 'executing', state: 'in_progress', currentAgent: 'qa-tester' });
|
|
330
|
+
out(handoffForAgent('qa-tester'));
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
function scheduleLegacyRetry(agent, retryAfterFix) {
|
|
334
|
+
const stepIndex = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
335
|
+
const predecessorAttemptId = status.lastCompletedAttempt?.attemptId || null;
|
|
336
|
+
appendMetric({ type: 'retry_scheduled', at: now, attemptId: predecessorAttemptId, nextAgent: agent, retryAfterFix: retryAfterFix || null, source: 'hook', attributionConfidence: 'unknown' });
|
|
337
|
+
activateAttempts({ agent }, stepIndex);
|
|
338
|
+
}
|
|
339
|
+
if (status.state === 'changes_requested' && REVIEW_AGENTS.has(status.currentAgent)) {
|
|
340
|
+
const reviewer = status.currentAgent;
|
|
341
|
+
scheduleLegacyRetry('feature-developer', reviewer);
|
|
342
|
+
writeStatus({
|
|
343
|
+
currentAgent: 'feature-developer',
|
|
344
|
+
state: 'in_progress',
|
|
345
|
+
phase: 'executing',
|
|
346
|
+
retryAfterFix: reviewer,
|
|
347
|
+
});
|
|
348
|
+
out(handoffForAgent('feature-developer') + ` After fixes, re-run ${reviewer}.`);
|
|
349
|
+
process.exit(0);
|
|
350
|
+
}
|
|
351
|
+
if (status.state === 'validation_failed' && status.currentAgent === 'build-verifier') {
|
|
352
|
+
scheduleLegacyRetry('feature-developer', 'build-verifier');
|
|
353
|
+
writeStatus({
|
|
354
|
+
currentAgent: 'feature-developer',
|
|
355
|
+
state: 'in_progress',
|
|
356
|
+
phase: 'executing',
|
|
357
|
+
retryAfterFix: 'build-verifier',
|
|
358
|
+
});
|
|
359
|
+
out(handoffForAgent('feature-developer') + ' Read validation-report.md. After fixes, re-run build-verifier.');
|
|
360
|
+
process.exit(0);
|
|
361
|
+
}
|
|
362
|
+
if (status.state === 'completed' && status.retryAfterFix) {
|
|
363
|
+
const retry = status.retryAfterFix;
|
|
364
|
+
scheduleLegacyRetry(retry, null);
|
|
365
|
+
writeStatus({ currentAgent: retry, state: 'in_progress', retryAfterFix: null });
|
|
366
|
+
out(handoffForAgent(retry));
|
|
367
|
+
process.exit(0);
|
|
368
|
+
}
|
|
369
|
+
if (status.phase === 'testing' && status.state === 'completed') {
|
|
370
|
+
writeStatus({ phase: 'done', state: 'completed' });
|
|
371
|
+
out(`Pipeline complete for "${slug}". Run /technical-retro ${slug}.`);
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
process.stdout.write('{}');
|
|
375
|
+
process.exit(0);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
if (hookInput.action === 'end_human_gate') {
|
|
380
|
+
status.awaitingHumanGate = false;
|
|
381
|
+
if (status.state === 'awaiting_approval') {
|
|
382
|
+
status.state = 'in_progress';
|
|
383
|
+
status.phase = status.phase === 'human_gate' ? 'executing' : status.phase;
|
|
384
|
+
}
|
|
385
|
+
if (status.activeHumanGateEventId) {
|
|
386
|
+
appendMetric({
|
|
387
|
+
type: 'human_gate_ended',
|
|
388
|
+
at: now,
|
|
389
|
+
gateStartedEventId: status.activeHumanGateEventId,
|
|
390
|
+
source: 'orchestrator',
|
|
391
|
+
attributionConfidence: 'high',
|
|
392
|
+
});
|
|
393
|
+
delete status.activeHumanGateEventId;
|
|
394
|
+
}
|
|
395
|
+
if (hookInput.activateNext && fs.existsSync(pipelinePath)) {
|
|
396
|
+
let resumePipeline = null;
|
|
397
|
+
try { resumePipeline = JSON.parse(fs.readFileSync(pipelinePath, 'utf8')); } catch {}
|
|
398
|
+
if (resumePipeline) {
|
|
399
|
+
const resumeSteps = resumePipeline.steps || [];
|
|
400
|
+
const resumeStart = (typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0) + 1;
|
|
401
|
+
const resumeNext = findNextExecutableStep(resumeSteps, resumeStart, taskDir);
|
|
402
|
+
if (resumeNext) {
|
|
403
|
+
const resumeAgents = getStepAgents(resumeNext.step);
|
|
404
|
+
activateAttempts(resumeNext.step, resumeNext.index);
|
|
405
|
+
writeStatus({
|
|
406
|
+
pipelineIndex: resumeNext.index,
|
|
407
|
+
currentAgent: resumeAgents[0],
|
|
408
|
+
phase: 'executing',
|
|
409
|
+
state: 'in_progress',
|
|
410
|
+
awaitingHumanGate: false,
|
|
411
|
+
parallelCompleted: [],
|
|
412
|
+
});
|
|
413
|
+
if (resumeNext.step.parallel && resumeAgents.length > 1) out(handoffForParallelStep(resumeNext.step));
|
|
414
|
+
else out(handoffForAgent(resumeAgents[0], resumeNext.step.scope));
|
|
415
|
+
process.exit(0);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
writeStatus({
|
|
420
|
+
awaitingHumanGate: false,
|
|
421
|
+
state: status.state,
|
|
422
|
+
phase: status.phase,
|
|
423
|
+
});
|
|
424
|
+
process.stdout.write(JSON.stringify({ ok: true, action: 'end_human_gate', activeHumanGateEventId: status.activeHumanGateEventId || null }));
|
|
425
|
+
process.exit(0);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (!fs.existsSync(pipelinePath)) {
|
|
429
|
+
legacyFlow();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
let pipeline;
|
|
433
|
+
try {
|
|
434
|
+
pipeline = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'));
|
|
435
|
+
} catch {
|
|
436
|
+
legacyFlow();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const steps = pipeline.steps || [];
|
|
440
|
+
const initialIdx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
441
|
+
const initialStep = steps[initialIdx];
|
|
442
|
+
const activeAttemptCountBeforeFinish = Object.keys(status.activeAttempts || {}).length;
|
|
443
|
+
const finishedAttempt = finishActiveAttempt();
|
|
444
|
+
if (!finishedAttempt && (hookInput.attemptId || activeAttemptCountBeforeFinish > 0)) {
|
|
445
|
+
process.stdout.write('{}');
|
|
446
|
+
process.exit(0);
|
|
447
|
+
}
|
|
448
|
+
endHumanGateIfResumed();
|
|
449
|
+
if (steps.length === 0 || pipeline.autoChain === false) {
|
|
450
|
+
process.stdout.write('{}');
|
|
451
|
+
process.exit(0);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (status.awaitingHumanGate || status.state === 'awaiting_approval') {
|
|
455
|
+
process.stdout.write('{}');
|
|
456
|
+
process.exit(0);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function nearestPrecedingStepIndex(agent, fromIndex, fallback) {
|
|
460
|
+
for (let index = fromIndex - 1; index >= 0; index -= 1) {
|
|
461
|
+
if (getStepAgents(steps[index]).includes(agent)) return index;
|
|
462
|
+
}
|
|
463
|
+
const match = steps.findIndex((step) => getStepAgents(step).includes(agent));
|
|
464
|
+
return match >= 0 ? match : fallback;
|
|
465
|
+
}
|
|
466
|
+
function scheduleRetry(agent, retryAfterFix, predecessor, explicitStepIndex = null) {
|
|
467
|
+
const currentIndex = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
468
|
+
const stepIndex = Number.isInteger(explicitStepIndex) ? explicitStepIndex : nearestPrecedingStepIndex(agent, currentIndex, currentIndex);
|
|
469
|
+
const predecessorAttemptId = predecessor?.attemptId || status.lastCompletedAttempt?.attemptId || null;
|
|
470
|
+
appendMetric({ type: 'retry_scheduled', at: now, attemptId: predecessorAttemptId, nextAgent: agent, retryAfterFix: retryAfterFix || null, source: 'hook', attributionConfidence: predecessor ? predecessor.confidence : 'unknown' });
|
|
471
|
+
activateAttempts(steps[stepIndex] || { agent }, stepIndex);
|
|
472
|
+
return stepIndex;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (status.state === 'changes_requested' && REVIEW_AGENTS.has(status.currentAgent)) {
|
|
476
|
+
const reviewer = status.currentAgent;
|
|
477
|
+
const retryAfterFixStepIndex = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
478
|
+
const developerIndex = scheduleRetry('feature-developer', reviewer, finishedAttempt);
|
|
479
|
+
writeStatus({
|
|
480
|
+
pipelineIndex: developerIndex,
|
|
481
|
+
currentAgent: 'feature-developer',
|
|
482
|
+
state: 'in_progress',
|
|
483
|
+
phase: 'executing',
|
|
484
|
+
retryAfterFix: reviewer,
|
|
485
|
+
retryAfterFixStepIndex,
|
|
486
|
+
});
|
|
487
|
+
out(handoffForAgent('feature-developer') + ` After fixes, re-run ${reviewer}.`);
|
|
488
|
+
process.exit(0);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (status.state === 'validation_failed' && status.currentAgent === 'build-verifier') {
|
|
492
|
+
const retryAfterFixStepIndex = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
493
|
+
const developerIndex = scheduleRetry('feature-developer', 'build-verifier', finishedAttempt);
|
|
494
|
+
writeStatus({
|
|
495
|
+
pipelineIndex: developerIndex,
|
|
496
|
+
currentAgent: 'feature-developer',
|
|
497
|
+
state: 'in_progress',
|
|
498
|
+
phase: 'executing',
|
|
499
|
+
retryAfterFix: 'build-verifier',
|
|
500
|
+
retryAfterFixStepIndex,
|
|
501
|
+
});
|
|
502
|
+
out(handoffForAgent('feature-developer') + ' Read validation-report.md. After fixes, re-run build-verifier.');
|
|
503
|
+
process.exit(0);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (status.state === 'completed' && status.retryAfterFix) {
|
|
507
|
+
const retry = status.retryAfterFix;
|
|
508
|
+
const retryIndex = scheduleRetry(retry, null, finishedAttempt, status.retryAfterFixStepIndex);
|
|
509
|
+
writeStatus({ pipelineIndex: retryIndex, currentAgent: retry, state: 'in_progress', retryAfterFix: null, retryAfterFixStepIndex: null });
|
|
510
|
+
out(handoffForAgent(retry));
|
|
511
|
+
process.exit(0);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
515
|
+
const currentStep = steps[idx];
|
|
516
|
+
if (!currentStep) {
|
|
517
|
+
writeStatus({ phase: 'done', state: 'completed', currentAgent: null });
|
|
518
|
+
out(`Pipeline complete for "${slug}". Run /technical-retro ${slug}.`);
|
|
519
|
+
process.exit(0);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const completedAgent = finishedAttempt?.entry?.agent || status.currentAgent || getStepAgents(currentStep)[0];
|
|
523
|
+
const stepAgents = getStepAgents(currentStep);
|
|
524
|
+
|
|
525
|
+
if (currentStep.parallel && stepAgents.length > 1) {
|
|
526
|
+
const done = [...(status.parallelCompleted || [])];
|
|
527
|
+
if (completedAgent && !done.includes(completedAgent)) {
|
|
528
|
+
done.push(completedAgent);
|
|
529
|
+
}
|
|
530
|
+
writeStatus({ parallelCompleted: done });
|
|
531
|
+
const remaining = stepAgents.filter((a) => !done.includes(a));
|
|
532
|
+
if (remaining.length > 0) {
|
|
533
|
+
process.stdout.write('{}');
|
|
534
|
+
process.exit(0);
|
|
535
|
+
}
|
|
536
|
+
writeStatus({ parallelCompleted: [] });
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (hasGateAfterStep(currentStep, pipeline.humanGates)) {
|
|
540
|
+
beginHumanGate();
|
|
541
|
+
writeStatus({
|
|
542
|
+
awaitingHumanGate: true,
|
|
543
|
+
state: 'awaiting_approval',
|
|
544
|
+
currentAgent: completedAgent,
|
|
545
|
+
});
|
|
546
|
+
process.stdout.write('{}');
|
|
547
|
+
process.exit(0);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const next = findNextExecutableStep(steps, idx + 1, taskDir);
|
|
551
|
+
if (!next) {
|
|
552
|
+
completeTaskMetrics();
|
|
553
|
+
writeStatus({
|
|
554
|
+
pipelineIndex: steps.length,
|
|
555
|
+
phase: 'done',
|
|
556
|
+
state: 'completed',
|
|
557
|
+
currentAgent: null,
|
|
558
|
+
awaitingHumanGate: false,
|
|
559
|
+
});
|
|
560
|
+
out(`Pipeline complete for "${slug}". Run /technical-retro ${slug}.`);
|
|
561
|
+
process.exit(0);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const { step: nextStep, index: nextIdx } = next;
|
|
565
|
+
const nextAgents = getStepAgents(nextStep);
|
|
566
|
+
|
|
567
|
+
activateAttempts(nextStep, nextIdx);
|
|
568
|
+
writeStatus({
|
|
569
|
+
pipelineIndex: nextIdx,
|
|
570
|
+
currentAgent: nextAgents[0],
|
|
571
|
+
phase: 'executing',
|
|
572
|
+
state: 'in_progress',
|
|
573
|
+
awaitingHumanGate: false,
|
|
574
|
+
parallelCompleted: [],
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
if (nextStep.parallel && nextAgents.length > 1) {
|
|
578
|
+
out(handoffForParallelStep(nextStep));
|
|
579
|
+
} else {
|
|
580
|
+
out(handoffForAgent(nextAgents[0], nextStep.scope));
|
|
581
|
+
}
|
|
582
|
+
NODE
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Hooks cookbook (examples)
|
|
2
|
+
|
|
3
|
+
Optional scripts under `hooks/examples/`. **Not** referenced from default `hooks.json` (only `guard-shell-command.sh` + `chain-team-phases.sh`).
|
|
4
|
+
|
|
5
|
+
| Script | Purpose |
|
|
6
|
+
|--------|---------|
|
|
7
|
+
| `secret-guard.example.sh` | Fail if staged diff looks like secrets |
|
|
8
|
+
| `format-edited.example.sh` | Stack formatter / linter on edited files |
|
|
9
|
+
| `test-on-save.example.sh` | Scoped tests for the stack |
|
|
10
|
+
|
|
11
|
+
## Wiring (Cursor)
|
|
12
|
+
|
|
13
|
+
1. Copy an example into `hooks/` (drop `.example` suffix), e.g. `cp hooks/examples/secret-guard.example.sh hooks/secret-guard.sh`.
|
|
14
|
+
2. `chmod +x hooks/secret-guard.sh`.
|
|
15
|
+
3. Add a hook entry in `.cursor/hooks.json` (e.g. `beforeShellExecution` or `afterFileEdit` — see Cursor hooks docs).
|
|
16
|
+
4. Keep cookbook scripts **out** of the default preset `hooks.json` when contributing back upstream.
|
|
17
|
+
|
|
18
|
+
Stack: **go** — recipes must stay stack-safe (no cross-stack lint/test commands).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Optional: project formatter on edited PHP sources.
|
|
3
|
+
# NOT in default hooks.json. Discover formatter from repo/CI — do not invent one.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
changed=$(git diff --name-only --diff-filter=ACMR HEAD 2>/dev/null | grep '\.php$' || true)
|
|
6
|
+
if [[ -n "${changed:-}" ]]; then
|
|
7
|
+
# Prefer repo tool already configured — placeholders only:
|
|
8
|
+
# vendor/bin/php-cs-fixer fix $changed OR vendor/bin/pint $changed
|
|
9
|
+
true
|
|
10
|
+
fi
|
|
11
|
+
exit 0
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Optional cookbook: block committing likely secrets in staged diffs.
|
|
3
|
+
# NOT wired in default hooks.json — copy to hooks/ and register manually.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
PATTERN='(AKIA[0-9A-Z]{16}|-----BEGIN (RSA |OPENSSH )?PRIVATE KEY-----|api[_-]?key\s*[:=]\s*['\''\"][^'\''\"]{12,})'
|
|
6
|
+
if git diff --cached -U0 2>/dev/null | grep -Eiq "$PATTERN"; then
|
|
7
|
+
echo "secret-guard: possible secret in staged diff — review before commit" >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
exit 0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Optional: Composer/PHPUnit (or Pest) after PHP edits.
|
|
3
|
+
# NOT in default hooks.json. Prefer Composer scripts when present.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
changed=$(git diff --name-only --diff-filter=ACMR HEAD 2>/dev/null | grep '\.php$' || true)
|
|
6
|
+
if [[ -z "${changed:-}" ]]; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
if [[ ! -f composer.json ]]; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
if composer test -q >/dev/null 2>&1; then
|
|
13
|
+
composer test -q 2>/dev/null || true
|
|
14
|
+
elif [[ -x vendor/bin/pest ]]; then
|
|
15
|
+
vendor/bin/pest 2>/dev/null || true
|
|
16
|
+
elif [[ -x vendor/bin/phpunit ]]; then
|
|
17
|
+
vendor/bin/phpunit 2>/dev/null || true
|
|
18
|
+
fi
|
|
19
|
+
exit 0
|