blume 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +2 -2
  4. package/dist/cli/{chunk-f7t03s3g.js → chunk-27g6wdth.js} +2 -2
  5. package/dist/cli/{chunk-mnqj32sj.js → chunk-2hn4b8z7.js} +13 -13
  6. package/dist/cli/chunk-2hn4b8z7.js.map +12 -0
  7. package/dist/cli/{chunk-by2290sx.js → chunk-5shv93fd.js} +2 -2
  8. package/dist/cli/{chunk-a9kptbw5.js → chunk-6crbhc3x.js} +3 -3
  9. package/dist/cli/{chunk-a9kptbw5.js.map → chunk-6crbhc3x.js.map} +1 -1
  10. package/dist/cli/{chunk-mwt1k8n7.js → chunk-6hsn950k.js} +20 -20
  11. package/dist/cli/chunk-6hsn950k.js.map +10 -0
  12. package/dist/cli/{chunk-11j0384y.js → chunk-6vm74dry.js} +13 -13
  13. package/dist/cli/{chunk-11j0384y.js.map → chunk-6vm74dry.js.map} +3 -3
  14. package/dist/cli/{chunk-j8mw0za6.js → chunk-79jhk4py.js} +8 -8
  15. package/dist/cli/{chunk-j8mw0za6.js.map → chunk-79jhk4py.js.map} +2 -2
  16. package/dist/cli/{chunk-nk3ts2xk.js → chunk-82bbrxdn.js} +2 -2
  17. package/dist/cli/{chunk-2q1dwty4.js → chunk-ah61y8py.js} +8 -8
  18. package/dist/cli/{chunk-2q1dwty4.js.map → chunk-ah61y8py.js.map} +4 -4
  19. package/dist/cli/{chunk-zxccj738.js → chunk-ce574jw2.js} +1 -1
  20. package/dist/cli/{chunk-y3e45rc8.js → chunk-ch6g3ar0.js} +3 -3
  21. package/dist/cli/{chunk-beat36xx.js → chunk-dh8cwk36.js} +5 -5
  22. package/dist/cli/{chunk-beat36xx.js.map → chunk-dh8cwk36.js.map} +2 -2
  23. package/dist/cli/{chunk-1w8dp3qb.js → chunk-epjnccmv.js} +13 -13
  24. package/dist/cli/{chunk-ernrthtr.js → chunk-f2z5v128.js} +13 -13
  25. package/dist/cli/{chunk-zg2gtj10.js → chunk-fs23ddbb.js} +2 -2
  26. package/dist/cli/{chunk-7ez8ny0t.js → chunk-fxypxtvm.js} +2 -2
  27. package/dist/cli/{chunk-tzne8qfq.js → chunk-fz5wtpmh.js} +13 -13
  28. package/dist/cli/{chunk-b5aj94ah.js → chunk-hdpx1tax.js} +4 -4
  29. package/dist/cli/{chunk-d80hr03s.js → chunk-jwyddg7y.js} +9 -9
  30. package/dist/cli/{chunk-d80hr03s.js.map → chunk-jwyddg7y.js.map} +2 -2
  31. package/dist/cli/{chunk-fh5hj5jt.js → chunk-kdp5q7ke.js} +15 -15
  32. package/dist/cli/{chunk-6k8vp3ta.js → chunk-kpf8rrjc.js} +9 -9
  33. package/dist/cli/{chunk-6k8vp3ta.js.map → chunk-kpf8rrjc.js.map} +3 -3
  34. package/dist/cli/{chunk-5a2z0198.js → chunk-m3vmjgmq.js} +9 -9
  35. package/dist/cli/{chunk-5a2z0198.js.map → chunk-m3vmjgmq.js.map} +2 -2
  36. package/dist/cli/{chunk-bctazmbk.js → chunk-mb2919y2.js} +4 -4
  37. package/dist/cli/{chunk-79njf86q.js → chunk-q5163e60.js} +13 -13
  38. package/dist/cli/{chunk-xaz13gwg.js → chunk-qkqwkpte.js} +196 -208
  39. package/dist/cli/{chunk-xaz13gwg.js.map → chunk-qkqwkpte.js.map} +48 -48
  40. package/dist/cli/{chunk-sqn5t4q0.js → chunk-qs4q5p4e.js} +3 -3
  41. package/dist/cli/{chunk-bw22s759.js → chunk-qwsrynx5.js} +1 -1
  42. package/dist/cli/{chunk-z01ze5c1.js → chunk-s1p84fyh.js} +15 -15
  43. package/dist/cli/{chunk-pnnvybbk.js → chunk-s6jhgk0q.js} +5 -5
  44. package/dist/cli/{chunk-pnnvybbk.js.map → chunk-s6jhgk0q.js.map} +2 -2
  45. package/dist/cli/{chunk-f2972sbt.js → chunk-vtk4a6dg.js} +1 -1
  46. package/dist/cli/{chunk-z1f5arsg.js → chunk-wgm7m9qk.js} +21 -21
  47. package/dist/cli/{chunk-z1f5arsg.js.map → chunk-wgm7m9qk.js.map} +4 -4
  48. package/dist/cli/{chunk-bnbmcwfb.js → chunk-wm7js3j9.js} +5 -5
  49. package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-wm7js3j9.js.map} +3 -3
  50. package/dist/cli/{chunk-d1tadaw7.js → chunk-yt5n7ppj.js} +3 -3
  51. package/dist/cli/{chunk-pat2zzwc.js → chunk-yw7dm696.js} +1 -1
  52. package/dist/cli/{chunk-pat2zzwc.js.map → chunk-yw7dm696.js.map} +1 -1
  53. package/dist/cli/{chunk-88cpgt6h.js → chunk-zxcczpyx.js} +1 -1
  54. package/dist/cli/{chunk-41za066z.js → chunk-zxh4d9vy.js} +4 -4
  55. package/dist/cli/index.js +17 -17
  56. package/dist/types/ai/agent-readability.d.ts +1 -1
  57. package/dist/types/ai/api/paths.d.ts +1 -1
  58. package/dist/types/ai/ask-context.d.ts +7 -7
  59. package/dist/types/ai/ask.d.ts +43 -43
  60. package/dist/types/ai/index.d.ts +3 -3
  61. package/dist/types/ai/openapi-components.d.ts +1 -1
  62. package/dist/types/ai/serializers.d.ts +1 -1
  63. package/dist/types/ai/visibility.d.ts +1 -1
  64. package/dist/types/core/config-input.d.ts +17 -17
  65. package/dist/types/core/config.d.ts +3 -3
  66. package/dist/types/core/data.d.ts +3 -3
  67. package/dist/types/core/i18n-ui.d.ts +6 -8
  68. package/dist/types/core/schema.d.ts +5 -5
  69. package/dist/types/core/unrecognized-keys.d.ts +1 -1
  70. package/dist/types/search/documents.d.ts +1 -1
  71. package/dist/types/search/orama-index.d.ts +1 -1
  72. package/docs/02-deployment.mdx +4 -4
  73. package/docs/03-upgrading.mdx +22 -9
  74. package/docs/04-migrating.mdx +4 -4
  75. package/docs/08-faq.mdx +3 -3
  76. package/docs/advanced/custom-pages.mdx +2 -2
  77. package/docs/advanced/skills.mdx +1 -1
  78. package/docs/cli/audit.mdx +3 -3
  79. package/docs/cli/doctor.mdx +3 -3
  80. package/docs/cli/evals.mdx +7 -7
  81. package/docs/cli/index.mdx +2 -2
  82. package/docs/cli/translate.mdx +8 -8
  83. package/docs/configuration/{ask-ai.mdx → assistant.mdx} +19 -19
  84. package/docs/configuration/customization.mdx +2 -2
  85. package/docs/configuration/index.mdx +2 -2
  86. package/docs/configuration/meta.ts +1 -1
  87. package/docs/configuration/search.mdx +1 -1
  88. package/docs/content/i18n.mdx +2 -2
  89. package/docs/content/islands.mdx +1 -1
  90. package/docs/discoverability/agent-discovery.mdx +1 -1
  91. package/docs/discoverability/index.mdx +1 -1
  92. package/docs/index.mdx +2 -2
  93. package/package.json +1 -1
  94. package/skills/blume/SKILL.md +5 -5
  95. package/skills/blume-migrate/SKILL.md +2 -2
  96. package/src/ai/agent-readability.ts +4 -4
  97. package/src/ai/api/paths.ts +1 -1
  98. package/src/ai/ask-context.ts +7 -7
  99. package/src/ai/ask-data.ts +2 -2
  100. package/src/ai/ask.ts +84 -71
  101. package/src/ai/cors.ts +3 -3
  102. package/src/ai/index.ts +16 -16
  103. package/src/ai/openapi-components.ts +1 -1
  104. package/src/ai/serializers.ts +1 -1
  105. package/src/ai/visibility.ts +1 -1
  106. package/src/astro/generate.ts +19 -18
  107. package/src/astro/module-types.ts +1 -1
  108. package/src/astro/runtime-deps.ts +6 -6
  109. package/src/astro/templates.ts +26 -26
  110. package/src/blume-modules.d.ts +2 -2
  111. package/src/cli/commands/audit.ts +1 -1
  112. package/src/cli/commands/doctor.ts +7 -5
  113. package/src/cli/commands/eval.ts +3 -3
  114. package/src/cli/commands/migrate.ts +2 -2
  115. package/src/cli/commands/translate.ts +3 -3
  116. package/src/cli/commands/upgrade.ts +2 -2
  117. package/src/cli/required-secrets.ts +3 -3
  118. package/src/components/copy-feedback.ts +1 -1
  119. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  120. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +23 -23
  121. package/src/components/islands/hooks.ts +14 -12
  122. package/src/components/layout/Header.astro +10 -10
  123. package/src/components/layout/PageLayout.astro +6 -6
  124. package/src/components/layout/Pagination.astro +7 -7
  125. package/src/components/layout/ReferenceLayout.astro +1 -1
  126. package/src/components/layout/RootLayout.astro +6 -6
  127. package/src/components/layout/Search.astro +13 -13
  128. package/src/components/layout/analytics-client.ts +1 -1
  129. package/src/components/layout/drawer-inert.ts +1 -1
  130. package/src/components/openapi/description.ts +2 -2
  131. package/src/core/code-fences.ts +1 -1
  132. package/src/core/config-input.ts +19 -19
  133. package/src/core/config.ts +3 -3
  134. package/src/core/data.ts +3 -3
  135. package/src/core/i18n-ui.ts +35 -9
  136. package/src/core/request-body.ts +1 -1
  137. package/src/core/schema.ts +25 -20
  138. package/src/core/server-features.ts +2 -2
  139. package/src/core/ui-packs/ar.ts +4 -5
  140. package/src/core/ui-packs/bg.ts +4 -5
  141. package/src/core/ui-packs/bn.ts +4 -5
  142. package/src/core/ui-packs/ca.ts +4 -5
  143. package/src/core/ui-packs/cs.ts +4 -5
  144. package/src/core/ui-packs/da.ts +4 -5
  145. package/src/core/ui-packs/de.ts +4 -5
  146. package/src/core/ui-packs/el.ts +4 -5
  147. package/src/core/ui-packs/es.ts +4 -5
  148. package/src/core/ui-packs/fa.ts +4 -5
  149. package/src/core/ui-packs/fi.ts +4 -5
  150. package/src/core/ui-packs/fr.ts +4 -5
  151. package/src/core/ui-packs/he.ts +4 -5
  152. package/src/core/ui-packs/hi.ts +4 -5
  153. package/src/core/ui-packs/hr.ts +4 -5
  154. package/src/core/ui-packs/hu.ts +4 -5
  155. package/src/core/ui-packs/id.ts +4 -5
  156. package/src/core/ui-packs/it.ts +4 -5
  157. package/src/core/ui-packs/ja.ts +4 -5
  158. package/src/core/ui-packs/ko.ts +4 -5
  159. package/src/core/ui-packs/nl.ts +4 -5
  160. package/src/core/ui-packs/no.ts +4 -5
  161. package/src/core/ui-packs/pl.ts +4 -5
  162. package/src/core/ui-packs/pt-br.ts +4 -5
  163. package/src/core/ui-packs/pt.ts +4 -5
  164. package/src/core/ui-packs/ro.ts +4 -5
  165. package/src/core/ui-packs/ru.ts +4 -5
  166. package/src/core/ui-packs/sk.ts +4 -5
  167. package/src/core/ui-packs/sr.ts +4 -5
  168. package/src/core/ui-packs/sv.ts +4 -5
  169. package/src/core/ui-packs/th.ts +4 -5
  170. package/src/core/ui-packs/tr.ts +4 -5
  171. package/src/core/ui-packs/uk.ts +4 -5
  172. package/src/core/ui-packs/vi.ts +4 -5
  173. package/src/core/ui-packs/zh-tw.ts +4 -5
  174. package/src/core/ui-packs/zh.ts +4 -5
  175. package/src/core/unrecognized-keys.ts +1 -1
  176. package/src/registry/eject.ts +19 -18
  177. package/src/search/documents.ts +2 -2
  178. package/src/search/orama-index.ts +1 -1
  179. package/src/translate/report.ts +1 -1
  180. package/src/upgrade/upgrade.ts +1 -1
  181. package/dist/cli/chunk-mnqj32sj.js.map +0 -12
  182. package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
  183. /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-27g6wdth.js.map} +0 -0
  184. /package/dist/cli/{chunk-by2290sx.js.map → chunk-5shv93fd.js.map} +0 -0
  185. /package/dist/cli/{chunk-nk3ts2xk.js.map → chunk-82bbrxdn.js.map} +0 -0
  186. /package/dist/cli/{chunk-zxccj738.js.map → chunk-ce574jw2.js.map} +0 -0
  187. /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-ch6g3ar0.js.map} +0 -0
  188. /package/dist/cli/{chunk-1w8dp3qb.js.map → chunk-epjnccmv.js.map} +0 -0
  189. /package/dist/cli/{chunk-ernrthtr.js.map → chunk-f2z5v128.js.map} +0 -0
  190. /package/dist/cli/{chunk-zg2gtj10.js.map → chunk-fs23ddbb.js.map} +0 -0
  191. /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-fxypxtvm.js.map} +0 -0
  192. /package/dist/cli/{chunk-tzne8qfq.js.map → chunk-fz5wtpmh.js.map} +0 -0
  193. /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-hdpx1tax.js.map} +0 -0
  194. /package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-kdp5q7ke.js.map} +0 -0
  195. /package/dist/cli/{chunk-bctazmbk.js.map → chunk-mb2919y2.js.map} +0 -0
  196. /package/dist/cli/{chunk-79njf86q.js.map → chunk-q5163e60.js.map} +0 -0
  197. /package/dist/cli/{chunk-sqn5t4q0.js.map → chunk-qs4q5p4e.js.map} +0 -0
  198. /package/dist/cli/{chunk-bw22s759.js.map → chunk-qwsrynx5.js.map} +0 -0
  199. /package/dist/cli/{chunk-z01ze5c1.js.map → chunk-s1p84fyh.js.map} +0 -0
  200. /package/dist/cli/{chunk-f2972sbt.js.map → chunk-vtk4a6dg.js.map} +0 -0
  201. /package/dist/cli/{chunk-d1tadaw7.js.map → chunk-yt5n7ppj.js.map} +0 -0
  202. /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-zxcczpyx.js.map} +0 -0
  203. /package/dist/cli/{chunk-41za066z.js.map → chunk-zxh4d9vy.js.map} +0 -0
