Skip to content
Kotoshu Kotoshu 言修

Documentation

Ignores & baselines

Inline ignore directives per document format, CI baselines that freeze existing spelling debt, and the shipped pre-commit hook — the three tools for real-world prose.

Real documents are full of words a dictionary will never know — product names, code identifiers quoted in prose, dialect. Kotoshu gives those words three homes: a directive in the document, a baseline for the debt you inherited, and a hook for the commit that would add more.

Inline directives

Ignore words where they live

Instead of polluting the personal dictionary with words only one file uses, mark them in place. Four directives, one semantics:

kotoshu:disable-line                 suppress everything on this line
kotoshu:disable-next-line            suppress the following line
kotoshu:disable-next-line foo bar    suppress only these words, next line
kotoshu:disable-file / enable-file   block suppression (nestable)

Each document format recognizes the directive inside its own comment syntax — and the directive line itself is never spellchecked, because it is an instruction, not prose. Inside a comment the directive must appear at the very start: prose that merely mentions kotoshu:disable-line is not a directive.

FormatFormTrailing directive
Plain texta bare line: kotoshu:disable-linenot supported — in prose, a trailing directive would be ambiguous
Markdown<!-- kotoshu:disable-next-line -->supported
AsciiDoc// kotoshu:disable-next-linesupported — the // must sit at line start or after whitespace, so URLs are not comments

Word lists are only meaningful on disable-next-line — the one form that scopes to chosen words. Blocks nest, and a disable-file that is never re-enabled runs to the end of the file.

<!-- kotoshu:disable-next-line Kotoshu FastText -->
Kotoshu pairs FastText embeddings with Hunspell dictionaries.

// kotoshu:disable-file
generated section: never proofread this
// kotoshu:enable-file

The Ruby API picks suppressions up automatically: Kotoshu.spellchecker_for("en").check(text) moves suppressed words to result.suppressed_errors — each marked suppressed: true, suppressed_by: "inline" — so only real errors stay in result.errors. On the CLI, check --show-suppressed lists them with their [inline] marker.

CI baselines

Freeze the debt, fail the new

Turning Kotoshu on for the first time on a repository with years of documents means one thing: a wall of findings you are not going to fix today. A baseline records that wall — one entry per file and word, with the occurrence count, stable across reformatting — so CI fails only on new errors.

# Record current findings — writes .kotoshu-baseline.json
kotoshu baseline init README.md docs/*.adoc

# CI: errors the baseline covers pass, new errors fail
kotoshu check README.md --baseline .kotoshu-baseline.json

The budget is per file and word: if the baseline recorded three occurrences of teh in a file, the first three pass and the fourth fails. Words the baseline never saw fail on first sight. Errors suppressed by inline directives were filtered upstream and never touch the budget.

Fix some of the debt and the check tells you: a summary line reports baseline entries that have gone stale — the error no longer exists — so the budget shrinks visibly. Refresh by re-running kotoshu baseline init and committing the smaller file.

Machine formats carry the same story: JSON output adds suppressedCount / suppressedErrors plus a baseline block (suppressedCount, staleCount), and SARIF marks baseline-suppressed results with a suppressions entry (justification: "baseline") at note level — so code-review tools show them without failing the upload. The GitHub Action wraps the whole flow.

pre-commit

Stop the debt at the door

The gem repository ships a pre-commit hook definition, so every contributor with the framework gets the same check before the commit lands:

repos:
- repo: https://github.com/kotoshu/kotoshu
rev: v0.9.2   # pin to a released tag
hooks:
- id: kotoshu

The hook matches prose files — .md, .adoc, .rst, .org, and the rest of the text-family extensions — and runs kotoshu check on the staged ones. It exits 1 on any unsuppressed misspelling, so the directive and baseline tools above are exactly what keeps it quiet honestly.

An honest requirement, stated plainly: the hook is language: system — it runs the kotoshu executable from PATH, so the machine needs a Ruby environment with the gem installed (gem install kotoshu, then kotoshu setup en for the languages you write in). A WebAssembly client — the same engine the playground runs — is the future zero-Ruby path.

The personal dictionary reaches the check path

Since kotoshu 0.10.0, kotoshu check consults ~/.config/kotoshu/personal.dic the same way the editor integration always has: a word you added with kotoshu personal add stops being an error on the next check, case-insensitively, with no metadata attached. One load per process; the LSP keeps its mtime-based hot reload. Opt out with --no-personal, KOTOSHU_PERSONAL_DICTIONARY=false, or Kotoshu.configure { personal_dictionary false }. The inline directives below stay the per-file story - the personal dictionary is the cross-file one.

Every directive, flag, and exit code on this page was read from the gem source (lib/kotoshu/documents/suppressions, lib/kotoshu/baseline, lib/kotoshu/cli) and the kotoshu baseline --help output — not inferred. CLI reference · gem README