dkit
DevKit CLI — routes shell commands transparently into a running devcontainer.
When you cd into a project, dkit intercepts configured commands (e.g. rails, bundle, rspec) and executes them inside the devcontainer instead of on the host, preserving the correct working directory. When no container is running the commands fall through to the host as normal.
Requirements
- macOS or Linux
- Ruby >= 2.7
- Docker with Compose v2 (
docker compose) - A project with
.devcontainer/devcontainer.jsonusingdockerComposeFile+service
Installation
gem install dkit
echo 'eval "$(dkit hook)"' >> ~/.zshrc && exec zsh
From source
gem build dkit.gemspec && gem install dkit-*.gem
Shell integration
The hook registers a chpwd listener that loads and unloads command shims as you navigate between projects. Add it once to ~/.zshrc:
eval "$(dkit hook)"
code and claude are always intercepted when a devcontainer is active — no configuration needed.
Project setup
cd ~/projects/my-app
dkit init
dkit init detects the project type (Ruby, Node) and creates .devcontainer/dkit-intercept with sensible defaults. Commit that file so your whole team gets the same behavior:
git add .devcontainer/dkit-intercept
git commit -m "chore: add dkit intercept config"
Managing intercepted commands
dkit intercept list # show active commands for this project
dkit intercept add terraform # add a command
dkit intercept remove terraform # remove a command
exec zsh # reload shell to apply changes
Verbose routing messages
By default, dkit prints a line to stderr whenever it intercepts a command:
[dkit] rails server → myapp-dev
To disable per project (committed, shared with the team), add to .devcontainer/dkit-intercept:
verbose: false
rails
bundle
To disable personally regardless of project config:
export DKIT_VERBOSE=0 # add to ~/.zshrc
Usage
dkit exec <cmd> [args] Run command without TTY (scripting/CI)
dkit run <cmd> [args] Run command interactively (TTY)
dkit shell Open interactive zsh shell in container
dkit code [path] Open VS Code attached to devcontainer
dkit claude [args] Run claude inside container
dkit status Show resolved devcontainer context
dkit status --quiet Exit 0 if running, 1 otherwise (for scripting)
dkit root Print project root (no docker required)
dkit up [service] docker compose up -d
dkit down [flags] docker compose down
dkit logs [service] docker compose logs -f
dkit init Create .devcontainer/dkit-intercept
dkit intercept list|add|remove <cmd>
dkit hook Emit shell hook for ~/.zshrc
dkit version
How it works
- On
cd, the shell hook callsdkit rootto find the nearest.devcontainer/devcontainer.json. - It reads
.devcontainer/dkit-interceptand defines a shell function for each listed command. - Each function calls
dkit status --quietto check if the container is running. If yes, it delegates todkit run <cmd>; otherwise it calls the host binary. dkit runresolves the container name from the devcontainer config (via compose YAML, docker labels, ordocker compose ps) and execs into it at the mirrored working directory.
devcontainer.json requirements
dkit reads the following fields:
| Field | Default | Purpose |
|---|---|---|
service |
"app" |
Compose service name |
dockerComposeFile |
— | Path(s) to compose file(s) |
workspaceFolder |
"/workspace" |
Container working directory root |
remoteUser |
"root" |
User for docker exec |
Environment variables
| Variable | Purpose |
|---|---|
DKIT_PROJECT_ROOT |
Override project root resolution (useful in scripts) |
DKIT_VERBOSE |
Set to 0 to suppress routing messages globally |
License
MIT