# Just Pro

> This skill provides patterns for setting up just (command runner) in projects. Use PROACTIVELY when creating build systems, setting up new repos, or when the user asks about just/justfile configuration. Covers both simple single-project repos and monorepos with hierarchical justfile modules.

- Skill: `majiayu000/just-pro` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/just-pro`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/just-pro/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/just-pro

---


# Justfile Skill

Build system configuration using [just](https://just.systems), a modern command runner.

**Related skills:**
- **mise** - Tool version management (includes just+mise integration patterns)
- **go-pro**, **rust-pro**, **typescript-pro** - Language-specific templates

## Installation

```bash
# Via mise (recommended - version pinned per-project)
mise use just

# macOS
brew install just

# Linux/Windows (via cargo)
cargo install just

# Or prebuilt binaries: https://github.com/casey/just/releases
```

## When to Use Just

| Scenario | Recommendation |
|----------|----------------|
| Cross-language monorepo | **just** - Unified interface across packages |
| Single Go/Rust project | **just** or language-native (go/cargo) |
| Node.js project | npm scripts primary, just optional wrapper |
| CI/CD porcelain | **just** - Single entry point for all operations |
| Simple scripts | **just** - Better than shell scripts |

## Project Patterns

### Simple Repo (Single Package)

For single-language projects, create one `justfile` at repo root.

```just
# Project Build System
# Usage: just --list

default:
    @just --list

# === Quality Gates ===
check: fmt lint test
    @echo "All checks passed"

fmt:
    go fmt ./...

lint:
    go tool golangci-lint run

test:
    go test -race ./...

build:
    go build -o bin/app ./cmd/app
```

See `references/simple-repo.just` for complete templates.

### Monorepo (Multiple Packages)

For monorepos, use hierarchical justfiles with the `mod` system:

```
repo/
├── justfile              # Router - imports package modules
└── packages/
    ├── api-go/
    │   └── justfile      # Go package recipes
    ├── web/
    │   └── justfile      # TypeScript package recipes (optional)
    └── ops/
        └── justfile      # DevOps recipes
```

**Root justfile** (router):
```just
# Monorepo Build System
# Usage: just --list
# Usage: just go <recipe>

mod go "packages/api-go"
mod web "packages/web"
mod ops "ops"

default:
    @just --list

# Umbrella recipes call into modules
check: (go::check) (web::check)
    @echo "All checks passed"

setup: (go::setup) (web::setup)
    @echo "All toolchains ready"
```

**Commands become**: `just go check`, `just go lint`, `just web build`, `just ops deploy`

See `references/monorepo-root.just` and `references/package-go.just` for templates.

## Recipe Patterns

### Quality Gates

Always provide a single `check` recipe that runs all quality gates:

```just
# Full quality gates - run before commits
check: fmt lint test coverage-check
    @echo "All checks passed"
```

### Quick Check (Dev Iteration)

Fast feedback loop for development - essential checks only:

```just
# Quick feedback loop - essential checks only
check-quick: lint test
    @echo "Quick checks passed"
```

### Clean Recipe

Standard cleanup for build artifacts and caches:

```just
# Remove build artifacts and caches
clean:
    rm -rf build/ coverage.out node_modules/.cache
```

### Parallel Check Execution (CI)

Run package checks in parallel for faster CI:

```just
# Run all package checks in parallel (CI optimization)
check-parallel:
    @just api check & just web check & wait
```

### Coverage Enforcement

Use shebang for multi-line shell logic:

```just
# Check coverage meets 70% minimum
coverage-check:
    #!/usr/bin/env bash
    set -euo pipefail
    go test -race -coverprofile=coverage.out ./...
    COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
    COVERAGE_INT=${COVERAGE%.*}
    [ "$COVERAGE_INT" -ge 70 ] || (echo "FAIL: Coverage ${COVERAGE}% < 70%" && exit 1)
    echo "PASS: Coverage ${COVERAGE}%"
```

The `#!/usr/bin/env bash` shebang runs the entire recipe as a single shell script (variables persist across lines).

### Umbrella Recipes with Module Dependencies

```just
# Root justfile calling into modules
check: (go::check) (web::check)
    @echo "All checks passed"
```

### Optional Modules

For packages that may not exist in all environments:

```just
mod? optional-package "packages/optional"
```

### Documentation Comments

Every recipe should have a doc comment (shown in `just --list`):

```just
# Run unit tests with race detection
test:
    go test -race ./...
```

**Module doc comments** - Comments above `mod` statements also appear in `just --list`:

```just
# Go API (GraphQL + Watermill)
mod api "packages/api"

# Next.js frontend
mod web "packages/web"

# PostgreSQL database
mod db "packages/db"
```

Output of `just --list`:
```
api ...      # Go API (GraphQL + Watermill)
db ...       # PostgreSQL database
web ...      # Next.js frontend
```

Always add doc comments to module imports for discoverability.

## Module System Details

### Syntax

```just
mod <name> "<path>"              # Required module
mod? <name> "<path>"             # Optional module (no error if missing)
mod <name>                       # Module at ./<name>/justfile
```

### Working Directory

Recipes in modules run with their working directory set to the module's directory, not the root. This is the desired behavior for package-local commands.

### Listing Module Recipes

```bash
just --list           # Root recipes + module names
just --list go        # Recipes in 'go' module
just --list-submodules # All recipes including submodules
```

**Important**: Use `just --list <module>` not `just <module> --list`. The flag must come before the module name.

### Calling Module Recipes

```bash
just go check         # From command line
(go::check)          # As dependency in justfile
```

## Integration with Language Toolchains

### Go Projects

```just
# Uses go.mod tool directive for pinned versions
lint:
    go tool golangci-lint run

# Auto-fix linting issues (chains goimports to fix imports after modernize changes)
fix:
    go tool golangci-lint run --fix
    go tool goimports -w .
```

**Why chain goimports?** The `modernize` linter may change code (e.g., `fmt.Errorf()` → `errors.New()`) without updating imports, breaking builds. Running `goimports` after `--fix` resolves this.

### TypeScript/Node Projects

Two approaches:

**Option A**: Thin wrapper (recommended for consistency)
```just
# packages/web/justfile
check:
    npm run check

lint:
    npm run lint

test:
    npm test
```

**Option B**: Direct delegation from root (simpler)
```just
# Root justfile - no web module
ts-web-check:
    cd packages/web && npm run check
```

### Rust Projects

```just
check: fmt lint test
    @echo "All checks passed"

fmt:
    cargo fmt --all

lint:
    cargo clippy -- -D warnings

test:
    cargo test
```

## Mise Integration

When a project uses [mise](https://mise.jdx.dev) for tool version management, just recipes should use `mise exec` to ensure pinned versions are used regardless of developer shell setup.

**See the `mise` skill** for full mise setup including shell config, direnv integration, and project `.mise.toml` patterns.

### Shell Override (Recommended)

```just
# All recipes automatically use mise-pinned tools
set shell := ["mise", "exec", "--", "bash", "-c"]

build:
    npm run build

test:
    go test ./...
```

### Graceful Degradation

For repos where mise is optional:

```just
set shell := ["bash", "-c"]

# Use mise if available, otherwise fall back to PATH
_exec cmd:
    #!/usr/bin/env bash
    if command -v mise &>/dev/null; then
        mise exec -- {{cmd}}
    else
        {{cmd}}
    fi

build: (_exec "npm run build")
test: (_exec "go test ./...")
lint: (_exec "golangci-lint run")
```

Note: No `.mise.toml` check needed - mise traverses parent directories automatically.

### Why This Matters

| Developer Setup | Without mise exec | With mise exec |
|-----------------|-------------------|----------------|
| Has direnv + mise | Correct versions | Correct versions |
| Has mise activate | Correct versions | Correct versions |
| Fresh clone, no setup | System tools (wrong version) | Pinned versions |
| CI/CD | Needs activation step | Just works |

**Recommendation**: Use the shell override for team repos. Use graceful degradation for open source where mise adoption varies.

### Caveats

**Failure mode**: Shell override + mise not installed = cryptic "could not find shell" error. Use graceful degradation for open source.

**First clone**: Contributors must run `mise trust` to allow the repo's config:

```just
setup:
    mise trust
    mise install
    @echo "Toolchain ready"
```

---

## Security Auditing

All language templates include consistent audit recipes:

| Recipe | Purpose |
|--------|---------|
| `audit` | Check for vulnerabilities (informational) |
| `audit-fix` | Auto-fix where possible (npm only) |
| `audit-ci` | CI-friendly (strict, production deps only) |

**Language tools:**

| Language | Tool | Installation |
|----------|------|--------------|
| Node/npm | `npm audit` | Built-in |
| Go | `govulncheck` | `go get -tool golang.org/x/vuln/cmd/govulncheck@latest` |
| Rust | `cargo audit` | `cargo install cargo-audit` |

---

## Reference Templates

Load the appropriate template based on project structure:

| Template | Use Case |
|----------|----------|
| `references/simple-repo.just` | Single-package repositories |
| `references/monorepo-root.just` | Monorepo root router |
| `references/package-go.just` | Go package in monorepo |
| `references/package-ts.just` | TypeScript package in monorepo |
| `references/package-rust.just` | Rust package in monorepo |

**Note**: Reference files use `.just` extension for organization. Actual project files must be named `justfile` (no extension) or `.justfile`.

