๐Ÿงน kondo

A timesaver: get gigabytes back from projects you forgot about, without touching the source code, with every command run for real on sample projects.

Video: kondo (Timesavers video)
Prefer to watch? This short video explains what kondo is for, then cleans sample projects on a real Ubuntu 24.04 machine, starting with a dry run, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
kondo: find old projects and delete the folders you can rebuild.
kondo -n .                 look first: list what would be cleaned, delete nothing
kondo --older 6M .         only projects not touched for 6 months
kondo .                    ask for each project: y, n, a (all), q (quit)
kondo -a --older 6M .      clean everything that is old, without asking

kondo is a command-line program that walks through a folder, recognises projects by their files (a package.json, a Cargo.toml, a pom.xml and so on) and deletes their build and dependency folders: node_modules, target, build, .venv and more. The source code stays. It is useful on a disk full of experiments, before a backup, or to get space back on a laptop. It deletes for good, so its own README says it is "essentially rm -rf with a prompt" and to have a backup.

This page is the written version of the Timesavers video. Every command below was run for real, in an Ubuntu 24.04 container with kondo 0.9 (the release tarball), on made-up sample projects whose "dependencies" are files of random bytes. Nothing real was ever cleaned. The section "What was not run" lists what was not tried.

What kondo is for

Use kondo when projects have piled up and their build folders are what takes the space:

a disk full of old experiments        node_modules and target folders of gigabytes each
before a backup or a copy              leave the dependencies out, they come back with an install
a project you have not touched in a year   --older picks projects by age
one clean sweep                       -a, with an age limit, so nothing recent goes

What it removes is meant to be rebuilt: the next npm install or cargo build brings it back, and that takes time and a network. It is not a trash can, and there is no undo. It does not touch your source files, but it can only be as careful as its idea of "build folder", so look first with -n. It knows more than 20 project types (the help lists Cargo, Node, Unity, SBT, Haskell Stack, Maven, Unreal Engine, Jupyter Notebook, Python, CMake, Composer, Pub, Elixir, Swift, Gradle and .NET).

Install

The project's README lists winget, Homebrew, MacPorts, Arch Linux, a source build with cargo, and release binaries on the GitHub releases page. The lab used the release tarball for Linux (x86_64, musl). The release has no checksum file next to it, so if that matters build it from source with cargo or check the download yourself:

$ mkdir -p ~/.local/bin
$ curl -fsSL https://github.com/tbillington/kondo/releases/download/v0.9/kondo-x86_64-unknown-linux-musl.tar.gz | tar xz -C ~/.local/bin
$ ls -l ~/.local/bin/kondo | awk '{print $5, $9}'
2856608 /home/ubuntu/.local/bin/kondo

This version has no --version flag (the lab tried it):

$ kondo --version
error: unexpected argument '--version' found

  tip: to pass '--version' as a value, use '-- --version'

Usage: kondo [OPTIONS] [DIRS]...

For more information, try '--help'.

The help (kondo --help) starts with:

Kondo recursively cleans project directories.

Supported project types: Cargo, Node, Unity, SBT, Haskell Stack, Maven, Unreal Engine, Jupyter Notebook, Python, Jupyter Notebooks, CMake, Composer, Pub, Elixir, Swift, Gradle, and .NET projects.

The page uses release 0.9, from the GitHub release name. A graphical version, kondo-ui, also exists and was not run.

The sample projects

Four sample projects and one folder that is not a project. Each "dependency" folder is made of 1 MB files of random bytes. old-blog was given file dates 400 days in the past, so it looks forgotten:

$ du -sh *
32M     api-rs
13M     billing
8.0K    notes
23M     old-blog
55M     web-shop

web-shop and old-blog are Node projects (a package.json and a node_modules folder), api-rs is a Rust project (Cargo.toml and a target folder), billing is a Maven project (pom.xml and a target folder), and notes is only a text file.

Look first: the dry run

-n (or --dry-run) lists what would be cleaned and deletes nothing. For each project it shows the path, the type, how long ago a file in it was last changed, and the folders it would remove with their size:

$ kondo -n .
billing Maven project (8 seconds ago)
  โ””โ”€ target (12.0MiB)
web-shop Node project (8 seconds ago)
  โ””โ”€ node_modules (54.0MiB)
api-rs Cargo project (8 seconds ago)
  โ””โ”€ target (31.0MiB)