package/src/ai/cors.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CORS for the generated Ask AI route (`ai.ask.cors`).
2
+ * CORS for the assistant's generated route (`ai.assistant.cors`).
3
3
  *
4
4
  * A browser only lets a page on another origin read a response that names
5
5
  * that origin, and a JSON `POST` preflights first. `preflightResponse` answers
@@ -8,12 +8,12 @@
8
8
  * rather than stamping each `return`, keeps the next return site added to the
9
9
  * handler from shipping an opaque failure for that one status.
10
10
  *
11
- * `allowed` is the `ai.ask.cors` list: origins already reduced to their
11
+ * `allowed` is the `ai.assistant.cors` list: origins already reduced to their
12
12
  * `scheme://host[:port]` form by the config schema, or the single entry `"*"`
13
13
  * to admit every origin.
14
14
  */
15
15
 
16
- /** The `ai.ask.cors` entry that admits every origin. */
16
+ /** The `ai.assistant.cors` entry that admits every origin. */
17
17
  export const ANY_ORIGIN = "*";
18
18
 
19
19
  /** The headers a response carries for a cross-origin caller. */
package/src/ai/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ask AI provider adapters for `blume.config.ts`:
2
+ * Assistant provider adapters for `blume.config.ts`:
3
3
  *
4
4
  * ```ts
5
5
  * import { defineConfig } from "blume";
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * export default defineConfig({
9
9
  * ai: {
10
- * ask: {
10
+ * assistant: {
11
11
  * enabled: true,
12
12
  * provider: openrouter({ model: "anthropic/claude-sonnet-4-5" }),
13
13
  * },
@@ -27,19 +27,19 @@ export {
27
27
  openrouter,
28
28
  } from "./ask.ts";
29
29
  export type {
30
- AskAdapter,
31
- AskAdapterOptions,
32
- AskGatewayAdapter,
33
- AskGatewayOptions,
34
- AskInkeepAdapter,
35
- AskInkeepOptions,
36
- AskLlmGatewayAdapter,
37
- AskLlmGatewayOptions,
38
- AskOpenAICompatibleAdapter,
39
- AskOpenAICompatibleOptions,
40
- AskOpenRouterAdapter,
41
- AskOpenRouterOptions,
42
- AskProviderOptions,
43
- AskReasoning,
30
+ AssistantAdapter,
31
+ AssistantAdapterOptions,
32
+ AssistantGatewayAdapter,
33
+ AssistantGatewayOptions,
34
+ AssistantInkeepAdapter,
35
+ AssistantInkeepOptions,
36
+ AssistantLlmGatewayAdapter,
37
+ AssistantLlmGatewayOptions,
38
+ AssistantOpenAICompatibleAdapter,
39
+ AssistantOpenAICompatibleOptions,
40
+ AssistantOpenRouterAdapter,
41
+ AssistantOpenRouterOptions,
42
+ AssistantProviderOptions,
43
+ AssistantReasoning,
44
44
  } from "./ask.ts";
45
45
  export type { AdapterDescriptor } from "../core/adapter.ts";
@@ -44,7 +44,7 @@ const listItem = (
44
44
  * the body plus one of these components, deliberately: the structured UI is the
45
45
  * component's job, and the prose stays Markdown so it indexes. That split is
46
46
  * right for the rendered page and lossy everywhere else — `<route>.md`,
47
- * llms-full.txt, MCP `get_page` and the Ask AI corpus all downlevel components
47
+ * llms-full.txt, MCP `get_page` and the assistant corpus all downlevel components
48
48
  * to Markdown, and these three had no serializer, so an operation page reached
49
49
  * an agent as its description followed by a bare tag. On a site whose reference
50
50
  * is most of the corpus, that is most of the corpus: measured on one 449-page
@@ -6,7 +6,7 @@ import { openapiComponentSerializers } from "./openapi-components.ts";
6
6
 
7
7
  /**
8
8
  * Every serializer a project brings to a downlevel pass, layered once for all
9
- * the agent surfaces (`<route>.md`, llms-full.txt, the search and Ask AI
9
+ * the agent surfaces (`<route>.md`, llms-full.txt, the search and assistant
10
10
  * corpora): the built-in families that read project data — examples, then the
11
11
  * API reference — under the user's `agents.markdownComponents`, which is spread
12
12
  * last so an entry of the same name still wins. One layering, so a new family
@@ -67,7 +67,7 @@ export const applyAudienceVisibility = (
67
67
 
68
68
  /**
69
69
  * Resolve `<Visibility>` blocks for agent-facing Markdown (llms-full.txt, the
70
- * `.md`/`.mdx` mirrors, MCP tools, Ask AI grounding): `for="web"` content is
70
+ * `.md`/`.mdx` mirrors, MCP tools, assistant grounding): `for="web"` content is
71
71
  * removed and `for="agents"` content is unwrapped.
72
72
  */
73
73
  export const applyAgentVisibility = (markdown: string): string =>
@@ -636,7 +636,7 @@ const writeIfChanged = async (
636
636
 
637
637
  /**
638
638
  * Delete generated files under `srcDir` that this pass didn't (re)write. The
639
- * generator emits many files conditionally — an Ask AI endpoint, OG images, a
639
+ * generator emits many files conditionally — an assistant endpoint, OG images, a
640
640
  * search index, RSS feeds, reference pages, the MCP server — so toggling a
641
641
  * feature off would otherwise leave a stale file behind, and a leftover
642
642
  * server-rendered endpoint breaks the static build. `writeIfChanged` only ever
@@ -1114,10 +1114,10 @@ export const buildRuntimeData = (project: BlumeProject): string => {
1114
1114
  config: {
1115
1115
  analytics: config.analytics,
1116
1116
  appleIcon: resolveAppleIcon(project),
1117
- ask: config.ai.ask?.enabled
1117
+ assistant: config.ai.assistant?.enabled
1118
1118
  ? {
1119
- endpoint: config.ai.ask.endpoint ?? null,
1120
- suggestions: config.ai.ask.suggestions,
1119
+ endpoint: config.ai.assistant.endpoint ?? null,
1120
+ suggestions: config.ai.assistant.suggestions,
1121
1121
  }
1122
1122
  : null,
1123
1123
  banner: resolveBanner(config),
@@ -1580,9 +1580,9 @@ const proxyAllowlistWarnings = (
1580
1580
  };
1581
1581
 
1582
1582
  /**
1583
- * Write the Ask AI endpoint and, unless the backend runs its own retrieval
1583
+ * Write the assistant endpoint and, unless the backend runs its own retrieval
1584
1584
  * (Inkeep), the grounding snapshot the endpoint queries at request time. A no-op
1585
- * when Ask AI is disabled.
1585
+ * when the assistant is disabled.
1586
1586
  */
1587
1587
  const writeAskFiles = async (
1588
1588
  project: BlumeProject,
@@ -1590,20 +1590,20 @@ const writeAskFiles = async (
1590
1590
  write: (path: string, content: string) => Promise<boolean>,
1591
1591
  modules: RuntimeModules
1592
1592
  ): Promise<void> => {
1593
- const { ask } = project.config.ai;
1594
- if (!(ask?.enabled && !ask.endpoint)) {
1593
+ const { assistant } = project.config.ai;
1594
+ if (!(assistant?.enabled && !assistant.endpoint)) {
1595
1595
  return;
1596
1596
  }
1597
- const backend = resolveAskBackend(ask.provider);
1597
+ const backend = resolveAskBackend(assistant.provider);
1598
1598
  if (backend.grounded) {
1599
1599
  modules.set("blume:ask-data", JSON.stringify(await buildAskData(project)));
1600
1600
  }
1601
1601
  await write(
1602
1602
  join(srcDir, "pages", "api", "ask.ts"),
1603
1603
  askEndpointTemplate(backend, {
1604
- cors: ask.cors,
1605
- instructions: ask.instructions,
1606
- retrieval: ask.retrieval,
1604
+ cors: assistant.cors,
1605
+ instructions: assistant.instructions,
1606
+ retrieval: assistant.retrieval,
1607
1607
  })
1608
1608
  );
1609
1609
  };
@@ -1645,7 +1645,7 @@ export const diagnosticWarning = (diagnostic: Diagnostic): string =>
1645
1645
 
1646
1646
  /**
1647
1647
  * Missing-dependency preflight: the search provider's SDK, content source
1648
- * SDKs, the Ask AI backend's provider SDK, the deployment adapter's package,
1648
+ * SDKs, the assistant backend's provider SDK, the deployment adapter's package,
1649
1649
  * and — since React ships with Blume while Vue/Svelte don't — any island
1650
1650
  * framework's Astro integration. A build fails here, before anything is
1651
1651
  * written, with the install command for the project's package manager —
@@ -1845,7 +1845,7 @@ export const generateRuntime = async (
1845
1845
  const homeLinkHeader =
1846
1846
  buildHomeLinkHeader(config, contentRoutes) ?? undefined;
1847
1847
 
1848
- const askEnabled = config.ai.ask?.enabled ?? false;
1848
+ const assistantEnabled = config.ai.assistant?.enabled ?? false;
1849
1849
  const exportPdf = config.export.pdf;
1850
1850
  const exportEpub = config.export.epub;
1851
1851
  // Every route's source Markdown: published as `blume:raw-markdown` below,
@@ -1897,7 +1897,7 @@ export const generateRuntime = async (
1897
1897
  project.examples = exampleMarkdownLookup(exampleDiscovery.examples);
1898
1898
 
1899
1899
  // Each island/example framework enables its Astro renderer. React also
1900
- // switches on for any project `.tsx`/`.jsx` and for Ask AI; Vue/Svelte are
1900
+ // switches on for any project `.tsx`/`.jsx` and for the assistant; Vue/Svelte are
1901
1901
  // island/example-driven. `.astro` examples need no renderer. Component
1902
1902
  // overrides referencing a framework component enable its renderer too.
1903
1903
  const frameworks = new Set<string>([
@@ -1906,7 +1906,8 @@ export const generateRuntime = async (
1906
1906
  ...slotPlan.frameworks,
1907
1907
  ]);
1908
1908
  const dependencyWarnings = await dependencyPreflight(project, frameworks);
1909
- const needsReact = detectedReact || askEnabled || frameworks.has("react");
1909
+ const needsReact =
1910
+ detectedReact || assistantEnabled || frameworks.has("react");
1910
1911
  const needsVue = frameworks.has("vue");
1911
1912
  const needsSvelte = frameworks.has("svelte");
1912
1913
 
@@ -2025,7 +2026,7 @@ export const generateRuntime = async (
2025
2026
  // The header's Ask trigger, behind the `blume:ask` alias. Always written
2026
2027
  // (even when Ask is off, as a component that renders nothing) so the alias
2027
2028
  // resolves — the same contract as `blume:search-client`.
2028
- write(askPath, askComponentTemplate(askEnabled)),
2029
+ write(askPath, askComponentTemplate(assistantEnabled)),
2029
2030
  write(join(srcDir, "generated", "components.ts"), slotPlan.module),
2030
2031
  write(
2031
2032
  join(srcDir, "generated", "examples.ts"),
@@ -2288,7 +2289,7 @@ export const generateRuntime = async (
2288
2289
  writeStagedContent(out, staged),
2289
2290
  ]);
2290
2291
 
2291
- // Remove anything under `.blume/src` this pass didn't write — e.g. an Ask AI
2292
+ // Remove anything under `.blume/src` this pass didn't write — e.g. an assistant
2292
2293
  // endpoint left behind after the feature was switched off.
2293
2294
  await pruneOrphans(srcDir, written);
2294
2295
 
@@ -14,7 +14,7 @@
14
14
  export const runtimeModuleDeclarations = (
15
15
  examplesModule: string
16
16
  ): string => `declare module "blume:ask" {
17
- const Ask: typeof import("blume/components/islands/AskAI.astro").default;
17
+ const Ask: typeof import("blume/components/islands/Assistant.astro").default;
18
18
  export default Ask;
19
19
  }
