# Asimov marks dependency folders for Time Machine by pairing them with a sentinel file

> Asimov is a MIT licensed Bash tool for macOS 14 and newer that scans your home directory, pairs dependency directories with their config file as a sentinel, and excludes them from Time Machine. The sentinel rule is what keeps it from eating a folder that merely shares a common name.

**AsimovMac/asimov** — Automatically exclude development dependencies from Apple Time Machine backups

- Repository: https://github.com/AsimovMac/asimov
- Stars: 1,947 · Forks: 102
- Language: Shell
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/asimovmac-asimov

## The waste Asimov targets is a function of how you work

The README opens by naming the cost in plain terms: every Time Machine snapshot copies `node_modules`, `.venv`, `target`, `DerivedData`, gigabytes of files you can rebuild with one command. That is the mechanism, and it is worth understanding because it is not a bug in Time Machine. 

What has changed is the multiplier. The README argues that with git worktrees and AI coding agents spinning up parallel copies of every project, the waste compounds fast. A single worktree carries its own `node_modules` and its own `.next`, so a feature branch multiplies the same payload. The output example in the README shows exactly that shape: one project contributing `node_modules` at 412M and `.next` at 89M, and worktrees under `~/Code/worktrees/feature-auth` and `~/Code/worktrees/refactor-billing` each contributing 1.6G.

So the audience is specific: a developer whose home directory is a code directory, who is already paying the storage cost and the snapshot time, and who would rather not hand-tune Time Machine exclusions by hand.

The framing line is blunt: "Stop backing up files you'll never restore." Whether that is true for your own workflow is worth deciding before you apply anything.

## Sentinel pairing is the whole safety argument

This is the design decision that separates Asimov from a naive `node_modules` pattern search, and the README explains it in two paragraphs.

Asimov pairs each dependency directory with a sentinel config file. A folder is only excluded if its sentinel exists alongside it. The mapping is given explicitly: `node_modules/` requires `package.json`, `vendor/` requires `composer.json` or `go.mod`, and `target/` requires `Cargo.toml` or `pom.xml`.

The consequence is stated too: Asimov never touches a folder that just happens to share a common name, which is why the README says it is safe to run anywhere in your home directory.

Note what the sentinel rule does not do. It verifies that you are inside a project, not that the directory is disposable. A `vendor/` folder next to a `composer.json` is skipped even if you have vendored edits in there, and a `target/` folder next to a `pom.xml` is skipped even if the Maven build output is the artifact you care about. The check is structural, not semantic.

The repository layout backs up the claim that this is a real lookup table rather than a hardcoded list. There are `data/` and `lib/` directories alongside `bin/asimov`, which is where patterns and pairing rules would live, and `tests/` with Bats tests, so the pairing behaviour is covered by tests rather than asserted.

## Thirty patterns across many ecosystems, and opt-in global caches

The pattern table is long enough to be useful and has one entry worth reading twice.

Per project, Asimov covers JavaScript and TypeScript with `node_modules`, `.next`, `.nuxt`, `.angular`, `.svelte-kit`, `.turbo`, `.yarn`, `.parcel-cache`, `bower_components` and `elm-stuff`; Python with `.venv`, `venv`, `.tox`, `.nox`, `__pypackages__`, `build` and `dist`; Rust with `target`; Java, Kotlin and Scala with `.gradle`, `build` and `target`; .NET with `bin` and `obj`; Swift and Apple with `.build`, `Carthage`, `Pods` and `DerivedData`; and then Elixir, Clojure, Haskell, OCaml, Zig, R, Dart and Flutter in similar fashion. DevOps and infrastructure as code adds `.terraform`, `.terragrunt-cache`, `.vagrant`, `.direnv` and `cdk.out`. Game development adds `.godot`, and a monorepo tool adds `.moon/cache`.

Several names appear under more than one ecosystem, `vendor` under Go, PHP and Ruby, `build` under Python, Java and Flutter. That is exactly where the sentinel rule earns its place, because a bare name search would be wrong so often.

