failproofai 0.0.2-beta.7 → 0.0.2-beta.9

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 (390) hide show
  1. package/.next/standalone/.claude/settings.json +316 -0
  2. package/.next/standalone/.failproofai/policies/workflow-policies.mjs +62 -0
  3. package/.next/standalone/.failproofai/policies-config.json +39 -0
  4. package/.next/standalone/.next/BUILD_ID +1 -1
  5. package/.next/standalone/.next/build-manifest.json +3 -3
  6. package/.next/standalone/.next/prerender-manifest.json +3 -3
  7. package/.next/standalone/.next/required-server-files.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  22. package/.next/standalone/.next/server/app/_not-found.rsc +17 -17
  23. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +17 -17
  24. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  25. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +11 -11
  26. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  27. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  28. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/index.html +1 -1
  30. package/.next/standalone/.next/server/app/index.rsc +16 -16
  31. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  32. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +16 -16
  33. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  34. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +11 -11
  35. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  36. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  37. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  40. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  43. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  46. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  47. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  50. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +3 -0
  53. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kjo7d_._.js +1 -1
  54. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  55. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
  56. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
  57. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
  58. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
  59. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
  60. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__05zi2mt._.js → [root-of-the-server]__0vn1ciw._.js} +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -1
  62. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0kkt_9z._.js → [root-of-the-server]__0z-n~~r._.js} +2 -2
  63. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
  64. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
  65. package/.next/standalone/.next/server/chunks/ssr/_0x..fj-._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
  67. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
  69. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  70. package/.next/standalone/.next/server/pages/404.html +2 -2
  71. package/.next/standalone/.next/server/pages/500.html +1 -1
  72. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  74. package/.next/standalone/.next/static/chunks/{0ltx5i0xv85_s.js → 04wavch6dsfes.js} +1 -1
  75. package/.next/standalone/.next/static/chunks/{13jdpvk~s2da8.js → 0drr--vxs_m-c.js} +1 -1
  76. package/.next/standalone/.next/static/chunks/{0jf9lx3rkmqx_.css → 0gu_a.a80ritd.css} +1 -1
  77. package/.next/standalone/.next/static/chunks/{0e76l4~hq_sei.js → 0i1ilz5554nv9.js} +1 -1
  78. package/.next/standalone/.next/static/chunks/{0suauczjqzn07.js → 0keqg6-cjs8aa.js} +1 -1
  79. package/.next/standalone/.next/static/chunks/{0w.rtg9.m8dk-.js → 0myzx7y.rqqi3.js} +2 -2
  80. package/.next/standalone/.next/static/chunks/{02u4v.k5amfah.js → 0zfrusm~j404v.js} +1 -1
  81. package/.next/standalone/.next/static/chunks/{0bkizbynk9via.js → 10xhknzfyigcu.js} +1 -1
  82. package/.next/standalone/.next/static/chunks/{0q7atesxo-36k.js → 16yg3xhkmdb9v.js} +1 -1
  83. package/.next/standalone/CHANGELOG.md +27 -0
  84. package/.next/standalone/CLAUDE.md +14 -0
  85. package/.next/standalone/README.md +16 -0
  86. package/.next/standalone/bun.lock +45 -0
  87. package/.next/standalone/dist/cli.mjs +44 -23
  88. package/.next/standalone/docs/ar/architecture.mdx +333 -0
  89. package/.next/standalone/docs/ar/built-in-policies.mdx +566 -0
  90. package/.next/standalone/docs/ar/cli/dashboard.mdx +28 -0
  91. package/.next/standalone/docs/ar/cli/environment-variables.mdx +34 -0
  92. package/.next/standalone/docs/ar/cli/hook.mdx +31 -0
  93. package/.next/standalone/docs/ar/cli/install-policies.mdx +49 -0
  94. package/.next/standalone/docs/ar/cli/list-policies.mdx +31 -0
  95. package/.next/standalone/docs/ar/cli/remove-policies.mdx +45 -0
  96. package/.next/standalone/docs/ar/cli/version.mdx +13 -0
  97. package/.next/standalone/docs/ar/configuration.mdx +223 -0
  98. package/.next/standalone/docs/ar/custom-policies.mdx +359 -0
  99. package/.next/standalone/docs/ar/dashboard.mdx +142 -0
  100. package/.next/standalone/docs/ar/examples.mdx +254 -0
  101. package/.next/standalone/docs/ar/for-agents.mdx +39 -0
  102. package/.next/standalone/docs/ar/getting-started.mdx +134 -0
  103. package/.next/standalone/docs/ar/introduction.mdx +58 -0
  104. package/.next/standalone/docs/ar/package-aliases.mdx +82 -0
  105. package/.next/standalone/docs/ar/testing.mdx +261 -0
  106. package/.next/standalone/docs/built-in-policies.mdx +17 -1
  107. package/.next/standalone/docs/configuration.mdx +1 -1
  108. package/.next/standalone/docs/custom-policies.mdx +3 -3
  109. package/.next/standalone/docs/de/architecture.mdx +332 -0
  110. package/.next/standalone/docs/de/built-in-policies.mdx +564 -0
  111. package/.next/standalone/docs/de/cli/dashboard.mdx +28 -0
  112. package/.next/standalone/docs/de/cli/environment-variables.mdx +34 -0
  113. package/.next/standalone/docs/de/cli/hook.mdx +30 -0
  114. package/.next/standalone/docs/de/cli/install-policies.mdx +48 -0
  115. package/.next/standalone/docs/de/cli/list-policies.mdx +31 -0
  116. package/.next/standalone/docs/de/cli/remove-policies.mdx +44 -0
  117. package/.next/standalone/docs/de/cli/version.mdx +12 -0
  118. package/.next/standalone/docs/de/configuration.mdx +222 -0
  119. package/.next/standalone/docs/de/custom-policies.mdx +357 -0
  120. package/.next/standalone/docs/de/dashboard.mdx +142 -0
  121. package/.next/standalone/docs/de/examples.mdx +253 -0
  122. package/.next/standalone/docs/de/for-agents.mdx +38 -0
  123. package/.next/standalone/docs/de/getting-started.mdx +134 -0
  124. package/.next/standalone/docs/de/introduction.mdx +57 -0
  125. package/.next/standalone/docs/de/package-aliases.mdx +82 -0
  126. package/.next/standalone/docs/de/testing.mdx +260 -0
  127. package/.next/standalone/docs/docs.json +922 -35
  128. package/.next/standalone/docs/es/architecture.mdx +332 -0
  129. package/.next/standalone/docs/es/built-in-policies.mdx +564 -0
  130. package/.next/standalone/docs/es/cli/dashboard.mdx +28 -0
  131. package/.next/standalone/docs/es/cli/environment-variables.mdx +34 -0
  132. package/.next/standalone/docs/es/cli/hook.mdx +30 -0
  133. package/.next/standalone/docs/es/cli/install-policies.mdx +48 -0
  134. package/.next/standalone/docs/es/cli/list-policies.mdx +31 -0
  135. package/.next/standalone/docs/es/cli/remove-policies.mdx +44 -0
  136. package/.next/standalone/docs/es/cli/version.mdx +12 -0
  137. package/.next/standalone/docs/es/configuration.mdx +222 -0
  138. package/.next/standalone/docs/es/custom-policies.mdx +357 -0
  139. package/.next/standalone/docs/es/dashboard.mdx +142 -0
  140. package/.next/standalone/docs/es/examples.mdx +253 -0
  141. package/.next/standalone/docs/es/for-agents.mdx +38 -0
  142. package/.next/standalone/docs/es/getting-started.mdx +134 -0
  143. package/.next/standalone/docs/es/introduction.mdx +57 -0
  144. package/.next/standalone/docs/es/package-aliases.mdx +82 -0
  145. package/.next/standalone/docs/es/testing.mdx +260 -0
  146. package/.next/standalone/docs/fr/architecture.mdx +332 -0
  147. package/.next/standalone/docs/fr/built-in-policies.mdx +564 -0
  148. package/.next/standalone/docs/fr/cli/dashboard.mdx +28 -0
  149. package/.next/standalone/docs/fr/cli/environment-variables.mdx +34 -0
  150. package/.next/standalone/docs/fr/cli/hook.mdx +30 -0
  151. package/.next/standalone/docs/fr/cli/install-policies.mdx +48 -0
  152. package/.next/standalone/docs/fr/cli/list-policies.mdx +31 -0
  153. package/.next/standalone/docs/fr/cli/remove-policies.mdx +44 -0
  154. package/.next/standalone/docs/fr/cli/version.mdx +12 -0
  155. package/.next/standalone/docs/fr/configuration.mdx +222 -0
  156. package/.next/standalone/docs/fr/custom-policies.mdx +357 -0
  157. package/.next/standalone/docs/fr/dashboard.mdx +142 -0
  158. package/.next/standalone/docs/fr/examples.mdx +253 -0
  159. package/.next/standalone/docs/fr/for-agents.mdx +38 -0
  160. package/.next/standalone/docs/fr/getting-started.mdx +134 -0
  161. package/.next/standalone/docs/fr/introduction.mdx +57 -0
  162. package/.next/standalone/docs/fr/package-aliases.mdx +82 -0
  163. package/.next/standalone/docs/fr/testing.mdx +260 -0
  164. package/.next/standalone/docs/he/architecture.mdx +333 -0
  165. package/.next/standalone/docs/he/built-in-policies.mdx +564 -0
  166. package/.next/standalone/docs/he/cli/dashboard.mdx +28 -0
  167. package/.next/standalone/docs/he/cli/environment-variables.mdx +34 -0
  168. package/.next/standalone/docs/he/cli/hook.mdx +30 -0
  169. package/.next/standalone/docs/he/cli/install-policies.mdx +48 -0
  170. package/.next/standalone/docs/he/cli/list-policies.mdx +32 -0
  171. package/.next/standalone/docs/he/cli/remove-policies.mdx +44 -0
  172. package/.next/standalone/docs/he/cli/version.mdx +12 -0
  173. package/.next/standalone/docs/he/configuration.mdx +222 -0
  174. package/.next/standalone/docs/he/custom-policies.mdx +357 -0
  175. package/.next/standalone/docs/he/dashboard.mdx +142 -0
  176. package/.next/standalone/docs/he/examples.mdx +253 -0
  177. package/.next/standalone/docs/he/for-agents.mdx +38 -0
  178. package/.next/standalone/docs/he/getting-started.mdx +135 -0
  179. package/.next/standalone/docs/he/introduction.mdx +57 -0
  180. package/.next/standalone/docs/he/package-aliases.mdx +82 -0
  181. package/.next/standalone/docs/he/testing.mdx +260 -0
  182. package/.next/standalone/docs/hi/architecture.mdx +334 -0
  183. package/.next/standalone/docs/hi/built-in-policies.mdx +564 -0
  184. package/.next/standalone/docs/hi/cli/dashboard.mdx +28 -0
  185. package/.next/standalone/docs/hi/cli/environment-variables.mdx +34 -0
  186. package/.next/standalone/docs/hi/cli/hook.mdx +30 -0
  187. package/.next/standalone/docs/hi/cli/install-policies.mdx +48 -0
  188. package/.next/standalone/docs/hi/cli/list-policies.mdx +31 -0
  189. package/.next/standalone/docs/hi/cli/remove-policies.mdx +44 -0
  190. package/.next/standalone/docs/hi/cli/version.mdx +12 -0
  191. package/.next/standalone/docs/hi/configuration.mdx +222 -0
  192. package/.next/standalone/docs/hi/custom-policies.mdx +357 -0
  193. package/.next/standalone/docs/hi/dashboard.mdx +142 -0
  194. package/.next/standalone/docs/hi/examples.mdx +255 -0
  195. package/.next/standalone/docs/hi/for-agents.mdx +38 -0
  196. package/.next/standalone/docs/hi/getting-started.mdx +134 -0
  197. package/.next/standalone/docs/hi/introduction.mdx +57 -0
  198. package/.next/standalone/docs/hi/package-aliases.mdx +82 -0
  199. package/.next/standalone/docs/hi/testing.mdx +260 -0
  200. package/.next/standalone/docs/i18n/README.ar.md +312 -0
  201. package/.next/standalone/docs/i18n/README.de.md +307 -0
  202. package/.next/standalone/docs/i18n/README.es.md +307 -0
  203. package/.next/standalone/docs/i18n/README.fr.md +307 -0
  204. package/.next/standalone/docs/i18n/README.he.md +312 -0
  205. package/.next/standalone/docs/i18n/README.hi.md +307 -0
  206. package/.next/standalone/docs/i18n/README.it.md +307 -0
  207. package/.next/standalone/docs/i18n/README.ja.md +307 -0
  208. package/.next/standalone/docs/i18n/README.ko.md +307 -0
  209. package/.next/standalone/docs/i18n/README.pt-br.md +307 -0
  210. package/.next/standalone/docs/i18n/README.ru.md +308 -0
  211. package/.next/standalone/docs/i18n/README.tr.md +308 -0
  212. package/.next/standalone/docs/i18n/README.vi.md +308 -0
  213. package/.next/standalone/docs/i18n/README.zh.md +307 -0
  214. package/.next/standalone/docs/it/architecture.mdx +333 -0
  215. package/.next/standalone/docs/it/built-in-policies.mdx +564 -0
  216. package/.next/standalone/docs/it/cli/dashboard.mdx +28 -0
  217. package/.next/standalone/docs/it/cli/environment-variables.mdx +34 -0
  218. package/.next/standalone/docs/it/cli/hook.mdx +30 -0
  219. package/.next/standalone/docs/it/cli/install-policies.mdx +48 -0
  220. package/.next/standalone/docs/it/cli/list-policies.mdx +31 -0
  221. package/.next/standalone/docs/it/cli/remove-policies.mdx +44 -0
  222. package/.next/standalone/docs/it/cli/version.mdx +12 -0
  223. package/.next/standalone/docs/it/configuration.mdx +223 -0
  224. package/.next/standalone/docs/it/custom-policies.mdx +358 -0
  225. package/.next/standalone/docs/it/dashboard.mdx +142 -0
  226. package/.next/standalone/docs/it/examples.mdx +253 -0
  227. package/.next/standalone/docs/it/for-agents.mdx +38 -0
  228. package/.next/standalone/docs/it/getting-started.mdx +134 -0
  229. package/.next/standalone/docs/it/introduction.mdx +57 -0
  230. package/.next/standalone/docs/it/package-aliases.mdx +82 -0
  231. package/.next/standalone/docs/it/testing.mdx +260 -0
  232. package/.next/standalone/docs/ja/architecture.mdx +332 -0
  233. package/.next/standalone/docs/ja/built-in-policies.mdx +562 -0
  234. package/.next/standalone/docs/ja/cli/dashboard.mdx +28 -0
  235. package/.next/standalone/docs/ja/cli/environment-variables.mdx +34 -0
  236. package/.next/standalone/docs/ja/cli/hook.mdx +30 -0
  237. package/.next/standalone/docs/ja/cli/install-policies.mdx +48 -0
  238. package/.next/standalone/docs/ja/cli/list-policies.mdx +31 -0
  239. package/.next/standalone/docs/ja/cli/remove-policies.mdx +44 -0
  240. package/.next/standalone/docs/ja/cli/version.mdx +12 -0
  241. package/.next/standalone/docs/ja/configuration.mdx +222 -0
  242. package/.next/standalone/docs/ja/custom-policies.mdx +357 -0
  243. package/.next/standalone/docs/ja/dashboard.mdx +142 -0
  244. package/.next/standalone/docs/ja/examples.mdx +253 -0
  245. package/.next/standalone/docs/ja/for-agents.mdx +38 -0
  246. package/.next/standalone/docs/ja/getting-started.mdx +134 -0
  247. package/.next/standalone/docs/ja/introduction.mdx +57 -0
  248. package/.next/standalone/docs/ja/package-aliases.mdx +82 -0
  249. package/.next/standalone/docs/ja/testing.mdx +260 -0
  250. package/.next/standalone/docs/ko/architecture.mdx +332 -0
  251. package/.next/standalone/docs/ko/built-in-policies.mdx +562 -0
  252. package/.next/standalone/docs/ko/cli/dashboard.mdx +28 -0
  253. package/.next/standalone/docs/ko/cli/environment-variables.mdx +34 -0
  254. package/.next/standalone/docs/ko/cli/hook.mdx +30 -0
  255. package/.next/standalone/docs/ko/cli/install-policies.mdx +48 -0
  256. package/.next/standalone/docs/ko/cli/list-policies.mdx +31 -0
  257. package/.next/standalone/docs/ko/cli/remove-policies.mdx +44 -0
  258. package/.next/standalone/docs/ko/cli/version.mdx +12 -0
  259. package/.next/standalone/docs/ko/configuration.mdx +222 -0
  260. package/.next/standalone/docs/ko/custom-policies.mdx +357 -0
  261. package/.next/standalone/docs/ko/dashboard.mdx +142 -0
  262. package/.next/standalone/docs/ko/examples.mdx +253 -0
  263. package/.next/standalone/docs/ko/for-agents.mdx +38 -0
  264. package/.next/standalone/docs/ko/getting-started.mdx +134 -0
  265. package/.next/standalone/docs/ko/introduction.mdx +57 -0
  266. package/.next/standalone/docs/ko/package-aliases.mdx +82 -0
  267. package/.next/standalone/docs/ko/testing.mdx +260 -0
  268. package/.next/standalone/docs/pt-br/architecture.mdx +332 -0
  269. package/.next/standalone/docs/pt-br/built-in-policies.mdx +564 -0
  270. package/.next/standalone/docs/pt-br/cli/dashboard.mdx +28 -0
  271. package/.next/standalone/docs/pt-br/cli/environment-variables.mdx +34 -0
  272. package/.next/standalone/docs/pt-br/cli/hook.mdx +30 -0
  273. package/.next/standalone/docs/pt-br/cli/install-policies.mdx +48 -0
  274. package/.next/standalone/docs/pt-br/cli/list-policies.mdx +31 -0
  275. package/.next/standalone/docs/pt-br/cli/remove-policies.mdx +44 -0
  276. package/.next/standalone/docs/pt-br/cli/version.mdx +12 -0
  277. package/.next/standalone/docs/pt-br/configuration.mdx +222 -0
  278. package/.next/standalone/docs/pt-br/custom-policies.mdx +357 -0
  279. package/.next/standalone/docs/pt-br/dashboard.mdx +142 -0
  280. package/.next/standalone/docs/pt-br/examples.mdx +253 -0
  281. package/.next/standalone/docs/pt-br/for-agents.mdx +38 -0
  282. package/.next/standalone/docs/pt-br/getting-started.mdx +134 -0
  283. package/.next/standalone/docs/pt-br/introduction.mdx +57 -0
  284. package/.next/standalone/docs/pt-br/package-aliases.mdx +82 -0
  285. package/.next/standalone/docs/pt-br/testing.mdx +260 -0
  286. package/.next/standalone/docs/ru/architecture.mdx +334 -0
  287. package/.next/standalone/docs/ru/built-in-policies.mdx +562 -0
  288. package/.next/standalone/docs/ru/cli/dashboard.mdx +28 -0
  289. package/.next/standalone/docs/ru/cli/environment-variables.mdx +34 -0
  290. package/.next/standalone/docs/ru/cli/hook.mdx +30 -0
  291. package/.next/standalone/docs/ru/cli/install-policies.mdx +48 -0
  292. package/.next/standalone/docs/ru/cli/list-policies.mdx +32 -0
  293. package/.next/standalone/docs/ru/cli/remove-policies.mdx +44 -0
  294. package/.next/standalone/docs/ru/cli/version.mdx +12 -0
  295. package/.next/standalone/docs/ru/configuration.mdx +223 -0
  296. package/.next/standalone/docs/ru/custom-policies.mdx +357 -0
  297. package/.next/standalone/docs/ru/dashboard.mdx +142 -0
  298. package/.next/standalone/docs/ru/examples.mdx +254 -0
  299. package/.next/standalone/docs/ru/for-agents.mdx +38 -0
  300. package/.next/standalone/docs/ru/getting-started.mdx +134 -0
  301. package/.next/standalone/docs/ru/introduction.mdx +57 -0
  302. package/.next/standalone/docs/ru/package-aliases.mdx +82 -0
  303. package/.next/standalone/docs/ru/testing.mdx +260 -0
  304. package/.next/standalone/docs/tr/architecture.mdx +333 -0
  305. package/.next/standalone/docs/tr/built-in-policies.mdx +562 -0
  306. package/.next/standalone/docs/tr/cli/dashboard.mdx +28 -0
  307. package/.next/standalone/docs/tr/cli/environment-variables.mdx +34 -0
  308. package/.next/standalone/docs/tr/cli/hook.mdx +30 -0
  309. package/.next/standalone/docs/tr/cli/install-policies.mdx +48 -0
  310. package/.next/standalone/docs/tr/cli/list-policies.mdx +31 -0
  311. package/.next/standalone/docs/tr/cli/remove-policies.mdx +45 -0
  312. package/.next/standalone/docs/tr/cli/version.mdx +12 -0
  313. package/.next/standalone/docs/tr/configuration.mdx +223 -0
  314. package/.next/standalone/docs/tr/custom-policies.mdx +357 -0
  315. package/.next/standalone/docs/tr/dashboard.mdx +142 -0
  316. package/.next/standalone/docs/tr/examples.mdx +253 -0
  317. package/.next/standalone/docs/tr/for-agents.mdx +38 -0
  318. package/.next/standalone/docs/tr/getting-started.mdx +134 -0
  319. package/.next/standalone/docs/tr/introduction.mdx +57 -0
  320. package/.next/standalone/docs/tr/package-aliases.mdx +82 -0
  321. package/.next/standalone/docs/tr/testing.mdx +260 -0
  322. package/.next/standalone/docs/vi/architecture.mdx +333 -0
  323. package/.next/standalone/docs/vi/built-in-policies.mdx +564 -0
  324. package/.next/standalone/docs/vi/cli/dashboard.mdx +28 -0
  325. package/.next/standalone/docs/vi/cli/environment-variables.mdx +34 -0
  326. package/.next/standalone/docs/vi/cli/hook.mdx +30 -0
  327. package/.next/standalone/docs/vi/cli/install-policies.mdx +48 -0
  328. package/.next/standalone/docs/vi/cli/list-policies.mdx +31 -0
  329. package/.next/standalone/docs/vi/cli/remove-policies.mdx +44 -0
  330. package/.next/standalone/docs/vi/cli/version.mdx +13 -0
  331. package/.next/standalone/docs/vi/configuration.mdx +222 -0
  332. package/.next/standalone/docs/vi/custom-policies.mdx +357 -0
  333. package/.next/standalone/docs/vi/dashboard.mdx +142 -0
  334. package/.next/standalone/docs/vi/examples.mdx +253 -0
  335. package/.next/standalone/docs/vi/for-agents.mdx +38 -0
  336. package/.next/standalone/docs/vi/getting-started.mdx +134 -0
  337. package/.next/standalone/docs/vi/introduction.mdx +57 -0
  338. package/.next/standalone/docs/vi/package-aliases.mdx +82 -0
  339. package/.next/standalone/docs/vi/testing.mdx +260 -0
  340. package/.next/standalone/docs/zh/architecture.mdx +332 -0
  341. package/.next/standalone/docs/zh/built-in-policies.mdx +562 -0
  342. package/.next/standalone/docs/zh/cli/dashboard.mdx +28 -0
  343. package/.next/standalone/docs/zh/cli/environment-variables.mdx +34 -0
  344. package/.next/standalone/docs/zh/cli/hook.mdx +30 -0
  345. package/.next/standalone/docs/zh/cli/install-policies.mdx +48 -0
  346. package/.next/standalone/docs/zh/cli/list-policies.mdx +31 -0
  347. package/.next/standalone/docs/zh/cli/remove-policies.mdx +44 -0
  348. package/.next/standalone/docs/zh/cli/version.mdx +12 -0
  349. package/.next/standalone/docs/zh/configuration.mdx +222 -0
  350. package/.next/standalone/docs/zh/custom-policies.mdx +357 -0
  351. package/.next/standalone/docs/zh/dashboard.mdx +142 -0
  352. package/.next/standalone/docs/zh/examples.mdx +253 -0
  353. package/.next/standalone/docs/zh/for-agents.mdx +38 -0
  354. package/.next/standalone/docs/zh/getting-started.mdx +134 -0
  355. package/.next/standalone/docs/zh/introduction.mdx +57 -0
  356. package/.next/standalone/docs/zh/package-aliases.mdx +82 -0
  357. package/.next/standalone/docs/zh/testing.mdx +260 -0
  358. package/.next/standalone/package.json +8 -2
  359. package/.next/standalone/scripts/translate-docs/cache.ts +62 -0
  360. package/.next/standalone/scripts/translate-docs/cli.ts +357 -0
  361. package/.next/standalone/scripts/translate-docs/config.ts +248 -0
  362. package/.next/standalone/scripts/translate-docs/mdx-translator.ts +153 -0
  363. package/.next/standalone/scripts/translate-docs/mintlify-nav.ts +107 -0
  364. package/.next/standalone/scripts/translate-docs/readme-translator.ts +154 -0
  365. package/.next/standalone/scripts/translate-docs/translator.ts +68 -0
  366. package/.next/standalone/scripts/translate-docs/types.ts +43 -0
  367. package/.next/standalone/server.js +1 -1
  368. package/.next/standalone/src/hooks/custom-hooks-loader.ts +12 -5
  369. package/.next/standalone/src/hooks/handler.ts +9 -3
  370. package/.next/standalone/src/hooks/manager.ts +10 -2
  371. package/.next/standalone/src/hooks/policy-evaluator.ts +20 -16
  372. package/README.md +16 -0
  373. package/dist/cli.mjs +44 -23
  374. package/package.json +8 -2
  375. package/scripts/translate-docs/cache.ts +62 -0
  376. package/scripts/translate-docs/cli.ts +357 -0
  377. package/scripts/translate-docs/config.ts +248 -0
  378. package/scripts/translate-docs/mdx-translator.ts +153 -0
  379. package/scripts/translate-docs/mintlify-nav.ts +107 -0
  380. package/scripts/translate-docs/readme-translator.ts +154 -0
  381. package/scripts/translate-docs/translator.ts +68 -0
  382. package/scripts/translate-docs/types.ts +43 -0
  383. package/src/hooks/custom-hooks-loader.ts +12 -5
  384. package/src/hooks/handler.ts +9 -3
  385. package/src/hooks/manager.ts +10 -2
  386. package/src/hooks/policy-evaluator.ts +20 -16
  387. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02nt~6d._.js +0 -3
  388. /package/.next/standalone/.next/static/{Opbai6exOQP2W488FWmr6 → XqGmAwGDuJ6fEQgD-8y60}/_buildManifest.js +0 -0
  389. /package/.next/standalone/.next/static/{Opbai6exOQP2W488FWmr6 → XqGmAwGDuJ6fEQgD-8y60}/_clientMiddlewareManifest.js +0 -0
  390. /package/.next/standalone/.next/static/{Opbai6exOQP2W488FWmr6 → XqGmAwGDuJ6fEQgD-8y60}/_ssgManifest.js +0 -0
@@ -0,0 +1,260 @@
1
+ ---
2
+ title: 测试
3
+ description: "单元测试、端到端测试与测试辅助工具"
4
+ icon: flask-vial
5
+ ---
6
+
7
+ failproofai 包含两套测试套件:**单元测试**(快速、使用 mock)和**端到端测试**(真实子进程调用)。
8
+
9
+ ---
10
+
11
+ ## 运行测试
12
+
13
+ ```bash
14
+ # 单次运行所有单元测试
15
+ bun run test:run
16
+
17
+ # 以监视模式运行单元测试
18
+ bun run test
19
+
20
+ # 运行端到端测试(需要配置环境,详见下文)
21
+ bun run test:e2e
22
+
23
+ # 仅进行类型检查,不构建
24
+ bunx tsc --noEmit
25
+
26
+ # 代码检查
27
+ bun run lint
28
+ ```
29
+
30
+ ---
31
+
32
+ ## 单元测试
33
+
34
+ 单元测试位于 `__tests__/` 目录,使用 [Vitest](https://vitest.dev) 和 `happy-dom`。
35
+
36
+ ```text
37
+ __tests__/
38
+ hooks/
39
+ builtin-policies.test.ts # 每个内置策略的逻辑测试
40
+ hooks-config.test.ts # 配置加载与作用域合并
41
+ policy-evaluator.test.ts # 参数注入与评估顺序
42
+ custom-hooks-registry.test.ts # globalThis 注册表的增删查
43
+ custom-hooks-loader.test.ts # ESM 加载器、传递性导入、错误处理
44
+ manager.test.ts # 安装/移除/列出操作
45
+ components/
46
+ sessions-list.test.tsx # 会话列表组件
47
+ project-list.test.tsx # 项目列表组件
48
+ ...
49
+ lib/
50
+ logger.test.ts
51
+ paths.test.ts
52
+ date-filters.test.ts
53
+ telemetry.test.ts
54
+ ...
55
+ actions/
56
+ get-hooks-config.test.ts
57
+ get-hook-activity.test.ts
58
+ ...
59
+ contexts/
60
+ ThemeContext.test.tsx
61
+ AutoRefreshContext.test.tsx
62
+ ```
63
+
64
+ ### 编写策略单元测试
65
+
66
+ ```typescript
67
+ import { describe, it, expect, beforeEach } from "vitest";
68
+ import { getBuiltinPolicies } from "../../src/hooks/builtin-policies";
69
+ import { allow, deny } from "../../src/hooks/policy-types";
70
+
71
+ describe("block-sudo", () => {
72
+ const policy = getBuiltinPolicies().find((p) => p.name === "block-sudo")!;
73
+
74
+ it("denies sudo commands", () => {
75
+ const ctx = {
76
+ eventType: "PreToolUse" as const,
77
+ payload: {},
78
+ toolName: "Bash",
79
+ toolInput: { command: "sudo apt install nodejs" },
80
+ params: { allowPatterns: [] },
81
+ };
82
+ expect(policy.fn(ctx)).toEqual(deny("sudo command blocked by failproofai"));
83
+ });
84
+
85
+ it("allows non-sudo commands", () => {
86
+ const ctx = {
87
+ eventType: "PreToolUse" as const,
88
+ payload: {},
89
+ toolName: "Bash",
90
+ toolInput: { command: "ls -la" },
91
+ params: { allowPatterns: [] },
92
+ };
93
+ expect(policy.fn(ctx)).toEqual(allow());
94
+ });
95
+
96
+ it("allows patterns in allowPatterns", () => {
97
+ const ctx = {
98
+ eventType: "PreToolUse" as const,
99
+ payload: {},
100
+ toolName: "Bash",
101
+ toolInput: { command: "sudo systemctl status nginx" },
102
+ params: { allowPatterns: ["sudo systemctl status"] },
103
+ };
104
+ expect(policy.fn(ctx)).toEqual(allow());
105
+ });
106
+ });
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 端到端测试
112
+
113
+ 端到端测试将真实的 `failproofai` 二进制文件作为子进程调用,通过 stdin 传入 JSON payload,并对 stdout 输出和退出码进行断言。这能够测试 Claude Code 所使用的完整集成路径。
114
+
115
+ ### 环境配置
116
+
117
+ 端到端测试直接从仓库源码运行二进制文件。在首次运行前,需要构建自定义 hook 文件从 `'failproofai'` 导入时所使用的 CJS 包:
118
+
119
+ ```bash
120
+ bun build src/index.ts --outdir dist --target node --format cjs
121
+ ```
122
+
123
+ 然后运行测试:
124
+
125
+ ```bash
126
+ bun run test:e2e
127
+ ```
128
+
129
+ 每当修改公共 hook API(`src/hooks/custom-hooks-registry.ts`、`src/hooks/policy-helpers.ts` 或 `src/hooks/policy-types.ts`)后,请重新构建 `dist/`。
130
+
131
+ ### 端到端测试结构
132
+
133
+ ```text
134
+ __tests__/e2e/
135
+ helpers/
136
+ hook-runner.ts # 启动二进制进程,传入 payload JSON,捕获退出码 + stdout + stderr
137
+ fixture-env.ts # 为每个测试创建包含配置文件的隔离临时目录
138
+ payloads.ts # 符合 Claude 格式的各事件类型 payload 工厂函数
139
+ hooks/
140
+ builtin-policies.e2e.test.ts # 每个内置策略的真实子进程测试
141
+ custom-hooks.e2e.test.ts # 自定义 hook 的加载与评估
142
+ config-scopes.e2e.test.ts # 跨项目/本地/全局的配置合并
143
+ policy-params.e2e.test.ts # 各参数化策略的参数注入
144
+ ```
145
+
146
+ ### 使用端到端测试辅助工具
147
+
148
+ **`FixtureEnv`** - 每个测试的隔离环境:
149
+
150
+ ```typescript
151
+ import { createFixtureEnv } from "../helpers/fixture-env";
152
+
153
+ const env = createFixtureEnv();
154
+ // env.cwd - 临时目录;作为 payload.cwd 传入以加载 .failproofai/policies-config.json
155
+ // env.home - 隔离的 home 目录;不会读取真实的 ~/.failproofai
156
+
157
+ env.writeConfig({
158
+ enabledPolicies: ["block-sudo"],
159
+ policyParams: {
160
+ "block-sudo": { allowPatterns: ["sudo systemctl status"] },
161
+ },
162
+ });
163
+ ```
164
+
165
+ `createFixtureEnv()` 会自动注册 `afterEach` 清理回调。
166
+
167
+ **`runHook`** - 调用二进制文件:
168
+
169
+ ```typescript
170
+ import { runHook } from "../helpers/hook-runner";
171
+ import { Payloads } from "../helpers/payloads";
172
+
173
+ const result = await runHook(
174
+ "PreToolUse",
175
+ Payloads.preToolUse.bash("sudo apt install nodejs", env.cwd),
176
+ { homeDir: env.home }
177
+ );
178
+
179
+ expect(result.exitCode).toBe(0);
180
+ expect(result.parsed?.hookSpecificOutput?.permissionDecision).toBe("deny");
181
+ ```
182
+
183
+ **`Payloads`** - 现成的 payload 工厂函数:
184
+
185
+ ```typescript
186
+ Payloads.preToolUse.bash(command, cwd)
187
+ Payloads.preToolUse.write(filePath, content, cwd)
188
+ Payloads.preToolUse.read(filePath, cwd)
189
+ Payloads.postToolUse.bash(command, output, cwd)
190
+ Payloads.postToolUse.read(filePath, content, cwd)
191
+ Payloads.notification(message, cwd)
192
+ Payloads.stop(cwd)
193
+ ```
194
+
195
+ ### 编写端到端测试
196
+
197
+ ```typescript
198
+ import { describe, it, expect } from "vitest";
199
+ import { createFixtureEnv } from "../helpers/fixture-env";
200
+ import { runHook } from "../helpers/hook-runner";
201
+ import { Payloads } from "../helpers/payloads";
202
+
203
+ describe("block-rm-rf (E2E)", () => {
204
+ it("denies rm -rf", async () => {
205
+ const env = createFixtureEnv();
206
+ env.writeConfig({ enabledPolicies: ["block-rm-rf"] });
207
+
208
+ const result = await runHook(
209
+ "PreToolUse",
210
+ Payloads.preToolUse.bash("rm -rf /", env.cwd),
211
+ { homeDir: env.home }
212
+ );
213
+
214
+ expect(result.exitCode).toBe(0);
215
+ expect(result.parsed?.hookSpecificOutput?.permissionDecision).toBe("deny");
216
+ });
217
+
218
+ it("allows non-recursive rm", async () => {
219
+ const env = createFixtureEnv();
220
+ env.writeConfig({ enabledPolicies: ["block-rm-rf"] });
221
+
222
+ const result = await runHook(
223
+ "PreToolUse",
224
+ Payloads.preToolUse.bash("rm /tmp/file.txt", env.cwd),
225
+ { homeDir: env.home }
226
+ );
227
+
228
+ expect(result.exitCode).toBe(0);
229
+ expect(result.stdout).toBe(""); // allow → 空 stdout
230
+ });
231
+ });
232
+ ```
233
+
234
+ ### 端到端响应格式
235
+
236
+ | 决策 | 退出码 | stdout |
237
+ |----------|-----------|--------|
238
+ | `PreToolUse` 拒绝 | `0` | `{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"..."}}` |
239
+ | `PostToolUse` 拒绝 | `0` | `{"hookSpecificOutput":{"additionalContext":"Blocked ... because: ..."}}` |
240
+ | 指令(非 Stop) | `0` | `{"hookSpecificOutput":{"additionalContext":"Instruction from failproofai: ..."}}` |
241
+ | Stop 指令 | `2` | stdout 为空;原因输出至 stderr |
242
+ | 允许 | `0` | 空字符串 |
243
+
244
+ ### Vitest 配置
245
+
246
+ 端到端测试使用 `vitest.config.e2e.mts`,配置如下:
247
+
248
+ - `environment: "node"` - 无需浏览器全局变量
249
+ - `pool: "forks"` - 真正的进程隔离(测试会派生子进程)
250
+ - `testTimeout: 20_000` - 每个测试 20 秒超时(含二进制启动和 hook 评估时间)
251
+
252
+ 使用 `forks` 池至关重要:基于线程的 worker 共享 `globalThis`,可能干扰需要派生子进程的测试。基于进程的 forks 模式可避免此问题。
253
+
254
+ ---
255
+
256
+ ## 持续集成
257
+
258
+ 在合并前,必须通过完整的 CI 运行(`bun run lint && bunx tsc --noEmit && bun run test:run && bun run build`)。端到端测试套件作为独立的 CI 任务并行运行。
259
+
260
+ 完整的合并前检查清单请参阅 [Contributing](../CONTRIBUTING.md)。
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.2-beta.7",
3
+ "version": "0.0.2-beta.9",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -32,7 +32,12 @@
32
32
  "preuninstall": "node scripts/preuninstall.mjs",
33
33
  "prepare": "bun run build",
34
34
  "test:e2e": "vitest run --config vitest.config.e2e.mts",
35
- "test:e2e:watch": "vitest --config vitest.config.e2e.mts"
35
+ "test:e2e:watch": "vitest --config vitest.config.e2e.mts",
36
+ "translate": "bun scripts/translate-docs/cli.ts",
37
+ "translate:readme": "bun scripts/translate-docs/cli.ts --readme-only",
38
+ "translate:docs": "bun scripts/translate-docs/cli.ts --docs-only",
39
+ "translate:dry-run": "bun scripts/translate-docs/cli.ts --dry-run",
40
+ "translate:validate": "bun scripts/translate-docs/cli.ts --validate"
36
41
  },
37
42
  "keywords": [
38
43
  "claude",
@@ -85,6 +90,7 @@
85
90
  "tailwind-merge": "^3.4.0",
86
91
  "tailwindcss": "^4.1.18",
87
92
  "typescript": "^6.0.2",
93
+ "@anthropic-ai/sdk": "^0.39.0",
88
94
  "vitest": "^4.0.18"
89
95
  },
90
96
  "dependencies": {
@@ -0,0 +1,62 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import type { CacheEntry, TranslationCache } from "./types";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const CACHE_FILE = join(__dirname, ".translation-cache.json");
9
+
10
+ export function contentHash(content: string): string {
11
+ return createHash("sha256").update(content).digest("hex").slice(0, 16);
12
+ }
13
+
14
+ export function readCache(): TranslationCache {
15
+ if (!existsSync(CACHE_FILE)) {
16
+ return { sourceHash: "", lastUpdated: "", translations: {} };
17
+ }
18
+ try {
19
+ return JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
20
+ } catch {
21
+ return { sourceHash: "", lastUpdated: "", translations: {} };
22
+ }
23
+ }
24
+
25
+ export function writeCache(cache: TranslationCache): void {
26
+ cache.lastUpdated = new Date().toISOString();
27
+ writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
28
+ }
29
+
30
+ export function getCacheKey(sourcePath: string, lang: string): string {
31
+ return `${sourcePath}::${lang}`;
32
+ }
33
+
34
+ export function isCached(
35
+ cache: TranslationCache,
36
+ sourcePath: string,
37
+ lang: string,
38
+ sourceContent: string,
39
+ ): boolean {
40
+ const key = getCacheKey(sourcePath, lang);
41
+ const entry = cache.translations[key];
42
+ if (!entry) return false;
43
+ return entry.sourceHash === contentHash(sourceContent);
44
+ }
45
+
46
+ export function setCacheEntry(
47
+ cache: TranslationCache,
48
+ sourcePath: string,
49
+ lang: string,
50
+ sourceContent: string,
51
+ inputTokens: number,
52
+ outputTokens: number,
53
+ ): void {
54
+ const key = getCacheKey(sourcePath, lang);
55
+ cache.translations[key] = {
56
+ sourceHash: contentHash(sourceContent),
57
+ targetLang: lang,
58
+ translatedAt: new Date().toISOString(),
59
+ inputTokens,
60
+ outputTokens,
61
+ };
62
+ }
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env bun
2
+ import { parseArgs } from "node:util";
3
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
4
+ import { dirname, join, relative } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { LANGUAGES, getLanguagesByTier, getLanguageByCode, getModelForTier } from "./config";
7
+ import { getEnglishMdxPages, translateMdxPage } from "./mdx-translator";
8
+ import { translateReadme } from "./readme-translator";
9
+ import { updateDocsJson, readDocsConfig } from "./mintlify-nav";
10
+ import { readCache, writeCache, isCached, setCacheEntry } from "./cache";
11
+ import type { TranslationResult } from "./types";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const DOCS_DIR = join(__dirname, "..", "..", "docs");
15
+
16
+ const { values: args } = parseArgs({
17
+ options: {
18
+ languages: { type: "string", short: "l" },
19
+ tier: { type: "string", short: "t" },
20
+ pages: { type: "string", short: "p" },
21
+ "readme-only": { type: "boolean", default: false },
22
+ "docs-only": { type: "boolean", default: false },
23
+ "dry-run": { type: "boolean", default: false },
24
+ force: { type: "boolean", short: "f", default: false },
25
+ "update-nav": { type: "boolean", default: false },
26
+ validate: { type: "boolean", default: false },
27
+ model: { type: "string", short: "m" },
28
+ help: { type: "boolean", short: "h", default: false },
29
+ },
30
+ strict: true,
31
+ });
32
+
33
+ if (args.help) {
34
+ console.log(`
35
+ Usage: bun scripts/translate-docs/cli.ts [options]
36
+
37
+ Options:
38
+ -l, --languages <codes> Comma-separated language codes (e.g. zh,ja,es)
39
+ -t, --tier <n> Translate all languages up to tier n (1, 2, or 3)
40
+ -p, --pages <files> Comma-separated page names to translate
41
+ --readme-only Only translate the README
42
+ --docs-only Only translate Mintlify docs
43
+ --dry-run Show what would be translated without calling the API
44
+ -f, --force Ignore cache, re-translate everything
45
+ --update-nav Regenerate docs.json navigation after translation
46
+ --validate Check all nav references resolve to files
47
+ -m, --model <model> Claude model override (default: Sonnet for Tier 1, Haiku for Tier 2/3)
48
+ -h, --help Show this help
49
+
50
+ Environment:
51
+ ANTHROPIC_API_KEY Required for translation (not needed for --dry-run or --validate)
52
+
53
+ Examples:
54
+ bun scripts/translate-docs/cli.ts --tier 1 # Translate Tier 1 languages
55
+ bun scripts/translate-docs/cli.ts -l zh,ja --docs-only # Translate Chinese + Japanese docs only
56
+ bun scripts/translate-docs/cli.ts --dry-run --tier 3 # Preview all translations
57
+ bun scripts/translate-docs/cli.ts --validate # Check nav references
58
+ bun scripts/translate-docs/cli.ts --update-nav # Regenerate docs.json
59
+ `);
60
+ process.exit(0);
61
+ }
62
+
63
+ function resolveLanguages(): string[] {
64
+ if (args.languages) {
65
+ const codes = args.languages.split(",").map((c) => c.trim());
66
+ for (const code of codes) {
67
+ if (!getLanguageByCode(code)) {
68
+ console.error(`Unknown language code: ${code}`);
69
+ console.error(`Available: ${LANGUAGES.map((l) => l.code).join(", ")}`);
70
+ process.exit(1);
71
+ }
72
+ }
73
+ return codes;
74
+ }
75
+ const tier = args.tier ? parseInt(args.tier, 10) : 1;
76
+ if (tier < 1 || tier > 3) {
77
+ console.error("Tier must be 1, 2, or 3");
78
+ process.exit(1);
79
+ }
80
+ return getLanguagesByTier(tier).map((l) => l.code);
81
+ }
82
+
83
+ async function validateNavReferences(): Promise<boolean> {
84
+ const config = readDocsConfig();
85
+ const nav = config.navigation as Record<string, unknown>;
86
+ let valid = true;
87
+ let total = 0;
88
+ let missing = 0;
89
+
90
+ const languages = (nav.languages || []) as Array<{
91
+ language: string;
92
+ tabs: Array<{ groups: Array<{ pages: string[] }> }>;
93
+ }>;
94
+
95
+ if (languages.length === 0) {
96
+ // Flat tabs structure (not yet migrated to languages)
97
+ const tabs = (nav.tabs || []) as Array<{
98
+ groups: Array<{ pages: string[] }>;
99
+ }>;
100
+ for (const tab of tabs) {
101
+ for (const group of tab.groups) {
102
+ for (const page of group.pages) {
103
+ total++;
104
+ const filePath = join(DOCS_DIR, `${page}.mdx`);
105
+ if (!existsSync(filePath)) {
106
+ console.error(` MISSING: ${page} -> ${filePath}`);
107
+ missing++;
108
+ valid = false;
109
+ }
110
+ }
111
+ }
112
+ }
113
+ } else {
114
+ for (const langEntry of languages) {
115
+ for (const tab of langEntry.tabs) {
116
+ for (const group of tab.groups) {
117
+ for (const page of group.pages) {
118
+ total++;
119
+ const filePath = join(DOCS_DIR, `${page}.mdx`);
120
+ if (!existsSync(filePath)) {
121
+ console.error(` MISSING [${langEntry.language}]: ${page} -> ${filePath}`);
122
+ missing++;
123
+ valid = false;
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ if (valid) {
132
+ console.log(`All ${total} page references are valid.`);
133
+ } else {
134
+ console.error(`\n${missing} of ${total} page references are missing.`);
135
+ }
136
+
137
+ return valid;
138
+ }
139
+
140
+ async function main() {
141
+ // Validate mode
142
+ if (args.validate) {
143
+ const valid = await validateNavReferences();
144
+ process.exit(valid ? 0 : 1);
145
+ }
146
+
147
+ // Update nav mode
148
+ if (args["update-nav"]) {
149
+ const langCodes = resolveLanguages();
150
+ console.log(`Updating docs.json with languages: ${langCodes.join(", ")}`);
151
+ updateDocsJson(langCodes);
152
+ console.log("docs.json updated.");
153
+ return;
154
+ }
155
+
156
+ const langCodes = resolveLanguages();
157
+ const isDryRun = args["dry-run"];
158
+ const isForce = args.force;
159
+ const modelOverride = args.model;
160
+
161
+ /** Resolve the model for a language: CLI override wins, otherwise tier-based default. */
162
+ function resolveModel(lang: string): string {
163
+ if (modelOverride) return modelOverride;
164
+ const langConfig = getLanguageByCode(lang);
165
+ return getModelForTier(langConfig?.tier ?? 1);
166
+ }
167
+
168
+ console.log(
169
+ `${isDryRun ? "[DRY RUN] " : ""}Translating into: ${langCodes.join(", ")}`,
170
+ );
171
+ if (!modelOverride) {
172
+ console.log(`Models: Tier 1 -> ${getModelForTier(1)}, Tier 2/3 -> ${getModelForTier(2)}`);
173
+ } else {
174
+ console.log(`Model override: ${modelOverride}`);
175
+ }
176
+
177
+ const results: TranslationResult[] = [];
178
+ const errors: Array<{ lang: string; source: string; error: string }> = [];
179
+
180
+ // Read cache once upfront — filter unchanged files before starting work
181
+ const cache = readCache();
182
+
183
+ // Concurrency limiter to avoid overwhelming the Anthropic API
184
+ const MAX_CONCURRENT = 10;
185
+ async function runWithConcurrency<T>(tasks: (() => Promise<T>)[]): Promise<T[]> {
186
+ const results: T[] = [];
187
+ let i = 0;
188
+ async function next(): Promise<void> {
189
+ while (i < tasks.length) {
190
+ const idx = i++;
191
+ results[idx] = await tasks[idx]();
192
+ }
193
+ }
194
+ await Promise.all(Array.from({ length: Math.min(MAX_CONCURRENT, tasks.length) }, () => next()));
195
+ return results;
196
+ }
197
+
198
+ // Translate docs
199
+ if (!args["readme-only"]) {
200
+ const pages = getEnglishMdxPages();
201
+ const filteredPages = args.pages
202
+ ? pages.filter((p) => {
203
+ const rel = relative(DOCS_DIR, p).replace(".mdx", "");
204
+ return args.pages!.split(",").some((f) => rel.includes(f.trim()));
205
+ })
206
+ : pages;
207
+
208
+ // Read each page once and reuse across languages
209
+ const pageContents = new Map<string, string>();
210
+ for (const page of filteredPages) {
211
+ pageContents.set(page, readFileSync(page, "utf-8"));
212
+ }
213
+
214
+ // Split into cached (skip) and uncached (need translation) upfront
215
+ type PageTask = { page: string; relPath: string; lang: string };
216
+ const cachedTasks: PageTask[] = [];
217
+ const uncachedTasks: PageTask[] = [];
218
+
219
+ for (const lang of langCodes) {
220
+ for (const page of filteredPages) {
221
+ const relPath = relative(DOCS_DIR, page);
222
+ const task = { page, relPath, lang };
223
+ if (!isForce && !isDryRun && isCached(cache, relPath, lang, pageContents.get(page)!)) {
224
+ cachedTasks.push(task);
225
+ } else {
226
+ uncachedTasks.push(task);
227
+ }
228
+ }
229
+ }
230
+
231
+ console.log(`\n${filteredPages.length} MDX pages x ${langCodes.length} languages = ${filteredPages.length * langCodes.length} total`);
232
+ console.log(` Cached (unchanged): ${cachedTasks.length}`);
233
+ console.log(` Need translation: ${uncachedTasks.length}`);
234
+
235
+ // Record cached results
236
+ for (const { page, relPath, lang } of cachedTasks) {
237
+ results.push({
238
+ lang,
239
+ sourcePath: page,
240
+ outputPath: join(DOCS_DIR, lang, relPath),
241
+ inputTokens: 0,
242
+ outputTokens: 0,
243
+ cached: true,
244
+ });
245
+ }
246
+
247
+ // Translate uncached pages with concurrency limit
248
+ if (uncachedTasks.length > 0) {
249
+ const taskResults = await runWithConcurrency(
250
+ uncachedTasks.map(({ page, relPath, lang }) => async () => {
251
+ try {
252
+ const result = await translateMdxPage(page, lang, {
253
+ force: isForce,
254
+ dryRun: isDryRun,
255
+ model: resolveModel(lang),
256
+ cache,
257
+ });
258
+ const status = isDryRun
259
+ ? "would translate"
260
+ : `translated (${result.inputTokens}+${result.outputTokens} tokens)`;
261
+ console.log(` ${relPath} [${lang}] -> ${status}`);
262
+ results.push(result);
263
+ if (!result.cached && !isDryRun) {
264
+ setCacheEntry(cache, relPath, lang, pageContents.get(page)!, result.inputTokens, result.outputTokens);
265
+ }
266
+ } catch (err) {
267
+ const msg = err instanceof Error ? err.message : String(err);
268
+ errors.push({ lang, source: relPath, error: msg });
269
+ console.error(` ${relPath} [${lang}] -> ERROR: ${msg}`);
270
+ }
271
+ }),
272
+ );
273
+ }
274
+ }
275
+
276
+ // Translate README
277
+ if (!args["docs-only"]) {
278
+ console.log(`\nTranslating README...`);
279
+
280
+ // Read README once
281
+ const readmeSource = readFileSync(join(DOCS_DIR, "..", "README.md"), "utf-8");
282
+ const uncachedLangs: string[] = [];
283
+ for (const lang of langCodes) {
284
+ if (!isForce && !isDryRun && isCached(cache, "README.md", lang, readmeSource)) {
285
+ console.log(` README.${lang}.md -> cached`);
286
+ results.push({
287
+ lang,
288
+ sourcePath: join(DOCS_DIR, "..", "README.md"),
289
+ outputPath: join(DOCS_DIR, "i18n", `README.${lang}.md`),
290
+ inputTokens: 0,
291
+ outputTokens: 0,
292
+ cached: true,
293
+ });
294
+ } else {
295
+ uncachedLangs.push(lang);
296
+ }
297
+ }
298
+
299
+ if (uncachedLangs.length > 0) {
300
+ await runWithConcurrency(
301
+ uncachedLangs.map((lang) => async () => {
302
+ try {
303
+ const langConfig = getLanguageByCode(lang)!;
304
+ const result = await translateReadme(lang, {
305
+ force: isForce,
306
+ dryRun: isDryRun,
307
+ model: resolveModel(lang),
308
+ cache,
309
+ });
310
+ const status = isDryRun
311
+ ? "would translate"
312
+ : `translated (${result.inputTokens}+${result.outputTokens} tokens)`;
313
+ console.log(` README.${lang}.md -> ${langConfig.nativeName}: ${status}`);
314
+ results.push(result);
315
+ if (!result.cached && !isDryRun) {
316
+ setCacheEntry(cache, "README.md", lang, readmeSource, result.inputTokens, result.outputTokens);
317
+ }
318
+ } catch (err) {
319
+ const msg = err instanceof Error ? err.message : String(err);
320
+ errors.push({ lang, source: "README.md", error: msg });
321
+ console.error(` README.${lang}.md -> ERROR: ${msg}`);
322
+ }
323
+ }),
324
+ );
325
+ }
326
+ }
327
+
328
+ // Batch write cache once at the end
329
+ if (!isDryRun) {
330
+ writeCache(cache);
331
+ }
332
+
333
+ // Summary
334
+ const translated = results.filter((r) => !r.cached && !isDryRun);
335
+ const cached = results.filter((r) => r.cached);
336
+ const totalInput = translated.reduce((s, r) => s + r.inputTokens, 0);
337
+ const totalOutput = translated.reduce((s, r) => s + r.outputTokens, 0);
338
+
339
+ console.log(`\n--- Summary ---`);
340
+ console.log(`Translated: ${translated.length}`);
341
+ console.log(`Cached (skipped): ${cached.length}`);
342
+ if (errors.length > 0) {
343
+ console.log(`Errors: ${errors.length}`);
344
+ }
345
+ if (totalInput > 0) {
346
+ console.log(`Total tokens: ${totalInput} input + ${totalOutput} output`);
347
+ }
348
+
349
+ if (errors.length > 0) {
350
+ process.exit(1);
351
+ }
352
+ }
353
+
354
+ main().catch((err) => {
355
+ console.error("Fatal error:", err);
356
+ process.exit(1);
357
+ });