agentsclimarketplace

Taskfile best practices

Skill CowDogMoo/squad-agents/skills/taskfile-best-practices

Review criteria and anti-patterns for go-task Taskfiles — structure, variable scoping, task design, command execution, dependency ordering, output, and security. Use when auditing or hardening a Taskfile.yaml in any language ecosystem. Also loaded by the taskfile-review agent as its knowledge base.From its SKILL.md

Install
npx -y skills add CowDogMoo/squad-agents --skill taskfile-best-practices

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

13.6 KB, as published. Nobody here has run it

Taskfile Best Practices

A comprehensive guide to writing effective Taskfile.yaml files following community best practices and the Taskfile philosophy. This document serves as the knowledge base for the taskfile-review agent.

Table of Contents

  1. Taskfile Philosophy
  2. File Structure
  3. Variable Management
  4. Task Design
  5. Command Execution
  6. Dependencies and Ordering
  7. Output and Logging
  8. Security Considerations
  9. Error Handling
  10. Includes and Composition
  11. Common Anti-Patterns
  12. Severity Classification

Taskfile Philosophy

Core Principles

PrincipleDescription
SimplicityTasks should be easy to understand at a glance
ReproducibilitySame inputs = same outputs across environments
ComposabilitySmall, focused tasks that combine well
DocumentationEvery task should be self-documenting
IdempotencyRunning a task twice should not break anything

Key Benefits Over Make

  • YAML syntax is more readable than Makefile syntax
  • Native cross-platform support (no shell dependency issues)
  • Built-in variable templating with Go templates
  • Better handling of task dependencies and ordering
  • Schema validation support

File Structure

Standard Layout

---
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: "3"

vars:
  # Global variables at the top
  BINARY_NAME: myapp
  CMD_PATH: ./cmd/myapp

includes:
  # External taskfiles (optional)
  go:
    taskfile: ./build/Taskfile.go.yaml

tasks:
  default:
    desc: Default task (runs when no task specified)
    cmds:
      - task: build

  build:
    desc: Build the application
    cmds:
      - go build -o {{.BINARY_NAME}} {{.CMD_PATH}}

Required Elements

ElementRequiredPurpose
version: "3"YesTaskfile schema version
Schema commentRecommendedEnables IDE validation
vars: sectionIf using variablesDefine all variables at top
desc: on tasksYesDocuments what task does

File Naming

  • Main file: Taskfile.yaml (preferred) or Taskfile.yml
  • Included files: Taskfile.<purpose>.yaml (e.g., Taskfile.go.yaml)
  • Placed in repository root or clearly documented location

Variable Management

Variable Declaration

Good - Variables at top:

vars:
  BINARY_NAME: myapp
  VERSION:
    sh: git describe --tags --always --dirty
  BUILD_FLAGS: -ldflags "-X main.version={{.VERSION}}"

Bad - Hardcoded values in commands:

tasks:
  build:
    cmds:
      - go build -o myapp ./cmd/myapp  # Hardcoded!

Variable Types

TypeSyntaxUse Case
StaticVAR: valueConstants
DynamicVAR: { sh: command }Runtime values
EnvironmentVAR: '{{.ENV_VAR | default "fallback"}}'User overrides

Environment Variable Handling

Good - Default with override:

vars:
  PROVIDER: '{{.PROVIDER | default "local"}}'
  API_KEY: '{{.API_KEY | default ""}}'
  _API_KEY_FLAG: '{{if .API_KEY}}--api-key {{.API_KEY}}{{end}}'

Anti-pattern - Hardcoded secrets:

vars:
  API_KEY: sk-secret-key-hardcoded  # NEVER do this

Conditional Flags

Use underscore-prefixed internal variables for conditional command construction:

vars:
  BASE_URL: '{{.BASE_URL | default ""}}'
  _BASE_URL_FLAG: '{{if .BASE_URL}}--base-url {{.BASE_URL}}{{end}}'

tasks:
  run:
    cmds:
      - myapp {{._BASE_URL_FLAG}} --other-flag

Task Design

Task Anatomy

