Strip the noise. Keep the code.
uncomment removes comments from source code using tree-sitter's AST — so it is 100% accurate and never touches comment-like text inside strings. It keeps what matters by default (TODO/FIXME, docs, and linting directives) across 300+ languages, with parallel processing and a safe dry-run mode.
AST-accurate · 306 languages · zero false positives · smart preservation · parallel · dry-run
Install · Features · Usage · Configuration · How it works · Contributing
Regex-based comment strippers guess. They delete a // inside a string literal, mangle a URL in a
docstring, or leave a linting directive your CI depends on. uncomment doesn't guess: it parses your
code into a real syntax tree and removes only the nodes that are genuinely comments.
Originally built to clean up AI-generated code drowning in explanatory comments, it now works on anything with a tree-sitter grammar.
- 100% accurate — tree-sitter AST parsing identifies comments structurally, not by pattern matching
- No false positives — never removes comment-like content from strings
- Smart preservation — keeps TODO/FIXME, docs, and language-specific linting directives by default
- 306 languages — powered by tree-sitter-language-pack, grammars downloaded on demand
- Parallel — multi-threaded processing that scales across cores
- Safe — dry-run mode with line-by-line diffs previews every change before you write
- Configurable — hierarchical TOML config with a smart
initcommand - Built-in benchmarking — optional performance analysis and profiling tools
| Channel | Command |
|---|---|
| Homebrew (macOS/Linux) | brew tap goldziher/tap && brew install uncomment |
| Cargo (Rust) | cargo install uncomment |
| npm (Node.js) | npm install -g uncomment-cli |
| pip (Python) | pip install uncomment |
Run without installing:
npx -y uncomment-cli@latest .
uvx uncomment .Add --dry-run to preview changes before writing.
Build from source
git clone https://github.com/Goldziher/uncomment.git
cd uncomment
cargo install --path .Requires Rust 1.70+. npm and pip packages download pre-built binaries automatically.
# Generate a configuration file tuned to your project
uncomment init
# Remove comments from a directory
uncomment src/
# Preview changes as a diff, write nothing
uncomment src/ --dry-run --diff# Single file
uncomment file.py
# Multiple files / globs
uncomment src/*.py
# Also strip doc comments and docstrings
uncomment --remove-doc file.py
# Also remove TODO and FIXME comments (preserved by default)
uncomment --remove-todo --remove-fixme file.py
# Add custom patterns to preserve
uncomment --ignore "HACK" --ignore "WARNING" file.py
# Process an entire tree with all CPU cores
uncomment . -j 0Run uncomment --help for the full, grouped list of options.
Configuring with init
The init command detects the languages in your project and writes a matching .uncommentrc.toml:
# Smart detection — includes only the languages it finds
uncomment init
# All 49 built-in languages
uncomment init --comprehensive
# Interactive selection
uncomment init --interactive
# Custom output location / overwrite
uncomment init --output config/uncomment.toml --forceOptional benchmarking tools
Development binaries for benchmarking and profiling are gated behind the bench-tools feature so
they are not installed for regular users:
# Install with extras
cargo install uncomment --features bench-tools
# Or run locally
cargo run --release --features bench-tools --bin benchmark -- --target /path/to/repo --iterations 3
cargo run --release --features bench-tools --bin profile -- /path/to/repouncomment ships with 49 built-in language configurations and can process any of the 306 languages in tree-sitter-language-pack — grammars are downloaded automatically on first use, and any language can be added via configuration.
49 built-in languages
Python (.py, .pyw, .pyi, .pyx, .pxd) · JavaScript (.js, .jsx, .mjs, .cjs) ·
TypeScript (.ts, .tsx, .mts, .cts, .d.ts) · Rust (.rs) · Go (.go) · Java (.java) ·
C (.c, .h) · C++ (.cpp, .cc, .cxx, .hpp, .hxx) · C# (.cs) ·
Ruby (.rb, .rake, .gemspec) · PHP (.php, .phtml) · Elixir (.ex, .exs) · TOML (.toml) ·
JSON (.json) · JSON with Comments (.jsonc) · YAML (.yml, .yaml) ·
HCL/Terraform (.hcl, .tf, .tfvars) · Makefile (Makefile, .mk) ·
Shell/Bash (.sh, .bash, .zsh) · Haskell (.hs, .lhs) · HTML (.html, .htm, .xhtml) ·
CSS (.css) · XML (.xml, .xsd, .xsl, .xslt, .svg) · SQL (.sql) · Kotlin (.kt, .kts) ·
Swift (.swift) · Lua (.lua) · Nix (.nix) · PowerShell (.ps1, .psm1, .psd1) ·
Protobuf (.proto) · INI-like configs (.ini, .cfg, .conf) · Dockerfile (Dockerfile) ·
Scala (.scala, .sc) · Dart (.dart) · R (.r, .R) · Julia (.jl) · Zig (.zig) ·
Clojure (.clj, .cljs, .cljc, .edn) · Elm (.elm) · Erlang (.erl, .hrl) · Vue (.vue) ·
Svelte (.svelte) · SCSS (.scss) · LaTeX (.tex, .sty, .cls) · Fish (.fish) ·
Perl (.pl, .pm) · Groovy (.groovy, .gradle) · OCaml (.ml, .mli) ·
Fortran (.f90, .f95, .f03, .f08)
Certain comments are never removed by default — uncomment protects the ones your tooling and teammates rely on.
Always preserved:
- Comments containing
~keep TODO(unless--remove-todo),FIXME(unless--remove-fixme)- Documentation comments (unless
--remove-doc)
Linting & formatter directives (always preserved)
| Language | Directives |
|---|---|
| Go | //nolint, //golangci-lint, //staticcheck, //go:generate |
| Python | # noqa, # type: ignore, # mypy:, # pyright:, # ruff:, # pylint:, # flake8:, # fmt: off/on, # black:, # isort:, # bandit:, # pyre-ignore |
| JS/TS | eslint-disable*, @ts-ignore, @ts-expect-error, @ts-nocheck, /// <reference, prettier-ignore, biome-ignore, deno-lint-ignore, v8/c8/istanbul ignore |
| Rust | #[allow], #[deny], #[warn], #[forbid], #[cfg], clippy::, #[rustfmt::skip] |
| Java | @SuppressWarnings, @SuppressFBWarnings, //noinspection, // checkstyle: |
| C/C++ | // NOLINT, // NOLINTNEXTLINE, #pragma, // clang-format off/on |
| Shell | # shellcheck disable, # hadolint ignore |
| YAML | # yamllint disable/enable |
| HCL/Terraform | # tfsec:ignore, # checkov:skip, # trivy:ignore, # tflint-ignore |
| Ruby | # rubocop:disable/enable, # reek:, # standard:disable/enable |
uncomment reads a hierarchical TOML configuration, merged highest-to-lowest precedence:
- Command-line flags
- Local
.uncommentrc.toml(closest to the file being processed wins) - Global
~/.config/uncomment/config.toml - Built-in defaults
[global]
remove_todos = false
remove_fixme = false
remove_docs = false
preserve_patterns = ["IMPORTANT", "NOTE", "WARNING"]
use_default_ignores = true
respect_gitignore = true
[languages.python]
extensions = ["py", "pyw", "pyi"]
preserve_patterns = ["noqa", "type:", "pragma:", "pylint:"]
[patterns."tests/**/*.py"]
# Keep all comments in test files
remove_todos = false
remove_fixme = false
remove_docs = falseAdding a language via configuration
Any of the 306 tree-sitter-language-pack languages works — grammars download automatically on first use, no manual grammar setup:
[languages.hare]
name = "Hare"
extensions = ["ha"]
comment_nodes = ["comment"]
preserve_patterns = ["TODO", "FIXME"]Unlike regex-based tools, uncomment builds a proper Abstract Syntax Tree of your code with tree-sitter, so it distinguishes:
- Real comments vs comment-like content in strings
- Documentation comments vs regular comments
- Inline comments vs standalone comments
- Language-specific metadata that must be preserved
The pipeline is modular: a language registry (49 built-ins + on-demand grammars) feeds an AST visitor that finds comment nodes, a preservation engine decides what to keep, and an output generator emits clean code.
pre-commit
repos:
- repo: https://github.com/Goldziher/uncomment
rev: v3.4.0
hooks:
- id: uncommentLefthook
pre-commit:
commands:
uncomment:
run: uncomment {staged_files}
stage_fixed: trueAST parsing costs a little more than regex, but the tool is fast and scales well with threads.
- Small files (<1000 lines): ~20-30ms
- Large files (>10000 lines): ~100-200ms
| Threads | Files/second | Speedup |
|---|---|---|
| 1 | 1,500 | 1.0× |
| 4 | 3,900 | 2.6× |
| 8 | 5,100 | 3.4× |
Benchmarked on a large enterprise codebase of ~5,000 mixed-language files. Measure your own with
the built-in benchmark and profile tools (see optional benchmarking tools).
cargo build # Debug build
cargo test # Run the test suite
cargo test -- --ignored # Include network-dependent tests
cargo clippy # Lint
cargo fmt --all # FormatSee CONTRIBUTING.md for local development, automation hooks, and release
procedures.
Issues and pull requests are welcome. If uncomment is useful to you, consider sponsoring development — it helps keep the project maintained for the community.