[@layout]: ./docs.layout.md

# CLI

The `pathmx` command manages, serves, and inspects a project from its `package.json` root. `pmx` is the short alias.

```sh
pmx [--root <path>] [--no-progress] <command>
```

| Option              | Meaning                                                  |
| ------------------- | -------------------------------------------------------- |
| `-r, --root <path>` | Source root. Defaults to `./paths` beside `package.json` |
| `--no-progress`     | Disable animated progress                                |

The global launcher owns `init` and `update`. Project commands delegate to the `@pathmx/core` version pinned by the selected project's `package.json`, so `pmx --version` reports the project release. A positional Source root or `--root` selects that project consistently for Core, `.env`, Plugins, and command execution. If both roots are supplied, they must resolve to the same directory.

Inside a repo, prefer its package scripts for everyday work: `bun run dev`,
`bun run check`, and `bun run start`. For the commands below, `bun run pmx`
uses the installed project CLI without requiring a global launcher. Complete
[Install](./start/install.guide.md) and [Quickstart](./start/first-site.guide.md) first.

Leave `pmx dev` or `pmx serve` running while you use other commands. Commands that
write Sources, admit Events, retry jobs, or drain jobs hand that work to the
running host for the same Source root. The host applies it through its own write
queue and worker and updates the running site. Without a running host, the
command owns the repository until it finishes. Read-only commands inspect the
repository directly. Do not start a second writer against the same state
directory to bypass its lock.

The CLI runs with Bun on macOS, Linux, and Windows. Bun may load `.env` files
from the calling directory before PathMX starts. PathMX then loads the selected
project's `.env` without overwriting variables already in the process, including
those loaded by Bun. When selecting another project with `--root`, values from
the calling directory therefore take precedence over that project's `.env`.

To let only PathMX load the selected project's `.env`, invoke the installed entry
with Bun's `--no-env-file` flag:

```sh
bun --no-env-file ./node_modules/@pathmx/core/dist/pathmx.js --root /path/to/project/paths lint
```

Use the same flag before the TypeScript entrypoint when running Core from source.

Run `pmx` for a short getting-started workflow, or `pmx --help` for the full command list. Core command help, such as `pmx dev --help`, remains available when Plugins are broken or Core dependencies are missing. Top-level help reports unavailable Plugin help separately. `pmx help plugins add` shows nested command help.

---

## init

Creates a minimal project with pinned PathMX, TypeScript tooling, and
`dev`, `start`, `check`, and `typecheck` package scripts.

```sh
pmx init [directory] [--no-install]
```

| Option         | Meaning                                   |
| -------------- | ----------------------------------------- |
| `directory`    | Project directory. Defaults to `.`        |
| `--no-install` | Write files without running `bun install` |

The completion message prints the commands to enter your new project and start it:

```sh
pmx init my-course --no-install
cd my-course
bun install
bun run dev
```

If installation fails after files are created, follow the recovery commands in the error message to finish installing in that directory.

---

## dev

Starts the development server. It watches Sources, rebuilds affected browser bundles, and restarts for engine, Plugin, or Action code changes.

The CLI supervises a separate Bun `--watch` server process. Explicit Bun runtime
arguments are preserved through project-package delegation and server restarts.
Imported component modules using `moduleRef` are also watched, including modules
in linked Plugin packages. Correcting a broken module or restoring a deleted
import recovers the failed start. Source edits continue through live updates.
For example, run a test preload in both the CLI and the server:

```sh
bun --preload ./mock.ts node_modules/.bin/pmx dev paths --port 3012
```

Relative preload paths resolve from the original working directory. Keep preloads
safe to run in each process and again after a restart. PathMX still controls the
server's watch mode and environment-file loading; a preload is not a browser script.

Editing a `.js` or `.mjs` file under the Source root reloads connected pages
in that development app. This refreshes the entire browser module graph,
including dynamically imported helpers, and resets in-memory page state.
Markdown and CSS edits continue to update in place. This script-reload policy
does not apply to `serve` or static publications.

```sh
pmx dev [root] [--port <port>] [--verbose] [--admin <actor-source>] [--admin-origin <origin>]
```

| Option                    | Meaning                                                 |
| ------------------------- | ------------------------------------------------------- |
| `-p, --port <port>`       | Port to listen on                                       |
| `--verbose`               | Show detailed development logs                          |
| `--admin <actor-source>`  | Admit one Actor Source as the loopback repository admin |
| `--admin-origin <origin>` | Admit the admin only on this HTTPS proxy origin; replaces localhost admission |

