npm package: @shobman/strata-cli
Description: Contract engine and CLI for the Strata layout protocol
Author: Simon Hobman
Latest version: 0.1.1

# @shobman/strata-cli

[![npm version](https://img.shields.io/npm/v/@shobman/strata-cli)](https://www.npmjs.com/package/@shobman/strata-cli)
[![bundle size](https://img.shields.io/bundlephobia/minzip/@shobman/strata-cli)](https://bundlephobia.com/package/@shobman/strata-cli)
[![license](https://img.shields.io/npm/l/@shobman/strata-cli)](../../LICENSE)

CLI toolchain and contract engine for the [Strata](../../README.md) layout protocol. Validates YAML contracts, generates TypeScript types, and scaffolds routes and components.

## Installation

```bash
npm install @shobman/strata-cli
```

## Commands

### `strata init`

Initialise a project with Strata contracts and AI skill file. Idempotent — safe to run multiple times.

```bash
npx strata init
```

Creates:

```
.claude/skills/STRATA-SKILL.md    # AI agent skill file
components/atoms/_contract.yml     # atom level contract
components/molecules/_contract.yml # molecule level contract
components/organisms/_contract.yml # organism level contract
routes/_contract.yml               # root route contract
```

Also adds `strata:check` and `strata:build` scripts to your `package.json`.

### `strata check`

Validate all contracts (YAML only, no source code). Fast — runs in CI.

```bash
npx strata check
```

Exit code `1` if errors are found. Output format:

```
error  fills-reference-real-slots       routes/funds/index/_contract.yml
       Fill "nonexistent" does not match any slot in ancestors or self

warn   route-level-consistency          routes/funds/_contract.yml
       Route-level contract should use level: route, rank: 3
```

**Validation rules:**

| Rule | Severity | Description |
|------|----------|-------------|
| `fills-reference-real-slots` | error | All fills must reference slots declared by ancestors or self |
| `required-slots-satisfied` | error | Required slots must be filled through the default/redirect chain |
| `default-redirect-mutual-exclusivity` | error | A route cannot have both `default` and `redirect` |
| `default-redirect-target-exists` | error | Named child must exist as a subfolder |
| `param-route-excluded-from-default-redirect` | error | Dynamic routes (`[param]`) cannot be defaults or redirects |
| `route-level-consistency` | warn | Routes should be `level: route, rank: 3` |
| `component-level-consistency` | warn | Components should match their directory level |

### `strata build`

Generate TypeScript types from YAML contracts.

```bash
npx strata build
```

Creates `.strata.types.ts` next to each `_contract.yml`:

```typescript
// AUTO-GENERATED by strata build — do not edit
export type FundsFundIdSlots = "tabs" | "contextPanel";
export type FundsFundIdRequiredSlots = "tabs";
export type FundsFundIdInheritedSlots = "breadcrumb" | "actions";
export type FundsFundIdAllSlots = FundsFundIdSlots | FundsFundIdInheritedSlots;
```

Type naming convention: path segments are PascalCased and joined. `routes/funds/[fundId]` becomes `FundsFundId`.

### `strata add route <path>`

Scaffold a new route with contract, page stub, and optional layout.

```bash
npx strata add route funds/[fundId]/compliance
npx strata add route funds --fills menu --slots listActions --default index
```

| Flag | Description |
|------|-------------|
| `--fills <slots>` | Comma-separated slot names this route fills |
| `--slots <slots>` | Comma-separated slot names this layout declares (generates `_layout.tsx`) |
| `--default <child>` | Child that renders at this route's URL (index route) |
| `--redirect <child>` | Child that this route redirects to |
| `--param <name>` | Route parameter name (auto-detected from brackets) |

Creates `_contract.yml` and `index.tsx`. If `--slots` is provided, also creates `_layout.tsx` with `SlotProvider`/`SlotTarget` boilerplate. Runs `strata build` automatically after scaffolding.

### `strata add <level> <name>`

Scaffold a component at the specified level.

```bash
npx strata add atom Button
npx strata add molecule SearchBar
npx strata add organism FundPanel
```

Creates `components/<level>s/<Name>/index.tsx` and the level's `_contract.yml` if missing.

## Contract Format

Every route directory contains a `_contract.yml`:

```yaml
param: fundId                     # route parameter name (optional)
level:
  name: route                     # atom | molecule | organism | route
  rank: 3                         # 0 | 1 | 2 | 3
fills: [breadcrumb, actions]      # ancestor slots this route fills
layout:
  slots:
    tabs: required                # slot declarations (required or null)
    contextPanel:
  default: overview               # child renders at this URL (index route)
  redirect: performance           # parent URL redirects to child URL
```

## Programmatic API

The contract engine is available as a library:

```typescript
import {
  buildContractTree,
  validateContracts,
  resolveAvailableSlots,
} from "@shobman/strata-cli";
```

| Export | Description |
|--------|-------------|
| `buildContractTree(rootDir)` | Build hierarchical contract tree from filesystem |
| `validateContracts(tree)` | Run all validation rules, returns diagnostics |
| `resolveAvailableSlots(node)` | Get all slots available to a node (own + ancestor) |
| `resolveAncestorSlots(node)` | Get ancestor slots only |
| `isSlotFilledInChain(node, name)` | Check if slot is filled through default/redirect chain |
| `parseContractFile(path)` | Parse a single `_contract.yml` |

## Tips

### YAML bracket quoting

`[fundId]` is valid YAML array syntax. All `_contract.yml` references to parameterised folders must use quotes:

```yaml
# Correct
default: "[fundId]"

# Wrong — YAML parses this as an array
default: [fundId]
```

The `strata add` command handles this automatically when generating contracts for bracket segments.

## License

[MIT](../../LICENSE)