---
name: aif-build-automation
description: >-
  Analyze project and generate or enhance build automation file (Makefile, Taskfile.yml, Justfile, Magefile.go).
  If a build file already exists, improves it by adding missing targets and best practices.
  Use when user says "generate makefile", "create taskfile", "add justfile", "setup mage", or "build automation".
argument-hint: "[makefile|taskfile|justfile|mage]"
allowed-tools: Read Edit Glob Grep Write Bash(git *) AskUserQuestion Questions
disable-model-invocation: false
metadata:
  author: AI Factory
  version: "1.0"
  category: build-automation
---

# Build Automation Generator

Generate or enhance a build automation file for any project. Supports Makefile, Taskfile.yml, Justfile, and Magefile.go.

**Two modes:**
- **Generate** — No build file exists → create one from scratch using best-practice templates
- **Enhance** — Build file already exists → analyze gaps, add missing targets, fix anti-patterns, preserve existing work

---

## Step 0: Load Project Context

Read the project description if available:

```
Read .ai-factory/DESCRIPTION.md
```

Store the project context (tech stack, framework, architecture) for use in later steps. If the file doesn't exist, that's fine — we'll detect everything in Step 2.

**Read `.ai-factory/skill-context/aif-build-automation/SKILL.md`** — MANDATORY if the file exists.

This file contains project-specific rules accumulated by `/aif-evolve` from patches,
codebase conventions, and tech-stack analysis. These rules are tailored to the current project.

**How to apply skill-context rules:**
- Treat them as **project-level overrides** for this skill's general instructions
- When a skill-context rule conflicts with a general rule written in this SKILL.md,
  **the skill-context rule wins** (more specific context takes priority — same principle as nested CLAUDE.md files)
- When there is no conflict, apply both: general rules from SKILL.md + project rules from skill-context
- Do NOT ignore skill-context rules even if they seem to contradict this skill's defaults —
  they exist because the project's experience proved the default insufficient
- **CRITICAL:** skill-context rules apply to ALL outputs of this skill — including the generated
  build files (Makefile, Taskfile, justfile, magefile). Templates in this skill are **base structures**.
  If a skill-context rule says "build file MUST include target X" or "MUST follow convention Y" —
  you MUST comply. Generating build automation that violates skill-context rules is a bug.

**Enforcement:** After generating any output artifact, verify it against all skill-context rules.
If any rule is violated — fix the output before presenting it to the user.

---

## Step 1: Detect Existing Build Files & Determine Mode

### 1.1 Scan for Existing Build Files

Before anything else, check if the project already has build automation:

```
Glob: Makefile, makefile, GNUmakefile, Taskfile.yml, Taskfile.yaml, taskfile.yml, justfile, Justfile, .justfile, magefile.go, magefiles/*.go
```

Build a list of `EXISTING_FILES` from the results.

### 1.2 Determine Mode

**Mode A — Enhance Existing** (if `EXISTING_FILES` is not empty):

- Set `MODE = "enhance"`
- Set `TARGET_TOOL` automatically from the detected file (Makefile → `makefile`, Taskfile.yml → `taskfile`, etc.)
- If multiple build files exist AND `$ARGUMENTS` specifies one, use the argument to pick which one to enhance
- If multiple build files exist AND no argument, ask which one to enhance:

```
AskUserQuestion: This project has multiple build files. Which one should I improve?

Options (dynamic, based on what exists):
1. Makefile — Enhance the existing Makefile
2. Taskfile.yml — Enhance the existing Taskfile
...
```

- Read the existing file content — this is the baseline for enhancement
- Store as `EXISTING_CONTENT`

**Mode B — Generate New** (if `EXISTING_FILES` is empty):

- Set `MODE = "generate"`
- Parse `$ARGUMENTS` to determine tool:

| Argument | Tool | Output File |
|----------|------|-------------|
| `makefile` or `make` | GNU Make | `Makefile` |
| `taskfile` or `task` | Taskfile | `Taskfile.yml` |
| `justfile` or `just` | Just | `justfile` |
| `mage` or `magefile` | Mage | `magefile.go` |

