jekyll-theme-zer0 1.26.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +558 -5
  3. data/README.md +10 -27
  4. data/_data/authors.yml +4 -3
  5. data/_data/backlog.yml +28 -0
  6. data/_data/consumers.yml +147 -0
  7. data/_data/features.yml +142 -23
  8. data/_data/i18n/fr.yml +98 -0
  9. data/_data/i18n/languages.yml +36 -0
  10. data/_data/i18n/manifest.yml +1502 -0
  11. data/_data/navigation/docs.yml +2 -0
  12. data/_data/navigation/main.yml +6 -0
  13. data/_data/series.yml +19 -0
  14. data/_data/theme-manifest.yml +348 -268
  15. data/_data/ui-text.yml +36 -246
  16. data/_includes/README.md +25 -2
  17. data/_includes/analytics/google-tag-manager-body.html +11 -2
  18. data/_includes/analytics/google-tag-manager-head.html +16 -6
  19. data/_includes/analytics/posthog.html +33 -4
  20. data/_includes/components/abc-letter.html +43 -0
  21. data/_includes/components/author-avatar-url.html +4 -2
  22. data/_includes/components/book-card.html +42 -0
  23. data/_includes/components/book-nav.html +80 -0
  24. data/_includes/components/book-plate.html +31 -0
  25. data/_includes/components/book-toc.html +47 -0
  26. data/_includes/components/bookshelf.html +68 -0
  27. data/_includes/components/card-grid.html +60 -0
  28. data/_includes/components/data-card.html +95 -0
  29. data/_includes/components/env-switcher.html +3 -1
  30. data/_includes/components/halfmoon.html +5 -1
  31. data/_includes/components/language-toggle.html +81 -0
  32. data/_includes/components/page-feedback.html +65 -2
  33. data/_includes/components/search-modal.html +2 -2
  34. data/_includes/components/shortcuts-modal.html +1 -1
  35. data/_includes/components/theme-controls-bar.html +10 -2
  36. data/_includes/components/theme-customizer.html +8 -2
  37. data/_includes/components/translation-notice.html +27 -0
  38. data/_includes/content/intro.html +16 -15
  39. data/_includes/content/seo.html +9 -3
  40. data/_includes/core/color-mode-init.html +13 -4
  41. data/_includes/core/favicon.html +46 -0
  42. data/_includes/core/footer.html +9 -4
  43. data/_includes/core/head.html +18 -0
  44. data/_includes/core/header.html +9 -4
  45. data/_includes/core/hreflang.html +33 -0
  46. data/_includes/core/i18n.html +36 -0
  47. data/_includes/custom/body-end.html +18 -0
  48. data/_includes/custom/body-start.html +17 -0
  49. data/_includes/custom/footer.html +18 -0
  50. data/_includes/custom/head.html +18 -0
  51. data/_includes/navigation/breadcrumbs.html +1 -1
  52. data/_includes/navigation/local-graph.html +28 -2
  53. data/_includes/navigation/nav-tree.html +3 -3
  54. data/_includes/navigation/navbar.html +5 -4
  55. data/_includes/navigation/sidebar-config.html +21 -0
  56. data/_includes/navigation/sidebar-nav.html +4 -0
  57. data/_includes/navigation/sidebar-pagetree.html +150 -0
  58. data/_includes/navigation/sidebar-right.html +3 -2
  59. data/_includes/navigation/unified-drawer.html +9 -3
  60. data/_includes/obsidian/full-graph.html +165 -136
  61. data/_layouts/404.html +260 -0
  62. data/_layouts/README.md +2 -0
  63. data/_layouts/article.html +7 -1
  64. data/_layouts/book-abc.html +106 -0
  65. data/_layouts/book-story.html +91 -0
  66. data/_layouts/book.html +113 -0
  67. data/_layouts/collection.html +17 -5
  68. data/_layouts/default.html +5 -3
  69. data/_layouts/home.html +5 -2
  70. data/_layouts/landing.html +9 -0
  71. data/_layouts/news.html +159 -33
  72. data/_layouts/root.html +29 -9
  73. data/_layouts/section.html +61 -17
  74. data/_sass/components/_book.scss +423 -0
  75. data/_sass/core/_navbar.scss +13 -0
  76. data/_sass/core/_obsidian.scss +295 -7
  77. data/_sass/layouts/_navbar-extras.scss +6 -1
  78. data/_sass/theme/_backgrounds.scss +21 -8
  79. data/assets/css/main.scss +1 -0
  80. data/assets/js/auto-hide-nav.js +5 -1
  81. data/assets/js/halfmoon.js +26 -0
  82. data/assets/js/obsidian-graph.js +707 -265
  83. data/assets/js/obsidian-local-graph.js +161 -54
  84. data/assets/js/search-modal.js +4 -1
  85. data/scripts/README.md +20 -26
  86. data/scripts/bin/audit-consumer +1 -1
  87. data/scripts/bin/manifest +31 -5
  88. data/scripts/bin/sync-plugins +0 -1
  89. data/scripts/bin/validate +5 -1
  90. data/scripts/dev/rasterize-svg.js +65 -0
  91. data/scripts/features/generate-preview-images +49 -1390
  92. data/scripts/features/install-preview-generator +55 -33
  93. data/scripts/install/README.md +55 -25
  94. data/scripts/install/ai/client.sh +302 -93
  95. data/scripts/install/ai/prompts/spec.schema.json +1 -1
  96. data/scripts/install/ai/prompts/wizard.system.md +8 -17
  97. data/scripts/install/ai/wizard.sh +10 -5
  98. data/scripts/install/apply.sh +7 -3
  99. data/scripts/install/cli.sh +54 -4
  100. data/scripts/install/config.sh +167 -0
  101. data/scripts/install/doctor.sh +38 -0
  102. data/scripts/install/plan.sh +10 -0
  103. data/scripts/install/spec.sh +15 -7
  104. data/scripts/install/template.sh +4 -0
  105. data/scripts/lib/README.md +1 -5
  106. data/scripts/lib/install/deploy/README.md +3 -9
  107. data/scripts/lib/preview_generator.py +2261 -1341
  108. data/scripts/propagate.rb +277 -0
  109. data/scripts/translate.rb +1204 -0
  110. metadata +34 -3
  111. data/_plugins/preview_image_generator.rb +0 -351