20
20
 
@@ -85,18 +85,18 @@ export const sourceAdapterDependencies = (
85
85
  );
86
86
 
87
87
  /**
88
- * The Ask AI adapter's provider SDK, when missing — only `gateway` needs
88
+ * The assistant adapter's provider SDK, when missing — only `gateway` needs
89
89
  * nothing beyond the core `ai` package Blume ships. An external `endpoint`
90
90
  * means no generated route, so no SDK is imported.
91
91
  */
92
- export const askProviderDependencies = (
93
- ask: ResolvedConfig["ai"]["ask"],
92
+ export const assistantProviderDependencies = (
93
+ ask: ResolvedConfig["ai"]["assistant"],
94
94
  root: string,
95
95
  pkgDir: string = packageRoot()
96
96
  ): MissingDependency[] =>
97
97
  ask?.enabled && !ask.endpoint
98
98
  ? unresolved(
99
- `Ask AI provider "${ask.provider.kind}"`,
99
+ `Assistant provider "${ask.provider.kind}"`,
100
100
  ask.provider.runtimeDeps,
101
101
  root,
102
102
  pkgDir
@@ -152,7 +152,7 @@ export const islandFrameworkDependencies = (
152
152
  /**
153
153
  * Every package the resolved config's adapters (and the given island
154
154
  * frameworks) import that isn't installed: the search SDK, content source
155
- * SDKs, the Ask AI provider SDK, the deployment adapter, and island
155
+ * SDKs, the assistant provider SDK, the deployment adapter, and island
156
156
  * integrations. The adapters' `runtimeDeps` are the one place that knows.
157
157
  */
158
158
  export const missingRuntimeDependencies = (
@@ -163,7 +163,7 @@ export const missingRuntimeDependencies = (
163
163
  ): MissingDependency[] => [
164
164
  ...searchProviderDependencies(config.search.provider, root, pkgDir),
165
165
  ...sourceAdapterDependencies(config.content.sources, root, pkgDir),
166
- ...askProviderDependencies(config.ai.ask, root, pkgDir),
166
+ ...assistantProviderDependencies(config.ai.assistant, root, pkgDir),
167
167
  ...deploymentAdapterDependencies(config.deployment, root, pkgDir),
168
168
  ...islandFrameworkDependencies(frameworks, root),
169
169
  ];
@@ -196,10 +196,10 @@ export const runtimeDependencies = (options: {
196
196
  }
197
197
  }
198
198
  }
199
- // Ask AI's provider SDK, as its adapter declares it (the gateway needs
199
+ // The assistant's provider SDK, as its adapter declares it (the gateway needs
200
200
  // nothing beyond core `ai`, so it declares none).
201
- if (config.ai.ask?.enabled && !config.ai.ask.endpoint) {
202
- deps.push(...config.ai.ask.provider.runtimeDeps);
201
+ if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
202
+ deps.push(...config.ai.assistant.provider.runtimeDeps);
203
203
  }
204
204
  // The deployment adapter's `@astrojs/*` package, for a server build; the
205
205
  // descriptor declares it (and nothing for a static build).
@@ -727,7 +727,7 @@ export const astroConfigTemplate = (options: {
727
727
  hasFonts: fontEntries.length > 0,
728
728
  });
729
729
 
730
- // Framework renderers are only wired in when an island (or Ask AI, for React)
730
+ // Framework renderers are only wired in when an island (or the assistant, for React)
731
731
  // needs them. The core theme is Astro-first and ships no client JS.
732
732
  const reactImport = needsReact ? `import react from "@astrojs/react";\n` : "";
733
733
  const vueImport = needsVue ? `import vue from "@astrojs/vue";\n` : "";
@@ -1064,17 +1064,17 @@ export const collections = { docs${options.staged ? ", staged" : ""} };
1064
1064
  const ASK_FALLBACK_PROMPT =
1065
1065
  "You are a helpful documentation assistant. Answer using the project's documentation.";
1066
1066
 
1067
- /** The `ai.ask` values the generated endpoint has to carry with it. */
1067
+ /** The `ai.assistant` values the generated endpoint has to carry with it. */
1068
1068
  export interface AskEndpointOptions {
1069
- /** `ai.ask.cors` — origins allowed to call the route from another site. */
1069
+ /** `ai.assistant.cors` — origins allowed to call the route from another site. */
1070
1070
  cors?: string[];
1071
- /** `ai.ask.instructions` — extra system-prompt text. */
1071
+ /** `ai.assistant.instructions` — extra system-prompt text. */
1072
1072
  instructions?: string;
1073
- /** `ai.ask.retrieval` — how much documentation each question carries. */
1073
+ /** `ai.assistant.retrieval` — how much documentation each question carries. */
1074
1074
  retrieval?: AskRetrievalOptions;
1075
1075
  }
1076
1076
 
1077
- /** The pieces `askEndpointTemplate` splices in for `ai.ask.cors`. */
1077
+ /** The pieces `askEndpointTemplate` splices in for `ai.assistant.cors`. */
1078
1078
  interface AskCorsTemplate {
1079
1079
  /** The route's closing token: `});` when the POST is wrapped, `};` otherwise. */
1080
1080
  close: string;
@@ -1087,7 +1087,7 @@ interface AskCorsTemplate {
1087
1087
  }
1088
1088
 
1089
1089
  /**
1090
- * `ai.ask.cors`: a browser only lets another origin read the stream when the
1090
+ * `ai.assistant.cors`: a browser only lets another origin read the stream when the
1091
1091
  * response names that origin, and a JSON POST preflights first, so the route
1092
1092
  * answers `OPTIONS` and wraps the `POST` in `withCors`, which stamps a listed
1093
1093
  * origin on every response — errors included, so a cross-origin caller can
@@ -1114,14 +1114,14 @@ export const OPTIONS: APIRoute = ({ request }) =>
1114
1114
  : { close: "};", imports: [], open: "async ({ request }) => {", setup: "" };
1115
1115
 
1116
1116
  /**
1117
- * Largest request body the Ask AI route reads: 64 KB, well above the
1117
+ * Largest request body the assistant route reads: 64 KB, well above the
1118
1118
  * 24,000-character message budget it validates next, so a real conversation
1119
1119
  * never meets it.
1120
1120
  */
1121
1121
  const ASK_BODY_LIMIT_BYTES = 65_536;
1122
1122
 
1123
1123
  /**
1124
- * Generate the Ask AI server endpoint (`.blume/src/pages/api/ask.ts`).
1124
+ * Generate the assistant server endpoint (`.blume/src/pages/api/ask.ts`).
1125
1125
  *
1126
1126
  * The provider-specific pieces — imports, the provider factory call, the model
1127
1127
  * expression, the credential guard, and the adapter's reasoning mapping and
@@ -1130,11 +1130,11 @@ const ASK_BODY_LIMIT_BYTES = 65_536;
1130
1130
  * `blume.config.ts`. `backend.grounded` decides whether answers are grounded
1131
1131
  * in the retrieved docs (every adapter but Inkeep, which retrieves itself).
1132
1132
  *
1133
- * `options.instructions` (the `ai.ask.instructions` config) is appended to the
1133
+ * `options.instructions` (the `ai.assistant.instructions` config) is appended to the
1134
1134
  * built-in prompt on every path: the grounded prompt via `createAskContext`,
1135
- * and the plain fallback here. `options.retrieval` (the `ai.ask.retrieval`
1135
+ * and the plain fallback here. `options.retrieval` (the `ai.assistant.retrieval`
1136
1136
  * config) is forwarded to `createAskContext` on the grounded path, where it
1137
- * sizes retrieval. `options.cors` (the `ai.ask.cors` config) adds a preflight
1137
+ * sizes retrieval. `options.cors` (the `ai.assistant.cors` config) adds a preflight
1138
1138
  * handler and wraps the `POST` so every response names a listed origin. All
1139
1139
  * three travel in one options object so a new call site can't silently drop
1140
1140
  * one of them.
@@ -1232,7 +1232,7 @@ export const askEndpointTemplate = (
1232
1232
  // Provider errors surface mid-stream, after the 200 is committed; this is
1233
1233
  // the only place they can be observed server-side.
1234
1234
  const onError = ` onError({ error }) {
1235
- console.error("Ask AI provider error:", error);
1235
+ console.error("Assistant provider error:", error);
1236
1236
  },`;
1237
1237
  // The `streamText` argument list, built once so the grounded and plain
1238
1238
  // paths can't drift: they differ only in where the instructions come from.
@@ -1281,9 +1281,9 @@ ${handler}
1281
1281
  * Generate `.blume/src/generated/Ask.astro` — the component behind the
1282
1282
  * `blume:ask` alias that the shared header renders in place of a per-page slot.
1283
1283
  *
1284
- * The header can't import the Ask AI island directly: it's a React component, so
1284
+ * The header can't import the assistant island directly: it's a React component, so
1285
1285
  * the import alone would drag the JSX renderer into the module graph of every
1286
- * project — including the ones that never enable Ask AI and therefore have no
1286
+ * project — including the ones that never enable the assistant and therefore have no
1287
1287
  * React integration wired into their generated Astro config (see `needsReact`).
1288
1288
  * Routing the import through a generated component keeps that dependency behind
1289
1289
  * the config switch: enabled projects get the island, disabled ones get a
@@ -1293,25 +1293,25 @@ ${handler}
1293
1293
  * state suggestions are read straight from the data snapshot, which is why no
1294
1294
  * page has to pass them.
1295
1295
  */
1296
- export const askComponentTemplate = (askEnabled: boolean): string =>
1297
- askEnabled
1296
+ export const askComponentTemplate = (assistantEnabled: boolean): string =>
1297
+ assistantEnabled
1298
1298
  ? `---
1299
1299
  // Generated by Blume. Do not edit.
1300
- import AskAI from "blume/components/islands/AskAI.astro";
1300
+ import Assistant from "blume/components/islands/Assistant.astro";
1301
1301
  import data from "blume:data";
1302
1302
 
1303
1303
  const { strings } = Astro.props;
1304
1304
  ---
1305
1305
 
1306
- <AskAI
1307
- endpoint={data.config.ask?.endpoint ?? undefined}
1308
- strings={strings ?? data.ui.ask}
1309
- suggestions={data.config.ask?.suggestions ?? []}
1306
+ <Assistant
1307
+ endpoint={data.config.assistant?.endpoint ?? undefined}
1308
+ strings={strings ?? data.ui.assistant}
1309
+ suggestions={data.config.assistant?.suggestions ?? []}
1310
1310
  />
1311
1311
  `
1312
1312
  : `---
1313
1313
  // Generated by Blume. Do not edit.
1314
- // Ask AI is off (\`ai.ask.enabled\`), so the header's Ask trigger renders nothing.
1314
+ // The assistant is off (\`ai.assistant.enabled\`), so the header's Ask trigger renders nothing.
1315
1315
  // Deliberately imports no React island, keeping the JSX renderer out of projects
1316
1316
  // that don't need it.
1317
1317
  ---
@@ -18,9 +18,9 @@ declare module "blume:search-client" {
18
18
  declare module "blume:ask" {
19
19
  /** The generated Ask trigger (see `askComponentTemplate`); empty when Ask is off. */
20
20
  // Mirrors the generated `.blume/src/env.d.ts` declaration: the trigger's type
21
- // is the AskAI island the enabled template re-exports.
21
+ // is the Assistant island the enabled template re-exports.
22
22
  // oxlint-disable-next-line typescript/consistent-type-imports
23
- const Ask: typeof import("./components/islands/AskAI.astro").default;
23
+ const Ask: typeof import("./components/islands/Assistant.astro").default;
24
24
  export default Ask;
25
25
  }
26
26
 
@@ -154,7 +154,7 @@ export const auditCommand = defineCommand({
154
154
  (kind) => args[kind]
155
155
  );
156
156
  if (agents.length > 1) {
157
- logger.error("Pass at most one of --claude or --codex.");
157
+ logger.error("Pass at most one of --codex or --claude.");
158
158
  process.exit(1);
159
159
  }
160
160
  const [agent] = agents;
@@ -153,12 +153,14 @@ export const doctorCommand = defineCommand({
153
153
  logger.info(
154
154
  `Sources: ${config.content.sources.map((source) => source.kind).join(", ")}`
155
155
  );
156
- const { ask } = config.ai;
157
- let askSummary = "off";
158
- if (ask?.enabled) {
159
- askSummary = ask.endpoint ? "external endpoint" : ask.provider.kind;
156
+ const { assistant } = config.ai;
157
+ let assistantSummary = "off";
158
+ if (assistant?.enabled) {
159
+ assistantSummary = assistant.endpoint
160
+ ? "external endpoint"
161
+ : assistant.provider.kind;
160
162
  }
161
- logger.info(`Ask AI: ${askSummary}`);
163
+ logger.info(`Assistant: ${assistantSummary}`);
162
164
  }
163
165
  } catch (error) {
164
166
  if (error instanceof BlumeError) {
@@ -82,7 +82,7 @@ interface EvalFlags {
82
82
  /** Validate the flag surface, exiting with a message on the first offense. */
83
83
  const parseFlags = (args: EvalFlags) => {
84
84
  if (!isAgentKind(args.agent)) {
85
- logger.error(`Invalid --agent "${args.agent}" (use claude | codex).`);
85
+ logger.error(`Invalid --agent "${args.agent}" (use codex | claude).`);
86
86
  process.exit(1);
87
87
  }
88
88
  if (args.action !== undefined && args.action !== "init") {
@@ -152,8 +152,8 @@ export const evalCommand = defineCommand({
152
152
  type: "positional",
153
153
  },
154
154
  agent: {
155
- default: "claude",
156
- description: "Agent CLI that reads and grades the docs: claude | codex.",
155
+ default: "codex",
156
+ description: "Agent CLI that reads and grades the docs: codex | claude.",
157
157
  type: "string",
158
158
  },
159
159
  file: {
@@ -53,7 +53,7 @@ export const migrateCommand = defineCommand({
53
53
  (kind) => args[kind]
54
54
  );
55
55
  if (agents.length > 1) {
56
- logger.error("Pass at most one of --claude or --codex.");
56
+ logger.error("Pass at most one of --codex or --claude.");
57
57
  process.exit(1);
58
58
  }
59
59
  const [agent] = agents;
@@ -88,7 +88,7 @@ export const migrateCommand = defineCommand({
88
88
  // plus the install line that puts the skill where agents look for it.
89
89
  if (!agent) {
90
90
  process.stderr.write(
91
- ` blume migrate runs the migration with a coding agent: rerun with --claude or --codex.\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-migrate\n`
91
+ ` blume migrate runs the migration 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-migrate\n`
92
92
  );
93
93
  return;
94
94
  }
@@ -63,18 +63,18 @@ const parseFlags = (args: TranslateFlags): ParsedTranslateFlags => {
63
63
  (kind) => args[kind]
64
64
  );
65
65
  if (agents.length > 1) {
66
- logger.error("Pass exactly one of --claude or --codex.");
66
+ logger.error("Pass exactly one of --codex or --claude.");
67
67
  process.exit(1);
68
68
  }
69
69
  if (args.check && agents.length > 0) {
70
70
  logger.error(
71
- "--check is read-only and never runs an agent; drop --claude/--codex."
71
+ "--check is read-only and never runs an agent; drop --codex/--claude."
72
72
  );
73
73
  process.exit(1);
74
74
  }
75
75
  if (!args.check && agents.length === 0) {
76
76
  logger.error(
77
- "Pass --claude or --codex to choose the agent CLI that translates."
77
+ "Pass --codex or --claude to choose the agent CLI that translates."
78
78
  );
79
79
  process.exit(1);
80
80
  }
@@ -56,7 +56,7 @@ export const upgradeCommand = defineCommand({
56
56
  (kind) => args[kind]
57
57
  );
58
58
  if (agents.length > 1) {
59
- logger.error("Pass at most one of --claude or --codex.");
59
+ logger.error("Pass at most one of --codex or --claude.");
60
60
  process.exit(1);
61
61
  }
62
62
  const [agent] = agents;
@@ -109,7 +109,7 @@ export const upgradeCommand = defineCommand({
109
109
 
110
110
  if (!agent) {
111
111
  process.stderr.write(
112
- ` Each change is covered in the upgrade guide: ${UPGRADE_GUIDE_URL}\n Rerun with --claude or --codex to hand them to a coding agent.\n`
112
+ ` Each change is covered in the upgrade guide: ${UPGRADE_GUIDE_URL}\n Rerun with --codex or --claude to hand them to a coding agent.\n`
113
113
  );
114
114
  process.exit(1);
115
115
  }
@@ -24,12 +24,12 @@ export const checkRequiredSecrets = (config: ResolvedConfig): Diagnostic[] => {
24
24
  });
25
25
  };
26
26
 
27
- if (config.ai.ask?.enabled && !config.ai.ask.endpoint) {
27
+ if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
28
28
  // The adapter descriptor names the env vars its route reads.
29
- const { provider } = config.ai.ask;
29
+ const { provider } = config.ai.assistant;
30
30
  const backend = resolveAskBackend(provider);
31
31
  for (const env of provider.requiredSecrets) {
32
- requireSecret(`Ask AI (${backend.label})`, env, backend.secretNote);
32
+ requireSecret(`Assistant (${backend.label})`, env, backend.secretNote);
33
33
  }
34
34
  }
35
35
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shared clipboard + "Copied" feedback used by every copy affordance (code
3
- * blocks, page actions, color swatches, prompts, API panels, Ask AI). One
3
+ * blocks, page actions, color swatches, prompts, API panels, the assistant). One
4
4
  * implementation owns the invariants each site used to hand-roll:
5
5
  *
6
6
  * - the clipboard write is guarded and a confirmation must never lie: a
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import type { UIStrings } from "../../core/i18n-ui.ts";
3
3
  import { resolveIcon } from "../../theme/icons.ts";
4
- import AskAI from "./ask-ai.tsx";
4
+ import Assistant from "./assistant.tsx";
5
5
 
6
6
  interface Suggestion {
7
7
  icon?: string;
@@ -10,7 +10,7 @@ interface Suggestion {
10
10
 
11
11
  interface Props {
12
12
  endpoint?: string;
13
- strings?: UIStrings["ask"];
13
+ strings?: UIStrings["assistant"];
14
14
  suggestions?: Suggestion[];
15
15
  }
16
16
 
@@ -48,19 +48,19 @@ const icons = {
48
48
  the draft input, and the open panel — across client-router navigations
49
49
  instead of remounting it empty on every page. The island re-portals its
50
50
  panel and re-stamps the body push attribute itself on astro:after-swap
51
- (see ask-ai.tsx), since the swap replaces <body> wholesale. */}
52
- <AskAI
51
+ (see assistant.tsx), since the swap replaces <body> wholesale. */}
52
+ <Assistant
53
53
  client:load
54
54
  endpoint={endpoint}
55
55
  icons={icons}
56
56
  strings={strings}
57
57
  suggestions={items}
58
- transition:persist="blume-ask"
58
+ transition:persist="blume-assistant"
59
59
  />
60
60
 
61
61
  <style is:global>
62
62
  :root {
63
- --blume-ask-width: min(30rem, 100vw);
63
+ --blume-assistant-width: min(30rem, 100vw);
64
64
  }
65
65
 
66
66
  /* Desktop docks the panel and shrinks the page to fit, like vercel.com/docs.
@@ -70,8 +70,8 @@ const icons = {
70
70
  transition: padding-inline-end 0.2s ease-out;
71
71
  }
72
72
 
73
- body[data-blume-ask="open"] {
74
- padding-inline-end: var(--blume-ask-width);
73
+ body[data-blume-assistant="open"] {
74
+ padding-inline-end: var(--blume-assistant-width);
75
75
  }
76
76
  }
77
77
 
@@ -81,11 +81,11 @@ const icons = {
81
81
  changelog index) is single-column, and forcing the sidebar tracks onto it
82
82
  would squeeze its only item — <main> — into the 17.5rem track. */
83
83
  @media (min-width: 1280px) {
84
- body[data-blume-ask="open"] [data-blume-toc] {
84
+ body[data-blume-assistant="open"] [data-blume-toc] {
85
85
  display: none;
86
86
  }
87
87
 
88
- body[data-blume-ask="open"] [data-blume-doc-grid]:has([data-blume-toc]) {
88
+ body[data-blume-assistant="open"] [data-blume-doc-grid]:has([data-blume-toc]) {
89
89
  grid-template-columns: 17.5rem minmax(0, 1fr);
90
90
  }
91
91
  }