| name | introduce-codespell |
| description | Introduce codespell spell-checking to a project. Creates branch, config file, CI workflow (GitHub Actions or Forgejo Actions for Codeberg), pre-commit hook. Lists typos, helps identify paths/words to ignore, and fixes typos interactively. Use when setting up codespell in a new project. |
| allowed-tools | Bash, Read, Edit, Write, Glob, Grep, AskUserQuestion |
| user-invocable | true |
Introduce Codespell to a Project
Set up codespell spell-checking infrastructure in a project, identify typos, configure exclusions, and fix spelling errors.
When to Use
- User wants to add spell-checking to a project
- User mentions "codespell" or wants to check for typos
- User wants to set up CI for spelling errors
- User asks to "introduce codespell" or run "/introduce-codespell"
Prerequisites
codespell must be available (can use uvx codespell if not installed)
gh CLI for PR creation (optional)
- Git repository
Commit Co-Authorship
All commits created during this workflow MUST include a Co-Authored-By trailer identifying
both Claude Code version and the model used. Get the version via claude --version and
use the model name from the environment. Format:
Co-Authored-By: Claude Code <VERSION> / Claude <MODEL> <noreply@anthropic.com>
Example:
Co-Authored-By: Claude Code 2.1.63 / Claude Opus 4.6 <noreply@anthropic.com>
This applies to ALL commits: config, workflow, ambiguous fixes, non-ambiguous fixes, formatting fixups.
Workflow Overview
- Check existing: See if codespell is already configured
- Setup: Create branch and initial configuration
- Analyze: List all detected typos + historical typo fix stats
- Configure exclusions: Identify paths and words to ignore
- Fix: Apply fixes for legitimate typos
- Review: Check for functional fixes (potential bug fixes)
- Finalize: Prepare PR message and create PR
Step 0: Check for Existing Codespell Integration
Before creating anything, check if codespell is already configured:
grep -l codespell pyproject.toml setup.cfg .codespellrc .pre-commit-config.yaml 2>/dev/null
ls .github/workflows/*codespell* .forgejo/workflows/*codespell* 2>/dev/null
Detect CI Platform
Determine whether the project uses GitHub, Codeberg/Forgejo, or another platform:
git remote -v
| Remote URL contains | Platform | Workflow directory | Runner label |
|---|
github.com | GitHub | .github/workflows/ | ubuntu-latest |
codeberg.org | Codeberg (Forgejo) | .forgejo/workflows/ | docker |
| Other Forgejo instance | Forgejo | .forgejo/workflows/ | docker (check with admin) |
Note: If the project is mirrored on multiple platforms, create workflows for each.
Forgejo Actions are disabled by default on Codeberg — the repo owner must enable them
in Settings > Units > Overview.
If codespell is already configured:
-
Run codespell to see if it passes:
uvx codespell
-
If clean: Project is already well-maintained, may not need changes
-
If not clean: Review and tune existing config:
-
If partially configured (e.g., config but no CI): Add missing pieces
Audit runner files for hardcoded codespell options (MANDATORY)
The central config (pyproject.toml / .codespellrc / setup.cfg) is only
authoritative if every runner reads it. A runner that invokes codespell with
its own --skip, --ignore-words-list, or --ignore-regex flags will silently
diverge from the central config — and worse, may mask errors that a
parallel runner (e.g. a CI workflow that runs codespell directly) does see.
Real incident: tox.ini had codespell . --skip="*.svg,*.html,*.bib,*.ipynb".
The tox-driven check passed because HTML test fixtures and a BibTeX file were
inline-skipped. When a GitHub Actions workflow was added that runs codespell
directly (not via tox), it surfaced 18 "errors" the skill spent time
investigating before discovering tox.ini had been hiding them all along. The
fix: remove the --skip="..." from tox.ini and migrate every pattern that
actually mattered into the central config so all runners agree.
Grep every runner-style file for codespell invocations and flag any that pass
their own options:
grep -nE 'codespell([^a-zA-Z_-]|$)' \
tox.ini Makefile noxfile.py \
.pre-commit-config.yaml \
.github/workflows/*.y*ml .forgejo/workflows/*.y*ml .gitlab-ci.y*ml \
package.json scripts/* bin/* \
2>/dev/null
For each hit, examine the line — anything beyond a bare codespell or
codespell . is a candidate to migrate:
| Pattern on the runner line | What to do |
|---|
codespell --skip="..." ... | Migrate paths into central skip; remove the flag |
codespell -I words.txt / --ignore-words-list=... | Migrate words into central ignore-words-list; remove the flag |
codespell --ignore-regex='...' | Migrate regex into central ignore-regex (combine with | if needed); remove the flag |
codespell <explicit-file-list> | Usually fine for pre-commit hooks that pass changed files; but note that explicit file arguments bypass skip patterns — verify the central config's ignore-* still applies and that no skipped file can be passed in |
codespell . (bare) | Already correct — reads central config |
When you migrate options, commit the runner-file change before running
codespell to analyze typos (Step 5). Otherwise the analysis will reflect the
runner's masking, not the project's real state. Suggested commit message:
Drop hardcoded codespell options from <runner-file>
Migrated to central config in <pyproject.toml/.codespellrc/setup.cfg>
so every runner (tox, pre-commit, CI) sees the same skip / ignore set.
Previously the inline --skip masked issues that <other-runner> surfaced.
Why this matters specifically for pre-commit: the pre-commit hook for
codespell (Step 4.2) passes file paths to codespell, which bypasses
directory-traversal skip patterns. If a path is only excluded via
central-config skip, pre-commit will still try to check it (codespell will
error per the explicit args). Use pre-commit's own exclude: regex to align
the hook with the central skip list:
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
exclude: '^(petprep/data/tests/|\.mailmap$)'
If no codespell configuration exists:
Proceed with Step 1 to set up from scratch.
Step 1: Initial Setup
Create Feature Branch
git checkout -b enh-codespell
Gather Historical Typo Fix Stats
Check git history for prior manual typo fixes to demonstrate value:
git log --oneline --all --grep="typo" --grep="spell" --grep="spelling" | wc -l
git log --oneline --all --grep="typo" | head -10
This shows the project has had typo issues before, justifying the addition of automated checking.
Determine Configuration Location
Choose based on existing project structure (in order of preference):
pyproject.toml - if exists, add [tool.codespell] section
setup.cfg - if exists, add [codespell] section
.codespellrc - fallback standalone config
Detect Files to Skip
Scan for common file types that should be skipped:
- Binary/generated:
*.pdf, *.svg, *.ai, *.min.*, *-min.*, *.pack.js
- Dependencies:
go.sum, package-lock.json, *.lock, *-lock.yaml, vendor/
- Virtual envs:
venv/, .venv/, venvs/, .tox/
- Cache directories:
.npm/, .cache/ (npm/uv caches - should NEVER be committed to git)
- Localization/i18n:
*/i18n/* (use wildcards - foreign language translations)
- Build artifacts:
*/build/* (Sphinx docs, compiled output - may be untracked)
- External/samples:
samples/, third_party/, vendor/ (external content)
- Data files:
*.niml, *.gii, *.pgm
- CSS:
*.css (often has vendor prefixes flagged as typos)
- Generated:
versioneer.py
Important: Check for untracked local directories (like docs/build/) that may contain
build artifacts. These won't show in git ls-files but will be scanned by codespell.
Initial Config Template
For .codespellrc:
[codespell]
skip = .git,.gitignore,.gitattributes,*.pdf,*.svg,*.css,*.min.*,.npm,.cache,*/i18n/*,*/build/*
check-hidden = true
For pyproject.toml:
[tool.codespell]
skip = '.git,.gitignore,.gitattributes,*.pdf,*.svg,*.css,*.min.*,.npm,.cache,*/i18n/*,*/build/*'
check-hidden = true
Jupyter Notebook Handling
If *.ipynb files exist, add ignore-regex for embedded images:
ignore-regex = ^\s*"image/\S+": ".*
Step 2: Create CI Workflow
Create the workflow file for the appropriate platform (detected in Step 0).
GitHub Actions
Create .github/workflows/codespell.yml:
---
name: Codespell
on:
push:
branches: [<MAIN_BRANCH>]
pull_request:
branches: [<MAIN_BRANCH>]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Codespell
uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579
Note: Problem matcher annotations are now built into actions-codespell@v2 (and later versions) - no need for a separate codespell-problem-matcher step.
Forgejo Actions (Codeberg and other Forgejo instances)
Create .forgejo/workflows/codespell.yml:
---
name: Codespell
on:
push:
branches: [<MAIN_BRANCH>]
pull_request:
branches: [<MAIN_BRANCH>]
jobs:
codespell:
name: Check for spelling errors
runs-on: docker
container:
image: ghcr.io/codespell-project/actions-codespell/master:latest
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v6
- name: Codespell
run: codespell .
Key differences from GitHub Actions:
- Directory:
.forgejo/workflows/ (NOT .github/workflows/)
- Runner:
runs-on: docker — Codeberg's shared runners use Docker containers
- Container image: Uses
ghcr.io/codespell-project/actions-codespell/master:latest
which has codespell pre-installed (avoids pip install step)
- Checkout action: Use fully qualified URL
https://code.forgejo.org/actions/checkout@v6
(Forgejo mirrors of common actions). Checkout is NOT automatic — you MUST include it.
- No
permissions block: Forgejo Actions doesn't support the permissions key
- codespell invocation: Run
codespell . directly (the action wrapper
codespell-project/actions-codespell@... is GitHub-specific)
- Enabling: Forgejo Actions are disabled by default on Codeberg repositories.
The repo owner must enable them in Settings > Units > Overview.
Both Platforms
If the project is mirrored on both GitHub and Codeberg, create both workflow files.
They can coexist — each platform only reads its own directory.
Commit: "Add CI workflow to codespell on push and PRs"
Step 3: Commit Initial Config
git add <CONFIG_FILE>
git commit -m 'Add rudimentary codespell config'
Step 4: Add Pre-commit Hook (if applicable)
4.1 Detect Pre-commit Config
ls .pre-commit-config.yaml 2>/dev/null
If .pre-commit-config.yaml exists, you MUST take pre-commit into account for the rest of this workflow. This is critical — see Step 4.3 below for why.
4.2 Add Codespell Hook to Pre-commit Config
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
For pyproject.toml config, add:
additional_dependencies:
- tomli; python_version<'3.11'
Commit: "Add pre-commit definition for codespell"
4.3 Install and Run Pre-commit (CRITICAL when formatters/linters are present)
Why this matters: Pre-commit configs commonly include reformatters (black, ruff-format) and linters (ruff, flake8, isort). Inline edits made later in this workflow — especially:
- Adding
codespell:ignore comments to protect regex patterns or variable names (Step 7.6)
- Manual edits to fix ambiguous typos via the Edit tool (Step 7.3)
- Realigning tables / RST underlines (Step 8)
— can trigger formatter/linter changes (line length, trailing whitespace, comment placement, import order). If we do not run pre-commit, those changes ride along in a later commit by the maintainer, fragmenting the history and forcing the user to amend our commits. One past incident required rewriting commits because codespell:ignore comments tripped ruff-format and we never ran the formatter.
Inspect which hooks could reformat code
Before installing, scan .pre-commit-config.yaml for hooks that modify files (so you know which steps below need a re-run):
| Hook id | Modifies files? | Notes |
|---|
black, ruff-format, autopep8, yapf | yes | Python reformatters |
ruff (with --fix), isort, autoflake | yes | Linters with auto-fix |
prettier, eslint --fix | yes | JS/TS formatters |
trailing-whitespace, end-of-file-fixer, mixed-line-ending | yes | Whitespace fixers |
nbstripout | yes | Strips notebook outputs |
flake8, mypy, check-yaml, check-added-large-files | no | Pure linters/checkers |
If any modifying hook is present, you MUST run pre-commit after every manual edit step (7.3, 7.6, 8) before committing. If only pure checkers are present, running pre-commit is still recommended but less critical.
Install pre-commit (if not already available)
pre-commit --version || uvx pre-commit --version
pre-commit install || uvx pre-commit install
uvx pre-commit ... is preferred when pre-commit is not on $PATH — it avoids polluting the project's environment.
Run pre-commit after edits
After every manual edit step in this workflow that touches source files (7.3 ambiguous fixes, 7.6 false-positive protections, 8 formatting fixups), run pre-commit on the touched files before committing:
pre-commit run --files <file1> <file2> ...
pre-commit run --all-files
If pre-commit modifies files (exit code 1 with "files were modified by this hook"), git add the changes and re-run to confirm a clean pass, then commit. Do not bypass with --no-verify — that defeats the purpose.
Order of operations within a fix step
- Make the manual edit (e.g., add
// codespell:ignore foo)
git add <file>
pre-commit run --files <file> — apply any reformatting/linting
git add <file> again if pre-commit modified it
- Re-run
pre-commit run --files <file> until clean
git commit -F .git-meta/COMMIT_MSG
This keeps each logical change (typo fix + its required reformatting) in a single atomic commit.
What about codespell -w runs (Step 9)?
When using datalad run 'codespell -w', codespell will modify files. After the datalad run completes, run pre-commit on the modified files and amend the commit if reformatting is needed:
datalad run -m "..." 'codespell -w'
pre-commit run --all-files || pre-commit run --all-files
git add -A && git commit --amend --no-edit
Prefer a separate fixup: ... commit over --amend when the formatting changes are substantial or touch files outside what codespell modified — it keeps the datalad run provenance intact.
Step 5: Analyze Typos
Run codespell to list all detected issues:
uvx codespell 2>&1 | head -200
Categorize Results
Group typos into categories:
- Legitimate typos to fix - actual spelling errors in code/docs
- False positives - paths to skip - vendored code, generated files, data directories
- False positives - words to ignore - domain-specific terms, variable names, abbreviations
Get Sorted Hit Counts
uvx codespell 2>&1 | grep -e '==>' | sed -e 's,.*: *,,g' | sort | uniq -c | sort -n
This shows which "typos" appear most frequently - often these are false positives that need exclusion.
Step 6: Configure Exclusions
Adding Paths to Skip
Update config skip field with additional paths:
- Vendored code directories (e.g.,
statics/, vendor/, third_party/)
- Generated documentation
- Test fixtures with intentional misspellings
- Binary or data file directories
Handling camelCase/PascalCase Identifiers (IMPORTANT!)
BEFORE adding individual identifiers to ignore-words-list, check if you can use regex instead:
If you detect 3 or more camelCase or PascalCase false positives (e.g., doubleClick, hasTables, OptIn), use regex patterns:
camelCase Pattern (starts lowercase, has uppercase):
\b[a-z]+[A-Z]\w*\b
Matches: doubleClick, hasTables, addRes, prevEnd, pixelX
PascalCase Pattern (starts uppercase, mixed case):
\b[A-Z][a-z]+[A-Z]\w*\b
Matches: OptIn, OptionA
Combined Pattern (recommended):
\b[a-z]+[A-Z]\w*\b|\b[A-Z][a-z]+[A-Z]\w*\b
Add to Configuration with Comment:
For .codespellrc (INI format):
[codespell]
skip = .git,.gitignore,.gitattributes,*.svg,dist
check-hidden = true
ignore-regex = \b[a-z]+[A-Z]\w*\b|\b[A-Z][a-z]+[A-Z]\w*\b
ignore-words-list = inout,rouge,caf
For pyproject.toml:
[tool.codespell]
skip = '.git,.gitignore,.gitattributes,*.svg,dist'
check-hidden = true
ignore-regex = '\b[a-z]+[A-Z]\w*\b|\b[A-Z][a-z]+[A-Z]\w*\b'
ignore-words-list = 'inout,rouge,caf'
Always add a descriptive comment above ignore-regex explaining what's being ignored and why.
Adding Words to ignore-words-list
Use ignore-words-list for case-insensitive word exclusions that DON'T fit the regex patterns above:
What goes in ignore-words-list:
- Language keywords:
inout (Swift), afterall (test frameworks)
- Domain-specific terms:
rouge (syntax highlighter), statics (directory name)
- File extensions:
caf (Core Audio Format)
- Short codes/abbreviations:
nd (2nd), ot (ANSI escape), fo (Windows flag)
- Legitimate words in special contexts: Words correct in the project's domain
What should NOT go in ignore-words-list (use regex instead):
- camelCase identifiers:
doubleClick, hasTables, addRes
- PascalCase identifiers:
OptIn, OptionA
Example of a clean config:
ignore-regex = \b[a-z]+[A-Z]\w*\b|\b[A-Z][a-z]+[A-Z]\w*\b
ignore-words-list = inout,rouge,statics,caf,afterall,nd,ot,fo
Multi-line ignore-words-list with per-word inline comments (.codespellrc only)
Once the list grows past a handful of entries, the comma-separated single
line becomes opaque — future readers (and future-you) can't tell why each
word is there, and PR reviewers can't sanity-check additions. In a
.codespellrc file, codespell honors INI continuation lines and #
comments inside the value, so you can split the list and document each
entry inline:
[codespell]
skip = .git,*.pdf,*.svg,go.sum,go.mod
check-hidden = true
ignore-regex = https?://\S+|\b[a-z]+[A-Z]\w*\b
ignore-words-list =
ser,
ist,
fo,
iinclude,
doesnt
Rules of thumb:
- One word per line, trailing comma after each (the last entry's comma is optional).
- Place the
# comment on the line immediately above the word, not on the same line — codespell's INI parser treats word, # comment as part of the value in some versions; putting # at column 0 of the previous line is safer and is what restic and similar projects do.
- Lead each comment with the reason the word is whitelisted (variable abbreviation, intentional test fixture, CLI flag, domain term, library name, etc.), not just a definition. The reason is what lets a future reviewer decide whether the entry is still needed.
- Real example from restic: https://github.com/restic/restic/pull/21807 — see
.codespellrc showing this exact pattern.
- This format works in
.codespellrc (INI) — but NOT in pyproject.toml where ignore-words-list is a single TOML string. For pyproject.toml, keep the single-line comma-separated form and document words with # comments on adjacent lines outside the value, or migrate to .codespellrc if per-word commentary matters.
When to adopt this format:
- The list has more than ~5 entries
- Multiple contributors have added entries over time and you can no longer tell what each one is for
- You're about to add an entry that isn't self-explanatory (e.g. a 2–3 letter abbreviation, an intentional-misspelling fixture, a vendor / package name)
Using ignore-regex for Other Pattern Exclusions
For more complex cases beyond camelCase/PascalCase, use regex:
ignore-regex = ^\s*"image/\S+": ".*|\\bTEH\\b|\b[a-z]+[A-Z]\w*\b
Comment guidelines:
- Place comment on the line immediately above
ignore-regex
- Use
# for INI/config files
- Briefly explain what patterns are being ignored
- Be specific (e.g., "Ignore camelCase identifiers" not just "Ignore patterns")
Step 7: Fix Typos - Proper Workflow
CRITICAL WORKFLOW: Handle ambiguous typos manually FIRST, then use codespell -w for non-ambiguous ones.
⚠️ CRITICAL REMINDER: After applying ALL fixes, you MUST review the complete diff for false positives before proceeding to the PR step. See Step 7.6 for details.
IMPORTANT: If redoing fixes after a previous attempt, tag the current state first:
git tag -f _enh-codespell-prev
This allows easy comparison with git diff _enh-codespell-prev..HEAD to review what changed.
7.1 Preview All Proposed Fixes
First, get the full list of typos codespell would fix:
uvx codespell 2>&1
7.2 Categorize Typos
For each detected typo, determine:
-
Non-ambiguous (single suggestion) - e.g., becuase ==> because
- Will be fixed automatically with
codespell -w later
- Review to ensure they're genuine typos, not domain terms
-
Ambiguous (multiple suggestions) - e.g., trough ==> through, trough
- Requires human/LLM decision based on context
- Must be fixed manually FIRST (before running codespell -w)
-
False positives - domain terms, abbreviations, intentional spellings
- Add to
ignore-words-list or skip config
- Do NOT fix
Cases where typos should be marked as FALSE POSITIVES:
-
Domain-specific terms: pres (pressure), ans (answer), hist (histogram)
-
Intentional spellings: test fixtures, example data, legacy API compatibility
-
Intentional creative / humorous typos: meme phrasings, deliberate misspellings,
jokes, or stylistic word choices in prose. Examples seen in real PRs:
Teh Slowest Language Evah — meme spelling of "the"
atomical caroller — author preferred the less common "atomical" over "atomic"
- Easter eggs in error messages, docstrings, ASCII art, marketing prose
Prefer an inline pragma at the site of the typo (Step 7.6) over
ignore-words-list / ignore-regex. A project-wide whitelist hides
future genuine typos of the same word, so reach for it only when the
pragma approach is impractical (e.g. the same intentional misspelling
recurs in many files, or the host file format has no comment syntax).
Choose the narrowest scope that does the job.
-
Code identifiers: variable/function names that would break if changed
-
External references: API endpoints, third-party constants, protocol terms
-
Abbreviations in comments: recv, addr, buf, etc.
-
Foreign words: in locale files or documentation
-
The "fix" changes meaning: e.g., rouge (the tool) vs rogue
7.3 Handle Ambiguous Typos FIRST (Manual Fixes)
For typos with multiple suggestions like trough ==> through, trough:
- Read the surrounding context (5-10 lines) using the Read tool
- Understand the semantic meaning
- Choose the correct replacement based on context
- Use the Edit tool to apply the fix
Example ambiguous cases:
trough ==> through, trough - is it "through" (preposition) or "trough" (container)?
loner ==> longer, loner - is it "longer" (comparison) or "loner" (person)?
manger ==> manager, manger - is it "manager" (person) or "manger" (feeding trough)?
seach ==> search, each, reach, ... - context determines which is correct
After fixing all ambiguous typos, run pre-commit (if .pre-commit-config.yaml exists — see Step 4.3) on the touched files, then commit:
git add -A
pre-commit run --all-files || true
git add -A
git commit -m "Fix ambiguous typos requiring context review
Fixed ambiguous typos:
- typo1 -> fix1 (file1:line) - rationale
- typo2 -> fix2 (file2:line) - rationale"
7.4 Spot Additional Typos Codespell Might Miss
While reviewing context, watch for:
- Typos not in codespell's dictionary (e.g.,
anonimization → anonymization)
- Context-dependent misspellings codespell can't detect
- Consistent misspellings across the codebase
If you spot typos codespell missed, fix them in the same manual commit.
7.4.1 Contribute Missed Typos to Codespell Dictionary
TODO: When you find typos that codespell missed (e.g., sycalls → syscalls,
liklihood → likelihood in hyphenated words like log-liklihood), consider
opening a PR to add them to the codespell dictionary at
https://github.com/codespell-project/codespell so that future users benefit.
Known typos not in codespell's dictionary:
Known limitations of codespell's word splitting:
- Hyphenated words like
log-liklihood may not be caught even though liklihood → likelihood
is in the dictionary, because codespell may not split on hyphens consistently.
7.5 Verify Non-Ambiguous Typos
Before running codespell -w, review the remaining non-ambiguous typos to ensure:
- They are genuine typos (not domain terms that should be ignored)
- The suggested fix is correct
- Files aren't read-only or external protocols that shouldn't be modified
If any should be skipped, add them to ignore-words-list first.
7.6 CRITICAL: Review ALL Diffs for False Positives
BEFORE committing or proceeding to PR creation, you MUST review the complete diff for semantic false positives that codespell incorrectly "fixed". This is the most critical step to avoid breaking code!
git diff master
Common False Positive Categories to Check:
1. Regex Patterns (MOST CRITICAL):
- Patterns intentionally matching typos in source text (e.g., OCR errors)
- Examples:
ddress matching "Electronic Addess" in email cleanup regexes
Acknowledge?ment? matching "Acknowledgment"/"Acknowledgement"
- DO NOT "fix" these to
address or meant - they MUST remain as-is
2. Variable Names and Abbreviations:
consol = consolidation variable (not "console")
countr = country code variable (not "counter")
inpu = singular of "inputs" loop variable (not "input")
currentY, currentX = coordinate variables (not "currently")
- Check the actual usage context, not just the name!
3. Domain-Specific Terms in Code:
- Technical abbreviations that look like typos
- API parameter names that must remain unchanged
- Protocol-specific terminology
4. Intentional / Humorous Typos in Prose (easy to miss — read prose carefully):
-
Meme phrasings: Teh Slowest Language Evah (don't "correct" to "The ... Ever")
-
Author's deliberate word choice: atomical caroller (the author wanted "atomical",
not "atomic"), superbness, archaic/poetic forms
-
Easter eggs in error messages, ASCII art, docstrings, marketing copy
-
Project-internal in-jokes, branded phrasings
-
These often live in README.md, pyproject.toml descriptions, CHANGELOG,
exception messages, log strings, and test fixtures.
Real incident (beartype/beartype#645): codespell "fixed" Teh → The and
atomical → atomic in pyproject.toml. The maintainer had to manually
re-introduce both and add per-word pragmas after the merge.
How to tell the difference: if the surrounding tone is playful, archaic,
or quoted from a meme / song / poem, suspect intent. When unsure, ask the user
rather than silently "correcting" it.
How to Fix False Positives:
For regex patterns - Add inline codespell:ignore comments:
email = email.replaceAll("(A|a)ddress", "");
Pattern.compile("Acknowledge?ment?");
For intentional / humorous typos - Prefer an inline
codespell:ignore <word> pragma adjacent to the line containing the
misspelled word. Reach for ignore-words-list / ignore-regex only when
the inline pragma isn't a good fit — e.g. the misspelling recurs across
many files (whitelist scales better than scattering pragmas), or the host
file format has no comment syntax (see fallback below). The narrowest
scope that works is the right choice; per-occurrence pragmas preserve
spell-checking for the same word elsewhere in the project.
Pragma syntax matches the host file's comment style:
description = "Teh Slowest Language Evah"
ERROR_TEMPLATE = "An atomical caroller"
<!-- Markdown / HTML -->
Welcome to **Teh** Slowest Language Evah. <!-- codespell:ignore teh,evah -->
.. RST: use a substitution or trailing comment on the same line
The Bear roars: "Superbness!" .. codespell:ignore superbness
motto: "Teh Slowest Language Evah"
For file formats without line comments (pure JSON, plain .txt), pragmas
aren't possible — either move the string into a commentable file, or add
the word to ignore-words-list (or a tight ignore-regex anchored to the
specific phrase) with a comment in the config naming the file and line
so a future reader knows why the project-wide whitelist exists.
For variable names - Add to ignore-words-list in config (these are
identifiers whose spelling is fixed by code, not prose where future typos
of the same word would be a real concern):
ignore-words-list = serie,consol,countr,inpu,currenty,currentx
Choosing the right scope (in increasing breadth — prefer the narrowest
that actually fits the situation):
| Situation | Suggested scope | Why |
|---|
| One-off intentional typo in prose (string, comment, doc, README) | Inline codespell:ignore | Keeps spell-check active for the same word elsewhere |
| One-off humorous / meme spelling | Inline codespell:ignore | Protect only this occurrence |
| Regex pattern intentionally matching a typo | Inline codespell:ignore | The typo is part of the data, not the project's vocabulary |
| Same intentional phrase recurring across a handful of files | Inline pragmas at each site, or a tight ignore-regex anchored to the phrase | Pragmas if review value > churn; anchored regex if churn dominates |
| Domain term recurring across many files | ignore-words-list | Pragma-per-site would be noisy and incomplete |
| Identifier (variable, function, parameter name) used many places | ignore-words-list | Spelling is fixed by code; whitelisting is safe and avoids pragma churn |
Intentional typo in a comment-less format (pure JSON, plain .txt) | ignore-words-list or anchored ignore-regex, with a config comment pointing back to the file | Inline pragma not syntactically possible |
When in doubt, start narrow (inline pragma). Widen to ignore-regex
(phrase-anchored) or ignore-words-list only when the narrower option is
genuinely worse — not just because it's easier to type.
Process:
- Review the entire diff line by line, looking for the categories above
- For each suspicious change, read the surrounding code context
- Revert incorrect fixes using Edit or git revert
- Add protection via inline comments or ignore-words-list
- Re-run codespell to verify zero errors after protections
- If
.pre-commit-config.yaml exists: run pre-commit run --all-files — inline codespell:ignore comments often trip ruff/black/ruff-format (line length, comment spacing). Stage any reformatting changes before committing. (See Step 4.3.)
- Commit the fixes to false positive handling
Example of fixing a false positive:
git diff
git add <file>
git commit -m "Fix regex pattern incorrectly changed by codespell
Revert Acknowledge?meant? → Acknowledge?ment? in regex pattern.
Added inline codespell:ignore comment to protect regex pattern."
This step is NON-NEGOTIABLE - skipping it will result in broken code!
Step 8: Verify Formatting Integrity
CRITICAL: After applying fixes, check that no formatting was broken.
8.1 Check the Diff
git diff
8.2 Look for Broken Formatting
Markdown issues to check:
- Table alignment - columns must still align with
| separators
- Heading underlines -
=== or --- must match heading length (for some parsers)
- Code fence closures - ensure ``` blocks are still properly closed
- List indentation - numbered/bulleted lists should maintain structure
reStructuredText issues to check:
- Table formatting - grid tables and simple tables have strict alignment
- Section underlines -
====, ----, ~~~~ must be at least as long as title
- Directive indentation - content under directives must be indented
- Role/reference syntax -
:role:text`` must remain intact
8.3 Common Formatting Breakage Patterns
| Issue | Example | Problem |
|---|
| Table column shift | Fixed word is longer/shorter | Misaligned ` |
| RST underline | Managment → Management | Underline now too short |
| Markdown table | Cell content changed length | Table renders incorrectly |
| Code in docs | Variable name in backticks | Might break doc references |
8.4 Fix Formatting Issues
If formatting is broken:
- Identify affected tables/headings in the diff
- Use Edit tool to realign tables, extend underlines, etc.
- These fixes go in a separate fixup commit
8.5 RST Underline Fix Example
If changing Managment to Management in RST:
Managment
---------
Must become:
Management
----------
(Add one more - to match the new length)
8.6 Markdown Table Realignment
If a cell content changes length, realign the entire table:
| Column | Description |
|-----------|-------------|
| old_word | meaning |
Step 9: Commit Non-Ambiguous Fixes Using datalad run + codespell -w
IMPORTANT: For non-ambiguous typos (single suggestion), use codespell -w wrapped in datalad run.
Note: datalad run works on plain git repositories - no need to initialize as a datalad
dataset first. Do NOT run datalad create - it adds unnecessary commits and configuration.
Install datalad if not available
Check if datalad is installed, and install it if needed:
datalad --version
uv pip install datalad
pip install datalad
uvx --from datalad datalad --version
Recommended: Use uvx --from datalad datalad to run datalad commands without permanent installation.
Commit Order Summary
By this point, you should have:
- Already committed ambiguous typos (manual fixes using Edit tool) - Step 7.3
- Now committing non-ambiguous typos using
codespell -w - This step
Commit Non-Ambiguous Typos with codespell -w
For typos with a single suggestion (e.g., becuase ==> because), use codespell -w:
datalad run -m "chore: fix non-ambiguous typos with codespell" 'codespell -w'
uvx --from datalad datalad run -m "chore: fix non-ambiguous typos with codespell" 'uvx codespell -w'
You can provide a detailed custom message with the -m option listing what was fixed:
datalad run -m "chore: fix non-ambiguous typos with codespell
Fixed typos:
- succint -> succinct (biomni/utils.py)
- becuase -> because (biomni/utils.py)
- softwares -> software (biomni/agent/a1.py, biomni_env/README.md)
- arugments -> arguments (biomni/tool/genomics.py)
- referece -> reference (biomni/tool/genomics.py)
" 'codespell -w'
uvx --from datalad datalad run -m "chore: fix non-ambiguous typos with codespell
Fixed typos:
- succint -> succinct (biomni/utils.py)
- becuase -> because (biomni/utils.py)
- softwares -> software (biomni/agent/a1.py, biomni_env/README.md)
- arugments -> arguments (biomni/tool/genomics.py)
- referece -> reference (biomni/tool/genomics.py)
" 'uvx codespell -w'
Note: When using uvx --from datalad datalad, also use uvx codespell -w inside the command
to ensure codespell is available in the same environment.
Why NOT use codespell -w for ambiguous fixes?
codespell -w only fixes typos with a single suggestion. Ambiguous typos (multiple suggestions)
are skipped by codespell -w, which is why you must fix them manually first.
Alternative: Interactive Mode (if in interactive terminal)
If you have access to an interactive terminal and want codespell to prompt for ambiguous fixes:
datalad run -m "chore: fix typos with codespell interactively" 'codespell -w -i 3 -C 4'
The -i 3 flag enables interactive mode, -C 4 shows 4 context lines.
However, the recommended workflow is to fix ambiguous typos manually first (Step 7.3),
then use codespell -w non-interactively for the remaining unambiguous typos.
Formatting Fixup Commit (if needed)
If Step 8 required formatting corrections (table realignment, RST underlines):
git add -A
git commit -m "fixup: realign tables/headings after typo fixes"
Note: Formatting fixups are manual edits, so use regular git commit (not datalad run).
Why datalad run?
- Reproducibility: The exact command is recorded in git commit metadata
- Re-runnable: Can be re-executed on different repo states with
datalad rerun
- Auditable: Clear record of what tool made the changes
- Automation-friendly: The commit message can be customized while preserving the command
This separation makes review easier and keeps the typo fixes atomic.
Step 10: Review for Functional Fixes
After fixing typos, review the diff to identify any that might be functional bug fixes
(typos in code that affected behavior but weren't caught by tests):
git diff HEAD~2..HEAD -- '*.py' '*.js' '*.ts' '*.go' '*.rs' '*.c' '*.cpp'
Look for typos fixed in:
- Variable/function names (could have been silently broken)
- String comparisons or dictionary keys
- Error messages that might be matched elsewhere
- Configuration keys
If any functional fixes are found:
- Note them prominently in the PR description
- Consider if tests should be added
- May warrant a separate commit or mention in changelog
Step 11: Prepare and Create Pull Request
CRITICAL: Verify codespell passes with zero errors
Before preparing the PR, run a final check:
uvx codespell 2>&1
If ANY errors appear, go back to Step 7.6 and fix the remaining false positives!
MANDATORY: Respect upstream PR template and CONTRIBUTING.md
Before drafting the PR body, follow the repo-wide rule in this collection's
top-level AGENTS.md ("Respect upstream PR conventions when preparing a
pull request"). In short:
- Look for a PR template — check
.github/PULL_REQUEST_TEMPLATE.md,
.github/pull_request_template.md, .github/PULL_REQUEST_TEMPLATE/*.md,
docs/PULL_REQUEST_TEMPLATE.md, root PULL_REQUEST_TEMPLATE.md, and
the Codeberg/Forgejo (.gitea/, .forgejo/) and GitLab
(.gitlab/merge_request_templates/) equivalents.
- If a template exists, use its section headings as the skeleton.
Fill every section. Tick checkbox items truthfully — for codespell PRs
that typically means:
- tests: usually
[ ] (codespell config is not user-facing logic)
- documentation: usually
[ ] (no manual changes required)
- changelog entry: if the project uses
changelog/unreleased/,
changelog.d/, news/, or CHANGELOG.md, add an entry as part of
this PR — do not just check the box. For restic-style projects, see
changelog/TEMPLATE.
- "ready for review" / DCO sign-off: tick only when actually ready
- Read
CONTRIBUTING.md (and .github/CONTRIBUTING.md,
DEVELOPMENT.md) at repo root. Honor branch-naming, commit-message,
issue-first, and changelog conventions it spells out.
- In the final report to the user, state plainly:
- which template file was found (or that none was) and that the PR
body follows it
- which CONTRIBUTING / DEVELOPMENT guide was found and any
contributor-facing requirements that shaped the PR
- any outstanding items the user must do manually (e.g. enabling
"Allow edits from maintainers" in the GitHub PR UI)
Prepare PR Message
If a template exists, the structure below is just the content to fold into
that template's sections. Do NOT overwrite the template's headings with
this default structure.
Write the PR description to .git/pr-description.md (NOT .git/prospective-PR.md):
IMPORTANT: Do NOT include the PR title in the body text - it will be added automatically by gh pr create --title.
Add codespell configuration and fix existing typos.
More about codespell: https://github.com/codespell-project/codespell
I personally introduced it to over a hundred of projects already mostly with a positive feedback
(see the ["improveit-dashboard"](https://github.com/yarikoptic/improveit-dashboard/blob/master/READMEs/yarikoptic.md)).
CI workflow has 'permissions' set only to 'read' so also should be safe.
## Changes
### Configuration & Infrastructure
- Added <CONFIG_FILE> configuration with comprehensive skip patterns
- Created GitHub Actions workflow to check spelling on push and PRs
- Pre-commit hook (if applicable)
- Configured to skip: [list skip patterns]
### Domain-Specific Whitelist
Added legitimate terms that codespell flags as typos:
- [List terms with brief explanations]
### Typo Fixes
**Ambiguous typos fixed manually** (N fixes with context review):
- [List with before → after and context]
**Non-ambiguous typos fixed automatically** (N fixes in N files):
Common fixes include: [list common patterns]
### Regex Pattern Protection
Added inline `codespell:ignore` comments for:
- [List files with regex patterns that were protected]
### Historical Context
This project has had X prior commits fixing typos manually, demonstrating the value of automated spell-checking.
## Testing
✅ Codespell passes with zero errors after all fixes
---
🤖 Generated with [Claude Code](https://claude.com/claude-code) and love to typos free code
Determine Remote and Branch Names
Identify the fork remote and upstream repository:
git remote -v
git branch --show-current
Provide Final PR Creation Command
After verifying everything is ready, provide the user with the complete command to push and create the PR.
GitHub Projects
Template:
git push -u <fork-remote> <branch-name> && gh pr create --repo <upstream-org>/<repo-name> --title "Add codespell support with configuration and fixes" --body-file .git/pr-description.md --web
Example:
git push -u gh-<username> enh-codespell && gh pr create --repo grobidOrg/grobid --title "Add codespell support with configuration and fixes" --body-file .git/pr-description.md --web
The --web flag opens the PR in browser for final review before submission.
Codeberg/Forgejo Projects
For Codeberg repositories, the gh CLI doesn't work directly. Provide a push command
and instruct the user to create the PR via the web interface:
Template:
git push -u <remote> <branch-name>
Then tell the user:
After pushing, create a pull request at:
https://codeberg.org/<org>/<repo>/compare/<main-branch>...<branch-name>
Copy the contents of .git/pr-description.md into the PR description.
Alternative: If the user has tea CLI installed
(Gitea/Forgejo CLI tool), provide:
git push -u <remote> <branch-name> && tea pr create --repo <org>/<repo> --title "Add codespell support with configuration and fixes" --description "$(cat .git/pr-description.md)"
This is the final deliverable - always provide the push command and PR creation
instructions as the last step!
Interactive Decision Points
When running this skill, ask the user about:
- Paths to skip: After seeing typo locations, ask which directories contain vendored/generated code
- Words to ignore: After seeing frequent "typos", ask which are legitimate domain terms
- Questionable fixes: When LLM is unsure if a fix is appropriate, ask the user
- Pre-commit: Whether to add pre-commit hook
- PR creation: Whether to create PR or just prepare commits
LLM Autonomous Decisions
The LLM should make these decisions WITHOUT asking the user:
- Clear typos:
managment → management, recieve → receive
- Obvious false positives: domain terms, abbreviations, code identifiers
- Ambiguous with clear context: when surrounding text makes the intent obvious
Ask User When Uncertain
- The word could legitimately be either spelling
- Changing it might break external compatibility
- It's in a string that might be user-visible (UI text)
- Multiple occurrences with different intended meanings
Common False Positives by Domain
Web Development
statics (static files directory, not "statistics")
navbar, btn (common abbreviations)
Scientific Computing
hist (histogram)
ans (answer)
pres (pressure or presentation)
Internationalization
- Words from other languages in locale files
Testing
- Intentional misspellings in test fixtures
Output
After completing the skill:
- Feature branch
enh-codespell with all changes
- Working codespell configuration
- CI workflow (GitHub Actions and/or Forgejo Actions depending on platform)
- Pre-commit hook (if requested)
- All legitimate typos fixed via
datalad run (reproducible commits)
- All false positives reviewed and fixed (regex patterns, variable names protected)
- Formatting intact (tables realigned, RST underlines adjusted)
- Functional fixes identified and noted
- Codespell passes with zero errors
- PR description saved to
.git/pr-description.md (without duplicate title)
- Ready-to-use command provided: push + PR creation (via
gh for GitHub, web UI or tea for Codeberg/Forgejo)
Tips
- Start with a minimal skip list and add paths as false positives are identified
- Use
ignore-words-list sparingly - prefer fixing actual typos
- Review typos before running
codespell -w to ensure they're genuine (not domain terms)
- Some projects may have legacy code with intentional misspellings for backwards compatibility
- Workflow order matters: Fix ambiguous typos manually first, then run
codespell -w for the rest
- ⚠️ CRITICAL: Always review the complete diff for false positives - especially regex patterns and variable names!
- Always check formatting after fixes - especially in RST/Markdown docs with tables
- For RST files, run
rst2html or similar to validate after changes
- When fixing typos in headings, immediately check/fix the underline length
- Use
datalad run with custom -m messages to document what typos were fixed
- If a "typo" appears many times and is domain-specific, add to ignore-words-list rather than skipping each instance
codespell -w is safe for non-ambiguous typos - it only fixes single-suggestion typos
- The Edit tool is for ambiguous typos that need context-based decisions
- Regex patterns: Use inline
// codespell:ignore <word> comments to protect patterns
- Intentional / humorous typos in prose: prefer an inline
codespell:ignore <word> pragma at the site of the misspelling over ignore-words-list / ignore-regex — a project-wide whitelist silences future genuine typos of the same word. Widen the scope only when the inline pragma is impractical (recurs in many files, or the file format has no comments). See Step 7.6 category #4 and the decision table; cf. beartype/beartype#645 for a real incident.
- Variable names: Add to
ignore-words-list with explanatory comments about what they represent
- Trust but verify: codespell is good but not perfect - human review of diffs is mandatory!
- Pre-commit interplay: If
.pre-commit-config.yaml includes formatters (black, ruff-format, prettier) or linters with auto-fix (ruff, isort), run pre-commit run --all-files after every manual edit step (especially after adding codespell:ignore comments) — otherwise reformatting changes leak into a later commit. See Step 4.3.
- Contribute back: When you find typos codespell misses (e.g.,
sycalls → syscalls), consider PRing them to https://github.com/codespell-project/codespell to improve the dictionary for everyone
- Hyphenated words: codespell may miss typos inside hyphenated compounds (e.g.,
log-liklihood) - grep for known typo patterns after codespell -w to catch these