blume 2.0.3 → 2.1.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 (607) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +142 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
  5. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
  6. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  7. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  8. package/dist/cli/chunk-150jk7km.js +52 -0
  9. package/dist/cli/chunk-150jk7km.js.map +10 -0
  10. package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
  11. package/dist/cli/chunk-313vnn40.js.map +10 -0
  12. package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
  13. package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
  14. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  15. package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
  16. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  17. package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
  18. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  19. package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
  20. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
  21. package/dist/cli/chunk-66w9j7p6.js +322 -0
  22. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  23. package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
  24. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  25. package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
  26. package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
  27. package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
  28. package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
  29. package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
  30. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  31. package/dist/cli/chunk-cm89scdv.js +443 -0
  32. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  33. package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
  34. package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
  35. package/dist/cli/chunk-e6atmn4h.js.map +36 -0
  36. package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
  37. package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
  38. package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
  39. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
  40. package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
  41. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  42. package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
  43. package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
  44. package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
  45. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
  46. package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
  47. package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
  48. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  49. package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
  50. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  51. package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
  52. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  53. package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
  54. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
  55. package/dist/cli/chunk-j134vrp2.js +65 -0
  56. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  57. package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
  58. package/dist/cli/chunk-kwrg63tm.js +31 -0
  59. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  60. package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
  61. package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
  62. package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
  63. package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
  64. package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
  65. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  66. package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
  67. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
  68. package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
  69. package/dist/cli/chunk-nrns10mj.js.map +10 -0
  70. package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
  71. package/dist/cli/chunk-t2m5j4pr.js +104 -0
  72. package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
  73. package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
  74. package/dist/cli/chunk-tphac74f.js +126 -0
  75. package/dist/cli/chunk-tphac74f.js.map +11 -0
  76. package/dist/cli/chunk-w1r4vase.js +18 -0
  77. package/dist/cli/chunk-w1r4vase.js.map +10 -0
  78. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  79. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  80. package/dist/cli/index.js +31 -20
  81. package/dist/cli/index.js.map +5 -5
  82. package/dist/types/ai/agent-surface.d.ts +8 -7
  83. package/dist/types/ai/ai-catalog.d.ts +7 -0
  84. package/dist/types/ai/ask-context.d.ts +26 -0
  85. package/dist/types/ai/ask.d.ts +271 -58
  86. package/dist/types/ai/component-markdown.d.ts +2 -0
  87. package/dist/types/ai/index.d.ts +2 -2
  88. package/dist/types/ai/openapi-components.d.ts +4 -1
  89. package/dist/types/ai/site-skill.d.ts +26 -0
  90. package/dist/types/ai/skills.d.ts +16 -0
  91. package/dist/types/astro/integration.d.ts +9 -0
  92. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  93. package/dist/types/astro/pages.d.ts +10 -0
  94. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  95. package/dist/types/captcha/index.d.ts +28 -0
  96. package/dist/types/captcha/schema.d.ts +29 -0
  97. package/dist/types/captcha/turnstile.d.ts +28 -0
  98. package/dist/types/cli/init/scaffold.d.ts +1 -1
  99. package/dist/types/components/content/api-field.d.ts +62 -0
  100. package/dist/types/components/content/api-page.d.ts +100 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  103. package/dist/types/components/openapi/helpers.d.ts +158 -0
  104. package/dist/types/components/openapi/security.d.ts +95 -0
  105. package/dist/types/components/openapi/snippets.d.ts +60 -0
  106. package/dist/types/consent/ethyca.d.ts +48 -0
  107. package/dist/types/consent/index.d.ts +37 -0
  108. package/dist/types/consent/native.d.ts +28 -0
  109. package/dist/types/consent/osano.d.ts +42 -0
  110. package/dist/types/consent/schema.d.ts +110 -0
  111. package/dist/types/core/changelog-index.d.ts +8 -0
  112. package/dist/types/core/config-input.d.ts +217 -23
  113. package/dist/types/core/data.d.ts +53 -4
  114. package/dist/types/core/footer.d.ts +8 -0
  115. package/dist/types/core/i18n-ui.d.ts +88 -0
  116. package/dist/types/core/includes.d.ts +8 -2
  117. package/dist/types/core/links.d.ts +8 -0
  118. package/dist/types/core/localizable.d.ts +7 -0
  119. package/dist/types/core/page-modes.d.ts +29 -0
  120. package/dist/types/core/probe.d.ts +2 -0
  121. package/dist/types/core/project-graph.d.ts +6 -0
  122. package/dist/types/core/redirect-patterns.d.ts +152 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +670 -56
  125. package/dist/types/core/sources/cache.d.ts +3 -0
  126. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  127. package/dist/types/core/sources/contentful.d.ts +17 -1
  128. package/dist/types/core/sources/lexical.d.ts +5 -0
  129. package/dist/types/core/sources/normalize.d.ts +20 -1
  130. package/dist/types/core/sources/read.d.ts +5 -0
  131. package/dist/types/core/sources/remote.d.ts +37 -10
  132. package/dist/types/core/sources/sanity.d.ts +1 -1
  133. package/dist/types/core/types.d.ts +18 -1
  134. package/dist/types/core/variables.d.ts +42 -0
  135. package/dist/types/deploy/adapter-output.d.ts +2 -1
  136. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  137. package/dist/types/deploy/adapters/node.d.ts +58 -23
  138. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  139. package/dist/types/deploy/adapters/types.d.ts +3 -2
  140. package/dist/types/deploy/node-headers.d.ts +13 -2
  141. package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
  142. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  143. package/dist/types/deploy/platforms/node.d.ts +3 -1
  144. package/dist/types/deploy/platforms/types.d.ts +18 -7
  145. package/dist/types/deploy/platforms/vercel.d.ts +26 -1
  146. package/dist/types/deploy/redirects.d.ts +55 -11
  147. package/dist/types/deploy/sitemap.d.ts +2 -1
  148. package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
  149. package/dist/types/markdown/code-title.d.ts +17 -0
  150. package/dist/types/markdown/features.d.ts +3 -2
  151. package/dist/types/narration/build.d.ts +70 -0
  152. package/dist/types/narration/provider.d.ts +26 -0
  153. package/dist/types/narration/script.d.ts +154 -0
  154. package/dist/types/openapi/checks.d.ts +27 -0
  155. package/dist/types/openapi/graphql.d.ts +6 -0
  156. package/dist/types/openapi/model.d.ts +27 -10
  157. package/dist/types/openapi/overlay.d.ts +54 -0
  158. package/dist/types/openapi/parse.d.ts +16 -0
  159. package/dist/types/openapi/references.d.ts +30 -4
  160. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  161. package/dist/types/ratelimit/index.d.ts +27 -0
  162. package/dist/types/ratelimit/memory.d.ts +36 -0
  163. package/dist/types/ratelimit/schema.d.ts +119 -0
  164. package/dist/types/ratelimit/upstash.d.ts +46 -0
  165. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  166. package/dist/types/reference/asyncapi.d.ts +10 -10
  167. package/dist/types/reference/graphql.d.ts +89 -11
  168. package/dist/types/reference/index.d.ts +1 -1
  169. package/dist/types/reference/openapi.d.ts +33 -12
  170. package/dist/types/reference/options.d.ts +82 -6
  171. package/dist/types/reference/scalar.d.ts +13 -0
  172. package/dist/types/reference/schema.d.ts +144 -36
  173. package/dist/types/search/adapters/registry.d.ts +3 -0
  174. package/dist/types/search/adapters/typesense.d.ts +14 -4
  175. package/dist/types/search/build.d.ts +3 -1
  176. package/dist/types/search/documents.d.ts +12 -1
  177. package/dist/types/search/orama-index.d.ts +13 -9
  178. package/dist/types/search/sync/algolia.d.ts +5 -2
  179. package/dist/types/search/sync/index.d.ts +3 -1
  180. package/dist/types/search/sync/typesense.d.ts +11 -7
  181. package/dist/types/seo/metatags.d.ts +20 -0
  182. package/dist/types/sources/contentful.d.ts +16 -0
  183. package/dist/types/sources/github-releases.d.ts +8 -1
  184. package/dist/types/sources/payload.d.ts +2 -0
  185. package/dist/types/sources/registry.d.ts +54 -0
  186. package/dist/types/sources/shared.d.ts +3 -0
  187. package/dist/types/sources/strapi.d.ts +2 -0
  188. package/docs/01-quickstart.mdx +4 -2
  189. package/docs/02-deployment.mdx +71 -17
  190. package/docs/03-upgrading.mdx +4 -4
  191. package/docs/04-migrating.mdx +1 -1
  192. package/docs/advanced/changelog.mdx +13 -2
  193. package/docs/advanced/skills.mdx +15 -1
  194. package/docs/cli/audit.mdx +51 -8
  195. package/docs/cli/doctor.mdx +5 -1
  196. package/docs/cli/evals.mdx +4 -4
  197. package/docs/cli/index.mdx +1 -0
  198. package/docs/cli/translate.mdx +1 -0
  199. package/docs/cli/validate.mdx +4 -3
  200. package/docs/configuration/analytics.mdx +3 -1
  201. package/docs/configuration/assistant.mdx +194 -38
  202. package/docs/configuration/consent.mdx +100 -0
  203. package/docs/configuration/customization.mdx +8 -6
  204. package/docs/configuration/export.mdx +5 -3
  205. package/docs/configuration/index.mdx +64 -5
  206. package/docs/configuration/meta.ts +3 -0
  207. package/docs/configuration/narration.mdx +119 -0
  208. package/docs/configuration/rate-limiting.mdx +90 -0
  209. package/docs/configuration/search.mdx +63 -12
  210. package/docs/configuration/theming.mdx +3 -1
  211. package/docs/content/components.mdx +108 -2
  212. package/docs/content/frontmatter.mdx +80 -2
  213. package/docs/content/i18n.mdx +39 -4
  214. package/docs/content/includes.mdx +16 -0
  215. package/docs/content/meta.mdx +1 -0
  216. package/docs/content/meta.ts +1 -0
  217. package/docs/content/navigation.mdx +32 -10
  218. package/docs/content/sources/contentful.mdx +35 -0
  219. package/docs/content/sources/custom.mdx +43 -0
  220. package/docs/content/sources/github-releases.mdx +39 -0
  221. package/docs/content/sources/index.mdx +91 -0
  222. package/docs/content/sources/meta.ts +16 -0
  223. package/docs/content/sources/notion.mdx +32 -0
  224. package/docs/content/sources/obsidian.mdx +37 -0
  225. package/docs/content/sources/payload.mdx +31 -0
  226. package/docs/content/sources/remote-mdx.mdx +44 -0
  227. package/docs/content/sources/sanity.mdx +29 -0
  228. package/docs/content/sources/strapi.mdx +32 -0
  229. package/docs/content/syntax.mdx +69 -4
  230. package/docs/content/variables.mdx +41 -0
  231. package/docs/discoverability/agent-discovery.mdx +29 -2
  232. package/docs/discoverability/index.mdx +2 -2
  233. package/docs/discoverability/json-api.mdx +2 -0
  234. package/docs/discoverability/llms-txt.mdx +2 -2
  235. package/docs/discoverability/markdown.mdx +3 -1
  236. package/docs/discoverability/mcp.mdx +4 -0
  237. package/docs/discoverability/metadata.mdx +17 -0
  238. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  239. package/docs/references/api-pages.mdx +74 -0
  240. package/docs/references/asyncapi.mdx +4 -2
  241. package/docs/references/graphql.mdx +14 -2
  242. package/docs/references/meta.ts +1 -1
  243. package/docs/references/openapi.mdx +148 -7
  244. package/docs/references/scalar.mdx +3 -1
  245. package/package.json +43 -8
  246. package/skills/blume/SKILL.md +3 -0
  247. package/skills/blume-migrate/SKILL.md +6 -6
  248. package/skills/blume-migrate/references/docusaurus.md +7 -7
  249. package/skills/blume-migrate/references/fumadocs.md +17 -13
  250. package/skills/blume-migrate/references/mintlify.md +37 -19
  251. package/skills/blume-migrate/references/nextra.md +10 -7
  252. package/skills/blume-migrate/references/starlight.md +11 -8
  253. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  254. package/skills/blume-write-skill/SKILL.md +82 -0
  255. package/src/ai/agent-readability.ts +2 -1
  256. package/src/ai/agent-surface.ts +9 -7
  257. package/src/ai/ai-catalog.ts +6 -3
  258. package/src/ai/ask-context.ts +103 -20
  259. package/src/ai/ask-data.ts +6 -0
  260. package/src/ai/ask-limits.ts +13 -0
  261. package/src/ai/ask-tools.ts +142 -0
  262. package/src/ai/ask.ts +354 -76
  263. package/src/ai/changelog-markdown.ts +11 -6
  264. package/src/ai/component-markdown.ts +76 -1
  265. package/src/ai/cors.ts +14 -3
  266. package/src/ai/index.ts +12 -0
  267. package/src/ai/llms.ts +6 -2
  268. package/src/ai/markdown.ts +2 -1
  269. package/src/ai/mcp/data.ts +6 -0
  270. package/src/ai/mcp/query.ts +14 -6
  271. package/src/ai/openapi-components.ts +36 -6
  272. package/src/ai/relative-links.ts +4 -38
  273. package/src/ai/site-skill.ts +274 -0
  274. package/src/ai/skills.ts +35 -2
  275. package/src/ai/write-skill.ts +97 -0
  276. package/src/astro/generate.ts +210 -66
  277. package/src/astro/index.ts +1 -0
  278. package/src/astro/integration.ts +42 -4
  279. package/src/astro/markdown-negotiation.ts +3 -0
  280. package/src/astro/module-types.ts +9 -1
  281. package/src/astro/pages.ts +23 -0
  282. package/src/astro/templates.ts +406 -108
  283. package/src/astro/variables.ts +31 -0
  284. package/src/audit/catalog.ts +38 -3
  285. package/src/audit/checks/contrast.ts +183 -0
  286. package/src/audit/checks/i18n.ts +2 -3
  287. package/src/audit/checks/indexability.ts +2 -3
  288. package/src/audit/checks/links.ts +19 -13
  289. package/src/audit/checks/network.ts +110 -6
  290. package/src/audit/checks/redirects.ts +6 -4
  291. package/src/audit/checks/robots.ts +58 -2
  292. package/src/audit/checks/sitemap.ts +2 -3
  293. package/src/audit/redirects.ts +46 -15
  294. package/src/audit/run.ts +5 -0
  295. package/src/audit/types.ts +4 -1
  296. package/src/audit/url.ts +14 -5
  297. package/src/blume-modules.d.ts +12 -1
  298. package/src/captcha/client.ts +152 -0
  299. package/src/captcha/hcaptcha.ts +36 -0
  300. package/src/captcha/index.ts +28 -0
  301. package/src/captcha/schema.ts +32 -0
  302. package/src/captcha/turnstile.ts +38 -0
  303. package/src/captcha/verify.ts +71 -0
  304. package/src/cli/args.ts +51 -0
  305. package/src/cli/build-failure.ts +23 -0
  306. package/src/cli/command-meta.ts +5 -0
  307. package/src/cli/commands/audit.ts +10 -1
  308. package/src/cli/commands/dev.ts +18 -2
  309. package/src/cli/commands/doctor.ts +67 -28
  310. package/src/cli/commands/eval.ts +36 -4
  311. package/src/cli/commands/init.ts +6 -4
  312. package/src/cli/commands/skill.ts +96 -0
  313. package/src/cli/commands/validate.ts +10 -1
  314. package/src/cli/index.ts +5 -0
  315. package/src/cli/init/scaffold.ts +3 -3
  316. package/src/cli/prepare.ts +6 -3
  317. package/src/cli/required-secrets.ts +72 -30
  318. package/src/components/BlumePage.astro +2 -0
  319. package/src/components/content/ApiEndpoint.astro +18 -0
  320. package/src/components/content/ApiField.astro +79 -0
  321. package/src/components/content/ApiPlayground.astro +66 -0
  322. package/src/components/content/ApiRail.astro +19 -0
  323. package/src/components/content/Color.astro +1 -0
  324. package/src/components/content/FileTree.astro +1 -0
  325. package/src/components/content/GithubInfo.astro +1 -0
  326. package/src/components/content/Panel.astro +1 -0
  327. package/src/components/content/ParamField.astro +25 -0
  328. package/src/components/content/RequestExample.astro +17 -0
  329. package/src/components/content/ResponseExample.astro +17 -0
  330. package/src/components/content/ResponseField.astro +32 -0
  331. package/src/components/content/Step.astro +1 -0
  332. package/src/components/content/Tile.astro +1 -0
  333. package/src/components/content/Tooltip.astro +1 -0
  334. package/src/components/content/Tree.astro +1 -0
  335. package/src/components/content/TypeTable.astro +4 -1
  336. package/src/components/content/View.astro +21 -0
  337. package/src/components/content/ViewSwitcher.astro +168 -0
  338. package/src/components/content/api-field.ts +103 -0
  339. package/src/components/content/api-page.ts +248 -0
  340. package/src/components/content/mermaid-element.ts +15 -1
  341. package/src/components/content/view-switcher.ts +71 -0
  342. package/src/components/index.ts +2 -0
  343. package/src/components/islands/Assistant.astro +10 -1
  344. package/src/components/islands/assistant.tsx +104 -14
  345. package/src/components/islands/code-question.ts +62 -0
  346. package/src/components/islands/hooks.ts +136 -21
  347. package/src/components/islands/support-link.ts +68 -0
  348. package/src/components/layout/Analytics.astro +11 -2
  349. package/src/components/layout/Banner.astro +14 -10
  350. package/src/components/layout/Breadcrumbs.astro +6 -2
  351. package/src/components/layout/ConsentBanner.astro +62 -0
  352. package/src/components/layout/ConsentHead.astro +38 -0
  353. package/src/components/layout/Directory.astro +94 -0
  354. package/src/components/layout/DrawerSwitchers.astro +51 -0
  355. package/src/components/layout/Empty.astro +2 -2
  356. package/src/components/layout/Header.astro +122 -80
  357. package/src/components/layout/LanguageSwitcher.astro +48 -15
  358. package/src/components/layout/NarrationPlayer.astro +1085 -0
  359. package/src/components/layout/NavSelector.astro +33 -7
  360. package/src/components/layout/PageActions.astro +139 -24
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +71 -3
  363. package/src/components/layout/ReferenceLayout.astro +17 -6
  364. package/src/components/layout/RelatedPages.astro +42 -0
  365. package/src/components/layout/RootLayout.astro +318 -47
  366. package/src/components/layout/Search.astro +47 -19
  367. package/src/components/layout/SiteFooter.astro +124 -0
  368. package/src/components/layout/SiteMetatags.astro +16 -0
  369. package/src/components/layout/TableOfContents.astro +24 -17
  370. package/src/components/layout/consent/native.ts +79 -0
  371. package/src/components/layout/consent/types.ts +37 -0
  372. package/src/components/layout/footer-links.ts +53 -0
  373. package/src/components/layout/footer-socials.ts +87 -0
  374. package/src/components/layout/head-scripts.ts +6 -1
  375. package/src/components/layout/locale-redirect.ts +107 -0
  376. package/src/components/layout/nav-utils.ts +83 -0
  377. package/src/components/layout/page-feedback.ts +96 -0
  378. package/src/components/layout/search/algolia.ts +10 -1
  379. package/src/components/layout/search/analytics.ts +126 -0
  380. package/src/components/layout/search/endpoint.ts +48 -5
  381. package/src/components/layout/search/flexsearch.ts +17 -5
  382. package/src/components/layout/search/orama-cloud.ts +26 -14
  383. package/src/components/layout/search/pagefind.ts +114 -5
  384. package/src/components/layout/search/types.ts +32 -0
  385. package/src/components/layout/search/typesense.ts +19 -3
  386. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  387. package/src/components/openapi/Authorization.astro +10 -0
  388. package/src/components/openapi/GraphqlOperation.astro +5 -0
  389. package/src/components/openapi/Operation.astro +184 -13
  390. package/src/components/openapi/ParametersTable.astro +8 -4
  391. package/src/components/openapi/Playground.astro +7 -1
  392. package/src/components/openapi/RequestBody.astro +24 -5
  393. package/src/components/openapi/RequestPanel.astro +59 -7
  394. package/src/components/openapi/Responses.astro +7 -4
  395. package/src/components/openapi/SchemaTable.astro +15 -3
  396. package/src/components/openapi/async-snippets.ts +65 -9
  397. package/src/components/openapi/async.ts +123 -29
  398. package/src/components/openapi/code-samples.ts +97 -0
  399. package/src/components/openapi/graphql-helpers.ts +35 -12
  400. package/src/components/openapi/helpers.ts +36 -2
  401. package/src/components/openapi/message-model.ts +54 -11
  402. package/src/components/openapi/message.ts +14 -4
  403. package/src/components/openapi/operation-model.ts +16 -7
  404. package/src/components/openapi/playground-client.ts +24 -5
  405. package/src/components/openapi/playground-schema.ts +1 -1
  406. package/src/components/openapi/schema-tree.ts +44 -14
  407. package/src/components/openapi/security.ts +16 -3
  408. package/src/components/openapi/snippets.ts +873 -10
  409. package/src/components/openapi/validate-json.ts +4 -3
  410. package/src/components/props.ts +6 -0
  411. package/src/consent/client.ts +95 -0
  412. package/src/consent/clients.ts +19 -0
  413. package/src/consent/ethyca.ts +71 -0
  414. package/src/consent/head.ts +24 -0
  415. package/src/consent/index.ts +37 -0
  416. package/src/consent/init.ts +29 -0
  417. package/src/consent/native.ts +36 -0
  418. package/src/consent/osano.ts +59 -0
  419. package/src/consent/schema.ts +30 -0
  420. package/src/core/builtin-tags.ts +5 -0
  421. package/src/core/changelog-index.ts +31 -1
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/component-diagnostics.ts +53 -0
  424. package/src/core/component-overrides.ts +32 -0
  425. package/src/core/config-input.ts +225 -23
  426. package/src/core/data.ts +46 -4
  427. package/src/core/diagnostics.ts +8 -2
  428. package/src/core/footer.ts +26 -0
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/i18n-ui.ts +84 -2
  431. package/src/core/includes.ts +21 -4
  432. package/src/core/links.ts +63 -19
  433. package/src/core/localizable.ts +26 -0
  434. package/src/core/navigation.ts +43 -12
  435. package/src/core/page-modes.ts +56 -0
  436. package/src/core/probe.ts +26 -6
  437. package/src/core/project-graph.ts +163 -16
  438. package/src/core/redirect-patterns.ts +520 -0
  439. package/src/core/related.ts +131 -0
  440. package/src/core/schema.ts +339 -66
  441. package/src/core/sources/cache.ts +6 -0
  442. package/src/core/sources/contentful-rich-text.ts +29 -6
  443. package/src/core/sources/contentful.ts +135 -25
  444. package/src/core/sources/filesystem.ts +26 -4
  445. package/src/core/sources/github-releases.ts +12 -1
  446. package/src/core/sources/lexical.ts +15 -10
  447. package/src/core/sources/mdx-remote.ts +62 -10
  448. package/src/core/sources/normalize.ts +228 -41
  449. package/src/core/sources/notion.ts +7 -1
  450. package/src/core/sources/obsidian.ts +16 -3
  451. package/src/core/sources/payload.ts +38 -15
  452. package/src/core/sources/read.ts +8 -3
  453. package/src/core/sources/remote.ts +72 -15
  454. package/src/core/sources/resolve.ts +11 -1
  455. package/src/core/sources/sanity.ts +43 -25
  456. package/src/core/sources/strapi.ts +2 -2
  457. package/src/core/types.ts +19 -1
  458. package/src/core/ui-packs/ar.ts +44 -0
  459. package/src/core/ui-packs/bg.ts +46 -0
  460. package/src/core/ui-packs/bn.ts +44 -0
  461. package/src/core/ui-packs/ca.ts +47 -0
  462. package/src/core/ui-packs/cs.ts +45 -0
  463. package/src/core/ui-packs/da.ts +46 -0
  464. package/src/core/ui-packs/de.ts +47 -0
  465. package/src/core/ui-packs/el.ts +46 -0
  466. package/src/core/ui-packs/es.ts +47 -0
  467. package/src/core/ui-packs/fa.ts +46 -0
  468. package/src/core/ui-packs/fi.ts +46 -0
  469. package/src/core/ui-packs/fr.ts +47 -0
  470. package/src/core/ui-packs/he.ts +44 -0
  471. package/src/core/ui-packs/hi.ts +45 -0
  472. package/src/core/ui-packs/hr.ts +46 -0
  473. package/src/core/ui-packs/hu.ts +45 -0
  474. package/src/core/ui-packs/id.ts +46 -0
  475. package/src/core/ui-packs/it.ts +46 -0
  476. package/src/core/ui-packs/ja.ts +46 -0
  477. package/src/core/ui-packs/ko.ts +45 -0
  478. package/src/core/ui-packs/nl.ts +47 -0
  479. package/src/core/ui-packs/no.ts +45 -0
  480. package/src/core/ui-packs/pl.ts +46 -0
  481. package/src/core/ui-packs/pt-br.ts +47 -0
  482. package/src/core/ui-packs/pt.ts +46 -0
  483. package/src/core/ui-packs/ro.ts +46 -0
  484. package/src/core/ui-packs/ru.ts +46 -0
  485. package/src/core/ui-packs/sk.ts +45 -0
  486. package/src/core/ui-packs/sr.ts +46 -0
  487. package/src/core/ui-packs/sv.ts +45 -0
  488. package/src/core/ui-packs/th.ts +44 -0
  489. package/src/core/ui-packs/tr.ts +46 -0
  490. package/src/core/ui-packs/uk.ts +46 -0
  491. package/src/core/ui-packs/vi.ts +46 -0
  492. package/src/core/ui-packs/zh-tw.ts +44 -0
  493. package/src/core/ui-packs/zh.ts +44 -0
  494. package/src/core/variables.ts +103 -0
  495. package/src/deploy/adapter-output.ts +7 -2
  496. package/src/deploy/adapters/cloudflare.ts +7 -5
  497. package/src/deploy/adapters/node.ts +47 -9
  498. package/src/deploy/adapters/types.ts +7 -3
  499. package/src/deploy/artifacts.ts +79 -9
  500. package/src/deploy/cloudflare-negotiation.ts +35 -5
  501. package/src/deploy/headers.ts +2 -1
  502. package/src/deploy/node-headers.ts +63 -9
  503. package/src/deploy/platforms/cloudflare.ts +45 -9
  504. package/src/deploy/platforms/netlify.ts +35 -2
  505. package/src/deploy/platforms/node.ts +12 -2
  506. package/src/deploy/platforms/types.ts +21 -7
  507. package/src/deploy/platforms/vercel.ts +149 -17
  508. package/src/deploy/redirects.ts +151 -24
  509. package/src/deploy/rss.ts +3 -1
  510. package/src/deploy/sitemap.ts +39 -5
  511. package/src/deploy/vercel-negotiation.ts +326 -102
  512. package/src/eval/agents.ts +18 -1
  513. package/src/eval/prompts.ts +6 -3
  514. package/src/eval/report.ts +34 -13
  515. package/src/eval/run.ts +5 -0
  516. package/src/markdown/api-rail.ts +259 -0
  517. package/src/markdown/code-title.ts +35 -9
  518. package/src/markdown/features.ts +3 -2
  519. package/src/markdown/fence-meta.ts +2 -0
  520. package/src/markdown/include.ts +11 -1
  521. package/src/markdown/index.ts +28 -5
  522. package/src/markdown/math.ts +4 -4
  523. package/src/markdown/relative-links.ts +3 -2
  524. package/src/markdown/variables.ts +56 -0
  525. package/src/markdown/views.ts +82 -0
  526. package/src/narration/build.ts +412 -0
  527. package/src/narration/player.ts +249 -0
  528. package/src/narration/provider.ts +29 -0
  529. package/src/narration/script.ts +592 -0
  530. package/src/og/card.ts +7 -6
  531. package/src/openapi/checks.ts +273 -0
  532. package/src/openapi/graphql-build.ts +21 -0
  533. package/src/openapi/graphql.ts +11 -0
  534. package/src/openapi/model.ts +72 -27
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +182 -4
  537. package/src/openapi/references.ts +64 -8
  538. package/src/openapi/render-mdx.ts +5 -1
  539. package/src/openapi/scalar.ts +40 -6
  540. package/src/openapi/source.ts +40 -15
  541. package/src/ratelimit/cloudflare.ts +54 -0
  542. package/src/ratelimit/index.ts +30 -0
  543. package/src/ratelimit/memory.ts +45 -0
  544. package/src/ratelimit/runtime.ts +247 -0
  545. package/src/ratelimit/schema.ts +28 -0
  546. package/src/ratelimit/upstash.ts +60 -0
  547. package/src/ratelimit/wrangler.ts +70 -0
  548. package/src/reference/asyncapi.ts +2 -2
  549. package/src/reference/graphql.ts +20 -3
  550. package/src/reference/index.ts +1 -0
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +80 -8
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +23 -5
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/locale-scope.ts +30 -0
  557. package/src/search/adapters/typesense.ts +14 -4
  558. package/src/search/build.ts +21 -3
  559. package/src/search/documents.ts +26 -2
  560. package/src/search/orama-index.ts +191 -34
  561. package/src/search/pagefind-ranking.ts +39 -0
  562. package/src/search/source-pages.ts +29 -0
  563. package/src/search/sync/algolia.ts +111 -10
  564. package/src/search/sync/index.ts +52 -13
  565. package/src/search/sync/orama-cloud.ts +3 -0
  566. package/src/search/sync/typesense.ts +76 -26
  567. package/src/seo/metatags.ts +67 -0
  568. package/src/sources/contentful.ts +12 -0
  569. package/src/sources/github-releases.ts +7 -1
  570. package/src/sources/shared.ts +3 -0
  571. package/src/theme/chrome-icons.ts +1 -0
  572. package/src/theme/color.ts +500 -0
  573. package/src/theme/entry.ts +85 -6
  574. package/src/theme/palette.ts +121 -45
  575. package/src/translate/prompts.ts +1 -0
  576. package/src/translate/validate.ts +31 -3
  577. package/src/upgrade/upgrade.ts +4 -171
  578. package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
  579. package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
  580. package/dist/cli/chunk-35d4wj9f.js.map +0 -15
  581. package/dist/cli/chunk-364znk6q.js +0 -75
  582. package/dist/cli/chunk-364znk6q.js.map +0 -11
  583. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  584. package/dist/cli/chunk-8ktnccpt.js.map +0 -10
  585. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  586. package/dist/cli/chunk-b07cmahc.js +0 -145
  587. package/dist/cli/chunk-b07cmahc.js.map +0 -11
  588. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  589. package/dist/cli/chunk-e04dxsz1.js +0 -39
  590. package/dist/cli/chunk-e04dxsz1.js.map +0 -10
  591. package/dist/cli/chunk-j85vccga.js.map +0 -36
  592. package/dist/cli/chunk-p3v96n38.js.map +0 -10
  593. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  594. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  595. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  596. package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
  597. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  598. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  599. package/docs/content/sources.mdx +0 -334
  600. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
  601. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
  602. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
  603. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
  604. /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
  605. /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
  606. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
  607. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
