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,205 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stale Work Detector for GENTYR Framework
5
+ *
6
+ * Detects stale work that may need attention:
7
+ * 1. Uncommitted changes (git status --porcelain)
8
+ * 2. Unpushed commits on local branches
9
+ * 3. Stale remote feature branches with no recent PR activity
10
+ *
11
+ * Returns a structured report for deputy-CTO briefing.
12
+ *
13
+ * @version 1.0.0
14
+ */
15
+
16
+ import { execSync } from 'child_process';
17
+
18
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
19
+ const DEFAULT_STALE_DAYS = 3;
20
+
21
+ const GIT_EXEC_OPTIONS = { cwd: PROJECT_DIR, encoding: 'utf8', timeout: 15000, stdio: 'pipe' };
22
+
23
+ /**
24
+ * Detect uncommitted changes via git status --porcelain.
25
+ * @returns {string[]} List of uncommitted file status lines
26
+ */
27
+ function getUncommittedFiles() {
28
+ const status = execSync('git status --porcelain', { cwd: PROJECT_DIR, encoding: 'utf8', timeout: 10000, stdio: 'pipe' }).trim();
29
+ return status ? status.split('\n').map(l => l.trim()) : [];
30
+ }
31
+
32
+ /**
33
+ * Detect local branches with commits not pushed to their remote tracking branch.
34
+ * @returns {Array<{ branch: string, commitCount: number }>}
35
+ */
36
+ function getUnpushedBranches() {
37
+ const branchOutput = execSync("git branch --format='%(refname:short)'", GIT_EXEC_OPTIONS).trim();
38
+ if (!branchOutput) return [];
39
+
40
+ const branches = branchOutput.split('\n').map(b => b.trim().replace(/^'|'$/g, '')).filter(Boolean);
41
+ const unpushed = [];
42
+
43
+ for (const branch of branches) {
44
+ try {
45
+ const logOutput = execSync(`git log origin/${branch}..${branch} --oneline`, GIT_EXEC_OPTIONS).trim();
46
+ if (logOutput) {
47
+ const commitCount = logOutput.split('\n').length;
48
+ unpushed.push({ branch, commitCount });
49
+ }
50
+ } catch {
51
+ // Branch may not have a remote tracking branch - skip it
52
+ }
53
+ }
54
+
55
+ return unpushed;
56
+ }
57
+
58
+ /**
59
+ * Check if a branch has a PR and whether that PR is also stale.
60
+ * @param {string} branchName - Branch name without origin/ prefix
61
+ * @param {number} staleDays - Number of days to consider stale
62
+ * @returns {{ hasPR: boolean, prStale: boolean }}
63
+ */
64
+ function checkPRStatus(branchName, staleDays) {
65
+ try {
66
+ const prJson = execSync(
67
+ `gh pr list --head ${branchName} --state all --json updatedAt --limit 1`,
68
+ GIT_EXEC_OPTIONS
69
+ ).trim();
70
+ const prs = JSON.parse(prJson);
71
+
72
+ if (!prs || prs.length === 0) {
73
+ return { hasPR: false, prStale: false };
74
+ }
75
+
76
+ const prUpdatedAt = new Date(prs[0].updatedAt);
77
+ const prAgeDays = (Date.now() - prUpdatedAt.getTime()) / (1000 * 86400);
78
+ return { hasPR: true, prStale: prAgeDays > staleDays };
79
+ } catch {
80
+ // gh CLI may not be available or authenticated
81
+ return { hasPR: false, prStale: false };
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Detect remote feature branches that have gone stale.
87
+ * @param {number} staleDays - Number of days without activity to consider stale
88
+ * @returns {Array<{ branch: string, lastCommitAge: number, lastCommitDate: string, hasPR: boolean }>}
89
+ */
90
+ function getStaleBranches(staleDays) {
91
+ let branchOutput;
92
+ try {
93
+ branchOutput = execSync("git branch -r --list 'origin/feature/*'", GIT_EXEC_OPTIONS).trim();
94
+ } catch {
95
+ return [];
96
+ }
97
+
98
+ if (!branchOutput) return [];
99
+
100
+ const remoteBranches = branchOutput.split('\n').map(b => b.trim()).filter(Boolean);
101
+ const staleBranches = [];
102
+
103
+ for (const remoteBranch of remoteBranches) {
104
+ try {
105
+ const timestampStr = execSync(`git log -1 --format=%ct ${remoteBranch}`, GIT_EXEC_OPTIONS).trim();
106
+ const timestamp = parseInt(timestampStr, 10);
107
+
108
+ if (isNaN(timestamp)) continue;
109
+
110
+ const ageDays = (Date.now() / 1000 - timestamp) / 86400;
111
+
112
+ if (ageDays > staleDays) {
113
+ const branchWithoutOrigin = remoteBranch.replace(/^origin\//, '');
114
+ const { hasPR, prStale } = checkPRStatus(branchWithoutOrigin, staleDays);
115
+
116
+ // Only report as stale if there's no PR or the PR itself is stale
117
+ if (!hasPR || prStale) {
118
+ const lastCommitDate = new Date(timestamp * 1000).toISOString();
119
+ staleBranches.push({
120
+ branch: branchWithoutOrigin,
121
+ lastCommitAge: Math.round(ageDays),
122
+ lastCommitDate,
123
+ hasPR
124
+ });
125
+ }
126
+ }
127
+ } catch {
128
+ // Skip branches we can't inspect
129
+ }
130
+ }
131
+
132
+ return staleBranches;
133
+ }
134
+
135
+ /**
136
+ * Detect all stale work in the project.
137
+ * @param {object} options
138
+ * @param {number} [options.staleDays=3] - Number of days to consider a branch stale
139
+ * @returns {{ uncommittedFiles: string[], unpushedBranches: Array<{ branch: string, commitCount: number }>, staleBranches: Array<{ branch: string, lastCommitAge: number, lastCommitDate: string, hasPR: boolean }>, hasIssues: boolean, timestamp: string }}
140
+ */
141
+ function detectStaleWork(options = {}) {
142
+ const staleDays = options.staleDays ?? DEFAULT_STALE_DAYS;
143
+
144
+ const uncommittedFiles = getUncommittedFiles();
145
+ const unpushedBranches = getUnpushedBranches();
146
+ const staleBranches = getStaleBranches(staleDays);
147
+
148
+ const hasIssues = uncommittedFiles.length > 0 || unpushedBranches.length > 0 || staleBranches.length > 0;
149
+
150
+ return {
151
+ uncommittedFiles,
152
+ unpushedBranches,
153
+ staleBranches,
154
+ hasIssues,
155
+ timestamp: new Date().toISOString()
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Format a stale work report into a human-readable string for deputy-CTO briefing.
161
+ * @param {{ uncommittedFiles: string[], unpushedBranches: Array<{ branch: string, commitCount: number }>, staleBranches: Array<{ branch: string, lastCommitAge: number, lastCommitDate: string, hasPR: boolean }>, hasIssues: boolean, timestamp: string }} report
162
+ * @returns {string}
163
+ */
164
+ function formatReport(report) {
165
+ const lines = ['## Stale Work Report', ''];
166
+
167
+ if (report.uncommittedFiles.length > 0) {
168
+ lines.push('### Uncommitted Changes');
169
+ for (const file of report.uncommittedFiles) {
170
+ lines.push(`- ${file}`);
171
+ }
172
+ lines.push('');
173
+ }
174
+
175
+ if (report.unpushedBranches.length > 0) {
176
+ lines.push('### Unpushed Branches');
177
+ for (const { branch, commitCount } of report.unpushedBranches) {
178
+ lines.push(`- ${branch} (${commitCount} commit${commitCount === 1 ? '' : 's'} ahead)`);
179
+ }
180
+ lines.push('');
181
+ }
182
+
183
+ if (report.staleBranches.length > 0) {
184
+ lines.push(`### Stale Feature Branches (>${DEFAULT_STALE_DAYS} days)`);
185
+ for (const { branch, lastCommitAge, hasPR } of report.staleBranches) {
186
+ const prStatus = hasPR ? 'PR stale' : 'no PR';
187
+ lines.push(`- ${branch} (${lastCommitAge} day${lastCommitAge === 1 ? '' : 's'}, ${prStatus})`);
188
+ }
189
+ lines.push('');
190
+ }
191
+
192
+ if (!report.hasIssues) {
193
+ lines.push('No stale work detected.');
194
+ lines.push('');
195
+ }
196
+
197
+ lines.push('---');
198
+ lines.push(`Generated: ${report.timestamp}`);
199
+
200
+ return lines.join('\n');
201
+ }
202
+
203
+ export { detectStaleWork, formatReport, DEFAULT_STALE_DAYS };
204
+
205
+ export default { detectStaleWork, formatReport, DEFAULT_STALE_DAYS };
@@ -0,0 +1,414 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stop Hook - Auto-continue for automated [Task] sessions + quota death detection
5
+ *
6
+ * This hook forces one continuation cycle for spawned sessions that begin with "[Task]".
7
+ * It checks:
8
+ * 1. Was the initial prompt tagged with "[Task]"? (automated session)
9
+ * 2. Is the session dying from a quota/rate limit? (detect and record for revival)
10
+ * 3. Is stop_hook_active false? (first stop, not already continuing)
11
+ *
12
+ * Quota detection: If the last JSONL entries show error:"rate_limit" + isApiErrorMessage,
13
+ * the hook writes recovery state and approves the stop immediately (instead of wasting
14
+ * the one remaining API call on a doomed retry). The session-reviver picks up later.
15
+ *
16
+ * It also attempts credential rotation when quota is hit, so the next retry (if any)
17
+ * or the revived session will use fresh credentials.
18
+ */
19
+
20
+ import { createInterface } from 'readline';
21
+ import fs from 'fs';
22
+ import path from 'path';
23
+ import {
24
+ readRotationState,
25
+ writeRotationState,
26
+ logRotationEvent,
27
+ updateActiveCredentials,
28
+ checkKeyHealth,
29
+ selectActiveKey,
30
+ refreshExpiredToken,
31
+ } from './key-sync.js';
32
+
33
+ // Debug logging - writes to file since stdout is used for hook response
34
+ const DEBUG = true;
35
+ const DEBUG_LOG_PATH = path.join(process.cwd(), '.claude', 'hooks', 'stop-hook-debug.log');
36
+
37
+ function debugLog(message, data = null) {
38
+ if (!DEBUG) return;
39
+ const timestamp = new Date().toISOString();
40
+ let logLine = `[${timestamp}] ${message}`;
41
+ if (data !== null) {
42
+ logLine += '\n' + JSON.stringify(data, null, 2);
43
+ }
44
+ logLine += '\n---\n';
45
+ try {
46
+ fs.appendFileSync(DEBUG_LOG_PATH, logLine);
47
+ } catch (err) {
48
+ // Ignore write errors
49
+ }
50
+ }
51
+
52
+ async function readStdin() {
53
+ return new Promise((resolve) => {
54
+ let data = '';
55
+ const rl = createInterface({ input: process.stdin });
56
+ rl.on('line', (line) => { data += line; });
57
+ rl.on('close', () => { resolve(data); });
58
+ // Timeout after 100ms if no data
59
+ setTimeout(() => { rl.close(); resolve(data); }, 100);
60
+ });
61
+ }
62
+
63
+ const STATE_DIR = path.join(process.cwd(), '.claude', 'state');
64
+ const QUOTA_INTERRUPTED_PATH = path.join(STATE_DIR, 'quota-interrupted-sessions.json');
65
+ const TAIL_BYTES = 8192;
66
+
67
+ /**
68
+ * Read the last N bytes of a file (seek to end, no full-file read).
69
+ */
70
+ function readTail(filePath, numBytes) {
71
+ let fd;
72
+ try {
73
+ fd = fs.openSync(filePath, 'r');
74
+ const stat = fs.fstatSync(fd);
75
+ const start = Math.max(0, stat.size - numBytes);
76
+ const buf = Buffer.alloc(Math.min(numBytes, stat.size));
77
+ const bytesRead = fs.readSync(fd, buf, 0, buf.length, start);
78
+ return buf.toString('utf8', 0, bytesRead);
79
+ } catch {
80
+ return '';
81
+ } finally {
82
+ if (fd !== undefined) fs.closeSync(fd);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Check if the session is dying from a quota/rate limit by examining recent JSONL entries.
88
+ * Returns { isQuotaDeath: boolean, quotaMessage?: string }
89
+ */
90
+ function detectQuotaDeath(transcriptPath) {
91
+ if (!transcriptPath) return { isQuotaDeath: false };
92
+
93
+ const tail = readTail(transcriptPath, TAIL_BYTES);
94
+ if (!tail) return { isQuotaDeath: false };
95
+
96
+ const lines = tail.split('\n').filter(l => l.trim());
97
+
98
+ // Check last 5 parseable entries for rate_limit error
99
+ let checked = 0;
100
+ for (let i = lines.length - 1; i >= 0 && checked < 5; i--) {
101
+ let parsed;
102
+ try {
103
+ parsed = JSON.parse(lines[i]);
104
+ } catch {
105
+ continue;
106
+ }
107
+ checked++;
108
+
109
+ if (parsed.error === 'rate_limit' && parsed.isApiErrorMessage === true) {
110
+ const quotaMessage = parsed.message?.content?.[0]?.text || 'Rate limit reached';
111
+ return { isQuotaDeath: true, quotaMessage };
112
+ }
113
+ }
114
+
115
+ return { isQuotaDeath: false };
116
+ }
117
+
118
+ /**
119
+ * Extract agent ID from the first user message in transcript.
120
+ */
121
+ function extractAgentId(transcriptPath) {
122
+ if (!transcriptPath) return null;
123
+ try {
124
+ // Read first 4KB for the initial prompt
125
+ let fd;
126
+ try {
127
+ fd = fs.openSync(transcriptPath, 'r');
128
+ const buf = Buffer.alloc(4096);
129
+ const bytesRead = fs.readSync(fd, buf, 0, 4096, 0);
130
+ const head = buf.toString('utf8', 0, bytesRead);
131
+ const match = head.match(/\[AGENT:(agent-[^\]]+)\]/);
132
+ return match ? match[1] : null;
133
+ } finally {
134
+ if (fd !== undefined) fs.closeSync(fd);
135
+ }
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Write a quota-interrupted session record for the session-reviver to pick up.
143
+ */
144
+ function writeQuotaInterruptedSession(record) {
145
+ try {
146
+ if (!fs.existsSync(STATE_DIR)) {
147
+ fs.mkdirSync(STATE_DIR, { recursive: true });
148
+ }
149
+
150
+ let data = { sessions: [] };
151
+ if (fs.existsSync(QUOTA_INTERRUPTED_PATH)) {
152
+ data = JSON.parse(fs.readFileSync(QUOTA_INTERRUPTED_PATH, 'utf8'));
153
+ if (!Array.isArray(data.sessions)) data.sessions = [];
154
+ }
155
+
156
+ // Don't duplicate
157
+ const existingIdx = data.sessions.findIndex(
158
+ s => s.transcriptPath === record.transcriptPath
159
+ );
160
+ if (existingIdx >= 0) {
161
+ data.sessions[existingIdx] = record;
162
+ } else {
163
+ data.sessions.push(record);
164
+ }
165
+
166
+ // Clean up entries older than 30 minutes (matches session-reviver reader window)
167
+ const cutoff = Date.now() - 30 * 60 * 1000;
168
+ data.sessions = data.sessions.filter(s => new Date(s.interruptedAt).getTime() > cutoff);
169
+
170
+ fs.writeFileSync(QUOTA_INTERRUPTED_PATH, JSON.stringify(data, null, 2), 'utf8');
171
+ } catch {
172
+ // Non-fatal
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Attempt credential rotation when quota is hit.
178
+ * Returns true if rotation succeeded (a usable key was found and swapped).
179
+ */
180
+ async function attemptQuotaRotation() {
181
+ try {
182
+ const state = readRotationState();
183
+ if (!state.active_key_id) return false;
184
+
185
+ // Refresh expired tokens before health-check so they can be candidates
186
+ for (const [keyId, keyData] of Object.entries(state.keys)) {
187
+ if (keyData.status === 'expired' && keyData.expiresAt && keyData.expiresAt < Date.now()) {
188
+ try {
189
+ const refreshed = await refreshExpiredToken(keyData);
190
+ if (refreshed === 'invalid_grant') {
191
+ keyData.status = 'invalid';
192
+ logRotationEvent(state, {
193
+ timestamp: Date.now(),
194
+ event: 'key_removed',
195
+ key_id: keyId,
196
+ reason: 'refresh_token_invalid_grant',
197
+ });
198
+ debugLog(`Refresh token revoked for key ${keyId.slice(0, 8)}... — marked invalid`);
199
+ } else if (refreshed) {
200
+ keyData.accessToken = refreshed.accessToken;
201
+ keyData.refreshToken = refreshed.refreshToken;
202
+ keyData.expiresAt = refreshed.expiresAt;
203
+ keyData.status = 'active';
204
+ logRotationEvent(state, {
205
+ timestamp: Date.now(),
206
+ event: 'key_added',
207
+ key_id: keyId,
208
+ reason: 'token_refreshed_by_stop_hook',
209
+ });
210
+ debugLog(`Refreshed expired token for key ${keyId.slice(0, 8)}...`);
211
+ }
212
+ } catch {
213
+ // Non-fatal: key stays expired
214
+ }
215
+ }
216
+ }
217
+ writeRotationState(state);
218
+
219
+ // Health-check all keys to get fresh usage data
220
+ const healthPromises = Object.entries(state.keys)
221
+ .filter(([_, k]) => k.status !== 'invalid' && k.status !== 'expired')
222
+ .map(async ([keyId, keyData]) => {
223
+ const result = await checkKeyHealth(keyData.accessToken);
224
+ if (result.valid && result.usage) {
225
+ keyData.last_health_check = Date.now();
226
+ keyData.last_usage = { ...result.usage, checked_at: Date.now() };
227
+ }
228
+ return { keyId, result };
229
+ });
230
+
231
+ await Promise.all(healthPromises);
232
+ writeRotationState(state);
233
+
234
+ const selectedKeyId = selectActiveKey(state);
235
+ if (selectedKeyId && selectedKeyId !== state.active_key_id) {
236
+ const previousKeyId = state.active_key_id;
237
+ state.active_key_id = selectedKeyId;
238
+ const selectedKey = state.keys[selectedKeyId];
239
+ selectedKey.last_used_at = Date.now();
240
+
241
+ logRotationEvent(state, {
242
+ timestamp: Date.now(),
243
+ event: 'key_switched',
244
+ key_id: selectedKeyId,
245
+ reason: `stop_hook_quota_rotation_from_${previousKeyId.slice(0, 8)}`,
246
+ });
247
+
248
+ updateActiveCredentials(selectedKey);
249
+ writeRotationState(state);
250
+ debugLog('Quota rotation succeeded', { from: previousKeyId.slice(0, 8), to: selectedKeyId.slice(0, 8) });
251
+ return true;
252
+ }
253
+
254
+ return false;
255
+ } catch (err) {
256
+ debugLog('Quota rotation error', { error: err.message });
257
+ return false;
258
+ }
259
+ }
260
+
261
+ async function main() {
262
+ debugLog('Stop hook triggered');
263
+
264
+ try {
265
+ const stdinData = await readStdin();
266
+
267
+ debugLog('Raw stdin data', stdinData ? stdinData.substring(0, 2000) : '(empty)');
268
+
269
+ if (!stdinData) {
270
+ // No input, allow stop
271
+ debugLog('No stdin data, allowing stop');
272
+ console.log(JSON.stringify({ decision: 'approve' }));
273
+ process.exit(0);
274
+ }
275
+
276
+ const input = JSON.parse(stdinData);
277
+
278
+ debugLog('Parsed input keys', Object.keys(input));
279
+ debugLog('Full input structure', input);
280
+
281
+ // Check if this is an automated [Task] session
282
+ // The initial prompt should be in the conversation history
283
+ const isTaskSession = checkIfTaskSession(input);
284
+
285
+ // Check for quota death BEFORE continuation logic
286
+ // If this is a rate_limit death, don't waste the API call on a continuation
287
+ if (isTaskSession) {
288
+ const quotaCheck = detectQuotaDeath(input.transcript_path);
289
+ if (quotaCheck.isQuotaDeath) {
290
+ debugLog('Quota death detected', { quotaMessage: quotaCheck.quotaMessage });
291
+
292
+ // Attempt credential rotation for the next session/revival
293
+ const rotated = await attemptQuotaRotation();
294
+
295
+ // Write recovery state for session-reviver
296
+ const agentId = extractAgentId(input.transcript_path);
297
+ writeQuotaInterruptedSession({
298
+ sessionId: input.session_id || null,
299
+ transcriptPath: input.transcript_path,
300
+ agentId,
301
+ quotaMessage: quotaCheck.quotaMessage,
302
+ interruptedAt: new Date().toISOString(),
303
+ credentialsRotated: rotated,
304
+ status: 'pending_revival',
305
+ });
306
+
307
+ debugLog('Decision: APPROVE (quota death - recorded for revival)', {
308
+ agentId,
309
+ rotated,
310
+ quotaMessage: quotaCheck.quotaMessage,
311
+ });
312
+
313
+ console.log(JSON.stringify({ decision: 'approve' }));
314
+ process.exit(0);
315
+ }
316
+ }
317
+
318
+ // Check if we're already in a continuation cycle
319
+ const alreadyContinuing = input.stop_hook_active === true;
320
+
321
+ debugLog('Decision factors', {
322
+ isTaskSession,
323
+ alreadyContinuing,
324
+ stop_hook_active: input.stop_hook_active,
325
+ CLAUDE_SPAWNED_SESSION: process.env.CLAUDE_SPAWNED_SESSION
326
+ });
327
+
328
+ if (isTaskSession && !alreadyContinuing) {
329
+ // First stop of a [Task] session - force one continuation
330
+ debugLog('Decision: BLOCK (first stop of [Task] session)');
331
+ console.log(JSON.stringify({
332
+ decision: 'block',
333
+ reason: 'If there is more work to investigate or resolve related to the initial [Task] request, continue working. Otherwise, you may stop.'
334
+ }));
335
+ } else {
336
+ // Either not a [Task] session, or already continued once - allow stop
337
+ debugLog('Decision: APPROVE', { reason: isTaskSession ? 'already continued once' : 'not a [Task] session' });
338
+ console.log(JSON.stringify({ decision: 'approve' }));
339
+ }
340
+
341
+ process.exit(0);
342
+ } catch (err) {
343
+ // On error, allow stop (fail open)
344
+ debugLog('Error in hook', { error: err.message, stack: err.stack });
345
+ console.error(`Stop hook error: ${err.message}`);
346
+ console.log(JSON.stringify({ decision: 'approve' }));
347
+ process.exit(0);
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Check if this session started with a [Task] prefix
353
+ * @param {object} input - Hook input containing conversation context
354
+ * @returns {boolean}
355
+ */
356
+ function checkIfTaskSession(input) {
357
+ // The Stop hook only receives: session_id, transcript_path, cwd, permission_mode, hook_event_name, stop_hook_active
358
+ // We need to read the transcript file to find the initial prompt
359
+
360
+ // 1. Read first 4KB of transcript file to find first user message (avoid full file read)
361
+ if (input.transcript_path) {
362
+ debugLog('Reading transcript head', input.transcript_path);
363
+ try {
364
+ let fd;
365
+ let transcriptHead;
366
+ try {
367
+ fd = fs.openSync(input.transcript_path, 'r');
368
+ const buf = Buffer.alloc(4096);
369
+ const bytesRead = fs.readSync(fd, buf, 0, 4096, 0);
370
+ transcriptHead = buf.toString('utf8', 0, bytesRead);
371
+ } finally {
372
+ if (fd !== undefined) fs.closeSync(fd);
373
+ }
374
+ const lines = transcriptHead.split('\n').filter(line => line.trim());
375
+
376
+ // JSONL format - each line is a JSON object
377
+ for (const line of lines.slice(0, 10)) { // Check first 10 lines
378
+ try {
379
+ const entry = JSON.parse(line);
380
+
381
+ // Look for human/user message type
382
+ if (entry.type === 'human' || entry.type === 'user') {
383
+ const content = entry.message?.content || entry.content || '';
384
+ debugLog('Found user message', content.substring(0, 300));
385
+
386
+ if (content.startsWith('[Task]')) {
387
+ debugLog('[Task] found in transcript first user message');
388
+ return true;
389
+ }
390
+ // Only check first user message
391
+ break;
392
+ }
393
+ } catch (parseErr) {
394
+ // Skip malformed lines
395
+ continue;
396
+ }
397
+ }
398
+ } catch (err) {
399
+ debugLog('Error reading transcript', { error: err.message });
400
+ }
401
+ }
402
+
403
+ // 2. Fallback: Check for CLAUDE_SPAWNED_SESSION env var
404
+ // This is set by hooks when spawning background agents
405
+ if (process.env.CLAUDE_SPAWNED_SESSION === 'true') {
406
+ debugLog('[Task] detected via CLAUDE_SPAWNED_SESSION env var');
407
+ return true;
408
+ }
409
+
410
+ debugLog('No [Task] marker found');
411
+ return false;
412
+ }
413
+
414
+ main();