@aotter/mantle 0.0.11-alpha.63 → 0.0.11-alpha.64

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 (41) hide show
  1. package/README.md +87 -12
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +52 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/generate.d.ts +2 -0
  7. package/dist/generate.d.ts.map +1 -0
  8. package/dist/generate.js +181 -0
  9. package/dist/generate.js.map +1 -0
  10. package/dist/skills.d.ts +2 -0
  11. package/dist/skills.d.ts.map +1 -0
  12. package/dist/skills.js +80 -0
  13. package/dist/skills.js.map +1 -0
  14. package/dist/update.d.ts +2 -0
  15. package/dist/update.d.ts.map +1 -0
  16. package/dist/update.js +387 -0
  17. package/dist/update.js.map +1 -0
  18. package/docs/adr/0001-four-atom-manifest-model.md +6 -7
  19. package/docs/adr/0007-ai-as-primary-author.md +100 -138
  20. package/docs/adr/0008-structured-diagnostic-shape.md +79 -99
  21. package/docs/adr/0009-consumer-supplied-manifests.md +101 -228
  22. package/docs/adr/0012-views-as-public-rest.md +43 -15
  23. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +43 -17
  24. package/docs/adr/0018-core-starters-repository-boundary.md +155 -0
  25. package/docs/adr/README.md +8 -6
  26. package/docs/cloudflare-low-level-composition.md +94 -0
  27. package/docs/design-atoms.md +59 -57
  28. package/docs/design-references/editorial-blog-2026-05-05.md +7 -7
  29. package/docs/labels.md +1 -1
  30. package/docs/media-uploads.md +1 -1
  31. package/docs/release-process.md +156 -523
  32. package/package.json +9 -6
  33. package/skills/README.md +20 -16
  34. package/skills/develop/SKILL.md +4 -4
  35. package/skills/install/SKILL.md +16 -2
  36. package/skills/plugin/SKILL.md +1 -1
  37. package/skills/provision/SKILL.md +1 -1
  38. package/skills/theme/SKILL.md +12 -10
  39. package/skills/update/SKILL.md +31 -19
  40. package/skills/customize-design/SKILL.md +0 -215
  41. package/skills/extend/SKILL.md +0 -257
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle",
3
- "version": "0.0.11-alpha.63",
3
+ "version": "0.0.11-alpha.64",
4
4
  "description": "Umbrella entry for @aotter/mantle. Adopters install this one package and import from subpaths: /spec, /runtime, /cloudflare, /admin-ui. Sub-packages remain individually installable on npm for tooling / alt-adapter authors. The Netlify adapter ships as a private workspace stub in v0.1 — its subpath will be added when the impl lands in v0.2.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://mantle.tools/",
@@ -14,6 +14,7 @@
14
14
  "main": "./dist/spec.js",
15
15
  "types": "./dist/spec.d.ts",
16
16
  "bin": {
17
+ "mantle": "./dist/cli.js",
17
18
  "mantle-harness": "./dist/harness-cli.js"
18
19
  },
19
20
  "publishConfig": {
@@ -54,10 +55,10 @@
54
55
  "README.md"
55
56
  ],
56
57
  "dependencies": {
57
- "@aotter/mantle-cloudflare": "0.0.11-alpha.63",
58
- "@aotter/mantle-runtime": "0.0.11-alpha.63",
59
- "@aotter/mantle-admin-ui": "0.0.11-alpha.63",
60
- "@aotter/mantle-spec": "0.0.11-alpha.63"
58
+ "@aotter/mantle-admin-ui": "0.0.11-alpha.64",
59
+ "@aotter/mantle-spec": "0.0.11-alpha.64",
60
+ "@aotter/mantle-cloudflare": "0.0.11-alpha.64",
61
+ "@aotter/mantle-runtime": "0.0.11-alpha.64"
61
62
  },
62
63
  "peerDependencies": {
63
64
  "@cloudflare/workers-oauth-provider": "^0.8.0",
@@ -73,6 +74,7 @@
73
74
  "better-auth": "^1.6.23",
74
75
  "hono": "^4.12.30",
75
76
  "typescript": "^6.0.3",
77
+ "vitest": "^4.1.10",
76
78
  "zod": "^4.4.2"
77
79
  },