tasks:
  build:
    desc: "Build the application binary"
    summary: |
      Compiles the Go application with version information embedded.

      The binary is output to ./bin/{{.BINARY_NAME}}.
    vars:
      OUTPUT_DIR: ./bin
    deps:
      - generate
    preconditions:
      - sh: test -f go.mod
        msg: "go.mod not found - run from repository root"
    cmds:
      - mkdir -p {{.OUTPUT_DIR}}
      - go build -o {{.OUTPUT_DIR}}/{{.BINARY_NAME}} {{.CMD_PATH}}
    sources:
      - "**/*.go"
    generates:
      - "{{.OUTPUT_DIR}}/{{.BINARY_NAME}}"

Required Task Properties

PropertyWhen RequiredPurpose
desc:AlwaysOne-line description for task --list
summary:Complex tasksDetailed explanation
preconditions:When assumptions existFail fast with clear message
cmds:AlwaysThe actual commands

Task Naming Conventions

PatternUse CaseExample
verbSimple actionsbuild, test, clean
noun:verbNamespaced actionsdocker:build, go:test
run:nameRunning agents/toolsrun:go-review, run:lint
check:nameValidation taskscheck:fmt, check:lint

Good naming:

tasks:
  build:           # Simple
  test:            # Simple
  docker:build:    # Namespaced
  run:go-review:   # Agent runner
  check:fmt:       # Validation

Bad naming:

tasks:
  BuildApp:        # PascalCase - not idiomatic
  DO_THE_BUILD:    # SCREAMING_CASE - not idiomatic
  b:               # Too abbreviated

Command Execution

Multi-line Commands

Good - Pipe for readability:

tasks:
  build:
    cmds:
      - |
        go build \
          -ldflags "{{.BUILD_FLAGS}}" \
          -o {{.BINARY_NAME}} \
          {{.CMD_PATH}}

Good - Separate commands for clarity:

tasks:
  setup:
    cmds:
      - mkdir -p ./bin
      - go mod download
      - go generate ./...

Command Chaining

OperatorMeaningUse Case
&&Continue if successDependent commands
;Always continueIndependent commands
|Pipe outputData transformation

Good:

cmds:
  - go build ./... && go test ./...  # Test only if build succeeds

Silent Mode

Use silent: true for tasks with intentional output:

tasks:
  run:
    desc: Run the application
    silent: true  # Only show application output
    cmds:
      - ./bin/myapp

Ignore Errors

Use sparingly, only when errors are truly acceptable:

cmds:
  - cmd: rm -rf ./tmp
    ignore_error: true  # OK if directory doesn't exist

Dependencies and Ordering

Task Dependencies

tasks:
  build:
    deps:
      - generate
      - lint
    cmds:
      - go build ./...

  generate:
    cmds:
      - go generate ./...

  lint:
    cmds:
      - golangci-lint run

Dependency Properties

PropertyBehavior
deps:Run in parallel before task
task: X in cmdsRun sequentially as part of task
preconditions:Check before running anything

Sequential vs Parallel

Parallel (default for deps):

deps:
  - lint      # These run in parallel
  - generate

Sequential:

cmds:
  - task: generate  # This runs first
  - task: build     # This waits for generate

Output and Logging

Output Control

SettingEffect
silent: trueSuppress task name prefix
set: [pipefail]Fail on pipe errors
shopt: [globstar]Enable ** glob

Echo Control

tasks:
  verbose:
    cmds:
      - echo "Starting build..."  # Informational
      - go build ./...
      - echo "Build complete."

  quiet:
    silent: true
    cmds:
      - go build ./...  # No extra output

Progress Indicators

tasks:
  build:
    cmds:
      - echo "Building {{.BINARY_NAME}}..."
      - go build -o {{.BINARY_NAME}} {{.CMD_PATH}}
      - echo "Build complete: {{.BINARY_NAME}}"

Security Considerations

Secret Handling

Good - Environment variable with no default:

vars:
  API_KEY: '{{.API_KEY | default ""}}'

tasks:
  deploy:
    preconditions:
      - sh: test -n "{{.API_KEY}}"
        msg: "API_KEY environment variable is required"
    cmds:
      - deploy --api-key {{.API_KEY}}

Good - External secret tool:

vars:
  API_KEY:
    sh: op item get "MyService" --fields password 2>/dev/null || echo ""

Bad - Hardcoded secrets:

vars:
  API_KEY: sk-abc123secretkey  # CRITICAL: Never commit secrets

Input Validation

tasks:
  run:
    vars:
      PROMPT: '{{.PROMPT | default ""}}'
    preconditions:
      - sh: test -n "{{.PROMPT}}"
        msg: "PROMPT is required. Usage: task run PROMPT='...'"
    cmds:
      - echo "Running with prompt: {{.PROMPT}}"

Path Safety

Good - Use variables for paths:

vars:
  OUTPUT_DIR: ./bin

tasks:
  clean:
    cmds:
      - rm -rf {{.OUTPUT_DIR}}  # Safe - controlled path

Dangerous - User-controlled paths:

tasks:
  clean:
    cmds:
      - rm -rf {{.USER_PATH}}  # DANGEROUS if USER_PATH is "../.."

Error Handling

Preconditions

tasks:
  build:
    preconditions:
      - sh: command -v go >/dev/null
        msg: "Go is not installed"
      - sh: test -f go.mod
        msg: "go.mod not found - run from repository root"
      - sh: test -d ./cmd
        msg: "cmd/ directory not found"

Status Checks

Use status: for idempotent tasks:

tasks:
  generate:
    cmds:
      - go generate ./...
    sources:
      - "**/*.go"
    generates:
      - "**/mock_*.go"
    status:
      - test -f ./internal/mock_service.go

Fail Fast

Default behavior is fail-fast. Override only when necessary:

tasks:
  cleanup:
    cmds:
      - cmd: rm -rf ./tmp
        ignore_error: true
      - cmd: docker rm -f test-container
        ignore_error: true

Includes and Composition

External Taskfiles

includes:
  go:
    taskfile: ./build/Taskfile.go.yaml
    vars:
      BINARY_NAME: myapp
  docker:
    taskfile: https://example.com/Taskfile.docker.yaml

Remote Includes

includes:
  go:
    taskfile: "https://raw.githubusercontent.com/org/taskfiles/main/go/Taskfile.yaml"
    vars:
      BINARY_NAME: myapp
      CMD_PATH: ./cmd/myapp

Variable Passing

includes:
  go:
    taskfile: ./Taskfile.go.yaml
    vars:
      # Pass parent vars to included taskfile
      BINARY_NAME: '{{.BINARY_NAME}}'
      VERSION: '{{.VERSION}}'

Common Anti-Patterns

CRITICAL

Anti-PatternIssueFix
Hardcoded secretsSecurity breach riskUse environment variables
No version fieldSchema undefinedAdd version: "3"
Unquoted template varsYAML parsing errorsQuote: '{{.VAR}}'

HIGH

Anti-PatternIssueFix
Missing desc:Unusable with task --listAdd description
Hardcoded pathsNot portableUse variables
Missing preconditionsConfusing failuresAdd input validation
Complex inline scriptsHard to maintainExtract to shell script

MEDIUM

Anti-PatternIssueFix
Inconsistent namingHard to discover tasksUse conventions
Duplicate commandsMaintenance burdenExtract to shared task
No silent: on runnersNoisy outputAdd silent: true
Missing summary:Complex tasks undocumentedAdd detailed summary

LOW

Anti-PatternIssueFix
No schema commentNo IDE validationAdd schema comment
Unused variablesClutterRemove them
Overly long commandsHard to readUse multi-line or script

Severity Classification

CRITICAL

Issues that affect security or cause immediate failures:

  • Hardcoded secrets or credentials
  • Missing version: field
  • Syntax errors in YAML or templates
  • Unvalidated user input in dangerous commands

HIGH

Issues that affect reliability or maintainability:

  • Missing desc: on tasks
  • Hardcoded values that should be variables
  • Missing preconditions for required inputs
  • Commands that fail silently

MEDIUM

Best practice violations:

  • Inconsistent task naming
  • Missing summary: on complex tasks
  • Duplicate command patterns
  • No silent: on runner tasks

LOW

Minor improvements:

  • Missing schema comment
  • Overly long single-line commands
  • Unused variables
  • Style inconsistencies

INFO

Suggestions for optimization:

  • Using sources: and generates: for caching
  • Parallel task execution opportunities
  • Remote include consolidation

Quick Reference Checklist

Before Committing

  • version: "3" present
  • Schema comment at top
  • All tasks have desc:
  • No hardcoded secrets
  • Variables for all paths/values
  • Preconditions for required inputs
  • Consistent task naming
  • Complex commands documented

Common Commands

# List all tasks
task --list

# Run with variable override
task build VERSION=1.0.0

# Dry run
task --dry

# Force run (ignore status)
task --force build

References


Last updated: 2026-02-04

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.