blume 1.7.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +245 -0
  3. package/README.md +34 -21
  4. package/dist/cli/{chunk-vacwm2hv.js → chunk-27g6wdth.js} +2 -2
  5. package/dist/cli/{chunk-ps4m1xh4.js → chunk-2hn4b8z7.js} +513 -550
  6. package/dist/cli/chunk-2hn4b8z7.js.map +12 -0
  7. package/dist/cli/chunk-5shv93fd.js +39 -0
  8. package/dist/cli/chunk-5shv93fd.js.map +10 -0
  9. package/dist/cli/chunk-6crbhc3x.js +361 -0
  10. package/dist/cli/chunk-6crbhc3x.js.map +14 -0
  11. package/dist/cli/{chunk-8cjtbafj.js → chunk-6hsn950k.js} +109 -383
  12. package/dist/cli/chunk-6hsn950k.js.map +10 -0
  13. package/dist/cli/chunk-6vm74dry.js +148 -0
  14. package/dist/cli/chunk-6vm74dry.js.map +10 -0
  15. package/dist/cli/{chunk-yg63d42r.js → chunk-79jhk4py.js} +87 -58
  16. package/dist/cli/chunk-79jhk4py.js.map +35 -0
  17. package/dist/cli/chunk-82bbrxdn.js +51 -0
  18. package/dist/cli/chunk-82bbrxdn.js.map +10 -0
  19. package/dist/cli/chunk-abh8yjkn.js +31 -0
  20. package/dist/cli/chunk-abh8yjkn.js.map +10 -0
  21. package/dist/cli/chunk-ah61y8py.js +75 -0
  22. package/dist/cli/chunk-ah61y8py.js.map +11 -0
  23. package/dist/cli/{chunk-dwgcp5sm.js → chunk-ce574jw2.js} +1 -1
  24. package/dist/cli/chunk-ch6g3ar0.js +102 -0
  25. package/dist/cli/chunk-ch6g3ar0.js.map +10 -0
  26. package/dist/cli/chunk-dh8cwk36.js +279 -0
  27. package/dist/cli/chunk-dh8cwk36.js.map +10 -0
  28. package/dist/cli/{chunk-rqy0s5wh.js → chunk-epjnccmv.js} +17 -16
  29. package/dist/cli/{chunk-rqy0s5wh.js.map → chunk-epjnccmv.js.map} +3 -3
  30. package/dist/cli/chunk-f2z5v128.js +97 -0
  31. package/dist/cli/chunk-f2z5v128.js.map +10 -0
  32. package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
  33. package/dist/cli/chunk-fa25z98p.js.map +11 -0
  34. package/dist/cli/{chunk-1jefwnfs.js → chunk-fs23ddbb.js} +1076 -2592
  35. package/dist/cli/chunk-fs23ddbb.js.map +35 -0
  36. package/dist/cli/{chunk-e7f42gdj.js → chunk-fxypxtvm.js} +2 -2
  37. package/dist/cli/{chunk-bvwwhd84.js → chunk-fz5wtpmh.js} +15 -15
  38. package/dist/cli/{chunk-bvwwhd84.js.map → chunk-fz5wtpmh.js.map} +1 -1
  39. package/dist/cli/chunk-hdpx1tax.js +91 -0
  40. package/dist/cli/chunk-hdpx1tax.js.map +10 -0
  41. package/dist/cli/{chunk-ct47dqpx.js → chunk-jts8mvcz.js} +67 -7
  42. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  43. package/dist/cli/{chunk-ahnw3kxw.js → chunk-jwyddg7y.js} +27 -22
  44. package/dist/cli/chunk-jwyddg7y.js.map +15 -0
  45. package/dist/cli/{chunk-cjtn640a.js → chunk-kdp5q7ke.js} +43 -20
  46. package/dist/cli/chunk-kdp5q7ke.js.map +10 -0
  47. package/dist/cli/{chunk-5g0w1e2c.js → chunk-kpf8rrjc.js} +28 -16
  48. package/dist/cli/{chunk-5g0w1e2c.js.map → chunk-kpf8rrjc.js.map} +4 -4
  49. package/dist/cli/chunk-m3vmjgmq.js +133 -0
  50. package/dist/cli/chunk-m3vmjgmq.js.map +10 -0
  51. package/dist/cli/{chunk-b27xqwn9.js → chunk-mb2919y2.js} +9 -5
  52. package/dist/cli/chunk-mb2919y2.js.map +10 -0
  53. package/dist/cli/{chunk-5qk08vmp.js → chunk-q5163e60.js} +133 -54
  54. package/dist/cli/chunk-q5163e60.js.map +11 -0
  55. package/dist/cli/chunk-qkqwkpte.js +12437 -0
  56. package/dist/cli/chunk-qkqwkpte.js.map +182 -0
  57. package/dist/cli/{chunk-4x36ddpw.js → chunk-qs4q5p4e.js} +81 -87
  58. package/dist/cli/chunk-qs4q5p4e.js.map +10 -0
  59. package/dist/cli/{chunk-0xjyb285.js → chunk-qwsrynx5.js} +15 -5
  60. package/dist/cli/{chunk-0xjyb285.js.map → chunk-qwsrynx5.js.map} +4 -4
  61. package/dist/cli/chunk-s1p84fyh.js +261 -0
  62. package/dist/cli/chunk-s1p84fyh.js.map +10 -0
  63. package/dist/cli/chunk-s6jhgk0q.js +176 -0
  64. package/dist/cli/chunk-s6jhgk0q.js.map +11 -0
  65. package/dist/cli/chunk-vtk4a6dg.js +374 -0
  66. package/dist/cli/chunk-vtk4a6dg.js.map +10 -0
  67. package/dist/cli/{chunk-k79xp7av.js → chunk-wgm7m9qk.js} +230 -700
  68. package/dist/cli/chunk-wgm7m9qk.js.map +36 -0
  69. package/dist/cli/chunk-wm7js3j9.js +145 -0
  70. package/dist/cli/chunk-wm7js3j9.js.map +11 -0
  71. package/dist/cli/chunk-yt5n7ppj.js +79 -0
  72. package/dist/cli/chunk-yt5n7ppj.js.map +10 -0
  73. package/dist/cli/{chunk-3r45185y.js → chunk-yw7dm696.js} +9 -11
  74. package/dist/cli/{chunk-3r45185y.js.map → chunk-yw7dm696.js.map} +3 -3
  75. package/dist/cli/{chunk-nn13znc2.js → chunk-zxcczpyx.js} +1 -1
  76. package/dist/cli/chunk-zxh4d9vy.js +122 -0
  77. package/dist/cli/chunk-zxh4d9vy.js.map +11 -0
  78. package/dist/cli/index.js +214 -57
  79. package/dist/cli/index.js.map +8 -7
  80. package/dist/types/ai/agent-readability.d.ts +52 -0
  81. package/dist/types/ai/ai-catalog.d.ts +42 -0
  82. package/dist/types/ai/api/paths.d.ts +17 -0
  83. package/dist/types/ai/api-catalog.d.ts +18 -0
  84. package/dist/types/ai/ask-context.d.ts +7 -7
  85. package/dist/types/ai/ask.d.ts +368 -0
  86. package/dist/types/ai/changelog-markdown.d.ts +2 -0
  87. package/dist/types/ai/component-markdown.d.ts +2 -2
  88. package/dist/types/ai/index.d.ts +24 -0
  89. package/dist/types/ai/link-headers.d.ts +24 -0
  90. package/dist/types/ai/llms.d.ts +25 -0
  91. package/dist/types/ai/markdown.d.ts +45 -0
  92. package/dist/types/ai/mcp/discovery.d.ts +68 -0
  93. package/dist/types/ai/mcp/tools.d.ts +16 -0
  94. package/dist/types/ai/openapi-components.d.ts +43 -0
  95. package/dist/types/ai/relative-links.d.ts +26 -0
  96. package/dist/types/ai/serializers.d.ts +15 -0
  97. package/dist/types/ai/skills.d.ts +42 -0
  98. package/dist/types/ai/tar.d.ts +25 -0
  99. package/dist/types/ai/visibility.d.ts +17 -0
  100. package/dist/types/ai/web-bot-auth.d.ts +16 -0
  101. package/dist/types/analytics/adobe.d.ts +35 -0
  102. package/dist/types/analytics/amplitude.d.ts +50 -0
  103. package/dist/types/analytics/clarity.d.ts +31 -0
  104. package/dist/types/analytics/clearbit.d.ts +29 -0
  105. package/dist/types/analytics/cloudflare.d.ts +41 -0
  106. package/dist/types/analytics/fathom.d.ts +41 -0
  107. package/dist/types/analytics/google-analytics.d.ts +44 -0
  108. package/dist/types/analytics/google-tag-manager.d.ts +40 -0
  109. package/dist/types/analytics/head.d.ts +30 -0
  110. package/dist/types/analytics/heap.d.ts +40 -0
  111. package/dist/types/analytics/hightouch.d.ts +44 -0
  112. package/dist/types/analytics/hotjar.d.ts +33 -0
  113. package/dist/types/analytics/index.d.ts +61 -0
  114. package/dist/types/analytics/inline.d.ts +17 -0
  115. package/dist/types/analytics/logrocket.d.ts +44 -0
  116. package/dist/types/analytics/mixpanel.d.ts +67 -0
  117. package/dist/types/analytics/pirsch.d.ts +42 -0
  118. package/dist/types/analytics/plausible.d.ts +56 -0
  119. package/dist/types/analytics/posthog.d.ts +45 -0
  120. package/dist/types/analytics/schema.d.ts +320 -0
  121. package/dist/types/analytics/script.d.ts +46 -0
  122. package/dist/types/analytics/segment.d.ts +50 -0
  123. package/dist/types/analytics/vercel.d.ts +48 -0
  124. package/dist/types/astro/integration.d.ts +76 -0
  125. package/dist/types/astro/markdown-negotiation.d.ts +23 -0
  126. package/dist/types/astro/module-types.d.ts +14 -0
  127. package/dist/types/astro/pages.d.ts +44 -0
  128. package/dist/types/cli/env.d.ts +12 -0
  129. package/dist/types/cli/init/scaffold.d.ts +154 -0
  130. package/dist/types/components/layout/nav-utils.d.ts +11 -0
  131. package/dist/types/core/adapter.d.ts +47 -0
  132. package/dist/types/core/api-name.d.ts +7 -0
  133. package/dist/types/core/changelog-index.d.ts +13 -0
  134. package/dist/types/core/config-input.d.ts +197 -526
  135. package/dist/types/core/config.d.ts +61 -35
  136. package/dist/types/core/content-assets.d.ts +76 -0
  137. package/dist/types/core/custom-pages.d.ts +33 -0
  138. package/dist/types/core/data.d.ts +30 -15
  139. package/dist/types/core/define-components.d.ts +12 -9
  140. package/dist/types/core/deployment-env.d.ts +6 -11
  141. package/dist/types/core/frontmatter.d.ts +10 -0
  142. package/dist/types/core/graph.d.ts +18 -0
  143. package/dist/types/core/heading-markers.d.ts +54 -0
  144. package/dist/types/core/i18n-ui.d.ts +12 -10
  145. package/dist/types/core/i18n.d.ts +87 -0
  146. package/dist/types/core/includes.d.ts +138 -0
  147. package/dist/types/core/last-modified.d.ts +47 -0
  148. package/dist/types/core/links.d.ts +95 -0
  149. package/dist/types/core/locale-links.d.ts +60 -0
  150. package/dist/types/core/manifest.d.ts +17 -0
  151. package/dist/types/core/meta.d.ts +38 -0
  152. package/dist/types/core/nav-diagnostics.d.ts +26 -0
  153. package/dist/types/core/navigation.d.ts +6 -0
  154. package/dist/types/core/node-require.d.ts +19 -0
  155. package/dist/types/core/package-json.d.ts +13 -0
  156. package/dist/types/core/probe.d.ts +42 -0
  157. package/dist/types/core/project-graph.d.ts +51 -0
  158. package/dist/types/core/project.d.ts +2 -0
  159. package/dist/types/core/safe-href.d.ts +2 -0
  160. package/dist/types/core/safe-links.d.ts +26 -0
  161. package/dist/types/core/schema.d.ts +2284 -454
  162. package/dist/types/core/site-url.d.ts +16 -0
  163. package/dist/types/core/sources/assets.d.ts +36 -0
  164. package/dist/types/core/sources/cache.d.ts +37 -0
  165. package/dist/types/core/sources/collection.d.ts +28 -0
  166. package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
  167. package/dist/types/core/sources/contentful.d.ts +40 -0
  168. package/dist/types/core/sources/filesystem.d.ts +24 -0
  169. package/dist/types/core/sources/github-releases.d.ts +31 -0
  170. package/dist/types/core/sources/json.d.ts +30 -0
  171. package/dist/types/core/sources/lexical.d.ts +19 -0
  172. package/dist/types/core/sources/lower.d.ts +75 -0
  173. package/dist/types/core/sources/mdx-remote.d.ts +28 -0
  174. package/dist/types/core/sources/normalize.d.ts +147 -0
  175. package/dist/types/core/sources/notion.d.ts +131 -0
  176. package/dist/types/core/sources/obsidian.d.ts +46 -0
  177. package/dist/types/core/sources/payload.d.ts +39 -0
  178. package/dist/types/core/sources/portable-text.d.ts +42 -0
  179. package/dist/types/core/sources/read.d.ts +23 -0
  180. package/dist/types/core/sources/remote.d.ts +74 -0
  181. package/dist/types/core/sources/resolve.d.ts +19 -0
  182. package/dist/types/core/sources/sanity.d.ts +43 -0
  183. package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
  184. package/dist/types/core/sources/strapi.d.ts +33 -0
  185. package/dist/types/core/sources/types.d.ts +7 -0
  186. package/dist/types/core/sources/watch.d.ts +45 -0
  187. package/dist/types/core/text-width.d.ts +11 -0
  188. package/dist/types/core/types.d.ts +15 -1
  189. package/dist/types/core/unrecognized-keys.d.ts +7 -0
  190. package/dist/types/core/versions.d.ts +72 -0
  191. package/dist/types/core/yaml.d.ts +9 -0
  192. package/dist/types/deploy/adapter-output.d.ts +45 -0
  193. package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
  194. package/dist/types/deploy/adapters/index.d.ts +29 -0
  195. package/dist/types/deploy/adapters/netlify.d.ts +37 -0
  196. package/dist/types/deploy/adapters/node.d.ts +37 -0
  197. package/dist/types/deploy/adapters/registry.d.ts +133 -0
  198. package/dist/types/deploy/adapters/types.d.ts +71 -0
  199. package/dist/types/deploy/adapters/vercel.d.ts +38 -0
  200. package/dist/types/deploy/artifacts.d.ts +65 -0
  201. package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
  202. package/dist/types/deploy/function-bundle.d.ts +80 -0
  203. package/dist/types/deploy/headers.d.ts +50 -0
  204. package/dist/types/deploy/node-headers.d.ts +42 -0
  205. package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
  206. package/dist/types/deploy/platforms/index.d.ts +18 -0
  207. package/dist/types/deploy/platforms/netlify.d.ts +13 -0
  208. package/dist/types/deploy/platforms/node.d.ts +11 -0
  209. package/dist/types/deploy/platforms/paths.d.ts +27 -0
  210. package/dist/types/deploy/platforms/static.d.ts +10 -0
  211. package/dist/types/deploy/platforms/types.d.ts +104 -0
  212. package/dist/types/deploy/platforms/vercel.d.ts +32 -0
  213. package/dist/types/deploy/redirects.d.ts +53 -0
  214. package/dist/types/deploy/robots.d.ts +8 -0
  215. package/dist/types/deploy/rss.d.ts +31 -0
  216. package/dist/types/deploy/sitemap.d.ts +21 -0
  217. package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
  218. package/dist/types/markdown/code-title.d.ts +32 -0
  219. package/dist/types/markdown/fence-meta.d.ts +23 -0
  220. package/dist/types/markdown/themes.d.ts +3 -3
  221. package/dist/types/openapi/asyncapi.d.ts +129 -0
  222. package/dist/types/openapi/graphql-build.d.ts +8 -0
  223. package/dist/types/openapi/graphql.d.ts +122 -0
  224. package/dist/types/openapi/model.d.ts +158 -0
  225. package/dist/types/openapi/parse.d.ts +57 -0
  226. package/dist/types/openapi/references.d.ts +37 -25
  227. package/dist/types/openapi/render-mdx.d.ts +33 -0
  228. package/dist/types/openapi/sentence.d.ts +7 -0
  229. package/dist/types/openapi/signature.d.ts +10 -0
  230. package/dist/types/openapi/source.d.ts +22 -0
  231. package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
  232. package/dist/types/reference/asyncapi.d.ts +166 -0
  233. package/dist/types/reference/graphql.d.ts +181 -0
  234. package/dist/types/reference/index.d.ts +32 -0
  235. package/dist/types/reference/openapi.d.ts +165 -0
  236. package/dist/types/reference/options.d.ts +157 -0
  237. package/dist/types/reference/scalar.d.ts +136 -0
  238. package/dist/types/reference/schema.d.ts +630 -0
  239. package/dist/types/search/adapters/algolia.d.ts +32 -0
  240. package/dist/types/search/adapters/flexsearch.d.ts +12 -0
  241. package/dist/types/search/adapters/index.d.ts +31 -0
  242. package/dist/types/search/adapters/mixedbread.d.ts +22 -0
  243. package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
  244. package/dist/types/search/adapters/orama.d.ts +13 -0
  245. package/dist/types/search/adapters/pagefind.d.ts +12 -0
  246. package/dist/types/search/adapters/registry.d.ts +228 -0
  247. package/dist/types/search/adapters/types.d.ts +34 -0
  248. package/dist/types/search/adapters/typesense.d.ts +42 -0
  249. package/dist/types/search/build.d.ts +23 -0
  250. package/dist/types/search/documents.d.ts +90 -0
  251. package/dist/types/search/facets.d.ts +3 -0
  252. package/dist/types/search/orama-index.d.ts +1 -1
  253. package/dist/types/search/sync/algolia.d.ts +14 -0
  254. package/dist/types/search/sync/index.d.ts +14 -0
  255. package/dist/types/search/sync/orama-cloud.d.ts +10 -0
  256. package/dist/types/search/sync/typesense.d.ts +14 -0
  257. package/dist/types/sources/contentful.d.ts +68 -0
  258. package/dist/types/sources/custom.d.ts +20 -0
  259. package/dist/types/sources/filesystem.d.ts +42 -0
  260. package/dist/types/sources/github-releases.d.ts +46 -0
  261. package/dist/types/sources/index.d.ts +48 -0
  262. package/dist/types/sources/mdx-remote.d.ts +63 -0
  263. package/dist/types/sources/notion.d.ts +65 -0
  264. package/dist/types/sources/obsidian.d.ts +34 -0
  265. package/dist/types/sources/payload.d.ts +68 -0
  266. package/dist/types/sources/registry.d.ts +1414 -0
  267. package/dist/types/sources/sanity.d.ts +69 -0
  268. package/dist/types/sources/shared.d.ts +46 -0
  269. package/dist/types/sources/strapi.d.ts +66 -0
  270. package/dist/types/theme/icon-kind.d.ts +11 -0
  271. package/dist/types/theme/icons.d.ts +20 -0
  272. package/docs/01-quickstart.mdx +18 -18
  273. package/docs/02-deployment.mdx +52 -28
  274. package/docs/03-upgrading.mdx +364 -0
  275. package/docs/04-migrating.mdx +58 -0
  276. package/docs/08-faq.mdx +5 -5
  277. package/docs/advanced/blog.mdx +13 -6
  278. package/docs/advanced/changelog.mdx +23 -35
  279. package/docs/advanced/custom-pages.mdx +22 -10
  280. package/docs/advanced/meta.ts +1 -8
  281. package/docs/advanced/skills.mdx +8 -0
  282. package/docs/cli/audit.mdx +646 -0
  283. package/docs/cli/doctor.mdx +29 -0
  284. package/docs/{reference/eval.mdx → cli/evals.mdx} +12 -11
  285. package/docs/cli/index.mdx +105 -0
  286. package/docs/cli/meta.ts +7 -0
  287. package/docs/{reference → cli}/translate.mdx +9 -9
  288. package/docs/cli/validate.mdx +41 -0
  289. package/docs/cli/version.mdx +40 -0
  290. package/docs/configuration/analytics.mdx +350 -59
  291. package/docs/configuration/assistant.mdx +357 -0
  292. package/docs/configuration/customization.mdx +16 -10
  293. package/docs/configuration/index.mdx +50 -31
  294. package/docs/configuration/meta.ts +1 -1
  295. package/docs/configuration/search.mdx +76 -55
  296. package/docs/configuration/theming.mdx +24 -15
  297. package/docs/content/components.mdx +21 -5
  298. package/docs/{reference → content}/frontmatter.mdx +35 -1
  299. package/docs/content/i18n.mdx +9 -7
  300. package/docs/content/includes.mdx +2 -4
  301. package/docs/content/index.mdx +1 -1
  302. package/docs/content/islands.mdx +11 -6
  303. package/docs/content/meta.mdx +1 -1
  304. package/docs/content/meta.ts +1 -0
  305. package/docs/content/navigation.mdx +9 -7
  306. package/docs/content/sources.mdx +145 -50
  307. package/docs/content/syntax.mdx +16 -12
  308. package/docs/content/versioning.mdx +3 -3
  309. package/docs/discoverability/agent-discovery.mdx +12 -12
  310. package/docs/discoverability/index.mdx +4 -4
  311. package/docs/discoverability/json-api.mdx +4 -4
  312. package/docs/discoverability/llms-txt.mdx +6 -6
  313. package/docs/discoverability/markdown.mdx +4 -4
  314. package/docs/discoverability/mcp.mdx +11 -11
  315. package/docs/discoverability/sitemap-and-robots.mdx +3 -3
  316. package/docs/index.mdx +4 -4
  317. package/docs/references/asyncapi.mdx +59 -0
  318. package/docs/{advanced → references}/graphql.mdx +45 -32
  319. package/docs/{reference → references}/meta.ts +2 -2
  320. package/docs/references/openapi.mdx +171 -0
  321. package/docs/references/scalar.mdx +64 -0
  322. package/package.json +42 -8
  323. package/skills/blume/SKILL.md +23 -10
  324. package/skills/blume-migrate/SKILL.md +22 -21
  325. package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
  326. package/skills/blume-migrate/references/docusaurus.md +5 -4
  327. package/skills/blume-migrate/references/fumadocs.md +4 -4
  328. package/skills/blume-migrate/references/mintlify.md +23 -8
  329. package/skills/blume-migrate/references/monorepo.md +6 -6
  330. package/skills/blume-migrate/references/starlight.md +4 -4
  331. package/src/ai/agent-readability.ts +25 -26
  332. package/src/ai/ai-catalog.ts +50 -32
  333. package/src/ai/api/handlers.ts +46 -11
  334. package/src/ai/api/paths.ts +1 -1
  335. package/src/ai/api-catalog.ts +8 -8
  336. package/src/ai/ask-context.ts +7 -7
  337. package/src/ai/ask-data.ts +3 -3
  338. package/src/ai/ask.ts +637 -100
  339. package/src/ai/changelog-markdown.ts +91 -0
  340. package/src/ai/component-markdown.ts +328 -12
  341. package/src/ai/cors.ts +3 -3
  342. package/src/ai/index.ts +45 -0
  343. package/src/ai/link-headers.ts +7 -6
  344. package/src/ai/llms.ts +20 -15
  345. package/src/ai/markdown.ts +35 -5
  346. package/src/ai/mcp/data.ts +9 -5
  347. package/src/ai/openapi-components.ts +5 -2
  348. package/src/ai/relative-links.ts +170 -0
  349. package/src/ai/serializers.ts +3 -3
  350. package/src/ai/skills.ts +1 -1
  351. package/src/ai/visibility.ts +1 -1
  352. package/src/ai/web-bot-auth.ts +2 -2
  353. package/src/analytics/adobe.ts +46 -0
  354. package/src/analytics/amplitude.ts +79 -0
  355. package/src/analytics/clarity.ts +46 -0
  356. package/src/analytics/clearbit.ts +47 -0
  357. package/src/analytics/cloudflare.ts +67 -0
  358. package/src/analytics/fathom.ts +64 -0
  359. package/src/analytics/google-analytics.ts +84 -0
  360. package/src/analytics/google-tag-manager.ts +61 -0
  361. package/src/analytics/head.ts +130 -0
  362. package/src/analytics/heap.ts +65 -0
  363. package/src/analytics/hightouch.ts +77 -0
  364. package/src/analytics/hotjar.ts +47 -0
  365. package/src/analytics/index.ts +71 -0
  366. package/src/analytics/inline.ts +21 -0
  367. package/src/analytics/logrocket.ts +71 -0
  368. package/src/analytics/mixpanel.ts +92 -0
  369. package/src/analytics/pirsch.ts +66 -0
  370. package/src/analytics/plausible.ts +83 -0
  371. package/src/analytics/posthog.ts +78 -0
  372. package/src/analytics/schema.ts +60 -0
  373. package/src/analytics/script.ts +60 -0
  374. package/src/analytics/segment.ts +85 -0
  375. package/src/analytics/vercel.ts +50 -0
  376. package/src/astro/adapter-root.ts +7 -9
  377. package/src/astro/component-slots.ts +131 -91
  378. package/src/astro/generate.ts +129 -361
  379. package/src/astro/integration.ts +2 -6
  380. package/src/astro/module-types.ts +1 -1
  381. package/src/astro/pages.ts +13 -101
  382. package/src/astro/render-deps.ts +379 -0
  383. package/src/astro/runtime-deps.ts +201 -0
  384. package/src/astro/templates.ts +575 -562
  385. package/src/audit/agent.ts +24 -0
  386. package/src/audit/catalog.ts +2 -2
  387. package/src/audit/checks/assets.ts +2 -2
  388. package/src/audit/checks/dns-aid.ts +1 -1
  389. package/src/audit/checks/duplicates.ts +3 -1
  390. package/src/audit/checks/i18n.ts +1 -1
  391. package/src/audit/checks/indexability.ts +7 -7
  392. package/src/audit/checks/links.ts +2 -2
  393. package/src/audit/checks/llms.ts +6 -6
  394. package/src/audit/checks/network.ts +3 -3
  395. package/src/audit/checks/og-image.ts +2 -2
  396. package/src/audit/checks/robots.ts +1 -1
  397. package/src/audit/checks/sitemap.ts +2 -2
  398. package/src/audit/checks/social.ts +1 -1
  399. package/src/audit/run.ts +4 -3
  400. package/src/audit/terms.ts +31 -0
  401. package/src/audit/url.ts +13 -13
  402. package/src/blume-modules.d.ts +2 -2
  403. package/src/cli/command-meta.ts +10 -0
  404. package/src/cli/commands/audit.ts +40 -21
  405. package/src/cli/commands/build.ts +71 -316
  406. package/src/cli/commands/check.ts +1 -0
  407. package/src/cli/commands/dev.ts +40 -1
  408. package/src/cli/commands/doctor.ts +92 -14
  409. package/src/cli/commands/eject.ts +44 -7
  410. package/src/cli/commands/eval.ts +5 -5
  411. package/src/cli/commands/init.ts +161 -41
  412. package/src/cli/commands/migrate.ts +121 -0
  413. package/src/cli/commands/preview.ts +7 -1
  414. package/src/cli/commands/translate.ts +5 -5
  415. package/src/cli/commands/upgrade.ts +141 -0
  416. package/src/cli/commands/version.ts +57 -44
  417. package/src/cli/eject-scripts.ts +121 -6
  418. package/src/cli/index.ts +11 -1
  419. package/src/cli/init/install.ts +70 -0
  420. package/src/cli/init/questions.ts +7 -0
  421. package/src/cli/init/scaffold.ts +459 -75
  422. package/src/cli/lazy-command.ts +37 -1
  423. package/src/cli/prepare.ts +40 -6
  424. package/src/cli/required-secrets.ts +39 -12
  425. package/src/cli/unknown-flags.ts +266 -0
  426. package/src/cli/yarn-pnp.ts +52 -0
  427. package/src/components/content/AccordionItem.astro +7 -1
  428. package/src/components/content/Card.astro +4 -3
  429. package/src/components/content/ColorItem.astro +22 -4
  430. package/src/components/content/GithubInfo.astro +2 -2
  431. package/src/components/content/Prompt.astro +25 -25
  432. package/src/components/content/Tabs.astro +3 -1
  433. package/src/components/content/Tile.astro +2 -3
  434. package/src/components/content/Tooltip.astro +57 -9
  435. package/src/components/content/content-strings.ts +34 -0
  436. package/src/components/content/diff.ts +1 -1
  437. package/src/components/content/mermaid-element.ts +13 -2
  438. package/src/components/content/prompt-markdown.ts +292 -0
  439. package/src/components/content/tooltip-id.ts +41 -0
  440. package/src/components/copy-feedback.ts +1 -1
  441. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  442. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +23 -23
  443. package/src/components/islands/hooks.ts +36 -11
  444. package/src/components/layout/Analytics.astro +21 -79
  445. package/src/components/layout/Banner.astro +3 -1
  446. package/src/components/layout/DiscoveryLinks.astro +69 -0
  447. package/src/components/layout/Header.astro +93 -30
  448. package/src/components/layout/LanguageSwitcher.astro +4 -1
  449. package/src/components/layout/Logo.astro +23 -2
  450. package/src/components/layout/NavSelector.astro +13 -2
  451. package/src/components/layout/NavTree.astro +12 -3
  452. package/src/components/layout/NavTreeScript.astro +17 -3
  453. package/src/components/layout/PageActions.astro +3 -3
  454. package/src/components/layout/PageFeedback.astro +11 -1
  455. package/src/components/layout/PageLayout.astro +58 -11
  456. package/src/components/layout/Pagination.astro +7 -7
  457. package/src/components/layout/ReferenceLayout.astro +22 -13
  458. package/src/components/layout/RootLayout.astro +75 -72
  459. package/src/components/layout/Search.astro +43 -19
  460. package/src/components/layout/WebMcp.astro +1 -1
  461. package/src/components/layout/analytics-client.ts +73 -16
  462. package/src/components/layout/drawer-inert.ts +113 -15
  463. package/src/components/layout/dropdown-clamp.ts +105 -0
  464. package/src/components/layout/head-scripts.ts +15 -6
  465. package/src/components/layout/nav-utils.ts +17 -0
  466. package/src/components/layout/search/algolia.ts +8 -8
  467. package/src/components/layout/search/orama-cloud.ts +9 -7
  468. package/src/components/layout/search/typesense.ts +11 -17
  469. package/src/components/openapi/MessageComposer.astro +2 -2
  470. package/src/components/openapi/Playground.astro +2 -2
  471. package/src/components/openapi/description.ts +2 -2
  472. package/src/components/openapi/playground-client.ts +79 -10
  473. package/src/core/changelog-index.ts +24 -0
  474. package/src/core/code-fences.ts +1 -1
  475. package/src/core/component-overrides.ts +399 -154
  476. package/src/core/config-input.ts +201 -566
  477. package/src/core/config.ts +131 -42
  478. package/src/core/custom-pages.ts +105 -0
  479. package/src/core/data.ts +34 -15
  480. package/src/core/define-components.ts +12 -9
  481. package/src/core/deployment-env.ts +18 -74
  482. package/src/core/diagnostics.ts +14 -7
  483. package/src/core/graph.ts +69 -25
  484. package/src/core/i18n-ui.ts +41 -11
  485. package/src/core/i18n.ts +17 -0
  486. package/src/core/includes.ts +156 -38
  487. package/src/core/last-modified.ts +6 -11
  488. package/src/core/links.ts +166 -2
  489. package/src/core/manifest.ts +10 -3
  490. package/src/core/navigation.ts +115 -16
  491. package/src/core/new-tab.ts +35 -0
  492. package/src/core/node-require.ts +21 -0
  493. package/src/core/project-graph.ts +42 -26
  494. package/src/core/project.ts +9 -4
  495. package/src/core/request-body.ts +61 -0
  496. package/src/core/safe-href.ts +28 -0
  497. package/src/core/safe-links.ts +68 -0
  498. package/src/core/schema.ts +528 -745
  499. package/src/core/server-features.ts +10 -11
  500. package/src/core/sources/assets.ts +47 -11
  501. package/src/core/sources/collection.ts +67 -0
  502. package/src/core/sources/contentful-rich-text.ts +285 -0
  503. package/src/core/sources/contentful.ts +173 -0
  504. package/src/core/sources/github-releases.ts +51 -1
  505. package/src/core/sources/json.ts +71 -0
  506. package/src/core/sources/lexical.ts +195 -0
  507. package/src/core/sources/lower.ts +226 -0
  508. package/src/core/sources/normalize.ts +52 -2
  509. package/src/core/sources/notion.ts +39 -28
  510. package/src/core/sources/payload.ts +135 -0
  511. package/src/core/sources/portable-text.ts +11 -16
  512. package/src/core/sources/remote.ts +226 -0
  513. package/src/core/sources/resolve.ts +104 -166
  514. package/src/core/sources/sanity.ts +16 -49
  515. package/src/core/sources/strapi-blocks.ts +124 -0
  516. package/src/core/sources/strapi.ts +191 -0
  517. package/src/core/sources/types.ts +12 -1
  518. package/src/core/types.ts +15 -1
  519. package/src/core/ui-packs/ar.ts +10 -7
  520. package/src/core/ui-packs/bg.ts +10 -7
  521. package/src/core/ui-packs/bn.ts +10 -7
  522. package/src/core/ui-packs/ca.ts +7 -6
  523. package/src/core/ui-packs/cs.ts +10 -7
  524. package/src/core/ui-packs/da.ts +10 -7
  525. package/src/core/ui-packs/de.ts +10 -7
  526. package/src/core/ui-packs/el.ts +7 -6
  527. package/src/core/ui-packs/es.ts +7 -6
  528. package/src/core/ui-packs/fa.ts +10 -7
  529. package/src/core/ui-packs/fi.ts +10 -7
  530. package/src/core/ui-packs/fr.ts +7 -6
  531. package/src/core/ui-packs/he.ts +10 -7
  532. package/src/core/ui-packs/hi.ts +10 -7
  533. package/src/core/ui-packs/hr.ts +10 -7
  534. package/src/core/ui-packs/hu.ts +10 -7
  535. package/src/core/ui-packs/id.ts +10 -7
  536. package/src/core/ui-packs/it.ts +7 -6
  537. package/src/core/ui-packs/ja.ts +7 -6
  538. package/src/core/ui-packs/ko.ts +7 -6
  539. package/src/core/ui-packs/nl.ts +10 -7
  540. package/src/core/ui-packs/no.ts +10 -7
  541. package/src/core/ui-packs/pl.ts +10 -7
  542. package/src/core/ui-packs/pt-br.ts +7 -6
  543. package/src/core/ui-packs/pt.ts +7 -6
  544. package/src/core/ui-packs/ro.ts +10 -7
  545. package/src/core/ui-packs/ru.ts +10 -7
  546. package/src/core/ui-packs/sk.ts +10 -7
  547. package/src/core/ui-packs/sr.ts +10 -7
  548. package/src/core/ui-packs/sv.ts +10 -7
  549. package/src/core/ui-packs/th.ts +7 -6
  550. package/src/core/ui-packs/tr.ts +10 -7
  551. package/src/core/ui-packs/uk.ts +10 -7
  552. package/src/core/ui-packs/vi.ts +7 -6
  553. package/src/core/ui-packs/zh-tw.ts +7 -6
  554. package/src/core/ui-packs/zh.ts +7 -6
  555. package/src/core/unrecognized-keys.ts +10 -0
  556. package/src/core/version-cut.ts +116 -8
  557. package/src/deploy/adapter-output.ts +57 -97
  558. package/src/deploy/adapters/cloudflare.ts +39 -0
  559. package/src/deploy/adapters/index.ts +41 -0
  560. package/src/deploy/adapters/netlify.ts +34 -0
  561. package/src/deploy/adapters/node.ts +34 -0
  562. package/src/deploy/adapters/registry.ts +127 -0
  563. package/src/deploy/adapters/types.ts +92 -0
  564. package/src/deploy/adapters/vercel.ts +35 -0
  565. package/src/deploy/artifacts.ts +55 -27
  566. package/src/deploy/cloudflare-negotiation.ts +179 -100
  567. package/src/deploy/function-bundle.ts +18 -3
  568. package/src/deploy/headers.ts +124 -23
  569. package/src/deploy/node-headers.ts +198 -0
  570. package/src/deploy/platforms/cloudflare.ts +312 -0
  571. package/src/deploy/platforms/index.ts +67 -0
  572. package/src/deploy/platforms/netlify.ts +52 -0
  573. package/src/deploy/platforms/node.ts +40 -0
  574. package/src/deploy/platforms/paths.ts +42 -0
  575. package/src/deploy/platforms/static.ts +29 -0
  576. package/src/deploy/platforms/types.ts +112 -0
  577. package/src/deploy/platforms/vercel.ts +193 -0
  578. package/src/deploy/redirects.ts +30 -18
  579. package/src/deploy/robots.ts +3 -3
  580. package/src/deploy/rss.ts +2 -2
  581. package/src/deploy/sitemap.ts +2 -2
  582. package/src/deploy/vercel-negotiation.ts +2 -2
  583. package/src/eval/agents.ts +32 -1
  584. package/src/eval/findings.ts +19 -11
  585. package/src/eval/report.ts +10 -2
  586. package/src/markdown/external-links.ts +65 -0
  587. package/src/markdown/include.ts +45 -27
  588. package/src/markdown/index.ts +34 -9
  589. package/src/markdown/inline-code.ts +12 -6
  590. package/src/markdown/relative-links.ts +324 -0
  591. package/src/markdown/themes.ts +3 -3
  592. package/src/migrate/migrate.ts +149 -0
  593. package/src/openapi/parse.ts +64 -16
  594. package/src/openapi/proxy.ts +62 -10
  595. package/src/openapi/references.ts +147 -147
  596. package/src/openapi/render-mdx.ts +32 -10
  597. package/src/openapi/scalar.ts +15 -13
  598. package/src/openapi/sentence.ts +14 -0
  599. package/src/openapi/source.ts +37 -21
  600. package/src/openapi/spec-dependency-error.ts +15 -0
  601. package/src/reference/asyncapi.ts +83 -0
  602. package/src/reference/graphql.ts +89 -0
  603. package/src/reference/index.ts +40 -0
  604. package/src/reference/openapi.ts +83 -0
  605. package/src/reference/options.ts +201 -0
  606. package/src/reference/scalar.ts +136 -0
  607. package/src/reference/schema.ts +88 -0
  608. package/src/registry/eject.ts +109 -53
  609. package/src/search/adapters/algolia.ts +46 -0
  610. package/src/search/adapters/flexsearch.ts +29 -0
  611. package/src/search/adapters/index.ts +39 -0
  612. package/src/search/adapters/mixedbread.ts +39 -0
  613. package/src/search/adapters/orama-cloud.ts +51 -0
  614. package/src/search/adapters/orama.ts +24 -0
  615. package/src/search/adapters/pagefind.ts +27 -0
  616. package/src/search/adapters/registry.ts +131 -0
  617. package/src/search/adapters/types.ts +46 -0
  618. package/src/search/adapters/typesense.ts +57 -0
  619. package/src/search/build.ts +8 -4
  620. package/src/search/documents.ts +8 -2
  621. package/src/search/orama-index.ts +1 -1
  622. package/src/search/sync/algolia.ts +12 -11
  623. package/src/search/sync/index.ts +35 -20
  624. package/src/search/sync/orama-cloud.ts +12 -9
  625. package/src/search/sync/typesense.ts +15 -13
  626. package/src/sources/contentful.ts +63 -0
  627. package/src/sources/custom.ts +38 -0
  628. package/src/sources/filesystem.ts +51 -0
  629. package/src/sources/github-releases.ts +52 -0
  630. package/src/sources/index.ts +60 -0
  631. package/src/sources/mdx-remote.ts +76 -0
  632. package/src/sources/notion.ts +63 -0
  633. package/src/sources/obsidian.ts +38 -0
  634. package/src/sources/payload.ts +60 -0
  635. package/src/sources/registry.ts +182 -0
  636. package/src/sources/sanity.ts +66 -0
  637. package/src/sources/shared.ts +52 -0
  638. package/src/sources/strapi.ts +58 -0
  639. package/src/theme/entry.ts +24 -2
  640. package/src/translate/report.ts +40 -7
  641. package/src/upgrade/upgrade.ts +499 -0
  642. package/dist/cli/chunk-0qymqwzz.js +0 -164
  643. package/dist/cli/chunk-0qymqwzz.js.map +0 -15
  644. package/dist/cli/chunk-1jefwnfs.js.map +0 -48
  645. package/dist/cli/chunk-2mzebbbz.js +0 -69
  646. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  647. package/dist/cli/chunk-4x36ddpw.js.map +0 -11
  648. package/dist/cli/chunk-5093q3n7.js +0 -68
  649. package/dist/cli/chunk-5093q3n7.js.map +0 -10
  650. package/dist/cli/chunk-5qk08vmp.js.map +0 -11
  651. package/dist/cli/chunk-7s8hm3b6.js +0 -5347
  652. package/dist/cli/chunk-7s8hm3b6.js.map +0 -58
  653. package/dist/cli/chunk-8cjtbafj.js.map +0 -13
  654. package/dist/cli/chunk-97r59kpr.js +0 -381
  655. package/dist/cli/chunk-97r59kpr.js.map +0 -12
  656. package/dist/cli/chunk-ahnw3kxw.js.map +0 -15
  657. package/dist/cli/chunk-b27xqwn9.js.map +0 -10
  658. package/dist/cli/chunk-bf6bt1xt.js +0 -185
  659. package/dist/cli/chunk-bf6bt1xt.js.map +0 -11
  660. package/dist/cli/chunk-cjtn640a.js.map +0 -10
  661. package/dist/cli/chunk-ct47dqpx.js.map +0 -11
  662. package/dist/cli/chunk-esphfr8p.js +0 -107
  663. package/dist/cli/chunk-esphfr8p.js.map +0 -11
  664. package/dist/cli/chunk-ex56aa81.js +0 -1016
  665. package/dist/cli/chunk-ex56aa81.js.map +0 -13
  666. package/dist/cli/chunk-garjf5z9.js +0 -30
  667. package/dist/cli/chunk-garjf5z9.js.map +0 -10
  668. package/dist/cli/chunk-js7saxwm.js +0 -1045
  669. package/dist/cli/chunk-js7saxwm.js.map +0 -22
  670. package/dist/cli/chunk-k79xp7av.js.map +0 -39
  671. package/dist/cli/chunk-ps4m1xh4.js.map +0 -15
  672. package/dist/cli/chunk-q4rae3bg.js +0 -60
  673. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  674. package/dist/cli/chunk-rz9jmfhz.js +0 -108
  675. package/dist/cli/chunk-rz9jmfhz.js.map +0 -10
  676. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  677. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  678. package/dist/cli/chunk-vrfp10qk.js +0 -81
  679. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  680. package/dist/cli/chunk-yg63d42r.js.map +0 -34
  681. package/docs/advanced/api-reference.mdx +0 -240
  682. package/docs/configuration/ask-ai.mdx +0 -256
  683. package/docs/reference/cli.mdx +0 -197
  684. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  685. package/src/components/content/changelog-element.ts +0 -69
  686. package/src/search/providers.ts +0 -91
  687. /package/dist/cli/{chunk-vacwm2hv.js.map → chunk-27g6wdth.js.map} +0 -0
  688. /package/dist/cli/{chunk-dwgcp5sm.js.map → chunk-ce574jw2.js.map} +0 -0
  689. /package/dist/cli/{chunk-e7f42gdj.js.map → chunk-fxypxtvm.js.map} +0 -0
  690. /package/dist/cli/{chunk-nn13znc2.js.map → chunk-zxcczpyx.js.map} +0 -0