@@ -14,13 +14,15 @@
14
14
  # -h, --help Show this help message
15
15
  # -d, --dry-run Preview what would be installed (no changes)
16
16
  # -f, --force Overwrite existing files
17
- # -p, --provider PROVIDER Set default AI provider (openai, stability, local)
17
+ # -p, --provider PROVIDER Set default renderer
18
+ # (openai, xai, stability, gemini, local)
18
19
  # --no-config Skip _config.yml modification
19
20
  # --no-tasks Skip VS Code tasks installation
20
21
  #
21
22
  # Requirements:
22
23
  # - Jekyll site using zer0-mistakes theme
23
24
  # - curl and jq installed
25
+ # - python3 with PyYAML (runs the generation engine)
24
26
  # - Git repository (for version tracking)
25
27
  #
26
28
 
@@ -43,12 +45,12 @@ RAW_URL="https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/main"
43
45
  SOURCE_FILES=(
44
46
  "scripts/features/generate-preview-images"
45
47
  "scripts/lib/preview_generator.py"
46
- "_plugins/preview_image_generator.rb"
48
+ "scripts/dev/rasterize-svg.js"
47
49
  )
48
50
  DEST_FILES=(
49
51
  "scripts/generate-preview-images.sh"
50
52
  "scripts/lib/preview_generator.py"
51
- "_plugins/preview_image_generator.rb"
53
+ "scripts/dev/rasterize-svg.js"
52
54
  )
53
55
 
54
56
  # Default options
@@ -96,20 +98,21 @@ ${YELLOW}OPTIONS:${NC}
96
98
  -h, --help Show this help message
97
99
  -d, --dry-run Preview what would be installed (no changes)
98
100
  -f, --force Overwrite existing files
99
- -p, --provider PROVIDER Set default AI provider (openai, stability, local)
101
+ -p, --provider PROVIDER Set default renderer
102
+ (openai, xai, stability, gemini, local)
100
103
  --no-config Skip _config.yml modification
101
104
  --no-tasks Skip VS Code tasks installation
102
105
  -l, --local PATH Copy files from local theme directory instead of downloading
