blume 2.0.2 → 2.0.3

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 (293) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/cli/{chunk-6k4ftwze.js → chunk-1d7ve1dm.js} +1 -1
  3. package/dist/cli/{chunk-zp79m0ts.js → chunk-2eytanqx.js} +2 -2
  4. package/dist/cli/{chunk-8g8ytmgx.js → chunk-2hsdwb9n.js} +19 -19
  5. package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-2hsdwb9n.js.map} +1 -1
  6. package/dist/cli/{chunk-g698a744.js → chunk-2z928egk.js} +5 -5
  7. package/dist/cli/{chunk-ppzjqwx2.js → chunk-35d4wj9f.js} +17 -18
  8. package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-35d4wj9f.js.map} +3 -3
  9. package/dist/cli/{chunk-mqc662a6.js → chunk-364znk6q.js} +2 -2
  10. package/dist/cli/{chunk-gs7r695n.js → chunk-3em5wd2y.js} +21 -8
  11. package/dist/cli/{chunk-gs7r695n.js.map → chunk-3em5wd2y.js.map} +3 -3
  12. package/dist/cli/{chunk-k7pj68a8.js → chunk-5f86nr5m.js} +15 -15
  13. package/dist/cli/{chunk-hr8ne106.js → chunk-5m5nmvyq.js} +68 -43
  14. package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
  15. package/dist/cli/{chunk-91ws1n6j.js → chunk-5xvm6tfj.js} +14 -14
  16. package/dist/cli/{chunk-w4bxdvsa.js → chunk-6dsbexzp.js} +14 -14
  17. package/dist/cli/{chunk-00gs3wqs.js → chunk-8ktnccpt.js} +1 -1
  18. package/dist/cli/{chunk-j85scx15.js → chunk-9t7a85s3.js} +130 -44
  19. package/dist/cli/chunk-9t7a85s3.js.map +10 -0
  20. package/dist/cli/{chunk-bbnwccaz.js → chunk-a58773jm.js} +2 -2
  21. package/dist/cli/{chunk-nfcyttvj.js → chunk-acanzt5p.js} +9 -9
  22. package/dist/cli/{chunk-nfcyttvj.js.map → chunk-acanzt5p.js.map} +1 -1
  23. package/dist/cli/{chunk-wdrt2k2v.js → chunk-akbpwfxc.js} +90 -26
  24. package/dist/cli/chunk-akbpwfxc.js.map +10 -0
  25. package/dist/cli/{chunk-sqw4ekg1.js → chunk-b07cmahc.js} +2 -2
  26. package/dist/cli/{chunk-xh43dwgw.js → chunk-c8chx29p.js} +42 -29
  27. package/dist/cli/{chunk-xh43dwgw.js.map → chunk-c8chx29p.js.map} +9 -9
  28. package/dist/cli/{chunk-7mbqtmgb.js → chunk-crgn1q09.js} +21 -12
  29. package/dist/cli/chunk-crgn1q09.js.map +10 -0
  30. package/dist/cli/chunk-e04dxsz1.js +39 -0
  31. package/dist/cli/chunk-e04dxsz1.js.map +10 -0
  32. package/dist/cli/{chunk-4e9b9ra6.js → chunk-ey84smr6.js} +3 -3
  33. package/dist/cli/{chunk-d1v5rhy0.js → chunk-g4hq16wv.js} +13 -13
  34. package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
  35. package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
  36. package/dist/cli/{chunk-pbg5a4s3.js → chunk-hm3vjy5s.js} +64 -45
  37. package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
  38. package/dist/cli/{chunk-5r8g91qn.js → chunk-j85vccga.js} +306 -107
  39. package/dist/cli/chunk-j85vccga.js.map +36 -0
  40. package/dist/cli/{chunk-n1yg3tj3.js → chunk-p3v96n38.js} +6 -6
  41. package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-p3v96n38.js.map} +3 -3
  42. package/dist/cli/{chunk-7vtckvaw.js → chunk-p73c0m7w.js} +14 -14
  43. package/dist/cli/{chunk-7vtckvaw.js.map → chunk-p73c0m7w.js.map} +1 -1
  44. package/dist/cli/{chunk-qkb5a8sa.js → chunk-pehfxfta.js} +3 -3
  45. package/dist/cli/{chunk-v6ya5kcb.js → chunk-pv29h0wf.js} +1223 -978
  46. package/dist/cli/{chunk-v6ya5kcb.js.map → chunk-pv29h0wf.js.map} +47 -46
  47. package/dist/cli/{chunk-6dtt0zfn.js → chunk-q58y5e6a.js} +10 -10
  48. package/dist/cli/{chunk-6dtt0zfn.js.map → chunk-q58y5e6a.js.map} +3 -3
  49. package/dist/cli/{chunk-fsmrqk8a.js → chunk-r20tn01b.js} +1 -1
  50. package/dist/cli/{chunk-bfwp9vp6.js → chunk-r9rcc4w7.js} +7 -7
  51. package/dist/cli/{chunk-h7k3nq3v.js → chunk-tkacnehg.js} +2 -2
  52. package/dist/cli/{chunk-h2ez8dzb.js → chunk-tzmab476.js} +4 -4
  53. package/dist/cli/{chunk-3yce002v.js → chunk-vg9r4eb9.js} +6 -6
  54. package/dist/cli/{chunk-3yce002v.js.map → chunk-vg9r4eb9.js.map} +4 -4
  55. package/dist/cli/{chunk-hqp2ajnh.js → chunk-wrr3j9w9.js} +16 -17
  56. package/dist/cli/{chunk-hqp2ajnh.js.map → chunk-wrr3j9w9.js.map} +7 -7
  57. package/dist/cli/{chunk-ddndchfr.js → chunk-yfyb25rh.js} +38 -26
  58. package/dist/cli/chunk-yfyb25rh.js.map +14 -0
  59. package/dist/cli/index.js +20 -18
  60. package/dist/cli/index.js.map +3 -3
  61. package/dist/types/ai/link-headers.d.ts +8 -1
  62. package/dist/types/ai/openapi-components.d.ts +5 -2
  63. package/dist/types/ai/relative-links.d.ts +9 -7
  64. package/dist/types/ai/skills.d.ts +4 -1
  65. package/dist/types/ai/tar.d.ts +1 -3
  66. package/dist/types/analytics/index.d.ts +2 -0
  67. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  68. package/dist/types/analytics/schema.d.ts +14 -0
  69. package/dist/types/astro/integration.d.ts +3 -2
  70. package/dist/types/core/base-path.d.ts +21 -7
  71. package/dist/types/core/config-input.d.ts +9 -7
  72. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  73. package/dist/types/core/heading-markers.d.ts +5 -7
  74. package/dist/types/core/i18n.d.ts +2 -0
  75. package/dist/types/core/last-modified.d.ts +10 -0
  76. package/dist/types/core/meta.d.ts +8 -0
  77. package/dist/types/core/schema.d.ts +7 -0
  78. package/dist/types/core/sources/lower.d.ts +29 -16
  79. package/dist/types/core/sources/normalize.d.ts +13 -1
  80. package/dist/types/core/sources/watch.d.ts +12 -5
  81. package/dist/types/core/standard-schema.d.ts +5 -0
  82. package/dist/types/deploy/artifacts.d.ts +6 -4
  83. package/dist/types/deploy/headers.d.ts +5 -0
  84. package/dist/types/deploy/platforms/types.d.ts +8 -0
  85. package/dist/types/deploy/redirects.d.ts +24 -13
  86. package/dist/types/markdown/directives.d.ts +62 -0
  87. package/dist/types/markdown/features.d.ts +21 -0
  88. package/dist/types/markdown/mdast.d.ts +63 -0
  89. package/dist/types/openapi/asyncapi.d.ts +4 -2
  90. package/docs/02-deployment.mdx +4 -2
  91. package/docs/08-faq.mdx +1 -1
  92. package/docs/advanced/custom-pages.mdx +3 -3
  93. package/docs/cli/audit.mdx +17 -1
  94. package/docs/cli/evals.mdx +3 -3
  95. package/docs/cli/translate.mdx +3 -3
  96. package/docs/cli/version.mdx +1 -1
  97. package/docs/configuration/analytics.mdx +20 -1
  98. package/docs/configuration/customization.mdx +2 -0
  99. package/docs/configuration/search.mdx +1 -1
  100. package/docs/content/components.mdx +1 -1
  101. package/docs/content/frontmatter.mdx +1 -1
  102. package/docs/content/i18n.mdx +2 -0
  103. package/docs/content/index.mdx +4 -2
  104. package/docs/content/islands.mdx +1 -1
  105. package/docs/content/meta.mdx +3 -1
  106. package/docs/content/sources.mdx +1 -5
  107. package/docs/content/syntax.mdx +14 -0
  108. package/docs/content/versioning.mdx +1 -1
  109. package/docs/discoverability/agent-discovery.mdx +1 -1
  110. package/docs/discoverability/index.mdx +2 -1
  111. package/docs/discoverability/markdown.mdx +2 -2
  112. package/docs/discoverability/open-graph.mdx +5 -3
  113. package/docs/discoverability/rss.mdx +1 -1
  114. package/docs/references/asyncapi.mdx +1 -1
  115. package/docs/references/graphql.mdx +1 -1
  116. package/docs/references/openapi.mdx +5 -3
  117. package/package.json +1 -1
  118. package/skills/blume-migrate/references/fumadocs.md +1 -1
  119. package/skills/blume-migrate/references/nextra.md +1 -1
  120. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +7 -2
  121. package/src/ai/agent-readability.ts +2 -2
  122. package/src/ai/ai-catalog.ts +6 -2
  123. package/src/ai/api/handlers.ts +2 -2
  124. package/src/ai/api/spec.ts +2 -2
  125. package/src/ai/api-catalog.ts +6 -2
  126. package/src/ai/changelog-markdown.ts +2 -2
  127. package/src/ai/link-headers.ts +14 -5
  128. package/src/ai/llms.ts +18 -7
  129. package/src/ai/mcp/discovery.ts +2 -2
  130. package/src/ai/mcp/query.ts +2 -2
  131. package/src/ai/mcp/server.ts +2 -2
  132. package/src/ai/openapi-components.ts +22 -6
  133. package/src/ai/relative-links.ts +42 -26
  134. package/src/ai/serializers.ts +2 -1
  135. package/src/ai/skills.ts +14 -1
  136. package/src/ai/tar.ts +139 -12
  137. package/src/analytics/head.ts +4 -0
  138. package/src/analytics/index.ts +5 -0
  139. package/src/analytics/one-dollar-stats.ts +86 -0
  140. package/src/analytics/schema.ts +2 -0
  141. package/src/astro/generate.ts +1 -1
  142. package/src/astro/include-hmr.ts +31 -12
  143. package/src/astro/include-refresh.ts +19 -8
  144. package/src/astro/integration.ts +108 -14
  145. package/src/astro/runtime-modules.ts +28 -13
  146. package/src/astro/templates.ts +81 -37
  147. package/src/audit/checks/links.ts +8 -1
  148. package/src/audit/graph.ts +3 -1
  149. package/src/audit/run.ts +12 -9
  150. package/src/audit/snapshot.ts +5 -0
  151. package/src/cli/args.ts +32 -0
  152. package/src/cli/commands/audit.ts +4 -1
  153. package/src/cli/commands/doctor.ts +3 -1
  154. package/src/cli/commands/eval.ts +19 -11
  155. package/src/cli/commands/translate.ts +9 -12
  156. package/src/cli/commands/validate.ts +3 -1
  157. package/src/cli/dev-lock.ts +157 -37
  158. package/src/cli/report-format.ts +11 -6
  159. package/src/components/colors.ts +19 -0
  160. package/src/components/content/Badge.astro +5 -3
  161. package/src/components/content/Component.astro +2 -2
  162. package/src/components/content/Tab.astro +0 -1
  163. package/src/components/content/Tabs.astro +4 -0
  164. package/src/components/content/badge-color.ts +5 -2
  165. package/src/components/content/base-href.ts +13 -36
  166. package/src/components/islands/assistant.tsx +21 -4
  167. package/src/components/islands/base-path.ts +47 -10
  168. package/src/components/islands/hooks.ts +35 -19
  169. package/src/components/islands/webmcp.ts +4 -2
  170. package/src/components/layout/Breadcrumbs.astro +5 -2
  171. package/src/components/layout/DiscoveryLinks.astro +9 -5
  172. package/src/components/layout/Header.astro +2 -2
  173. package/src/components/layout/LanguageSwitcher.astro +2 -2
  174. package/src/components/layout/NavSelector.astro +2 -2
  175. package/src/components/layout/NavTabMenu.astro +3 -3
  176. package/src/components/layout/NavTree.astro +16 -6
  177. package/src/components/layout/PageActions.astro +4 -3
  178. package/src/components/layout/PageLayout.astro +7 -6
  179. package/src/components/layout/Pagination.astro +3 -3
  180. package/src/components/layout/RootLayout.astro +5 -5
  181. package/src/components/layout/Search.astro +33 -13
  182. package/src/components/layout/VersionBanner.astro +2 -2
  183. package/src/components/layout/analytics-client.ts +12 -0
  184. package/src/components/layout/toc-active.ts +41 -0
  185. package/src/components/layout/toc-element.ts +8 -14
  186. package/src/components/openapi/ApiTagOperations.astro +2 -2
  187. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  188. package/src/components/openapi/GraphqlChip.astro +2 -2
  189. package/src/components/openapi/Operation.astro +2 -0
  190. package/src/components/openapi/Playground.astro +4 -4
  191. package/src/components/openapi/RequestPanel.astro +5 -2
  192. package/src/components/openapi/SchemaTable.astro +7 -0
  193. package/src/components/openapi/async.ts +38 -6
  194. package/src/components/openapi/helpers.ts +19 -9
  195. package/src/components/openapi/message-composer.ts +6 -1
  196. package/src/components/openapi/message.ts +17 -2
  197. package/src/components/openapi/operation-model.ts +81 -11
  198. package/src/components/openapi/panel.ts +4 -2
  199. package/src/components/openapi/param-style.ts +181 -0
  200. package/src/components/openapi/playground-client.ts +73 -16
  201. package/src/components/openapi/request.ts +190 -26
  202. package/src/components/openapi/schema-tree.ts +30 -24
  203. package/src/components/openapi/snippets.ts +85 -6
  204. package/src/components/openapi/ws-client.ts +18 -2
  205. package/src/core/base-path.ts +62 -16
  206. package/src/core/config-input.ts +9 -7
  207. package/src/core/diagnostics.ts +2 -0
  208. package/src/core/directive-diagnostics.ts +99 -0
  209. package/src/core/frontmatter.ts +21 -18
  210. package/src/core/heading-markers.ts +5 -18
  211. package/src/core/i18n.ts +31 -3
  212. package/src/core/last-modified.ts +25 -3
  213. package/src/core/locale-links.ts +5 -1
  214. package/src/core/meta.ts +33 -19
  215. package/src/core/navigation.ts +28 -8
  216. package/src/core/project-graph.ts +27 -5
  217. package/src/core/schema.ts +24 -4
  218. package/src/core/sources/contentful-rich-text.ts +27 -20
  219. package/src/core/sources/filesystem.ts +20 -2
  220. package/src/core/sources/github-releases.ts +25 -6
  221. package/src/core/sources/lexical.ts +23 -18
  222. package/src/core/sources/lower.ts +201 -34
  223. package/src/core/sources/mdx-remote.ts +51 -16
  224. package/src/core/sources/normalize.ts +325 -90
  225. package/src/core/sources/notion.ts +52 -25
  226. package/src/core/sources/obsidian.ts +23 -6
  227. package/src/core/sources/portable-text.ts +38 -21
  228. package/src/core/sources/strapi-blocks.ts +20 -16
  229. package/src/core/sources/watch.ts +20 -7
  230. package/src/core/standard-schema.ts +10 -6
  231. package/src/core/version-cut.ts +52 -15
  232. package/src/deploy/artifacts.ts +27 -6
  233. package/src/deploy/cloudflare-negotiation.ts +4 -12
  234. package/src/deploy/headers.ts +8 -4
  235. package/src/deploy/node-headers.ts +1 -1
  236. package/src/deploy/platforms/cloudflare.ts +12 -3
  237. package/src/deploy/platforms/netlify.ts +1 -0
  238. package/src/deploy/platforms/node.ts +1 -0
  239. package/src/deploy/platforms/static.ts +1 -0
  240. package/src/deploy/platforms/types.ts +8 -0
  241. package/src/deploy/platforms/vercel.ts +1 -0
  242. package/src/deploy/redirects.ts +54 -18
  243. package/src/deploy/robots.ts +2 -2
  244. package/src/deploy/rss.ts +4 -3
  245. package/src/deploy/sitemap.ts +7 -5
  246. package/src/markdown/base-links.ts +34 -36
  247. package/src/markdown/directives.ts +242 -36
  248. package/src/markdown/features.ts +17 -0
  249. package/src/markdown/index.ts +7 -6
  250. package/src/markdown/mdast.ts +5 -2
  251. package/src/markdown/relative-links.ts +12 -4
  252. package/src/og/card.ts +129 -5
  253. package/src/og/derive.ts +41 -31
  254. package/src/og/index.ts +1 -0
  255. package/src/openapi/asyncapi.ts +4 -2
  256. package/src/openapi/model.ts +22 -14
  257. package/src/openapi/proxy.ts +63 -10
  258. package/src/openapi/render-mdx.ts +41 -2
  259. package/src/registry/eject.ts +181 -24
  260. package/src/search/adapters/version-scope.ts +30 -0
  261. package/src/search/documents.ts +67 -26
  262. package/src/search/popular.ts +2 -1
  263. package/src/seo/jsonld.ts +7 -3
  264. package/src/translate/meta.ts +68 -24
  265. package/src/translate/run.ts +3 -3
  266. package/src/translate/validate.ts +4 -1
  267. package/src/translate/work-list.ts +56 -17
  268. package/dist/cli/chunk-5r8g91qn.js.map +0 -34
  269. package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
  270. package/dist/cli/chunk-ddndchfr.js.map +0 -14
  271. package/dist/cli/chunk-esh98wmb.js +0 -23
  272. package/dist/cli/chunk-esh98wmb.js.map +0 -10
  273. package/dist/cli/chunk-hr8ne106.js.map +0 -13
  274. package/dist/cli/chunk-j85scx15.js.map +0 -10
  275. package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
  276. package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
  277. /package/dist/cli/{chunk-6k4ftwze.js.map → chunk-1d7ve1dm.js.map} +0 -0
  278. /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-2eytanqx.js.map} +0 -0
  279. /package/dist/cli/{chunk-g698a744.js.map → chunk-2z928egk.js.map} +0 -0
  280. /package/dist/cli/{chunk-mqc662a6.js.map → chunk-364znk6q.js.map} +0 -0
  281. /package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5f86nr5m.js.map} +0 -0
  282. /package/dist/cli/{chunk-91ws1n6j.js.map → chunk-5xvm6tfj.js.map} +0 -0
  283. /package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-6dsbexzp.js.map} +0 -0
  284. /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-8ktnccpt.js.map} +0 -0
  285. /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-a58773jm.js.map} +0 -0
  286. /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-b07cmahc.js.map} +0 -0
  287. /package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-ey84smr6.js.map} +0 -0
  288. /package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-g4hq16wv.js.map} +0 -0
  289. /package/dist/cli/{chunk-qkb5a8sa.js.map → chunk-pehfxfta.js.map} +0 -0
  290. /package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-r20tn01b.js.map} +0 -0
  291. /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-r9rcc4w7.js.map} +0 -0
  292. /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-tkacnehg.js.map} +0 -0
  293. /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-tzmab476.js.map} +0 -0
