@haposoft/cafekit 0.16.0-rc.8 → 0.16.1

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 (323) hide show
  1. package/README.md +117 -28
  2. package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
  3. package/bin/__tests__/codex-hooks.test.js +175 -0
  4. package/bin/__tests__/codex-native.test.js +1882 -70
  5. package/bin/__tests__/develop-contract.test.js +442 -23
  6. package/bin/__tests__/grok-envelope.test.js +271 -0
  7. package/bin/__tests__/grok-platform.test.js +133 -0
  8. package/bin/__tests__/hook-payload.test.js +194 -0
  9. package/bin/__tests__/installer-safety.test.js +3 -3
  10. package/bin/__tests__/omp-bridge.test.js +168 -0
  11. package/bin/__tests__/omp-hooks.test.js +178 -0
  12. package/bin/__tests__/omp-runtime.test.js +150 -0
  13. package/bin/__tests__/optional-skill-inventory.test.js +249 -0
  14. package/bin/__tests__/output-styles.test.js +214 -0
  15. package/bin/__tests__/package-inventory.test.js +1735 -98
  16. package/bin/__tests__/runtime-dir.test.js +158 -0
  17. package/bin/__tests__/runtime-schema.test.js +113 -0
  18. package/bin/__tests__/secret-output-guardrail.test.js +122 -0
  19. package/bin/__tests__/skill-routing-source.test.js +172 -0
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +28 -1
  21. package/bin/install.js +15 -5
  22. package/bin/lib/codex-install.js +170 -5
  23. package/bin/lib/context.js +64 -3
  24. package/bin/lib/i18n.js +15 -36
  25. package/bin/lib/skill-deps.js +5 -5
  26. package/bin/phases/claude-runtime.js +23 -0
  27. package/bin/phases/claude-settings.js +8 -0
  28. package/bin/phases/codex-hooks.js +156 -0
  29. package/bin/phases/codex-runtime.js +29 -38
  30. package/bin/phases/copy-payload.js +15 -2
  31. package/bin/phases/omp-runtime.js +140 -0
  32. package/bin/phases/root-config.js +3 -0
  33. package/bin/phases/select-platform.js +22 -1
  34. package/bin/phases/select-skill-bundles.js +96 -0
  35. package/bin/phases/skill-inventory.js +71 -0
  36. package/bin/phases/skills-setup.js +9 -2
  37. package/bin/phases/summary.js +0 -4
  38. package/bin/phases/write-metadata.js +8 -5
  39. package/package.json +8 -2
  40. package/src/claude/CLAUDE.md +3 -3
  41. package/src/claude/agents/brainstormer.md +52 -69
  42. package/src/claude/agents/code-auditor.md +32 -14
  43. package/src/claude/agents/debugger.md +35 -20
  44. package/src/claude/agents/deployer.md +12 -5
  45. package/src/claude/agents/docs-keeper.md +11 -1
  46. package/src/claude/agents/implementer.md +25 -13
  47. package/src/claude/agents/inspector.md +6 -3
  48. package/src/claude/agents/project-manager.md +1 -1
  49. package/src/claude/agents/researcher.md +26 -55
  50. package/src/claude/agents/spec-maker.md +12 -7
  51. package/src/claude/agents/strategist.md +52 -0
  52. package/src/claude/agents/test-runner.md +117 -132
  53. package/src/claude/agents/ui-ux-designer.md +1 -1
  54. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -0
  55. package/src/claude/hooks/__tests__/precompact.test.js +158 -0
  56. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +2 -0
  57. package/src/claude/hooks/__tests__/spec-gate.test.js +348 -3
  58. package/src/claude/hooks/__tests__/state.test.js +7 -0
  59. package/src/claude/hooks/__tests__/statusline.test.js +196 -0
  60. package/src/claude/hooks/agent.cjs +13 -8
  61. package/src/claude/hooks/completion-authority.cjs +10 -4
  62. package/src/claude/hooks/docs-sync.cjs +14 -11
  63. package/src/claude/hooks/inspect-block.cjs +28 -8
  64. package/src/claude/hooks/lib/config.cjs +8 -2
  65. package/src/claude/hooks/lib/counter.cjs +7 -5
  66. package/src/claude/hooks/lib/hook-payload.cjs +160 -0
  67. package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
  68. package/src/claude/hooks/lib/runtime-dir.cjs +49 -0
  69. package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
  70. package/src/claude/hooks/precompact.cjs +106 -0
  71. package/src/claude/hooks/privacy-block.cjs +4 -2
  72. package/src/claude/hooks/rules.cjs +5 -3
  73. package/src/claude/hooks/secret-output-guardrail.cjs +44 -0
  74. package/src/claude/hooks/semantic-review-authority.cjs +6 -4
  75. package/src/claude/hooks/session.cjs +37 -9
  76. package/src/claude/hooks/spec-gate.cjs +21 -11
  77. package/src/claude/hooks/spec-state.cjs +11 -7
  78. package/src/claude/hooks/state.cjs +7 -5
  79. package/src/claude/hooks/task-scaffold-guard.cjs +30 -9
  80. package/src/claude/hooks/usage.cjs +6 -5
  81. package/src/claude/migration-manifest.json +31 -15
  82. package/src/claude/output-styles/coding-level-0-eli5.md +103 -0
  83. package/src/claude/output-styles/coding-level-1-junior.md +124 -0
  84. package/src/claude/output-styles/coding-level-2-mid.md +146 -0
  85. package/src/claude/output-styles/coding-level-3-senior.md +148 -0
  86. package/src/claude/output-styles/coding-level-4-lead.md +159 -0
  87. package/src/claude/output-styles/coding-level-5-god.md +91 -0
  88. package/src/claude/references/debugger/core-philosophy.md +5 -3
  89. package/src/claude/references/debugger/frontend-verification.md +2 -2
  90. package/src/claude/references/debugger/log-ci-analysis.md +13 -1
  91. package/src/claude/references/debugger/parallel-agent-hydration.md +19 -34
  92. package/src/claude/references/debugger/performance-diagnostics.md +2 -2
  93. package/src/claude/references/debugger/root-cause-tracing.md +37 -6
  94. package/src/claude/references/debugger/side-effect-gate.md +3 -1
  95. package/src/claude/references/debugger/verification-protocol.md +13 -12
  96. package/src/claude/rules/orchestrator.md +7 -1
  97. package/src/claude/rules/process-management.md +39 -0
  98. package/src/claude/rules/review-audit-self-decision.md +48 -0
  99. package/src/claude/rules/skill-domain-routing.md +26 -24
  100. package/src/claude/rules/skill-workflow-routing.md +22 -14
  101. package/src/claude/rules/state-sync.md +7 -1
  102. package/src/claude/rules/workflow.md +5 -3
  103. package/src/claude/runtime.json +1 -0
  104. package/src/claude/runtime.schema.json +205 -0
  105. package/src/claude/scripts/generate-skill-catalog.cjs +156 -122
  106. package/src/claude/scripts/provenance.cjs +29 -9
  107. package/src/claude/scripts/spec-receipt.cjs +65 -4
  108. package/src/claude/scripts/workflow-policy.cjs +1 -1
  109. package/src/claude/settings/settings.json +16 -0
  110. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  111. package/src/claude/skills/ai-multimodal/SKILL.md +1 -1
  112. package/src/claude/skills/brainstorm/SKILL.md +193 -172
  113. package/src/claude/skills/brainstorm/references/question-framework.md +105 -167
  114. package/src/claude/skills/chrome-devtools/SKILL.md +1 -1
  115. package/src/claude/skills/code-review/SKILL.md +43 -25
  116. package/src/claude/skills/code-review/references/spec-compliance-review.md +3 -4
  117. package/src/claude/skills/code-review/references/verification-gate.md +1 -1
  118. package/src/claude/skills/debug/SKILL.md +48 -19
  119. package/src/claude/skills/delegate/SKILL.md +1 -1
  120. package/src/claude/skills/develop/SKILL.md +92 -65
  121. package/src/claude/skills/develop/references/parallel-waves.md +36 -63
  122. package/src/claude/skills/develop/references/quality-gate.md +31 -4
  123. package/src/claude/skills/develop/references/subagent-patterns.md +21 -7
  124. package/src/claude/skills/docs/SKILL.md +42 -23
  125. package/src/claude/skills/docs/references/init-workflow.md +4 -4
  126. package/src/claude/skills/docs/references/reconstruct-workflow.md +6 -6
  127. package/src/claude/skills/docs/references/standard-docs-workflow.md +6 -2
  128. package/src/claude/skills/docs/references/summarize-workflow.md +2 -2
  129. package/src/claude/skills/docs/references/update-workflow.md +9 -7
  130. package/src/claude/skills/docs/templates/requirements-as-is.md +1 -1
  131. package/src/claude/skills/docx/SKILL.md +1 -1
  132. package/src/claude/skills/git/SKILL.md +1 -1
  133. package/src/claude/skills/git/references/commit-protocols.md +1 -1
  134. package/src/claude/skills/git/references/finish-branch.md +1 -1
  135. package/src/claude/skills/hotfix/SKILL.md +159 -170
  136. package/src/claude/skills/hotfix/references/diagnosis-protocol.md +14 -9
  137. package/src/claude/skills/hotfix/references/parallel-patterns.md +27 -13
  138. package/src/claude/skills/hotfix/references/prevention-gate.md +5 -0
  139. package/src/claude/skills/hotfix/references/review-cycle.md +21 -15
  140. package/src/claude/skills/hotfix/references/workflow-specialized.md +53 -21
  141. package/src/claude/skills/inspect/SKILL.md +55 -46
  142. package/src/claude/skills/inspect/references/internal-inspection.md +21 -5
  143. package/src/claude/skills/loop/SKILL.md +114 -0
  144. package/src/claude/skills/loop/references/bounded-loop-protocol.md +104 -0
  145. package/src/claude/skills/loop/references/metric-and-guard-contract.md +76 -0
  146. package/src/claude/skills/pdf/SKILL.md +1 -1
  147. package/src/claude/skills/pptx/SKILL.md +1 -1
  148. package/src/claude/skills/question/SKILL.md +16 -13
  149. package/src/claude/skills/research/SKILL.md +70 -43
  150. package/src/claude/skills/route/SKILL.md +94 -0
  151. package/src/claude/skills/route/references/agent-timing.md +46 -0
  152. package/src/claude/skills/route/references/chaining-patterns.md +53 -0
  153. package/src/claude/skills/route/references/task-taxonomy.md +48 -0
  154. package/src/claude/skills/specs/SKILL.md +51 -71
  155. package/src/claude/skills/specs/references/review.md +11 -8
  156. package/src/claude/skills/specs/references/templates.md +34 -18
  157. package/src/claude/skills/specs/templates/requirements-init.md +1 -1
  158. package/src/claude/skills/specs/templates/research.md +2 -2
  159. package/src/claude/skills/sync/SKILL.md +6 -6
  160. package/src/claude/skills/sync/references/sync-protocols.md +1 -1
  161. package/src/claude/skills/test/SKILL.md +120 -120
  162. package/src/claude/skills/test/references/execution-strategy.md +208 -361
  163. package/src/claude/skills/test/references/failure-triage.md +55 -121
  164. package/src/claude/skills/test/references/test-memory.md +20 -34
  165. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  166. package/src/claude/skills/web-testing/SKILL.md +1 -1
  167. package/src/claude/skills/xlsx/SKILL.md +1 -1
  168. package/src/claude/status.cjs +135 -33
  169. package/src/codex/AGENTS.md +14 -4
  170. package/src/codex/hooks/docs-sync.cjs +8 -3
  171. package/src/codex/hooks/lib/hook-context.cjs +18 -4
  172. package/src/codex/hooks/rules.cjs +29 -1
  173. package/src/codex/hooks/secret-output-guardrail.cjs +30 -0
  174. package/src/codex/hooks/spec-gate.cjs +3 -8
  175. package/src/codex/hooks/spec-state.cjs +3 -2
  176. package/src/codex/hooks.json +5 -0
  177. package/src/codex/rules/state-sync.md +7 -1
  178. package/src/codex/runtime.json +2 -0
  179. package/src/common/.env.example +1 -1
  180. package/src/omp/extensions/cafekit-bridge.mjs +233 -0
  181. package/src/omp/hooks/privacy-block.cjs +250 -0
  182. package/src/omp/runtime.json +33 -0
  183. package/bin/lib/rtk-run.js +0 -28
  184. package/bin/phases/setup-rtk.js +0 -159
  185. package/src/claude/skills/backend-development/SKILL.md +0 -101
  186. package/src/claude/skills/backend-development/references/backend-api-design.md +0 -495
  187. package/src/claude/skills/backend-development/references/backend-architecture.md +0 -454
  188. package/src/claude/skills/backend-development/references/backend-authentication.md +0 -338
  189. package/src/claude/skills/backend-development/references/backend-code-quality.md +0 -659
  190. package/src/claude/skills/backend-development/references/backend-debugging.md +0 -904
  191. package/src/claude/skills/backend-development/references/backend-devops.md +0 -491
  192. package/src/claude/skills/backend-development/references/backend-mindset.md +0 -387
  193. package/src/claude/skills/backend-development/references/backend-performance.md +0 -397
  194. package/src/claude/skills/backend-development/references/backend-security.md +0 -342
  195. package/src/claude/skills/backend-development/references/backend-technologies.md +0 -256
  196. package/src/claude/skills/backend-development/references/backend-testing.md +0 -429
  197. package/src/claude/skills/devops/.env.example +0 -76
  198. package/src/claude/skills/devops/SKILL.md +0 -102
  199. package/src/claude/skills/devops/references/browser-rendering.md +0 -305
  200. package/src/claude/skills/devops/references/cloudflare-d1-kv.md +0 -123
  201. package/src/claude/skills/devops/references/cloudflare-platform.md +0 -271
  202. package/src/claude/skills/devops/references/cloudflare-r2-storage.md +0 -280
  203. package/src/claude/skills/devops/references/cloudflare-workers-advanced.md +0 -312
  204. package/src/claude/skills/devops/references/cloudflare-workers-apis.md +0 -309
  205. package/src/claude/skills/devops/references/cloudflare-workers-basics.md +0 -418
  206. package/src/claude/skills/devops/references/docker-basics.md +0 -297
  207. package/src/claude/skills/devops/references/docker-compose.md +0 -292
  208. package/src/claude/skills/devops/references/gcloud-platform.md +0 -297
  209. package/src/claude/skills/devops/references/gcloud-services.md +0 -304
  210. package/src/claude/skills/devops/references/kubernetes-basics.md +0 -95
  211. package/src/claude/skills/devops/references/kubernetes-helm-advanced.md +0 -70
  212. package/src/claude/skills/devops/references/kubernetes-helm.md +0 -81
  213. package/src/claude/skills/devops/references/kubernetes-kubectl.md +0 -74
  214. package/src/claude/skills/devops/references/kubernetes-security-advanced.md +0 -94
  215. package/src/claude/skills/devops/references/kubernetes-security.md +0 -89
  216. package/src/claude/skills/devops/references/kubernetes-troubleshooting-advanced.md +0 -74
  217. package/src/claude/skills/devops/references/kubernetes-troubleshooting.md +0 -49
  218. package/src/claude/skills/devops/references/kubernetes-workflows-advanced.md +0 -75
  219. package/src/claude/skills/devops/references/kubernetes-workflows.md +0 -76
  220. package/src/claude/skills/devops/scripts/cloudflare_deploy.py +0 -269
  221. package/src/claude/skills/devops/scripts/docker_optimize.py +0 -332
  222. package/src/claude/skills/devops/scripts/requirements.txt +0 -20
  223. package/src/claude/skills/devops/scripts/tests/requirements.txt +0 -3
  224. package/src/claude/skills/devops/scripts/tests/test_cloudflare_deploy.py +0 -285
  225. package/src/claude/skills/devops/scripts/tests/test_docker_optimize.py +0 -436
  226. package/src/claude/skills/frontend-design/SKILL.md +0 -135
  227. package/src/claude/skills/frontend-design/references/ai-multimodal-overview.md +0 -165
  228. package/src/claude/skills/frontend-design/references/analysis-best-practices.md +0 -80
  229. package/src/claude/skills/frontend-design/references/analysis-prompts.md +0 -141
  230. package/src/claude/skills/frontend-design/references/analysis-techniques.md +0 -118
  231. package/src/claude/skills/frontend-design/references/animejs.md +0 -396
  232. package/src/claude/skills/frontend-design/references/anti-slop-rules.md +0 -103
  233. package/src/claude/skills/frontend-design/references/asset-generation.md +0 -337
  234. package/src/claude/skills/frontend-design/references/bento-motion-engine.md +0 -142
  235. package/src/claude/skills/frontend-design/references/design-extraction-overview.md +0 -71
  236. package/src/claude/skills/frontend-design/references/extraction-best-practices.md +0 -141
  237. package/src/claude/skills/frontend-design/references/extraction-output-templates.md +0 -162
  238. package/src/claude/skills/frontend-design/references/extraction-prompts.md +0 -127
  239. package/src/claude/skills/frontend-design/references/magicui-components.md +0 -129
  240. package/src/claude/skills/frontend-design/references/performance-guardrails.md +0 -169
  241. package/src/claude/skills/frontend-design/references/premium-design-patterns.md +0 -93
  242. package/src/claude/skills/frontend-design/references/redesign-audit-checklist.md +0 -114
  243. package/src/claude/skills/frontend-design/references/technical-accessibility.md +0 -119
  244. package/src/claude/skills/frontend-design/references/technical-best-practices.md +0 -97
  245. package/src/claude/skills/frontend-design/references/technical-optimization.md +0 -44
  246. package/src/claude/skills/frontend-design/references/technical-overview.md +0 -90
  247. package/src/claude/skills/frontend-design/references/technical-workflows.md +0 -150
  248. package/src/claude/skills/frontend-design/references/visual-analysis-overview.md +0 -95
  249. package/src/claude/skills/frontend-design/references/workflow-3d.md +0 -102
  250. package/src/claude/skills/frontend-design/references/workflow-describe.md +0 -87
  251. package/src/claude/skills/frontend-design/references/workflow-immersive.md +0 -87
  252. package/src/claude/skills/frontend-design/references/workflow-quick.md +0 -57
  253. package/src/claude/skills/frontend-design/references/workflow-screenshot.md +0 -63
  254. package/src/claude/skills/frontend-design/references/workflow-video.md +0 -74
  255. package/src/claude/skills/frontend-development/SKILL.md +0 -406
  256. package/src/claude/skills/frontend-development/resources/common-patterns.md +0 -331
  257. package/src/claude/skills/frontend-development/resources/complete-examples.md +0 -872
  258. package/src/claude/skills/frontend-development/resources/component-patterns.md +0 -502
  259. package/src/claude/skills/frontend-development/resources/data-fetching.md +0 -767
  260. package/src/claude/skills/frontend-development/resources/file-organization.md +0 -502
  261. package/src/claude/skills/frontend-development/resources/loading-and-error-states.md +0 -501
  262. package/src/claude/skills/frontend-development/resources/performance.md +0 -406
  263. package/src/claude/skills/frontend-development/resources/routing-guide.md +0 -364
  264. package/src/claude/skills/frontend-development/resources/styling-guide.md +0 -428
  265. package/src/claude/skills/frontend-development/resources/typescript-standards.md +0 -418
  266. package/src/claude/skills/mobile-development/SKILL.md +0 -218
  267. package/src/claude/skills/mobile-development/references/mobile-android.md +0 -604
  268. package/src/claude/skills/mobile-development/references/mobile-best-practices.md +0 -544
  269. package/src/claude/skills/mobile-development/references/mobile-debugging.md +0 -1089
  270. package/src/claude/skills/mobile-development/references/mobile-frameworks.md +0 -465
  271. package/src/claude/skills/mobile-development/references/mobile-ios.md +0 -496
  272. package/src/claude/skills/mobile-development/references/mobile-mindset.md +0 -542
  273. package/src/claude/skills/react-best-practices/AGENTS.md +0 -2249
  274. package/src/claude/skills/react-best-practices/README.md +0 -123
  275. package/src/claude/skills/react-best-practices/SKILL.md +0 -128
  276. package/src/claude/skills/react-best-practices/metadata.json +0 -15
  277. package/src/claude/skills/react-best-practices/rules/_sections.md +0 -46
  278. package/src/claude/skills/react-best-practices/rules/_template.md +0 -28
  279. package/src/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  280. package/src/claude/skills/react-best-practices/rules/advanced-use-latest.md +0 -49
  281. package/src/claude/skills/react-best-practices/rules/async-api-routes.md +0 -38
  282. package/src/claude/skills/react-best-practices/rules/async-defer-await.md +0 -80
  283. package/src/claude/skills/react-best-practices/rules/async-dependencies.md +0 -36
  284. package/src/claude/skills/react-best-practices/rules/async-parallel.md +0 -28
  285. package/src/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -99
  286. package/src/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -59
  287. package/src/claude/skills/react-best-practices/rules/bundle-conditional.md +0 -31
  288. package/src/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +0 -49
  289. package/src/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  290. package/src/claude/skills/react-best-practices/rules/bundle-preload.md +0 -50
  291. package/src/claude/skills/react-best-practices/rules/client-event-listeners.md +0 -74
  292. package/src/claude/skills/react-best-practices/rules/client-swr-dedup.md +0 -56
  293. package/src/claude/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
  294. package/src/claude/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
  295. package/src/claude/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
  296. package/src/claude/skills/react-best-practices/rules/js-cache-storage.md +0 -70
  297. package/src/claude/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
  298. package/src/claude/skills/react-best-practices/rules/js-early-exit.md +0 -50
  299. package/src/claude/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
  300. package/src/claude/skills/react-best-practices/rules/js-index-maps.md +0 -37
  301. package/src/claude/skills/react-best-practices/rules/js-length-check-first.md +0 -49
  302. package/src/claude/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
  303. package/src/claude/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
  304. package/src/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
  305. package/src/claude/skills/react-best-practices/rules/rendering-activity.md +0 -26
  306. package/src/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  307. package/src/claude/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
  308. package/src/claude/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
  309. package/src/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  310. package/src/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  311. package/src/claude/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
  312. package/src/claude/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
  313. package/src/claude/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
  314. package/src/claude/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
  315. package/src/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
  316. package/src/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  317. package/src/claude/skills/react-best-practices/rules/rerender-memo.md +0 -44
  318. package/src/claude/skills/react-best-practices/rules/rerender-transitions.md +0 -40
  319. package/src/claude/skills/react-best-practices/rules/server-after-nonblocking.md +0 -73
  320. package/src/claude/skills/react-best-practices/rules/server-cache-lru.md +0 -41
  321. package/src/claude/skills/react-best-practices/rules/server-cache-react.md +0 -26
  322. package/src/claude/skills/react-best-practices/rules/server-parallel-fetching.md +0 -79
  323. package/src/claude/skills/react-best-practices/rules/server-serialization.md +0 -38
@@ -0,0 +1,271 @@
1
+ 'use strict';
2
+
3
+ // Grok CLI runs these hooks through its Claude-compatibility layer, so the scripts must
4
+ // answer the same way whether the envelope came from Claude Code or from grok. Each case
5
+ // runs a real hook as a child process from a tree shaped like an install, with grok's
6
+ // camelCase envelope on stdin and CLAUDE_PROJECT_DIR set the way grok sets it.
7
+ //
8
+ // Every case names the gate it protects. Before the payload reader existed, each of them
9
+ // answered nothing: the hook read an undefined key, found no tool and no path, and let
10
+ // the call through while looking installed.
11
+
12
+ const assert = require('node:assert/strict');
13
+ const fs = require('node:fs');
14
+ const os = require('node:os');
15
+ const path = require('node:path');
16
+ const { spawnSync } = require('node:child_process');
17
+ const { test } = require('node:test');
18
+
19
+ const PACKAGE_ROOT = path.join(__dirname, '../..');
20
+ const MANIFEST = require(path.join(PACKAGE_ROOT, 'src/claude/migration-manifest.json'));
21
+
22
+ /**
23
+ * A `.claude` tree holding exactly what an install ships, inside a git repository.
24
+ * The repository matters because the Stop gate derives provenance with git before it
25
+ * reads anything else.
26
+ */
27
+ function withInstall(run, { git = false } = {}) {
28
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-grok-envelope-'));
29
+ try {
30
+ for (const rel of MANIFEST.runtime.files.filter((f) => f.startsWith('hooks/'))) {
31
+ const target = path.join(root, '.claude', rel);
32
+ fs.mkdirSync(path.dirname(target), { recursive: true });
33
+ fs.copyFileSync(path.join(PACKAGE_ROOT, 'src/claude', rel), target);
34
+ }
35
+ fs.cpSync(path.join(PACKAGE_ROOT, 'src/claude/scripts'), path.join(root, '.claude', 'scripts'),
36
+ { recursive: true });
37
+ if (git) {
38
+ for (const args of [['init', '-q'], ['config', 'user.email', 'cafekit@example.invalid'],
39
+ ['config', 'user.name', 'CafeKit Test'], ['commit', '--allow-empty', '-qm', 'fixture']]) {
40
+ const r = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
41
+ assert.equal(r.status, 0, r.stderr);
42
+ }
43
+ }
44
+ return run(root);
45
+ } finally {
46
+ fs.rmSync(root, { recursive: true, force: true });
47
+ }
48
+ }
49
+
50
+ /** Run one installed hook the way grok runs it. */
51
+ function runHook(root, name, payload, args = []) {
52
+ const r = spawnSync(process.execPath, [path.join(root, '.claude', 'hooks', name), ...args], {
53
+ cwd: root,
54
+ input: JSON.stringify(payload),
55
+ encoding: 'utf8',
56
+ // grok sets CLAUDE_PROJECT_DIR as an alias of GROK_WORKSPACE_ROOT on every hook.
57
+ env: { ...process.env, CLAUDE_PROJECT_DIR: root, PROJECT_ROOT: '', GROK_SESSION_ID: 'grok-1' },
58
+ });
59
+ return { code: r.status, stdout: r.stdout || '', stderr: r.stderr || '' };
60
+ }
61
+
62
+ function decisionOf(stdout) {
63
+ for (const line of stdout.trim().split('\n').filter(Boolean)) {
64
+ try {
65
+ const parsed = JSON.parse(line);
66
+ const d = parsed?.hookSpecificOutput?.permissionDecision ?? parsed?.decision;
67
+ if (d) return { decision: d, reason: parsed?.hookSpecificOutput?.permissionDecisionReason ?? parsed?.reason };
68
+ } catch { /* not JSON, keep looking */ }
69
+ }
70
+ return { decision: null, reason: null };
71
+ }
72
+
73
+ test('the privacy gate asks on a grok run_terminal_command', () => {
74
+ withInstall((root) => {
75
+ fs.writeFileSync(path.join(root, '.env'), 'TOKEN=redacted\n');
76
+ // grok's shell tool under grok's key names. Reading `data.tool_name` here yields
77
+ // undefined, so the command is never scanned and the secret is handed over.
78
+ const r = runHook(root, 'privacy-block.cjs', {
79
+ hookEventName: 'pre_tool_use',
80
+ sessionId: 'grok-1',
81
+ cwd: root,
82
+ toolName: 'run_terminal_command',
83
+ toolInput: { command: `cat ${path.join(root, '.env')}` },
84
+ });
85
+ const { decision, reason } = decisionOf(r.stdout);
86
+ assert.equal(decision, 'ask', 'a grok shell call touching .env must reach the approval prompt');
87
+ assert.match(reason, /\.env/, 'the prompt must name what it is protecting');
88
+ });
89
+ });
90
+
91
+ test('the privacy gate asks on a grok read_file path', () => {
92
+ withInstall((root) => {
93
+ fs.writeFileSync(path.join(root, '.env'), 'TOKEN=redacted\n');
94
+ // grok's read tool carries its argument as `path`, not `file_path`.
95
+ const r = runHook(root, 'privacy-block.cjs', {
96
+ hookEventName: 'pre_tool_use',
97
+ cwd: root,
98
+ toolName: 'read_file',
99
+ toolInput: { path: path.join(root, '.env') },
100
+ });
101
+ assert.equal(decisionOf(r.stdout).decision, 'ask');
102
+ });
103
+ });
104
+
105
+ test('an ordinary grok read is not disturbed', () => {
106
+ withInstall((root) => {
107
+ fs.writeFileSync(path.join(root, 'README.md'), '# hello\n');
108
+ const r = runHook(root, 'privacy-block.cjs', {
109
+ hookEventName: 'pre_tool_use',
110
+ cwd: root,
111
+ toolName: 'read_file',
112
+ toolInput: { path: path.join(root, 'README.md') },
113
+ });
114
+ assert.equal(decisionOf(r.stdout).decision, null, 'the gate must not ask about an ordinary file');
115
+ assert.equal(r.code, 0);
116
+ });
117
+ });
118
+
119
+ test('a broad glob is denied with its full reason in JSON', () => {
120
+ withInstall((root) => {
121
+ const r = runHook(root, 'inspect-block.cjs', {
122
+ hookEventName: 'pre_tool_use',
123
+ cwd: root,
124
+ toolName: 'Glob',
125
+ toolInput: { pattern: '**/*.ts' },
126
+ });
127
+ const { decision, reason } = decisionOf(r.stdout);
128
+ assert.equal(decision, 'deny');
129
+ // grok reads only the first stderr line, so the actionable half of the message has to
130
+ // travel in the JSON reason or the model is blocked without being told what to do.
131
+ assert.match(reason, /SCOPE LIMIT EXCEEDED/);
132
+ assert.match(reason, /Please narrow your scope/, 'the whole message must survive, not just its first line');
133
+ assert.equal(r.code, 2, 'exit 2 stays for a host that reads neither channel');
134
+ });
135
+ });
136
+
137
+ test('a nested task write is denied under the grok write tool', () => {
138
+ withInstall((root) => {
139
+ const target = path.join(root, 'specs', 'demo', 'tasks', 'task-R0-01-x.md');
140
+ fs.mkdirSync(path.dirname(target), { recursive: true });
141
+ // grok creates files with `write`; `search_replace` edits an existing one. The guard
142
+ // fires on Write only, so the alias has to keep those two apart.
143
+ const r = runHook(root, 'task-scaffold-guard.cjs', {
144
+ hookEventName: 'pre_tool_use',
145
+ cwd: root,
146
+ toolName: 'write',
147
+ toolInput: { path: target, content: '# x' },
148
+ });
149
+ const { decision, reason } = decisionOf(r.stdout);
150
+ assert.equal(decision, 'deny');
151
+ assert.match(reason, /TASK SCAFFOLD REQUIRED/);
152
+ assert.match(reason, /spec-scaffold\.cjs/, 'the command the model must run has to reach it');
153
+ assert.equal(r.code, 2);
154
+
155
+ // The escape route the message names must stay open: an edit is not a create.
156
+ const edit = runHook(root, 'task-scaffold-guard.cjs', {
157
+ hookEventName: 'pre_tool_use',
158
+ cwd: root,
159
+ toolName: 'search_replace',
160
+ toolInput: { path: target, old_string: 'a', new_string: 'b' },
161
+ });
162
+ assert.equal(decisionOf(edit.stdout).decision, null, 'search_replace must not be treated as a create');
163
+ assert.equal(edit.code, 0);
164
+ });
165
+ });
166
+
167
+ /** A packet the Stop gate will block: one done task with no receipt. */
168
+ function unprovenPacket(root) {
169
+ const feature = path.join(root, 'specs', 'demo');
170
+ fs.mkdirSync(feature, { recursive: true });
171
+ fs.writeFileSync(path.join(feature, 'plan.md'), '# Demo plan\n');
172
+ fs.writeFileSync(path.join(feature, 'task-01-demo.md'), [
173
+ '# Task 01: demo', '', 'Status: done', '', '## Dependencies', '', '- none', '',
174
+ '## Verification Plan', '', '- Command: node --test', '',
175
+ ].join('\n'));
176
+ }
177
+
178
+ test('the Stop gate honours stopHookActive', () => {
179
+ withInstall((root) => {
180
+ // The fixture must be one the gate would otherwise block, or silence proves nothing:
181
+ // an empty specs directory makes the gate exit quietly for a different reason.
182
+ unprovenPacket(root);
183
+ // Without the guard the gate re-blocks its own continuation until grok's cap ends
184
+ // the turn eight rounds later.
185
+ const r = runHook(root, 'spec-gate.cjs', {
186
+ hookEventName: 'stop',
187
+ sessionId: 'grok-1',
188
+ cwd: root,
189
+ stopHookActive: true,
190
+ });
191
+ assert.equal(r.stdout.trim(), '', 'a continuation round must pass silently');
192
+ assert.equal(r.code, 0);
193
+ }, { git: true });
194
+ });
195
+
196
+ test('the Stop gate still blocks an unproven done task', () => {
197
+ withInstall((root) => {
198
+ unprovenPacket(root);
199
+ // The guard must not become a way through: a Stop with no continuation flag still
200
+ // faces the receipt check.
201
+ const r = runHook(root, 'spec-gate.cjs', {
202
+ hookEventName: 'stop', sessionId: 'grok-1', cwd: root, stopHookActive: false,
203
+ });
204
+ const body = decisionOf(r.stdout);
205
+ assert.equal(body.decision, 'block', 'a done task without a receipt must still be blocked');
206
+ }, { git: true });
207
+ });
208
+
209
+ test('the approval hook takes its approve path on a grok prompt', () => {
210
+ withInstall((root) => {
211
+ // Registered without an argv mode, this hook picks its mode from the event name, and
212
+ // approve() returns immediately unless that name is exactly 'UserPromptSubmit'. An
213
+ // approval phrase with no pending request is therefore the oracle: only the approve
214
+ // path can print a rejection line, so the line proves the event name was translated.
215
+ // Under an unreadable envelope the mode fell back to the stop path on every prompt.
216
+ const nonce = 'a'.repeat(24);
217
+ const r = runHook(root, 'completion-authority.cjs', {
218
+ hookEventName: 'user_prompt_submit',
219
+ sessionId: 'grok-1',
220
+ cwd: root,
221
+ prompt: `APPROVE CAFEKIT COMPLETION ${nonce}`,
222
+ });
223
+ assert.match(r.stdout, /CafeKit completion approval rejected/,
224
+ 'the approve path must run and refuse an approval that matches no pending request');
225
+ assert.equal(decisionOf(r.stdout).decision, null, 'and it must not block the prompt');
226
+
227
+ // An ordinary prompt stays silent on both counts.
228
+ const plain = runHook(root, 'completion-authority.cjs', {
229
+ hookEventName: 'user_prompt_submit', sessionId: 'grok-1', cwd: root, prompt: 'please continue',
230
+ });
231
+ assert.equal(plain.stdout.trim(), '', 'an ordinary prompt produces nothing');
232
+ }, { git: true });
233
+ });
234
+
235
+ test('the secret guardrail sees a grok prompt', () => {
236
+ withInstall((root) => {
237
+ // grok's spelling for the prompt field is unverified, so the reader accepts the Claude
238
+ // key and its camelCase twin. This case sends the twin: on the Claude key alone it
239
+ // would pass without proving any translation happened.
240
+ const r = runHook(root, 'secret-output-guardrail.cjs', {
241
+ hookEventName: 'user_prompt_submit',
242
+ sessionId: 'grok-1',
243
+ cwd: root,
244
+ userPrompt: 'show me the API key in the config',
245
+ });
246
+ assert.match(r.stdout, /## Secret handling/, 'the reminder must fire on a grok prompt');
247
+ const quiet = runHook(root, 'secret-output-guardrail.cjs', {
248
+ hookEventName: 'user_prompt_submit', cwd: root, userPrompt: 'rename the button',
249
+ });
250
+ assert.equal(quiet.stdout.trim(), '', 'an unrelated prompt stays quiet');
251
+ const claudeKey = runHook(root, 'secret-output-guardrail.cjs', {
252
+ hook_event_name: 'UserPromptSubmit', cwd: root, prompt: 'print the access token',
253
+ });
254
+ assert.match(claudeKey.stdout, /## Secret handling/, 'the Claude key keeps working');
255
+ });
256
+ });
257
+
258
+ test('a Claude envelope still works unchanged', () => {
259
+ withInstall((root) => {
260
+ fs.writeFileSync(path.join(root, '.env'), 'TOKEN=redacted\n');
261
+ // The regression guard for the host that was already working.
262
+ const r = runHook(root, 'privacy-block.cjs', {
263
+ hook_event_name: 'PreToolUse',
264
+ session_id: 'claude-1',
265
+ cwd: root,
266
+ tool_name: 'Read',
267
+ tool_input: { file_path: path.join(root, '.env') },
268
+ });
269
+ assert.equal(decisionOf(r.stdout).decision, 'ask');
270
+ });
271
+ });
@@ -0,0 +1,133 @@
1
+ 'use strict';
2
+
3
+ // Grok CLI has no runtime directory of its own: it reads the Claude install through its
4
+ // compatibility layer. `--platform grok` therefore provisions `.claude/` and prints the
5
+ // one thing a user cannot infer, which is that project hooks stay inert until the folder
6
+ // is trusted. These cases run the real installer into a temp git repository.
7
+
8
+ const assert = require('node:assert/strict');
9
+ const fs = require('node:fs');
10
+ const os = require('node:os');
11
+ const path = require('node:path');
12
+ const { spawnSync } = require('node:child_process');
13
+ const { test } = require('node:test');
14
+
15
+ const PACKAGE_ROOT = path.join(__dirname, '../..');
16
+ const INSTALLER = path.join(PACKAGE_ROOT, 'bin/install.js');
17
+ const { PLATFORMS, resolvePlatformAliases, detectPlatforms } = require(path.join(PACKAGE_ROOT, 'bin/lib/context'));
18
+ const I18N = require(path.join(PACKAGE_ROOT, 'bin/lib/i18n'));
19
+
20
+ function inTempProject(run) {
21
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-grok-platform-'));
22
+ try {
23
+ const init = spawnSync('git', ['-C', root, 'init', '-q'], { encoding: 'utf8' });
24
+ assert.equal(init.status, 0, init.stderr);
25
+ return run(root);
26
+ } finally {
27
+ fs.rmSync(root, { recursive: true, force: true });
28
+ }
29
+ }
30
+
31
+ function install(root, platform, extra = []) {
32
+ return spawnSync(process.execPath, [INSTALLER, '--platform', platform, '--yes', ...extra],
33
+ { cwd: root, encoding: 'utf8', env: { ...process.env, HOME: path.join(root, 'home'), PATH: '/usr/bin:/bin' } });
34
+ }
35
+
36
+ test('grok is an alias, not a platform of its own', () => {
37
+ assert.equal(PLATFORMS.grok, undefined, 'a registry entry would create a .grok payload');
38
+ assert.deepEqual(resolvePlatformAliases(['grok']).platforms, ['claude']);
39
+ assert.equal(resolvePlatformAliases(['grok']).aliased, true);
40
+ assert.equal(resolvePlatformAliases(['claude']).aliased, false);
41
+ });
42
+
43
+ test('--platform grok installs the Claude runtime and no .grok directory', () => {
44
+ inTempProject((root) => {
45
+ const result = install(root, 'grok');
46
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
47
+
48
+ assert.equal(fs.existsSync(path.join(root, '.claude', 'hooks', 'privacy-block.cjs')), true,
49
+ 'grok runs the Claude gate scripts, so they have to be installed');
50
+ assert.equal(fs.existsSync(path.join(root, '.claude', 'settings.json')), true,
51
+ 'grok discovers the hooks through this file');
52
+ assert.equal(fs.existsSync(path.join(root, '.grok')), false, 'CafeKit ships nothing under .grok');
53
+
54
+ const metadata = JSON.parse(fs.readFileSync(path.join(root, '.claude', 'cafekit.json'), 'utf8'));
55
+ assert.equal(metadata.platform, 'claude', 'the install is a Claude install and must record itself as one');
56
+ });
57
+ });
58
+
59
+ test('the trust reminder is printed once in the selected language', () => {
60
+ inTempProject((root) => {
61
+ const en = install(root, 'grok', ['--lang', 'en']);
62
+ assert.equal(en.status, 0, en.stderr);
63
+ const occurrences = en.stdout.split('grok --trust').length - 1;
64
+ assert.equal(occurrences, 1, 'the notice must appear exactly once');
65
+ assert.match(en.stdout, /\/hooks-trust/, 'both ways to grant trust are named');
66
+ });
67
+
68
+ inTempProject((root) => {
69
+ const vi = install(root, 'grok', ['--lang', 'vi']);
70
+ assert.equal(vi.status, 0, vi.stderr);
71
+ // The Vietnamese string, not the English one: the notice follows --lang like every
72
+ // other installer line.
73
+ assert.match(vi.stdout, /lớp tương thích Claude/);
74
+ });
75
+ });
76
+
77
+ test('grok and claude together install once', () => {
78
+ inTempProject((root) => {
79
+ const result = install(root, 'grok,claude');
80
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
81
+ const lines = result.stdout.split('\n').filter((l) => l.includes('Claude Code'));
82
+ assert.ok(lines.length > 0, 'the Claude install must be reported');
83
+ const metadata = JSON.parse(fs.readFileSync(path.join(root, '.claude', 'cafekit.json'), 'utf8'));
84
+ assert.equal(metadata.platform, 'claude');
85
+ assert.equal(fs.existsSync(path.join(root, '.grok')), false);
86
+ });
87
+ });
88
+
89
+ test('a plain claude install prints no grok notice', () => {
90
+ inTempProject((root) => {
91
+ const result = install(root, 'claude');
92
+ assert.equal(result.status, 0, result.stderr);
93
+ assert.doesNotMatch(result.stdout, /grok --trust/, 'the alias must leave the Claude path unchanged');
94
+ });
95
+ });
96
+
97
+ test('.grok is not a detection marker', () => {
98
+ // A repository that merely holds `.grok/` has not asked for CafeKit. Adding `.grok` to
99
+ // Claude's detection markers would make a reinstall in a Codex project silently grow a
100
+ // `.claude/` tree beside it, because detected platforms are unioned with saved ones.
101
+ // (An install with no platform at all still defaults to Claude; that is separate, and
102
+ // predates this alias.)
103
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-grok-detect-'));
104
+ const cwd = process.cwd();
105
+ try {
106
+ fs.mkdirSync(path.join(root, '.grok', 'agents'), { recursive: true });
107
+ fs.mkdirSync(path.join(root, '.codex'), { recursive: true });
108
+ process.chdir(root);
109
+ const detected = detectPlatforms();
110
+ assert.deepEqual(detected, ['codex'], 'only the platform with a real payload may be detected');
111
+ } finally {
112
+ process.chdir(cwd);
113
+ fs.rmSync(root, { recursive: true, force: true });
114
+ }
115
+ });
116
+
117
+ test('an unknown platform is still rejected', () => {
118
+ inTempProject((root) => {
119
+ const result = install(root, 'nonesuch');
120
+ assert.notEqual(result.status, 0, 'the alias table must not swallow a typo');
121
+ assert.match(`${result.stdout}${result.stderr}`, /Unknown platform/);
122
+ });
123
+ });
124
+
125
+ test('the notice exists in every shipped locale', () => {
126
+ // A missing key renders as the key name, which is how a translation gap ships silently.
127
+ for (const code of I18N.SUPPORTED) {
128
+ const text = I18N.MESSAGES?.[code]?.grokCompatNotice ?? I18N[code]?.grokCompatNotice;
129
+ assert.ok(typeof text === 'string' && text.length > 0, `missing grokCompatNotice for ${code}`);
130
+ assert.match(text, /grok --trust/, `${code} must name the command`);
131
+ assert.match(text, /hooks-trust/, `${code} must name the slash command`);
132
+ }
133
+ });
@@ -0,0 +1,194 @@
1
+ 'use strict';
2
+
3
+ // The gate hooks read Claude Code's envelope. Grok CLI runs the same scripts through its
4
+ // Claude-compatibility layer but sends camelCase keys and its own tool names, and omp's
5
+ // bridge sends Claude keys with lowercase tool names. Each case below states the failure
6
+ // it prevents, because every one of them is a gate that looks installed and answers
7
+ // nothing when the reader gets a spelling wrong.
8
+ //
9
+ // Envelope samples follow grok 1.0.13's embedded hooks documentation; regenerate it with
10
+ // `strings -n 6 ~/.grok/downloads/grok-1.0.13-macos-aarch64`.
11
+
12
+ const assert = require('node:assert/strict');
13
+ const path = require('node:path');
14
+ const { test } = require('node:test');
15
+
16
+ const PACKAGE_ROOT = path.join(__dirname, '../..');
17
+ const { normalizeHookPayload, normalizeToolName } = require(
18
+ path.join(PACKAGE_ROOT, 'src/claude/hooks/lib/hook-payload.cjs'));
19
+
20
+ test('a grok PreToolUse envelope becomes the Claude payload', () => {
21
+ const grok = {
22
+ hookEventName: 'pre_tool_use',
23
+ sessionId: 'abc-123',
24
+ cwd: '/Users/you/project',
25
+ workspaceRoot: '/Users/you/project',
26
+ permissionMode: 'default',
27
+ toolName: 'run_terminal_command',
28
+ toolInput: { command: 'npm test' },
29
+ toolUseId: 'call_1',
30
+ timestamp: '2026-04-14T12:00:00Z',
31
+ };
32
+ const out = normalizeHookPayload(grok);
33
+ // The four keys every gate reads first. Without them privacy-block sees no tool and
34
+ // no command, and allows a call it was installed to examine.
35
+ assert.equal(out.hook_event_name, 'PreToolUse');
36
+ assert.equal(out.session_id, 'abc-123');
37
+ assert.equal(out.tool_name, 'Bash');
38
+ assert.deepEqual(out.tool_input, { command: 'npm test' });
39
+ assert.equal(out.tool_use_id, 'call_1');
40
+ assert.equal(out.permission_mode, 'default');
41
+ assert.equal(out.workspace_root, '/Users/you/project');
42
+ assert.equal(out.cwd, '/Users/you/project', 'cwd is already Claude-shaped and must survive');
43
+ });
44
+
45
+ test('read_file path becomes file_path', () => {
46
+ const out = normalizeHookPayload({
47
+ hookEventName: 'pre_tool_use',
48
+ toolName: 'read_file',
49
+ toolInput: { path: '/p/.env' },
50
+ });
51
+ assert.equal(out.tool_name, 'Read');
52
+ // privacy-block reads file_path and path, but task-scaffold-guard reads file_path first
53
+ // and inspect-block's path list is keyed the same way.
54
+ assert.equal(out.tool_input.file_path, '/p/.env');
55
+ assert.equal(out.tool_input.path, '/p/.env', 'the original key stays for hooks that read it');
56
+ });
57
+
58
+ test('an existing file_path is never overwritten by path', () => {
59
+ const out = normalizeHookPayload({
60
+ toolName: 'read_file',
61
+ toolInput: { file_path: '/p/real.txt', path: '/p/other.txt' },
62
+ });
63
+ assert.equal(out.tool_input.file_path, '/p/real.txt');
64
+ });
65
+
66
+ test('Stop and subagent fields map', () => {
67
+ const stop = normalizeHookPayload({
68
+ hookEventName: 'stop',
69
+ sessionId: 's1',
70
+ stopHookActive: true,
71
+ lastAssistantMessage: 'done',
72
+ reason: 'end_turn',
73
+ });
74
+ // Without stop_hook_active the Stop gate loses its loop guard and blocks every
75
+ // continuation round until the host's own cap ends the turn.
76
+ assert.equal(stop.hook_event_name, 'Stop');
77
+ assert.equal(stop.stop_hook_active, true);
78
+ assert.equal(stop.last_assistant_message, 'done');
79
+
80
+ const sub = normalizeHookPayload({
81
+ hookEventName: 'subagent_stop',
82
+ subagentType: 'code-auditor',
83
+ subagentId: 'agent-7',
84
+ lastAssistantMessage: 'REVIEW: PASS',
85
+ });
86
+ // semantic-review-authority refuses to record an attestation without agent_id.
87
+ assert.equal(sub.hook_event_name, 'SubagentStop');
88
+ assert.equal(sub.agent_type, 'code-auditor');
89
+ assert.equal(sub.agent_id, 'agent-7');
90
+
91
+ const post = normalizeHookPayload({ hookEventName: 'post_tool_use', toolResult: { ok: true } });
92
+ assert.deepEqual(post.tool_response, { ok: true });
93
+ });
94
+
95
+ test('every name reaching a Bash matcher becomes Bash', () => {
96
+ // Grok expands the Bash matcher to its own shell tools, so a call can arrive under any
97
+ // of these names. One unmapped name means privacy-block skips its command scan and a
98
+ // secret-bearing command is never examined.
99
+ for (const name of ['run_terminal_command', 'run_terminal_cmd', 'PowerShell', 'bash']) {
100
+ assert.equal(normalizeToolName(name), 'Bash', `${name} must reach the Bash rule`);
101
+ }
102
+ });
103
+
104
+ test('write and search_replace map to different Claude tools', () => {
105
+ // The scaffold guard only fires on Write. Grok creates files with `write` and edits
106
+ // them with `search_replace`; conflating them would either disable the guard or make it
107
+ // reject the Edit-fill step the guard's own message tells the model to run.
108
+ assert.equal(normalizeToolName('write'), 'Write');
109
+ assert.equal(normalizeToolName('delete_file'), 'Write');
110
+ assert.equal(normalizeToolName('search_replace'), 'Edit');
111
+ assert.equal(normalizeToolName('read_file'), 'Read');
112
+ assert.equal(normalizeToolName('grep'), 'Grep');
113
+ assert.equal(normalizeToolName('grep_search'), 'Grep');
114
+ assert.equal(normalizeToolName('list_dir'), 'Glob');
115
+ assert.equal(normalizeToolName('spawn_subagent'), 'Task');
116
+ });
117
+
118
+ test('omp lowercase names are aliased even under a snake_case key', () => {
119
+ // omp's bridge sends Claude-shaped keys with lowercase values, so a reader that only
120
+ // translated camelCase keys would leave `bash` unmapped and reopen the exact gap the
121
+ // omp tool-name table was written to close.
122
+ const out = normalizeHookPayload({
123
+ hook_event_name: 'PreToolUse',
124
+ session_id: 'omp-1',
125
+ tool_name: 'bash',
126
+ tool_input: { command: 'cat .env' },
127
+ });
128
+ assert.equal(out.tool_name, 'Bash');
129
+ for (const [omp, claude] of [['read', 'Read'], ['edit', 'Edit'], ['write', 'Write'], ['grep', 'Grep'], ['ls', 'Glob']]) {
130
+ assert.equal(normalizeToolName(omp), claude);
131
+ }
132
+ });
133
+
134
+ test('a Claude payload keeps its keys', () => {
135
+ const claude = {
136
+ hook_event_name: 'PreToolUse',
137
+ session_id: 's1',
138
+ cwd: '/p',
139
+ tool_name: 'Bash',
140
+ tool_input: { command: 'ls' },
141
+ transcript_path: '/p/t.jsonl',
142
+ };
143
+ const before = JSON.stringify(claude);
144
+ const out = normalizeHookPayload(claude);
145
+ assert.deepEqual(out, claude, 'a Claude payload must come back with the same keys and values');
146
+ assert.notEqual(out, claude, 'the reader returns a copy');
147
+ assert.equal(JSON.stringify(claude), before, 'the input must not be mutated');
148
+ });
149
+
150
+ test('an unknown tool name is left alone', () => {
151
+ // A future host tool degrades to "not specially handled" rather than being renamed into
152
+ // a privileged rule it does not belong to.
153
+ assert.equal(normalizeToolName('some_future_tool'), 'some_future_tool');
154
+ assert.equal(normalizeToolName('Bash'), 'Bash');
155
+ const out = normalizeHookPayload({ toolName: 'some_future_tool', toolInput: {} });
156
+ assert.equal(out.tool_name, 'some_future_tool');
157
+ });
158
+
159
+ test('hostile input never throws', () => {
160
+ // The Stop gate and the approval hook end in a catch that emits a block, so an
161
+ // exception raised here would block completion on Claude, not just under grok.
162
+ const hostile = [
163
+ null, undefined, 42, 'string', true, [],
164
+ { toolName: 42, toolInput: 'not-an-object' },
165
+ { toolInput: [] },
166
+ { hookEventName: {} },
167
+ JSON.parse('{"__proto__": {"polluted": true}, "toolName": "read_file"}'),
168
+ Object.create(null),
169
+ ];
170
+ for (const input of hostile) {
171
+ assert.doesNotThrow(() => normalizeHookPayload(input), `threw on ${JSON.stringify(input)}`);
172
+ }
173
+ assert.equal(normalizeHookPayload(null), null);
174
+ assert.equal(normalizeHookPayload(42), 42);
175
+ assert.deepEqual(normalizeHookPayload([]), []);
176
+ assert.equal({}.polluted, undefined, 'a __proto__ key must not reach Object.prototype');
177
+ });
178
+
179
+ test('the event map covers every event grok emits', () => {
180
+ const expected = {
181
+ pre_tool_use: 'PreToolUse', post_tool_use: 'PostToolUse',
182
+ post_tool_use_failure: 'PostToolUseFailure', permission_denied: 'PermissionDenied',
183
+ user_prompt_submit: 'UserPromptSubmit', session_start: 'SessionStart',
184
+ session_end: 'SessionEnd', stop: 'Stop', stop_failure: 'StopFailure',
185
+ stop_cancelled: 'StopCancelled', subagent_start: 'SubagentStart',
186
+ subagent_stop: 'SubagentStop', pre_compact: 'PreCompact', post_compact: 'PostCompact',
187
+ notification: 'Notification',
188
+ };
189
+ for (const [snake, pascal] of Object.entries(expected)) {
190
+ assert.equal(normalizeHookPayload({ hookEventName: snake }).hook_event_name, pascal);
191
+ }
192
+ assert.equal(normalizeHookPayload({ hook_event_name: 'Stop' }).hook_event_name, 'Stop',
193
+ 'an already-PascalCase value is kept');
194
+ });
@@ -228,8 +228,8 @@ test('combined install keeps CORE neutral and records native shared-root trade-o
228
228
  [agents, CODEX_START]
229
229
  ]) assert.equal((content.match(new RegExp(marker, 'g')) || []).length, 1);
230
230
 
231
- assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/hapo:|\$hapo-/i);
232
- assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$hapo-/i);
231
+ assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/cf:|\$cf-/i);
232
+ assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$cf-/i);
233
233
  assert.match(codex, /native project instruction surface is root `AGENTS\.md`/);
234
234
  assert.match(agents, /shared-root trade-off is intentional/);
235
235
  // H5 remediation: ownership/ignore contract must be explicit — not just marker presence
@@ -241,7 +241,7 @@ test('combined install keeps CORE neutral and records native shared-root trade-o
241
241
  assert.doesNotMatch(codex, /If you are Codex CLI or any other runtime, ignore this entire Codex block/i);
242
242
  assert.match(codex, /fail-safe/i);
243
243
  // CORE must not contain runtime-specific directives that would leak
244
- assert.doesNotMatch(core, /\$hapo-|hapo:/i);
244
+ assert.doesNotMatch(core, /\$cf-|cf:/i);
245
245
  // No cross-runtime directive leakage: Claude block must not contain foreign runtime directives
246
246
  assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT CODEX /);
247
247
  assert.doesNotMatch(codex, /<!-- CAFEKIT CORE /);