gentyr 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/antipattern-hunter.md +176 -0
- package/.claude/agents/code-reviewer.md +205 -0
- package/.claude/agents/code-writer.md +154 -0
- package/.claude/agents/deputy-cto.md +309 -0
- package/.claude/agents/feedback-agent.md +101 -0
- package/.claude/agents/investigator.md +136 -0
- package/.claude/agents/product-manager.md +97 -0
- package/.claude/agents/project-manager.md +116 -0
- package/.claude/agents/repo-hygiene-expert.md +626 -0
- package/.claude/agents/secret-manager.md +324 -0
- package/.claude/agents/test-writer.md +354 -0
- package/.claude/commands/configure-personas.md +144 -0
- package/.claude/commands/cto-report.md +36 -0
- package/.claude/commands/demo.md +89 -0
- package/.claude/commands/deputy-cto.md +345 -0
- package/.claude/commands/hotfix.md +31 -0
- package/.claude/commands/overdrive-gentyr.md +167 -0
- package/.claude/commands/product-manager.md +32 -0
- package/.claude/commands/push-migrations.md +86 -0
- package/.claude/commands/push-secrets.md +97 -0
- package/.claude/commands/services.json.example +30 -0
- package/.claude/commands/setup-gentyr.md +396 -0
- package/.claude/commands/show.md +42 -0
- package/.claude/commands/spawn-tasks.md +79 -0
- package/.claude/commands/toggle-automation-gentyr.md +75 -0
- package/.claude/commands/toggle-product-manager.md +19 -0
- package/.claude/commands/triage.md +69 -0
- package/.claude/hooks/README.md +686 -0
- package/.claude/hooks/__tests__/README.md +129 -0
- package/.claude/hooks/agent-tracker.js +434 -0
- package/.claude/hooks/antipattern-hunter-hook.js +401 -0
- package/.claude/hooks/api-key-watcher.js +289 -0
- package/.claude/hooks/block-no-verify.js +301 -0
- package/.claude/hooks/bypass-approval-hook.js +313 -0
- package/.claude/hooks/compliance-checker.js +1309 -0
- package/.claude/hooks/config-reader.js +143 -0
- package/.claude/hooks/credential-file-guard.js +1139 -0
- package/.claude/hooks/credential-health-check.js +168 -0
- package/.claude/hooks/credential-sync-hook.js +79 -0
- package/.claude/hooks/cto-notification-hook.js +656 -0
- package/.claude/hooks/feedback-launcher.js +424 -0
- package/.claude/hooks/feedback-orchestrator.js +367 -0
- package/.claude/hooks/gentyr-splash.js +47 -0
- package/.claude/hooks/gentyr-sync.js +389 -0
- package/.claude/hooks/hourly-automation.js +3340 -0
- package/.claude/hooks/key-sync.js +899 -0
- package/.claude/hooks/lib/approval-utils.js +731 -0
- package/.claude/hooks/lib/feature-branch-helper.js +102 -0
- package/.claude/hooks/lib/worktree-manager.js +330 -0
- package/.claude/hooks/mapping-validator.js +285 -0
- package/.claude/hooks/plan-executor.js +398 -0
- package/.claude/hooks/playwright-cli-guard.js +104 -0
- package/.claude/hooks/playwright-health-check.js +71 -0
- package/.claude/hooks/pre-commit-review.js +725 -0
- package/.claude/hooks/prompts/local-spec-enforcement.md +310 -0
- package/.claude/hooks/prompts/mapping-fix.md +92 -0
- package/.claude/hooks/prompts/mapping-review.md +140 -0
- package/.claude/hooks/prompts/schema-mapper.md +185 -0
- package/.claude/hooks/prompts/spec-enforcement.md +233 -0
- package/.claude/hooks/protected-action-approval-hook.js +336 -0
- package/.claude/hooks/protected-action-gate.js +562 -0
- package/.claude/hooks/protected-actions.json +208 -0
- package/.claude/hooks/protected-actions.json.template +122 -0
- package/.claude/hooks/quota-monitor.js +490 -0
- package/.claude/hooks/reporters/jest-failure-reporter.js +401 -0
- package/.claude/hooks/reporters/playwright-failure-reporter.js +446 -0
- package/.claude/hooks/reporters/vitest-failure-reporter.js +443 -0
- package/.claude/hooks/schema-mapper-hook.js +544 -0
- package/.claude/hooks/secret-leak-detector.js +216 -0
- package/.claude/hooks/session-reviver.js +514 -0
- package/.claude/hooks/slash-command-prefetch.js +1145 -0
- package/.claude/hooks/stale-work-detector.js +205 -0
- package/.claude/hooks/stop-continue-hook.js +414 -0
- package/.claude/hooks/todo-maintenance.js +522 -0
- package/.claude/hooks/todo-processing-prompt.md +75 -0
- package/.claude/hooks/usage-optimizer.js +791 -0
- package/.claude/mcp/README.md +246 -0
- package/.claude/settings.json.template +168 -0
- package/.mcp.json.template +207 -0
- package/CLAUDE.md +340 -0
- package/CLAUDE.md.gentyr-section +89 -0
- package/LICENSE +21 -0
- package/README.md +297 -0
- package/cli/commands/init.js +471 -0
- package/cli/commands/migrate.js +132 -0
- package/cli/commands/protect.js +271 -0
- package/cli/commands/scaffold.js +48 -0
- package/cli/commands/status.js +133 -0
- package/cli/commands/sync.js +101 -0
- package/cli/commands/uninstall.js +207 -0
- package/cli/index.js +111 -0
- package/cli/lib/config-gen.js +214 -0
- package/cli/lib/resolve-framework.js +97 -0
- package/cli/lib/state.js +140 -0
- package/cli/lib/symlinks.js +260 -0
- package/docs/AUTOMATION-SYSTEMS.md +484 -0
- package/docs/BINARY-PATCHING.md +212 -0
- package/docs/CHANGELOG.md +2830 -0
- package/docs/CREDENTIAL-DETECTION.md +151 -0
- package/docs/CTO-DASHBOARD.md +476 -0
- package/docs/DEPLOYMENT-FLOW.md +477 -0
- package/docs/DEVELOPER.md +116 -0
- package/docs/Executive.md +372 -0
- package/docs/SECRET-PATHS.md +77 -0
- package/docs/SETUP-GUIDE.md +419 -0
- package/docs/STACK.md +109 -0
- package/docs/TESTING.md +440 -0
- package/docs/assets/claude-logo.svg +3 -0
- package/docs/sessions/2026-01-24-spec-suite-implementation.md +190 -0
- package/docs/sessions/2026-02-15-feedback-e2e-audit.md +484 -0
- package/docs/sessions/2026-02-20-credential-rotation-experiments.md +340 -0
- package/docs/sessions/TEST-COVERAGE-REPORT-2026-02-20.md +168 -0
- package/docs/shared/EPHEMERAL-STATE-FILES.md +115 -0
- package/docs/shared/PROTECTION-SYSTEM.md +341 -0
- package/husky/post-commit +10 -0
- package/husky/pre-commit +40 -0
- package/husky/pre-push +94 -0
- package/package.json +43 -0
- package/packages/cto-dashboard/package-lock.json +3510 -0
- package/packages/cto-dashboard/package.json +41 -0
- package/packages/cto-dashboard/pnpm-lock.yaml +2168 -0
- package/packages/mcp-servers/dist/__testUtils__/fixtures.d.ts +220 -0
- package/packages/mcp-servers/dist/__testUtils__/fixtures.d.ts.map +1 -0
- package/packages/mcp-servers/dist/__testUtils__/fixtures.js +376 -0
- package/packages/mcp-servers/dist/__testUtils__/fixtures.js.map +1 -0
- package/packages/mcp-servers/dist/__testUtils__/index.d.ts +121 -0
- package/packages/mcp-servers/dist/__testUtils__/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/__testUtils__/index.js +180 -0
- package/packages/mcp-servers/dist/__testUtils__/index.js.map +1 -0
- package/packages/mcp-servers/dist/__testUtils__/schemas.d.ts +84 -0
- package/packages/mcp-servers/dist/__testUtils__/schemas.d.ts.map +1 -0
- package/packages/mcp-servers/dist/__testUtils__/schemas.js +309 -0
- package/packages/mcp-servers/dist/__testUtils__/schemas.js.map +1 -0
- package/packages/mcp-servers/dist/agent-reports/index.d.ts +7 -0
- package/packages/mcp-servers/dist/agent-reports/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/agent-reports/index.js +8 -0
- package/packages/mcp-servers/dist/agent-reports/index.js.map +1 -0
- package/packages/mcp-servers/dist/agent-reports/server.d.ts +22 -0
- package/packages/mcp-servers/dist/agent-reports/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/agent-reports/server.js +535 -0
- package/packages/mcp-servers/dist/agent-reports/server.js.map +1 -0
- package/packages/mcp-servers/dist/agent-reports/types.d.ts +258 -0
- package/packages/mcp-servers/dist/agent-reports/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/agent-reports/types.js +81 -0
- package/packages/mcp-servers/dist/agent-reports/types.js.map +1 -0
- package/packages/mcp-servers/dist/agent-tracker/index.d.ts +5 -0
- package/packages/mcp-servers/dist/agent-tracker/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/agent-tracker/index.js +5 -0
- package/packages/mcp-servers/dist/agent-tracker/index.js.map +1 -0
- package/packages/mcp-servers/dist/agent-tracker/server.d.ts +12 -0
- package/packages/mcp-servers/dist/agent-tracker/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/agent-tracker/server.js +919 -0
- package/packages/mcp-servers/dist/agent-tracker/server.js.map +1 -0
- package/packages/mcp-servers/dist/agent-tracker/types.d.ts +328 -0
- package/packages/mcp-servers/dist/agent-tracker/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/agent-tracker/types.js +128 -0
- package/packages/mcp-servers/dist/agent-tracker/types.js.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/browser-tips.d.ts +27 -0
- package/packages/mcp-servers/dist/chrome-bridge/browser-tips.d.ts.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/browser-tips.js +167 -0
- package/packages/mcp-servers/dist/chrome-bridge/browser-tips.js.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/index.d.ts +6 -0
- package/packages/mcp-servers/dist/chrome-bridge/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/index.js +6 -0
- package/packages/mcp-servers/dist/chrome-bridge/index.js.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/server.d.ts +13 -0
- package/packages/mcp-servers/dist/chrome-bridge/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/server.js +959 -0
- package/packages/mcp-servers/dist/chrome-bridge/server.js.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/types.d.ts +41 -0
- package/packages/mcp-servers/dist/chrome-bridge/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/chrome-bridge/types.js +8 -0
- package/packages/mcp-servers/dist/chrome-bridge/types.js.map +1 -0
- package/packages/mcp-servers/dist/cloudflare/index.d.ts +8 -0
- package/packages/mcp-servers/dist/cloudflare/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cloudflare/index.js +8 -0
- package/packages/mcp-servers/dist/cloudflare/index.js.map +1 -0
- package/packages/mcp-servers/dist/cloudflare/server.d.ts +16 -0
- package/packages/mcp-servers/dist/cloudflare/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cloudflare/server.js +253 -0
- package/packages/mcp-servers/dist/cloudflare/server.js.map +1 -0
- package/packages/mcp-servers/dist/cloudflare/types.d.ts +141 -0
- package/packages/mcp-servers/dist/cloudflare/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cloudflare/types.js +53 -0
- package/packages/mcp-servers/dist/cloudflare/types.js.map +1 -0
- package/packages/mcp-servers/dist/codecov/index.d.ts +7 -0
- package/packages/mcp-servers/dist/codecov/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/codecov/index.js +7 -0
- package/packages/mcp-servers/dist/codecov/index.js.map +1 -0
- package/packages/mcp-servers/dist/codecov/server.d.ts +21 -0
- package/packages/mcp-servers/dist/codecov/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/codecov/server.js +376 -0
- package/packages/mcp-servers/dist/codecov/server.js.map +1 -0
- package/packages/mcp-servers/dist/codecov/types.d.ts +269 -0
- package/packages/mcp-servers/dist/codecov/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/codecov/types.js +128 -0
- package/packages/mcp-servers/dist/codecov/types.js.map +1 -0
- package/packages/mcp-servers/dist/cto-report/index.d.ts +9 -0
- package/packages/mcp-servers/dist/cto-report/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cto-report/index.js +9 -0
- package/packages/mcp-servers/dist/cto-report/index.js.map +1 -0
- package/packages/mcp-servers/dist/cto-report/server.d.ts +14 -0
- package/packages/mcp-servers/dist/cto-report/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cto-report/server.js +859 -0
- package/packages/mcp-servers/dist/cto-report/server.js.map +1 -0
- package/packages/mcp-servers/dist/cto-report/types.d.ts +213 -0
- package/packages/mcp-servers/dist/cto-report/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cto-report/types.js +29 -0
- package/packages/mcp-servers/dist/cto-report/types.js.map +1 -0
- package/packages/mcp-servers/dist/cto-reports/index.d.ts +7 -0
- package/packages/mcp-servers/dist/cto-reports/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cto-reports/index.js +8 -0
- package/packages/mcp-servers/dist/cto-reports/index.js.map +1 -0
- package/packages/mcp-servers/dist/cto-reports/server.d.ts +20 -0
- package/packages/mcp-servers/dist/cto-reports/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cto-reports/server.js +538 -0
- package/packages/mcp-servers/dist/cto-reports/server.js.map +1 -0
- package/packages/mcp-servers/dist/cto-reports/types.d.ts +236 -0
- package/packages/mcp-servers/dist/cto-reports/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/cto-reports/types.js +77 -0
- package/packages/mcp-servers/dist/cto-reports/types.js.map +1 -0
- package/packages/mcp-servers/dist/deputy-cto/index.d.ts +7 -0
- package/packages/mcp-servers/dist/deputy-cto/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/deputy-cto/index.js +8 -0
- package/packages/mcp-servers/dist/deputy-cto/index.js.map +1 -0
- package/packages/mcp-servers/dist/deputy-cto/server.d.ts +23 -0
- package/packages/mcp-servers/dist/deputy-cto/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/deputy-cto/server.js +1700 -0
- package/packages/mcp-servers/dist/deputy-cto/server.js.map +1 -0
- package/packages/mcp-servers/dist/deputy-cto/types.d.ts +439 -0
- package/packages/mcp-servers/dist/deputy-cto/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/deputy-cto/types.js +102 -0
- package/packages/mcp-servers/dist/deputy-cto/types.js.map +1 -0
- package/packages/mcp-servers/dist/elastic-logs/index.d.ts +5 -0
- package/packages/mcp-servers/dist/elastic-logs/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/elastic-logs/index.js +5 -0
- package/packages/mcp-servers/dist/elastic-logs/index.js.map +1 -0
- package/packages/mcp-servers/dist/elastic-logs/server.d.ts +18 -0
- package/packages/mcp-servers/dist/elastic-logs/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/elastic-logs/server.js +259 -0
- package/packages/mcp-servers/dist/elastic-logs/server.js.map +1 -0
- package/packages/mcp-servers/dist/elastic-logs/types.d.ts +107 -0
- package/packages/mcp-servers/dist/elastic-logs/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/elastic-logs/types.js +31 -0
- package/packages/mcp-servers/dist/elastic-logs/types.js.map +1 -0
- package/packages/mcp-servers/dist/feedback-explorer/index.d.ts +2 -0
- package/packages/mcp-servers/dist/feedback-explorer/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/feedback-explorer/index.js +2 -0
- package/packages/mcp-servers/dist/feedback-explorer/index.js.map +1 -0
- package/packages/mcp-servers/dist/feedback-explorer/server.d.ts +21 -0
- package/packages/mcp-servers/dist/feedback-explorer/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/feedback-explorer/server.js +580 -0
- package/packages/mcp-servers/dist/feedback-explorer/server.js.map +1 -0
- package/packages/mcp-servers/dist/feedback-explorer/types.d.ts +331 -0
- package/packages/mcp-servers/dist/feedback-explorer/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/feedback-explorer/types.js +40 -0
- package/packages/mcp-servers/dist/feedback-explorer/types.js.map +1 -0
- package/packages/mcp-servers/dist/feedback-reporter/index.d.ts +9 -0
- package/packages/mcp-servers/dist/feedback-reporter/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/feedback-reporter/index.js +9 -0
- package/packages/mcp-servers/dist/feedback-reporter/index.js.map +1 -0
- package/packages/mcp-servers/dist/feedback-reporter/server.d.ts +36 -0
- package/packages/mcp-servers/dist/feedback-reporter/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/feedback-reporter/server.js +392 -0
- package/packages/mcp-servers/dist/feedback-reporter/server.js.map +1 -0
- package/packages/mcp-servers/dist/feedback-reporter/types.d.ts +152 -0
- package/packages/mcp-servers/dist/feedback-reporter/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/feedback-reporter/types.js +67 -0
- package/packages/mcp-servers/dist/feedback-reporter/types.js.map +1 -0
- package/packages/mcp-servers/dist/github/index.d.ts +7 -0
- package/packages/mcp-servers/dist/github/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/github/index.js +7 -0
- package/packages/mcp-servers/dist/github/index.js.map +1 -0
- package/packages/mcp-servers/dist/github/server.d.ts +15 -0
- package/packages/mcp-servers/dist/github/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/github/server.js +686 -0
- package/packages/mcp-servers/dist/github/server.js.map +1 -0
- package/packages/mcp-servers/dist/github/types.d.ts +660 -0
- package/packages/mcp-servers/dist/github/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/github/types.js +209 -0
- package/packages/mcp-servers/dist/github/types.js.map +1 -0
- package/packages/mcp-servers/dist/index.d.ts +30 -0
- package/packages/mcp-servers/dist/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/index.js +32 -0
- package/packages/mcp-servers/dist/index.js.map +1 -0
- package/packages/mcp-servers/dist/makerkit-docs/index.d.ts +5 -0
- package/packages/mcp-servers/dist/makerkit-docs/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/makerkit-docs/index.js +5 -0
- package/packages/mcp-servers/dist/makerkit-docs/index.js.map +1 -0
- package/packages/mcp-servers/dist/makerkit-docs/server.d.ts +15 -0
- package/packages/mcp-servers/dist/makerkit-docs/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/makerkit-docs/server.js +252 -0
- package/packages/mcp-servers/dist/makerkit-docs/server.js.map +1 -0
- package/packages/mcp-servers/dist/makerkit-docs/types.d.ts +74 -0
- package/packages/mcp-servers/dist/makerkit-docs/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/makerkit-docs/types.js +20 -0
- package/packages/mcp-servers/dist/makerkit-docs/types.js.map +1 -0
- package/packages/mcp-servers/dist/onepassword/index.d.ts +2 -0
- package/packages/mcp-servers/dist/onepassword/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/onepassword/index.js +2 -0
- package/packages/mcp-servers/dist/onepassword/index.js.map +1 -0
- package/packages/mcp-servers/dist/onepassword/server.d.ts +2 -0
- package/packages/mcp-servers/dist/onepassword/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/onepassword/server.js +159 -0
- package/packages/mcp-servers/dist/onepassword/server.js.map +1 -0
- package/packages/mcp-servers/dist/onepassword/types.d.ts +55 -0
- package/packages/mcp-servers/dist/onepassword/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/onepassword/types.js +22 -0
- package/packages/mcp-servers/dist/onepassword/types.js.map +1 -0
- package/packages/mcp-servers/dist/playwright/helpers.d.ts +20 -0
- package/packages/mcp-servers/dist/playwright/helpers.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright/helpers.js +31 -0
- package/packages/mcp-servers/dist/playwright/helpers.js.map +1 -0
- package/packages/mcp-servers/dist/playwright/index.d.ts +5 -0
- package/packages/mcp-servers/dist/playwright/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright/index.js +5 -0
- package/packages/mcp-servers/dist/playwright/index.js.map +1 -0
- package/packages/mcp-servers/dist/playwright/server.d.ts +13 -0
- package/packages/mcp-servers/dist/playwright/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright/server.js +1201 -0
- package/packages/mcp-servers/dist/playwright/server.js.map +1 -0
- package/packages/mcp-servers/dist/playwright/types.d.ts +216 -0
- package/packages/mcp-servers/dist/playwright/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright/types.js +172 -0
- package/packages/mcp-servers/dist/playwright/types.js.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/browser-manager.d.ts +39 -0
- package/packages/mcp-servers/dist/playwright-feedback/browser-manager.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/browser-manager.js +71 -0
- package/packages/mcp-servers/dist/playwright-feedback/browser-manager.js.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/index.d.ts +5 -0
- package/packages/mcp-servers/dist/playwright-feedback/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/index.js +5 -0
- package/packages/mcp-servers/dist/playwright-feedback/index.js.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/server.d.ts +34 -0
- package/packages/mcp-servers/dist/playwright-feedback/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/server.js +538 -0
- package/packages/mcp-servers/dist/playwright-feedback/server.js.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/types.d.ts +305 -0
- package/packages/mcp-servers/dist/playwright-feedback/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/playwright-feedback/types.js +123 -0
- package/packages/mcp-servers/dist/playwright-feedback/types.js.map +1 -0
- package/packages/mcp-servers/dist/product-manager/server.d.ts +17 -0
- package/packages/mcp-servers/dist/product-manager/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/product-manager/server.js +690 -0
- package/packages/mcp-servers/dist/product-manager/server.js.map +1 -0
- package/packages/mcp-servers/dist/product-manager/types.d.ts +286 -0
- package/packages/mcp-servers/dist/product-manager/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/product-manager/types.js +99 -0
- package/packages/mcp-servers/dist/product-manager/types.js.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/index.d.ts +7 -0
- package/packages/mcp-servers/dist/programmatic-feedback/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/index.js +7 -0
- package/packages/mcp-servers/dist/programmatic-feedback/index.js.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/sandbox.d.ts +19 -0
- package/packages/mcp-servers/dist/programmatic-feedback/sandbox.d.ts.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/sandbox.js +174 -0
- package/packages/mcp-servers/dist/programmatic-feedback/sandbox.js.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/server.d.ts +35 -0
- package/packages/mcp-servers/dist/programmatic-feedback/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/server.js +465 -0
- package/packages/mcp-servers/dist/programmatic-feedback/server.js.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/types.d.ts +127 -0
- package/packages/mcp-servers/dist/programmatic-feedback/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/programmatic-feedback/types.js +80 -0
- package/packages/mcp-servers/dist/programmatic-feedback/types.js.map +1 -0
- package/packages/mcp-servers/dist/render/index.d.ts +8 -0
- package/packages/mcp-servers/dist/render/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/render/index.js +8 -0
- package/packages/mcp-servers/dist/render/index.js.map +1 -0
- package/packages/mcp-servers/dist/render/server.d.ts +15 -0
- package/packages/mcp-servers/dist/render/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/render/server.js +428 -0
- package/packages/mcp-servers/dist/render/server.js.map +1 -0
- package/packages/mcp-servers/dist/render/types.d.ts +273 -0
- package/packages/mcp-servers/dist/render/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/render/types.js +102 -0
- package/packages/mcp-servers/dist/render/types.js.map +1 -0
- package/packages/mcp-servers/dist/resend/index.d.ts +7 -0
- package/packages/mcp-servers/dist/resend/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/resend/index.js +7 -0
- package/packages/mcp-servers/dist/resend/index.js.map +1 -0
- package/packages/mcp-servers/dist/resend/server.d.ts +15 -0
- package/packages/mcp-servers/dist/resend/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/resend/server.js +298 -0
- package/packages/mcp-servers/dist/resend/server.js.map +1 -0
- package/packages/mcp-servers/dist/resend/types.d.ts +222 -0
- package/packages/mcp-servers/dist/resend/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/resend/types.js +58 -0
- package/packages/mcp-servers/dist/resend/types.js.map +1 -0
- package/packages/mcp-servers/dist/review-queue/index.d.ts +6 -0
- package/packages/mcp-servers/dist/review-queue/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/review-queue/index.js +6 -0
- package/packages/mcp-servers/dist/review-queue/index.js.map +1 -0
- package/packages/mcp-servers/dist/review-queue/server.d.ts +17 -0
- package/packages/mcp-servers/dist/review-queue/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/review-queue/server.js +348 -0
- package/packages/mcp-servers/dist/review-queue/server.js.map +1 -0
- package/packages/mcp-servers/dist/review-queue/types.d.ts +162 -0
- package/packages/mcp-servers/dist/review-queue/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/review-queue/types.js +56 -0
- package/packages/mcp-servers/dist/review-queue/types.js.map +1 -0
- package/packages/mcp-servers/dist/secret-sync/server.d.ts +19 -0
- package/packages/mcp-servers/dist/secret-sync/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/secret-sync/server.js +1139 -0
- package/packages/mcp-servers/dist/secret-sync/server.js.map +1 -0
- package/packages/mcp-servers/dist/secret-sync/types.d.ts +442 -0
- package/packages/mcp-servers/dist/secret-sync/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/secret-sync/types.js +113 -0
- package/packages/mcp-servers/dist/secret-sync/types.js.map +1 -0
- package/packages/mcp-servers/dist/session-events/index.d.ts +5 -0
- package/packages/mcp-servers/dist/session-events/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/session-events/index.js +5 -0
- package/packages/mcp-servers/dist/session-events/index.js.map +1 -0
- package/packages/mcp-servers/dist/session-events/server.d.ts +11 -0
- package/packages/mcp-servers/dist/session-events/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/session-events/server.js +290 -0
- package/packages/mcp-servers/dist/session-events/server.js.map +1 -0
- package/packages/mcp-servers/dist/session-events/types.d.ts +213 -0
- package/packages/mcp-servers/dist/session-events/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/session-events/types.js +69 -0
- package/packages/mcp-servers/dist/session-events/types.js.map +1 -0
- package/packages/mcp-servers/dist/session-restart/index.d.ts +9 -0
- package/packages/mcp-servers/dist/session-restart/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/session-restart/index.js +9 -0
- package/packages/mcp-servers/dist/session-restart/index.js.map +1 -0
- package/packages/mcp-servers/dist/session-restart/server.d.ts +20 -0
- package/packages/mcp-servers/dist/session-restart/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/session-restart/server.js +411 -0
- package/packages/mcp-servers/dist/session-restart/server.js.map +1 -0
- package/packages/mcp-servers/dist/session-restart/types.d.ts +26 -0
- package/packages/mcp-servers/dist/session-restart/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/session-restart/types.js +16 -0
- package/packages/mcp-servers/dist/session-restart/types.js.map +1 -0
- package/packages/mcp-servers/dist/setup-helper/index.d.ts +5 -0
- package/packages/mcp-servers/dist/setup-helper/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/setup-helper/index.js +5 -0
- package/packages/mcp-servers/dist/setup-helper/index.js.map +1 -0
- package/packages/mcp-servers/dist/setup-helper/server.d.ts +14 -0
- package/packages/mcp-servers/dist/setup-helper/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/setup-helper/server.js +454 -0
- package/packages/mcp-servers/dist/setup-helper/server.js.map +1 -0
- package/packages/mcp-servers/dist/setup-helper/types.d.ts +81 -0
- package/packages/mcp-servers/dist/setup-helper/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/setup-helper/types.js +41 -0
- package/packages/mcp-servers/dist/setup-helper/types.js.map +1 -0
- package/packages/mcp-servers/dist/shared/audited-server.d.ts +31 -0
- package/packages/mcp-servers/dist/shared/audited-server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/audited-server.js +126 -0
- package/packages/mcp-servers/dist/shared/audited-server.js.map +1 -0
- package/packages/mcp-servers/dist/shared/constants.d.ts +26 -0
- package/packages/mcp-servers/dist/shared/constants.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/constants.js +41 -0
- package/packages/mcp-servers/dist/shared/constants.js.map +1 -0
- package/packages/mcp-servers/dist/shared/index.d.ts +6 -0
- package/packages/mcp-servers/dist/shared/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/index.js +6 -0
- package/packages/mcp-servers/dist/shared/index.js.map +1 -0
- package/packages/mcp-servers/dist/shared/readonly-db.d.ts +11 -0
- package/packages/mcp-servers/dist/shared/readonly-db.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/readonly-db.js +47 -0
- package/packages/mcp-servers/dist/shared/readonly-db.js.map +1 -0
- package/packages/mcp-servers/dist/shared/resolve-framework.d.ts +20 -0
- package/packages/mcp-servers/dist/shared/resolve-framework.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/resolve-framework.js +65 -0
- package/packages/mcp-servers/dist/shared/resolve-framework.js.map +1 -0
- package/packages/mcp-servers/dist/shared/server.d.ts +86 -0
- package/packages/mcp-servers/dist/shared/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/server.js +291 -0
- package/packages/mcp-servers/dist/shared/server.js.map +1 -0
- package/packages/mcp-servers/dist/shared/types.d.ts +113 -0
- package/packages/mcp-servers/dist/shared/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/shared/types.js +36 -0
- package/packages/mcp-servers/dist/shared/types.js.map +1 -0
- package/packages/mcp-servers/dist/show/server.d.ts +12 -0
- package/packages/mcp-servers/dist/show/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/show/server.js +97 -0
- package/packages/mcp-servers/dist/show/server.js.map +1 -0
- package/packages/mcp-servers/dist/show/types.d.ts +19 -0
- package/packages/mcp-servers/dist/show/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/show/types.js +32 -0
- package/packages/mcp-servers/dist/show/types.js.map +1 -0
- package/packages/mcp-servers/dist/specs-browser/index.d.ts +5 -0
- package/packages/mcp-servers/dist/specs-browser/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/specs-browser/index.js +5 -0
- package/packages/mcp-servers/dist/specs-browser/index.js.map +1 -0
- package/packages/mcp-servers/dist/specs-browser/server.d.ts +13 -0
- package/packages/mcp-servers/dist/specs-browser/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/specs-browser/server.js +692 -0
- package/packages/mcp-servers/dist/specs-browser/server.js.map +1 -0
- package/packages/mcp-servers/dist/specs-browser/types.d.ts +337 -0
- package/packages/mcp-servers/dist/specs-browser/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/specs-browser/types.js +134 -0
- package/packages/mcp-servers/dist/specs-browser/types.js.map +1 -0
- package/packages/mcp-servers/dist/supabase/index.d.ts +10 -0
- package/packages/mcp-servers/dist/supabase/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/supabase/index.js +10 -0
- package/packages/mcp-servers/dist/supabase/index.js.map +1 -0
- package/packages/mcp-servers/dist/supabase/server.d.ts +20 -0
- package/packages/mcp-servers/dist/supabase/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/supabase/server.js +451 -0
- package/packages/mcp-servers/dist/supabase/server.js.map +1 -0
- package/packages/mcp-servers/dist/supabase/types.d.ts +196 -0
- package/packages/mcp-servers/dist/supabase/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/supabase/types.js +76 -0
- package/packages/mcp-servers/dist/supabase/types.js.map +1 -0
- package/packages/mcp-servers/dist/todo-db/index.d.ts +5 -0
- package/packages/mcp-servers/dist/todo-db/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/todo-db/index.js +5 -0
- package/packages/mcp-servers/dist/todo-db/index.js.map +1 -0
- package/packages/mcp-servers/dist/todo-db/server.d.ts +13 -0
- package/packages/mcp-servers/dist/todo-db/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/todo-db/server.js +649 -0
- package/packages/mcp-servers/dist/todo-db/server.js.map +1 -0
- package/packages/mcp-servers/dist/todo-db/types.d.ts +225 -0
- package/packages/mcp-servers/dist/todo-db/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/todo-db/types.js +69 -0
- package/packages/mcp-servers/dist/todo-db/types.js.map +1 -0
- package/packages/mcp-servers/dist/user-feedback/index.d.ts +7 -0
- package/packages/mcp-servers/dist/user-feedback/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/user-feedback/index.js +8 -0
- package/packages/mcp-servers/dist/user-feedback/index.js.map +1 -0
- package/packages/mcp-servers/dist/user-feedback/server.d.ts +25 -0
- package/packages/mcp-servers/dist/user-feedback/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/user-feedback/server.js +914 -0
- package/packages/mcp-servers/dist/user-feedback/server.js.map +1 -0
- package/packages/mcp-servers/dist/user-feedback/types.d.ts +415 -0
- package/packages/mcp-servers/dist/user-feedback/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/user-feedback/types.js +132 -0
- package/packages/mcp-servers/dist/user-feedback/types.js.map +1 -0
- package/packages/mcp-servers/dist/vercel/index.d.ts +9 -0
- package/packages/mcp-servers/dist/vercel/index.d.ts.map +1 -0
- package/packages/mcp-servers/dist/vercel/index.js +9 -0
- package/packages/mcp-servers/dist/vercel/index.js.map +1 -0
- package/packages/mcp-servers/dist/vercel/server.d.ts +17 -0
- package/packages/mcp-servers/dist/vercel/server.d.ts.map +1 -0
- package/packages/mcp-servers/dist/vercel/server.js +265 -0
- package/packages/mcp-servers/dist/vercel/server.js.map +1 -0
- package/packages/mcp-servers/dist/vercel/types.d.ts +189 -0
- package/packages/mcp-servers/dist/vercel/types.d.ts.map +1 -0
- package/packages/mcp-servers/dist/vercel/types.js +65 -0
- package/packages/mcp-servers/dist/vercel/types.js.map +1 -0
- package/packages/mcp-servers/package-lock.json +3765 -0
- package/packages/mcp-servers/package.json +64 -0
- package/packages/mcp-servers/test/reporters/test-failure-reporter.ts +372 -0
- package/packages/mcp-servers/vitest.config.ts +27 -0
- package/scripts/__tests__/README.md +163 -0
- package/scripts/apply-credential-hardening.sh +271 -0
- package/scripts/credential-providers/manual.js +56 -0
- package/scripts/credential-providers/onepassword.js +85 -0
- package/scripts/credential-providers/provider-interface.js +104 -0
- package/scripts/encrypt-credential.js +337 -0
- package/scripts/feedback-launcher.js +338 -0
- package/scripts/feedback-orchestrator.js +373 -0
- package/scripts/fix-mcp-launcher-issues.sh +97 -0
- package/scripts/force-spawn-tasks.js +651 -0
- package/scripts/force-triage-reports.js +560 -0
- package/scripts/generate-protected-actions-spec.js +142 -0
- package/scripts/generate-proxy-certs.sh +158 -0
- package/scripts/grant-chrome-ext-permissions.sh +242 -0
- package/scripts/mcp-launcher.js +125 -0
- package/scripts/merge-settings.cjs +167 -0
- package/scripts/patch-clawd.py +844 -0
- package/scripts/patch-credential-cache.py +313 -0
- package/scripts/patches/credential-file-guard-patched.mjs +573 -0
- package/scripts/patches/credential-file-guard.js.patched +573 -0
- package/scripts/patches/verify-tokenizer.mjs +132 -0
- package/scripts/protect-framework.sh +478 -0
- package/scripts/readme-chrome.template +12 -0
- package/scripts/reap-completed-agents.js +439 -0
- package/scripts/reinstall.sh +86 -0
- package/scripts/resign-node.sh +185 -0
- package/scripts/rotation-proxy.js +656 -0
- package/scripts/rotation-stress-monitor.mjs +862 -0
- package/scripts/setup-automation-service.sh +648 -0
- package/scripts/setup-check.js +251 -0
- package/scripts/watch-claude-version.js +142 -0
- package/specs/framework/CORE-INVARIANTS.md +161 -0
- package/specs/patterns/AGENT-PATTERNS.md +223 -0
- package/specs/patterns/HOOK-PATTERNS.md +242 -0
- package/specs/patterns/MCP-SERVER-PATTERNS.md +144 -0
- package/templates/config/gitignore.template +14 -0
- package/templates/config/merge-chain-check.yml.template +51 -0
- package/templates/config/package.json.template +18 -0
- package/templates/config/pnpm-workspace.yaml +5 -0
- package/templates/config/services.json.template +18 -0
- package/templates/config/tsconfig.base.json +17 -0
- package/templates/scaffold/integrations/_template/.gitkeep +0 -0
- package/templates/scaffold/packages/logger/package.json +17 -0
- package/templates/scaffold/packages/logger/src/logger.ts +44 -0
- package/templates/scaffold/packages/shared/package.json +17 -0
- package/templates/scaffold/packages/shared/src/errors.ts +43 -0
- package/templates/scaffold/products/_product/apps/backend/package.json +21 -0
- package/templates/scaffold/products/_product/apps/backend/src/index.ts +17 -0
- package/templates/scaffold/products/_product/apps/extension/.gitkeep +0 -0
- package/templates/scaffold/products/_product/apps/web/.gitkeep +0 -0
- package/templates/scaffold/specs/global/.gitkeep +0 -0
- package/templates/scaffold/specs/local/.gitkeep +0 -0
- package/templates/scaffold/specs/reference/.gitkeep +0 -0
- package/version.json +15 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Pre-Commit Review Hook (v3.0 - Approval Token)
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. First commit attempt → Rejected, spawns deputy-cto review in background
|
|
7
|
+
* 2. Deputy-CTO reviews and approves → Writes approval token
|
|
8
|
+
* 3. Second commit attempt → Token valid? Allow. Otherwise reject.
|
|
9
|
+
*
|
|
10
|
+
* Token expires after 5 minutes and is tied to the staged files hash.
|
|
11
|
+
*
|
|
12
|
+
* @version 3.0.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import crypto from 'crypto';
|
|
18
|
+
import { spawn, execSync } from 'child_process';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { registerSpawn, AGENT_TYPES, HOOK_TYPES } from './agent-tracker.js';
|
|
21
|
+
import { getCooldown } from './config-reader.js';
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
|
|
26
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
27
|
+
const DEPUTY_CTO_DB = path.join(PROJECT_DIR, '.claude', 'deputy-cto.db');
|
|
28
|
+
const APPROVAL_TOKEN_FILE = path.join(PROJECT_DIR, '.claude', 'commit-approval-token.json');
|
|
29
|
+
|
|
30
|
+
// Token expires after configured minutes (default 5)
|
|
31
|
+
const TOKEN_EXPIRY_MS = getCooldown('pre_commit_review', 5) * 60 * 1000;
|
|
32
|
+
|
|
33
|
+
// Try to import better-sqlite3
|
|
34
|
+
let Database = null;
|
|
35
|
+
try {
|
|
36
|
+
Database = (await import('better-sqlite3')).default;
|
|
37
|
+
} catch {
|
|
38
|
+
console.error('[pre-commit] Warning: better-sqlite3 not available');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get staged files and compute a hash for token validation
|
|
43
|
+
*/
|
|
44
|
+
function getStagedInfo() {
|
|
45
|
+
try {
|
|
46
|
+
const files = execSync('git diff --cached --name-only', { encoding: 'utf8' }).trim();
|
|
47
|
+
const stat = execSync('git diff --cached --stat', { encoding: 'utf8' }).trim();
|
|
48
|
+
const diff = execSync('git diff --cached', { encoding: 'utf8' });
|
|
49
|
+
|
|
50
|
+
// Hash the diff to ensure token matches the staged changes
|
|
51
|
+
const diffHash = crypto.createHash('sha256').update(diff).digest('hex').substring(0, 16);
|
|
52
|
+
|
|
53
|
+
// Truncate diff if too long
|
|
54
|
+
const truncatedDiff = diff.length > 10000
|
|
55
|
+
? diff.substring(0, 10000) + '\n\n... [diff truncated, ' + diff.length + ' chars total]'
|
|
56
|
+
: diff;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
files: files.split('\n').filter(f => f),
|
|
60
|
+
stat,
|
|
61
|
+
diff: truncatedDiff,
|
|
62
|
+
diffHash,
|
|
63
|
+
error: false,
|
|
64
|
+
};
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error(`[pre-commit] Error getting staged diff: ${err.message}`);
|
|
67
|
+
return { files: [], stat: '', diff: '', diffHash: '', error: true };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check for pending CTO items that block commits (G020 compliance)
|
|
73
|
+
* This includes ALL pending questions (not just rejections) and pending triage items.
|
|
74
|
+
*/
|
|
75
|
+
function hasPendingCtoItems() {
|
|
76
|
+
if (!Database || !fs.existsSync(DEPUTY_CTO_DB)) {
|
|
77
|
+
return { hasItems: false, count: 0, error: false };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const db = new Database(DEPUTY_CTO_DB, { readonly: true });
|
|
82
|
+
|
|
83
|
+
// Check ALL pending questions (any type, not just rejections)
|
|
84
|
+
const questionsResult = db.prepare(
|
|
85
|
+
"SELECT COUNT(*) as count FROM questions WHERE status = 'pending'"
|
|
86
|
+
).get();
|
|
87
|
+
const questionCount = questionsResult?.count || 0;
|
|
88
|
+
|
|
89
|
+
db.close();
|
|
90
|
+
|
|
91
|
+
// Also check pending triage items from cto-reports.db
|
|
92
|
+
let triageCount = 0;
|
|
93
|
+
const CTO_REPORTS_DB = path.join(PROJECT_DIR, '.claude', 'cto-reports.db');
|
|
94
|
+
if (fs.existsSync(CTO_REPORTS_DB)) {
|
|
95
|
+
try {
|
|
96
|
+
const reportsDb = new Database(CTO_REPORTS_DB, { readonly: true });
|
|
97
|
+
// Check if triage_status column exists
|
|
98
|
+
const columns = reportsDb.pragma('table_info(reports)');
|
|
99
|
+
const hasTriageStatus = columns.some(c => c.name === 'triage_status');
|
|
100
|
+
|
|
101
|
+
if (hasTriageStatus) {
|
|
102
|
+
const triageResult = reportsDb.prepare(
|
|
103
|
+
"SELECT COUNT(*) as count FROM reports WHERE triage_status = 'pending'"
|
|
104
|
+
).get();
|
|
105
|
+
triageCount = triageResult?.count || 0;
|
|
106
|
+
} else {
|
|
107
|
+
// Fallback for databases without triage_status column
|
|
108
|
+
const triageResult = reportsDb.prepare(
|
|
109
|
+
"SELECT COUNT(*) as count FROM reports WHERE triaged_at IS NULL"
|
|
110
|
+
).get();
|
|
111
|
+
triageCount = triageResult?.count || 0;
|
|
112
|
+
}
|
|
113
|
+
reportsDb.close();
|
|
114
|
+
} catch {
|
|
115
|
+
// G001: Fail closed - if we can't read triage count, assume there are pending items
|
|
116
|
+
// This blocks commits when the database is corrupted/unreadable (safer default)
|
|
117
|
+
triageCount = 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const totalCount = questionCount + triageCount;
|
|
122
|
+
return { hasItems: totalCount > 0, count: totalCount, questionCount, triageCount, error: false };
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error(`[pre-commit] Error checking CTO items: ${err.message}`);
|
|
125
|
+
// G001: Fail closed - on error reading DB, block commits (safer default)
|
|
126
|
+
return { hasItems: true, count: 1, error: true };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if a valid approval token exists for the current staged changes
|
|
132
|
+
*/
|
|
133
|
+
function checkApprovalToken(diffHash) {
|
|
134
|
+
if (!fs.existsSync(APPROVAL_TOKEN_FILE)) {
|
|
135
|
+
return { valid: false, reason: 'no-token' };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const token = JSON.parse(fs.readFileSync(APPROVAL_TOKEN_FILE, 'utf8'));
|
|
140
|
+
|
|
141
|
+
// Empty object means token was consumed (overwrite pattern for sticky-bit compat)
|
|
142
|
+
if (!token.expiresAt && !token.diffHash) {
|
|
143
|
+
return { valid: false, reason: 'no-token' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
|
|
148
|
+
// Check expiry
|
|
149
|
+
const expiresAt = new Date(token.expiresAt).getTime();
|
|
150
|
+
if (isNaN(expiresAt) || now > expiresAt) {
|
|
151
|
+
fs.writeFileSync(APPROVAL_TOKEN_FILE, '{}'); // Clean up expired token (overwrite for sticky-bit compat)
|
|
152
|
+
return { valid: false, reason: 'expired' };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check diff hash matches
|
|
156
|
+
if (token.diffHash !== diffHash) {
|
|
157
|
+
return { valid: false, reason: 'diff-changed' };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { valid: true, token };
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.error(`[pre-commit] Error reading token: ${err.message}`);
|
|
163
|
+
return { valid: false, reason: 'read-error' };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Consume (delete) the approval token after successful use
|
|
169
|
+
*/
|
|
170
|
+
function consumeApprovalToken() {
|
|
171
|
+
try {
|
|
172
|
+
if (fs.existsSync(APPROVAL_TOKEN_FILE)) {
|
|
173
|
+
fs.writeFileSync(APPROVAL_TOKEN_FILE, '{}');
|
|
174
|
+
}
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.error(`[pre-commit] Warning: Could not clear token: ${err.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get branch info
|
|
182
|
+
*/
|
|
183
|
+
function getBranchInfo() {
|
|
184
|
+
try {
|
|
185
|
+
return execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
186
|
+
} catch {
|
|
187
|
+
return 'unknown';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Spawn deputy-cto to review and potentially approve the commit
|
|
193
|
+
*/
|
|
194
|
+
function spawnDeputyCtoReview(stagedInfo) {
|
|
195
|
+
const branch = getBranchInfo();
|
|
196
|
+
|
|
197
|
+
const prompt = `[Task][deputy-cto-review] You are an orchestrator. Review this pending commit and decide whether to approve it.
|
|
198
|
+
|
|
199
|
+
## IMMEDIATE ACTION
|
|
200
|
+
|
|
201
|
+
Your first action MUST be to spawn the deputy-cto sub-agent:
|
|
202
|
+
\`\`\`
|
|
203
|
+
Task(subagent_type='deputy-cto', prompt='Review this pending commit on branch ${branch} (${stagedInfo.files.length} files changed) and decide whether to approve or reject it. Use mcp__deputy-cto__approve_commit or mcp__deputy-cto__reject_commit.')
|
|
204
|
+
\`\`\`
|
|
205
|
+
|
|
206
|
+
The deputy-cto sub-agent has specialized instructions loaded from .claude/agents/deputy-cto.md.
|
|
207
|
+
|
|
208
|
+
## Context
|
|
209
|
+
- Branch: ${branch}
|
|
210
|
+
- Files changed: ${stagedInfo.files.length}
|
|
211
|
+
- Diff hash: ${stagedInfo.diffHash}
|
|
212
|
+
|
|
213
|
+
## Staged Files
|
|
214
|
+
${stagedInfo.files.join('\n')}
|
|
215
|
+
|
|
216
|
+
## Diff Statistics
|
|
217
|
+
${stagedInfo.stat}
|
|
218
|
+
|
|
219
|
+
## Full Diff
|
|
220
|
+
\`\`\`diff
|
|
221
|
+
${stagedInfo.diff}
|
|
222
|
+
\`\`\`
|
|
223
|
+
|
|
224
|
+
## Your Task
|
|
225
|
+
|
|
226
|
+
1. Review the changes for:
|
|
227
|
+
- Security issues (hardcoded credentials, exposed secrets) - CRITICAL
|
|
228
|
+
- Architectural violations (cross-product boundary violations) - CRITICAL
|
|
229
|
+
- Breaking changes without documentation - IMPORTANT
|
|
230
|
+
- Code quality issues - NOTE for later
|
|
231
|
+
|
|
232
|
+
2. Make a decision:
|
|
233
|
+
- If APPROVED: Call mcp__deputy-cto__approve_commit({ rationale: "..." })
|
|
234
|
+
This writes an approval token so the developer can commit.
|
|
235
|
+
- If REJECTED: Call mcp__deputy-cto__reject_commit({ title: "...", description: "..." })
|
|
236
|
+
This blocks commits until addressed via /deputy-cto.
|
|
237
|
+
|
|
238
|
+
3. For non-critical observations, use mcp__deputy-cto__add_question() to note items for CTO review.
|
|
239
|
+
|
|
240
|
+
IMPORTANT: You MUST call either approve_commit or reject_commit. The developer is waiting to commit.`;
|
|
241
|
+
|
|
242
|
+
// Register spawn
|
|
243
|
+
const agentId = registerSpawn({
|
|
244
|
+
type: AGENT_TYPES.DEPUTY_CTO_REVIEW,
|
|
245
|
+
hookType: HOOK_TYPES.PRE_COMMIT_REVIEW,
|
|
246
|
+
description: `Review: ${stagedInfo.files.length} files on ${branch}`,
|
|
247
|
+
prompt: prompt,
|
|
248
|
+
metadata: {
|
|
249
|
+
fileCount: stagedInfo.files.length,
|
|
250
|
+
files: stagedInfo.files.slice(0, 10),
|
|
251
|
+
branch,
|
|
252
|
+
diffHash: stagedInfo.diffHash,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const mcpConfigPath = path.join(PROJECT_DIR, '.mcp.json');
|
|
257
|
+
|
|
258
|
+
// Spawn as detached background process
|
|
259
|
+
const claude = spawn('claude', [
|
|
260
|
+
'--dangerously-skip-permissions',
|
|
261
|
+
'--mcp-config', mcpConfigPath,
|
|
262
|
+
'-p', prompt,
|
|
263
|
+
], {
|
|
264
|
+
cwd: PROJECT_DIR,
|
|
265
|
+
detached: true,
|
|
266
|
+
stdio: 'ignore',
|
|
267
|
+
env: {
|
|
268
|
+
...process.env,
|
|
269
|
+
CLAUDE_PROJECT_DIR: PROJECT_DIR,
|
|
270
|
+
CLAUDE_SPAWNED_SESSION: 'true',
|
|
271
|
+
CLAUDE_AGENT_ID: agentId,
|
|
272
|
+
DEPUTY_CTO_DIFF_HASH: stagedInfo.diffHash, // Pass hash for token creation
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
claude.unref();
|
|
277
|
+
|
|
278
|
+
return { agentId, pid: claude.pid };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Check for a valid emergency bypass decision (created by execute_bypass)
|
|
283
|
+
* Returns true if there's a recent bypass that should allow the commit through
|
|
284
|
+
*/
|
|
285
|
+
function hasValidBypassDecision() {
|
|
286
|
+
if (!Database || !fs.existsSync(DEPUTY_CTO_DB)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
const db = new Database(DEPUTY_CTO_DB, { readonly: true });
|
|
292
|
+
const fiveMinutesAgo = Math.floor((Date.now() - 5 * 60 * 1000) / 1000);
|
|
293
|
+
|
|
294
|
+
// Check for recent bypass decisions (created by execute_bypass)
|
|
295
|
+
const bypass = db.prepare(`
|
|
296
|
+
SELECT id, rationale FROM commit_decisions
|
|
297
|
+
WHERE decision = 'approved'
|
|
298
|
+
AND rationale LIKE 'EMERGENCY BYPASS%'
|
|
299
|
+
AND question_id IS NOT NULL
|
|
300
|
+
AND created_timestamp > ?
|
|
301
|
+
ORDER BY created_timestamp DESC
|
|
302
|
+
LIMIT 1
|
|
303
|
+
`).get(fiveMinutesAgo);
|
|
304
|
+
|
|
305
|
+
db.close();
|
|
306
|
+
|
|
307
|
+
if (bypass) {
|
|
308
|
+
console.log('[deputy-cto] ✓ Emergency bypass active - commit allowed');
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
} catch {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Verify that git core.hooksPath hasn't been tampered with.
|
|
319
|
+
* An agent could bypass hooks entirely by changing this setting.
|
|
320
|
+
*/
|
|
321
|
+
function verifyGitHooksPath() {
|
|
322
|
+
try {
|
|
323
|
+
const hooksPath = execSync('git config --get core.hooksPath', { encoding: 'utf8' }).trim();
|
|
324
|
+
|
|
325
|
+
// Empty string means core.hooksPath is set to nothing, which means default .git/hooks
|
|
326
|
+
if (hooksPath === '') {
|
|
327
|
+
return { valid: true, path: hooksPath };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Resolve the configured path to an absolute path
|
|
331
|
+
const resolvedHooksPath = path.isAbsolute(hooksPath)
|
|
332
|
+
? path.resolve(hooksPath)
|
|
333
|
+
: path.resolve(PROJECT_DIR, hooksPath);
|
|
334
|
+
|
|
335
|
+
// Build list of allowed roots: current PROJECT_DIR + main repo root (for worktrees)
|
|
336
|
+
const allowedRoots = [PROJECT_DIR];
|
|
337
|
+
|
|
338
|
+
// In a worktree, PROJECT_DIR is the worktree root, but .husky may be in the main repo
|
|
339
|
+
try {
|
|
340
|
+
const commonDir = execSync('git rev-parse --git-common-dir', { encoding: 'utf8' }).trim();
|
|
341
|
+
const mainRepoRoot = path.dirname(path.resolve(commonDir));
|
|
342
|
+
if (mainRepoRoot !== PROJECT_DIR) {
|
|
343
|
+
allowedRoots.push(mainRepoRoot);
|
|
344
|
+
}
|
|
345
|
+
} catch {
|
|
346
|
+
// Not in a worktree or git error -- PROJECT_DIR alone is sufficient
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check if resolvedHooksPath matches any allowed root's .husky
|
|
350
|
+
const isAllowed = allowedRoots.some(
|
|
351
|
+
(root) => resolvedHooksPath === path.resolve(root, '.husky')
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
if (!isAllowed) {
|
|
355
|
+
return { valid: false, path: hooksPath };
|
|
356
|
+
}
|
|
357
|
+
return { valid: true, path: hooksPath };
|
|
358
|
+
} catch {
|
|
359
|
+
// No hooksPath set means using default .git/hooks - that's fine if this hook is running
|
|
360
|
+
return { valid: true, path: '(default)' };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Verify that critical files are protected (root-owned).
|
|
366
|
+
* This is defense-in-depth: even if an agent modifies files, this check catches it.
|
|
367
|
+
*/
|
|
368
|
+
function verifyProtectionStatus() {
|
|
369
|
+
const protectedFiles = [
|
|
370
|
+
path.join(__dirname, 'pre-commit-review.js'),
|
|
371
|
+
path.join(__dirname, 'bypass-approval-hook.js'),
|
|
372
|
+
path.join(PROJECT_DIR, 'eslint.config.js'),
|
|
373
|
+
path.join(PROJECT_DIR, '.husky', 'pre-commit'),
|
|
374
|
+
path.join(PROJECT_DIR, 'package.json'),
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
const unprotectedFiles = [];
|
|
378
|
+
|
|
379
|
+
for (const file of protectedFiles) {
|
|
380
|
+
if (!fs.existsSync(file)) {
|
|
381
|
+
continue; // File doesn't exist, skip
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const stats = fs.statSync(file);
|
|
386
|
+
// Check if file is owned by root (uid 0)
|
|
387
|
+
if (stats.uid !== 0) {
|
|
388
|
+
unprotectedFiles.push(file);
|
|
389
|
+
}
|
|
390
|
+
} catch {
|
|
391
|
+
// Skip files we can't stat
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Also check git hooksPath
|
|
396
|
+
const hooksPathCheck = verifyGitHooksPath();
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
protected: unprotectedFiles.length === 0 && hooksPathCheck.valid,
|
|
400
|
+
unprotectedFiles,
|
|
401
|
+
hooksPathTampered: !hooksPathCheck.valid,
|
|
402
|
+
hooksPath: hooksPathCheck.path,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Verify lint configuration integrity.
|
|
408
|
+
* Blocks if any files exist that could weaken linting strictness.
|
|
409
|
+
*/
|
|
410
|
+
function verifyLintConfigIntegrity() {
|
|
411
|
+
const forbiddenFiles = [
|
|
412
|
+
// ESLint override files
|
|
413
|
+
'.eslintignore',
|
|
414
|
+
'eslint.config.mjs',
|
|
415
|
+
'eslint.config.cjs',
|
|
416
|
+
'.eslintrc',
|
|
417
|
+
'.eslintrc.js',
|
|
418
|
+
'.eslintrc.cjs',
|
|
419
|
+
'.eslintrc.json',
|
|
420
|
+
'.eslintrc.yaml',
|
|
421
|
+
'.eslintrc.yml',
|
|
422
|
+
// lint-staged override files
|
|
423
|
+
'.lintstagedrc',
|
|
424
|
+
'.lintstagedrc.json',
|
|
425
|
+
'.lintstagedrc.yaml',
|
|
426
|
+
'.lintstagedrc.yml',
|
|
427
|
+
'.lintstagedrc.mjs',
|
|
428
|
+
'.lintstagedrc.cjs',
|
|
429
|
+
'.lintstagedrc.js',
|
|
430
|
+
'lint-staged.config.js',
|
|
431
|
+
'lint-staged.config.mjs',
|
|
432
|
+
'lint-staged.config.cjs',
|
|
433
|
+
// Husky override files
|
|
434
|
+
'.huskyrc',
|
|
435
|
+
'.huskyrc.json',
|
|
436
|
+
'.huskyrc.js',
|
|
437
|
+
'.huskyrc.yaml',
|
|
438
|
+
'.huskyrc.yml',
|
|
439
|
+
'husky.config.js',
|
|
440
|
+
];
|
|
441
|
+
|
|
442
|
+
const foundFiles = [];
|
|
443
|
+
|
|
444
|
+
for (const file of forbiddenFiles) {
|
|
445
|
+
const filePath = path.join(PROJECT_DIR, file);
|
|
446
|
+
if (fs.existsSync(filePath)) {
|
|
447
|
+
foundFiles.push(file);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
valid: foundFiles.length === 0,
|
|
453
|
+
forbiddenFiles: foundFiles,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Run ESLint directly on staged TypeScript files with --max-warnings 0.
|
|
459
|
+
* This is belt-and-suspenders enforcement - even if lint-staged is bypassed,
|
|
460
|
+
* this check will catch lint issues.
|
|
461
|
+
*
|
|
462
|
+
* SECURITY: Uses project-local eslint from node_modules to prevent PATH manipulation.
|
|
463
|
+
*/
|
|
464
|
+
function runStrictLint(stagedFiles) {
|
|
465
|
+
// Filter to only TypeScript files that exist on disk (exclude deleted files)
|
|
466
|
+
const tsFiles = stagedFiles.filter(f =>
|
|
467
|
+
(f.endsWith('.ts') || f.endsWith('.tsx')) && fs.existsSync(path.join(PROJECT_DIR, f))
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
if (tsFiles.length === 0) {
|
|
471
|
+
return { success: true, skipped: true };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// SECURITY: Use project-local eslint binary directly, not npx (which could be PATH-manipulated)
|
|
475
|
+
const eslintBin = path.join(PROJECT_DIR, 'node_modules', '.bin', 'eslint');
|
|
476
|
+
|
|
477
|
+
if (!fs.existsSync(eslintBin)) {
|
|
478
|
+
return {
|
|
479
|
+
success: false,
|
|
480
|
+
output: `ESLint binary not found at ${eslintBin}. Run npm install.`,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
// Run eslint with max-warnings 0 on staged TS files
|
|
486
|
+
// Use absolute path to eslint binary to prevent PATH manipulation attacks
|
|
487
|
+
const result = execSync(
|
|
488
|
+
`"${eslintBin}" --max-warnings 0 ${tsFiles.map(f => `"${f}"`).join(' ')}`,
|
|
489
|
+
{
|
|
490
|
+
encoding: 'utf8',
|
|
491
|
+
cwd: PROJECT_DIR,
|
|
492
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
493
|
+
env: {
|
|
494
|
+
...process.env,
|
|
495
|
+
// Clear any potentially malicious env vars
|
|
496
|
+
ESLINT_USE_FLAT_CONFIG: 'true',
|
|
497
|
+
},
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
return { success: true, output: result };
|
|
501
|
+
} catch (err) {
|
|
502
|
+
// ESLint returns non-zero exit code on errors/warnings
|
|
503
|
+
const output = err.stdout || err.stderr || err.message;
|
|
504
|
+
return { success: false, output };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Main entry point
|
|
510
|
+
*/
|
|
511
|
+
async function main() {
|
|
512
|
+
// NOTE: SKIP_DEPUTY_CTO_REVIEW env var bypass has been REMOVED.
|
|
513
|
+
// To bypass, agents must use request_bypass() and the CTO must type
|
|
514
|
+
// "APPROVE BYPASS <code>" in the chat. This creates an approval token
|
|
515
|
+
// that execute_bypass() verifies.
|
|
516
|
+
|
|
517
|
+
// ============================================================================
|
|
518
|
+
// UNBYPASSABLE SECURITY CHECKS (run before any bypass checks)
|
|
519
|
+
// ============================================================================
|
|
520
|
+
|
|
521
|
+
// Check for forbidden lint config files (CRITICAL - no bypass allowed)
|
|
522
|
+
const lintConfigCheck = verifyLintConfigIntegrity();
|
|
523
|
+
if (!lintConfigCheck.valid) {
|
|
524
|
+
console.error('');
|
|
525
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
526
|
+
console.error(' COMMIT BLOCKED: Forbidden lint configuration files detected!');
|
|
527
|
+
console.error('');
|
|
528
|
+
console.error(' These files can weaken linting strictness and must be removed:');
|
|
529
|
+
for (const file of lintConfigCheck.forbiddenFiles) {
|
|
530
|
+
console.error(` - ${file}`);
|
|
531
|
+
}
|
|
532
|
+
console.error('');
|
|
533
|
+
console.error(' This check cannot be bypassed. Remove the files to proceed.');
|
|
534
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
535
|
+
console.error('');
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Check protection status (defense in depth)
|
|
540
|
+
const protectionCheck = verifyProtectionStatus();
|
|
541
|
+
|
|
542
|
+
// BLOCK if git hooksPath has been tampered with (critical security issue)
|
|
543
|
+
if (protectionCheck.hooksPathTampered) {
|
|
544
|
+
console.error('');
|
|
545
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
546
|
+
console.error(' COMMIT BLOCKED: Git hooks path has been tampered with!');
|
|
547
|
+
console.error('');
|
|
548
|
+
console.error(` Current core.hooksPath: ${protectionCheck.hooksPath}`);
|
|
549
|
+
console.error(' Expected: .husky (or unset)');
|
|
550
|
+
console.error('');
|
|
551
|
+
console.error(' This may be an attempt to bypass security hooks.');
|
|
552
|
+
console.error(' To fix: git config --unset core.hooksPath');
|
|
553
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
554
|
+
console.error('');
|
|
555
|
+
process.exit(1);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Warn if files not protected (but don't block)
|
|
559
|
+
if (protectionCheck.unprotectedFiles.length > 0) {
|
|
560
|
+
console.error('');
|
|
561
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
562
|
+
console.error(' WARNING: Framework protection not enabled');
|
|
563
|
+
console.error('');
|
|
564
|
+
console.error(' Unprotected files:');
|
|
565
|
+
for (const file of protectionCheck.unprotectedFiles) {
|
|
566
|
+
console.error(` - ${file}`);
|
|
567
|
+
}
|
|
568
|
+
console.error('');
|
|
569
|
+
console.error(' Run: sudo ./scripts/protect-framework.sh');
|
|
570
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
571
|
+
console.error('');
|
|
572
|
+
// Continue anyway - this is a warning, not a block
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Get staged changes (needed for lint check)
|
|
576
|
+
const stagedInfo = getStagedInfo();
|
|
577
|
+
|
|
578
|
+
if (stagedInfo.error) {
|
|
579
|
+
console.error('[pre-commit] Error getting staged changes');
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (stagedInfo.files.length === 0) {
|
|
584
|
+
console.log('[deputy-cto] No staged files, skipping review.');
|
|
585
|
+
process.exit(0);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// ============================================================================
|
|
589
|
+
// STRICT LINT CHECK (UNBYPASSABLE - runs before any bypass checks)
|
|
590
|
+
// ============================================================================
|
|
591
|
+
const lintResult = runStrictLint(stagedInfo.files);
|
|
592
|
+
if (!lintResult.success && !lintResult.skipped) {
|
|
593
|
+
console.error('');
|
|
594
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
595
|
+
console.error(' COMMIT BLOCKED: ESLint errors or warnings detected!');
|
|
596
|
+
console.error('');
|
|
597
|
+
console.error(' Lint output:');
|
|
598
|
+
console.error(lintResult.output);
|
|
599
|
+
console.error('');
|
|
600
|
+
console.error(' This check cannot be bypassed. Fix all lint issues to proceed.');
|
|
601
|
+
console.error(' Rule: --max-warnings 0 (zero tolerance for warnings)');
|
|
602
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
603
|
+
console.error('');
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ============================================================================
|
|
608
|
+
// PROTECTED BRANCH GUARD (UNBYPASSABLE)
|
|
609
|
+
// ============================================================================
|
|
610
|
+
// Prevents direct commits to protected branches.
|
|
611
|
+
// Merge chain: feature/* -> preview -> staging -> main
|
|
612
|
+
// Only promotion pipeline agents can operate on protected branches.
|
|
613
|
+
// ============================================================================
|
|
614
|
+
const PROTECTED_BRANCHES = ['main', 'preview', 'staging'];
|
|
615
|
+
const currentBranchForGuard = getBranchInfo();
|
|
616
|
+
|
|
617
|
+
if (PROTECTED_BRANCHES.includes(currentBranchForGuard)) {
|
|
618
|
+
if (process.env.GENTYR_PROMOTION_PIPELINE !== 'true') {
|
|
619
|
+
console.error('');
|
|
620
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
621
|
+
console.error(` COMMIT BLOCKED: Direct commits to '${currentBranchForGuard}' are forbidden.`);
|
|
622
|
+
console.error('');
|
|
623
|
+
console.error(' Merge chain: feature/* -> preview -> staging -> main');
|
|
624
|
+
console.error('');
|
|
625
|
+
console.error(' Create a feature branch:');
|
|
626
|
+
console.error(' git checkout -b feature/<name> preview');
|
|
627
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
628
|
+
console.error('');
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
631
|
+
// Promotion pipeline agents are allowed to operate on protected branches
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// ============================================================================
|
|
635
|
+
// BYPASSABLE CHECKS (emergency bypass can skip CTO review, but NOT lint)
|
|
636
|
+
// ============================================================================
|
|
637
|
+
|
|
638
|
+
// Check for emergency bypass (allows commit even with pending CTO items)
|
|
639
|
+
if (hasValidBypassDecision()) {
|
|
640
|
+
console.log('[deputy-cto] ✓ Emergency bypass active - skipping CTO review');
|
|
641
|
+
console.log('[deputy-cto] ✓ Lint passed - commit approved');
|
|
642
|
+
process.exit(0);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// G020: Branch-aware commit blocking
|
|
646
|
+
const ctoItemsCheck = hasPendingCtoItems();
|
|
647
|
+
if (ctoItemsCheck.hasItems) {
|
|
648
|
+
const currentBranch = getBranchInfo();
|
|
649
|
+
|
|
650
|
+
if (currentBranch === 'main' || currentBranch === 'unknown') {
|
|
651
|
+
// MAIN/UNKNOWN: Hard block (G001 fail-closed treats unknown as main)
|
|
652
|
+
console.error('');
|
|
653
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
654
|
+
console.error(' COMMIT BLOCKED: Pending CTO item(s) require attention');
|
|
655
|
+
console.error('');
|
|
656
|
+
if (ctoItemsCheck.questionCount > 0) {
|
|
657
|
+
console.error(` • ${ctoItemsCheck.questionCount} CTO question(s) pending`);
|
|
658
|
+
}
|
|
659
|
+
if (ctoItemsCheck.triageCount > 0) {
|
|
660
|
+
console.error(` • ${ctoItemsCheck.triageCount} untriaged report(s) pending`);
|
|
661
|
+
}
|
|
662
|
+
console.error('');
|
|
663
|
+
console.error(' Run /deputy-cto to address blocking items');
|
|
664
|
+
console.error('══════════════════════════════════════════════════════════════');
|
|
665
|
+
console.error('');
|
|
666
|
+
process.exit(1);
|
|
667
|
+
} else if (currentBranch === 'develop' || currentBranch === 'staging') {
|
|
668
|
+
// STAGING/DEVELOP: Warn but allow commit
|
|
669
|
+
console.warn('');
|
|
670
|
+
console.warn('══════════════════════════════════════════════════════════════');
|
|
671
|
+
console.warn(` WARNING: Pending CTO items exist (committing to ${currentBranch})`);
|
|
672
|
+
console.warn('');
|
|
673
|
+
if (ctoItemsCheck.questionCount > 0) {
|
|
674
|
+
console.warn(` • ${ctoItemsCheck.questionCount} CTO question(s) pending`);
|
|
675
|
+
}
|
|
676
|
+
if (ctoItemsCheck.triageCount > 0) {
|
|
677
|
+
console.warn(` • ${ctoItemsCheck.triageCount} untriaged report(s) pending`);
|
|
678
|
+
}
|
|
679
|
+
console.warn('');
|
|
680
|
+
console.warn(' These must be resolved before merging to main.');
|
|
681
|
+
console.warn('══════════════════════════════════════════════════════════════');
|
|
682
|
+
console.warn('');
|
|
683
|
+
// Allow commit to proceed (do NOT exit)
|
|
684
|
+
}
|
|
685
|
+
// Feature branches: no blocking, no warning -- items checked on merge
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Check for valid approval token
|
|
689
|
+
const tokenCheck = checkApprovalToken(stagedInfo.diffHash);
|
|
690
|
+
|
|
691
|
+
if (tokenCheck.valid) {
|
|
692
|
+
// Token is valid - allow the commit
|
|
693
|
+
console.log('[deputy-cto] ✓ Lint passed (--max-warnings 0)');
|
|
694
|
+
console.log('[deputy-cto] ✓ Approval token valid - commit approved');
|
|
695
|
+
console.log(`[deputy-cto] Approved by: ${tokenCheck.token.approvedBy || 'deputy-cto'}`);
|
|
696
|
+
consumeApprovalToken(); // One-time use
|
|
697
|
+
process.exit(0);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// No valid token - spawn review and reject this attempt
|
|
701
|
+
console.log('');
|
|
702
|
+
console.log('══════════════════════════════════════════════════════════════');
|
|
703
|
+
console.log(' COMMIT PENDING: Deputy-CTO review required');
|
|
704
|
+
console.log('══════════════════════════════════════════════════════════════');
|
|
705
|
+
console.log('');
|
|
706
|
+
console.log(` Files: ${stagedInfo.files.length} staged`);
|
|
707
|
+
console.log(` Hash: ${stagedInfo.diffHash}`);
|
|
708
|
+
console.log('');
|
|
709
|
+
|
|
710
|
+
// Spawn the review
|
|
711
|
+
const { agentId, pid } = spawnDeputyCtoReview(stagedInfo);
|
|
712
|
+
|
|
713
|
+
console.log(` Review spawned (agent: ${agentId})`);
|
|
714
|
+
console.log('');
|
|
715
|
+
console.log(' → Wait for approval, then retry your commit');
|
|
716
|
+
console.log(` → Token expires in ${Math.round(TOKEN_EXPIRY_MS / 60000)} minutes after approval`);
|
|
717
|
+
console.log('');
|
|
718
|
+
console.log('══════════════════════════════════════════════════════════════');
|
|
719
|
+
console.log('');
|
|
720
|
+
|
|
721
|
+
// Reject this commit attempt
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
main();
|