# CLI

```
node-design-seam <pin|bindings|verify|adopt|preview> [--config <path>]
  adopt   <path-to-new-drop>
  preview [--drop <path>] [--port <n>] [--html <rel-path>]
```

Every command loads [the config file](configuration.md) first — `--config <path>` or
`./node-design-seam.config.json` by default. `--config` may appear anywhere in the
argument list; the flag and its value are stripped before the command sees the rest.
`preview` is the one exception: given `--drop`, it runs with no config at all.

**Exit codes** are `0` on success and `1` on any failure, including an unknown
command (which prints the usage line to stderr) and any thrown error (printed as
`node-design-seam: <message>`).

> **Check the bin actually runs.** Run `node-design-seam` with no arguments: it must
> print the usage line and exit 1. In 0.1.1 it printed nothing and exited 0 through
> npm's `.bin` symlink, which made a CI `verify` step pass while checking nothing.
> Fixed in 0.1.3 — see the [CHANGELOG](../CHANGELOG.md).

## `pin`

Hashes every file in the drop and writes the manifest to `manifestOut`.

```sh
node-design-seam pin
```
```
pin: 7 files hashed from design/wallet-stage-v1
```

Walks `dropRoot` recursively, skipping `.DS_Store` and anything named in
`notDropContent`. Writes one sha256 row per file, sorted by path, creating parent
directories as needed.

If the drop has no `AGENTS.md`, it warns on **stderr** and still exits 0:

```
node-design-seam pin: WARNING — drop has no AGENTS.md; copy the previous drop's
forward (the design agent loses its contract guidance without it).
```

> **Run `pin` only when adopting a drop.** Regenerating the manifest to silence a
> failing `verify` is the one canonical misuse — it launders an edit to a
> design-owned file into a pinned state. The generated file's own header says so.

## `bindings`

Scans the drop and writes the generated binding table to `bindingsOut`.

```sh
node-design-seam bindings
```
```
bindings: 12 generated + 0 legacy
```

A textual token scan — no DOM parser, no `eval`. Slots come from `.html` files only;
`.view.js` files are read for their `id` alone. Entries from the config's
`legacyAnchors` are appended after the generated rows. Output is deterministic:
scalars are sorted by source, then by form.

**On any scan error it writes nothing** and exits 1:

```
bindings: stage.html: data-bind-attr uses "class" — not in the allowlist [aria-label, aria-valuenow, data-count, data-state, hidden, href, title]
```

Fail-closed is deliberate — a binding that can't be read correctly is never one that
gets guessed at. The full list of scan errors is in
[Write vocabulary](write-vocabulary.md#scan-errors).

## `verify`

The CI gate. Runs four checks plus a contract-version gate; all must hold.

```sh
node-design-seam verify
```
```
verify: contract coverage 12/12 slots bound; views: wallet.cards
verify: PASS (integrity, anchors, contract, freshness)
```

The coverage line prints on **every** run, pass or fail, and includes tracked debt
when `unwired` is non-empty:

```
verify: contract coverage 5/7 slots bound (2 unwired — tracked debt); views: obs.traces
```

On failure, every failing check prints a `FAIL <prefix>: …` line to stderr and the
command exits 1. Each prefix names the check:

| Check | Asserts |
| --- | --- |
| `integrity` | every drop file hashes to what the manifest pinned — nothing modified, added or removed |
| `anchors` | every binding's anchor token is present in the drop's text |
| `contract` | scan is error-free; every declared slot is bound or `unwired`; every binding targets a declared slot |
| `freshness` | regenerating the bindings table reproduces the committed file byte-for-byte |
| `contract-version` | the drop's `AGENTS.md` `contract: N` is not newer than this CLI supports |

Both generated files are read as **text** and parsed by regex — `verify` never
imports or evaluates them.

What each failure means and how to respond: [Troubleshooting](troubleshooting.md).

## `adopt`

The adoption runbook as one command.

```sh
node-design-seam adopt ../inbox/wallet-stage-v2
```
```
adopt: wallet-stage-v2 in place.
  contract diff — added: [wallet.summary.pending] removed: [wallet.summary.legacyTotal]
  next: wire added slots (or record in unwired), delete bindings for removed ones, run verify.
```

In order:

1. Reads and parses the config file **before touching the filesystem**, so a bad
   `--config` path fails fast instead of leaving a half-adopted repo.
2. Records the currently bound sources, for the diff.
3. Copies the source folder to a sibling of the current `dropRoot`, named after the
   source folder's own basename.
4. If the incoming drop has no `AGENTS.md` and the previous one does, carries it
   forward and says so on stderr.
5. Rewrites `dropRoot` in the config file on disk, then reloads it.
6. Runs `pin`, then `bindings` (reporting scan errors without aborting).
7. Prints the **contract diff** — slots added and removed versus the previous table.

The diff is the point of the command. It is the only place the change in the data
contract between two drops is stated plainly, and it tells you exactly what wiring
work the new drop created.

`adopt` does not run `verify` — do that yourself once the added slots are wired.

Missing the path argument exits 1:

```
adopt: usage — node-design-seam adopt <path-to-new-drop>
```

## `preview`

Serves the drop through the real runtime, for authoring and for capturing goldens.

```sh
node-design-seam preview --drop design/wallet-stage-v1
```
```
preview: serving 9 drop files at http://127.0.0.1:4321
  the page drives the real applier — a golden captured here matches production
  Ctrl-C to stop
```

The page imports `mountDropFragment` and `createSeam` from this package and drives
them exactly as a stage entry does — it has no binder of its own, so there is no
second implementation of the write vocabulary to drift from `src/apply.js`. Bindings
come from the same scan `bindings` and `verify` use, so a drop can be previewed
before it has ever been adopted.

A toolbar gives **fixture state** × **theme** × **host width**. Each state re-mounts
the fragment from the drop's own bytes before applying, so no state inherits writes
from the one before it.

| Flag | Default | |
| --- | --- | --- |
| `--drop <path>` | the config's `dropRoot` | preview a drop with no config file at all |
| `--port <n>` | `4321` | must be an integer 1–65535 |
| `--html <rel-path>` | `stage.html` | the entry fragment, relative to the drop root |

Binds to `127.0.0.1` only. **Nothing is written into the drop**, so `pin`'s hashes
still hold and `verify` still passes while a preview is open.

Scalar slots show the literals authored in the markup unless the drop ships an
optional `tools/preview-states.js` exporting `states` (a snapshot per state, in the
shape `seam.update()` takes) and optionally `themes` (merged over the selected
state). View modules are fed their own `fixtures[state]`; a module missing that
state is reported in a status line rather than rendered misleadingly.

Scan errors print as `preview: contract: …` and the page still serves, so a drop
with a bad `data-bind-attr` can be looked at while it is being fixed.

Walked through in [Working in parallel](workflow.md#the-designers-loop).

## Using it from an app's build

```json
"scripts": {
  "build": "vite build && node-design-seam verify",
  "verify:design": "node-design-seam verify"
}
```

If the app also stamps its own integrity manifest, `verify` must run **before** the
stamp — see [two hash layers](concepts.md#two-hash-layers-two-lifecycles).