@@ -12,6 +12,7 @@ import { codeToHtml } from "shiki";
12
12
  import { baseLinksPlugin } from "./base-links.ts";
13
13
  import { codeTitleTransformer } from "./code-title.ts";
14
14
  import { directiveToCalloutPlugin } from "./directives.ts";
15
+ import { externalLinksPlugin } from "./external-links.ts";
15
16
  import { MARKDOWN_FEATURES, MDX_FEATURES } from "./features.ts";
16
17
  import { headingAnchorPlugin } from "./heading-anchors.ts";
17
18
  import { includePlugin } from "./include.ts";
@@ -20,6 +21,7 @@ import { languageIconTransformer } from "./language-icon.ts";
20
21
  import { mathPlugin } from "./math.ts";
21
22
  import { mermaidPlugin } from "./mermaid.ts";
22
23
  import { packageInstallPlugin } from "./package-install.ts";
24
+ import { relativeLinksPlugin } from "./relative-links.ts";
23
25
  import { tableWrapPlugin } from "./table-wrap.ts";
24
26
  import { DEFAULT_CODE_THEMES } from "./themes.ts";
25
27
  import type { CodeThemes } from "./themes.ts";
@@ -87,12 +89,14 @@ const asShikiTransformer = (transformer: { name: string }): ShikiTransformer =>
87
89
  * (`[#custom-id]`, `[!toc]`, `[toc]`), which must parse regardless of config —
