Task Runners skill for Claude Code

One command menu for every repo, same in CI. Claude inventories the commands scattered across README, CI and scripts, picks one runner (npm scripts, just, Taskfile or Make), and maps them to standard names like setup, dev, test and check. Tasks fail fast under strict shell settings, list themselves with descriptions, confirm before deploying, and CI calls the very same check task developers run locally.

  • nametask-runners
  • size224 lines · ≈1,800 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Sets up project task automation with Make, just, Taskfile (go-task), or npm scripts — a self-documenting command menu, consistent names like setup, dev, test, lint, and build, strict shell settings, dependencies between tasks, incremental builds with sources and generates, dotenv loading, confirmation on dangerous tasks, and the same commands in CI and locally. Use when creating or cleaning up a Makefile, justfile, Taskfile.yml, or package.json scripts, standardizing developer commands, onboarding onto a repo's tasks, or making CI call the same tasks developers run.

Try asking

  • Add a justfile to this repo with setup, dev, test, lint and a CI check task
  • Our Makefile has 40 targets nobody understands, make it self-documenting and fix the phony targets
  • CI runs different commands than we do locally, unify them with a Taskfile

Use it when

  • A repo needs a standard set of developer commands
  • A Makefile, justfile or Taskfile needs cleanup
  • CI and local commands have drifted apart
  • New contributors cannot find how to run things

Skip it when

How to check the result

On a fresh clone, running the runner with no arguments lists described tasks, setup then check succeeds, and a deliberately broken test makes check exit non-zero.

The common mistake it prevents

Forgetting .PHONY in a Makefile, so a file or folder named test or build silently turns the task into a no-op.

~/.claude/skills/task-runners/SKILL.md

--- name: task-runners description: Sets up project task automation with Make, just, Taskfile (go-task), or npm scripts — a self-documenting command menu, consistent names… license: MIT ---

Done well means: a new contributor runs one command to list tasks and one to set up, task names are predictable across repos, each task fails on the first error, CI calls the same tasks as developers, and dangerous tasks ask for confirmation.

Hard rules

  • One task runner per repo as the front door. Language tools (npm, cargo, uv) do the work; the runner only names and sequences them.
  • Every task fails fast: strict shell flags, no || true without a comment.
  • Running the runner with no arguments lists the tasks with descriptions.
  • Deploy, publish, migrate-prod, and data-deletion tasks require confirmation (or an explicit CONFIRM=1/--yes in CI) and print the target before acting.
  • Secrets come from the environment or an untracked .env; never hardcode them in the task file. Commit .env.example, not .env.

Pick the runner

SituationDefaultWhy
JS/TS-only reponpm scripts (package.json)Already there; npm run lists them
Polyglot repo, commands not filesjustSimple syntax, args, --list, no tab/.PHONY traps
Need incremental builds, cross-platform, YAMLTaskfile (go-task v3)sources/generates checksums, deps in parallel, Windows-friendly
Already has a Makefile, or C/C++ buildsMakeUbiquitous, real file-based dependency graph
Tool versions + tasks in one filemise tasksIf the repo already uses mise for runtimes

Standard names (use these so muscle memory transfers): setup, dev, test, lint, fmt, typecheck, build, check (everything CI runs), clean, deploy.

Workflow

- [ ] 1. Inventory existing commands (README, CI config, scripts/, package.json)
- [ ] 2. Choose the runner; map commands to standard names
- [ ] 3. Write tasks with descriptions, strict shell, dependencies
- [ ] 4. Add a `check` task that mirrors CI exactly
- [ ] 5. Point CI at the tasks; update README to "run `just` to see tasks"
- [ ] 6. Verify on a clean clone

just

# justfile
set dotenv-load
set shell := ["bash", "-euo", "pipefail", "-c"]

# list tasks
default:
    @just --list --unsorted

# install toolchains and dependencies
setup:
    uv sync --frozen
    pnpm install --frozen-lockfile

# run the app with reload
[group('dev')]
dev:
    uv run uvicorn app.main:app --reload

# run tests; pass extra args: just test -k auth
[group('qa')]
test *args:
    uv run pytest {{args}}

[group('qa')]
lint:
    uv run ruff check .
    uv run ruff format --check .

# everything CI runs
check: lint test

