# CLI

# CLI

Madori source checkout includes command-line tools for scaffolding, content migration, code generation, and administration. Generated `create-madori-app` projects do not bundle this CLI; manage those projects through control panel instead. `@madori/cli` is currently source-checkout tooling rather than a published registry package.

---

## Configuration Reference

### Running CLI Commands

From Madori source checkout, run commands with:

```bash
pnpm madori <command>
```

### Available Commands

| Command | Description |
|---------|-------------|
| `make:user` | Create a new user account interactively |
| `make:blueprint <handle>` | Generate a blueprint from content, schema, or interactively |
| `make:collection <handle>` | Scaffold a complete collection with blueprint and example entry |
| `migrate:definitions` | Migrate legacy entity definitions from config to flat files |
| `migrate:wordpress <export-file>` | Migrate content from a WordPress WXR export file |
| `migrate:markdown <source-directory>` | Migrate Markdown files from a directory into a collection |
| `export <path>` | Export blueprints, collections, fieldsets, and content as an archive |
| `import <archive-path>` | Import resources and content from an archive |
| `init:preset <preset-name>` | Initialise project with an opinionated preset structure |
| `registry:pull <repository-url>` | Pull resources from a shared Git registry |
| `registry:push <repository-url>` | Push local resources to a shared Git registry |
| `git:status` | Show Git sync status for configured repositories |
| `git:sync` | Commit and optionally push pending tracked changes |
| `git:retry --repository <id>` | Retry a pending Git push for repository ID from `git:status` |
| `seo:migrate` | Migrate legacy SEO frontmatter fields into nested `seo` values |
| `seo:rollback --plan <path>` | Roll back an SEO migration plan |
| `backup <path>` | Create a checksummed operational backup |
| `backup:verify <archive-path>` | Verify backup structure and checksums |
| `restore <archive-path> --yes` | Restore a verified backup and retain rollback copies |
| `generate` | Generate TypeScript types, schemas, and SDK from blueprints |
| `check` | Check manifest schema-version compatibility |

---

## Scaffolding Commands

Git sync commands and setup are documented in [Git Content Sync](/docs/git-sync).

### make:blueprint

Generate a blueprint from existing content, a JSON Schema file, or interactively.

```bash
pnpm madori make:blueprint <handle> [options]
```

| Flag | Type | Description |
|------|------|-------------|
| `--from-content <path>` | `string` | Infer blueprint from a Markdown file's frontmatter |
| `--from-schema <path>` | `string` | Generate blueprint from a JSON Schema file |

When run without flags, the command runs interactively. Fields inferred with low confidence default to `text` and are flagged in the output.

**Example — infer from content:**

```bash
pnpm madori make:blueprint blog --from-content content/collections/blog/hello.md
```

```
✓ Blueprint "blog" created successfully!

  Output: resources/blueprints/collections/blog.yaml
  Fields: 6

⚠ 1 field(s) defaulted to "text" (low confidence):
    - custom_meta
```

**Example — from JSON Schema:**

```bash
pnpm madori make:blueprint products --from-schema schemas/product.json
```

### make:collection

Scaffold a complete collection in one step — creates the collection definition, blueprint, and an example entry.

```bash
pnpm madori make:collection <handle> [options]
```

| Flag | Type | Description |
|------|------|-------------|
| `--fields <fields>` | `string` | Comma-separated field definitions: `handle:type[:required]` |
| `--route <route>` | `string` | Route pattern for the collection |

**Example:**

```bash
pnpm madori make:collection products --fields "name:text:required,price:number:required,description:tiptap,image:asset" --route "/products/{slug}"
```

```
✓ Collection "products" created successfully!

Files created:
  - resources/collections/products.yaml
  - resources/blueprints/collections/products.yaml
  - content/collections/products/example.md
```

### init:preset

Initialise a project with an opinionated preset structure for common use cases.

```bash
pnpm madori init:preset <preset-name> [options]
```

| Flag | Description |
|------|-------------|
| `--force` | Skip conflict prompts and overwrite existing resources |

**Available presets:**

| Preset | Description |
|--------|-------------|
| `marketing-site` | Pages, sections, and team members |
| `blog` | Blog posts with categories and tags |
| `documentation` | Documentation pages with navigation |
| `saas-landing` | SaaS marketing with features and pricing |
| `agency-portfolio` | Portfolio with case studies and team |

**Example:**

```bash
pnpm madori init:preset blog
```

```
✓ Preset "blog" applied successfully!

Files created:
  - resources/collections/posts.yaml
  - resources/blueprints/collections/posts.yaml
  - resources/taxonomies/categories.yaml
  - resources/taxonomies/tags.yaml
  - content/navigation/main.yaml

Next steps:
  Run `pnpm madori check` to check manifest schema compatibility.
```

---

## Migration Commands

### migrate:wordpress

Migrate content from a WordPress WXR export file into Madori entries. Converts HTML to Markdown, preserves categories and tags as taxonomy terms, and maps WordPress statuses.

```bash
pnpm madori migrate:wordpress <export-file> [options]
```

| Flag | Type | Description |
|------|------|-------------|
| `--collection <handle>` | `string` | Target collection handle (default: `posts` for posts, `pages` for pages) |

**Example:**

```bash
pnpm madori migrate:wordpress wordpress-export.xml --collection blog
```

```
Migrating WordPress content from: /path/to/wordpress-export.xml

Migration complete!

  Total processed: 142
  Entries created:  138
  Skipped:          4

Taxonomies:
  Categories: 8 terms → resources/taxonomies/categories.yaml
  Tags:       23 terms → resources/taxonomies/tags.yaml

Summary: 142 processed, 138 created, 4 skipped, 0 warnings
```

The migration:
- Converts HTML content to Markdown
- Maps `publish` → `published`, `draft`/`private` → `draft`
- Preserves author, categories, and tags
- Auto-creates taxonomy definition files

### migrate:markdown

Migrate a directory of Markdown files into a Madori collection. Extra frontmatter fields are preserved, but migration sets `title`, `slug`, `status`, `createdAt`, and `updatedAt`; imported entries become drafts. Review generated entries before publishing.

```bash
pnpm madori migrate:markdown <source-directory> [options]
```

| Flag | Type | Description |
|------|------|-------------|
| `--collection <handle>` | `string` | Target collection handle (prompted if not provided) |

**Example:**

```bash
pnpm madori migrate:markdown ./old-blog-posts --collection blog
```

```
Migrating Markdown files from: /path/to/old-blog-posts
Target collection: blog

Migration complete!

  Files processed:  47
  Entries created:  47
  Skipped:          0

Summary: 47 processed, 47 created, 0 skipped, 0 warnings
```

### migrate:definitions

Migrates `taxonomies`, `globals`, and `navigations` arrays from the config file into individual YAML files under `resources/`.

```bash
pnpm madori migrate:definitions [options]
```

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--config <path>` | `string` | `./madori.config.ts` | Path to the config file to migrate from |
| `--resources <path>` | `string` | `./resources` | Output directory for generated definition files |

---

## Portability Commands

### export

Export project resources and content as a portable archive.

```bash
pnpm madori export <output-path> [options]
```

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--format <format>` | `string` | `zip` | Archive format: `zip` or `tar` |
| `--resources <types>` | `string` | all | Comma-separated resource types to include |

Resource types: `blueprints`, `collections`, `fieldsets`, `content`

**Example:**

```bash
pnpm madori export ./backup.zip --format zip --resources "blueprints,fieldsets"
```

```
✓ Export completed successfully!

  Archive: ./backup.zip
  Size:    24.3 KB
  Files:   18
```

### import

Import resources and content from a previously exported archive.

```bash
pnpm madori import <archive-path>
```

**Example:**

```bash
pnpm madori import ./backup.zip
```

```
✓ Import completed successfully!

  Total files: 18
  Imported:    16
  Skipped:     2
  Conflicts:   0
```

Existing files are skipped by default to prevent accidental overwrites.

### Operational backup and restore

Back up configured content, resources, assets, users, configuration, SEO state,
sessions, and the schema manifest. Restore requires an explicit `--yes` and
keeps pre-restore rollback copies.

```bash
pnpm madori backup ./storage/madori-backup.tar.gz
pnpm madori backup:verify ./storage/madori-backup.tar.gz
pnpm madori restore ./storage/madori-backup.tar.gz --yes
```

Use `--rollback-dir <path>` with `restore` to choose the rollback directory.

### registry:pull

Pull shared resources from a Git repository into your local project.

```bash
pnpm madori registry:pull <repository-url> [options]
```

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--resources <types>` | `string` | all | Comma-separated resource types to pull |
| `--branch <branch>` | `string` | `main` | Git branch to pull from |

**Example:**

```bash
pnpm madori registry:pull https://github.com/my-agency/shared-blueprints.git --resources "blueprints,fieldsets"
```

### registry:push

Push local resources to a shared Git registry.

```bash
pnpm madori registry:push <repository-url> [options]
```

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--resources <types>` | `string` | all | Comma-separated resource types to push |
| `--branch <branch>` | `string` | `main` | Git branch to push to |

---

## Code Generation

### generate

Generate TypeScript types, Zod schemas, and a typed GraphQL SDK from your blueprints. Output goes to `.madori/generated/` by default.

```bash
pnpm madori generate [options]
```

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `-o, --output <path>` | `string` | `.madori/generated` | Output directory for generated files |
| `-w, --watch` | `boolean` | `false` | Watch blueprint files and regenerate on change |

**Example:**

```bash
pnpm madori generate
```

```
✓ Generate complete: 3 blueprint(s) processed in 42ms
```

**Watch mode:**

```bash
pnpm madori generate --watch
```