After starting an admin session, the compact development strip lets you select
a repository user and choose **Switch**. Switching opens that user's home with
their normal permissions and saved state. Repository-admin authority is suspended
until you select **Repository admin** and switch back. **End session** ends the
whole development admin session, including while viewing as another user.
The selected user persists across page reloads, Source edits, and automatic code
restarts. Stopping the dev command ends its sessions; starting it again requires
a new admin session.

The switcher uses the existing loopback or explicitly trusted HTTPS origin and
requires an active development admin session. It is available with `dev --admin`;
production `serve` does not enable it. Actions performed while viewing as a user
apply to that user's local data.


---

## serve

Starts the production server and keeps connected browsers in sync as authored Markdown and CSS change.

```sh
pmx serve [root] [--port <port>] [--host <hostname>]
```

| Option              | Meaning                                                     |
| ------------------- | ----------------------------------------------------------- |
| `-p, --port <port>` | Port. Falls back to `PATHMX_PORT`, then `PORT`, then `3000` |
| `--host <hostname>` | Bind address. Falls back to `PATHMX_HOST`, then `0.0.0.0`   |

Set `PATHMX_ORIGIN` to the one canonical public origin in production. `GET /api/runtime/health` reports whether the watched Sources are still valid.

---

## export

Writes the anonymous static publication and its route manifest.

```sh
pmx export [root] --origin <url> [--base-path <path>] [--out <path>]
```

| Option               | Meaning                                                   |
| -------------------- | --------------------------------------------------------- |
| `--origin <url>`     | Canonical public origin. Required                         |
| `--base-path <path>` | Public mount path. Defaults to `/`                        |
| `-o, --out <path>`   | Output directory. Defaults to `dist/` in the project root |

The output must not overlap the Source root, including through symlinked parent directories. Files and symlinks cannot be output directories. Export accepts a new or empty directory, or replaces a recognized prior PathMX publication identified by `.pathmx/manifest.json`. Treat a publication directory as generated output: replacement removes all of its previous contents.

An unrelated nonempty directory is refused. Choose a new or empty directory with `--out`. The adapter contract lands in `.pathmx/manifest.json` inside the output.

---

## lint

Lint checks indexed links, declared schemas, compiled Components, and installed
Plugin diagnostics. It does not exercise sign-in, Actions, persistence, browser
interaction, or a deployment's proxy configuration. Verify those journeys in the
running application after authoring checks pass.

