Skip to content

typos-format rewrites file content on every edit with no user-visible disclosure #1578

Description

@kyle-sexton

Summary

typos-format rewrites the content of the user's files on every Write/Edit of every file type
and, on the path where it succeeds, says nothing at all. A correction drawn from typos-cli's built-in
dictionary that is wrong for the repository — an acronym, an identifier, a proper noun mapped to an
unrelated English word — therefore lands in the file invisibly and is indistinguishable from a benign
reformat.

Observed in a real consuming session: multiple evidence files silently corrupted, a self-contradicting
line in a written record, and a test fixture name made ambiguous. The corrupting hook was never named
once across the whole session.

Why it is invisible

plugins/typos-format/hooks/typos-format.sh runs typos --write-changes --force-exclude --format json
and inspects the result only when it is non-zero.

Verified empirically against typos-cli 1.44.0: --write-changes emits nothing for a correction it
applies.
A file whose findings are all auto-fixable exits 0 with completely empty stdout after
rewriting the file:

$ typos --format json work.txt          # read-only
{"type":"typo",...,"typo":"teh","corrections":["the"]}
RC=2
$ typos --write-changes --format json work.txt
RC=0
OUTPUT=[]                                # nothing. The rewrite is unreported.

So the RC-0 branch has no information to report even if it wanted to, and it emits no
additionalContext, no systemMessage — telemetry only. The harness's own signal is a generic
"PostToolUse hook modified <file> after your edit (likely a formatter)" line that names no hook and
shows no diff, and the hooks reference (https://code.claude.com/docs/en/hooks, fetched 2026-07-26)
documents no file-change or diff channel a hook could use instead.

Second half: the remediation advice is on the wrong branch

The hook already knows the right fix — "if intentional, add it to extend-words /
extend-identifiers in your typos config" — but that text sits only on the residual-findings branch,
which by construction fires for findings that were not applied. It therefore never fires for the
corrections that actually change file content.

This matters because the autocorrect has no memory. Repairing a mangled word by hand gets it
re-corrected on the very next save; the allow-list entry is the only thing that stops it, and nothing
told the user that.

Expected

  1. Every correction the hook applies is reported: the token, its replacement, and the line — on the
    agent channel and on the user channel, since the person whose file changed is the only one who
    can judge whether the change was correct.
  2. The allow-list remediation rides on the applied-correction path, not only the residual one.
  3. The disclosure is bounded, so a file with hundreds of corrections cannot turn the fix into a
    context flood.
  4. A way to run the hook report-only, without modifying files at all.

Also in scope

plugins/typos-format/hooks/typos-format.test.sh skips its entire suite when no typos binary is
present — which is the CI runner's state. Nothing about this hook is gated in CI today, so a
regression in the disclosure contract would land green.

Environment

typos-cli 1.44.0, Windows 11 / Git Bash. The dictionary corrections are typos-cli-version-specific;
the invisibility is not.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: highSignificant impact, or blocks an imminent release; staff this cycle.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions