plugins/language-pro/skills/just-pro/SKILL.md
Patterns for setting up just (command runner) in projects. Use when creating build systems, setting up new repos, organizing build/test/lint recipes, or when the user asks about just/justfile configuration. Covers both simple single-project repos and monorepos with hierarchical justfile modules, mise integration, and conventional recipe naming (check, build, test, lint, fmt).
npx skillsauth add rbergman/dark-matter-marketplace just-proInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build system configuration using just, a modern command runner.
Related skills:
# 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
| 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 |
For single-language projects, create one justfile at repo root.
# 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.
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):
# 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.
Always provide a single check recipe that runs all quality gates:
# Full quality gates
check: fmt lint test coverage-check
@echo "All checks passed"
In single-package repos, just check can run in a pre-commit hook. In monorepos, it's too slow for pre-commit — use lint-staged in the hook and run just check manually or in CI.
The dm-work Stop hook (run-gates-on-stop.sh) runs quality gates after every turn. It auto-detects: just check-fast → just check → npm run check, in that order. If just check is slow (>10s), add a check-fast recipe that drops the slowest steps — no additional hook configuration needed.
Profile first to find what's slow — usually production builds and coverage, not tests:
# Full gate — pre-commit, CI
check: fmt lint test coverage-check build
@echo "All checks passed"
# Fast gate — Stop hook, iterative dev (drop slow build + coverage)
check-fast: fmt lint test
@echo "Fast checks passed"
Tips: Add --cache to ESLint for repeat-run speedup (~6s to ~1s). Add .eslintcache to .gitignore.
Standard cleanup for build artifacts and caches:
# Remove build artifacts and caches
clean:
rm -rf build/ coverage.out node_modules/.cache
just doesn't have native parallel deps. Use shell backgrounding for monorepos:
# Parallel check across packages
check:
#!/usr/bin/env bash
set -uo pipefail
just api check & PID1=$!
just web check & PID2=$!
just mcp check & PID3=$!
FAIL=0
wait $PID1 || FAIL=1
wait $PID2 || FAIL=1
wait $PID3 || FAIL=1
[ $FAIL -eq 0 ] || { echo "Checks failed"; exit 1; }
echo "All checks passed"
Note: use set -uo pipefail (not -euo) — with -e, a failed wait exits before checking the others.
Use shebang for multi-line shell logic:
# 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).
# Root justfile calling into modules
check: (go::check) (web::check)
@echo "All checks passed"
For packages that may not exist in all environments:
mod? optional-package "packages/optional"
Every recipe should have a doc comment (shown in just --list):
# Run unit tests with race detection
test:
go test -race ./...
Module doc comments - Comments above mod statements also appear in just --list:
# 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.
mod <name> "<path>" # Required module
mod? <name> "<path>" # Optional module (no error if missing)
mod <name> # Module at ./<name>/justfile
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.
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.
just go check # From command line
(go::check) # As dependency in justfile
# 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.
Always export node_modules/.bin onto PATH so recipes can call tools directly without npx (slow) or npm run (loses justfile's value as a unified interface):
export PATH := "./node_modules/.bin:" + env_var("PATH")
With this, recipes call tsc, eslint, vitest etc. directly. Without it, every recipe either needs npx (adds startup overhead) or delegates to npm run (making just a thin pass-through).
Two approaches:
Option A: Direct invocation (recommended — self-contained recipes)
export PATH := "./node_modules/.bin:" + env_var("PATH")
check: typecheck lint test
@echo "All checks passed"
typecheck:
tsc --noEmit
lint:
eslint .
test:
vitest run
Option B: Thin wrapper (simpler, delegates to package.json scripts)
# packages/web/justfile
check:
npm run check
lint:
npm run lint
test:
npm test
check: fmt lint test
@echo "All checks passed"
fmt:
cargo fmt --all
lint:
cargo clippy -- -D warnings
test:
cargo test
When a project uses mise 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.
# All recipes automatically use mise-pinned tools
set shell := ["mise", "exec", "--", "bash", "-c"]
build:
npm run build
test:
go test ./...
For repos where mise is optional:
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.
| 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.
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:
setup:
mise trust
mise install
@echo "Toolchain ready"
When RTK is installed, justfile recipes can use it as a binary wrapper to get compressed output in CI, shell scripts, and non-Claude-Code contexts — not just inside Claude Code's PreToolUse hook.
The pattern: try RTK first, fall back to the raw command if RTK isn't available:
# Token-efficient typecheck — compressed output when rtk is available
typecheck:
#!/usr/bin/env bash
if command -v rtk &>/dev/null; then
rtk tsc --noEmit
else
tsc --noEmit
fi
# Token-efficient test run
test:
#!/usr/bin/env bash
if command -v rtk &>/dev/null; then
rtk vitest run
else
vitest run
fi
This works because rtk <command> acts as a transparent wrapper — it runs the command and filters the output through RTK's compression. The PreToolUse hook only fires inside Claude Code; the wrapper pattern gives you compression everywhere.
See output-compression skill for RTK setup and gotchas.
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 |
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.
development
Initialize a new repository with standard scaffolding - git, gitignore, AGENTS.md, justfile, mise, beads, and timbers. Use when starting a new project or setting up an existing repo for Claude Code workflows.
data-ai
Activate at session start when using Agent Teams for complex multi-agent work. Establishes team lead role with delegation protocols, teammate spawning, model selection, and beads integration. You coordinate the team; teammates implement.
data-ai
Use when creating a worktree, setting up a worktree, starting feature work that needs isolation, or before executing implementation plans. Covers git worktree creation under .worktrees/, gitignore setup, beads integration, and merge guardrails.
data-ai
Activate when you are a delegated subagent (not the orchestrator). Establishes subagent protocol with terse returns, details to history/, file ownership boundaries, and escalation rules. You implement; orchestrator reviews and commits.