CLI tool
azat-io/todoctor avatar
azat-io/todoctor

todoctor: reading the technical debt hiding in a Git history

🐍 CLI tool to analyze and report TODO comments in JavaScript and TypeScript Git repositories

524 stars9 forksRustMIT

At a glance

What is it?
todoctor is a Rust CLI published to npm that scans a JavaScript or TypeScript repository commit by commit, collects TODO and FIXME style comments, and renders a report showing how that backlog grew or shrank over time.
Who is it for?
todoctor suits maintainers of a JavaScript or TypeScript repository who suspect their TODO comments are accumulating faster than they are being cleared, and who want to see the backlog as a curve rather than as a count. It does not fit a Python or Go codebase, it does not track ownership in an issue tracker, and its default keyword set is broad enough that you will want to narrow it before reading the numbers.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 27 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The backlog that never reaches a tracker

TODO comments are cheap to write and easy to forget. The project's own diagnosis is blunt: developers leave these comments to mark areas that need improvement or refactoring, but they are rarely converted into tasks in tracking systems, so they remain in the code and can sit there for years without attention. Todoctor treats that hidden backlog as its input. It is a command line tool, distributed on npm as todoctor and implemented in Rust, and its declared purpose is analyzing, tracking and visualizing technical debt in a codebase using Git.

The audience is narrower than the phrase technical debt usually attracts. The package description limits it to JavaScript and TypeScript Git repositories, and the tool works on the files that Git already tracks, which means untracked or ignored working files are out of scope by construction. A team maintaining a monorepo of TypeScript packages can point it at the root; a team whose hot code lives in Python or Go will get nothing from it.

The interesting part is the time dimension. A lint rule can tell you how many TODOs exist right now. Todoctor instead walks the commit history and keeps a record of what each comment looked like across time, so the output is a history of added, resolved and modified comments rather than a snapshot. That difference matters for the specific failure the project names: a backlog nobody sees is a backlog nobody reduces.

Commit history plus git blame as the data source

The mechanism has two halves that most static analyzers lack. First, it analyzes each commit to gather and update comment history, so a TODO that appeared two years ago and was later removed still exists in the record. Second, it integrates with git blame to track the authorship and timing of changes, which is what makes the per comment age figures possible.

The Rust manifest shows the pieces that support this. Dependencies include chrono for date handling and serde, clap with the derive feature for the command line surface, tokio with the full feature set for asynchronous work, indicatif for the progress output you see during a scan, and the open crate, which is the usual way a Rust program hands a file to the system browser. Parsing and text work are covered by oxc, a JavaScript and TypeScript parser, together with regex and ropey. Serialisation for the report formats relies on csv and serde_json. The dependency list is a reasonable sketch of a parser that walks a history, extracts comments, and writes a file at the end.

The comment itself is not the only unit tracked. Alongside the comment text, the generated list carries the file path, line numbers and authorship information, so a report entry can be traced back to a person and a place in the tree. Whether that metadata is fully accurate for comments that were moved by later commits is not something the documentation addresses.

A first run with npx todoctor

There is no build step for the end user. The documented usage is to run the tool from the root directory of your project, and the project is published to npm under the name todoctor, so npx is enough:

sh
npx todoctor

The tool collects the data automatically and displays the history of TODO and FIXME comments across commits. Because the analysis is anchored in Git history rather than in the working tree, running it in a shallow clone would give you a different and much thinner result than a full clone, and the documentation does not call this out, so it is worth checking the clone depth before you trust an empty graph.

If you prefer a local install over an on the fly download, the package manifest declares a bin entry named todoctor pointing at ./bin/todoctor.js, and the published files include ./bin, ./build and ./dist, which means the package ships a compiled binary alongside the JavaScript entry point. The version reported by --version matches the npm package version, currently 1.4.2.

A useful first invocation on a real repository is one with a wider time window than the default, since three months of history on an established project often contains too few commits to show a trend:

sh
todoctor --months 6

