Makefiles and Taskfiles skill for Codex

One command to list, run and trust every task. Codex picks the right runner for the repo (Make for real file targets, just for readable aliases, Task for cross-platform teams), then writes recipes with pipefail shells, .PHONY targets, sources and generates checks and a built-in task list. Destructive tasks get prompts and environment guards, and CI is pointed at the same entry points developers use.

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- makefiles-taskfiles

Installs to ~/.agents/skills/makefiles-taskfiles/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $makefiles-taskfiles, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Writes and repairs project command runners - GNU Make Makefiles, just justfiles and Taskfile.yml (go-task) - with self-documenting help, strict shells, real file targets, incremental sources/generates checks, safe argument passing and confirmation gates on destructive recipes. Use when a repo needs one entry point for build, test, lint, dev and release commands, when a Makefile has tab, .PHONY or $$ escaping bugs, when choosing between make, just and Task, or when porting npm scripts or shell scripts into a task runner.

Use it when

  • A repo needs a single entry point for build, test, lint and dev commands
  • A Makefile has tab, .PHONY, $$ escaping or pipefail bugs
  • You are choosing between make, just and Task for a team
  • Local commands and CI steps have drifted apart

Use something else when

  • You need cached task graphs across many JS packages: use monorepo-tooling
  • The real problem is slow CI pipelines rather than local commands: use ci-speedup

How to check the result

List tasks with make help, just --list or task --list, run each non-destructive task, deliberately break one command to confirm a non-zero exit, and run file-producing tasks twice to see the second skip.

The mistake it prevents

Leaving command targets out of .PHONY, so a stray file named test or build makes Make silently report the target as up to date.

~/.agents/skills/makefiles-taskfiles/SKILL.md

--- name: makefiles-taskfiles description: Writes and repairs project command runners - GNU Make Makefiles, just justfiles and Taskfile.yml (go-task) - with self-documenting help, strict… license: MIT ---

Done well means: a newcomer (or Codex) runs one command to list every task, each task fails loudly on the first error, tasks that produce files skip work when inputs are unchanged, and nothing destructive runs without a confirmation.

Before writing anything

  1. Read AGENTS.md, README, CONTRIBUTING.md, CI workflow files and package.json scripts. The runner must call the same commands CI calls, or the two drift.
  2. Check what already exists: Makefile, GNUmakefile, justfile/.justfile, Taskfile.yml/Taskfile.yaml. Extend the existing runner; do not add a second one.
  3. Check tool availability: make --version (macOS ships GNU Make 3.81 at /usr/bin/make, which lacks .ONESHELL, --output-sync and $(file ...)), just --version, task --version. Installing just or Task needs network and may need approval in the Codex sandbox; note it rather than assuming it is present.

Pick the runner

SituationDefaultWhy
Builds real files from other files (C, protobuf codegen, docs)GNU MakeTimestamp-based rebuilds are its core strength
Only command aliases (test, lint, dev, deploy), team wants readable syntaxjustNo tabs, no .PHONY, real arguments, just --list built in
Cross-platform team incl. Windows without WSL, YAML is acceptedTask (go-task v3)Single static binary, checksum-based up-to-date checks, includes for monorepos
JS-only repo with a handful of scriptspackage.json scriptsDo not add a runner for five commands
Already has a working MakefileKeep MakeMigration costs more than syntax pain

Make is installed almost everywhere; just and Task are one binary each but still an extra install. In repos used by agents, pick the one whose install is already documented.

GNU Make

Header every Makefile should have

SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.DEFAULT_GOAL := help
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables --no-builtin-rules