- If `$ARGUMENTS` is empty or doesn't match, ask the user interactively:

```
AskUserQuestion: Which build automation tool do you want to generate?

Options:
1. Makefile — GNU Make (universal, no install needed)
2. Taskfile.yml — Task runner (YAML, modern, cross-platform)
3. justfile — Just command runner (simple, fast, ergonomic)
4. magefile.go — Mage (Go-native, type-safe, no shell scripts)
```

Store the chosen tool as `TARGET_TOOL`.

---

## Step 2: Analyze Project

Detect the project profile by scanning the repository. Run these checks using `Glob` and `Grep`:

### 2.1 Primary Language

Check for these files (first match wins):

| File | Language |
|------|----------|
| `go.mod` | Go |
| `package.json` | Node.js / JavaScript / TypeScript |
| `pyproject.toml` or `setup.py` or `setup.cfg` | Python |
| `Cargo.toml` | Rust |
| `composer.json` | PHP |
| `Gemfile` | Ruby |
| `build.gradle` or `pom.xml` | Java/Kotlin |
| `*.csproj` or `*.sln` | C# / .NET |

### 2.2 Package Manager

Check lock files:

| File | Package Manager |
|------|-----------------|
| `bun.lockb` | bun |
| `pnpm-lock.yaml` | pnpm |
| `yarn.lock` | yarn |
| `package-lock.json` | npm |
| `poetry.lock` | poetry |
| `uv.lock` | uv |
| `Pipfile.lock` | pipenv |

### 2.3 Framework Detection

For Node.js projects, check `package.json` dependencies for:
- `next` → Next.js
- `nuxt` → Nuxt
- `@remix-run/node` → Remix
- `express` → Express
- `fastify` → Fastify
- `hono` → Hono
- `@nestjs/core` → NestJS

For Python projects, check `pyproject.toml` or imports for:
- `fastapi` → FastAPI
- `django` → Django
- `flask` → Flask

For PHP projects, check `composer.json` require for:
- `laravel/framework` → Laravel
- `symfony/framework-bundle` → Symfony
- `slim/slim` → Slim
- `cakephp/cakephp` → CakePHP

For Go projects, check `go.mod` for:
- `gin-gonic/gin` → Gin
- `labstack/echo` → Echo
- `gofiber/fiber` → Fiber
- `go-chi/chi` → Chi

### 2.4 Docker (Deep Scan)

```
Glob: Dockerfile, Dockerfile.*, docker-compose.yml, docker-compose.yaml, compose.yml, compose.yaml, .dockerignore
```

If any exist, set `HAS_DOCKER=true` and perform a deeper analysis:

**Read the Dockerfile(s)** to detect:
- Multi-stage builds (separate `dev` / `prod` stages) → `DOCKER_MULTISTAGE=true`
- Exposed ports → `DOCKER_PORTS` (e.g., `3000`, `8080`)
- Base image → `DOCKER_BASE` (e.g., `node:20-alpine`, `golang:1.22`)
- Entrypoint/CMD → understand how the app is started inside the container

**Read docker-compose / compose file** to detect:
- Service names → `DOCKER_SERVICES` (e.g., `app`, `db`, `redis`, `worker`)
- Volume mounts → understand dev vs prod setup
- Profiles (if any) → `dev`, `production`, `test`
- Dependency services (postgres, redis, rabbitmq, etc.) → `DOCKER_DEPS`

Store as `DOCKER_PROFILE`:
- `has_compose`: boolean
- `has_multistage`: boolean
- `services`: list of service names
- `deps`: list of infrastructure services (db, cache, queue)
- `ports`: exposed ports
- `has_dev_stage`: boolean (Dockerfile has a `dev` or `development` stage)

### 2.5 CI/CD

```
Glob: .github/workflows/*.yml, .gitlab-ci.yml, .circleci/config.yml, Jenkinsfile, .travis.yml
```