The final row is different in kind. Global caches such as `~/.cache`, `~/.gradle/caches`, `~/.m2/repository`, `~/.npm/_cacache`, `~/.nuget/packages`, `~/.kube/cache`, `~/go/pkg/mod` and `~/.vagrant.d/boxes` are marked opt-in. Excluding a directory called `build` inside a project is low risk. Excluding `~/.m2/repository` is a decision about your whole machine, and the README keeps it behind a choice you have to make.

For a tool the project does not list, the README says you can teach it your own directory and sentinel pairs in a couple of lines, though that section is cut off in the version available here.

## Install with Homebrew, or curl into ~/.local

Two documented install routes, and the README is candid about the trade.

Homebrew is the packaged route:

```sh
brew install asimov
brew services start asimov
```

The caveat is written directly above it: the homebrew-core formula is updated automatically, usually within a few hours of a release, and for the newest version the moment it ships you should use the curl installer instead.

The curl route takes whatever is on `main`:

```sh
curl -fsSL https://raw.githubusercontent.com/AsimovMac/asimov/main/scripts/install-remote.sh | bash
```

That installs to `~/.local`, specifically `bin/asimov` plus its library and data files under `libexec/` and `share/`. Piping a remote script into bash is the obvious hazard here; if you want to read the script before running it, the repository has `scripts/install.sh` alongside `scripts/install-remote.sh`, which is also what Shellcheck runs against in the Makefile.

The README badge states macOS 14+ as the requirement, linking to Apple's support page, so this is not a script you can take to Linux.

A third path exists for developers. The Makefile's `install` target builds from source, and `scripts/test.sh` runs the Bats test suite.

## Dry run first, because exclusions are not visibly reversible

The quick start is deliberately two commands:

```bash
asimov --dry-run    # preview what would be excluded — changes nothing
asimov              # apply
```

That ordering is the correct instinct on your side too. A normal run applies the exclusions, while `--dry-run` changes nothing. In exchange, Asimov avoids backing up dependency directories that README says can be rebuilt with one command.

`--stats` can be added to either command to see sizes and how much space you are saving, and the README shows the shape of that output: a list of directories with their sizes, ending in a total line. It also notes you can drop `--stats` as well as `--dry-run` if you want it fast, which suggests the size calculation is the expensive part of a scan.

After installing, Asimov runs itself once a day, every day. With Homebrew that is `brew services start asimov`; the repository ships `com.stevegrunwell.asimov.plist` at the top level, which is the launchd property list behind that behaviour.

For confirming the result, the Makefile has a target worth knowing:

```bash
make exclusions
```

It runs `sudo mdfind "com_apple_backup_excludeItem = 'com.apple.backupd'"`, which queries Spotlight for the exclusion attributes macOS has recorded. This is the independent check on whether asimov's run actually took effect.

## A Bash script that deliberately tests against macOS system bash

Two details in the Makefile say more about this project's engineering culture than a badge would.

The first is the interpreter pinning. The Makefile comments explain that the shebang is `#!/usr/bin/env bash`, so by default the script runs under whichever bash is first on PATH, which is usually 5.x locally and 3.2 on a stock Mac. There is a comment asking you to use `make test BASH_BIN=/bin/bash` to check what your users actually get, and a dedicated target `make test-system-bash` that runs the Bats tests under the macOS system bash 3.2 as shipped users receive it.

That is the right instinct for a Homebrew-installed tool whose users will not have upgraded their bash, and it is the kind of constraint that silently breaks a script when nobody tests against the old interpreter.

The second is that tests can run concurrently. `BATS_JOBS=N` runs them in parallel, which requires GNU parallel and the Makefile notes you can get it with `brew install parallel`.

Linting is Shellcheck, invoked as `shellcheck -x --source-path=. bin/asimov` so that sourced library files resolve, and there is a separate lint pass over `scripts/install.sh` and the other install scripts. There is also a `bench` and `bench-home` target, so scan performance is treated as something to measure, and an `UPGRADING.md` at the repository root, which signals that the project expects its own version jumps to affect users.

