Just Task Runner — Modern Build Orchestration
Acts as a senior developer who designs clean, portable build scripts using Just's full feature set. When this skill is active, the model creates structured justfile files with named arguments, subcommands, dotenv integration, and platform-agnostic recipe execution for developer workflow automation.
TL;DR Checklist
When to Use
Use this skill when:
- Replacing a Makefile that's hard to read due to tab/space confusion or complex variable scoping
- Need named arguments with defaults so team members run
just deploy --env staging without memorizing flag order
- Team works across Linux, macOS, and Windows needing identical recipe execution without platform-specific shells
- Want
.env file support baked in without external tools like direnv or custom shell wrappers
- Building CLI tooling or developer experience scripts that benefit from subcommands (e.g.,
just docker build, just db migrate)
When NOT to Use
Avoid this skill for:
- Need incremental build tracking with file dependency graphs → use Make or Bazel instead
- Maintaining a C/C++ project requiring Make's pattern rules (
%.o: %.c) and automatic dependency generation
- CI/CD system has no Just binary available and cannot install dependencies
- Project already uses Gradle, Maven, or Nx for its build pipeline — don't layer a second tool
- Recipes require complex shell pipelines with
&&, ||, or process substitution — Just's shell integration handles these but native Make rules are more idiomatic
Core Workflow
Project Setup and Installation — Install just from your system package manager. Verify version 1.20+ for full feature support including runsets, subcommands, and .env loading. Check existing project root for a Makefile or shell script that Just can replace.
Checkpoint: Run just --version and confirm ≥ 1.20.0 before proceeding.
Create Justfile with Settings — Start the justfile with global settings block: dotenv-load, export-vars, and shell. Define backtick variables for computed values. Add a help: recipe as the first entry. This becomes the contract all team members follow.
Checkpoint: Run just --list to verify help output is readable and complete before adding more recipes.
Define Recipes with Named Arguments — Translate existing build/deploy/test commands into Just recipes using named arguments with defaults instead of positional flags or hardcoded values. Use set dotenv-load at the top of any recipe that reads from .env. Group related recipes under subcommands using subcommand: syntax.
Checkpoint: Every public recipe must have a ## comment describing its purpose and accepted arguments.
Deploy and Release Workflows — Implement environment-specific deploy recipes with rollback support. Use conditional logic based on argument values (e.g., env ?= "production" with @if env == "staging" { ... }). Add release tagging and changelog generation as a separate subcommand.
Checkpoint: Deploy recipe must validate required variables exist before executing any remote commands.
Maintenance and Quality Recipes — Add cleanup, formatting, linting, and auditing recipes. These should be idempotent (safe to run multiple times) and fast. Group them under a .maintenance: private subcommand or quality: public subcommand.
Checkpoint: Format recipe must run successfully on a clean checkout before committing the justfile.
Implementation Patterns
Pattern 1: Variable Interpolation and Computed Values
Just supports backtick variables that execute shell commands and capture output at parse time. Use these for computed values like timestamps, git commit hashes, and platform detection. Conditional expressions (expr ?= default) provide fallback defaults.
# Platform detection using backtick variable
os := `uname -s`
arch := `uname -m`
timestamp := `date +%Y%m%d-%H%M%S`
git_branch := `git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main"`
# Conditional default with fallback chain
app_name ?= "myapp"
config_file := "config/{{ app_name }}.yaml"
# Computed version from git tags
version ?= `git describe --tags --always 2>/dev/null || echo "0.0.0"`
build:
@echo "Building {{ app_name }} v{{ version }}"
@echo "Platform: {{ os }}/{{ arch }}"
@echo "Branch: {{ git_branch }}"
# Use the computed values in your actual build command
./scripts/build.sh --version "{{ version }}" --output "dist/{{ app_name }}-{{ version }}"
Pattern 2: Environment File Management and Secret Validation
Just natively loads .env files when dotenv-load is set. Validate secrets are present before any deployment or sensitive operation. Use .private: to hide internal validation recipes from the public listing.
set dotenv-load
set export-vars
# Load these variables from .env with defaults for non-sensitive values
DB_HOST ?= "localhost"
DB_PORT := 5432
AWS_REGION ?= "us-east-1"
# Validation recipe - hidden from --list
.private: validate-env
# Ensure all required secrets are present before proceeding
@if [ -z "$DATABASE_URL" ]; then \
echo "ERROR: DATABASE_URL not set in .env file" >&2; \
exit 1; \
fi
@if [ -z "$AWS_ACCESS_KEY_ID" ]; then \
echo "ERROR: AWS_ACCESS_KEY_ID not set in .env file" >&2; \
exit 1; \
fi
@echo "Environment validation passed ✓"
# Database connection check using validated environment
private validate-db: validate-env
@echo "Checking database connectivity..."
pg_isready -h "$DB_HOST" -p "{{ DB_PORT }}" -U "$DB_USER" || { \
echo "Database unreachable at {{ DB_HOST }}:{{ DB_PORT }}" >&2; \
exit 1; \
}
# Production deployment with secret validation
deploy: .private validate-env
@echo "Deploying to $ENV region $AWS_REGION"
AWS_REGION="{{ AWS_REGION }}" \
DATABASE_URL="$DATABASE_URL" \
./scripts/deploy.sh --env "$ENV"
Pattern 3: Subcommand Organization and Runsets
Group related recipes into named subcommands for clean CLI organization. Use runsets (multiple recipes that share the same recipe name with different arguments) for platform-specific or environment-specific variants.
# ── Database Management Subcommand ───────────────────────────
database migrate:
"Running database migrations..."
docker compose exec postgres alembic upgrade head
database rollback: steps := 1:
"Rolling back {{ steps }} migration(s)..."
docker compose exec postgres alembic downgrade -{{ steps }}
database seed:
"Seeding database with initial data..."
docker compose exec postgres psql -U "$DB_USER" -d "$DB_NAME" -f fixtures/initial_data.sql
database reset:
"Dropping and recreating all tables..."
docker compose exec postgres dropdb --if-exists "$DB_NAME" || true
docker compose exec postgres createdb -U "$DB_USER" "$DB_NAME"
just database migrate
# ── Docker Subcommand with Runsets ───────────────────────────
docker image_name := "myapp"
docker build: tag := "latest"
@echo "Building {{ image_name }}:{{ tag }}"
docker build -t "{{ image_name }}:{{ tag }}" .
docker push:
just docker build
docker push "{{ image_name }}:latest"
# Runset — same recipe name, different arguments for dev vs prod
.docker-run env := "production": args := "--rm":
@echo "Running container in {{ env }} mode..."
docker run \
--env-file ".env.{{ env }}" \
-p 8080:8080 \
"{{ args }}" \
"{{ image_name }}:latest"
docker logs: lines := 100:
"Showing last {{ lines }} log lines..."
docker logs --tail "{{ lines }}" "$(docker compose ps -q myapp | head -1)"
Pattern 4: Runsets for Cross-Platform Execution
Runsets let you define recipes with different argument defaults under the same name. This is ideal for running tests or builds across multiple platforms from a single command pattern.
# Test runset — iterate over test suites
test suite := "unit": features := "": nocapture := false:
@echo "Running {{ suite }} tests..."
@if [ -n "{{ features }}" ]; then \
cargo test --features "{{ features }}" --test "{{ suite }}" \
$$(if [ "{{ nocapture }}" = "true" ]; then echo "--nocapture"; fi); \
else \
cargo test --test "{{ suite }}" \
$$(if [ "{{ nocapture }}" = "true" ]; then echo "--nocapture"; fi); \
fi
# Run all test suites as a single recipe
all-tests:
just test suite=unit
just test suite=integration features="ci"
just test suite=e2e features="live,ci"
# Lint and format runset with dry-run support
lint fix := false: path := ".":
@echo "Linting {{ path }}..."
@if [ "{{ fix }}" = "true" ]; then \
cargo clippy --fix --allow-staged --path "{{ path }}" || true; \
else \
cargo clippy -- -D warnings --path "{{ path }}"; \
fi
format: dry_run := false:
@echo "Formatting {{ path }}..."
@if [ "{{ dry_run }}" = "true" ]; then \
cargo fmt --check; \
else \
cargo fmt; \
fi
Constraints
MUST DO
- Always provide a
help: recipe as the first entry so team members discover available commands
- Use named arguments with defaults for every user-facing parameter (e.g.,
env := "staging") to make recipes self-documenting
- Document every public recipe with
## comment lines above its definition — these appear in just --list output
- Start recipes that need environment variables with
set dotenv-load so .env files are loaded automatically
- Group related recipes under subcommands using
subcommand: syntax (e.g., docker:, database:, deploy:)
MUST NOT DO
- Don't use tabs in the recipe body — Just uses tab-indented lines as command execution, and mixing spaces/tabs breaks parsing
- Don't put secrets directly in the Justfile — always load sensitive values from
.env files via dotenv-load
- Don't nest subcommands more than 2 levels deep (e.g.,
docker compose exec is fine; docker compose exec db psql should be a separate recipe)
- Don't use Make-specific features like pattern rules (
%.o: %.c) or automatic variables ($@, $<) — Just has no equivalent syntax
- Don't create circular dependencies between recipes (e.g., A depends on B depends on A)
Output Template
When this skill is active, produce the following output structure:
- Project Analysis — Identify existing build scripts, CI configuration, and team platform mix (Linux/macOS/Windows)
- Justfile Draft — Complete
justfile with settings, variables, subcommands, and at least 5 public recipes
- Migration Plan (from Makefile if applicable) — Side-by-side mapping of existing Make targets to Just recipes with command translation notes
- Team Setup Instructions — Installation commands per platform (
brew, apt, pacman, nix), .env template, and first-run instructions
- CI/CD Integration — Add Justfile to CI pipeline configuration, include
just --list verification step before running recipe targets
Related Skills
| Skill |
Purpose |
makefile |
GNU Make for C/C++ projects needing pattern rules and dependency graphs |
mage-build-tool |
Go-based build tool using actual Go code as build scripts |
linux-make-build-system |
GNU Make with cross-compilation support and complex dependency resolution |
Live References
Authoritative documentation links for the Just task runner. The model follows markdown links at load time to resolve external references and inline content.
1---2name: just-task-runner3description: Implements the Just task runner as a modern alternative to GNU Make with named arguments, subcommands, runsets, environment file support, and cross-platform portability for developer workflow automation.4license: MIT5---67891011# Just Task Runner — Modern Build Orchestration1213Acts as a senior developer who designs clean, portable build scripts using Just's full feature set. When this skill is active, the model creates structured `justfile` files with named arguments, subcommands, dotenv integration, and platform-agnostic recipe execution for developer workflow automation.1415## TL;DR Checklist1617- [ ] Place `justfile` at project root — never nest it in subdirectories18- [ ] Use named arguments with defaults for every user-facing parameter (e.g., `target := "debug"`)19- [ ] Declare `.private:` before internal recipes to hide them from `just --list`20- [ ] Enable `dotenv-load` at the top of recipe blocks that need environment variables21- [ ] Use backtick variables (`var := `backtick expression`backtick`) for computed values like timestamps or git info22- [ ] Group related recipes into subcommands using `subcommand:` syntax (e.g., `db:`, `docker:`)23- [ ] Always include a `help:` recipe as the first entry in the justfile2425---2627## When to Use2829Use this skill when:3031- Replacing a Makefile that's hard to read due to tab/space confusion or complex variable scoping32- Need named arguments with defaults so team members run `just deploy --env staging` without memorizing flag order33- Team works across Linux, macOS, and Windows needing identical recipe execution without platform-specific shells34- Want `.env` file support baked in without external tools like direnv or custom shell wrappers35- Building CLI tooling or developer experience scripts that benefit from subcommands (e.g., `just docker build`, `just db migrate`)3637---3839## When NOT to Use4041Avoid this skill for:4243- Need incremental build tracking with file dependency graphs → use Make or Bazel instead44- Maintaining a C/C++ project requiring Make's pattern rules (`%.o: %.c`) and automatic dependency generation45- CI/CD system has no Just binary available and cannot install dependencies46- Project already uses Gradle, Maven, or Nx for its build pipeline — don't layer a second tool47- Recipes require complex shell pipelines with `&&`, `||`, or process substitution — Just's shell integration handles these but native Make rules are more idiomatic4849---5051## Core Workflow52531. **Project Setup and Installation** — Install `just` from your system package manager. Verify version 1.20+ for full feature support including runsets, subcommands, and `.env` loading. Check existing project root for a Makefile or shell script that Just can replace.54 **Checkpoint:** Run `just --version` and confirm ≥ 1.20.0 before proceeding.55562. **Create Justfile with Settings** — Start the justfile with global settings block: `dotenv-load`, `export-vars`, and `shell`. Define backtick variables for computed values. Add a `help:` recipe as the first entry. This becomes the contract all team members follow.57 **Checkpoint:** Run `just --list` to verify help output is readable and complete before adding more recipes.58593. **Define Recipes with Named Arguments** — Translate existing build/deploy/test commands into Just recipes using named arguments with defaults instead of positional flags or hardcoded values. Use `set dotenv-load` at the top of any recipe that reads from `.env`. Group related recipes under subcommands using `subcommand:` syntax.60 **Checkpoint:** Every public recipe must have a `##` comment describing its purpose and accepted arguments.61624. **Deploy and Release Workflows** — Implement environment-specific deploy recipes with rollback support. Use conditional logic based on argument values (e.g., `env ?= "production"` with `@if env == "staging" { ... }`). Add release tagging and changelog generation as a separate subcommand.63 **Checkpoint:** Deploy recipe must validate required variables exist before executing any remote commands.64655. **Maintenance and Quality Recipes** — Add cleanup, formatting, linting, and auditing recipes. These should be idempotent (safe to run multiple times) and fast. Group them under a `.maintenance:` private subcommand or `quality:` public subcommand.66 **Checkpoint:** Format recipe must run successfully on a clean checkout before committing the justfile.6768---6970## Implementation Patterns7172### Pattern 1: Variable Interpolation and Computed Values7374Just supports backtick variables that execute shell commands and capture output at parse time. Use these for computed values like timestamps, git commit hashes, and platform detection. Conditional expressions (`expr ?= default`) provide fallback defaults.7576```just77# Platform detection using backtick variable78os := `uname -s`79arch := `uname -m`80timestamp := `date +%Y%m%d-%H%M%S`81git_branch := `git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main"`8283# Conditional default with fallback chain84app_name ?= "myapp"85config_file := "config/{{ app_name }}.yaml"8687# Computed version from git tags88version ?= `git describe --tags --always 2>/dev/null || echo "0.0.0"`8990build:91 @echo "Building {{ app_name }} v{{ version }}"92 @echo "Platform: {{ os }}/{{ arch }}"93 @echo "Branch: {{ git_branch }}"94 # Use the computed values in your actual build command95 ./scripts/build.sh --version "{{ version }}" --output "dist/{{ app_name }}-{{ version }}"96```9798### Pattern 2: Environment File Management and Secret Validation99100Just natively loads `.env` files when `dotenv-load` is set. Validate secrets are present before any deployment or sensitive operation. Use `.private:` to hide internal validation recipes from the public listing.101102```just103set dotenv-load104set export-vars105106# Load these variables from .env with defaults for non-sensitive values107DB_HOST ?= "localhost"108DB_PORT := 5432109AWS_REGION ?= "us-east-1"110111# Validation recipe - hidden from --list112.private: validate-env113 # Ensure all required secrets are present before proceeding114 @if [ -z "$DATABASE_URL" ]; then \115 echo "ERROR: DATABASE_URL not set in .env file" >&2; \116 exit 1; \117 fi118 @if [ -z "$AWS_ACCESS_KEY_ID" ]; then \119 echo "ERROR: AWS_ACCESS_KEY_ID not set in .env file" >&2; \120 exit 1; \121 fi122 @echo "Environment validation passed ✓"123124# Database connection check using validated environment125private validate-db: validate-env126 @echo "Checking database connectivity..."127 pg_isready -h "$DB_HOST" -p "{{ DB_PORT }}" -U "$DB_USER" || { \128 echo "Database unreachable at {{ DB_HOST }}:{{ DB_PORT }}" >&2; \129 exit 1; \130 }131132# Production deployment with secret validation133deploy: .private validate-env134 @echo "Deploying to $ENV region $AWS_REGION"135 AWS_REGION="{{ AWS_REGION }}" \136 DATABASE_URL="$DATABASE_URL" \137 ./scripts/deploy.sh --env "$ENV"138```139140### Pattern 3: Subcommand Organization and Runsets141142Group related recipes into named subcommands for clean CLI organization. Use runsets (multiple recipes that share the same recipe name with different arguments) for platform-specific or environment-specific variants.143144```just145# ── Database Management Subcommand ───────────────────────────146database migrate:147 "Running database migrations..."148 docker compose exec postgres alembic upgrade head149150database rollback: steps := 1:151 "Rolling back {{ steps }} migration(s)..."152 docker compose exec postgres alembic downgrade -{{ steps }}153154database seed:155 "Seeding database with initial data..."156 docker compose exec postgres psql -U "$DB_USER" -d "$DB_NAME" -f fixtures/initial_data.sql157158database reset:159 "Dropping and recreating all tables..."160 docker compose exec postgres dropdb --if-exists "$DB_NAME" || true161 docker compose exec postgres createdb -U "$DB_USER" "$DB_NAME"162 just database migrate163164# ── Docker Subcommand with Runsets ───────────────────────────165docker image_name := "myapp"166167docker build: tag := "latest"168 @echo "Building {{ image_name }}:{{ tag }}"169 docker build -t "{{ image_name }}:{{ tag }}" .170171docker push:172 just docker build173 docker push "{{ image_name }}:latest"174175# Runset — same recipe name, different arguments for dev vs prod176.docker-run env := "production": args := "--rm":177 @echo "Running container in {{ env }} mode..."178 docker run \179 --env-file ".env.{{ env }}" \180 -p 8080:8080 \181 "{{ args }}" \182 "{{ image_name }}:latest"183184docker logs: lines := 100:185 "Showing last {{ lines }} log lines..."186 docker logs --tail "{{ lines }}" "$(docker compose ps -q myapp | head -1)"187```188189### Pattern 4: Runsets for Cross-Platform Execution190191Runsets let you define recipes with different argument defaults under the same name. This is ideal for running tests or builds across multiple platforms from a single command pattern.192193```just194# Test runset — iterate over test suites195test suite := "unit": features := "": nocapture := false:196 @echo "Running {{ suite }} tests..."197 @if [ -n "{{ features }}" ]; then \198 cargo test --features "{{ features }}" --test "{{ suite }}" \199 $$(if [ "{{ nocapture }}" = "true" ]; then echo "--nocapture"; fi); \200 else \201 cargo test --test "{{ suite }}" \202 $$(if [ "{{ nocapture }}" = "true" ]; then echo "--nocapture"; fi); \203 fi204205# Run all test suites as a single recipe206all-tests:207 just test suite=unit208 just test suite=integration features="ci"209 just test suite=e2e features="live,ci"210211# Lint and format runset with dry-run support212lint fix := false: path := ".":213 @echo "Linting {{ path }}..."214 @if [ "{{ fix }}" = "true" ]; then \215 cargo clippy --fix --allow-staged --path "{{ path }}" || true; \216 else \217 cargo clippy -- -D warnings --path "{{ path }}"; \218 fi219220format: dry_run := false:221 @echo "Formatting {{ path }}..."222 @if [ "{{ dry_run }}" = "true" ]; then \223 cargo fmt --check; \224 else \225 cargo fmt; \226 fi227```228229---230231## Constraints232233### MUST DO234- Always provide a `help:` recipe as the first entry so team members discover available commands235- Use named arguments with defaults for every user-facing parameter (e.g., `env := "staging"`) to make recipes self-documenting236- Document every public recipe with `## comment` lines above its definition — these appear in `just --list` output237- Start recipes that need environment variables with `set dotenv-load` so `.env` files are loaded automatically238- Group related recipes under subcommands using `subcommand:` syntax (e.g., `docker:`, `database:`, `deploy:`)239240### MUST NOT DO241- Don't use tabs in the recipe body — Just uses tab-indented lines as command execution, and mixing spaces/tabs breaks parsing242- Don't put secrets directly in the Justfile — always load sensitive values from `.env` files via `dotenv-load`243- Don't nest subcommands more than 2 levels deep (e.g., `docker compose exec` is fine; `docker compose exec db psql` should be a separate recipe)244- Don't use Make-specific features like pattern rules (`%.o: %.c`) or automatic variables (`$@`, `$<`) — Just has no equivalent syntax245- Don't create circular dependencies between recipes (e.g., A depends on B depends on A)246247---248249## Output Template250251When this skill is active, produce the following output structure:2522531. **Project Analysis** — Identify existing build scripts, CI configuration, and team platform mix (Linux/macOS/Windows)2542. **Justfile Draft** — Complete `justfile` with settings, variables, subcommands, and at least 5 public recipes2553. **Migration Plan** (from Makefile if applicable) — Side-by-side mapping of existing Make targets to Just recipes with command translation notes2564. **Team Setup Instructions** — Installation commands per platform (`brew`, `apt`, `pacman`, `nix`), `.env` template, and first-run instructions2575. **CI/CD Integration** — Add Justfile to CI pipeline configuration, include `just --list` verification step before running recipe targets258259---260261## Related Skills262263| Skill | Purpose |264|---|---|265| `makefile` | GNU Make for C/C++ projects needing pattern rules and dependency graphs |266| `mage-build-tool` | Go-based build tool using actual Go code as build scripts |267| `linux-make-build-system` | GNU Make with cross-compilation support and complex dependency resolution |268269---270271## Live References272273> Authoritative documentation links for the Just task runner. The model follows markdown links at load time to resolve external references and inline content.274275- [Just Official Documentation](https://just.systems/man/en/)276- [Just GitHub Repository](https://github.com/casey/just)277- [Just Recipes Book — Real-World Examples](https://github.com/casey/just/tree/main/recipes)278- [Just Changelog and Release Notes](https://github.com/casey/just/releases)279- [Awesome Just — Curated Collection of Justfiles](https://github.com/okdnet/awesome-just)