Skip to content

behnamazimi/samecmd

Repository files navigation

samecmd

Checks Latest release Bun License: MIT

Your projects already have commands. npm run dev, cargo run, make test, poetry run pytest, they're all there, just spelled differently every time.

samecmd reads whatever project you're in and gives you the same short names (dev, build, test, fmt, and more) everywhere.

cd ~/projects/web-app        # Node/pnpm
dev                           # → pnpm run dev

cd ~/projects/api             # Rust
dev                           # → cargo run

cd ~/projects/data-pipeline    # Python/uv + Makefile
test                            # → make test
fmt                             # → make fmt

No config needed. Just cd and start typing.

samecmd demo

Who is this for?

  • You jump between projects and keep mistyping commands. test should just work — whether the project uses Jest, pytest, cargo test, or a Makefile.
  • Your team keeps asking "how do I run this?" Drop a samecmd.yml in the repo. Everyone gets the same commands, no docs needed.
  • You're switching from yarn to pnpm, or Poetry to uv. The underlying tool changes — dev and test don't.

Installation

Quick install

curl -fsSL https://raw.githubusercontent.com/behnamazimi/samecmd/main/install.sh | bash

Installs the prebuilt binary for your platform from the latest release to ~/.local/bin/samecmd. On Windows, run it from WSL or Git Bash; native cmd.exe/PowerShell can't run .sh scripts directly.

Tip

Installing somewhere else? Set SAMECMD_INSTALL_DIR first, e.g. SAMECMD_INSTALL_DIR=/usr/local/bin curl -fsSL .../install.sh | bash for a system-wide install. Pin a specific release with SAMECMD_VERSION=0.1.0 curl -fsSL .../install.sh | bash.

From source

Requires Bun.

git clone https://github.com/behnamazimi/samecmd.git
cd samecmd
bun install
make install

This installs the binary to ~/.local/bin/samecmd. For a system-wide install, use sudo make install-system instead.

Finish setup

Whichever way you installed, wire it into your shell:

samecmd setup

This detects your shell (zsh or bash), adds the hook to your rc file, and tells you to reload it. Do this once — after that, commands activate automatically every time you cd.

Tip

Uninstalling later? No need to clone the repo:

curl -fsSL https://raw.githubusercontent.com/behnamazimi/samecmd/main/uninstall.sh | bash

This removes the shell hook, the binary, and ~/.config/samecmd — wherever it was installed. If you have a cloned copy of the repo, make uninstall does the same thing.

How it works

cd into a directory and samecmd looks for files it recognizes — package.json, Cargo.toml, go.mod, Makefile, and more — then maps the tool those files imply onto standard command names.

samecmd list
samecmd common: Node/pnpm (pnpm-lock.yaml)

  dev        pnpm run dev     [common]
  build      pnpm run build   [common]
  test       pnpm test        [common]
  lint       pnpm run lint    [common]
  fmt        pnpm run fmt     [common]
  typecheck  pnpm run tsc     [common]

For Node projects, it checks your actual package.json scripts, so a command only shows up if it really exists — and it recognizes near-misses too (a project with lint:format gets fmt working automatically).

Not sure why a command resolved the way it did? Run samecmd why for a full explanation.

Arguments after a command pass straight through to the underlying tool:

dev --host 0.0.0.0 --port 3000    # → pnpm run dev -- --host 0.0.0.0 --port 3000
test --watch                       # → pnpm test -- --watch
build --no-cache                   # → docker compose build --no-cache

Add --dry-run to any command to print the resolved command instead of running it:

build --dry-run    # → pnpm run build   (printed, not executed)

Supported stacks

Stack Detected by
Make Makefile
Node/pnpm pnpm-lock.yaml
Node/yarn yarn.lock
Node/bun bun.lockb or bun.lock
Node/npm package.json
Rust Cargo.toml
Go go.mod
Python/uv uv.lock
Python/PDM pdm.lock
Python/Poetry poetry.lock
Python/Hatch pyproject.toml with [tool.hatch]
Python/pip setup.py
Gradle gradlew, build.gradle, or build.gradle.kts
Maven pom.xml
Docker docker-compose.yml / compose.yaml

If a directory matches more than one (say, a Makefile sitting on top of a Python project), the more specific tool wins — Makefile always takes priority.

Canonical commands

Every stack maps onto the same short names, where they make sense for that ecosystem:

Command What it does
dev Start the dev server / run the app
build Compile or bundle for production
test Run the test suite
e2e Run end-to-end tests
lint Check for code issues
fmt Format code
typecheck Type-check without building
start Start in production mode
clean Remove build artifacts
migrate Run database migrations
seed Seed the database
docs Serve or build documentation

samecmd also recognizes common near-misses, so graphql:codegen, test:e2e, type-check, db:migrate, and similar variants all resolve to the right canonical command automatically.

Custom commands

Auto-detection covers most cases. When it doesn't, add a samecmd.yml to the project (or any directory — it doesn't need to be a recognized project):

commands:
  dev: "node --inspect server.js" # override a common command

  deploy:
    cmd: "fly deploy"
    description: "Deploy to production"

Anything here overrides the common command of the same name; new names are added alongside it.

Important

A samecmd.yml runs arbitrary shell and ships with the repo — so the first time you run a command from an untrusted one, samecmd shows you what it will run and asks first.

samecmd trust        # review and approve everything in the current project
samecmd trust --yes  # approve without the interactive prompt
samecmd distrust      # stop trusting: ignore samecmd.yml, don't ask again

Editing the file (e.g. after a git pull) re-triggers the prompt. Records live in ~/.config/samecmd/trust.json; common commands like npm test or cargo build are never gated.

If you'd rather have samecmd expose every package.json script or Makefile target — not just the canonical set — turn on expose_scripts:

samecmd config set expose_scripts true

Running these custom commands/scrips will go through the same trust review before they run.

Settings

samecmd config list                         # show all settings
samecmd config get disabled                 # read a single setting
samecmd config set list_on_enter true       # print commands on every cd
samecmd config set disabled true            # pause samecmd without touching your shell config
Setting Default What it does
disabled false Kill switch — disables samecmd entirely without touching your shell config
list_on_enter false Print available commands whenever you enter a project
expose_scripts false Expose scripts/targets outside the canonical set

Some directories — a monorepo root, a scratch folder, home itself — shouldn't activate samecmd even if they look like a project:

samecmd ignore add            # ignore the current directory
samecmd ignore add ~/scratch  # ignore a specific path
samecmd ignore remove         # unignore the current directory
samecmd ignore list           # show everything ignored

Troubleshooting

A command isn't showing up. Run samecmd list to see what's available and samecmd why to see how it was resolved. Make sure the underlying tool is actually installed (which pnpm, which cargo, …).

Multiple lockfiles warning:

[samecmd] Warning: multiple lockfiles found (pnpm-lock.yaml, package-lock.json).
          Using pnpm. Remove package-lock.json if you've fully migrated.

Delete the stale lockfile and the warning goes away.

About

Gives every project the same short terminal commands (dev, build, test, etc.) regardless of the tooling underneath.

Topics

Resources

License

Stars

4 stars

Watchers

0 watching

Forks

Contributors