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,1145 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Slash Command Prefetch Hook
4
+ *
5
+ * Intercepts slash command prompts via UserPromptSubmit and pre-gathers data.
6
+ * Gathers data and returns as systemMessage for Claude to use.
7
+ *
8
+ * @version 1.0.0
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import os from 'os';
14
+ import { execSync, spawn } from 'child_process';
15
+
16
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
17
+
18
+ // Lazy-loaded SQLite — deferred until a Mode 2 handler actually needs it
19
+ let Database = null;
20
+ async function getDatabase() {
21
+ if (Database === undefined) return null; // Previously failed
22
+ if (Database) return Database;
23
+ try {
24
+ Database = (await import('better-sqlite3')).default;
25
+ return Database;
26
+ } catch {
27
+ Database = undefined; // Mark as unavailable
28
+ return null;
29
+ }
30
+ }
31
+
32
+ // ============================================================================
33
+ // Stdin
34
+ // ============================================================================
35
+
36
+ async function readStdin() {
37
+ return new Promise((resolve) => {
38
+ let data = '';
39
+ const timeout = setTimeout(() => resolve(data.trim()), 100);
40
+ process.stdin.setEncoding('utf8');
41
+ process.stdin.on('data', (chunk) => { data += chunk; });
42
+ process.stdin.on('end', () => { clearTimeout(timeout); resolve(data.trim()); });
43
+ if (!process.stdin.readable) { clearTimeout(timeout); resolve(''); }
44
+ });
45
+ }
46
+
47
+ // ============================================================================
48
+ // Sentinel detection
49
+ // ============================================================================
50
+
51
+ const SENTINELS = {
52
+ 'cto-report': '<!-- HOOK:GENTYR:cto-report -->',
53
+ 'deputy-cto': '<!-- HOOK:GENTYR:deputy-cto -->',
54
+ 'toggle-automation': '<!-- HOOK:GENTYR:toggle-automation -->',
55
+ 'overdrive': '<!-- HOOK:GENTYR:overdrive -->',
56
+ 'setup-gentyr': '<!-- HOOK:GENTYR:setup-gentyr -->',
57
+ 'push-migrations': '<!-- HOOK:GENTYR:push-migrations -->',
58
+ 'push-secrets': '<!-- HOOK:GENTYR:push-secrets -->',
59
+ 'configure-personas': '<!-- HOOK:GENTYR:configure-personas -->',
60
+ 'spawn-tasks': '<!-- HOOK:GENTYR:spawn-tasks -->',
61
+ 'show': '<!-- HOOK:GENTYR:show -->',
62
+ 'product-manager': '<!-- HOOK:GENTYR:product-manager -->',
63
+ 'toggle-product-manager': '<!-- HOOK:GENTYR:toggle-product-manager -->',
64
+ 'triage': '<!-- HOOK:GENTYR:triage -->',
65
+ 'demo': '<!-- HOOK:GENTYR:demo -->',
66
+ };
67
+
68
+ /**
69
+ * Extract the prompt string from raw stdin.
70
+ * UserPromptSubmit hooks receive JSON like {"prompt":"/cto-report",...}
71
+ * but the expanded .md content contains the sentinel comments.
72
+ * This extracts the raw user input so we can match bare slash commands.
73
+ */
74
+ function extractPrompt(raw) {
75
+ try {
76
+ const parsed = JSON.parse(raw);
77
+ if (typeof parsed.prompt === 'string') return parsed.prompt;
78
+ } catch {
79
+ // Not JSON — use raw string as-is
80
+ }
81
+ return raw;
82
+ }
83
+
84
+ /**
85
+ * Check if text matches a command by either raw slash command name or sentinel.
86
+ * Handles both the expanded .md content (contains sentinel) and the raw JSON
87
+ * stdin (contains bare "/command-name").
88
+ */
89
+ function matchesCommand(text, commandName) {
90
+ if (text.trim() === `/${commandName}`) return true;
91
+ if (text.includes(SENTINELS[commandName])) return true;
92
+ return false;
93
+ }
94
+
95
+ // ============================================================================
96
+ // DB helpers
97
+ // ============================================================================
98
+
99
+ function openDb(dbPath) {
100
+ if (!Database) return null;
101
+ if (!fs.existsSync(dbPath)) return null;
102
+ try {
103
+ return new Database(dbPath, { readonly: true });
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ function queryDb(dbPath, queries) {
110
+ const db = openDb(dbPath);
111
+ if (!db) return null;
112
+ try {
113
+ const results = {};
114
+ for (const [key, { sql, params }] of Object.entries(queries)) {
115
+ try {
116
+ if (sql.trimStart().toUpperCase().startsWith('SELECT COUNT')) {
117
+ results[key] = db.prepare(sql).get(...(params || []));
118
+ } else {
119
+ results[key] = db.prepare(sql).all(...(params || []));
120
+ }
121
+ } catch {
122
+ results[key] = null;
123
+ }
124
+ }
125
+ return results;
126
+ } finally {
127
+ db.close();
128
+ }
129
+ }
130
+
131
+ function readJson(filePath) {
132
+ try {
133
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
134
+ } catch {
135
+ return null;
136
+ }
137
+ }
138
+
139
+ // ============================================================================
140
+ // Paths
141
+ // ============================================================================
142
+
143
+ const DEPUTY_CTO_DB = path.join(PROJECT_DIR, '.claude', 'deputy-cto.db');
144
+ const CTO_REPORTS_DB = path.join(PROJECT_DIR, '.claude', 'cto-reports.db');
145
+ const TODO_DB = path.join(PROJECT_DIR, '.claude', 'todo.db');
146
+ const USER_FEEDBACK_DB = path.join(PROJECT_DIR, '.claude', 'user-feedback.db');
147
+ const AUTONOMOUS_MODE_PATH = path.join(PROJECT_DIR, '.claude', 'autonomous-mode.json');
148
+ const AUTOMATION_STATE_PATH = path.join(PROJECT_DIR, '.claude', 'hourly-automation-state.json');
149
+ const AUTOMATION_CONFIG_PATH = path.join(PROJECT_DIR, '.claude', 'state', 'automation-config.json');
150
+ const SERVICES_CONFIG_PATH = path.join(PROJECT_DIR, '.claude', 'config', 'services.json');
151
+ const PRODUCT_MANAGER_DB = path.join(PROJECT_DIR, '.claude', 'state', 'product-manager.db');
152
+
153
+ // ============================================================================
154
+ // Session utilities
155
+ // ============================================================================
156
+
157
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
158
+
159
+ export function getSessionDir() {
160
+ const projectPath = PROJECT_DIR.replace(/[^a-zA-Z0-9]/g, '-').replace(/^-/, '');
161
+ return path.join(os.homedir(), '.claude', 'projects', `-${projectPath}`);
162
+ }
163
+
164
+ function discoverSessionIdViaLsof(sessionDir, claudePid) {
165
+ try {
166
+ const output = execSync(`lsof -p ${claudePid} 2>/dev/null`, { encoding: 'utf8', timeout: 5000 });
167
+ for (const line of output.split('\n')) {
168
+ if (!line.includes('.jsonl')) continue;
169
+ const match = line.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl/);
170
+ if (match && match[1]) {
171
+ const candidate = path.join(sessionDir, `${match[1]}.jsonl`);
172
+ if (fs.existsSync(candidate)) return match[1];
173
+ }
174
+ }
175
+ } catch {
176
+ // lsof unavailable or failed
177
+ }
178
+ return null;
179
+ }
180
+
181
+ function discoverSessionIdViaContent(sessionDir) {
182
+ const TAIL_BYTES = 8192;
183
+ const candidates = [];
184
+ let fileNames;
185
+ try {
186
+ fileNames = fs.readdirSync(sessionDir).filter(f => f.endsWith('.jsonl'));
187
+ } catch {
188
+ return null;
189
+ }
190
+ for (const f of fileNames) {
191
+ const id = f.replace('.jsonl', '');
192
+ if (!UUID_REGEX.test(id)) continue;
193
+ const filePath = path.join(sessionDir, f);
194
+ try {
195
+ const stat = fs.statSync(filePath);
196
+ if (stat.size === 0) continue;
197
+ const fd = fs.openSync(filePath, 'r');
198
+ const readSize = Math.min(TAIL_BYTES, stat.size);
199
+ const buffer = Buffer.alloc(readSize);
200
+ fs.readSync(fd, buffer, 0, readSize, Math.max(0, stat.size - readSize));
201
+ fs.closeSync(fd);
202
+ if (buffer.toString('utf8').includes('session_restart')) {
203
+ candidates.push({ id, mtime: stat.mtimeMs });
204
+ }
205
+ } catch {
206
+ // Skip unreadable files
207
+ }
208
+ }
209
+ if (candidates.length === 0) return null;
210
+ candidates.sort((a, b) => b.mtime - a.mtime);
211
+ return candidates[0].id;
212
+ }
213
+
214
+ export function discoverSessionId() {
215
+ const sessionDir = getSessionDir();
216
+ if (!fs.existsSync(sessionDir)) {
217
+ throw new Error(`Session directory not found: ${sessionDir}`);
218
+ }
219
+
220
+ const claudePid = process.ppid;
221
+ const lsofResult = discoverSessionIdViaLsof(sessionDir, claudePid);
222
+ if (lsofResult) return lsofResult;
223
+
224
+ // Note: discoverSessionIdViaContent is NOT used here because in hook context
225
+ // Claude hasn't written the tool call to the transcript yet (the hook fires
226
+ // BEFORE Claude processes the prompt). Fall through directly to mtime.
227
+
228
+ let files;
229
+ try {
230
+ files = fs.readdirSync(sessionDir)
231
+ .filter(f => f.endsWith('.jsonl'))
232
+ .map(f => {
233
+ const filePath = path.join(sessionDir, f);
234
+ const stat = fs.statSync(filePath);
235
+ return { name: f, mtime: stat.mtimeMs, size: stat.size };
236
+ })
237
+ .sort((a, b) => b.mtime - a.mtime || b.size - a.size);
238
+ } catch (err) {
239
+ throw new Error(`Failed to read session directory: ${err.message}`);
240
+ }
241
+
242
+ if (files.length === 0) {
243
+ throw new Error(`No session files found in: ${sessionDir}`);
244
+ }
245
+
246
+ const sessionId = files[0].name.replace('.jsonl', '');
247
+ if (!UUID_REGEX.test(sessionId)) {
248
+ throw new Error(`Session filename is not a valid UUID: ${sessionId}`);
249
+ }
250
+ return sessionId;
251
+ }
252
+
253
+ export function getClaudePid() {
254
+ const pid = process.ppid;
255
+ if (!Number.isInteger(pid) || pid <= 0) {
256
+ throw new Error(`Invalid parent PID: ${pid}`);
257
+ }
258
+ try {
259
+ const command = execSync(`ps -o command= -p ${pid}`, { encoding: 'utf8' }).trim();
260
+ if (!command.toLowerCase().includes('claude')) {
261
+ throw new Error(`Parent process (PID ${pid}) does not appear to be Claude Code: "${command}"`);
262
+ }
263
+ } catch (err) {
264
+ if (err instanceof Error && err.message.includes('does not appear')) throw err;
265
+ throw new Error(`Could not verify parent process (PID ${pid}): ${err.message}`);
266
+ }
267
+ return pid;
268
+ }
269
+
270
+ export function detectTerminal() {
271
+ if (process.platform !== 'darwin') return 'unknown';
272
+ const termProgram = process.env.TERM_PROGRAM || '';
273
+ if (termProgram === 'Apple_Terminal') return 'apple_terminal';
274
+ if (termProgram === 'iTerm.app' || termProgram === 'iTerm2') return 'iterm';
275
+ return 'unknown';
276
+ }
277
+
278
+ export function shellEscape(s) {
279
+ if (/^[a-zA-Z0-9._\-/~]+$/.test(s)) return s;
280
+ return `'${s.replace(/'/g, "'\\''")}'`;
281
+ }
282
+
283
+ // ============================================================================
284
+ // Mode 2 handlers
285
+ // ============================================================================
286
+
287
+ function getTriageStats(dbPath) {
288
+ const db = openDb(dbPath);
289
+ if (!db) return null;
290
+ try {
291
+ const rows = db.prepare(
292
+ "SELECT triage_status, COUNT(*) as count FROM reports GROUP BY triage_status"
293
+ ).all();
294
+ const stats = {};
295
+ for (const row of rows) {
296
+ stats[row.triage_status] = row.count;
297
+ }
298
+ return stats;
299
+ } catch {
300
+ return null;
301
+ } finally {
302
+ db.close();
303
+ }
304
+ }
305
+
306
+ function handleCtoReport() {
307
+ const output = { command: 'cto-report', gathered: {} };
308
+
309
+ // deputy-cto.db
310
+ const deputyDb = openDb(DEPUTY_CTO_DB);
311
+ if (deputyDb) {
312
+ try {
313
+ const pendingCount = deputyDb.prepare("SELECT COUNT(*) as count FROM questions WHERE status = 'pending'").get();
314
+ const rejectionCount = deputyDb.prepare("SELECT COUNT(*) as count FROM questions WHERE type = 'rejection' AND status = 'pending'").get();
315
+ output.gathered.deputyCto = {
316
+ pendingQuestions: pendingCount?.count ?? 0,
317
+ pendingRejections: rejectionCount?.count ?? 0,
318
+ };
319
+ } catch {
320
+ output.gathered.deputyCto = { error: 'query failed' };
321
+ } finally {
322
+ deputyDb.close();
323
+ }
324
+ } else {
325
+ output.gathered.deputyCto = { error: 'database not found' };
326
+ }
327
+
328
+ // todo.db
329
+ const todoDb = openDb(TODO_DB);
330
+ if (todoDb) {
331
+ try {
332
+ const rows = todoDb.prepare(
333
+ "SELECT section, status, COUNT(*) as count FROM tasks GROUP BY section, status"
334
+ ).all();
335
+ output.gathered.todos = rows;
336
+ } catch {
337
+ output.gathered.todos = { error: 'query failed' };
338
+ } finally {
339
+ todoDb.close();
340
+ }
341
+ } else {
342
+ output.gathered.todos = { error: 'database not found' };
343
+ }
344
+
345
+ // cto-reports.db
346
+ const triageStats = getTriageStats(CTO_REPORTS_DB);
347
+ output.gathered.ctoReports = triageStats ?? { error: 'database not found' };
348
+
349
+ // autonomous-mode.json
350
+ const autonomousMode = readJson(AUTONOMOUS_MODE_PATH);
351
+ output.gathered.autonomousMode = autonomousMode ?? { error: 'file not found' };
352
+
353
+ // hourly-automation-state.json
354
+ const automationState = readJson(AUTOMATION_STATE_PATH);
355
+ output.gathered.automationState = automationState
356
+ ? { lastRun: automationState.lastRun }
357
+ : { error: 'file not found' };
358
+
359
+ // automation-config.json
360
+ const automationConfig = readJson(AUTOMATION_CONFIG_PATH);
361
+ if (automationConfig) {
362
+ output.gathered.automationConfig = {
363
+ effective: automationConfig.effective,
364
+ overdrive: automationConfig.overdrive
365
+ ? {
366
+ active: automationConfig.overdrive.active,
367
+ expiresAt: automationConfig.overdrive.expires_at,
368
+ }
369
+ : null,
370
+ };
371
+ } else {
372
+ output.gathered.automationConfig = { error: 'file not found' };
373
+ }
374
+
375
+ console.log(JSON.stringify({
376
+ continue: true,
377
+ hookSpecificOutput: {
378
+ hookEventName: 'UserPromptSubmit',
379
+ additionalContext: `[PREFETCH:cto-report] ${JSON.stringify(output)}`,
380
+ },
381
+ }));
382
+ }
383
+
384
+ function handleDeputyCto() {
385
+ const output = { command: 'deputy-cto', gathered: {} };
386
+
387
+ // Read deputy-cto agent instructions for identity injection
388
+ const agentMdPath = path.join(PROJECT_DIR, '.claude', 'agents', 'deputy-cto.md');
389
+ try {
390
+ const raw = fs.readFileSync(agentMdPath, 'utf8');
391
+ // Strip YAML frontmatter (between --- markers)
392
+ const stripped = raw.replace(/^---[\s\S]*?---\n*/, '');
393
+ output.gathered.agentInstructions = stripped.trim();
394
+ } catch {
395
+ // Agent file not found — non-fatal
396
+ output.gathered.agentInstructions = null;
397
+ }
398
+
399
+ // deputy-cto.db: pending questions
400
+ const deputyDb = openDb(DEPUTY_CTO_DB);
401
+ if (deputyDb) {
402
+ try {
403
+ // Discover available columns to handle schema variations across db versions
404
+ const tableInfo = deputyDb.prepare("PRAGMA table_info(questions)").all();
405
+ const availableCols = new Set(tableInfo.map(c => c.name));
406
+
407
+ // Always-present columns
408
+ const selectCols = ['id', 'type', 'title', 'description'].filter(c => availableCols.has(c));
409
+ // Optional columns added in later schema versions
410
+ const optionalCols = ['suggested_options', 'recommendation', 'created_at', 'created_timestamp'];
411
+ for (const col of optionalCols) {
412
+ if (availableCols.has(col)) selectCols.push(col);
413
+ }
414
+
415
+ const orderBy = availableCols.has('created_at')
416
+ ? 'ORDER BY created_at ASC'
417
+ : availableCols.has('created_timestamp')
418
+ ? 'ORDER BY created_timestamp ASC'
419
+ : '';
420
+
421
+ const sql = `SELECT ${selectCols.join(', ')} FROM questions WHERE status = 'pending' ${orderBy}`.trim();
422
+ const questions = deputyDb.prepare(sql).all();
423
+
424
+ const rejectionCount = deputyDb.prepare(
425
+ "SELECT COUNT(*) as count FROM questions WHERE type = 'rejection' AND status = 'pending'"
426
+ ).get();
427
+ output.gathered.pendingQuestions = questions;
428
+ output.gathered.pendingRejections = rejectionCount?.count ?? 0;
429
+ output.gathered.commitsBlocked = (rejectionCount?.count ?? 0) > 0;
430
+ } catch {
431
+ output.gathered.pendingQuestions = [];
432
+ output.gathered.error = 'deputy-cto query failed';
433
+ } finally {
434
+ deputyDb.close();
435
+ }
436
+ } else {
437
+ output.gathered.pendingQuestions = [];
438
+ output.gathered.error = 'deputy-cto database not found';
439
+ }
440
+
441
+ // cto-reports.db: triage stats
442
+ const triageStats = getTriageStats(CTO_REPORTS_DB);
443
+ output.gathered.triageStats = triageStats ?? { error: 'cto-reports database not found' };
444
+
445
+ console.log(JSON.stringify({
446
+ continue: true,
447
+ hookSpecificOutput: {
448
+ hookEventName: 'UserPromptSubmit',
449
+ additionalContext: `[PREFETCH:deputy-cto] ${JSON.stringify(output)}`,
450
+ },
451
+ }));
452
+ }
453
+
454
+ function handleToggleAutomation() {
455
+ const content = readJson(AUTONOMOUS_MODE_PATH);
456
+ const output = {
457
+ command: 'toggle-automation',
458
+ gathered: {
459
+ autonomousMode: content ?? { error: 'autonomous-mode.json not found' },
460
+ },
461
+ };
462
+
463
+ console.log(JSON.stringify({
464
+ continue: true,
465
+ hookSpecificOutput: {
466
+ hookEventName: 'UserPromptSubmit',
467
+ additionalContext: `[PREFETCH:toggle-automation] ${JSON.stringify(output)}`,
468
+ },
469
+ }));
470
+ }
471
+
472
+ function handleOverdrive() {
473
+ const content = readJson(AUTOMATION_CONFIG_PATH);
474
+ const output = {
475
+ command: 'overdrive',
476
+ gathered: {
477
+ automationConfig: content ?? { error: 'automation-config.json not found' },
478
+ },
479
+ };
480
+
481
+ console.log(JSON.stringify({
482
+ continue: true,
483
+ hookSpecificOutput: {
484
+ hookEventName: 'UserPromptSubmit',
485
+ additionalContext: `[PREFETCH:overdrive] ${JSON.stringify(output)}`,
486
+ },
487
+ }));
488
+ }
489
+
490
+ function getAccountInventory() {
491
+ const ROTATION_STATE_PATH = path.join(os.homedir(), '.claude', 'api-key-rotation.json');
492
+ try {
493
+ if (!fs.existsSync(ROTATION_STATE_PATH)) return null;
494
+ const state = JSON.parse(fs.readFileSync(ROTATION_STATE_PATH, 'utf8'));
495
+ if (!state || state.version !== 1 || typeof state.keys !== 'object') return null;
496
+
497
+ // Deduplicate by account_uuid (same logic as api-key-watcher.js)
498
+ const accountMap = new Map();
499
+ let totalKeys = 0;
500
+ let activeKeys = 0;
501
+ let expiredKeys = 0;
502
+ let invalidKeys = 0;
503
+
504
+ for (const [id, k] of Object.entries(state.keys)) {
505
+ totalKeys++;
506
+ if (k.status === 'active') activeKeys++;
507
+ else if (k.status === 'expired') expiredKeys++;
508
+ else if (k.status === 'invalid') invalidKeys++;
509
+
510
+ const dedupeKey = k.account_uuid || `fp:${id}`;
511
+ if (!accountMap.has(dedupeKey) || k.status === 'active') {
512
+ accountMap.set(dedupeKey, {
513
+ email: k.account_email || null,
514
+ uuid: k.account_uuid || null,
515
+ keyId: id.slice(0, 8),
516
+ status: k.status,
517
+ usage: k.last_usage ? {
518
+ five_hour: Math.round(k.last_usage.five_hour ?? 0),
519
+ seven_day: Math.round(k.last_usage.seven_day ?? 0),
520
+ seven_day_sonnet: Math.round(k.last_usage.seven_day_sonnet ?? 0),
521
+ } : null,
522
+ subscription: k.subscriptionType || 'unknown',
523
+ });
524
+ }
525
+ }
526
+
527
+ // Cross-match null-UUID keys against UUID-bearing keys with matching usage.
528
+ // Prevents "unknown" accounts when profile resolution hasn't run yet.
529
+ const fpKeys = [...accountMap.keys()].filter(k => k.startsWith('fp:'));
530
+ for (const fpKey of fpKeys) {
531
+ const fpEntry = accountMap.get(fpKey);
532
+ if (!fpEntry.usage) continue;
533
+ for (const [uuidKey, uuidEntry] of accountMap) {
534
+ if (uuidKey.startsWith('fp:') || !uuidEntry.email || !uuidEntry.usage) continue;
535
+ if (uuidEntry.usage.seven_day === fpEntry.usage.seven_day && uuidEntry.usage.seven_day_sonnet === fpEntry.usage.seven_day_sonnet) {
536
+ accountMap.delete(fpKey);
537
+ break;
538
+ }
539
+ }
540
+ }
541
+
542
+ return {
543
+ accounts: [...accountMap.values()],
544
+ totalKeys,
545
+ activeKeys,
546
+ expiredKeys,
547
+ invalidKeys,
548
+ };
549
+ } catch {
550
+ return null;
551
+ }
552
+ }
553
+
554
+ function handleSetupGentyr() {
555
+ // Find framework dir: if .claude/hooks is a symlink, follow it
556
+ const hooksPath = path.join(PROJECT_DIR, '.claude', 'hooks');
557
+ let frameworkDir = null;
558
+
559
+ try {
560
+ const stat = fs.lstatSync(hooksPath);
561
+ if (stat.isSymbolicLink()) {
562
+ const resolved = fs.realpathSync(hooksPath);
563
+ // hooks -> <framework>/.claude/hooks, so framework is 2 levels up
564
+ frameworkDir = path.resolve(resolved, '..', '..');
565
+ }
566
+ } catch {
567
+ // hooks dir is not a symlink or doesn't exist
568
+ }
569
+
570
+ // Fallback: look for node_modules/gentyr (npm install model) or .claude-framework (legacy symlink)
571
+ if (!frameworkDir) {
572
+ const npmPath = path.join(PROJECT_DIR, 'node_modules', 'gentyr');
573
+ const legacyPath = path.join(PROJECT_DIR, '.claude-framework');
574
+ const frameworkLink = fs.existsSync(npmPath) ? npmPath : legacyPath;
575
+ if (fs.existsSync(frameworkLink)) {
576
+ try {
577
+ frameworkDir = fs.realpathSync(frameworkLink);
578
+ } catch {
579
+ frameworkDir = frameworkLink;
580
+ }
581
+ }
582
+ }
583
+
584
+ let setupCheckOutput = null;
585
+ if (frameworkDir) {
586
+ const setupCheckScript = path.join(frameworkDir, 'scripts', 'setup-check.js');
587
+ if (fs.existsSync(setupCheckScript)) {
588
+ try {
589
+ setupCheckOutput = execSync(`node ${shellEscape(setupCheckScript)} 2>/dev/null`, {
590
+ encoding: 'utf8',
591
+ timeout: 15000,
592
+ });
593
+ } catch {
594
+ setupCheckOutput = null;
595
+ }
596
+ }
597
+ }
598
+
599
+ let parsedSetupCheck = null;
600
+ if (setupCheckOutput) {
601
+ try {
602
+ parsedSetupCheck = JSON.parse(setupCheckOutput);
603
+ } catch {
604
+ // Raw output if not JSON
605
+ parsedSetupCheck = { raw: setupCheckOutput };
606
+ }
607
+ }
608
+
609
+ const output = {
610
+ command: 'setup-gentyr',
611
+ gathered: {
612
+ frameworkDir,
613
+ setupCheck: parsedSetupCheck ?? { error: frameworkDir ? 'setup-check.js failed or not found' : 'framework directory not found' },
614
+ accountInventory: getAccountInventory(),
615
+ },
616
+ };
617
+
618
+ console.log(JSON.stringify({
619
+ continue: true,
620
+ hookSpecificOutput: {
621
+ hookEventName: 'UserPromptSubmit',
622
+ additionalContext: `[PREFETCH:setup-gentyr] ${JSON.stringify(output)}`,
623
+ },
624
+ }));
625
+ }
626
+
627
+ function handlePushMigrations() {
628
+ const services = readJson(SERVICES_CONFIG_PATH);
629
+ const output = { command: 'push-migrations', gathered: {} };
630
+
631
+ const migrationsDir = services?.supabase?.migrationsDir;
632
+ if (!migrationsDir) {
633
+ output.gathered.migrationsDir = null;
634
+ output.gathered.migrationFiles = null;
635
+ output.gathered.note = 'supabase.migrationsDir not configured in services.json';
636
+ } else {
637
+ output.gathered.migrationsDir = migrationsDir;
638
+ const fullDir = path.isAbsolute(migrationsDir)
639
+ ? migrationsDir
640
+ : path.join(PROJECT_DIR, migrationsDir);
641
+
642
+ if (fs.existsSync(fullDir)) {
643
+ try {
644
+ const files = fs.readdirSync(fullDir)
645
+ .filter(f => f.endsWith('.sql'))
646
+ .sort();
647
+ output.gathered.migrationFiles = files;
648
+ } catch {
649
+ output.gathered.migrationFiles = { error: 'failed to read migrations directory' };
650
+ }
651
+ } else {
652
+ output.gathered.migrationFiles = { error: `directory not found: ${fullDir}` };
653
+ }
654
+ }
655
+
656
+ console.log(JSON.stringify({
657
+ continue: true,
658
+ hookSpecificOutput: {
659
+ hookEventName: 'UserPromptSubmit',
660
+ additionalContext: `[PREFETCH:push-migrations] ${JSON.stringify(output)}`,
661
+ },
662
+ }));
663
+ }
664
+
665
+ function handlePushSecrets() {
666
+ const services = readJson(SERVICES_CONFIG_PATH);
667
+ // Only expose key names and whether values are op:// refs — never include actual values
668
+ let secretsSummary = null;
669
+ if (services?.secrets) {
670
+ secretsSummary = {};
671
+ for (const [target, mappings] of Object.entries(services.secrets)) {
672
+ secretsSummary[target] = {};
673
+ for (const [key, value] of Object.entries(mappings)) {
674
+ secretsSummary[target][key] = typeof value === 'string' && value.startsWith('op://') ? 'op://' : '[direct]';
675
+ }
676
+ }
677
+ }
678
+ const output = {
679
+ command: 'push-secrets',
680
+ gathered: {
681
+ secretKeyNames: secretsSummary,
682
+ servicesConfigExists: fs.existsSync(SERVICES_CONFIG_PATH),
683
+ note: !services ? 'services.json not found' : (!services.secrets ? 'no secrets section in services.json' : null),
684
+ },
685
+ };
686
+
687
+ console.log(JSON.stringify({
688
+ continue: true,
689
+ hookSpecificOutput: {
690
+ hookEventName: 'UserPromptSubmit',
691
+ additionalContext: `[PREFETCH:push-secrets] ${JSON.stringify(output)}`,
692
+ },
693
+ }));
694
+ }
695
+
696
+ function handleConfigurePersonas() {
697
+ const output = { command: 'configure-personas', gathered: {} };
698
+
699
+ const feedbackDb = openDb(USER_FEEDBACK_DB);
700
+ if (feedbackDb) {
701
+ try {
702
+ const personas = feedbackDb.prepare(
703
+ "SELECT id, name, consumption_mode, enabled FROM personas ORDER BY name"
704
+ ).all();
705
+ const features = feedbackDb.prepare(
706
+ "SELECT id, name, category FROM features ORDER BY name"
707
+ ).all();
708
+ const mappings = feedbackDb.prepare(
709
+ "SELECT persona_id, feature_id, priority FROM persona_features"
710
+ ).all();
711
+ output.gathered.personas = personas;
712
+ output.gathered.features = features;
713
+ output.gathered.mappings = mappings;
714
+ } catch {
715
+ output.gathered.error = 'query failed';
716
+ } finally {
717
+ feedbackDb.close();
718
+ }
719
+ } else {
720
+ output.gathered.personas = [];
721
+ output.gathered.features = [];
722
+ output.gathered.mappings = [];
723
+ output.gathered.note = 'user-feedback.db not found (no personas configured yet)';
724
+ }
725
+
726
+ console.log(JSON.stringify({
727
+ continue: true,
728
+ hookSpecificOutput: {
729
+ hookEventName: 'UserPromptSubmit',
730
+ additionalContext: `[PREFETCH:configure-personas] ${JSON.stringify(output)}`,
731
+ },
732
+ }));
733
+ }
734
+
735
+ function handleSpawnTasks() {
736
+ const output = { command: 'spawn-tasks', gathered: {} };
737
+
738
+ const todoDb = openDb(TODO_DB);
739
+ if (todoDb) {
740
+ try {
741
+ const rows = todoDb.prepare(
742
+ "SELECT section, COUNT(*) as count FROM tasks WHERE status = 'pending' AND section IN ('CODE-REVIEWER', 'INVESTIGATOR & PLANNER', 'TEST-WRITER', 'PROJECT-MANAGER', 'DEPUTY-CTO', 'PRODUCT-MANAGER') GROUP BY section"
743
+ ).all();
744
+ output.gathered.pendingBySection = rows;
745
+ const total = rows.reduce((sum, r) => sum + r.count, 0);
746
+ output.gathered.totalPending = total;
747
+ } catch {
748
+ output.gathered.pendingBySection = [];
749
+ output.gathered.totalPending = 0;
750
+ output.gathered.error = 'query failed';
751
+ } finally {
752
+ todoDb.close();
753
+ }
754
+ } else {
755
+ output.gathered.pendingBySection = [];
756
+ output.gathered.totalPending = 0;
757
+ output.gathered.note = 'todo.db not found';
758
+ }
759
+
760
+ // Count running agents via pgrep
761
+ let runningAgents = 0;
762
+ try {
763
+ const result = execSync(
764
+ "pgrep -cf 'claude.*--dangerously-skip-permissions'",
765
+ { encoding: 'utf8', timeout: 5000, stdio: 'pipe' }
766
+ ).trim();
767
+ runningAgents = parseInt(result, 10) || 0;
768
+ } catch {
769
+ // pgrep returns exit code 1 when no processes match
770
+ }
771
+ output.gathered.runningAgents = runningAgents;
772
+
773
+ // Read max concurrent from automation-config.json
774
+ const automationConfig = readJson(AUTOMATION_CONFIG_PATH);
775
+ const maxConcurrent = automationConfig?.effective?.MAX_CONCURRENT_AGENTS ?? 10;
776
+ output.gathered.maxConcurrent = maxConcurrent;
777
+ output.gathered.availableSlots = Math.max(0, maxConcurrent - runningAgents);
778
+
779
+ console.log(JSON.stringify({
780
+ continue: true,
781
+ hookSpecificOutput: {
782
+ hookEventName: 'UserPromptSubmit',
783
+ additionalContext: `[PREFETCH:spawn-tasks] ${JSON.stringify(output)}`,
784
+ },
785
+ }));
786
+ }
787
+
788
+ function handleProductManager() {
789
+ const output = { command: 'product-manager', gathered: {} };
790
+
791
+ // Check feature toggle
792
+ const autonomousConfig = readJson(AUTONOMOUS_MODE_PATH);
793
+ output.gathered.productManagerEnabled = autonomousConfig?.productManagerEnabled === true;
794
+
795
+ const pmDb = openDb(PRODUCT_MANAGER_DB);
796
+ if (pmDb) {
797
+ try {
798
+ const meta = pmDb.prepare("SELECT status, last_updated_at, initiated_by, approved_by FROM analysis_meta WHERE id = 'default'").get();
799
+ output.gathered.meta = meta ?? { status: 'not_started' };
800
+
801
+ const sections = pmDb.prepare('SELECT section_number, title, content FROM sections ORDER BY section_number').all();
802
+ const sectionStatus = sections.map(s => {
803
+ const isListSection = s.section_number === 2 || s.section_number === 6;
804
+ let populated = false;
805
+ let entryCount = 0;
806
+ if (isListSection) {
807
+ const count = pmDb.prepare('SELECT COUNT(*) as c FROM section_entries WHERE section_number = ?').get(s.section_number);
808
+ entryCount = count?.c ?? 0;
809
+ populated = entryCount > 0;
810
+ } else {
811
+ populated = !!s.content;
812
+ }
813
+ return { number: s.section_number, title: s.title, populated, entryCount: isListSection ? entryCount : undefined };
814
+ });
815
+ output.gathered.sections = sectionStatus;
816
+ output.gathered.populatedCount = sectionStatus.filter(s => s.populated).length;
817
+
818
+ // Compliance
819
+ const totalPainPoints = pmDb.prepare("SELECT COUNT(*) as c FROM section_entries WHERE section_number = 6").get();
820
+ const mappedCount = pmDb.prepare("SELECT COUNT(DISTINCT pain_point_id) as c FROM pain_point_personas").get();
821
+ output.gathered.compliance = {
822
+ totalPainPoints: totalPainPoints?.c ?? 0,
823
+ mapped: mappedCount?.c ?? 0,
824
+ };
825
+ } catch {
826
+ output.gathered.error = 'query failed';
827
+ } finally {
828
+ pmDb.close();
829
+ }
830
+ } else {
831
+ output.gathered.meta = { status: 'not_started' };
832
+ output.gathered.sections = [];
833
+ output.gathered.populatedCount = 0;
834
+ output.gathered.note = 'product-manager.db not found';
835
+ }
836
+
837
+ console.log(JSON.stringify({
838
+ continue: true,
839
+ hookSpecificOutput: {
840
+ hookEventName: 'UserPromptSubmit',
841
+ additionalContext: `[PREFETCH:product-manager] ${JSON.stringify(output)}`,
842
+ },
843
+ }));
844
+ }
845
+
846
+ function handleToggleProductManager() {
847
+ const content = readJson(AUTONOMOUS_MODE_PATH);
848
+ const output = {
849
+ command: 'toggle-product-manager',
850
+ gathered: {
851
+ productManagerEnabled: content?.productManagerEnabled === true,
852
+ autonomousMode: content ?? { error: 'autonomous-mode.json not found' },
853
+ },
854
+ };
855
+
856
+ console.log(JSON.stringify({
857
+ continue: true,
858
+ hookSpecificOutput: {
859
+ hookEventName: 'UserPromptSubmit',
860
+ additionalContext: `[PREFETCH:toggle-product-manager] ${JSON.stringify(output)}`,
861
+ },
862
+ }));
863
+ }
864
+
865
+ function handleTriage() {
866
+ const output = { command: 'triage', gathered: {} };
867
+
868
+ // cto-reports.db: triage status breakdown
869
+ const triageStats = getTriageStats(CTO_REPORTS_DB);
870
+ output.gathered.triageStats = triageStats ?? { error: 'cto-reports database not found' };
871
+
872
+ // Count running agents via pgrep
873
+ let runningAgents = 0;
874
+ try {
875
+ const result = execSync(
876
+ "pgrep -cf 'claude.*--dangerously-skip-permissions'",
877
+ { encoding: 'utf8', timeout: 5000, stdio: 'pipe' }
878
+ ).trim();
879
+ runningAgents = parseInt(result, 10) || 0;
880
+ } catch {
881
+ // pgrep returns exit code 1 when no processes match
882
+ }
883
+ output.gathered.runningAgents = runningAgents;
884
+
885
+ // Read max concurrent from automation-config.json
886
+ const automationConfig = readJson(AUTOMATION_CONFIG_PATH);
887
+ const maxConcurrent = automationConfig?.effective?.MAX_CONCURRENT_AGENTS ?? 10;
888
+ output.gathered.maxConcurrent = maxConcurrent;
889
+ output.gathered.availableSlots = Math.max(0, maxConcurrent - runningAgents);
890
+
891
+ console.log(JSON.stringify({
892
+ continue: true,
893
+ hookSpecificOutput: {
894
+ hookEventName: 'UserPromptSubmit',
895
+ additionalContext: `[PREFETCH:triage] ${JSON.stringify(output)}`,
896
+ },
897
+ }));
898
+ }
899
+
900
+ function handleShow() {
901
+ // Lightweight check: confirm dashboard binary exists and list sections
902
+ const npmPath = path.join(PROJECT_DIR, 'node_modules', 'gentyr');
903
+ const legacyPath = path.join(PROJECT_DIR, '.claude-framework');
904
+ const frameworkLink = fs.existsSync(npmPath) ? npmPath : legacyPath;
905
+ let dashboardExists = false;
906
+ try {
907
+ if (fs.existsSync(frameworkLink)) {
908
+ const frameworkDir = fs.realpathSync(frameworkLink);
909
+ const dashboardPath = path.join(frameworkDir, 'packages', 'cto-dashboard', 'dist', 'index.js');
910
+ dashboardExists = fs.existsSync(dashboardPath);
911
+ }
912
+ } catch {
913
+ // ignore
914
+ }
915
+
916
+ // Canonical source: packages/mcp-servers/src/show/types.ts SECTION_IDS
917
+ const sections = [
918
+ 'quota', 'accounts', 'deputy-cto', 'usage', 'automations',
919
+ 'testing', 'deployments', 'worktrees', 'infra', 'logging',
920
+ 'timeline', 'tasks', 'product-market-fit',
921
+ ];
922
+
923
+ const output = {
924
+ command: 'show',
925
+ gathered: {
926
+ dashboardAvailable: dashboardExists,
927
+ availableSections: sections,
928
+ },
929
+ };
930
+
931
+ console.log(JSON.stringify({
932
+ continue: true,
933
+ hookSpecificOutput: {
934
+ hookEventName: 'UserPromptSubmit',
935
+ additionalContext: `[PREFETCH:show] ${JSON.stringify(output)}`,
936
+ },
937
+ }));
938
+ }
939
+
940
+ function handleDemo() {
941
+ const output = { command: 'demo', gathered: {} };
942
+
943
+ // Check playwright.config.ts existence
944
+ const tsConfig = path.join(PROJECT_DIR, 'playwright.config.ts');
945
+ const jsConfig = path.join(PROJECT_DIR, 'playwright.config.js');
946
+ output.gathered.configExists = fs.existsSync(tsConfig) || fs.existsSync(jsConfig);
947
+ output.gathered.configPath = fs.existsSync(tsConfig) ? 'playwright.config.ts' : (fs.existsSync(jsConfig) ? 'playwright.config.js' : null);
948
+
949
+ // Check @playwright/test in node_modules
950
+ const pwTestDir = path.join(PROJECT_DIR, 'node_modules', '@playwright', 'test');
951
+ output.gathered.depsInstalled = fs.existsSync(pwTestDir);
952
+
953
+ // Check Chromium in browser cache directories
954
+ const cacheLocations = [
955
+ path.join(os.homedir(), 'Library', 'Caches', 'ms-playwright'),
956
+ path.join(os.homedir(), '.cache', 'ms-playwright'),
957
+ ];
958
+ let browsersFound = false;
959
+ for (const cacheDir of cacheLocations) {
960
+ if (!fs.existsSync(cacheDir)) continue;
961
+ try {
962
+ const entries = fs.readdirSync(cacheDir);
963
+ if (entries.some(e => e.startsWith('chromium-'))) {
964
+ browsersFound = true;
965
+ break;
966
+ }
967
+ } catch {
968
+ // ignore
969
+ }
970
+ }
971
+ output.gathered.browsersInstalled = browsersFound;
972
+
973
+ // Credential env var status
974
+ const credentialKeys = ['SUPABASE_URL', 'SUPABASE_ANON_KEY', 'SUPABASE_SERVICE_ROLE_KEY'];
975
+ const credentials = {};
976
+ for (const key of credentialKeys) {
977
+ const value = process.env[key];
978
+ if (!value) {
979
+ credentials[key] = 'missing';
980
+ } else if (value.startsWith('op://')) {
981
+ credentials[key] = 'unresolved_op_ref';
982
+ } else {
983
+ credentials[key] = 'set';
984
+ }
985
+ }
986
+ output.gathered.credentials = credentials;
987
+
988
+ // Test file counts per project directory
989
+ const activeDirs = {
990
+ 'vendor-owner': 'e2e/vendor',
991
+ 'manual': 'e2e/manual',
992
+ 'extension-manual': 'e2e/extension/manual',
993
+ 'demo': 'e2e/demo',
994
+ };
995
+ const testCounts = {};
996
+ for (const [project, testDir] of Object.entries(activeDirs)) {
997
+ const fullDir = path.join(PROJECT_DIR, testDir);
998
+ if (!fs.existsSync(fullDir)) {
999
+ testCounts[project] = 0;
1000
+ continue;
1001
+ }
1002
+ try {
1003
+ const files = fs.readdirSync(fullDir, { recursive: true });
1004
+ testCounts[project] = files.filter(f => {
1005
+ const filename = String(f);
1006
+ return filename.endsWith('.spec.ts') || filename.endsWith('.manual.ts');
1007
+ }).length;
1008
+ } catch {
1009
+ testCounts[project] = 0;
1010
+ }
1011
+ }
1012
+ output.gathered.testCounts = testCounts;
1013
+
1014
+ // Fast path: read cached health signal from SessionStart hook
1015
+ const healthFile = path.join(PROJECT_DIR, '.claude', 'playwright-health.json');
1016
+ let authCheckedFromCache = false;
1017
+ if (fs.existsSync(healthFile)) {
1018
+ try {
1019
+ const cached = JSON.parse(fs.readFileSync(healthFile, 'utf-8'));
1020
+ const cacheAge = Date.now() - new Date(cached.checkedAt).getTime();
1021
+ if (cacheAge < 3_600_000) { // 1 hour
1022
+ output.gathered.authState = cached.authState;
1023
+ output.gathered.extensionBuilt = cached.extensionBuilt;
1024
+ authCheckedFromCache = true;
1025
+ }
1026
+ } catch { /* ignore — fall through to manual checks */ }
1027
+ }
1028
+
1029
+ // Auth state freshness (manual check if cache miss)
1030
+ if (!authCheckedFromCache) {
1031
+ const authDir = path.join(PROJECT_DIR, '.auth');
1032
+ const primaryAuth = path.join(authDir, 'vendor-owner.json');
1033
+ let authState = { exists: false, ageHours: null, cookiesExpired: false, isStale: true };
1034
+
1035
+ if (fs.existsSync(primaryAuth)) {
1036
+ try {
1037
+ const stat = fs.statSync(primaryAuth);
1038
+ const ageMs = Date.now() - stat.mtimeMs;
1039
+ const ageHours = ageMs / (1000 * 60 * 60);
1040
+
1041
+ let cookiesExpired = false;
1042
+ try {
1043
+ const state = JSON.parse(fs.readFileSync(primaryAuth, 'utf-8'));
1044
+ const now = Date.now() / 1000;
1045
+ const cookies = state.cookies || [];
1046
+ cookiesExpired = cookies.some(c => c.expires && c.expires > 0 && c.expires < now);
1047
+ } catch { /* ignore */ }
1048
+
1049
+ authState = {
1050
+ exists: true,
1051
+ ageHours: Math.round(ageHours * 10) / 10,
1052
+ cookiesExpired,
1053
+ isStale: cookiesExpired || ageHours > 24,
1054
+ };
1055
+ } catch { /* ignore */ }
1056
+ }
1057
+ output.gathered.authState = authState;
1058
+ }
1059
+
1060
+ // Summary: critical issues
1061
+ const criticalIssues = [];
1062
+ if (!output.gathered.configExists) criticalIssues.push('playwright.config.ts not found');
1063
+ if (!output.gathered.depsInstalled) criticalIssues.push('@playwright/test not installed');
1064
+ if (!output.gathered.browsersInstalled) criticalIssues.push('Chromium browser not installed');
1065
+ output.gathered.criticalIssues = criticalIssues;
1066
+ output.gathered.readyForPreflight = criticalIssues.length === 0;
1067
+
1068
+ console.log(JSON.stringify({
1069
+ continue: true,
1070
+ hookSpecificOutput: {
1071
+ hookEventName: 'UserPromptSubmit',
1072
+ additionalContext: `[PREFETCH:demo] ${JSON.stringify(output)}`,
1073
+ },
1074
+ }));
1075
+ }
1076
+
1077
+ // ============================================================================
1078
+ // Main
1079
+ // ============================================================================
1080
+
1081
+ async function main() {
1082
+ const raw = await readStdin();
1083
+ if (!raw) process.exit(0);
1084
+
1085
+ const prompt = extractPrompt(raw);
1086
+
1087
+ // Mode 2 handlers — load Database lazily only when needed
1088
+ const needsDb = ['cto-report', 'deputy-cto', 'configure-personas', 'spawn-tasks', 'product-manager', 'triage'];
1089
+ const matchedCommand = Object.keys(SENTINELS).find(key => matchesCommand(prompt, key));
1090
+ if (matchedCommand) {
1091
+ if (needsDb.includes(matchedCommand)) {
1092
+ await getDatabase();
1093
+ }
1094
+ }
1095
+
1096
+ if (matchesCommand(prompt, 'cto-report')) {
1097
+ return handleCtoReport();
1098
+ }
1099
+ if (matchesCommand(prompt, 'deputy-cto')) {
1100
+ return handleDeputyCto();
1101
+ }
1102
+ if (matchesCommand(prompt, 'toggle-automation')) {
1103
+ return handleToggleAutomation();
1104
+ }
1105
+ if (matchesCommand(prompt, 'overdrive')) {
1106
+ return handleOverdrive();
1107
+ }
1108
+ if (matchesCommand(prompt, 'setup-gentyr')) {
1109
+ return handleSetupGentyr();
1110
+ }
1111
+ if (matchesCommand(prompt, 'push-migrations')) {
1112
+ return handlePushMigrations();
1113
+ }
1114
+ if (matchesCommand(prompt, 'push-secrets')) {
1115
+ return handlePushSecrets();
1116
+ }
1117
+ if (matchesCommand(prompt, 'configure-personas')) {
1118
+ return handleConfigurePersonas();
1119
+ }
1120
+ if (matchesCommand(prompt, 'spawn-tasks')) {
1121
+ return handleSpawnTasks();
1122
+ }
1123
+ if (matchesCommand(prompt, 'product-manager')) {
1124
+ return handleProductManager();
1125
+ }
1126
+ if (matchesCommand(prompt, 'toggle-product-manager')) {
1127
+ return handleToggleProductManager();
1128
+ }
1129
+ if (matchesCommand(prompt, 'triage')) {
1130
+ return handleTriage();
1131
+ }
1132
+ if (matchesCommand(prompt, 'show')) {
1133
+ return handleShow();
1134
+ }
1135
+ if (matchesCommand(prompt, 'demo')) {
1136
+ return handleDemo();
1137
+ }
1138
+
1139
+ process.exit(0);
1140
+ }
1141
+
1142
+ main().catch(() => {
1143
+ // All errors: silent exit, Claude falls back to MCP tools
1144
+ process.exit(0);
1145
+ });