todo-scan
A CI gate that fails your build when TODO comments exceed thresholds, so technical debt stays visible and under control.
Why not grep -r TODO .?
- No CI enforcement — grep finds TODOs but can't fail a build when the count crosses a limit
- No git awareness — grep can't show which TODOs were added in a branch or since a specific commit
- No structure — grep gives raw text; todo-scan extracts tags, authors, priorities, issue refs, and deadlines
- No format enforcement — grep can't reject malformed TODOs like
todo fix thisin a pre-merge check
Quick start
# See what you have
# Set a ceiling and enforce it in CI
Key commands
| Command | What it does |
|---|---|
todo-scan check --max 100 |
CI gate — fails the build when TODO count exceeds the threshold |
todo-scan diff main |
Shows TODOs added/removed since a git ref — useful in PR review |
todo-scan lint |
Enforces consistent TODO formatting (uppercase, colon, author) |
Features
- Discover
- Analyze
- Enforce
- Scale
- Report & Integrate
- Productivity
Scan & List TODOs
🔥 Problem
TODO comments scatter across hundreds of files, making it hard to know what's outstanding.
🌱 Solution
todo-scan list scans your entire codebase and displays every TODO, FIXME, HACK, XXX, BUG, and NOTE comment with color-coded tags, flexible grouping (--group-by file|tag|priority|author|dir), and filtering by priority, author, path glob, and result limit.
🎁 Outcome
One command gives you a complete, filterable inventory of all technical debt markers in your project.
Search TODOs
🔥 Problem
Scrolling through todo-scan list output or manually grepping to find specific TODOs is impractical in large codebases with hundreds of items.
🌱 Solution
todo-scan search filters TODO comments by message text or issue reference using case-insensitive substring matching, with --exact for case-sensitive searches and -C for context lines.
🎁 Outcome
You can instantly find relevant TODOs without scrolling through hundreds of items.
Inline Code Context
🔥 Problem
TODO lists show file:line references but lack surrounding code, forcing you to open files to understand what each TODO refers to.
🌱 Solution
todo-scan context displays the code around a specific line with related TODOs in the same file, and the -C N flag on list and diff adds inline context to every item.
🎁 Outcome
You understand what each TODO refers to without leaving the terminal.
Progressive Detail Levels
🔥 Problem
Humans scanning a terminal need compact output, while AI agents need full metadata — but every command outputs the same level of detail regardless of the consumer.
🌱 Solution
--detail minimal|normal|full controls information density across list, diff, and search: minimal shows only file, line, tag, and message; normal (default) preserves current behavior; full injects match_key and auto-collects surrounding source context.
🎁 Outcome
One flag adapts todo-scan output from quick human glances to rich machine-readable payloads without separate commands.
Diff Against Git Refs
🔥 Problem
New TODOs slip into pull requests unnoticed while resolved ones go unrecognized.
🌱 Solution
todo-scan diff compares the current working tree against any git ref and shows exactly which TODOs were added or removed.
🎁 Outcome
Every PR review shows precisely what TODO debt changed, making it impossible to sneak in untracked work.
Dashboard & Statistics
🔥 Problem
A flat list of TODOs makes it hard to see the big picture — whether tech debt is growing, who owns the most items, and which files are hotspots.
🌱 Solution
todo-scan stats provides a dashboard summary with tag and author breakdowns, priority distribution, and top files by TODO count, with --since <ref> for trend analysis.
🎁 Outcome
You get an at-a-glance view of your project's technical debt health and trends.
Compressed Brief Summary
🔥 Problem
AI agents and developers working in token-constrained environments need a quick TODO landscape overview without consuming excessive context window or screen space.
🌱 Solution
todo-scan brief produces a compressed 2-4 line summary showing total counts, priority breakdown, and the single most urgent item, with optional --since <ref> for trend info and --budget N to cap output lines.
🎁 Outcome
You get the essential TODO health signal in minimal output, ideal for CI summaries and AI agent context.
Git Blame Integration
🔥 Problem
TODO comments lack accountability — you can't tell who wrote them or when without manually running git blame.
🌱 Solution
todo-scan blame enriches each TODO with git blame metadata including author, commit date, and age in days, and flags items older than a configurable threshold as stale.
🎁 Outcome
Every TODO has clear ownership and age, making it easy to prioritize and assign cleanup work.
Discover TODO Relationships
🔥 Problem
TODOs in large codebases form implicit dependency chains, but existing tools treat each item in isolation.
🌱 Solution
todo-scan relate discovers relationships between TODO comments using same-file proximity, shared keywords, cross-references (same issue or author), and tag similarity, scoring each pair on a 0–1 scale.
🎁 Outcome
Related TODOs surface as actionable clusters, revealing hidden patterns in your technical debt.
Inline Suppression
🔥 Problem
Some TODO comments are intentional or false positives, but the only way to exclude them is file-level patterns in .todo-scan.toml, which is too coarse.
🌱 Solution
Add todo-scan:ignore at the end of a TODO line to suppress that specific item, or place todo-scan:ignore-next-line on the line above to suppress the following TODO. Suppressed items are excluded from counts, checks, and output by default. Use --show-ignored to reveal them.
🎁 Outcome
You get fine-grained, inline control over false positives without maintaining exclusion lists in config files.
// TODO: this is tracked normally
// TODO: known false positive todo-scan:ignore
// todo-scan:ignore-next-line
// FIXME: suppressed item
Lint TODO Format
🔥 Problem
TODO comments in team codebases drift in format — inconsistent casing, missing colons, missing authors — degrading scanner reliability and code hygiene.
🌱 Solution
todo-scan lint enforces configurable formatting rules (uppercase tags, colons, author attribution, issue references, message length) and exits with code 1 on violations, making it CI-ready out of the box.
🎁 Outcome
Every TODO in your codebase follows consistent formatting, improving both machine parseability and human readability.
Clean Stale & Duplicate TODOs
🔥 Problem
TODOs accumulate faster than they resolve, and no amount of listing or linting reduces the pile.
🌱 Solution
todo-scan clean identifies TODOs referencing closed GitHub issues (stale) and those with identical messages across files (duplicates).
🎁 Outcome
You get an actionable cleanup list that targets the lowest-hanging fruit first.
CI Quality Gate
🔥 Problem
Without enforcement, TODO debt grows silently until it becomes unmanageable.
🌱 Solution
todo-scan check acts as a CI gate that fails the build when TODO counts exceed a threshold, forbidden tags appear, too many new TODOs are introduced, or deadlines have expired.
🎁 Outcome
Your CI pipeline automatically prevents TODO debt from spiraling out of control.
Workspace-Aware Scanning
🔥 Problem
Monorepos lack per-package TODO visibility — you can't tell which packages are accumulating debt without manually scanning each one.
🌱 Solution
todo-scan workspace list auto-detects your workspace format (Cargo, npm, pnpm, Nx, Go workspaces), scans each package independently, and displays a summary table with TODO counts, configured thresholds, and pass/fail status.
🎁 Outcome
Every package's TODO health is visible at a glance, making it easy to spot where debt concentrates.
Per-Package CI Gate
🔥 Problem
A single global --max threshold doesn't work for monorepos where packages have different maturity levels.
🌱 Solution
todo-scan check --workspace evaluates per-package thresholds defined in [workspace.packages.<name>] config sections, failing the build if any package exceeds its individual limit or uses forbidden tags.
🎁 Outcome
Each package enforces its own TODO budget, matching reality instead of a one-size-fits-all limit.
Single Package Scope
🔥 Problem
Sometimes you only need to see TODOs in one package without the noise from the rest of the monorepo.
🌱 Solution
The --package flag on list, check, and diff scopes the scan to a single workspace package.
🎁 Outcome
You get focused results for just the package you're working on.
HTML Report Generation
🔥 Problem
Presenting TODO metrics to stakeholders requires manual data collection and slide preparation.
🌱 Solution
todo-scan report generates a self-contained HTML dashboard with summary cards, trend charts from git history, tag/priority/age distribution, author breakdowns, and a sortable items table — zero external dependencies.
🎁 Outcome
You get a shareable, presentation-ready report droppable into any CI pipeline as an artifact.
CI Output Formats
🔥 Problem
Plain text output requires extra tooling to integrate with CI dashboards and PR workflows.
🌱 Solution
todo-scan supports --format github-actions for inline PR annotations, --format sarif for GitHub's Code Scanning tab via SARIF, and --format markdown for PR comment bot tables.
🎁 Outcome
todo-scan integrates natively with your CI pipeline without any glue scripts.
Claude Code Task Export
🔥 Problem
Bridging TODO scanning with AI task orchestration requires manually parsing todo-scan list --format json output and constructing TaskCreate calls.
🌱 Solution
todo-scan tasks exports scanned TODOs as Claude Code Task-compatible JSON with action-verb subjects, code context in descriptions, and priority-based ordering.
🎁 Outcome
Your TODOs become AI-assignable tasks with a single command.
Real-time File Watching
🔥 Problem
Re-running todo-scan list after every edit breaks flow when actively cleaning up TODO debt.
🌱 Solution
todo-scan watch monitors the filesystem and shows real-time TODO additions and removals as files change, with optional --max threshold warnings.
🎁 Outcome
You see the impact of your cleanup work instantly without switching context.
Interactive Setup
🔥 Problem
New users must manually create .todo-scan.toml from documentation, slowing onboarding.
🌱 Solution
todo-scan init walks you through an interactive setup that detects your project type (Rust, Node, Go, Python), suggests appropriate exclude directories, and lets you choose which tags to track.
🎁 Outcome
You go from zero to a working configuration in seconds, not minutes of documentation reading.
Shell Completions
🔥 Problem
Shell completions are table stakes for CLI tools but require manual setup.
🌱 Solution
todo-scan completions generates completion scripts for bash, zsh, fish, elvish, and PowerShell and outputs them to stdout for easy installation.
🎁 Outcome
Tab completion works out of the box for every major shell.
What it detects
Tags: TODO, FIXME, HACK, XXX, BUG, NOTE (case-insensitive)
// TODO: basic task
// FIXME(alice): broken parsing logic
// BUG: !! crashes on empty input ← priority: urgent
// TODO: fix layout issue #123 ← issue ref extracted
// HACK(bob): workaround for JIRA-456 ← author + issue ref
// TODO(2025-06-01): migrate to v2 API ← deadline (YYYY-MM-DD)
// TODO(alice, 2025-Q2): refactor auth ← author + deadline (quarter)
// TODO: false positive todo-scan:ignore ← suppressed from output
// todo-scan:ignore-next-line ← suppresses the line below
// FIXME: suppressed item
Supported comment syntax
The scanner uses line-based heuristic comment detection, not a language parser. The following comment prefixes are recognized:
| Prefix | Languages |
|---|---|
// |
Rust, C/C++, Java, Go, JavaScript, TypeScript, Swift, Kotlin, C# |
# |
Python, Ruby, Shell, YAML, TOML, Perl, R |
/* |
C/C++, Java, JavaScript, CSS (block comment start) |
* |
Block comment continuation lines |
-- |
SQL, Haskell, Lua, Ada |
<!-- |
HTML, XML, Markdown |
; |
Lisp, Clojure, Assembly, INI |
(* |
OCaml, Pascal, F# |
{- |
Haskell (block) |
% |
LaTeX, Erlang, MATLAB |
Note: Detection is line-based. Multi-line constructs (Python docstrings, heredocs) are not supported. Tags must appear as standalone words —
todo-scanandTODOSwill not matchTODO.
Supported workspace formats
todo-scan auto-detects monorepo/workspace layouts by checking for these manifest files in order:
| Format | Manifest File | Member Field |
|---|---|---|
| Cargo | Cargo.toml |
[workspace] members |
| npm | package.json |
"workspaces" array |
| pnpm | pnpm-workspace.yaml |
packages list |
| Nx | workspace.json |
"projects" map |
| Go | go.work |
use directives |
Glob patterns in member lists (e.g., packages/*, crates/*) are expanded automatically. You can also define packages manually in .todo-scan.toml with [workspace] configuration.
Installation
Prebuilt binaries (macOS / Linux)
|
Prebuilt binaries (Windows)
powershell -ExecutionPolicy ByPass -c "irm https://github.com/sotayamashita/todo-scan/releases/latest/download/todo-scan-installer.ps1 | iex"
From source
Usage
List TODOs
# List all TODOs in current directory
# Short alias
# Filter by tag
# Filter by priority, author, or path
# Combine filters
# Limit results
# Group by tag, priority, author, or directory (default: file)
# Sort by priority or tag severity
# JSON output
Search TODOs
# Search by message text (case-insensitive)
# Short alias
# Case-sensitive exact match
# Search by issue reference
# Combine with filters
# Show context lines around matches
# JSON output with query metadata
Show context around TODOs
# Show code context around a specific line (default: 5 lines)
# Custom context window
# JSON output with related TODOs
# Add context lines to list output
# Add context lines to diff output
Diff against a git ref
# Compare against main branch
# Compare against recent commits
# Filter diff by tag
# JSON output
Blame — TODO age and ownership
# Show all TODOs with git blame metadata
# Sort by age (oldest first)
# Filter by author (substring match)
# Filter by minimum age
# Set stale threshold (default: 365 days)
# Filter by tag or path
# JSON output
Stats dashboard
# Show tag/priority/author/hotspot summary
# Show trend compared to a git ref
# JSON output
Brief summary
# Compressed summary (2-4 lines)
# With trend info compared to a git ref
# Limit output to N lines
# JSON output
Lint TODO formatting
# Check formatting with sensible defaults (uppercase, colon, no bare tags)
# Require author for specific tags
# Require issue reference for BUG tags
# Enforce max message length
# Combine rules
# JSON output
Exit codes: 0 = pass, 1 = fail, 2 = error.
Clean — stale issues and duplicates
# Dry-run: show stale and duplicate TODOs (always exit 0)
# CI gate: exit 1 if any violations found
# Only flag issues closed more than 30 days ago
# JSON output
Exit codes (with --check): 0 = pass, 1 = fail, 2 = error. Without --check, always exits 0.
HTML report
# Generate report with default settings (todo-scan-report.html)
# Custom output path
# Sample more commits for trend chart
# Skip history analysis (faster)
# Set stale threshold
CI gate
# Fail if total TODOs exceed 100
# Fail if any FIXME or BUG tags exist
# Fail if new TODOs were added since main
# Fail if any TODOs have expired deadlines
# Combine rules
Exit codes: 0 = pass, 1 = fail, 2 = error.
Workspace — monorepo support
# List all packages with TODO counts
# Short aliases
# JSON output
# Scope any command to a single package
# Per-package CI gate (uses [workspace.packages.*] config)
Relate — TODO relationships and clusters
# Discover all relationships (text output)
# JSON output
# Group related TODOs into clusters
# Show TODOs related to a specific item
# Set minimum relationship score (default: 0.3)
# Adjust proximity threshold (default: 10 lines)
# Combine options
Export as Claude Code Tasks
# Preview tasks as JSON to stdout
# Write individual task files to a directory
# Filter by tag, priority, author, or path
# Only TODOs added since a git ref
# Control context lines in task descriptions (default: 3)
# JSON output
Global flags
| Flag | Description |
|---|---|
--root <path> |
Set the project root directory (default: current directory) |
--format <format> |
Output format: text, json, github-actions, sarif, markdown (default: text) |
--config <path> |
Path to config file (default: auto-discover .todo-scan.toml) |
--show-ignored |
Show items suppressed by todo-scan:ignore markers |
Output formats
# GitHub Actions annotations — inline warnings/errors in PR diffs
# SARIF — upload to GitHub Code Scanning / Security tab
# Markdown — tables for PR comment bots
Quick start
# Interactive setup — generates .todo-scan.toml
# Non-interactive with defaults
Shell completions
# Bash
# Zsh
# Fish
Configuration
Create a .todo-scan.toml in your project root (or run todo-scan init). The file is discovered by searching upward from the current directory.
# Tags to scan for (default: all supported tags)
= ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"]
# Directories to exclude from scanning
= ["vendor", "third_party"]
# Regex patterns to exclude files
= [".*\\.min\\.js$", ".*generated.*"]
[]
# Maximum total TODOs allowed
= 100
# Maximum new TODOs allowed (requires --since)
= 0
# Tags that cause check to fail immediately
= ["BUG"]
# Fail if any TODOs have expired deadlines
= true
[]
# Days threshold for marking TODOs as stale (default: 365d)
= "180d"
[]
# Enable stale issue detection (default: true)
= true
# Enable duplicate detection (default: true)
= true
# Only flag issues closed longer than this duration (default: disabled)
# since = "30d"
[]
# Disable automatic workspace detection (default: true)
# auto_detect = false
# Per-package check thresholds
[]
= 50
= ["BUG"]
[]
= 20
[]
# Reject TODOs with empty message (default: true)
= true
# Enforce uppercase tag names (default: true)
= true
# Enforce colon after tag (default: true)
= true
# Enforce max message character count (default: disabled)
# max_message_length = 120
# Require (author) for specified tags (default: disabled)
# require_author = ["TODO", "FIXME"]
# Require issue ref for specified tags (default: disabled)
# require_issue_ref = ["BUG"]
All fields are optional. Unspecified values use sensible defaults.
A machine-readable JSON Schema is available at schema/todo-scan.schema.json for editor validation and autocompletion (e.g., Taplo, Even Better TOML).
Configuration Reference
Top-level fields
| Field | Type | Default | Description |
|---|---|---|---|
tags |
string[] |
["TODO","FIXME","HACK","XXX","BUG","NOTE"] |
Tag keywords to scan for |
exclude_dirs |
string[] |
[] |
Directory names to skip during scanning |
exclude_patterns |
string[] |
[] |
Regex patterns; matching file paths are excluded |
[check] section
| Field | Type | Default | Description |
|---|---|---|---|
max |
integer |
(none) | Maximum total TODOs allowed |
max_new |
integer |
(none) | Maximum new TODOs allowed (requires --since) |
block_tags |
string[] |
[] |
Tags that cause check to fail immediately |
expired |
boolean |
(none) | Fail if any TODOs have expired deadlines |
[blame] section
| Field | Type | Default | Description |
|---|---|---|---|
stale_threshold |
string |
"365d" |
Duration threshold for marking TODOs as stale |
[clean] section
| Field | Type | Default | Description |
|---|---|---|---|
stale_issues |
boolean |
true |
Enable stale issue detection via gh CLI |
duplicates |
boolean |
true |
Enable duplicate TODO detection |
since |
string |
(none) | Only flag issues closed longer than this duration (e.g., "30d") |
[lint] section
| Field | Type | Default | Description |
|---|---|---|---|
no_bare_tags |
boolean |
true |
Reject TODOs with empty message |
uppercase_tag |
boolean |
true |
Enforce uppercase tag names |
require_colon |
boolean |
true |
Enforce colon after tag |
max_message_length |
integer |
(none) | Enforce max message character count |
require_author |
string[] |
(none) | Require (author) for specified tags |
require_issue_ref |
string[] |
(none) | Require issue ref for specified tags |
[workspace] section
| Field | Type | Default | Description |
|---|---|---|---|
auto_detect |
boolean |
true |
Enable automatic workspace detection |
[workspace.packages.<name>] section
Per-package check thresholds for todo-scan check --workspace.
| Field | Type | Default | Description |
|---|---|---|---|
max |
integer |
(none) | Maximum TODOs allowed for this package |
block_tags |
string[] |
[] |
Tags that cause check to fail for this package |
Agent Skill
todo-scan provides a Claude Code plugin that enables AI coding agents to automatically use todo-scan commands for TODO tracking, CI gate configuration, and code quality checks.
Install from plugin marketplace (recommended)
Install with skills CLI
Manual install
CI Integration
GitHub Action
The easiest way to add todo-scan to your CI pipeline. No binary installation or workflow boilerplate needed.
Minimal usage:
- uses: sotayamashita/todo-scan@v1
with:
max: '100'
Full example with SARIF and diff:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Required for base-ref diff
- uses: sotayamashita/todo-scan@v1
with:
max: '100'
block-tags: 'BUG,FIXME'
max-new: '0'
base-ref: 'origin/main'
sarif: 'true'
Inputs
| Input | Default | Description |
|---|---|---|
version |
latest |
todo-scan version to install (e.g. 0.1.0) |
max |
Maximum total TODOs allowed | |
block-tags |
Comma-separated tags that cause check to fail | |
max-new |
Maximum new TODOs allowed (requires base-ref) |
|
base-ref |
Git ref to diff against (requires fetch-depth: 0) |
|
expired |
false |
Fail if any TODOs have expired deadlines |
sarif |
false |
Generate and upload SARIF to GitHub Code Scanning |
root |
Project root directory | |
config |
Path to .todo-scan.toml config file |
|
token |
github.token |
GitHub token for SARIF upload |
Outputs
| Output | Description |
|---|---|
total-count |
Total number of TODOs found |
passed |
Whether the check passed (true/false) |
sarif-file |
Path to generated SARIF file (if sarif: true) |
GitHub Actions (manual setup)
- name: Check TODOs
run: |
todo-scan check --max 100 --block-tags BUG,FIXME
GitHub Actions with inline annotations
- name: Check TODOs with annotations
run: |
todo-scan check --max 100 --format github-actions
SARIF upload to Code Scanning
- name: Scan TODOs
run: todo-scan list --format sarif > todo-scan.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: todo-scan.sarif
HTML report artifact
- name: Generate TODO report
run: todo-scan report --output todo-scan-report.html
- name: Upload TODO report
uses: actions/upload-artifact@v4
with:
name: todo-scan-report
path: todo-scan-report.html
PR review with diff
Note:
todo-scan diffandtodo-scan check --sinceneed access to the base ref's git history.actions/checkout@v4usesfetch-depth: 1(shallow clone) by default, which means the base SHA is not available. Setfetch-depth: 0to fetch the full history.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for todo-scan to access the base ref
- name: Check new TODOs
run: |
todo-scan check --max-new 0 --since origin/main
Release Workflow
Releases are fully automated via release-please and cargo-dist.
How to release
- Merge PRs with Conventional Commits into
main - release-please automatically opens (or updates) a Release PR that bumps the version and updates the changelog
- Merge the Release PR — this creates a git tag and GitHub Release
- cargo-dist builds platform binaries and uploads them to the GitHub Release
sequenceDiagram
participant M as main branch
participant RP as release-please
participant CD as cargo-dist
participant GH as GitHub Release
M->>RP: push with conventional commits
RP->>M: open/update Release PR (bump version + changelog)
M->>RP: merge Release PR
RP->>GH: create git tag + draft release
GH->>CD: tag push triggers release workflow
CD->>GH: upload binaries, installers, checksums
Commit conventions
| Prefix | Version bump | Example |
|---|---|---|
fix: |
patch (0.0.x) | fix(scanner): handle empty files |
feat: |
minor (0.x.0) | feat(cli): add --verbose flag |
feat!: or BREAKING CHANGE: |
major (x.0.0) | feat!: remove deprecated --count flag |
Supported targets
| Target | OS | Arch |
|---|---|---|
aarch64-apple-darwin |
macOS | Apple Silicon |
x86_64-apple-darwin |
macOS | Intel |
x86_64-unknown-linux-gnu |
Linux | x86_64 |
x86_64-pc-windows-msvc |
Windows | x86_64 |
Release artifacts
Each release includes:
- Platform-specific binary archives (
.tar.xz/.zip) - Shell installer (
todo-scan-installer.sh) for macOS and Linux - PowerShell installer (
todo-scan-installer.ps1) for Windows - Checksums for verification
Development
# Build
# Run tests
# Run against a project