Skip to main content

Rusty TODO.md — A Pre-Commit Hook & CLI for Managing TODOs

PyPI - Version PyPI - Python Version License: MIT

Rusty TODO.md helps you find, centralize, and maintain all your TODO comments across your codebase. It can run as a pre-commit hook or from the CLI, automatically extracting TODO-style comments into a structured TODO.md file.

Supports a wide range of languages and file types, with sectioned formatting, multi-line support, and smart sync.


When pre-commit installs a hook from a Git repo, it runs pip install . from that repo — which would normally build Rusty TODO.md from source (requiring a Rust toolchain).

The shim repository (rusty-todo-md-pre-commit) solves this by depending on the rusty_todo_md PyPI package, ensuring prebuilt wheels are used.

Add this to your .pre-commit-config.yaml:

repos:
  - repo: https://github.com/simone-viozzi/rusty-todo-md-pre-commit
    rev: v1.9.1  # Use the latest upstream tag (shim mirrors upstream)
    hooks:
      - id: rusty-todo-md
        args: ["--auto-add", "--markers", "TODO", "FIXME", "HACK", "--"]
        language_version: python3.11
  • args customise the markers to scan for and enable --auto-add to stage TODO.md automatically.
  • language_version forces the hook to run with a specific Python interpreter.

Example with exclusions:

repos:
  - repo: https://github.com/simone-viozzi/rusty-todo-md-pre-commit
    rev: v1.9.1
    hooks:
      - id: rusty-todo-md
        args:
          - "--auto-add"
          - "--markers"
          - "TODO"
          - "FIXME"
          - "HACK"
          - "--exclude-dir"
          - "node_modules"
          - "--exclude-dir"
          - "target"
          - "--exclude"
          - "**/*.test.js"
          - --
        language_version: python3.11

Then install the hook:

pre-commit install

✅ No Rust toolchain is required when using the shim and a supported platform.


⚙️ CLI installation

You can also install Rusty TODO.md directly for manual CLI use:

pip install rusty_todo_md

Then run:

rusty-todo-md --help