88
90
  * while `markdown.headingAnchors: false` only turns off the self-linking
89
91
  * anchor wrap on `<h2>`–`<h6>`. Inline code runs first so the anchor wrap
90
- * re-refs already-highlighted code.
92
+ * re-refs already-highlighted code. The external-link plugin is added only
93
+ * when `markdown.externalLinks` is on, so off stays today's exact output.
91
94
  */
92
95
  const blumeHastPlugins = (options: BlumeMarkdownOptions): HastPlugin[] => [
93
96
  asHastPlugin(inlineCodeHighlightPlugin(options.codeThemes)),
94
97
  asHastPlugin(tableWrapPlugin()),
95
98
  asHastPlugin(headingAnchorPlugin({ wrap: options.headingAnchors !== false })),
99
+ ...(options.externalLinks ? [asHastPlugin(externalLinksPlugin())] : []),
96
100
  ];
97
101
 
98
102
  /**
@@ -196,7 +200,7 @@ export interface HighlightCodeOptions extends BlumeShikiOptions {
196
200
  /** Extra `<pre>` class names, e.g. `blume-source` for a height-capped pane. */
197
201
  className?: string;
198
202
  /**
199
- * Light/dark Shiki themes (`markdown.codeBlocks.theme`). Defaults to the same
203
+ * Light/dark Shiki themes (`markdown.code.theme`). Defaults to the same
200
204
  * github pair fenced code uses, so out-of-pipeline code stays in lockstep.
201
205
  */