78
80
  "engines": {
@@ -81,6 +83,7 @@
81
83
  "scripts": {
82
84
  "prebuild": "rm -rf dist .tsbuildinfo",
83
85
  "build": "tsc -b tsconfig.lib.json",
84
- "typecheck": "tsc --noEmit -p tsconfig.lib.json"
86
+ "typecheck": "tsc --noEmit -p tsconfig.lib.json",
87
+ "test": "vitest run"
85
88
  }
86
89
  }
package/skills/README.md CHANGED
@@ -9,26 +9,25 @@ Agent-readable skill briefs for consumers of `@aotter/mantle-*`. Discoverable by
9
9
  | [`theme`](theme/SKILL.md) | `mantle:theme`: Core-owned visual workflow. Reads project context but does not depend on starter-owned skill semantics. |
10
10
  | [`update`](update/SKILL.md) | `mantle:update`: Core-owned drift check workflow for SDK, starter snapshots, and plugin lockfiles. |
11
11
  | [`install`](install/SKILL.md) | User wants to create a local Mantle site from a deterministic starter bundle or continue an existing local / landing-generated project. |
12
- | [`customize-design`](customize-design/SKILL.md) | Legacy publication-specific design guide. Prefer `mantle:theme` for generated repos. |
13
- | [`extend`](extend/SKILL.md) | Legacy atom-authoring guide. Prefer `mantle:develop` or `mantle:plugin` depending on whether the work is one-off or installable. |
14
12
  | [`provision`](provision/SKILL.md) | User wants a local or landing-generated project shipped to Cloudflare with production auth and operator handoff. |
15
13
 
16
- The skills target `mantle@v0.1.0`. Each one names its assumed grammar version
17
- in front-matter `metadata.applies_to`; future versions add a sibling SKILL.md
18
- or update the existing one.
14
+ The skills target Mantle's v0.1 grammar. The installed package version, not
15
+ duplicated skill prose, selects the exact runtime and embedded docs.
19
16
 
20
17
  ## Skill authority
21
18
 
22
- The `mantle:*` namespace is owned by `@aotter/mantle`. Generated starters may
23
- vendor Core workflow skills pinned to their starter ref for offline use. Use
24
- those skills for workflow and compatibility recovery; use the installed
25
- package version plus `node_modules/@aotter/mantle/docs/` for runtime/API
26
- behavior. Starter launch files and plugin recipes are project context, not
27
- competing contracts.
19
+ The `mantle:*` namespace is owned by `@aotter/mantle`. Run `mantle skills` to
20
+ project the installed package's `develop`, `plugin`, `theme`, and `update`
21
+ skills to identical `.agent` and `.claude` paths; use `mantle skills --check`
22
+ to fail closed on drift. The installed package and
23
+ `node_modules/@aotter/mantle/docs/` are the single version-matched authority.
24
+ Starter launch files and plugin recipes are project context, not competing
25
+ contracts.
28
26
 
29
- ## Marketplace install
27
+ ## Source-repository marketplace install
30
28
 
31
- The repo is also an agent plugin bundle:
29
+ The source repository is also an agent plugin bundle. These manifests are not
30
+ duplicated into the npm package:
32
31
 
33
32
  - Claude Code: `.claude-plugin/plugin.json` plus `.claude-plugin/marketplace.json`.
34
33
  - Codex: `.codex-plugin/plugin.json` plus `.agents/plugins/marketplace.json`.
@@ -37,11 +36,14 @@ The repo is also an agent plugin bundle:
37
36
 
38
37
  ## Audience
39
38
 
40
- These are written for **AI agents acting on behalf of consumers of mantle**, not for agents maintaining the mantle SDK itself. SDK-internal guidance lives in [`/CLAUDE.md`](../CLAUDE.md). Two audiences, two artifacts.
39
+ These are written for **AI agents acting on behalf of consumers of mantle**,
40
+ not for agents maintaining the Mantle SDK itself. SDK maintainers use the
41
+ repo-root `CLAUDE.md` from a source checkout; it is intentionally not shipped
42
+ inside the npm package. Two audiences, two artifacts.
41
43
 
42
44
  ## Discoverability
43
45
 