Validates Sources, links, compilation, foundation rules, and repository Plugin rules without starting a server.
Malformed Source YAML is reported with file coordinates when available; lint
continues checking other Sources and exits nonzero when errors remain. Invalid
dependencies also identify pages that cannot finish compilation. See
[Source recovery](./authoring/sources.reference#invalid-sources-and-recovery)
for development and live-server behavior.

```sh
pmx lint [root] [--format text|json]
```

Run it after every edit. Package and repository Plugins add their own rules to the same report. Text is the default; use JSON for scripts and editors:

```sh
pmx lint --format json > lint.json
```

JSON is a deterministically sorted array of issue records. A clean project returns `[]`. Every record has `type` and `source`; the remaining fields depend on the issue:

| Type              | Additional fields            |
| ----------------- | ---------------------------- |
| `link.unresolved` | `href`, optional `label`     |
| `source.invalid`  | `plugin`, `message`          |
| `source.warning`  | `message`, optional `plugin` |

```json
[
  {
    "type": "link.unresolved",
    "source": "/index.md",
    "href": "./missing.md",
    "label": "Missing lesson"
  }
]
```

Lint exits `0` when clean or when only warnings are present, and `1` for unresolved links or authoring errors. If the project cannot load or the command is invalid, stdout has no report; the error goes to stderr with exit code `1`.

Plugin authoring issues identify the owning Plugin and Source. Error and warning
records may also include `code`, `hint`, `block`, `line`, and `column`. Codes are
scoped to their Plugin; use `(plugin, code)` to identify a rule rather than matching
message text. `block` is the Block id. Line and column, when available, are
one-based positions in the complete Source file; column requires line. Missing
location fields mean the producer does not know an exact location.

Text output includes these details and prints hints as `Fix:` instructions.
Warnings alone exit `0`; errors exit `1`. Redundant stored Completion keys for
derived targets are warnings because the owning Plugin computes their completion.

Schema and expected compilation failures are collected across Sources, including
invalid component attributes, unknown components, and malformed query fences or
requests. An unexpected Plugin exception names the failing lint hook when
applicable, exits `1`, and aborts without a JSON report. Treat this as an incomplete
check, rather than a clean repository.

Plugin authors can [contribute diagnostics](./extend/plugins.guide.md#report-authoring-issues)
with exact locations and recovery hints. This command reads those contributions.
Local authoring commands use development setup, so checking a project with Auth
does not require production email credentials. `serve` and `export` keep their
explicit production runtime.

---

## tree

Prints the Source tree with each file's type, Block count, and outgoing and incoming link counts.

```sh
pmx tree [root] [--all]
```

| Option  | Meaning                    |
| ------- | -------------------------- |
| `--all` | Include non-Markdown files |

For example, after Quickstart, `bun run pmx tree` lists `index.md`, `about.md`,
and the generated layout/navigation Sources with their types and Block counts.
Add `--all` to include CSS, JavaScript, and image files too.

---

## query

Runs the same access-controlled query as a `query` fence and writes JSON, JSON Lines, or a table.

```sh
pmx query <sources|blocks|plugin.name> [options]
```

| Option                         | Meaning                                            |
| ------------------------------ | -------------------------------------------------- |
| `--from <scope>`               | Canonical or Actor-relative folder scope           |
| `--type <type>`                | Source or Block type                               |
| `--where <field=value>`        | Scalar equality filter. Repeatable                 |
| `--has <prop>`                 | Required registered data prop. Repeatable          |
| `--sort <field[:asc\|desc]>`   | Result ordering. Repeatable                        |
| `--limit <number>`             | Maximum result count                               |
| `--links-to <source-or-block>` | Sources linking to a Source or Block               |
| `--links-from <source>`        | Sources linked from a Source                       |
| `--no-recursive`               | Exclude nested folders                             |
| `--input <json>`               | Input for a named `plugin.name` query              |
| `--request <file>`             | Complete request as JSON or YAML, or `-` for stdin |
| `--as <actor>`                 | Inspect through an Actor's effective access        |
| `--format <format>`            | `json` (default), `jsonl`, or `table`              |

```sh
pmx query sources --from /docs --type reference --format table
```

---

## perms

Shows effective read, write, and execute capabilities for one Source, or every Source one Actor can read.

```sh
pmx perms [source] [--as <actor>] [--format text|json]
```

| Option              | Meaning                               |
| ------------------- | ------------------------------------- |
| `source`            | Source id or path                     |
| `--as <actor>`      | Actor id, such as `/users/alice.user` |
| `--format <format>` | `text` (default) or `json`            |

Try `bun run pmx perms /about.page` in the Quickstart project: it reports the
page's effective capabilities. After [Ada's account setup](./accounts/auth.reference.md#local-actor),
compare `bun run pmx perms /ada --as /ada.user` with the same command without
`--as`. The Home's owner can read/write personal work; anonymous access is denied.

`--as` is trusted local inspection. It creates no session and does not impersonate the Actor over HTTP.

---

## mv

Moves a Source, an individual asset, or an entire folder and rewrites authored references.

```sh
pmx mv lesson.md lessons/lesson.md --dry-run
pmx mv resources/codex codex --dry-run
pmx mv resources/codex codex
pmx mv work.css studio.css --dry-run
```

Paths are relative to the configured Source root. A Source destination must end in
`.md`; an asset destination must remain a non-Markdown file path. A folder destination
must be a new directory location. Existing destinations,
overlapping trees, and paths escaping the Source root are rejected. A folder
containing an `index.md` still moves as a folder; name the file explicitly to move
only that Source.

An asset move preserves the file's exact bytes and updates indexed references,
including Markdown links/images, `[@styles]` imports, and writable Plugin relations.
Relative versus absolute paths, query strings and fragments are preserved. The
asset's own contents are unchanged: relative URLs inside CSS or JavaScript imports
are not rewritten. The Bun host supports asset moves, including automatic handoff
to a running dev or production host. Other adapters must implement `storage.moveFile`.

A folder move carries all authored files, including binary assets, hidden files,
and empty directories. Relative links between moved files retain their spelling.
Inbound Markdown links, reference definitions (including typed layouts), HTML
anchor hrefs, and writable Plugin relations retarget to the new tree. Relative
outbound links to files outside the tree rebase. External URLs stay unchanged.
The query Plugin rewrites explicit `from:` scopes; omitted scopes continue to
follow the query's containing directory.

`--dry-run` lists each file move, shows reference diffs and public URL warnings,
and validates the complete plan without writing. `--format json` includes a
`tree` inventory for folders and `move` entries with `from` and destination `path`
for folders and individual assets. Asset preview content is `null`; bytes are not
embedded in the receipt. Apply rechecks the inventory or asset digest, destination,
and Source contents before writing.

When durable storage is active, Bun journals individual asset moves with their
reference edits and recovers interrupted work before normal access resumes.
Conflicting asset bytes or Source edits stop recovery for inspection. Durable
folder moves remain unsupported.

The Bun adapter renames the tree and restores text edits and the original tree
if a write fails. This provides recovery from handled I/O failures, not a
power-loss journal or a lock against another process editing simultaneously.
The browser adapter uses one IndexedDB transaction. Custom storage adapters must
provide the [folder-move capability](./extend/core.reference#folder-move-storage).

Symbolic links and trees containing excluded control directories such as `.git`,
`.pathmx`, or `node_modules` are rejected. Scripts and stylesheets travel unchanged;
arbitrary strings in code, frontmatter without a relation contribution, and
dynamically interpolated query scopes are outside the rewrite contract.

---

## rm

Removes a Source and unlinks every reference to it.

```sh
pmx rm <path> [--dry-run] [--format text|json]
```

---

## Preview and apply Source edits

Leave `pmx dev` or `pmx serve` running while using `mv` or `rm`. The CLI contacts
that host on the same machine; the same preview and receipt format applies with
or without a host. This local handoff does not require browser sign-in and is not
exposed through the site's HTTP routes. The host must use the same PathMX release
and state directory as the command; otherwise the command asks you to restart the
host or use its `PATHMX_DB_DIR`. If a handoff fails, the CLI reports the error
without retrying as a standalone writer. A lost response can follow a completed
write: inspect the files before retrying. Restart the host if its local endpoint
is no longer reachable.

For both `mv` and `rm`, `--dry-run` shows the proposed file changes as unified diffs and confirms that no
files changed. Each diff includes three surrounding context lines and one hunk
spanning the changed region. A successful operation reports the distinct
changed-file count. Failed operations do not print a completion message.

```sh
pmx mv lesson.md lessons/lesson.md --dry-run
pmx rm lesson.md --dry-run --format json
pmx mv lesson.md lessons/lesson.md --format json
```

JSON output is one object with `version: 1`, `type`, `changes`, and `warnings`.
Changes are ordered by Source-root-relative path, with a leading `/`.

| Result           | `type`    | Each change                       |
| ---------------- | --------- | --------------------------------- |
| Dry run          | `preview` | `type`, `path`, `before`, `after` |
| Successful write | `applied` | `type`, `path`                    |

Change types are `create`, `replace`, `delete`, and `move`. A `move` also includes `from`. Preview content is the complete
file text: `before` is `null` for creation, and `after` is `null` for deletion.
Warnings are strings in the JSON object. Actual writes omit content from their
receipt. A Source move reports deletion of the old path and creation of the new
path; folder and asset moves use `move` entries. Reference edits use `replace`.

A preview is informational; rerunning the command recomputes the plan from the
current repository. There is no saved-plan apply command. Failures write an error
to stderr, exit `1`, and emit no JSON receipt. A filesystem failure during a
multi-file write can leave some changes applied; inspect the files before retrying.

---

## plugins

Manages official `@pathmx/*` package Plugins. Names accept `player` or `@pathmx/player`.

```sh
pmx plugins list
pmx plugins add <name...>
pmx plugins remove <name...>
pmx plugins update [name...]
```

`add` installs from the CLI's release channel and activates the package in `package.json#pathmx.plugins`. `update` with no names updates every installed package.

New activations append after existing Plugins. Repeating `add` preserves their
order and configuration. Already-installed packages are checked for activation
support before any missing packages are installed. `remove` changes activation
only after Bun succeeds, preserving other configuration and Bun's manifest edits.

If a package operation or activation fails after installation starts, the error
reports the observed package state and points to `pmx plugins list`. A dependency
can remain installed when activation fails; inspect that state before retrying.

`list` shows activated Plugins as `active`, available but unselected Plugins as
`inactive`, and supporting packages such as Core, React, and UI as `installed`.
Supporting packages do not require Plugin activation.

---

## update

Checks for or installs a newer global CLI from the release channel.

```sh
pmx update [--check]
```

With the optional global launcher installed, run `pmx update --check` to see
whether the channel matches your installed version or offers a newer release.
This check does not install anything. `pmx update` installs the newer global CLI
and prints a version-verification step; it does not update a project's pinned
packages. Use `bun run pmx plugins update` for that project workflow.

---

## Plugin commands

Installed Plugins can mount trusted administrator commands beneath their id, such as `pmx auth setup`, `pmx auth doctor`, and `pmx search <words...>`. They appear in `pmx --help` only when the owning package or repository Plugin is active in the current project.

Search and Auth commands include their own examples. With those Plugins active:

```sh
pmx search learning --format json
pmx auth users list --format json
```

Both commands accept `--format table|json|jsonl`. JSON returns an array; Auth user records contain `name` and `email`, ordered by email. Empty JSON results are `[]`, and empty JSON Lines results write nothing. Table output gives an explicit empty-result message; Auth tables include Email and Name headers.

---

## Output and errors

Inspection results and Source-edit plans go to stdout. CLI logs, export progress, and operation warnings go to stderr, so redirecting output keeps those diagnostics separate. Plugins should use their command context logger for diagnostics and reserve stdout for results.

```sh
pmx tree > source-tree.txt
pmx query sources --format json > sources.json
pmx export --origin https://example.com > export.txt 2> export.log
```

Core command help includes examples and global options. Invalid format flags list the supported choices; ports must be whole numbers from `1` to `65535`, and query limits must be nonnegative whole numbers. Missing Source directories report the selected path and commands to select or create a project.

---

## Terminal presentation

Interactive terminals use a cyan accent for headings and the server URL, with
text labels for status. `pmx dev` shows the listening URL, version, Source count,
and selected directory. Use `pmx dev --verbose` for detailed development logs.

Exports show a subtle activity indicator after 350 ms. Fast exports finish
without animation. The indicator uses stderr and clears when the operation
finishes or fails; warnings remain in scrollback.

```sh
pmx export --origin https://example.com --no-progress
NO_COLOR=1 pmx dev
```

`--no-progress` disables animation. A nonempty `NO_COLOR` disables color;
combine both for plain, stationary output. CI, dumb terminals, and redirected
streams receive no color or animation. Each output stream is checked separately,
so redirecting results does not prevent feedback in an interactive stderr.


---

## Image optimization

```sh
pmx image optimize --dry-run
pmx image optimize
```

Inspect authored image uses and generate the sizes those uses request. Dry-run
reports planned widths without writing variants. Normal runs reuse the content
cache, report byte sizes and failures, and preserve original files. Development
and static publishing also generate variants automatically. See
[Images](./authoring/images.reference.md) for supported formats and presentation parameters.


---

## deploy

Install `@pathmx/deploy` to configure and deploy sites to existing SSH/launchd
servers sharing the project's Git repository:

```sh
pmx plugins add deploy
pmx deploy init --host user@server --directory /absolute/checkout
pmx deploy --dry-run
pmx deploy
```

`init` saves local target configuration. `--dry-run` checks prerequisites without
changing the server. Deployment coordinates live writers, backs up durable state,
updates the selected revision, and verifies the restarted site. See
[Server deployment](./hosting/deployment.reference) for setup, maintenance,
checkpoint coordination, and recovery requirements.

---

## Durable jobs and Email

Use `bun run pmx jobs list`, `jobs inspect <id>`, and `jobs operation <id>` to
inspect saved work. `jobs drain --limit 32` runs due jobs now and
`jobs retry <id>` requeues compatible blocked or failed work. With `pmx dev` or
`pmx serve` running, both go through that host's worker and its effect policy.
Without a host, add `--production` to drain using production effect policy; with
a host, `--production` must match the host's mode. See [durable jobs](./extend/jobs.reference).

When Email is installed, `email preview`, `email send`, `email list`,
`email inspect`, `email retry`, and `email prune` manage messages. Sending captures
by default; `email send --live` explicitly requests one real submission. See
[Email](./email.reference) for template arguments and credentials.