202
206
  themes?: CodeThemes;
@@ -248,9 +252,14 @@ export const highlightCode = async (
248
252
  export interface BlumeMarkdownOptions {
249
253
  /**
250
254
  * Light/dark Shiki themes for inline `` `code`{:lang} `` highlighting
251
- * (`markdown.codeBlocks.theme`). Defaults to the github pair fenced code uses.
255
+ * (`markdown.code.theme`). Defaults to the github pair fenced code uses.
252
256
  */
253
257
  codeThemes?: CodeThemes;
258
+ /**
259
+ * Open external links in a new tab, marked with an arrow icon and a
260
+ * screen-reader hint (`markdown.externalLinks`). Off unless `true`.
261
+ */
262
+ externalLinks?: boolean;
254
263
  /**
255
264
  * Wrap `<h2>`–`<h6>` in self-linking anchors (`markdown.headingAnchors`).
256
265
  * On unless explicitly `false`.
@@ -271,25 +280,41 @@ export interface BlumeMarkdownOptions {
271
280
  /**
272
281
  * The docs content root, bounding `<include>` target resolution (and
273
282
  * anchoring `/`-leading include paths). When unset, relative includes still
274
- * resolve from the including file.
283
+ * resolve from the including file. Also the base the `docs` collection's
284
+ * entry ids are relative to, which relative page links resolve through.
275
285
  */
276
286
  contentRoot?: string;
287
+ /**
288
+ * The `blume:data` snapshot file an ejected app aliases. Relative page links
289
+ * resolve through the snapshot the CLI publishes; with no CLI in the
290
+ * process, they read this file instead.
291
+ */
292
+ dataFile?: string;
277
293
  }
278
294
 
279
295
  /**
280
- * MDAST plugins that apply to both `.md` and `.mdx`: the base-path link
281
- * rewrite (added only when a `basePath` or `deployBase` is configured).
296
+ * MDAST plugins that apply to both `.md` and `.mdx`: relative page links
297
+ * rewritten to the root-relative route they mean, then the base-path link
298
+ * rewrite (added only when a `basePath` or `deployBase` is configured), which
299
+ * layers `deployment.base` over the rewritten routes.
282
300
  */
283
301
  const blumeSharedMdastPlugins = (
284
302
  options: BlumeMarkdownOptions
285
- ): MdastPlugin[] =>
286
- options.basePath || options.deployBase
303
+ ): MdastPlugin[] => [
304
+ asMdastPlugin(
305
+ relativeLinksPlugin({
306
+ contentRoot: options.contentRoot,
307
+ dataFile: options.dataFile,
308
+ })
309
+ ),
310
+ ...(options.basePath || options.deployBase
287
311
  ? [
288
312
  asMdastPlugin(
289
313
  baseLinksPlugin(options.deployBase ?? "", options.basePath ?? "")
290
314
  ),
291
315
  ]
292
- : [];
316
+ : []),
317
+ ];
293
318
 
