blume 2.0.3 → 2.1.0

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 (455) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
  12. package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
  13. package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
  14. package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
  15. package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
  16. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  17. package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
  18. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
  19. package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
  20. package/dist/cli/{chunk-2z928egk.js → chunk-5wj2qwn2.js} +5 -5
  21. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  22. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  23. package/dist/cli/{chunk-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
  24. package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
  25. package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
  26. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
  27. package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
  28. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  29. package/dist/cli/{chunk-b07cmahc.js → chunk-amrb6hgh.js} +2 -2
  30. package/dist/cli/chunk-cm89scdv.js +443 -0
  31. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  32. package/dist/cli/{chunk-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
  33. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  34. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  35. package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
  36. package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
  37. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  38. package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
  39. package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
  40. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  41. package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
  42. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
  43. package/dist/cli/chunk-kwrg63tm.js +31 -0
  44. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  45. package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
  46. package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
  47. package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
  48. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
  49. package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
  50. package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
  51. package/dist/cli/{chunk-ey84smr6.js.map → chunk-py62fewh.js.map} +1 -1
  52. package/dist/cli/chunk-q3wxxq39.js +126 -0
  53. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  54. package/dist/cli/{chunk-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
  55. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  56. package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
  57. package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
  58. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  59. package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
  60. package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
  61. package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
  62. package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
  63. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
  64. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  65. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  66. package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
  67. package/dist/cli/chunk-zf748avv.js.map +37 -0
  68. package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
  69. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
  70. package/dist/cli/index.js +26 -20
  71. package/dist/cli/index.js.map +5 -5
  72. package/dist/types/ai/agent-surface.d.ts +8 -7
  73. package/dist/types/ai/ai-catalog.d.ts +7 -0
  74. package/dist/types/ai/ask-context.d.ts +22 -0
  75. package/dist/types/ai/ask.d.ts +271 -58
  76. package/dist/types/ai/index.d.ts +2 -2
  77. package/dist/types/ai/site-skill.d.ts +26 -0
  78. package/dist/types/ai/skills.d.ts +16 -0
  79. package/dist/types/astro/integration.d.ts +8 -0
  80. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  81. package/dist/types/captcha/index.d.ts +28 -0
  82. package/dist/types/captcha/schema.d.ts +29 -0
  83. package/dist/types/captcha/turnstile.d.ts +28 -0
  84. package/dist/types/components/content/api-field.d.ts +59 -0
  85. package/dist/types/components/content/api-page.d.ts +95 -0
  86. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  87. package/dist/types/components/openapi/helpers.d.ts +137 -0
  88. package/dist/types/components/openapi/security.d.ts +85 -0
  89. package/dist/types/consent/ethyca.d.ts +48 -0
  90. package/dist/types/consent/index.d.ts +37 -0
  91. package/dist/types/consent/native.d.ts +28 -0
  92. package/dist/types/consent/osano.d.ts +42 -0
  93. package/dist/types/consent/schema.d.ts +110 -0
  94. package/dist/types/core/config-input.d.ts +187 -20
  95. package/dist/types/core/data.d.ts +49 -4
  96. package/dist/types/core/footer.d.ts +8 -0
  97. package/dist/types/core/i18n-ui.d.ts +86 -0
  98. package/dist/types/core/includes.d.ts +8 -2
  99. package/dist/types/core/localizable.d.ts +7 -0
  100. package/dist/types/core/page-modes.d.ts +29 -0
  101. package/dist/types/core/redirect-patterns.d.ts +144 -0
  102. package/dist/types/core/related.d.ts +40 -0
  103. package/dist/types/core/schema.d.ts +551 -56
  104. package/dist/types/core/sources/read.d.ts +5 -0
  105. package/dist/types/core/types.d.ts +1 -1
  106. package/dist/types/core/variables.d.ts +42 -0
  107. package/dist/types/deploy/node-headers.d.ts +13 -2
  108. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  109. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  110. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  111. package/dist/types/deploy/redirects.d.ts +39 -11
  112. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  113. package/dist/types/markdown/code-title.d.ts +17 -0
  114. package/dist/types/markdown/features.d.ts +3 -2
  115. package/dist/types/narration/build.d.ts +70 -0
  116. package/dist/types/narration/provider.d.ts +26 -0
  117. package/dist/types/narration/script.d.ts +154 -0
  118. package/dist/types/openapi/model.d.ts +12 -6
  119. package/dist/types/openapi/overlay.d.ts +54 -0
  120. package/dist/types/openapi/parse.d.ts +13 -0
  121. package/dist/types/openapi/references.d.ts +14 -4
  122. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  123. package/dist/types/ratelimit/index.d.ts +27 -0
  124. package/dist/types/ratelimit/memory.d.ts +36 -0
  125. package/dist/types/ratelimit/schema.d.ts +111 -0
  126. package/dist/types/ratelimit/upstash.d.ts +21 -0
  127. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  128. package/dist/types/reference/asyncapi.d.ts +10 -10
  129. package/dist/types/reference/graphql.d.ts +13 -10
  130. package/dist/types/reference/openapi.d.ts +33 -12
  131. package/dist/types/reference/options.d.ts +45 -5
  132. package/dist/types/reference/scalar.d.ts +13 -0
  133. package/dist/types/reference/schema.d.ts +60 -36
  134. package/dist/types/search/documents.d.ts +12 -1
  135. package/dist/types/search/orama-index.d.ts +4 -0
  136. package/dist/types/seo/metatags.d.ts +20 -0
  137. package/docs/02-deployment.mdx +41 -5
  138. package/docs/03-upgrading.mdx +4 -4
  139. package/docs/04-migrating.mdx +1 -1
  140. package/docs/advanced/changelog.mdx +2 -2
  141. package/docs/advanced/skills.mdx +14 -0
  142. package/docs/cli/audit.mdx +21 -1
  143. package/docs/cli/index.mdx +1 -0
  144. package/docs/configuration/analytics.mdx +3 -1
  145. package/docs/configuration/assistant.mdx +188 -36
  146. package/docs/configuration/consent.mdx +100 -0
  147. package/docs/configuration/customization.mdx +4 -4
  148. package/docs/configuration/index.mdx +63 -5
  149. package/docs/configuration/meta.ts +3 -0
  150. package/docs/configuration/narration.mdx +119 -0
  151. package/docs/configuration/rate-limiting.mdx +69 -0
  152. package/docs/configuration/search.mdx +29 -1
  153. package/docs/configuration/theming.mdx +3 -1
  154. package/docs/content/components.mdx +106 -0
  155. package/docs/content/frontmatter.mdx +76 -0
  156. package/docs/content/i18n.mdx +38 -3
  157. package/docs/content/includes.mdx +16 -0
  158. package/docs/content/meta.mdx +1 -0
  159. package/docs/content/meta.ts +1 -0
  160. package/docs/content/navigation.mdx +28 -6
  161. package/docs/content/sources/contentful.mdx +32 -0
  162. package/docs/content/sources/custom.mdx +39 -0
  163. package/docs/content/sources/github-releases.mdx +31 -0
  164. package/docs/content/sources/index.mdx +91 -0
  165. package/docs/content/sources/meta.ts +16 -0
  166. package/docs/content/sources/notion.mdx +32 -0
  167. package/docs/content/sources/obsidian.mdx +37 -0
  168. package/docs/content/sources/payload.mdx +30 -0
  169. package/docs/content/sources/remote-mdx.mdx +40 -0
  170. package/docs/content/sources/sanity.mdx +29 -0
  171. package/docs/content/sources/strapi.mdx +31 -0
  172. package/docs/content/syntax.mdx +67 -2
  173. package/docs/content/variables.mdx +41 -0
  174. package/docs/discoverability/agent-discovery.mdx +29 -2
  175. package/docs/discoverability/index.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/metadata.mdx +17 -0
  178. package/docs/references/api-pages.mdx +74 -0
  179. package/docs/references/asyncapi.mdx +1 -1
  180. package/docs/references/graphql.mdx +2 -2
  181. package/docs/references/meta.ts +1 -1
  182. package/docs/references/openapi.mdx +102 -3
  183. package/docs/references/scalar.mdx +3 -1
  184. package/package.json +36 -1
  185. package/skills/blume/SKILL.md +3 -0
  186. package/skills/blume-migrate/SKILL.md +5 -5
  187. package/skills/blume-migrate/references/docusaurus.md +3 -3
  188. package/skills/blume-migrate/references/mintlify.md +37 -19
  189. package/skills/blume-migrate/references/nextra.md +2 -2
  190. package/skills/blume-migrate/references/starlight.md +2 -1
  191. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  192. package/skills/blume-write-skill/SKILL.md +82 -0
  193. package/src/ai/agent-readability.ts +2 -1
  194. package/src/ai/agent-surface.ts +9 -7
  195. package/src/ai/ai-catalog.ts +6 -3
  196. package/src/ai/ask-context.ts +73 -20
  197. package/src/ai/ask-data.ts +6 -0
  198. package/src/ai/ask-tools.ts +142 -0
  199. package/src/ai/ask.ts +354 -76
  200. package/src/ai/component-markdown.ts +75 -0
  201. package/src/ai/cors.ts +2 -0
  202. package/src/ai/index.ts +12 -0
  203. package/src/ai/llms.ts +6 -2
  204. package/src/ai/markdown.ts +2 -1
  205. package/src/ai/mcp/data.ts +6 -0
  206. package/src/ai/openapi-components.ts +9 -3
  207. package/src/ai/site-skill.ts +274 -0
  208. package/src/ai/skills.ts +35 -2
  209. package/src/ai/write-skill.ts +97 -0
  210. package/src/astro/generate.ts +132 -18
  211. package/src/astro/index.ts +1 -0
  212. package/src/astro/integration.ts +35 -0
  213. package/src/astro/module-types.ts +6 -0
  214. package/src/astro/templates.ts +283 -30
  215. package/src/astro/variables.ts +31 -0
  216. package/src/audit/catalog.ts +19 -0
  217. package/src/audit/checks/contrast.ts +183 -0
  218. package/src/audit/checks/i18n.ts +2 -3
  219. package/src/audit/checks/indexability.ts +2 -3
  220. package/src/audit/checks/links.ts +5 -12
  221. package/src/audit/checks/sitemap.ts +2 -3
  222. package/src/audit/redirects.ts +46 -15
  223. package/src/audit/run.ts +2 -0
  224. package/src/audit/types.ts +2 -1
  225. package/src/blume-modules.d.ts +7 -0
  226. package/src/captcha/client.ts +152 -0
  227. package/src/captcha/hcaptcha.ts +36 -0
  228. package/src/captcha/index.ts +28 -0
  229. package/src/captcha/schema.ts +32 -0
  230. package/src/captcha/turnstile.ts +38 -0
  231. package/src/captcha/verify.ts +71 -0
  232. package/src/cli/command-meta.ts +5 -0
  233. package/src/cli/commands/doctor.ts +22 -16
  234. package/src/cli/commands/skill.ts +96 -0
  235. package/src/cli/index.ts +5 -0
  236. package/src/cli/required-secrets.ts +71 -29
  237. package/src/components/BlumePage.astro +2 -0
  238. package/src/components/content/ApiEndpoint.astro +18 -0
  239. package/src/components/content/ApiField.astro +79 -0
  240. package/src/components/content/ApiPlayground.astro +62 -0
  241. package/src/components/content/ApiRail.astro +19 -0
  242. package/src/components/content/Color.astro +1 -0
  243. package/src/components/content/FileTree.astro +1 -0
  244. package/src/components/content/GithubInfo.astro +1 -0
  245. package/src/components/content/Panel.astro +1 -0
  246. package/src/components/content/ParamField.astro +25 -0
  247. package/src/components/content/RequestExample.astro +17 -0
  248. package/src/components/content/ResponseExample.astro +17 -0
  249. package/src/components/content/ResponseField.astro +32 -0
  250. package/src/components/content/Step.astro +1 -0
  251. package/src/components/content/Tile.astro +1 -0
  252. package/src/components/content/Tooltip.astro +1 -0
  253. package/src/components/content/Tree.astro +1 -0
  254. package/src/components/content/TypeTable.astro +4 -1
  255. package/src/components/content/View.astro +21 -0
  256. package/src/components/content/ViewSwitcher.astro +168 -0
  257. package/src/components/content/api-field.ts +100 -0
  258. package/src/components/content/api-page.ts +224 -0
  259. package/src/components/content/view-switcher.ts +71 -0
  260. package/src/components/index.ts +2 -0
  261. package/src/components/islands/Assistant.astro +10 -1
  262. package/src/components/islands/assistant.tsx +104 -14
  263. package/src/components/islands/code-question.ts +62 -0
  264. package/src/components/islands/hooks.ts +110 -21
  265. package/src/components/islands/support-link.ts +68 -0
  266. package/src/components/layout/Analytics.astro +11 -2
  267. package/src/components/layout/Banner.astro +14 -10
  268. package/src/components/layout/Breadcrumbs.astro +6 -2
  269. package/src/components/layout/ConsentBanner.astro +62 -0
  270. package/src/components/layout/ConsentHead.astro +38 -0
  271. package/src/components/layout/Directory.astro +94 -0
  272. package/src/components/layout/DrawerSwitchers.astro +51 -0
  273. package/src/components/layout/Empty.astro +2 -2
  274. package/src/components/layout/Header.astro +122 -80
  275. package/src/components/layout/LanguageSwitcher.astro +48 -15
  276. package/src/components/layout/NarrationPlayer.astro +1085 -0
  277. package/src/components/layout/NavSelector.astro +33 -7
  278. package/src/components/layout/PageFeedback.astro +54 -49
  279. package/src/components/layout/PageLayout.astro +71 -3
  280. package/src/components/layout/ReferenceLayout.astro +17 -6
  281. package/src/components/layout/RelatedPages.astro +42 -0
  282. package/src/components/layout/RootLayout.astro +308 -45
  283. package/src/components/layout/Search.astro +31 -17
  284. package/src/components/layout/SiteFooter.astro +124 -0
  285. package/src/components/layout/SiteMetatags.astro +16 -0
  286. package/src/components/layout/consent/native.ts +79 -0
  287. package/src/components/layout/consent/types.ts +37 -0
  288. package/src/components/layout/footer-links.ts +53 -0
  289. package/src/components/layout/footer-socials.ts +87 -0
  290. package/src/components/layout/locale-redirect.ts +107 -0
  291. package/src/components/layout/nav-utils.ts +83 -0
  292. package/src/components/layout/page-feedback.ts +96 -0
  293. package/src/components/layout/search/analytics.ts +98 -0
  294. package/src/components/layout/search/flexsearch.ts +17 -5
  295. package/src/components/layout/search/orama-cloud.ts +26 -14
  296. package/src/components/layout/search/types.ts +20 -0
  297. package/src/components/layout/search/typesense.ts +5 -1
  298. package/src/components/openapi/Operation.astro +161 -13
  299. package/src/components/openapi/RequestBody.astro +6 -4
  300. package/src/components/openapi/RequestPanel.astro +59 -7
  301. package/src/components/openapi/Responses.astro +6 -4
  302. package/src/components/openapi/async-snippets.ts +4 -4
  303. package/src/components/openapi/code-samples.ts +97 -0
  304. package/src/components/openapi/security.ts +6 -3
  305. package/src/components/openapi/snippets.ts +869 -8
  306. package/src/components/props.ts +6 -0
  307. package/src/consent/client.ts +95 -0
  308. package/src/consent/clients.ts +19 -0
  309. package/src/consent/ethyca.ts +71 -0
  310. package/src/consent/head.ts +24 -0
  311. package/src/consent/index.ts +37 -0
  312. package/src/consent/init.ts +29 -0
  313. package/src/consent/native.ts +36 -0
  314. package/src/consent/osano.ts +59 -0
  315. package/src/consent/schema.ts +30 -0
  316. package/src/core/builtin-tags.ts +5 -0
  317. package/src/core/client-address.ts +19 -0
  318. package/src/core/config-input.ts +194 -20
  319. package/src/core/data.ts +44 -4
  320. package/src/core/diagnostics.ts +3 -2
  321. package/src/core/footer.ts +26 -0
  322. package/src/core/graph.ts +39 -36
  323. package/src/core/i18n-ui.ts +82 -2
  324. package/src/core/includes.ts +20 -3
  325. package/src/core/links.ts +16 -2
  326. package/src/core/localizable.ts +26 -0
  327. package/src/core/navigation.ts +43 -12
  328. package/src/core/page-modes.ts +56 -0
  329. package/src/core/project-graph.ts +111 -5
  330. package/src/core/redirect-patterns.ts +491 -0
  331. package/src/core/related.ts +131 -0
  332. package/src/core/schema.ts +318 -64
  333. package/src/core/sources/normalize.ts +2 -1
  334. package/src/core/sources/read.ts +8 -3
  335. package/src/core/types.ts +1 -1
  336. package/src/core/ui-packs/ar.ts +43 -0
  337. package/src/core/ui-packs/bg.ts +45 -0
  338. package/src/core/ui-packs/bn.ts +43 -0
  339. package/src/core/ui-packs/ca.ts +46 -0
  340. package/src/core/ui-packs/cs.ts +44 -0
  341. package/src/core/ui-packs/da.ts +45 -0
  342. package/src/core/ui-packs/de.ts +46 -0
  343. package/src/core/ui-packs/el.ts +45 -0
  344. package/src/core/ui-packs/es.ts +46 -0
  345. package/src/core/ui-packs/fa.ts +45 -0
  346. package/src/core/ui-packs/fi.ts +45 -0
  347. package/src/core/ui-packs/fr.ts +46 -0
  348. package/src/core/ui-packs/he.ts +43 -0
  349. package/src/core/ui-packs/hi.ts +44 -0
  350. package/src/core/ui-packs/hr.ts +45 -0
  351. package/src/core/ui-packs/hu.ts +44 -0
  352. package/src/core/ui-packs/id.ts +45 -0
  353. package/src/core/ui-packs/it.ts +45 -0
  354. package/src/core/ui-packs/ja.ts +45 -0
  355. package/src/core/ui-packs/ko.ts +44 -0
  356. package/src/core/ui-packs/nl.ts +46 -0
  357. package/src/core/ui-packs/no.ts +44 -0
  358. package/src/core/ui-packs/pl.ts +45 -0
  359. package/src/core/ui-packs/pt-br.ts +46 -0
  360. package/src/core/ui-packs/pt.ts +45 -0
  361. package/src/core/ui-packs/ro.ts +45 -0
  362. package/src/core/ui-packs/ru.ts +45 -0
  363. package/src/core/ui-packs/sk.ts +44 -0
  364. package/src/core/ui-packs/sr.ts +45 -0
  365. package/src/core/ui-packs/sv.ts +44 -0
  366. package/src/core/ui-packs/th.ts +43 -0
  367. package/src/core/ui-packs/tr.ts +45 -0
  368. package/src/core/ui-packs/uk.ts +45 -0
  369. package/src/core/ui-packs/vi.ts +45 -0
  370. package/src/core/ui-packs/zh-tw.ts +43 -0
  371. package/src/core/ui-packs/zh.ts +43 -0
  372. package/src/core/variables.ts +103 -0
  373. package/src/deploy/artifacts.ts +77 -8
  374. package/src/deploy/cloudflare-negotiation.ts +35 -5
  375. package/src/deploy/headers.ts +2 -1
  376. package/src/deploy/node-headers.ts +63 -9
  377. package/src/deploy/platforms/cloudflare.ts +32 -0
  378. package/src/deploy/platforms/netlify.ts +33 -1
  379. package/src/deploy/platforms/vercel.ts +37 -2
  380. package/src/deploy/redirects.ts +91 -24
  381. package/src/deploy/vercel-negotiation.ts +36 -0
  382. package/src/markdown/api-rail.ts +259 -0
  383. package/src/markdown/code-title.ts +35 -9
  384. package/src/markdown/features.ts +3 -2
  385. package/src/markdown/fence-meta.ts +2 -0
  386. package/src/markdown/include.ts +11 -1
  387. package/src/markdown/index.ts +28 -5
  388. package/src/markdown/math.ts +4 -4
  389. package/src/markdown/variables.ts +56 -0
  390. package/src/markdown/views.ts +82 -0
  391. package/src/narration/build.ts +412 -0
  392. package/src/narration/player.ts +249 -0
  393. package/src/narration/provider.ts +29 -0
  394. package/src/narration/script.ts +592 -0
  395. package/src/og/card.ts +7 -6
  396. package/src/openapi/model.ts +57 -24
  397. package/src/openapi/overlay.ts +256 -0
  398. package/src/openapi/parse.ts +55 -2
  399. package/src/openapi/references.ts +21 -5
  400. package/src/openapi/render-mdx.ts +5 -1
  401. package/src/openapi/scalar.ts +40 -6
  402. package/src/openapi/source.ts +1 -1
  403. package/src/ratelimit/cloudflare.ts +54 -0
  404. package/src/ratelimit/index.ts +30 -0
  405. package/src/ratelimit/memory.ts +45 -0
  406. package/src/ratelimit/runtime.ts +229 -0
  407. package/src/ratelimit/schema.ts +28 -0
  408. package/src/ratelimit/upstash.ts +30 -0
  409. package/src/ratelimit/wrangler.ts +70 -0
  410. package/src/reference/asyncapi.ts +2 -2
  411. package/src/reference/graphql.ts +5 -2
  412. package/src/reference/openapi.ts +21 -7
  413. package/src/reference/options.ts +51 -7
  414. package/src/reference/scalar.ts +33 -8
  415. package/src/registry/eject.ts +20 -4
  416. package/src/registry/registry.ts +7 -0
  417. package/src/search/documents.ts +26 -2
  418. package/src/search/orama-index.ts +28 -3
  419. package/src/search/pagefind-ranking.ts +39 -0
  420. package/src/search/sync/algolia.ts +15 -4
  421. package/src/search/sync/orama-cloud.ts +3 -0
  422. package/src/search/sync/typesense.ts +5 -0
  423. package/src/seo/metatags.ts +67 -0
  424. package/src/theme/chrome-icons.ts +1 -0
  425. package/src/theme/color.ts +500 -0
  426. package/src/theme/entry.ts +56 -3
  427. package/src/theme/palette.ts +121 -45
  428. package/src/translate/prompts.ts +1 -0
  429. package/src/translate/validate.ts +8 -2
  430. package/src/upgrade/upgrade.ts +4 -171
  431. package/dist/cli/chunk-364znk6q.js +0 -75
  432. package/dist/cli/chunk-364znk6q.js.map +0 -11
  433. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  434. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  435. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  436. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  437. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  438. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  439. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  440. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  441. package/docs/content/sources.mdx +0 -334
  442. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
  443. /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
  444. /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
  445. /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
  446. /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
  447. /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
  448. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
  449. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
  450. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
  451. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
  452. /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
  453. /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
  454. /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
  455. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
package/src/ai/index.ts CHANGED
@@ -20,23 +20,35 @@
20
20
  * nothing here runs at request time.
21
21
  */
22
22
  export {
23
+ anthropic,
23
24
  gateway,
25
+ gemini,
26
+ grok,
24
27
  inkeep,
25
28
  llmgateway,
29
+ openai,
26
30
  openaiCompatible,
27
31
  openrouter,
28
32
  } from "./ask.ts";
29
33
  export type {
30
34
  AssistantAdapter,
31
35
  AssistantAdapterOptions,
36
+ AssistantAnthropicAdapter,
37
+ AssistantAnthropicOptions,
32
38
  AssistantGatewayAdapter,
33
39
  AssistantGatewayOptions,
40
+ AssistantGeminiAdapter,
41
+ AssistantGeminiOptions,
42
+ AssistantGrokAdapter,
43
+ AssistantGrokOptions,
34
44
  AssistantInkeepAdapter,
35
45
  AssistantInkeepOptions,
36
46
  AssistantLlmGatewayAdapter,
37
47
  AssistantLlmGatewayOptions,
48
+ AssistantOpenAIAdapter,
38
49
  AssistantOpenAICompatibleAdapter,
39
50
  AssistantOpenAICompatibleOptions,
51
+ AssistantOpenAIOptions,
40
52
  AssistantOpenRouterAdapter,
41
53
  AssistantOpenRouterOptions,
42
54
  AssistantProviderOptions,
package/src/ai/llms.ts CHANGED
@@ -12,7 +12,11 @@ import { API_PAGES_PATH, OPENAPI_PATH } from "./api/paths.ts";
12
12
  import { downlevelComponents } from "./component-markdown.ts";
13
13
  import { relativeLinkRewriter } from "./relative-links.ts";
14
14
  import { projectComponentSerializers } from "./serializers.ts";
15
- import { AGENT_SKILLS_DIR, AGENT_SKILLS_INDEX_PATH } from "./skills.ts";
15
+ import {
16
+ AGENT_SKILLS_DIR,
17
+ AGENT_SKILLS_INDEX_PATH,
18
+ servesSkillsIndex,
19
+ } from "./skills.ts";
16
20
  import type { SkillArtifact } from "./skills.ts";
17
21
  import { applyAgentVisibility } from "./visibility.ts";
18
22
 
@@ -75,7 +79,7 @@ const agentResourceLines = (project: BlumeProject): string[] => {
75
79
  `- [MCP server](${url(config.agents.mcp.route)}): Streamable HTTP Model Context Protocol server with search_docs, get_page, list_pages, and get_navigation tools, plus every page as a resource. Discovery document: ${url("/.well-known/mcp.json")}`
76
80
  );
77
81
  }
78
- if (config.agents.skills) {
82
+ if (servesSkillsIndex(config)) {
79
83
  lines.push(
80
84
  `- [Agent skills](${url(AGENT_SKILLS_INDEX_PATH)}): Agent Skills discovery index of the skills this site publishes.`
81
85
  );
@@ -14,6 +14,7 @@ import { downlevelComponents } from "./component-markdown.ts";
14
14
  import { buildLlmsIndex } from "./llms.ts";
15
15
  import { relativeLinkRewriter } from "./relative-links.ts";
16
16
  import { projectComponentSerializers } from "./serializers.ts";
17
+ import { servesSkillsIndex } from "./skills.ts";
17
18
  import { applyAgentVisibility } from "./visibility.ts";
18
19
 
19
20
  /** One route's raw-Markdown variants. */
@@ -115,7 +116,7 @@ export const buildRawMarkdown = async (
115
116
  config: advertisedConfig(project.config, {
116
117
  mcp: servesMcp(project, userPages),
117
118
  // Skills are collected at the end of the build, after this runs.
118
- skills: Boolean(project.config.agents.skills),
119
+ skills: servesSkillsIndex(project.config),
119
120
  }),
120
121
  }),
121
122
  };
@@ -130,6 +130,12 @@ export const buildMcpData = async (project: BlumeProject): Promise<McpData> => {
130
130
  if (doc.facets) {
131
131
  document.facets = doc.facets;
132
132
  }
133
+ if (doc.boost !== undefined) {
134
+ document.boost = doc.boost;
135
+ }
136
+ if (doc.keywords) {
137
+ document.keywords = doc.keywords;
138
+ }
133
139
  return document;
134
140
  }),
135
141
  instructions: config.agents.mcp.instructions,
@@ -134,9 +134,15 @@ export const openapiComponentSerializers = (
134
134
  return null;
135
135
  }
136
136
  const signature = inlineCode(operationSignature(data, operation));
137
- return operation.deprecated
138
- ? `${signature}\n\n**Deprecated.**`
139
- : signature;
137
+ const notes = [
138
+ // An agent reading the page must not take a webhook for an endpoint
139
+ // it can call.
140
+ operation.webhook
141
+ ? "**Webhook.** The API sends this request to your endpoint."
142
+ : "",
143
+ operation.deprecated ? "**Deprecated.**" : "",
144
+ ].filter(Boolean);
145
+ return [signature, ...notes].join("\n\n");
140
146
  },
141
147
  } satisfies Record<string, ComponentMarkdown>;