The --months option sets how many months of repository history are included when tracking TODOs, and it defaults to 3 months.

The default keyword list is broad on purpose

What counts as technical debt is configurable, and the default is deliberately generous. The built in set of markers is TODO, FIXME, XXX, HACK, BUG, OPTIMIZE, REFACTOR, TEMP, CHANGED, IDEA, NOTE, REVIEW, NB, QUESTION, DEBUG, KLUDGE, COMPAT, WARNING, DANGER, INFO, DEPRECATED and COMBAK.

That list has consequences. NOTE, INFO, REVIEW and WARNING are common in ordinary prose comments and in tooling annotations, so a project that comments carefully will see its total inflated by comments nobody intended as a debt marker. Tooling suppression comments such as eslint-disable-next-line are the kind of thing teams tend to want counted, which is what --include-keywords is for:

sh
todoctor --include-keywords eslint-disable-next-line

The mirror operation removes markers from the report:

sh
todoctor --exclude-keywords WARNING --exclude-keywords DEPRECATED

Both options can be repeated. If you plan to compare two reports over time, keep the keyword set identical between runs, otherwise a change in totals may reflect a change in configuration rather than a change in the code.

Reading the graph, the statistics block and the todo table

The report has three parts, and they answer three different questions. The first is the todos graph, a detailed visualisation of how many todo comments were added, resolved or modified across the project's history, which is the view that tells you whether the backlog is draining. The second is a statistics block with concrete figures: the total number of todo comments, the age of the oldest todo, the average age of all todos, and the author with the highest number of todo comments. The third is a tabular list of every todo comment, including the comment text, the file path, and metadata such as line numbers and authorship information.

The middle section is the one that changes behaviour most, and it is also the one that is most easily misread. An average age close to the oldest age means the backlog is static, a handful of very old markers carrying a long tail of fresh ones. A large total paired with a short average means many recent additions, which points at a process problem rather than an old cleanup debt. The author ranking is blunt by design and should be treated as a signal about where the comments originate, not as a metric for judging people.

A published example report is available at the project homepage, https://todoctor.azat.io, which is the fastest way to decide whether the graph format suits you before running anything on your own repository.

Where the report lands and how you can script it

The output location is a project relative folder, and the format is selectable. By default the report file is saved into a todoctor folder in the project root and the format is html:

sh
todoctor --output report

The output format option accepts html, json and csv, with html as the default:

sh
todoctor --output-format json

That choice matters more than it first appears. The html report is the readable one, with the graph and the statistics rendered, but json and csv are the ones that can be diffed across runs, archived as build artefacts, or fed to another tool. Since the whole point of the tool is a time series, the ability to store a machine readable snapshot per run is what turns a one off curiosity into a trend you can watch. The open dependency and the default html format also suggest the tool intends to open the result in a browser once the scan finishes, although the documentation does not describe that behaviour in detail.

The tool also exposes --help, which lists the available options, and --version, which prints the current version of Todoctor. There is no documented flag for suppressing the progress display, for limiting the scan to a subdirectory, or for changing the theme of the generated page.

What the scan cannot see

The limits are structural rather than bugs. Todoctor only understands JavaScript and TypeScript, so TODO comments in configuration files, templates, Markdown, shell scripts or any other language are invisible to it, even though the keyword list itself contains markers such as KLUDGE and COMPAT that often appear in exactly those places. It also only sees what Git tracks, which excludes anything matched by .gitignore, and files in .gitignore are ignored automatically so you do not need to list them again.

The --ignore option exists for the tracked files you do not want counted:

sh
todoctor --ignore src/deprecated/ --ignore tests/legacy.test.js

A second limit is that the tool counts what is in the comments, not whether the work behind them was done. A TODO can be edited in place across twenty commits, and the report will show modifications; it does not tell you whether the refactor it describes ever landed. The documentation does not explain how such repeated edits are counted in the added, resolved or modified totals, so treat the graph as a shape rather than as an exact ledger.