@@ -0,0 +1,19 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/commands/translate.ts", "../src/translate/agents.ts", "../src/translate/ledger.ts", "../src/translate/report.ts", "../src/translate/run.ts", "../src/translate/meta.ts", "../src/translate/prompts.ts", "../src/translate/anchors.ts", "../src/translate/validate.ts", "../src/translate/work-list.ts"],
4
+ "sourcesContent": [
5
+ "import { defineCommand } from \"citty\";\n\nimport { AGENTS } from \"../../audit/agent.ts\";\nimport type { AgentKind } from \"../../audit/agent.ts\";\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { i18nEnabled, localeCodes } from \"../../core/i18n.ts\";\nimport type { ResolvedI18nConfig } from \"../../core/schema.ts\";\nimport { DEFAULT_TRANSLATE_TIMEOUT_MS } from \"../../translate/agents.ts\";\nimport {\n pruneLedger,\n readLedger,\n stampLedger,\n writeLedger,\n} from \"../../translate/ledger.ts\";\nimport {\n checkLines,\n checkReportJson,\n checkSummaryLine,\n createProgressRenderer,\n diagnosticLines,\n hasDrift,\n translateHeaderLine,\n translateReportJson,\n translateSummaryLine,\n} from \"../../translate/report.ts\";\nimport { runTranslate } from \"../../translate/run.ts\";\nimport {\n computeWorkList,\n scanForTranslation,\n} from \"../../translate/work-list.ts\";\nimport { parseTimeoutSeconds } from \"../args.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { reportInternalError } from \"../internal-error.ts\";\nimport { logger, reportDiagnostics } from \"../log.ts\";\n\n/** Wall-clock ceiling per file, in seconds. */\nconst DEFAULT_TIMEOUT_S = DEFAULT_TRANSLATE_TIMEOUT_MS / 1000;\n\n/** Parallel agent sessions per run; each one is a full agent process. */\nconst DEFAULT_CONCURRENCY = 4;\nconst MAX_CONCURRENCY = 16;\n\ninterface TranslateFlags {\n check?: boolean;\n claude?: boolean;\n codex?: boolean;\n concurrency?: string;\n force?: boolean;\n json?: boolean;\n locale?: string;\n timeout?: string;\n}\n\n/** The validated flag surface `parseFlags` hands the command body. */\ninterface ParsedTranslateFlags {\n agent: AgentKind | undefined;\n concurrency: number;\n timeoutS: number;\n}\n\n/** Validate the flag surface, exiting with a message on the first offense. */\nconst parseFlags = (args: TranslateFlags): ParsedTranslateFlags => {\n // SAFETY: Object.keys of the AGENTS table returns exactly its declared\n // AgentKind keys.\n const agents = (Object.keys(AGENTS) as AgentKind[]).filter(\n (kind) => args[kind]\n );\n if (agents.length > 1) {\n logger.error(\"Pass exactly one of --codex or --claude.\");\n process.exit(1);\n }\n if (args.check && agents.length > 0) {\n logger.error(\n \"--check is read-only and never runs an agent; drop --codex/--claude.\"\n );\n process.exit(1);\n }\n if (!args.check && agents.length === 0) {\n logger.error(\n \"Pass --codex or --claude to choose the agent CLI that translates.\"\n );\n process.exit(1);\n }\n const timeoutS = parseTimeoutSeconds(args.timeout, DEFAULT_TIMEOUT_S);\n const concurrency =\n args.concurrency === undefined\n ? DEFAULT_CONCURRENCY\n : Number(args.concurrency);\n if (\n !Number.isInteger(concurrency) ||\n concurrency < 1 ||\n concurrency > MAX_CONCURRENCY\n ) {\n logger.error(\n `Invalid --concurrency \"${args.concurrency}\" (use 1..${MAX_CONCURRENCY}).`\n );\n process.exit(1);\n }\n return { agent: agents[0], concurrency, timeoutS };\n};\n\n/**\n * Resolve `--locale` against the configured locales: comma-separated,\n * case-insensitive, adopting the configured casing. Unknown codes and the\n * default locale (the translation source) are errors.\n */\nconst parseLocales = (\n value: string | undefined,\n i18n: ResolvedI18nConfig\n): string[] | undefined => {\n if (value === undefined) {\n return;\n }\n const configured = new Map(\n i18n.locales.map((locale) => [locale.code.toLowerCase(), locale.code])\n );\n const resolved: string[] = [];\n for (const part of value.split(\",\")) {\n const code = configured.get(part.trim().toLowerCase());\n if (code === undefined) {\n logger.error(\n `Unknown --locale \"${part.trim()}\" (configured: ${localeCodes(i18n).join(\", \")}).`\n );\n process.exit(1);\n }\n if (code === i18n.defaultLocale) {\n logger.error(\n `--locale \"${code}\" is the default locale — it is the translation source, not a target.`\n );\n process.exit(1);\n }\n resolved.push(code);\n }\n return resolved;\n};\n\nconst notInstalled = (agent: AgentKind): never => {\n const cli = AGENTS[agent];\n logger.error(\n `${cli.name} (\\`${cli.bin}\\`) was not found on PATH. Install it with \\`${cli.install}\\`.`\n );\n return process.exit(1);\n};\n\nexport const translateCommand = defineCommand({\n args: {\n check: {\n description:\n \"Report missing/stale translations without writing anything; exits 1 on drift (for CI).\",\n type: \"boolean\",\n },\n claude: {\n description: \"Translate with Claude Code.\",\n type: \"boolean\",\n },\n codex: {\n description: \"Translate with Codex.\",\n type: \"boolean\",\n },\n concurrency: {\n description: `Parallel agent sessions. Defaults to ${DEFAULT_CONCURRENCY}, max ${MAX_CONCURRENCY}.`,\n type: \"string\",\n },\n force: {\n description:\n \"Retranslate everything, up-to-date and hand-authored files included.\",\n type: \"boolean\",\n },\n json: {\n description: \"Emit the report as JSON on stdout (for CI/editors).\",\n type: \"boolean\",\n },\n locale: {\n description:\n \"Comma-separated target locale codes; defaults to every non-default locale.\",\n type: \"string\",\n },\n timeout: {\n description: `Agent time limit per file, in seconds. Defaults to ${DEFAULT_TIMEOUT_S}.`,\n type: \"string\",\n },\n },\n meta: commandMeta.translate,\n async run({ args }) {\n const root = process.cwd();\n const { agent, concurrency, timeoutS } = parseFlags(args);\n\n try {\n // A scan, not `prepareProject`: translation reads the content tree and\n // writes source files, never `.blume/`, so it doesn't contend with a\n // running dev server. Same reasoning as `blume audit`/`eval`. Drafts\n // are included (see `scanForTranslation`).\n const project = await scanForTranslation(root);\n if (!i18nEnabled(project.config)) {\n logger.error(\n \"i18n is not configured — add `i18n.locales` to blume.config to use `blume translate`.\"\n );\n process.exit(1);\n }\n const { i18n } = project.config;\n if (localeCodes(i18n).every((code) => code === i18n.defaultLocale)) {\n logger.error(\n \"i18n.locales has no locale besides the default — nothing to translate into.\"\n );\n process.exit(1);\n }\n const locales = parseLocales(args.locale, i18n);\n const ledger = await readLedger(root);\n const workList = await computeWorkList(project, ledger, {\n force: !args.check && Boolean(args.force),\n locales,\n });\n\n if (args.check) {\n const lines = [\n ...diagnosticLines(workList.diagnostics),\n ...checkLines(workList),\n \"\",\n ` ${checkSummaryLine(workList)}`,\n \"\",\n ];\n process.stderr.write(`${lines.join(\"\\n\")}\\n`);\n if (args.json) {\n process.stdout.write(checkReportJson(workList));\n }\n if (hasDrift(workList)) {\n // Set the code and return rather than `process.exit`, which waits for\n // neither a piped stderr (the drift report) nor a piped stdout.\n process.exitCode = 1;\n }\n return;\n }\n\n // SAFETY: parseFlags exits unless --check was passed or exactly one\n // agent flag was; on this non-check path the agent is therefore set.\n const kind = agent as AgentKind;\n process.stderr.write(\n `${translateHeaderLine(workList.items.length, workList.targetLocales.length, kind)}\\n\\n`\n );\n\n // Adopt pre-existing hand-authored translations (stamp, never rewrite)\n // and persist BEFORE the agents run, so adoption survives an interrupt.\n for (const entry of workList.untracked) {\n stampLedger(ledger, entry.sourceRel, entry.locale, entry.hash);\n }\n await writeLedger(root, ledger);\n\n const renderer = createProgressRenderer({\n isTTY: process.stderr.isTTY === true,\n write: (chunk) => process.stderr.write(chunk),\n });\n const result = await runTranslate({\n agent: kind,\n concurrency,\n ledger,\n onProgress: (event) => renderer.onProgress(event),\n // Flush after every finished item, so stopping a long run mid-way\n // keeps everything already translated out of the next work list.\n persistLedger: () => writeLedger(root, ledger),\n project,\n timeoutMs: timeoutS * 1000,\n workList,\n });\n renderer.stop();\n\n // Prune entries whose source or locale no longer exists. Pruning spans\n // ALL non-default locales — a `--locale fr` run must not drop the other\n // locales' stamps.\n const knownLocales = new Set(\n localeCodes(i18n).filter((code) => code !== i18n.defaultLocale)\n );\n await writeLedger(\n root,\n pruneLedger(ledger, workList.knownSources, knownLocales)\n );\n\n const tail = [\n \"\",\n ...diagnosticLines(workList.diagnostics),\n ` ${translateSummaryLine(result, workList)}`,\n \"\",\n ];\n process.stderr.write(tail.join(\"\\n\"));\n\n if (args.json) {\n process.stdout.write(translateReportJson(result, workList));\n }\n if (result.counts.failed > 0 || result.counts.partial > 0) {\n // The ledger write above already persisted every success, so a failed\n // rerun only retries what actually failed. Set the code rather than\n // `process.exit`, which would cut off a piped stderr summary.\n process.exitCode = 1;\n }\n } catch (error) {\n if (error instanceof BlumeError) {\n reportDiagnostics([error.diagnostic], root);\n process.exit(1);\n }\n // SAFETY: an ENOENT from spawning the agent CLI is an ErrnoException;\n // any other thrown shape reads `code` as undefined and falls through.\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\" && agent) {\n notInstalled(agent);\n }\n reportInternalError(error);\n process.exit(1);\n }\n },\n});\n",
6
+ "import type { AgentKind } from \"../audit/agent.ts\";\nimport { CODEX_LOCKDOWN, DISALLOWED_TOOLS } from \"../eval/agents.ts\";\n\n/**\n * argv builders for the translator role. The subprocess machinery\n * (`runAgentHeadless`, `readAgentOutput`, the `HeadlessRunner` test seam) is\n * shared with `blume eval` — only the argument surface differs: a translator\n * is a pure text→text call with no MCP servers and no tools at all, so the\n * agent can neither read the repo nor write files (Blume owns every write).\n */\n\n/**\n * Wall-clock ceiling per file. Generous by design: translating a large page\n * means regenerating the whole file token by token, and a 20KB+ reference\n * page comfortably exceeds the eval reader's 3-minute precedent. The ceiling\n * exists to catch hung agents, not to police slow-but-progressing ones.\n */\nexport const DEFAULT_TRANSLATE_TIMEOUT_MS = 600_000;\n\nconst claudeArgs = (): string[] => [\n \"-p\",\n \"--output-format\",\n \"json\",\n \"--strict-mcp-config\",\n \"--disallowedTools\",\n DISALLOWED_TOOLS.join(\",\"),\n \"--max-turns\",\n \"1\",\n];\n\n// `--sandbox read-only` alone still lets codex's shell read any file the user\n// can, and the source page being translated is untrusted input (a remote\n// contributor's prose), so the translator gets the same lockdown as an eval\n// run: no shell, exec, or local-image tool, and no inherited environment.\nconst codexArgs = (lastMessagePath: string): string[] => [\n \"exec\",\n \"--skip-git-repo-check\",\n \"--ignore-user-config\",\n \"--ephemeral\",\n \"--sandbox\",\n \"read-only\",\n ...CODEX_LOCKDOWN,\n \"--output-last-message\",\n lastMessagePath,\n \"-\",\n];\n\n/**\n * Build the argv for one headless translation call. `lastMessagePath` is where\n * codex writes its final message (its stdout interleaves progress); claude\n * ignores it and answers as JSON on stdout.\n */\nexport const translateAgentArgs = (\n kind: AgentKind,\n lastMessagePath: string\n): string[] => (kind === \"claude\" ? claudeArgs() : codexArgs(lastMessagePath));\n",
7
+ "import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\n\nimport { join } from \"pathe\";\nimport { z } from \"zod\";\n\nimport { BlumeError } from \"../core/diagnostics.ts\";\nimport { writeTextAtomic } from \"../core/fs-atomic.ts\";\n\n/**\n * The committed translation ledger: which source files have been translated\n * into which locales, and at what source content. Named \"ledger\" to avoid\n * colliding with the route manifest (`core/manifest.ts`). It lives at the\n * project root — never inside `.blume/` (init gitignores that dir wholesale,\n * and the whole point is that the ledger is committed alongside the docs).\n */\nexport const LEDGER_FILE = \"blume.translations.json\";\n\n/**\n * `files` maps a POSIX root-relative source path to, per locale, the hash of\n * the raw source text at the moment that locale's translation was written.\n */\nexport interface TranslationLedger {\n version: 1;\n files: Record<string, Record<string, string>>;\n}\n\nconst ledgerSchema = z.object({\n files: z.record(z.string(), z.record(z.string(), z.string())),\n version: z.literal(1),\n});\n\nexport const emptyLedger = (): TranslationLedger => ({\n files: {},\n version: 1,\n});\n\n/**\n * Hash the raw source text (frontmatter included), so any edit invalidates\n * every locale's stamp. sha256-16 like the audit snapshot's content hash.\n * Kept as its own function rather than sharing core's `hashText`: ledger\n * stamps persist in user repos, so this hash must never change shape when an\n * ephemeral cache hash does. CRLF line endings are normalized to LF first, so\n * a Windows checkout (`core.autocrlf`) hashes the same as everyone else's —\n * LF text hashes exactly as it always has.\n */\nexport const hashSource = (text: string): string =>\n createHash(\"sha256\")\n .update(text.replaceAll(\"\\r\\n\", \"\\n\"))\n .digest(\"hex\")\n .slice(0, 16);\n\n/** A git merge-conflict marker line: `<<<<<<< ours`, `=======`, `>>>>>>> theirs`. */\nconst CONFLICT_MARKER = /^(?:<{7}|={7}|>{7})(?:[ \\r]|$)/mu;\n\n/**\n * Read the ledger at `root`, tolerantly: a missing file, unparseable JSON, or\n * an unknown shape/version all resolve to an empty ledger rather than an error\n * (same posture as the dev lock's `parseLock`) — the worst outcome of a\n * corrupt ledger is retranslating files that were already up to date.\n *\n * Unresolved merge-conflict markers are the exception, and an error: read as\n * empty, every existing translation would be adopted as current, so\n * `--check` would pass over translations that are out of date.\n */\nexport const readLedger = async (root: string): Promise<TranslationLedger> => {\n const path = join(root, LEDGER_FILE);\n let raw: string;\n try {\n raw = await readFile(path, \"utf-8\");\n } catch {\n return emptyLedger();\n }\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n if (CONFLICT_MARKER.test(raw)) {\n throw new BlumeError({\n code: \"BLUME_TRANSLATE_LEDGER_CONFLICT\",\n file: path,\n message: `${LEDGER_FILE} has unresolved merge-conflict markers, so it can't say which translations are current.`,\n severity: \"error\",\n suggestion: `Resolve the merge conflict in ${LEDGER_FILE}, then rerun.`,\n });\n }\n return emptyLedger();\n }\n const parsed = ledgerSchema.safeParse(data);\n return parsed.success ? parsed.data : emptyLedger();\n};\n\n/** Deterministic serialization: keys sorted at both levels, 2-space indent. */\nexport const serializeLedger = (ledger: TranslationLedger): string => {\n const files: Record<string, Record<string, string>> = {};\n for (const source of Object.keys(ledger.files).toSorted()) {\n const locales = ledger.files[source] ?? {};\n files[source] = Object.fromEntries(\n Object.keys(locales)\n .toSorted()\n .map((locale) => [locale, locales[locale] ?? \"\"])\n );\n }\n return `${JSON.stringify({ files, version: ledger.version }, null, 2)}\\n`;\n};\n\n/**\n * Write the ledger at `root`, returning whether anything changed on disk. A\n * byte-identical ledger is left untouched (no mtime churn, no git noise);\n * a changed one lands via temp-file-plus-rename so a concurrent reader never\n * observes a half-written file.\n */\nexport const writeLedger = async (\n root: string,\n ledger: TranslationLedger\n): Promise<boolean> => {\n const path = join(root, LEDGER_FILE);\n const content = serializeLedger(ledger);\n let existing: string | null = null;\n try {\n existing = await readFile(path, \"utf-8\");\n } catch {\n existing = null;\n }\n if (existing === content) {\n return false;\n }\n await writeTextAtomic(path, content);\n return true;\n};\n\n/** Record that `sourceRel` is translated into `locale` at source hash `hash`. */\nexport const stampLedger = (\n ledger: TranslationLedger,\n sourceRel: string,\n locale: string,\n hash: string\n): void => {\n const locales = ledger.files[sourceRel] ?? {};\n locales[locale] = hash;\n ledger.files[sourceRel] = locales;\n};\n\n/**\n * Drop entries for sources that no longer exist and locales that are no longer\n * configured, so deleted pages and removed locales don't linger in the ledger\n * forever. Returns a new ledger.\n */\nexport const pruneLedger = (\n ledger: TranslationLedger,\n knownSources: ReadonlySet<string>,\n knownLocales: ReadonlySet<string>\n): TranslationLedger => {\n const files: Record<string, Record<string, string>> = {};\n for (const [source, locales] of Object.entries(ledger.files)) {\n if (!knownSources.has(source)) {\n continue;\n }\n const kept = Object.fromEntries(\n Object.entries(locales).filter(([locale]) => knownLocales.has(locale))\n );\n if (Object.keys(kept).length > 0) {\n files[source] = kept;\n }\n }\n return { files, version: ledger.version };\n};\n",
8
+ "import { colors } from \"consola/utils\";\nimport type { ColorFunction } from \"consola/utils\";\n\nimport { AGENTS } from \"../audit/agent.ts\";\nimport type { AgentKind } from \"../audit/agent.ts\";\nimport { duration, money, seconds } from \"../cli/report-format.ts\";\nimport { countBySeverity } from \"../core/diagnostics.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\nimport type {\n TranslateItemResult,\n TranslateItemStatus,\n TranslateResult,\n} from \"./run.ts\";\nimport type {\n MetaWorkEntry,\n TranslateWorkList,\n WorkItem,\n WorkStatus,\n} from \"./work-list.ts\";\n\n/**\n * The live progress UI, colored via consola's `colors` (which honors\n * NO_COLOR/FORCE_COLOR and TTY detection), matching the eval report's palette.\n * Render functions are pure; the renderer takes an injectable `write`/`now` so\n * tests never touch a real TTY or clock. The command sends everything here to\n * stderr via the raw `write` — never `logger.info`, which consola drops in\n * test and CI environments.\n */\n\nconst ESC = String.fromCodePoint(27);\n\nconst GLYPH = {\n failed: \"✖\",\n partial: \"!\",\n translated: \"✔\",\n} satisfies Record<TranslateItemStatus, string>;\n\nconst STATUS_COLOR = {\n failed: colors.red,\n partial: colors.yellow,\n translated: colors.green,\n} satisfies Record<TranslateItemStatus, ColorFunction>;\n\nexport const SPINNER_FRAMES = [\n \"⠋\",\n \"⠙\",\n \"⠹\",\n \"⠸\",\n \"⠼\",\n \"⠴\",\n \"⠦\",\n \"⠧\",\n \"⠇\",\n \"⠏\",\n];\n\n/** How often the TTY spinner advances (one frame per interval). */\nexport const SPINNER_INTERVAL_MS = 80;\n\n/** The clear-to-start-of-line prefix every TTY rewrite uses. */\nconst REWRITE = `\\r${ESC}[K`;\n\n/** `docs/guides/install.mdx → fr`, or the batched meta call's label. */\nexport const itemLabel = (item: WorkItem): string =>\n item.kind === \"page\"\n ? `${item.sourceRel} → ${item.locale}`\n : `meta title${item.entries.length === 1 ? \"\" : \"s\"} (${item.entries.length}) → ${item.locale}`;\n\n/**\n * The in-flight spinner line a TTY rewrites in place: the oldest active\n * item's label, how many more lanes are running, and the run's progress.\n */\nexport const spinnerLine = (\n active: WorkItem[],\n done: number,\n total: number,\n frame: number\n): string => {\n // SAFETY: the renderer only paints while at least one item is active, so the\n // oldest active entry exists.\n const first = active[0] as WorkItem;\n const more = active.length > 1 ? ` (+${active.length - 1} more)` : \"\";\n // SAFETY: `frame % SPINNER_FRAMES.length` is always an index into the\n // non-empty frames array.\n return ` ${colors.cyan(SPINNER_FRAMES[frame % SPINNER_FRAMES.length] as string)} ${itemLabel(first)}${more} ${colors.dim(`${done}/${total}`)}`;\n};\n\n/** The permanent line printed when an item finishes. */\nexport const itemEndLine = (result: TranslateItemResult): string => {\n const color = STATUS_COLOR[result.status];\n const glyph = color(GLYPH[result.status]);\n const label = itemLabel(result.item);\n if (result.status === \"translated\") {\n const cells = [seconds(result.durationMs), money(result.costUsd)]\n .filter((cell) => cell !== \"\")\n .join(\" \");\n return ` ${glyph} ${label} ${colors.dim(cells)}`;\n }\n const word = result.status === \"partial\" ? \"partial\" : \"failed\";\n return ` ${glyph} ${label} ${color(word)}${\n result.detail ? colors.dim(`: ${result.detail}`) : \"\"\n }`;\n};\n\n/** The header line the command prints before the first item runs. */\nexport const translateHeaderLine = (\n itemCount: number,\n localeCount: number,\n agent: AgentKind\n): string =>\n `${colors.bold(\"blume translate\")} ${itemCount} item(s) · ${localeCount} locale(s) · ${AGENTS[agent].name}`;\n\n/**\n * `Translated 11 files into 2 locales · 1 failed · 2 adopted · 8 already up to\n * date · 4m 12s · $0.41` (cost only when the agent reports one).\n */\nexport const translateSummaryLine = (\n result: TranslateResult,\n workList: TranslateWorkList\n): string => {\n const { counts } = result;\n const parts = [\n `Translated ${counts.translated} file${counts.translated === 1 ? \"\" : \"s\"} into ${workList.targetLocales.length} locale${workList.targetLocales.length === 1 ? \"\" : \"s\"}`,\n counts.failed > 0 ? `${counts.failed} failed` : \"\",\n counts.partial > 0 ? `${counts.partial} partial` : \"\",\n workList.untracked.length > 0 ? `${workList.untracked.length} adopted` : \"\",\n workList.upToDate > 0 ? `${workList.upToDate} already up to date` : \"\",\n duration(result.durationMs),\n money(result.costUsd),\n ].filter((part) => part !== \"\");\n return parts.join(\" · \");\n};\n\n/** Dim warnings for work-list diagnostics (e.g. a factory-form meta file). */\nexport const diagnosticLines = (diagnostics: Diagnostic[]): string[] =>\n diagnostics.map(\n (diagnostic) => ` ${colors.yellow(\"⚠\")} ${colors.dim(diagnostic.message)}`\n );\n\n/** One (source, locale) pair a `--check` run flags. */\ninterface DriftRow {\n /** Absolute path of the default-locale source. */\n file: string;\n locale: string;\n sourceRel: string;\n status: WorkStatus;\n}\n\n/** Every (source, locale, status) drift row in a work list, pages then meta. */\nconst driftRows = (workList: TranslateWorkList): DriftRow[] =>\n workList.items.flatMap((item) =>\n item.kind === \"page\"\n ? [\n {\n file: item.sourcePath,\n locale: item.locale,\n sourceRel: item.sourceRel,\n status: item.status,\n },\n ]\n : item.entries.map((entry: MetaWorkEntry) => ({\n file: entry.meta.file,\n locale: item.locale,\n sourceRel: entry.meta.sourceRel,\n status: entry.status,\n }))\n );\n\n/**\n * Each missing or stale translation as an error diagnostic, so the `--check`\n * JSON's `summary` counts the drift that fails the gate — the exit-code\n * contract `blume validate/audit/eval --json` keep, where a non-zero exit\n * always shows as a non-zero `summary.error`.\n */\nconst driftDiagnostics = (workList: TranslateWorkList): Diagnostic[] =>\n driftRows(workList).map((row) => ({\n code:\n row.status === \"missing\"\n ? \"BLUME_TRANSLATE_MISSING\"\n : \"BLUME_TRANSLATE_STALE\",\n file: row.file,\n message:\n row.status === \"missing\"\n ? `${row.sourceRel} has no \"${row.locale}\" translation.`\n : `The \"${row.locale}\" translation of ${row.sourceRel} is stale: its source changed since it was translated.`,\n severity: \"error\",\n suggestion:\n \"Run `blume translate --codex` (or `--claude`) to bring the translations up to date.\",\n }));\n\n/** One line per missing/stale pair, plus dim lines for untracked adoptions. */\nexport const checkLines = (workList: TranslateWorkList): string[] => [\n ...driftRows(workList).map(\n (row) =>\n ` ${colors.red(\"✖\")} ${row.sourceRel} → ${row.locale} ${colors.dim(row.status)}`\n ),\n ...workList.untracked.map(\n (entry) =>\n ` ${colors.dim(`⊘ ${entry.sourceRel} → ${entry.locale} untracked (adopted by the next translate run)`)}`\n ),\n];\n\n/** The `--check` totals: `2 missing · 1 stale · 1 untracked · 14 up to date`. */\nexport const checkSummaryLine = (workList: TranslateWorkList): string => {\n const rows = driftRows(workList);\n const missing = rows.filter((row) => row.status === \"missing\").length;\n const stale = rows.filter((row) => row.status === \"stale\").length;\n const parts = [\n missing > 0 ? `${missing} missing` : \"\",\n stale > 0 ? `${stale} stale` : \"\",\n workList.untracked.length > 0\n ? `${workList.untracked.length} untracked`\n : \"\",\n `${workList.upToDate} up to date`,\n ].filter((part) => part !== \"\");\n return parts.join(\" · \");\n};\n\n/** Whether a work list fails the `--check` gate (untracked never does). */\nexport const hasDrift = (workList: TranslateWorkList): boolean =>\n workList.items.length > 0;\n\n/**\n * The machine-readable `--check` report. The `diagnostics` + `summary` shape\n * matches `blume validate/audit/eval --json` exactly — every missing or stale\n * translation is an error diagnostic there, so `summary.error` agrees with the\n * exit code — with the drift report grouped by locale under `translate`.\n */\nexport const checkReportJson = (workList: TranslateWorkList): string => {\n const diagnostics = [...workList.diagnostics, ...driftDiagnostics(workList)];\n const locales: Record<\n string,\n { missing: string[]; stale: string[]; untracked: string[] }\n > = {};\n for (const locale of workList.targetLocales) {\n locales[locale] = { missing: [], stale: [], untracked: [] };\n }\n for (const row of driftRows(workList)) {\n locales[row.locale]?.[row.status].push(row.sourceRel);\n }\n for (const entry of workList.untracked) {\n locales[entry.locale]?.untracked.push(entry.sourceRel);\n }\n return `${JSON.stringify(\n {\n diagnostics,\n summary: countBySeverity(diagnostics),\n translate: { locales, upToDate: workList.upToDate },\n },\n null,\n 2\n )}\\n`;\n};\n\n/** One run result lowered to JSON-friendly, root-relative fields. */\nconst resultJson = (result: TranslateItemResult) => ({\n costUsd: result.costUsd,\n detail: result.detail,\n durationMs: result.durationMs,\n kind: result.item.kind,\n locale: result.item.locale,\n status: result.status,\n ...(result.item.kind === \"page\"\n ? { source: result.item.sourceRel, target: result.item.targetRel }\n : {\n sources: result.item.entries.map((entry) => entry.meta.sourceRel),\n }),\n});\n\n/** The machine-readable report for a translation run (`--json`). */\nexport const translateReportJson = (\n result: TranslateResult,\n workList: TranslateWorkList\n): string => {\n const diagnostics = [...workList.diagnostics, ...result.diagnostics];\n return `${JSON.stringify(\n {\n diagnostics,\n summary: countBySeverity(diagnostics),\n translate: {\n adopted: workList.untracked.length,\n agent: result.agent,\n costUsd: result.costUsd,\n counts: result.counts,\n durationMs: result.durationMs,\n results: result.results.map(resultJson),\n upToDate: workList.upToDate,\n },\n },\n null,\n 2\n )}\\n`;\n};\n\nexport interface ProgressRenderer {\n onProgress: (\n event:\n | {\n kind: \"item-end\";\n index: number;\n result: TranslateItemResult;\n total: number;\n }\n | { kind: \"item-start\"; index: number; item: WorkItem; total: number }\n ) => void;\n stop: () => void;\n}\n\n/**\n * Live progress: on a TTY the in-flight items render as one spinner line\n * rewritten in place (`\\r\\x1B[K`) — a concurrent run shows the oldest active\n * item plus a `(+n more)` count — and each completion prints its permanent\n * line above it; off-TTY (CI) there is no interval and only the permanent\n * per-item lines print.\n */\nexport const createProgressRenderer = (options: {\n isTTY: boolean;\n write: (chunk: string) => void;\n now?: () => number;\n}): ProgressRenderer => {\n const now = options.now ?? (() => performance.now());\n let timer: ReturnType<typeof setInterval> | undefined;\n const active = new Map<number, WorkItem>();\n let done = 0;\n let total = 0;\n let startedAt = 0;\n\n const paint = (): void => {\n if (active.size > 0) {\n const frame = Math.floor((now() - startedAt) / SPINNER_INTERVAL_MS);\n options.write(\n `${REWRITE}${spinnerLine([...active.values()], done, total, frame)}`\n );\n }\n };\n const clearTimer = (): void => {\n if (timer) {\n clearInterval(timer);\n timer = undefined;\n }\n };\n\n return {\n onProgress(event) {\n ({ total } = event);\n if (event.kind === \"item-start\") {\n active.set(event.index, event.item);\n if (!options.isTTY) {\n return;\n }\n if (!timer) {\n startedAt = now();\n timer = setInterval(paint, SPINNER_INTERVAL_MS);\n timer.unref?.();\n }\n paint();\n return;\n }\n active.delete(event.index);\n done += 1;\n const line = itemEndLine(event.result);\n if (!options.isTTY) {\n options.write(`${line}\\n`);\n return;\n }\n options.write(`${REWRITE}${line}\\n`);\n if (active.size > 0) {\n paint();\n } else {\n clearTimer();\n }\n },\n stop() {\n clearTimer();\n active.clear();\n },\n };\n};\n",
9
+ "import { existsSync } from \"node:fs\";\nimport { mkdtemp, readFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\n\nimport pLimit from \"p-limit\";\nimport pMap from \"p-map\";\nimport { dirname, join } from \"pathe\";\n\nimport { AGENTS } from \"../audit/agent.ts\";\nimport type { AgentKind } from \"../audit/agent.ts\";\nimport { writeTextAtomic } from \"../core/fs-atomic.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport type { LocaleConfig } from \"../core/schema.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\nimport { readAgentOutput, runAgentHeadless } from \"../eval/agents.ts\";\nimport type { AgentOutput, HeadlessRunner } from \"../eval/agents.ts\";\nimport { DEFAULT_TRANSLATE_TIMEOUT_MS, translateAgentArgs } from \"./agents.ts\";\nimport { hashSource, stampLedger } from \"./ledger.ts\";\nimport type { TranslationLedger } from \"./ledger.ts\";\nimport { generateMetaModule } from \"./meta.ts\";\nimport type { TranslatableMeta } from \"./meta.ts\";\nimport { metaPrompt, pagePrompt } from \"./prompts.ts\";\nimport { parseMetaTitles, validateTranslation } from \"./validate.ts\";\nimport type {\n MetaWorkItem,\n PageWorkItem,\n TranslateWorkList,\n WorkItem,\n} from \"./work-list.ts\";\n\nexport type TranslateItemStatus = \"failed\" | \"partial\" | \"translated\";\n\nexport interface TranslateItemResult {\n costUsd?: number;\n /** Why the item failed (agent error, validation failure, missing titles). */\n detail?: string;\n durationMs: number;\n item: WorkItem;\n status: TranslateItemStatus;\n}\n\nexport type TranslateProgress =\n | {\n kind: \"item-end\";\n index: number;\n result: TranslateItemResult;\n total: number;\n }\n | { kind: \"item-start\"; index: number; item: WorkItem; total: number };\n\nexport interface TranslateRunOptions {\n agent: AgentKind;\n /** Parallel agent sessions. Defaults to 1 (serial). */\n concurrency?: number;\n /** Mutated in place: every validated write stamps its entry immediately. */\n ledger: TranslationLedger;\n onProgress?: (event: TranslateProgress) => void;\n /**\n * Called after each finished item to flush the ledger to disk, so an\n * interrupted run keeps everything already translated. Calls are serialized\n * here — concurrent workers finishing together never race the same file.\n * The flush's result (`writeLedger`'s wrote-or-not boolean) is ignored.\n */\n persistLedger?: () => Promise<boolean | undefined>;\n project: BlumeProject;\n /** The spawn function — injectable so tests never launch a real agent. */\n run?: HeadlessRunner;\n timeoutMs?: number;\n workList: TranslateWorkList;\n}\n\nexport interface TranslateResult {\n agent: AgentKind;\n /** Total spend, when the agent CLI reports it (claude does, codex doesn't). */\n costUsd?: number;\n counts: Record<TranslateItemStatus, number>;\n diagnostics: Diagnostic[];\n durationMs: number;\n results: TranslateItemResult[];\n}\n\ninterface RunContext {\n bin: string;\n dir: string;\n kind: AgentKind;\n run: HeadlessRunner;\n source: LocaleConfig;\n targets: Map<string, LocaleConfig>;\n timeoutMs: number;\n}\n\n/**\n * A meta file's key in a meta-titles prompt: its directory relative to the\n * project root. The content-root-relative `dir` isn't unique — two\n * filesystem sources can both have a `guides/` folder — and colliding keys\n * would hand both folders one translation.\n */\nconst metaKey = (meta: TranslatableMeta): string => dirname(meta.sourceRel);\n\n/**\n * One headless agent call. A missing executable rejects with ENOENT on every\n * platform (the runner spawns without a shell), which the command layer turns\n * into an install hint.\n */\nconst invokeAgent = async (\n context: RunContext,\n prompt: string,\n index: number\n): Promise<AgentOutput> => {\n const messagePath = join(context.dir, `message-${index}.txt`);\n const result = await context.run(\n context.bin,\n translateAgentArgs(context.kind, messagePath),\n { cwd: context.dir, prompt, timeoutMs: context.timeoutMs }\n );\n return await readAgentOutput(context.kind, result, messagePath);\n};\n\nconst runPageItem = async (\n item: PageWorkItem,\n index: number,\n context: RunContext,\n ledger: TranslationLedger\n): Promise<TranslateItemResult> => {\n const started = performance.now();\n const done = (\n status: TranslateItemStatus,\n detail?: string,\n costUsd?: number\n ): TranslateItemResult => ({\n costUsd,\n detail,\n durationMs: Math.round(performance.now() - started),\n item,\n status,\n });\n\n const sourceText = await readFile(item.sourcePath, \"utf-8\");\n if (item.verbatim) {\n // A non-markdown include partial (code embed) has no prose to translate;\n // it's copied unchanged so the locale tree's relative includes resolve.\n await writeTextAtomic(item.targetPath, sourceText);\n stampLedger(ledger, item.sourceRel, item.locale, hashSource(sourceText));\n return done(\"translated\");\n }\n // SAFETY: `targets` maps every configured locale, and work items only carry\n // configured locale codes.\n const target = context.targets.get(item.locale) as LocaleConfig;\n // A stale item's target is the existing translation's own file, even at a\n // non-canonical name (see `PageWorkItem.targetPath`), so it's the style\n // precedent and the file rewritten; a missing item has nothing to read.\n const previousTranslation = existsSync(item.targetPath)\n ? await readFile(item.targetPath, \"utf-8\")\n : undefined;\n const output = await invokeAgent(\n context,\n pagePrompt(sourceText, target, context.source, previousTranslation),\n index\n );\n if (output.isError) {\n return done(\"failed\", output.detail ?? \"agent failed\", output.costUsd);\n }\n const validated = validateTranslation(sourceText, output.text);\n if (!validated.ok) {\n return done(\"failed\", validated.reason, output.costUsd);\n }\n await writeTextAtomic(item.targetPath, validated.text);\n stampLedger(ledger, item.sourceRel, item.locale, hashSource(sourceText));\n return done(\"translated\", undefined, output.costUsd);\n};\n\nconst runMetaItem = async (\n item: MetaWorkItem,\n index: number,\n context: RunContext,\n ledger: TranslationLedger\n): Promise<TranslateItemResult> => {\n const started = performance.now();\n const done = (\n status: TranslateItemStatus,\n detail?: string,\n costUsd?: number\n ): TranslateItemResult => ({\n costUsd,\n detail,\n durationMs: Math.round(performance.now() - started),\n item,\n status,\n });\n\n const titles = Object.fromEntries(\n item.entries.map((entry) => [metaKey(entry.meta), entry.meta.title])\n );\n // SAFETY: `targets` maps every configured locale, and work items only carry\n // configured locale codes.\n const target = context.targets.get(item.locale) as LocaleConfig;\n const output = await invokeAgent(\n context,\n metaPrompt(titles, target, context.source),\n index\n );\n if (output.isError) {\n return done(\"failed\", output.detail ?? \"agent failed\", output.costUsd);\n }\n\n const parsed = parseMetaTitles(output.text, Object.keys(titles));\n for (const entry of item.entries) {\n const translated = parsed.titles[metaKey(entry.meta)];\n if (translated === undefined) {\n continue;\n }\n // Each entry writes and stamps independently, so a partially usable reply\n // still lands the titles it did translate.\n // oxlint-disable-next-line no-await-in-loop\n await writeTextAtomic(\n entry.targetPath,\n generateMetaModule(entry.meta.data, translated)\n );\n stampLedger(\n ledger,\n entry.meta.sourceRel,\n item.locale,\n hashSource(entry.meta.raw)\n );\n }\n\n if (parsed.missing.length === item.entries.length) {\n return done(\"failed\", \"reply contained no usable titles\", output.costUsd);\n }\n if (parsed.missing.length > 0) {\n return done(\n \"partial\",\n `no translation for: ${parsed.missing.join(\", \")}`,\n output.costUsd\n );\n }\n return done(\"translated\", undefined, output.costUsd);\n};\n\nconst itemDiagnostic = (result: TranslateItemResult): Diagnostic => {\n const { item } = result;\n const site =\n item.kind === \"page\"\n ? { file: item.sourcePath, subject: item.sourceRel }\n : {\n file: item.entries[0]?.meta.file,\n subject: `meta titles (${item.entries.length})`,\n };\n return {\n code:\n result.status === \"partial\"\n ? \"BLUME_TRANSLATE_META_PARTIAL\"\n : \"BLUME_TRANSLATE_FAILED\",\n file: site.file,\n message: `Translating ${site.subject} into \"${item.locale}\" ${\n result.status === \"partial\" ? \"partially failed\" : \"failed\"\n }: ${result.detail ?? \"unknown error\"}.`,\n severity: result.status === \"partial\" ? \"warning\" : \"error\",\n };\n};\n\n/**\n * Run every work item through the agent, `concurrency` at a time: each worker\n * is a serial lane pulling the next unclaimed item, so results stay indexed\n * by item and progress events interleave but never duplicate. A failed item\n * never writes or stamps; its lane continues. After every finished item the\n * ledger is flushed via `persistLedger` (serialized across lanes), so an\n * interrupted run resumes from what already landed instead of from scratch.\n */\nexport const runTranslate = async (\n options: TranslateRunOptions\n): Promise<TranslateResult> => {\n const started = performance.now();\n const { i18n } = options.project.config;\n if (!i18n) {\n throw new Error(\"blume translate requires i18n to be configured\");\n }\n // SAFETY: the config schema requires `defaultLocale` to be one of `locales`.\n const source = i18n.locales.find(\n (locale) => locale.code === i18n.defaultLocale\n ) as LocaleConfig;\n\n const context: RunContext = {\n bin: AGENTS[options.agent].bin,\n dir: await mkdtemp(join(tmpdir(), \"blume-translate-\")),\n kind: options.agent,\n run: options.run ?? runAgentHeadless,\n source,\n targets: new Map(i18n.locales.map((locale) => [locale.code, locale])),\n timeoutMs: options.timeoutMs ?? DEFAULT_TRANSLATE_TIMEOUT_MS,\n };\n\n const { items } = options.workList;\n const concurrency = Math.max(\n 1,\n Math.min(options.concurrency ?? 1, items.length || 1)\n );\n\n // The persist mutex: ledger flushes from concurrent lanes are serialized so\n // two lanes never write the ledger file at the same time.\n const persistLimit = pLimit(1);\n const persist = (): Promise<boolean | undefined> =>\n persistLimit(() => options.persistLedger?.());\n\n const results = await pMap(\n items,\n async (item: WorkItem, index): Promise<TranslateItemResult> => {\n options.onProgress?.({\n index,\n item,\n kind: \"item-start\",\n total: items.length,\n });\n const result = await (item.kind === \"page\"\n ? runPageItem(item, index, context, options.ledger)\n : runMetaItem(item, index, context, options.ledger));\n // Flush this item's stamps before the slot frees for the next item, so\n // a kill loses at most the in-flight items.\n await persist();\n options.onProgress?.({\n index,\n kind: \"item-end\",\n result,\n total: items.length,\n });\n return result;\n },\n { concurrency }\n );\n\n const diagnostics: Diagnostic[] = [];\n for (const result of results) {\n if (result.status !== \"translated\") {\n diagnostics.push(itemDiagnostic(result));\n }\n }\n\n const counts = {\n failed: 0,\n partial: 0,\n translated: 0,\n } satisfies Record<TranslateItemStatus, number>;\n for (const result of results) {\n counts[result.status] += 1;\n }\n const costs = results.flatMap((result) =>\n result.costUsd === undefined ? [] : [result.costUsd]\n );\n\n return {\n agent: options.agent,\n costUsd:\n costs.length > 0\n ? costs.reduce((total, cost) => total + cost, 0)\n : undefined,\n counts,\n diagnostics,\n durationMs: Math.round(performance.now() - started),\n results,\n };\n};\n",
10
+ "import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\n\nimport { dirname, join, relative } from \"pathe\";\n\nimport { localeTargetPath } from \"../core/i18n.ts\";\nimport { createModuleLoader } from \"../core/load-module.ts\";\nimport { findFolderMetaFiles } from \"../core/meta.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport { folderMetaSchema } from \"../core/schema.ts\";\nimport type { FolderMeta, ResolvedI18nConfig } from \"../core/schema.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\n\n/**\n * Folder-nav `meta.ts` titles are translatable under the `dir` parser ONLY:\n * per-locale meta is a whole-file replacement, not a merge (`applyFolderMeta`\n * looks up `<locale>/<group>` and falls back to shared meta, never to the\n * default locale's file), and the `dot` parser has no per-locale meta\n * mechanism at all. So the generated module must copy EVERY source key\n * (order/pages/icon/collapsed) with only the title translated — otherwise the\n * locale's navigation loses its ordering.\n */\n\n/** One default-locale meta file whose title can be translated. */\nexport interface TranslatableMeta {\n /** The parsed meta module; copied wholesale into the generated module. */\n data: FolderMeta;\n /** Directory relative to the owning source's content root (`\"\"` = root). */\n dir: string;\n /** Absolute path of the source meta file. */\n file: string;\n /** The owning source's content root (locale directories live under it). */\n contentRoot: string;\n /** Raw source text at discovery time — what the ledger hashes. */\n raw: string;\n /** POSIX root-relative path of the source meta file — the ledger key. */\n sourceRel: string;\n /** The source title to translate. */\n title: string;\n}\n\n/** A folder's meta module names, in the order a lookup prefers them. */\nconst META_NAMES = [\"meta.ts\", \"meta.js\", \"meta.mjs\"];\n\nconst META_FILES = META_NAMES.map((name) => `**/${name}`);\n\n/**\n * Whether a loaded meta module default-exports a factory function. Generic so\n * it can decode the loader's untyped module value at this boundary.\n */\nconst isFactoryModule = <T>(\n value: T\n): value is T & ((...args: never[]) => FolderMeta) =>\n typeof value === \"function\";\n\n/**\n * Where a locale's meta module lives. The locale folder is resolved the way\n * pages resolve it (`localeTargetPath`): `folders` are the content root's\n * existing top-level folders, and one naming the locale in another casing\n * (`pt-br/` for a configured `pt-BR`) is the locale's folder already. In that\n * folder, a `meta.js`/`meta.mjs` that already exists is the target — the\n * generated module is plain JavaScript, and writing a `meta.ts` beside it\n * would leave the folder with two meta files. Otherwise it's a new `meta.ts`.\n */\nexport const metaTargetPath = (\n meta: TranslatableMeta,\n locale: string,\n i18n: ResolvedI18nConfig,\n folders: readonly string[]\n): string => {\n const dir = join(\n meta.contentRoot,\n dirname(\n localeTargetPath(join(meta.dir, \"meta.ts\"), \".ts\", locale, i18n, folders)\n )\n );\n const existing = META_NAMES.find((name) => existsSync(join(dir, name)));\n return join(dir, existing ?? \"meta.ts\");\n};\n\n/**\n * Discover the default-locale `meta.{ts,js,mjs}` files whose titles a\n * translation run covers. Skips (in order): non-`dir` i18n projects entirely,\n * files the scan's own folder-meta discovery never reads either — those\n * matching the source's `exclude` globs (application code) or in folders its\n * `include` globs don't reach (see `findFolderMetaFiles`) — files inside a\n * configured locale directory (those ARE translations), files inside an\n * archived-version snapshot (frozen, with their own translations — the same\n * folders the navigation's meta lookup hoists as versions), modules that fail\n * to load (the scan already errors on those), factory-form modules (a warning\n * — the generator can't re-emit a function), modules that fail meta\n * validation (the scan already errors on those too), and modules with no\n * `title` (nothing to translate).\n */\nexport const discoverTranslatableMeta = async (\n project: BlumeProject\n): Promise<{ metas: TranslatableMeta[]; diagnostics: Diagnostic[] }> => {\n const { i18n } = project.config;\n if (!i18n || i18n.parser !== \"dir\") {\n return { diagnostics: [], metas: [] };\n }\n const localeDirs = new Set(\n i18n.locales.flatMap((locale) =>\n locale.code === i18n.defaultLocale ? [] : [locale.code.toLowerCase()]\n )\n );\n const versionDirs = new Set(\n project.config.versions?.archived.map((version) => version.id)\n );\n\n const load = createModuleLoader();\n const metas: TranslatableMeta[] = [];\n const diagnostics: Diagnostic[] = [];\n\n const roots = project.sources.flatMap((source) =>\n source.staged || !source.contentRoot\n ? []\n : [\n {\n contentRoot: source.contentRoot,\n exclude: source.exclude,\n include: source.include,\n },\n ]\n );\n for (const { contentRoot, exclude, include } of roots) {\n // The scan's own file set (`discoverFolderMeta`): a `meta.ts` under an\n // excluded folder (`src/lib/meta.ts` beside `exclude: [\"src/**\"]`) is\n // application code, and one in a folder no `include` glob reaches\n // configures no group the build renders.\n // oxlint-disable-next-line no-await-in-loop -- a project has O(1) sources\n const files = await findFolderMetaFiles(\n { exclude, include, root: contentRoot },\n META_FILES\n );\n for (const file of files.toSorted()) {\n const dir = relative(contentRoot, dirname(file));\n const [head] = dir.split(\"/\");\n if (\n head &&\n (localeDirs.has(head.toLowerCase()) || versionDirs.has(head))\n ) {\n continue;\n }\n let mod: Awaited<ReturnType<typeof load>>;\n try {\n // oxlint-disable-next-line no-await-in-loop -- sequential, ordered discovery\n mod = await load(file);\n } catch {\n // Folder meta that fails to load is the scan's BLUME_META_LOAD_FAILED\n // error, not a crash here; either way there's no title to read.\n continue;\n }\n // oxlint-disable-next-line no-await-in-loop -- sequential, ordered discovery\n const raw = await readFile(file, \"utf-8\");\n if (isFactoryModule(mod)) {\n diagnostics.push({\n code: \"BLUME_TRANSLATE_META_FACTORY\",\n file,\n message:\n \"This meta file default-exports a function, so `blume translate` cannot generate per-locale copies of it.\",\n severity: \"warning\",\n suggestion:\n \"Export a plain object, or author the locale's meta file by hand.\",\n });\n continue;\n }\n const parsed = folderMetaSchema.safeParse(mod);\n if (!parsed.success || parsed.data.title === undefined) {\n continue;\n }\n metas.push({\n contentRoot,\n data: parsed.data,\n dir: dir === \".\" ? \"\" : dir,\n file,\n raw,\n sourceRel: relative(project.context.root, file),\n title: parsed.data.title,\n });\n }\n }\n return { diagnostics, metas };\n};\n\n/**\n * Emit the per-locale meta module: every source key copied verbatim, only the\n * title swapped for its translation. Keys alphabetical, values as JSON.\n */\nexport const generateMetaModule = (\n meta: FolderMeta,\n translatedTitle: string\n): string => {\n const data = {\n ...meta,\n title: translatedTitle,\n };\n const lines = Object.entries(data)\n .toSorted(([a], [b]) => (a < b ? -1 : 1))\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => ` ${key}: ${JSON.stringify(value)},`);\n return [\n \"// Generated by `blume translate` — edit the default locale's meta file\",\n \"// and rerun the translation instead of editing this copy.\",\n \"export default {\",\n ...lines,\n \"};\",\n \"\",\n ].join(\"\\n\");\n};\n",
11
+ "import type { LocaleConfig } from \"../core/schema.ts\";\n\n/**\n * The frontmatter key paths whose *values* an agent may translate. Everything\n * else in the frontmatter is copied from the source verbatim by the validator,\n * so this list is both the prompt's instruction and the reconciliation\n * contract in `validate.ts`.\n */\nexport const TRANSLATABLE_KEY_PATHS: readonly (readonly string[])[] = [\n [\"title\"],\n [\"description\"],\n [\"sidebar\", \"label\"],\n [\"sidebar\", \"badge\"],\n [\"seo\", \"title\"],\n [\"seo\", \"description\"],\n];\n\n/** \"French (fr)\" — the configured display label plus the code. */\nconst localeName = (locale: LocaleConfig): string =>\n `${locale.label} (${locale.code})`;\n\nconst KEY_LIST = TRANSLATABLE_KEY_PATHS.map((path) => path.join(\".\")).join(\n \", \"\n);\n\n/**\n * The page-translation prompt. Delivered over stdin (no argv limits), so the\n * full source file rides along inline. When the page was translated before,\n * the previous translation rides along too: without it, every retranslation\n * is a from-scratch rewrite in which the agent re-decides register, dialect,\n * and terminology (du vs Sie, pt-BR vs pt-PT) and churns the whole page for\n * a one-paragraph source edit.\n */\nexport const pagePrompt = (\n sourceText: string,\n target: LocaleConfig,\n source: LocaleConfig,\n previousTranslation?: string\n): string => {\n const styleRule =\n target.style === undefined\n ? \"\"\n : `\n- Write the translation in this style: ${target.style}`;\n const previousRule =\n previousTranslation === undefined\n ? \"\"\n : `\n- A translation of an earlier revision of this page is included below. Match its register, formality, dialect, and terminology exactly; re-translate only what the changed source requires and keep everything else word-for-word identical.${\n target.style === undefined\n ? \"\"\n : \" Where the previous translation disagrees with the style rule above, the style rule wins.\"\n }`;\n const previousSection =\n previousTranslation === undefined\n ? \"\"\n : `\nThe previous translation begins after this line and ends at the \"page source\" marker.\n${previousTranslation}`;\n return `Translate the following documentation page from ${localeName(\n source\n )} into ${localeName(target)}.\n\nRules:\n- Translate the prose: headings, paragraphs, list items, table cells, admonitions, and image alt text.\n- In the YAML frontmatter, translate ONLY the values of these keys: ${KEY_LIST}. Copy every other frontmatter key and value exactly as written.\n- Never translate or alter: code blocks, inline code, import/export statements, JSX/MDX component names and their attributes, URLs, link targets, HTML tags, or frontmatter keys.\n- A heading may end with anchor markers: \\`[#some-id]\\`, \\`{#some-id}\\` (also written \\`\\\\{#some-id\\\\}\\`), \\`[!toc]\\`, or \\`[toc]\\`. Copy every marker exactly as written, in the same position at the end of the translated heading — never translate, reorder, or drop a marker, and never change the id inside \\`[#...]\\` or \\`{#...}\\` (anchors must stay identical across languages).\n\n- Preserve the document structure exactly: the same headings, the same lists and tables, and the same number of code fences.${styleRule}${previousRule}\n- Output ONLY the complete translated file. Do not wrap it in a code fence. Do not add commentary before or after it.\n${previousSection}\nThe page source begins after this line.\n${sourceText}`;\n};\n\n/**\n * The sidebar-titles prompt: a JSON object of `{directory: title}` in, the\n * same keys with translated values out.\n */\nexport const metaPrompt = (\n titles: Record<string, string>,\n target: LocaleConfig,\n source: LocaleConfig\n): string => `Translate the following documentation sidebar section titles from ${localeName(\n source\n)} into ${localeName(target)}.\n\nThe input is a JSON object mapping a directory path to its title. Reply with ONLY a JSON object that has exactly the same keys, where each value is the title translated into ${localeName(\n target\n)}. Do not translate the keys. Do not add commentary.${\n target.style === undefined\n ? \"\"\n : `\\nWrite the titles in this style: ${target.style}`\n}\n\n${JSON.stringify(titles, null, 2)}`;\n",
12
+ "import { scanBody } from \"../core/sources/normalize.ts\";\n\n/**\n * Keep heading anchors identical across languages. A translated heading slugs\n * to a different id (`## Ordering` → `#ordering`, `## Reihenfolge` →\n * `#reihenfolge`), so a `/guide#ordering` link — which the runtime keeps\n * inside the reader's locale — would land at the top of the translated page.\n * After a translation validates, every heading whose rendered id would differ\n * from its source counterpart gets the source id pinned as a trailing\n * `[#id]` marker, so the anchor index (and every `#fragment` link) agrees in\n * every locale. Headings the translation already pins are left alone: the\n * agent is told to copy markers verbatim, and a hand-authored pin is the\n * translator's choice.\n *\n * Headings pair positionally, so the translation must mirror the source's\n * heading structure (the prompt demands exactly that); when it doesn't, no\n * pins are added and the text is returned unchanged with a reason.\n */\n\nexport interface PinResult {\n /** Number of headings that received a pin. */\n pinned: number;\n /** Why no pins were added, when the structures don't line up. */\n skipped?: string;\n text: string;\n}\n\n/** An ATX heading's optional closing hash run, which a marker must precede. */\nconst ATX_CLOSE = /\\s+#+$/u;\n\nconst structureOf = (headings: readonly { depth: number }[]): string =>\n headings.map((heading) => heading.depth).join(\",\");\n\n/** Append `[#id]` to a heading line, ahead of any closing `##` and line end. */\nconst pinLine = (line: string, id: string): string => {\n const eol = line.endsWith(\"\\r\") ? \"\\r\" : \"\";\n const body = line.slice(0, line.length - eol.length).trimEnd();\n const close = body.match(ATX_CLOSE)?.[0] ?? \"\";\n const head = body.slice(0, body.length - close.length);\n return `${head} [#${id}]${close}${eol}`;\n};\n\nexport const pinHeadingAnchors = (\n sourceText: string,\n translatedText: string\n): PinResult => {\n const source = scanBody(sourceText);\n const translated = scanBody(translatedText);\n if (structureOf(source.headings) !== structureOf(translated.headings)) {\n return {\n pinned: 0,\n skipped: `heading structure differs (source has ${source.headings.length} headings, translation has ${translated.headings.length})`,\n text: translatedText,\n };\n }\n\n const lines = translatedText.split(\"\\n\");\n let pinned = 0;\n for (const [index, heading] of translated.headings.entries()) {\n const site = translated.sites[index];\n const sourceId = source.headings[index]?.slug;\n if (\n !site ||\n site.pinned ||\n sourceId === undefined ||\n heading.slug === sourceId\n ) {\n continue;\n }\n lines[site.line - 1] = pinLine(lines[site.line - 1] ?? \"\", sourceId);\n pinned += 1;\n }\n if (pinned === 0) {\n return { pinned, text: translatedText };\n }\n\n // The pins must reproduce the source ids exactly once rendered; anything\n // else (a heading the line-based rewrite couldn't reach) keeps the text as\n // the agent wrote it rather than shipping a half-pinned page.\n const text = lines.join(\"\\n\");\n const rendered = scanBody(text).headings.map((heading) => heading.slug);\n const expected = source.headings.map((heading) => heading.slug);\n if (rendered.join(\"\\n\") !== expected.join(\"\\n\")) {\n return {\n pinned: 0,\n skipped: \"pinned anchors did not reproduce the source heading ids\",\n text: translatedText,\n };\n }\n return { pinned, text };\n};\n",
13
+ "import matter from \"../core/frontmatter.ts\";\nimport { pinHeadingAnchors } from \"./anchors.ts\";\nimport { TRANSLATABLE_KEY_PATHS } from \"./prompts.ts\";\n\n/**\n * Structural validation of agent output. The agent is never trusted for\n * structure: the final frontmatter is *reconstructed* from the source (clone\n * the source data, overlay only the translatable string values), so invented\n * keys are dropped, deleted keys are restored, and slugs/icons/orders/dates\n * stay source-verbatim by construction. Only then is the file written.\n */\n\nexport type ValidationResult =\n | { ok: true; text: string }\n | { ok: false; reason: string };\n\n/**\n * A parsed YAML frontmatter value (agent meta replies parse from JSON into the\n * same shape). js-yaml can also mint Dates and other rich scalars; the\n * traversal below only ever distinguishes \"keyed object\" from \"string\", so\n * they ride along as the object arm.\n */\ntype FrontmatterValue =\n | string\n | number\n | boolean\n | null\n | FrontmatterValue[]\n | FrontmatterData;\n\ninterface FrontmatterData {\n [key: string]: FrontmatterValue;\n}\n\n// gray-matter strips a leading byte order mark before it looks for the fence,\n// so a BOM-prefixed file still has frontmatter — this test must agree, or the\n// reconstruction below writes the body alone and the metadata is lost.\nconst FRONTMATTER_OPEN = /^\uFEFF?---\\r?\\n/u;\nconst FENCE_LINE = /^\\s*(?:```|~~~)/u;\n\n/**\n * Strip exactly one symmetric outer code fence (any info string) — the one\n * wrapper agents add despite being told not to. Anything else (no fence, or a\n * fence that isn't the entire output) is returned trimmed and untouched.\n */\nexport const stripOuterFence = (text: string): string => {\n const trimmed = text.trim();\n const match = trimmed.match(\n /^(?<fence>`{3,}|~{3,})[^\\n]*\\n(?<inner>[\\s\\S]*?)\\n\\k<fence>\\s*$/u\n );\n return match?.groups?.inner ?? trimmed;\n};\n\n/** Lines opening or closing a code fence; their count must survive translation. */\nconst countFenceLines = (text: string): number =>\n text.split(\"\\n\").filter((line) => FENCE_LINE.test(line)).length;\n\nconst isKeyedObject = (\n value: FrontmatterValue | undefined\n): value is FrontmatterData => typeof value === \"object\" && value !== null;\n\nconst isString = (value: FrontmatterValue | undefined): value is string =>\n typeof value === \"string\";\n\nconst getPath = (\n data: FrontmatterValue,\n path: readonly string[]\n): FrontmatterValue | undefined => {\n let value: FrontmatterValue | undefined = data;\n for (const key of path) {\n if (!isKeyedObject(value)) {\n return;\n }\n value = value[key];\n }\n return value;\n};\n\n/** Set `path` on `data`; only called for paths whose parents exist in `data`. */\nconst setPath = (\n data: FrontmatterData,\n path: readonly string[],\n value: string\n): void => {\n let parent = data;\n for (const key of path.slice(0, -1)) {\n // SAFETY: callers only set paths that getPath already resolved to a string\n // on this same (cloned) data, so every intermediate step is a keyed object.\n parent = parent[key] as FrontmatterData;\n }\n // SAFETY: every TRANSLATABLE_KEY_PATHS entry is a non-empty tuple, so the\n // path always has a final key.\n parent[path.at(-1) as string] = value;\n};\n\nconst ensureTrailingNewline = (text: string): string =>\n text.endsWith(\"\\n\") ? text : `${text}\\n`;\n\n/**\n * Validate one translated page against its source and reassemble the file to\n * write. Fails (no write happens) on empty output, unparseable or missing\n * frontmatter, an empty body, or a changed code-fence count.\n */\nexport const validateTranslation = (\n sourceText: string,\n agentText: string\n): ValidationResult => {\n // Only a fence the agent added is stripped. A source that is itself one\n // code block (a frontmatter-less partial holding a single bash block)\n // starts and ends with its own fence, which the translation must keep — so\n // for such a source the reply loses its outer fence only when it has more\n // fence lines than the source.\n const sourceIsFenced = stripOuterFence(sourceText) !== sourceText.trim();\n const candidate =\n sourceIsFenced && countFenceLines(agentText) <= countFenceLines(sourceText)\n ? agentText.trim()\n : stripOuterFence(agentText);\n if (candidate === \"\") {\n return { ok: false, reason: \"agent returned empty output\" };\n }\n\n const sourceHasFrontmatter = FRONTMATTER_OPEN.test(sourceText);\n const source = matter(sourceText);\n\n if (sourceHasFrontmatter && !FRONTMATTER_OPEN.test(candidate)) {\n return {\n ok: false,\n reason: \"translation dropped the frontmatter (must start with ---)\",\n };\n }\n\n let parsed: { content: string; data: FrontmatterData };\n try {\n parsed = matter(candidate);\n } catch {\n return { ok: false, reason: \"frontmatter does not parse as YAML\" };\n }\n\n const rawBody = ensureTrailingNewline(parsed.content.replace(/^\\r?\\n/u, \"\"));\n if (rawBody.trim() === \"\") {\n return { ok: false, reason: \"translation has an empty body\" };\n }\n\n const sourceFences = countFenceLines(source.content);\n const candidateFences = countFenceLines(rawBody);\n if (sourceFences !== candidateFences) {\n return {\n ok: false,\n reason: `code fence count changed (source has ${sourceFences}, translation has ${candidateFences})`,\n };\n }\n\n // Anchors stay identical across languages: each translated heading is\n // pinned to its source heading's id (see `anchors.ts`).\n const body = pinHeadingAnchors(source.content, rawBody).text;\n\n if (!sourceHasFrontmatter) {\n // A frontmatter-less source writes the body alone; any frontmatter the\n // agent invented is dropped with it.\n return { ok: true, text: body };\n }\n\n // Reconciliation by reconstruction: start from the SOURCE data and overlay\n // only the translatable key paths where both sides hold a string and the\n // translation is non-empty.\n const data: FrontmatterData = structuredClone(source.data);\n for (const path of TRANSLATABLE_KEY_PATHS) {\n const original = getPath(source.data, path);\n const translated = getPath(parsed.data, path);\n if (\n isString(original) &&\n isString(translated) &&\n translated.trim() !== \"\"\n ) {\n setPath(data, path, translated);\n }\n }\n\n return {\n ok: true,\n text: ensureTrailingNewline(matter.stringify(body, data)),\n };\n};\n\n/** A meta-batch parse: recovered titles by key, plus the keys still missing. */\nexport interface MetaTitlesResult {\n titles: Record<string, string>;\n missing: string[];\n}\n\n/**\n * Extract translated sidebar titles from a meta reply: tolerant first-`{`\n * to-last-`}` extraction (the eval `parseVerdict` idiom). Keys missing or\n * non-string in the reply land in `missing`, so a batch can partially succeed.\n */\nexport const parseMetaTitles = (\n agentText: string,\n expectedKeys: readonly string[]\n): MetaTitlesResult => {\n const start = agentText.indexOf(\"{\");\n const end = agentText.lastIndexOf(\"}\");\n let parsed: FrontmatterValue | undefined;\n if (start !== -1 && end > start) {\n try {\n parsed = JSON.parse(agentText.slice(start, end + 1));\n } catch {\n parsed = undefined;\n }\n }\n const record: FrontmatterData = isKeyedObject(parsed) ? parsed : {};\n\n const titles: Record<string, string> = {};\n const missing: string[] = [];\n for (const key of expectedKeys) {\n const value = record[key];\n if (isString(value) && value.trim() !== \"\") {\n titles[key] = value;\n } else {\n missing.push(key);\n }\n }\n return { missing, titles };\n};\n",
14
+ "import { existsSync, readdirSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\n\nimport { extname, join, relative } from \"pathe\";\n\nimport { localeCodes, localeTargetPath } from \"../core/i18n.ts\";\nimport { scanProject } from \"../core/project-graph.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport type { Diagnostic, PageRecord } from \"../core/types.ts\";\nimport { hashSource } from \"./ledger.ts\";\nimport type { TranslationLedger } from \"./ledger.ts\";\nimport { discoverTranslatableMeta, metaTargetPath } from \"./meta.ts\";\nimport type { TranslatableMeta } from \"./meta.ts\";\n\n/**\n * Classification of every (source, target locale) pair:\n * - `missing` — no non-fallback page record for (translationKey, locale).\n * Existence is the graph record, never a disk probe, so a hand-authored\n * translation at a non-canonical name still counts as existing.\n * - `stale` — a record exists, but the ledger hash differs from the current\n * source hash (the source changed since its last translation).\n * - up-to-date — hashes match (counted, not itemized).\n * - untracked — a record exists with no ledger entry: a pre-existing human\n * translation. Adopted (stamped at the current hash), never overwritten;\n * only `--force` retranslates it.\n */\nexport type WorkStatus = \"missing\" | \"stale\";\n\nexport interface PageWorkItem {\n kind: \"page\";\n /** Target locale code (configured casing). */\n locale: string;\n /** Absolute path of the default-locale source file. */\n sourcePath: string;\n /** POSIX root-relative source path — the ledger key. */\n sourceRel: string;\n status: WorkStatus;\n /**\n * Absolute target path: an existing translation's own file (which can sit\n * at a non-canonical name, like a hand-written `fr/guide.md` for\n * `guide.mdx`), otherwise the canonical `localeTargetPath`.\n */\n targetPath: string;\n /** POSIX root-relative target path, for display. */\n targetRel: string;\n /** Copy the file unchanged instead of translating it — a non-markdown\n * include partial (code embed) has no prose, but must still exist in each\n * locale tree so the translated pages' relative includes resolve. */\n verbatim?: boolean;\n}\n\n/** One meta file needing its title in one locale, inside a `MetaWorkItem`. */\nexport interface MetaWorkEntry {\n meta: TranslatableMeta;\n status: WorkStatus;\n /** Absolute path of the per-locale meta module (see `metaTargetPath`). */\n targetPath: string;\n}\n\n/** All of one locale's needed meta titles — a single agent call. */\nexport interface MetaWorkItem {\n kind: \"meta\";\n entries: MetaWorkEntry[];\n locale: string;\n}\n\nexport type WorkItem = MetaWorkItem | PageWorkItem;\n\n/** A pre-existing translation with no ledger entry, to adopt (stamp) as-is. */\nexport interface UntrackedEntry {\n /** The current source hash to stamp. */\n hash: string;\n kind: \"meta\" | \"page\";\n locale: string;\n sourceRel: string;\n}\n\nexport interface TranslateWorkList {\n diagnostics: Diagnostic[];\n items: WorkItem[];\n /** Every ledger key in the current universe (for pruning). */\n knownSources: Set<string>;\n /** The locales this run targets (configured casing, default excluded). */\n targetLocales: string[];\n untracked: UntrackedEntry[];\n /** Count of (source, locale) pairs already translated and current. */\n upToDate: number;\n}\n\nconst PAGE_EXTENSIONS = new Set([\".md\", \".mdx\"]);\n\n/**\n * The names in each content root, read once per root: a locale folder authored\n * in another casing (`pt-br/` for `pt-BR`) is where `localeTargetPath` puts\n * that locale's translations.\n */\nconst rootFolders = (): ((contentRoot: string) => string[]) => {\n const read = new Map<string, string[]>();\n return (contentRoot) => {\n const cached = read.get(contentRoot);\n if (cached) {\n return cached;\n }\n const names = readdirSync(contentRoot);\n read.set(contentRoot, names);\n return names;\n };\n};\n\n/**\n * Scan the project for a translation run: drafts included. A production\n * (`build`) scan drops them, so a hand-written translation marked\n * `draft: true` would look missing and be overwritten, and a source page\n * drafted for a while would lose its ledger stamps — its outdated\n * translations then reading as current once it's published again. A dev scan\n * keeps drafts and differs only in reusing cached remote content, which\n * translation never reads.\n */\nexport const scanForTranslation = (root: string): Promise<BlumeProject> =>\n scanProject(root, { mode: \"dev\" });\n\n/**\n * The translatable page universe: filesystem-backed default-locale pages.\n * Remote/staged sources have no writable path; fallback records are padding;\n * a `.$.` shared file already materializes into every locale.\n */\nconst translatablePages = (\n project: BlumeProject,\n i18n: NonNullable<BlumeProject[\"config\"][\"i18n\"]>\n): {\n page: PageRecord;\n contentRoot: string;\n ext: string;\n sourcePath: string;\n}[] => {\n const rootsByName = new Map(\n project.sources.flatMap((source) =>\n source.staged || !source.contentRoot\n ? []\n : [[source.name, source.contentRoot] as const]\n )\n );\n const seen = new Set<string>();\n const universe: {\n page: PageRecord;\n contentRoot: string;\n ext: string;\n sourcePath: string;\n }[] = [];\n for (const page of project.graph.pages) {\n if (\n page.locale !== i18n.defaultLocale ||\n page.fallback ||\n !page.sourcePath ||\n seen.has(page.sourcePath) ||\n // Archived snapshots are frozen and carry their own translations —\n // they are never (re)translated. (`localeTargetPath` would also compute\n // `fr/v1.0/…` where the snapshot's translation lives at `v1.0/fr/…`.)\n page.version !== \"\"\n ) {\n continue;\n }\n const ext = extname(page.sourcePath);\n const base = page.sourcePath.slice(0, page.sourcePath.length - ext.length);\n const contentRoot = rootsByName.get(page.source.name);\n if (!PAGE_EXTENSIONS.has(ext) || base.endsWith(\".$\") || !contentRoot) {\n continue;\n }\n seen.add(page.sourcePath);\n universe.push({ contentRoot, ext, page, sourcePath: page.sourcePath });\n }\n return universe;\n};\n\n/** Outcome of classifying one (source, locale) pair against the ledger:\n * a work status, an existing translation to adopt, or already current. */\ntype PairOutcome = WorkStatus | \"current\" | \"untracked\";\n\n/** The shared pair classifier — see the WorkStatus doc for the taxonomy. */\nconst classifyPair = (\n exists: boolean,\n stamp: string | undefined,\n hash: string,\n force: boolean\n): PairOutcome => {\n if (!exists) {\n return \"missing\";\n }\n if (stamp === undefined) {\n return force ? \"stale\" : \"untracked\";\n }\n return stamp !== hash || force ? \"stale\" : \"current\";\n};\n\n/** The classified work for the include partials the pages reference. */\ninterface PartialWork {\n items: PageWorkItem[];\n /** Ledger keys to add to the run's known-source universe. */\n sources: string[];\n untracked: UntrackedEntry[];\n upToDate: number;\n}\n\n/**\n * Work for the include partials referenced by the translatable pages. Under\n * the `dir` parser each locale is its own tree, so a translated page's\n * relative `<include>` resolves *inside the locale tree* — the partial must\n * exist there too, or the next build fails with BLUME_INCLUDE_NOT_FOUND on\n * every locale. (Under `dot` the translated file sits beside its source and\n * the shared partial already resolves; nothing to do.) Markdown partials are\n * translated like pages; anything else (code embeds) is copied verbatim. A\n * partial that is itself a page is skipped — its own translation already\n * lands at the mirrored path. Existence is a disk probe: partials have no\n * page records to consult.\n */\nconst partialWorkItems = async (\n universe: ReturnType<typeof translatablePages>,\n project: BlumeProject,\n i18n: NonNullable<BlumeProject[\"config\"][\"i18n\"]>,\n ledger: TranslationLedger,\n targetLocales: string[],\n force: boolean\n): Promise<PartialWork> => {\n const work: PartialWork = {\n items: [],\n sources: [],\n untracked: [],\n upToDate: 0,\n };\n if (i18n.parser !== \"dir\") {\n return work;\n }\n const { root } = project.context;\n const pagePaths = new Set(\n project.graph.pages.flatMap((page) =>\n page.sourcePath ? [page.sourcePath] : []\n )\n );\n const folders = rootFolders();\n const partials = new Map<string, string>();\n for (const { page, contentRoot } of universe) {\n for (const partial of page.includes ?? []) {\n if (!(pagePaths.has(partial) || partials.has(partial))) {\n partials.set(partial, contentRoot);\n }\n }\n }\n for (const [partialPath, contentRoot] of partials) {\n const sourceRel = relative(root, partialPath);\n work.sources.push(sourceRel);\n // oxlint-disable-next-line no-await-in-loop -- one read per partial\n const hash = hashSource(await readFile(partialPath, \"utf-8\"));\n const ext = extname(partialPath);\n const verbatim = !PAGE_EXTENSIONS.has(ext.toLowerCase());\n const contentRel = relative(contentRoot, partialPath);\n for (const locale of targetLocales) {\n const targetPath = join(\n contentRoot,\n localeTargetPath(contentRel, ext, locale, i18n, folders(contentRoot))\n );\n const item = (status: WorkStatus): PageWorkItem => {\n const partialItem: PageWorkItem = {\n kind: \"page\",\n locale,\n sourcePath: partialPath,\n sourceRel,\n status,\n targetPath,\n targetRel: relative(root, targetPath),\n };\n if (verbatim) {\n partialItem.verbatim = true;\n }\n return partialItem;\n };\n const stamp = ledger.files[sourceRel]?.[locale];\n const outcome = classifyPair(existsSync(targetPath), stamp, hash, force);\n if (outcome === \"untracked\") {\n work.untracked.push({ hash, kind: \"page\", locale, sourceRel });\n } else if (outcome === \"current\") {\n work.upToDate += 1;\n } else {\n work.items.push(item(outcome));\n }\n }\n }\n return work;\n};\n\n/**\n * Every existing translation, keyed by `translationKey` and locale, with the\n * file it was read from when that file is on disk in a filesystem source. A\n * retranslation writes there: an adopted hand-written `fr/guide.md` for a\n * `guide.mdx` source is replaced in place, never joined by a canonical\n * `fr/guide.mdx` that would publish the route twice.\n */\nconst existingTranslations = (\n project: BlumeProject\n): Map<string, string | undefined> => {\n const writableSources = new Set(\n project.sources.flatMap((source) =>\n source.staged || !source.contentRoot ? [] : [source.name]\n )\n );\n const translated = new Map<string, string | undefined>();\n for (const page of project.graph.pages) {\n const key = `${page.translationKey}\\0${page.locale}`;\n if (page.fallback || translated.has(key)) {\n continue;\n }\n translated.set(\n key,\n page.sourcePath && writableSources.has(page.source.name)\n ? page.sourcePath\n : undefined\n );\n }\n return translated;\n};\n\n/**\n * Compute the run's work: which (source, locale) pairs are missing or stale,\n * which existing translations to adopt, and what's already up to date.\n * `force` promotes every pair to work; `locales` narrows the targets.\n */\nexport const computeWorkList = async (\n project: BlumeProject,\n ledger: TranslationLedger,\n options: { force?: boolean; locales?: string[] } = {}\n): Promise<TranslateWorkList> => {\n const { i18n } = project.config;\n if (!i18n) {\n return {\n diagnostics: [],\n items: [],\n knownSources: new Set(),\n targetLocales: [],\n untracked: [],\n upToDate: 0,\n };\n }\n const targetLocales = localeCodes(i18n).filter(\n (code) =>\n code !== i18n.defaultLocale &&\n (options.locales === undefined || options.locales.includes(code))\n );\n\n const translated = existingTranslations(project);\n\n const { root } = project.context;\n const knownSources = new Set<string>();\n const untracked: UntrackedEntry[] = [];\n const pageItems: PageWorkItem[] = [];\n let upToDate = 0;\n\n const universe = translatablePages(project, i18n);\n const folders = rootFolders();\n\n for (const { page, contentRoot, ext, sourcePath } of universe) {\n const sourceRel = relative(root, sourcePath);\n knownSources.add(sourceRel);\n // oxlint-disable-next-line no-await-in-loop -- one read per source file\n const hash = hashSource(await readFile(sourcePath, \"utf-8\"));\n const contentRel = relative(contentRoot, sourcePath);\n\n for (const locale of targetLocales) {\n const key = `${page.translationKey}\\0${locale}`;\n const targetPath =\n translated.get(key) ??\n join(\n contentRoot,\n localeTargetPath(contentRel, ext, locale, i18n, folders(contentRoot))\n );\n const item = (status: WorkStatus): PageWorkItem => ({\n kind: \"page\",\n locale,\n sourcePath,\n sourceRel,\n status,\n targetPath,\n targetRel: relative(root, targetPath),\n });\n const exists = translated.has(key);\n const stamp = ledger.files[sourceRel]?.[locale];\n const outcome = classifyPair(exists, stamp, hash, options.force === true);\n if (outcome === \"untracked\") {\n untracked.push({ hash, kind: \"page\", locale, sourceRel });\n } else if (outcome === \"current\") {\n upToDate += 1;\n } else {\n pageItems.push(item(outcome));\n }\n }\n }\n\n const partialWork = await partialWorkItems(\n universe,\n project,\n i18n,\n ledger,\n targetLocales,\n options.force ?? false\n );\n for (const rel of partialWork.sources) {\n knownSources.add(rel);\n }\n pageItems.push(...partialWork.items);\n untracked.push(...partialWork.untracked);\n upToDate += partialWork.upToDate;\n\n const meta = await discoverTranslatableMeta(project);\n const metaItems: MetaWorkItem[] = [];\n for (const locale of targetLocales) {\n const entries: MetaWorkEntry[] = [];\n for (const source of meta.metas) {\n knownSources.add(source.sourceRel);\n // The locale folder's existing meta module when there is one, so a\n // retranslation rewrites it instead of adding a `meta.ts` beside it.\n const targetPath = metaTargetPath(\n source,\n locale,\n i18n,\n folders(source.contentRoot)\n );\n const exists = existsSync(targetPath);\n const hash = hashSource(source.raw);\n const stamp = ledger.files[source.sourceRel]?.[locale];\n const entry = (status: WorkStatus): MetaWorkEntry => ({\n meta: source,\n status,\n targetPath,\n });\n const outcome = classifyPair(exists, stamp, hash, options.force === true);\n if (outcome === \"untracked\") {\n untracked.push({\n hash,\n kind: \"meta\",\n locale,\n sourceRel: source.sourceRel,\n });\n } else if (outcome === \"current\") {\n upToDate += 1;\n } else {\n entries.push(entry(outcome));\n }\n }\n if (entries.length > 0) {\n metaItems.push({ entries, kind: \"meta\", locale });\n }\n }\n\n pageItems.sort((a, b) =>\n a.sourceRel === b.sourceRel\n ? a.locale.localeCompare(b.locale)\n : a.sourceRel.localeCompare(b.sourceRel)\n );\n metaItems.sort((a, b) => a.locale.localeCompare(b.locale));\n\n return {\n diagnostics: meta.diagnostics,\n items: [...pageItems, ...metaItems],\n knownSources,\n targetLocales,\n untracked,\n upToDate,\n };\n};\n"
15
+ ],
16
+ "mappings": "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",
17
+ "debugId": "FFF735A581A85E1364756E2164756E21",
18
+ "names": []
19
+ }