@@ -20,8 +20,31 @@ const buildErrorSchema = z.looseObject({
20
20
  .optional(),
21
21
  });
22
22
 
23
+ /**
24
+ * The diagnostic a `BlumeError` thrown inside the build carries, such as a
25
+ * failed hosted search sync in `astro:build:done`. The hook runs Blume's
26
+ * source rather than the CLI bundle, so its error is another copy of the class
27
+ * that the command's `instanceof BlumeError` doesn't recognize.
28
+ */
29
+ const blumeErrorSchema = z.looseObject({
30
+ diagnostic: z.object({
31
+ code: z.string(),
32
+ column: z.number().optional(),
33
+ docsUrl: z.string().optional(),
34
+ file: z.string().optional(),
35
+ line: z.number().optional(),
36
+ message: z.string(),
37
+ severity: z.enum(["error", "info", "warning"]),
38
+ suggestion: z.string().optional(),
39
+ }),
40
+ });
41
+
23
42
  /** One failure as a diagnostic at the file (and position) it names. */
24
43
  const failureDiagnostic = (error: Error): Diagnostic => {
44
+ const raised = blumeErrorSchema.safeParse(error);
45
+ if (raised.success) {
46
+ return raised.data.diagnostic;
47
+ }
25
48
  const parsed = buildErrorSchema.safeParse(error);
26
49
  const { id, loc } = parsed.success ? parsed.data : {};
27
50
  // A named error (`MDXError`, `CompilerError`) says which step failed.
@@ -62,6 +62,11 @@ export const commandMeta = {
62
62
  description: "Preview the last production build.",
63
63
  name: "preview",
64
64
  },
65
+ skill: {
66
+ description:
67
+ "Write the docs site's agent skill with a coding agent, in place of the generated one.",
68
+ name: "skill",
69
+ },
65
70
  sync: {
66
71
  description: "Re-fetch remote content sources and regenerate the runtime.",
67
72
  name: "sync",
@@ -15,6 +15,7 @@ import { isHttpUrl } from "../../audit/url.ts";
15
15
  import { BlumeError } from "../../core/diagnostics.ts";
16
16
  import { scanProject } from "../../core/project-graph.ts";
17
17
  import type { DiagnosticSeverity } from "../../core/types.ts";
18
+ import { parseIgnoreFlag } from "../args.ts";
18
19
  import { commandMeta } from "../command-meta.ts";
19
20
  import { reportInternalError } from "../internal-error.ts";
20
21
  import { flushStdout, logger, reportDiagnostics } from "../log.ts";
@@ -103,6 +104,12 @@ export const auditCommand = defineCommand({
103
104
  "Exit non-zero at this severity or above: error | warning | info. Defaults to error.",
104
105
  type: "string",
105
106
  },
107
+ ignore: {
108
+ description:
109
+ 'With --external, skip outbound links whose URL matches this glob (e.g. "https://example.com/**"). Repeat it for more; internal links are always checked.',
110
+ type: "string",
111
+ valueHint: "glob",
112
+ },
106
113
  json: {
107
114
  description: "Emit the report as JSON on stdout (for CI/editors).",
108
115
  type: "boolean",
@@ -134,7 +141,7 @@ export const auditCommand = defineCommand({
134
141
  },
135
142
  },
136
143
  meta: commandMeta.audit,
137
- async run({ args }) {
144
+ async run({ args, rawArgs }) {
138
145
  if (args["list-checks"]) {
139
146
  process.stdout.write(formatCatalog());
140
147
  return;
@@ -172,6 +179,7 @@ export const auditCommand = defineCommand({
172
179
  const only = splitTerms(args.only);
173
180
  const skip = splitTerms(args.skip);
174
181
  rejectUnknownTerms([...only, ...skip]);
182
+ const ignore = parseIgnoreFlag(rawArgs);
175
183
  let result: AuditResult;
176
184
  try {
177
185
  // `scanProject`, not `prepareProject`: the audit reads the *existing*
@@ -180,6 +188,7 @@ export const auditCommand = defineCommand({
180
188
  const project = await scanProject(root, { mode: "build" });
181
189
  result = await runAudit({
182
190
  external: args.external,
191
+ ignore,
183
192
  only,
184
193
  origin: args.url,
185
194
  project,
@@ -11,7 +11,9 @@ import {
11
11
  import { BlumeError } from "../../core/diagnostics.ts";
12
12
  import { scanProject } from "../../core/project-graph.ts";
13
13
  import { resolveRuntimeDir } from "../../core/project.ts";
14
+ import { referenceSpecFiles } from "../../openapi/references.ts";
14
15
  import { parsePort } from "../args.ts";
16
+ import { astroBuildDiagnostics } from "../build-failure.ts";
15
17
  import { commandMeta } from "../command-meta.ts";
16
18
  import {
17
19
  acquireDevLock,
@@ -140,7 +142,19 @@ export const devCommand = defineCommand({
140
142
  server: { host: normalizeHost(args.host), open, port: listenPort },
141
143
  });
142
144
 
143
- let server = await createServer(explicitPort, args.open ?? false);
145
+ let server: Awaited<ReturnType<typeof createServer>>;
146
+ try {
147
+ server = await createServer(explicitPort, args.open ?? false);
148
+ } catch (error) {
149
+ // Astro's first content sync rejects on a page it can't load (front
150
+ // matter that isn't valid YAML): report it at the file it names, as
151
+ // `blume build` does, not as an internal error blaming Blume.
152
+ if (error instanceof BlumeError || !(error instanceof Error)) {
153
+ throw error;
154
+ }
155
+ reportDiagnostics(astroBuildDiagnostics(error), root);
156
+ process.exit(1);
157
+ }
144
158
 
145
159
  // Vite bumps to the next free port when the default is taken, so record
146
160
  // the port the server actually bound — the lock's URL is what a refused
@@ -229,7 +243,8 @@ export const devCommand = defineCommand({
229
243
 
230
244
  // Content is watched per source (filesystem uses fs.watch; remote sources
231
245
  // are frozen for the session). The remaining project inputs — user pages,
232
- // config, theme, and component overrides — are watched directly.
246
+ // config, theme, component overrides, and the local specs and overlays API
247
+ // references read — are watched directly.
233
248
  const dirTargets = [project.context.pagesRoot].filter(
234
249
  (target) => target !== null
235
250
  );
@@ -237,6 +252,7 @@ export const devCommand = defineCommand({
237
252
  project.context.configFile,
238
253
  project.context.themeFile,
239
254
  project.context.componentsFile,
255
+ ...referenceSpecFiles(project.config, root),
240
256
  ].filter((target) => target !== null);
241
257
 
242
258
  // chokidar handles what raw fs.watch made us hand-roll: recursive
@@ -5,15 +5,20 @@ import { defineCommand } from "citty";
5
5
  import { join } from "pathe";
6
6
  import { satisfies } from "semver";
7
7
 
8
+ import { discoverExamples } from "../../astro/examples.ts";
8
9
  import { discoverIslands } from "../../astro/islands.ts";
10
+ import { discoverPages, navTargetRoutes } from "../../astro/pages.ts";
9
11
  import { missingDependencyDiagnostic } from "../../astro/runtime-deps.ts";
12
+ import { missingExampleDiagnostics } from "../../core/component-diagnostics.ts";
10
13
  import {
11
14
  analyzeComponentOverrides,
12
15
  ComponentOverridesError,
13
16
  } from "../../core/component-overrides.ts";
14
17
  import { BlumeError } from "../../core/diagnostics.ts";
18
+ import { validateNavTargets } from "../../core/nav-diagnostics.ts";
15
19
  import { packageRoot } from "../../core/package-root.ts";
16
20
  import { scanProject } from "../../core/project-graph.ts";
21
+ import type { ResolvedConfig } from "../../core/schema.ts";
17
22
  import { serverFeatures } from "../../core/server-features.ts";
18
23
  import type { Diagnostic } from "../../core/types.ts";
19
24
  import { unregisteredSnapshotDiagnostics } from "../../core/version-cut.ts";
@@ -34,23 +39,24 @@ const FALLBACK_NODE_RANGE = ">=22.12.0";
34
39
  * Plan `components.ts` the way `blume dev`/`build` do, reporting each override
35
40
  * that can't be planned — a non-static form, or an import of a file that
36
41
  * doesn't exist — with its own line. The scan alone never reads the file, so
37
- * without this doctor would pass a project whose build fails.
42
+ * without this doctor would pass a project whose build fails. Also returns
43
+ * the MDX tags the file overrides.
38
44
  */
39
45
  const componentsDiagnostics = async (
40
46
  componentsFile: string | null
41
- ): Promise<Diagnostic[]> => {
47
+ ): Promise<{ issues: Diagnostic[]; mdx: string[] }> => {
42
48
  if (!componentsFile) {
43
- return [];
49
+ return { issues: [], mdx: [] };
44
50
  }
45
51
  try {
46
- analyzeComponentOverrides(
52
+ const { mdx } = analyzeComponentOverrides(
47
53
  await readFile(componentsFile, "utf-8"),
48
54
  componentsFile
49
55
  );
50
- return [];
56
+ return { issues: [], mdx: mdx.map((entry) => entry.key) };
51
57
  } catch (error) {
52
58
  if (error instanceof ComponentOverridesError) {
53
- return error.issues;
59
+ return { issues: error.issues, mdx: [] };
54
60
  }
55
61
  throw error;
56
62
  }
@@ -71,6 +77,25 @@ const supportedNodeRange = (): string => {
71
77
  }
72
78
  };
73
79
 
80
+ /** The adapters a project runs, one line per kind, for the doctor summary. */
81
+ const integrationSummary = (config: ResolvedConfig): string[] => {
82
+ const { assistant } = config.ai;
83
+ let assistantSummary = "off";
84
+ if (assistant?.enabled) {
85
+ assistantSummary = assistant.endpoint
86
+ ? "external endpoint"
87
+ : assistant.provider.kind;
88
+ }
89
+ return [
90
+ `References: ${config.reference.map((adapter) => adapter.kind).join(", ") || "none"}`,
91
+ `Analytics: ${config.analytics.map((adapter) => adapter.kind).join(", ") || "none"}`,
92
+ `Consent: ${config.consent?.kind ?? "none"}`,
93
+ `Rate limiting: ${config.rateLimit?.kind ?? "off"}`,
94
+ `Sources: ${config.content.sources.map((source) => source.kind).join(", ")}`,
95
+ `Assistant: ${assistantSummary}`,
96
+ ];
97
+ };
98
+
74
99
  export const doctorCommand = defineCommand({
75
100
  args: {
76
101
  json: {
@@ -97,15 +122,33 @@ export const doctorCommand = defineCommand({
97
122
  loadEnvFiles(root);
98
123
 
99
124
  try {
100
- const project = await scanProject(root, { mode: "build" });
125
+ const project = await scanProject(root, {
126
+ // A missing secret fails at the first request. Checked before the
127
+ // sources fetch, so it's still reported when one of them fails the
128
+ // scan for want of it.
129
+ beforeSources: (config) => {
130
+ diagnostics.push(...checkRequiredSecrets(config));
131
+ },
132
+ mode: "build",
133
+ });
134
+ // Tabs, selector items, and featured links can point at custom pages
135
+ // and generated routes too, so they're checked against every route
136
+ // the site serves, as `blume dev`/`build` check them.
137
+ const userPages = project.context.pagesRoot
138
+ ? await discoverPages(project.context.pagesRoot)
139
+ : [];
101
140
  diagnostics.push(
102
141
  ...project.diagnostics,
103
- ...unregisteredSnapshotDiagnostics(project)
142
+ ...unregisteredSnapshotDiagnostics(project),
143
+ ...validateNavTargets(
144
+ project.graph.navigation,
145
+ navTargetRoutes(project, userPages)
146
+ )
104
147
  );
105
148
 
106
149
  const { config } = project;
107
- // The packages and secrets a build would need: an adapter's missing SDK
108
- // fails the build, and a missing secret fails at the first request.
150
+ // The packages a build would need: an adapter's missing SDK fails the
151
+ // build.
109
152
  const { islands } = await discoverIslands(root);
110
153
  const dependencies = await missingDependencyDiagnostic(
111
154
  config,
@@ -116,9 +159,19 @@ export const doctorCommand = defineCommand({
116
159
  if (dependencies) {
117
160
  diagnostics.push(dependencies);
118
161
  }
162
+ const overrides = await componentsDiagnostics(
163
+ project.context.componentsFile
164
+ );
119
165
  diagnostics.push(
120
- ...(await componentsDiagnostics(project.context.componentsFile)),
121
- ...checkRequiredSecrets(config)
166
+ ...overrides.issues,
167
+ // A `<Component path>` naming no example renders a "No example
168
+ // found" box, which dev and build warn about too.
169
+ ...missingExampleDiagnostics(
170
+ project.graph.pages,
171
+ await discoverExamples(root, config.examples.source),
172
+ root,
173
+ new Set([...islands.map((island) => island.name), ...overrides.mdx])
174
+ )
122
175
  );
123
176
  const features = serverFeatures(config);
124
177
  if (
@@ -144,23 +197,9 @@ export const doctorCommand = defineCommand({
144
197
  logger.info(`Output: ${config.deployment.options.output}`);
145
198
  logger.info(`Adapter: ${config.deployment.kind}`);
146
199
  logger.info(`Search: ${config.search.provider.kind}`);
147
- logger.info(
148
- `References: ${config.reference.map((adapter) => adapter.kind).join(", ") || "none"}`
149
- );
150
- logger.info(
151
- `Analytics: ${config.analytics.map((adapter) => adapter.kind).join(", ") || "none"}`
152
- );
153
- logger.info(
154
- `Sources: ${config.content.sources.map((source) => source.kind).join(", ")}`
155
- );
156
- const { assistant } = config.ai;
157
- let assistantSummary = "off";
158
- if (assistant?.enabled) {
159
- assistantSummary = assistant.endpoint
160
- ? "external endpoint"
161
- : assistant.provider.kind;
200
+ for (const line of integrationSummary(config)) {
201
+ logger.info(line);
162
202
  }
163
- logger.info(`Assistant: ${assistantSummary}`);
164
203
  }
165
204
  } catch (error) {
166
205
  if (error instanceof BlumeError) {
@@ -104,12 +104,37 @@ const parseFlags = (args: EvalFlags) => {
104
104
  return { agent: args.agent, threshold, timeoutS };
105
105
  };
106
106
 
107
+ /** A shell word for the rerun command, single-quoted only when it has to be. */
108
+ const shellWord = (value: string): string =>
109
+ /^[\w./:@%+=,-]+$/u.test(value)
110
+ ? value
111
+ : `'${value.replaceAll("'", String.raw`'\''`)}'`;
112
+
113
+ /**
114
+ * The command a `--fix` agent reruns to verify its edits. It carries the run's
115
+ * own flags: a bare `blume eval` would fall back to Codex and `evals.yaml`,
116
+ * grading a different file with a different agent than the run it's fixing.
117
+ */
118
+ const rerunCommand = (
119
+ args: EvalFlags,
120
+ { agent, threshold, timeoutS }: ReturnType<typeof parseFlags>
121
+ ): string =>
122
+ [
123
+ `blume eval --agent ${agent}`,
124
+ args.file === DEFAULT_FILE ? "" : `--file ${shellWord(args.file)}`,
125
+ args.threshold === undefined ? "" : `--threshold ${threshold}`,
126
+ args.timeout === undefined ? "" : `--timeout ${timeoutS}`,
127
+ ]
128
+ .filter((part) => part !== "")
129
+ .join(" ");
130
+
107
131
  /** `blume eval --fix`: hand the failing report to the interactive agent. */
108
132
  const runFixHandoff = async (
109
133
  agent: AgentKind,
110
134
  result: EvalResult,
111
135
  root: string,
112
- threshold: number
136
+ threshold: number,
137
+ command: string
113
138
  ): Promise<void> => {
114
139
  const count = result.counts.fail + result.counts.error;
115
140
  if (count === 0) {
@@ -120,7 +145,7 @@ const runFixHandoff = async (
120
145
  process.stderr.write(
121
146
  ` Handing ${count} failed question${count === 1 ? "" : "s"} to ${cli.name}…\n\n`
122
147
  );
123
- const code = await launchAgentCode(agent, evalFixPrompt(report));
148
+ const code = await launchAgentCode(agent, evalFixPrompt(report, command));
124
149
  if (code !== 0) {
125
150
  process.exit(code);
126
151
  }
@@ -184,7 +209,8 @@ export const evalCommand = defineCommand({
184
209
  meta: commandMeta.eval,
185
210
  async run({ args }) {
186
211
  const root = process.cwd();
187
- const { agent, threshold, timeoutS } = parseFlags(args);
212
+ const flags = parseFlags(args);
213
+ const { agent, threshold, timeoutS } = flags;
188
214
  if (args.action === "init") {
189
215
  await runInit(agent, args.file);
190
216
  return;
@@ -259,7 +285,13 @@ export const evalCommand = defineCommand({
259
285
  if (args.fix) {
260
286
  // The gate is a CI concern; a handoff run succeeds when the agent
261
287
  // session does, not when the docs already passed.
262
- await runFixHandoff(agent, result, root, threshold);
288
+ await runFixHandoff(
289
+ agent,
290
+ result,
291
+ root,
292
+ threshold,
293
+ rerunCommand(args, flags)
294
+ );
263
295
  return;
264
296
  }
265
297
 
@@ -272,14 +272,16 @@ export const initCommand = defineCommand({
272
272
  // which `init` leaves alone; say what it still needs before installing.
273
273
  const note = workspaceNote(root, answers, env);
274
274
 
275
- // Keep installed dependencies, Blume's generated runtime (`.blume/`), and
276
- // build output (`dist/`) out of version control. Idempotent: creates
277
- // `.gitignore` when absent and skips entries already present
278
- // (trailing-slash agnostic).
275
+ // Keep installed dependencies, Blume's generated runtime (`.blume/`),
276
+ // build output (`dist/`), and local secrets (`.env.local`, where every
277
+ // missing-secret warning says to put them) out of version control.
278
+ // Idempotent: creates `.gitignore` when absent and skips entries already
279
+ // present (trailing-slash agnostic).
279
280
  const ignored = await ensureGitignore(root, [
280
281
  "node_modules/",
281
282
  ".blume/",
282
283
  "dist/",
284
+ ".env.local",
283
285
  ]);
284
286
  if (ignored.length > 0) {
285
287
  sink.success(`Added ${ignored.join(", ")} to .gitignore`);
@@ -0,0 +1,96 @@
1
+ import { defineCommand } from "citty";
2
+ import { join } from "pathe";
3
+
4
+ import {
5
+ planSkill,
6
+ WRITE_SKILL_DIR,
7
+ writeSkillPrompt,
8
+ } from "../../ai/write-skill.ts";
9
+ import { AGENTS, launchInstalledAgent } from "../../audit/agent.ts";
10
+ import type { AgentKind } from "../../audit/agent.ts";
11
+ import { BlumeError } from "../../core/diagnostics.ts";
12
+ import { packageRoot } from "../../core/package-root.ts";
13
+ import { scanProject } from "../../core/project-graph.ts";
14
+ import { getBlumeVersion } from "../../core/version.ts";
15
+ import { commandMeta } from "../command-meta.ts";
16
+ import { reportInternalError } from "../internal-error.ts";
17
+ import { logger, reportDiagnostics } from "../log.ts";
18
+
19
+ /**
20
+ * `blume skill` — write the docs site's agent skill with a coding agent. Every
21
+ * build publishes a generated skill at `/skill.md` (a map of the docs); this
22
+ * opens Claude Code or Codex on the `blume-write-skill` skill bundled in this
23
+ * package to write the richer one, grounded in the docs, under the same name
24
+ * so it takes the generated one's place. The judgment stays in the skill; the
25
+ * command is the front door, like `blume migrate`. Status lines go straight to
26
+ * stderr: consola drops info-level lines in test and CI environments.
27
+ */
28
+ export const skillCommand = defineCommand({
29
+ args: {
30
+ claude: {
31
+ description: "Write the skill with Claude Code.",
32
+ type: "boolean",
33
+ },
34
+ codex: {
35
+ description: "Write the skill with Codex.",
36
+ type: "boolean",
37
+ },
38
+ },
39
+ meta: commandMeta.skill,
40
+ async run({ args }) {
41
+ const root = process.cwd();
42
+ // SAFETY: AGENTS is a closed record keyed by AgentKind, so its keys are
43
+ // exactly the agent kinds.
44
+ const agents = (Object.keys(AGENTS) as AgentKind[]).filter(
45
+ (kind) => args[kind]
46
+ );
47
+ if (agents.length > 1) {
48
+ logger.error("Pass at most one of --codex or --claude.");
49
+ process.exit(1);
50
+ }
51
+ const [agent] = agents;
52
+
53
+ try {
54
+ // A scan, not `prepareProject`: the agent reads the content tree and
55
+ // the runtime isn't touched, so a running dev server is left alone.
56
+ const plan = planSkill(await scanProject(root, { mode: "build" }));
57
+ const skillDir = join(packageRoot(), WRITE_SKILL_DIR);
58
+ process.stderr.write(
59
+ ` ${plan.exists ? "Updating" : "Writing"} the "${plan.name}" skill at ${plan.file}.\n`
60
+ );
61
+
62
+ // Without an agent flag the command is the pointer for any other agent:
63
+ // the bundled skill's path, and the install line that puts the skill
64
+ // where agents look for it.
65
+ if (!agent) {
66
+ process.stderr.write(
67
+ ` blume skill writes it with a coding agent: rerun with --codex or --claude.\n Using another agent? Point it at ${join(skillDir, "SKILL.md")},\n or install the skill where your agent looks for skills: npx skills add haydenbleasel/blume --skill blume-write-skill\n`
68
+ );
69
+ return;
70
+ }
71
+
72
+ const cli = AGENTS[agent];
73
+ process.stderr.write(` Handing it to ${cli.name}…\n\n`);
74
+ const code = await launchInstalledAgent(
75
+ cli.bin,
76
+ writeSkillPrompt(plan, { skillDir, version: getBlumeVersion() })
77
+ );
78
+ if (code === null) {
79
+ logger.error(
80
+ `${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`
81
+ );
82
+ process.exit(1);
83
+ }
84
+ if (code !== 0) {
85
+ process.exit(code);
86
+ }
87
+ } catch (error) {
88
+ if (error instanceof BlumeError) {
89
+ reportDiagnostics([error.diagnostic], root);
90
+ process.exit(1);
91
+ }
92
+ reportInternalError(error);
93
+ process.exit(1);
94
+ }
95
+ },
96
+ });
@@ -13,6 +13,7 @@ import { validateLinks } from "../../core/links.ts";
13
13
  import { buildManifest } from "../../core/manifest.ts";
14
14
  import { scanProject } from "../../core/project-graph.ts";
15
15
  import type { Diagnostic } from "../../core/types.ts";
16
+ import { parseIgnoreFlag } from "../args.ts";
16
17
  import { commandMeta } from "../command-meta.ts";
17
18
  import { reportInternalError } from "../internal-error.ts";
18
19
  import {
@@ -28,6 +29,12 @@ export const validateCommand = defineCommand({
28
29
  description: "Check external (HTTP) links over the network.",
29
30
  type: "boolean",
30
31
  },
32
+ ignore: {
33
+ description:
34
+ 'With --external, skip external links whose URL matches this glob (e.g. "https://example.com/**"). Repeat it for more; internal links are always checked.',
35
+ type: "string",
36
+ valueHint: "glob",
37
+ },
31
38
  json: {
32
39
  description: "Emit diagnostics as JSON on stdout (for CI/editors).",
33
40
  type: "boolean",
@@ -38,8 +45,9 @@ export const validateCommand = defineCommand({
38
45
  },
39
46
  },
40
47
  meta: commandMeta.validate,
41
- async run({ args }) {
48
+ async run({ args, rawArgs }) {
42
49
  const root = process.cwd();
50
+ const ignore = parseIgnoreFlag(rawArgs);
43
51
  const diagnostics: Diagnostic[] = [];
44
52
 
45
53
  try {
@@ -83,6 +91,7 @@ export const validateCommand = defineCommand({
83
91
  checkExternal: Boolean(args.external),
84
92
  extraRoutes,
85
93
  i18n: project.config.i18n,
94
+ ignore,
86
95
  publicDir: existsSync(publicDir) ? publicDir : null,
87
96
  redirects: project.config.redirects,
88
97
  }))
package/src/cli/index.ts CHANGED
@@ -81,6 +81,11 @@ const main = defineCommand({
81
81
  () => import("./commands/preview.ts"),
82
82
  "previewCommand"
83
83
  ),
84
+ skill: lazyCommand(
85
+ commandMeta.skill,
86
+ () => import("./commands/skill.ts"),
87
+ "skillCommand"
88
+ ),
84
89
  sync: lazyCommand(
85
90
  commandMeta.sync,
86
91
  () => import("./commands/sync.ts"),
@@ -679,7 +679,7 @@ const ESBUILD_APPROVAL = " allowBuilds:\n esbuild: true";
679
679
  * What the project still needs from a workspace it sits in, when `init` left
680
680
  * that workspace's config alone: a place in a pnpm workspace's `packages`
681
681
  * (pnpm skips a folder it doesn't list), pnpm's approval for esbuild's build
682
- * script (pnpm 10 and later fail the install without it), or Yarn Berry's
682
+ * script (pnpm 11 and later fail the install without it), or Yarn Berry's
683
683
  * `node_modules` linker. Undefined when the workspace already has it, or the
684
684
  * project doesn't sit in one.
685
685
  */
@@ -699,12 +699,12 @@ export const workspaceNote = (
699
699
  const entry = ` packages:\n - ${relative(workspace, root)}`;
700
700
  return approved
701
701
  ? `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing:\n\n${entry}`
702
- : `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing, and approve esbuild's build script, or pnpm 10 and later stop the install:\n\n${entry}\n${ESBUILD_APPROVAL}`;
702
+ : `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing, and approve esbuild's build script, or pnpm 11 and later stop the install:\n\n${entry}\n${ESBUILD_APPROVAL}`;
703
703
  }
704
704
  if (approved) {
705
705
  return;
706
706
  }
707
- return `This project joins the pnpm workspace at ${file}. Approve esbuild's build script there before installing, or pnpm 10 and later stop the install:\n\n${ESBUILD_APPROVAL}`;
707
+ return `This project joins the pnpm workspace at ${file}. Approve esbuild's build script there before installing, or pnpm 11 and later stop the install:\n\n${ESBUILD_APPROVAL}`;
708
708
  }
709
709
  if (answers.packageManager !== "yarn" || yarnMajor(env.userAgent) === 1) {
710
710
  return;
@@ -59,6 +59,12 @@ export const prepareProject = async (
59
59
  let project: BlumeProject;
60
60
  try {
61
61
  project = await scanProject(options.root, {
62
+ // Fail-fast on missing secrets: warn now, not at the first request, and
63
+ // before the sources fetch, so a source that can't authenticate on a
64
+ // fresh build still gets its variable named.
65
+ beforeSources: (config) => {
66
+ reportDiagnostics(checkRequiredSecrets(config), options.root);
67
+ },
62
68
  devServerUrl: options.devServerUrl,
63
69
  mode: options.mode,
64
70
  overrides: options.overrides,
@@ -138,8 +144,5 @@ export const prepareProject = async (
138
144
  logger.warn(warning);
139
145
  }
140
146
 
141
- // Fail-fast on missing runtime secrets: warn now, not at the first request.
142
- reportDiagnostics(checkRequiredSecrets(project.config), options.root);
143
-
144
147
  return project;
145
148
  };