Skip to content

CLI reference

manni check and manni status are the two verbs of the umbrella that belong to no single domain. Both call other domains’ command cores in process and own no checks of their own. check runs the checks a repository has set up. status says which those are.

Terminal window
manni check [options] [paths...]
manni status [options]

Global options are accepted before the subcommand.

OptionDescription
-V, --versionPrint the manni version and exit.
-h, --helpPrint help for the program or a subcommand.

Run every check the repository has set up, and nothing else. A domain is in play when its own section of manni.config.yaml exists or when the pages carry its declarations. What puts a domain in play has the rule for each. Only error-level findings fail the run, which is the rule each domain applies to its own exit code.

Terminal window
manni check [paths...] [options]
ArgumentRequiredDescription
[paths...]noFiles, directories or globs, space-separated. The per-file checks run on the collection members among them. Without any, every in-play check runs over every collection.
OptionArgumentDefaultDescription
-c, --config<path>discoveredPath to a manni config file, instead of discovery.
-f, --format<format>prettypretty, json, github or sarif. Any other value is refused (exit 2). Not read under a hook.
--no-colorn/acolor on a TTYDisable colored output. Color applies to the pretty output only, on a TTY, and never under NO_COLOR. The same rules as every domain.

check reads no stdin as an input. Unlike meta validate, - does not name standard input. Stdin is read only to look for a hook envelope. A stream that stays silent for 1.5 seconds counts as nothing piped in.

Say what is set up, and so what manni check runs. It reads the config and the pages and runs no check. It always exits 0, except with no config, which exits 2 with the message check uses.

Terminal window
manni status [options]
OptionArgumentDefaultDescription
-c, --config<path>discoveredPath to a manni config file, instead of discovery.
-f, --format<format>prettypretty or json. Any other value is refused (exit 2).

There are no config keys for this. A domain is in play when its own config section exists, or when the content carries its declarations. Nothing manni would apply by default counts as set up. Each row also names the command check runs and the reason status and the skipped line print when the domain is not in play. status prints the next step under the reason.

DomainIn play whenCheckReason when not in playNext step
metameta: exists, or a page names its own $schema. A file covered only by the built-in default schemas is skipped.meta validateno meta: section. With meta: present: only the built-in default schemas cover these files.Add a meta: section that names your schemas. manni meta schemas lists the built-in ones.
citeA page carries citations, as markers or in a citations manifest.cite checkno page carries citationsPin a sentence to its source with manni cite add.
lintlint: exists.lint checkno lint: sectionAdd a lint: section. manni lint templates lists the templates it can apply.
docevalsdocevals: exists, or a page declares evals. A run that resolves no evals counts as not in play.docevals run, with --deterministic-only, no generation and no executionno page resolves any evalsAdd a docevals: section, or declare evals in a page's frontmatter.
termThe collections hold at least one term.term checkno terms in any collectionAdd a page with type: term to a collection.
strategyThe collections hold at least one record, a page of type audience, persona or journey.strategy checkno strategy records in any collectionAdd a page with type: journey to a collection.
graphgraph: exists.graph build in memory, writing no file, then graph checkno graph: sectionRun manni graph init to add a starter graph: section.
a11yNever. check does not run it, since it needs a built site and a browser.noneneeds a running site and a browserRun manni site preview, then manni a11y check.
testNever. check does not run it, since it runs the pages’ shell steps and synthetic users.noneruns the pages' shell steps and synthetic usersRun manni test procedures or manni test tasks.
tracevalsOnly under a Claude Code hook, only when tracevals: holds a runtime: section, and only when MANNI_TRACEVALS_HOOKS is 1. A bare check never runs it, since it needs recorded sessions and a model.none on the command linejudges recorded sessions with a model. With runtime: present and the variable not 1: runtime evals are set up, and turned off here.Run manni tracevals run over sessions, or add tracevals.runtime to judge each turn. With runtime: present and the variable not 1: Turn on the manni plugin's tracevals option, or set MANNI_TRACEVALS_HOOKS=1.

check reads only the files that belong to a collection declared under collections:. A file outside every collection passes silently, with one notice when you named it.

InputPer-file checksWhole-set checks
nothingEvery in-play check over every collection. This is the CI gate.
pathsmeta, cite, lint and docevals on the documents among the paths that belong to a collection.none
a PostToolUse hook for Edit, Write, MultiEdit or NotebookEditmeta, cite, lint and docevals on the edited file.none
a Stop hookmeta, lint and docevals on the working tree’s changed members.cite and strategy. term too, when a collection document changed or was removed. graph never runs here.

With paths, cite check runs on the named pages. On a bare run or at a Stop, it runs over every page that carries citations, since a source edit can drift any of them. term check, strategy check and graph check always cover the whole set. All three run on a bare check, and none runs when you name files. At a Stop, term check runs only when the session changed or removed a collection document, since it reads nothing else. strategy check runs on any change, as cite check does. A page’s personas: and journeys: often sit in its {page}.meta.yaml sidecar, which is not a collection document. graph check never runs at a Stop. It is the slowest whole-set check, and its findings span pages, so CI runs it through a bare check. A session that changed only source code runs cite check and strategy check.

The working tree’s changed members are the files git status reports against HEAD that belong to a collection and have a supported document extension. That covers modified, added, renamed and untracked files. A deleted file is not among them, but a deletion still counts as a change, so cite check and strategy check run. A deleted or renamed collection document also runs term check. A clean tree runs nothing, including the whole-set checks.

Checks run, and print, in a fixed order. For a bare run and for paths it is meta validate, cite check, lint check, docevals run, term check, strategy check, then graph check. At a Stop it is meta validate, lint check, docevals run, cite check, term check, then strategy check, leaving out any that do not run.

The default. Each check that ran prints its own domain’s report under the check’s name. The checks that did not run follow, one line each, then a summary.

Terminal window
$ manni check docs/limits.md
meta validate
✗ docs/limits.md
(root) must have required property 'description' (line 1) [./schemas/page.schema.json]
1 file checked, 0 passed, 1 failed, 1 error
skipped cite check no page carries citations
skipped lint check no lint: section
skipped docevals run no page resolves any evals
4 checks over 1 file: 1 failed, 3 skipped
# exit 1

A line for a check that did not run starts with skipped or error, then the check, then the first line of its message. The summary counts every check, the failed ones, the skipped ones, and, only when there are any, the ones that could not run: 4 checks over 1 file: 0 failed, 2 skipped, 1 could not run.

A run by hand or in CI also rolls every check’s findings up by the readers their pages serve. The roll-up appears when a check reports a finding on a page that names a persona or journey record. In pretty it is one block, after the lines for checks that did not run and before the summary.

Terminal window
$ manni check
…
skipped graph check no graph: section
The pages with findings serve 2 personas and 3 journeys.
devin Devin 3 errors and 1 warning on 2 pages
maya Maya 2 errors on 1 page
m2 Tighten the site 2 errors and 1 warning on 2 pages
m1 Set up the site 2 errors on 1 page
d1 Add the gate (draft) 3 errors on 1 page
7 checks over 10 files: 2 failed, 4 skipped
# exit 1

A finding counts once under every persona and journey its page names, whichever check reported it. So the rows add up to more than the run’s findings. A finding in a page’s {page} manifest, such as index.meta.yaml, counts on the page. A finding on a file that is no page, such as a record or a source, counts nowhere. A baselined finding is not reported, so it counts nowhere either.

What a page names is its personas: and journeys:, read through meta’s merged view. Only values with a record count, and only for a type that has records. The records come from every collection, whatever paths the run names.

  • Order. Personas come first, then journeys. A journey whose lifecycle is draft or archived comes after every live one, and its title gains (draft) or (archived). Then rows run by errors, warnings and notices, most first, then by id in numeric order.
  • Counts. A row names each nonzero severity, then the pages: 16 errors on 5 pages, or 1 error and 2 warnings on 1 page.
  • Color. The heading is bold on a TTY. Nothing else is colored.
  • Absence. No block prints when no reported finding sits on a page that names a record. A hook’s reply never carries one.

The roll-up never changes a finding, a severity or the exit code. When the strategy set cannot be read, the report prints without it, and stderr says so once. Messages has the line.

The github output of each check that ran, concatenated: one ::error or ::warning annotation per finding. A skipped check and a check that could not run print nothing to stdout.

Terminal window
$ manni check -f github
::error file=docs/limits.md,line=1::[./schemas/page.schema.json] (root) must have required property 'description'
# exit 1

When the roll-up is not empty, one ::notice comes first, before every check’s own lines. GitHub shows ten annotations of each level per step, so a notice printed later could be hidden. Its title is the roll-up’s heading. Each line of its message is <id> <title>: <counts>, in the roll-up’s order.

Terminal window
$ manni check -f github | head -1
::notice title=The pages with findings serve 2 personas and 3 journeys.::devin Devin: 3 errors and 1 warning on 2 pages%0Amaya Maya: 2 errors on 1 page%0Am2 Tighten the site: 2 errors and 1 warning on 2 pages%0Am1 Set up the site: 2 errors on 1 page%0Ad1 Add the gate (draft): 3 errors on 1 page

One object. Each report is that domain’s own -f json output, unchanged.

{
"status": "fail",
"files": ["docs/limits.md"],
"checks": [
{ "command": "meta validate", "status": "fail", "report": { "summary": { "files": 1, "passed": 0, "failed": 1, "errors": 1 }, "results": [ … ] } },
{ "command": "cite check", "status": "skipped", "message": "no page carries citations" }
]
}
FieldTypeMeaning
statuspass or failfail when any check failed. A check that could not run does not make it fail, and the exit code is still 2.
filesstring[]The collection members the per-file checks covered, relative to the working directory. On a bare run, every member of every collection.
checks[].commandstringThe check, such as meta validate.
checks[].statuspass, fail, error or skippederror means the check could not run, or its own JSON report did not parse. The other checks’ reports are still there.
checks[].reportobjectOn pass and fail. The domain’s own JSON report.
checks[].messagestringOn error and skipped. Why the check did not run.
impactobject[]The roll-up, after checks, in its order. Absent when empty.
impact[].typepersona or journeyWhich page field named the record.
impact[].idstringThe record’s id.
impact[].titlestringThe record’s title. Absent when the record has none.
impact[].lifecyclestringA journey record’s lifecycle. Absent when the record has none.
impact[].findingsintegerThe reported findings, from every check, on the pages that name the record.
impact[].errors, .warnings, .noticesintegerThe same findings by severity. They sum to findings.
impact[].pagesstring[]Those pages, relative to the working directory as files spells them, sorted.

Each checks[].report is unchanged, so cite’s own report still carries its own impact for its own findings.

Terminal window
$ manni check -f json | jq -r '.impact[] | select(.id == "m2") | .pages[]'
docs/sidecar.md
docs/two-journeys.md

One SARIF 2.1.0 log. Each check that ran adds its domain’s own -f sarif run, unchanged except for two keys:

  • automationDetails.id is manni/<domain>/, such as manni/meta/ or manni/strategy/. meta, cite, term and strategy share the driver manni, so the category is what tells them apart.
  • A run with no invocations gains [{ "executionSuccessful": true }].

A last run, driver manni-check and category manni/check/, holds no results. Its invocation carries one notification for each check that did not run. When the roll-up is not empty, this run also carries it as properties.impact, the same array json prints.

SituationLevelText
The check is skippednote<check> skipped: <reason>.
The check could not runerror<check> could not run: <its error>
The check’s own SARIF does not parseerror<check> -f sarif printed output that is not SARIF.

executionSuccessful is false when any notification is an error. A check that could not run gets no run of its own. An empty run would tell code scanning that every open alert in its category is fixed. The exit code is the same as for every other format.

Terminal window
$ manni check -f sarif > manni.sarif
# exit 1
$ jq -r '.runs[].automationDetails.id' manni.sarif
manni/meta/
manni/check/

Upload the log with no category. One would replace every run’s own, and the runs that share a driver would collide.

Terminal window
$ manni status
manni 4.3.0 config manni.config.yaml collection site (2 files)
meta in play meta: section
cite not set up no page carries citations
Pin a sentence to its source with manni cite add.
lint not set up no lint: section
Add a lint: section. manni lint templates lists the templates it can apply.
docevals not set up no page resolves any evals
Add a docevals: section, or declare evals in a page's frontmatter.
term not set up no terms in any collection
Add a page with type: term to a collection.
strategy not set up no strategy records in any collection
Add a page with type: journey to a collection.
graph not set up no graph: section
Run manni graph init to add a starter graph: section.
a11y not checked needs a running site and a browser
Run manni site preview, then manni a11y check.
test not checked runs the pages' shell steps and synthetic users
Run manni test procedures or manni test tasks.
tracevals not checked judges recorded sessions with a model
Run manni tracevals run over sessions, or add tracevals.runtime to judge each turn.
# exit 0

The first line names the manni version, the config file and each collection with its file count. -f json prints the same facts:

FieldTypeMeaning
versionstringThe manni version.
configstringThe config file, as discovery found it or as -c named it.
collections[]{ name, files }Each declared collection and the number of files it holds.
domains[]{ name, status, reason, next }One row per domain. status is in-play, not-set-up, not-checked or unknown. reason says why, and next says what to do. next is absent on an in-play row. A tracevals row in play also carries models, the hook and extraction { provider, model } pairs, and host when a model host runs.

In pretty, the next step prints on its own line under the reason. Rows that share a next step print it once, under the last of them.

A config with no collections: gives check no files to read. Every domain row is then not set up, with the reason no collections: in manni.config.yaml and the next step Add a collections: entry that names your docs.

A Claude Code hook gives its command one JSON object on stdin. When stdin is not a terminal and holds an object with a string hook_event_name, both verbs treat it as a hook envelope. Detection is by content, so no flag turns it on. Anything else is a run by hand.

An envelope narrows the scope as the table above says, and it switches the output to the hook protocol. It never changes what is checked beyond the scope. Positional paths win over an envelope’s own scope.

The protocol exists because exit 2 means “block” to a hook. An operational error must never use it, so under an envelope check exits 0 for every case except errors found after an edit. -f and --no-color are not read, and notices from the domains are dropped. The reply carries no roll-up of its own.

