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.
manni check [options] [paths...]manni status [options]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. |
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.
manni check [paths...] [options]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
[paths...] | no | Files, 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. |
Options
Section titled “Options”| Option | Argument | Default | Description |
|---|---|---|---|
-c, --config | <path> | discovered | Path to a manni config file, instead of discovery. |
-f, --format | <format> | pretty | pretty, json, github or sarif. Any other value is refused (exit 2). Not read under a hook. |
--no-color | n/a | color on a TTY | Disable 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.
status
Section titled “status”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.
manni status [options]Options
Section titled “Options”| Option | Argument | Default | Description |
|---|---|---|---|
-c, --config | <path> | discovered | Path to a manni config file, instead of discovery. |
-f, --format | <format> | pretty | pretty or json. Any other value is refused (exit 2). |
What puts a domain in play
Section titled “What puts a domain in play”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.
| Domain | In play when | Check | Reason when not in play | Next step |
|---|---|---|---|---|
meta | meta: exists, or a page names its own $schema. A file covered only by the built-in default schemas is skipped. | meta validate | no 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. |
cite | A page carries citations, as markers or in a citations manifest. | cite check | no page carries citations | Pin a sentence to its source with manni cite add. |
lint | lint: exists. | lint check | no lint: section | Add a lint: section. manni lint templates lists the templates it can apply. |
docevals | docevals: 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 execution | no page resolves any evals | Add a docevals: section, or declare evals in a page's frontmatter. |
term | The collections hold at least one term. | term check | no terms in any collection | Add a page with type: term to a collection. |
strategy | The collections hold at least one record, a page of type audience, persona or journey. | strategy check | no strategy records in any collection | Add a page with type: journey to a collection. |
graph | graph: exists. | graph build in memory, writing no file, then graph check | no graph: section | Run manni graph init to add a starter graph: section. |
a11y | Never. check does not run it, since it needs a built site and a browser. | none | needs a running site and a browser | Run manni site preview, then manni a11y check. |
test | Never. check does not run it, since it runs the pages’ shell steps and synthetic users. | none | runs the pages' shell steps and synthetic users | Run manni test procedures or manni test tasks. |
tracevals | Only 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 line | judges 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.
What each scope runs
Section titled “What each scope runs”| Input | Per-file checks | Whole-set checks |
|---|---|---|
| nothing | Every in-play check over every collection. This is the CI gate. | |
| paths | meta, cite, lint and docevals on the documents among the paths that belong to a collection. | none |
a PostToolUse hook for Edit, Write, MultiEdit or NotebookEdit | meta, cite, lint and docevals on the edited file. | none |
a Stop hook | meta, 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.
Output
Section titled “Output”pretty
Section titled “pretty”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.
$ manni check docs/limits.mdmeta 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 citationsskipped lint check no lint: sectionskipped docevals run no page resolves any evals
4 checks over 1 file: 1 failed, 3 skipped# exit 1A 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.
Who the findings reach
Section titled “Who the findings reach”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.
$ 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 1A 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
lifecycleisdraftorarchivedcomes 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, or1 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.
github
Section titled “github”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.
$ manni check -f github::error file=docs/limits.md,line=1::[./schemas/page.schema.json] (root) must have required property 'description'# exit 1When 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.
$ 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 pageOne 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" } ]}| Field | Type | Meaning |
|---|---|---|
status | pass or fail | fail when any check failed. A check that could not run does not make it fail, and the exit code is still 2. |
files | string[] | The collection members the per-file checks covered, relative to the working directory. On a bare run, every member of every collection. |
checks[].command | string | The check, such as meta validate. |
checks[].status | pass, fail, error or skipped | error means the check could not run, or its own JSON report did not parse. The other checks’ reports are still there. |
checks[].report | object | On pass and fail. The domain’s own JSON report. |
checks[].message | string | On error and skipped. Why the check did not run. |
impact | object[] | The roll-up, after checks, in its order. Absent when empty. |
impact[].type | persona or journey | Which page field named the record. |
impact[].id | string | The record’s id. |
impact[].title | string | The record’s title. Absent when the record has none. |
impact[].lifecycle | string | A journey record’s lifecycle. Absent when the record has none. |
impact[].findings | integer | The reported findings, from every check, on the pages that name the record. |
impact[].errors, .warnings, .notices | integer | The same findings by severity. They sum to findings. |
impact[].pages | string[] | 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.
$ manni check -f json | jq -r '.impact[] | select(.id == "m2") | .pages[]'docs/sidecar.mddocs/two-journeys.mdOne SARIF 2.1.0 log. Each check that ran adds its domain’s own -f sarif run,
unchanged except for two keys:
automationDetails.idismanni/<domain>/, such asmanni/meta/ormanni/strategy/. meta, cite, term and strategy share the drivermanni, so the category is what tells them apart.- A run with no
invocationsgains[{ "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.
| Situation | Level | Text |
|---|---|---|
| The check is skipped | note | <check> skipped: <reason>. |
| The check could not run | error | <check> could not run: <its error> |
| The check’s own SARIF does not parse | error | <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.
$ manni check -f sarif > manni.sarif# exit 1$ jq -r '.runs[].automationDetails.id' manni.sarifmanni/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.
status output
Section titled “status output”$ manni statusmanni 4.3.0 config manni.config.yaml collection site (2 files)
meta in play meta: sectioncite 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 0The first line names the manni version, the config file and each collection with
its file count. -f json prints the same facts:
| Field | Type | Meaning |
|---|---|---|
version | string | The manni version. |
config | string | The 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.
Hook protocol
Section titled “Hook protocol”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 event | Situation | Exit | Output |
|---|---|---|---|
PostToolUse | Errors in the edited file | 2 | stderr: 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. |
PostToolUse | A check could not run | 0 | stdout: {"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. |
PostToolUse | The run itself failed, such as an unreadable config | 0 | stdout: the same additionalContext, naming manni check as the check. |
Stop | Errors, stop_hook_active false | 0 | stdout: {"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>"} |
Stop | Errors, stop_hook_active true | 0 | stdout: {"systemMessage":"manni still reports errors after one repair pass. Run manni check to see them."}. The agent stops and you see the message. |
| any | Clean, the file is not a collection member, no config, nothing set up, a tool other than the four, or any other event | 0 | nothing |
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.
tracevals under hooks
Section titled “tracevals under hooks”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.
| Event | Matcher | Command | Timeout (s) | Runs for |
|---|---|---|---|---|
SessionStart | startup | manni tracevals capture | 60 | a user who turned tracevals on |
SessionStart | startup|resume | manni tracevals prepare, async | not enforced | a user who turned tracevals on |
Stop | all | manni check | 600 | everyone, for the file checks |
SubagentStop | all | manni check | 600 | a user who turned tracevals on |
SessionEnd | all | manni tracevals release | 10 | a 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_TRACEVALS_HOOKS
Section titled “MANNI_TRACEVALS_HOOKS”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.
| Value | Effect |
|---|---|
1 | On. check under a Stop or SubagentStop judges the turn where tracevals.runtime exists, and status reports tracevals in play. |
| unset or empty | Off. |
| anything else | Off, 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.
| Envelope | What runs when runtime exists and the variable is 1 |
|---|---|
SessionStart | tracevals prepare, in the background. |
Stop | The file checks above, then a judgement of transcript_path with the hook model. |
SubagentStop | A judgement of agent_transcript_path alone, with the hook model. |
SessionEnd | tracevals release for the session. |
| none | manni 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.
| Situation | Reply |
|---|---|
| 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 failure | nothing |
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 3mstatus
Section titled “status”Only a SessionStart envelope changes status. Its stdout becomes the agent’s
context.
| Situation | Exit | Output |
|---|---|---|
| At least one domain in play | 0 | stdout: 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 collection | 0 | stdout: 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.
Exit codes
Section titled “Exit codes”| Code | check | status |
|---|---|---|
0 | Every check that ran passed, or nothing needed to run. Skipped checks do not change this. | The report printed. |
1 | At least one check that ran found an error-level problem. | n/a |
2 | Usage or operational error, or a check could not run. See messages. Never used for an operational error under a hook. | Usage or operational error. |
Messages
Section titled “Messages”Each line goes to stderr and starts with manni: unless it is a usage error.
| Message | Exit |
|---|---|
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 |
Examples
Section titled “Examples”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.
$ manni statusmanni 4.3.0 config manni.config.yaml collection site (2 files)
meta in play meta: sectioncite 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 checkmeta 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 citationsskipped lint check no lint: sectionskipped docevals run no page resolves any evalsskipped term check no terms in any collectionskipped strategy check no strategy records in any collectionskipped graph check no graph: section
7 checks over 2 files: 1 failed, 6 skipped# exit 1
$ manni check docs/index.mdmeta validate1 file checked, 1 passed, 0 failed, 0 errors
skipped cite check no page carries citationsskipped lint check no lint: sectionskipped 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 sarifmanni: 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 1In 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.sarifWhat a PostToolUse hook runs, after an agent edits a page that now fails.
The reply carries only the failing checks’ reports:
$ echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"docs/limits.md"},"cwd":"."}' | manni checkmanni 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 2The same run at a second Stop, with errors still outstanding:
$ 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 0Usage errors, each with its stderr line:
$ manni check -f sarferror: option '-f, --format <format>' argument 'sarf' is invalid. Allowed choices are pretty, json, github, sarif.(add --help for usage)# exit 2
$ manni status -f sariferror: option '-f, --format <format>' argument 'sarif' is invalid. Allowed choices are pretty, json.(add --help for usage)# exit 2
$ manni checkmanni: 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.mdmanni: File not found: "docs/nope.md". Check the path. Named paths resolve from the working directory.# exit 2
$ manni check -c nope.yamlmanni: Config file not found: "nope.yaml".# exit 2