44
- The skills target ADR-0007's "AI as primary author" thesis: agents reach these files by URL when the user invokes them by intent ("install mantle", "extend my CMS", "deploy"). No `/skill install` slash command is required — point the agent at the GitHub raw URL or pass the markdown content directly.
46
+ The skills target ADR-0007's "AI as primary author" thesis: agents reach these files by URL when the user invokes them by intent ("install mantle", "develop my Mantle site", "deploy"). No `/skill install` slash command is required — point the agent at a version tag or pass the version-matched markdown content directly.
45
47
 
46
48
  ## Conventions
47
49
 
@@ -56,4 +58,6 @@ Each SKILL.md ships:
56
58
  - **Don't** — reviewer-style list of patterns the agent must reject (often citing ADRs).
57
59
  - **When you're done** — what to report back to the user.
58
60
 
59
- If you're writing a new SKILL, follow the same structure. The CLI commands referenced are stable across v0.1.x.
61
+ If you're writing a new SKILL, follow the same structure. Commands and prose
62
+ must match the package version that carries the skill; later prereleases may
63
+ revise both together.
@@ -4,14 +4,14 @@ description: Work on any Mantle project using the Core SDK contract. Use for man
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/develop/SKILL.md
7
- applies_to: mantle@v0.1.0
7
+ applies_to: mantle grammar v0.1
8
8
  ---
9
9
 
10
10
  # Mantle Develop
11
11
 
12
- This is the Core workflow skill for an existing Mantle project. A repo-local
13
- copy may carry compatibility guidance pinned to the starter ref; the installed
14
- package version and embedded docs govern runtime/API behavior.
12
+ This is the Core workflow skill for an existing Mantle project. Repo-local
13
+ copies are byte-for-byte projections from the installed package; its embedded
14
+ docs govern runtime/API behavior.
15
15
 
16
16
  ## First Read
17
17
 
@@ -4,7 +4,7 @@ description: Start a new Mantle site locally from a deterministic starter bundle
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/install/SKILL.md
7
- applies_to: mantle@v0.1.0
7
+ applies_to: mantle grammar v0.1
8
8
  ---
9
9
 
10
10
  # Mantle Install
@@ -75,6 +75,8 @@ the generated bundle JSON.
75
75
  cd <target-dir>
76
76
  git init -b main
77
77
  pnpm install --frozen-lockfile
78
+ pnpm exec mantle skills
79
+ pnpm exec mantle skills --check
78
80
  pnpm validate
79
81
  pnpm typecheck
80
82
  pnpm dev
@@ -93,6 +95,18 @@ Read these before editing:
93
95
  1. `.mantle/launch-state.json`, `.mantle/features.json`, and
94
96
  `.mantle/handoff.md`.
95
97
  2. `package.json` for the installed `@aotter/mantle*` versions.
98
+
99
+ Install the locked dependency graph and replace any stale projected Core
100
+ skills before reading them:
101
+
102
+ ```bash
103
+ pnpm install --frozen-lockfile
104
+ pnpm exec mantle skills
105
+ pnpm exec mantle skills --check
106
+ ```
107
+
108
+ Then read:
109
+
96
110
  3. Repo-local Mantle skills under `.agent/skills/` or `.claude/skills/`.
97
111
  4. Matching embedded docs under `node_modules/@aotter/mantle/docs/`.
98
112
 
@@ -105,7 +119,7 @@ live URL, and auth response, then skip work that is already complete.
105
119
  Then run:
106
120
 
107
121
  ```bash
108
- pnpm install --frozen-lockfile
122
+ pnpm exec mantle skills --check
109
123
  pnpm validate
110
124
  pnpm typecheck
111
125
  ```
@@ -4,7 +4,7 @@ description: Discover, plan, apply, and verify Mantle marketplace plugins throug
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/plugin/SKILL.md
7
- applies_to: mantle@v0.1.0
7
+ applies_to: mantle grammar v0.1
8
8
  ---
9
9
 
10
10
  # Mantle Plugin
@@ -4,7 +4,7 @@ description: Ship a local or Mantle landing-generated project to Cloudflare and
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/provision/SKILL.md
7
- applies_to: mantle@v0.1.0
7
+ applies_to: mantle grammar v0.1
8
8
  ---
9
9
 
10
10
  # Provision a Mantle Project
@@ -4,31 +4,33 @@ description: Apply brand and visual direction in a generated Mantle project usin
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/theme/SKILL.md
7
- applies_to: mantle@v0.1.0
7
+ applies_to: mantle grammar v0.1
8
8
  ---