Note which CI system is in use.

### 2.6 Database & Migrations

Search for migration tools:

```
Grep: prisma|drizzle|knex|typeorm|sequelize|alembic|django.*migrate|goose|migrate|atlas|sqlx
```

Check for:
- `prisma/schema.prisma` → Prisma
- `drizzle.config.ts` → Drizzle
- `alembic/` directory → Alembic
- `migrations/` directory → Generic migrations

### 2.7 Test Framework

| Language | Check For |
|----------|-----------|
| Node.js | `jest`, `vitest`, `mocha`, `ava` in package.json |
| Python | `pytest` in pyproject.toml/requirements, `unittest` imports |
| Go | Go has built-in testing; check for `testify` in go.mod |
| Rust | Built-in; check for integration test directory `tests/` |

### 2.8 Linters & Formatters

```
Glob: .eslintrc*, eslint.config.*, .prettierrc*, biome.json, .golangci.yml, .golangci.yaml
Grep in pyproject.toml: ruff|black|flake8|pylint|isort
```

### 2.9 Monorepo Detection

```
Glob: turbo.json, nx.json, lerna.json, pnpm-workspace.yaml
```

### Summary

Build a `PROJECT_PROFILE` object with:
- `language`: primary language
- `package_manager`: detected PM
- `framework`: detected framework (if any)
- `has_docker`: boolean
- `docker_profile`: `DOCKER_PROFILE` object (if `has_docker`)
- `ci_system`: detected CI (if any)
- `has_migrations`: boolean + tool name
- `test_framework`: detected test runner
- `linters`: list of detected linters
- `is_monorepo`: boolean
- `has_dev_server`: boolean (framework with dev server)

---

## Step 3: Read Best Practices

Read the best practices reference for the chosen tool:

```
Read skills/build-automation/references/BEST-PRACTICES.md
```

Focus on the section matching `TARGET_TOOL`:
- Makefile → Section 1
- Taskfile → Section 2
- Justfile → Section 3
- Magefile → Section 4

Also read the "Cross-Cutting Concerns" section for standard targets.

---

## Step 4: Select & Read Template

Pick the closest matching template based on `language` + `TARGET_TOOL`:

| Tool | Go | Node.js | Python | PHP | Other |
|------|----|---------|--------|-----|-------|
| Makefile | `makefile-go.mk` | `makefile-node.mk` | `makefile-python.mk` | `makefile-php.mk` | Use closest match |
| Taskfile | `taskfile-go.yml` | `taskfile-node.yml` | `taskfile-python.yml` | `taskfile-php.yml` | Use closest match |
| Justfile | `justfile-go` | `justfile-node` | `justfile-python` | `justfile-php` | Use closest match |
| Magefile | `magefile-basic.go` | `magefile-full.go` | `magefile-full.go` | N/A (use Makefile) | `magefile-basic.go` |

For Magefile: use `magefile-full.go` if `HAS_DOCKER` or `has_migrations` is true, otherwise `magefile-basic.go`.

For PHP + Magefile: Mage is Go-specific and not applicable to PHP projects. If the user explicitly requested `mage` for a PHP project, explain this and suggest Makefile as the closest alternative (universal, no install needed). Ask via `AskUserQuestion` whether to proceed with Makefile instead.

Read the selected template:

```
Read skills/build-automation/templates/<selected-template>
```

---

## Step 5: Generate or Enhance File

### Mode B — Generate New File

Using the `PROJECT_PROFILE`, best practices, and template as reference, generate a customized build file from scratch.

#### Generation Rules

1. **Start with the tool's required preamble** (from best practices)
2. **Include all standard targets**: help/default, build, test, lint, clean, dev, fmt
3. **Add conditional targets** based on project profile:
   - Docker targets → only if `has_docker`
   - Database targets → only if `has_migrations` (use correct migration tool)
   - Deploy targets → only if CI/CD detected
   - Generate target → only if code generation detected
   - Typecheck target → only if TypeScript or mypy detected