142
148
  };
@@ -0,0 +1,274 @@
1
+ import { hasGeneratedChangelog } from "../astro/pages.ts";
2
+ import {
3
+ mountBasePath,
4
+ normalizeBasePath,
5
+ withBasePath,
6
+ } from "../core/base-path.ts";
7
+ import { CHANGELOG_INDEX_ROUTE } from "../core/changelog-index.ts";
8
+ import { discoverPagesSync } from "../core/custom-pages.ts";
9
+ import type { BlumeProject } from "../core/project-graph.ts";
10
+ import { absoluteUrl } from "../core/site-url.ts";
11
+ import type { NavNode, PageRecord } from "../core/types.ts";
12
+ import { resolveReferences } from "../openapi/references.ts";
13
+ import { docsPhrase } from "./ai-catalog.ts";
14
+ import { API_PAGES_PATH } from "./api/paths.ts";
15
+ import { asciiSlugify } from "./mcp/discovery.ts";
16
+ import {
17
+ SKILL_DESCRIPTION_MAX,
18
+ generatesSiteSkill,
19
+ skillMdArtifact,
20
+ } from "./skills.ts";
21
+ import type { SkillArtifact } from "./skills.ts";
22
+
23
+ /**
24
+ * The site's own agent skill (`agents.skillMd`): a `SKILL.md` in the Agent
25
+ * Skills format, built from what the build already knows — the site's title
26
+ * and description, how to read any page as Markdown, llms.txt and the MCP
27
+ * server, the API references, and a map of the docs with each page's one-line
28
+ * description. No model writes it, so it costs nothing and is current with
29
+ * every build: a guide to the docs rather than a summary of the product. A
30
+ * hand-written skill of the same name in `agents.skills` replaces it.
31
+ */
32
+
33
+ /** The most pages the map lists; llms.txt holds the rest. */
34
+ export const MAX_SKILL_PAGES = 200;
35
+
36
+ /** The Agent Skills spec caps a skill name at 64 characters. */
37
+ const NAME_MAX = 64;
38
+
39
+ /** Where the site skill is served besides the discovery index. */
40
+ export const SKILL_MD_PATH = "/skill.md";
41
+
42
+ /**
43
+ * The site skill's name: the title as a skill name (lowercase alphanumerics
44
+ * and single hyphens), else the site's host, for a title with no Latin
45
+ * letters in it.
46
+ */
47
+ export const siteSkillName = (project: BlumeProject): string => {
48
+ const { site } = project.config.deployment.options;
49
+ const fromTitle = asciiSlugify(project.config.title);
50
+ const name =
51
+ fromTitle || asciiSlugify(site ? new URL(site).hostname : "") || "docs";
52
+ return name.slice(0, NAME_MAX).replace(/-+$/u, "");
53
+ };
54
+
55
+ /** A title as Markdown link text (brackets escaped, as in llms.txt). */
56
+ const linkText = (title: string): string =>
57
+ title.replaceAll(/[\\[\]]/gu, String.raw`\$&`);
58
+
59
+ /**
60
+ * Current-version, default-locale pages worth listing: not API operations,
61
+ * and not changelog entries, which the changelog index covers in one line.
62
+ */
63
+ const mappedPages = (project: BlumeProject): PageRecord[] => {
64
+ const defaultLocale = project.config.i18n?.defaultLocale;
65
+ return project.graph.pages.filter(
66
+ (page) =>
67
+ !(
68
+ page.meta.ai.exclude ||
69
+ page.meta.draft ||
70
+ page.meta.sidebar.hidden ||
71
+ page.meta.seo.noindex ||
72
+ page.version !== "" ||
73
+ page.source.name === "openapi" ||
74
+ page.contentType === "changelog"
75
+ ) &&
76
+ (defaultLocale === undefined || page.locale === defaultLocale)
77
+ );
78
+ };
79
+
80
+ /** Renders the page map, listing each page once, up to the cap. */
81
+ class PageMap {
82
+ readonly #byRoute: Map<string, PageRecord>;
83
+ readonly #listed = new Set<string>();
84
+ readonly #url: (route: string) => string;
85
+
86
+ constructor(pages: PageRecord[], url: (route: string) => string) {
87
+ this.#byRoute = new Map(pages.map((page) => [page.route, page]));
88
+ this.#url = url;
89
+ }
90
+
91
+ /** How many listable pages the cap left out. */
92
+ get omitted(): number {
93
+ return this.#byRoute.size - this.#listed.size;
94
+ }
95
+
96
+ /** The Markdown link line for a route, or nothing when it isn't listed. */
97
+ line(route: string): string[] {
98
+ const page = this.#byRoute.get(route);
99
+ if (
100
+ !page ||
101
+ this.#listed.has(route) ||
102
+ this.#listed.size >= MAX_SKILL_PAGES
103
+ ) {
104
+ return [];
105
+ }
106
+ this.#listed.add(route);
107
+ const summary = page.description ? `: ${page.description}` : "";
108
+ return [`- [${linkText(page.title)}](${this.#url(route)})${summary}`];
109
+ }
110
+
111
+ /** One navigation level: its loose pages, then each group under a heading. */
112
+ level(nodes: NavNode[], depth: number): string[] {
113
+ const list: string[] = [];
114
+ const groups: string[] = [];
115
+ for (const node of nodes) {
116
+ if (node.kind === "page") {
117
+ list.push(...this.line(node.route));
118
+ continue;
119
+ }
120
+ const blocks = [
121
+ ...(node.route ? this.line(node.route) : []),
122
+ ...this.level(node.children, depth + 1),
123
+ ];
124
+ if (blocks.length > 0) {
125
+ groups.push(
126
+ "",
127
+ `${"#".repeat(Math.min(depth, 6))} ${node.label}`,
128
+ "",
129
+ ...blocks
130
+ );
131
+ }
132
+ }
133
+ return [...list, ...groups];
134
+ }
135
+
136
+ /** Pages the navigation doesn't reach, in route order. */
137
+ rest(): string[] {
138
+ return [...this.#byRoute.keys()]
139
+ .toSorted((a, b) => a.localeCompare(b))
140
+ .flatMap((route) => this.line(route));
141
+ }
142
+ }
143
+
144
+ /** The "Reading the docs" bullets: every agent surface the site serves. */
145
+ const surfaceLines = (
146
+ project: BlumeProject,
147
+ url: (path: string) => string
148
+ ): string[] => {
149
+ const { agents } = project.config;
150
+ const lines = [
151
+ `- Any page as Markdown: add \`.md\` to its URL (the home page is ${url("/index.md")}). Every page link below already points at the Markdown.`,
152
+ ];
153
+ if (agents.llmsTxt.enabled) {
154
+ lines.push(
155
+ `- [llms.txt](${url("/llms.txt")}): every page with a one-line summary.`,
156
+ `- [llms-full.txt](${url("/llms-full.txt")}): every page in one file, for when you need all of it.`
157
+ );
158
+ }
159
+ if (agents.mcp.enabled) {
160
+ lines.push(
161
+ `- MCP server at ${url(agents.mcp.route)}, with \`search_docs\`, \`get_page\`, \`list_pages\`, and \`get_navigation\`. Connect it to search the docs instead of guessing URLs.`
162
+ );
163
+ }
164
+ if (agents.api) {
165
+ lines.push(
166
+ `- [JSON API](${url(API_PAGES_PATH)}): the page index, with each page as JSON and Markdown.`
167
+ );
168
+ }
169
+ const userPages = project.context.pagesRoot
170
+ ? discoverPagesSync(project.context.pagesRoot)
171
+ : [];
172
+ if (hasGeneratedChangelog(project, userPages)) {
173
+ lines.push(
174
+ `- [Changelog](${url(`${CHANGELOG_INDEX_ROUTE}.md`)}): release notes, newest first.`
175
+ );
176
+ }
177
+ return lines;
178
+ };
179
+
180
+ /** The "API reference" bullets, one per rendered reference. */
181
+ const referenceLines = (
182
+ project: BlumeProject,
183
+ url: (path: string) => string
184
+ ): string[] =>
185
+ resolveReferences(project.config).map((reference) => {
186
+ // Scalar renders its own page with no Markdown mirror; Blume-rendered
187
+ // references are pages like any other, one per operation.
188
+ const route =
189
+ reference.kind === "scalar"
190
+ ? reference.route
191
+ : withBasePath(reference.basePath, reference.route);
192
+ const pages =
193
+ reference.kind === "scalar"
194
+ ? ""
195
+ : ", with a page per operation (add `.md` for its Markdown)";
196
+ return `- [${linkText(reference.label)}](${url(route)})${pages}.`;
197
+ });
198
+
199
+ /** The skill's frontmatter `description`: what it's for and when to use it. */
200
+ const skillDescription = (project: BlumeProject): string => {
201
+ const { description, title } = project.config;
202
+ const summary = description ? ` ${description.trim()}` : "";
203
+ return `Read ${docsPhrase(title, "docs")} instead of answering from memory.${summary} Use when a task involves ${title}: how it works, how to set it up or configure it, or its API.`.slice(
204
+ 0,
205
+ SKILL_DESCRIPTION_MAX
206
+ );
207
+ };
208
+
209
+ /** The site skill's `SKILL.md`. */
210
+ const skillMarkdown = (project: BlumeProject, name: string): string => {
211
+ const { config } = project;
212
+ const { site = "" } = config.deployment.options;
213
+ const base = normalizeBasePath(config.deployment.options.base);
214
+ // Mounted and encoded like llms.txt's links.
215
+ const url = (path: string): string =>
216
+ encodeURI(absoluteUrl(site, mountBasePath(base, path)));
217
+ const markdownUrl = (route: string): string =>
218
+ url(route === "/" ? "/index.md" : `${route}.md`);
219
+
220
+ const map = new PageMap(mappedPages(project), markdownUrl);
221
+ const pages = [
222
+ ...map.level(project.graph.navigation.sidebar, 3),
223
+ ...map.rest(),
224
+ ];
225
+ const { omitted } = map;
226
+ if (omitted > 0) {
227
+ pages.push(
228
+ "",
229
+ config.agents.llmsTxt.enabled
230
+ ? `…and ${omitted} more. [llms.txt](${url("/llms.txt")}) lists every page.`
231
+ : `…and ${omitted} more.`
232
+ );
233
+ }
234
+ const references = referenceLines(project, url);
235
+
236
+ return [
237
+ "---",
238
+ `name: ${name}`,
239
+ `description: ${JSON.stringify(skillDescription(project))}`,
240
+ "---",
241
+ "",
242
+ `# ${config.title}`,
243
+ "",
244
+ ...(config.description ? [config.description.trim(), ""] : []),
245
+ `This skill points you at ${docsPhrase(config.title, "docs")}, at ${url("/")}. When a task involves ${config.title}, read the relevant page before relying on what you remember: the docs are the source of truth, and they change.`,
246
+ "",
247
+ "## Reading the docs",
248
+ "",
249
+ ...surfaceLines(project, url),
250
+ ...(references.length > 0
251
+ ? ["", "## API reference", "", ...references]
252
+ : []),
253
+ ...(pages.length > 0 ? ["", "## Pages", "", ...pages] : []),
254
+ "",
255
+ ]
256
+ .join("\n")
257
+ .replaceAll(/\n{3,}/gu, "\n\n");
258
+ };
259
+
260
+ /**
261
+ * The generated site skill, or `null` when `agents.skillMd` is off or there
262
+ * is no `deployment.site` to make its links absolute.
263
+ */
264
+ export const buildSiteSkill = (project: BlumeProject): SkillArtifact | null => {
265
+ if (!generatesSiteSkill(project.config)) {
266
+ return null;
267
+ }
268
+ const name = siteSkillName(project);
269
+ return skillMdArtifact(
270
+ name,
271
+ skillDescription(project),
272
+ skillMarkdown(project, name)
273
+ );
274
+ };
package/src/ai/skills.ts CHANGED
@@ -29,7 +29,7 @@ const AGENT_SKILLS_SCHEMA =
29
29
  const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
30
30
  const SKILL_NAME_MAX = 64;
31
31
  /** The Agent Skills spec caps `description` at 1024 characters. */
32
- const DESCRIPTION_MAX = 1024;
32
+ export const SKILL_DESCRIPTION_MAX = 1024;
33
33
 
34
34
  /** One publishable skill artifact plus its index entry. */
35
35
  export interface SkillArtifact {
@@ -142,7 +142,7 @@ const skillMeta = (raw: string, dirName: string): SkillMetaResult => {
142
142
  };
143
143
  }
144
144
  return {
145
- meta: { description: description.slice(0, DESCRIPTION_MAX), name },
145
+ meta: { description: description.slice(0, SKILL_DESCRIPTION_MAX), name },
146
146
  };
147
147
  };
