--- name: work-with-justfiles description: Guidance for structuring and organizing justfiles. Use when creating, editing, or discussing justfile organization, command grouping, or repo CLI conventions. --- Justfiles capture the common commands of a repository in one place. This skill provides principles for organizing justfiles effectively - preventing scope creep, documenting workflows, and creating clear command aliases that work consistently. The goal is a single source of truth for "how do I do X in this repo" that anyone can read and understand. **Justfiles always run from the repo root.** Never `cd` in recipes. Use absolute paths or paths relative to the justfile location. ```just # Group: Development dev: npm run dev # Group: Testing test: npm test test-watch: npm test -- --watch ``` **Always run from root, never cd** Justfiles execute from the repository root. This is a feature, not a limitation: - Predictable behavior regardless of where you invoke `just` - Paths in recipes are always relative to repo root - No confusion about "where am I running this from" ```just # Good - explicit path from root build: cd packages/app && npm run build # Bad - assumes current directory build: npm run build ``` If a command needs to run in a subdirectory, use `cd dir &&` prefix explicitly. **Capture every common command as an alias** The justfile should be the single source of truth for repo commands. If someone asks "how do I run tests?" or "how do I deploy?", the answer is in the justfile. ```just # Instead of remembering: npm run test:unit -- --coverage --reporter=html test-coverage: npm run test:unit -- --coverage --reporter=html # Instead of remembering: docker compose -f docker-compose.dev.yml up -d dev-services: docker compose -f docker-compose.dev.yml up -d ``` **Benefits:** - No tribal knowledge ("oh you need to pass --reporter=html") - Commands are documented by their existence - Easy to discover: `just --list` **Organize commands into logical groups** Use comments to create visual groupings. Let the groups emerge from your actual commands: ```just # ───────────────────────────────────────────────────────────── # Development # ───────────────────────────────────────────────────────────── dev: npm run dev # ───────────────────────────────────────────────────────────── # Testing # ───────────────────────────────────────────────────────────── test: npm test test-watch: npm test -- --watch ``` **Common groupings** (adapt to your project): - Setup / Installation - Development - Testing - Building - Deployment - Database / Migrations - Utilities / Helpers **One place for CLI commands, not everything** Justfiles replace scattered shell commands and npm scripts for common operations. They don't replace: - Build tool configuration (webpack, vite, etc.) - CI/CD pipeline definitions - Complex scripting (use actual scripts in `scripts/`) **Rule of thumb:** If a recipe is longer than 5-10 lines, extract it to a script file and call that from the justfile: ```just # Good - justfile calls the script deploy: ./scripts/deploy.sh # Avoid - complex logic inline deploy: if [ "$ENV" = "prod" ]; then # ... 20 lines of logic fi ``` **Make common workflows discoverable** Use recipe names that describe the workflow, not the tool: ```just # Good - describes what you're doing setup: npm install cp .env.example .env just db-migrate # Less good - just wraps the tool npm-install: npm install ``` Chain related commands with dependencies: ```just # Running `just deploy` runs lint, test, build first deploy: lint test build ./scripts/deploy.sh ``` **Set a sensible default** The first recipe (or one named `default`) runs when you type just `just`: ```just # Show available commands by default default: @just --list # Or start development by default default: just dev ``` **Accept arguments for flexibility** ```just # Positional argument test file: npm test {{file}} # With default value build env="dev": npm run build -- --env={{env}} # Variadic arguments run *args: npm run {{args}} ``` **Use environment variables** ```just # From .env file set dotenv-load # Inline variable db_name := "myapp_dev" # Recipe-local export migrate: export DATABASE_URL=$DATABASE_URL && npm run migrate ``` **Require confirmation for dangerous operations** ```just [confirm("Are you sure you want to reset the database?")] db-reset: dropdb myapp && createdb myapp && just db-migrate ``` **Hide helper recipes** Prefix with `_` to hide from `just --list`: ```just # Public - shows in list build: _check-deps npm run build # Private - hidden helper _check-deps: @command -v node >/dev/null || (echo "Node required" && exit 1) ``` **Add descriptions for complex recipes** ```just # Deploy to production (requires VPN connection) [doc("Deploy the application to production environment")] deploy-prod: ./scripts/deploy.sh prod ``` **Avoid changing directory implicitly** ```just # Bad - unclear where this runs build: cd src npm run build # Good - explicit and stays in root build: cd src && npm run build ``` **Don't just wrap every npm script** If `npm run dev` is obvious and memorable, you don't need `just dev` unless it adds value (like running setup first). Add justfile recipes when: - The command has flags that are hard to remember - Multiple commands need to run together - The command needs environment setup **Consolidate CLI tools** Before: Developers need to know npm, docker-compose, aws, terraform, kubectl... After: `just --list` shows everything, actual tools are implementation details. ```just # These hide complexity behind simple names deploy: terraform apply -auto-approve db-shell: kubectl exec -it postgres-0 -- psql ``` A well-organized justfile: - Runs all commands from repo root (no implicit cd) - Has logical groupings with clear visual separation - Captures common commands as aliases (single source of truth) - Uses descriptive recipe names (workflow-focused, not tool-focused) - Keeps recipes short (complex logic in scripts/) - Has a sensible default recipe - Is discoverable via `just --list`