# deploy to an environment (default staging)
[confirm('Deploy to the target environment?')]
deploy env='staging':
    @echo "Deploying to {{env}}"
    ./scripts/deploy.sh {{env}}
  • Comments directly above a recipe become its --list description.
  • Recipe lines run in separate shells unless the recipe starts with a shebang (#!/usr/bin/env bash plus set -euo pipefail) for multi-line logic.
  • just --dry-run deploy, just --evaluate, just --fmt --unstable (format check varies by version; run just --help if a flag is rejected).

Taskfile (go-task v3)

# Taskfile.yml
version: '3'
dotenv: ['.env']
vars:
  BIN: ./bin/app
tasks:
  default:
    cmds: [task --list]
    silent: true
  build:
    desc: Compile the binary (skipped when sources unchanged)
    sources: ['**/*.go', go.mod, go.sum]
    generates: ['{{.BIN}}']
    cmds:
      - go build -o {{.BIN}} ./cmd/app
  test:
    desc: Run tests; extra args after --, e.g. task test -- -run TestAuth
    cmds:
      - go test ./... {{.CLI_ARGS}}
  lint:
    desc: Static checks
    cmds: [golangci-lint run]
  check:
    desc: Everything CI runs
    deps: [lint, test]          # deps run in parallel
  deploy:
    desc: Deploy to ENV (default staging)
    prompt: Deploy {{.ENV | default "staging"}}?
    preconditions:
      - sh: git diff --quiet
        msg: Working tree is dirty; commit first
    cmds:
      - ./scripts/deploy.sh {{.ENV | default "staging"}}

task --list shows only tasks with desc; task --list-all shows all. Use status: for “already done” checks that are not file-based (e.g., a container exists). Pass --yes to skip prompts in CI.

Make (self-documenting)

SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.DEFAULT_GOAL := help
MAKEFLAGS += --warn-undefined-variables --no-builtin-rules
.PHONY: help setup test lint check deploy

help: ## List tasks
	@grep -hE '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf "  \033[36m%-12s\033[0m %s\n", $$1, $$2}'

setup: ## Install dependencies
	npm ci

test: ## Run tests
	npm test

lint: ## Run linters
	npm run lint

check: lint test ## Everything CI runs

deploy: ## Deploy (requires CONFIRM=1)
	@test "$${CONFIRM:-}" = 1 || { echo "Refusing: run 'make deploy CONFIRM=1'"; exit 1; }
	./scripts/deploy.sh
  • Recipe lines must start with a tab. Each line is a separate shell unless .ONESHELL:.
  • Mark non-file targets .PHONY, or a file named test will make make test a no-op.
  • $$ escapes $ for the shell. macOS ships GNU Make 3.81, which ignores .SHELLFLAGS and lacks other 3.82+/4.x features; require Homebrew gmake or put set -euo pipefail in multi-line scripts instead.
  • Use real file targets for build artifacts so Make skips up-to-date work.

npm scripts

{
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "test": "vitest run",
    "lint": "eslint . && prettier --check .",
    "typecheck": "tsc --noEmit",
    "check": "npm run lint && npm run typecheck && npm run test"
  }
}
  • Pass args after --: npm test -- --watch. node --run <script> (Node 22+) starts faster than npm run but skips pre/post hooks.
  • Avoid implicit pre*/post* hooks for important steps; they surprise people, and package managers differ in whether they run them.
  • Cross-platform: avoid rm -rf and VAR=x cmd in scripts if Windows contributors exist; use rimraf/cross-env or move logic to a Node script.
  • Parallel: concurrently or npm-run-all2 (run-p, run-s).

CI uses the same tasks

# GitHub Actions excerpt
- uses: extractions/setup-just@v3     # or arduino/setup-task, or npm ci
- run: just check

If CI runs commands the task file does not have, developers cannot reproduce CI failures locally. Move them into check.

Verification

git clone <repo> /tmp/fresh && cd /tmp/fresh
just            # or: task / make / npm run  -> lists tasks with descriptions
just setup && just check; echo "exit=$?"
just deploy     # prompts (or refuses) before doing anything

Break a test on purpose: check must exit non-zero, and so must CI.

Gotchas

  • A failing command in the middle of a multi-command line (a; b) is ignored without strict flags; use && or strict shell settings.
  • Tasks that cd affect only that line in Make and just (each line is a new shell).
  • Hidden tasks nobody can discover get reimplemented; every public task needs a description.
  • Do not wrap a single command in five layers (npm script -> make -> shell script); one level of indirection is enough.

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc