···11-+++
22-title = "CLI Reference"
33-description = "Command-line tool documentation"
44-weight = 3
55-+++
66-77-## Installation
88-99-Clone and build from source:
1010-1111-```bash
1212-git clone https://tangled.org/@stavola.xyz/mlf
1313-cd mlf
1414-cargo build --release
1515-```
1616-1717-The binary will be at `target/release/mlf`.
1818-1919-### Install with cargo
2020-2121-```bash
2222-# Install with all code generators (default: TypeScript, Go, Rust + JSON)
2323-cargo install --path mlf-cli --all-features
2424-2525-# Install with only specific generators
2626-cargo install --path mlf-cli --no-default-features --features typescript
2727-cargo install --path mlf-cli --no-default-features --features typescript,go
2828-2929-# Install with JSON generation only (minimal)
3030-cargo install --path mlf-cli --no-default-features
3131-```
3232-3333-### Available Features
3434-3535-The CLI uses cargo features for optional code generators:
3636-3737-- `typescript` - TypeScript type definitions and interfaces
3838-- `go` - Go structs with JSON tags
3939-- `rust` - Rust structs with serde derive macros
4040-- **default** = `["typescript", "go", "rust"]` - All generators enabled
4141-4242-The JSON lexicon generator is always available (built into `mlf-codegen`).
4343-4444-## Commands
4545-4646-### `mlf check`
4747-4848-Validate MLF lexicon files for syntax and type errors.
4949-5050-```bash
5151-mlf check [INPUT]...
5252-```
5353-5454-**Arguments:**
5555-- `[INPUT]...` - MLF lexicon file(s) to validate (glob patterns supported)
5656-5757-**Examples:**
5858-5959-```bash
6060-# Check a single file
6161-mlf check thread.mlf
6262-6363-# Check multiple files
6464-mlf check thread.mlf profile.mlf reply.mlf
6565-6666-# Check with glob patterns
6767-mlf check "lexicons/**/*.mlf"
6868-```
6969-7070-**Output:**
7171-- ✓ Success message if valid
7272-- Detailed error messages with source context if invalid
7373-7474----
7575-7676-### `mlf validate`
7777-7878-Validate a JSON record against an MLF lexicon.
7979-8080-```bash
8181-mlf validate <LEXICON> <RECORD>
8282-```
8383-8484-**Arguments:**
8585-- `<LEXICON>` - MLF lexicon file
8686-- `<RECORD>` - JSON record file to validate against the lexicon
8787-8888-**Example:**
8989-9090-```bash
9191-mlf validate thread.mlf record.json
9292-```
9393-9494-**Output:**
9595-- ✓ Success if record is valid
9696-- Detailed validation errors if invalid
9797-9898----
9999-100100-### `mlf generate lexicon`
101101-102102-Generate ATProto JSON lexicons from MLF files.
103103-104104-```bash
105105-mlf generate lexicon --output <OUTPUT> [OPTIONS]
106106-```
107107-108108-**Options:**
109109-- `-i, --input <INPUT>` - Input MLF files (glob patterns supported, can be specified multiple times)
110110-- `-o, --output <OUTPUT>` - Output directory (required)
111111-- `--flat` - Use flat file structure (e.g., `com.example.thread.json`)
112112-113113-**Examples:**
114114-115115-```bash
116116-# Generate with folder structure
117117-mlf generate lexicon -i thread.mlf -o lexicons/
118118-# Creates: lexicons/com/example/thread.json
119119-120120-# Generate with flat structure
121121-mlf generate lexicon -i thread.mlf -o lexicons/ --flat
122122-# Creates: lexicons/com.example.thread.json
123123-124124-# Generate from multiple files
125125-mlf generate lexicon -i thread.mlf -i reply.mlf -o lexicons/
126126-127127-# Generate from glob pattern
128128-mlf generate lexicon -i "src/**/*.mlf" -o dist/lexicons/
129129-```
130130-131131----
132132-133133-### `mlf generate code`
134134-135135-Generate code in various programming languages from MLF files.
136136-137137-```bash
138138-mlf generate code --generator <GENERATOR> --output <OUTPUT> [OPTIONS]
139139-```
140140-141141-**Options:**
142142-- `-g, --generator <GENERATOR>` - Generator to use: `json`, `typescript`, `go`, or `rust` (required)
143143-- `-i, --input <INPUT>` - Input MLF files (glob patterns supported, can be specified multiple times)
144144-- `-o, --output <OUTPUT>` - Output directory (required)
145145-- `--flat` - Use flat file structure (e.g., `com.example.thread.ts`)
146146-147147-**Available Generators:**
148148-149149-| Generator | Output | Description |
150150-|-----------|--------|-------------|
151151-| `json` | `.json` | AT Protocol JSON lexicons (always available) |
152152-| `typescript` | `.ts` | TypeScript interfaces with JSDoc comments |
153153-| `go` | `.go` | Go structs with JSON tags and proper capitalization |
154154-| `rust` | `.rs` | Rust structs with serde derive macros |
155155-156156-**TypeScript Example:**
157157-158158-```bash
159159-mlf generate code -g typescript -i thread.mlf -o src/types/
160160-# Creates: src/types/com/example/thread.ts
161161-```
162162-163163-Generated TypeScript:
164164-```typescript
165165-/**
166166- * Generated from com.example.thread
167167- * Do not edit manually
168168- */
169169-170170-export interface Thread {
171171- /** Thread title */
172172- title: string;
173173- /** Creation timestamp */
174174- createdAt: string;
175175- posts: Post[];
176176-}
177177-```
178178-179179-**Go Example:**
180180-181181-```bash
182182-mlf generate code -g go -i thread.mlf -o pkg/models/
183183-# Creates: pkg/models/com/example/thread.go
184184-```
185185-186186-Generated Go:
187187-```go
188188-// Generated from com.example.thread
189189-// Do not edit manually
190190-191191-package thread
192192-193193-// Thread represents a discussion thread
194194-type Thread struct {
195195- // Thread title
196196- Title string `json:"title"`
197197- // Creation timestamp
198198- CreatedAt string `json:"createdAt"`
199199- Posts []Post `json:"posts"`
200200-}
201201-```
202202-203203-**Rust Example:**
204204-205205-```bash
206206-mlf generate code -g rust -i thread.mlf -o src/models/
207207-# Creates: src/models/com/example/thread.rs
208208-```
209209-210210-Generated Rust:
211211-```rust
212212-// Generated from com.example.thread
213213-// Do not edit manually
214214-215215-use serde::{Deserialize, Serialize};
216216-217217-/// Thread represents a discussion thread
218218-#[derive(Debug, Clone, Serialize, Deserialize)]
219219-pub struct Thread {
220220- /// Thread title
221221- pub title: String,
222222- /// Creation timestamp
223223- #[serde(rename = "createdAt")]
224224- pub created_at: String,
225225- pub posts: Vec<Post>,
226226-}
227227-```
228228-229229-**Notes:**
230230-231231-- TypeScript: Uses interfaces for records, type aliases for defs, optional fields use `?`
232232-- Go: Uses structs with JSON tags, pointer types for optional fields, PascalCase for exports
233233-- Rust: Uses structs with serde, `Option<T>` for optional fields, snake_case with `#[serde(rename)]`
234234-- All generators handle doc comments, constraints are preserved in generated code where applicable
235235-236236----
237237-238238-### `mlf generate mlf`
239239-240240-Convert ATProto JSON lexicons back to MLF format.
241241-242242-```bash
243243-mlf generate mlf --output <OUTPUT> [OPTIONS]
244244-```
245245-246246-**Options:**
247247-- `-i, --input <INPUT>` - Input JSON lexicon files (glob patterns supported, can be specified multiple times)
248248-- `-o, --output <OUTPUT>` - Output directory (required)
249249-250250-**Examples:**
251251-252252-```bash
253253-# Convert a single JSON lexicon to MLF
254254-mlf generate mlf -i com.example.thread.json -o ./lexicons/
255255-# Creates: lexicons/com/example/thread.mlf
256256-257257-# Convert multiple JSON lexicons
258258-mlf generate mlf -i lexicon1.json -i lexicon2.json -o ./mlf/
259259-260260-# Convert using glob pattern
261261-mlf generate mlf -i "dist/lexicons/**/*.json" -o ./src/
262262-```
263263-264264-**Features:**
265265-266266-- **Smart type conversion** - Automatically converts format strings (did, datetime, handle) to prelude types (Did, Datetime, Handle)
267267-- **Proper formatting** - Generates clean, properly indented MLF with correct syntax
268268-- **Reference conversion** - Converts `namespace#name` refs to `namespace.name` notation
269269-- **Complete coverage** - Supports records, queries, procedures, subscriptions, tokens, and type definitions
270270-271271-**Input format:**
272272-273273-The JSON lexicon must be a valid ATProto lexicon with:
274274-- `lexicon: 1` version field
275275-- `id` field containing the namespace
276276-- `defs` object with type definitions
277277-278278-**Example:**
279279-280280-Given a JSON lexicon:
281281-```json
282282-{
283283- "lexicon": 1,
284284- "id": "com.example.thread",
285285- "defs": {
286286- "main": {
287287- "type": "record",
288288- "key": "tid",
289289- "record": {
290290- "type": "object",
291291- "required": ["title", "createdAt"],
292292- "properties": {
293293- "title": {
294294- "type": "string",
295295- "maxLength": 200
296296- },
297297- "createdAt": {
298298- "type": "string",
299299- "format": "datetime"
300300- }
301301- }
302302- }
303303- }
304304- }
305305-}
306306-```
307307-308308-Generates MLF:
309309-```mlf
310310-record main {
311311- title!: string constrained {
312312- maxLength: 200,
313313- },
314314- createdAt!: Datetime,
315315-};
316316-```
317317-318318-**Use cases:**
319319-320320-- **Migration** - Convert existing JSON lexicons to MLF format
321321-- **Interoperability** - Work with lexicons from other tools or repositories
322322-- **Comparison** - Generate MLF from JSON to compare with hand-written MLF
323323-- **Learning** - See how JSON lexicons translate to MLF syntax
324324-325325----
326326-327327-## Error Messages
328328-329329-MLF provides rich error messages with:
330330-331331-- Source code context
332332-- Labeled spans showing exact error locations
333333-- Helpful suggestions for fixing errors
334334-- Error codes for categorization
335335-336336-**Example error:**
337337-338338-```
339339- × Undefined reference to 'ProfileView'
340340- ╭─[profile.mlf:5:12]
341341- 5 │ author: ProfileView,
342342- · ^^^^^^^^^^^ 'ProfileView' is not defined
343343- ╰────
344344- help: Make sure this type is defined in the same file or imported via 'use'.
345345-```
346346-347347-## Environment Variables
348348-349349-None currently used.
350350-351351-## Exit Codes
352352-353353-- `0` - Success
354354-- `1` - Error (syntax, validation, or runtime error)
+77
website/content/docs/cli/01-installation.md
···11++++
22+title = "Installation"
33+description = "How to install the MLF CLI"
44+weight = 1
55++++
66+77+## Building from Source
88+99+Clone and build from source:
1010+1111+```bash
1212+git clone https://tangled.org/@stavola.xyz/mlf
1313+cd mlf
1414+cargo build --release
1515+```
1616+1717+The binary will be at `target/release/mlf`.
1818+1919+## Installing with Cargo
2020+2121+### All Features (Recommended)
2222+2323+Install with all code generators enabled:
2424+2525+```bash
2626+cargo install --path mlf-cli --all-features
2727+```
2828+2929+This includes TypeScript, Go, Rust, and JSON lexicon generators.
3030+3131+### Selective Installation
3232+3333+Install only specific generators to reduce compilation time and binary size:
3434+3535+```bash
3636+# TypeScript only
3737+cargo install --path mlf-cli --no-default-features --features typescript
3838+3939+# TypeScript and Go
4040+cargo install --path mlf-cli --no-default-features --features typescript,go
4141+4242+# Minimal (JSON lexicon generation only)
4343+cargo install --path mlf-cli --no-default-features
4444+```
4545+4646+## Available Features
4747+4848+The CLI uses cargo features for optional code generators:
4949+5050+| Feature | Description | Output Format |
5151+|---------|-------------|---------------|
5252+| `typescript` | TypeScript type definitions and interfaces | `.ts` |
5353+| `go` | Go structs with JSON tags | `.go` |
5454+| `rust` | Rust structs with serde derive macros | `.rs` |
5555+| (built-in) | JSON lexicon generator | `.json` |
5656+5757+**Default features:** `["typescript", "go", "rust"]` - All generators enabled.
5858+5959+The JSON lexicon generator is always available (built into `mlf-codegen` core).
6060+6161+## Verify Installation
6262+6363+After installation, verify the CLI is working:
6464+6565+```bash
6666+mlf --version
6767+mlf --help
6868+```
6969+7070+## Next Steps
7171+7272+Once installed, you can:
7373+7474+1. [Configure your project](02-configuration.md) with an `mlf.toml` file
7575+2. [Check MLF files](03-check.md) for syntax and type errors
7676+3. [Generate code](05-generate.md) in your preferred language
7777+4. [Fetch remote lexicons](06-fetch.md) from ATProto repositories
+216
website/content/docs/cli/02-configuration.md
···11++++
22+title = "Configuration"
33+description = "Project configuration with mlf.toml"
44+weight = 2
55++++
66+77+MLF projects can be configured with an `mlf.toml` file that specifies source directories, output configurations, and dependencies. This allows you to run commands without repeatedly specifying arguments.
88+99+## Basic Structure
1010+1111+Create an `mlf.toml` file in your project root:
1212+1313+```toml
1414+[source]
1515+directory = "./lexicons"
1616+1717+[[output]]
1818+type = "lexicon"
1919+directory = "./dist/lexicons"
2020+2121+[[output]]
2222+type = "typescript"
2323+directory = "./src/types"
2424+2525+[dependencies]
2626+dependencies = ["stream.place", "app.bsky"]
2727+```
2828+2929+## Configuration Sections
3030+3131+### Source Directory
3232+3333+The `[source]` section specifies where your MLF files are located:
3434+3535+```toml
3636+[source]
3737+directory = "./lexicons"
3838+```
3939+4040+**Default:** `"./lexicons"`
4141+4242+This directory is used by:
4343+- `mlf check` (when run without arguments)
4444+- `mlf generate` (when run without arguments)
4545+4646+### Output Configurations
4747+4848+The `[[output]]` section defines code generation targets. You can have multiple output blocks:
4949+5050+```toml
5151+[[output]]
5252+type = "lexicon"
5353+directory = "./dist/lexicons"
5454+5555+[[output]]
5656+type = "typescript"
5757+directory = "./src/types"
5858+5959+[[output]]
6060+type = "go"
6161+directory = "./pkg/lexicons"
6262+6363+[[output]]
6464+type = "rust"
6565+directory = "./src/lexicons"
6666+```
6767+6868+**Supported types:**
6969+- `"lexicon"` - Generate JSON lexicons
7070+- `"typescript"` - Generate TypeScript types
7171+- `"go"` - Generate Go structs
7272+- `"rust"` - Generate Rust structs
7373+7474+When you run `mlf generate` without arguments, it will generate all configured outputs.
7575+7676+### Dependencies
7777+7878+The `[dependencies]` section lists external lexicon namespaces to fetch:
7979+8080+```toml
8181+[dependencies]
8282+dependencies = [
8383+ "stream.place",
8484+ "app.bsky",
8585+ "com.atproto"
8686+]
8787+```
8888+8989+These dependencies are fetched when you run `mlf fetch` without arguments.
9090+9191+## Commands Using Configuration
9292+9393+### `mlf check`
9494+9595+When run without arguments, checks all files in the source directory:
9696+9797+```bash
9898+mlf check
9999+# Equivalent to: mlf check "./lexicons/**/*.mlf"
100100+```
101101+102102+### `mlf generate`
103103+104104+When run without a subcommand, generates all configured outputs:
105105+106106+```bash
107107+mlf generate
108108+# Runs all [[output]] configurations
109109+```
110110+111111+### `mlf fetch`
112112+113113+When run without arguments, fetches all dependencies:
114114+115115+```bash
116116+mlf fetch
117117+# Fetches: stream.place, app.bsky, com.atproto
118118+```
119119+120120+Add `--save` flag to automatically update `mlf.toml`:
121121+122122+```bash
123123+mlf fetch stream.place --save
124124+# Downloads lexicons AND adds to dependencies array
125125+```
126126+127127+## Complete Example
128128+129129+Here's a complete `mlf.toml` for a TypeScript project using ATProto lexicons:
130130+131131+```toml
132132+[source]
133133+directory = "./lexicons"
134134+135135+[[output]]
136136+type = "lexicon"
137137+directory = "./dist/lexicons"
138138+139139+[[output]]
140140+type = "typescript"
141141+directory = "./src/types"
142142+143143+[dependencies]
144144+dependencies = [
145145+ "app.bsky",
146146+ "com.atproto",
147147+ "chat.bsky"
148148+]
149149+```
150150+151151+### Workflow
152152+153153+1. **Fetch dependencies:**
154154+ ```bash
155155+ mlf fetch
156156+ ```
157157+ Downloads `app.bsky`, `com.atproto`, and `chat.bsky` lexicons to `.mlf/lexicons/`
158158+159159+2. **Write your lexicons:**
160160+ Create `.mlf` files in `./lexicons/` that reference the fetched lexicons
161161+162162+3. **Check for errors:**
163163+ ```bash
164164+ mlf check
165165+ ```
166166+ Validates all MLF files in `./lexicons/`
167167+168168+4. **Generate outputs:**
169169+ ```bash
170170+ mlf generate
171171+ ```
172172+ Creates:
173173+ - JSON lexicons in `./dist/lexicons/`
174174+ - TypeScript types in `./src/types/`
175175+176176+## Fetched Lexicons Cache
177177+178178+When you fetch lexicons, they're stored in `.mlf/lexicons/`:
179179+180180+```
181181+.mlf/
182182+├── .gitignore # Automatically created
183183+├── .lexicon-cache.toml # Metadata about fetched lexicons
184184+└── lexicons/
185185+ ├── json/ # Original JSON lexicons
186186+ │ ├── app.bsky.actor.profile.json
187187+ │ └── ...
188188+ └── mlf/ # Converted MLF format
189189+ ├── app.bsky.actor.profile.mlf
190190+ └── ...
191191+```
192192+193193+The `.mlf` directory is automatically added to `.gitignore`, so fetched lexicons won't be committed to your repository.
194194+195195+## Best Practices
196196+197197+1. **Commit `mlf.toml`** - Version control your configuration
198198+2. **Don't commit `.mlf/`** - Let each developer fetch dependencies
199199+3. **Use semantic namespaces** - Organize lexicons by domain
200200+4. **Multiple outputs** - Generate both lexicons and code simultaneously
201201+5. **CI/CD integration** - Run `mlf check` in your CI pipeline
202202+203203+## Override Configuration
204204+205205+You can always override configuration with explicit arguments:
206206+207207+```bash
208208+# Override source directory
209209+mlf check "./other-lexicons/**/*.mlf"
210210+211211+# Override output
212212+mlf generate lexicon -i custom.mlf -o ./custom-output/
213213+214214+# Fetch specific namespace (ignoring dependencies list)
215215+mlf fetch stream.place
216216+```
+191
website/content/docs/cli/03-check.md
···11++++
22+title = "Check Command"
33+description = "Validate MLF lexicon files"
44+weight = 3
55++++
66+77+The `mlf check` command validates MLF lexicon files for syntax and type errors.
88+99+## Usage
1010+1111+```bash
1212+mlf check [INPUT]...
1313+```
1414+1515+**Arguments:**
1616+- `[INPUT]...` - MLF lexicon file(s) to validate (glob patterns supported)
1717+1818+If no input files are provided, `mlf check` will use the source directory from your `mlf.toml` configuration.
1919+2020+## Examples
2121+2222+### Check with Configuration
2323+2424+If you have an `mlf.toml` file:
2525+2626+```toml
2727+[source]
2828+directory = "./lexicons"
2929+```
3030+3131+Simply run:
3232+3333+```bash
3434+mlf check
3535+```
3636+3737+This automatically checks all `.mlf` files in `./lexicons/`.
3838+3939+### Check Specific Files
4040+4141+```bash
4242+# Check a single file
4343+mlf check thread.mlf
4444+4545+# Check multiple files
4646+mlf check thread.mlf profile.mlf reply.mlf
4747+```
4848+4949+### Check with Glob Patterns
5050+5151+```bash
5252+# Check all MLF files in a directory
5353+mlf check "lexicons/**/*.mlf"
5454+5555+# Check files matching a pattern
5656+mlf check "src/*/schema.mlf"
5757+5858+# Check all MLF files recursively
5959+mlf check "**/*.mlf"
6060+```
6161+6262+## Validation Checks
6363+6464+The check command performs comprehensive validation:
6565+6666+### 1. **Syntax Validation**
6767+- Correct MLF syntax
6868+- Proper use of keywords
6969+- Valid identifiers and namespaces
7070+- Correct constraint syntax
7171+7272+### 2. **Type Validation**
7373+- All referenced types exist
7474+- Type constraints are valid
7575+- Required fields are properly marked
7676+- Array and object structures are correct
7777+7878+### 3. **Semantic Validation**
7979+- No duplicate definitions
8080+- Valid record keys
8181+- Proper XRPC method signatures
8282+- Correct union type usage
8383+8484+### 4. **Cross-Reference Validation**
8585+- External references resolve correctly
8686+- Import statements are valid
8787+- Prelude types are accessible
8888+- Fetched dependencies are available
8989+9090+## Output
9191+9292+### Success
9393+9494+When all files are valid:
9595+9696+```
9797+✓ thread.mlf: Parsed successfully
9898+✓ profile.mlf: Parsed successfully
9999+✓ reply.mlf: Parsed successfully
100100+101101+✓ All lexicons are valid
102102+```
103103+104104+### Errors
105105+106106+When errors are found, detailed diagnostics are shown:
107107+108108+```
109109+ × Undefined reference to 'ProfileView'
110110+ ╭─[profile.mlf:5:12]
111111+ 5 │ author: ProfileView,
112112+ · ^^^^^^^^^^^ 'ProfileView' is not defined
113113+ ╰────
114114+ help: Make sure this type is defined in the same file or imported via 'use'.
115115+```
116116+117117+Each error includes:
118118+- Error message with context
119119+- Source code snippet
120120+- Exact location (line and column)
121121+- Helpful suggestions for fixing
122122+123123+## Working with Dependencies
124124+125125+If your lexicons reference external types (e.g., from `app.bsky` or `com.atproto`), make sure to fetch them first:
126126+127127+```bash
128128+# Fetch dependencies
129129+mlf fetch
130130+131131+# Then check your lexicons
132132+mlf check
133133+```
134134+135135+The check command automatically loads lexicons from `.mlf/lexicons/mlf/` if they exist.
136136+137137+## Exit Codes
138138+139139+- `0` - All files are valid
140140+- `1` - Validation errors found
141141+142142+## Common Issues
143143+144144+### Undefined Reference
145145+146146+```
147147+× Undefined reference to 'SomeType'
148148+```
149149+150150+**Solution:**
151151+- Define `SomeType` in the same file, or
152152+- Fetch the lexicon containing `SomeType` using `mlf fetch`, or
153153+- Add an import statement if needed
154154+155155+### Parse Error
156156+157157+```
158158+× Expected 'constrained', found 'contsrained'
159159+```
160160+161161+**Solution:** Fix the typo in your MLF file
162162+163163+### Type Mismatch
164164+165165+```
166166+× Field 'count' expects integer, found string
167167+```
168168+169169+**Solution:** Correct the field type to match the schema
170170+171171+## Integration with CI/CD
172172+173173+Use `mlf check` in your continuous integration pipeline:
174174+175175+```yaml
176176+# GitHub Actions example
177177+- name: Validate MLF Lexicons
178178+ run: |
179179+ mlf fetch
180180+ mlf check
181181+```
182182+183183+This ensures all lexicons remain valid as your project evolves.
184184+185185+## Tips
186186+187187+1. **Use configuration** - Set up `mlf.toml` to avoid typing paths repeatedly
188188+2. **Check often** - Run `mlf check` frequently during development
189189+3. **Version control** - Commit valid lexicons only
190190+4. **Fetch first** - Always fetch dependencies before checking
191191+5. **Read errors carefully** - MLF provides detailed error messages with helpful suggestions
+211
website/content/docs/cli/04-validate.md
···11++++
22+title = "Validate Command"
33+description = "Validate JSON records against lexicons"
44+weight = 4
55++++
66+77+The `mlf validate` command validates JSON record data against an MLF lexicon schema.
88+99+## Usage
1010+1111+```bash
1212+mlf validate <LEXICON> <RECORD>
1313+```
1414+1515+**Arguments:**
1616+- `<LEXICON>` - MLF lexicon file defining the schema
1717+- `<RECORD>` - JSON file containing the record to validate
1818+1919+## Example
2020+2121+Given a lexicon file `thread.mlf`:
2222+2323+```mlf
2424+record main {
2525+ title!: string constrained {
2626+ maxLength: 200,
2727+ },
2828+ createdAt!: Datetime,
2929+ posts: Post[],
3030+};
3131+3232+def Post = {
3333+ text!: string,
3434+ createdAt!: Datetime,
3535+};
3636+```
3737+3838+And a JSON record file `my-thread.json`:
3939+4040+```json
4141+{
4242+ "title": "My Discussion Thread",
4343+ "createdAt": "2024-01-15T10:30:00Z",
4444+ "posts": [
4545+ {
4646+ "text": "First post!",
4747+ "createdAt": "2024-01-15T10:30:00Z"
4848+ }
4949+ ]
5050+}
5151+```
5252+5353+Validate the record:
5454+5555+```bash
5656+mlf validate thread.mlf my-thread.json
5757+```
5858+5959+## Output
6060+6161+### Valid Record
6262+6363+```
6464+✓ Lexicon parsed successfully
6565+✓ JSON record parsed successfully
6666+✓ Record is valid according to the lexicon schema
6767+```
6868+6969+### Invalid Record
7070+7171+If the record doesn't match the schema:
7272+7373+```
7474+✗ Record validation failed with 2 error(s):
7575+ • Field 'title' is required but missing
7676+ • Field 'createdAt': expected string in datetime format, found "not-a-date"
7777+```
7878+7979+## Validation Rules
8080+8181+The validate command checks:
8282+8383+### 1. **Required Fields**
8484+- All fields marked with `!` must be present
8585+- Optional fields can be omitted
8686+8787+### 2. **Type Matching**
8888+- String fields must be strings
8989+- Integer fields must be numbers
9090+- Arrays must be arrays
9191+- Objects must have correct structure
9292+9393+### 3. **Constraints**
9494+- String length constraints (`maxLength`, `minLength`)
9595+- String grapheme constraints (`maxGraphemes`, `minGraphemes`)
9696+- Integer range constraints (`minimum`, `maximum`)
9797+- Array length constraints
9898+- Format validation (`datetime`, `uri`, `at-uri`, etc.)
9999+100100+### 4. **Nested Structures**
101101+- Object fields are recursively validated
102102+- Array items match their item schema
103103+- References to other defs are resolved
104104+105105+## Use Cases
106106+107107+### 1. **Test Data Validation**
108108+109109+Validate test fixtures before using them:
110110+111111+```bash
112112+mlf validate profile.mlf test-data/profile-1.json
113113+```
114114+115115+### 2. **API Response Validation**
116116+117117+Check that API responses match your schema:
118118+119119+```bash
120120+curl https://api.example.com/profile > response.json
121121+mlf validate profile.mlf response.json
122122+```
123123+124124+### 3. **Schema Migration**
125125+126126+When updating schemas, validate existing records against the new schema:
127127+128128+```bash
129129+# Validate all records
130130+for file in records/*.json; do
131131+ mlf validate schema.mlf "$file" || echo "Failed: $file"
132132+done
133133+```
134134+135135+### 4. **Development Workflow**
136136+137137+Validate records during development:
138138+139139+```bash
140140+# Write some test data
141141+echo '{"title": "Test", "createdAt": "2024-01-15T10:30:00Z"}' > test.json
142142+143143+# Validate it
144144+mlf validate thread.mlf test.json
145145+```
146146+147147+## Common Validation Errors
148148+149149+### Missing Required Field
150150+151151+```
152152+✗ Field 'title' is required but missing
153153+```
154154+155155+**Solution:** Add the required field to your JSON
156156+157157+### Type Mismatch
158158+159159+```
160160+✗ Field 'count': expected integer, found "123"
161161+```
162162+163163+**Solution:** Use a number instead of a string: `"count": 123`
164164+165165+### Constraint Violation
166166+167167+```
168168+✗ Field 'title': string length 250 exceeds maxLength 200
169169+```
170170+171171+**Solution:** Shorten the string to meet the constraint
172172+173173+### Invalid Format
174174+175175+```
176176+✗ Field 'createdAt': expected datetime format, found "2024-01-15"
177177+```
178178+179179+**Solution:** Use full ISO 8601 datetime format: `"2024-01-15T10:30:00Z"`
180180+181181+### Invalid Array Item
182182+183183+```
184184+✗ Array item at index 0: Field 'text' is required but missing
185185+```
186186+187187+**Solution:** Ensure all array items match the schema
188188+189189+## Exit Codes
190190+191191+- `0` - Record is valid
192192+- `1` - Validation failed or error occurred
193193+194194+## Limitations
195195+196196+The validate command currently validates against the lexicon structure but does not:
197197+198198+- Validate CID references
199199+- Validate blob content types
200200+- Check external references
201201+- Verify cryptographic signatures
202202+203203+These checks would typically be performed by the PDS or other ATProto infrastructure.
204204+205205+## Tips
206206+207207+1. **Start simple** - Validate basic records first, then add complexity
208208+2. **Use test data** - Create JSON fixtures for your schemas
209209+3. **Automate validation** - Add validation to your test suite
210210+4. **Check constraints** - Pay attention to string lengths and ranges
211211+5. **Format matters** - Use proper datetime/URI formats as specified by ATProto
+355
website/content/docs/cli/05-generate.md
···11++++
22+title = "Generate Commands"
33+description = "Generate code and lexicons from MLF"
44+weight = 5
55++++
66+77+The `mlf generate` command converts MLF files to various output formats including JSON lexicons and code in multiple programming languages.
88+99+## Overview
1010+1111+```bash
1212+# Generate all configured outputs
1313+mlf generate
1414+1515+# Generate JSON lexicons
1616+mlf generate lexicon -i <INPUT> -o <OUTPUT>
1717+1818+# Generate code in a specific language
1919+mlf generate code -g <GENERATOR> -i <INPUT> -o <OUTPUT>
2020+2121+# Convert JSON lexicons to MLF
2222+mlf generate mlf -i <INPUT> -o <OUTPUT>
2323+```
2424+2525+## Generate All Outputs
2626+2727+When run without a subcommand, `mlf generate` uses your `mlf.toml` configuration to generate all specified outputs:
2828+2929+```toml
3030+[[output]]
3131+type = "lexicon"
3232+directory = "./dist/lexicons"
3333+3434+[[output]]
3535+type = "typescript"
3636+directory = "./src/types"
3737+```
3838+3939+```bash
4040+mlf generate
4141+```
4242+4343+This will:
4444+1. Read the source directory from configuration
4545+2. Process all `.mlf` files
4646+3. Generate each configured output type
4747+4848+**Output:**
4949+```
5050+Running 2 output configuration(s)...
5151+5252+Generating lexicon output to ./dist/lexicons...
5353+Generated: ./dist/lexicons/com/example/thread.json
5454+ ✓ Generated lexicon output successfully
5555+5656+Generating typescript output to ./src/types...
5757+Generated: ./src/types/com/example/thread.ts
5858+ ✓ Generated typescript output successfully
5959+6060+✓ Successfully generated all 2 output(s)
6161+```
6262+6363+---
6464+6565+## Generate Lexicon (JSON)
6666+6767+Generate ATProto JSON lexicons from MLF files.
6868+6969+```bash
7070+mlf generate lexicon -i <INPUT> -o <OUTPUT> [OPTIONS]
7171+```
7272+7373+**Options:**
7474+- `-i, --input <INPUT>` - Input MLF files (glob patterns supported, can be specified multiple times)
7575+- `-o, --output <OUTPUT>` - Output directory (required)
7676+- `--flat` - Use flat file structure (e.g., `com.example.thread.json`)
7777+7878+**Examples:**
7979+8080+```bash
8181+# Generate with folder structure
8282+mlf generate lexicon -i thread.mlf -o lexicons/
8383+# Creates: lexicons/com/example/thread.json
8484+8585+# Generate with flat structure
8686+mlf generate lexicon -i thread.mlf -o lexicons/ --flat
8787+# Creates: lexicons/com.example.thread.json
8888+8989+# Generate from multiple files
9090+mlf generate lexicon -i thread.mlf -i reply.mlf -o lexicons/
9191+9292+# Generate from glob pattern
9393+mlf generate lexicon -i "src/**/*.mlf" -o dist/lexicons/
9494+```
9595+9696+---
9797+9898+## Generate Code
9999+100100+Generate code in various programming languages from MLF files.
101101+102102+```bash
103103+mlf generate code -g <GENERATOR> -i <INPUT> -o <OUTPUT> [OPTIONS]
104104+```
105105+106106+**Options:**
107107+- `-g, --generator <GENERATOR>` - Generator to use (required): `json`, `typescript`, `go`, or `rust`
108108+- `-i, --input <INPUT>` - Input MLF files (glob patterns supported, can be specified multiple times)
109109+- `-o, --output <OUTPUT>` - Output directory (required)
110110+- `--flat` - Use flat file structure
111111+112112+**Available Generators:**
113113+114114+| Generator | Output | Features |
115115+|-----------|--------|----------|
116116+| `json` | `.json` | AT Protocol JSON lexicons (always available) |
117117+| `typescript` | `.ts` | TypeScript interfaces with JSDoc, optional fields with `?` |
118118+| `go` | `.go` | Go structs with JSON tags, proper capitalization |
119119+| `rust` | `.rs` | Rust structs with serde, `Option<T>` for optional fields |
120120+121121+### TypeScript Example
122122+123123+```bash
124124+mlf generate code -g typescript -i thread.mlf -o src/types/
125125+```
126126+127127+**Input MLF:**
128128+```mlf
129129+/// A discussion thread
130130+record main {
131131+ /// Thread title
132132+ title!: string constrained {
133133+ maxLength: 200,
134134+ },
135135+ /// Creation timestamp
136136+ createdAt!: Datetime,
137137+ posts: Post[],
138138+};
139139+```
140140+141141+**Generated TypeScript:**
142142+```typescript
143143+/**
144144+ * Generated from com.example.thread
145145+ * Do not edit manually
146146+ */
147147+148148+/**
149149+ * A discussion thread
150150+ */
151151+export interface Main {
152152+ /** Thread title */
153153+ title: string;
154154+ /** Creation timestamp */
155155+ createdAt: string;
156156+ posts?: Post[];
157157+}
158158+```
159159+160160+### Go Example
161161+162162+```bash
163163+mlf generate code -g go -i thread.mlf -o pkg/models/
164164+```
165165+166166+**Generated Go:**
167167+```go
168168+// Generated from com.example.thread
169169+// Do not edit manually
170170+171171+package thread
172172+173173+// Main represents a discussion thread
174174+type Main struct {
175175+ // Thread title
176176+ Title string `json:"title"`
177177+ // Creation timestamp
178178+ CreatedAt string `json:"createdAt"`
179179+ // Posts (optional)
180180+ Posts []Post `json:"posts,omitempty"`
181181+}
182182+```
183183+184184+### Rust Example
185185+186186+```bash
187187+mlf generate code -g rust -i thread.mlf -o src/models/
188188+```
189189+190190+**Generated Rust:**
191191+```rust
192192+// Generated from com.example.thread
193193+// Do not edit manually
194194+195195+use serde::{Deserialize, Serialize};
196196+197197+/// A discussion thread
198198+#[derive(Debug, Clone, Serialize, Deserialize)]
199199+pub struct Main {
200200+ /// Thread title
201201+ pub title: String,
202202+ /// Creation timestamp
203203+ #[serde(rename = "createdAt")]
204204+ pub created_at: String,
205205+ /// Posts (optional)
206206+ #[serde(skip_serializing_if = "Option::is_none")]
207207+ pub posts: Option<Vec<Post>>,
208208+}
209209+```
210210+211211+---
212212+213213+## Generate MLF
214214+215215+Convert ATProto JSON lexicons back to MLF format.
216216+217217+```bash
218218+mlf generate mlf -i <INPUT> -o <OUTPUT>
219219+```
220220+221221+**Options:**
222222+- `-i, --input <INPUT>` - Input JSON lexicon files (glob patterns supported, can be specified multiple times)
223223+- `-o, --output <OUTPUT>` - Output directory (required)
224224+225225+**Examples:**
226226+227227+```bash
228228+# Convert a single JSON lexicon to MLF
229229+mlf generate mlf -i com.example.thread.json -o ./lexicons/
230230+# Creates: lexicons/com/example/thread.mlf
231231+232232+# Convert multiple JSON lexicons
233233+mlf generate mlf -i lexicon1.json -i lexicon2.json -o ./mlf/
234234+235235+# Convert using glob pattern
236236+mlf generate mlf -i "dist/lexicons/**/*.json" -o ./src/
237237+```
238238+239239+**Features:**
240240+241241+- **Smart type conversion** - Automatically converts format strings to prelude types
242242+ - `"format": "did"` → `Did`
243243+ - `"format": "datetime"` → `Datetime`
244244+ - `"format": "handle"` → `Handle`
245245+- **Proper formatting** - Generates clean, properly indented MLF
246246+- **Reference conversion** - Converts `namespace#name` to `namespace.name`
247247+- **Complete coverage** - Supports all ATProto lexicon types
248248+249249+**Input JSON:**
250250+```json
251251+{
252252+ "lexicon": 1,
253253+ "id": "com.example.thread",
254254+ "defs": {
255255+ "main": {
256256+ "type": "record",
257257+ "record": {
258258+ "type": "object",
259259+ "required": ["title", "createdAt"],
260260+ "properties": {
261261+ "title": {
262262+ "type": "string",
263263+ "maxLength": 200
264264+ },
265265+ "createdAt": {
266266+ "type": "string",
267267+ "format": "datetime"
268268+ }
269269+ }
270270+ }
271271+ }
272272+ }
273273+}
274274+```
275275+276276+**Generated MLF:**
277277+```mlf
278278+record main {
279279+ title!: string constrained {
280280+ maxLength: 200,
281281+ },
282282+ createdAt!: Datetime,
283283+};
284284+```
285285+286286+---
287287+288288+## Code Generator Features
289289+290290+### Documentation Comments
291291+292292+All generators preserve documentation comments from MLF:
293293+294294+```mlf
295295+/// This is a user profile
296296+def Profile = {
297297+ /// The user's display name
298298+ displayName: string,
299299+};
300300+```
301301+302302+Generates appropriate doc comments for each language (JSDoc, Go comments, Rust doc comments).
303303+304304+### Optional Fields
305305+306306+Optional fields are handled idiomatically in each language:
307307+308308+- **TypeScript**: `field?: Type`
309309+- **Go**: `Field *Type` or `json:",omitempty"`
310310+- **Rust**: `field: Option<Type>` with `#[serde(skip_serializing_if = "Option::is_none")]`
311311+312312+### Field Naming Conventions
313313+314314+Each generator follows language conventions:
315315+316316+- **TypeScript**: camelCase (matches JSON)
317317+- **Go**: PascalCase with JSON tags
318318+- **Rust**: snake_case with `#[serde(rename)]` attributes
319319+320320+### Type Mapping
321321+322322+MLF types are mapped to appropriate language types:
323323+324324+| MLF Type | TypeScript | Go | Rust |
325325+|----------|------------|-------|------|
326326+| `string` | `string` | `string` | `String` |
327327+| `integer` | `number` | `int64` | `i64` |
328328+| `boolean` | `boolean` | `bool` | `bool` |
329329+| `bytes` | `Uint8Array` | `[]byte` | `Vec<u8>` |
330330+| `array` | `T[]` | `[]T` | `Vec<T>` |
331331+| `Did` | `string` | `string` | `String` |
332332+| `Datetime` | `string` | `string` | `String` |
333333+334334+---
335335+336336+## Tips
337337+338338+1. **Use configuration** - Set up `mlf.toml` for multi-output generation
339339+2. **Commit generated code** - If it's part of your build artifacts
340340+3. **Regenerate often** - Run `mlf generate` after any lexicon changes
341341+4. **Use flat mode** - For simpler directory structures
342342+5. **Multiple generators** - Generate multiple languages from the same MLF files
343343+6. **Version control** - Track both MLF source and generated code
344344+345345+## Error Handling
346346+347347+If generation fails for some files, you'll see detailed errors:
348348+349349+```
350350+3 file(s) generated successfully, 1 error(s) encountered:
351351+352352+ thread.mlf - Type resolution error: undefined reference to 'Post'
353353+```
354354+355355+The command exits with status `1` if any errors occur.
+307
website/content/docs/cli/06-fetch.md
···11++++
22+title = "Fetch Command"
33+description = "Download lexicons from remote repositories"
44+weight = 6
55++++
66+77+The `mlf fetch` command downloads ATProto lexicons from remote repositories and converts them to MLF format.
88+99+## Usage
1010+1111+```bash
1212+# Fetch all dependencies from mlf.toml
1313+mlf fetch
1414+1515+# Fetch a specific namespace
1616+mlf fetch <NAMESPACE>
1717+1818+# Fetch and save to dependencies
1919+mlf fetch <NAMESPACE> --save
2020+```
2121+2222+**Arguments:**
2323+- `[NAMESPACE]` - Optional namespace to fetch (e.g., `stream.place`, `app.bsky`)
2424+2525+**Options:**
2626+- `--save` - Add the namespace to dependencies in `mlf.toml`
2727+2828+## How It Works
2929+3030+The fetch command follows the ATProto lexicon discovery protocol:
3131+3232+1. **DNS Lookup** - Queries `_lexicon.<reversed-authority>` TXT record
3333+2. **DID Resolution** - Resolves the DID to a PDS endpoint
3434+3. **Fetch Records** - Queries `com.atproto.repo.listRecords` for lexicon schemas
3535+4. **Save & Convert** - Saves JSON and converts to MLF format
3636+3737+## Examples
3838+3939+### Fetch All Dependencies
4040+4141+With an `mlf.toml` file:
4242+4343+```toml
4444+[dependencies]
4545+dependencies = ["stream.place", "app.bsky"]
4646+```
4747+4848+Run:
4949+5050+```bash
5151+mlf fetch
5252+```
5353+5454+**Output:**
5555+```
5656+Fetching 2 dependencies...
5757+5858+Fetching: stream.place
5959+Fetching lexicons for authority: stream.place
6060+ → Resolved DID: did:web:stream.place
6161+ → Using PDS: https://stream.place
6262+ → Found 5 lexicon record(s)
6363+ Processing: stream.place.thread
6464+ → Saved JSON to .mlf/lexicons/json/stream.place.thread.json
6565+ → Converted to MLF at .mlf/lexicons/mlf/stream.place.thread.mlf
6666+✓ Successfully fetched lexicons for stream.place
6767+6868+Fetching: app.bsky
6969+...
7070+7171+✓ Successfully fetched all 2 dependencies
7272+```
7373+7474+### Fetch Specific Namespace
7575+7676+```bash
7777+mlf fetch stream.place
7878+```
7979+8080+This downloads all lexicons under the `stream.place` authority.
8181+8282+### Fetch and Save
8383+8484+```bash
8585+mlf fetch stream.place --save
8686+```
8787+8888+This:
8989+1. Downloads the lexicons
9090+2. Adds `"stream.place"` to the dependencies array in `mlf.toml`
9191+3. Creates `mlf.toml` if it doesn't exist
9292+9393+## Storage Structure
9494+9595+Fetched lexicons are stored in `.mlf/lexicons/`:
9696+9797+```
9898+.mlf/
9999+├── .gitignore # Auto-generated
100100+├── .lexicon-cache.toml # Cache metadata
101101+└── lexicons/
102102+ ├── json/ # Original JSON lexicons
103103+ │ ├── stream.place.thread.json
104104+ │ └── app.bsky.actor.profile.json
105105+ └── mlf/ # Converted MLF format
106106+ ├── stream.place.thread.mlf
107107+ └── app.bsky.actor.profile.mlf
108108+```
109109+110110+### Cache File
111111+112112+The `.lexicon-cache.toml` tracks what's been fetched:
113113+114114+```toml
115115+[[lexicons.stream.place.thread]]
116116+nsid = "stream.place.thread"
117117+fetched_at = "2024-01-15T10:30:00Z"
118118+did = "did:web:stream.place"
119119+```
120120+121121+## DNS Resolution
122122+123123+For a namespace like `stream.place.thread`:
124124+125125+1. Extract authority: `stream.place`
126126+2. Reverse for DNS: `place.stream`
127127+3. Query TXT record: `_lexicon.place.stream`
128128+4. Parse `did=did:web:...` or `did=did:plc:...`
129129+130130+**Example DNS record:**
131131+```
132132+_lexicon.place.stream. 300 IN TXT "did=did:web:stream.place"
133133+```
134134+135135+## DID Resolution
136136+137137+### did:web
138138+139139+For `did:web:stream.place`, the PDS is `https://stream.place`
140140+141141+### did:plc
142142+143143+For `did:plc:abc123...`, query `https://plc.directory/did:plc:abc123...` to get the PDS endpoint from the DID document.
144144+145145+## Fetched Lexicons in Your Code
146146+147147+Once fetched, lexicons in `.mlf/lexicons/mlf/` are automatically available for:
148148+149149+### Type References
150150+151151+```mlf
152152+use stream.place.thread;
153153+154154+def Reply = {
155155+ thread!: stream.place.thread,
156156+ text!: string,
157157+};
158158+```
159159+160160+### Code Generation
161161+162162+```bash
163163+mlf generate code -g typescript -i my-lexicon.mlf -o src/types/
164164+```
165165+166166+The generator can resolve references to fetched lexicons.
167167+168168+### Validation
169169+170170+```bash
171171+mlf check my-lexicon.mlf
172172+```
173173+174174+The check command loads fetched lexicons for type resolution.
175175+176176+## Working Without mlf.toml
177177+178178+If you don't have an `mlf.toml`, the fetch command will offer to create one:
179179+180180+```bash
181181+$ mlf fetch stream.place
182182+No mlf.toml found in current or parent directories.
183183+Would you like to create one in the current directory? (y/n)
184184+y
185185+Created mlf.toml in /path/to/current/dir
186186+...
187187+```
188188+189189+## Re-fetching
190190+191191+If a namespace is already cached, fetch skips it:
192192+193193+```bash
194194+$ mlf fetch stream.place
195195+Lexicon 'stream.place.thread' is already cached. Skipping fetch.
196196+ (Use --force to re-fetch)
197197+```
198198+199199+To re-fetch:
200200+201201+```bash
202202+mlf fetch stream.place --force # Not yet implemented
203203+```
204204+205205+## Error Handling
206206+207207+### DNS Errors
208208+209209+```
210210+✗ DNS lookup failed: No TXT record found for _lexicon.place.stream
211211+```
212212+213213+**Causes:**
214214+- Domain doesn't have a lexicon TXT record
215215+- DNS propagation delay
216216+- Network issues
217217+218218+### DID Resolution Errors
219219+220220+```
221221+✗ Failed to resolve DID: No PDS endpoint found in DID document
222222+```
223223+224224+**Causes:**
225225+- Invalid DID format
226226+- PLC directory unreachable
227227+- DID document missing PDS service
228228+229229+### No Records Found
230230+231231+```
232232+✗ No lexicon records found for stream.place
233233+```
234234+235235+**Causes:**
236236+- Namespace exists but has no published lexicons
237237+- Wrong namespace (typo)
238238+- PDS doesn't support lexicon publishing
239239+240240+## Best Practices
241241+242242+1. **Fetch before work** - Always fetch dependencies before coding
243243+2. **Use --save** - Keep `mlf.toml` up to date with dependencies
244244+3. **Don't commit `.mlf/`** - Let each developer fetch independently
245245+4. **Check DNS** - Verify TXT records before fetching
246246+5. **Version dependencies** - Consider tracking lexicon versions (future feature)
247247+248248+## CI/CD Integration
249249+250250+In your CI pipeline:
251251+252252+```yaml
253253+# GitHub Actions example
254254+- name: Fetch ATProto Lexicons
255255+ run: |
256256+ mlf fetch
257257+258258+- name: Generate Code
259259+ run: |
260260+ mlf generate
261261+```
262262+263263+This ensures builds have access to the latest lexicons.
264264+265265+## Comparison with npm/cargo
266266+267267+The fetch command is similar to package managers:
268268+269269+| Command | npm | cargo | mlf |
270270+|---------|-----|-------|-----|
271271+| Install deps | `npm install` | `cargo fetch` | `mlf fetch` |
272272+| Add dep | `npm install pkg --save` | `cargo add pkg` | `mlf fetch ns --save` |
273273+| Config file | `package.json` | `Cargo.toml` | `mlf.toml` |
274274+| Cache | `node_modules/` | `~/.cargo/` | `.mlf/` |
275275+276276+## Troubleshooting
277277+278278+### Network Issues
279279+280280+```bash
281281+# Check DNS resolution
282282+dig TXT _lexicon.place.stream
283283+284284+# Test DID resolution
285285+curl https://plc.directory/did:plc:abc123
286286+```
287287+288288+### Invalid Namespace
289289+290290+Make sure you're using the correct namespace format:
291291+- ✓ `stream.place`
292292+- ✓ `app.bsky`
293293+- ✗ `stream.place.thread` (too specific)
294294+295295+### Permission Errors
296296+297297+Ensure you have write permissions for the project directory to create `.mlf/`.
298298+299299+## Future Features
300300+301301+Planned enhancements:
302302+303303+- `--force` flag to re-fetch cached lexicons
304304+- Version pinning (fetch specific lexicon versions)
305305+- Private/authenticated repositories
306306+- Offline mode with local cache
307307+- Fetch from local directories
+335
website/content/docs/cli/07-errors.md
···11++++
22+title = "Error Messages"
33+description = "Understanding MLF error diagnostics"
44+weight = 7
55++++
66+77+MLF provides rich, helpful error messages with source code context and suggestions for fixing issues.
88+99+## Error Format
1010+1111+All MLF errors follow this format:
1212+1313+```
1414+ × Error title
1515+ ╭─[file.mlf:5:12]
1616+ 5 │ author: ProfileView,
1717+ · ^^^^^^^^^^^ error message
1818+ ╰────
1919+ help: Suggestion for fixing the issue
2020+```
2121+2222+**Components:**
2323+- **× Error title** - Brief description of the error
2424+- **Source location** - File name, line, and column
2525+- **Source context** - The relevant code snippet
2626+- **Error span** - Highlighted location with `^^^`
2727+- **help:** - Actionable suggestion for fixing
2828+2929+## Common Errors
3030+3131+### Parse Errors
3232+3333+#### Expected Token
3434+3535+```
3636+ × Expected '}', found ','
3737+ ╭─[thread.mlf:10:5]
3838+10 │ title: string,
3939+ · ^ expected '}'
4040+ ╰────
4141+ help: Check for missing closing braces or extra commas
4242+```
4343+4444+**Cause:** Syntax error in MLF code
4545+4646+**Fix:** Correct the syntax according to MLF grammar
4747+4848+#### Invalid Identifier
4949+5050+```
5151+ × Invalid identifier: '123invalid'
5252+ ╭─[thread.mlf:5:5]
5353+ 5 │ 123invalid: string,
5454+ · ^^^^^^^^^^ identifiers cannot start with numbers
5555+ ╰────
5656+ help: Identifiers must start with a letter or underscore
5757+```
5858+5959+**Cause:** Identifier doesn't follow naming rules
6060+6161+**Fix:** Start identifiers with letters or underscores
6262+6363+### Type Errors
6464+6565+#### Undefined Reference
6666+6767+```
6868+ × Undefined reference to 'ProfileView'
6969+ ╭─[thread.mlf:8:12]
7070+ 8 │ author: ProfileView,
7171+ · ^^^^^^^^^^^ 'ProfileView' is not defined
7272+ ╰────
7373+ help: Make sure this type is defined in the same file or imported via 'use'.
7474+```
7575+7676+**Causes:**
7777+- Type is not defined
7878+- Type is in another file and not imported
7979+- Typo in type name
8080+8181+**Fixes:**
8282+- Define the type in the same file
8383+- Use `mlf fetch` to download external lexicons
8484+- Add a `use` statement (if MLF supports imports)
8585+- Check spelling
8686+8787+#### Type Mismatch
8888+8989+```
9090+ × Type mismatch: expected integer, found string
9191+ ╭─[thread.mlf:12:15]
9292+12 │ count: string,
9393+ · ^^^^^^ expected integer type
9494+ ╰────
9595+ help: Change this to 'integer' or update the constraint
9696+```
9797+9898+**Cause:** Field type doesn't match its definition
9999+100100+**Fix:** Update the type to match the schema
101101+102102+### Constraint Errors
103103+104104+#### Invalid Constraint
105105+106106+```
107107+ × Invalid constraint for type 'integer': 'maxLength'
108108+ ╭─[thread.mlf:15:9]
109109+15 │ maxLength: 100,
110110+ · ^^^^^^^^^ 'maxLength' is only valid for string types
111111+ ╰────
112112+ help: Use 'maximum' for integer constraints
113113+```
114114+115115+**Cause:** Constraint doesn't apply to the type
116116+117117+**Fix:** Use the correct constraint for the type:
118118+- String: `maxLength`, `minLength`, `maxGraphemes`, `minGraphemes`
119119+- Integer: `minimum`, `maximum`
120120+- Array: `maxLength`, `minLength`
121121+122122+#### Constraint Value Error
123123+124124+```
125125+ × Constraint value must be positive
126126+ ╭─[thread.mlf:18:20]
127127+18 │ maxLength: -10,
128128+ · ^^^ negative value not allowed
129129+ ╰────
130130+ help: Use a positive integer value
131131+```
132132+133133+**Cause:** Constraint value is invalid
134134+135135+**Fix:** Use a valid value according to the constraint rules
136136+137137+### Record Errors
138138+139139+#### Missing Record Key
140140+141141+```
142142+ × Record definition must specify a key type
143143+ ╭─[thread.mlf:3:1]
144144+ 3 │ record main {
145145+ · ^^^^^^^^^^^ missing key specification
146146+ ╰────
147147+ help: Add 'key: "tid"' or another valid key type to the record
148148+```
149149+150150+**Cause:** Record doesn't specify how records are keyed
151151+152152+**Fix:** Add a key specification to the record definition
153153+154154+### XRPC Errors
155155+156156+#### Invalid Response Code
157157+158158+```
159159+ × Invalid HTTP response code: 999
160160+ ╭─[api.mlf:25:5]
161161+25 │ 999: error,
162162+ · ^^^ response code must be between 200-599
163163+ ╰────
164164+ help: Use a valid HTTP status code
165165+```
166166+167167+**Cause:** Invalid HTTP status code in query/procedure
168168+169169+**Fix:** Use standard HTTP status codes (200, 400, 401, 404, 500, etc.)
170170+171171+#### Missing Required Response
172172+173173+```
174174+ × Query/procedure must define at least one success response
175175+ ╭─[api.mlf:20:1]
176176+20 │ query getProfile(...) {
177177+ · ^^^^^^^^^^^^^^^^^^^^^ no 200-level responses defined
178178+ ╰────
179179+ help: Add at least one 2xx response (typically 200)
180180+```
181181+182182+**Cause:** XRPC method has no success responses
183183+184184+**Fix:** Add a 200-level response
185185+186186+### Union Errors
187187+188188+#### Empty Union
189189+190190+```
191191+ × Union must have at least one type
192192+ ╭─[thread.mlf:30:15]
193193+30 │ data: unit | ,
194194+ · ^ empty union
195195+ ╰────
196196+ help: Add at least one type to the union
197197+```
198198+199199+**Cause:** Union has no types
200200+201201+**Fix:** Add types to the union: `string | integer`
202202+203203+#### Duplicate Union Types
204204+205205+```
206206+ × Duplicate type in union: 'string'
207207+ ╭─[thread.mlf:32:20]
208208+32 │ data: string | string,
209209+ · ^^^^^^ duplicate type
210210+ ╰────
211211+ help: Remove duplicate types from the union
212212+```
213213+214214+**Cause:** Same type appears multiple times in union
215215+216216+**Fix:** Remove duplicates
217217+218218+## Validation Errors
219219+220220+### Record Validation
221221+222222+```
223223+✗ Record validation failed with 2 error(s):
224224+ • Field 'title' is required but missing
225225+ • Field 'count': expected integer, found "123"
226226+```
227227+228228+**Cause:** JSON record doesn't match lexicon schema
229229+230230+**Fix:** Update the JSON to match the schema
231231+232232+## Generation Errors
233233+234234+### File Read Error
235235+236236+```
237237+Failed to read file: permission denied
238238+```
239239+240240+**Cause:** Cannot read input file
241241+242242+**Fix:** Check file permissions and path
243243+244244+### Type Resolution Error
245245+246246+```
247247+Type resolution error: circular reference detected
248248+```
249249+250250+**Cause:** Types reference each other in a cycle
251251+252252+**Fix:** Break the circular dependency
253253+254254+### Code Generation Error
255255+256256+```
257257+Generator 'typescript' not found
258258+```
259259+260260+**Cause:** Generator feature not enabled
261261+262262+**Fix:** Install with `--features typescript` or use `--all-features`
263263+264264+## Fetch Errors
265265+266266+### DNS Error
267267+268268+```
269269+✗ DNS lookup failed: No TXT record found for _lexicon.place.stream
270270+```
271271+272272+**Cause:** Domain has no lexicon TXT record
273273+274274+**Fix:** Verify the namespace is correct and has published lexicons
275275+276276+### Network Error
277277+278278+```
279279+✗ Failed to fetch lexicon records: connection timeout
280280+```
281281+282282+**Cause:** Network connectivity issues
283283+284284+**Fix:** Check internet connection and try again
285285+286286+### Parse Error
287287+288288+```
289289+✗ Failed to parse lexicon JSON: unexpected end of input
290290+```
291291+292292+**Cause:** Malformed JSON from remote repository
293293+294294+**Fix:** Report issue to namespace maintainer
295295+296296+## Exit Codes
297297+298298+All MLF commands use standard exit codes:
299299+300300+- `0` - Success
301301+- `1` - Error occurred
302302+303303+Use in scripts:
304304+305305+```bash
306306+if mlf check; then
307307+ echo "✓ Valid"
308308+else
309309+ echo "✗ Invalid"
310310+ exit 1
311311+fi
312312+```
313313+314314+## Tips for Reading Errors
315315+316316+1. **Read the title** - Gives you the high-level issue
317317+2. **Check the location** - Find the exact line and column
318318+3. **Examine the span** - See what code is problematic
319319+4. **Follow the help** - Actionable suggestions for fixes
320320+5. **Look for patterns** - Similar errors often have similar fixes
321321+322322+## Reporting Bugs
323323+324324+If you encounter an error that seems like a bug:
325325+326326+1. Note the exact error message
327327+2. Create a minimal reproduction case
328328+3. Check if it's a known issue
329329+4. Report at: https://github.com/anthropics/claude-code/issues
330330+331331+Include:
332332+- MLF version (`mlf --version`)
333333+- Complete error message
334334+- Minimal MLF code that reproduces the issue
335335+- Expected behavior vs actual behavior
+60
website/content/docs/cli/_index.md
···11++++
22+title = "CLI Reference"
33+description = "Command-line tool documentation"
44+weight = 3
55+sort_by = "weight"
66+template = "section.html"
77++++
88+99+The MLF CLI is a powerful command-line tool for working with Matt's Lexicon Format and ATProto lexicons.
1010+1111+## Overview
1212+1313+The `mlf` command-line tool provides:
1414+1515+- **Validation** - Check MLF files for syntax and type errors
1616+- **Code Generation** - Generate TypeScript, Go, Rust, and JSON from MLF
1717+- **Lexicon Fetching** - Download lexicons from remote ATProto repositories
1818+- **Format Conversion** - Convert between MLF and JSON lexicon formats
1919+- **Project Management** - Configure projects with `mlf.toml`
2020+2121+## Quick Start
2222+2323+```bash
2424+# Check MLF files
2525+mlf check
2626+2727+# Generate TypeScript types
2828+mlf generate code -g typescript -i "lexicons/**/*.mlf" -o src/types/
2929+3030+# Fetch remote lexicons
3131+mlf fetch stream.place --save
3232+3333+# Generate all configured outputs
3434+mlf generate
3535+```
3636+3737+## Project Configuration
3838+3939+Projects can be configured with an `mlf.toml` file that specifies source directories, output configurations, and dependencies. This allows you to run commands without repeatedly specifying arguments.
4040+4141+See the [Configuration](02-configuration.md) section for details.
4242+4343+## Command Categories
4444+4545+### Validation Commands
4646+- `mlf check` - Validate MLF syntax and types
4747+- `mlf validate` - Validate JSON records against lexicons
4848+4949+### Generation Commands
5050+- `mlf generate lexicon` - Generate JSON lexicons
5151+- `mlf generate code` - Generate code in various languages
5252+- `mlf generate mlf` - Convert JSON lexicons to MLF
5353+- `mlf generate` (no subcommand) - Generate all configured outputs
5454+5555+### Fetching Commands
5656+- `mlf fetch` - Download lexicons from remote repositories
5757+- `mlf fetch <namespace>` - Fetch specific namespace
5858+- `mlf fetch <namespace> --save` - Fetch and add to dependencies
5959+6060+This guide walks through each command in detail, with examples and usage patterns.