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.
Files changed (599) hide show
  1. package/.claude/agents/antipattern-hunter.md +176 -0
  2. package/.claude/agents/code-reviewer.md +205 -0
  3. package/.claude/agents/code-writer.md +154 -0
  4. package/.claude/agents/deputy-cto.md +309 -0
  5. package/.claude/agents/feedback-agent.md +101 -0
  6. package/.claude/agents/investigator.md +136 -0
  7. package/.claude/agents/product-manager.md +97 -0
  8. package/.claude/agents/project-manager.md +116 -0
  9. package/.claude/agents/repo-hygiene-expert.md +626 -0
  10. package/.claude/agents/secret-manager.md +324 -0
  11. package/.claude/agents/test-writer.md +354 -0
  12. package/.claude/commands/configure-personas.md +144 -0
  13. package/.claude/commands/cto-report.md +36 -0
  14. package/.claude/commands/demo.md +89 -0
  15. package/.claude/commands/deputy-cto.md +345 -0
  16. package/.claude/commands/hotfix.md +31 -0
  17. package/.claude/commands/overdrive-gentyr.md +167 -0
  18. package/.claude/commands/product-manager.md +32 -0
  19. package/.claude/commands/push-migrations.md +86 -0
  20. package/.claude/commands/push-secrets.md +97 -0
  21. package/.claude/commands/services.json.example +30 -0
  22. package/.claude/commands/setup-gentyr.md +396 -0
  23. package/.claude/commands/show.md +42 -0
  24. package/.claude/commands/spawn-tasks.md +79 -0
  25. package/.claude/commands/toggle-automation-gentyr.md +75 -0
  26. package/.claude/commands/toggle-product-manager.md +19 -0
  27. package/.claude/commands/triage.md +69 -0
  28. package/.claude/hooks/README.md +686 -0
  29. package/.claude/hooks/__tests__/README.md +129 -0
  30. package/.claude/hooks/agent-tracker.js +434 -0
  31. package/.claude/hooks/antipattern-hunter-hook.js +401 -0
  32. package/.claude/hooks/api-key-watcher.js +289 -0
  33. package/.claude/hooks/block-no-verify.js +301 -0
  34. package/.claude/hooks/bypass-approval-hook.js +313 -0
  35. package/.claude/hooks/compliance-checker.js +1309 -0
  36. package/.claude/hooks/config-reader.js +143 -0
  37. package/.claude/hooks/credential-file-guard.js +1139 -0
  38. package/.claude/hooks/credential-health-check.js +168 -0
  39. package/.claude/hooks/credential-sync-hook.js +79 -0
  40. package/.claude/hooks/cto-notification-hook.js +656 -0
  41. package/.claude/hooks/feedback-launcher.js +424 -0
  42. package/.claude/hooks/feedback-orchestrator.js +367 -0
  43. package/.claude/hooks/gentyr-splash.js +47 -0
  44. package/.claude/hooks/gentyr-sync.js +389 -0
  45. package/.claude/hooks/hourly-automation.js +3340 -0
  46. package/.claude/hooks/key-sync.js +899 -0
  47. package/.claude/hooks/lib/approval-utils.js +731 -0
  48. package/.claude/hooks/lib/feature-branch-helper.js +102 -0
  49. package/.claude/hooks/lib/worktree-manager.js +330 -0
  50. package/.claude/hooks/mapping-validator.js +285 -0
  51. package/.claude/hooks/plan-executor.js +398 -0
  52. package/.claude/hooks/playwright-cli-guard.js +104 -0
  53. package/.claude/hooks/playwright-health-check.js +71 -0
  54. package/.claude/hooks/pre-commit-review.js +725 -0
  55. package/.claude/hooks/prompts/local-spec-enforcement.md +310 -0
  56. package/.claude/hooks/prompts/mapping-fix.md +92 -0
  57. package/.claude/hooks/prompts/mapping-review.md +140 -0
  58. package/.claude/hooks/prompts/schema-mapper.md +185 -0
  59. package/.claude/hooks/prompts/spec-enforcement.md +233 -0
  60. package/.claude/hooks/protected-action-approval-hook.js +336 -0
  61. package/.claude/hooks/protected-action-gate.js +562 -0
  62. package/.claude/hooks/protected-actions.json +208 -0
  63. package/.claude/hooks/protected-actions.json.template +122 -0
  64. package/.claude/hooks/quota-monitor.js +490 -0
  65. package/.claude/hooks/reporters/jest-failure-reporter.js +401 -0
  66. package/.claude/hooks/reporters/playwright-failure-reporter.js +446 -0
  67. package/.claude/hooks/reporters/vitest-failure-reporter.js +443 -0
  68. package/.claude/hooks/schema-mapper-hook.js +544 -0
  69. package/.claude/hooks/secret-leak-detector.js +216 -0
  70. package/.claude/hooks/session-reviver.js +514 -0
  71. package/.claude/hooks/slash-command-prefetch.js +1145 -0
  72. package/.claude/hooks/stale-work-detector.js +205 -0
  73. package/.claude/hooks/stop-continue-hook.js +414 -0
  74. package/.claude/hooks/todo-maintenance.js +522 -0
  75. package/.claude/hooks/todo-processing-prompt.md +75 -0
  76. package/.claude/hooks/usage-optimizer.js +791 -0
  77. package/.claude/mcp/README.md +246 -0
  78. package/.claude/settings.json.template +168 -0
  79. package/.mcp.json.template +207 -0
  80. package/CLAUDE.md +340 -0
  81. package/CLAUDE.md.gentyr-section +89 -0
  82. package/LICENSE +21 -0
  83. package/README.md +297 -0
  84. package/cli/commands/init.js +471 -0
  85. package/cli/commands/migrate.js +132 -0
  86. package/cli/commands/protect.js +271 -0
  87. package/cli/commands/scaffold.js +48 -0
  88. package/cli/commands/status.js +133 -0
  89. package/cli/commands/sync.js +101 -0
  90. package/cli/commands/uninstall.js +207 -0
  91. package/cli/index.js +111 -0
  92. package/cli/lib/config-gen.js +214 -0
  93. package/cli/lib/resolve-framework.js +97 -0
  94. package/cli/lib/state.js +140 -0
  95. package/cli/lib/symlinks.js +260 -0
  96. package/docs/AUTOMATION-SYSTEMS.md +484 -0
  97. package/docs/BINARY-PATCHING.md +212 -0
  98. package/docs/CHANGELOG.md +2830 -0
  99. package/docs/CREDENTIAL-DETECTION.md +151 -0
  100. package/docs/CTO-DASHBOARD.md +476 -0
  101. package/docs/DEPLOYMENT-FLOW.md +477 -0
  102. package/docs/DEVELOPER.md +116 -0
  103. package/docs/Executive.md +372 -0
  104. package/docs/SECRET-PATHS.md +77 -0
  105. package/docs/SETUP-GUIDE.md +419 -0
  106. package/docs/STACK.md +109 -0
  107. package/docs/TESTING.md +440 -0
  108. package/docs/assets/claude-logo.svg +3 -0
  109. package/docs/sessions/2026-01-24-spec-suite-implementation.md +190 -0
  110. package/docs/sessions/2026-02-15-feedback-e2e-audit.md +484 -0
  111. package/docs/sessions/2026-02-20-credential-rotation-experiments.md +340 -0
  112. package/docs/sessions/TEST-COVERAGE-REPORT-2026-02-20.md +168 -0
  113. package/docs/shared/EPHEMERAL-STATE-FILES.md +115 -0
  114. package/docs/shared/PROTECTION-SYSTEM.md +341 -0
  115. package/husky/post-commit +10 -0
  116. package/husky/pre-commit +40 -0
  117. package/husky/pre-push +94 -0
  118. package/package.json +43 -0
  119. package/packages/cto-dashboard/package-lock.json +3510 -0
  120. package/packages/cto-dashboard/package.json +41 -0
  121. package/packages/cto-dashboard/pnpm-lock.yaml +2168 -0
  122. package/packages/mcp-servers/dist/__testUtils__/fixtures.d.ts +220 -0
  123. package/packages/mcp-servers/dist/__testUtils__/fixtures.d.ts.map +1 -0
  124. package/packages/mcp-servers/dist/__testUtils__/fixtures.js +376 -0
  125. package/packages/mcp-servers/dist/__testUtils__/fixtures.js.map +1 -0
  126. package/packages/mcp-servers/dist/__testUtils__/index.d.ts +121 -0
  127. package/packages/mcp-servers/dist/__testUtils__/index.d.ts.map +1 -0
  128. package/packages/mcp-servers/dist/__testUtils__/index.js +180 -0
  129. package/packages/mcp-servers/dist/__testUtils__/index.js.map +1 -0
  130. package/packages/mcp-servers/dist/__testUtils__/schemas.d.ts +84 -0
  131. package/packages/mcp-servers/dist/__testUtils__/schemas.d.ts.map +1 -0
  132. package/packages/mcp-servers/dist/__testUtils__/schemas.js +309 -0
  133. package/packages/mcp-servers/dist/__testUtils__/schemas.js.map +1 -0
  134. package/packages/mcp-servers/dist/agent-reports/index.d.ts +7 -0
  135. package/packages/mcp-servers/dist/agent-reports/index.d.ts.map +1 -0
  136. package/packages/mcp-servers/dist/agent-reports/index.js +8 -0
  137. package/packages/mcp-servers/dist/agent-reports/index.js.map +1 -0
  138. package/packages/mcp-servers/dist/agent-reports/server.d.ts +22 -0
  139. package/packages/mcp-servers/dist/agent-reports/server.d.ts.map +1 -0
  140. package/packages/mcp-servers/dist/agent-reports/server.js +535 -0
  141. package/packages/mcp-servers/dist/agent-reports/server.js.map +1 -0
  142. package/packages/mcp-servers/dist/agent-reports/types.d.ts +258 -0
  143. package/packages/mcp-servers/dist/agent-reports/types.d.ts.map +1 -0
  144. package/packages/mcp-servers/dist/agent-reports/types.js +81 -0
  145. package/packages/mcp-servers/dist/agent-reports/types.js.map +1 -0
  146. package/packages/mcp-servers/dist/agent-tracker/index.d.ts +5 -0
  147. package/packages/mcp-servers/dist/agent-tracker/index.d.ts.map +1 -0
  148. package/packages/mcp-servers/dist/agent-tracker/index.js +5 -0
  149. package/packages/mcp-servers/dist/agent-tracker/index.js.map +1 -0
  150. package/packages/mcp-servers/dist/agent-tracker/server.d.ts +12 -0
  151. package/packages/mcp-servers/dist/agent-tracker/server.d.ts.map +1 -0
  152. package/packages/mcp-servers/dist/agent-tracker/server.js +919 -0
  153. package/packages/mcp-servers/dist/agent-tracker/server.js.map +1 -0
  154. package/packages/mcp-servers/dist/agent-tracker/types.d.ts +328 -0
  155. package/packages/mcp-servers/dist/agent-tracker/types.d.ts.map +1 -0
  156. package/packages/mcp-servers/dist/agent-tracker/types.js +128 -0
  157. package/packages/mcp-servers/dist/agent-tracker/types.js.map +1 -0
  158. package/packages/mcp-servers/dist/chrome-bridge/browser-tips.d.ts +27 -0
  159. package/packages/mcp-servers/dist/chrome-bridge/browser-tips.d.ts.map +1 -0
  160. package/packages/mcp-servers/dist/chrome-bridge/browser-tips.js +167 -0
  161. package/packages/mcp-servers/dist/chrome-bridge/browser-tips.js.map +1 -0
  162. package/packages/mcp-servers/dist/chrome-bridge/index.d.ts +6 -0
  163. package/packages/mcp-servers/dist/chrome-bridge/index.d.ts.map +1 -0
  164. package/packages/mcp-servers/dist/chrome-bridge/index.js +6 -0
  165. package/packages/mcp-servers/dist/chrome-bridge/index.js.map +1 -0
  166. package/packages/mcp-servers/dist/chrome-bridge/server.d.ts +13 -0
  167. package/packages/mcp-servers/dist/chrome-bridge/server.d.ts.map +1 -0
  168. package/packages/mcp-servers/dist/chrome-bridge/server.js +959 -0
  169. package/packages/mcp-servers/dist/chrome-bridge/server.js.map +1 -0
  170. package/packages/mcp-servers/dist/chrome-bridge/types.d.ts +41 -0
  171. package/packages/mcp-servers/dist/chrome-bridge/types.d.ts.map +1 -0
  172. package/packages/mcp-servers/dist/chrome-bridge/types.js +8 -0
  173. package/packages/mcp-servers/dist/chrome-bridge/types.js.map +1 -0
  174. package/packages/mcp-servers/dist/cloudflare/index.d.ts +8 -0
  175. package/packages/mcp-servers/dist/cloudflare/index.d.ts.map +1 -0
  176. package/packages/mcp-servers/dist/cloudflare/index.js +8 -0
  177. package/packages/mcp-servers/dist/cloudflare/index.js.map +1 -0
  178. package/packages/mcp-servers/dist/cloudflare/server.d.ts +16 -0
  179. package/packages/mcp-servers/dist/cloudflare/server.d.ts.map +1 -0
  180. package/packages/mcp-servers/dist/cloudflare/server.js +253 -0
  181. package/packages/mcp-servers/dist/cloudflare/server.js.map +1 -0
  182. package/packages/mcp-servers/dist/cloudflare/types.d.ts +141 -0
  183. package/packages/mcp-servers/dist/cloudflare/types.d.ts.map +1 -0
  184. package/packages/mcp-servers/dist/cloudflare/types.js +53 -0
  185. package/packages/mcp-servers/dist/cloudflare/types.js.map +1 -0
  186. package/packages/mcp-servers/dist/codecov/index.d.ts +7 -0
  187. package/packages/mcp-servers/dist/codecov/index.d.ts.map +1 -0
  188. package/packages/mcp-servers/dist/codecov/index.js +7 -0
  189. package/packages/mcp-servers/dist/codecov/index.js.map +1 -0
  190. package/packages/mcp-servers/dist/codecov/server.d.ts +21 -0
  191. package/packages/mcp-servers/dist/codecov/server.d.ts.map +1 -0
  192. package/packages/mcp-servers/dist/codecov/server.js +376 -0
  193. package/packages/mcp-servers/dist/codecov/server.js.map +1 -0
  194. package/packages/mcp-servers/dist/codecov/types.d.ts +269 -0
  195. package/packages/mcp-servers/dist/codecov/types.d.ts.map +1 -0
  196. package/packages/mcp-servers/dist/codecov/types.js +128 -0
  197. package/packages/mcp-servers/dist/codecov/types.js.map +1 -0
  198. package/packages/mcp-servers/dist/cto-report/index.d.ts +9 -0
  199. package/packages/mcp-servers/dist/cto-report/index.d.ts.map +1 -0
  200. package/packages/mcp-servers/dist/cto-report/index.js +9 -0
  201. package/packages/mcp-servers/dist/cto-report/index.js.map +1 -0
  202. package/packages/mcp-servers/dist/cto-report/server.d.ts +14 -0
  203. package/packages/mcp-servers/dist/cto-report/server.d.ts.map +1 -0
  204. package/packages/mcp-servers/dist/cto-report/server.js +859 -0
  205. package/packages/mcp-servers/dist/cto-report/server.js.map +1 -0
  206. package/packages/mcp-servers/dist/cto-report/types.d.ts +213 -0
  207. package/packages/mcp-servers/dist/cto-report/types.d.ts.map +1 -0
  208. package/packages/mcp-servers/dist/cto-report/types.js +29 -0
  209. package/packages/mcp-servers/dist/cto-report/types.js.map +1 -0
  210. package/packages/mcp-servers/dist/cto-reports/index.d.ts +7 -0
  211. package/packages/mcp-servers/dist/cto-reports/index.d.ts.map +1 -0
  212. package/packages/mcp-servers/dist/cto-reports/index.js +8 -0
  213. package/packages/mcp-servers/dist/cto-reports/index.js.map +1 -0
  214. package/packages/mcp-servers/dist/cto-reports/server.d.ts +20 -0
  215. package/packages/mcp-servers/dist/cto-reports/server.d.ts.map +1 -0
  216. package/packages/mcp-servers/dist/cto-reports/server.js +538 -0
  217. package/packages/mcp-servers/dist/cto-reports/server.js.map +1 -0
  218. package/packages/mcp-servers/dist/cto-reports/types.d.ts +236 -0
  219. package/packages/mcp-servers/dist/cto-reports/types.d.ts.map +1 -0
  220. package/packages/mcp-servers/dist/cto-reports/types.js +77 -0
  221. package/packages/mcp-servers/dist/cto-reports/types.js.map +1 -0
  222. package/packages/mcp-servers/dist/deputy-cto/index.d.ts +7 -0
  223. package/packages/mcp-servers/dist/deputy-cto/index.d.ts.map +1 -0
  224. package/packages/mcp-servers/dist/deputy-cto/index.js +8 -0
  225. package/packages/mcp-servers/dist/deputy-cto/index.js.map +1 -0
  226. package/packages/mcp-servers/dist/deputy-cto/server.d.ts +23 -0
  227. package/packages/mcp-servers/dist/deputy-cto/server.d.ts.map +1 -0
  228. package/packages/mcp-servers/dist/deputy-cto/server.js +1700 -0
  229. package/packages/mcp-servers/dist/deputy-cto/server.js.map +1 -0
  230. package/packages/mcp-servers/dist/deputy-cto/types.d.ts +439 -0
  231. package/packages/mcp-servers/dist/deputy-cto/types.d.ts.map +1 -0
  232. package/packages/mcp-servers/dist/deputy-cto/types.js +102 -0
  233. package/packages/mcp-servers/dist/deputy-cto/types.js.map +1 -0
  234. package/packages/mcp-servers/dist/elastic-logs/index.d.ts +5 -0
  235. package/packages/mcp-servers/dist/elastic-logs/index.d.ts.map +1 -0
  236. package/packages/mcp-servers/dist/elastic-logs/index.js +5 -0
  237. package/packages/mcp-servers/dist/elastic-logs/index.js.map +1 -0
  238. package/packages/mcp-servers/dist/elastic-logs/server.d.ts +18 -0
  239. package/packages/mcp-servers/dist/elastic-logs/server.d.ts.map +1 -0
  240. package/packages/mcp-servers/dist/elastic-logs/server.js +259 -0
  241. package/packages/mcp-servers/dist/elastic-logs/server.js.map +1 -0
  242. package/packages/mcp-servers/dist/elastic-logs/types.d.ts +107 -0
  243. package/packages/mcp-servers/dist/elastic-logs/types.d.ts.map +1 -0
  244. package/packages/mcp-servers/dist/elastic-logs/types.js +31 -0
  245. package/packages/mcp-servers/dist/elastic-logs/types.js.map +1 -0
  246. package/packages/mcp-servers/dist/feedback-explorer/index.d.ts +2 -0
  247. package/packages/mcp-servers/dist/feedback-explorer/index.d.ts.map +1 -0
  248. package/packages/mcp-servers/dist/feedback-explorer/index.js +2 -0
  249. package/packages/mcp-servers/dist/feedback-explorer/index.js.map +1 -0
  250. package/packages/mcp-servers/dist/feedback-explorer/server.d.ts +21 -0
  251. package/packages/mcp-servers/dist/feedback-explorer/server.d.ts.map +1 -0
  252. package/packages/mcp-servers/dist/feedback-explorer/server.js +580 -0
  253. package/packages/mcp-servers/dist/feedback-explorer/server.js.map +1 -0
  254. package/packages/mcp-servers/dist/feedback-explorer/types.d.ts +331 -0
  255. package/packages/mcp-servers/dist/feedback-explorer/types.d.ts.map +1 -0
  256. package/packages/mcp-servers/dist/feedback-explorer/types.js +40 -0
  257. package/packages/mcp-servers/dist/feedback-explorer/types.js.map +1 -0
  258. package/packages/mcp-servers/dist/feedback-reporter/index.d.ts +9 -0
  259. package/packages/mcp-servers/dist/feedback-reporter/index.d.ts.map +1 -0
  260. package/packages/mcp-servers/dist/feedback-reporter/index.js +9 -0
  261. package/packages/mcp-servers/dist/feedback-reporter/index.js.map +1 -0
  262. package/packages/mcp-servers/dist/feedback-reporter/server.d.ts +36 -0
  263. package/packages/mcp-servers/dist/feedback-reporter/server.d.ts.map +1 -0
  264. package/packages/mcp-servers/dist/feedback-reporter/server.js +392 -0
  265. package/packages/mcp-servers/dist/feedback-reporter/server.js.map +1 -0
  266. package/packages/mcp-servers/dist/feedback-reporter/types.d.ts +152 -0
  267. package/packages/mcp-servers/dist/feedback-reporter/types.d.ts.map +1 -0
  268. package/packages/mcp-servers/dist/feedback-reporter/types.js +67 -0
  269. package/packages/mcp-servers/dist/feedback-reporter/types.js.map +1 -0
  270. package/packages/mcp-servers/dist/github/index.d.ts +7 -0
  271. package/packages/mcp-servers/dist/github/index.d.ts.map +1 -0
  272. package/packages/mcp-servers/dist/github/index.js +7 -0
  273. package/packages/mcp-servers/dist/github/index.js.map +1 -0
  274. package/packages/mcp-servers/dist/github/server.d.ts +15 -0
  275. package/packages/mcp-servers/dist/github/server.d.ts.map +1 -0
  276. package/packages/mcp-servers/dist/github/server.js +686 -0
  277. package/packages/mcp-servers/dist/github/server.js.map +1 -0
  278. package/packages/mcp-servers/dist/github/types.d.ts +660 -0
  279. package/packages/mcp-servers/dist/github/types.d.ts.map +1 -0
  280. package/packages/mcp-servers/dist/github/types.js +209 -0
  281. package/packages/mcp-servers/dist/github/types.js.map +1 -0
  282. package/packages/mcp-servers/dist/index.d.ts +30 -0
  283. package/packages/mcp-servers/dist/index.d.ts.map +1 -0
  284. package/packages/mcp-servers/dist/index.js +32 -0
  285. package/packages/mcp-servers/dist/index.js.map +1 -0
  286. package/packages/mcp-servers/dist/makerkit-docs/index.d.ts +5 -0
  287. package/packages/mcp-servers/dist/makerkit-docs/index.d.ts.map +1 -0
  288. package/packages/mcp-servers/dist/makerkit-docs/index.js +5 -0
  289. package/packages/mcp-servers/dist/makerkit-docs/index.js.map +1 -0
  290. package/packages/mcp-servers/dist/makerkit-docs/server.d.ts +15 -0
  291. package/packages/mcp-servers/dist/makerkit-docs/server.d.ts.map +1 -0
  292. package/packages/mcp-servers/dist/makerkit-docs/server.js +252 -0
  293. package/packages/mcp-servers/dist/makerkit-docs/server.js.map +1 -0
  294. package/packages/mcp-servers/dist/makerkit-docs/types.d.ts +74 -0
  295. package/packages/mcp-servers/dist/makerkit-docs/types.d.ts.map +1 -0
  296. package/packages/mcp-servers/dist/makerkit-docs/types.js +20 -0
  297. package/packages/mcp-servers/dist/makerkit-docs/types.js.map +1 -0
  298. package/packages/mcp-servers/dist/onepassword/index.d.ts +2 -0
  299. package/packages/mcp-servers/dist/onepassword/index.d.ts.map +1 -0
  300. package/packages/mcp-servers/dist/onepassword/index.js +2 -0
  301. package/packages/mcp-servers/dist/onepassword/index.js.map +1 -0
  302. package/packages/mcp-servers/dist/onepassword/server.d.ts +2 -0
  303. package/packages/mcp-servers/dist/onepassword/server.d.ts.map +1 -0
  304. package/packages/mcp-servers/dist/onepassword/server.js +159 -0
  305. package/packages/mcp-servers/dist/onepassword/server.js.map +1 -0
  306. package/packages/mcp-servers/dist/onepassword/types.d.ts +55 -0
  307. package/packages/mcp-servers/dist/onepassword/types.d.ts.map +1 -0
  308. package/packages/mcp-servers/dist/onepassword/types.js +22 -0
  309. package/packages/mcp-servers/dist/onepassword/types.js.map +1 -0
  310. package/packages/mcp-servers/dist/playwright/helpers.d.ts +20 -0
  311. package/packages/mcp-servers/dist/playwright/helpers.d.ts.map +1 -0
  312. package/packages/mcp-servers/dist/playwright/helpers.js +31 -0
  313. package/packages/mcp-servers/dist/playwright/helpers.js.map +1 -0
  314. package/packages/mcp-servers/dist/playwright/index.d.ts +5 -0
  315. package/packages/mcp-servers/dist/playwright/index.d.ts.map +1 -0
  316. package/packages/mcp-servers/dist/playwright/index.js +5 -0
  317. package/packages/mcp-servers/dist/playwright/index.js.map +1 -0
  318. package/packages/mcp-servers/dist/playwright/server.d.ts +13 -0
  319. package/packages/mcp-servers/dist/playwright/server.d.ts.map +1 -0
  320. package/packages/mcp-servers/dist/playwright/server.js +1201 -0
  321. package/packages/mcp-servers/dist/playwright/server.js.map +1 -0
  322. package/packages/mcp-servers/dist/playwright/types.d.ts +216 -0
  323. package/packages/mcp-servers/dist/playwright/types.d.ts.map +1 -0
  324. package/packages/mcp-servers/dist/playwright/types.js +172 -0
  325. package/packages/mcp-servers/dist/playwright/types.js.map +1 -0
  326. package/packages/mcp-servers/dist/playwright-feedback/browser-manager.d.ts +39 -0
  327. package/packages/mcp-servers/dist/playwright-feedback/browser-manager.d.ts.map +1 -0
  328. package/packages/mcp-servers/dist/playwright-feedback/browser-manager.js +71 -0
  329. package/packages/mcp-servers/dist/playwright-feedback/browser-manager.js.map +1 -0
  330. package/packages/mcp-servers/dist/playwright-feedback/index.d.ts +5 -0
  331. package/packages/mcp-servers/dist/playwright-feedback/index.d.ts.map +1 -0
  332. package/packages/mcp-servers/dist/playwright-feedback/index.js +5 -0
  333. package/packages/mcp-servers/dist/playwright-feedback/index.js.map +1 -0
  334. package/packages/mcp-servers/dist/playwright-feedback/server.d.ts +34 -0
  335. package/packages/mcp-servers/dist/playwright-feedback/server.d.ts.map +1 -0
  336. package/packages/mcp-servers/dist/playwright-feedback/server.js +538 -0
  337. package/packages/mcp-servers/dist/playwright-feedback/server.js.map +1 -0
  338. package/packages/mcp-servers/dist/playwright-feedback/types.d.ts +305 -0
  339. package/packages/mcp-servers/dist/playwright-feedback/types.d.ts.map +1 -0
  340. package/packages/mcp-servers/dist/playwright-feedback/types.js +123 -0
  341. package/packages/mcp-servers/dist/playwright-feedback/types.js.map +1 -0
  342. package/packages/mcp-servers/dist/product-manager/server.d.ts +17 -0
  343. package/packages/mcp-servers/dist/product-manager/server.d.ts.map +1 -0
  344. package/packages/mcp-servers/dist/product-manager/server.js +690 -0
  345. package/packages/mcp-servers/dist/product-manager/server.js.map +1 -0
  346. package/packages/mcp-servers/dist/product-manager/types.d.ts +286 -0
  347. package/packages/mcp-servers/dist/product-manager/types.d.ts.map +1 -0
  348. package/packages/mcp-servers/dist/product-manager/types.js +99 -0
  349. package/packages/mcp-servers/dist/product-manager/types.js.map +1 -0
  350. package/packages/mcp-servers/dist/programmatic-feedback/index.d.ts +7 -0
  351. package/packages/mcp-servers/dist/programmatic-feedback/index.d.ts.map +1 -0
  352. package/packages/mcp-servers/dist/programmatic-feedback/index.js +7 -0
  353. package/packages/mcp-servers/dist/programmatic-feedback/index.js.map +1 -0
  354. package/packages/mcp-servers/dist/programmatic-feedback/sandbox.d.ts +19 -0
  355. package/packages/mcp-servers/dist/programmatic-feedback/sandbox.d.ts.map +1 -0
  356. package/packages/mcp-servers/dist/programmatic-feedback/sandbox.js +174 -0
  357. package/packages/mcp-servers/dist/programmatic-feedback/sandbox.js.map +1 -0
  358. package/packages/mcp-servers/dist/programmatic-feedback/server.d.ts +35 -0
  359. package/packages/mcp-servers/dist/programmatic-feedback/server.d.ts.map +1 -0
  360. package/packages/mcp-servers/dist/programmatic-feedback/server.js +465 -0
  361. package/packages/mcp-servers/dist/programmatic-feedback/server.js.map +1 -0
  362. package/packages/mcp-servers/dist/programmatic-feedback/types.d.ts +127 -0
  363. package/packages/mcp-servers/dist/programmatic-feedback/types.d.ts.map +1 -0
  364. package/packages/mcp-servers/dist/programmatic-feedback/types.js +80 -0
  365. package/packages/mcp-servers/dist/programmatic-feedback/types.js.map +1 -0
  366. package/packages/mcp-servers/dist/render/index.d.ts +8 -0
  367. package/packages/mcp-servers/dist/render/index.d.ts.map +1 -0
  368. package/packages/mcp-servers/dist/render/index.js +8 -0
  369. package/packages/mcp-servers/dist/render/index.js.map +1 -0
  370. package/packages/mcp-servers/dist/render/server.d.ts +15 -0
  371. package/packages/mcp-servers/dist/render/server.d.ts.map +1 -0
  372. package/packages/mcp-servers/dist/render/server.js +428 -0
  373. package/packages/mcp-servers/dist/render/server.js.map +1 -0
  374. package/packages/mcp-servers/dist/render/types.d.ts +273 -0
  375. package/packages/mcp-servers/dist/render/types.d.ts.map +1 -0
  376. package/packages/mcp-servers/dist/render/types.js +102 -0
  377. package/packages/mcp-servers/dist/render/types.js.map +1 -0
  378. package/packages/mcp-servers/dist/resend/index.d.ts +7 -0
  379. package/packages/mcp-servers/dist/resend/index.d.ts.map +1 -0
  380. package/packages/mcp-servers/dist/resend/index.js +7 -0
  381. package/packages/mcp-servers/dist/resend/index.js.map +1 -0
  382. package/packages/mcp-servers/dist/resend/server.d.ts +15 -0
  383. package/packages/mcp-servers/dist/resend/server.d.ts.map +1 -0
  384. package/packages/mcp-servers/dist/resend/server.js +298 -0
  385. package/packages/mcp-servers/dist/resend/server.js.map +1 -0
  386. package/packages/mcp-servers/dist/resend/types.d.ts +222 -0
  387. package/packages/mcp-servers/dist/resend/types.d.ts.map +1 -0
  388. package/packages/mcp-servers/dist/resend/types.js +58 -0
  389. package/packages/mcp-servers/dist/resend/types.js.map +1 -0
  390. package/packages/mcp-servers/dist/review-queue/index.d.ts +6 -0
  391. package/packages/mcp-servers/dist/review-queue/index.d.ts.map +1 -0
  392. package/packages/mcp-servers/dist/review-queue/index.js +6 -0
  393. package/packages/mcp-servers/dist/review-queue/index.js.map +1 -0
  394. package/packages/mcp-servers/dist/review-queue/server.d.ts +17 -0
  395. package/packages/mcp-servers/dist/review-queue/server.d.ts.map +1 -0
  396. package/packages/mcp-servers/dist/review-queue/server.js +348 -0
  397. package/packages/mcp-servers/dist/review-queue/server.js.map +1 -0
  398. package/packages/mcp-servers/dist/review-queue/types.d.ts +162 -0
  399. package/packages/mcp-servers/dist/review-queue/types.d.ts.map +1 -0
  400. package/packages/mcp-servers/dist/review-queue/types.js +56 -0
  401. package/packages/mcp-servers/dist/review-queue/types.js.map +1 -0
  402. package/packages/mcp-servers/dist/secret-sync/server.d.ts +19 -0
  403. package/packages/mcp-servers/dist/secret-sync/server.d.ts.map +1 -0
  404. package/packages/mcp-servers/dist/secret-sync/server.js +1139 -0
  405. package/packages/mcp-servers/dist/secret-sync/server.js.map +1 -0
  406. package/packages/mcp-servers/dist/secret-sync/types.d.ts +442 -0
  407. package/packages/mcp-servers/dist/secret-sync/types.d.ts.map +1 -0
  408. package/packages/mcp-servers/dist/secret-sync/types.js +113 -0
  409. package/packages/mcp-servers/dist/secret-sync/types.js.map +1 -0
  410. package/packages/mcp-servers/dist/session-events/index.d.ts +5 -0
  411. package/packages/mcp-servers/dist/session-events/index.d.ts.map +1 -0
  412. package/packages/mcp-servers/dist/session-events/index.js +5 -0
  413. package/packages/mcp-servers/dist/session-events/index.js.map +1 -0
  414. package/packages/mcp-servers/dist/session-events/server.d.ts +11 -0
  415. package/packages/mcp-servers/dist/session-events/server.d.ts.map +1 -0
  416. package/packages/mcp-servers/dist/session-events/server.js +290 -0
  417. package/packages/mcp-servers/dist/session-events/server.js.map +1 -0
  418. package/packages/mcp-servers/dist/session-events/types.d.ts +213 -0
  419. package/packages/mcp-servers/dist/session-events/types.d.ts.map +1 -0
  420. package/packages/mcp-servers/dist/session-events/types.js +69 -0
  421. package/packages/mcp-servers/dist/session-events/types.js.map +1 -0
  422. package/packages/mcp-servers/dist/session-restart/index.d.ts +9 -0
  423. package/packages/mcp-servers/dist/session-restart/index.d.ts.map +1 -0
  424. package/packages/mcp-servers/dist/session-restart/index.js +9 -0
  425. package/packages/mcp-servers/dist/session-restart/index.js.map +1 -0
  426. package/packages/mcp-servers/dist/session-restart/server.d.ts +20 -0
  427. package/packages/mcp-servers/dist/session-restart/server.d.ts.map +1 -0
  428. package/packages/mcp-servers/dist/session-restart/server.js +411 -0
  429. package/packages/mcp-servers/dist/session-restart/server.js.map +1 -0
  430. package/packages/mcp-servers/dist/session-restart/types.d.ts +26 -0
  431. package/packages/mcp-servers/dist/session-restart/types.d.ts.map +1 -0
  432. package/packages/mcp-servers/dist/session-restart/types.js +16 -0
  433. package/packages/mcp-servers/dist/session-restart/types.js.map +1 -0
  434. package/packages/mcp-servers/dist/setup-helper/index.d.ts +5 -0
  435. package/packages/mcp-servers/dist/setup-helper/index.d.ts.map +1 -0
  436. package/packages/mcp-servers/dist/setup-helper/index.js +5 -0
  437. package/packages/mcp-servers/dist/setup-helper/index.js.map +1 -0
  438. package/packages/mcp-servers/dist/setup-helper/server.d.ts +14 -0
  439. package/packages/mcp-servers/dist/setup-helper/server.d.ts.map +1 -0
  440. package/packages/mcp-servers/dist/setup-helper/server.js +454 -0
  441. package/packages/mcp-servers/dist/setup-helper/server.js.map +1 -0
  442. package/packages/mcp-servers/dist/setup-helper/types.d.ts +81 -0
  443. package/packages/mcp-servers/dist/setup-helper/types.d.ts.map +1 -0
  444. package/packages/mcp-servers/dist/setup-helper/types.js +41 -0
  445. package/packages/mcp-servers/dist/setup-helper/types.js.map +1 -0
  446. package/packages/mcp-servers/dist/shared/audited-server.d.ts +31 -0
  447. package/packages/mcp-servers/dist/shared/audited-server.d.ts.map +1 -0
  448. package/packages/mcp-servers/dist/shared/audited-server.js +126 -0
  449. package/packages/mcp-servers/dist/shared/audited-server.js.map +1 -0
  450. package/packages/mcp-servers/dist/shared/constants.d.ts +26 -0
  451. package/packages/mcp-servers/dist/shared/constants.d.ts.map +1 -0
  452. package/packages/mcp-servers/dist/shared/constants.js +41 -0
  453. package/packages/mcp-servers/dist/shared/constants.js.map +1 -0
  454. package/packages/mcp-servers/dist/shared/index.d.ts +6 -0
  455. package/packages/mcp-servers/dist/shared/index.d.ts.map +1 -0
  456. package/packages/mcp-servers/dist/shared/index.js +6 -0
  457. package/packages/mcp-servers/dist/shared/index.js.map +1 -0
  458. package/packages/mcp-servers/dist/shared/readonly-db.d.ts +11 -0
  459. package/packages/mcp-servers/dist/shared/readonly-db.d.ts.map +1 -0
  460. package/packages/mcp-servers/dist/shared/readonly-db.js +47 -0
  461. package/packages/mcp-servers/dist/shared/readonly-db.js.map +1 -0
  462. package/packages/mcp-servers/dist/shared/resolve-framework.d.ts +20 -0
  463. package/packages/mcp-servers/dist/shared/resolve-framework.d.ts.map +1 -0
  464. package/packages/mcp-servers/dist/shared/resolve-framework.js +65 -0
  465. package/packages/mcp-servers/dist/shared/resolve-framework.js.map +1 -0
  466. package/packages/mcp-servers/dist/shared/server.d.ts +86 -0
  467. package/packages/mcp-servers/dist/shared/server.d.ts.map +1 -0
  468. package/packages/mcp-servers/dist/shared/server.js +291 -0
  469. package/packages/mcp-servers/dist/shared/server.js.map +1 -0
  470. package/packages/mcp-servers/dist/shared/types.d.ts +113 -0
  471. package/packages/mcp-servers/dist/shared/types.d.ts.map +1 -0
  472. package/packages/mcp-servers/dist/shared/types.js +36 -0
  473. package/packages/mcp-servers/dist/shared/types.js.map +1 -0
  474. package/packages/mcp-servers/dist/show/server.d.ts +12 -0
  475. package/packages/mcp-servers/dist/show/server.d.ts.map +1 -0
  476. package/packages/mcp-servers/dist/show/server.js +97 -0
  477. package/packages/mcp-servers/dist/show/server.js.map +1 -0
  478. package/packages/mcp-servers/dist/show/types.d.ts +19 -0
  479. package/packages/mcp-servers/dist/show/types.d.ts.map +1 -0
  480. package/packages/mcp-servers/dist/show/types.js +32 -0
  481. package/packages/mcp-servers/dist/show/types.js.map +1 -0
  482. package/packages/mcp-servers/dist/specs-browser/index.d.ts +5 -0
  483. package/packages/mcp-servers/dist/specs-browser/index.d.ts.map +1 -0
  484. package/packages/mcp-servers/dist/specs-browser/index.js +5 -0
  485. package/packages/mcp-servers/dist/specs-browser/index.js.map +1 -0
  486. package/packages/mcp-servers/dist/specs-browser/server.d.ts +13 -0
  487. package/packages/mcp-servers/dist/specs-browser/server.d.ts.map +1 -0
  488. package/packages/mcp-servers/dist/specs-browser/server.js +692 -0
  489. package/packages/mcp-servers/dist/specs-browser/server.js.map +1 -0
  490. package/packages/mcp-servers/dist/specs-browser/types.d.ts +337 -0
  491. package/packages/mcp-servers/dist/specs-browser/types.d.ts.map +1 -0
  492. package/packages/mcp-servers/dist/specs-browser/types.js +134 -0
  493. package/packages/mcp-servers/dist/specs-browser/types.js.map +1 -0
  494. package/packages/mcp-servers/dist/supabase/index.d.ts +10 -0
  495. package/packages/mcp-servers/dist/supabase/index.d.ts.map +1 -0
  496. package/packages/mcp-servers/dist/supabase/index.js +10 -0
  497. package/packages/mcp-servers/dist/supabase/index.js.map +1 -0
  498. package/packages/mcp-servers/dist/supabase/server.d.ts +20 -0
  499. package/packages/mcp-servers/dist/supabase/server.d.ts.map +1 -0
  500. package/packages/mcp-servers/dist/supabase/server.js +451 -0
  501. package/packages/mcp-servers/dist/supabase/server.js.map +1 -0
  502. package/packages/mcp-servers/dist/supabase/types.d.ts +196 -0
  503. package/packages/mcp-servers/dist/supabase/types.d.ts.map +1 -0
  504. package/packages/mcp-servers/dist/supabase/types.js +76 -0
  505. package/packages/mcp-servers/dist/supabase/types.js.map +1 -0
  506. package/packages/mcp-servers/dist/todo-db/index.d.ts +5 -0
  507. package/packages/mcp-servers/dist/todo-db/index.d.ts.map +1 -0
  508. package/packages/mcp-servers/dist/todo-db/index.js +5 -0
  509. package/packages/mcp-servers/dist/todo-db/index.js.map +1 -0
  510. package/packages/mcp-servers/dist/todo-db/server.d.ts +13 -0
  511. package/packages/mcp-servers/dist/todo-db/server.d.ts.map +1 -0
  512. package/packages/mcp-servers/dist/todo-db/server.js +649 -0
  513. package/packages/mcp-servers/dist/todo-db/server.js.map +1 -0
  514. package/packages/mcp-servers/dist/todo-db/types.d.ts +225 -0
  515. package/packages/mcp-servers/dist/todo-db/types.d.ts.map +1 -0
  516. package/packages/mcp-servers/dist/todo-db/types.js +69 -0
  517. package/packages/mcp-servers/dist/todo-db/types.js.map +1 -0
  518. package/packages/mcp-servers/dist/user-feedback/index.d.ts +7 -0
  519. package/packages/mcp-servers/dist/user-feedback/index.d.ts.map +1 -0
  520. package/packages/mcp-servers/dist/user-feedback/index.js +8 -0
  521. package/packages/mcp-servers/dist/user-feedback/index.js.map +1 -0
  522. package/packages/mcp-servers/dist/user-feedback/server.d.ts +25 -0
  523. package/packages/mcp-servers/dist/user-feedback/server.d.ts.map +1 -0
  524. package/packages/mcp-servers/dist/user-feedback/server.js +914 -0
  525. package/packages/mcp-servers/dist/user-feedback/server.js.map +1 -0
  526. package/packages/mcp-servers/dist/user-feedback/types.d.ts +415 -0
  527. package/packages/mcp-servers/dist/user-feedback/types.d.ts.map +1 -0
  528. package/packages/mcp-servers/dist/user-feedback/types.js +132 -0
  529. package/packages/mcp-servers/dist/user-feedback/types.js.map +1 -0
  530. package/packages/mcp-servers/dist/vercel/index.d.ts +9 -0
  531. package/packages/mcp-servers/dist/vercel/index.d.ts.map +1 -0
  532. package/packages/mcp-servers/dist/vercel/index.js +9 -0
  533. package/packages/mcp-servers/dist/vercel/index.js.map +1 -0
  534. package/packages/mcp-servers/dist/vercel/server.d.ts +17 -0
  535. package/packages/mcp-servers/dist/vercel/server.d.ts.map +1 -0
  536. package/packages/mcp-servers/dist/vercel/server.js +265 -0
  537. package/packages/mcp-servers/dist/vercel/server.js.map +1 -0
  538. package/packages/mcp-servers/dist/vercel/types.d.ts +189 -0
  539. package/packages/mcp-servers/dist/vercel/types.d.ts.map +1 -0
  540. package/packages/mcp-servers/dist/vercel/types.js +65 -0
  541. package/packages/mcp-servers/dist/vercel/types.js.map +1 -0
  542. package/packages/mcp-servers/package-lock.json +3765 -0
  543. package/packages/mcp-servers/package.json +64 -0
  544. package/packages/mcp-servers/test/reporters/test-failure-reporter.ts +372 -0
  545. package/packages/mcp-servers/vitest.config.ts +27 -0
  546. package/scripts/__tests__/README.md +163 -0
  547. package/scripts/apply-credential-hardening.sh +271 -0
  548. package/scripts/credential-providers/manual.js +56 -0
  549. package/scripts/credential-providers/onepassword.js +85 -0
  550. package/scripts/credential-providers/provider-interface.js +104 -0
  551. package/scripts/encrypt-credential.js +337 -0
  552. package/scripts/feedback-launcher.js +338 -0
  553. package/scripts/feedback-orchestrator.js +373 -0
  554. package/scripts/fix-mcp-launcher-issues.sh +97 -0
  555. package/scripts/force-spawn-tasks.js +651 -0
  556. package/scripts/force-triage-reports.js +560 -0
  557. package/scripts/generate-protected-actions-spec.js +142 -0
  558. package/scripts/generate-proxy-certs.sh +158 -0
  559. package/scripts/grant-chrome-ext-permissions.sh +242 -0
  560. package/scripts/mcp-launcher.js +125 -0
  561. package/scripts/merge-settings.cjs +167 -0
  562. package/scripts/patch-clawd.py +844 -0
  563. package/scripts/patch-credential-cache.py +313 -0
  564. package/scripts/patches/credential-file-guard-patched.mjs +573 -0
  565. package/scripts/patches/credential-file-guard.js.patched +573 -0
  566. package/scripts/patches/verify-tokenizer.mjs +132 -0
  567. package/scripts/protect-framework.sh +478 -0
  568. package/scripts/readme-chrome.template +12 -0
  569. package/scripts/reap-completed-agents.js +439 -0
  570. package/scripts/reinstall.sh +86 -0
  571. package/scripts/resign-node.sh +185 -0
  572. package/scripts/rotation-proxy.js +656 -0
  573. package/scripts/rotation-stress-monitor.mjs +862 -0
  574. package/scripts/setup-automation-service.sh +648 -0
  575. package/scripts/setup-check.js +251 -0
  576. package/scripts/watch-claude-version.js +142 -0
  577. package/specs/framework/CORE-INVARIANTS.md +161 -0
  578. package/specs/patterns/AGENT-PATTERNS.md +223 -0
  579. package/specs/patterns/HOOK-PATTERNS.md +242 -0
  580. package/specs/patterns/MCP-SERVER-PATTERNS.md +144 -0
  581. package/templates/config/gitignore.template +14 -0
  582. package/templates/config/merge-chain-check.yml.template +51 -0
  583. package/templates/config/package.json.template +18 -0
  584. package/templates/config/pnpm-workspace.yaml +5 -0
  585. package/templates/config/services.json.template +18 -0
  586. package/templates/config/tsconfig.base.json +17 -0
  587. package/templates/scaffold/integrations/_template/.gitkeep +0 -0
  588. package/templates/scaffold/packages/logger/package.json +17 -0
  589. package/templates/scaffold/packages/logger/src/logger.ts +44 -0
  590. package/templates/scaffold/packages/shared/package.json +17 -0
  591. package/templates/scaffold/packages/shared/src/errors.ts +43 -0
  592. package/templates/scaffold/products/_product/apps/backend/package.json +21 -0
  593. package/templates/scaffold/products/_product/apps/backend/src/index.ts +17 -0
  594. package/templates/scaffold/products/_product/apps/extension/.gitkeep +0 -0
  595. package/templates/scaffold/products/_product/apps/web/.gitkeep +0 -0
  596. package/templates/scaffold/specs/global/.gitkeep +0 -0
  597. package/templates/scaffold/specs/local/.gitkeep +0 -0
  598. package/templates/scaffold/specs/reference/.gitkeep +0 -0
  599. 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
+ });