The third limit is the default window. Three months is a short horizon on a repository with infrequent commits, and an empty or nearly flat graph is a plausible outcome rather than proof that the codebase is clean.

Where a tracker fits better than a scan, and what the project costs to keep

The obvious alternative to todoctor is the path the project describes in its own motivation section: convert the comments into tasks in a tracking system. That approach and todoctor answer the same question with opposite machinery. A tracker gives you ownership, priority, status and a closing workflow, and it only contains the items somebody decided to promote. Todoctor reads the whole repository history and gives you the backlog nobody promoted, with ages and authorship, but it has no notion of priority, assignee, due date or done state. If the goal is to get one specific piece of work scheduled, a tracker is the better tool. If the goal is to discover work that was never scheduled, todoctor is the one that can see it.

On maintenance, the facts are recent but not constant. The last push was on 2026-09-26, and the repository is not archived. Releases come at a moderate pace rather than a fixed cadence: v1.4.0 on 2025-06-15, v1.4.1 on 2026-04-30 and v1.4.2 on 2026-07-04. The npm version and the npm package version field both read 1.4.2. The Cargo manifest still carries version 1.0.0 for the crate while the npm package reads 1.4.2, which is a versioning detail to be aware of when you compare release notes against the crate rather than the package.

The licence is MIT, which means the code can be used and modified under permissive terms with the notice retained. It also means the dependency licences, including the JavaScript parser and the asynchronous runtime, come along with the tool, and reviewing those is a reasonable step for a team shipping a report to a browser.

For an engineering team that wants a single view of an aging JavaScript or TypeScript backlog and can accept an MIT licensed Rust binary that writes a report folder into the repository root, todoctor is a small, focused way to get that view. The first thing to verify is the keyword set: run it with the defaults, run it again with --exclude-keywords applied to the markers your codebase uses casually, and see whether the total and the age of the oldest todo look like the backlog you actually recognise.

Editorial conclusion

todoctor suits maintainers of a JavaScript or TypeScript repository who suspect their TODO comments are accumulating faster than they are being cleared, and who want to see the backlog as a curve rather than as a count. It does not fit a Python or Go codebase, it does not track ownership in an issue tracker, and its default keyword set is broad enough that you will want to narrow it before reading the numbers. Before adopting it, run it once with --months 6 on a repository you already know well, compare the reported total and oldest-comment age against your own sense of the backlog, and check that the default keyword list has not swept in noise such as NOTE, INFO or WARNING.

Frequently asked questions

How do I install and run todoctor on a JavaScript or TypeScript repository?

Run it from the root directory of the project with npx todoctor, which collects the data and displays the history of TODO and FIXME comments across commits. The npm package also declares a todoctor binary, so a local install exposes the same command line options.

Which comment markers does todoctor treat as technical debt, and how can I change the list?

The default set covers TODO, FIXME, XXX, HACK, BUG, OPTIMIZE, REFACTOR, TEMP, CHANGED, IDEA, NOTE, REVIEW, NB, QUESTION, DEBUG, KLUDGE, COMPAT, WARNING, DANGER, INFO, DEPRECATED and COMBAK. Use --exclude-keywords to remove markers and --include-keywords to add your own, and both options can be repeated.

Can todoctor output something other than an HTML report?

Yes. The --output-format option accepts html, json and csv, with html as the default, and the --output option sets the folder where the report file is saved instead of the default todoctor folder in the project root.

Does todoctor work outside JavaScript and TypeScript projects?

No. The tool is described as a CLI tool to analyze and report TODO comments in JavaScript and TypeScript Git repositories, and it works on the files that Git tracks, so files ignored by .gitignore are skipped automatically.

How far back does todoctor look by default?

The --months option controls the window and defaults to 3 months. Setting it to a wider value, for example todoctor --months 6, gives a longer view of how the comment backlog changed.

Official sources

  1. azat-io/todoctor on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/azat-io-todoctor.svg)](https://hysenlabs.com/projects/azat-io-todoctor)

Was this useful?