148
148
 
@@ -206,6 +206,39 @@ export const collectSkills = async (dir: string): Promise<CollectedSkills> => {
206
206
  return { skills, warnings };
207
207
  };
208
208
 
209
+ /** A skill that is one `SKILL.md`, published verbatim. */
210
+ export const skillMdArtifact = (
211
+ name: string,
212
+ description: string,
213
+ markdown: string
214
+ ): SkillArtifact => {
215
+ const content = new TextEncoder().encode(markdown);
216
+ return {
217
+ content,
218
+ description: description.slice(0, SKILL_DESCRIPTION_MAX),
219
+ digest: sha256(content),
220
+ name,
221
+ path: `${name}/SKILL.md`,
222
+ type: "skill-md",
223
+ };
224
+ };
225
+
226
+ /**
227
+ * Whether the build generates the site's own skill (`agents.skillMd`): on,
228
+ * and with a `deployment.site` to make its links absolute, since an installed
229
+ * skill is read away from the site.
230
+ */
231
+ export const generatesSiteSkill = (config: ResolvedConfig): boolean =>
232
+ config.agents.skillMd && Boolean(config.deployment.options.site);
233
+
234
+ /**
235
+ * Whether a skills discovery index is served: from `agents.skills`, or for
236
+ * the generated site skill alone. On an advertised config (see
237
+ * `advertisedConfig`), both are already switched off when nothing published.
238
+ */
239
+ export const servesSkillsIndex = (config: ResolvedConfig): boolean =>
240
+ Boolean(config.agents.skills) || generatesSiteSkill(config);
241
+
209
242
  /**
210
243
  * The discovery index (v0.2.0 schema). Artifact URLs are path-absolute under
211
244
  * `deployment.base` — the RFC resolves them against the index origin.
@@ -0,0 +1,97 @@
1
+ import { existsSync } from "node:fs";
2
+
3
+ import { join, relative, resolve } from "pathe";
4
+
5
+ import type { BlumeProject } from "../core/project-graph.ts";
6
+ import { siteSkillName } from "./site-skill.ts";
7
+
8
+ /**
9
+ * `blume skill`'s logic, kept out of the command module so it runs (and is
10
+ * covered) in-process. Writing a product skill is judgment work — what the
11
+ * product is, which tasks matter, where it bites — so it lives in the
12
+ * `blume-write-skill` skill, shipped inside the package. This module only
13
+ * works out where the skill goes and writes the prompt that points a coding
14
+ * agent at the skill and the docs.
15
+ */
16
+
17
+ /** The skill's directory inside the published package. */
18
+ export const WRITE_SKILL_DIR = join("skills", "blume-write-skill");
19
+
20
+ /** Where `agents.skills` points when the config doesn't set it. */
21
+ const DEFAULT_SKILLS_DIR = "skills";
22
+
23
+ /** Where the skill goes, and what the agent needs to know to write it. */
24
+ export interface SkillPlan {
25
+ /** The `agents.skills` value to add, when the config has none. */
26
+ configure?: string;
27
+ /** The config file to add it to, relative to the project root. */
28
+ configFile: string;
29
+ /** The content root, relative to the project root. */
30
+ contentRoot: string;
31
+ /** Whether a skill is already there to update. */
32
+ exists: boolean;
33
+ /** Where to write the skill, relative to the project root. */
34
+ file: string;
35
+ /** The skill's name: the generated site skill's, so it replaces it. */
36
+ name: string;
37
+ /** The site's URL, for the skill's absolute links. */
38
+ site?: string;
39
+ title: string;
40
+ }
41
+
42
+ /** Plan the skill for a scanned project. */
43
+ export const planSkill = (project: BlumeProject): SkillPlan => {
44
+ const { config, context } = project;
45
+ const name = siteSkillName(project);
46
+ const file = resolve(
47
+ context.root,
48
+ config.agents.skills ?? DEFAULT_SKILLS_DIR,
49
+ name,
50
+ "SKILL.md"
51
+ );
52
+ const plan: SkillPlan = {
53
+ configFile: context.configFile
54
+ ? relative(context.root, context.configFile)
55
+ : "blume.config.ts",
56
+ contentRoot: relative(context.root, context.contentRoot) || ".",
57
+ exists: existsSync(file),
58
+ file: relative(context.root, file),
59
+ name,
60
+ site: config.deployment.options.site,
61
+ title: config.title,
62
+ };
63
+ return config.agents.skills
64
+ ? plan
65
+ : { ...plan, configure: `./${DEFAULT_SKILLS_DIR}` };
66
+ };
67
+
68
+ /** The prompt that hands the plan to a coding agent. */
69
+ export const writeSkillPrompt = (
70
+ plan: SkillPlan,
71
+ options: { skillDir: string; version: string }
72
+ ): string => {
73
+ const { skillDir, version } = options;
74
+ const lines = [
75
+ `Write the agent skill for the ${plan.title} docs, a Blume ${version} site.`,
76
+ "",
77
+ `Follow the blume-write-skill skill: read ${join(skillDir, "SKILL.md")} first and work through its workflow.`,
78
+ "",
79
+ plan.exists
80
+ ? `A skill already exists at ${plan.file}. Update it against the current docs rather than starting over, and keep what's still true.`
81
+ : `Write it to ${plan.file}.`,
82
+ `Its frontmatter \`name\` must be \`${plan.name}\`: that's the name of the skill Blume generates for this site, and a skill with the same name replaces it.`,
83
+ ];
84
+ if (plan.configure) {
85
+ lines.push(
86
+ `Then set \`agents.skills: "${plan.configure}"\` in ${plan.configFile} so the build publishes it.`
87
+ );
88
+ }
89
+ lines.push(
90
+ "",
91
+ `The docs source is in ${plan.contentRoot}.`,
92
+ plan.site
93
+ ? `The site is served at ${plan.site}, so link pages as ${plan.site.replace(/\/$/u, "")}<route>.md.`
94
+ : `The config has no \`deployment.site\`, and the skill's links must be absolute. Ask me for the site's URL, and set it as \`deployment.site\` in ${plan.configFile}.`
95
+ );
96
+ return lines.join("\n");
97
+ };