Watches YAML files under `resources/blueprints/**/*.yaml` and regenerates with
a 300ms debounce. It does not watch collection definitions or fieldsets, so run
`pnpm madori generate` explicitly after changing those resources.

The generator reads `resources/blueprints/**/*.yaml` from the project root and
does not derive this input directory from a custom `resourcesPath`. The generated
output includes:
- TypeScript interfaces for each collection's entries
- Zod schemas for runtime validation
- A typed GraphQL SDK with operations for each collection
- A barrel `index.ts` for convenient imports
- A `tsconfig.paths.json` for path alias configuration

Generated consumers import `@madori/sdk`, which is currently a source workspace package. Build and link the SDK before using those imports; see [SDK and content access](/docs/sdk) for setup. Path aliases alone do not install the package.

---

## Administration Commands

### make:user

Create a new user account interactively.

```bash
pnpm madori make:user
```

| Prompt | Type | Validation |
|--------|------|------------|
| Email address | `string` | Must be valid email, must not already exist |
| Display name | `string` | Required, non-empty |
| Password | `string` | Required, minimum 8 characters |
| Roles | `string[]` | Comma-separated list of role handles |

Output: Creates a YAML file at `users/{id}.yaml` with a scrypt password hash.

### check

Check whether the manifest's schema version matches the version expected by the running code. This command does not validate all blueprints, content, or application configuration.

```bash
pnpm madori check
```

Use `--manifest <path>` to override the default `.madori/manifest.json` location.

---

## Usage Examples

### Creating a User

```bash
pnpm madori make:user
```

Interactive prompts:

```
? Email address: editor@example.com
? Display name: Jane Editor
? Password: ********
? Roles (comma-separated): editor

✓ User created: users/<generated-id>.yaml
```

### Full Agency Workflow

Set up a new client project from a preset, then customise:

```bash
# Start from a preset
pnpm madori init:preset marketing-site

# Add a blog collection
pnpm madori make:collection blog --fields "title:text:required,slug:slug:required,body:tiptap,image:asset"

# Validate everything
pnpm madori check

# Generate typed SDK
pnpm madori generate
```

### Migrating from WordPress

```bash
# Export from WordPress (WXR format)
# Then import into Madori:
pnpm madori migrate:wordpress wordpress-export.xml

# Generate blueprint from migrated content
pnpm madori make:blueprint posts --from-content content/collections/posts/first-post.md
```

### Sharing Resources Between Projects

```bash
# Push blueprints and fieldsets to a shared registry
pnpm madori registry:push https://github.com/my-agency/shared-resources.git --resources "blueprints,fieldsets"

# Pull into another project
pnpm madori registry:pull https://github.com/my-agency/shared-resources.git
```

### Resource Portability

```bash
# Export supported resources and content
pnpm madori export ./project-backup.zip

# Import on another machine; existing files are skipped
pnpm madori import ./project-backup.zip
```

This portable export is not an operational backup of users, sessions, assets, or all configured storage roots. Use `backup`, `backup:verify`, and `restore` for disaster recovery.

---

## Common Patterns

### Scripting User Creation

For CI/CD or staging setup, prefer `make:user` or your configured auth provider.
If you write provider files directly, use the provider's documented hash format;
Madori's built-in file provider uses `scrypt:<salt>:<hash>` values.

```yaml
# users/staging-admin.yaml
id: staging-admin
email: staging@example.com
name: Staging Admin
password_hash: scrypt:<salt hex>:<hash hex>
roles:
  - admin
created_at: 2026-01-01T00:00:00.000Z
```

### Code Generation in CI

Add generation to your build pipeline to ensure types stay fresh:

```json
{
  "scripts": {
    "prebuild": "pnpm madori generate",
    "build": "next build"
  }
}
```

### SEO Frontmatter Migration

Move legacy `meta_title`, `meta_description`, and `og_image` fields into a nested `seo` object. The command is idempotent, preserves unknown fields, writes per-file backups, and refuses rollback if a target changed after migration. Records do not carry the standalone SEO document's `version` wrapper.

```bash
# Preview changes
pnpm madori seo:migrate --root ./content --dry-run

# Apply migration
pnpm madori seo:migrate --root ./content
```

For a reversible apply, write a private rollback plan and keep it out of Git:

```bash
pnpm madori seo:migrate --root ./content --plan ./storage/seo-migration-plan.json
pnpm madori seo:rollback --plan ./storage/seo-migration-plan.json
```

Review the JSON output and `rollbackPlan` before committing. Commit migrated content and SEO defaults through the repository that owns those paths; operational SEO state under `storage/seo` is not part of this migration and should remain outside content Git.

### Legacy Definitions Migration

When upgrading from an older Madori version that stored definitions inline:

1. Run `pnpm madori migrate:definitions` to extract definitions to files
2. Review the generated files in `resources/`
3. Remove the migrated arrays from `madori.config.ts`
4. Commit the new files and updated config

The migration is non-destructive — existing files are never overwritten.