Hook eventSituationExitOutput
PostToolUseErrors in the edited file2stderr: manni found errors in <file>. Fix them before you continue. The manni:fix skill says how to repair each finding., a blank line, then the failing checks’ pretty reports with color off.
PostToolUseA check could not run0stdout: {"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"manni <check> could not check <file>. The cause is in manni's setup, not in the file. Tell the user what it says, and leave manni.config.yaml as it is.\n<the whole cause>"}}. One such paragraph per check.
PostToolUseThe run itself failed, such as an unreadable config0stdout: the same additionalContext, naming manni check as the check.
StopErrors, stop_hook_active false0stdout: {"decision":"block","reason":"manni found errors in the files you changed. Fix them, then finish. The manni:fix skill says how to repair each finding.\n\n<report>"}
StopErrors, stop_hook_active true0stdout: {"systemMessage":"manni still reports errors after one repair pass. Run manni check to see them."}. The agent stops and you see the message.
anyClean, the file is not a collection member, no config, nothing set up, a tool other than the four, or any other event0nothing

The PostToolUse file is tool_input.file_path, or tool_input.notebook_path for NotebookEdit. It is shown relative to the hook’s cwd. Relative paths resolve from cwd, which is the working directory when the envelope names none.

The Stop run needs git. Outside a repository it prints nothing and exits 0.

When tracevals.runtime exists in manni.config.yaml and MANNI_TRACEVALS_HOOKS is 1, manni check also runs tracevals under a hook envelope. The plugin’s hooks are these. Each runs the first manni the plugin’s launcher finds, as Claude Code describes.

EventMatcherCommandTimeout (s)Runs for
SessionStartstartupmanni tracevals capture60a user who turned tracevals on
SessionStartstartup|resumemanni tracevals prepare, asyncnot enforceda user who turned tracevals on
Stopallmanni check600everyone, for the file checks
SubagentStopallmanni check600a user who turned tracevals on
SessionEndallmanni tracevals release10a user who turned tracevals on

An async hook never blocks the session, and Claude Code does not enforce its timeout. For a user who left the plugin’s tracevals option off, the plugin starts no manni for the four tracevals hooks. Its Stop then runs the file checks alone and never reads the transcript.

manni reads this variable to decide whether the tracevals hooks are on. The plugin sets it to 1 for every manni it starts when its tracevals option is on. Without the plugin, set it yourself, for example in the env block of your own ~/.claude/settings.json.

ValueEffect
1On. check under a Stop or SubagentStop judges the turn where tracevals.runtime exists, and status reports tracevals in play.
unset or emptyOff.
anything elseOff, and one warning on stderr: manni: MANNI_TRACEVALS_HOOKS is "<value>"; only 1 turns the tracevals hooks on.

status reads the environment of the shell that runs it. In your own terminal it shows tracevals as turned off here unless you export the variable there. The session briefing reads the plugin’s option.

EnvelopeWhat runs when runtime exists and the variable is 1
SessionStarttracevals prepare, in the background.
StopThe file checks above, then a judgement of transcript_path with the hook model.
SubagentStopA judgement of agent_transcript_path alone, with the hook model.
SessionEndtracevals release for the session.
nonemanni check does not run tracevals, because there is no session to judge.

Without a runtime section, or with the variable not 1, the judgement never runs, and the tracevals row of status stays not checked. Every reply below exits 0 and writes JSON to stdout.

SituationReply
A rule broken, first stop{"decision":"block","reason":"This turn broke 1 rule from the files and requests that governed it. Fix the work, or say why the rule does not apply here, then finish.\n\n<report>"}
A rule still broken after the repair pass{"systemMessage":"tracevals still finds broken rules after one repair pass. Run manni tracevals check <transcript_path> to see them."}
The local model is still downloading{"systemMessage":"tracevals skipped this turn: <model> is still downloading."}
The model is busy for 120 seconds{"systemMessage":"tracevals skipped this turn: <model> was busy with other judgements for 2 minutes."}
The local model is not on disk{"systemMessage":"tracevals skipped this turn: <model> is not downloaded yet. Run manni tracevals prepare to fetch it."}
The model does not fit in memory{"systemMessage":"tracevals skipped this turn: <model> needs about <n> GB and <n> GB is free."}
Needs review, a clean turn, or an operational failurenothing

The report in a block is the pretty report of tracevals check with color off. Only an error break blocks. Claude Code’s default system prompt only gives warnings, so the hook leaves it out, and tracevals check by hand reports it. When the file checks fail too, the reason carries their paragraph first. After the repair pass, one message carries both sentences. A skip message is said once per session, and a block carries it as a systemMessage beside the reason. A tracevals judgement needs no collection, because it judges a turn rather than files.

In status, a tracevals row replaces not checked when the section exists and the variable is 1. It names both models, and the model host when one runs.

tracevals judges each turn hook: llama-cpp qwen3.5-4b · extraction: anthropic claude-sonnet-5-5
model host qwen3.5-4b loaded, 2 sessions, idle 3m

Only a SessionStart envelope changes status. Its stdout becomes the agent’s context.

SituationExitOutput
At least one domain in play0stdout: the pretty table without its next-step lines, a blank line, then three lines that say when check runs and which skill repairs a finding. The stop line names only the whole-set checks in play, and says the glossary runs when a page changed.
No domain in play, or no collection0stdout: the table alone, without its next-step lines, since there is no check to promise.

The next steps are advice for whoever owns the config, so the agent never sees them. | No config, or a config that cannot be read | 0 | nothing |

After the table, status also appends export MANNI_GENERATED_BY=<model> to the file CLAUDE_ENV_FILE names. That happens only when the envelope carries a model, CLAUDE_ENV_FILE is set, and MANNI_GENERATED_BY is not already set. model is a string, or an object with an id. A trailing context-window suffix such as [1m] is dropped, since it names no model. A value that holds characters outside a model id, such as spaces or quotes, is not written.

Codecheckstatus
0Every check that ran passed, or nothing needed to run. Skipped checks do not change this.The report printed.
1At least one check that ran found an error-level problem.n/a
2Usage or operational error, or a check could not run. See messages. Never used for an operational error under a hook.Usage or operational error.

Each line goes to stderr and starts with manni: unless it is a usage error.

MessageExit
Skipped 1 file outside every collection: README.md, a notice naming each file you passed that is outside every collection. The run continues.unchanged
No manni.config.yaml found from <cwd> up to the repository root. Create one there with a collections: entry for your docs.2
Nothing is set up to check in manni.config.yaml. Run manni status to see what each domain needs. Printed when no collection is declared, and when a bare run finds every check skipped.2
manni <check> could not run: <its error>. That check prints as error, and the other checks still run.2
personas and journeys left out of the report. <strategy's error>, when the strategy set cannot be read for the roll-up. Said once per run, though cite’s own report meets the same error.unchanged
Config file not found: "<path>". for a -c path that does not exist.2
MANNI_TRACEVALS_HOOKS is "<value>"; only 1 turns the tracevals hooks on., once per process, for any value but 1 or empty. The run continues with the tracevals hooks off.unchanged
File not found: "<path>". Check the path. Named paths resolve from the working directory. for a named path that does not exist.2
error: option '-f, --format <format>' argument 'sarf' is invalid. Allowed choices are pretty, json, github, sarif. Unlike the lines above, it has no manni: prefix, and (add --help for usage) follows. status lists pretty, json.2

From the bare minimum to every option at once, then the usage errors. Each rung shows the command, what it prints, and the exit code.

Terminal window
$ manni status
manni 4.3.0 config manni.config.yaml collection site (2 files)
meta in play meta: section
cite not set up no page carries citations
Pin a sentence to its source with manni cite add.
lint not set up no lint: section
Add a lint: section. manni lint templates lists the templates it can apply.
docevals not set up no page resolves any evals
Add a docevals: section, or declare evals in a page's frontmatter.
term not set up no terms in any collection
Add a page with type: term to a collection.
strategy not set up no strategy records in any collection
Add a page with type: journey to a collection.
graph not set up no graph: section
Run manni graph init to add a starter graph: section.
a11y not checked needs a running site and a browser
Run manni site preview, then manni a11y check.
test not checked runs the pages' shell steps and synthetic users
Run manni test procedures or manni test tasks.
tracevals not checked judges recorded sessions with a model
Run manni tracevals run over sessions, or add tracevals.runtime to judge each turn.
# exit 0
$ manni check
meta validate
✗ docs/limits.md
(root) must have required property 'description' (line 1) [./schemas/page.schema.json]
2 files checked, 1 passed, 1 failed, 1 error
skipped cite check no page carries citations
skipped lint check no lint: section
skipped docevals run no page resolves any evals
skipped term check no terms in any collection
skipped strategy check no strategy records in any collection
skipped graph check no graph: section
7 checks over 2 files: 1 failed, 6 skipped
# exit 1
$ manni check docs/index.md
meta validate
1 file checked, 1 passed, 0 failed, 0 errors
skipped cite check no page carries citations
skipped lint check no lint: section
skipped docevals run no page resolves any evals
4 checks over 1 file: 0 failed, 3 skipped
# exit 0
$ manni check -f github
::error file=docs/limits.md,line=1::[./schemas/page.schema.json] (root) must have required property 'description'
# exit 1
$ manni check docs/ -f json --no-color
{ "status": "fail", "files": ["docs/index.md", "docs/limits.md"], "checks": [ … ] }
# exit 1
$ manni check "docs/**/*.md" -c ci/manni.config.yaml -f json --no-color
{ "status": "pass", "files": ["docs/index.md", "docs/limits.md"], "checks": [ … ] }
# exit 0
$ manni check -f sarif > manni.sarif
# exit 1
$ manni check docs/index.md -f sarif
{ "version": "2.1.0", "runs": [ { … "automationDetails": { "id": "manni/meta/" }, "results": [] }, { … "automationDetails": { "id": "manni/check/" } } ] }
# exit 0
$ manni check README.md -f sarif
manni: Skipped 1 file outside every collection: README.md
{ "version": "2.1.0", "runs": [ { … "automationDetails": { "id": "manni/check/" }, "invocations": [ { "executionSuccessful": true } ], "results": [] } ] }
# exit 0
$ manni check "docs/**/*.md" -c ci/manni.config.yaml -f sarif --no-color > manni.sarif
# exit 1

In CI, upload the log once, with no category:

- run: npx -y @hawkeyexl/manni check -f sarif > manni.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: manni.sarif

What a PostToolUse hook runs, after an agent edits a page that now fails. The reply carries only the failing checks’ reports:

Terminal window
$ echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"docs/limits.md"},"cwd":"."}' | manni check
manni found errors in docs/limits.md. Fix them before you continue. The manni:fix skill says how to repair each finding.
meta validate
✗ docs/limits.md
(root) must have required property 'description' (line 1) [./schemas/page.schema.json]
1 file checked, 0 passed, 1 failed, 1 error
# exit 2

The same run at a second Stop, with errors still outstanding:

Terminal window
$ echo '{"hook_event_name":"Stop","stop_hook_active":true,"cwd":"."}' | manni check
{"systemMessage":"manni still reports errors after one repair pass. Run manni check to see them."}
# exit 0

Usage errors, each with its stderr line:

Terminal window
$ manni check -f sarf
error: option '-f, --format <format>' argument 'sarf' is invalid. Allowed choices are pretty, json, github, sarif.
(add --help for usage)
# exit 2
$ manni status -f sarif
error: option '-f, --format <format>' argument 'sarif' is invalid. Allowed choices are pretty, json.
(add --help for usage)
# exit 2
$ manni check
manni: No manni.config.yaml found from /work/demo up to the repository root. Create one there with a collections: entry for your docs.
# exit 2
$ manni check docs/nope.md
manni: File not found: "docs/nope.md". Check the path. Named paths resolve from the working directory.
# exit 2
$ manni check -c nope.yaml
manni: Config file not found: "nope.yaml".
# exit 2