4. **Use correct package manager** commands (not hardcoded npm/pip/go)
5. **Include CI aggregate target** that runs lint + test + build
6. **Follow the template's structure** for organization and grouping
7. **Adapt variable names** to match the actual project (module name, binary name, source dirs)
8. **Include version/commit/build-time** detection via git
9. **Docker-aware targets** — if `has_docker`, generate a dedicated Docker section (see below)

#### Docker-Aware Target Generation

When `has_docker` is true, generate **two layers** of commands:

**Layer 1 — Container lifecycle** (always when Docker detected):

| Target | Purpose |
|--------|---------|
| `docker-build` or `docker:build` | Build the Docker image |
| `docker-run` or `docker:run` | Run the container |
| `docker-stop` or `docker:stop` | Stop running containers |
| `docker-logs` or `docker:logs` | Tail container logs |
| `docker-push` or `docker:push` | Push image to registry |
| `docker-clean` or `docker:clean` | Remove images and stopped containers |

**Layer 2 — Dev vs Production separation** (when compose or multistage detected):

```
##@ Docker — Development
docker-dev:          ## Start all services in dev mode (with hot reload, mounted volumes)
docker-dev-build:    ## Rebuild dev containers
docker-dev-down:     ## Stop dev environment and remove volumes

##@ Docker — Production
docker-prod-build:   ## Build production image (optimized, multi-stage)
docker-prod-run:     ## Run production container locally for testing
docker-prod-push:    ## Push production image to registry
```

**Generation logic:**

- If `has_compose` → use `docker compose` commands (not `docker-compose`)
- If compose has profiles → use `--profile dev` / `--profile production`
- If `has_multistage` → use `--target dev` for dev builds, no target (or `--target production`) for prod
- If `docker_profile.deps` exist (db, redis, etc.) → add `infra-up` / `infra-down` targets to start/stop only infrastructure services without the app
- If compose detected → `docker-dev` should run `docker compose up` with correct profile/services
- If no compose but Dockerfile → `docker-dev` should run `docker build --target dev` + `docker run` with volume mounts

**Layer 3 — Container-based commands** (mirror host commands via container):

When the project is Docker-based, also generate container-exec variants so that users who run everything in Docker can use the same targets:

```
# Run tests inside the container
docker-test:         ## Run tests inside the Docker container
  docker compose exec app [test command]

# Run linter inside the container
docker-lint:         ## Run linter inside the Docker container
  docker compose exec app [lint command]

# Open shell in the container
docker-shell:        ## Open a shell inside the running container
  docker compose exec app sh
```

Only generate `docker-*` exec variants if the project appears to be Docker-first (compose file mounts source code as volumes, or no local language runtime setup is apparent).

#### Customization from Project Profile

- **Binary name**: Use the actual project name from `go.mod`, `package.json`, or directory name
- **Source directory**: Use actual src dir (e.g., `src/`, `app/`, `cmd/`)
- **Dev server command**: Match the framework's dev server (e.g., `next dev`, `uvicorn --reload`, `air`)
- **Test command**: Match the detected test runner
- **Lint command**: Match the detected linters
- **Migration commands**: Match the detected migration tool exactly
- **Port numbers**: Use framework defaults (3000 for Node, 8000 for Python, 8080 for Go)

### Mode A — Enhance Existing File

When `MODE = "enhance"`, do NOT replace the file from scratch. Instead, analyze it and improve it surgically.

#### 5A.1 Analyze Existing File

Compare `EXISTING_CONTENT` against the `PROJECT_PROFILE` and best practices. Build a gap analysis:

**Missing preamble/config** — Check if the file has the recommended preamble:
- Makefile: `SHELL := bash`, `.ONESHELL`, `.SHELLFLAGS`, `.DELETE_ON_ERROR`, `MAKEFLAGS`
- Taskfile: `version: '3'`, `output:`, `dotenv:`
- Justfile: `set shell`, `set dotenv-load`, `set export`
- Magefile: `//go:build mage`, proper imports