.PHONY: help
help: ## List available targets
	@grep -E '^[a-zA-Z0-9_.-]+:.*?## ' $(MAKEFILE_LIST) | \
	  awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[36m%-18s\033[0m %s\n", $$1, $$2}'
  • .SHELLFLAGS with pipefail makes cmd | tee log fail when cmd fails.
  • .DELETE_ON_ERROR removes a half-written target so the next run does not treat it as up to date.
  • Every recipe line starts with a literal tab. Spaces give missing separator.

Command targets vs file targets

.PHONY: test lint fmt dev
test: ## Run unit tests
	go test ./...

lint: ## Static checks
	golangci-lint run

# File target: rebuilt only when a .proto file is newer than the output
gen/api.pb.go: $(wildcard proto/*.proto)
	protoc --go_out=gen proto/*.proto

build: bin/app ## Build the binary
bin/app: $(shell find . -name '*.go' -not -path './vendor/*') go.mod go.sum
	go build -o $@ ./cmd/app
  • Mark every non-file target .PHONY, otherwise a file or folder named test makes make test report “up to date”.
  • Use $@ (target), $< (first prerequisite), $^ (all prerequisites).
  • Shell variables need $$: for f in *.sql; do echo $$f; done.
  • Each recipe line runs in a new shell. cd dir on one line does not affect the next; write cd dir && cmd or use .ONESHELL: (GNU Make 3.82+ only).

Variables and arguments

ENV ?= dev                      # override: make deploy ENV=staging
VERSION := $(shell git describe --tags --always --dirty)
IMAGE := ghcr.io/acme/app:$(VERSION)
  • := evaluates once; = re-evaluates on every use (slow with $(shell ...)).
  • ?= sets a default the caller can override from the command line or environment.
  • Make has no positional arguments. Use VAR=value, never the $(MAKECMDGOALS) hack.

just

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

# List recipes (first recipe is the default)
default:
    @just --list

# Run tests, optionally filtered: just test ./pkg/...
test *args:
    go test {{args}}

# Build and tag an image
image tag=`git rev-parse --short HEAD`:
    docker build -t ghcr.io/acme/app:{{tag}} .

[confirm("Drop and recreate the local database?")]
db-reset:
    dropdb --if-exists app_dev && createdb app_dev && just migrate

[private]
migrate:
    goose -dir migrations postgres "$DATABASE_URL" up
  • Comments directly above a recipe become its just --list description.
  • Recipes run each line in a fresh shell, like Make. For multi-line logic use a shebang recipe (first line #!/usr/bin/env bash plus set -euo pipefail).
  • {{args}} is substituted unquoted. If arguments may contain spaces, add set positional-arguments and use "$@" inside the recipe instead.
  • [confirm] requires a reasonably recent just; check just --version and fall back to an explicit read -p prompt in a shebang recipe on old versions.
  • [group('db')] organises long lists; just --list --unsorted keeps file order.

Task (go-task v3)

version: '3'

dotenv: ['.env']

vars:
  VERSION:
    sh: git describe --tags --always --dirty

tasks:
  default:
    cmds: [task --list]
    silent: true

  test:
    desc: Run unit tests (extra args after --)
    cmds:
      - go test ./... {{.CLI_ARGS}}

  build:
    desc: Build the binary
    sources: ['**/*.go', go.mod, go.sum]
    generates: [bin/app]
    cmds:
      - go build -ldflags "-X main.version={{.VERSION}}" -o bin/app ./cmd/app

  db:reset:
    desc: Drop and recreate the local database
    prompt: This deletes all local data. Continue?
    preconditions:
      - sh: '[ "$APP_ENV" != "production" ]'
        msg: Refusing to reset a production database
    cmds:
      - dropdb --if-exists app_dev
      - createdb app_dev
  • task --list shows only tasks with a desc; task --list-all shows everything.
  • sources/generates use checksums by default (stored in .task/, add it to .gitignore); method: timestamp switches to mtime.
  • {{.CLI_ARGS}} receives everything after --: task test -- -run TestLogin.
  • deps run in parallel; use sequential cmds: [{task: a}, {task: b}] when order matters.
  • includes: pulls in per-package Taskfiles in monorepos (task api:test).
  • --yes auto-confirms prompts. Never put it in CI for destructive tasks.

Hard rules

  • The runner calls the same scripts CI calls. Better still, CI calls the runner (make ci, just ci, task ci) so local and CI cannot diverge.
  • Destructive or outward-facing tasks (deploy, db-reset, release, publish, clean that deletes outside build dirs) need a confirmation gate and an environment guard. When Codex is asked to run one, it asks the user first even if the task has --yes.
  • No secrets in the runner file. Read them from the environment or .env (gitignored).
  • Keep recipes thin: more than ~10 lines of shell belongs in scripts/<name>.sh with set -euo pipefail, called from the runner.
  • Do not shadow real tools: a Make target named docker or go confuses everyone.
  • One naming scheme: verbs (test, lint, build), namespaced with : in Task or - in Make/just (db-migrate).

Gotchas

SymptomCauseFix
missing separator. Stop.Spaces instead of a tabRe-indent recipe lines with tabs; check editorconfig
make test says nothing to doFile/dir named test and no .PHONYAdd .PHONY: test
$f is empty in a loopMake expanded $fEscape as $$f
Pipeline failure ignoredDefault sh -c without pipefailSHELL := bash plus .SHELLFLAGS
Works on Linux, breaks on macOSGNU Make 3.81 or BSD sed -iAvoid .ONESHELL; use sed -i.bak or a script
Task never re-runs buildgenerates missing or wrong globRun task build --force, fix globs, task --status build
just recipe ignores .envdotenv-load not setAdd set dotenv-load

Verification

make -n deploy ENV=staging     # dry run: print commands without executing
make help && make test
just --list && just --dry-run image
task --list && task --dry build && task build && task build   # 2nd run: "up to date"
  • Run each new task once (non-destructive ones) and confirm the exit code is non-zero when you break the underlying command on purpose.
  • For file targets, run twice: the second run must do nothing.
  • Update AGENTS.md with the runner commands so Codex and humans use them.

Output contract

When adding or rewriting a runner, report:

**Runner:** justfile (just 1.x required; install documented in README)
**Tasks:** default (list), test, lint, fmt, dev, build, image, db-reset (confirm)
**CI:** .github/workflows/ci.yml now calls `just ci`
**Checked:** `just --list`, `just test`, `just lint` pass; `just --dry-run image` shown
**Not run:** db-reset, deploy (destructive; need your go-ahead)

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Makefiles and Taskfiles"
  short_description: "One command to list, run and trust every task"
  default_prompt: "$makefiles-taskfiles Add a Makefile with help, test, lint and build targets that match our CI"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem