---
title: CLI Reference
description: Every Artisan command WireKit ships — install, scaffold, verify, theme, list, and publish-icons
visibility: guest
draft: false
---

# CLI Reference

WireKit ships a family of Artisan commands covering install, diagnostics, scaffolding, asset publishing, extension installers, AI-tooling integration, and machine-readable manifests. All commands live under the `wirekit:` namespace and are registered automatically by the package's service provider — no manual registration required.

## Discoverability features

Every WireKit CLI surface follows the same contract so AI tools and human users can rely on consistent behavior across commands.

### "Did you mean?" semantics

Every command that reports `Unknown X: <value>` (unknown component, unknown preset, unknown category, unknown font key, unknown icon preset, unknown `--as` / `--format` value) follows the suggestion up with a Levenshtein-ranked hint when the typo is within distance 3 of a real value. The suggestion list is ranked closest-first with ties broken by shorter name first.

Concrete examples:

```bash
php artisan wirekit:show buttn
# → "Unknown component: buttn" + "Did you mean: button?"

php artisan wirekit:theme cuprtino
# → "Unknown preset: cuprtino" + "Did you mean: cupertino?"

php artisan wirekit:list --category=Markting
# → "Unknown category: Markting" + "Markting: Did you mean: Marketing?"

php artisan wirekit:publish-icons heroicns
# → "Unknown preset 'heroicns'" + "Did you mean: heroicons?"
```

The contract: distance ≤ 3, top-3 ranked suggestions, fires uniformly on every `Unknown X` error message. Runtime prop validation (`WireKit::validateProp()`) uses the same helper, so an invalid `intent="primry"` produces the same hint shape.

### Command aliases

| Canonical name | Alias |
| --- | --- |
| `wirekit:verify` | `wirekit:doctor` |

Aliases resolve to the SAME command — running either name walks the identical check pipeline. `php artisan list wirekit` shows the canonical name with the alias listed underneath, NOT as a separate command row.

### Exit codes

All commands follow Symfony's three-value exit-code convention:

| Code | Meaning |
| --- | --- |
| `0` (`SUCCESS`) | Command completed cleanly. Continue your pipeline. |
| `1` (`FAILURE`) | Runtime failure mid-command — investigate the output. |
| `2` (`INVALID`) | Input rejected (unknown value, mutually-exclusive flags, malformed argument). No side-effects ran. |