**Missing standard targets** — Check which of these are absent:
- `help` / `default` (self-documenting)
- `build`, `test`, `lint`, `clean`, `dev`, `fmt`
- `ci` (aggregate target)

**Missing project-specific targets** — Based on `PROJECT_PROFILE`, check for:
- Docker targets (if `has_docker` but no docker targets in file)
- Database/migration targets (if `has_migrations` but no db targets)
- Typecheck target (if TypeScript/mypy detected but no typecheck target)
- Generate target (if code generation tools detected)
- Coverage target (if test target exists but no coverage variant)

**Quality issues** — Check for anti-patterns from best practices:
- Targets without descriptions/documentation
- Missing `.PHONY` declarations (Makefile)
- Hardcoded tool paths that should be variables
- Missing version/commit detection
- No self-documenting help target

#### 5A.2 Plan Changes

Build a list of specific changes to make:

```
CHANGES = [
  { type: "add_preamble", detail: "Add .SHELLFLAGS and .DELETE_ON_ERROR" },
  { type: "add_target", name: "docker-build", detail: "Dockerfile detected but no docker target" },
  { type: "add_target", name: "help", detail: "No self-documenting help target" },
  { type: "fix_quality", detail: "Add ## comments to 3 targets missing descriptions" },
  { type: "add_variable", detail: "Add VERSION/COMMIT detection via git" },
  ...
]
```

#### 5A.3 Apply Changes

- **Preserve the existing structure** — Keep the user's ordering, naming, and style
- **Preserve existing targets exactly** — Do NOT modify working targets unless fixing a clear bug or adding a missing description
- **Add new targets in the appropriate section** — Follow the existing grouping pattern (if the file uses `##@` sections, add to matching section; if no sections, append logically)
- **Add missing preamble lines** at the top, before existing content
- **Add missing variables** near existing variable declarations
- Use the template as reference for the syntax of new targets, but adapt to match the style already present in the file (e.g., if existing Makefile uses tabs + simple recipes, don't introduce complex multi-line scripts)

### Quality Checks (Both Modes)

Before writing the file, verify:
- [ ] All targets have descriptions/documentation (## comments, desc:, [doc()], doc comments)
- [ ] No hardcoded paths that should be variables
- [ ] Package manager detection is correct
- [ ] Self-documenting help target is included
- [ ] `.PHONY` declarations for all non-file targets (Makefile only)
- [ ] Dangerous operations have confirmations (Justfile) or warnings

---

## Step 6: Write File & Report

### 6.1 Write the File

**Mode B (Generate New):**

Write the generated content using the `Write` tool:

| Tool | Output Path |
|------|-------------|
| Makefile | `Makefile` |
| Taskfile | `Taskfile.yml` |
| Justfile | `justfile` |
| Magefile | `magefile.go` |

**Mode A (Enhance Existing):**

Write the enhanced content to the same path where the existing file was found (preserving the original filename casing and location). The file is updated in-place — no need to ask about overwriting since we're improving, not replacing.

### 6.2 Display Summary

Display summary using format from `references/SUMMARY-FORMAT.md`. Shows targets table, project profile used, and quick start command for Mode B (generate), or what changed + new/existing targets for Mode A (enhance). Include installation hints if the tool requires setup.

---

## Step 7: Project Documentation Integration

After writing the build file, integrate quick commands into project docs.
For detailed integration procedures (README, AGENTS.md, existing markdown) → read `references/DOC-INTEGRATION.md`

Brief: scan for existing command sections, update or append quick reference, suggest AGENTS.md creation if missing.

## Artifact Ownership and Config Policy

- Primary ownership: generated or enhanced build automation files (`Makefile`, `Taskfile.yml`, `justfile`, `magefile.go`).
- Allowed companion updates: quick command snippets in existing docs or `AGENTS.md` when directly tied to the generated build workflow.
- Config policy: config-agnostic by design. This skill uses repository detection and fixed AI Factory context files rather than `config.yaml`.