103
106
 
104
107
  ${YELLOW}EXAMPLES:${NC}
105
- # Install with defaults
108
+ # Install with defaults (OpenAI renders; Claude analyzes + reviews)
106
109
  ./$SCRIPT_NAME
107
110
 
108
111
  # Preview installation
109
112
  ./$SCRIPT_NAME --dry-run
110
113
 
111
- # Install with Stability AI as default provider
112
- ./$SCRIPT_NAME --provider stability
114
+ # Install with Gemini as the renderer
115
+ ./$SCRIPT_NAME --provider gemini
113
116
 
114
117
  # Force reinstall
115
118
  ./$SCRIPT_NAME --force
@@ -118,8 +121,13 @@ ${YELLOW}REMOTE INSTALLATION:${NC}
118
121
  curl -fsSL ${RAW_URL}/scripts/$SCRIPT_NAME | bash
119
122
 
120
123
  ${YELLOW}AFTER INSTALLATION:${NC}
121
- 1. Set your API key in .env file:
122
- OPENAI_API_KEY=your-key-here
124
+ 1. Set credentials in .env:
125
+ OPENAI_API_KEY=... # the default renderer
126
+ Plus ONE Claude credential so Claude can analyze articles and
127
+ review the rendered images (optional — degrades to template prompts):
128
+ CLAUDE_CODE_OAUTH_TOKEN=... # from \`claude setup-token\`
129
+ ANTHROPIC_API_KEY=... # from console.anthropic.com
130
+ (or just have the \`claude\` CLI installed and logged in)
123
131
 
124
132
  2. Generate preview images:
125
133
  ./scripts/generate-preview-images.sh --list-missing
@@ -145,13 +153,13 @@ check_jekyll_site() {
145
153
  # Check dependencies
146
154
  check_dependencies() {
147
155
  local missing=()
148
-
149
- for cmd in curl jq; do
156
+
157
+ for cmd in curl jq python3; do
150
158
  if ! command -v "$cmd" &> /dev/null; then
151
159
  missing+=("$cmd")
152
160
  fi
153
161
  done
154
-
162
+
155
163
  if [[ ${#missing[@]} -gt 0 ]]; then
156
164
  error "Missing required dependencies: ${missing[*]}"
157
165
  info "Install them with:"
@@ -159,7 +167,13 @@ check_dependencies() {
159
167
  info " Ubuntu: sudo apt-get install ${missing[*]}"
160
168
  exit 1
161
169
  fi
162
-
170
+
171
+ # The generation engine needs PyYAML (front matter / _config.yml parsing)
172
+ if ! python3 -c "import yaml" &> /dev/null; then
173
+ warn "PyYAML not found — the generator will not run until it is installed:"
174
+ info " pip3 install pyyaml (or: python3 -m pip install --user pyyaml)"
175
+ fi
176
+
163
177
  log "All dependencies satisfied"
164
178
  }
165
179
 
@@ -242,19 +256,22 @@ update_config() {
242
256
 
243
257
  # =============================================================================
244
258
  # AI Preview Image Generator Configuration
245
- # Feature: ZER0-003
259
+ # Feature: ZER0-004
246
260
  # Documentation: ${REPO_URL}/blob/main/docs/features/preview-image-generator.md
247
261
  # =============================================================================
248
262
  preview_images:
249
263
  enabled: true
250
- provider: ${DEFAULT_PROVIDER} # openai, stability, or local
251
- model: dall-e-3 # OpenAI model to use
252
- size: "1792x1024" # Landscape banner size
253
- quality: standard # standard or hd
264
+ provider: ${DEFAULT_PROVIDER} # renderer: openai, xai, stability, gemini, local
265
+ model: "" # empty = renderer default (gpt-image-2, grok-2-image, ...)
266
+ size: "1536x1024" # Landscape banner size
267
+ quality: auto # auto for GPT Image; standard/hd for DALL-E 3
254
268
  style: "retro pixel art, 8-bit video game aesthetic, vibrant colors, nostalgic, clean pixel graphics"
255
269
  style_modifiers: "pixelated, retro gaming style, CRT screen glow effect, limited color palette"
256
270
  output_dir: assets/images/previews # Where to save generated images
257
- auto_generate: false # Generate during build (slow, use script instead)
271
+ prompt_engine: claude # claude analyzes the article (template = built-in prompt)
272
+ review_engine: claude # claude reviews the render (none = skip)
273
+ assets_prefix: /assets # Prefix prepended to relative preview paths
274
+ auto_prefix: true # Auto-add assets_prefix when missing
258
275
  collections: # Collections to scan for missing previews
259
276
  - posts
260
277
  - docs
@@ -270,28 +287,32 @@ create_env_example() {
270
287
 
271
288
  if [[ -f "$env_example" ]]; then
272
289
  # Check if our keys are already there
273
- if grep -q "OPENAI_API_KEY" "$env_example"; then
290
+ if grep -q "CLAUDE_CODE_OAUTH_TOKEN" "$env_example"; then
274
291
  info ".env.example already contains preview generator keys"
275
292
  return 0
276
293
  fi
277
294
  fi
278
-
295
+
279
296
  if [[ "$DRY_RUN" == true ]]; then
280
297
  dry_run_log "Would add API keys to .env.example"
281
298
  return 0
282
299
  fi
283
-
300
+
284
301
  step "Updating .env.example"
285
-
286
- cat >> "$env_example" << 'EOF'
287
302
 
288
- # AI Preview Image Generator API Keys
289
- # Get your key from: https://platform.openai.com/api-keys
290
- OPENAI_API_KEY=your-openai-api-key-here
303
+ cat >> "$env_example" << 'EOF'
291
304
 
292
- # Stability AI (alternative provider)
293
- # Get your key from: https://platform.stability.ai/
294
- STABILITY_API_KEY=your-stability-api-key-here
305
+ # AI Preview Image Generator credentials
306
+ # Renderer (the default provider is openai):
307
+ # OPENAI_API_KEY=... # https://platform.openai.com/api-keys (also powers --enhance)
308
+ # XAI_API_KEY=... # https://console.x.ai/
309
+ # STABILITY_API_KEY=... # https://platform.stability.ai/
310
+ # GEMINI_API_KEY=... # https://aistudio.google.com/apikey
311
+
312
+ # Claude orchestration (analyzes articles + reviews renders) — any ONE of
313
+ # (or a logged-in `claude` CLI; optional, degrades to template prompts):
314
+ # CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... # from `claude setup-token`
315
+ # ANTHROPIC_API_KEY=sk-ant-... # from console.anthropic.com
295
316
  EOF
296
317
 
297
318
  log "Updated .env.example"
@@ -431,9 +452,10 @@ print_instructions() {
431
452
  echo ""
432
453
  echo -e "${YELLOW}NEXT STEPS:${NC}"
433
454
  echo ""
434
- echo " 1. ${BLUE}Set up your API key:${NC}"
435
- echo " cp .env.example .env"
436
- echo " # Edit .env and add your OPENAI_API_KEY"
455
+ echo " 1. ${BLUE}Set up credentials:${NC}"
456
+ echo " cp .env.example .env # add OPENAI_API_KEY (the renderer)"
457
+ echo " claude setup-token # optional: lets Claude analyze + review"
458
+ echo " (a logged-in \`claude\` CLI also works for the Claude side)"
437
459
  echo ""
438
460
  echo " 2. ${BLUE}Check for missing preview images:${NC}"
439
461
  echo " ./scripts/generate-preview-images.sh --list-missing"
@@ -50,8 +50,7 @@ scripts/install/
50
50
 
51
51
  ### The Spec
52
52
 
53
- A single JSON document (`.zer0/install.spec.json`) is the universal contract
54
- between all front-ends and the executor:
53
+ A single JSON document (`.zer0/install.spec.json`) is the universal contract between all front-ends and the executor:
55
54
 
56
55
  ```
57
56
  CLI flags → plan.sh → spec.json → apply.sh → tasks → files on disk
@@ -59,21 +58,18 @@ AI wizard → spec.json → apply.sh → tasks → files on disk
59
58
  TUI wizard → spec.json → apply.sh → tasks → files on disk
60
59
  ```
61
60
 
62
- The spec schema is defined in `ai/prompts/spec.schema.json`. The AI is
63
- constrained to emit only valid spec JSON — never raw file content.
61
+ The spec schema is defined in `ai/prompts/spec.schema.json`. The AI is constrained to emit only valid spec JSON — never raw file content.
64
62
 
65
63
  ### Write contract
66
64
 
67
- **ALL filesystem writes go through `fs.sh`**. No raw `>`, `cp`, or `echo >`
68
- outside of `fs.sh` functions. This enforces:
65
+ **ALL filesystem writes go through `fs.sh`**. No raw `>`, `cp`, or `echo >` outside of `fs.sh` functions. This enforces:
69
66
  - `--dry-run`: zero mutations
70
67
  - `--backup`: auto-backup before overwrite
71
68
  - `--force`: overwrite without prompting
72
69
 
73
70
  ### Template contract
74
71
 
75
- **ALL generated file content comes from `templates/`**. No heredocs in shell
76
- code. Templates use `{{VARIABLE}}` substitution via `template.sh::tmpl_apply`.
72
+ **ALL generated file content comes from `templates/`**. No heredocs in shell code. Templates use `{{VARIABLE}}` substitution via `template.sh::tmpl_apply`.
77
73
 
78
74
  ### Bash 3.2 compatibility
79
75
 
@@ -90,8 +86,11 @@ This installer runs on macOS's `/bin/bash` (3.2). Restrictions:
90
86
  # Local development
91
87
  ./scripts/bin/install help
92
88
  ./scripts/bin/install init . --profile blog --site-title "My Blog"
93
- ./scripts/bin/install wizard . --ai
94
- ./scripts/bin/install doctor .
89
+ ./scripts/bin/install init . --config zer0.install.yml # config-file driven
90
+ ./scripts/bin/install wizard . --ai # Claude Code OAuth if available
91
+ ./scripts/bin/install wizard . --ai --ai-provider anthropic
92
+ ./scripts/bin/install suggest . "a docs site for a Rust CLI"
93
+ ./scripts/bin/install doctor . # shows the active AI provider
95
94
  ./scripts/bin/install diff .
96
95
  ./scripts/bin/install plan . --profile docs
97
96
 
@@ -114,24 +113,59 @@ curl -fsSL https://raw.githubusercontent.com/bamr87/zer0-mistakes/main/scripts/b
114
113
  3. Add template to `templates/config/` or `templates/pages/`
115
114
  4. List in the appropriate profile YAML under `templates/profiles/`
116
115
 
116
+ ## Config file layer
117
+
118
+ Sites can persist installer choices in a small YAML file instead of re-typing flags. `config.sh` discovers and merges these (low→high precedence):
119
+
120
+ 1. `~/.config/zer0/install.yml` (user-global)
121
+ 2. `<target>/zer0.install.yml` (project-local)
122
+ 3. `<target>/.zer0/config.yml` (project-local, hidden)
123
+ 4. `$ZER0_CONFIG` / `--config FILE` (explicit — wins)
124
+
125
+ The config layer sits between profile defaults and env/flag overrides:
126
+
127
+ ```
128
+ defaults < profile < CONFIG FILE < env vars < CLI flags
129
+ ```
130
+
131
+ Recognised keys (nested **or** flat/dotted both work): `profile`, `site.{title,description,author,email,url,timezone,locale}`, `github.{user,repo,pages_branch,enable_pages}`, `theme.{source,version}`, `deploy`, `agents`, `tasks`, `ai.provider`, `ai.model`. Example:
132
+
133
+ ```yaml
134
+ profile: github-pages
135
+ site:
136
+ title: "My Docs"
137
+ deploy: [github-pages]
138
+ ai:
139
+ provider: claude-cli # auto | claude-cli | anthropic | openai | none
140
+ ```
141
+
142
+ API keys are **never** read from config files — they stay in the environment.
143
+
117
144
  ## AI integration
118
145
 
119
146
  The AI path is a first-class citizen but never mandatory:
120
147
 
121
148
  - `ai_wizard_run` → interactive LLM spec generation
122
- - `ai_diagnose_run` → post-build error analysis
123
- - `ai_suggest_run` → profile + deploy recommendation
149
+ - `ai_diagnose_run` → post-build error analysis
150
+ - `ai_suggest_run` → profile + deploy recommendation (also `install suggest`)
151
+
152
+ All are guarded by the `ZER0_NO_AI=1` kill-switch and degrade gracefully to defaults or rule-based logic when AI is unavailable.
153
+
154
+ ### Providers (`ai/client.sh`)
155
+
156
+ The client is multi-provider. `ZER0_AI_PROVIDER` (or config `ai.provider`, or `--ai-provider`) selects one; the default `auto` resolves in this order:
124
157
 
125
- All three are guarded by `ZER0_NO_AI=1` kill-switch and degrade gracefully
126
- to defaults or rule-based logic when AI is unavailable.
158
+ | # | Provider | Auth | Notes |
159
+ |---|--------------|-------------------------------------------------|-------|
160
+ | 1 | `claude-cli` | the logged-in `claude` CLI (**Claude Code OAuth**) | Zero key handling — reuses `claude setup-token` / `claude login`. Preferred when the `claude` binary is on `PATH`. |
161
+ | 2 | `anthropic` | `CLAUDE_CODE_OAUTH_TOKEN` (OAuth) or `ANTHROPIC_API_KEY` | Anthropic Messages API over HTTPS. |
162
+ | 3 | `openai` | `OPENAI_API_KEY` (or `OPENAI_BASE_URL` for Azure/Ollama) | OpenAI-compatible `/chat/completions`. |
127
163
 
128
- To enable: set `OPENAI_API_KEY` (or `OPENAI_BASE_URL` for Azure/Ollama).
164
+ Model is auto-defaulted per provider and overridable with `ZER0_AI_MODEL` / `--ai-model` (`ANTHROPIC_MODEL` / `OPENAI_MODEL` also honoured). Run `install doctor` to see which provider is active. All calls are single-attempt with a 30s timeout; user context is sanitized before it leaves the host.
129
165
 
130
166
  ## Deploy plugins
131
167
 
132
- Deploy targets listed in the spec (`SPEC_DEPLOY`) are dispatched as
133
- `tasks/deploy_<target>.sh` modules that render reusable templates from
134
- `templates/deploy/<target>/`. Built-in plugins:
168
+ Deploy targets listed in the spec (`SPEC_DEPLOY`) are dispatched as `tasks/deploy_<target>.sh` modules that render reusable templates from `templates/deploy/<target>/`. Built-in plugins:
135
169
 
136
170
  | Target | Writes |
137
171
  |-------------------|---------------------------------------------------------------------|
@@ -139,14 +173,11 @@ Deploy targets listed in the spec (`SPEC_DEPLOY`) are dispatched as
139
173
  | `azure-swa` | `.github/workflows/azure-static-web-apps.yml`, `staticwebapp.config.json` |
140
174
  | `docker-prod` | `Dockerfile.prod`, `docker-compose.prod.yml`, `nginx.conf`, `.dockerignore` |
141
175
 
142
- Add a new plugin by dropping `tasks/deploy_<target>.sh` with a
143
- `task_deploy_<target>_run` function plus a `templates/deploy/<target>/`
144
- template directory. The dispatcher is generic — no registry changes needed.
176
+ Add a new plugin by dropping `tasks/deploy_<target>.sh` with a `task_deploy_<target>_run` function plus a `templates/deploy/<target>/` template directory. The dispatcher is generic — no registry changes needed.
145
177
 
146
178
  ## Testing
147
179
 
148
- A regression harness lives at [`test/test_installer.sh`](../../test/test_installer.sh)
149
- and is wired into the main runner as the `installer` suite:
180
+ A regression harness lives at [`test/test_installer.sh`](../../test/test_installer.sh) and is wired into the main runner as the `installer` suite:
150
181
 
151
182
  ```bash
152
183
  # Standalone (auto-enables AI tier when OPENAI_API_KEY is set)
@@ -158,5 +189,4 @@ and is wired into the main runner as the `installer` suite:
158
189
  ./test/test_runner.sh --suites installer
159
190
  ```
160
191
 
161
- The harness covers: module syntax, all 6 profile inits, all 3 deploy plugins,
162
- all 5 agent flavours, and (when keyed) the full AI wizard → apply pipeline.
192
+ The harness covers: module syntax, all 6 profile inits, all 3 deploy plugins, all 5 agent flavours, and (when keyed) the full AI wizard → apply pipeline.