A GitHub CLI extension that extracts TODO-style comments from pull request diffs, helping you track action items and reminders in your code changes.
- PR-Focused Detection: Extracts TODO-style comments only from pull request diff additions
- Syntax-Aware Parsing: Uses Tree-sitter for accurate comment detection in supported languages, with regex fallback for others
- Configurable Marker Policy: Customize marker types, severities, and ignored types with CLI flags or YAML config
- Config Initialization: Create project or global config files with
gh pr-todo init - CI and GitHub Actions Support: Emit workflow annotations and fail CI only for marker types configured as
error - Flexible Output: Colorized output with grouping, file-name-only, and count-only modes
gh ext install Suree33/gh-pr-todoPrerequisites:
- GitHub CLI installed and authenticated
Navigate to your repository with an open pull request and run:
gh pr-todoYou can specify PR numbers, URLs, or branches, and you can target another repository with -R/--repo or a PR URL:
# Specify a specific PR number
gh pr-todo 123
# Specify a PR from a different repository
gh pr-todo 456 -R owner/repo
# Specify a PR by URL
gh pr-todo https://github.com/owner/repo/pull/789
# Specify a branch
gh pr-todo feature-branch
# Specify a branch from a different repository
gh pr-todo feature-branch -R owner/repo
# Display only names of the files containing TODO-style comments
gh pr-todo --name-only
# Display only the number of TODO-style comments
gh pr-todo -c
# Group TODO-style comments by file (or type)
gh pr-todo --group-by file
# Override severities for one or more TODO types
# Format: --severity LEVEL=TYPE[,TYPE...]
gh pr-todo --severity warning=TODO,HACK --severity error=FIXME
# Ignore specific marker types from detection (affects all output modes)
gh pr-todo --ignore NOTE,HACK[<number> | <url> | <branch>]: Specify a PR by number, URL, or branch name-R, --repo [HOST/]OWNER/REPO: Select another repository using the [HOST/]OWNER/REPO format (requires a PR number, URL, or branch argument)--group-by: Group TODO-style comments byfileortype--name-only: Display only names of the files containing TODO-style comments. If both--name-onlyand--countare specified,--name-onlytakes precedence-c, --count: Display only the number of TODO-style comments--severity LEVEL=TYPE[,TYPE...]: Override severity for one or more TODO types; repeatable, whitespace-tolerant, and last assignment wins for duplicate types--ignore TYPE[,TYPE...]: Ignore specified marker types; repeatable, case-insensitive, whitespace-tolerant. Ignored types are not detected or reported in any mode, including annotations and CI failure counts-h, --help: Display help information--no-ci-fail: Disable non-zero exit when error-level TODOs are found in CI (see below)
Use gh pr-todo init to create a default configuration file. Without an explicit location, it prompts in terminals and falls back to a plain text prompt when redirected:
gh pr-todo initFor non-interactive scripts, pass a location:
gh pr-todo init --repo # Project (.gh-pr-todo.yml)
gh pr-todo init --global # user config dir/gh-pr-todo/config.ymlPrompted locations are:
Project (.gh-pr-todo.yml)— repository-scoped config, created at the root of the current Git repository; shown in the interactive selector only when run inside a Git repositoryGlobal (user config dir/gh-pr-todo/config.yml)— global config, shared across all repos (typically$XDG_CONFIG_HOME/gh-pr-todo/config.ymlon Linux, falling back to~/.config/gh-pr-todo/config.yml; actual path depends on your OS)
If the selected config file already exists, init refuses to overwrite it unless --force is passed. --force can be combined with --repo or --global. --repo and --global are mutually exclusive, and using both returns an error. If you choose the repo-root location and .github/gh-pr-todo.yml exists, init reports it instead because that narrower config takes precedence over the root project config:
gh pr-todo init --force
gh pr-todo init --repo --forceThe default configuration matches the runtime severity policy:
severity:
notice:
- TODO
- NOTE
warning:
- FIXME
- HACK
- XXX
- BUG
error: []
ignore: []Severity overrides and ignored types can be persisted in YAML configuration files. Config files use the following schema:
severity:
notice|warning|error: [TYPE...]
ignore:
- TYPEExample (.gh-pr-todo.yml):
severity:
warning:
- TODO
error:
- FIXME
ignore:
- NOTEConfig files use whole-file replacement by precedence: each existing file replaces the entire previous configuration. This applies to both severity overrides and ignored types.
Local files (in order of increasing precedence):
- User config dir
gh-pr-todo/config.yml(global, shared across all repos; usually~/.config/gh-pr-todo/config.ymlon Linux) <repo>/.gh-pr-todo.yml(repository root)<repo>/.github/gh-pr-todo.yml(narrower repo scope)- CLI
--severityand--ignoreflags (highest priority)
If a repo config file exists (root or .github), the global config is not applied. Within a repo, .github/gh-pr-todo.yml replaces .gh-pr-todo.yml entirely. CLI flags are applied on top of the resolved config.
Configured TODO types are automatically detected in PR diffs alongside the built-in types. You can define custom types like SECURITY or PERF and they will be recognized.
When targeting another repository with --repo or a PR URL, remote configuration is resolved with the following precedence:
- Remote PR head branch config
- Remote PR base branch config
- Remote default branch config
- Global local config, only when no remote config exists
- CLI
--severityand--ignoreflags (highest priority)
For each remote scope, .github/gh-pr-todo.yml replaces .gh-pr-todo.yml entirely. A remote config file replaces the global config entirely; global config is only used as a fallback when no remote config exists.
The ignore config key lists marker types to exclude entirely from detection. Ignored types are not parsed or reported in any mode:
- Default output
--count--name-only--group-by fileor--group-by type- GitHub Actions annotations
- CI failure counts
Example:
# .gh-pr-todo.yml
severity:
warning:
- TODO
error:
- FIXME
ignore:
- NOTE
- HACKThis config makes the tool ignore NOTE and HACK markers while still detecting TODO and FIXME. Ignored types can be built-in or custom and take precedence over severity: even if a type has error severity, ignoring it removes it from detection entirely.
Use the CLI --ignore flag to extend the ignore set temporarily:
gh pr-todo --ignore NOTE,HACKWhen the CI environment variable is truthy (e.g. 1, true, parsed via Go's strconv.ParseBool), gh pr-todo exits with status 1 if any error-level TODO-style comments are detected in the PR diff. By default, no built-in keyword type is mapped to error-level, so gh pr-todo does not fail CI based on default keywords alone. Use configuration files or --severity to promote recognized TODO keywords to error when you want CI failures, for example --severity error=FIXME. GITHUB_ACTIONS=true (set by the GitHub Actions runner) is treated as CI=true even when CI is missing or falsy.
# GitHub Actions example — CI=true is set automatically
- run: gh pr-todo ${{ github.event.pull_request.number }}Pass --no-ci-fail to suppress non-zero exit even when error-level TODOs exist:
gh pr-todo --count --severity error=FIXME --no-ci-failWhen GITHUB_ACTIONS=true (set automatically by the GitHub Actions runner), gh pr-todo additionally emits workflow commands so each TODO appears as an annotation on the workflow run and pull request:
Default annotation severities:
TODO,NOTE→::noticeannotationsFIXME,HACK,XXX,BUG→::warningannotations
You can override them with configuration files or --severity, for example:
--severity warning=TODO,NOTE--severity error=FIXME
Annotations reflect the resolved severity of each keyword and are independent of CI exit behavior: warning annotations are displayed but do not cause a non-zero exit by default. Only error-level TODOs cause CI failure.
Each annotation is anchored to the file and line of the TODO, with the keyword used as the annotation title. Regular human-readable output is still printed, and the spinner is suppressed to keep Actions logs clean.
Workflow commands are only emitted in the default mode. The machine-readable modes --count and --name-only keep their plain output unchanged so that count=$(gh pr-todo --count) and similar shell pipelines stay reliable in Actions.
✔ Fetching PR diff...
Found 3 TODO-style comment(s)
* src/api/users.go:42
// TODO: Add input validation for email format
* components/Header.tsx:15
// FIXME: Memory leak in event listener cleanup
* docs/setup.md:8
<!-- NOTE: Update this section after v2.0 release -->
The tool recognizes TODO-style comments in various formats:
| Format | Example |
|---|---|
| C-style | // TODO: Fix this bug |
| C-style block | /* HACK: Quick fix for demo */ |
| Shell/Python | # FIXME: Optimization needed |
| HTML/XML | <!-- NOTE: Review this section --> |
| Assembly/Config | ; XXX: Temporary workaround |
TODOFIXMEHACKNOTEXXXBUG
Additional keywords can be defined via configuration files or the --severity CLI flag. Any custom type assigned a severity will be detected in PR diffs alongside the default keywords. For example:
# .gh-pr-todo.yml
severity:
error:
- SECURITY
warning:
- PERFThis configures SECURITY and PERF as recognized TODO markers.
- Severity keys (
notice,warning,error) are case-insensitive. - Empty lists (
warning: []) are allowed and ignored. - A TODO type must not appear under multiple severity levels in the same file.
- The old
TYPE: levelformat is not supported.
git clone https://github.com/Suree33/gh-pr-todo.git
cd gh-pr-todo
go build -o gh-pr-todo .├── main.go # CLI entry point
├── internal/
│ ├── config/
│ │ ├── config.go # YAML config parsing and local loading
│ │ └── remote.go # Remote config loading
│ ├── github/
│ │ └── client.go # GitHub API client (diffs, file contents, remote config)
│ ├── output/
│ │ ├── printer.go # Terminal output rendering
│ │ └── workflow.go # GitHub Actions annotation commands
│ └── parser.go # Diff parsing logic (Tree-sitter + regex)
├── pkg/
│ └── types/
│ ├── groupby.go # GroupBy enum
│ └── todo.go # TODO type definitions
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the terms specified in the LICENSE file.
Found a bug or have a feature idea? Please open an issue on GitHub.