✨ Key Features

  1. Automatic TODO Collection By default, Rusty TODO.md scans the files passed to it (typically staged files from pre-commit) for markers like TODO and FIXME, and updates your TODO.md with any new entries.

  2. Sectioned TODO.md Format The TODO.md file is now organized into sections, grouped first by marker (e.g., # TODO, # FIXME), then by file. Each marker section begins with a header (# <MARKER>), each file with a sub-header (## <file-path>), followed by a list of extracted TODO items.

  3. Multi-line TODO Support Handles multi-line and indented TODO comments, merging them into a single entry.

  4. Sync Mechanism

    • Each run rescans the files being committed plus every tracked file TODO.md already lists, and rewrites TODO.md from that scan.
    • Removes entries when their corresponding TODOs (or files) are no longer present in the source code.
  5. Language-Aware Parsing Supports precise parsing for Python, Rust, JavaScript, and Go out-of-the-box, with plans for additional languages such as TypeScript, PHP, and Java.

  6. Seamless Pre-Commit Integration Easily integrate Rusty TODO.md into your workflow by adding it to your .pre-commit-config.yaml.

  7. Auto-stage Updated TODO.md With the --auto-add flag, the tool can automatically stage the TODO.md file after updates.


🧩 CLI usage

Scan staged files

rusty-todo-md

Use multiple markers

rusty-todo-md --markers TODO FIXME HACK

Specify files to process with markers

When using --markers as the last option before specifying files, use -- to separate markers from files:

rusty-todo-md --markers TODO FIXME HACK -- file1.rs file2.rs

Without the -- separator, the files would be incorrectly treated as additional markers.

Automatically stage TODO.md

rusty-todo-md --auto-add path/to/file.rs

Custom TODO.md path

rusty-todo-md --todo-path docs/TODOS.md

Exclude files and directories

Rusty TODO.md supports glob-based exclusion patterns to filter out files and directories from TODO extraction.

Exclude specific files or patterns

# Exclude all log files
rusty-todo-md --exclude "*.log"

# Exclude specific file
rusty-todo-md --exclude "config.rs"

# Exclude files in a specific directory
rusty-todo-md --exclude "src/generated/*"

Exclude directories

# Exclude a directory (and all files within it)
rusty-todo-md --exclude-dir "build"

# Or use --exclude with trailing slash
rusty-todo-md --exclude "build/"

# Exclude multiple directories
rusty-todo-md --exclude-dir "node_modules" --exclude-dir "target"

Recursive exclusion with wildcards

# Exclude all files under src/ recursively
rusty-todo-md --exclude "src/**"

# Exclude all .test.js files anywhere in the tree
rusty-todo-md --exclude "**/*.test.js"

# Exclude all 'vendor' directories at any depth
rusty-todo-md --exclude-dir "**/vendor"

Multiple exclusion patterns

# Combine multiple exclusions
rusty-todo-md \
  --exclude "*.log" \
  --exclude "*.tmp" \
  --exclude-dir "build" \
  --exclude-dir "dist" \
  --markers TODO FIXME

Glob pattern syntax

  • * — matches any sequence of characters within a single path component
  • ? — matches any single character
  • ** — matches zero or more path components (recursive)
  • / suffix — indicates directory-only matching (when used with --exclude)

Note: Patterns are matched relative to the scan root. The --exclude-dir flag automatically ensures directory-only matching.


🔀 Rebase conflicts in TODO.md

Why it happens

Each TODO.md bullet embeds the line number twice — once in the label and once in the link anchor (* [file:line](file#L<line>)). When two branches both shift the same TODO to different line numbers, git's line-by-line text merge sees the same physical TODO.md line edited differently on each side and produces a conflict — at every replayed commit during a rebase. The user did not meaningfully edit TODO.md; the line numbers just moved.

Fix: one line in .gitattributes

Commit this to your repository. If you use --todo-path, write that path instead of TODO.md, relative to the repository root (.gitattributes patterns are repository-relative); a path containing spaces or *, ?, [, #, ", \, or starting with !, needs gitattributes quoting/escaping:

/TODO.md merge=union

(The leading / anchors the rule to the root TODO.md; without it, it would also apply to any other file named TODO.md in a subdirectory.)

union is built into git, so there is nothing to install or configure per clone. Git then merges TODO.md by keeping the lines from both sides instead of stopping with a conflict, so rebases and merges never stop on TODO.md.

The merged TODO.md is temporarily wrong (old and new line numbers side by side, out of order), and that's expected. Each hook run rebuilds TODO.md from the files being committed plus every file TODO.md already lists, so the next normal commit replaces whatever the merge left, whatever files it touches. Commits replayed during the rebase keep the approximate TODO.md, because git doesn't run hooks during a rebase.

Without the .gitattributes line, resolve a TODO.md conflict by keeping both sides (leaving the conflict markers in is fine too); the next hook run rebuilds it the same way, and prints a hint with the exact merge=union line for your path (or points here when the path needs escaping). Don't resolve it by taking only one side (e.g. git checkout --ours TODO.md): the files listed only on the other side would no longer be listed, so their TODOs would not come back until those files change or you run --regenerate. To rebuild it right away:

rusty-todo-md --regenerate

This re-scans every tracked file and rewrites TODO.md from scratch. Source files that still contain conflict markers are skipped with a warning rather than producing garbled TODOs.


📝 Supported languages & extensions

Rusty TODO.md detects comment syntax based on file extension:

Language / Type Extensions
Python py
Rust rs
JavaScript / JSX js, jsx, mjs
TypeScript ts, tsx
Java java
C / C++ headers cpp, hpp, cc, hh
C# cs
Swift swift
Kotlin kt, kts
JSON json
Go go
Shell sh
YAML yml, yaml
TOML toml
Dockerfile dockerfile
Markdown md

Many extensions share the same parser (e.g., JS-style comment parsing for TS, Java, C-like languages).


🔍 Output format (stable)

Entries in TODO.md use this format:

* [path/to/file.ext:LINE](path/to/file.ext#L{LINE}): MESSAGE

This format is stable and designed for easy linking to code in hosted repos.

The file has a single # TODOs title, one ## section per marker and one ### subsection per file:

# TODOs

## TODO
### src/main.rs
* [src/main.rs:10](src/main.rs#L10): Refactor initialization logic

A TODO.md written by a version before 1.11 (markers as #, files as ##) is rebuilt automatically, from all tracked files, the first time the hook runs.


📦 Requirements & Supported Platforms

  • Python ≥ 3.10
  • No Rust toolchain needed if using the shim or PyPI wheels

Prebuilt wheels are published for:

OS / libc Architectures
Linux (manylinux) x86_64, x86, aarch64, armv7, ppc64le
Linux (musllinux) x86_64, x86, aarch64, armv7
Windows x64, x86
macOS x86_64 (macOS 15), aarch64 (macOS 14)

🛠 Troubleshooting

  • If no wheel is available for your platform, pip will try to build from source — which requires a Rust toolchain.
  • If you encounter build errors, please:
    1. Check the latest releases to confirm wheel availability.
    2. Open an issue with your OS/arch details.

👩‍💻 Development

If you want to run Rusty TODO.md directly from the main repo via pre-commit (building from source):

repos:
  - repo: https://github.com/simone-viozzi/rusty-todo-md
    rev: v1.7.5
    hooks:
      - id: rusty-todo-md

⚠️ This will compile the Rust source and requires a working Rust toolchain.


🤝 Contributing

Contributions are welcome!

  • Open an issue for bug reports or feature requests.
  • Submit a pull request with improvements, new parsers, or fixes.


⚖️ License

Licensed under the MIT License.


❤️ Support

If you find Rusty TODO.md helpful, please consider giving it a ⭐ on GitHub to help others discover the project.

Metadata

Release files for rusty-todo-md 1.11.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rusty-todo-md 1.11.0
File Size Uploaded
rusty_todo_md-1.11.0.tar.gz 112.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for rusty-todo-md 1.11.0
File
rusty_todo_md-1.11.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
rusty_todo_md-1.11.0-py3-none-win32.whl Python 3 none Windows x86-32 Details
rusty_todo_md-1.11.0-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
rusty_todo_md-1.11.0-py3-none-musllinux_1_2_i686.whl Python 3 none Linux musl 1.2+ x86-32 Details
rusty_todo_md-1.11.0-py3-none-musllinux_1_2_armv7l.whl Python 3 none Linux musl 1.2+ ARMv7l Details
rusty_todo_md-1.11.0-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
rusty_todo_md-1.11.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
rusty_todo_md-1.11.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl Python 3 none Linux glibc 2.17+ PowerPC 64-le Details
rusty_todo_md-1.11.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl Python 3 none Linux glibc 2.17+ x86-32 Details
rusty_todo_md-1.11.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl Python 3 none Linux glibc 2.17+ ARMv7l Details
rusty_todo_md-1.11.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
rusty_todo_md-1.11.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
rusty_todo_md-1.11.0-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 23.6 MB

Release files / rusty_todo_md-1.11.0.tar.gz

Download URL rusty_todo_md-1.11.0.tar.gz
Size 112.3 kB
Tags Source
SHA-256 checksum
How to use checksums
cdece256f2df6c215aa7a54736e86835b340250c2e721830a7a9af60ba863166
BLAKE2b-256 checksum
How to use checksums
60cd47eed046e7e62cdf07cd46648de9af8d9475db0b24ef54acfc4cf0fd845d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-win_amd64.whl

Download URL rusty_todo_md-1.11.0-py3-none-win_amd64.whl
Size 1.8 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
99627dd8a1b821c760bdf72cf792059f6f00933c0ac38091cbab6af31d52f0e9
BLAKE2b-256 checksum
How to use checksums
20568d655a9fe24e35023b4a9f73f5f4859ff70693e59415829c82b892cf7328
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-win32.whl

Download URL rusty_todo_md-1.11.0-py3-none-win32.whl
Size 1.6 MB
Tags Python 3 Windows x86-32
SHA-256 checksum
How to use checksums
5492c9aafb77d3b94c9f9468c77b50381c1057d4f8be763c3b4c5ab2913d0829
BLAKE2b-256 checksum
How to use checksums
f9ae55abf3451bf3e395c1f563986a634b24b1886948e88cd56dee1b8d669b8e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-musllinux_1_2_x86_64.whl

Download URL rusty_todo_md-1.11.0-py3-none-musllinux_1_2_x86_64.whl
Size 1.9 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
80471f8e3038f777e4445d0ed95322594ea03230c8a6d99bfab4222c1803894a
BLAKE2b-256 checksum
How to use checksums
a2d194ed2c38d0d1b9fbe959fb05fa7afb2810f688932c52b9b8ff8aa2674dd3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-musllinux_1_2_i686.whl

Download URL rusty_todo_md-1.11.0-py3-none-musllinux_1_2_i686.whl
Size 1.9 MB
Tags Linux musl 1.2+ x86-32 Python 3
SHA-256 checksum
How to use checksums
851227a636735d9138944e77520c1dfcfaf00b8f1c1545cf3709ab6ae0f286ab
BLAKE2b-256 checksum
How to use checksums
f087c0904402f809e94d1a12be9d49fe9c9fee1f1c8e73ecd7859f13f42389bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-musllinux_1_2_armv7l.whl

Download URL rusty_todo_md-1.11.0-py3-none-musllinux_1_2_armv7l.whl
Size 1.8 MB
Tags Linux musl 1.2+ ARMv7l Python 3
SHA-256 checksum
How to use checksums
33cdabab360444797d238cdb26c15d83f3c8445317dc74e625b9c46f34d1a13b
BLAKE2b-256 checksum
How to use checksums
da43cf515284353c6f13d88bf836cbf27618f46cd5ded972b9847fa0dd7eecda
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-musllinux_1_2_aarch64.whl

Download URL rusty_todo_md-1.11.0-py3-none-musllinux_1_2_aarch64.whl
Size 1.8 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
b2667339ef4e26010197473140f40180e632a215c35548f19c84360e7765047b
BLAKE2b-256 checksum
How to use checksums
bef23fd08214756d0d4547863ab22c399338caef96ed78632410fdd421ddfd09
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL rusty_todo_md-1.11.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.8 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
42aaadfb5f3ffe5edd70540d5bc2b4f6eaf7e62204b8fae7f8c72eb8be9baaeb
BLAKE2b-256 checksum
How to use checksums
a7af18d28de10acc6d8bb6872ef557522ebd9041d5f300b7c3b0afa2e212a9fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl

Download URL rusty_todo_md-1.11.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Size 2.2 MB
Tags Linux glibc 2.17+ PowerPC 64-le Python 3
SHA-256 checksum
How to use checksums
4e7b214a1e3127c1593364d35865496cf04285375cad410e785a23d2b26ead57
BLAKE2b-256 checksum
How to use checksums
a87ed8c28c2fdda19ad8fc8b6500bc34700b482b3eb71161fa00b680ba5a72d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl

Download URL rusty_todo_md-1.11.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl
Size 1.9 MB
Tags Linux glibc 2.17+ x86-32 Python 3
SHA-256 checksum
How to use checksums
f5ed030154b4797163e5f089bdd5a28eb855bbe40c87e9ce48bcccfc9ea7006b
BLAKE2b-256 checksum
How to use checksums
00b2867fce8eea8b19c82d7bbb751e8829dfa7e00b1d3ff8d21b8473c691dafa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl

Download URL rusty_todo_md-1.11.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Size 1.7 MB
Tags Linux glibc 2.17+ ARMv7l Python 3
SHA-256 checksum
How to use checksums
d0421144a6ae00a07d9884686cdfceb1c1e95bd6f5734297984337676e8fc3f4
BLAKE2b-256 checksum
How to use checksums
f1f4eb6a2b787f68af00b8131b72eb8339b8c90def95cef6e12411ec88c76923
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL rusty_todo_md-1.11.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.8 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
a2cffe9514909cbc82b483ddddbcaa8276893fc3a17d24c4b52078135b7988fa
BLAKE2b-256 checksum
How to use checksums
586652420884cf927f72ed5f068b19090cd6b996f955c466d7fb9441df1b416a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-macosx_11_0_arm64.whl

Download URL rusty_todo_md-1.11.0-py3-none-macosx_11_0_arm64.whl
Size 1.6 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
078ead08b6f4789db82cf8ac9a3d36c9c5dd64ae976c87fe03e5438d31346268
BLAKE2b-256 checksum
How to use checksums
c61bc941d877f4e106bfb786a4f2cdbf48d00d9c62a64dae4e9ffb9fee8f0a75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / rusty_todo_md-1.11.0-py3-none-macosx_10_12_x86_64.whl

Download URL rusty_todo_md-1.11.0-py3-none-macosx_10_12_x86_64.whl
Size 1.6 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
118b314a61d5aeafa23d0d00602a74ac9ce8a527fa9e5784250be2c8f141751e
BLAKE2b-256 checksum
How to use checksums
1d7430975a9060e26f03beff023d64b3b2a85432d14201268c362aaebd00fedd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release history Release notifications | RSS feed

This release

1.11.0 This release

14 release files

1.9.2

14 release files

1.9.1

14 release files

1.8.0

14 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page