294
319
  /**
295
320
  * The `<include>` splice. Always first: its mutations apply before the next
@@ -7,11 +7,14 @@
7
7
  * marker and replaces the code's text with Shiki tokens (dual github-light/dark
8
8
  * via CSS variables, like fenced blocks).
9
9
  *
10
- * Shiki is imported lazily, so it loads only on pages that actually use inline
10
+ * Shiki is loaded lazily, so it loads only on pages that actually use inline
11
11
  * highlighting — and never when `markdown.code.inline` is off (the plugin is not
12
12
  * added to the pipeline at all).
13
13
  */
14
14
 
15
+ import type * as ShikiModule from "shiki";
16
+
17
+ import { nodeRequire } from "../core/node-require.ts";
15
18
  import { DEFAULT_CODE_THEMES } from "./themes.ts";
16
19
  import type { CodeThemes } from "./themes.ts";
17
20
 
@@ -77,13 +80,16 @@ type InlineHighlighter = (
77
80
  }
78
81
  ) => Promise<{ children: HastNode[] }>;
79
82
 
80
- // `import()` caches the module, so this dedupes Shiki across calls on its own.
81
- const loadHighlighter = async (): Promise<InlineHighlighter> => {
82
- const mod = await import("shiki");
83
+ // `require` caches the module, so this dedupes Shiki across calls on its own.
84
+ // Not `import()`: the plugin runs after Astro has closed the module runner an
85
+ // installed Blume's config was evaluated in (see `core/node-require.ts`), so
86
+ // every inline snippet would silently lose its highlighting.
87
+ const loadHighlighter = (): InlineHighlighter => {
88
+ const { codeToHast }: typeof ShikiModule = nodeRequire("shiki");
83
89
  // SAFETY: Shiki accepts arbitrary lang/theme strings at runtime (an unknown
84
90
  // one rejects the promise, which the caller catches); only its bundled types
85
91
  // constrain them to known ids, so the loose signature narrows nothing real.
86
- return mod.codeToHast as InlineHighlighter;
92
+ return codeToHast as InlineHighlighter;
87
93
  };
88
94
 
89
95
  /** Build the plugin. Highlights inline `` `code{:lang}` `` snippets. */
@@ -102,7 +108,7 @@ export const inlineCodeHighlightPlugin = (
102
108
  return;
103
109
  }
104
110
  try {
105
- const codeToHast = await loadHighlighter();
111
+ const codeToHast = loadHighlighter();
106
112
  const root = await codeToHast(parsed.code, {
107
113
  defaultColor: false,
108
114
  lang: parsed.lang,
@@ -0,0 +1,324 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ import { dirname, normalize, relative, resolve } from "pathe";
5
+
6
+ import { readRuntimeModule } from "../astro/runtime-modules.ts";
7
+ import { isIndexFileName, resolveRelativeHref } from "../core/links.ts";
8
+ import type { RelativeLinkBase } from "../core/links.ts";
9
+ import type { MdastNode, MdastValue } from "./mdast.ts";
10
+
11
+ interface UrlNode extends MdastNode {
12
+ url?: string | null;
13
+ }
14
+
15
+ /** An MDX JSX attribute, as the plugin reads it. */
16
+ interface JsxAttribute {
17
+ [key: string]: MdastValue;
18
+ name?: string;
19
+ type?: string;
20
+ value?: MdastValue;
21
+ }
22
+
23
+ /** An MDX JSX element (`<Card href="./install" />`), as the plugin reads it. */
24
+ interface JsxNode extends MdastNode {
25
+ attributes?: JsxAttribute[];
26
+ children?: MdastValue[];
27
+ name?: string | null;
28
+ }
29
+
30
+ /** The visitor-context slice this plugin uses (see `mdast.ts` for the model). */
31
+ interface RelativeLinksContext {
32
+ fileURL: URL | undefined;
33
+ replaceNode: (node: MdastNode, replacement: MdastNode) => void;
34
+ setProperty: (node: MdastNode, key: "url", value: string) => void;
35
+ }
36
+
37
+ /** The slice of the `blume:data` snapshot the plugin reads. */
38
+ interface RouteData {
39
+ config: {
40
+ i18n?: { defaultLocale: string; locales: { code: string }[] } | null;
41
+ };
42
+ routes: {
43
+ collection: string;
44
+ entryId: string;
45
+ fallback: boolean;
46
+ locale: string;
47
+ path: string;
48
+ }[];
49
+ }
50
+
51
+ /** What the plugin knows about where content files publish. */
52
+ interface RouteIndex {
53
+ /** `collection` + NUL + `entryId` → the route that entry publishes at. */
54
+ routes: Map<string, string>;
55
+ /** Every route the site serves, fallback copies included. */
56
+ paths: Set<string>;
57
+ /** Tokens an index file name may carry before its extension. */
58
+ localeTokens: string[];
59
+ /** The locale folders beside the default tree (every locale but the default). */
60
+ localeFolders: string[];
61
+ }
62
+
63
+ /** A content file, located: its page, and how to find the files beside it. */
64
+ interface LocatedPage extends RelativeLinkBase {
65
+ /** The route a sibling file publishes at, from its path relative to `file`. */
66
+ routeOf: (path: string) => string | undefined;
67
+ /** Every route the site serves, for dotted page names (`./node.js`). */
68
+ routes: ReadonlySet<string>;
69
+ }
70
+
71
+ /** A JSX component name (`Card`, `Tree.File`), as opposed to an HTML tag. */
72
+ const COMPONENT_NAME = /^[A-Z]/u;
73
+
74
+ /** A plain string attribute value; an expression value isn't a link target. */
75
+ const isStringValue = (value: MdastValue): value is string =>
76
+ typeof value === "string";
77
+
78
+ const entryKey = (collection: string, entryId: string): string =>
79
+ `${collection}\0${entryId}`;
80
+
81
+ /**
82
+ * Index the snapshot's routes by collection entry. A file shared by every
83
+ * locale publishes once per locale; the default locale's route stands for it,
84
+ * and the rendered link moves into the reader's locale afterwards (see
85
+ * `LocaleLinks.astro`). Fallback copies render another locale's file, so they
86
+ * never stand for it.
87
+ */
88
+ const indexRoutes = (data: RouteData): RouteIndex => {
89
+ const defaultLocale = data.config.i18n?.defaultLocale;
90
+ const routes = new Map<string, string>();
91
+ for (const route of data.routes) {
92
+ if (route.fallback) {
93
+ continue;
94
+ }
95
+ const key = entryKey(route.collection, route.entryId);
96
+ if (!routes.has(key) || route.locale === defaultLocale) {
97
+ routes.set(key, route.path);
98
+ }
99
+ }
100
+ const locales = data.config.i18n?.locales.map((locale) => locale.code) ?? [];
101
+ return {
102
+ localeFolders: locales.filter((code) => code !== defaultLocale),
103
+ localeTokens: data.config.i18n ? ["$", ...locales] : [],
104
+ paths: new Set(data.routes.map((route) => route.path)),
105
+ routes,
106
+ };
107
+ };
108
+
109
+ /**
110
+ * The same entry path in the default tree: a page in a locale folder that
111
+ * links a sibling not translated yet (`fr/guides/setup.mdx` missing) means
112
+ * the default tree's file (`guides/setup.mdx`), whose route — its own `slug`
113
+ * included — moves into the reader's locale afterwards, onto the fallback
114
+ * copy (see `LocaleLinks.astro`).
115
+ */
116
+ const defaultTreePath = (
117
+ entryId: string,
118
+ localeFolders: readonly string[]
119
+ ): string | undefined => {
120
+ const slash = entryId.indexOf("/");
121
+ return slash !== -1 && localeFolders.includes(entryId.slice(0, slash))
122
+ ? entryId.slice(slash + 1)
123
+ : undefined;
124
+ };
125
+
126
+ export interface RelativeLinksPluginOptions {
127
+ /** The `docs` collection's base directory, which entry ids are relative to. */
128
+ contentRoot?: string;
129
+ /**
130
+ * The `blume:data` JSON file, for an ejected app: with no CLI in the process
131
+ * to publish the snapshot, the plugin reads the file eject writes instead.
132
+ */
133
+ dataFile?: string;
134
+ }
135
+
136
+ /**
137
+ * Satteri MDAST plugin that rewrites relative page links (`[Install](./install)`,
138
+ * `[Setup](../guides/setup.md)`) to the root-relative route they mean, using
139
+ * the same file-style reading `blume validate` checks them against
140
+ * (`resolveRelativeHref`). Left as written, the browser resolves them against
141
+ * the page's slashless URL — so `./install` on an index page (`/guides`) lands
142
+ * on `/install`, and a `.md` link opens the raw Markdown source instead of the
143
+ * page. The page's own route, and a linked file's, come from the `blume:data`
144
+ * snapshot the CLI publishes (or, ejected, the file eject writes), keyed by
145
+ * collection entry: `docs` entries by their path under `contentRoot`, staged
146
+ * entries (remote sources) by the longest trailing path that names one. A file
147
+ * the snapshot doesn't know keeps its links as written.
148
+ *
149
+ * Runs before the base-links plugin, whose `basePath` layering is idempotent,
150
+ * so a rewritten route (which already carries `basePath`) only gains the
151
+ * `deployment.base` prefix.
152
+ */
153
+ export const relativeLinksPlugin = (
154
+ options: RelativeLinksPluginOptions = {}
155
+ ) => {
156
+ const contentRoot = options.contentRoot
157
+ ? resolve(options.contentRoot)
158
+ : undefined;
159
+ const dataFile = options.dataFile ? resolve(options.dataFile) : undefined;
160
+
161
+ // Parsed once per published snapshot: the CLI republishes on regeneration,
162
+ // and an unchanged snapshot is the same string, so every page compiled
163
+ // between regenerations reuses one index.
164
+ let cached: { index: RouteIndex; text: string } | undefined;
165
+ let fileStamp: number | undefined;
166
+ let fileText: string | undefined;
167
+
168
+ /** The ejected snapshot file's text, re-read only when it changes. */
169
+ const readDataFile = (path: string): string | undefined => {
170
+ let stamp: number;
171
+ try {
172
+ stamp = statSync(path).mtimeMs;
173
+ } catch {
174
+ return undefined;
175
+ }
176
+ if (stamp !== fileStamp) {
177
+ fileStamp = stamp;
178
+ fileText = readFileSync(path, "utf-8");
179
+ }
180
+ return fileText;
181
+ };
182
+
183
+ const routeIndex = (): RouteIndex | undefined => {
184
+ const text =
185
+ readRuntimeModule("blume:data") ??
186
+ (dataFile ? readDataFile(dataFile) : undefined);
187
+ if (text === undefined) {
188
+ return undefined;
189
+ }
190
+ if (cached?.text !== text) {
191
+ const data: RouteData = JSON.parse(text);
192
+ cached = { index: indexRoutes(data), text };
193
+ }
194
+ return cached.index;
195
+ };
196
+
197
+ /** The page `file` renders, with a resolver for the files beside it. */
198
+ const locate = (file: string, index: RouteIndex): LocatedPage | undefined => {
199
+ const locateIn = (
200
+ collection: string,
201
+ base: string
202
+ ): LocatedPage | undefined => {
203
+ const entryId = relative(base, file);
204
+ const route = index.routes.get(entryKey(collection, entryId));
205
+ if (route === undefined) {
206
+ return undefined;
207
+ }
208
+ return {
209
+ isIndex: isIndexFileName(entryId, index.localeTokens),
210
+ route,
211
+ routeOf: (path) => {
212
+ const target = relative(base, resolve(dirname(file), path));
213
+ const inDefaultTree = defaultTreePath(target, index.localeFolders);
214
+ return (
215
+ index.routes.get(entryKey(collection, target)) ??
216
+ (inDefaultTree === undefined
217
+ ? undefined
218
+ : index.routes.get(entryKey(collection, inDefaultTree)))
219
+ );
220
+ },
221
+ routes: index.paths,
222
+ };
223
+ };
224
+ if (contentRoot && !relative(contentRoot, file).startsWith("../")) {
225
+ return locateIn("docs", contentRoot);
226
+ }
227
+ // A staged entry's id is its path under the staging dir, which the plugin
228
+ // isn't told: the longest trailing path naming a staged entry wins.
229
+ const segments = file.split("/");
230
+ for (let start = 1; start < segments.length; start += 1) {
231
+ const staged = locateIn(
232
+ "staged",
233
+ segments.slice(0, start).join("/") || "/"
234
+ );
235
+ if (staged) {
236
+ return staged;
237
+ }
238
+ }
239
+ return undefined;
240
+ };
241
+
242
+ /** The route `url` means on the page at `fileURL`, when it's relative. */
243
+ const resolveUrl = (
244
+ url: string,
245
+ fileURL: URL | undefined
246
+ ): string | undefined => {
247
+ // Cheap first: a target that can't be relative never needs the index. A
248
+ // dotted name (`./node.js`) passes, since only the index knows its route.
249
+ if (
250
+ !fileURL ||
251
+ resolveRelativeHref(
252
+ url,
253
+ { isIndex: false, route: "/" },
254
+ undefined,
255
+ () => true
256
+ ) === undefined
257
+ ) {
258
+ return undefined;
259
+ }
260
+ const index = routeIndex();
261
+ const page = index && locate(normalize(fileURLToPath(fileURL)), index);
262
+ if (!page) {
263
+ return undefined;
264
+ }
265
+ const next = resolveRelativeHref(url, page, page.routeOf, (route) =>
266
+ page.routes.has(route)
267
+ );
268
+ return next === url ? undefined : next;
269
+ };
270
+
271
+ const rewrite = (node: UrlNode, ctx: RelativeLinksContext): void => {
272
+ const { url } = node;
273
+ const next = isStringValue(url) ? resolveUrl(url, ctx.fileURL) : undefined;
274
+ if (next !== undefined) {
275
+ ctx.setProperty(node, "url", next);
276
+ }
277
+ };
278
+
279
+ // A component's string `href` (`<Card href="./install">`) is the same kind
280
+ // of link, and `blume validate` reads it as one. Lowercase elements are
281
+ // raw HTML in a `.md` page, which neither rewrites nor checks, so only
282
+ // components are rewritten; an expression-valued `href={…}` is left alone.
283
+ const rewriteHref = (node: JsxNode, ctx: RelativeLinksContext): void => {
284
+ if (!node.name || !COMPONENT_NAME.test(node.name)) {
285
+ return;
286
+ }
287
+ const attributes = node.attributes ?? [];
288
+ const at = attributes.findIndex(
289
+ (attribute) =>
290
+ attribute.type === "mdxJsxAttribute" &&
291
+ attribute.name === "href" &&
292
+ isStringValue(attribute.value)
293
+ );
294
+ const value = attributes[at]?.value;
295
+ const next = isStringValue(value)
296
+ ? resolveUrl(value, ctx.fileURL)
297
+ : undefined;
298
+ if (next !== undefined) {
299
+ // Sätteri can't set a JSX element's attributes in place, so the element
300
+ // is swapped for a copy; its children carry over and are still visited.
301
+ ctx.replaceNode(node, {
302
+ attributes: attributes.map((attribute, position) =>
303
+ position === at
304
+ ? { name: attribute.name, type: attribute.type, value: next }
305
+ : attribute
306
+ ),
307
+ children: node.children ?? [],
308
+ name: node.name,
309
+ type: node.type,
310
+ });
311
+ }
312
+ };
313
+
314
+ // `link` covers inline links; `definition` covers reference-style
315
+ // definitions (`[x]: ./install`). Images stay with the image pipeline, which
316
+ // resolves relative embeds from beside the page source.
317
+ return {
318
+ definition: rewrite,
319
+ link: rewrite,
320
+ mdxJsxFlowElement: rewriteHref,
321
+ mdxJsxTextElement: rewriteHref,
322
+ name: "blume-relative-links",
323
+ };
324
+ };
@@ -2,7 +2,7 @@
2
2
  * The light/dark Shiki themes Blume highlights code with. Every Shiki surface —
3
3
  * fenced code (the generated Astro `shikiConfig.themes`), inline `` `code`{:lang} ``,
4
4
  * out-of-pipeline `highlightCode`, and `<Diff>` — resolves to the same pair so a
5
- * project's `markdown.codeBlocks.theme` shifts them all in lockstep. This is the
5
+ * project's `markdown.code.theme` shifts them all in lockstep. This is the
6
6
  * single home for the github fallback used when nothing is configured.
7
7
  */
8
8
 
@@ -12,7 +12,7 @@ import type { ThemeRegistrationAny } from "shiki";
12
12
  export type CodeTheme = string | ThemeRegistrationAny;
13
13
 
14
14
  /**
15
- * A light/dark Shiki theme pair (`markdown.codeBlocks.theme`). A `type` (not an
15
+ * A light/dark Shiki theme pair (`markdown.code.theme`). A `type` (not an
16
16
  * `interface`) so it keeps the implicit index signature Shiki's `themes`
17
17
  * parameter (`Partial<Record<string, …>>`) expects.
18
18
  */
@@ -22,7 +22,7 @@ export type CodeThemes = {
22
22
  light: CodeTheme;
23
23
  };
24
24
 
25
- /** The default pair, used when `markdown.codeBlocks.theme` is unset. */
25
+ /** The default pair, used when `markdown.code.theme` is unset. */
26
26
  export const DEFAULT_CODE_THEMES: CodeThemes = {
27
27
  dark: "github-dark",
28
28
  light: "github-light",
@@ -0,0 +1,149 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+
4
+ import { join } from "pathe";
5
+
6
+ /**
7
+ * `blume migrate`'s logic, kept out of the command module so it runs (and is
8
+ * covered) in-process. Migration is judgment work — idiomatic navigation,
9
+ * component rewrites, what to drop — so it stays in the `blume-migrate` skill,
10
+ * shipped inside the package; this module only names the source (from the
11
+ * argument, or detected from the repo's own files) and writes the prompt that
12
+ * points a coding agent at the skill and that source's mappings.
13
+ */
14
+
15
+ /** The docs frameworks the skill has a mapping reference for. */
16
+ export const MIGRATE_SOURCES = [
17
+ "mintlify",
18
+ "fumadocs",
19
+ "docusaurus",
20
+ "starlight",
21
+ "nextra",
22
+ ] as const;
23
+
24
+ export type MigrateSourceId = (typeof MIGRATE_SOURCES)[number];
25
+
26
+ /** Display names, for messages and the prompt. */
27
+ export const MIGRATE_SOURCE_NAMES: Record<MigrateSourceId, string> = {
28
+ docusaurus: "Docusaurus",
29
+ fumadocs: "Fumadocs",
30
+ mintlify: "Mintlify",
31
+ nextra: "Nextra",
32
+ starlight: "Starlight",
33
+ };
34
+
35
+ /** The skill's directory inside the published package. */
36
+ export const MIGRATE_SKILL_DIR = join("skills", "blume-migrate");
37
+
38
+ export const isMigrateSource = (value: string): value is MigrateSourceId =>
39
+ MIGRATE_SOURCES.some((source) => source === value);
40
+
41
+ /** A detected source and the file or dependency that gave it away. */
42
+ export interface DetectedSource {
43
+ evidence: string;
44
+ source: MigrateSourceId;
45
+ }
46
+
47
+ /** The slice of `package.json` detection reads. */
48
+ interface PackageJson {
49
+ dependencies?: Record<string, string>;
50
+ devDependencies?: Record<string, string>;
51
+ }
52
+
53
+ /** Every dependency the project's `package.json` names, or none when unreadable. */
54
+ const dependencyNames = async (root: string): Promise<Set<string>> => {
55
+ const path = join(root, "package.json");
56
+ if (!existsSync(path)) {
57
+ return new Set();
58
+ }
59
+ try {
60
+ // SAFETY: package.json is a JSON object; only its dependency maps are read.
61
+ const pkg = JSON.parse(await readFile(path, "utf-8")) as PackageJson;
62
+ return new Set([
63
+ ...Object.keys(pkg.dependencies ?? {}),
64
+ ...Object.keys(pkg.devDependencies ?? {}),
65
+ ]);
66
+ } catch {
67
+ // A malformed package.json is the agent's to deal with; detection just
68
+ // falls back to the other signals.
69
+ return new Set();
70
+ }
71
+ };
72
+
73
+ /**
74
+ * Each source's tell, in the order the skill's workflow checks them: a config
75
+ * file the framework owns, or a dependency only it installs.
76
+ */
77
+ const SIGNALS: {
78
+ deps?: string[];
79
+ files?: string[];
80
+ source: MigrateSourceId;
81
+ }[] = [
82
+ { files: ["docs.json", "mint.json"], source: "mintlify" },
83
+ {
84
+ files: [
85
+ "docusaurus.config.ts",
86
+ "docusaurus.config.js",
87
+ "docusaurus.config.mjs",
88
+ "docusaurus.config.cjs",
89
+ ],
90
+ source: "docusaurus",
91
+ },
92
+ {
93
+ deps: ["fumadocs-core", "fumadocs-ui", "fumadocs-mdx"],
94
+ files: ["source.config.ts"],
95
+ source: "fumadocs",
96
+ },
97
+ { deps: ["nextra"], source: "nextra" },
98
+ { deps: ["@astrojs/starlight"], source: "starlight" },
99
+ ];
100
+
101
+ /**
102
+ * Detect the docs framework a project is built on from its own files, or
103
+ * null when nothing gives it away (the skill still migrates any framework,
104
+ * working from its general model instead of a mapping reference).
105
+ */
106
+ export const detectMigrateSource = async (
107
+ root: string
108
+ ): Promise<DetectedSource | null> => {
109
+ const deps = await dependencyNames(root);
110
+ for (const { deps: tells = [], files = [], source } of SIGNALS) {
111
+ const file = files.find((name) => existsSync(join(root, name)));
112
+ if (file) {
113
+ return { evidence: file, source };
114
+ }
115
+ const dep = tells.find((name) => deps.has(name));
116
+ if (dep) {
117
+ return { evidence: `${dep} in package.json`, source };
118
+ }
119
+ }
120
+ return null;
121
+ };
122
+
123
+ /**
124
+ * The handoff prompt: follow the skill, where its mappings for this source
125
+ * are, and the few rules worth restating — the target version, and that no
126
+ * page or URL is lost.
127
+ */
128
+ export const migratePrompt = (options: {
129
+ detected: DetectedSource | null;
130
+ skillDir: string;
131
+ source: MigrateSourceId | null;
132
+ version: string;
133
+ }): string => {
134
+ const { detected, skillDir, source, version } = options;
135
+ const evidence =
136
+ detected && detected.source === source
137
+ ? ` (detected from ${detected.evidence})`
138
+ : "";
139
+ const sourceLine = source
140
+ ? `The source is ${MIGRATE_SOURCE_NAMES[source]}${evidence}. Its exact mappings are in ${join(skillDir, "references", `${source}.md`)}.`
141
+ : `The source framework wasn't detected, so inventory the repo first. If it's ${MIGRATE_SOURCES.map((id) => MIGRATE_SOURCE_NAMES[id]).join(", ")}, read that framework's file in ${join(skillDir, "references")}; otherwise follow the skill's mental model directly.`;
142
+ return `Migrate this documentation project to Blume ${version}.
143
+
144
+ Follow the blume-migrate skill: read ${join(skillDir, "SKILL.md")} first and work through its migration workflow. Wherever the skill says \`<skill>\`, it means ${skillDir}.
145
+
146
+ ${sourceLine}
147
+
148
+ Add \`blume@^${version}\` as the docs package's dependency. Keep every page, and add a redirect for any URL that moves. Report everything you drop or approximate, and run the skill's verification steps before you finish.`;
149
+ };