CLI reference
manni site exposes four verbs. start, build
and preview find the site, detect its framework, and run
the framework’s own command with inherited stdio.
annotate joins the graph’s pins and statements to the
built pages, as data. site has no default subcommand. A bare manni site
prints usage and exits 2.
manni site [options] <command> [command options] [dir] [-- <args...>]Global options
Section titled “Global options”Global options are accepted before the subcommand.
| Option | Description |
|---|---|
-V, --version | Print the manni version and exit. |
-h, --help | Print help for the program or a subcommand. |
Start, build and preview the docs site. Every command on this page lives under
it. site accepts its own -V, --version, which reports the manni version,
and -h, --help. Run with no subcommand it prints usage, exit 2.
manni site [options] <command> [command options] [dir] [-- <args...>]site start
Section titled “site start”Run the framework’s dev server, with hot reload, until Ctrl-C or SIGTERM stops
it. A stop by signal exits 0. Flare has no development server, so start
stops with exit 2 there.
manni site start [dir] [options] [-- <args...>]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
[dir] | no | The site’s directory. Omitted: site.dir, else found by search. One directory, not a list. |
Options
Section titled “Options”| Option | Argument | Default | Description |
|---|---|---|---|
--port | <n> | none | The port, an integer from 1 to 65535. Without it, the port of a local collection url:, else the framework’s default. See the port and host. |
--host | <host> | none | The host to bind. Without it, the host of a local collection url:, else the framework’s default. Refused for Mintlify and Fern, whose dev servers take no host. |
-c, --config | <file> | discovered | Path to a manni config file. |
site build
Section titled “site build”Run the framework’s production build once. A build that fails exits 2, and
the framework’s own output above the last line says why. Mintlify and Fern
have no local build, so build stops with exit 2 there. build takes no
--port or --host, because nothing is served. With
site.annotate.after-build set to true, a successful
build is followed by annotate, and a failed one is not.
manni site build [dir] [options] [-- <args...>]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
[dir] | no | The site’s directory. Omitted: site.dir, else found by search. |
Options
Section titled “Options”| Option | Argument | Default | Description |
|---|---|---|---|
-c, --config | <file> | discovered | Path to a manni config file. |
site preview
Section titled “site preview”Run build, then serve the output until Ctrl-C or SIGTERM stops it. It always
builds first, so it never serves stale output. A build that fails exits 2
before anything is served. The serve step is the framework’s own preview
command, or the built-in server for a framework that
has none. With site.annotate.after-build set to true, preview annotates
the build before it serves it.
Flare is the exception. Its build is slow and runs only where Flare is
installed, so preview serves the output of the last site build without
building. With no output there, it exits 2.
manni site preview [dir] [options] [-- <args...>]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
[dir] | no | The site’s directory. Omitted: site.dir, else found by search. |
Options
Section titled “Options”| Option | Argument | Default | Description |
|---|---|---|---|
--port | <n> | none | The port the output is served on, an integer from 1 to 65535. Without it, the port of a local collection url:, else the framework’s default. |
--host | <host> | none | The host the output is served on. Without it, the host of a local collection url:, else the framework’s default. |
-c, --config | <file> | discovered | Path to a manni config file. |
site annotate
Section titled “site annotate”Join the chunk artifact manni graph publish writes to the built site, and
write the result into each built page as data. Each placed block gains
data-manni-* attributes, and each page gains one JSON
island. Nothing visible changes: annotate writes no mark, stylesheet or
script. It is not a gate. It exits 0 whatever the pins say, and 2 when it
cannot run. Show readers what is proven walks
through it.
manni site annotate [dir] [options]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
[dir] | no | The site’s directory. Omitted: site.dir, else found by search. |
Options
Section titled “Options”| Option | Argument | Default | Description |
|---|---|---|---|
--out | <dir> | none | The built site, relative to the working directory. Without it, site.out, else the framework’s output from the table below. |
--chunks | <file> | none | The chunk manifest. Without it, chunks.json in the directory of graph.out, where manni graph publish writes it. |
-f, --format | <format> | pretty | pretty or json. |
-c, --config | <file> | discovered | Path to a manni config file. |
A framework is needed only for its default output and its routes. With
--out or site.out, a site no framework marks is annotated too.
The join
Section titled “The join”Each pin’s claim lines, and each statement’s lines, are joined to the built HTML in two steps.
- Lines. A block stamped by
@hawkeyexl/manni/site/rehypewhose lines overlap the claim. The innermost stamped blocks win, so a claim over two list items lands on the items, not on the list. - Text. With no stamped block, the claim rendered as plain text. Either one block holds it, or one run of adjacent blocks makes it up. Inside a longer claim, a block shorter than 12 characters does not count.
A claim whose text appears twice is not placed, and the report says so. A tie
is never broken by document order. Statements join by lines alone. Every
data-manni-src stamp is removed afterwards, from every page. So annotate
once per build: a second run over the same build finds no stamps, and places
by text alone.
Add the plugin to the framework’s rehype plugins. For Starlight that is
astro.config.mjs:
import manniLines from "@hawkeyexl/manni/site/rehype";
export default defineConfig({ markdown: { rehypePlugins: [manniLines] },});Output and routes
Section titled “Output and routes”A page with no stamps is found by its route. <path> is the page’s path below
the content directory, without its extension, and an index page maps to its
directory.
| Framework | Output | Content | Route | Lines step |
|---|---|---|---|---|
| Starlight | dist | src/content/docs | <path>/index.html | the rehype plugin |
| Docusaurus | build | docs | docs/<path>/index.html | the rehype plugin |
| Nextra | out | pages | <path>.html | the rehype plugin |
| Fumadocs | out | content/docs | docs/<path>.html | the rehype plugin |
| Rspress | doc_build | docs | <path>.html | the rehype plugin |
| VitePress | .vitepress/dist under its root | its root | <path>.html | none, text only |
| MkDocs, Zensical | site | docs | <path>/index.html | none, text only |
| Sphinx | <src>/_build/html | <src> | <path>.html | none, text only |
| Hugo | public | content | <path>/index.html | none, text only |
| Jekyll | _site | the site’s directory | <path>.html | none, text only |
Mintlify and Fern build on their own hosts, so there is no local site to
annotate. annotate does not read Flare output, so it exits 2 for a Flare
site. Nextra and Fumadocs write static pages only with
output: "export" in next.config.
What a page gains
Section titled “What a page gains”| Attribute | On | Value |
|---|---|---|
data-manni-node | a placed block, or a heading | The graph node IRI of the section, or of the graph:Block a block statement mints. |
data-manni-pins | a block with placed pins | Space-separated citation ids. A citation with no id appears as #<n>, its place in the page’s citations. |
data-manni-state | a block with placed pins | current when no pin on it has an error-severity finding, otherwise drifted. |
data-manni-verified | a block or heading whose statement sets last-verified | The day or timestamp, as written. |
data-manni-verified-by | a block or heading whose statement sets verified-by | Space-separated verified-by[].tool values. |
data-manni-statement | a block or heading with a statement | current, changed or unpinned. |
data-manni-applies-to | a heading whose statement’s section sets applies-to | Space-separated values. |
data-manni-commit | <main>, or <body> without one | The docs commit. Absent outside git. |
data-manni-last-verified | <main>, or <body> without one | The page’s last-verified, when set. |
The island is one <script type="application/json" id="manni-provenance">,
before </body>. It holds { page, commit, worktree, dates, records }.
records are the page’s provenance records from the chunk rows, unchanged, in
lines order. A browser never runs it, so a strict content security policy
allows it. An encrypted source stays its ciphertext token.
Output
Section titled “Output”pretty prints one line, then one line per page that has something to say:
docs/dist: 258 pages annotated. 4163 of 4202 pins placed, 39 listed on their page only. 2 blocks carry a verification. cite/fix/index.html: 1 pin not placed, because its text appears twice: decode-sample-changed.json prints one object:
{ "out": "docs/dist", "pages": 258, "pins": { "total": 4202, "placed": 4163 }, "statements": { "total": 2, "placed": 2 }, "unplaced": [ { "page": "cite/fix/index.html", "kind": "pin", "ids": ["decode-sample-changed"], "reason": "repeated-text" } ], "drifted": []}reason is repeated-text, no-block or no-page. page is the built page
relative to the output, or the source page when it has no built page. A
statement’s ids are its lines.
Arguments after --
Section titled “Arguments after --”Everything after -- is appended verbatim to the command manni runs, after
npm’s own -- where one is needed. The arguments are space-separated, because
they are argv. An argument the shell would split, such as "My Docs", is quoted
for a configured command, so it arrives as one argument. Under preview they go to the serve step, not the build. A
build’s flags belong in site.commands.build. The
built-in server takes no arguments, so preview with
arguments after -- is refused there, exit 2.
$ manni site preview -- --openmanni: Starlight in docs/. Running npm run buildmanni: Starlight in docs/. Annotating docs/dist from docs/public/graph/chunks.json.docs/dist: 258 pages annotated. 4163 of 4202 pins placed, 39 listed on their page only. 2 blocks carry a verification.manni: Starlight in docs/. Running npm run preview -- --host 127.0.0.1 --port 4321 --openstart, build and preview have no -f, --format. They print no report,
so the framework’s own output is the output. annotate takes no arguments
after --, because it runs no framework command.
How the site is found
Section titled “How the site is found”[dir]when it is given.- Else
site.dir, resolved against the config file’s directory. - Else a search, where the first match wins. It looks in the config file’s
directory, or the working directory when there is no config. Then it looks
in that directory’s
docs/,website/andsite/.
In a directory, the markers in the framework table decide the
framework. Fern is the exception to “in a directory”. Its marker is
fern/fern.config.json, and the site’s directory is the parent of fern/.
Two frameworks in one directory are refused, exit 2. A [dir] with no
marker is refused too, unless site.commands sets every command the verb
needs.
Frameworks
Section titled “Frameworks”| Framework | Marker | start | build | preview | Port / host flags | Default port |
|---|---|---|---|---|---|---|
| Mintlify | mint.json, or docs.json with Mintlify’s $schema, or with a theme and a navigation | mint dev | none | none | --port / none | 3000 |
| Fern | fern/fern.config.json | fern docs dev | none | none | --port / none | 3000 |
| Starlight | an @astrojs/starlight dependency | dev, else astro dev | build, else astro build | preview, else astro preview | --port / --host | 4321 |
| Docusaurus | an @docusaurus/core dependency | start, else docusaurus start | build | serve, else docusaurus serve | --port / --host | 3000 |
| VitePress | a vitepress dependency | docs:dev, else vitepress dev <root> | docs:build | docs:preview | --port / --host | 5173 |
| Nextra | a nextra dependency | dev, else next dev | build | start, else next start | --port / --hostname | 3000 |
| Fumadocs | a fumadocs-core dependency | dev, else next dev | build | start, else next start | --port / --hostname | 3000 |
| Rspress | an rspress or @rspress/core dependency | dev, else rspress dev | build | preview | --port / --host | 3000 |
| MkDocs | mkdocs.yml | mkdocs serve | mkdocs build | the built-in server over site/ | -a host:port | 8000 |
| Zensical | zensical.toml, or mkdocs.yml with zensical in the requirements or pyproject.toml | zensical serve | zensical build | the built-in server over site/ | -a host:port | 8000 |
| Sphinx | conf.py or source/conf.py | sphinx-autobuild <src> <src>/_build/html | sphinx-build -M html <src> <src>/_build | the built-in server over _build/html | --port / --host | 8000 |
| Hugo | hugo.toml, hugo.yaml or hugo.json | hugo server | hugo | the built-in server over public/ | --port / --bind | 1313 |
| Jekyll | _config.yml, with a Gemfile naming jekyll or github-pages | jekyll serve | jekyll build | the built-in server over _site/ | --port / --host | 4000 |
| Flare | a .flprj file | none | madbuild -project <flprj> -target <target> | the built-in server over Output/<USERNAME>/<target>/ | none | 4321 |
A dependency counts in dependencies or devDependencies of the site
directory’s package.json.
- A script, else a binary. Where a cell names a script and then a binary,
the script runs when
package.jsonhas it, asnpm run dev. It carries the project’s own flags. Otherwise the binary runs through the package manager. - The package manager. The
packageManagerfield ofpackage.jsonnames it. Without one, the lockfile found walking up to the git root does. Without either, it is npm. - Python and Ruby. MkDocs, Zensical and Sphinx run under
uv runwhen auv.lockis present, orpoetry runfor apoetry.lock. Jekyll runs underbundle exec. - Output directories. The built-in server reads
site.out, else the framework’s default output directory. A customsite_dirorpublishDirneedssite.out. - Flare. The target is
tools.flare.target, else the project’s only target. A project with several targets and none named exits2.madbuildis found onPATH, then in the foldertools.flare.homenames, then in the newestMadCap Flareinstall underProgram Files.USERNAMEis the account running manni, which madbuild writes into the output path. The Flare guide walks through it.
The port and host
Section titled “The port and host”start and preview pick the port and the host in this order:
--portand--host.- The
url:of a declared collection, when its host islocalhost,127.0.0.1or[::1]. - The framework’s default. manni passes no port or host flag then, so the framework’s own default stands.
The url: is the one manni a11y check seeds its crawl from, so the served
site and the crawl agree. When several collections name different local host
and port pairs, manni warns and uses the framework’s default. Neither flag
applies to a command set in site.commands, and passing one is refused.
The built-in server
Section titled “The built-in server”MkDocs, Zensical, Sphinx, Hugo, Jekyll and Flare have no preview server of
their own.
For those, preview serves the build output with a small static server inside
manni.
- It binds
127.0.0.1, or the host chosen above. - It mounts the output at the collection
url:path when there is one, so a site built under/manni/answers there. Collections that share one local server mount at the path they have in common, such as/manni/for/manni/guides/and/manni/api/. - It serves
index.htmlfor a directory, elseDefault.htm, where Flare’s output starts. It serves404.htmlfor a missing path when the output has one. - It serves
.htmland.htmfiles as HTML. - It prints its URL to stdout, as
http://127.0.0.1:8000/.
Configuration
Section titled “Configuration”Every key under site: is optional. This repository’s own config sets only
annotate.after-build, because detection finds Starlight in docs/.
site: # The site's directory, relative to this file. dir: docs # Whole command lines, run through the shell in `dir`. commands: start: pnpm dev --port 4000 build: pnpm build preview: npx serve dist -l 4000 # The built site, relative to `dir`. out: dist annotate: # `build` and `preview` run `annotate` after a successful build. after-build: true| Key | Type | Default | Description |
|---|---|---|---|
site.dir | string | detected | The site’s directory, resolved against the config file. |
site.commands.start | string | detected | Replaces the dev-server command. |
site.commands.build | string | detected | Replaces the build command. preview runs it too. |
site.commands.preview | string | detected | Replaces the serve step of preview. The build still runs first. |
site.out | string | the framework’s output | The built site, relative to site.dir. annotate and the built-in server read it. |
site.annotate.after-build | boolean | false | When true, build and preview run annotate after a successful build. start never annotates. |
A set command replaces the detected one for its verb, and the others stay
detected. When every command a verb needs is set, detection is skipped. So a
framework the table does not list works through config alone, and dir then
defaults to the config file’s directory. Any other key under site: is
refused, exit 2.
Messages and exit codes
Section titled “Messages and exit codes”Every diagnostic goes to stderr, prefixed manni:. Stdout carries the
framework’s own output, the built-in server’s URL and the report of annotate.
| When | Line | Exit |
|---|---|---|
| Before a detected command runs | manni: Starlight in docs/. Running npm run dev -- --host 127.0.0.1 --port 4321 | n/a |
| Before a configured command runs | manni: Running site.commands.start in docs/: pnpm dev --port 4000 | n/a |
| The built-in server is listening (stdout) | http://127.0.0.1:8000/ | n/a |
Ctrl-C or SIGTERM stops a server, or preview during its build | nothing | 0 |
| A build succeeded | the framework’s output | 0 |
| Local collection URLs disagree (a warning) | manni: collections site and api name different local URLs. Using Starlight's default port; pass --port to pick one. | n/a |
| No site found | manni: no docs site found in ./, docs/, website/ or site/. Pass the site's directory, or set site.commands.start in manni.config.yaml. | 2 |
[dir] has no marker | manni: no docs framework detected in website/. Set site.commands.build in manni.config.yaml. | 2 |
| Two frameworks in one directory | manni: docs/ holds more than one docs site: Docusaurus (@docusaurus/core), MkDocs (mkdocs.yml). Set site.commands.start in manni.config.yaml. | 2 |
| No local build (Mintlify, Fern) | manni: Mintlify has no local build. Run manni site start, or set site.commands.build. | 2 |
start on Flare | manni: Flare has no development server. Run manni site build, then manni site preview. | 2 |
| A Flare project with several targets and none named | manni: Widget.flprj has 2 targets. Name the one to build with tools.flare.target. | 2 |
| A Flare project with no targets | manni: Widget.flprj has no targets, so there is nothing to build. Add a target in Flare. | 2 |
tools.flare.target names no target | manni: tools.flare.target "PDF" names no target in Widget.flprj. Its targets are HTML5 and Print. | 2 |
madbuild is not found | manni: madbuild was not found. Install MadCap Flare, or set tools.flare.home to the folder that holds madbuild.exe. | 2 |
preview on Flare before a build | manni: No Flare output at Output/maya/HTML5. Run manni site build first. | 2 |
Flare, with no USERNAME and no system user | manni: manni cannot tell whose Output folder to read, because USERNAME is not set and the system names no user. Set USERNAME to the account that runs madbuild. | 2 |
| The binary is not installed | manni: mint not found on PATH. Install Mintlify's CLI, or set site.commands.start. | 2 |
| A runner manni adds is not installed | manni: uv not found on PATH. Install uv, or set site.commands.start. | 2 |
| The binary cannot start | manni: hugo could not start: spawn EACCES | 2 |
| Node dependencies are not installed | manni: astro is not installed for docs/. Run npm ci in docs/ first. | 2 |
| The command failed | manni: npm run build exited with code 1. | 2 |
| The port is taken (built-in server) | manni: port 8000 is in use. Pass --port, or stop the process holding it. | 2 |
A bad --port | manni: --port must be an integer from 1 to 65535, got "abc". | 2 |
--port with a configured command | manni: --port does not apply to site.commands.start. Put the port in that command. | 2 |
--host with a configured command | manni: --host does not apply to site.commands.start. Put the host in that command. | 2 |
--host on Mintlify or Fern | manni: Mintlify's dev server takes no host option. Drop --host. | 2 |
preview on Mintlify or Fern, with only site.commands.build set | manni: Mintlify has no local preview. Run manni site start, or set site.commands.preview. | 2 |
Arguments after -- for the built-in server | manni: MkDocs previews through manni's built-in server, which takes no extra arguments. Drop the arguments after --. | 2 |
A -c file that does not exist | manni: Config file not found: "ci/manni.config.yaml". | 2 |
[dir] does not exist | manni: website/ does not exist. | 2 |
| An unknown config key | manni.config.yaml: `site:` has unknown key "framework". Supported keys: dir, commands, out, annotate. | 2 |
| A config value of the wrong type | manni.config.yaml: site.commands.start must be a string. | 2 |
| An empty configured command | manni.config.yaml: site.commands.build must not be empty. | 2 |
A wrong after-build type | manni.config.yaml: site.annotate.after-build must be a boolean. | 2 |
annotate prints these. A line under the success line is indented by two
spaces, and names the built page relative to the output.
| When | Line | Exit |
|---|---|---|
| Before the join | manni: Starlight in docs/. Annotating docs/dist from docs/public/graph/chunks.json. | n/a |
| Annotated (stdout) | docs/dist: 258 pages annotated. 4163 of 4202 pins placed, 39 listed on their page only. 2 blocks carry a verification. | 0 |
| A pin’s text appears twice (stdout) | cite/fix/index.html: 1 pin not placed, because its text appears twice: decode-sample-changed. | 0 |
| No block holds a pin’s text (stdout) | key/ci/index.html: 1 pin not placed, because no block holds its text: no-key-message. | 0 |
| No block holds a statement’s lines (stdout) | cite/set-up/index.html: 1 statement not placed, because no block holds lines 53-54. | 0 |
| A page with no built page (stdout) | docs/src/content/docs/x.mdx: no built page at docs/dist/x/index.html. | 0 |
| A block with an error-severity pin (stdout) | cite/index.html: 1 block drifted, because a source changed: fetch-timeout. | 0 |
| A unified framework with no stamps (a warning) | manni: docs/dist carries no line stamps, so blocks are placed by text alone. Add @hawkeyexl/manni/site/rehype to the framework's rehype plugins. | n/a |
| A page source that cannot be read (a warning) | manni: docs/src/content/docs/x.mdx could not be read, so its pins are placed by line alone: ENOENT: no such file or directory, open 'docs/src/content/docs/x.mdx'. | n/a |
| Chunks published outside git | manni: chunks.json carries no commit, so pages get no data-manni-commit. | n/a |
| No built site | manni: no built site at docs/dist. Run manni site build first, or pass --out. | 2 |
| No chunk artifact | manni: docs/public/graph/chunks.json does not exist; run manni graph publish. | 2 |
| Not a chunk manifest | manni: notes.json is not a chunk manifest; run manni graph publish. | 2 |
| An artifact from another graph | manni: docs/public/graph/chunks.json was built from another graph; run manni graph publish. | 2 |
| A Nextra or Fumadocs server build | manni: docs/out does not exist. Nextra writes static pages only with output: "export" in next.config. | 2 |
| Mintlify or Fern | manni: Mintlify builds on its own host, so there is no local site to annotate. | 2 |
| Flare | manni: manni site annotate does not read Flare output. | 2 |
No site found, and no --out | manni: no docs site found in ./, docs/, website/ or site/. Pass the site's directory, or pass --out. | 2 |
No framework, and no --out | manni: no docs framework detected in website/. Pass --out, or set site.out in manni.config.yaml. | 2 |
Two frameworks, and no --out | manni: docs/ holds more than one docs site: Docusaurus (@docusaurus/core), MkDocs (mkdocs.yml). Pass --out, or set site.out in manni.config.yaml. | 2 |
An unknown --format | manni: Unknown --format "sarif". Use pretty or json. | 2 |
A hint that names site.commands.<verb> names the verb that ran. The current
directory shows as ./, as in Starlight in ./. for a site at the root. Two
Node frameworks in one directory are named by their package, in table order.
The missing-dependencies hint names the install command of the package
manager. That is npm ci when an npm lockfile exists, else npm install,
pnpm install, yarn install or bun install. Exit 1 is
never used, because nothing here is a finding. A failed build is an
operational error, and the framework has already printed why.
| Code | Meaning |
|---|---|
0 | The build succeeded, a signal stopped the server, or annotate ran, whatever the pins say. |
2 | Nothing to run, a usage error, or the command manni ran failed. |
Examples
Section titled “Examples”In this repository unless noted.
# The dev server, on the port the collection url: names$ manni site startmanni: Starlight in docs/. Running npm run dev -- --host 127.0.0.1 --port 4321
# A production build, annotated, because site.annotate.after-build is true here$ manni site buildmanni: Starlight in docs/. Running npm run buildmanni: Starlight in docs/. Annotating docs/dist from docs/public/graph/chunks.json.docs/dist: 258 pages annotated. 4163 of 4202 pins placed, 39 listed on their page only. 2 blocks carry a verification.
# Build, annotate, then serve; a11y check in another terminal needs no flags$ manni site previewmanni: Starlight in docs/. Running npm run buildmanni: Starlight in docs/. Annotating docs/dist from docs/public/graph/chunks.json.docs/dist: 258 pages annotated. 4163 of 4202 pins placed, 39 listed on their page only. 2 blocks carry a verification.manni: Starlight in docs/. Running npm run preview -- --host 127.0.0.1 --port 4321
# A directory named, and a port chosen (a Docusaurus repository)$ manni site start website --port 3001manni: Docusaurus in website/. Running npm run start -- --port 3001
# The built-in server (an MkDocs repository with uv)$ manni site previewmanni: MkDocs in docs/. Running uv run mkdocs buildhttp://127.0.0.1:8000/
# A configured command (site.commands.start: pnpm dev --port 4000)$ manni site startmanni: Running site.commands.start in docs/: pnpm dev --port 4000
# Join the graph's pins to the built site, after manni graph publish$ manni site annotatemanni: Starlight in docs/. Annotating docs/dist from docs/public/graph/chunks.json.docs/dist: 258 pages annotated. 4163 of 4202 pins placed, 39 listed on their page only. 2 blocks carry a verification.
# The unplaced pins whose text repeats, for a script$ manni site annotate -f json | jq '.unplaced[] | select(.reason == "repeated-text")'
# Every annotate option at once$ manni site annotate docs --out docs/dist --chunks docs/public/graph/chunks.json -f json -c manni.config.yaml
# Everything at once$ manni site preview docs -c ci/manni.config.yaml --host 0.0.0.0 --port 4000 -- --openmanni: Starlight in docs/. Running npm run buildmanni: Starlight in docs/. Running npm run preview -- --host 0.0.0.0 --port 4000 --openIn CI, start preview in the background and wait for the port before the
crawl. A server started with & returns at once, and the build runs first:
- run: npx @hawkeyexl/manni site preview > preview.log 2>&1 &- run: until curl -fsS http://127.0.0.1:4321/manni/ > /dev/null; do sleep 1; done- run: npx @hawkeyexl/manni a11y check -f github