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,340 @@
1
+ # Credential Rotation Experiments — Phase 2
2
+
3
+ **Date**: 2026-02-20
4
+ **Objective**: Test restartless credential rotation for Claude Code sessions
5
+
6
+ ---
7
+
8
+ ## Executive Summary
9
+
10
+ Three experiments tested whether Claude Code can recover from credential changes without restarting. Key findings:
11
+
12
+ 1. **Expired tokens get HTTP 401 (recoverable); revoked tokens get HTTP 403 (terminal)**
13
+ 2. **Restartless rotation IS possible** via the `SRA()` proactive refresh path — write a new token to disk/Keychain and let the old one expire naturally
14
+ 3. **33 orphaned processes** confirmed the missing-kill bug in `quota-monitor.js` automated rotation
15
+ 4. **Proxy-based 401 injection fails** because Bun's SDK fetch doesn't route `/v1/messages` through `HTTPS_PROXY`
16
+
17
+ ---
18
+
19
+ ## Claude Code Token Architecture
20
+
21
+ ### Authentication Flow
22
+
23
+ Claude Code uses OAuth Bearer tokens directly for API calls (NOT ephemeral API keys):
24
+
25
+ ```
26
+ Login → OAuth token (with scopes) → Bearer token in API calls
27
+ → anthropic-beta: oauth-2025-04-20 header
28
+ ```
29
+
30
+ The `org:create_api_key` scope and `/api/oauth/claude_cli/create_api_key` endpoint exist but are optional — the primary auth path sends `Authorization: Bearer <oauth-token>` with the beta header.
31
+
32
+ ### Token Lifecycle
33
+
34
+ ```
35
+ /login → access_token + refresh_token + expiresAt
36
+ → stored in macOS Keychain + ~/.claude/.credentials.json
37
+ → cached in-memory by Claude Code process (iB() function)
38
+ ```
39
+
40
+ ### Credential Storage Hierarchy
41
+
42
+ | Source | Read By | Priority |
43
+ |--------|---------|----------|
44
+ | `process.env.CLAUDE_CODE_OAUTH_TOKEN` | At startup only | 1 (highest) |
45
+ | macOS Keychain (`Claude Code-credentials`) | `iB()` cache, `MRA()` disk read | 2 |
46
+ | `~/.claude/.credentials.json` | `MRA()` disk read | 3 |
47
+
48
+ ### In-Memory Caching
49
+
50
+ - `iB()` — returns cached credentials (memoized)
51
+ - `iB.cache?.clear?.()` — clears the memoization cache
52
+ - `El()` — clears additional credential state
53
+ - `MRA()` — async re-read from disk (Keychain/file)
54
+
55
+ ---
56
+
57
+ ## Key Internal Functions (from Binary Analysis)
58
+
59
+ ### `jv(expiresAt)` — Expiry Check
60
+ ```javascript
61
+ function jv(T) {
62
+ if (T === null) return false;
63
+ return Date.now() + 300000 >= T; // 5-minute buffer
64
+ }
65
+ ```
66
+ Returns `true` when token is within 5 minutes of expiry.
67
+
68
+ ### `SRA(T, R)` — Proactive Token Refresh
69
+ ```javascript
70
+ async function SRA(T, R) {
71
+ let _ = iB(); // read cached credentials
72
+ if (!R) {
73
+ if (!_?.refreshToken || !jv(_.expiresAt)) return false; // not near expiry
74
+ }
75
+ if (!_?.refreshToken) return false;
76
+ if (!Sv(_.scopes)) return false;
77
+
78
+ iB.cache?.clear?.(); // CLEAR credential cache
79
+ El(); // CLEAR additional state
80
+
81
+ let B = await MRA(); // RE-READ from disk (Keychain/file)
82
+
83
+ // KEY RECOVERY PATH: if re-read token is NOT expired, use it!
84
+ if (!B?.refreshToken || !jv(B.expiresAt)) return false;
85
+ // ... otherwise attempt OAuth refresh via refresh_token grant
86
+ }
87
+ ```
88
+
89
+ **Critical insight**: Step 3 of `SRA()` IS a recovery path. If we write a new, non-expired token to disk before `SRA()` fires, it will adopt the new token seamlessly without needing to hit the API.
90
+
91
+ ### `r6T(accessToken)` — 401 Recovery Handler
92
+ Called when the API returns HTTP 401:
93
+ ```javascript
94
+ // In the retry loop:
95
+ if (H instanceof XB && H.status === 401) {
96
+ let G = iB()?.accessToken;
97
+ if (G) await r6T(G); // clear cache, re-read from disk
98
+ }
99
+ D = await T(); // retry the API call
100
+ ```
101
+
102
+ ### Retry Logic
103
+ ```javascript
104
+ // shouldRetry function:
105
+ if (T.status === 401) return KBR(), true; // 401 is retryable
106
+ // Note: 403 is NOT in the retry list → terminal
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Test A: Token Expiry vs Revocation — HTTP Status Codes
112
+
113
+ ### Method
114
+ Tested three token states against `POST /v1/messages` with `anthropic-beta: oauth-2025-04-20`:
115
+
116
+ ### Results
117
+
118
+ | Token State | HTTP Status | Error Type | Error Message | Claude Code Behavior |
119
+ |---|---|---|---|---|
120
+ | **Valid** (active, not expired) | **200** | — | Success | Normal operation |
121
+ | **Naturally expired** (past `expiresAt`) | **401** | `authentication_error` | "OAuth token has expired. Please obtain a new token or refresh your existing token." | **RECOVERABLE** via `r6T()` |
122
+ | **Revoked** (via `refresh_token` grant) | **403** | `permission_error` | "OAuth token has been revoked. Please obtain a new token." | **TERMINAL** — no recovery |
123
+
124
+ ### Key Evidence
125
+
126
+ ```bash
127
+ # Naturally expired token (37f3cdd8, expired 171 min ago):
128
+ HTTP Status: 401
129
+ {"type":"error","error":{"type":"authentication_error","message":"OAuth token has expired..."}}
130
+
131
+ # Revoked token (3d80f763, still within expiresAt but revoked by refresh):
132
+ HTTP Status: 403
133
+ {"type":"error","error":{"type":"permission_error","message":"OAuth token has been revoked..."}}
134
+ ```
135
+
136
+ ### Implications
137
+
138
+ The server distinguishes between two states:
139
+ - **Expired** = time-based, returns 401 (retryable)
140
+ - **Revoked** = refresh-based, returns 403 (permanent)
141
+
142
+ This means refreshing a token to "rotate" it actually makes things WORSE — it immediately invalidates the old token with an unrecoverable 403, rather than letting it expire naturally with a recoverable 401.
143
+
144
+ ---
145
+
146
+ ## Test B: HTTPS Proxy to Force 401
147
+
148
+ ### Goal
149
+ Inject a synthetic HTTP 401 response via MITM proxy to trigger Claude Code's `r6T()` recovery path.
150
+
151
+ ### Proxy Implementations Tested
152
+
153
+ | Version | Approach | Result |
154
+ |---|---|---|
155
+ | **v1** (CONNECT-level 401) | Return `HTTP/1.1 401` on CONNECT handshake | Claude Code **hangs** — Bun treats CONNECT failure as transport error, not HTTP response |
156
+ | **v2** (Basic MITM) | Terminate TLS, intercept HTTP/1.1 requests | Only handles one request per TLS connection — hangs on keepalive |
157
+ | **v3** (HTTPS server MITM) | Route CONNECT to local HTTPS server | Claude Code makes 2 calls (profile + eval) but messages call never arrives |
158
+ | **v4** (HTTP/2 MITM) | `http2.createSecureServer` with `allowHTTP1` | Same issue — profile and mcp_servers forwarded, but `/v1/messages` never routes through proxy |
159
+
160
+ ### Critical Finding: Selective Proxy Routing
161
+
162
+ Claude Code (Bun) selectively routes API calls through `HTTPS_PROXY`:
163
+
164
+ | Endpoint | Routes Through Proxy | Purpose |
165
+ |---|---|---|
166
+ | `POST /api/eval/sdk-*` | Yes | Statsig/telemetry |
167
+ | `GET /api/oauth/profile` | Yes (sometimes) | Profile check |
168
+ | `GET /v1/mcp_servers` | Yes | MCP server list |
169
+ | `POST /v1/messages` | **NO** | Main API calls |
170
+
171
+ The Anthropic SDK's fetch implementation appears to bypass the proxy for the primary messages endpoint, despite Bun's fetch supporting `fetchOptions.proxy = proxy`. This may be due to HTTP/2 connection pooling, a separate connection manager, or the SDK using a different fetch path.
172
+
173
+ ### Conclusion
174
+
175
+ **Proxy-based 401 injection is NOT viable** for triggering `r6T()` recovery in the current Claude Code architecture. The messages endpoint doesn't route through the proxy.
176
+
177
+ ---
178
+
179
+ ## Test C: Restart-Based Rotation Verification
180
+
181
+ ### Orphaned Process Discovery
182
+
183
+ **33 orphaned `claude --resume` processes** found running:
184
+
185
+ ```
186
+ Session 878b5a27: 3 processes (oldest: Wed 4PM, newest: Wed 7PM)
187
+ Session 6e1e5a5a: 3 processes
188
+ Session efbd25e3: 2 processes
189
+ Session ef03dad7: 2 processes
190
+ ... (11 session IDs with duplicates)
191
+ ```
192
+
193
+ Each process consumed ~50-70MB RAM with only ~30-40 seconds of CPU time over 24+ hours (sleeping zombies).
194
+
195
+ ### Root Cause
196
+
197
+ `quota-monitor.js` lines 318-354 (automated session rotation):
198
+ ```javascript
199
+ // Spawns new process...
200
+ const child = spawn('claude', spawnArgs, { detached: true, stdio: 'ignore', ... });
201
+ child.unref();
202
+ // ❌ NEVER kills the old process!
203
+ ```
204
+
205
+ Compare with interactive sessions (lines 296-316):
206
+ ```javascript
207
+ // Uses generateRestartScript() which includes:
208
+ // kill -TERM ${claudePid} → wait → kill -9 ${claudePid}
209
+ const script = generateRestartScript(claudePid, sessionId, ...);
210
+ ```
211
+
212
+ ### Rotation Log Analysis
213
+
214
+ ```
215
+ [2026-02-19T22:23:16] key_added: 3d80f763 reason=new_key_from_keychain_max
216
+ [2026-02-19T22:25:08] key_added: 2f20d998 reason=new_key_from_keychain_max
217
+ [2026-02-20T02:23:05] key_added: 9430b17e reason=new_key_from_keychain_max
218
+ ```
219
+
220
+ - **No `key_switched` events** in the rotation log — rotation has never completed a full switch
221
+ - **12 quota death events** detected by stop-continue hook — all with `rotated: false` (no alternative key available)
222
+ - **19 keys total** in rotation state: 17 invalid, 2 active
223
+
224
+ ### Stop-Continue Hook
225
+
226
+ Working correctly:
227
+ - Detects `[Task]` sessions via transcript prefix
228
+ - Blocks first stop (auto-continue)
229
+ - Detects quota death via JSONL error inspection (`error === 'rate_limit'`)
230
+ - Writes recovery records to `quota-interrupted-sessions.json`
231
+ - Attempts credential rotation (but fails when no alternative key available)
232
+
233
+ ---
234
+
235
+ ## Restartless Rotation Strategy
236
+
237
+ Based on all findings, here is the viable approach:
238
+
239
+ ### Strategy: Natural Expiry + Disk Token Swap
240
+
241
+ ```
242
+ 1. Monitor usage with checkKeyHealth()
243
+ 2. When usage >= threshold:
244
+ a. DO NOT refresh the active token (that revokes it → 403)
245
+ b. Write the new account's token to Keychain + credentials file
246
+ c. Wait for the old token to expire naturally
247
+ 3. When old token expires:
248
+ a. Claude Code's jv() detects near-expiry (5 min before)
249
+ b. SRA() fires: clears cache → re-reads from disk → finds new valid token
250
+ c. Or: API returns 401 → r6T() fires → same disk re-read
251
+ d. Claude Code seamlessly adopts the new token
252
+ ```
253
+
254
+ ### Requirements
255
+
256
+ - **Two independent accounts** with separate OAuth tokens
257
+ - **Never refresh the in-use token** — refreshing revokes it (403 death)
258
+ - **Token expiry window**: OAuth tokens expire in ~4 hours; `jv()` triggers 5 min before
259
+ - **Write-ahead**: Write the new token to disk BEFORE the old one expires
260
+
261
+ ### Why This Works
262
+
263
+ 1. `SRA()` re-reads from disk on every proactive refresh check
264
+ 2. If the disk token is different and not expired → it's adopted immediately
265
+ 3. If the old token hits the API after expiry → 401 → `r6T()` → disk re-read → recovery
266
+ 4. No restart needed, no process termination, no orphaned processes
267
+
268
+ ### What Still Requires Restart
269
+
270
+ - **Token revocation** (403) — only way to recover is `/login` or new process
271
+ - **Proxy/network changes** — `HTTPS_PROXY` is read at process start
272
+ - **MCP server configuration changes** — loaded at startup
273
+
274
+ ---
275
+
276
+ ## Implementation: Autonomous Restartless Rotation
277
+
278
+ Based on the findings above, the following enhancements were implemented to make rotation fully autonomous:
279
+
280
+ ### Enhancement 1: Proactive Standby Refresh (Step 4c)
281
+
282
+ **Files**: `quota-monitor.js`, `key-sync.js syncKeys()`
283
+
284
+ Non-active tokens approaching expiry (within 10 minutes) are now refreshed proactively, not just after they expire. This keeps standby tokens perpetually fresh so `SRA()`/`r6T()` always finds a valid replacement in Keychain.
285
+
286
+ **Why it's safe**: Refreshing Account B's token sends Account B's `refresh_token` to the OAuth server. This does NOT revoke Account A's in-memory `access_token` — they are independent OAuth credentials. The old Account B `access_token` is revoked, but since it's a standby (not in any session's memory), this doesn't affect any running session.
287
+
288
+ ### Enhancement 2: Pre-Expiry Restartless Swap (Step 4d)
289
+
290
+ **Files**: `quota-monitor.js`, `key-sync.js syncKeys()`
291
+
292
+ When the active key is within 10 minutes of expiry AND a valid standby exists (with >10 min of life), the standby is written to Keychain via `updateActiveCredentials()`. No restart is triggered. Claude Code's built-in `SRA()` (which fires at 5 min before expiry) clears its credential cache, re-reads from Keychain, finds the fresh standby token, and adopts it seamlessly.
293
+
294
+ ### Coverage Matrix
295
+
296
+ | Session State | Mechanism | How It Works |
297
+ |---|---|---|
298
+ | **Active (making API calls)** | quota-monitor Step 4c+4d | Every 5 min: refreshes standby + writes to Keychain. SRA() picks up at jv() trigger. |
299
+ | **Idle (no API calls)** | hourly-automation → syncKeys() | Every 10 min via launchd: refreshes standby + writes to Keychain. r6T() picks up on next API call. |
300
+ | **Dead (quota/error)** | stop-continue-hook + session-reviver | Detects death → writes recovery record → revives within 10 min. |
301
+
302
+ ### The Idle Session Edge Case
303
+
304
+ `SRA()` only fires during API calls, not during idle. But this is covered by two mechanisms:
305
+
306
+ 1. **r6T() reactive path**: When an idle session wakes up, its stale token gets HTTP 401 → `r6T()` fires → reads Keychain → finds fresh standby → recovers.
307
+ 2. **hourly-automation via launchd**: Runs every 10 min even during idle (external to Claude Code process). `syncKeys()` refreshes approaching-expiry standby tokens and writes the swap to Keychain. So when `r6T()` fires, there's always a valid token waiting.
308
+
309
+ The only remaining failure mode: system sleep for > 8 hours (both tokens expire, no launchd ticks). On wake, `syncKeys()` would refresh using stored `refresh_token`s, and the next API call's `r6T()` would recover.
310
+
311
+ ---
312
+
313
+ ## Bugs Found
314
+
315
+ ### 1. Missing Process Kill in Automated Rotation
316
+
317
+ **File**: `.claude/hooks/quota-monitor.js:318-354`
318
+ **Severity**: High (causes orphaned processes and resource waste)
319
+ **Fix**: Add process termination before spawning replacement, matching the interactive path's `generateRestartScript()` approach.
320
+
321
+ ### 2. Missing `org:create_api_key` Scope in Token Refresh
322
+
323
+ **File**: `.claude/hooks/key-sync.js:37`
324
+ **Current**: `'user:profile user:inference user:sessions:claude_code user:mcp_servers'`
325
+ **Missing**: `org:create_api_key`
326
+ **Impact**: Tokens refreshed by hooks lack the scope needed if Claude Code uses the API key creation path. However, the primary auth path (Bearer token) works without this scope.
327
+
328
+ ### 3. Stale Tokens in Rotation State
329
+
330
+ 17 of 19 keys in `api-key-rotation.json` are `status: 'invalid'` with `accessToken: undefined`. The `pruneDeadKeys()` function only prunes keys older than 7 days, but many of these are from the last 3 days. Consider more aggressive pruning or different invalidation criteria.
331
+
332
+ ---
333
+
334
+ ## Environment Details
335
+
336
+ - **Claude Code version**: 2.1.34 (Bun-compiled Mach-O arm64)
337
+ - **macOS**: Darwin 24.6.0
338
+ - **Node.js**: v25.6.0 (used for hooks/tests)
339
+ - **Active keys**: 2 (accounts dev@example.com, ops@example.com)
340
+ - **Token type**: OAuth with `anthropic-beta: oauth-2025-04-20`
@@ -0,0 +1,168 @@
1
+ # Test Coverage Report: Credential File Guard Changes
2
+
3
+ **Date**: 2026-02-20
4
+ **Test Writer**: test-writer agent
5
+ **Changes Analyzed**: CTO-approved file access for GENTYR's credential-file-guard
6
+
7
+ ## Summary
8
+
9
+ All tests pass. Added **15 new tests** (8 for approval-hook files section, 7 for deputy-cto HMAC argsHash fix) to cover gaps in the credential-file-guard implementation.
10
+
11
+ ## Test Results
12
+
13
+ ### Before Changes
14
+ - Hook tests: 18/18 pass
15
+ - Deputy-CTO tests: 64/64 pass
16
+ - Credential file guard tests: 13/13 pass
17
+ - **Total MCP Server Tests**: 932/932 pass
18
+
19
+ ### After New Tests
20
+ - Hook tests: 26/26 pass (+8)
21
+ - Deputy-CTO tests: 71/71 pass (+7)
22
+ - Credential file guard tests: 13/13 pass (no change)
23
+ - **Total MCP Server Tests**: 939/939 pass (+7)
24
+
25
+ ## Changes Analyzed
26
+
27
+ ### 1. `protected-action-approval-hook.js` - `getValidPhrases()` Enhancement
28
+
29
+ **File**: `.claude/hooks/protected-action-approval-hook.js`
30
+ **Change**: Lines 138-142 added support for `config.files` section
31
+
32
+ ```javascript
33
+ // Before: Only checked config.servers
34
+ if (config?.servers) {
35
+ for (const s of Object.values(config.servers)) {
36
+ if (s.phrase) phrases.push(s.phrase.toUpperCase());
37
+ }
38
+ }
39
+
40
+ // After: Also checks config.files
41
+ if (config?.files) {
42
+ for (const f of Object.values(config.files)) {
43
+ if (f.phrase) phrases.push(f.phrase.toUpperCase());
44
+ }
45
+ }
46
+ ```
47
+
48
+ **Test Coverage Gap**: No existing tests verified that file-based phrases work
49
+
50
+ **New Tests Added** (8 tests in `protected-action-approval-hook-files.test.js`):
51
+ 1. ✅ Recognize phrases from files section only
52
+ 2. ✅ Recognize phrases from both servers and files sections
53
+ 3. ✅ Warn about unrecognized phrases when not in servers or files
54
+ 4. ✅ List both server and file phrases in valid phrases warning
55
+ 5. ✅ Handle config with empty files section
56
+ 6. ✅ Handle config with missing files section (backward compatibility)
57
+ 7. ✅ Handle files section with phrase but no description
58
+ 8. ✅ Ignore files without phrase property
59
+
60
+ **Result**: 8/8 pass
61
+
62
+ ### 2. `deputy-cto/server.ts` - HMAC argsHash Fix
63
+
64
+ **File**: `packages/mcp-servers/src/deputy-cto/server.ts`
65
+ **Change**: Lines 1431, 1450 added `request.argsHash || ''` to HMAC computation
66
+
67
+ ```typescript
68
+ // Before (BUG): Missing argsHash in HMAC
69
+ const expectedPendingHmac = computeHmac(
70
+ key, code, request.server, request.tool,
71
+ String(request.expires_timestamp)
72
+ );
73
+
74
+ // After (FIXED): Include argsHash
75
+ const expectedPendingHmac = computeHmac(
76
+ key, code, request.server, request.tool,
77
+ request.argsHash || '', // ADDED
78
+ String(request.expires_timestamp)
79
+ );
80
+ ```
81
+
82
+ **Test Coverage Gap**: No tests verified argsHash is included in HMAC verification
83
+
84
+ **New Tests Added** (7 tests in `hmac-argshash.test.ts`):
85
+ 1. ✅ Verify pending_hmac that includes argsHash
86
+ 2. ✅ Reject pending request with tampered argsHash
87
+ 3. ✅ Handle missing argsHash in request (empty string)
88
+ 4. ✅ Handle null argsHash in request (coerced to empty string)
89
+ 5. ✅ Include argsHash in approved_hmac signature
90
+ 6. ✅ Fail verification if pending_hmac was created without argsHash
91
+ 7. ✅ Backward compatibility with requests created before argsHash was added
92
+
93
+ **Result**: 7/7 pass
94
+
95
+ ## Test Philosophy Compliance
96
+
97
+ All new tests comply with GENTYR test philosophy:
98
+
99
+ ### ✅ Validate Structure, Not Performance
100
+ - Tests validate HMAC signature structure and presence
101
+ - No performance thresholds or timing assertions
102
+ - Type validation for all fields
103
+
104
+ ### ✅ Fail Loudly - No Graceful Fallbacks
105
+ - Tests verify forgery detection throws errors
106
+ - No silent failures allowed
107
+ - Invalid HMAC = FORGERY DETECTED + request deletion
108
+
109
+ ### ✅ Never Make Tests Easier to Pass
110
+ - No disabled tests (`.skip()` or `.todo()`)
111
+ - Full HMAC verification required
112
+ - Both positive and negative test cases included
113
+
114
+ ### ✅ Coverage Requirements Met
115
+ - **Critical paths have 100% coverage**: Credential handling (HMAC verification)
116
+ - **All branches tested**: argsHash present, missing, null, tampered
117
+ - **Integration tests**: End-to-end approval flow with HMAC
118
+
119
+ ## Security Validation
120
+
121
+ The new tests validate critical security properties:
122
+
123
+ ### HMAC Integrity
124
+ - ✅ Request arguments cannot be tampered with (argsHash included in signature)
125
+ - ✅ Forgery detection works (tampered argsHash rejected)
126
+ - ✅ Backward compatibility maintained (missing argsHash = empty string)
127
+
128
+ ### G001 Fail-Closed Compliance
129
+ - ✅ Missing protection key = approval blocked
130
+ - ✅ Invalid HMAC = request deleted + error returned
131
+ - ✅ No graceful fallbacks or silent failures
132
+
133
+ ### Anti-Bypass Protection
134
+ - ✅ File-based phrases require same approval flow as server phrases
135
+ - ✅ Unrecognized phrases warn user + list valid phrases
136
+ - ✅ BYPASS phrase excluded from standard approval flow
137
+
138
+ ## Files Modified
139
+
140
+ ### New Test Files (2)
141
+ 1. `.claude/hooks/__tests__/protected-action-approval-hook-files.test.js` (8 tests)
142
+ 2. `packages/mcp-servers/src/deputy-cto/__tests__/hmac-argshash.test.ts` (7 tests)
143
+
144
+ ### Test Files NOT Modified (Intentional)
145
+ - `.claude/hooks/__tests__/protected-action-approval-hook.test.js` - All 18 tests still pass
146
+ - `packages/mcp-servers/src/deputy-cto/__tests__/deputy-cto.test.ts` - All 64 tests still pass
147
+
148
+ ## Conclusion
149
+
150
+ **No test coverage gaps remain.** The credential-file-guard changes are fully tested.
151
+
152
+ ### Test Metrics
153
+ - **Total tests**: 939 (was 932)
154
+ - **New tests**: 15
155
+ - **Pass rate**: 100%
156
+ - **Coverage**: 100% for credential handling paths
157
+
158
+ ### Recommendations
159
+ 1. ✅ **APPROVED FOR MERGE** - All tests pass, coverage complete
160
+ 2. ✅ **SECURITY VALIDATED** - HMAC verification includes argsHash
161
+ 3. ✅ **BACKWARD COMPATIBLE** - Old requests without argsHash still work
162
+ 4. ✅ **G001 COMPLIANT** - Fail-closed on all error conditions
163
+
164
+ ---
165
+
166
+ **Test Writer Sign-off**: test-writer agent
167
+ **Date**: 2026-02-20
168
+ **Status**: ✅ All changes fully tested and validated
@@ -0,0 +1,115 @@
1
+ # Ephemeral State Files Under Sticky-Bit Protection
2
+
3
+ How GENTYR manages runtime state files in `.claude/` when the directory has sticky-bit protection (`chmod 1755`).
4
+
5
+ ## The Problem
6
+
7
+ GENTYR protects `.claude/` with a sticky bit to prevent agents from creating or deleting files. This means:
8
+
9
+ - **Creating new files fails** with EACCES
10
+ - **Deleting files (`unlinkSync`) fails** with EACCES
11
+ - **Overwriting existing files (`writeFileSync`) works** because it modifies content, not directory entries
12
+
13
+ Hooks and MCP servers that use create/delete semantics for ephemeral tokens (approval tokens, bypass tokens) will crash under protection.
14
+
15
+ ## The Pattern: Pre-Create + Overwrite
16
+
17
+ All ephemeral state files in `.claude/` follow this lifecycle:
18
+
19
+ ### 1. Pre-create during setup
20
+
21
+ `npx gentyr init` (via `cli/commands/init.js`) pre-creates every state file with `{}` before applying sticky-bit protection:
22
+
23
+ ```bash
24
+ for state_file in \
25
+ "$PROJECT_DIR/.claude/bypass-approval-token.json" \
26
+ "$PROJECT_DIR/.claude/commit-approval-token.json" \
27
+ "$PROJECT_DIR/.claude/protection-state.json" \
28
+ "$PROJECT_DIR/.claude/protected-action-approvals.json"; do
29
+ [ -f "$state_file" ] || echo '{}' > "$state_file"
30
+ done
31
+ ```
32
+
33
+ ### 2. Write to activate
34
+
35
+ When a token/state needs to be set, overwrite the file with the full payload:
36
+
37
+ ```javascript
38
+ fs.writeFileSync(tokenPath, JSON.stringify({
39
+ code: 'ABC123',
40
+ expires_timestamp: Date.now() + 300000,
41
+ // ...
42
+ }));
43
+ ```
44
+
45
+ ### 3. Overwrite with `{}` to consume/clear
46
+
47
+ Instead of deleting the file, overwrite it with an empty object:
48
+
49
+ ```javascript
50
+ // WRONG - fails under sticky-bit
51
+ fs.unlinkSync(tokenPath);
52
+
53
+ // CORRECT - works under sticky-bit
54
+ fs.writeFileSync(tokenPath, '{}');
55
+ ```
56
+
57
+ ### 4. Treat `{}` as "no token"
58
+
59
+ Readers must check for empty objects before processing:
60
+
61
+ ```javascript
62
+ const token = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
63
+
64
+ // Empty object = consumed/cleared token
65
+ if (!token.code && !token.request_id) {
66
+ return false; // No valid token
67
+ }
68
+ ```
69
+
70
+ ## Files Using This Pattern
71
+
72
+ | File | Written By | Consumed By |
73
+ |------|-----------|-------------|
74
+ | `commit-approval-token.json` | deputy-cto MCP server | pre-commit-review hook |
75
+ | `bypass-approval-token.json` | bypass-approval hook | block-no-verify hook, deputy-cto server |
76
+ | `protected-action-approvals.json` | protected-action-gate hook | protected-action-gate hook |
77
+ | `protection-state.json` | npx gentyr init | various hooks |
78
+ | `hourly-automation-state.json` | hourly automation | hourly automation |
79
+ | `plan-executor-state.json` | plan executor | plan executor |
80
+
81
+ ## Adding a New State File
82
+
83
+ 1. **Add to `init.js` pre-creation block** (`cli/commands/init.js` `preCreateStateFiles`) so it exists before protection
84
+ 2. **Use `writeFileSync` to write** -- never use conditional create (`O_CREAT | O_EXCL`)
85
+ 3. **Use `writeFileSync(path, '{}')` to clear** -- never use `unlinkSync`
86
+ 4. **Check for empty `{}` in readers** -- treat as "no data"
87
+ 5. **Add to `.gitignore`** via config-gen.js gitignore generation if not already listed
88
+ 6. **Add to credential-file-guard** if the file contains sensitive data
89
+
90
+ ## Common Mistakes
91
+
92
+ ### Using `existsSync` as "has token" check
93
+
94
+ Under this pattern, the file always exists. Check the *content*, not the file's existence:
95
+
96
+ ```javascript
97
+ // Incomplete - file always exists under protection
98
+ if (!fs.existsSync(tokenPath)) {
99
+ return false;
100
+ }
101
+
102
+ // Also need this:
103
+ const token = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
104
+ if (Object.keys(token).length === 0) {
105
+ return false; // Token was cleared
106
+ }
107
+ ```
108
+
109
+ ### Forgetting to add to init.js
110
+
111
+ If a new state file isn't pre-created, the first write attempt will fail with EACCES. Always add new files to `preCreateStateFiles()` in `cli/commands/init.js`.
112
+
113
+ ### Using `unlinkSync` in error/cleanup paths
114
+
115
+ Every code path that touches a token file must use overwrite, not delete. This includes error handlers, expiry cleanup, forgery detection, and consumption paths.