- created
- 2025-12-16T00:00:00.000Z
- modified
- 2026-09-06T00:00:00.000Z
- reviewed
- 2026-02-06T00:00:00.000Z
- name
- justfile-expert
- description
- Just command runner expertise — Justfile syntax, recipes, parameters, modules, shebang recipes. Use when authoring justfiles, project commands, or task automation.
- user-invocable
- false
- allowed-tools
- Bash, Grep, Glob, Read, Write, Edit, TodoWrite
- model
- sonnet
# Justfile Expert
Expert knowledge for Just command runner, recipe development, and task automation with focus on cross-platform compatibility and project standardization.
## When to Use This Skill
| Use this skill when... | Use alternative when... |
|------------------------|------------------------|
| Creating/editing justfiles for task automation | Need build system with incremental compilation → Make |
| Writing cross-platform project commands | Need tool version management bundled → mise tasks |
| Adding shebang recipes (Python, Node, Ruby, etc.) | Already using mise for all project tooling |
| Configuring dotenv loading and settings | Authoring the shell itself (pipes, traps, arg parsing) → `shell-expert` |
| Setting up CI/CD with just recipes | Project already has extensive Makefile |
| Standardizing recipes across projects | Exposing a module for bulk smoke-testing → `cli-smoke-recipes` |
## Core Expertise
**Command Runner Mastery**
- Justfile syntax and recipe structure
- Cross-platform task automation (Linux, macOS, Windows)
- Parameter handling and argument forwarding
- Module organization for large projects
**Recipe Development Excellence**
- Recipe patterns for common operations
- Dependency management between recipes
- Shebang recipes for complex logic
- Environment variable integration
**Project Standardization**
- Golden template with standard naming and section structure
- Self-documenting project operations
- Portable patterns across projects
- Integration with CI/CD pipelines
## Recipe Naming Conventions
| Rule | Pattern | Examples |
|------|---------|---------|
| Hyphen-separated | `word-word` | `test-unit`, `format-check` |
| Verb-first (actions) | `verb-object` | `lint`, `build`, `clean` |
| Noun-first (categories) | `noun-verb` | `db-migrate`, `docs-serve` |
| Private prefix | `_name` | `_generate-secrets`, `_setup` |
| `-check` suffix | Read-only verification | `format-check` |
| `-fix` suffix | Auto-correction | `lint-fix`, `check-fix` |
| `-watch` suffix | Watch mode | `test-watch`, `docs-watch` |
| Modifiers after base | `base-modifier` | `build-release` (not `release-build`) |
## Semantic Workflow Recipes
Standard composite recipes with defined meanings:
| Recipe | Composition | Purpose |
|--------|-------------|---------|
| `check` | `format-check` + `lint` + `typecheck` | Code quality only, no tests |
| `pre-commit` | `format-check` + `lint` + `typecheck` + `test-unit` | Fast, non-mutating validation |
| `ci` | `check` + `test-coverage` + `build` | Full CI simulation |
| `clean` | Remove build artifacts | Partial cleanup |
| `clean-all` | `clean` + remove deps/caches | Full cleanup |
```just
# Composite: code quality only (no tests)
check: format-check lint typecheck
# Pre-commit checks (fast, non-mutating)
pre-commit: format-check lint typecheck test-unit
@echo "Pre-commit checks passed"
# Full CI simulation
ci: check test-coverage build
@echo "CI simulation passed"
# Clean build artifacts
clean:
rm -rf dist build .next
# Clean everything including deps
clean-all: clean
rm -rf node_modules .venv __pycache__
```
## Key Capabilities
**Recipe Parameters**
- **Required parameters**: `recipe param:` - must be provided
- **Default values**: `recipe param="default":` - optional with fallback
- **Variadic `+`**: `recipe +FILES:` - one or more arguments
- **Variadic `*`**: `recipe *FLAGS:` - zero or more arguments
- **Environment export**: `recipe $VAR:` - parameter as env var
**Settings Configuration**
- **`set dotenv-load`**: Load `.env` file automatically
- **`set positional-arguments`**: Enable `$1`, `$2` syntax
- **`set export`**: Export all variables as env vars
- **`set shell`**: Custom shell interpreter
- **`set quiet`**: Suppress command echoing
**Recipe Attributes**
- **`[doc("text")]`**: The `--list` description. Overrides the comment above the
recipe; bare **`[doc]`** suppresses it. See "What `--list` Shows" below —
without this attribute only the comment block's LAST line is used
- **`[private]`**: Hide from `--list` and `--summary` output
- **`[no-cd]`**: Don't change directory
- **`[no-exit-message]`**: Suppress exit messages
- **`[unix]`** / **`[windows]`** / **`[linux]`** / **`[macos]`**: Platform-specific recipes
- **`[positional-arguments]`**: Per-recipe positional args
- **`[confirm]`** / **`[confirm("message")]`**: Require confirmation before running
- **`[group: "name"]`** / **`[group("name")]`**: Section recipes in `--list`; both
spellings work, and `--groups` lists the group names
- **`[working-directory: "path"]`**: Run in specific directory
**Module System**
- **`mod name`**: Declare submodule
- **`mod name 'path'`**: Custom module path
- **Invocation**: `just module::recipe` or `just module recipe`
- **`set fallback` is NOT inherited by a module.** The parent may fall through to
*its* parent, but `just sub::parent-recipe` fails with `justfile does not
contain recipe`. A module's recipes resolve only within that module
## Essential Syntax
**Basic Recipe Structure**
```just
# Comment describes the recipe
recipe-name:
command1
command2
```
**Recipe with Parameters**
```just
build target:
@echo "Building {{target}}..."
cd {{quote(target)}} && make
test *args:
uv run pytest {{args}}
```
**Interpolation is UNQUOTED — quote anything that can contain spaces**
`{{...}}` splices raw text into the recipe body *before* the shell parses it,
so a value carrying spaces or quotes word-splits. This bites hardest on the
`*args` passthrough above, because the error is reported by the *called
program* rather than by just, which makes it read like a bug in the tool:
```just
# Trap — one argument with spaces arrives as several
caption *ARGS:
./tool.py {{ARGS}}
```
```
$ just caption ./data "the subject's face"
tool.py: error: unrecognized arguments: subjects face
```
The outer shell consumed the quotes (taking the apostrophe with them) and
`the` / `subject's` / `face` arrived as three separate argv entries. Name the
parameters that can contain spaces and run them through `quote()`, which emits
a properly shell-escaped literal:
```just
# Correct — named params are quoted; trailing flags still pass through
caption DIR SUBJECT="" *ARGS:
./tool.py {{quote(DIR)}} {{quote(SUBJECT)}} {{ARGS}}
```
`quote()` covers embedded spaces, `'`, `"`, and `$`. Keep `{{ARGS}}` bare —
that is what lets several trailing flags expand as separate words — and accept
its corollary: an individual passthrough flag's value must not contain spaces.
When one might, promote it to a named parameter too.
**What `--list` Shows Is ONE Line, and It Is Not Your Comment Block**
`just --list` renders a single description per recipe. With no `[doc]`
attribute it takes the **last line** of the comment block immediately above the
recipe — not the first line, and not the block:
| Above the recipe | `--list` shows |
|---|---|
| `[doc("Build the release bundle.")]` | that text |
| a comment block, no attribute | **only its last line** |
| bare `[doc]` | nothing |
| nothing | nothing |
So "add a comment before each recipe" is **not** the same as documenting it. A
block that ends in an example or a caveat — the normal way to write one — lists
as that fragment:
```just
# Pitch-correct the singing in an MP4. Video is stream-copied.
# just autotune take.mp4 out.mp4 --key C:minor
autotune IN OUT *FLAGS:
```
```
$ just --list
autotune IN OUT *FLAGS # just autotune take.mp4 out.mp4 --key C:minor
```
**Add `[doc("one line")]` as soon as a recipe's comment block exceeds one
line.** The block stays where it is and keeps carrying the detail; the
attribute is the only thing `--list` reads.
**The block binds by ADJACENCY, and reassignment is silent.** A blank line ends
a block, so inserting a recipe between a block and the recipe it describes
hands the block to the newcomer — the original then lists blank, and nothing
warns. Re-read `just --list` after inserting a recipe into an existing file.
**A recipe with a required positional has no `--help` form.** `recipe *ARGS:`
forwards `--help` to the underlying tool, but just refuses the call before the
tool runs once a positional is required:
```
$ just autotune --help
error: recipe `autotune` got 1 positional argument but takes at least 2
```
There is no bare-help spelling for such a recipe. Put the flags in its `[doc]`
or comment block, or add a `help` recipe that prints them.
**Recipe Dependencies**
```just
default: build test
build: _setup
cargo build --release
_setup:
@echo "Setting up..."
```
**Variables and Interpolation**
```just
version := "1.0.0"
project := env('PROJECT_NAME', 'default')
info:
@echo "Project: {{project}} v{{version}}"
```
**Conditional Recipes**
```just
[unix]
open:
xdg-open http://localhost:8080
[windows]
open:
start http://localhost:8080
```
## Standard Recipes
Every project should provide these standard recipes, organized by section:
```just
# Justfile - Project task runner
# Run `just` or `just help` to see available recipes
set dotenv-load
set positional-arguments
# Default recipe - show help
default:
@just --list
# Show available recipes with descriptions
help:
@just --list --unsorted
####################
# Development
####################
# Start development environment
dev:
# bun run dev / uv run uvicorn app:app --reload / skaffold dev
# Build for production
build:
# bun run build / cargo build --release / docker build
# Clean build artifacts
clean:
# rm -rf dist build .next
####################
# Code Quality
####################
# Run linter (read-only)
lint *args:
# bun run lint / uv run ruff check {{args}}
# Auto-fix lint issues
lint-fix:
# bun run lint:fix / uv run ruff check --fix .
# Format code (mutating)
format *args:
# bun run format / uv run ruff format {{args}}
# Check formatting without modifying (non-mutating)
format-check *args:
# bun run format:check / uv run ruff format --check {{args}}
# Type checking
typecheck:
# bunx tsc --noEmit / uv run basedpyright
####################
# Testing
####################
# Run all tests
test *args:
# bun test {{args}} / uv run pytest {{args}}
# Run unit tests only
test-unit *args:
# bun test --grep unit {{args}} / uv run pytest -m unit {{args}}
####################
# Workflows
####################
# Composite: code quality (no tests)
check: format-check lint typecheck
# Pre-commit checks (fast, non-mutating)
pre-commit: format-check lint typecheck test-unit
@echo "Pre-commit checks passed"
# Full CI simulation
ci: check test-coverage build
@echo "CI simulation passed"
```
### Section Structure
Organize recipes into these standard sections:
| Section | Recipes | Purpose |
GitHub에서 보기