> ## Documentation Index
> Fetch the complete documentation index at: https://narrator.ami.rip/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting started

> Install Narrator, narrate files and diffs from the terminal, then do the same from code.

This page takes you from nothing to narrating your own code and your own diffs, first with the CLI and then from code. The `@usenarrator/*` packages are published as ESM JavaScript with type declarations, so they work in Node and Bun without a bundler.

| Runtime | What runs |
| - | - |
| Node 20.19 or newer (or 22.12 or newer on the 22 line) | The packages. Save the examples below as `.mjs` files and drop the type annotations, or run the `.ts` files with `npx tsx`. |
| Node 22.18 or newer, or Node 23.6 or newer | The packages, the CLI, and the `.ts` examples as written, since Node strips the types itself. |
| [Bun](https://bun.sh) 1.1 or newer | Everything, including the `.ts` examples. |

The CLI needs Node 22.17 or newer because it uses Node's built-in glob support, which became stable in that release.

The examples are ES modules. In a new npm project, run `npm pkg set type=module` first, because `npm init` marks projects as CommonJS. Otherwise name the files `.mts` (or `.mjs` without types).

<Steps>
  <Step title="Try the CLI">
    Install the CLI, or run it once with `npx`:

    ```bash theme={null}
    npm install --global @usenarrator/cli
    # or, without installing:
    npx @usenarrator/cli src/billing.ts
    ```

    Give it files, directories or globs. It prints the English for each TypeScript or JavaScript file it finds. Inside directories and globs it skips anything your `.gitignore` excludes (exceptions such as `!keep.ts` are respected), plus files people don't write by hand: dependencies, build output such as `dist` and `.next`, vendored code such as `.yarn`, minified files, and anything `.gitattributes` marks `linguist-generated` or `linguist-vendored`. A file you name directly is always narrated. With no arguments it reads code from standard input.

    ```bash theme={null}
    narrator src/billing.ts            # one file
    narrator src/                      # every TS/JS file under src
    narrator "src/**/*.test.ts"        # a glob
    echo 'if (basket.includes(apple)) biteTheApple()' | narrator --plain
    ```

    The last command prints `If apple is in basket, bite the apple.`

    By default, the CLI reads the `package.json` files in your repository and loads only the plugins for libraries you depend on, so a Hono app gets the Hono plugin without any configuration. See [Dependency detection](/concepts/dependency-detection).

    | Option | What it does |
    | - | - |
    | `--plugins zod,drizzle` | Load exactly these plugins instead of detecting them. Use `all` or `none` for every plugin or none. |
    | `--locale en` | The output language. Other locales are loaded from `@usenarrator/locale-<id>`. |
    | `--include-generated` | Also narrate generated, vendored, minified and build files. |
    | `--plain`, `--ansi`, `--json`, `--html` | The output format. The default is coloured text in a terminal and plain text otherwise. |

    The [CLI reference](/reference/cli) lists every plugin name and the packages that turn it on.
  </Step>

  <Step title="Narrate a diff from the terminal">
    `narrator diff` narrates a git change set. Give it a range, or a single revision to compare against your working tree.

    ```bash theme={null}
    narrator diff main..HEAD      # what this branch changed
    narrator diff main...HEAD     # the same, from the merge base
    narrator diff HEAD            # uncommitted changes
    ```

    Each changed declaration is printed as a unit, with removed sentences marked `-` and added sentences marked `+`. Generated, vendored and minified files are skipped the same way as above and listed with one line each, such as `.yarn/releases/yarn.cjs [deleted, skipped: vendored]`. A file that can't be read, for example because of a syntax error, gets a line starting with `!` and the rest of the diff carries on. The next step shows the same output from code. Use `--json` for structured output, or `--html` for a standalone side-by-side page.
  </Step>

  <Step title="Narrate a file from code">
    Install the packages this page imports, plus any plugins you want:

    ```bash theme={null}
    npm install @usenarrator/core @usenarrator/lang-ts @usenarrator/locale-en @usenarrator/node @usenarrator/plugin-zod
    ```

    `@usenarrator/node` already depends on the other three, but you import from them directly below, so list them yourself. Strict package managers such as pnpm only let you import what you declare. See [What to install](#what-to-install) for the full picture.

    `createNodeNarrator` sets up the TypeScript language with the native parser, the English locale, and the dependency manifests of the git repository around your working directory.

    ```ts node-narrator.ts theme={null}
    import { renderText } from "@usenarrator/core";
    import { createNodeNarrator } from "@usenarrator/node";
    import { zod } from "@usenarrator/plugin-zod";

    const narrator = createNodeNarrator({ plugins: [zod()] });

    const source = `
    import { z } from "zod";

    export const Signup = z.object({ email: z.string().email(), plan: z.enum(["free", "pro"]) });

    export function countOpenSeats(team: Team) {
      if (team.plan === "enterprise") return Infinity;
      return team.seatLimit - team.members.length;
    }
    `;

    const { lines } = narrator.narrate(source, "src/signup.ts");
    console.log(renderText(lines));
    ```

    Running it prints:

    ```text Output theme={null}
    Let signup be a schema for an object with email and plan.

    To count open seats, given team (Team):
      If team's plan is "enterprise", give back infinity.
      Give back team's seat limit minus the number of team's members.
    ```

    `narrate` returns an object whose `lines` are `Line` objects, not a string. Each line knows its indent depth, its kind (heading, statement, bullet and so on), and the source offsets it came from. `renderText` is the simplest way to turn them into text. In a browser, or when you want to choose every piece yourself, use `createNarrator` from `@usenarrator/core` instead. See [Embedding in your app](/guides/embedding).
  </Step>

  <Step title="Narrate a diff from code">
    Give `narrator.diffFiles` the old and new source of each changed file. Narrator narrates both sides, splits them into units (one per top-level declaration), and lines up the English so you see what changed in meaning rather than in characters. `renderDiffText` prints the result the way the CLI does.

    ```ts narrate-diff.ts theme={null}
    import { createNarrator, renderDiffText } from "@usenarrator/core";
    import { typescript } from "@usenarrator/lang-ts";
    import { oxcParser } from "@usenarrator/lang-ts/oxc";
    import { en } from "@usenarrator/locale-en";

    const narrator = createNarrator({ languages: [typescript({ parser: oxcParser })], locale: en });

    const before = `
    export function countOpenSeats(team: Team) {
      if (team.plan === "enterprise") return Infinity;
      return team.seatLimit - team.members.length;
    }
    `;
    const after = `
    export function countOpenSeats(team: Team) {
      if (team.plan === "enterprise" || team.isTrial) return Infinity;
      const used = team.members.filter((m) => !m.isGuest).length;
      return Math.max(0, team.seatLimit - used);
    }
    `;

    const files = narrator.diffFiles([{ path: "team.ts", oldPath: null, status: "modified", oldSrc: before, newSrc: after }]);
    console.log(renderDiffText({ files }));
    ```

    ```text Output theme={null}
    team.ts [modified, +3 -2]
      ~ To count open seats [modified]
          To count open seats, given team (Team):
        -   If team's plan is "enterprise", give back infinity.
        +   If team's plan is "enterprise" or team is trial, give back infinity.
        -   Give back team's seat limit minus the number of team's members.
        +   Let used be the number of team's members where m is not guest.
        +   Give back the larger of 0 and team's seat limit minus used.
    ```

    The first condition was edited in place, so it shows as a removed line and an added line. The `return` grew into two sentences, which is easier to review than the code change that caused it. To render diffs as HTML instead, use `narrator.diff`, described in [Units and diffs](/concepts/units-and-diffs).

    To build the file list from a git range, the way the CLI does, use `changedFilesFromGit` from `@usenarrator/node`. It also marks generated and vendored files so they are skipped without being read. See the [Node reference](/reference/node).
  </Step>
</Steps>

## Bad code and failing plugins

Narration never stops for one bad file. `narrate` returns a `problems` list next to the lines, and each entry in a diff has one too. A syntax error is reported with its line number and Narrator keeps whatever it could read. A plugin rule that throws is reported with the plugin's name and the plain reading is used instead. If a file can't be narrated at all, it has no lines and a single problem explaining why, while every other file in the call is narrated as usual. See [`NarrationProblem`](/reference/core#narrationproblem).

## What to install

| You want | Install |
| - | - |
| The command line tool | `@usenarrator/cli`. It includes the parser and every plugin. |
| Narration from Node or Bun | `@usenarrator/node`, which brings `@usenarrator/core`, `@usenarrator/lang-ts`, `@usenarrator/locale-en` and the native parser `oxc-parser`. List any of those you import yourself. |
| A plugin | `@usenarrator/plugin-<name>`. Every plugin has `@usenarrator/core` and `@usenarrator/lang-ts` as peer dependencies, so all plugins share one copy of each. npm and pnpm install peers for you; with Yarn, add them yourself. |
| Drizzle or Next.js | `@usenarrator/plugin-drizzle` brings `@usenarrator/plugin-sql`, and `@usenarrator/plugin-nextjs` brings `@usenarrator/plugin-react`, as ordinary dependencies. You don't install them separately. |
| SQL strings | `@usenarrator/plugin-sql`, plus `pgsql-ast-parser` if you use the `@usenarrator/plugin-sql/pgsql` parser. |
| A browser or edge runtime | `@usenarrator/core`, `@usenarrator/lang-ts` and `@usenarrator/locale-en`. The WASM parser ships inside `@usenarrator/lang-ts`. See [Embedding in your app](/guides/embedding). |

## What to read next

<Columns cols={2}>
  <Card title="How it works" icon="workflow" href="/concepts/how-it-works">
    The path from source code to sentences, and the pieces you can swap.
  </Card>

  <Card title="Embedding in your app" icon="box" href="/guides/embedding">
    Use the native parser on the server or the WASM parser in a browser.
  </Card>

  <Card title="Plugins" icon="puzzle" href="/plugins/overview">
    Add React, Next.js, Drizzle, Stripe and more so library calls read naturally.
  </Card>

  <Card title="Write a plugin" icon="pencil" href="/guides/writing-a-plugin">
    Teach Narrator what your own helpers mean.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.