9
9
 
10
10
  # Mantle Theme
11
11
 
12
- Theme work is project-owned source editing. Starters may ship Kiwa files,
12
+ Theme work is project-owned source editing. Starters may ship UI files,
13
13
  tokens, or recipes, but the skill contract is Core-owned.
14
14
 
15
15
  ## First Read
16
16
 
17
17
  1. `.mantle/handoff.md` and `.mantle/recipes/` if present.
18
- 2. `styles/`, `components/`, `src/web/`, `src/theme*`, and `kiwa-ui.json`
18
+ 2. `styles/`, `components/`, `src/web/`, `src/theme*`, and UI-library config
19
19
  if present.
20
- 3. `kiwa/manifest.json` for the pinned source and per-file `mirrors`.
20
+ 3. A vendored UI palette's manifest and license, if present.
21
21
  4. `manifests/` to understand which content shape drives the public UI.
22
22
 
23
23
  ## Ownership
24
24
 
25
25
  - `styles/globals.css` is the token contract. Start a whole-site reskin with
26
- its `:root` and `.dark` values; Kiwa components inherit those variables.
27
- - `src/web/` is project-owned. Put new sections and page composition there.
28
- - `kiwa/` is a vendored snapshot. In `components/`, treat paths whose manifest
29
- entry's `mirrors` contains `blank` as sync-managed in the project root. For
30
- a structural variant, fork or wrap the block under `src/web/sections/`
31
- instead of editing a synced file.
26
+ its `:root` and `.dark` values; runtime components inherit those variables.
27
+ - `components/` is the runtime-facing component surface when present.
28
+ `src/web/` is project-owned composition; put new sections there.
29
+ - If the project includes a vendored UI reference palette, treat it as
30
+ offline source material and provenance, not runtime source. Copy only a
31
+ needed primitive or block into the project's runtime directories, or
32
+ fork/wrap it under `src/web/sections/`; do not import the palette from
33
+ Worker or runtime code.
32
34
 
33
35
  ## Work
34
36
 
@@ -4,7 +4,7 @@ description: Check a Mantle project for drift against its Core SDK, starter sour
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/update/SKILL.md
7
- applies_to: mantle@v0.1.0
7
+ applies_to: mantle grammar v0.1
8
8
  ---
9
9
 
10
10
  # Mantle Update
@@ -19,23 +19,37 @@ Use this for drift checks. Do not blindly overwrite user-owned code.
19
19
  3. `.mantle/plugins.json` and `.mantle/plugins.lock.json` when plugins are
20
20
  installed.
21
21
  4. Existing project scripts such as `mantle:update`, `validate`, and
22
- `typecheck`.
22
+ `typecheck`; the installed `mantle update` command is authoritative.
23
23
 
24
24
  ## Workflow
25
25
 
26
26
  1. Start from a clean git worktree.
27
27
  2. Resolve a mutable target branch to its current commit SHA, then run the
28
- project's existing update or compare script with that immutable ref.
29
- If it exits before writing a report (for example, an older
30
- `mantle:update` rejects a new bundle placeholder), fetch the provision
31
- bundle from that `aotter/mantle-starters` commit, extract
32
- `scripts/update.mjs` and `scripts/materialize.mjs` together into a temporary
33
- directory, and run the updater from the project root with the same commit
34
- SHA. Do not replace the project's updater before reviewing the report.
28
+ installed command with that immutable ref:
29
+
30
+ ```bash
31
+ pnpm exec mantle update --ref <immutable-ref>
32
+ ```
33
+
34
+ For the one-time alpha.63 bridge, invoke the exact newer Core package and
35
+ provide the current bundle location because alpha.63 metadata does not
36
+ contain it:
37
+
38
+ ```bash
39
+ pnpm dlx @aotter/mantle@<exact-version> update \
40
+ --ref <immutable-ref> \
41
+ --bundle-base-url 'https://raw.githubusercontent.com/aotter/mantle-starters/{ref}/provision-bundles'
42
+ ```
43
+
44
+ Do not extract or replace a repo-local updater. The Core command accepts
45
+ the alpha.63 no-`v` source ref, writes only the report, and records the
46
+ versioned bundle location for the reviewed metadata migration.
35
47
  3. Read the generated report before editing.
36
48
  4. Triage each path; do not treat the report as a patch or merge plan.
