Skip to main content

TodoScope

TodoScope finds maintenance comments (TODO, FIXME, ...) in your code and prints a clean report. Optionally, it asks an AI to interpret each comment and estimate its priority — without ever sending your source code anywhere.

todoscope src/

What it does

  • Scans Python, JavaScript, TypeScript, JSX/TSX, Rust, Java, Go, C, C++, and C# files for comments that start with your markers (TODO by default). The default enabled set is .py .js .jsx .ts .tsx .rs; enable more extensions (.java .go .c .h .cpp .cc .cxx .hpp .cs) through extensions in .todoscope.json.
  • Only real comments count: TODO inside strings, template literals, JSX text, or raw strings is ignored.
  • Respects every .gitignore in the tree (root and nested, with git's override semantics) and an optional exclusion list.
  • Works fully offline — the AI part is optional.
  • When AI is on, it sends only each comment's ID, marker, and text. No file names, no paths, no line numbers, no code.

Install

Requires Python 3.12+.

pipx install todoscope        # recommended
# or
uv tool install todoscope     # if you use uv
# or
python3 -m pip install todoscope

Use

todoscope src/                # scan a folder recursively
todoscope src/main.py         # scan one file
todoscope .                   # scan the whole project
todoscope src/ --no-ai        # normal report, skip AI
todoscope src/ --quiet        # one line per finding, no headings, no AI
todoscope src/ --verbose      # extra details on stderr
todoscope src/ --format json  # machine-readable JSON report on stdout

That's it. Findings are sorted by folder depth, then path, then line.

--format json prints a deterministic JSON document to stdout (scan metadata, findings, skipped counts, and the AI section with a machine- readable status/reason). Verbose details and errors always go to stderr. JSON never contains API keys or environment values.

Configuration

Everything optional lives in a .todoscope.json in your project root:

{
  "markers": ["TODO", "FIXME"],
  "extensions": [".py", ".js", ".jsx", ".ts", ".tsx", ".rs"],
  "exclude": ["tests/fixtures/", "generated/"],
  "model": "your-ai-model-id",
  "max_ai_characters": 20000
}
Key What it does
markers Replaces the default marker list (["TODO"]). Matching is case-sensitive and prefix-based; the longest matching marker wins.
extensions Replaces the default scanned extensions.
exclude Skips exact project-root-relative paths or directory prefixes.
model Required for AI analysis. There is no default model.
max_ai_characters Lower AI payload limit (hard ceiling: 100,000).

Invalid configuration stops with a clear error (exit code 3).

AI analysis

To enable it you need both:

  1. An API key — from your shell (TODOSCOPE_API_KEY) or a .env file in the project root:

    TODOSCOPE_API_KEY=...
    TODOSCOPE_SECONDARY_API_KEY=...
    

    Shell values win over .env. If a key comes from .env, that file must be ignored by your .gitignore, otherwise AI is refused for safety.

  2. A model in .todoscope.json.

When enabled, TodoScope makes one request and then prints one complete report: per finding you get a short interpretation and an estimated priority (High / Medium / Low / Unclear), plus an overall summary. If the request fails and a secondary key is configured, an interactive terminal offers one retry with it — the secondary key is never used silently.

Priorities are estimated from comment text only. No source code was provided to the AI.

Using DeepSeek (or another OpenAI-compatible provider)

The OpenAI SDK reads OPENAI_BASE_URL from your environment. For DeepSeek:

export OPENAI_BASE_URL=https://api.deepseek.com
todoscope .

or as a permanent alias in ~/.zshrc:

alias todoscope="OPENAI_BASE_URL=https://api.deepseek.com /home/$USER/.local/bin/todoscope"

Privacy

The only data from your repository that reaches the AI is each finding's ID, marker, and extracted comment text. Everything else stays local. Comments are treated as untrusted data — instructions written inside a comment can never change TodoScope's behaviour. Never put credentials or secrets in code comments, because comment text may be sent to the AI.

Exit codes

  • 0 — scan finished (including local-only results after any AI problem)
  • 1 — unexpected failure
  • 2 — bad path/usage, or an ignored target refused in non-interactive mode
  • 3 — configuration error

Development

uv sync                       # set up the environment
uv run pytest                 # tests
uv run ruff check .           # lint
uv run ruff format --check .  # format check
uv build                      # wheel + sdist

Continuous integration runs these same checks on every push and pull request (Python 3.12 and 3.13).

Releasing

  1. Bump version in pyproject.toml (minor for features, patch for fixes).
  2. Add a CHANGELOG.md entry for the new version.
  3. Commit, then tag and push the tag:
git tag v0.2.0
git push
git push --tags

The publish workflow verifies everything, uploads to PyPI using the PYPI_TOKEN repository secret, and creates a GitHub release automatically.

Changelog

See CHANGELOG.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

todoscope-0.6.0.tar.gz (20.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

todoscope-0.6.0-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

File details

Details for the file todoscope-0.6.0.tar.gz.

File metadata

  • Download URL: todoscope-0.6.0.tar.gz
  • Upload date:
  • Size: 20.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for todoscope-0.6.0.tar.gz
Algorithm Hash digest
SHA256 c8ef82163d3215bc03e3d77386b6eebd464644885d7310faf9090323a11a13cd
MD5 890d1d6dba168af3a888bf18ddc7ef0f
BLAKE2b-256 bac1965804aaca9a2b6a13185cf7df318f806a9ca4927b4d31f6bc38cacbfed9

See more details on using hashes here.

File details

Details for the file todoscope-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: todoscope-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 25.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for todoscope-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b9a7b5a19c0fe30c34a2f801d1c0024d06d2a577802050e2d8ded9ac199ef97
MD5 4a0a019d1ac62e0c02bbbf2cd3970504
BLAKE2b-256 efe7da047c6ae987d6b8e7bbcef407dd8d5d2908b7423acac2430bad3168acde

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page