## The wrong tool for shared folders, network homes, and non-macOS

The boundary is macOS. Time Machine's exclusion mechanism is what Asimov writes to, so on Linux or Windows the script has nothing to act on regardless of how well the sentinel logic works.

Inside macOS, two cases deserve thought before you apply it broadly.

The first is the sentinel rule's blind spot noted earlier. `target` next to a `pom.xml`, `build` next to a `package.json`-less Java project, `vendor` with local modifications: all are excluded on structure alone. If you keep build output you cannot regenerate quickly, either move it outside the project or accept losing the history of it.

The second is the opt-in global cache row. `~/.cache` and `~/.m2/repository` are far from project directories and far from a local, rebuildable dependency tree; they are the accumulated state of many projects. Turning them off is a separate decision from turning off `node_modules`, and the README keeps them separate for that reason.

A third practical limit is scope. Asimov scans the home directory by default. It has no documented option for pointing it at a specific project root, and no documented per-directory ignore file in the README. If your projects live outside your home directory, or you want to exclude one project but not another, the README does not describe how.

Maintenance: the repository is not archived, the last push was on 2026-09-17, and the recent tags v0.12.0, v0.11.0 and v0.10.0 landed on 2026-07-30, 2026-07-29 and 2026-07-26. The licence is MIT, which permits modification and redistribution with the notice retained. That is the terms of the licence, not advice on how it applies to your setup.

## Conclusion

Adopt Asimov if you keep projects on macOS 14 or newer, run Time Machine, and lose disk or backup time to rebuildable dependency trees, especially now that git worktrees multiply them. Do not reach for it on Linux or Windows, and do not apply it without reading the opt-in global cache list, since `~/.cache` and `~/.m2/repository` are excluded only when you ask. Verify first with `asimov --dry-run --stats`, then confirm the result with the Makefile's `make exclusions`, which queries `com_apple_backup_excludeItem` through `mdfind` and needs sudo.

## FAQ

### How do I install Asimov on macOS?

Either `brew install asimov` followed by `brew services start asimov`, or the curl installer which places `bin/asimov` plus library and data files under `~/.local`. The README notes the Homebrew formula usually updates within a few hours of a release.

### How does Asimov avoid excluding folders that are not dependencies?

It pairs each dependency directory with a sentinel config file and only excludes it when the sentinel exists alongside. `node_modules/` needs `package.json`, `vendor/` needs `composer.json` or `go.mod`, and `target/` needs `Cargo.toml` or `pom.xml`.

### How do I preview what Asimov would exclude?

Run `asimov --dry-run`, which previews without changing anything. Add `--stats` to either the dry run or the real run to see sizes and the space saved.

### Which directories does Asimov skip inside a project?

The README lists more than thirty patterns, including `node_modules`, `.next`, `.venv`, `target`, `.gradle`, `bin`, `obj`, `Pods`, `_build`, `.terraform` and `.godot`, grouped by ecosystem from JavaScript and TypeScript through to Elixir, Haskell and Zig.

### Are global caches like ~/.cache excluded by default?

No. The README marks global caches such as `~/.cache`, `~/.gradle/caches`, `~/.m2/repository` and `~/.npm/_cacache` as opt-in, separate from the per project patterns.

### How can I confirm the exclusions actually took effect?

The Makefile ships a target that runs `sudo mdfind "com_apple_backup_excludeItem = 'com.apple.backupd'"` to list every path excluded from Time Machine, which is an independent check rather than trusting Asimov's own report.

## Sources

- [AsimovMac/asimov on GitHub](https://github.com/AsimovMac/asimov)
- [Issues](https://github.com/AsimovMac/asimov/issues)
- [License: MIT](https://github.com/AsimovMac/asimov/blob/main/LICENSE)
- [README](https://github.com/AsimovMac/asimov/blob/main/README.md)
- [Releases](https://github.com/AsimovMac/asimov/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/asimovmac-asimov
