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,656 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CTO Notification Hook
4
+ *
5
+ * Runs on UserPromptSubmit to notify the user of pending CTO items and session metrics.
6
+ * Checks deputy-cto and agent-reports databases, token usage, and session counts.
7
+ *
8
+ * Usage: Called by Claude Code UserPromptSubmit hook
9
+ *
10
+ * @version 2.0.0
11
+ */
12
+
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import os from 'os';
16
+ import { fileURLToPath } from 'url';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ // Try to import better-sqlite3
22
+ let Database = null;
23
+ try {
24
+ Database = (await import('better-sqlite3')).default;
25
+ } catch {
26
+ // Database not available
27
+ }
28
+
29
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
30
+ const DEPUTY_CTO_DB = path.join(PROJECT_DIR, '.claude', 'deputy-cto.db');
31
+ const CTO_REPORTS_DB = path.join(PROJECT_DIR, '.claude', 'cto-reports.db');
32
+ const TODO_DB = path.join(PROJECT_DIR, '.claude', 'todo.db');
33
+ const AGENT_TRACKER_HISTORY = path.join(PROJECT_DIR, '.claude', 'state', 'agent-tracker-history.json');
34
+ const AUTONOMOUS_CONFIG_PATH = path.join(PROJECT_DIR, '.claude', 'autonomous-mode.json');
35
+ const AUTOMATION_STATE_PATH = path.join(PROJECT_DIR, '.claude', 'hourly-automation-state.json');
36
+ const KEY_ROTATION_STATE_PATH = path.join(os.homedir(), '.claude', 'api-key-rotation.json');
37
+ const CREDENTIALS_PATH = path.join(os.homedir(), '.claude', '.credentials.json');
38
+ const ANTHROPIC_API_URL = 'https://api.anthropic.com/api/oauth/usage';
39
+ const ANTHROPIC_BETA_HEADER = 'oauth-2025-04-20';
40
+ const COOLDOWN_MINUTES = 55;
41
+ // Cache goes in ~/.claude/ (user-owned) since project .claude/ may be root-protected
42
+ const METRICS_CACHE_PATH = path.join(os.homedir(), '.claude', `cto-metrics-cache-${PROJECT_DIR.replace(/[^a-zA-Z0-9]/g, '-').replace(/^-/, '')}.json`);
43
+
44
+ /**
45
+ * Get session directory path for this project
46
+ */
47
+ function getSessionDir() {
48
+ const projectPath = PROJECT_DIR.replace(/[^a-zA-Z0-9]/g, '-').replace(/^-/, '');
49
+ return path.join(os.homedir(), '.claude', 'projects', `-${projectPath}`);
50
+ }
51
+
52
+ /**
53
+ * Get pending counts from deputy-cto database
54
+ * G001: Returns null on error to allow caller to handle appropriately
55
+ */
56
+ function getDeputyCtoCounts() {
57
+ if (!Database) {
58
+ // Database module not available - this is expected in some environments
59
+ return { pending: 0, rejections: 0, error: false };
60
+ }
61
+
62
+ if (!fs.existsSync(DEPUTY_CTO_DB)) {
63
+ // No database yet - first run, no pending items
64
+ return { pending: 0, rejections: 0, error: false };
65
+ }
66
+
67
+ try {
68
+ const db = new Database(DEPUTY_CTO_DB, { readonly: true });
69
+
70
+ const pending = db.prepare(
71
+ "SELECT COUNT(*) as count FROM questions WHERE status = 'pending'"
72
+ ).get();
73
+
74
+ const rejections = db.prepare(
75
+ "SELECT COUNT(*) as count FROM questions WHERE type = 'rejection' AND status = 'pending'"
76
+ ).get();
77
+
78
+ db.close();
79
+
80
+ return {
81
+ pending: pending?.count || 0,
82
+ rejections: rejections?.count || 0,
83
+ error: false,
84
+ };
85
+ } catch (err) {
86
+ // G001: Log error and signal failure
87
+ console.error(`[cto-notification] Database error: ${err.message}`);
88
+ return { pending: 0, rejections: 0, error: true };
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Get unread count from agent-reports database
94
+ */
95
+ function getUnreadReportsCount() {
96
+ if (!Database || !fs.existsSync(CTO_REPORTS_DB)) {
97
+ return 0;
98
+ }
99
+
100
+ try {
101
+ const db = new Database(CTO_REPORTS_DB, { readonly: true });
102
+
103
+ const result = db.prepare(
104
+ "SELECT COUNT(*) as count FROM reports WHERE read_at IS NULL"
105
+ ).get();
106
+
107
+ db.close();
108
+
109
+ return result?.count || 0;
110
+ } catch {
111
+ return 0;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Get autonomous mode status
117
+ */
118
+ function getAutonomousModeStatus() {
119
+ // Get config
120
+ let enabled = false;
121
+ if (fs.existsSync(AUTONOMOUS_CONFIG_PATH)) {
122
+ try {
123
+ const config = JSON.parse(fs.readFileSync(AUTONOMOUS_CONFIG_PATH, 'utf8'));
124
+ enabled = config.enabled === true;
125
+ } catch (err) {
126
+ console.error(`[cto-notification] Config parse error (autonomous mode disabled): ${err.message}`);
127
+ }
128
+ }
129
+
130
+ // Get next run time
131
+ let nextRunMinutes = null;
132
+ if (enabled && fs.existsSync(AUTOMATION_STATE_PATH)) {
133
+ try {
134
+ const state = JSON.parse(fs.readFileSync(AUTOMATION_STATE_PATH, 'utf8'));
135
+ const lastRun = state.lastRun || 0;
136
+ const now = Date.now();
137
+ const timeSinceLastRun = now - lastRun;
138
+ const cooldownMs = COOLDOWN_MINUTES * 60 * 1000;
139
+
140
+ if (timeSinceLastRun >= cooldownMs) {
141
+ nextRunMinutes = 0;
142
+ } else {
143
+ nextRunMinutes = Math.ceil((cooldownMs - timeSinceLastRun) / 60000);
144
+ }
145
+ } catch (err) {
146
+ console.error(`[cto-notification] State file parse error: ${err.message}`);
147
+ nextRunMinutes = null;
148
+ }
149
+ } else if (enabled) {
150
+ nextRunMinutes = 0; // First run
151
+ }
152
+
153
+ return { enabled, nextRunMinutes };
154
+ }
155
+
156
+ /**
157
+ * Load metrics cache from disk
158
+ */
159
+ function loadMetricsCache() {
160
+ try {
161
+ if (fs.existsSync(METRICS_CACHE_PATH)) {
162
+ return JSON.parse(fs.readFileSync(METRICS_CACHE_PATH, 'utf8'));
163
+ }
164
+ } catch {}
165
+ return { files: {}, totals: { tokens: 0, taskSessions: 0, userSessions: 0 } };
166
+ }
167
+
168
+ /**
169
+ * Save metrics cache to disk
170
+ */
171
+ function saveMetricsCache(cache) {
172
+ try {
173
+ fs.writeFileSync(METRICS_CACHE_PATH, JSON.stringify(cache), 'utf8');
174
+ } catch {}
175
+ }
176
+
177
+ /**
178
+ * Scan a single session file for tokens and session type.
179
+ * For session type, only reads the first 4KB to find the first user message.
180
+ */
181
+ function scanSessionFile(filePath, since) {
182
+ let tokens = 0;
183
+ let isTask = false;
184
+
185
+ try {
186
+ // Read first 4KB for session type detection
187
+ const fd = fs.openSync(filePath, 'r');
188
+ const headerBuf = Buffer.alloc(4096);
189
+ const bytesRead = fs.readSync(fd, headerBuf, 0, 4096, 0);
190
+ fs.closeSync(fd);
191
+
192
+ const headerText = headerBuf.toString('utf8', 0, bytesRead);
193
+ const headerLines = headerText.split('\n');
194
+ for (const line of headerLines) {
195
+ if (!line.trim()) continue;
196
+ try {
197
+ const entry = JSON.parse(line);
198
+ if (entry.type === 'human' || entry.type === 'user') {
199
+ const msg = typeof entry.message?.content === 'string'
200
+ ? entry.message.content
201
+ : entry.content;
202
+ if (msg && msg.startsWith('[Task]')) {
203
+ isTask = true;
204
+ }
205
+ break;
206
+ }
207
+ } catch {}
208
+ }
209
+
210
+ // Full scan for token usage
211
+ const content = fs.readFileSync(filePath, 'utf8');
212
+ const lines = content.split('\n');
213
+ for (const line of lines) {
214
+ if (!line.trim()) continue;
215
+ try {
216
+ const entry = JSON.parse(line);
217
+ if (entry.timestamp) {
218
+ const entryTime = new Date(entry.timestamp).getTime();
219
+ if (entryTime < since) continue;
220
+ }
221
+ const usage = entry.message?.usage;
222
+ if (usage) {
223
+ tokens += usage.input_tokens || 0;
224
+ tokens += usage.output_tokens || 0;
225
+ tokens += usage.cache_read_input_tokens || 0;
226
+ tokens += usage.cache_creation_input_tokens || 0;
227
+ }
228
+ } catch {}
229
+ }
230
+ } catch {}
231
+
232
+ return { tokens, isTask };
233
+ }
234
+
235
+ /**
236
+ * Get token usage and session metrics for last 30 days using incremental cache.
237
+ * Only re-scans files that are new or have changed since last cache update.
238
+ * Uses a time budget (3s) to avoid blocking — builds cache across multiple prompts.
239
+ */
240
+ function getSessionMetricsCached() {
241
+ const sessionDir = getSessionDir();
242
+ const since = Date.now() - (30 * 24 * 60 * 60 * 1000);
243
+ const TIME_BUDGET_MS = 3000;
244
+
245
+ if (!fs.existsSync(sessionDir)) {
246
+ return { tokens: 0, taskSessions: 0, userSessions: 0 };
247
+ }
248
+
249
+ const cache = loadMetricsCache();
250
+ let changed = false;
251
+ const startTime = Date.now();
252
+
253
+ try {
254
+ const files = fs.readdirSync(sessionDir).filter(f => f.endsWith('.jsonl'));
255
+
256
+ // Remove cache entries for files that no longer exist or are outside 30d window
257
+ for (const key of Object.keys(cache.files)) {
258
+ if (!files.includes(key)) {
259
+ delete cache.files[key];
260
+ changed = true;
261
+ }
262
+ }
263
+
264
+ // Sort files: prioritize current session (most recently modified) first
265
+ const fileMetas = [];
266
+ for (const file of files) {
267
+ try {
268
+ const stat = fs.statSync(path.join(sessionDir, file));
269
+ fileMetas.push({ file, mtime: stat.mtime.getTime(), size: stat.size });
270
+ } catch {}
271
+ }
272
+ fileMetas.sort((a, b) => b.mtime - a.mtime);
273
+
274
+ // Scan new or modified files within time budget
275
+ for (const { file, mtime, size } of fileMetas) {
276
+ // Check time budget
277
+ if (Date.now() - startTime > TIME_BUDGET_MS) {
278
+ break;
279
+ }
280
+
281
+ const filePath = path.join(sessionDir, file);
282
+
283
+ // Skip files outside 30-day window
284
+ if (mtime < since) {
285
+ if (cache.files[file]) {
286
+ delete cache.files[file];
287
+ changed = true;
288
+ }
289
+ continue;
290
+ }
291
+
292
+ const cached = cache.files[file];
293
+
294
+ // Skip if file hasn't changed
295
+ if (cached && cached.size === size && cached.mtime === mtime) {
296
+ continue;
297
+ }
298
+
299
+ // Re-scan this file
300
+ const result = scanSessionFile(filePath, since);
301
+ cache.files[file] = {
302
+ size,
303
+ mtime,
304
+ tokens: result.tokens,
305
+ isTask: result.isTask,
306
+ };
307
+ changed = true;
308
+ }
309
+ } catch {}
310
+
311
+ // Recompute totals from cache
312
+ let tokens = 0, taskSessions = 0, userSessions = 0;
313
+ for (const entry of Object.values(cache.files)) {
314
+ tokens += entry.tokens || 0;
315
+ if (entry.isTask) {
316
+ taskSessions++;
317
+ } else {
318
+ userSessions++;
319
+ }
320
+ }
321
+
322
+ cache.totals = { tokens, taskSessions, userSessions };
323
+
324
+ if (changed) {
325
+ saveMetricsCache(cache);
326
+ }
327
+
328
+ return cache.totals;
329
+ }
330
+
331
+ /**
332
+ * Get TODO counts by status
333
+ * Returns both queued (pending) and active (in_progress) counts
334
+ */
335
+ function getTodoCounts() {
336
+ if (!Database || !fs.existsSync(TODO_DB)) {
337
+ return { queued: 0, active: 0 };
338
+ }
339
+
340
+ try {
341
+ const db = new Database(TODO_DB, { readonly: true });
342
+ const queued = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE status = 'pending'").get();
343
+ const active = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE status = 'in_progress'").get();
344
+ db.close();
345
+ return {
346
+ queued: queued?.count || 0,
347
+ active: active?.count || 0,
348
+ };
349
+ } catch {
350
+ return { queued: 0, active: 0 };
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Format token count for display (e.g., 1.2M, 500K)
356
+ */
357
+ function formatTokens(tokens) {
358
+ if (tokens >= 1000000) {
359
+ return `${(tokens / 1000000).toFixed(1)}M`;
360
+ } else if (tokens >= 1000) {
361
+ return `${(tokens / 1000).toFixed(0)}K`;
362
+ }
363
+ return `${tokens}`;
364
+ }
365
+
366
+ /**
367
+ * Format hours as human readable (e.g., "2h", "3d")
368
+ */
369
+ function formatHours(hours) {
370
+ if (hours >= 24) {
371
+ const days = Math.round(hours / 24);
372
+ return `${days}d`;
373
+ }
374
+ return `${Math.round(hours)}h`;
375
+ }
376
+
377
+ /**
378
+ * Build a simple text progress bar
379
+ */
380
+ function progressBar(percent, width = 10) {
381
+ const filled = Math.round((percent / 100) * width);
382
+ const empty = width - filled;
383
+ return '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
384
+ }
385
+
386
+ /**
387
+ * Get aggregate quota from api-key-rotation.json (active keys only)
388
+ * Groups keys by account_uuid to deduplicate same-account tokens.
389
+ * Returns { activeCount, fiveHourPct, sevenDayPct, accounts } or null
390
+ * Percentages are of total capacity (average across unique accounts)
391
+ */
392
+ function getAggregateQuota() {
393
+ if (!fs.existsSync(KEY_ROTATION_STATE_PATH)) {
394
+ return null;
395
+ }
396
+
397
+ try {
398
+ const state = JSON.parse(fs.readFileSync(KEY_ROTATION_STATE_PATH, 'utf8'));
399
+ if (!state || state.version !== 1 || !state.keys) {
400
+ return null;
401
+ }
402
+
403
+ // Group by account_uuid to deduplicate same-account tokens
404
+ const accountMap = new Map();
405
+ const activeKey = state.active_key_id ? state.keys[state.active_key_id] : null;
406
+ const activeAccountUuid = activeKey?.account_uuid || null;
407
+
408
+ for (const [keyId, keyData] of Object.entries(state.keys)) {
409
+ if ((keyData.status === 'active' || keyData.status === 'exhausted') && keyData.last_usage) {
410
+ const dedupeKey = keyData.account_uuid || `fp:${keyData.last_usage.seven_day}:${keyData.last_usage.seven_day_sonnet}`;
411
+ const isActiveKey = keyId === state.active_key_id;
412
+ if (!accountMap.has(dedupeKey)) {
413
+ accountMap.set(dedupeKey, {
414
+ email: keyData.account_email || null,
415
+ fiveHour: keyData.last_usage.five_hour || 0,
416
+ sevenDay: keyData.last_usage.seven_day || 0,
417
+ sevenDaySonnet: keyData.last_usage.seven_day_sonnet || 0,
418
+ isActive: isActiveKey || (activeAccountUuid && keyData.account_uuid === activeAccountUuid),
419
+ });
420
+ } else if (isActiveKey) {
421
+ accountMap.get(dedupeKey).isActive = true;
422
+ }
423
+ }
424
+ }
425
+
426
+ // Cross-match fingerprint-based entries (null-UUID keys) against UUID-bearing entries.
427
+ // If a null-UUID key has the same seven_day + seven_day_sonnet as a UUID-bearing key,
428
+ // it's the same account — merge it to prevent "unknown" in the display.
429
+ const fpKeys = [...accountMap.keys()].filter(k => k.startsWith('fp:'));
430
+ for (const fpKey of fpKeys) {
431
+ const fpEntry = accountMap.get(fpKey);
432
+ for (const [uuidKey, uuidEntry] of accountMap) {
433
+ if (uuidKey.startsWith('fp:') || !uuidEntry.email) continue;
434
+ if (uuidEntry.sevenDay === fpEntry.sevenDay && uuidEntry.sevenDaySonnet === fpEntry.sevenDaySonnet) {
435
+ // Same account — take the higher fiveHour value and transfer isActive
436
+ uuidEntry.fiveHour = Math.max(uuidEntry.fiveHour, fpEntry.fiveHour);
437
+ if (fpEntry.isActive) uuidEntry.isActive = true;
438
+ accountMap.delete(fpKey);
439
+ break;
440
+ }
441
+ }
442
+ }
443
+
444
+ if (accountMap.size === 0) {
445
+ return null;
446
+ }
447
+
448
+ const accounts = Array.from(accountMap.values());
449
+ const fiveHourSum = accounts.reduce((sum, a) => sum + a.fiveHour, 0);
450
+ const sevenDaySum = accounts.reduce((sum, a) => sum + a.sevenDay, 0);
451
+
452
+ return {
453
+ activeCount: accounts.length,
454
+ fiveHourPct: Math.round(fiveHourSum / accounts.length),
455
+ sevenDayPct: Math.round(sevenDaySum / accounts.length),
456
+ accounts,
457
+ };
458
+ } catch {
459
+ return null;
460
+ }
461
+ }
462
+
463
+ /**
464
+ * Fetch quota status from Anthropic API
465
+ */
466
+ async function getQuotaStatus() {
467
+ const emptyStatus = { five_hour: null, seven_day: null, error: null };
468
+
469
+ if (!fs.existsSync(CREDENTIALS_PATH)) {
470
+ return { ...emptyStatus, error: 'no-creds' };
471
+ }
472
+
473
+ try {
474
+ const creds = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf8'));
475
+ const token = creds.claudeAiOauth?.accessToken;
476
+ if (!token) {
477
+ return { ...emptyStatus, error: 'no-token' };
478
+ }
479
+
480
+ const response = await fetch(ANTHROPIC_API_URL, {
481
+ method: 'GET',
482
+ headers: {
483
+ 'Authorization': `Bearer ${token}`,
484
+ 'Content-Type': 'application/json',
485
+ 'User-Agent': 'claude-code/2.1.14',
486
+ 'anthropic-beta': ANTHROPIC_BETA_HEADER,
487
+ },
488
+ });
489
+
490
+ if (!response.ok) {
491
+ return { ...emptyStatus, error: `api-${response.status}` };
492
+ }
493
+
494
+ const data = await response.json();
495
+
496
+ const parseReset = (isoDate) => {
497
+ const resetTime = new Date(isoDate).getTime();
498
+ const hours = (resetTime - Date.now()) / (1000 * 60 * 60);
499
+ return Math.max(0, hours);
500
+ };
501
+
502
+ return {
503
+ five_hour: data.five_hour ? {
504
+ utilization: data.five_hour.utilization,
505
+ resets_in_hours: parseReset(data.five_hour.resets_at),
506
+ } : null,
507
+ seven_day: data.seven_day ? {
508
+ utilization: data.seven_day.utilization,
509
+ resets_in_hours: parseReset(data.seven_day.resets_at),
510
+ } : null,
511
+ error: null,
512
+ };
513
+ } catch (err) {
514
+ return { ...emptyStatus, error: err.message };
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Main entry point
520
+ */
521
+ async function main() {
522
+ // Skip for spawned sessions
523
+ if (process.env.CLAUDE_SPAWNED_SESSION === 'true') {
524
+ console.log(JSON.stringify({
525
+ continue: true,
526
+ suppressOutput: true,
527
+ }));
528
+ return;
529
+ }
530
+
531
+ // Skip for slash commands (contain GENTYR sentinel markers or raw /command-name)
532
+ try {
533
+ const stdin = fs.readFileSync('/dev/stdin', 'utf-8');
534
+ let prompt = stdin;
535
+ try {
536
+ const parsed = JSON.parse(stdin);
537
+ if (typeof parsed.prompt === 'string') prompt = parsed.prompt;
538
+ } catch {
539
+ // Not JSON — use raw stdin
540
+ }
541
+ if (prompt.includes('<!-- HOOK:GENTYR:') || /^\/[\w-]+$/.test(prompt.trim())) {
542
+ console.log(JSON.stringify({ continue: true, suppressOutput: true }));
543
+ return;
544
+ }
545
+ } catch {
546
+ // No stdin available — continue normally
547
+ }
548
+
549
+ // Gather all metrics (quota is async, session metrics use incremental cache)
550
+ const sessionMetricsCached = getSessionMetricsCached();
551
+ const aggregateQuota = getAggregateQuota();
552
+ const [quota, deputyCto, unreadReports, autonomousMode, todoCounts] = await Promise.all([
553
+ getQuotaStatus(),
554
+ Promise.resolve(getDeputyCtoCounts()),
555
+ Promise.resolve(getUnreadReportsCount()),
556
+ Promise.resolve(getAutonomousModeStatus()),
557
+ Promise.resolve(getTodoCounts()),
558
+ ]);
559
+ const tokenUsage = sessionMetricsCached.tokens;
560
+ const sessionMetrics = { task: sessionMetricsCached.taskSessions, user: sessionMetricsCached.userSessions };
561
+
562
+ // Check if commits are blocked
563
+ const isCritical = deputyCto.rejections > 0;
564
+
565
+ // Build autonomous status part
566
+ let autonomousPart = '';
567
+ if (autonomousMode.enabled) {
568
+ if (autonomousMode.nextRunMinutes === null) {
569
+ autonomousPart = 'Deputy: ON';
570
+ } else if (autonomousMode.nextRunMinutes === 0) {
571
+ autonomousPart = 'Deputy: ON (ready)';
572
+ } else {
573
+ autonomousPart = `Deputy: ON (${autonomousMode.nextRunMinutes}min)`;
574
+ }
575
+ } else {
576
+ autonomousPart = 'Deputy: OFF';
577
+ }
578
+
579
+ // Build quota status part (compact for critical mode)
580
+ let quotaPart = '';
581
+ if (aggregateQuota && aggregateQuota.activeCount > 1) {
582
+ // Compact aggregate display for critical mode (% of total capacity)
583
+ quotaPart = `Quota (${aggregateQuota.activeCount} accounts): 5h ${aggregateQuota.fiveHourPct}% 7d ${aggregateQuota.sevenDayPct}%`;
584
+ } else if (!quota.error && quota.five_hour && quota.seven_day) {
585
+ // Single key display
586
+ const fiveHour = `5h: ${Math.round(quota.five_hour.utilization)}%`;
587
+ const sevenDay = `7d: ${Math.round(quota.seven_day.utilization)}%`;
588
+ quotaPart = `Quota ${fiveHour} ${sevenDay}`;
589
+ }
590
+
591
+ // Build message based on state
592
+ let message;
593
+ if (isCritical) {
594
+ // Critical blocking mode - compact format
595
+ const parts = [];
596
+ parts.push(`${deputyCto.rejections} rejection(s)`);
597
+ if (quotaPart) parts.push(quotaPart);
598
+ parts.push(`${formatTokens(tokenUsage)} tokens`);
599
+ parts.push(autonomousPart);
600
+ message = `COMMITS BLOCKED: ${parts.join(' | ')}. Use /deputy-cto to address.`;
601
+ } else {
602
+ // Normal CTO report format - multi-line for readability
603
+ const lines = [];
604
+
605
+ // Line 1: Quota status - use aggregate if available, otherwise single-key
606
+ if (aggregateQuota && aggregateQuota.activeCount > 1) {
607
+ // Multi-account aggregate display (% of total capacity)
608
+ const fhBar = progressBar(aggregateQuota.fiveHourPct, 8);
609
+ const sdBar = progressBar(aggregateQuota.sevenDayPct, 8);
610
+ lines.push(`Quota (${aggregateQuota.activeCount} accounts): 5h ${fhBar} ${aggregateQuota.fiveHourPct}% | 7d ${sdBar} ${aggregateQuota.sevenDayPct}%`);
611
+
612
+ // Accounts line with emails and per-account 5h usage
613
+ if (aggregateQuota.accounts) {
614
+ const accountParts = aggregateQuota.accounts.map(a => {
615
+ const label = a.email || 'unknown';
616
+ const display = a.isActive ? `[${label}]` : label;
617
+ return `${display} (${Math.round(a.fiveHour)}% 5h)`;
618
+ });
619
+ lines.push(`Accounts: ${accountParts.join(' | ')}`);
620
+ }
621
+ } else if (quota.five_hour && quota.seven_day) {
622
+ // Single-key display with reset times
623
+ const fh = quota.five_hour;
624
+ const sd = quota.seven_day;
625
+ lines.push(`Quota: 5-hour ${progressBar(fh.utilization, 8)} ${Math.round(fh.utilization)}% (resets ${formatHours(fh.resets_in_hours)}) | 7-day ${progressBar(sd.utilization, 8)} ${Math.round(sd.utilization)}% (resets ${formatHours(sd.resets_in_hours)})`);
626
+ }
627
+
628
+ // Line 2: Token usage, sessions, and TODOs
629
+ const todosPart = todoCounts.active > 0
630
+ ? `TODOs: ${todoCounts.queued} queued, ${todoCounts.active} active`
631
+ : `TODOs: ${todoCounts.queued} queued`;
632
+ lines.push(`Usage (30d): ${formatTokens(tokenUsage)} tokens | ${sessionMetrics.task} task / ${sessionMetrics.user} user sessions | ${todosPart} | ${autonomousPart}`);
633
+
634
+ // Line 3: Pending items (if any)
635
+ const ctoPending = [];
636
+ if (deputyCto.pending > 0) {
637
+ ctoPending.push(`${deputyCto.pending} CTO decision(s)`);
638
+ }
639
+ if (unreadReports > 0) {
640
+ ctoPending.push(`${unreadReports} unread report(s)`);
641
+ }
642
+ if (ctoPending.length > 0) {
643
+ lines.push(`Pending: ${ctoPending.join(', ')}`);
644
+ }
645
+
646
+ message = lines.join('\n');
647
+ }
648
+
649
+ console.log(JSON.stringify({
650
+ continue: true,
651
+ suppressOutput: false,
652
+ systemMessage: message,
653
+ }));
654
+ }
655
+
656
+ main();