A pre-commit hook that prevents TODO, FIXME, and other work comments without ticket references from any issue tracking system.
This tool helps maintain code quality by ensuring all work comments are properly linked to issues in your project tracker (Jira, GitHub Issues, Linear, Asana, etc.).
Currently Windows platforms are not supported due to the lack of grep. Mac and Linux should work fine and we're working on Windows support.
- ❌ Blocks commit of unplanned work items: TODOs without ticket references in staged files prevent commits
- ✅ Universal issue tracker support: Works with Jira, GitHub, Linear, and any tracker using
PREFIX-NUMBERformat - 🔍 Multiple comment types: Checks TODO, FIXME, XXX, HACK (customizable)
- 🎯 Branch-aware: Automatically calls out TODOs for the current branch's ticket
- ⚡ Fast: Uses
grepfor efficient batch processing - 🎨 Flexible output: Standard, quiet, or verbose modes
- 🔧 Configurable: Extensive filtering via
.pre-commit-config.yaml - 📝 Line-by-line exclusions: Use
# noqacomments to skip specific lines ⚠️ Warning mode: Informational checks without blocking commits via--succeed-always
Add to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
rev: v1.0.0 # Use the latest release
hooks:
- id: prevent-dangling-todos
args: ['-t', 'MYPROJECT'] # Replace with your ticket prefixThat's it! Now commits will be blocked if they contain TODOs without MYPROJECT-123 style references.
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
rev: v1.0.0
hooks:
- id: prevent-dangling-todos
args: ['-p', 'TODO', '-t', 'MYPROJECT', '--check-unstaged']This allows direct usage of the CLI tool prevent-dangling-todos.
pip install git+https://github.com/lasp/prevent-dangling-todos.gitGet violations AND branch-specific TODOs, check all files:
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
hooks:
- id: prevent-dangling-todos
args: ['-t', 'MYPROJECT', '-v', '--check-unstaged']
verbose: true # Always show output (pre-commit defaults to no output on hook success)
always_run: true # Always run, even when no changes are staged (allows consistent checking of all files)This configuration:
- Shows un-ticketed work items in staged files (❌ blocking errors)
- Shows un-ticketed work items in unstaged files (
⚠️ warnings only) - Tracks current ticket work-items for branches that include a ticket reference (
⚠️ informational) - Always displays output even on success
- Note: unstaged changes are stashed before pre-commit runs, so changes in unstaged files will not be reflected in the output.
Alert about violations without blocking commits:
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
hooks:
- id: prevent-dangling-todos
args: ['-t', 'MYPROJECT', '--succeed-always', '-v']
verbose: truePerfect for:
- Gradual migration to enforced TODO references
- Large codebases with existing TODOs
- Raising awareness without strict enforcement
| Argument | Short | Description |
|---|---|---|
--ticket-prefix |
-t |
Ticket prefix(es) from your issue tracker (comma-separated) |
--comment-prefix |
-c |
Comment types to check (default: TODO,FIXME,XXX,HACK) |
--check-unstaged |
-u |
Also check unstaged files (as warnings). Note: unstaged changes are stashed before pre-commit runs and are therefore ignored. |
--verbose |
-v |
Show configuration, file status, and help text |
--quiet |
-q |
Silent mode - no output, only exit codes |
--succeed-always |
Always exit 0, even with violations | |
--version |
Show version information |
Deprecated (but still working):
-j/--jira-prefix→ Use-t/--ticket-prefixinsteadJIRA_PREFIXenv var → UseTICKET_PREFIXinstead
Set in your shell instead of using command-line arguments:
export TICKET_PREFIX=MYPROJECT,GITHUB
export COMMENT_PREFIX=TODO,FIXMECommand-line arguments take precedence over environment variables.
By default, these comment types are checked:
TODO- General tasksFIXME- Known issues needing fixesXXX- Warning/attention markersHACK- Temporary workarounds
Customize with -c or --comment-prefix.
Security Note: Custom comment prefixes are matched using grep's -F flag (fixed strings, not regex patterns) to prevent command injection vulnerabilities.
Exclude specific TODO comments using # noqa:
# TODO: This will fail - no ticket reference
# TODO: This is excluded # noqa
# FIXME: Also excluded # noqa: FIX002
# XXX: Excluded with any FIX code # noqa: FIX001,FIX003The tool follows Flake8's FIX001-FIX004 exclusion patterns, allowing you to:
- Exclude generated code
- Document intentional technical debt
- Skip specific TODO comments that don't need tracking
By default, only staged files are checked. Use --check-unstaged to check all repository files:
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
hooks:
- id: prevent-dangling-todos
args: ['-t', 'MYPROJECT', '--check-unstaged']Behavior:
- Staged file violations → ❌ Block commit (red errors)
- Unstaged file violations →
⚠️ Warning only (yellow warnings) - Unstaged violations don't affect exit code
- Uses
grepfor efficient batch processing - Unstaged changes are stashed before pre-commit runs and will not be reflected in output.
Use cases:
- Get visibility into ALL TODOs in your codebase
- Track technical debt across the entire repository
- Monitor TODO status without blocking development
The tool respects your .pre-commit-config.yaml filtering configuration:
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
hooks:
- id: prevent-dangling-todos
args: ['-t', 'MYPROJECT']
# Regex pattern for files to include
files: '^src/.*\.py$'
# Regex pattern for files to exclude
exclude: '^(tests/|docs/)'
# File types (uses `identify` library)
types: [python] # Only Python files
types_or: [python, javascript] # Python OR JavaScript
exclude_types: [markdown, json] # Exclude markdown and JSONAvailable filters:
files: Regex pattern - only check matching filesexclude: Regex pattern - exclude matching filestypes: All specified types must matchtypes_or: At least one specified type must matchexclude_types: Exclude all specified types
These filters apply to both staged and unstaged file checking when using --check-unstaged.
File types are detected using the identify library.
The tool automatically detects your current git branch and extracts ticket IDs matching your configured prefixes. TODOs referencing the current branch's ticket are shown as informational warnings (
Example:
- Branch:
feature/PROJ-123-new-feature - Detected ticket:
PROJ-123 TODO PROJ-123: Complete implementation→⚠️ Warning (not a violation)TODO PROJ-456: Different ticket→ Valid (different ticket)TODO: No reference→ ❌ Violation
Configuration:
repos:
- repo: https://github.com/lasp/prevent-dangling-todos
hooks:
- id: prevent-dangling-todos
args: ['-t', 'PROJ', '-v']Note: Branch detection messages are shown only for included files. If --check-unstaged is not set, you will not have visibility into branch-specific TODO items in unstaged files. It is recommended to turn on unstaged files checking for this reason.
1. Command-line arguments:
# Before (0.x)
args: ['-j', 'MYPROJECT']
# After (1.0)
args: ['-t', 'MYPROJECT'] # Recommended
# Still works (with deprecation warning)
args: ['-j', 'MYPROJECT'] # Will show warning2. Environment variables:
# Before (0.x)
export JIRA_PREFIX=MYPROJECT
# After (1.0)
export TICKET_PREFIX=MYPROJECT # Recommended
# Still works (with deprecation warning)
export JIRA_PREFIX=MYPROJECT # Will show warning3. Python API (if using programmatically):
# Before (0.x)
from prevent_dangling_todos.prevent_todos import TodoChecker
checker = TodoChecker(jira_prefixes=['PROJ'])
# After (1.0)
from prevent_dangling_todos.prevent_todos import TodoChecker
checker = TodoChecker(ticket_prefixes=['PROJ'])-
Update
.pre-commit-config.yaml:- Replace
-jwith-t - No other changes needed
- Replace
-
Update environment variables (if used):
- Replace
JIRA_PREFIXwithTICKET_PREFIX
- Replace
-
No immediate action required:
- Old arguments continue to work
- Deprecation warnings will guide you
- Plan to migrate before version 2.0
1. "No ticket prefix specified" message
- Ensure you provide
-t/--ticket-prefixargument orTICKET_PREFIXenvironment variable - If intentional (to disallow ALL TODOs), this is expected behavior
2. Violations not showing when using --succeed-always
- By default, pre-commit hides output from successful hooks
- Solution: Add
verbose: trueto your hook configuration
3. Comments not being detected
- Check format:
# TODO PROJ-123: Description - Ensure ticket prefix matches your configuration
- Verify comment type is in your
--comment-prefixlist
4. Branch-specific TODOs not appearing
- Use
verbose: truein your config to see branch detection - Check that branch name contains a valid ticket ID (e.g.,
feature/PROJ-123-description) - Ensure branch detection isn't failing (check verbose output)
5. Unstaged files not being checked
- Add
--check-unstaged/-uflag to your args - Ensure you don't have conflicting
filesorexcludepatterns
6. File filtering not working as expected
- Check your
.pre-commit-config.yamlfilters (files,exclude,types, etc.) - Use
--verboseto see which files are being checked - Remember: filters apply to both staged and unstaged files
7. False positives in generated code
- Use
# noqacomments to exclude specific lines - Add file/directory exclusions in
.pre-commit-config.yaml - Adjust
--comment-prefixto check only specific comment types
8. Want to see all TODOs across codebase
- Use
--check-unstagedflag - Add
verbose: trueto see detailed output - Consider
--succeed-alwaysto avoid blocking while gaining visibility
# Show all available options
prevent-dangling-todos --help
# Check version
prevent-dangling-todos --versionFor issues or feature requests, please visit: https://github.com/lasp/prevent-dangling-todos/issues
Contributions are welcome! Please feel free to submit a Pull Request.
BSD-3 License - See LICENSE file for details.
See CHANGELOG.md for detailed release notes.