See [`wirekit:install` exit codes](/getting-started/integration#ci--deploy-script-discipline) for the canonical CI / deploy wiring of these codes.

### Interactive vs non-interactive mode

Commands that may prompt the user (e.g. `wirekit:install`'s guided setup, `wirekit:component`'s `--base` chooser) detect TTY status automatically. The behavior:

- **TTY present** → prompts fire normally.
- **Non-TTY** (CI, piped, redirected) → prompts are skipped; commands run with defaults or fail-fast on missing required input.
- **`--no-interaction`** → forces non-TTY semantics even in a TTY.
- **`--interactive`** → forces prompt mode even when TTY detection misfires (Herd / Docker / WSL setups).

### ThemePresetRegistry

`Pushery\WireKit\Theming\ThemePresetRegistry` is the single source of truth for every WireKit theme preset. `wirekit:theme`, `wirekit:install --preset=`, and `wirekit:export-api-map themesGroup()` all read from this class — drift between their lists is impossible.

| Key | Label | Shape |
| --- | --- | --- |
| `default` | Default | No-op preset; applying it removes any existing `wirekit:theme start/end` block from `app.css` |
| `minimal` | Minimal | Clean, borderless aesthetic |
| `soft` | Soft | Rounded, gentle shadows |
| `material` | Material | Google Material Design 3 inspired |
| `brutalist` | Brutalist | Bold borders, no shadows |
| `retro-terminal` | Retro Terminal | Green-on-black hacker aesthetic |
| `cupertino` | Cupertino | Apple HIG inspired |

Downstream packages can register custom presets at runtime via `ThemePresetRegistry::register()` from a service-provider boot hook:

```php
use Pushery\WireKit\Theming\ThemePresetRegistry;

public function boot(): void
{
    ThemePresetRegistry::register('fintech', [
        'label' => 'FintechKit',
        'vars' => "    --color-wk-accent: oklch(0.65 0.22 220);",
        'dark_vars' => null,
    ]);
}
```

After registration the preset appears in `wirekit:theme fintech`, `wirekit:install --preset=fintech`, and the AI-tooling export.

## Quick Reference

| Command | Purpose |
| --- | --- |
| [`wirekit:install`](#wirekitinstall) | One-command initial setup: publish config, publish assets, hint layout directives |
| [`wirekit:verify`](#wirekitverify-and-wirekitdoctor) | Diagnose integration health (assets, directives, Tailwind `@source`, optional deps) |
| [`wirekit:doctor`](#wirekitverify-and-wirekitdoctor) | Alias for `wirekit:verify` |
| [`wirekit:doctor:a11y`](#wirekitdoctora11y) | Static-analysis a11y linter — scan your app's Blade templates for missing aria-labels, dialog-without-label, role="img" without label |
| [`wirekit:list`](#wirekitlist) | List every component grouped by category |
| [`wirekit:fonts`](#wirekitfonts) | List every font preset grouped by category — pick a key for `wirekit:install --font=…` |
| [`wirekit:icons`](#wirekiticons) | List every icon alias grouped by preset — discover which presets ship which aliases |
| [`wirekit:show {name}`](#wirekitshow-name) | Show props, slots, and docs URL for a single component |
| [`wirekit:theme {preset}`](#wirekittheme-preset) | Inject a theme preset's CSS block into `app.css` |
| [`wirekit:make {name}`](#wirekitmake-name) | Scaffold a Livewire page pre-wired with WireKit components |
| [`wirekit:component {name}`](#wirekitcomponent-name) | Scaffold a custom component derived from a WireKit base |
| [`wirekit:publish-icons {preset}`](#wirekitpublish-icons-preset) | Publish a specific icon-set's SVG directory |
| [`wirekit:publish-fonts`](#wirekitpublish-fonts) | Publish the font families your config names |
| [`wirekit:glass install`](#wirekitglass-install) | Publish the Liquid Glass extension CSS to your layout |
| [`wirekit:editor-preset {preset}`](#wirekiteditor-preset-preset) | Scaffold the `window.wirekitEditor()` factory snippet for the editor |
| [`wirekit:export-json`](#wirekitexport-json) | Emit a machine-readable JSON manifest of every component |
| [`wirekit:export-api-map`](#wirekitexport-api-map) | Emit an AI-friendly hierarchical sitemap of every WireKit surface |
| [`wirekit:cursor-rules`](#wirekitcursor-rules) | Publish the WireKit Cursor rules file to `.cursor/rules/wirekit.mdc` |
| [`wirekit:mcp-serve`](#wirekitmcp-serve) | Run the local MCP server (JSON-RPC over stdio) for AI coding assistants |
| [`wirekit:boost-skills`](#wirekitboost-skills) | Publish a Laravel Boost skill manifest (.boost/wirekit.json) for AI-editor autocomplete |

## `wirekit:install`

```bash
php artisan wirekit:install
php artisan wirekit:install --preset=cupertino
php artisan wirekit:install --font=inter
php artisan wirekit:install --font=inter --font-serif=lora --font-mono=jetbrains-mono
php artisan wirekit:install --interactive       # Force interactive prompts even when TTY detection misfires (Herd / Docker / WSL)
php artisan wirekit:install --no-gitignore      # Skip auto-adding /public/vendor/wirekit to .gitignore (commit assets to repo)
php artisan wirekit:install --apex-license=community  # Opt into the optional ApexCharts adapter + record license tier
```

**Flags:**

- `--preset=...` — apply a theme preset during install (default: `default`)
- `--font=...` / `--font-serif=...` / `--font-mono=...` — inject font-family overrides
- `--interactive` — force the guided prompt mode even when Symfony's TTY detection returns false (common in Herd / Docker / WSL setups where stdin goes through a wrapper)
- `--no-gitignore` — skip the automatic `/public/vendor/wirekit` entry in your `.gitignore`. Use this when your deploy pipeline does NOT run `vendor:publish --tag=wirekit-assets --force` and you prefer to commit the published assets to your repo instead
- `--apex-license=community|commercial|oem` — opt into the optional ApexCharts adapter and record your license tier in `config/wirekit.php` `charts.apex_license`. Always prints the License Notice once (Community License covers organizations under $2M USD revenue; Commercial License required above; OEM only for redistributed-product embeddings). Suppresses the `wirekit:doctor` reminder when the tier is `commercial` or `oem`. ApexCharts is non-MIT — see [the License section on the Chart overview](/components/chart#license-apexcharts-only) and [apexcharts.com/license](https://apexcharts.com/license/) for the full terms.
- `--no-strict` — opt OUT of strict-by-default mode. Pre-flight warnings print but do not abort. Recommended only for legacy CI scripts that depended on the v2.0.0 "warnings as success" behavior.
- `--force` — bypass pre-flight warnings (token clobber, hand-edited marker blocks). Errors still abort. Mutually exclusive with `--no-strict`.
- `--ignore-failed-flags` — per-flag failures report but do NOT abort install. Other flags still apply. Exit code reflects partial failure (non-zero so CI still detects it). Requires `--no-strict` (since strict-default would abort first).
- `--diff` — dry-run mode. Reports what WOULD change in `app.css` / layout / config without writing any files. Exits 0 after rendering the would-be-state. Use to preview an install before committing.
- `--rollback` — reverse the most-recent install session by replaying `.wirekit-install.log` before-snapshots. Per-file restore. Returns 0 on full success, 1 on partial restore. Mutually exclusive with every install flag.

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | SUCCESS — install completed cleanly (or dry-run / rollback completed cleanly). |
| `1` | FAILURE — runtime error mid-install (verify failed, theme call failed, partial rollback). |
| `2` | INVALID — pre-flight validation found errors, OR mutually-exclusive flags were combined. No filesystem mutations happened. |

### Decision matrix

| Pre-flight state | default (strict) | `--no-strict` | `--force` |
|------------------|------------------|---------------|-----------|
| Clean            | proceed          | proceed       | proceed   |
| Warnings only    | **abort (exit 2)** | proceed     | proceed   |
| Errors           | abort (exit 2)   | abort (exit 2) | abort (exit 2) |

Pre-flight collects EVERY error in one pass — the user sees the full picture before deciding how to fix.

Runs the one-command bootstrap:

1. Publishes `config/wirekit.php` (overridable defaults for every component)
2. Publishes `dist/wirekit.css` and `dist/wirekit.js` to `public/vendor/wirekit/`
3. Adds `public/vendor/wirekit/` to your `.gitignore`
4. Prints the layout snippet to paste into your Blade master view
5. When `--font=<key>` is passed: publishes the bundled font CSS to `public/vendor/wirekit/fonts/` and injects an idempotent override block into `resources/css/app.css` setting BOTH `--font-sans` (drives Tailwind utilities) AND `--font-wk-sans` (drives WireKit chrome) to the resolved font family — so the two stay aligned automatically.

| Flag | Purpose |
|------|---------|
| `--preset=<name>` | Theme preset: `default`, `minimal`, `soft`, `material`, `brutalist`, `retro-terminal`, `cupertino` |
| `--font=<key>` | Inject sans font-family override (e.g. `inter`, `roboto`, `open-sans`). Must be a sans-category key from `FontRegistry`; lists available keys on error. Local font CSS only — WireKit ships GDPR-compliant local font files; nothing is fetched from a CDN. |
| `--font-serif=<key>` | Inject serif font-family override (e.g. `lora`, `playfair-display`, `merriweather`). Must be a serif-category key from `FontRegistry`. |
| `--font-mono=<key>` | Inject mono font-family override (e.g. `jetbrains-mono`, `fira-code`). Must be a mono-category key from `FontRegistry`. |

All three font flags are combinable and produce independent marker-pair blocks in `app.css` — each idempotent, each swappable on re-run. Wrong-category passes throw with a list of valid keys for the correct category.

### Tailwind config support

WireKit detects which Tailwind config shape your project uses and writes the font override to the right file:

| Shape | Detected by | Where the override is written |
|-------|-------------|-------------------------------|
| **CSS-first** (Tailwind v4 default) | `resources/css/app.css` contains `@theme {…}` | `@theme { --font-sans: … }` block in `app.css` |
| **JS-config** (legacy) | `tailwind.config.js` exists, no `@theme` in app.css | `theme.extend.fontFamily.{sans,serif,mono}` array in `tailwind.config.js` |
| **Both** | both files exist | CSS-first wins; info-log shows the choice (Tailwind v4 deprecates JS config) |
| **Neither** | none of the above | logs warning + skips font injection |

If your `tailwind.config.js` has a custom shape WireKit can't auto-edit (heavy comments, non-standard `module.exports` layout), the install command logs an actionable skip message with the exact line to add manually.

### Interactive mode

When you run `wirekit:install` without any flags AND in an interactive TTY (i.e. you're at the terminal, not in CI), the command opens a guided setup:

```text
$ php artisan wirekit:install
Installing WireKit...

  i Interactive setup — press Enter at any prompt to skip.

  Theme preset [default]:
    default, minimal, soft, material, brutalist, retro-terminal, cupertino
  > cupertino

  Sans-serif font (skip = use bundled defaults) [skip]:
    skip, inter, roboto, open-sans, lato, montserrat
  > inter

  Serif font (optional) [skip]:
    skip, lora, playfair-display, merriweather
  >

  Monospace font (optional) [skip]:
    skip, jetbrains-mono, fira-code, source-code-pro
  > jetbrains-mono

  ✓ Published config/wirekit.php
  ...
```

Selected values route into the same code path as if you'd passed `--preset=cupertino --font=inter --font-mono=jetbrains-mono` on the command line.

**CI-friendly**: the prompts are skipped automatically when:

- Any flag is passed (e.g. `--preset=cupertino` alone disables prompts)
- `--no-interaction` is set
- The command is running in a non-interactive context (piped scripts, GitHub Actions, etc.)

So `php artisan wirekit:install --no-interaction` in a CI workflow runs exactly as in v1.5.0 — zero new behavior.

Idempotent: safe to re-run after a `composer update` to refresh published assets. Re-running with the same `--font=<key>` produces byte-identical `app.css`; re-running with a different key swaps the bracketed override block in place.

## `wirekit:verify` and `wirekit:doctor`

```bash
php artisan wirekit:verify
php artisan wirekit:doctor                       # alias — same checks, more conventional name
php artisan wirekit:verify --tier=package        # Run only package-tier checks (1-14)
php artisan wirekit:verify --tier=environment    # Run only environment-tier checks (15)
php artisan wirekit:verify --fix                 # Self-heal missing public/vendor/wirekit/* assets
```

**Flags:**

- `--tier=package|environment` — filter to a single check tier. `package` covers the WireKit install itself (asset publishing / config / directives / Tailwind source / token alignment / Alpine cleanup — checks 1–14 below). `environment` covers Laravel host state that bites in interactive dev even when the package install is clean (currently just the compiled-views-freshness check — check 15 below). Default (flag omitted) runs every tier. Unknown tier values exit with INVALID (exit code 2).
- `--fix` — proactively self-heal missing `public/vendor/wirekit/*.{css,js}` assets by running `vendor:publish --tag=wirekit-assets --force` and re-checking. Useful right after a fresh clone (where `public/vendor/wirekit/` is `.gitignored` and starts empty) to avoid a red doctor on first run. Without `--fix`, the missing-assets check still emits the actionable "Run: php artisan vendor:publish --tag=wirekit-assets" hint and the `--fix` alternative is offered alongside.

**Diagnostic checks** grouped into named sections — see the dedicated [`wirekit:doctor` reference](cli-reference/wirekit-doctor.md) for per-check explanations, mismatch examples, and CI / hook wiring.

### Package-tier checks (run with default or `--tier=package`)

1. Assets published (`wirekit.css` / `wirekit.js` in `public/vendor/wirekit/`)
2. Asset freshness (md5 hash matches package source)
3. Tailwind `@source` directive scanning the package views
4. Config file published (`config/wirekit.php`)
5. Blade directives present (`@wirekitStyles` + `@wirekitScripts`)
6. Alpine.js available (skipped on Livewire v4+ which bundles Alpine)
7. Bundle config valid (`scripts.bundle` is `full` or `core`)
8. Published views not stale (warns if `views/vendor/wirekit/` exists with file count)
9. Font assets published when a custom font preset is configured
10. CSS `@import` anti-pattern detection (warn if `wirekit.css` is `@import`-ed in `app.css`)
11. Optional dependencies (Chart.js adapter, bacon-qr-code)
12. **Token alignment** — token-pair checks across font, color, radius, shadow. Reports drift between Tailwind tokens (`--font-sans`, `--color-accent`, `--radius`, …) and the matching WireKit tokens (`--font-wk-sans`, `--color-wk-accent`, `--radius-wk`, …). Skips `var(...)` references.
13. **Light/dark color-token symmetry** — every `--color-wk-*` declared in your `:root {}` block is also declared in your `.dark {}` block (and vice versa). Asymmetric tokens are reported with the missing-side name so you can add the matching declaration. Scoped to color tokens only; font / radius / shadow tokens are theme-agnostic and excluded.
14. **Alpine plugin cleanup hygiene** — static analysis of your `resources/js/` tree for custom Alpine plugins. Flags two anti-patterns: an observer (`IntersectionObserver` / `MutationObserver` / `ResizeObserver`) instantiated without a `destroy()` lifecycle method, AND a `disconnect()` call inside an observer callback without a preceding null-guard. Both produce silent `TypeError` console errors after Livewire morph / conditional render / SPA navigation. Respects a `// wirekit-doctor: cleanup-ok` opt-out comment marker for intentional patterns the heuristic doesn't recognize.

### Environment-tier checks (run with default or `--tier=environment`)

1. **Compiled-views freshness** — detects when `resources/views/` mtimes exceed `storage/framework/views/` by ≥ 60 seconds. The canonical "I edited the Blade source an hour ago, the test still asserts the old output" failure mode caused by Laravel's compiled-view cache lag. Emits a WARN with the actionable `php artisan view:clear` fix. 60-second buffer avoids false-positives on fast file-edit cycles; slow filesystems (NFS / Docker on macOS) may need a higher threshold (see the check's docblock).

Exit code 1 on any failure — add to your CI pipeline, or register as a post-task verification step in your AI coding agent (Claude Code's hooks system, ChatGPT Codex, Cursor rules, Aider, etc.) so drift between WireKit and your app surfaces automatically after every change.

## `wirekit:doctor:a11y`

Static-analysis accessibility linter for your application's Blade templates. Scans every `.blade.php` file under a given path (defaults to `resources/views`) for the high-value bug classes that axe-core catches at runtime, but with zero browser cost:

- `<x-wirekit::button>` whose only child is an icon (no text label) and which doesn't carry `aria-label` → **ERROR** (WCAG 2.1 "Buttons must have discernible text").
- Any element with `role="dialog"` or `role="alertdialog"` that doesn't carry `aria-label` or `aria-labelledby` → **ERROR** (WCAG 4.1.2 + ARIA APG dialog pattern).
- Any element with `role="img"` that doesn't carry `aria-label` or `aria-labelledby` → **ERROR** (WCAG 1.1.1 non-text content).

```bash
php artisan wirekit:doctor:a11y                 # Scan resources/views (default)
php artisan wirekit:doctor:a11y app/Livewire    # Scan a specific path
php artisan wirekit:doctor:a11y --fail-on=warning   # Gate CI on warnings too
php artisan wirekit:doctor:a11y --theme-contrast    # + WCAG contrast audit on theme tokens
```

**Flags:**

- `--fail-on={error|warning|none}` — Severity threshold that triggers a non-zero exit. `error` (default) — only ERROR findings fail the build. `warning` — both ERROR and WARNING fail (strict CI gate). `none` — never fail; print findings only.
- `--theme-contrast` — Opt-in second stage. After the Blade scan, parses `resources/css/app.css` for `--color-wk-*` token overrides under `:root` and `.dark` blocks (accepting both the bare and `:where(...)` wrapped selector forms), then computes WCAG 2.1 contrast ratios for the canonical token pairings (`accent` as text on `bg`, `accent-fg` on `accent` — the primary-button label pairing — `text-muted` on `bg`, `danger-fg` on `danger`, etc.). Reports PASS / WARN / FAIL / EXEMPT per pairing × mode. Catches the bug class where a developer customizes `--color-wk-accent` without verifying the new value still clears 4.5:1 against `--color-wk-accent-fg`. **Border handling follows WCAG 1.4.11:** the *communicating* borders (the focus `ring`, plus the stateful `border-error` / `border-success` on `bg`) are hard-checked at 3:1, while the *resting decorative* borders (`border`, `border-strong` on `bg`) are reported as advisory `INFO (decorative, WCAG 1.4.11 exempt)` — printed with their ratio but never counted toward the verdict or the exit code, because pure dividers are exempt and WireKit's default palette intentionally sits in that ~1.3–2.5:1 band (see the theming guide's *Intentional trade-offs* section). Also enabled by setting `WIREKIT_DOCTOR_THEME_CONTRAST=1` in the environment.

Exit code 1 when any finding at or above the `--fail-on` threshold is present. Pair with `wirekit:verify` in CI to gate on integration health AND a11y in one pass.

::: info
**Scope.** This linter audits two surfaces: the accessibility patterns above in your Blade templates, and — with `--theme-contrast` — the `--color-wk-*` token overrides in your `app.css`. It stops there: contrast *inside your own CSS custom classes* (e.g. a hand-written `.promo-badge { background: …; color: #fff }`) is outside its reach, so verify those colors yourself. A green run means the scanned surfaces are clean — not that every color on the page clears AA.
:::

## `wirekit:list`

```bash
php artisan wirekit:list
php artisan wirekit:list --category=Form               # Filter to a single category
php artisan wirekit:list --category=Marketing,Display  # Multi-category union (comma-separated)
php artisan wirekit:list --as=count                    # Just the integer count (script-API)
php artisan wirekit:list --as=slugs                    # Newline-separated component names
php artisan wirekit:list --as=categories               # JSON: per-category count map
php artisan wirekit:list --as=json                     # Full JSON manifest (name + tag + category + description)
php artisan wirekit:list --format=json                 # Alias for --as
```

Prints every component in `ComponentRegistry`, grouped by category, with a one-line description per component.

**Flags:**

- `--category=...` — narrow the listing to one category. Accepts a comma-separated list (e.g. `--category=Marketing,Display`) for a multi-category union. Canonical category enum: `Form`, `Layout`, `Typography`, `Navigation`, `Overlay`, `Display`, `Marketing`, `System`. Useful when you remember "I need a form input" but not the exact component name.
- `--as=count|slugs|categories|json` — emit a machine-readable format instead of the human-readable table. Stable for scripting:
  - `count` → single integer, no decoration, no trailing newline. `WK_COUNT=$(php artisan wirekit:list --as=count)`.
  - `slugs` → newline-separated component names, one per line.
  - `categories` → JSON object mapping each category to its component count.
  - `json` → array of `{name, tag, category, description}` objects. Use this when you need the full per-component metadata in a single pipe.
- `--format=...` — alias for `--as`. Matches the `--format=json` convention used by other Laravel commands; the two flags accept the same value set and must not be passed with different values simultaneously.

### Programmatic discovery — PHP

For Laravel apps that need the component catalog at runtime (not via shell):

```php
use Pushery\WireKit\ComponentRegistry;

$count = count(ComponentRegistry::all());
$names = array_keys(ComponentRegistry::all());
$formComponents = ComponentRegistry::category('Form');
$categories = ComponentRegistry::categories();
```

`ComponentRegistry::all()` is the canonical PHP-side discovery surface. The CLI `--as=...` flags above are thin wrappers over the same registry, optimized for shell-script consumption.

## `wirekit:fonts`

```bash
php artisan wirekit:fonts
php artisan wirekit:fonts --category=sans     # Filter to a single category
php artisan wirekit:fonts --as=count          # Just the integer count
php artisan wirekit:fonts --as=slugs          # Newline-separated keys
php artisan wirekit:fonts --as=categories     # Newline-separated category names
php artisan wirekit:fonts --as=json           # Full JSON manifest
php artisan wirekit:fonts --format=json       # Alias for --as
```

Lists every font preset shipped with WireKit, grouped by category (`sans` / `serif` / `mono`). Each row shows the preset key, label, and font-family. The shipped preset keys map 1:1 to the values accepted by `wirekit:install --font={key}` — copy any key from the listing into the install command.

**Flags:**

- `--category=...` — narrow the listing to one of `sans`, `serif`, `mono`. Unknown categories fail-fast with a Levenshtein-ranked Did-you-mean hint.
- `--as=count|slugs|categories|json` — emit a machine-readable format. `count` for shell-script consumption, `slugs` for pipe-into-grep, `categories` for the list of available categories, `json` for the full per-preset metadata (each entry carries `key` / `label` / `family` / `category` / `install` fields).
- `--format=...` — alias for `--as`. Matches the `--format=json` convention used by other Laravel commands.

### Programmatic discovery — PHP

```php
use Pushery\WireKit\Fonts\FontRegistry;

$allFonts = FontRegistry::all();
$sansFonts = FontRegistry::category('sans');
$inter = FontRegistry::get('inter');
```

`FontRegistry` is the canonical PHP-side discovery surface; the CLI's `--as=...` flags are thin wrappers over the same registry.

## `wirekit:icons`

```bash
php artisan wirekit:icons
php artisan wirekit:icons --preset=heroicons-marketing   # Filter to one preset
php artisan wirekit:icons --as=count                     # Total alias count
php artisan wirekit:icons --as=presets                   # Newline-separated preset keys
php artisan wirekit:icons --as=aliases                   # Newline-separated unique alias list
php artisan wirekit:icons --as=json                      # Full JSON manifest
php artisan wirekit:icons --format=json                  # Alias for --as
```

Lists every icon alias shipped with WireKit, grouped by preset (`heroicons` / `heroicons-app` / `heroicons-marketing` / `lucide` / `phosphor` / `tabler`). Each section shows the alias-count summary, the `[active]` / `[opt-in]` indicator against your current `wirekit.icons.preset` / `wirekit.icons.presets` config, and every alias → Blade-Icon identifier mapping.

**Active vs opt-in.** Out of the box, `heroicons` is the only active preset. `heroicons-app` and `heroicons-marketing` are extension presets — opt in via `wirekit.icons.presets => ['heroicons', 'heroicons-app']` in `config/wirekit.php` to stack additional alias sets. `lucide` / `phosphor` / `tabler` are alternative base presets — set `wirekit.icons.preset => 'lucide'` to swap out the heroicons default.

**Flags:**

- `--preset=...` — narrow the listing to one preset. Unknown values fail with a Levenshtein Did-you-mean.
- `--as=count|presets|aliases|json` — machine-readable formats:
  - `count` → total alias count across the (optionally filtered) preset set.
  - `presets` → newline-separated preset keys.
  - `aliases` → newline-separated UNIQUE alias list across every selected preset (sorted; useful for "do any presets define `bolt`?" lookups).
  - `json` → array of `{key, count, active, requires, aliases}` entries. The `aliases` field is the full mapping; `requires` carries the Composer-package dependency.
- `--format=...` — alias for `--as`.

### Programmatic discovery — PHP

```php
use Pushery\WireKit\Icons\Presets\HeroiconsMarketingPreset;
use Pushery\WireKit\Icons\IconResolver;

$marketingAliases = (new HeroiconsMarketingPreset)->icons();
$builtIn = IconResolver::availablePresets();
```

## `wirekit:show {name}`

```bash
php artisan wirekit:show button
php artisan wirekit:show card.body                              # Dotted sub-component
php artisan wirekit:show button --as=json                       # Structured schema as JSON
php artisan wirekit:show button --validate-against=app.blade.php # Lint a developer Blade file
```

Prints the component's props (with types and defaults), slots, and docs URL. Anti-drift tested — every prop in the Blade `@props([...])` block has a registry entry.

**Dotted sub-component names** (`card.body`, `modal.header`, `dropdown.item`, etc.) resolve directly. The command walks `resources/views/components/{parent}/{child}.blade.php`, extracts props with the same parser used for top-level components, and emits the same human-readable output (or JSON via `--as=json`). Useful for IDE tooling that needs the schema for any sub-component without first running `wirekit:show <parent>` to discover the sub-tree.

**Flags:**

- `--as=json` — emit the component's structured schema as JSON to stdout (no decoration). Includes `name`, `tag`, `category`, `description`, `docs_url`, `props[]` (full prop records with `default`, `default_normalized`, `comment`), and `sub_components[]`. Use this when your tooling needs the per-component schema without parsing the human-readable output.
- `--validate-against=<path>` — read a developer Blade file, find every `<x-wirekit::{name} ...>` usage in it, and warn when a passed attribute does NOT match a known prop name. The warning includes the closest matching prop (Levenshtein-ranked) so typos like `intnet` → `intent` surface immediately. Exits 1 on any unknown attribute — wire into pre-commit hooks for a pre-runtime "did I typo a prop?" catch. Skips standard Blade / Alpine / Livewire attributes (`class`, `style`, `wire:*`, `x-*`, `@*`, `data-*`, `aria-*`).

## `wirekit:theme {preset}`

```bash
php artisan wirekit:theme cupertino
php artisan wirekit:theme retro-terminal
php artisan wirekit:theme default      # Remove any existing preset block — return to bundled values
```

Injects the preset's CSS block from `docs/theming.md` into your `resources/css/app.css`. Idempotent — re-running with the same preset is a no-op.

Available presets: `default`, `minimal`, `soft`, `material`, `brutalist`, `retro-terminal`, `cupertino`. The full list is read from [`Pushery\WireKit\Theming\ThemePresetRegistry`](#themepresetregistry) — the single source of truth shared across `wirekit:theme`, `wirekit:install --preset=`, and `wirekit:export-api-map`.

The `default` preset is a special "stay on the bundled values" entry:

- If a `wirekit:theme start/end` block already exists in `app.css`, it is removed and the bundled defaults take effect again.
- If no preset block is present, the command succeeds without changes.

Use it as the undo path for an earlier `wirekit:theme cupertino` (etc.) when you want to clear the preset without manually editing `app.css`.

Unknown presets exit 1 with a Did-you-mean hint — e.g. `wirekit:theme cuprtino` suggests `cupertino`. See [Discoverability features](#discoverability-features) for the suggestion semantics.

## `wirekit:make {name}`

```bash
php artisan wirekit:make page:dashboard
php artisan wirekit:make page:settings
php artisan wirekit:make page:login

# Recipe scaffolds — each maps to a docs.wirekit.app/blueprints/recipes/<name> page
php artisan wirekit:make recipe:marketing-landing-page
php artisan wirekit:make recipe:documentation-reader
php artisan wirekit:make recipe:live-kpi-strip
php artisan wirekit:make recipe:feature-numbered-marker
php artisan wirekit:make recipe:hero-with-code-aside
php artisan wirekit:make recipe:long-form-article
php artisan wirekit:make recipe:marketing-landing-toc
php artisan wirekit:make recipe:stat-with-sparkline
php artisan wirekit:make recipe:toolbar-filter-bar
```

Scaffolds a Livewire component class + Blade view pre-wired with WireKit's components.

**Page templates** (3): `page:dashboard`, `page:settings`, `page:login` produce skeletal Livewire pages with stat-grid, form-stack, and centerd-card patterns respectively.

**Recipe templates** (9): each `recipe:<name>` mirrors the corresponding `docs/blueprints/recipes/<name>.md` page — the scaffold ships a representative skeleton of the recipe's structural composition (e.g. `recipe:marketing-landing-page` lays out brand-bar + hero + feature-grid + cta + footer). Treat the scaffold as a starting point: expand each section with your copy + assets. Every generated Blade view includes a comment cross-linking to the full reference at `https://docs.wirekit.app/blueprints/recipes/<name>`.

Unknown templates fail-fast with a Levenshtein-ranked Did-you-mean hint covering both page and recipe families.

## `wirekit:component {name}`

```bash
# 1. Implicit base derivation — strip the rightmost dash-segment and
#    verify it's a real WireKit component. `my-button` → `button`.
php artisan wirekit:component my-button

# 2. Explicit base — wins over derivation when both apply.
php artisan wirekit:component my-thing --base=card

# 3. --force overwrites an existing custom/{name}.blade.php.
php artisan wirekit:component my-button --force

# 4. --interactive prompts via choice() when derivation has no clear
#    match. Default ON when stdin is a TTY; pass --no-interaction to
#    suppress in CI.
php artisan wirekit:component customer-dashboard --interactive
```

Copies a WireKit base component's Blade file to `resources/views/components/custom/{name}.blade.php` so you can override classes, variants, and slot logic without publishing the entire `views/vendor/wirekit/` directory (which copies ~109 files at once).

**Flags:**

- `--base=<component>` — explicit base component to copy from. Wins over the implicit derivation chain below. Pass a flat name (`button`) or a dotted sub-name (`card.header`).
- `--force` — overwrite an existing `resources/views/components/custom/{name}.blade.php`. Safe-by-default refuses to overwrite without this flag.
- `--interactive` — force the `choice()` prompt even when stdin TTY detection misfires (Herd / Docker / WSL setups). Default is auto-detect: TTY → on; non-TTY (CI, piped) → off. When the prompt fires, it shows the ranked Levenshtein suggestions (up to 5 closest matches) plus a `<cancel>` sentinel; pressing return without a selection picks the top suggestion. Picking `<cancel>` aborts with FAILURE so the user can re-run with explicit `--base=`. The flag is symmetrical to Symfony's built-in `--no-interaction` — pass one OR the other, never both.

**`--base` derivation rules** (when `--base` is not passed):

1. **Right-segment strip:** strip the rightmost dash-segment of the name and check if it names a real component. `my-button` → `button`, `custom-card` → `card`, `derived-modal` → `modal`. Verified against the package's blade-component directory.
2. **Levenshtein fallback:** if the right-segment isn't a real component, fall back to a closest-match suggestion against `ComponentRegistry::all()`. Inside a TTY, the command prompts via `choice()` with the ranked candidates plus a `<cancel>` sentinel. Outside a TTY (or with `--no-interaction`), the command exits 1 with the suggestion list — you re-run with explicit `--base=`.
3. **No match found:** the command fails with `Could not derive a --base from '{name}'. Pass --base= explicitly.` and prints the Did-you-mean suggestion list when one exists.

Worked examples:

```bash
# Implicit derivation — strip rightmost dash-segment
php artisan wirekit:component my-button
# Derives --base=button → copies button.blade.php
# Output: "ℹ Derived --base=button from 'my-button'."

# No derivation — name has no parent component to derive from
php artisan wirekit:component customer-dashboard
# Fails with "Could not derive a --base..." plus a Did-you-mean
# suggestion if any close match exists.
```

After scaffolding, use the component as `<x-custom::{name}>`.

::: tip
For lighter customization, use `WireKit::personalize()` instead — see [Customization](/customization). Scaffolding via `wirekit:component` is the right tool when you need to fork the Blade structure itself.
:::

## `wirekit:publish-icons {preset}`

```bash
php artisan wirekit:publish-icons heroicons
php artisan wirekit:publish-icons lucide --force
```

Publishes the SVG directory of a specific icon-set composer package to `public/vendor/wirekit/icons/{preset}/`. Refuses if the corresponding `blade-ui-kit/blade-{preset}-icons` (or equivalent) package is not installed and prints the exact `composer require` line as the fix.

Available presets: `heroicons`, `heroicons-app`, `heroicons-marketing`, `lucide`, `phosphor`, `tabler`.

## `wirekit:publish-fonts`

```bash
php artisan wirekit:publish-fonts             # the families config/wirekit.php names
php artisan wirekit:publish-fonts --all       # every bundled family (5.8 MB)
php artisan wirekit:publish-fonts --prune     # …and delete families no longer configured
php artisan wirekit:publish-fonts --force     # overwrite even a family already up to date
```

Copies the font families named by `fonts.sans` / `fonts.serif` / `fonts.mono` into
`public/vendor/wirekit/fonts/`. A typical two-family setup is roughly 430 KB
against 5.8 MB for the whole tree.

**Upgrade-safe by default.** The command compares the bundled bytes against the
published copy and only skips a family that is already up to date — after a
`composer update` that ships new font bytes, an unforced run re-publishes the
drifted families automatically (and names each one it updates), so the app stops
serving the previous release's fonts. Safe to wire into `composer
post-autoload-dump`. `wirekit:verify` also flags outdated published fonts, the same
freshness check it already runs for `wirekit.css` / `wirekit.js`.

**Flags:**

- `--all` — publish every bundled family instead of only the configured ones. The
  right answer when your app offers a font picker at runtime.
- `--prune` — remove published families the config no longer names. Switching a
  family otherwise leaves the old one in `public/` indefinitely, which is how a
  "slim" publish ends up larger than the all-or-nothing one after a few changes.
- `--force` — overwrite unconditionally, even a family that is already up to date.
  Use it when you have hand-edited a published font file and want the bundled
  version back.

**Why not the per-family publish tags?** `vendor:publish --tag=wirekit-font-inter`
works, but it means writing the family name in a second place. Change the font in
config and the publish command silently keeps shipping the old one — or nothing.
This command reads the config, so the name lives in exactly one place. That matters
most for a template: a clone changes one line and its setup script keeps working.

Fonts are served even when they were never published — the package route
(`/wirekit/fonts/…`) reads straight from the installed package. Publishing is a
performance choice, not a correctness one: a static file beats a PHP round trip.

## `wirekit:glass install`

```bash
php artisan wirekit:glass install
```

Publishes the Liquid Glass extension CSS to `public/vendor/wirekit/wirekit-glass.css` and prints the `<x-wirekit::glass />` snippet to paste into your layout's `<head>`. The Liquid Glass extension is opt-in and adds a frosted-glass surface effect to overlay components on top of the Cupertino theme preset.

## `wirekit:editor-preset {preset}`

```bash
php artisan wirekit:editor-preset             # prints the `basic` factory
php artisan wirekit:editor-preset full        # prints the `full` factory
php artisan wirekit:editor-preset full --write=resources/js/editor.js
php artisan wirekit:editor-preset --write=resources/js/editor.js --force
```

Scaffolds the `window.wirekitEditor(config)` factory that [`<x-wirekit::editor>`](/components/editor) calls at Alpine init, pre-wired for a chosen toolbar preset (the legacy `window.tiptapEditor` name still works as a deprecated alias). The editor ships no engine code — Tiptap is your peer dependency, exposed through this factory — and writing the factory by hand (forwarding every `config.*` callback, the security-correct `Link` config, the right extension set) is the one fiddly setup step. This command emits a ready-to-paste version straight from the documented factory contract.

The `preset` argument is `basic` (default — bold / italic / strike / link / lists, matching `toolbar="basic"`) or `full` (adds underline, headings, quote, code block, history, matching `toolbar="full"`). The `full` preset additionally needs `@tiptap/extension-underline`, which StarterKit does not bundle — the emitted `npm install` line includes it. An unknown preset exits `1` with the valid list.

**Flags:**

- `--write=<path>` — write the snippet to a file (relative to the project root, or an absolute path) instead of printing to stdout.
- `--force` — overwrite the `--write` target if it already exists. Without it, the command refuses an existing file and exits `1`.

After scaffolding, the command reminds you which JS bundle registers the editor glue (`wirekit.js` / `wirekit-alpine.js`, or `wirekit-tiptap.js` alongside `wirekit.core.js`). See the [editor docs](/components/editor) for the full factory `config` contract and the `editorProps` forwarding requirement.

## `wirekit:export-json`

```bash
php artisan wirekit:export-json
php artisan wirekit:export-json --pretty
php artisan wirekit:export-json --public
```

Emits a machine-readable JSON manifest of every WireKit component on stdout: `{ version, generated_at, components: [{ name, tag, category, description, docs_url, props, slots, sub_components, component_kind }] }`.

**Flags:**

- `--pretty` — pretty-print (multi-line) instead of minified output.
- `--public` — produces the manifest that docs.wirekit.app serves at its `/components.json` endpoint. General integrations should omit it; the default emits the full component inventory for your tooling.

The `component_kind` field disambiguates two distinct composition shapes:

- `"anonymous"` — the component is an anonymous Blade file (`resources/views/components/<name>.blade.php`). Props come from a `@props([...])` block; named template slots are valid composition.
- `"class"` — the component is a class-based view component (a PHP class extending `Illuminate\View\Component`). Props come from the constructor signature; the template typically references public class properties as `{{ $name }}` expressions that are NOT developer-facing template slots. Currently the only class-based component is `<x-wirekit-chart>`.

Downstream AI tooling should branch on `component_kind` when generating composition code: anonymous components accept `<x-slot:name>` slots; class-based components accept only constructor-mapped props.

Consumed by:

- The [`/components.json`](https://docs.wirekit.app/components.json) endpoint on docs.wirekit.app
- AI tooling (Claude Code, ChatGPT Codex, Cursor, Aider, etc.) for prop-aware autocomplete
- Design-system audits comparing component coverage across releases

The flag set bakes in `JSON_HEX_TAG` so user-controlled string values cannot break out of any consuming `<script type="application/ld+json">` block.

## `wirekit:export-api-map`

```bash
php artisan wirekit:export-api-map
php artisan wirekit:export-api-map --pretty
php artisan wirekit:export-api-map --public
```

Emits an AI-friendly hierarchical sitemap covering every WireKit surface: components, theme presets, font presets, icon presets, layouts, blueprints, recipes, and CLI commands. Superset of `wirekit:export-json` — designed for MCP servers, Claude Code, ChatGPT Codex, Cursor, Aider, and other AI tooling that needs a single entry point.

**Flags:**

- `--pretty` — pretty-print (multi-line) instead of minified output.
- `--public` — produces the sitemap that docs.wirekit.app serves at its `/api-map.json` endpoint. General integrations should omit it; the default emits the full sitemap for your tooling.

Output shape:

```json
{
  "version": "1.x.x",
  "generated_at": "2026-04-26T15:00:00+00:00",
  "docs_base": "https://docs.wirekit.app",
  "groups": [
    { "id": "components",  "count": <N>, "items": [...] },
    { "id": "themes",      "count": <N>, "items": [...] },
    { "id": "fonts",       "count": <N>, "items": [...] },
    { "id": "icons",       "count": <N>, "items": [...] },
    { "id": "layouts",     "count": <N>, "items": [...] },
    { "id": "blueprints",  "count": <N>, "items": [...] },
    { "id": "recipes",     "count": <N>, "items": [...] },
    { "id": "commands",    "count": <N>, "items": [...] },
    { "id": "helpers",     "count": <N>, "items": [...] },
    { "id": "css-classes", "count": <N>, "items": [...] }
  ]
}
```

Counts are intentionally elided — the canonical numbers grow with every release. Run `php artisan wirekit:export-api-map --pretty | jq '.groups[] | {id, count}'` against your installed version for current values, or fetch the live JSON below for the released-at-tag snapshot.

Consumed by:

- The [`/api-map.json`](https://docs.wirekit.app/api-map.json) endpoint on docs.wirekit.app
- AI agents looking for one place to enumerate every WireKit doc

`JSON_HEX_TAG` is set so user-controlled string values cannot break out of consuming `<script>` blocks.

## `wirekit:class-by-area`

```bash
php artisan wirekit:class-by-area
php artisan wirekit:class-by-area --format=full
php artisan wirekit:class-by-area --format=json
php artisan wirekit:class-by-area --area=blade --area=compiled
```

Inventories every Tailwind-class candidate across the five layers WireKit's component output is built from — Blade templates, PHP class strings, JS factory literals, the sample's compiled Tailwind output, and dist/wirekit.css's custom-CSS selectors — and reports both per-area counts and inter-area diffs.

Useful when you want to answer questions like:

- "Which classes does Blade emit that Tailwind never generates a rule for?"
- "Which compiled selectors have no source emission anywhere?"
- "How many wirekit-namespaced custom selectors does dist/wirekit.css ship?"

The default `--format=summary` output is human-readable per-area counts plus 5 canonical diffs. `--format=full` prints the first 50 entries of every list. `--format=json` emits a machine-consumable structured report (integrate into CI dashboards, audit-history pipelines, or downstream tooling).

**Flags:**

- `--format=summary|full|json` — output shape; default is `summary`
- `--area=blade|php|js|compiled|wirekit-css` — restrict the analysis to specific areas (repeatable)

## `wirekit:cursor-rules`

```bash
php artisan wirekit:cursor-rules
php artisan wirekit:cursor-rules --force   # overwrite existing
```

Publishes the package's `.cursor/rules/wirekit.mdc` file to your project's `.cursor/rules/wirekit.mdc`. Cursor and other AI editors with native `.mdc` support automatically pick up the rules for every `*.blade.php` and `*.css` file in the project — no manual configuration.

The rules file covers component invocation syntax, the variant system, design tokens, icon usage, layout primitives, accessibility defaults, Livewire integration patterns, browser-support baseline, and the full CLI. Refuses to overwrite an existing copy without `--force`.

## `wirekit:mcp-serve`

```bash
php artisan wirekit:mcp-serve   # spawned by your editor / MCP client over stdio
```

Runs the WireKit Model Context Protocol (MCP) server. AI coding assistants and MCP clients spawn it as a local child process and talk JSON-RPC 2.0 over stdin/stdout to query the component catalog live while authoring — so the editor reads real prop signatures instead of guessing them.

It is a local, zero-hosting server: no port, no daemon, always version-matched to your installed WireKit. It exposes four read-only tools — `search_components`, `list_components`, `get_component`, and `get_tokens` — sourced from the shipped component registry and design tokens (nothing leaves your machine). Point your editor's MCP settings at `php artisan wirekit:mcp-serve`; it is not meant to be run interactively.

## `wirekit:boost-skills`

```bash
php artisan wirekit:boost-skills          # write .boost/wirekit.json
php artisan wirekit:boost-skills --force  # overwrite / refresh an existing manifest
```

Publishes a Laravel Boost skill manifest to `.boost/wirekit.json` in your project — a typed bundle (every component with its real props + defaults, the theme presets, the customization decision tree, and the CLI) that an AI-augmented editor loads for WireKit-aware autocomplete.

The manifest is auto-generated from the installed package — the component registry, the PropsParser-derived `@props`, the theme-preset registry, and the registered commands — so it never drifts from your WireKit version. Re-run after upgrading WireKit to refresh it; it refuses to overwrite an existing manifest without `--force`, and carries a `format-version` so a future schema revision won't break a manifest you have already committed.
