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,401 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Antipattern Hunter Hook
|
|
5
|
+
*
|
|
6
|
+
* Called from husky post-commit hook. If 6 hours have passed since the last spawn,
|
|
7
|
+
* spawns TWO Claude sessions:
|
|
8
|
+
* 1. Repo-wide hunter: Scans the entire codebase for spec violations
|
|
9
|
+
* 2. Commit-focused hunter: Reviews only the files changed in the current commit
|
|
10
|
+
*
|
|
11
|
+
* Both hunters raise critical issues to the CTO via mcp__agent-reports__report_to_deputy_cto.
|
|
12
|
+
*
|
|
13
|
+
* Usage: node .claude/hooks/antipattern-hunter-hook.js
|
|
14
|
+
*
|
|
15
|
+
* @version 2.0.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { spawn, execSync } from 'child_process';
|
|
21
|
+
import { fileURLToPath } from 'url';
|
|
22
|
+
import { registerSpawn, registerHookExecution, AGENT_TYPES, HOOK_TYPES } from './agent-tracker.js';
|
|
23
|
+
import { getCooldown } from './config-reader.js';
|
|
24
|
+
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
const __dirname = path.dirname(__filename);
|
|
27
|
+
|
|
28
|
+
// Project directory
|
|
29
|
+
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
30
|
+
|
|
31
|
+
// Configuration
|
|
32
|
+
const CONFIG = {
|
|
33
|
+
stateFile: path.join(projectDir, '.claude', 'state', 'antipattern-hunter-state.json'),
|
|
34
|
+
cooldownHours: getCooldown('antipattern_hunter', 360) / 60, // config is in minutes, convert to hours
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read state file
|
|
39
|
+
*/
|
|
40
|
+
function readState() {
|
|
41
|
+
try {
|
|
42
|
+
if (!fs.existsSync(CONFIG.stateFile)) {
|
|
43
|
+
return { lastSpawn: null };
|
|
44
|
+
}
|
|
45
|
+
return JSON.parse(fs.readFileSync(CONFIG.stateFile, 'utf8'));
|
|
46
|
+
} catch {
|
|
47
|
+
return { lastSpawn: null };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Write state file
|
|
53
|
+
*/
|
|
54
|
+
function writeState(state) {
|
|
55
|
+
fs.writeFileSync(CONFIG.stateFile, JSON.stringify(state, null, 2), 'utf8');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if cooldown has elapsed
|
|
60
|
+
*/
|
|
61
|
+
function isCooldownElapsed() {
|
|
62
|
+
const state = readState();
|
|
63
|
+
|
|
64
|
+
if (!state.lastSpawn) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const lastSpawn = new Date(state.lastSpawn);
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const hoursSince = (now - lastSpawn) / (1000 * 60 * 60);
|
|
71
|
+
|
|
72
|
+
return hoursSince >= CONFIG.cooldownHours;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the files changed in the most recent commit
|
|
77
|
+
* @returns {object} { files: string[], diff: string, commitMessage: string }
|
|
78
|
+
*/
|
|
79
|
+
function getCommitChanges() {
|
|
80
|
+
try {
|
|
81
|
+
// Get list of changed files
|
|
82
|
+
const filesOutput = execSync('git diff-tree --no-commit-id --name-only -r HEAD', {
|
|
83
|
+
cwd: projectDir,
|
|
84
|
+
encoding: 'utf8'
|
|
85
|
+
}).trim();
|
|
86
|
+
|
|
87
|
+
const files = filesOutput ? filesOutput.split('\n').filter(Boolean) : [];
|
|
88
|
+
|
|
89
|
+
// Get the diff for the commit
|
|
90
|
+
const diff = execSync('git show --stat HEAD', {
|
|
91
|
+
cwd: projectDir,
|
|
92
|
+
encoding: 'utf8',
|
|
93
|
+
maxBuffer: 1024 * 1024 // 1MB max
|
|
94
|
+
}).trim();
|
|
95
|
+
|
|
96
|
+
// Get commit message
|
|
97
|
+
const commitMessage = execSync('git log -1 --pretty=%B', {
|
|
98
|
+
cwd: projectDir,
|
|
99
|
+
encoding: 'utf8'
|
|
100
|
+
}).trim();
|
|
101
|
+
|
|
102
|
+
return { files, diff, commitMessage };
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error(`[antipattern-hunter] Failed to get commit changes: ${err.message}`);
|
|
105
|
+
return { files: [], diff: '', commitMessage: '' };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* CTO reporting instructions (shared by both hunters)
|
|
111
|
+
*/
|
|
112
|
+
const CTO_REPORTING_INSTRUCTIONS = `
|
|
113
|
+
## CTO Reporting (CRITICAL)
|
|
114
|
+
|
|
115
|
+
You MUST report important findings to the CTO using the agent-reports MCP server.
|
|
116
|
+
|
|
117
|
+
**Report when you find:**
|
|
118
|
+
- Security violations (G004 hardcoded credentials, G009 missing RLS, G010 missing auth)
|
|
119
|
+
- Architecture boundary violations (cross-product separation)
|
|
120
|
+
- Critical spec violations requiring immediate attention
|
|
121
|
+
- Patterns of repeated violations (3+ similar issues)
|
|
122
|
+
|
|
123
|
+
**How to report:**
|
|
124
|
+
\`\`\`javascript
|
|
125
|
+
mcp__agent-reports__report_to_deputy_cto({
|
|
126
|
+
reporting_agent: "antipattern-hunter",
|
|
127
|
+
title: "Brief title (max 200 chars)",
|
|
128
|
+
summary: "Detailed summary with file paths, line numbers, and severity (max 2000 chars)",
|
|
129
|
+
category: "security" | "architecture" | "performance" | "breaking-change" | "blocker" | "decision" | "other",
|
|
130
|
+
priority: "low" | "normal" | "high" | "critical"
|
|
131
|
+
})
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
**Priority guidelines:**
|
|
135
|
+
- critical: Security issues, data exposure risks
|
|
136
|
+
- high: Architecture violations, widespread pattern issues
|
|
137
|
+
- normal: Standard spec violations
|
|
138
|
+
- low: Minor style/consistency issues
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Spawn repo-wide antipattern hunter
|
|
143
|
+
*/
|
|
144
|
+
function spawnRepoWideHunter() {
|
|
145
|
+
const prompt = `[Task][antipattern-hunter-repo] REPO-WIDE ANTIPATTERN HUNT - Scan the entire codebase for spec violations.
|
|
146
|
+
|
|
147
|
+
## IMMEDIATE ACTION
|
|
148
|
+
|
|
149
|
+
Your first action MUST be to spawn the antipattern-hunter sub-agent:
|
|
150
|
+
\`\`\`
|
|
151
|
+
Task(subagent_type='antipattern-hunter', prompt='Perform a REPO-WIDE antipattern hunt. Systematically scan the entire codebase for spec violations, focusing on areas with accumulated technical debt. Prioritize G001, G004, G009, G010, G016.')
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
The antipattern-hunter sub-agent has specialized instructions loaded from .claude/agents/antipattern-hunter.md.
|
|
155
|
+
|
|
156
|
+
You are a REPO-WIDE antipattern hunter. Your job is to systematically scan the ENTIRE codebase
|
|
157
|
+
looking for spec violations, focusing on areas that may have accumulated technical debt.
|
|
158
|
+
|
|
159
|
+
## Your Focus Areas
|
|
160
|
+
- Hunt across ALL directories: src/, packages/, products/, integrations/
|
|
161
|
+
- Look for systemic patterns of violations
|
|
162
|
+
- Check areas that don't change frequently (may have old violations)
|
|
163
|
+
- Prioritize high-severity specs (G001, G004, G009, G010, G016)
|
|
164
|
+
|
|
165
|
+
## Workflow
|
|
166
|
+
|
|
167
|
+
### Step 1: Load Specifications
|
|
168
|
+
\`\`\`javascript
|
|
169
|
+
mcp__specs-browser__list_specs({})
|
|
170
|
+
mcp__specs-browser__get_spec({ spec_id: "G001" }) // No graceful fallbacks
|
|
171
|
+
mcp__specs-browser__get_spec({ spec_id: "G004" }) // No hardcoded credentials
|
|
172
|
+
mcp__specs-browser__get_spec({ spec_id: "G009" }) // RLS policies required
|
|
173
|
+
mcp__specs-browser__get_spec({ spec_id: "G010" }) // Session auth validation
|
|
174
|
+
mcp__specs-browser__get_spec({ spec_id: "G016" }) // Integration boundary
|
|
175
|
+
\`\`\`
|
|
176
|
+
|
|
177
|
+
### Step 2: Hunt for Violations
|
|
178
|
+
Use Grep to systematically scan for violation patterns:
|
|
179
|
+
- G001: \`|| null\`, \`|| undefined\`, \`?? 0\`, \`|| []\`, \`|| {}\`
|
|
180
|
+
- G002: \`TODO\`, \`FIXME\`, \`throw new Error('Not implemented')\`
|
|
181
|
+
- G004: Hardcoded API keys, credentials, secrets
|
|
182
|
+
- G011: \`MOCK_MODE\`, \`isSimulation\`, \`isMockMode\`
|
|
183
|
+
|
|
184
|
+
### Step 3: For Each Violation
|
|
185
|
+
a. Call code-reviewer sub-agent to review proposed fix
|
|
186
|
+
b. Create TODO item:
|
|
187
|
+
\`\`\`javascript
|
|
188
|
+
mcp__todo-db__create_task({
|
|
189
|
+
section: "CODE-WRITER",
|
|
190
|
+
title: "Fix [SPEC-ID] violation in [file]",
|
|
191
|
+
description: "[Details and approved fix]",
|
|
192
|
+
assigned_by: "ANTIPATTERN-HUNTER-REPO"
|
|
193
|
+
})
|
|
194
|
+
\`\`\`
|
|
195
|
+
|
|
196
|
+
### Step 4: Report Critical Issues to CTO
|
|
197
|
+
${CTO_REPORTING_INSTRUCTIONS}
|
|
198
|
+
|
|
199
|
+
### Step 5: END SESSION
|
|
200
|
+
After creating TODO items and CTO reports, provide a summary and END YOUR SESSION.
|
|
201
|
+
Do NOT implement fixes yourself.
|
|
202
|
+
|
|
203
|
+
Focus on finding SYSTEMIC issues across the codebase, not just isolated violations.`;
|
|
204
|
+
|
|
205
|
+
// Register spawn
|
|
206
|
+
const agentId = registerSpawn({
|
|
207
|
+
type: AGENT_TYPES.ANTIPATTERN_HUNTER_REPO,
|
|
208
|
+
hookType: HOOK_TYPES.ANTIPATTERN_HUNTER,
|
|
209
|
+
description: 'Repo-wide antipattern hunt after commit',
|
|
210
|
+
prompt,
|
|
211
|
+
metadata: { trigger: 'post-commit', scope: 'repo-wide', cooldownHours: CONFIG.cooldownHours },
|
|
212
|
+
projectDir
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Spawn Claude session (fire-and-forget, detached)
|
|
216
|
+
const claude = spawn('claude', [
|
|
217
|
+
'--dangerously-skip-permissions',
|
|
218
|
+
'-p',
|
|
219
|
+
prompt
|
|
220
|
+
], {
|
|
221
|
+
detached: true,
|
|
222
|
+
stdio: 'ignore',
|
|
223
|
+
cwd: projectDir,
|
|
224
|
+
env: {
|
|
225
|
+
...process.env,
|
|
226
|
+
CLAUDE_PROJECT_DIR: projectDir,
|
|
227
|
+
CLAUDE_SPAWNED_SESSION: 'true'
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
claude.unref();
|
|
232
|
+
|
|
233
|
+
console.log(`[antipattern-hunter] Spawned REPO-WIDE hunter ${agentId} (PID: ${claude.pid})`);
|
|
234
|
+
return agentId;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Spawn commit-focused antipattern hunter
|
|
239
|
+
*/
|
|
240
|
+
function spawnCommitFocusedHunter() {
|
|
241
|
+
const { files, diff, commitMessage } = getCommitChanges();
|
|
242
|
+
|
|
243
|
+
if (files.length === 0) {
|
|
244
|
+
console.log('[antipattern-hunter] No files in commit, skipping commit-focused hunter');
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const fileList = files.join('\n - ');
|
|
249
|
+
|
|
250
|
+
const prompt = `[Task][antipattern-hunter-commit] COMMIT-FOCUSED ANTIPATTERN HUNT - Review only the changes in the current commit.
|
|
251
|
+
|
|
252
|
+
## IMMEDIATE ACTION
|
|
253
|
+
|
|
254
|
+
Your first action MUST be to spawn the antipattern-hunter sub-agent:
|
|
255
|
+
\`\`\`
|
|
256
|
+
Task(subagent_type='antipattern-hunter', prompt='Perform a COMMIT-FOCUSED antipattern hunt. Review ONLY the files changed in the most recent commit for spec violations. Commit: ${commitMessage.split('\n')[0]}. Changed files: ${fileList}')
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
The antipattern-hunter sub-agent has specialized instructions loaded from .claude/agents/antipattern-hunter.md.
|
|
260
|
+
|
|
261
|
+
You are a COMMIT-FOCUSED antipattern hunter. Your job is to deeply review ONLY the files
|
|
262
|
+
that were changed in the most recent commit, checking for spec violations introduced or
|
|
263
|
+
existing in those specific files.
|
|
264
|
+
|
|
265
|
+
## Commit Information
|
|
266
|
+
**Message:** ${commitMessage}
|
|
267
|
+
|
|
268
|
+
**Changed Files:**
|
|
269
|
+
- ${fileList}
|
|
270
|
+
|
|
271
|
+
**Commit Summary:**
|
|
272
|
+
\`\`\`
|
|
273
|
+
${diff}
|
|
274
|
+
\`\`\`
|
|
275
|
+
|
|
276
|
+
## Your Focus
|
|
277
|
+
- ONLY examine the files listed above
|
|
278
|
+
- Check for violations INTRODUCED by this commit
|
|
279
|
+
- Check for PRE-EXISTING violations in these files that should be fixed
|
|
280
|
+
- Be thorough - read each changed file completely
|
|
281
|
+
|
|
282
|
+
## Workflow
|
|
283
|
+
|
|
284
|
+
### Step 1: Load Relevant Specifications
|
|
285
|
+
\`\`\`javascript
|
|
286
|
+
mcp__specs-browser__list_specs({})
|
|
287
|
+
// Load specs most relevant to the changed files
|
|
288
|
+
mcp__specs-browser__get_spec({ spec_id: "G001" }) // No graceful fallbacks
|
|
289
|
+
mcp__specs-browser__get_spec({ spec_id: "G003" }) // Input validation required
|
|
290
|
+
mcp__specs-browser__get_spec({ spec_id: "G004" }) // No hardcoded credentials
|
|
291
|
+
\`\`\`
|
|
292
|
+
|
|
293
|
+
### Step 2: Read and Analyze Each Changed File
|
|
294
|
+
For each file in the commit:
|
|
295
|
+
1. Read the file using the Read tool
|
|
296
|
+
2. Check against ALL relevant specs
|
|
297
|
+
3. Note any violations with exact line numbers
|
|
298
|
+
|
|
299
|
+
### Step 3: For Each Violation
|
|
300
|
+
a. Call code-reviewer sub-agent to review proposed fix
|
|
301
|
+
b. Create TODO item:
|
|
302
|
+
\`\`\`javascript
|
|
303
|
+
mcp__todo-db__create_task({
|
|
304
|
+
section: "CODE-WRITER",
|
|
305
|
+
title: "Fix [SPEC-ID] violation in [file]:[line]",
|
|
306
|
+
description: "[Details and approved fix]. Found in commit: ${commitMessage.split('\n')[0]}",
|
|
307
|
+
assigned_by: "ANTIPATTERN-HUNTER-COMMIT"
|
|
308
|
+
})
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
### Step 4: Report Critical Issues to CTO
|
|
312
|
+
${CTO_REPORTING_INSTRUCTIONS}
|
|
313
|
+
|
|
314
|
+
### Step 5: END SESSION
|
|
315
|
+
After creating TODO items and CTO reports, provide a summary and END YOUR SESSION.
|
|
316
|
+
Do NOT implement fixes yourself.
|
|
317
|
+
|
|
318
|
+
Be THOROUGH with the commit files - this is a deep review, not a surface scan.`;
|
|
319
|
+
|
|
320
|
+
// Register spawn
|
|
321
|
+
const agentId = registerSpawn({
|
|
322
|
+
type: AGENT_TYPES.ANTIPATTERN_HUNTER_COMMIT,
|
|
323
|
+
hookType: HOOK_TYPES.ANTIPATTERN_HUNTER,
|
|
324
|
+
description: `Commit-focused antipattern hunt: ${commitMessage.split('\n')[0].substring(0, 50)}`,
|
|
325
|
+
prompt,
|
|
326
|
+
metadata: {
|
|
327
|
+
trigger: 'post-commit',
|
|
328
|
+
scope: 'commit-focused',
|
|
329
|
+
filesChanged: files.length,
|
|
330
|
+
files: files.slice(0, 20), // Store first 20 files
|
|
331
|
+
cooldownHours: CONFIG.cooldownHours
|
|
332
|
+
},
|
|
333
|
+
projectDir
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Spawn Claude session (fire-and-forget, detached)
|
|
337
|
+
const claude = spawn('claude', [
|
|
338
|
+
'--dangerously-skip-permissions',
|
|
339
|
+
'-p',
|
|
340
|
+
prompt
|
|
341
|
+
], {
|
|
342
|
+
detached: true,
|
|
343
|
+
stdio: 'ignore',
|
|
344
|
+
cwd: projectDir,
|
|
345
|
+
env: {
|
|
346
|
+
...process.env,
|
|
347
|
+
CLAUDE_PROJECT_DIR: projectDir,
|
|
348
|
+
CLAUDE_SPAWNED_SESSION: 'true'
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
claude.unref();
|
|
353
|
+
|
|
354
|
+
console.log(`[antipattern-hunter] Spawned COMMIT-FOCUSED hunter ${agentId} (PID: ${claude.pid})`);
|
|
355
|
+
return agentId;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Main entry point
|
|
360
|
+
*/
|
|
361
|
+
function main() {
|
|
362
|
+
const startTime = Date.now();
|
|
363
|
+
|
|
364
|
+
// Check cooldown
|
|
365
|
+
if (!isCooldownElapsed()) {
|
|
366
|
+
const state = readState();
|
|
367
|
+
const hoursSince = (Date.now() - new Date(state.lastSpawn).getTime()) / (1000 * 60 * 60);
|
|
368
|
+
const hoursRemaining = CONFIG.cooldownHours - hoursSince;
|
|
369
|
+
console.log(`[antipattern-hunter] Cooldown active (${hoursRemaining.toFixed(1)}h remaining)`);
|
|
370
|
+
|
|
371
|
+
registerHookExecution({
|
|
372
|
+
hookType: HOOK_TYPES.ANTIPATTERN_HUNTER,
|
|
373
|
+
status: 'skipped',
|
|
374
|
+
durationMs: Date.now() - startTime,
|
|
375
|
+
metadata: { reason: 'cooldown', hoursRemaining: hoursRemaining.toFixed(1) }
|
|
376
|
+
});
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
console.log('[antipattern-hunter] Spawning antipattern hunters...');
|
|
381
|
+
|
|
382
|
+
// Spawn BOTH hunters
|
|
383
|
+
const repoAgentId = spawnRepoWideHunter();
|
|
384
|
+
const commitAgentId = spawnCommitFocusedHunter();
|
|
385
|
+
|
|
386
|
+
// Update state (cooldown applies to both)
|
|
387
|
+
writeState({ lastSpawn: new Date().toISOString() });
|
|
388
|
+
|
|
389
|
+
console.log(`[antipattern-hunter] Spawned 2 hunters:`);
|
|
390
|
+
console.log(` - Repo-wide: ${repoAgentId}`);
|
|
391
|
+
console.log(` - Commit-focused: ${commitAgentId || 'skipped (no files)'}`);
|
|
392
|
+
|
|
393
|
+
registerHookExecution({
|
|
394
|
+
hookType: HOOK_TYPES.ANTIPATTERN_HUNTER,
|
|
395
|
+
status: 'success',
|
|
396
|
+
durationMs: Date.now() - startTime,
|
|
397
|
+
metadata: { repoAgentId, commitAgentId }
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
main();
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* API Key Rotation Hook
|
|
4
|
+
*
|
|
5
|
+
* Runs on SessionStart to track multiple Claude API keys and automatically
|
|
6
|
+
* rotate between them based on utilization thresholds.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Captures new keys from all sources (env var, macOS Keychain, credentials file)
|
|
10
|
+
* - Monitors usage via Anthropic Usage API
|
|
11
|
+
* - Rotates to lower-utilization keys when current key hits 90%+ usage
|
|
12
|
+
* - Attempts OAuth token refresh for expired keys
|
|
13
|
+
* - Logs all rotation events for debugging
|
|
14
|
+
*
|
|
15
|
+
* Storage:
|
|
16
|
+
* - ~/.claude/api-key-rotation.json - Tracked keys and state (user-level, cross-project)
|
|
17
|
+
* - <project>/.claude/api-key-rotation.log - Human-readable event log (project-level)
|
|
18
|
+
*
|
|
19
|
+
* @version 2.0.0
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { fileURLToPath } from 'url';
|
|
23
|
+
import { registerHookExecution, HOOK_TYPES } from './agent-tracker.js';
|
|
24
|
+
import {
|
|
25
|
+
syncKeys,
|
|
26
|
+
readRotationState,
|
|
27
|
+
writeRotationState,
|
|
28
|
+
logRotationEvent,
|
|
29
|
+
updateActiveCredentials,
|
|
30
|
+
refreshExpiredToken,
|
|
31
|
+
checkKeyHealth,
|
|
32
|
+
selectActiveKey,
|
|
33
|
+
fetchAccountProfile,
|
|
34
|
+
HIGH_USAGE_THRESHOLD,
|
|
35
|
+
EXHAUSTED_THRESHOLD,
|
|
36
|
+
} from './key-sync.js';
|
|
37
|
+
|
|
38
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Main entry point
|
|
42
|
+
*/
|
|
43
|
+
async function main() {
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
|
|
46
|
+
// Skip for spawned sessions
|
|
47
|
+
if (process.env.CLAUDE_SPAWNED_SESSION === 'true') {
|
|
48
|
+
registerHookExecution({
|
|
49
|
+
hookType: HOOK_TYPES.API_KEY_WATCHER,
|
|
50
|
+
status: 'skipped',
|
|
51
|
+
durationMs: Date.now() - startTime,
|
|
52
|
+
metadata: { reason: 'spawned_session' }
|
|
53
|
+
});
|
|
54
|
+
console.log(JSON.stringify({
|
|
55
|
+
continue: true,
|
|
56
|
+
suppressOutput: true,
|
|
57
|
+
}));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Step 1: Sync keys from all credential sources (env, keychain, file)
|
|
62
|
+
const syncResult = await syncKeys();
|
|
63
|
+
|
|
64
|
+
// Step 2: Read the synced rotation state for health checks + rotation
|
|
65
|
+
const state = readRotationState();
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
|
|
68
|
+
if (Object.keys(state.keys).length === 0) {
|
|
69
|
+
// No keys tracked, nothing to do
|
|
70
|
+
console.log(JSON.stringify({
|
|
71
|
+
continue: true,
|
|
72
|
+
suppressOutput: true,
|
|
73
|
+
}));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Step 3: Run health checks on all tracked keys (with token refresh for expired)
|
|
78
|
+
const healthCheckPromises = Object.entries(state.keys).map(async ([keyId, keyData]) => {
|
|
79
|
+
// Skip invalid keys
|
|
80
|
+
if (keyData.status === 'invalid') {
|
|
81
|
+
return { keyId, result: null };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check if token is expired - attempt refresh first
|
|
85
|
+
if (keyData.expiresAt && keyData.expiresAt < now) {
|
|
86
|
+
if (keyData.status !== 'expired') {
|
|
87
|
+
const refreshed = await refreshExpiredToken(keyData);
|
|
88
|
+
if (refreshed === 'invalid_grant') {
|
|
89
|
+
keyData.status = 'invalid';
|
|
90
|
+
logRotationEvent(state, {
|
|
91
|
+
timestamp: now,
|
|
92
|
+
event: 'key_removed',
|
|
93
|
+
key_id: keyId,
|
|
94
|
+
reason: 'refresh_token_invalid_grant',
|
|
95
|
+
});
|
|
96
|
+
return { keyId, result: null };
|
|
97
|
+
} else if (refreshed) {
|
|
98
|
+
keyData.accessToken = refreshed.accessToken;
|
|
99
|
+
keyData.refreshToken = refreshed.refreshToken;
|
|
100
|
+
keyData.expiresAt = refreshed.expiresAt;
|
|
101
|
+
keyData.status = 'active';
|
|
102
|
+
logRotationEvent(state, {
|
|
103
|
+
timestamp: now,
|
|
104
|
+
event: 'key_added',
|
|
105
|
+
key_id: keyId,
|
|
106
|
+
reason: 'token_refreshed_during_health_check',
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
keyData.status = 'expired';
|
|
110
|
+
logRotationEvent(state, {
|
|
111
|
+
timestamp: now,
|
|
112
|
+
event: 'key_removed',
|
|
113
|
+
key_id: keyId,
|
|
114
|
+
reason: 'token_expired',
|
|
115
|
+
});
|
|
116
|
+
return { keyId, result: null };
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
return { keyId, result: null };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Run health check
|
|
124
|
+
const result = await checkKeyHealth(keyData.accessToken);
|
|
125
|
+
|
|
126
|
+
// Fetch account profile if not already known (non-blocking)
|
|
127
|
+
if (result.valid && !keyData.account_uuid) {
|
|
128
|
+
const profile = await fetchAccountProfile(keyData.accessToken);
|
|
129
|
+
if (profile) {
|
|
130
|
+
keyData.account_uuid = profile.account_uuid;
|
|
131
|
+
keyData.account_email = profile.email;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { keyId, result };
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const healthResults = await Promise.all(healthCheckPromises);
|
|
139
|
+
|
|
140
|
+
// Process health check results
|
|
141
|
+
for (const { keyId, result } of healthResults) {
|
|
142
|
+
if (!result) continue;
|
|
143
|
+
|
|
144
|
+
const keyData = state.keys[keyId];
|
|
145
|
+
keyData.last_health_check = now;
|
|
146
|
+
|
|
147
|
+
if (!result.valid) {
|
|
148
|
+
keyData.status = 'invalid';
|
|
149
|
+
logRotationEvent(state, {
|
|
150
|
+
timestamp: now,
|
|
151
|
+
event: 'key_removed',
|
|
152
|
+
key_id: keyId,
|
|
153
|
+
reason: `health_check_failed_${result.error}`,
|
|
154
|
+
});
|
|
155
|
+
} else if (result.usage) {
|
|
156
|
+
keyData.last_usage = {
|
|
157
|
+
...result.usage,
|
|
158
|
+
checked_at: now,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Check if exhausted
|
|
162
|
+
const isExhausted = result.usage.five_hour >= EXHAUSTED_THRESHOLD ||
|
|
163
|
+
result.usage.seven_day >= EXHAUSTED_THRESHOLD ||
|
|
164
|
+
result.usage.seven_day_sonnet >= EXHAUSTED_THRESHOLD;
|
|
165
|
+
|
|
166
|
+
if (isExhausted && keyData.status !== 'exhausted') {
|
|
167
|
+
keyData.status = 'exhausted';
|
|
168
|
+
logRotationEvent(state, {
|
|
169
|
+
timestamp: now,
|
|
170
|
+
event: 'key_exhausted',
|
|
171
|
+
key_id: keyId,
|
|
172
|
+
reason: 'hit_100_percent',
|
|
173
|
+
usage_snapshot: result.usage,
|
|
174
|
+
});
|
|
175
|
+
} else if (!isExhausted && keyData.status === 'exhausted') {
|
|
176
|
+
keyData.status = 'active';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
logRotationEvent(state, {
|
|
180
|
+
timestamp: now,
|
|
181
|
+
event: 'health_check',
|
|
182
|
+
key_id: keyId,
|
|
183
|
+
usage_snapshot: result.usage,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Step 4: Select the best key
|
|
189
|
+
const selectedKeyId = selectActiveKey(state);
|
|
190
|
+
|
|
191
|
+
// Check if we need to switch keys
|
|
192
|
+
if (selectedKeyId && selectedKeyId !== state.active_key_id) {
|
|
193
|
+
const previousKeyId = state.active_key_id;
|
|
194
|
+
state.active_key_id = selectedKeyId;
|
|
195
|
+
|
|
196
|
+
const selectedKey = state.keys[selectedKeyId];
|
|
197
|
+
selectedKey.last_used_at = now;
|
|
198
|
+
|
|
199
|
+
logRotationEvent(state, {
|
|
200
|
+
timestamp: now,
|
|
201
|
+
event: 'key_switched',
|
|
202
|
+
key_id: selectedKeyId,
|
|
203
|
+
reason: previousKeyId ? `switched_from_${previousKeyId.slice(0, 8)}` : 'initial_selection',
|
|
204
|
+
usage_snapshot: selectedKey.last_usage ? {
|
|
205
|
+
five_hour: selectedKey.last_usage.five_hour,
|
|
206
|
+
seven_day: selectedKey.last_usage.seven_day,
|
|
207
|
+
seven_day_sonnet: selectedKey.last_usage.seven_day_sonnet,
|
|
208
|
+
} : undefined,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Update credentials in all stores if switching to a different key
|
|
212
|
+
if (previousKeyId) {
|
|
213
|
+
updateActiveCredentials(selectedKey);
|
|
214
|
+
}
|
|
215
|
+
} else if (!state.active_key_id && selectedKeyId) {
|
|
216
|
+
// First time setting active key
|
|
217
|
+
state.active_key_id = selectedKeyId;
|
|
218
|
+
state.keys[selectedKeyId].last_used_at = now;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Save state
|
|
222
|
+
writeRotationState(state);
|
|
223
|
+
|
|
224
|
+
// Build notification message — only count keys that responded to health checks,
|
|
225
|
+
// deduplicated per account (prefer account_uuid, fall back to usage fingerprint).
|
|
226
|
+
const respondingKeys = Object.entries(state.keys)
|
|
227
|
+
.filter(([_, k]) => k.last_usage && (k.status === 'active' || k.status === 'exhausted'));
|
|
228
|
+
|
|
229
|
+
// Deduplicate by account: prefer account_uuid, fall back to usage fingerprint
|
|
230
|
+
const seen = new Set();
|
|
231
|
+
const uniqueAccounts = respondingKeys.filter(([_, k]) => {
|
|
232
|
+
const dedupeKey = k.account_uuid || `fp:${k.last_usage.seven_day}:${k.last_usage.seven_day_sonnet}`;
|
|
233
|
+
if (seen.has(dedupeKey)) return false;
|
|
234
|
+
seen.add(dedupeKey);
|
|
235
|
+
return true;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const accountCount = uniqueAccounts.length;
|
|
239
|
+
let message = null;
|
|
240
|
+
|
|
241
|
+
if (accountCount > 1) {
|
|
242
|
+
const activeKey = state.keys[state.active_key_id];
|
|
243
|
+
const usage = activeKey?.last_usage;
|
|
244
|
+
|
|
245
|
+
if (usage) {
|
|
246
|
+
const maxUsage = Math.max(usage.five_hour, usage.seven_day, usage.seven_day_sonnet);
|
|
247
|
+
const activeLabel = activeKey.account_email || `${state.active_key_id.slice(0, 8)}...`;
|
|
248
|
+
message = `Accounts: ${accountCount} tracked | Active: ${activeLabel} (${Math.round(maxUsage)}% max usage)`;
|
|
249
|
+
} else {
|
|
250
|
+
const activeLabel2 = state.keys[state.active_key_id]?.account_email || `${state.active_key_id.slice(0, 8)}...`;
|
|
251
|
+
message = `Accounts: ${accountCount} tracked | Active: ${activeLabel2}`;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
registerHookExecution({
|
|
256
|
+
hookType: HOOK_TYPES.API_KEY_WATCHER,
|
|
257
|
+
status: 'success',
|
|
258
|
+
durationMs: Date.now() - startTime,
|
|
259
|
+
metadata: {
|
|
260
|
+
keyCount: accountCount,
|
|
261
|
+
switched: selectedKeyId !== state.active_key_id,
|
|
262
|
+
keysAdded: syncResult.keysAdded,
|
|
263
|
+
tokensRefreshed: syncResult.tokensRefreshed,
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
console.log(JSON.stringify({
|
|
268
|
+
continue: true,
|
|
269
|
+
suppressOutput: !message,
|
|
270
|
+
...(message && { systemMessage: message }),
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
main().catch(err => {
|
|
275
|
+
console.error(`[api-key-watcher] Error: ${err.message}`);
|
|
276
|
+
|
|
277
|
+
registerHookExecution({
|
|
278
|
+
hookType: HOOK_TYPES.API_KEY_WATCHER,
|
|
279
|
+
status: 'failure',
|
|
280
|
+
durationMs: 0,
|
|
281
|
+
metadata: { error: err.message }
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Don't block on errors
|
|
285
|
+
console.log(JSON.stringify({
|
|
286
|
+
continue: true,
|
|
287
|
+
suppressOutput: true,
|
|
288
|
+
}));
|
|
289
|
+
});
|