run-cli gives a project one stable command surface: run.
Instead of remembering whether this repo uses bun run, node, python, go run, cargo run, or a shell script, you define the project contract once in .run.toml and use the same launcher everywhere.
run has one job: launch the project's command intentionally and cheaply.
runexecutes the project's default commandrun -p <profile>executes a named profilerun -- <args...>forwards args to the child command untouchedrun upmanages long-running processes started byrunrun doctorexplains what the CLI resolved
The current version is built around those rules. This is the canonical contract.
Think about the CLI in three layers.
Plain run means:
runThat executes the effective default profile from .run.toml.
Profiles are explicit:
run -p dev
run -p workerIf you want a named workflow, use -p or --profile.
Anything after -- belongs to your app, not the CLI:
run -- --watch
run -p dev -- --port 3000
run -- doctorThis is how you pass words like doctor, inspect, or ports to the underlying project command instead of triggering run subcommands.
Rule of thumb:
- before
--=runCLI territory - after
--= child command territory
Most repos already have a real entrypoint, but the entrypoint is hidden behind ecosystem-specific commands and local habits.
run-cli makes that entrypoint explicit and versioned.
It is intentionally:
- lightweight: Bun runtime, no runtime deps, cheap local state
- explicit: profiles are named, config is local, no magic env activation
- deterministic: dry-run, doctor output, and managed process metadata all come from the same resolved command path
- agent-friendly: the tool exposes stable human and machine-readable diagnostics
Prerequisite: Bun >= 1.3.9
bun add -g @kitsunekode/run-cliYou can also install with npm if Bun is already available on your PATH at runtime:
npm install -g @kitsunekode/run-cliThat exposes:
run
bun install
bun run build
bun linkRefresh the global link after local updates:
bun run relink:global- Add a changeset with
bun run changesetfor user-facing or release-worthy changes. - Work from short-lived branches and merge to
main. .github/workflows/version-packages.ymlopens or updates aVersion PackagesPR from merged changesets.- Merging the version PR updates
package.jsonand changelog entries for the next release. CIruns on pull requests andmain, and its workflow summary records the exact package version it validated.Releaseruns on pushes tomainand manual dispatch. It publishes the exactpackage.jsonversion of@kitsunekode/run-clithrough npm trusted publishing and creates a GitHub release taggedvX.Y.Z.- For a brand-new package, do one manual bootstrap publish first so the npm package settings page exists and you can attach the trusted publisher to
release.yml.
Inside a project:
run init
run
run -p dev
run -- --watchMinimal config example:
version = 1
default_profile = "default"
[profiles.default]
command = "bun run index.ts"
[profiles.dev]
command = "bun --hot index.ts"runrun -p dev
run -p workerrun -- --watch
run -p dev -- --port 3000
run -- inspectrun --dry-run
run -p dev --dry-run
run -- --watch --dry-runrun doctor
run doctor --json
run config validaterun up
run up -p worker -- --port 4000
run ps
run ps --details
run dashboard
run inspect my-app:worker
run logs my-app:worker --follow
run ports
run stop my-app:worker
run restart my-app:worker
run prunerun [args...] [-p <profile>] [-v] [--dry-run] [--no-cache] [--config <path>] [--cwd <path>]
run init [--force] [--yes] [--command <cmd>] [--default-profile <name>] [--add-profile <name=command>]
run completion <zsh|bash>
run doctor [--json]
run profiles [--json]
run up [args...] [-p <profile>] [--name <name>]
run ps [--json] [--details] [--watch]
run dashboard
run inspect <name|id> [--json]
run logs <name|id> [--lines <n>] [--follow]
run stop <name|id>
run restart <name|id>
run kill <name|id>
run prune [--json] [--dry-run]
run ports [--json]
run config <view|path|edit|validate> [--global]
run help
These are built into run:
initcompletiondoctorprofilesuppsdashboardinspectlogsstoprestartkillpruneportsconfighelp
If you want the underlying project command to receive one of those words as an argument, use --:
run -- doctor
run -- inspect
run -p dev -- portsThe canonical project config file is:
.run.toml
Legacy support still exists for:
.run.config.toml
But .run.toml is the real contract and the file you should create, commit, and document.
run walks upward from the current directory and uses the nearest project config only.
That means:
- monorepo packages can own their own run config
- repo roots can still provide a shared default
- ancestor configs are not merged
version = 1
default_profile = "dev"
[profiles.default]
command = "bun run src/index.ts"
[profiles.dev]
command = "bun --hot src/index.ts"
description = "local development server"
[profiles.worker]
command = "bun run src/worker.ts"
cwd = "services/worker"
[profiles.worker.env]
QUEUE_NAME = "jobs"
DEBUG = trueThe CLI tries to stay polished without becoming noisy.
- default execution prints a compact startup banner
--verbose/-vadds resolution details like profile, cwd, config path, and cache state--dry-runprints the exact shell commandrun doctoris the readable diagnostic reportrun doctor --jsonis the machine-readable diagnostic report
run doctor --json currently returns:
cwd: current effective working directoryconfigLookup: resolved config metadata ornullglobalConfigPath: global config pathcacheFilePath: cache file pathshell: effective shellcacheEnabled: whether cache is enableddetectedProject: detected project metadata ornull
configLookup, when present, contains:
sourcePathcacheHitlegacy
detectedProject, when present, contains:
rootmarkerscacheHitsuggestions
run up stores a lightweight registry for processes it starts itself.
Managed process metadata includes:
- project name
- profile
- full resolved command
- forwarded args
- pid
- cwd
- config path
- log path
- restart count
Performance behavior is intentional:
run psshows memory usage by default with color-coded status (green=running, yellow=stopped, red=exited)run ps --watchlive-refreshes the process list every 2 secondsrun ps --detailsadds port information alongside metricsrun inspectandrun portsopt into more detailed process informationrun pruneremoves dead (exited/stopped) processes from the registry- Memory thresholds warn at 512MB (yellow) and 1GB (red) in the MEM column
This keeps the common path fast and low-overhead.
Use these when something feels off:
run doctor
run doctor --json
run config path
run config view
run config validate
run --dry-runTypical workflow:
run config pathto confirm which config is activerun config validateto confirm it parsesrun --dry-runto see the final commandrun doctorif you need the full resolution story
Generate completion scripts from the CLI:
mkdir -p ~/.config/zsh/completions
run completion zsh > ~/.config/zsh/completions/_run
run completion bash > ~/.config/bash/run.bashFor Zsh:
fpath=(~/.config/zsh/completions $fpath)
autoload -Uz compinit
compinitChecked-in loader scripts also exist in:
completions/run.zshcompletions/run.bash
Global defaults live at:
$XDG_CONFIG_HOME/run/config.toml- fallback:
~/.config/run/config.toml
Global config is intentionally limited to defaults such as:
shelleditorcachedetection
It does not define project commands or project profiles.
Example:
version = 1
shell = "/bin/zsh"
editor = "code -w"
cache = true
detection = "suggest"Cache data is written to:
$XDG_CACHE_HOME/run/cache.json- fallback:
~/.cache/run/cache.json
The cache stores cheap metadata such as:
- resolved config paths for known working directories
- detection results for known project roots
Use --no-cache to bypass cache reads and writes.
version = 1
default_profile = "dev"
[profiles.default]
command = "bun run src/index.ts"
[profiles.dev]
command = "bun --hot src/index.ts"version = 1
[profiles.default]
command = "node index.js"version = 1
[profiles.default]
command = "python exp.py"
[profiles.dev]
command = "python -m uvicorn app:app --reload"If project-native tooling is present, run init prefers explicit commands like:
uv run python main.pypipenv run python main.pypoetry run python main.py.venv/bin/python main.py
version = 1
[profiles.default]
command = "go run ."Useful repo docs:
docs/config-reference.mddocs/architecture.mddocs/contributing.mdAGENTS.md
Local quality workflow:
bun run format
bun run lint
bun test
bun run build- Bun is required to run the CLI
- v1 targets macOS and Linux shell behavior
- Windows support is intentionally deferred