old-blog Node project (14 months ago)
  โ””โ”€ node_modules (22.0MiB)
Projects cleaned: 0/4, Bytes deleted: 0.0B / 119.0MiB

The last line is the total: 0 of 4 projects cleaned, 0 bytes deleted, and 119 MiB that could be. notes is not listed, because it is not a project. The "0 seconds ago" on the first three says the samples had just been made, and old-blog shows 14 months, from its backdated files. The order of the projects is the order the folder was read in, so it can differ on your machine.

Only old projects, or leave one out

--older takes a time: a number and a unit, where m is minutes, h hours, d days, w weeks, M months and y years. Only projects that have not been changed for that long are listed. -I (or --ignored-dirs) names a folder to skip, and kondo does not go into it:

$ kondo -n --older 6M .
$ kondo -n -I web-shop .
old-blog Node project (14 months ago)
  โ””โ”€ node_modules (22.0MiB)
Projects cleaned: 0/1, Bytes deleted: 0.0B / 22.0MiB
ubuntu@timesaver:~/projects$ kondo -n -I web-shop .
billing Maven project (16 seconds ago)
  โ””โ”€ target (12.0MiB)
api-rs Cargo project (16 seconds ago)
  โ””โ”€ target (31.0MiB)
old-blog Node project (14 months ago)
  โ””โ”€ node_modules (22.0MiB)
Projects cleaned: 0/3, Bytes deleted: 0.0B / 65.0MiB

The first command found only old-blog. The second left out web-shop and listed the other three. --older is the safest way to use kondo on a real disk: a project you worked on last week is never in the list.

Choose one project at a time

Without -n and -a, kondo asks about each project. The answers are y (delete this one), n (skip it), a (all, which was not tried) and q (quit). Typing the letter and pressing Enter, here y, n, y and q:

$ kondo .
billing Maven project (18 seconds ago)
  โ””โ”€ target (12.0MiB)
  delete above artifact directories? ([y]es, [n]o, [a]ll, [q]uit): y
web-shop Node project (19 seconds ago)
  โ””โ”€ node_modules (54.0MiB)
  delete above artifact directories? ([y]es, [n]o, [a]ll, [q]uit): n
api-rs Cargo project (19 seconds ago)
  โ””โ”€ target (31.0MiB)
  delete above artifact directories? ([y]es, [n]o, [a]ll, [q]uit): y
old-blog Node project (14 months ago)
  โ””โ”€ node_modules (22.0MiB)
  delete above artifact directories? ([y]es, [n]o, [a]ll, [q]uit): q

Projects cleaned: 2/4, Bytes deleted: 43.0MiB / 119.0MiB

$ du -sh *
16K     api-rs
20K     billing
8.0K    notes
23M     old-blog
55M     web-shop

billing and api-rs lost their target folders (12 and 31 MiB, 43 in total), and are now 20 K and 16 K, which is their source. web-shop was skipped with n, and old-blog was left by q. The files that are left are the source files and the project file, such as pom.xml. An invalid key does not delete anything: in the lab, ? printed "invalid choice, please choose between y, n, a, or q" and asked again.

The uppercase letter in a prompt, as in [Y]es, is the default when you press Enter on an empty line. That appears with -d (--default), which the lab tried:

$ printf '\n' | kondo -d cm
cm CMake project (0 seconds ago)
  โ””โ”€ build (2.0MiB)
  delete above artifact directories? ([Y]es, [n]o, [a]ll, [q]uit):   defaulting to yes...
Projects cleaned: 1/1, Bytes deleted: 2.0MiB / 2.0MiB
CMakeLists.txt

Use -d with care: it makes Enter mean yes.

Clean everything old, with no questions

-a (or --all) cleans all the projects found without asking. Combine it with --older so that only forgotten ones go:

$ kondo -a --older 6M .
old-blog Node project (14 months ago)
  โ””โ”€ node_modules (22.0MiB)
Projects cleaned: 1/1, Bytes deleted: 22.0MiB / 22.0MiB
ubuntu@timesaver:~/projects$ du -sh *
16K     api-rs
20K     billing
8.0K    notes
16K     old-blog
55M     web-shop

old-blog's node_modules was deleted, and it is now 16 K. web-shop, which was newer and had been skipped, was not in the run at all. -q (quiet) only works together with -a, as the lab showed, and -qq stops all output:

$ kondo -n -q .
Quiet mode can only be used with --all.
exit status: 1

That is handy in a script that runs from cron. Make sure the arguments are right before you put -a in a script.

Other project types, and what is not a project

kondo recognises projects by their files and takes the folders that belong to each type. A lab folder with seven directories, each with a 2 MB "build" folder:

$ ls -d */ ; ls -a nothing
cm/ dn/ gr/ nothing/ ph/ py/ rs2/ 
.
..
node_modules
readme.txt

$ kondo -n .
cm CMake project (0 seconds ago)
  โ””โ”€ build (2.0MiB)
gr Gradle project (0 seconds ago)
  โ””โ”€ build (2.0MiB)
  โ””โ”€ .gradle (2.0MiB)
rs2 Cargo project (0 seconds ago)
  โ””โ”€ target (2.0MiB)
dn .NET project (0 seconds ago)
  โ””โ”€ bin (2.0MiB)
  โ””โ”€ obj (2.0MiB)
py Python project (0 seconds ago)
  โ””โ”€ .venv (2.0MiB)
  โ””โ”€ __pycache__ (2.0MiB)
ph Composer project (0 seconds ago)
  โ””โ”€ vendor (2.0MiB)
Projects cleaned: 0/6, Bytes deleted: 0.0B / 18.0MiB

CMake and Gradle show build (Gradle also .gradle), Cargo target, .NET bin and obj, Python .venv and __pycache__, and Composer vendor. The directory nothing has a node_modules folder and a readme, but no package.json, so kondo did not list it. That is the safety: it looks for the project file before it takes a folder named like a build folder. The same applies the other way, so if a project has an unusual layout kondo may not find it.

Options at a glance

The options of this version, from kondo --help:

  -I, --ignored-dirs <IGNORED_DIRS>
  -q, --quiet...
  -a, --all
  -L, --follow-symlinks
  -s, --same-filesystem
  -o, --older <OLDER>
      --completions <GENERATOR>
  -d, --default
  -n, --dry-run
      --single-key
  -h, --help

-L follows symbolic links (that can lead kondo outside the folder you named, so be careful with it), -s stays on the same filesystem, and --single-key accepts a one-letter answer without Enter. --completions makes a completion script for bash, elvish, fish, powershell or zsh:

$ kondo --completions bash | head -3
Generating completion file for Bash...
_kondo() {
    local i cur prev opts cmd
    COMPREPLY=()

A directory that does not exist is reported and nothing is cleaned, but the exit status is still 0:

$ kondo -n /nonexistent
error: directory /nonexistent does not exist
Projects cleaned: 0/0, Bytes deleted: 0.0B / 0.0B
exit status: 0

What was not run in the lab

Only the commands on this page were run, on sample projects. These were not: the graphical version (kondo-ui); real projects, or a large disk with thousands of folders; -L (symlinks) and -s (same filesystem); --single-key in a terminal (in the lab it was tried with piped input, where it used a full CPU core and never finished, so run it only from a real terminal); the answer a (all) at the prompt; the project types not shown above (Unity, Unreal, SBT, Haskell Stack, Jupyter, Pub, Elixir, Swift, Turborepo, Terraform, React Native and Godot); and Windows and macOS. The release tarball was not checked against a checksum, because none is published next to it. Whether a folder named like a build folder is always rebuilt without loss depends on your project: for example a .venv that you created by hand can have packages that are not in a requirements file. Everything ran on Ubuntu 24.04 with kondo 0.9.

Common problems and tips

It listed nothing. The project file is missing or the folder is too young for --older. kondo needs a package.json, Cargo.toml, pom.xml and so on, and with --older the project must be older than the limit.

Quiet mode can only be used with --all. -q is only allowed together with -a.

I deleted something I needed. There is no trash and no undo. A node_modules or target folder can be rebuilt, but patches you made inside node_modules, or a .venv with packages that are not listed anywhere, cannot. Check the list with -n first and keep a backup.

It found projects inside a folder I wanted left alone. Use -I with the folder name. kondo will not go into it.

Tips. Always start with kondo -n. Use --older, as in 6M or 1y, so that active work is never in the list. Run it on one folder at a time (kondo -n ~/projects) rather than on your whole home folder. And keep -a for a time when you have read the list.