{
  "type": "document",
  "pos": {
    "line": 1,
    "column": 1
  },
  "endLine": 206,
  "meta": {
    "filename": "/home/runner/work/noma/noma/examples/tech-doc.noma",
    "title": "Noma CLI Reference",
    "author": "ferax564",
    "date": "2026-05-09T00:00:00.000Z",
    "tags": [
      "docs",
      "reference",
      "cli"
    ]
  },
  "children": [
    {
      "type": "frontmatter",
      "data": {
        "title": "Noma CLI Reference",
        "author": "ferax564",
        "date": "2026-05-09T00:00:00.000Z",
        "tags": [
          "docs",
          "reference",
          "cli"
        ]
      },
      "raw": "title: Noma CLI Reference\nauthor: ferax564\ndate: 2026-05-09\ntags: [docs, reference, cli]",
      "pos": {
        "line": 1,
        "column": 1
      },
      "endLine": 6
    },
    {
      "type": "section",
      "id": "noma-cli-reference",
      "level": 1,
      "title": "Noma CLI Reference",
      "children": [
        {
          "type": "directive",
          "name": "summary",
          "attrs": {},
          "children": [
            {
              "type": "paragraph",
              "content": "The `noma` CLI parses, renders, and validates `.noma` source files. This page\nis itself written in Noma — the same source produces the rendered HTML you're\nreading and the deterministic LLM context an agent would consume.",
              "pos": {
                "line": 11,
                "column": 1
              },
              "endLine": 13
            }
          ],
          "pos": {
            "line": 10,
            "column": 1
          },
          "endLine": 14,
          "body": "The `noma` CLI parses, renders, and validates `.noma` source files. This page\nis itself written in Noma — the same source produces the rendered HTML you're\nreading and the deterministic LLM context an agent would consume."
        },
        {
          "type": "section",
          "id": "install",
          "level": 2,
          "title": "Install",
          "children": [
            {
              "type": "code",
              "lang": "bash",
              "content": "npm install -g @ferax564/noma-cli\n# or one-off\nnpx @ferax564/noma-cli render path/to/file.noma --to html",
              "pos": {
                "line": 18,
                "column": 1
              },
              "endLine": 22
            },
            {
              "type": "directive",
              "name": "callout",
              "attrs": {
                "tone": "tip"
              },
              "children": [
                {
                  "type": "paragraph",
                  "content": "The CLI auto-detects the `themes/default.css` shipped with the package. To\nuse a custom theme, pass `--theme path/to/theme.css` (coming in v0.2).",
                  "pos": {
                    "line": 25,
                    "column": 1
                  },
                  "endLine": 26
                }
              ],
              "pos": {
                "line": 24,
                "column": 1
              },
              "endLine": 27,
              "body": "The CLI auto-detects the `themes/default.css` shipped with the package. To\nuse a custom theme, pass `--theme path/to/theme.css` (coming in v0.2)."
            }
          ],
          "pos": {
            "line": 16,
            "column": 1
          },
          "endLine": 27
        },
        {
          "type": "section",
          "id": "commands",
          "level": 2,
          "title": "Commands",
          "children": [
            {
              "type": "directive",
              "name": "tabs",
              "attrs": {},
              "children": [
                {
                  "type": "directive",
                  "name": "tab",
                  "attrs": {
                    "title": "parse"
                  },
                  "children": [
                    {
                      "type": "section",
                      "id": "noma-parse-file",
                      "level": 3,
                      "title": "`noma parse <file>`",
                      "children": [],
                      "pos": {
                        "line": 33,
                        "column": 1
                      },
                      "_idIsExplicit": false,
                      "endLine": 33
                    },
                    {
                      "type": "paragraph",
                      "content": "Print the parsed AST as JSON. Useful for debugging the parser or building\ntools that consume Noma documents.",
                      "pos": {
                        "line": 35,
                        "column": 1
                      },
                      "endLine": 36
                    },
                    {
                      "type": "code",
                      "lang": "bash",
                      "content": "noma parse examples/thesis.noma\nnoma parse examples/thesis.noma --out ast.json",
                      "pos": {
                        "line": 38,
                        "column": 1
                      },
                      "endLine": 41
                    },
                    {
                      "type": "paragraph",
                      "content": "Returns the full typed AST defined in `src/ast.ts`. Block IDs are stable\nacross re-parses of unchanged content, so AST diffs map cleanly to source\ndiffs.",
                      "pos": {
                        "line": 43,
                        "column": 1
                      },
                      "endLine": 45
                    }
                  ],
                  "pos": {
                    "line": 32,
                    "column": 1
                  },
                  "endLine": 46
                },
                {
                  "type": "directive",
                  "name": "tab",
                  "attrs": {
                    "title": "render"
                  },
                  "children": [
                    {
                      "type": "section",
                      "id": "noma-render-file-to-target",
                      "level": 3,
                      "title": "`noma render <file> [--to <target>]`",
                      "children": [],
                      "pos": {
                        "line": 49,
                        "column": 1
                      },
                      "_idIsExplicit": false,
                      "endLine": 49
                    },
                    {
                      "type": "paragraph",
                      "content": "Render a `.noma` file to one of the supported targets.",
                      "pos": {
                        "line": 51,
                        "column": 1
                      },
                      "endLine": 51
                    },
                    {
                      "type": "table",
                      "header": [
                        "Target",
                        "Output"
                      ],
                      "align": [
                        null,
                        null
                      ],
                      "rows": [
                        [
                          "`html`",
                          "Standalone HTML document with the default theme"
                        ],
                        [
                          "`llm`",
                          "Deterministic plain-text context for LLM consumption"
                        ],
                        [
                          "`json`",
                          "The parsed AST (alias of `noma export`)"
                        ]
                      ],
                      "pos": {
                        "line": 53,
                        "column": 1
                      },
                      "endLine": 57
                    },
                    {
                      "type": "code",
                      "lang": "bash",
                      "content": "noma render docs/spec.noma --to html --out dist/spec.html\nnoma render docs/spec.noma --to llm\nnoma render docs/spec.noma --to json --out dist/spec.json",
                      "pos": {
                        "line": 59,
                        "column": 1
                      },
                      "endLine": 63
                    },
                    {
                      "type": "paragraph",
                      "content": "Use `--no-standalone` to emit just the HTML body (for embedding inside an\nexisting page). Use `--title \"...\"` to override the document title.",
                      "pos": {
                        "line": 65,
                        "column": 1
                      },
                      "endLine": 66
                    }
                  ],
                  "pos": {
                    "line": 48,
                    "column": 1
                  },
                  "endLine": 67
                },
                {
                  "type": "directive",
                  "name": "tab",
                  "attrs": {
                    "title": "check"
                  },
                  "children": [
                    {
                      "type": "section",
                      "id": "noma-check-file",
                      "level": 3,
                      "title": "`noma check <file>`",
                      "children": [],
                      "pos": {
                        "line": 70,
                        "column": 1
                      },
                      "_idIsExplicit": false,
                      "endLine": 70
                    },
                    {
                      "type": "paragraph",
                      "content": "Validate a Noma document. Exits 1 if any errors are present, 0 otherwise.",
                      "pos": {
                        "line": 72,
                        "column": 1
                      },
                      "endLine": 72
                    },
                    {
                      "type": "code",
                      "lang": "bash",
                      "content": "noma check examples/thesis.noma",
                      "pos": {
                        "line": 74,
                        "column": 1
                      },
                      "endLine": 76
                    },
                    {
                      "type": "paragraph",
                      "content": "Catches: duplicate block IDs, broken `for=` references on evidence blocks,\nbroken internal links, invalid frontmatter, plot blocks missing a dataset\nor `data=` attribute, and (in v0.2) claims missing supporting evidence.",
                      "pos": {
                        "line": 78,
                        "column": 1
                      },
                      "endLine": 80
                    }
                  ],
                  "pos": {
                    "line": 69,
                    "column": 1
                  },
                  "endLine": 81
                },
                {
                  "type": "directive",
                  "name": "tab",
                  "attrs": {
                    "title": "export"
                  },
                  "children": [
                    {
                      "type": "section",
                      "id": "noma-export-file",
                      "level": 3,
                      "title": "`noma export <file>`",
                      "children": [],
                      "pos": {
                        "line": 84,
                        "column": 1
                      },
                      "_idIsExplicit": false,
                      "endLine": 84
                    },
                    {
                      "type": "paragraph",
                      "content": "Alias for `noma render <file> --to json`. Kept as a separate command because\nit's the most common scripted use case (CI pipelines, agent context, RAG\nindexing).",
                      "pos": {
                        "line": 86,
                        "column": 1
                      },
                      "endLine": 88
                    }
                  ],
                  "pos": {
                    "line": 83,
                    "column": 1
                  },
                  "endLine": 89
                }
              ],
              "pos": {
                "line": 31,
                "column": 1
              },
              "endLine": 90
            }
          ],
          "pos": {
            "line": 29,
            "column": 1
          },
          "endLine": 90
        },
        {
          "type": "section",
          "id": "architecture",
          "level": 2,
          "title": "Architecture",
          "children": [
            {
              "type": "directive",
              "name": "grid",
              "attrs": {
                "columns": 3
              },
              "children": [
                {
                  "type": "directive",
                  "name": "card",
                  "attrs": {
                    "title": "Parser",
                    "icon": "parse"
                  },
                  "children": [
                    {
                      "type": "paragraph",
                      "content": "Hand-written recursive descent. Tracks fence depth by counting leading colons.\nNever throws on malformed input — produces a best-effort AST and lets the\nvalidator complain.",
                      "pos": {
                        "line": 96,
                        "column": 1
                      },
                      "endLine": 98
                    }
                  ],
                  "pos": {
                    "line": 95,
                    "column": 1
                  },
                  "endLine": 99,
                  "body": "Hand-written recursive descent. Tracks fence depth by counting leading colons.\nNever throws on malformed input — produces a best-effort AST and lets the\nvalidator complain."
                },
                {
                  "type": "directive",
                  "name": "card",
                  "attrs": {
                    "title": "AST",
                    "icon": "tree"
                  },
                  "children": [
                    {
                      "type": "paragraph",
                      "content": "Discriminated union in `src/ast.ts`. Single source of truth. Adding a node\ntype is a one-line change that the TypeScript compiler propagates to every\nrenderer's switch.",
                      "pos": {
                        "line": 102,
                        "column": 1
                      },
                      "endLine": 104
                    }
                  ],
                  "pos": {
                    "line": 101,
                    "column": 1
                  },
                  "endLine": 105,
                  "body": "Discriminated union in `src/ast.ts`. Single source of truth. Adding a node\ntype is a one-line change that the TypeScript compiler propagates to every\nrenderer's switch."
                },
                {
                  "type": "directive",
                  "name": "card",
                  "attrs": {
                    "title": "Renderers",
                    "icon": "render"
                  },
                  "children": [
                    {
                      "type": "paragraph",
                      "content": "Pure functions. `AST → string`. No I/O, no globals. Three in core today\n(HTML, LLM, JSON); PDF is a wrapper around the HTML renderer + Puppeteer.",
                      "pos": {
                        "line": 108,
                        "column": 1
                      },
                      "endLine": 109
                    }
                  ],
                  "pos": {
                    "line": 107,
                    "column": 1
                  },
                  "endLine": 110,
                  "body": "Pure functions. `AST → string`. No I/O, no globals. Three in core today\n(HTML, LLM, JSON); PDF is a wrapper around the HTML renderer + Puppeteer."
                }
              ],
              "pos": {
                "line": 94,
                "column": 1
              },
              "endLine": 111
            }
          ],
          "pos": {
            "line": 92,
            "column": 1
          },
          "endLine": 111
        },
        {
          "type": "section",
          "id": "data-flow",
          "level": 2,
          "title": "Data flow",
          "children": [
            {
              "type": "code",
              "lang": "txt",
              "content": ".noma source\n   │\n   ▼\n┌──────────┐\n│  Parser  │  src/parser.ts  (line-based, recursive descent)\n└─────┬────┘\n      │  typed AST  (src/ast.ts)\n      ▼\n┌──────────────────────────────────┐\n│  Renderers (pure)                │\n│  ├── renderer-html.ts → HTML     │\n│  ├── renderer-llm.ts  → LLM ctx  │\n│  └── renderer-json.ts → JSON     │\n└──────────┬───────────────────────┘\n           │\n           ▼\n       artifact",
              "pos": {
                "line": 115,
                "column": 1
              },
              "endLine": 133
            }
          ],
          "pos": {
            "line": 113,
            "column": 1
          },
          "endLine": 133
        },
        {
          "type": "section",
          "id": "common-errors",
          "level": 2,
          "title": "Common errors",
          "children": [
            {
              "type": "directive",
              "name": "callout",
              "attrs": {
                "tone": "warning"
              },
              "children": [
                {
                  "type": "paragraph",
                  "content": "**Stray triple-colon at top level.** `:::card` only opens a child block one\nlevel deeper. At the top of a document it has no parent and the parser will\nemit a `parser/orphan-fence` diagnostic. Fix: wrap in `::grid` or use `::card`.",
                  "pos": {
                    "line": 138,
                    "column": 1
                  },
                  "endLine": 140
                }
              ],
              "pos": {
                "line": 137,
                "column": 1
              },
              "endLine": 141,
              "body": "**Stray triple-colon at top level.** `:::card` only opens a child block one\nlevel deeper. At the top of a document it has no parent and the parser will\nemit a `parser/orphan-fence` diagnostic. Fix: wrap in `::grid` or use `::card`."
            },
            {
              "type": "directive",
              "name": "callout",
              "attrs": {
                "tone": "warning"
              },
              "children": [
                {
                  "type": "paragraph",
                  "content": "**Duplicate block ID.** Block IDs are user-facing API. The validator emits\n`validator/duplicate-id` and `noma check` exits non-zero. Fix: rename one\nof the blocks. If they were intentionally duplicated, they shouldn't have\nbeen — split or merge.",
                  "pos": {
                    "line": 144,
                    "column": 1
                  },
                  "endLine": 147
                }
              ],
              "pos": {
                "line": 143,
                "column": 1
              },
              "endLine": 148,
              "body": "**Duplicate block ID.** Block IDs are user-facing API. The validator emits\n`validator/duplicate-id` and `noma check` exits non-zero. Fix: rename one\nof the blocks. If they were intentionally duplicated, they shouldn't have\nbeen — split or merge."
            },
            {
              "type": "directive",
              "name": "callout",
              "attrs": {
                "tone": "note"
              },
              "children": [
                {
                  "type": "paragraph",
                  "content": "**Plot without data.** A `::plot` block without a `data=` or `dataset=`\nattribute renders as a placeholder SVG. The validator emits\n`validator/plot-missing-data` as a warning, not an error, so the build\nstill passes — but the artifact won't have real data in it.",
                  "pos": {
                    "line": 151,
                    "column": 1
                  },
                  "endLine": 154
                }
              ],
              "pos": {
                "line": 150,
                "column": 1
              },
              "endLine": 155,
              "body": "**Plot without data.** A `::plot` block without a `data=` or `dataset=`\nattribute renders as a placeholder SVG. The validator emits\n`validator/plot-missing-data` as a warning, not an error, so the build\nstill passes — but the artifact won't have real data in it."
            }
          ],
          "pos": {
            "line": 135,
            "column": 1
          },
          "endLine": 155
        },
        {
          "type": "section",
          "id": "programmatic-use",
          "level": 2,
          "title": "Programmatic use",
          "children": [
            {
              "type": "code",
              "lang": "ts",
              "content": "import { parse, renderHtml, validate } from \"@ferax564/noma-cli\";\n\nconst source = await fs.readFile(\"doc.noma\", \"utf8\");\nconst ast = parse(source, { filename: \"doc.noma\" });\nconst diagnostics = validate(ast);\nif (diagnostics.some(d => d.severity === \"error\")) {\n  throw new Error(\"invalid Noma document\");\n}\nconst html = renderHtml(ast, { standalone: true });",
              "pos": {
                "line": 159,
                "column": 1
              },
              "endLine": 169
            }
          ],
          "pos": {
            "line": 157,
            "column": 1
          },
          "endLine": 169
        },
        {
          "type": "section",
          "id": "patch-protocol-preview-v03",
          "level": 2,
          "title": "Patch protocol (preview, v0.3)",
          "children": [
            {
              "type": "paragraph",
              "content": "Agents should not rewrite full files. They propose block-level operations\nthat the CLI applies safely.",
              "pos": {
                "line": 173,
                "column": 1
              },
              "endLine": 174
            },
            {
              "type": "code",
              "lang": "json",
              "content": "{\n  \"op\": \"replace_block\",\n  \"id\": \"asml-euv-moat\",\n  \"content\": \"ASML's moat rests on EUV optics, mechanics, and supply chain — not just exclusivity.\"\n}",
              "pos": {
                "line": 176,
                "column": 1
              },
              "endLine": 182
            },
            {
              "type": "paragraph",
              "content": "Operations: `add_block`, `replace_block`, `delete_block`, `move_block`,\n`update_attribute`, `add_evidence`, `add_comment`, `resolve_comment`,\n`rename_id`. See `docs/agent-protocol.noma` for the full schema.",
              "pos": {
                "line": 184,
                "column": 1
              },
              "endLine": 186
            }
          ],
          "pos": {
            "line": 171,
            "column": 1
          },
          "endLine": 186
        },
        {
          "type": "section",
          "id": "architecture-v05-interactive-diagrams",
          "level": 2,
          "title": "Architecture (v0.5 — interactive diagrams)",
          "children": [
            {
              "type": "directive",
              "name": "diagram",
              "attrs": {
                "kind": "mermaid",
                "id": "cli-flow"
              },
              "children": [
                {
                  "type": "paragraph",
                  "content": "flowchart LR\n  A[.noma source] --> B[parser]\n  B --> C[AST]\n  C --> D[validator]\n  C --> E[renderer-html]\n  C --> F[renderer-llm]\n  C --> G[renderer-noma]\n  G --> H[noma fmt / patch]",
                  "pos": {
                    "line": 191,
                    "column": 1
                  },
                  "endLine": 198
                }
              ],
              "pos": {
                "line": 190,
                "column": 1
              },
              "endLine": 199,
              "id": "cli-flow",
              "body": "flowchart LR\n  A[.noma source] --> B[parser]\n  B --> C[AST]\n  C --> D[validator]\n  C --> E[renderer-html]\n  C --> F[renderer-llm]\n  C --> G[renderer-noma]\n  G --> H[noma fmt / patch]"
            }
          ],
          "pos": {
            "line": 188,
            "column": 1
          },
          "endLine": 199
        },
        {
          "type": "section",
          "id": "cross-links",
          "level": 2,
          "title": "Cross-links",
          "children": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                {
                  "type": "list_item",
                  "content": "See `docs/spec.noma` for the full block reference.",
                  "pos": {
                    "line": 203,
                    "column": 1
                  },
                  "endLine": 203
                },
                {
                  "type": "list_item",
                  "content": "See `docs/agent-protocol.noma` for the patch protocol.",
                  "pos": {
                    "line": 204,
                    "column": 1
                  },
                  "endLine": 204
                },
                {
                  "type": "list_item",
                  "content": "See `docs/direction.noma` for the product positioning.",
                  "pos": {
                    "line": 205,
                    "column": 1
                  },
                  "endLine": 205
                },
                {
                  "type": "list_item",
                  "content": "See `examples/research-thesis.noma` for a reasoning-heavy demo.",
                  "pos": {
                    "line": 206,
                    "column": 1
                  },
                  "endLine": 206
                }
              ],
              "pos": {
                "line": 203,
                "column": 1
              },
              "endLine": 206
            }
          ],
          "pos": {
            "line": 201,
            "column": 1
          },
          "endLine": 206
        }
      ],
      "pos": {
        "line": 8,
        "column": 1
      },
      "endLine": 206,
      "aliases": [
        "tech-doc"
      ]
    }
  ]
}