grimoire-wizard 0.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/LICENSE +21 -0
- package/README.md +1399 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2485 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +354 -0
- package/dist/index.js +1922 -0
- package/dist/index.js.map +1 -0
- package/examples/all-features.yaml +54 -0
- package/examples/base.yaml +28 -0
- package/examples/basic.yaml +61 -0
- package/examples/conditional.yaml +126 -0
- package/examples/demo.yaml +112 -0
- package/examples/ebay-mcp-setup.yaml +171 -0
- package/examples/extended.yaml +34 -0
- package/examples/themed.yaml +92 -0
- package/examples/with-checks.yaml +34 -0
- package/package.json +73 -0
- package/schema/grimoire.schema.json +964 -0
package/README.md
ADDED
|
@@ -0,0 +1,1399 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="grimoire-pro.png" alt="Grimoire" width="720" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">grimoire</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
A wizard's spell book. Your config is the spell, the CLI is the magic.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/grimoire-wizard"><img src="https://img.shields.io/npm/v/grimoire-wizard" alt="npm version" /></a>
|
|
13
|
+
<a href="./LICENSE"><img src="https://img.shields.io/npm/l/grimoire-wizard" alt="license" /></a>
|
|
14
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/grimoire-wizard" alt="node" /></a>
|
|
15
|
+
<a href="https://github.com/YosefHayim/grimoire/actions"><img src="https://img.shields.io/github/actions/workflow/status/YosefHayim/grimoire/ci.yml?label=tests" alt="tests" /></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Grimoire is a config-driven CLI wizard framework for Node.js. You write a YAML or JSON file describing your prompts, branching logic, and output format, then grimoire handles the rest: rendering interactive prompts, tracking navigation history, evaluating conditions, and writing structured output. No code required for simple wizards. Full TypeScript support when you need to go deeper. It works equally well as a standalone CLI tool, a library embedded in your own tooling, or a non-interactive automation step in CI/CD pipelines.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **10 input types** — text, select, multiselect, confirm, password, number, search, editor, path, toggle
|
|
25
|
+
- **Conditional branching** — show or skip steps based on previous answers using `when` conditions
|
|
26
|
+
- **Route-based navigation** — select steps can branch to different step sequences
|
|
27
|
+
- **Back-navigation** — built-in history stack lets users go back through steps
|
|
28
|
+
- **Step groups** — organize steps into named sections with visual headers
|
|
29
|
+
- **Visual progress bar** — step counter shown at each prompt
|
|
30
|
+
- **Theming** — 7 semantic color tokens and 4 icon overrides for full visual control
|
|
31
|
+
- **Structured output** — export answers as JSON, YAML, or `.env` format
|
|
32
|
+
- **Config inheritance** — `extends` keyword merges a base config into your wizard
|
|
33
|
+
- **Pre-flight checks** — run shell commands before the wizard starts; abort on failure
|
|
34
|
+
- **Plugin system** — register custom step types with their own render and validate logic
|
|
35
|
+
- **`$ENV_VAR` resolution** — use environment variables as default values in any step
|
|
36
|
+
- **Async validation** — hook into step completion to run async checks (API calls, file system, etc.)
|
|
37
|
+
- **`--dry-run` preview** — print the full step plan without running any prompts
|
|
38
|
+
- **`--mock` non-interactive mode** — supply preset answers as JSON for CI/CD pipelines
|
|
39
|
+
- **`--json` structured output** — emit a machine-readable JSON result envelope for AI agents and scripts
|
|
40
|
+
- **JSON Schema** — `grimoire.schema.json` for IDE autocomplete in VS Code and any JSON Schema-aware editor
|
|
41
|
+
- **Shell completions** — bash, zsh, and fish completion scripts via `grimoire completion`
|
|
42
|
+
- **`grimoire create` scaffolder** — interactively generate a new wizard config file
|
|
43
|
+
- **`grimoire demo` showcase** — run a built-in demo that exercises all 10 step types
|
|
44
|
+
- **Answer caching** — previous answers become defaults on the next run; password steps are never cached
|
|
45
|
+
- **Templates** — save and load named answer presets per wizard
|
|
46
|
+
- **MRU ordering** — frequently selected options float to the top of select/multiselect/search lists
|
|
47
|
+
- **`optionsFrom`** — load select/multiselect/search options from an external JSON or YAML file
|
|
48
|
+
- **Lifecycle hooks** — `onBeforeStep` and `onAfterStep` callbacks in the programmatic API
|
|
49
|
+
- **Ink renderer** — alternative renderer with box-drawing characters and progress percentages
|
|
50
|
+
- **ASCII art banner** — figlet + gradient banner shown at startup; suppressed with `--plain`
|
|
51
|
+
- **`--plain` / `--no-color` flags** — disable colors and banner for plain-text environments
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install -g grimoire-wizard
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Create `setup.yaml`:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
meta:
|
|
63
|
+
name: Project Setup
|
|
64
|
+
|
|
65
|
+
steps:
|
|
66
|
+
- id: project-name
|
|
67
|
+
type: text
|
|
68
|
+
message: What is your project name?
|
|
69
|
+
validate:
|
|
70
|
+
- rule: required
|
|
71
|
+
- rule: minLength
|
|
72
|
+
value: 2
|
|
73
|
+
|
|
74
|
+
- id: language
|
|
75
|
+
type: select
|
|
76
|
+
message: Pick a language
|
|
77
|
+
options:
|
|
78
|
+
- { value: typescript, label: TypeScript }
|
|
79
|
+
- { value: javascript, label: JavaScript }
|
|
80
|
+
|
|
81
|
+
output:
|
|
82
|
+
format: json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Run it:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
grimoire run setup.yaml
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or scaffold a new config interactively:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
grimoire create my-wizard.yaml
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install grimoire-wizard
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Node.js >= 18 required. ESM only — your project must use `"type": "module"` or import from `.mjs` files.
|
|
104
|
+
|
|
105
|
+
## CLI Commands
|
|
106
|
+
|
|
107
|
+
### `grimoire run <config>`
|
|
108
|
+
|
|
109
|
+
Run a wizard from a config file. Accepts `.yaml`, `.json`, `.js`, or `.ts` files.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
grimoire run setup.yaml
|
|
113
|
+
grimoire run setup.yaml -o answers.json
|
|
114
|
+
grimoire run setup.yaml --dry-run
|
|
115
|
+
grimoire run setup.yaml --mock '{"project-name":"my-app","language":"typescript"}'
|
|
116
|
+
grimoire run setup.yaml --json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Flag | Description |
|
|
120
|
+
|------|-------------|
|
|
121
|
+
| `-o, --output <path>` | Write answers to a file |
|
|
122
|
+
| `-f, --format <format>` | Output format: `json`, `yaml`, or `env` (default: `json`) |
|
|
123
|
+
| `-q, --quiet` | Suppress header and summary output |
|
|
124
|
+
| `--dry-run` | Print the step plan without running any prompts |
|
|
125
|
+
| `--mock <json>` | Run non-interactively with preset answers (JSON object string) |
|
|
126
|
+
| `--json` | Emit a structured JSON result envelope to stdout |
|
|
127
|
+
| `--no-cache` | Disable answer caching for this run |
|
|
128
|
+
| `--template <name>` | Load a saved template as default answers |
|
|
129
|
+
| `--renderer <type>` | Renderer to use: `inquirer` (default) or `ink` |
|
|
130
|
+
| `--plain` | Plain output mode (no colors, no banner) |
|
|
131
|
+
| `--no-color` | Disable colored output |
|
|
132
|
+
|
|
133
|
+
#### `--dry-run`
|
|
134
|
+
|
|
135
|
+
Prints every step with its type, ID, prompt message, visibility status, and any conditions or routes. Useful for reviewing a config before running it.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Dry Run: "Project Setup"
|
|
139
|
+
|
|
140
|
+
Step 1 text project-name "What is your project name?"
|
|
141
|
+
Step 2 select language "Pick a language"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `--mock`
|
|
145
|
+
|
|
146
|
+
Runs the wizard without any interactive prompts. Pass a JSON object where keys are step IDs and values are the answers. Steps with defaults will use their defaults if not provided in the mock object.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
grimoire run setup.yaml --mock '{"project-name":"my-app","language":"typescript"}'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### `--json`
|
|
153
|
+
|
|
154
|
+
Suppresses all interactive output and emits a single JSON object to stdout:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"ok": true,
|
|
159
|
+
"wizard": "Project Setup",
|
|
160
|
+
"answers": {
|
|
161
|
+
"project-name": "my-app",
|
|
162
|
+
"language": "typescript"
|
|
163
|
+
},
|
|
164
|
+
"stepsCompleted": 2,
|
|
165
|
+
"format": "json"
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
On error, emits `{ "ok": false, "error": "..." }` and exits with code 1.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### `grimoire validate <config>`
|
|
174
|
+
|
|
175
|
+
Parse and validate a config file without running any prompts. Exits with a non-zero code if the config is invalid.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
grimoire validate setup.yaml
|
|
179
|
+
# Valid wizard config: "Project Setup"
|
|
180
|
+
# 2 steps defined
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### `grimoire create [output]`
|
|
186
|
+
|
|
187
|
+
Interactively scaffold a new wizard config file. Asks for a wizard name, description, number of steps, step types, output format, and optional theme. Writes a ready-to-run YAML file.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
grimoire create # writes to wizard.yaml
|
|
191
|
+
grimoire create my-wizard.yaml # writes to my-wizard.yaml
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### `grimoire demo`
|
|
197
|
+
|
|
198
|
+
Run the built-in demo wizard that exercises all 10 step types, step groups, and theming.
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
grimoire demo
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### `grimoire completion <shell>`
|
|
207
|
+
|
|
208
|
+
Print a shell completion script to stdout. Pipe it into your shell's completion directory.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
grimoire completion bash
|
|
212
|
+
grimoire completion zsh
|
|
213
|
+
grimoire completion fish
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
See [Shell Completions](#shell-completions) for installation instructions.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
### `grimoire cache clear [name]`
|
|
221
|
+
|
|
222
|
+
Delete cached answers for a specific wizard, or all wizards if no name is given.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
grimoire cache clear # clears all cached answers
|
|
226
|
+
grimoire cache clear "Project Setup" # clears cache for one wizard
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### `grimoire template list <wizard-name>`
|
|
232
|
+
|
|
233
|
+
List all saved templates for a wizard.
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
grimoire template list "Project Setup"
|
|
237
|
+
# Templates for "Project Setup":
|
|
238
|
+
# - staging
|
|
239
|
+
# - production
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### `grimoire template delete <wizard-name> <template-name>`
|
|
245
|
+
|
|
246
|
+
Delete a saved template.
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
grimoire template delete "Project Setup" staging
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Config Format
|
|
255
|
+
|
|
256
|
+
A grimoire config has these top-level sections:
|
|
257
|
+
|
|
258
|
+
| Section | Required | Description |
|
|
259
|
+
|---------|----------|-------------|
|
|
260
|
+
| `meta` | yes | Wizard name, version, description |
|
|
261
|
+
| `steps` | yes | Array of step definitions |
|
|
262
|
+
| `output` | no | Output format and file path |
|
|
263
|
+
| `theme` | no | Color tokens and icon overrides |
|
|
264
|
+
| `checks` | no | Pre-flight shell commands |
|
|
265
|
+
| `extends` | no | Path to a base config to inherit from |
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
meta:
|
|
269
|
+
name: My Wizard
|
|
270
|
+
version: 1.0.0
|
|
271
|
+
description: A short description shown at startup.
|
|
272
|
+
|
|
273
|
+
extends: ./base.yaml
|
|
274
|
+
|
|
275
|
+
theme:
|
|
276
|
+
tokens:
|
|
277
|
+
primary: "#cba6f7"
|
|
278
|
+
icons:
|
|
279
|
+
pointer: ">"
|
|
280
|
+
|
|
281
|
+
checks:
|
|
282
|
+
- name: Git available
|
|
283
|
+
run: git --version
|
|
284
|
+
message: Git is required.
|
|
285
|
+
|
|
286
|
+
steps:
|
|
287
|
+
- id: project-name
|
|
288
|
+
type: text
|
|
289
|
+
message: What is your project name?
|
|
290
|
+
|
|
291
|
+
output:
|
|
292
|
+
format: json
|
|
293
|
+
path: answers.json
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### `meta`
|
|
297
|
+
|
|
298
|
+
| Field | Required | Description |
|
|
299
|
+
|-------|----------|-------------|
|
|
300
|
+
| `name` | yes | Wizard name, shown in the header |
|
|
301
|
+
| `version` | no | Config version string |
|
|
302
|
+
| `description` | no | Short description shown below the name |
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Step Types Reference
|
|
307
|
+
|
|
308
|
+
Every step requires `id`, `type`, and `message`. All other fields are optional unless noted.
|
|
309
|
+
|
|
310
|
+
### `text`
|
|
311
|
+
|
|
312
|
+
Free-form text input with optional placeholder, default, and validation.
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
- id: project-name
|
|
316
|
+
type: text
|
|
317
|
+
message: What is your project name?
|
|
318
|
+
placeholder: my-awesome-project
|
|
319
|
+
default: my-app
|
|
320
|
+
validate:
|
|
321
|
+
- rule: required
|
|
322
|
+
- rule: minLength
|
|
323
|
+
value: 2
|
|
324
|
+
- rule: maxLength
|
|
325
|
+
value: 64
|
|
326
|
+
- rule: pattern
|
|
327
|
+
value: "^[a-z0-9-]+$"
|
|
328
|
+
message: Only lowercase letters, numbers, and hyphens
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### `select`
|
|
332
|
+
|
|
333
|
+
Single-choice list. Returns the selected `value` string. Options can have a `hint` and can be `disabled`.
|
|
334
|
+
|
|
335
|
+
```yaml
|
|
336
|
+
- id: language
|
|
337
|
+
type: select
|
|
338
|
+
message: Pick a language
|
|
339
|
+
default: typescript
|
|
340
|
+
options:
|
|
341
|
+
- { value: typescript, label: TypeScript }
|
|
342
|
+
- { value: javascript, label: JavaScript }
|
|
343
|
+
- { value: python, label: Python }
|
|
344
|
+
- { value: go, label: Go, hint: "Fast and simple" }
|
|
345
|
+
- { value: rust, label: Rust, disabled: true }
|
|
346
|
+
routes:
|
|
347
|
+
typescript: ts-config
|
|
348
|
+
python: python-version
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Instead of inline `options`, use `optionsFrom` to load options from an external file. See [Dynamic Options (`optionsFrom`)](#dynamic-options-optionsfrom).
|
|
352
|
+
|
|
353
|
+
### `multiselect`
|
|
354
|
+
|
|
355
|
+
Multi-choice list. Returns an array of selected `value` strings. Use `min` and `max` to constrain selection count.
|
|
356
|
+
|
|
357
|
+
```yaml
|
|
358
|
+
- id: features
|
|
359
|
+
type: multiselect
|
|
360
|
+
message: Select features to include
|
|
361
|
+
min: 1
|
|
362
|
+
max: 4
|
|
363
|
+
options:
|
|
364
|
+
- { value: eslint, label: ESLint, hint: Linting }
|
|
365
|
+
- { value: prettier, label: Prettier, hint: Formatting }
|
|
366
|
+
- { value: vitest, label: Vitest, hint: Testing }
|
|
367
|
+
- { value: husky, label: Husky, hint: "Git hooks" }
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Supports `optionsFrom` as an alternative to inline `options`. See [Dynamic Options (`optionsFrom`)](#dynamic-options-optionsfrom).
|
|
371
|
+
|
|
372
|
+
### `confirm`
|
|
373
|
+
|
|
374
|
+
Yes/no prompt. Returns a boolean.
|
|
375
|
+
|
|
376
|
+
```yaml
|
|
377
|
+
- id: use-typescript
|
|
378
|
+
type: confirm
|
|
379
|
+
message: Use TypeScript?
|
|
380
|
+
default: true
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### `password`
|
|
384
|
+
|
|
385
|
+
Masked text input. The value is included in output but never echoed to the terminal.
|
|
386
|
+
|
|
387
|
+
```yaml
|
|
388
|
+
- id: api-key
|
|
389
|
+
type: password
|
|
390
|
+
message: Enter your API key
|
|
391
|
+
validate:
|
|
392
|
+
- rule: required
|
|
393
|
+
- rule: minLength
|
|
394
|
+
value: 32
|
|
395
|
+
message: API keys must be at least 32 characters
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### `number`
|
|
399
|
+
|
|
400
|
+
Numeric input. Supports `min`, `max`, and `step` constraints.
|
|
401
|
+
|
|
402
|
+
```yaml
|
|
403
|
+
- id: port
|
|
404
|
+
type: number
|
|
405
|
+
message: Dev server port
|
|
406
|
+
default: 3000
|
|
407
|
+
min: 1024
|
|
408
|
+
max: 65535
|
|
409
|
+
step: 1
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### `search`
|
|
413
|
+
|
|
414
|
+
Searchable single-choice list with fuzzy filtering. Useful for long option lists.
|
|
415
|
+
|
|
416
|
+
```yaml
|
|
417
|
+
- id: framework
|
|
418
|
+
type: search
|
|
419
|
+
message: Search for a framework
|
|
420
|
+
placeholder: Type to filter...
|
|
421
|
+
options:
|
|
422
|
+
- { value: nextjs, label: "Next.js" }
|
|
423
|
+
- { value: remix, label: Remix }
|
|
424
|
+
- { value: astro, label: Astro }
|
|
425
|
+
- { value: sveltekit, label: SvelteKit }
|
|
426
|
+
- { value: nuxt, label: Nuxt }
|
|
427
|
+
- { value: gatsby, label: Gatsby }
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Supports `optionsFrom` as an alternative to inline `options`. See [Dynamic Options (`optionsFrom`)](#dynamic-options-optionsfrom).
|
|
431
|
+
|
|
432
|
+
### `editor`
|
|
433
|
+
|
|
434
|
+
Opens a multi-line text editor in the terminal. Returns the full text content when the user saves and exits.
|
|
435
|
+
|
|
436
|
+
```yaml
|
|
437
|
+
- id: readme-content
|
|
438
|
+
type: editor
|
|
439
|
+
message: Write your README content
|
|
440
|
+
default: "# My Project\n\nA short description."
|
|
441
|
+
required: false
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### `path`
|
|
445
|
+
|
|
446
|
+
File system path input with tab-completion for existing paths.
|
|
447
|
+
|
|
448
|
+
```yaml
|
|
449
|
+
- id: project-dir
|
|
450
|
+
type: path
|
|
451
|
+
message: Where should we create the project?
|
|
452
|
+
placeholder: ./my-project
|
|
453
|
+
default: .
|
|
454
|
+
validate:
|
|
455
|
+
- rule: required
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### `toggle`
|
|
459
|
+
|
|
460
|
+
A binary toggle with custom labels for each state. Returns a boolean.
|
|
461
|
+
|
|
462
|
+
```yaml
|
|
463
|
+
- id: dark-mode
|
|
464
|
+
type: toggle
|
|
465
|
+
message: Color scheme
|
|
466
|
+
active: Dark
|
|
467
|
+
inactive: Light
|
|
468
|
+
default: true
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Dynamic Options (`optionsFrom`)
|
|
474
|
+
|
|
475
|
+
`select`, `multiselect`, and `search` steps can load their options from an external JSON or YAML file instead of defining them inline. This is useful when the option list is large, shared across multiple wizards, or generated by another tool.
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
- id: framework
|
|
479
|
+
type: select
|
|
480
|
+
message: Pick a framework
|
|
481
|
+
optionsFrom: ./frameworks.json
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
`frameworks.json`:
|
|
485
|
+
|
|
486
|
+
```json
|
|
487
|
+
[
|
|
488
|
+
{ "value": "nextjs", "label": "Next.js" },
|
|
489
|
+
{ "value": "remix", "label": "Remix" },
|
|
490
|
+
{ "value": "astro", "label": "Astro" }
|
|
491
|
+
]
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Or as YAML:
|
|
495
|
+
|
|
496
|
+
```yaml
|
|
497
|
+
# frameworks.yaml
|
|
498
|
+
- value: nextjs
|
|
499
|
+
label: Next.js
|
|
500
|
+
- value: remix
|
|
501
|
+
label: Remix
|
|
502
|
+
- value: astro
|
|
503
|
+
label: Astro
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
```yaml
|
|
507
|
+
- id: framework
|
|
508
|
+
type: select
|
|
509
|
+
message: Pick a framework
|
|
510
|
+
optionsFrom: ./frameworks.yaml
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
The path in `optionsFrom` is resolved relative to the config file. Absolute paths are also accepted. You cannot use both `options` and `optionsFrom` on the same step. `optionsFrom` is not supported in `parseWizardYAML`; use `loadWizardConfig` with a file path instead.
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Step Groups
|
|
518
|
+
|
|
519
|
+
Add a `group` field to any step to display a section header when that group starts. The header is shown once, the first time a step in that group is reached.
|
|
520
|
+
|
|
521
|
+
```yaml
|
|
522
|
+
steps:
|
|
523
|
+
- id: name
|
|
524
|
+
type: text
|
|
525
|
+
group: "Project Info"
|
|
526
|
+
message: Project name?
|
|
527
|
+
|
|
528
|
+
- id: description
|
|
529
|
+
type: text
|
|
530
|
+
group: "Project Info"
|
|
531
|
+
message: Short description?
|
|
532
|
+
|
|
533
|
+
- id: framework
|
|
534
|
+
type: select
|
|
535
|
+
group: "Tech Stack"
|
|
536
|
+
message: Pick a framework
|
|
537
|
+
options:
|
|
538
|
+
- { value: react, label: React }
|
|
539
|
+
- { value: vue, label: Vue }
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Conditions
|
|
545
|
+
|
|
546
|
+
Use the `when` field on any step to show it only when a condition is met. Steps that don't pass their condition are skipped and excluded from the output.
|
|
547
|
+
|
|
548
|
+
```yaml
|
|
549
|
+
- id: ts-config
|
|
550
|
+
type: select
|
|
551
|
+
message: Which tsconfig preset?
|
|
552
|
+
when:
|
|
553
|
+
field: language
|
|
554
|
+
equals: typescript
|
|
555
|
+
options:
|
|
556
|
+
- { value: strict, label: Strict }
|
|
557
|
+
- { value: base, label: Base }
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Condition operators
|
|
561
|
+
|
|
562
|
+
| Operator | Shape | Description |
|
|
563
|
+
|----------|-------|-------------|
|
|
564
|
+
| `equals` | `{ field, equals }` | Field value equals the given value |
|
|
565
|
+
| `notEquals` | `{ field, notEquals }` | Field value does not equal the given value |
|
|
566
|
+
| `includes` | `{ field, includes }` | Array field includes the given value |
|
|
567
|
+
| `notIncludes` | `{ field, notIncludes }` | Array field does not include the given value |
|
|
568
|
+
| `greaterThan` | `{ field, greaterThan }` | Numeric field is greater than the given value |
|
|
569
|
+
| `lessThan` | `{ field, lessThan }` | Numeric field is less than the given value |
|
|
570
|
+
| `isEmpty` | `{ field, isEmpty: true }` | Field is empty, null, or an empty array |
|
|
571
|
+
| `isNotEmpty` | `{ field, isNotEmpty: true }` | Field is not empty |
|
|
572
|
+
|
|
573
|
+
### Compound conditions
|
|
574
|
+
|
|
575
|
+
Combine conditions with `all`, `any`, or `not`:
|
|
576
|
+
|
|
577
|
+
```yaml
|
|
578
|
+
# All conditions must be true
|
|
579
|
+
when:
|
|
580
|
+
all:
|
|
581
|
+
- field: language
|
|
582
|
+
equals: typescript
|
|
583
|
+
- field: features
|
|
584
|
+
includes: eslint
|
|
585
|
+
|
|
586
|
+
# Any condition must be true
|
|
587
|
+
when:
|
|
588
|
+
any:
|
|
589
|
+
- field: project-type
|
|
590
|
+
equals: web
|
|
591
|
+
- field: project-type
|
|
592
|
+
equals: api
|
|
593
|
+
|
|
594
|
+
# Negate a condition
|
|
595
|
+
when:
|
|
596
|
+
not:
|
|
597
|
+
field: skip-tests
|
|
598
|
+
equals: true
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Routes
|
|
604
|
+
|
|
605
|
+
A `select` step can branch to different step sequences based on the chosen value. Set `routes` to a map of option values to step IDs. Grimoire jumps to the mapped step instead of the next step in the list.
|
|
606
|
+
|
|
607
|
+
```yaml
|
|
608
|
+
- id: project-type
|
|
609
|
+
type: select
|
|
610
|
+
message: What kind of project?
|
|
611
|
+
options:
|
|
612
|
+
- { value: web, label: "Web App" }
|
|
613
|
+
- { value: api, label: "REST API" }
|
|
614
|
+
- { value: cli, label: "CLI Tool" }
|
|
615
|
+
routes:
|
|
616
|
+
web: web-framework
|
|
617
|
+
api: api-framework
|
|
618
|
+
cli: cli-features
|
|
619
|
+
|
|
620
|
+
- id: web-framework
|
|
621
|
+
type: select
|
|
622
|
+
message: Which web framework?
|
|
623
|
+
options:
|
|
624
|
+
- { value: nextjs, label: "Next.js" }
|
|
625
|
+
- { value: remix, label: Remix }
|
|
626
|
+
next: deploy
|
|
627
|
+
|
|
628
|
+
- id: api-framework
|
|
629
|
+
type: select
|
|
630
|
+
message: Which API framework?
|
|
631
|
+
options:
|
|
632
|
+
- { value: express, label: Express }
|
|
633
|
+
- { value: fastify, label: Fastify }
|
|
634
|
+
next: deploy
|
|
635
|
+
|
|
636
|
+
- id: cli-features
|
|
637
|
+
type: multiselect
|
|
638
|
+
message: CLI features to include
|
|
639
|
+
options:
|
|
640
|
+
- { value: args, label: "Argument parsing" }
|
|
641
|
+
- { value: colors, label: "Colored output" }
|
|
642
|
+
next: deploy
|
|
643
|
+
|
|
644
|
+
- id: deploy
|
|
645
|
+
type: select
|
|
646
|
+
message: Deployment target?
|
|
647
|
+
options:
|
|
648
|
+
- { value: vercel, label: Vercel }
|
|
649
|
+
- { value: docker, label: Docker }
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
Steps not reached via routing are skipped and excluded from output. Use `next` on any step to override the default sequential flow and jump to a specific step ID. The special value `__done__` ends the wizard immediately.
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## Validation
|
|
657
|
+
|
|
658
|
+
Add a `validate` array to any step that supports it (`text`, `password`, `editor`, `path`). Rules are checked in order and the first failure stops validation.
|
|
659
|
+
|
|
660
|
+
```yaml
|
|
661
|
+
validate:
|
|
662
|
+
- rule: required
|
|
663
|
+
- rule: minLength
|
|
664
|
+
value: 2
|
|
665
|
+
- rule: maxLength
|
|
666
|
+
value: 128
|
|
667
|
+
- rule: pattern
|
|
668
|
+
value: "^[a-zA-Z0-9_-]+$"
|
|
669
|
+
message: Only letters, numbers, underscores, and hyphens allowed
|
|
670
|
+
- rule: min
|
|
671
|
+
value: 0
|
|
672
|
+
- rule: max
|
|
673
|
+
value: 100
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
| Rule | Applies to | Description |
|
|
677
|
+
|------|-----------|-------------|
|
|
678
|
+
| `required` | all | Value must not be empty |
|
|
679
|
+
| `minLength` | text, password, editor, path | Minimum character count |
|
|
680
|
+
| `maxLength` | text, password, editor, path | Maximum character count |
|
|
681
|
+
| `pattern` | text, password, editor, path | Must match the given regex string |
|
|
682
|
+
| `min` | number | Minimum numeric value |
|
|
683
|
+
| `max` | number | Maximum numeric value |
|
|
684
|
+
|
|
685
|
+
All rules accept an optional `message` field to override the default error text.
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## Theming
|
|
690
|
+
|
|
691
|
+
Define a `theme` block in your config to customize colors and icons. Colors accept any 6-digit hex string. All tokens are optional and fall back to grimoire's defaults.
|
|
692
|
+
|
|
693
|
+
```yaml
|
|
694
|
+
theme:
|
|
695
|
+
tokens:
|
|
696
|
+
primary: "#89b4fa"
|
|
697
|
+
success: "#a6e3a1"
|
|
698
|
+
error: "#f38ba8"
|
|
699
|
+
warning: "#fab387"
|
|
700
|
+
info: "#74c7ec"
|
|
701
|
+
muted: "#6c7086"
|
|
702
|
+
accent: "#cba6f7"
|
|
703
|
+
icons:
|
|
704
|
+
step: "●"
|
|
705
|
+
stepDone: "✔"
|
|
706
|
+
stepPending: "○"
|
|
707
|
+
pointer: "❯"
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Color tokens
|
|
711
|
+
|
|
712
|
+
| Token | Used for |
|
|
713
|
+
|-------|---------|
|
|
714
|
+
| `primary` | Step headers, active selections |
|
|
715
|
+
| `success` | Completion messages, check marks |
|
|
716
|
+
| `error` | Validation errors, failed checks |
|
|
717
|
+
| `warning` | Cancellation messages |
|
|
718
|
+
| `info` | Informational text |
|
|
719
|
+
| `muted` | Descriptions, secondary text |
|
|
720
|
+
| `accent` | Highlights, group headers |
|
|
721
|
+
|
|
722
|
+
### Icons
|
|
723
|
+
|
|
724
|
+
| Icon | Used for |
|
|
725
|
+
|------|---------|
|
|
726
|
+
| `step` | Current step indicator |
|
|
727
|
+
| `stepDone` | Completed step indicator |
|
|
728
|
+
| `stepPending` | Upcoming step indicator |
|
|
729
|
+
| `pointer` | Selection cursor in lists |
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
## Environment Variables
|
|
734
|
+
|
|
735
|
+
Any `default` field that starts with `$` is treated as an environment variable reference. Grimoire resolves it at runtime from `process.env`. If the variable is not set, the literal string (e.g., `$MY_VAR`) is used as the default.
|
|
736
|
+
|
|
737
|
+
```yaml
|
|
738
|
+
- id: api-url
|
|
739
|
+
type: text
|
|
740
|
+
message: API base URL
|
|
741
|
+
default: $API_BASE_URL
|
|
742
|
+
|
|
743
|
+
- id: port
|
|
744
|
+
type: number
|
|
745
|
+
message: Port number
|
|
746
|
+
default: $PORT
|
|
747
|
+
|
|
748
|
+
- id: debug
|
|
749
|
+
type: confirm
|
|
750
|
+
message: Enable debug mode?
|
|
751
|
+
default: $DEBUG_MODE
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Supported on: `text`, `select`, `search`, `editor`, `path`, `number`, `confirm`, `toggle`.
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
## Config Inheritance
|
|
759
|
+
|
|
760
|
+
Use `extends` to inherit from a base config. The child config's `steps`, `theme`, `output`, and `checks` replace the base config's values entirely. The `meta` from the child takes precedence.
|
|
761
|
+
|
|
762
|
+
```yaml
|
|
763
|
+
# base.yaml
|
|
764
|
+
meta:
|
|
765
|
+
name: Base Wizard
|
|
766
|
+
theme:
|
|
767
|
+
tokens:
|
|
768
|
+
primary: "#7C3AED"
|
|
769
|
+
steps:
|
|
770
|
+
- id: name
|
|
771
|
+
type: text
|
|
772
|
+
message: Project name?
|
|
773
|
+
validate:
|
|
774
|
+
- rule: required
|
|
775
|
+
output:
|
|
776
|
+
format: json
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
```yaml
|
|
780
|
+
# extended.yaml
|
|
781
|
+
extends: ./base.yaml
|
|
782
|
+
|
|
783
|
+
meta:
|
|
784
|
+
name: Extended Wizard
|
|
785
|
+
description: Extends base config with additional steps
|
|
786
|
+
|
|
787
|
+
steps:
|
|
788
|
+
- id: name
|
|
789
|
+
type: text
|
|
790
|
+
message: Project name?
|
|
791
|
+
validate:
|
|
792
|
+
- rule: required
|
|
793
|
+
|
|
794
|
+
- id: language
|
|
795
|
+
type: select
|
|
796
|
+
message: Language?
|
|
797
|
+
options:
|
|
798
|
+
- { value: typescript, label: TypeScript }
|
|
799
|
+
- { value: javascript, label: JavaScript }
|
|
800
|
+
|
|
801
|
+
- id: confirm
|
|
802
|
+
type: confirm
|
|
803
|
+
message: Create project?
|
|
804
|
+
next: __done__
|
|
805
|
+
|
|
806
|
+
output:
|
|
807
|
+
format: yaml
|
|
808
|
+
path: project-config.yaml
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
The `extends` path is resolved relative to the child config file.
|
|
812
|
+
|
|
813
|
+
---
|
|
814
|
+
|
|
815
|
+
## Pre-flight Checks
|
|
816
|
+
|
|
817
|
+
The `checks` block runs shell commands before the wizard starts. If any command exits with a non-zero code, grimoire prints the associated `message` and aborts. Pre-flight checks are skipped in `--mock` mode.
|
|
818
|
+
|
|
819
|
+
```yaml
|
|
820
|
+
checks:
|
|
821
|
+
- name: Node.js installed
|
|
822
|
+
run: node --version
|
|
823
|
+
message: "Node.js is required. Install from https://nodejs.org"
|
|
824
|
+
|
|
825
|
+
- name: Git available
|
|
826
|
+
run: git --version
|
|
827
|
+
message: "Git is required. Install from https://git-scm.com"
|
|
828
|
+
|
|
829
|
+
- name: Docker running
|
|
830
|
+
run: docker info
|
|
831
|
+
message: "Docker must be running before deployment."
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
| Field | Required | Description |
|
|
835
|
+
|-------|----------|-------------|
|
|
836
|
+
| `name` | yes | Display name shown in the pre-flight output |
|
|
837
|
+
| `run` | yes | Shell command to execute |
|
|
838
|
+
| `message` | yes | Error message shown if the command fails |
|
|
839
|
+
|
|
840
|
+
---
|
|
841
|
+
|
|
842
|
+
## Plugin System
|
|
843
|
+
|
|
844
|
+
Plugins let you register custom step types with their own render and validate logic. A plugin is a plain object with a `name` and a `steps` map.
|
|
845
|
+
|
|
846
|
+
```typescript
|
|
847
|
+
import { runWizard, defineWizard } from 'grimoire-wizard'
|
|
848
|
+
import type { GrimoirePlugin } from 'grimoire-wizard'
|
|
849
|
+
|
|
850
|
+
const myPlugin: GrimoirePlugin = {
|
|
851
|
+
name: 'my-plugin',
|
|
852
|
+
steps: {
|
|
853
|
+
'date-picker': {
|
|
854
|
+
async render(config, state, theme) {
|
|
855
|
+
// config is the raw step config object
|
|
856
|
+
// state is the current WizardState
|
|
857
|
+
// theme is the ResolvedTheme
|
|
858
|
+
const answer = await myDatePickerPrompt(config.message as string)
|
|
859
|
+
return answer
|
|
860
|
+
},
|
|
861
|
+
validate(value, config) {
|
|
862
|
+
if (!value) return 'A date is required'
|
|
863
|
+
return null // null means valid
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const config = defineWizard({
|
|
870
|
+
meta: { name: 'My Wizard' },
|
|
871
|
+
steps: [
|
|
872
|
+
{
|
|
873
|
+
id: 'launch-date',
|
|
874
|
+
type: 'date-picker',
|
|
875
|
+
message: 'Pick a launch date',
|
|
876
|
+
} as any,
|
|
877
|
+
],
|
|
878
|
+
output: { format: 'json' },
|
|
879
|
+
})
|
|
880
|
+
|
|
881
|
+
const answers = await runWizard(config, {
|
|
882
|
+
plugins: [myPlugin],
|
|
883
|
+
})
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
Built-in step types (`text`, `select`, `multiselect`, `confirm`, `password`, `number`, `search`, `editor`, `path`, `toggle`) cannot be overridden by plugins.
|
|
887
|
+
|
|
888
|
+
### Plugin interfaces
|
|
889
|
+
|
|
890
|
+
```typescript
|
|
891
|
+
interface GrimoirePlugin {
|
|
892
|
+
name: string;
|
|
893
|
+
steps: Record<string, StepPlugin>;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
interface StepPlugin {
|
|
897
|
+
render(
|
|
898
|
+
config: Record<string, unknown>,
|
|
899
|
+
state: WizardState,
|
|
900
|
+
theme: ResolvedTheme
|
|
901
|
+
): Promise<unknown>;
|
|
902
|
+
validate?(value: unknown, config: Record<string, unknown>): string | null;
|
|
903
|
+
}
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
---
|
|
907
|
+
|
|
908
|
+
## Programmatic API
|
|
909
|
+
|
|
910
|
+
### Core functions
|
|
911
|
+
|
|
912
|
+
```typescript
|
|
913
|
+
import { loadWizardConfig, runWizard, defineWizard } from 'grimoire-wizard'
|
|
914
|
+
|
|
915
|
+
// Load from a file (YAML, JSON, JS, or TS)
|
|
916
|
+
const config = await loadWizardConfig('./setup.yaml')
|
|
917
|
+
const answers = await runWizard(config)
|
|
918
|
+
console.log(answers)
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
### `defineWizard(config)`
|
|
922
|
+
|
|
923
|
+
Identity function that returns the config unchanged. Its value is type inference: TypeScript will check your config object against `WizardConfig` at compile time.
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
import { defineWizard, runWizard } from 'grimoire-wizard'
|
|
927
|
+
|
|
928
|
+
const config = defineWizard({
|
|
929
|
+
meta: {
|
|
930
|
+
name: 'Deploy Config',
|
|
931
|
+
description: 'Configure your deployment target.',
|
|
932
|
+
},
|
|
933
|
+
steps: [
|
|
934
|
+
{
|
|
935
|
+
id: 'environment',
|
|
936
|
+
type: 'select',
|
|
937
|
+
message: 'Target environment?',
|
|
938
|
+
options: [
|
|
939
|
+
{ label: 'Production', value: 'prod' },
|
|
940
|
+
{ label: 'Staging', value: 'staging' },
|
|
941
|
+
],
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
id: 'confirm',
|
|
945
|
+
type: 'confirm',
|
|
946
|
+
message: 'Ready to deploy?',
|
|
947
|
+
default: false,
|
|
948
|
+
},
|
|
949
|
+
],
|
|
950
|
+
output: { format: 'json' },
|
|
951
|
+
})
|
|
952
|
+
|
|
953
|
+
const answers = await runWizard(config, {
|
|
954
|
+
onStepComplete: (stepId, value, state) => {
|
|
955
|
+
console.log(`${stepId} answered: ${String(value)}`)
|
|
956
|
+
},
|
|
957
|
+
onCancel: (state) => {
|
|
958
|
+
console.log('Wizard cancelled')
|
|
959
|
+
process.exit(1)
|
|
960
|
+
},
|
|
961
|
+
})
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
### `RunWizardOptions`
|
|
965
|
+
|
|
966
|
+
| Option | Type | Description |
|
|
967
|
+
|--------|------|-------------|
|
|
968
|
+
| `renderer` | `WizardRenderer` | Custom renderer (defaults to `InquirerRenderer`) |
|
|
969
|
+
| `quiet` | `boolean` | Suppress title, description, and summary output |
|
|
970
|
+
| `plain` | `boolean` | Disable colors and banner (plain-text mode) |
|
|
971
|
+
| `mockAnswers` | `Record<string, unknown>` | Preset answers for non-interactive mode |
|
|
972
|
+
| `templateAnswers` | `Record<string, unknown>` | Pre-loaded template answers used as defaults |
|
|
973
|
+
| `onBeforeStep` | `(stepId, step, state) => void \| Promise<void>` | Called before each step is rendered |
|
|
974
|
+
| `onAfterStep` | `(stepId, value, state) => void \| Promise<void>` | Called after each step completes, before moving on |
|
|
975
|
+
| `onStepComplete` | `(stepId, value, state) => void` | Called after each step completes |
|
|
976
|
+
| `onCancel` | `(state) => void` | Called when the user cancels with Ctrl+C |
|
|
977
|
+
| `plugins` | `GrimoirePlugin[]` | Custom step type plugins |
|
|
978
|
+
| `asyncValidate` | `(stepId, value, answers) => Promise<string \| null>` | Async validation hook called after each step |
|
|
979
|
+
| `cache` | `boolean \| { dir?: string }` | Enable/disable answer caching, or set a custom cache directory (default: `true`) |
|
|
980
|
+
| `mru` | `boolean` | Enable/disable MRU ordering for select/multiselect/search steps (default: `true`) |
|
|
981
|
+
|
|
982
|
+
### Async validation
|
|
983
|
+
|
|
984
|
+
The `asyncValidate` hook runs after each step's synchronous validation passes. Return a string to show as an error and re-prompt the step, or return `null` to accept the value.
|
|
985
|
+
|
|
986
|
+
```typescript
|
|
987
|
+
const answers = await runWizard(config, {
|
|
988
|
+
asyncValidate: async (stepId, value, answers) => {
|
|
989
|
+
if (stepId === 'username') {
|
|
990
|
+
const taken = await checkUsernameAvailability(value as string)
|
|
991
|
+
if (taken) return 'That username is already taken'
|
|
992
|
+
}
|
|
993
|
+
return null
|
|
994
|
+
},
|
|
995
|
+
})
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
### Lifecycle hooks
|
|
999
|
+
|
|
1000
|
+
`onBeforeStep` fires just before a step is rendered. `onAfterStep` fires after the user answers but before the wizard advances. Both can be async.
|
|
1001
|
+
|
|
1002
|
+
```typescript
|
|
1003
|
+
const answers = await runWizard(config, {
|
|
1004
|
+
onBeforeStep: async (stepId, step, state) => {
|
|
1005
|
+
console.log(`About to render: ${stepId}`)
|
|
1006
|
+
},
|
|
1007
|
+
onAfterStep: async (stepId, value, state) => {
|
|
1008
|
+
await logAnswer(stepId, value)
|
|
1009
|
+
},
|
|
1010
|
+
})
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
### Ink renderer
|
|
1014
|
+
|
|
1015
|
+
The `InkRenderer` is an alternative to the default `InquirerRenderer`. It uses box-drawing characters, a filled progress bar with percentage, and formatted step headers.
|
|
1016
|
+
|
|
1017
|
+
```typescript
|
|
1018
|
+
import { runWizard } from 'grimoire-wizard'
|
|
1019
|
+
import { InkRenderer } from 'grimoire-wizard/renderers/ink'
|
|
1020
|
+
|
|
1021
|
+
const answers = await runWizard(config, {
|
|
1022
|
+
renderer: new InkRenderer(),
|
|
1023
|
+
})
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
Or from the CLI:
|
|
1027
|
+
|
|
1028
|
+
```bash
|
|
1029
|
+
grimoire run setup.yaml --renderer ink
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
### Exported types
|
|
1033
|
+
|
|
1034
|
+
```typescript
|
|
1035
|
+
import type {
|
|
1036
|
+
WizardConfig,
|
|
1037
|
+
StepConfig,
|
|
1038
|
+
TextStepConfig,
|
|
1039
|
+
SelectStepConfig,
|
|
1040
|
+
MultiSelectStepConfig,
|
|
1041
|
+
ConfirmStepConfig,
|
|
1042
|
+
PasswordStepConfig,
|
|
1043
|
+
NumberStepConfig,
|
|
1044
|
+
SearchStepConfig,
|
|
1045
|
+
EditorStepConfig,
|
|
1046
|
+
PathStepConfig,
|
|
1047
|
+
ToggleStepConfig,
|
|
1048
|
+
SelectOption,
|
|
1049
|
+
ValidationRule,
|
|
1050
|
+
Condition,
|
|
1051
|
+
ThemeConfig,
|
|
1052
|
+
ResolvedTheme,
|
|
1053
|
+
WizardState,
|
|
1054
|
+
WizardTransition,
|
|
1055
|
+
WizardRenderer,
|
|
1056
|
+
PreFlightCheck,
|
|
1057
|
+
RunWizardOptions,
|
|
1058
|
+
GrimoirePlugin,
|
|
1059
|
+
StepPlugin,
|
|
1060
|
+
} from 'grimoire-wizard'
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
### Utility functions
|
|
1064
|
+
|
|
1065
|
+
```typescript
|
|
1066
|
+
import {
|
|
1067
|
+
parseWizardConfig, // Parse and validate a raw config object against the schema
|
|
1068
|
+
parseWizardYAML, // Parse a YAML string into a WizardConfig
|
|
1069
|
+
evaluateCondition, // Evaluate a Condition against an answers map
|
|
1070
|
+
isStepVisible, // Check if a step should be shown given current answers
|
|
1071
|
+
createWizardState, // Create an initial WizardState from a config
|
|
1072
|
+
wizardReducer, // Pure reducer for wizard state transitions
|
|
1073
|
+
getVisibleSteps, // Get all currently visible steps given current answers
|
|
1074
|
+
resolveNextStep, // Resolve the next step ID (respects routes and next fields)
|
|
1075
|
+
validateStepAnswer, // Run validation rules against a value
|
|
1076
|
+
resolveTheme, // Merge a ThemeConfig with defaults into a ResolvedTheme
|
|
1077
|
+
resolveEnvDefault, // Resolve a $ENV_VAR string from process.env
|
|
1078
|
+
runPreFlightChecks, // Run a checks array and throw on first failure
|
|
1079
|
+
registerPlugin, // Register a GrimoirePlugin into the global registry
|
|
1080
|
+
getPluginStep, // Look up a registered StepPlugin by type name
|
|
1081
|
+
clearPlugins, // Clear all registered plugins
|
|
1082
|
+
// Cache
|
|
1083
|
+
loadCachedAnswers, // Load cached answers for a wizard by name
|
|
1084
|
+
saveCachedAnswers, // Save answers to the cache for a wizard
|
|
1085
|
+
clearCache, // Delete cached answers (one wizard or all)
|
|
1086
|
+
// MRU
|
|
1087
|
+
recordSelection, // Record a user's selection for MRU tracking
|
|
1088
|
+
getOrderedOptions, // Return options sorted by selection frequency
|
|
1089
|
+
// Templates
|
|
1090
|
+
saveTemplate, // Save a named answer preset for a wizard
|
|
1091
|
+
loadTemplate, // Load a named answer preset
|
|
1092
|
+
listTemplates, // List all saved template names for a wizard
|
|
1093
|
+
deleteTemplate, // Delete a named template
|
|
1094
|
+
// Banner
|
|
1095
|
+
renderBanner, // Render the ASCII art banner for a wizard name
|
|
1096
|
+
} from 'grimoire-wizard'
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
---
|
|
1100
|
+
|
|
1101
|
+
## Answer Caching
|
|
1102
|
+
|
|
1103
|
+
Grimoire remembers your answers between runs. The next time you run the same wizard, cached answers become the default values for each step, so you only need to change what's different.
|
|
1104
|
+
|
|
1105
|
+
Password steps are never cached.
|
|
1106
|
+
|
|
1107
|
+
Cache files are stored in `~/.config/grimoire/cache/` as JSON, one file per wizard (named by slugifying the wizard's `meta.name`).
|
|
1108
|
+
|
|
1109
|
+
Caching is enabled by default. Disable it for a single run with `--no-cache`:
|
|
1110
|
+
|
|
1111
|
+
```bash
|
|
1112
|
+
grimoire run setup.yaml --no-cache
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
Clear the cache from the CLI:
|
|
1116
|
+
|
|
1117
|
+
```bash
|
|
1118
|
+
grimoire cache clear # clears all wizards
|
|
1119
|
+
grimoire cache clear "Project Setup" # clears one wizard
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
Control caching in the programmatic API via the `cache` option:
|
|
1123
|
+
|
|
1124
|
+
```typescript
|
|
1125
|
+
// Disable caching entirely
|
|
1126
|
+
const answers = await runWizard(config, { cache: false })
|
|
1127
|
+
|
|
1128
|
+
// Use a custom cache directory
|
|
1129
|
+
const answers = await runWizard(config, { cache: { dir: '/tmp/my-cache' } })
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
---
|
|
1133
|
+
|
|
1134
|
+
## Templates
|
|
1135
|
+
|
|
1136
|
+
Templates are named answer presets. Save a set of answers under a name, then load them as defaults on future runs. Useful for environments like staging vs. production that share the same wizard but need different values.
|
|
1137
|
+
|
|
1138
|
+
Templates are stored in `~/.config/grimoire/templates/<wizard-slug>/`.
|
|
1139
|
+
|
|
1140
|
+
### Saving a template
|
|
1141
|
+
|
|
1142
|
+
Templates are saved programmatically after a run:
|
|
1143
|
+
|
|
1144
|
+
```typescript
|
|
1145
|
+
import { runWizard, saveTemplate } from 'grimoire-wizard'
|
|
1146
|
+
|
|
1147
|
+
const answers = await runWizard(config)
|
|
1148
|
+
saveTemplate('Project Setup', 'production', answers)
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
### Loading a template via CLI
|
|
1152
|
+
|
|
1153
|
+
```bash
|
|
1154
|
+
grimoire run setup.yaml --template production
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
Template answers become the defaults for each step. The user can still change any value.
|
|
1158
|
+
|
|
1159
|
+
### Managing templates
|
|
1160
|
+
|
|
1161
|
+
```bash
|
|
1162
|
+
grimoire template list "Project Setup"
|
|
1163
|
+
grimoire template delete "Project Setup" production
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
### Programmatic API
|
|
1167
|
+
|
|
1168
|
+
```typescript
|
|
1169
|
+
import { saveTemplate, loadTemplate, listTemplates, deleteTemplate } from 'grimoire-wizard'
|
|
1170
|
+
|
|
1171
|
+
saveTemplate('Project Setup', 'staging', answers)
|
|
1172
|
+
|
|
1173
|
+
const staging = loadTemplate('Project Setup', 'staging')
|
|
1174
|
+
|
|
1175
|
+
const names = listTemplates('Project Setup')
|
|
1176
|
+
// ['production', 'staging']
|
|
1177
|
+
|
|
1178
|
+
deleteTemplate('Project Setup', 'staging')
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
---
|
|
1182
|
+
|
|
1183
|
+
## MRU (Most Recently Used) Ordering
|
|
1184
|
+
|
|
1185
|
+
For `select`, `multiselect`, and `search` steps, grimoire tracks which options you pick and floats the most frequently chosen ones to the top of the list on subsequent runs. Options you've never picked stay in their original order below.
|
|
1186
|
+
|
|
1187
|
+
MRU data is stored in `~/.config/grimoire/mru/` and is per-wizard, per-step.
|
|
1188
|
+
|
|
1189
|
+
MRU ordering is enabled by default. Disable it programmatically:
|
|
1190
|
+
|
|
1191
|
+
```typescript
|
|
1192
|
+
const answers = await runWizard(config, { mru: false })
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
There's no CLI flag for this. It's a programmatic-only option.
|
|
1196
|
+
|
|
1197
|
+
---
|
|
1198
|
+
|
|
1199
|
+
## Banner
|
|
1200
|
+
|
|
1201
|
+
When grimoire starts a wizard in interactive mode, it renders the wizard name as ASCII art using figlet with a purple-to-blue-to-green gradient. If figlet rendering fails for any reason, it falls back to plain bold text.
|
|
1202
|
+
|
|
1203
|
+
The banner is suppressed in `--quiet` mode and in `--plain` mode. Use `--plain` to get clean, color-free output suitable for terminals that don't support ANSI codes or for piping:
|
|
1204
|
+
|
|
1205
|
+
```bash
|
|
1206
|
+
grimoire run setup.yaml --plain
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
The `NO_COLOR` environment variable also disables colors and the banner:
|
|
1210
|
+
|
|
1211
|
+
```bash
|
|
1212
|
+
NO_COLOR=1 grimoire run setup.yaml
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
---
|
|
1216
|
+
|
|
1217
|
+
## CI/CD Integration
|
|
1218
|
+
|
|
1219
|
+
Combine `--mock` and `--json` to run wizards non-interactively in pipelines and capture structured output.
|
|
1220
|
+
|
|
1221
|
+
```bash
|
|
1222
|
+
# Run with preset answers, capture JSON output
|
|
1223
|
+
grimoire run deploy.yaml \
|
|
1224
|
+
--mock '{"environment":"staging","version":"1.2.3","confirm":true}' \
|
|
1225
|
+
--json > deploy-answers.json
|
|
1226
|
+
|
|
1227
|
+
# Use the output in subsequent steps
|
|
1228
|
+
cat deploy-answers.json | jq '.answers.environment'
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
The `--json` flag always emits to stdout. On success:
|
|
1232
|
+
|
|
1233
|
+
```json
|
|
1234
|
+
{
|
|
1235
|
+
"ok": true,
|
|
1236
|
+
"wizard": "Deployment Wizard",
|
|
1237
|
+
"answers": { "environment": "staging", "version": "1.2.3" },
|
|
1238
|
+
"stepsCompleted": 3,
|
|
1239
|
+
"format": "json"
|
|
1240
|
+
}
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
On failure:
|
|
1244
|
+
|
|
1245
|
+
```json
|
|
1246
|
+
{
|
|
1247
|
+
"ok": false,
|
|
1248
|
+
"error": "Mock mode: no answer provided for step \"confirm\" and no default available"
|
|
1249
|
+
}
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
Exit code is `0` on success and `1` on failure, so pipelines can branch on the result.
|
|
1253
|
+
|
|
1254
|
+
---
|
|
1255
|
+
|
|
1256
|
+
## JSON Schema
|
|
1257
|
+
|
|
1258
|
+
Grimoire ships a JSON Schema at `schema/grimoire.schema.json`. Add it to your editor for autocomplete and inline validation of config files.
|
|
1259
|
+
|
|
1260
|
+
### VS Code
|
|
1261
|
+
|
|
1262
|
+
Add this to your workspace `.vscode/settings.json`:
|
|
1263
|
+
|
|
1264
|
+
```json
|
|
1265
|
+
{
|
|
1266
|
+
"yaml.schemas": {
|
|
1267
|
+
"./node_modules/grimoire-wizard/schema/grimoire.schema.json": "*.wizard.yaml"
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
Or add a `$schema` comment at the top of any config file:
|
|
1273
|
+
|
|
1274
|
+
```yaml
|
|
1275
|
+
# yaml-language-server: $schema=./node_modules/grimoire-wizard/schema/grimoire.schema.json
|
|
1276
|
+
meta:
|
|
1277
|
+
name: My Wizard
|
|
1278
|
+
```
|
|
1279
|
+
|
|
1280
|
+
### Other editors
|
|
1281
|
+
|
|
1282
|
+
Point your editor's JSON Schema support at:
|
|
1283
|
+
|
|
1284
|
+
```
|
|
1285
|
+
./node_modules/grimoire-wizard/schema/grimoire.schema.json
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
---
|
|
1289
|
+
|
|
1290
|
+
## Shell Completions
|
|
1291
|
+
|
|
1292
|
+
### Bash
|
|
1293
|
+
|
|
1294
|
+
```bash
|
|
1295
|
+
grimoire completion bash > ~/.bash_completion.d/grimoire
|
|
1296
|
+
source ~/.bash_completion.d/grimoire
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
Or add to your `.bashrc`:
|
|
1300
|
+
|
|
1301
|
+
```bash
|
|
1302
|
+
eval "$(grimoire completion bash)"
|
|
1303
|
+
```
|
|
1304
|
+
|
|
1305
|
+
### Zsh
|
|
1306
|
+
|
|
1307
|
+
```bash
|
|
1308
|
+
grimoire completion zsh > ~/.zsh/completions/_grimoire
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
Make sure `~/.zsh/completions` is in your `$fpath`. Then reload:
|
|
1312
|
+
|
|
1313
|
+
```bash
|
|
1314
|
+
autoload -Uz compinit && compinit
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
### Fish
|
|
1318
|
+
|
|
1319
|
+
```bash
|
|
1320
|
+
grimoire completion fish > ~/.config/fish/completions/grimoire.fish
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
---
|
|
1324
|
+
|
|
1325
|
+
## Examples
|
|
1326
|
+
|
|
1327
|
+
The `examples/` directory contains 8 ready-to-run configs.
|
|
1328
|
+
|
|
1329
|
+
### `examples/basic.yaml`
|
|
1330
|
+
|
|
1331
|
+
A 6-step project setup wizard covering the core input types: project name (text with validation and pattern), description (optional text), language (select with default), features (multiselect with min), license (select), and confirm. Good starting point for your own configs.
|
|
1332
|
+
|
|
1333
|
+
```bash
|
|
1334
|
+
grimoire run examples/basic.yaml
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
### `examples/conditional.yaml`
|
|
1338
|
+
|
|
1339
|
+
An 11-step wizard that branches based on project type. A `select` step with `routes` sends users down one of four paths (web, api, cli, lib), each with its own framework or feature selection step. Additional steps use `when` conditions to show or hide options based on earlier answers.
|
|
1340
|
+
|
|
1341
|
+
```bash
|
|
1342
|
+
grimoire run examples/conditional.yaml
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
### `examples/themed.yaml`
|
|
1346
|
+
|
|
1347
|
+
An 8-step account setup wizard styled with the Catppuccin Mocha color palette. Demonstrates all 7 color tokens and custom icon overrides. Steps cover username, email, role, experience, interests, newsletter, password, and confirm.
|
|
1348
|
+
|
|
1349
|
+
```bash
|
|
1350
|
+
grimoire run examples/themed.yaml
|
|
1351
|
+
```
|
|
1352
|
+
|
|
1353
|
+
### `examples/demo.yaml`
|
|
1354
|
+
|
|
1355
|
+
The built-in demo config used by `grimoire demo`. Exercises all 10 step types across 4 named groups: Text Inputs (text, editor, path, password), Selections (select, multiselect, search), Toggles and Numbers (toggle, number), and Confirmation (confirm).
|
|
1356
|
+
|
|
1357
|
+
```bash
|
|
1358
|
+
grimoire run examples/demo.yaml
|
|
1359
|
+
# or
|
|
1360
|
+
grimoire demo
|
|
1361
|
+
```
|
|
1362
|
+
|
|
1363
|
+
### `examples/all-features.yaml`
|
|
1364
|
+
|
|
1365
|
+
A focused showcase of the newer step types: search, path, toggle, editor, and number, organized into two groups (Project Setup and Configuration).
|
|
1366
|
+
|
|
1367
|
+
```bash
|
|
1368
|
+
grimoire run examples/all-features.yaml
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
### `examples/base.yaml`
|
|
1372
|
+
|
|
1373
|
+
A minimal base config with a name step, optional description, and confirm. Intended to be inherited via `extends`.
|
|
1374
|
+
|
|
1375
|
+
```bash
|
|
1376
|
+
grimoire run examples/base.yaml
|
|
1377
|
+
```
|
|
1378
|
+
|
|
1379
|
+
### `examples/extended.yaml`
|
|
1380
|
+
|
|
1381
|
+
Extends `base.yaml` and adds a language select step. Demonstrates how `extends` merges configs and how the child's `output` and `meta` override the base.
|
|
1382
|
+
|
|
1383
|
+
```bash
|
|
1384
|
+
grimoire run examples/extended.yaml
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1387
|
+
### `examples/with-checks.yaml`
|
|
1388
|
+
|
|
1389
|
+
A deployment wizard with two pre-flight checks (Node.js and Git). Shows how `checks` abort the wizard before any prompts if the environment isn't ready.
|
|
1390
|
+
|
|
1391
|
+
```bash
|
|
1392
|
+
grimoire run examples/with-checks.yaml
|
|
1393
|
+
```
|
|
1394
|
+
|
|
1395
|
+
---
|
|
1396
|
+
|
|
1397
|
+
## License
|
|
1398
|
+
|
|
1399
|
+
MIT. PRs welcome.
|