doma v0.1.0
Directory tag manager — put your directories on the cutting board.
Installation • Shell integration • Pipelines • AI agents • Contributing • Releases
doma is a Crystal CLI for tagging directories so you can recall, browse, or batch-execute across them. Tag your projects once (crystal, work/proj-a, bookmark), then jump to them, list them, or run a command across the whole set without re-typing paths.
doma add ~/Projects/my-app -t crystal -t web
doma list -t crystal --paths | xargs -I{} sh -c 'cd {} && shards build'
doma cd crystal # interactive picker, then cd (needs `doma setup install`)
doma mark spike # bookmark cwd for 7 days
Features
Tagging
- Multiple tags per directory; tags are reusable across paths
- Auto-tag from basename and Git remote (
--auto-tag,--git-tag) - Glob filter on tags (
list -t 'work/*',run 'work/*' -- cmd) — shell-style:*matches within one segment,**crosses/,?is a single non-/character - Hierarchical tag display (
tags --tree) - TTL on tags:
--ttl 30m | 1h | 7d | 2w,--tmpfor the 7-day default,markfor the cwd + 7-day shortcut - Stable 7-char
short_idper directory — survives renames; usable viarm <id>andtrash restore <id>
Navigation & operations
list --pickresolves to a single path (Crystal-native picker, no fzf dependency); thedoma cd <tag>shell wrapper fromdoma setup installbuilds on itrun <tag> -- <cmd>to execute a command in every tagged directory;--parallel(with--jobs N, default CPU count) and--fail-fastavailablemoveto follow a path that moved on disk; tags carry overrenameto merge or relabel tags- Recency tracking — most-used directories surface first in pickers and
list --by recent - Substring search across path / basename / tag (
doma list <query>) - Single-entry detail view (
doma infodefaults to cwd) — tags, TTLs, last-used, exists check - Dead-path detection:
list --check,prune --gone; expiry purge:prune --expired
Pipelines & scripting
list -t TAG --paths— newline-separated paths forwhile read/xargslist -t TAG -0— NUL-separated forxargs -0, safe for paths with spaceslist --json,tags --json,stats --json,export --json|--yaml- "Did you mean ..." hints (Levenshtein) for typos
- Output stays color-free when piped; SIGPIPE-safe
Storage & safety
- SQLite-backed (WAL, foreign keys, busy_timeout) — handles concurrent doma calls across multiple shells
- Path canonicalization:
~, symlinks, and trailing slashes all collapse to one stable key - Transactional writes; atomic snapshot import/export
- Schema migrations via
PRAGMA user_version— old DBs upgrade transparently - Strict tag validation; sanitized auto-tags so a
.dotfilesrepo doesn't break--auto-tag
Distribution & integration
- Single static binary (musl + sqlite-static); no runtime dependencies
- Packaged for Homebrew, AUR, Snap, .deb, .rpm, .apk
- Multi-arch container image at
ghcr.io/hahwul/doma - CycloneDX SBOM published with every release
- Shell integration installer (
doma setup install) for bash, zsh, fish - AI skill for Claude / Cursor / etc. — see skills/doma/SKILL.md
Installation
Homebrew
brew tap hahwul/doma
brew install doma
From source
git clone https://github.com/hahwul/doma.git
cd doma
shards install
shards build --release --no-debug --production
Pre-built binaries
Static Linux and macOS binaries are attached to every release. .deb / .rpm / .apk / .snap packages are published alongside.
Container
docker pull ghcr.io/hahwul/doma:latest
docker run --rm -it -v "$HOME/.config/doma:/root/.config/doma" \
ghcr.io/hahwul/doma list
Shell integration
doma cd lives in a shell function rather than the binary — a child process can't change its parent shell's working directory. The function calls doma list -t <tag> --pick (the binary's single-pick primitive) and runs cd on the result. One-shot install:
doma setup install # auto-detects $SHELL, appends to your rc
exec $SHELL # or `source ~/.zshrc`
doma cd crystal # interactive picker, then cd
If you'd rather wire it manually:
eval "$(doma setup init zsh)" # bash / zsh
doma setup init fish | source # fish
Without the wrapper, the equivalent inline form works everywhere:
cd "$(doma list -t crystal --pick)"
Pipelines
doma is designed to compose with the rest of your shell. A few common shapes:
# Update CI files across every Crystal project
doma list -t crystal --paths | while read -r d; do
(cd "$d" && sed -i 's/crystal: 1.20/crystal: 1.21/' .github/workflows/*.yml)
done
# Status across work repos (paths-with-spaces safe)
doma list -t 'work/*' -0 | xargs -0 -I{} sh -c 'cd "{}" && git status -s | head'
# Structured access via jq
doma list --json | jq -r '.[] | "\(.short_id)\t\(.path)\t\(.tags|join(","))"'
# Ad-hoc bookmarks during a code review session
doma mark auth-review
# ... cd around ...
doma list -t auth-review --paths
AI agents
doma ships a Claude Code skill that teaches an agent when to query the database for a path list ("update CI for all my Crystal projects") and when to register or bookmark a directory ("track this", "remember this for later").
Install via Vercel Skills:
npx skills add hahwul/doma
Or copy the file by hand into your agent's skill directory:
cp -r skills/doma ~/.claude/skills/
# or symlink so updates land automatically:
ln -s "$(pwd)/skills/doma" ~/.claude/skills/doma
Contributing
doma is open-source and PRs are welcome. Please check CONTRIBUTING.md before sending a patch.
Why "doma"?
Doma (도마) is the Korean word for cutting board — the workbench where ingredients are gathered, grouped, and chopped before going into the pan. doma aims to be the same kind of workbench for your directories: pull the ones you care about onto a single board, group them by category (crystal, work/proj-a, bookmark), and run bulk operations across the piles as if they were mise en place.
doma
- 2
- 0
- 0
- 0
- 2
- about 5 hours ago
- April 25, 2026
MIT License
Sun, 03 May 2026 16:04:28 GMT