37
49
  5. Port confirmed upstream changes one hunk at a time.
38
- 6. Re-run:
50
+ 6. Apply only the report's `.mantle/launch-state.json` and
51
+ `.mantle/features.json` metadata migration, preserving every other field.
52
+ 7. Re-run:
39
53
 
40
54
  ```bash
41
55
  pnpm validate
@@ -49,15 +63,13 @@ ref, and the local project.
49
63
 
50
64
  - Review `upstream` to find starter changes worth porting. Use `local` only to
51
65
  understand project-owned drift from the original starter.
52
- - Never copy generated comparison versions of `wrangler.toml`,
53
- `.dev.vars.example`, `.mantle/launch-state.json`, or
54
- `.mantle/features.json`. Preserve Worker/D1 names, bindings, origins,
55
- provider values, and launch state. Port a reviewed upstream line manually
56
- only when it does not replace project identity or state.
57
- - The updater omits the two `.mantle/*.json` state files and reproduces project
58
- identity. If `upstream` proposes `mantle-<type>` names for a real project,
59
- stop: the comparator is stale or incompatible. Use the target updater
60
- recovery in step 2, then regenerate the report.
66
+ - Never copy generated comparison versions of `.mantle/launch-state.json` or
67
+ `.mantle/features.json`; the report omits them and gives a field-level
68
+ migration instead. Preserve Worker/D1 names, bindings, origins, provider
69
+ values, and all unlisted launch state.
70
+ - The updater reproduces project identity in legacy Wrangler files. If
71
+ `upstream` proposes `mantle-<type>` names for a real project, stop: the
72
+ bundle is incompatible and must not be ported.
61
73
  - A large `local` section is normal after customization. Counts are not a
62
74
  confidence score.
63
75
 
@@ -1,215 +0,0 @@
1
- ---
2
- name: customize-design
3
- description: Layer custom design over a mantle publication starter project using the L1–L4 theme stack (tokens / extraCss+icons+i18n / Header+Footer+PageShell slots / whole-template fork). Use when the user wants to rebrand, restyle, or swap UI pieces without forking the whole starter.
4
- metadata:
5
- source: "@aotter/mantle"
6
- sourcePath: skills/customize-design/SKILL.md
7
- applies_to: mantle@v0.1.0 + publication archetype
8
- ---
9
-
10
- # Customize the design of a mantle publication site
11
-
12
- You are layering a consumer theme over a project built from the `publication` starter. The baseline lives at `src/theme.default/` (read-only by convention). Consumer overrides live at `src/theme/`. Always escalate from L1 → L4 and stop at the lowest layer that solves the user's stated need.
13
-
14
- ## Layer cheatsheet
15
-
16
- | Layer | Where | Use for |
17
- |---|---|---|
18
- | **L1** tokens | `src/theme/tokens.ts` | palette, font stack, type scale, measure, gutter |
19
- | **L2** extraCss | `src/theme/index.ts:extraCss` | extra CSS rules (border-radius, hero size, etc.) |
20
- | **L2** icons | `src/theme/icons.ts` | replace baseline icons by name; add new ones |
21
- | **L2** i18n | `src/theme/i18n/<locale>.json` | retitle UI strings per locale (deep-merge) |
22
- | **L3** components — chrome | `src/theme/components/{Header,Footer}.tsx` | swap navigation chrome |
23
- | **L3** components — body layout | `src/theme/components/PageShell.tsx` | reshape body layout: sidebar variants, sticky CTAs, full-bleed hero, alternative Header / `<main>` / Footer arrangement |
24
- | **L4** templates | `src/theme/templates/<name>.tsx` | replace a page kind end-to-end |
25
-
26
- ## Conversation pattern
27
-
28
- 1. **Map the user's intent to a layer.** "Change the colors" → L1. "Different header structure" → L3. Tell the user the layer + the escape hatch ("I'll start at L1; if it doesn't get close enough, we can escalate to a custom Header at L3").
29
- 2. **Fork → edit → review.** Run `pnpm theme:fork <path>`, edit the new file in `src/theme/`, ask the user to reload `pnpm dev`.
30
- 3. **On dissatisfaction, iterate or revert.** `pnpm theme:reset <path>` removes the override and restores the baseline. The user can roll back any single layer without affecting others.
31
-
32
- ## Layer recipes
33
-
34
- ### L1 — tokens
35
-
36
- ```bash
37
- pnpm theme:fork tokens.ts
38
- ```
39
-
40
- Edit `src/theme/tokens.ts`:
41
-
42
- ```ts
43
- export const TOKENS_CSS = `
44
- :root {
45
- --paper: #fffbf3;
46
- --ink: #1a1814;
47
- --accent: #a3331f;
48
- --font-display: "Fraunces", Georgia, serif;
49
- --font-body: "Source Serif 4", Georgia, serif;
50
- --measure: 36rem;
51
- }
52
- [data-theme="dark"] {
53
- --paper: #1a1814;
54
- --ink: #f1ebdf;
55
- --accent: #e6594a;
56
- }
57
- `;
58
- ```
59
-
60
- The override is concatenated AFTER baseline tokens, so later declarations win on standard CSS specificity. Only redeclare the vars you want to change.
61
-
62
- **Custom web fonts**: if `--font-display` references a font not in the system stack, register it with L2 `extraCss` using `@font-face`. Don't use CSS `@import`: `extraCss` is appended after baseline rules, and browsers ignore late `@import` statements.
63
-
64
- ```ts
65
- const overrides: ThemeOverride = {
66
- extraCss: `
67
- @font-face {
68
- font-family: "FrauncesLocal";
69
- src: url("/fonts/fraunces.woff2") format("woff2");
70
- font-weight: 400 700;
71
- font-display: swap;
72
- }
73
- `,
74
- tokens: `:root { --font-display: "FrauncesLocal", Georgia, serif; }`,
75
- };
76
- ```
77
-
78
- Revert: `pnpm theme:reset tokens.ts`.
79
-
80
- ### L2 — extraCss
81
-
82
- Edit `src/theme/index.ts` — set the `extraCss` field directly (no fork needed, it's just a string):
83
-
84
- ```ts
85
- const overrides: ThemeOverride = {
86
- extraCss: `
87
- .site-main { max-width: 64rem; }
88
- .post-cover { border-radius: 8px; }
89
- blockquote { background: var(--rule); padding: 1rem; }
90
- `,
91
- };
92
- ```
93
-
94
- Revert: clear the field.
95
-
96
- ### L2 — icons
97
-
98
- ```bash
99
- pnpm theme:fork icons.ts
100
- ```
101
-
102
- Edit `src/theme/icons.ts`:
103
-
104
- ```ts
105
- const customIcons: Record<string, string> = {
106
- // override an existing baseline icon
107
- globe: '<circle cx="12" cy="12" r="9"/><path d="..."/>',
108
- // add a new icon
109
- logo: '<path d="M12 2L2 7v10l10 5 10-5V7l-10-5z"/>',
110
- };
111
- export default customIcons;
112
- ```
113
-
114
- Use in any template via `icon("logo", { size: 24 })`. SVG path content only — no `<svg>` wrapper. The fork ships a stub (not a copy of the baseline), since you're extending a registry.
115
-
116
- Revert: `pnpm theme:reset icons.ts`.
117
-
118
- ### L2 — i18n
119
-
120
- ```bash
121
- pnpm theme:fork i18n/en.json
122
- ```
123
-
124
- Edit `src/theme/i18n/en.json` — partial bundle, deep-merged over baseline:
125
-
126
- ```json
127
- {
128
- "header": { "posts": "Articles" },
129
- "home": { "eyebrow": "the dispatch" },
130
- "notFound": { "title": "Lost at sea" }
131
- }
132
- ```
133
-
134
- Other keys carry forward from baseline. To support a new locale (`ja`, `de`, etc.), edit `src/i18n/<locale>.json` directly — locale set is consumer-level, not a theme slot.
135
-
136
- Revert: `pnpm theme:reset i18n/en.json`.
137
-
138
- ### L3 — Header / Footer
139
-
140
- ```bash
141
- pnpm theme:fork components/Header.tsx
142
- pnpm theme:fork components/Footer.tsx
143
- ```
144
-
145
- Use this when the user wants different chrome — different brand mark, nav arrangement, language switcher, footer copy — but the page body still reads top → main → bottom. Key contracts:
146
-
147
- - Don't change the props signature: `Header(props: HeaderProps)`.
148
- - `props.site.brand`, `props.site.locales`, `props.locale`, `props.current` are available.
149
- - Baseline siblings (icon registry, etc.) auto-rewritten on fork to `../../theme.default/<path>`. Keep those unless you want to drop the baseline icon set.
150
-
151
- Same shape for `Footer`.
152
-
153
- Revert: `pnpm theme:reset components/Header.tsx`.
154
-
155
- ### L3 — PageShell (body layout)
156
-
157
- ```bash
158
- pnpm theme:fork components/PageShell.tsx
159
- ```
160
-
161
- Use this to reshape the **body layout** rather than swap chrome — for example:
162
-
163
- - Sidebar with table-of-contents alongside `<main>` for docs-lite pages
164
- - Sticky CTA bar between `<main>` and `<Footer>`
165
- - Full-bleed hero section above Header on the home page
166
- - Different Header / `<main>` / Footer ordering (e.g., side-rail logo)
167
- - Custom `<main>` container width or padding rules per template kind
168
-
169
- The forked PageShell takes ownership of how (or whether) to render the Header / Footer overrides. The baseline composes them top → main → bottom; a consumer-supplied PageShell can ignore or recompose them.
170
-
171
- Revert: `pnpm theme:reset components/PageShell.tsx`.
172
-
173
- ### Layout is not forkable
174
-
175
- Only `Header`, `Footer`, and `PageShell` are supported component slots. `theme:fork components/Layout.tsx` exits with a clear error pointing at PageShell as the body-layout escape hatch. Don't try to override Layout by hand-editing `src/theme/components/Layout.tsx` outside the fork machinery — the override surface won't register it.
176
-
177
- `Layout` (the document envelope — `<html>` / `<head>` / `<body>` + SEO meta + theme bootstrap) is locked. If the user needs to change `<head>` content beyond what tokens / extraCss can express, that crosses the starter-family line — switch starter rather than fork every template.
178
-
179
- ### L4 — whole template (escape hatch)
180
-
181
- ```bash
182
- pnpm theme:fork templates/post.tsx
183
- ```
184
-
185
- Edit `src/theme/templates/post.tsx`. The forked file imports baseline Layout via `../../theme.default/components/Layout.js`.
186
-
187
- L4 is the last resort. If the user wants more than two L4 forks, suggest the conversation: "The shape you're describing isn't really `publication` any more — it sounds closer to `community` (member posts), `micro-shop` (catalog + orders), or `leads-inbox` (lead pipeline). Once you cross those lines, switching starter family is cheaper than forking more templates."
188
-
189
- Revert: `pnpm theme:reset templates/post.tsx`.
190
-
191
- ## Hard rules
192
-
193
- - Don't edit `src/theme.default/`. Use fork.
194
- - Don't add new top-level keys to `ThemeOverride` — extend through the existing slots.
195
- - `Layout` is locked — change envelope shape via L4 forks (every template) or pick another starter.
196
- - After a fork, the file is a normal `.ts` / `.tsx` / `.json` — TS errors surface on `pnpm typecheck` as usual.
197
-
198
- ## Diagnostic recipes
199
-
200
- | Symptom | Cause | Fix |
201
- |---|---|---|
202
- | `pnpm theme:fork X` exits with "Override already exists" | Already forked | `pnpm theme:reset X` first |
203
- | `pnpm typecheck` fails in `src/theme/components/<Name>.tsx` after fork | A baseline import didn't auto-rewrite | Manually change `from "../<sibling>"` to `from "../../theme.default/<sibling>"` |
204
- | Color change not visible after edit | Browser CSS cache | Hard reload (Cmd+Shift+R / Ctrl+F5); if it persists, restart `pnpm dev` |
205
- | Forked `en.json` discards keys I didn't redeclare | Bundle isn't deep-merging | Verify `src/i18n/index.ts:deepMerge` is present. If missing, update to latest starter |
206
-
207
- ## When you're done
208
-
209
- Tell the user three things:
210
-
211
- 1. **Layer landed at** — "I made the change at L1 (tokens) — palette only, no structural edits."
212
- 2. **Revert command** — "If you don't like it, `pnpm theme:reset tokens.ts` rolls it back."
213
- 3. **Next escalation step** — "If the colors aren't enough, the next layer would be L3 — replace the Header component."
214
-
215
- Stop after one layer per turn unless the user explicitly says "go deeper". The point of layering is to keep each customization step reversible and inspectable.