blume 1.2.1 → 1.3.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.
- package/CHANGELOG.md +37 -0
- package/dist/cli/index.js +1680 -534
- package/dist/cli/index.js.map +37 -28
- package/dist/types/core/config-input.d.ts +131 -11
- package/dist/types/core/config.d.ts +9 -1
- package/dist/types/core/data.d.ts +24 -5
- package/dist/types/core/i18n-ui.d.ts +58 -799
- package/dist/types/core/schema.d.ts +534 -3305
- package/dist/types/theme/fonts.d.ts +55 -11
- package/docs/02-deployment.mdx +2 -0
- package/docs/07-faq.mdx +14 -14
- package/docs/advanced/skills.mdx +2 -2
- package/docs/configuration/ai.mdx +126 -2
- package/docs/configuration/index.mdx +19 -1
- package/docs/configuration/search.mdx +2 -0
- package/docs/configuration/seo.mdx +26 -3
- package/docs/configuration/theming.mdx +44 -2
- package/docs/content/syntax.mdx +18 -2
- package/docs/reference/cli.mdx +3 -3
- package/package.json +9 -8
- package/skills/blume/SKILL.md +6 -4
- package/skills/blume-migrate/SKILL.md +5 -3
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/monorepo.md +2 -1
- package/src/ai/agent-readability.ts +31 -1
- package/src/ai/api-catalog.ts +81 -0
- package/src/ai/link-headers.ts +52 -0
- package/src/ai/llms.ts +12 -1
- package/src/ai/markdown.ts +15 -2
- package/src/ai/mcp/discovery.ts +70 -15
- package/src/ai/mcp/server.ts +5 -4
- package/src/ai/skills.ts +193 -0
- package/src/ai/tar.ts +104 -0
- package/src/ai/web-bot-auth.ts +30 -0
- package/src/astro/generate.ts +116 -6
- package/src/astro/integration.ts +52 -14
- package/src/astro/templates.ts +176 -33
- package/src/audit/catalog.ts +20 -0
- package/src/audit/checks/dns-aid.ts +190 -0
- package/src/audit/report.ts +5 -0
- package/src/audit/run.ts +2 -0
- package/src/cli/commands/build.ts +178 -9
- package/src/cli/init/scaffold.ts +1 -1
- package/src/components/islands/ask-ai.tsx +4 -1
- package/src/components/islands/webmcp.ts +203 -0
- package/src/components/layout/PageLayout.astro +2 -0
- package/src/components/layout/ReferenceLayout.astro +2 -0
- package/src/components/layout/RootLayout.astro +62 -10
- package/src/components/layout/Search.astro +2 -2
- package/src/components/layout/WebMcp.astro +49 -0
- package/src/core/config-input.ts +143 -11
- package/src/core/config.ts +17 -1
- package/src/core/content-assets.ts +199 -0
- package/src/core/data.ts +21 -5
- package/src/core/diagnostics.ts +6 -5
- package/src/core/i18n-ui.ts +19 -28
- package/src/core/project-graph.ts +6 -0
- package/src/core/schema.ts +224 -71
- package/src/core/sources/normalize.ts +5 -5
- package/src/deploy/headers.ts +45 -3
- package/src/deploy/vercel-negotiation.ts +233 -0
- package/src/markdown/mermaid.ts +7 -1
- package/src/markdown/table-wrap.ts +33 -1
- package/src/og/card.ts +91 -22
- package/src/og/derive.ts +200 -0
- package/src/og/index.ts +6 -1
- package/src/search/orama-index.ts +98 -4
- package/src/theme/entry.ts +34 -13
- package/src/theme/fonts.ts +183 -30
- package/dist/types/og/card.d.ts +0 -63
- package/dist/types/og/dimensions.d.ts +0 -12
|
@@ -1,23 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Fonts exposed through `theme.fonts`.
|
|
3
3
|
*
|
|
4
|
-
* Each
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* only resolves config
|
|
4
|
+
* Each role accepts a curated Google Font slug, a remote-provider family (any
|
|
5
|
+
* Google/Fontsource/Bunny/Fontshare family by name), or local font files. All
|
|
6
|
+
* forms resolve to entries for Astro's built-in Fonts API, which self-hosts
|
|
7
|
+
* and optimizes them; this module only resolves config values into the data
|
|
8
|
+
* that drives it.
|
|
8
9
|
*/
|
|
9
10
|
export type FontCategory = "sans" | "serif" | "mono";
|
|
10
11
|
/** The three configurable roles in `theme.fonts`. */
|
|
11
12
|
export type FontSlot = "display" | "body" | "mono";
|
|
12
|
-
/**
|
|
13
|
-
export type
|
|
14
|
-
/** A
|
|
15
|
-
export interface
|
|
13
|
+
/** Zero-config Astro font providers usable from `theme.fonts`. */
|
|
14
|
+
export type RemoteFontProvider = "google" | "fontsource" | "bunny" | "fontshare";
|
|
15
|
+
/** A remote-provider family: any family name the provider knows. */
|
|
16
|
+
export interface RemoteFontConfig {
|
|
17
|
+
/** Family name as the provider lists it, e.g. `"Noto Sans JP"`. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Which provider serves the family. Defaults to `"google"`. */
|
|
20
|
+
provider?: RemoteFontProvider;
|
|
21
|
+
/** Weights (or variable ranges like `"100..900"`) to load. Defaults to `[400, 500, 600, 700]`. */
|
|
22
|
+
weights?: (number | string)[];
|
|
23
|
+
/** Fallback stack category. Defaults to `"mono"` for the mono role, `"sans"` otherwise. */
|
|
24
|
+
fallback?: FontCategory;
|
|
25
|
+
}
|
|
26
|
+
/** One `@font-face` declaration for a local font. */
|
|
27
|
+
export interface LocalFontVariant {
|
|
28
|
+
/** Font file path, relative to the project root. */
|
|
29
|
+
src: string;
|
|
30
|
+
/** Face weight; inferred from the file when omitted. */
|
|
31
|
+
weight?: number | string;
|
|
32
|
+
/** Face style; inferred from the file when omitted. */
|
|
33
|
+
style?: "normal" | "italic" | "oblique";
|
|
34
|
+
}
|
|
35
|
+
/** A self-hosted family loaded from files in the project. */
|
|
36
|
+
export interface LocalFontConfig {
|
|
37
|
+
/** Family name used in CSS and the OG card. */
|
|
38
|
+
name: string;
|
|
39
|
+
/** The faces to declare (at least one). */
|
|
40
|
+
variants: LocalFontVariant[];
|
|
41
|
+
/** Fallback stack category. Defaults to `"mono"` for the mono role, `"sans"` otherwise. */
|
|
42
|
+
fallback?: FontCategory;
|
|
43
|
+
}
|
|
44
|
+
/** A role's font: curated slug, remote family, or local files. */
|
|
45
|
+
export type FontValue = string | RemoteFontConfig | LocalFontConfig;
|
|
46
|
+
/** Resolved theme fonts (a validated value per role, all optional). */
|
|
47
|
+
export type FontsConfig = Partial<Record<FontSlot, FontValue>> | undefined;
|
|
48
|
+
/** A single Astro `fonts:` entry (sans the literal `fontProviders.*()` call). */
|
|
49
|
+
export type FontEntry = {
|
|
50
|
+
kind: "remote";
|
|
51
|
+
provider: RemoteFontProvider;
|
|
16
52
|
cssVariable: string;
|
|
17
53
|
fallbacks: string[];
|
|
18
54
|
name: string;
|
|
19
|
-
weights: number[];
|
|
20
|
-
}
|
|
55
|
+
weights: (number | string)[];
|
|
56
|
+
} | {
|
|
57
|
+
kind: "local";
|
|
58
|
+
cssVariable: string;
|
|
59
|
+
fallbacks: string[];
|
|
60
|
+
name: string;
|
|
61
|
+
variants: LocalFontVariant[];
|
|
62
|
+
};
|
|
21
63
|
/** Slug -> Google family + weights + fallback category. Keep keys alphabetical. */
|
|
22
64
|
export declare const GOOGLE_FONTS: {
|
|
23
65
|
"dm-sans": {
|
|
@@ -151,6 +193,8 @@ export type FontSlug = keyof typeof GOOGLE_FONTS;
|
|
|
151
193
|
export declare const FONT_SLUGS: string[];
|
|
152
194
|
/** Type guard: is `value` a supported font slug? */
|
|
153
195
|
export declare const isFontSlug: (value: string) => value is FontSlug;
|
|
196
|
+
/** Kebab-case a family name into a slug (`"Noto Sans JP"` -> `"noto-sans-jp"`). */
|
|
197
|
+
export declare const slugifyFontName: (name: string) => string;
|
|
154
198
|
/** The unique Astro `fonts:` entries for the configured roles (deduped). */
|
|
155
199
|
export declare const buildFontEntries: (fonts: FontsConfig) => FontEntry[];
|
|
156
200
|
/**
|
package/docs/02-deployment.mdx
CHANGED
|
@@ -101,6 +101,8 @@ On **Vercel**, **Netlify**, and **Cloudflare Pages**, Blume picks the matching a
|
|
|
101
101
|
|
|
102
102
|
A server build includes everything a static build does, plus any Astro endpoints or middleware you add. The `node` adapter produces a standalone server you can run directly.
|
|
103
103
|
|
|
104
|
+
On Vercel, a server build also turns on [`Accept: text/markdown` content negotiation](/docs/configuration/ai#content-negotiation): Blume splices header-conditional rewrites into the deploy's routing config, so an agent requesting any content page with that header receives its raw-Markdown mirror at the same URL.
|
|
105
|
+
|
|
104
106
|
:::note
|
|
105
107
|
Server features have their own configuration — for example, Ask AI needs a model API key. See the [AI guide](/docs/configuration/ai) for setup.
|
|
106
108
|
:::
|
package/docs/07-faq.mdx
CHANGED
|
@@ -87,19 +87,19 @@ We reported it upstream in [oxc-project/oxc#24096](https://github.com/oxc-projec
|
|
|
87
87
|
|
|
88
88
|
Patch oxfmt so it preserves the line break that sits directly against a `:::` fence. Blume ships the same fix in its own repo, and you can apply it in any project.
|
|
89
89
|
|
|
90
|
-
1. Save the patch as `patches/oxfmt@0.
|
|
91
|
-
|
|
92
|
-
```diff patches/oxfmt@0.
|
|
93
|
-
diff --git a/dist/markdown-
|
|
94
|
-
index
|
|
95
|
-
--- a/dist/markdown-
|
|
96
|
-
+++ b/dist/markdown-
|
|
97
|
-
@@ -
|
|
98
|
-
case "sentence": return
|
|
99
|
-
case "word": return t.parser !== "mdx" ?
|
|
90
|
+
1. Save the patch as `patches/oxfmt@0.61.0.patch`:
|
|
91
|
+
|
|
92
|
+
```diff patches/oxfmt@0.61.0.patch
|
|
93
|
+
diff --git a/dist/markdown-ZuiQU4Xe.js b/dist/markdown-ZuiQU4Xe.js
|
|
94
|
+
index 566b9e6d27f36061d64b93736e238e871e1ee2b2..82d0595acc010807c2939fc4a1717dde887a8555 100644
|
|
95
|
+
--- a/dist/markdown-ZuiQU4Xe.js
|
|
96
|
+
+++ b/dist/markdown-ZuiQU4Xe.js
|
|
97
|
+
@@ -4875,7 +4875,43 @@ function lu(e, t, r) {
|
|
98
|
+
case "sentence": return Oh(e, r);
|
|
99
|
+
case "word": return t.parser !== "mdx" ? zh(e, t) : Uh(e);
|
|
100
100
|
case "whitespace": {
|
|
101
|
-
- let { next:
|
|
102
|
-
+ let { next:
|
|
101
|
+
- let { next: a } = e, u = a && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(a.value) && !NE(e) && !(t.proseWrap === "preserve" && RE(e)) ? "never" : t.proseWrap;
|
|
102
|
+
+ let { next: a, previous: oxfmtFencePrev } = e;
|
|
103
103
|
+ // Preserve line breaks that sit directly against a `:::` container
|
|
104
104
|
+ // directive fence, so `proseWrap: "never"` keeps the opening/closing
|
|
105
105
|
+ // fence on their own lines instead of joining them into the prose (which
|
|
@@ -135,7 +135,7 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
135
135
|
+ }
|
|
136
136
|
+ oxfmtPrevIsTitledFence = oxfmtIsFence(oxfmtLineFirst);
|
|
137
137
|
+ }
|
|
138
|
-
+ let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtPrevIsTitledFence || oxfmtIsFence(
|
|
138
|
+
+ let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtPrevIsTitledFence || oxfmtIsFence(a) ? "preserve" : a && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(a.value) && !NE(e) && !(t.proseWrap === "preserve" && RE(e)) ? "never" : t.proseWrap;
|
|
139
139
|
return ou(e, n.value, u, !1, t);
|
|
140
140
|
}
|
|
141
141
|
case "emphasis": {
|
|
@@ -146,7 +146,7 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
146
146
|
```json package.json
|
|
147
147
|
{
|
|
148
148
|
"patchedDependencies": {
|
|
149
|
-
"oxfmt@0.
|
|
149
|
+
"oxfmt@0.61.0": "patches/oxfmt@0.61.0.patch"
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
```
|
package/docs/advanced/skills.mdx
CHANGED
|
@@ -3,11 +3,11 @@ title: Skills
|
|
|
3
3
|
description: The agent skills Blume ships — playbooks that teach a coding agent like Claude Code, Codex, or Cursor to build and maintain a Blume docs site.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Blume ships [agent skills](https://docs.claude.com/en/docs/claude-code/skills) — playbooks that teach a coding agent (Claude Code, Codex, Cursor) how to do a Blume-shaped job without you explaining it. They live on GitHub in the repo's `skills/` folder and are bundled in the package
|
|
6
|
+
Blume ships [agent skills](https://docs.claude.com/en/docs/claude-code/skills) — playbooks that teach a coding agent (Claude Code, Codex, Cursor) how to do a Blume-shaped job without you explaining it. They live on GitHub in the repo's `skills/` folder and are bundled in the installed package under `skills/`, so any agent can be pointed at a `SKILL.md` directly.
|
|
7
7
|
|
|
8
8
|
## Blume
|
|
9
9
|
|
|
10
|
-
The core skill. It teaches the agent what Blume is and how to scaffold, write, and configure a site — filesystem-derived navigation, the config schema, content components — and points it at the full docs bundled in the installed package (`
|
|
10
|
+
The core skill. It teaches the agent what Blume is and how to scaffold, write, and configure a site — filesystem-derived navigation, the config schema, content components — and points it at the full docs bundled in the installed package (the `docs/` directory inside `blume`, resolved from wherever your package manager installs it — in a pnpm monorepo that's the depending workspace's `node_modules`, not the repo root). Install it in any project where an agent helps you build your docs:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
npx skills add haydenbleasel/blume
|
|
@@ -61,6 +61,10 @@ Nested routes work the same way (`/content/syntax.md`), and the home page is ser
|
|
|
61
61
|
|
|
62
62
|
The `.md` variant _downlevels_ components to plain Markdown for consumers that can't interpret JSX: `<TypeTable>` becomes a Markdown table, `<Callout>` a labeled blockquote, `<Steps>` an ordered list, `<Tabs>` bold-labeled sections, and `<YouTube>` a link. Props are evaluated with the page's `frontmatter` in scope, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Anything that can't be converted faithfully — a custom component, or a prop computed from an import — is left as-is, and component markup inside fenced code blocks is never touched. The same conversion applies to `llms-full.txt` and the MCP server's `get_page` tool, so every agent-facing surface reads clean Markdown. When you want the untransformed source, use the `.mdx` variant.
|
|
63
63
|
|
|
64
|
+
### Content negotiation
|
|
65
|
+
|
|
66
|
+
Agents don't need to know the `.md` convention: requesting a page's own URL with an [`Accept: text/markdown`](https://acceptmarkdown.com) header serves the Markdown variant at the same address, with `Vary: Accept` so caches keep the two apart. The dev server honors the header out of the box, and a [Vercel server build](/docs/deployment#server-rendering) wires the same negotiation into the deploy's routing rules automatically — no configuration needed. Other deploy targets serve prerendered pages from a static layer with no request-time hook, so agents there fetch the `.md` URL directly; the [agent readability manifest](#agent-readability) advertises `contentNegotiation` only on deployments that honor the header.
|
|
67
|
+
|
|
64
68
|
### Custom component serializers
|
|
65
69
|
|
|
66
70
|
Give your own components a Markdown form with `ai.markdownComponents` — a map of JSX name to serializer. Each serializer receives the component's `props` (statically evaluated from the MDX attributes, with the page's `frontmatter` in scope), its `children` (already downleveled to Markdown), and the page's `frontmatter` data, and returns the replacement — or `null` to leave the JSX as-is:
|
|
@@ -249,7 +253,7 @@ ai: {
|
|
|
249
253
|
| `name` | title | Server name shown to clients (defaults to title). |
|
|
250
254
|
| `instructions` | — | Optional system hint passed to connecting agents. |
|
|
251
255
|
|
|
252
|
-
The server exposes read-only tools — `search_docs`, `get_page`, `list_pages`, and `get_navigation` — and publishes discovery documents at `/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. Each page's **Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, VS Code, and Codex (shown once [`deployment.site`](/docs/deployment) is set).
|
|
256
|
+
The server exposes read-only tools — `search_docs`, `get_page`, `list_pages`, and `get_navigation` — and publishes discovery documents at `/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. The server card follows the [SEP-2127](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127) Server Card extension schema (reverse-DNS `name`, `remotes` transport endpoints), with initialize-shaped compat fields (`serverInfo`, `capabilities`, `transports`) for scanners built against the proposal's earlier revision. Each page's **Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, VS Code, and Codex (shown once [`deployment.site`](/docs/deployment) is set).
|
|
253
257
|
|
|
254
258
|
`search_docs` runs its own full-text index, so it works regardless of your [search](/docs/configuration/search) provider — and even when search is set to `none`. The MCP server is a separate feature from on-page search.
|
|
255
259
|
|
|
@@ -306,8 +310,128 @@ The manifest lists only what you've enabled — the [raw Markdown](#raw-markdown
|
|
|
306
310
|
}
|
|
307
311
|
```
|
|
308
312
|
|
|
313
|
+
The `contentNegotiation` field appears only when the deployed site actually honors the `Accept: text/markdown` header — see [content negotiation](#content-negotiation); on every other deployment the manifest advertises just the `.md` mirror pattern.
|
|
314
|
+
|
|
309
315
|
Set `seo.agentReadability` to `false` to skip it, or ship your own `public/agent-readability.json` to take over — Blume never overwrites a file you place in `public/`.
|
|
310
316
|
|
|
317
|
+
### Discovery Link header
|
|
318
|
+
|
|
319
|
+
Agents that probe a site don't know to look for the manifest — so Blume also advertises it in an [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) `Link` response header on the homepage, using IANA-registered relation types:
|
|
320
|
+
|
|
321
|
+
```http
|
|
322
|
+
Link: </agent-readability.json>; rel="describedby"; type="application/json",
|
|
323
|
+
</llms.txt>; rel="describedby"; type="text/plain",
|
|
324
|
+
</index.md>; rel="alternate"; type="text/markdown"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Each entry appears only when its feature is on, and the `alternate` link only when your homepage is a content page with a [raw Markdown](#raw-markdown) mirror. Sites that publish APIs also get a `rel="api-catalog"` entry pointing at the [generated API catalog](#api-catalog). The header rides on every surface Blume controls: the dev server (check it with `curl -I localhost:4321`), static builds via the emitted `_headers` file (Netlify and Cloudflare), and Vercel server builds via the deploy's routing rules. Hosts that ignore `_headers` on static output (GitHub Pages, S3) can't send custom response headers at all — there, agents still find everything through `llms.txt` and `agent-readability.json` at the site root.
|
|
328
|
+
|
|
329
|
+
### API catalog
|
|
330
|
+
|
|
331
|
+
When the site publishes APIs, Blume generates an [RFC 9727](https://www.rfc-editor.org/rfc/rfc9727) API catalog at `/.well-known/api-catalog` — a [linkset](https://www.rfc-editor.org/rfc/rfc9264) that lets agents enumerate your APIs from the domain alone, served with its registered `application/linkset+json` media type on every build surface. There's nothing to configure: the catalog is derived from what's already in `blume.config.ts`. Each [OpenAPI or AsyncAPI reference](/docs/advanced/api-reference) becomes an entry anchored at its rendered docs route, with `service-doc` pointing at those docs and `service-desc` at the spec when it lives at a fetchable URL; the [MCP server](#mcp-server) becomes an entry with its discovery document as the service description:
|
|
332
|
+
|
|
333
|
+
```json .well-known/api-catalog
|
|
334
|
+
{
|
|
335
|
+
"linkset": [
|
|
336
|
+
{
|
|
337
|
+
"anchor": "https://docs.example.com/reference",
|
|
338
|
+
"service-doc": [
|
|
339
|
+
{ "href": "https://docs.example.com/reference", "type": "text/html" }
|
|
340
|
+
],
|
|
341
|
+
"service-desc": [{ "href": "https://api.example.com/openapi.json" }]
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"anchor": "https://docs.example.com/mcp",
|
|
345
|
+
"service-desc": [
|
|
346
|
+
{
|
|
347
|
+
"href": "https://docs.example.com/.well-known/mcp.json",
|
|
348
|
+
"type": "application/json"
|
|
349
|
+
}
|
|
350
|
+
],
|
|
351
|
+
"service-doc": [
|
|
352
|
+
{ "href": "https://docs.example.com/", "type": "text/html" }
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
A site with no API references and no MCP server emits no catalog — there'd be nothing in it. As everywhere, a `public/.well-known/api-catalog` file you ship yourself wins over the generated one.
|
|
360
|
+
|
|
361
|
+
### WebMCP
|
|
362
|
+
|
|
363
|
+
[WebMCP](https://webmachinelearning.github.io/webmcp/) is an emerging browser API that lets a page register tools directly with an agentic browser — no separate server connection needed. Every Blume page registers the docs' read-only surface on the page's model context: `search_docs` (site search), `get_page` (a page's [raw Markdown](#raw-markdown)), and `list_pages` (the [`llms.txt`](#llmstxt) index). The script is tiny, loads no search machinery until a tool is actually called, and silently no-ops in every browser without the API — which today is all of them outside [Chrome's early preview](https://developer.chrome.com/blog/webmcp-epp). It registers on whichever surface the in-flux spec exposes (`navigator.modelContext` or `document.modelContext`), via `provideContext` or per-tool `registerTool`.
|
|
364
|
+
|
|
365
|
+
It's on by default; set `webmcp: false` to opt out:
|
|
366
|
+
|
|
367
|
+
```ts blume.config.ts lineNumbers
|
|
368
|
+
ai: {
|
|
369
|
+
webmcp: false,
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Skills discovery
|
|
374
|
+
|
|
375
|
+
If your project ships [agent skills](https://agentskills.io) — the [Blume repo itself does](#agent-skill) — point `ai.skills` at the directory that holds them, and the build publishes them for discovery per the [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc):
|
|
376
|
+
|
|
377
|
+
```ts blume.config.ts lineNumbers
|
|
378
|
+
ai: {
|
|
379
|
+
skills: "./skills",
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The path resolves against your project root, and each subdirectory with a `SKILL.md` becomes a published skill. A skill that's a lone `SKILL.md` is copied verbatim to `/.well-known/agent-skills/<name>/SKILL.md` (`type: "skill-md"`); a skill with supporting resources (`scripts/`, `references/`, `assets/`) is bundled into a deterministic `.tar.gz` (`type: "archive"`) so its relative references resolve after unpacking, with script execute bits preserved. The discovery index at `/.well-known/agent-skills/index.json` carries the v0.2.0 `$schema` and, per skill, its name, type, description (from the `SKILL.md` frontmatter), artifact URL, and the SHA-256 digest clients verify downloads against.
|
|
384
|
+
|
|
385
|
+
Skills with a missing or spec-invalid `name`/`description` are skipped with a build warning rather than published broken, and a `public/.well-known/agent-skills/index.json` you ship yourself takes over the whole surface.
|
|
386
|
+
|
|
387
|
+
### DNS-based discovery (DNS-AID)
|
|
388
|
+
|
|
389
|
+
[DNS for AI Discovery](https://datatracker.ietf.org/doc/draft-mozleywilliams-dnsop-dnsaid/) is an emerging IETF draft that lets agents discover a site's AI surface before making a single HTTP request, by querying ServiceMode [SVCB/HTTPS records](https://www.rfc-editor.org/rfc/rfc9460) at a well-known DNS entrypoint. DNS records live in your zone, not in the build, so this is the one discovery surface Blume can't publish for you — instead, add a record with your DNS provider:
|
|
390
|
+
|
|
391
|
+
```txt
|
|
392
|
+
_index._agents.docs.example.com. 3600 IN HTTPS 1 docs.example.com. alpn=h2
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Use the `HTTPS` record type if your provider offers it (Vercel DNS does; it doesn't support the plain `SVCB` type), or a ServiceMode `SVCB` record with `alpn` and `port` parameters otherwise. The draft also recommends signing the zone with DNSSEC so validating resolvers return authenticated answers — providers like Cloudflare enable it in one click, while some (including Vercel DNS) don't support it at all.
|
|
396
|
+
|
|
397
|
+
`blume audit --url <origin>` checks this for you: when [`deployment.site`](/docs/deployment) is set, the network tier queries the entrypoint over DNS-over-HTTPS and reports the exact record to publish if none exists, plus whether the answers are DNSSEC-authenticated. Set `BLUME_DOH_URL` to point the lookup at your own resolver if your network blocks the public ones (Google, Cloudflare).
|
|
398
|
+
|
|
399
|
+
### Web Bot Auth
|
|
400
|
+
|
|
401
|
+
[Web Bot Auth](https://datatracker.ietf.org/wg/webbotauth/about/) works in the other direction: it's not about agents reading your docs, but about **your organization's agents identifying themselves** when they make requests elsewhere. Your agents sign their requests with [HTTP Message Signatures](https://www.rfc-editor.org/rfc/rfc9421), and receiving sites verify them against a public-key directory published on your domain. If your org runs agents and your Blume site lives at the domain they identify as, publish their public keys:
|
|
402
|
+
|
|
403
|
+
```ts blume.config.ts lineNumbers
|
|
404
|
+
ai: {
|
|
405
|
+
webBotAuth: {
|
|
406
|
+
keys: [{ kty: "OKP", crv: "Ed25519", x: "JrQLj5P_89iXES9-vFgrIy29c…" }],
|
|
407
|
+
},
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Blume then serves the JWKS at `/.well-known/http-message-signatures-directory` with its registered media type on every build surface. The directory is public by definition, so the config only admits public keys — a JWK containing private material (`d`, `p`, `q`, …) fails validation with an error rather than shipping a leaked credential. Generate an Ed25519 pair with:
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
node -e 'const { generateKeyPairSync } = require("node:crypto"); const { publicKey, privateKey } = generateKeyPairSync("ed25519"); console.log("public: ", JSON.stringify(publicKey.export({ format: "jwk" }))); console.log("private:", JSON.stringify(privateKey.export({ format: "jwk" })))'
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
The public JWK goes in the config above; the private one goes wherever your signing agent runs (a secret manager, never the repo). If your organization doesn't operate agents, skip this — an empty directory advertises nothing worth verifying.
|
|
418
|
+
|
|
419
|
+
Since `blume.config.ts` is executed at build time, the key doesn't have to be hardcoded — load it from a build-time environment variable to keep the config free of key blobs and rotate without a commit:
|
|
420
|
+
|
|
421
|
+
```ts blume.config.ts lineNumbers
|
|
422
|
+
const webBotAuthKey = process.env.WEB_BOT_AUTH_PUBLIC_JWK;
|
|
423
|
+
|
|
424
|
+
export default defineConfig({
|
|
425
|
+
ai: {
|
|
426
|
+
webBotAuth: {
|
|
427
|
+
keys: webBotAuthKey ? [JSON.parse(webBotAuthKey)] : [],
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Environments without the variable publish no directory, and a key loaded this way is validated exactly like an inline one — including the private-material check. (The public key isn't a secret, so committing it inline is equally fine; the env var is an ergonomic choice, not a security one.)
|
|
434
|
+
|
|
311
435
|
## Agent skill
|
|
312
436
|
|
|
313
437
|
Building a Blume site with the help of a coding agent? Install the Blume [agent skill](https://docs.claude.com/en/docs/claude-code/skills) so it knows how Blume works without you explaining it:
|
|
@@ -316,6 +440,6 @@ Building a Blume site with the help of a coding agent? Install the Blume [agent
|
|
|
316
440
|
npx skills add haydenbleasel/blume
|
|
317
441
|
```
|
|
318
442
|
|
|
319
|
-
The skill teaches the agent what Blume is and how to scaffold, write, and configure a site, and points it at the full docs bundled in the installed package (`
|
|
443
|
+
The skill teaches the agent what Blume is and how to scaffold, write, and configure a site, and points it at the full docs bundled in the installed package (the `docs/` directory inside `blume`, wherever your package manager installs it).
|
|
320
444
|
|
|
321
445
|
It's one of the [agent skills Blume ships](/docs/advanced/skills), alongside a skill for keeping docs in sync with your product from a scheduled agent run.
|
|
@@ -190,7 +190,25 @@ content: {
|
|
|
190
190
|
| `pages` | `"pages"` | Folder for custom `.astro` pages. |
|
|
191
191
|
| `defaultType` | `"doc"` | Page `type` used when frontmatter omits it. |
|
|
192
192
|
|
|
193
|
-
Static assets live in `public/` — a file at `public/logo.png` is served at `/logo.png`, so a reference like `` resolves against `public/images/create.png`.
|
|
193
|
+
Static assets live in `public/` — a file at `public/logo.png` is served at `/logo.png`, so a reference like `` resolves against `public/images/create.png`. Images referenced by **relative path** (``) live next to your content instead, and are [optimized at build time](/docs/content/syntax#links-and-images).
|
|
194
|
+
|
|
195
|
+
## Images
|
|
196
|
+
|
|
197
|
+
Local images referenced by relative path are optimized automatically at build time — compressed, converted to WebP, and given intrinsic `width`/`height` attributes so the layout doesn't shift while they load. There's nothing to configure; see [Links and images](/docs/content/syntax#links-and-images) for authoring guidance.
|
|
198
|
+
|
|
199
|
+
Remote images are served untouched by default. To have Blume download and optimize them at build time too, authorize their hosts:
|
|
200
|
+
|
|
201
|
+
```ts blume.config.ts lineNumbers
|
|
202
|
+
image: {
|
|
203
|
+
domains: ["cdn.example.com"],
|
|
204
|
+
remotePatterns: [{ protocol: "https", hostname: "**.example.com" }],
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Option | Default | Description |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| `domains` | `[]` | Hostnames whose remote images may be optimized. |
|
|
211
|
+
| `remotePatterns` | `[]` | Pattern-based authorization (`protocol`, `hostname`, `port`, `pathname`); hostnames accept `*.` (one level) and `**.` (any depth) wildcards. |
|
|
194
212
|
|
|
195
213
|
## Frontmatter
|
|
196
214
|
|
|
@@ -80,6 +80,8 @@ i18n: {
|
|
|
80
80
|
|
|
81
81
|
The same tokenizer serves the search dialog, the MCP server's `search_docs` tool, and Ask AI grounding. On a mixed-language site the whole index shares the default locale's tokenizer — that's safe, because Latin words survive segmentation intact, so pages in English (or any spaced language) stay searchable alongside the default language.
|
|
82
82
|
|
|
83
|
+
Japanese and Chinese go one step further. Segmenting alone indexes a compound term as its parts — 資金決済法 as 資金, 決済 and 法 — which lets a page mentioning each part somewhere outrank the page the term is actually about. Han, Hiragana and Katakana are therefore indexed as overlapping character pairs, and queries on those indexes prefer pages carrying a term's pairs together, loosening to any-pair matching when no page carries them all, so typing a whole sentence still returns its closest pages. Korean and Thai keep their segmented words.
|
|
84
|
+
|
|
83
85
|
### FlexSearch
|
|
84
86
|
|
|
85
87
|
A second keyless, client-side option. It reuses the same `/blume-search.json` index Orama ships and builds a [FlexSearch](https://github.com/nextapps-de/flexsearch) document index in the browser. Works in `blume dev` and `blume build`.
|
|
@@ -149,9 +149,27 @@ Every palette color accepts any CSS color — hex, `oklch(…)`, `rgb(…)`, and
|
|
|
149
149
|
|
|
150
150
|
Emoji in a page title or site title render as [Twemoji](https://github.com/jdecked/twemoji) glyphs, fetched from a CDN while the card renders — so a build whose titles contain emoji needs network access. Each glyph is fetched once per build, however many pages use it.
|
|
151
151
|
|
|
152
|
-
###
|
|
152
|
+
### Show, hide, or override card layers
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
Beyond the headline, the card carries three optional layers: the **brand mark** in the top-left (your logo, or an accent tile with the site title's initial), the **subtitle** under the headline (your site `description`), and a **footer** with your repo slug (from `github`) and the site's URL — the deployment site's host plus [`deployment.base`](/docs/deployment#subpath-deploys), so a GitHub Pages project site reads `user.github.io/repo`. Override any of them with a string of your own, or hide one with `false`:
|
|
155
|
+
|
|
156
|
+
```ts blume.config.ts lineNumbers
|
|
157
|
+
seo: {
|
|
158
|
+
og: {
|
|
159
|
+
site: "docs.acme.com", // footer URL text, or false to hide it
|
|
160
|
+
description: false, // hide the subtitle; a string overrides it
|
|
161
|
+
logo: false, // no brand mark at all — not even the initial tile
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Card fonts
|
|
167
|
+
|
|
168
|
+
By default the card renders in Takumi's built-in font, which covers only Latin glyphs — a title in another script (Japanese, Chinese, Korean, Arabic, …) would render as tofu, empty boxes.
|
|
169
|
+
|
|
170
|
+
**Set [`theme.fonts`](/docs/configuration/theming#fonts) and the card follows it.** When your config picks its own fonts, the generated cards automatically render the headline in your display font and the description and footer in your body font, so shared links match the site — including non-Latin coverage, with nothing to configure here. (Families from non-Google providers are skipped — the card renderer can only fetch from Google Fonts — but local font files work.)
|
|
171
|
+
|
|
172
|
+
To use different fonts on cards than on the site, or to add script coverage without touching the theme, set `og.fonts` explicitly — it always wins over the theme-derived fonts:
|
|
155
173
|
|
|
156
174
|
```ts blume.config.ts lineNumbers
|
|
157
175
|
seo: {
|
|
@@ -159,12 +177,17 @@ seo: {
|
|
|
159
177
|
fonts: [
|
|
160
178
|
"Noto Sans JP",
|
|
161
179
|
{ name: "Inter", weight: [400, 700] },
|
|
180
|
+
{ name: "Berkeley Mono", src: "./fonts/BerkeleyMono-Regular.woff2" },
|
|
162
181
|
],
|
|
163
182
|
},
|
|
164
183
|
}
|
|
165
184
|
```
|
|
166
185
|
|
|
167
|
-
Each entry is a family name,
|
|
186
|
+
Each entry is a Google Fonts family name, an object pinning its `weight` (a number, a list, or a variable range like `"100..900"`) and `style` (`"normal"`, `"italic"`, or both), or a local font file — `src` resolves from the project root, with optional `weight` and `style` when the file's own metadata shouldn't decide.
|
|
187
|
+
|
|
188
|
+
Google families are fetched at build — so a build that uses them needs network access — and the renderer only pulls the glyph subsets each title actually uses. Fallback is per-glyph, so adding a family only affects glyphs the other fonts can't draw.
|
|
189
|
+
|
|
190
|
+
An explicit `og.fonts: []` opts out entirely: cards keep the built-in font even when `theme.fonts` is set.
|
|
168
191
|
|
|
169
192
|
### Custom page titles
|
|
170
193
|
|
|
@@ -79,7 +79,7 @@ theme: {
|
|
|
79
79
|
|
|
80
80
|
Fonts are **self-hosted**: Blume downloads them at build time and serves them from your own site, so there's no runtime request to Google and no layout shift (Astro generates fallback-metric faces automatically).
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
A bare string is a Google Fonts slug from the curated set below:
|
|
83
83
|
|
|
84
84
|
| Category | Slugs |
|
|
85
85
|
| --- | --- |
|
|
@@ -87,7 +87,49 @@ Each value is a Google Fonts slug from the curated set below:
|
|
|
87
87
|
| Serif | `ibm-plex-serif` `lora` `merriweather` `playfair-display` `source-serif-4` |
|
|
88
88
|
| Mono | `fira-code` `geist-mono` `ibm-plex-mono` `jetbrains-mono` `roboto-mono` `source-code-pro` `space-mono` |
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
#### Any provider family
|
|
91
|
+
|
|
92
|
+
Need a family that isn't in the curated set — say, one that covers a non-Latin script? Pass an object with the family's exact name. It's self-hosted and optimized the same way:
|
|
93
|
+
|
|
94
|
+
```ts blume.config.ts lineNumbers
|
|
95
|
+
theme: {
|
|
96
|
+
fonts: {
|
|
97
|
+
display: { name: "Noto Sans JP", weights: [400, 700] },
|
|
98
|
+
body: { name: "Noto Sans JP", weights: [400, 500, 700] },
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- **`name`** — the family name exactly as the provider lists it.
|
|
104
|
+
- **`provider`** — where the family comes from: `google` (default), `fontsource`, `bunny`, or `fontshare`.
|
|
105
|
+
- **`weights`** — the weights to load, as numbers or a variable range like `"100..900"`. Defaults to `[400, 500, 600, 700]`.
|
|
106
|
+
- **`fallback`** — the system stack shown while the font loads and for missing glyphs: `sans`, `serif`, or `mono`. Defaults to `mono` for the mono role and `sans` otherwise.
|
|
107
|
+
|
|
108
|
+
#### Local font files
|
|
109
|
+
|
|
110
|
+
For a font you own (or one no provider serves), point a role at font files in your project. Each variant becomes one `@font-face`:
|
|
111
|
+
|
|
112
|
+
```ts blume.config.ts lineNumbers
|
|
113
|
+
theme: {
|
|
114
|
+
fonts: {
|
|
115
|
+
display: {
|
|
116
|
+
name: "Berkeley Mono",
|
|
117
|
+
variants: [
|
|
118
|
+
{ src: "./fonts/BerkeleyMono-Regular.woff2", weight: 400 },
|
|
119
|
+
{ src: "./fonts/BerkeleyMono-Bold.woff2", weight: 700 },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Paths resolve from the project root. `weight` and `style` (`normal`, `italic`, `oblique`) are optional — Astro reads them from the font file when omitted.
|
|
127
|
+
|
|
128
|
+
:::note
|
|
129
|
+
When you set `theme.fonts` explicitly, your display and body fonts also style the generated [Open Graph cards](/docs/configuration/seo#card-fonts) automatically, so shared links match the site. Non-Google provider families are skipped there (the card renderer can only fetch from Google Fonts); local files work everywhere.
|
|
130
|
+
:::
|
|
131
|
+
|
|
132
|
+
Want to drop back to the system stack? Override the `--blume-font-*` tokens directly in [`theme.css`](#themecss).
|
|
91
133
|
|
|
92
134
|
### Dark-mode colors
|
|
93
135
|
|
package/docs/content/syntax.mdx
CHANGED
|
@@ -94,18 +94,34 @@ Tabulate structured data — config options, comparison matrices, parameter list
|
|
|
94
94
|
| `blume build` | Build the static site | `dist/` |
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
For a table without a header row — key–value pairs, for example — leave the header cells empty. Markdown requires the header and divider rows syntactically, but Blume drops the empty header from the rendered table.
|
|
98
|
+
|
|
99
|
+
```md
|
|
100
|
+
| | |
|
|
101
|
+
| -------------- | -------- |
|
|
102
|
+
| Current status | E-3 visa |
|
|
103
|
+
```
|
|
104
|
+
|
|
97
105
|
## Links and images
|
|
98
106
|
|
|
99
|
-
Link to other pages or external sites. Images accept any path under `public/` (served at the site root) or a remote URL.
|
|
107
|
+
Link to other pages or external sites. Images accept a relative path to a file next to your content, any path under `public/` (served at the site root), or a remote URL.
|
|
100
108
|
|
|
101
109
|
Read the [quickstart](/docs/quickstart) to get started.
|
|
102
110
|
|
|
103
111
|
```md
|
|
104
112
|
Read the [quickstart](/docs/quickstart) to get started.
|
|
105
113
|
|
|
106
|
-

|
|
107
115
|
```
|
|
108
116
|
|
|
117
|
+
**Prefer relative paths for local images** — they're optimized at build time: compressed, converted to WebP, and stamped with intrinsic `width`/`height` so the page doesn't shift while loading. Keep the image next to the page that uses it (or in a shared folder inside your content directory) and reference it relatively:
|
|
118
|
+
|
|
119
|
+
```md
|
|
120
|
+

|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Absolute paths under `public/` (``) are served verbatim with no optimization — use them for files that must keep their exact bytes and URL, like a logo referenced from outside your docs. Remote images are also passed through untouched unless their host is authorized in the [`image` config](/docs/configuration#images).
|
|
124
|
+
|
|
109
125
|
Content images are click-to-zoom by default — readers can click any image to open it in a lightbox. Turn this off with `markdown: { imageZoom: false }` in `blume.config.ts`, or opt a single image out with `data-no-zoom`.
|
|
110
126
|
|
|
111
127
|
## Horizontal rule
|
package/docs/reference/cli.mdx
CHANGED
|
@@ -56,7 +56,7 @@ blume <command> [options]
|
|
|
56
56
|
- `blume audit --external` — probe outbound links over the network.
|
|
57
57
|
- `blume audit --only <check|category>` / `--skip <check|category>` — narrow the report while you work through it (comma-separated).
|
|
58
58
|
- `blume audit --list-checks` — print every check the audit can report.
|
|
59
|
-
- `blume audit --verbose` — list every affected page
|
|
59
|
+
- `blume audit --verbose` — list every affected page with each finding's full detail, such as which link target is broken.
|
|
60
60
|
- `blume audit --json` — emit the report as JSON on stdout.
|
|
61
61
|
- `blume audit --claude` / `--codex` — hand the findings to Claude Code or Codex to fix interactively.
|
|
62
62
|
- `blume eval` — run the questions in `evals.yaml` through an agent that reads only your docs; see [Evals](/docs/reference/eval).
|
|
@@ -140,7 +140,7 @@ blume build
|
|
|
140
140
|
blume audit
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
Findings are grouped by check rather than listed per page, so the report reads as a to-do list. Use `--verbose` to expand every affected page, and `--only`/`--skip` to work through one category at a time. `blume audit --list-checks` prints the full catalog.
|
|
143
|
+
Findings are grouped by check rather than listed per page, so the report reads as a to-do list. Use `--verbose` to expand every affected page with its full detail, and `--only`/`--skip` to work through one category at a time. `blume audit --list-checks` prints the full catalog.
|
|
144
144
|
|
|
145
145
|
### Failing CI
|
|
146
146
|
|
|
@@ -186,6 +186,6 @@ Two limits worth stating plainly:
|
|
|
186
186
|
Anything the audit did not run is reported as skipped rather than silently passing:
|
|
187
187
|
|
|
188
188
|
```
|
|
189
|
-
⊘ network skipped — pass --url <origin> (
|
|
189
|
+
⊘ network skipped — pass --url <origin> (11 checks)
|
|
190
190
|
⊘ external skipped — pass --external (2 checks)
|
|
191
191
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blume",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Documentation that's fast, AI-ready, and zero-config.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"@tailwindcss/typography": "^0.5.20",
|
|
88
88
|
"@tailwindcss/vite": "^4",
|
|
89
89
|
"@vercel/analytics": "^2.0.1",
|
|
90
|
-
"ai": "^
|
|
90
|
+
"ai": "^7.0.42",
|
|
91
91
|
"astro": "^7.0.2",
|
|
92
92
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
93
93
|
"citty": "^0.1.6",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"gray-matter": "^4.0.3",
|
|
99
99
|
"jiti": "^2.4.0",
|
|
100
100
|
"js-yaml": "^4.1.0",
|
|
101
|
-
"katex": "^0.
|
|
101
|
+
"katex": "^0.18.1",
|
|
102
102
|
"marked": "^18.0.5",
|
|
103
103
|
"mermaid": "^11.15.0",
|
|
104
104
|
"node-html-parser": "^9.0.0",
|
|
@@ -107,15 +107,16 @@
|
|
|
107
107
|
"react": "^19.0.0",
|
|
108
108
|
"react-dom": "^19.0.0",
|
|
109
109
|
"satteri": "^0.9.5",
|
|
110
|
+
"sharp": "^0.35.3",
|
|
110
111
|
"shiki": "^4.2.0",
|
|
111
112
|
"simple-icons": "^13.0.0",
|
|
112
|
-
"tailwindcss": "^4.3.
|
|
113
|
+
"tailwindcss": "^4.3.3",
|
|
113
114
|
"takumi-js": "^2.2.1",
|
|
114
115
|
"tinyglobby": "^0.2.10",
|
|
115
116
|
"twoslash": "^0.3.9",
|
|
116
117
|
"typescript": "^6.0.3",
|
|
117
118
|
"undici": "^8.6.0",
|
|
118
|
-
"zod": "^3.
|
|
119
|
+
"zod": "^4.3.6"
|
|
119
120
|
},
|
|
120
121
|
"devDependencies": {
|
|
121
122
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -126,16 +127,16 @@
|
|
|
126
127
|
"bun-types": "^1.3.14"
|
|
127
128
|
},
|
|
128
129
|
"peerDependencies": {
|
|
129
|
-
"@ai-sdk/openai-compatible": "^
|
|
130
|
+
"@ai-sdk/openai-compatible": "^3.0.0",
|
|
130
131
|
"@astrojs/cloudflare": "^14.0.0",
|
|
131
132
|
"@astrojs/netlify": "^8.0.0",
|
|
132
133
|
"@astrojs/svelte": "^9.0.0",
|
|
133
134
|
"@astrojs/vue": "^7.0.0",
|
|
134
135
|
"@mixedbread/sdk": "^0.76.0",
|
|
135
136
|
"@notionhq/client": "^2.2.15",
|
|
136
|
-
"@openrouter/ai-sdk-provider": "^
|
|
137
|
+
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
137
138
|
"@oramacloud/client": "^2.1.0",
|
|
138
|
-
"@sanity/client": "^6.21.0",
|
|
139
|
+
"@sanity/client": "^6.21.0 || ^7.0.0",
|
|
139
140
|
"algoliasearch": "^5.55.0",
|
|
140
141
|
"flexsearch": "^0.8.0",
|
|
141
142
|
"typesense": "^3.0.0"
|
package/skills/blume/SKILL.md
CHANGED
|
@@ -64,10 +64,12 @@ The Blume CLI discovers your content, builds a content graph, and generates a hi
|
|
|
64
64
|
|
|
65
65
|
## Full documentation
|
|
66
66
|
|
|
67
|
-
This is a high-level overview. For complete, authoritative docs — configuration reference, every CLI command and flag, component APIs, content authoring, navigation, search, SEO, AI features, theming, and deployment — read the bundled
|
|
67
|
+
This is a high-level overview. For complete, authoritative docs — configuration reference, every CLI command and flag, component APIs, content authoring, navigation, search, SEO, AI features, theming, and deployment — read the `docs/` directory bundled inside the installed `blume` package.
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
**Locate the package first — it is not always at the repository root.** In a workspace monorepo (pnpm especially), `blume` is installed in the depending workspace's `node_modules` (e.g. `apps/docs/node_modules/blume/docs`), not the root. From the package that depends on `blume`, this prints the exact location:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
node -e "console.log(require.resolve('blume/package.json'))"
|
|
71
73
|
```
|
|
72
74
|
|
|
73
|
-
Start with `
|
|
75
|
+
The docs sit in `docs/` next to that `package.json`. Start with `docs/index.mdx` (Introduction) and `docs/01-quickstart.mdx`, then browse the `configuration/`, `content/`, `reference/`, and `advanced/` sections for specifics.
|