Build Automation Generator
SkillFiles & storageAnalyze 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".
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Build Automation Generator skill
What this skill tells your AI
The instructions your AI receives, as published by artemyurov/tomobar in .claude/skills/aif-build-automation/SKILL.md and read by ahel’s review.
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_TOOLautomatically from the detected file (Makefile →makefile, Taskfile.yml →taskfile, etc.) - If multiple build files exist AND
$ARGUMENTSspecifies 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
$ARGUMENTSto 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
$ARGUMENTSis 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 with Glob and Grep. Use the same flow for every stack: primary language → package manager / build entrypoints → frameworks → Docker → CI → migrations → tests → linters → monorepo, then the Summary object. JVM projects are handled inside those steps (not a separate pipeline).
2.1 Primary Language
Check for these files (first match wins in the table order below). For Java / Kotlin (JVM), infer language from build files: default Java unless Kotlin plugins / kotlin("jvm") / dominant .kt layout suggests Kotlin.
| File / signal | 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 |
| JVM: Gradle root or wrapper (see §2.2) | Java / Kotlin (JVM) |
JVM: pom.xml | Java / Kotlin (JVM) |
*.csproj or *.sln | C# / .NET |
2.2 Package manager & build entrypoints
Lock files and wrappers (same idea as package-lock.json → npm):
| File | Package manager / tool |
|---|---|
bun.lockb | bun |
pnpm-lock.yaml | pnpm |
yarn.lock | yarn |
package-lock.json | npm |
poetry.lock | poetry |
uv.lock | uv |
Pipfile.lock | pipenv |
gradle/wrapper/gradle-wrapper.properties | ./gradlew |
.mvn/wrapper/maven-wrapper.properties | ./mvnw |
Java / Kotlin (JVM) — Gradle vs Maven: Detect Gradle with one batch of checks (single Glob over the paths below, or parallel existence checks — avoid redundant sequential walks):
settings.gradle,settings.gradle.kts,build.gradle,build.gradle.kts(repo root),gradle/wrapper/gradle-wrapper.properties
If any Gradle signal matches → Gradle is in play. pom.xml indicates Maven. Set PROJECT_PROFILE.java_build.build_tool from this table:
| Condition | build_tool | Notes |
|---|---|---|
| Gradle signals present | gradle | Wire targets to Gradle commands below. |
No Gradle, pom.xml present | maven | Wire targets to Maven commands below. |
Gradle and pom.xml | gradle | Set java_build.mixed_maven_gradle: true and append a warning to PROJECT_PROFILE.warnings (both builds present; recipes follow Gradle — user confirms authoritative build). |
Concrete JVM Entrypoint: Persist the detected entrypoint in PROJECT_PROFILE.build_entrypoint based on wrapper presence:
- If
build_toolisgradle: use./gradlewifgradleworgradle/wrapper/gradle-wrapper.propertiesexists, else fallback togradle. - If
build_toolismaven: use./mvnwifmvnwor.mvn/wrapper/maven-wrapper.propertiesexists, else fallback tomvn.
Single source of truth: The predicate above is the same rule the JVM templates implement in shell (ENTRYPOINT / entrypoint — test ./gradlew or gradle/wrapper/gradle-wrapper.properties; test ./mvnw or .mvn/wrapper/maven-wrapper.properties). When generating or enhancing build files, set PROJECT_PROFILE.build_entrypoint to the result those tests imply (./gradlew vs gradle, ./mvnw vs mvn). Do not emit a different entrypoint string than that predicate unless the user overrides (e.g. Makefile ENTRYPOINT=…). Templates re-resolve at recipe runtime so clones stay correct without editing.
Version catalog: If gradle/libs.versions.toml exists, set java_build.has_version_catalog and document PROJECT_PROFILE.build_entrypoint / catalog usage in comments where helpful.
Commands to wire into Makefile / Taskfile / Just for JVM (same role as npm run build / pytest for other stacks; use gradlew.bat on Windows):
| Goal | Gradle | Maven |
|---|---|---|
| Full compile + checks | <build_entrypoint> build | <build_entrypoint> verify |
| Unit / integration tests | <build_entrypoint> test | <build_entrypoint> test |
| Verification (tests + static analysis where configured) | <build_entrypoint> check | <build_entrypoint> verify |
| Package only | <build_entrypoint> assemble (or jar / bootJar) | <build_entrypoint> package |
| Dev server — Spring Boot (see §2.3) | <build_entrypoint> bootRun | <build_entrypoint> spring-boot:run |
| Dev server — Quarkus | <build_entrypoint> quarkusDev | <build_entrypoint> quarkus:dev |
| Dev server — Micronaut | <build_entrypoint> run | <build_entrypoint> mn:run |
| Dev server — Vert.x | <build_entrypoint> vertxRun | <build_entrypoint> vertx:run |
| Spring Boot — runnable JAR | <build_entrypoint> bootJar | <build_entrypoint> package (spring-boot repackage) |
| Clean | <build_entrypoint> clean | <build_entrypoint> clean |
| Multi-module | <build_entrypoint> :subproject:build | <build_entrypoint> -pl module -am package |
dev target (templates + generated files): Resolve the framework dev task/goal from the same signals as §2.3, fixed priority (first match wins): Quarkus → Micronaut → Vert.x → Spring Boot. Scan Gradle: build.gradle, build.gradle.kts, settings.gradle, settings.gradle.kts, gradle/libs.versions.toml with the same grep -E patterns you use for §2.3 (quarkus / io.quarkus; micronaut / io.micronaut; Vert.x Gradle plugin — vertx-plugin or io.vertx.vertx; Spring Boot — fallback). Scan Maven: pom.xml only; Vert.x Maven — vertx-maven-plugin or io.reactiverse. If the repo root is an aggregator and detection misses, override the template’s dev task variable (same idea as JVM_MODULE).
Templates: JVM Makefile/Taskfile/Just ship a fixed catalog: lint → Gradle check / Maven verify; fmt → spotlessApply / spotless:apply; lint-checkstyle, lint-spotbugs, lint-pmd, lint-spotless (Taskfile lint:*); db-migrate-liquibase, db-migrate-flyway (Taskfile db:migrate:*). Multi-module: module-* with JVM_MODULE. Step 5 removes catalog entries the repo does not wire (see JVM template rules).
2.3 Framework Detection
For Node.js projects, check package.json dependencies for:
next→ Next.jsnuxt→ Nuxt@remix-run/node→ Remixexpress→ Expressfastify→ Fastifyhono→ Hono@nestjs/core→ NestJS
For Python projects, check pyproject.toml or imports for:
fastapi→ FastAPIdjango→ Djangoflask→ Flask
For PHP projects, check composer.json require for:
laravel/framework→ Laravelsymfony/framework-bundle→ Symfonyslim/slim→ Slimcakephp/cakephp→ CakePHP
For Go projects, check go.mod for:
gin-gonic/gin→ Ginlabstack/echo→ Echogofiber/fiber→ Fibergo-chi/chi→ Chi
For Rust projects, read Cargo.toml (workspace members and [dependencies] / [workspace.dependencies]) for:
axum→ Axumactix-web→ Actix Webrocket→ Rocketwarp→ Warp
For Ruby projects, read Gemfile for:
rails→ Ruby on Railssinatra→ Sinatrahanami→ Hanamiroda→ Roda
For Java / JVM projects, read pom.xml, build.gradle*, and gradle/libs.versions.toml (when present) for dependencies and plugins — same discovery depth as package.json for Node:
spring-boot,spring-boot-starter,spring-boot-parent→ Spring Bootgrpc,protobuf,spring-grpcor*.protoin repo → gRPC / protobufquarkus,io.quarkus→ Quarkusmicronaut→ Micronautvertx/ Vert.x stack → Vert.xliquibasein deps ordb.changelog*→ Liquibase (see §2.6)- Flyway
org.flywaydb/flyway-core/flyway-maven-plugin/ Flyway Gradle plugin inpom.xml,build.gradle*, orgradle/libs.versions.toml→ Flyway (see §2.6) - Prefer Jakarta (
jakarta.*) for Java 9+ / Spring Boot 3+; flag legacyjavax.*migration if both appear
Map findings into framework / java_build flags (spring_boot, grpc, liquibase, flyway) like other ecosystems map Express vs NestJS.
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/prodstages) →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: booleanhas_multistage: booleanservices: list of service namesdeps: list of infrastructure services (db, cache, queue)ports: exposed portshas_dev_stage: boolean (Dockerfile has adevordevelopmentstage)
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|liquibase|flyway
Check for:
prisma/schema.prisma→ Prismadrizzle.config.ts→ Drizzlealembic/directory → Alembicmigrations/directory → Generic migrations- Liquibase —
db.changelog*,liquibasein Gradle/Maven or resources → Liquibase (JVM and others); setjava_build.liquibase: true - Flyway — dependency or plugin (
org.flywaydb,flyway-core,flyway-maven-plugin, Flyway Gradle plugin) inpom.xml,build.gradle*, orgradle/libs.versions.toml; setjava_build.flyway: true
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/ |
| Ruby | rspec in Gemfile → RSpec; minitest / minitest- gems → Minitest; else default rake test when Rakefile exists |
| Java / Kotlin (JVM) | junit-jupiter, junit-jupiter-api, JUnitPlatform, JUnit5, testcontainers, mockito, rest-assured, cucumber in Gradle/Maven / libs.versions.toml |
2.8 Linters & Formatters
Scan for formatter/linter configs (EditorConfig, Checkstyle on JVM, ESLint/Prettier/Biome, Python tools, PHP, Go, Rust, Ruby):
Glob: .eslintrc*, eslint.config.*, .prettierrc*, biome.json, biome.jsonc, .golangci.yml, .golangci.yaml
Glob: checkstyle.xml, .checkstyle.xml, config/checkstyle/checkstyle.xml, .editorconfig
Glob: ruff.toml, .ruff.toml, .flake8, phpcs.xml, phpcs.xml.dist
Glob: rustfmt.toml, .rustfmt.toml, clippy.toml, .rubocop.yml, .rubocop_todo.yml, .standard.yml
Grep in pyproject.toml: ruff|black|flake8|pylint|isort
Grep in build.gradle*, pom.xml: spotless|spotbugs|pmd|errorprone|checkstyle (when not covered by config files alone)
Merge JVM matches into PROJECT_PROFILE.linters as normalized ids (e.g. checkstyle, spotless, spotbugs, pmd, errorprone) for use when wiring lint / fmt targets (Step 5).
2.9 Monorepo Detection
Glob: turbo.json, nx.json, lerna.json, pnpm-workspace.yaml
Summary
Build a PROJECT_PROFILE object with:
language: primary languagepackage_manager: detected PM (npm, pnpm, Gradle, Maven, …)build_entrypoint: the exact entrypoint command detected (e.g../gradlew,mvn,npm,cargo)framework: detected framework (if any); JVM frameworks map here the same way as NestJS or Djangowarnings: optional string array (e.g. mixed Maven+Gradle from §2.2)java_build: optional — when language is JVM:{ build_tool: "gradle"|"maven", mixed_maven_gradle?: boolean, has_version_catalog: boolean, spring_boot: boolean, grpc: boolean, liquibase: boolean, flyway: boolean }has_docker: booleandocker_profile:DOCKER_PROFILEobject (ifhas_docker)ci_system: detected CI (if any)has_migrations: boolean + tool nametest_framework: detected test runnerlinters: list of detected lintersis_monorepo: booleanhas_dev_server: boolean (framework with dev server)
Step 3: Read Best Practices
Read the best practices reference for the chosen tool:
Read skills/aif-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 | Rust | Ruby | Java / JVM | Other |
|---|---|---|---|---|---|---|---|---|
| Makefile | makefile-go.mk | makefile-node.mk | makefile-python.mk | makefile-php.mk | makefile-rust.mk | makefile-ruby.mk | makefile-gradle.mk or makefile-maven.mk | Use closest match |
| Taskfile | taskfile-go.yml | taskfile-node.yml | taskfile-python.yml | taskfile-php.yml | taskfile-rust.yml | taskfile-ruby.yml | taskfile-gradle.yml or taskfile-maven.yml | Use closest match |
| Justfile | justfile-go | justfile-node | justfile-python | justfile-php | justfile-rust | justfile-ruby | justfile-gradle or justfile-maven | Use closest match |
| Magefile | magefile-basic.go | magefile-full.go | magefile-full.go | N/A (use Makefile) | N/A (use Makefile) | N/A (use Makefile) | N/A (use Makefile) | N/A (use Makefile) |
For Java / JVM, select the Gradle or Maven template based on PROJECT_PROFILE.java_build.build_tool.
If language is not among Go, Node.js, Python, PHP, Rust, Ruby, or Java / JVM in the table above, use the Node.js template as the structural fallback and adapt it to the detected build_entrypoint and language conventions (e.g., dotnet build).
For Magefile: use magefile-full.go if HAS_DOCKER or has_migrations is true, otherwise magefile-basic.go.
For PHP, Rust, Ruby, or Java/JVM + Magefile: Mage is Go-specific and not generally applicable to these stacks. If the user explicitly requested mage for such a 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/aif-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
- Start with the tool's required preamble (from best practices)
- Include all standard targets from the selected template (help/default, build, test, lint, clean, dev, fmt,
ci). JVM: the template is a complete catalog; prune targets in Mode B per Step 5 JVM rules (do not invent one-offlintrecipes). - Add conditional targets based on project profile:
- Docker targets → only if
has_docker - Database targets → only if
has_migrations(non-JVM); JVM: use the canonicaldb-migrate-liquibase/db-migrate-flyway(or Taskfiledb:migrate:*) only when the matchingjava_buildflag is true — omit the other - Deploy targets → only if CI/CD detected
- Generate target → only if code generation detected
- Typecheck target → only if TypeScript or mypy detected
- Docker targets → only if
- Use correct package manager — match
PROJECT_PROFILE(§2.2): JVM →<build_entrypoint>(from §2.2); Node → npm/pnpm/yarn/bun; Python → uv/poetry/pip; Go →go; Rust →cargo; Ruby → Bundler (bundle,bundle exec); do not substitute the wrong ecosystem (e.g. npm scripts for a Gradle-only repo) - Include CI aggregate target — default
ci= clean + build on JVM (already runscheck/verify); addlint/fmttocionly if those targets remain after pruning - Follow the template's structure for organization and grouping
- Adapt variable names to match the actual project (module name, binary name, source dirs); JVM multi-module repos → set
JVM_MODULEformodule-*targets (§2.2) - Include version/commit/build-time detection via git
- Docker-aware targets — if
has_docker, generate a dedicated Docker section (see below)
JVM template catalog (fixed names; prune unused tools in Mode B) — Source of truth is skills/aif-build-automation/templates/*gradle* and *maven*. Always use these exact Gradle/Maven task names in generated files unless the build files use a different official task name for the same plugin (document in a comment next to the recipe).
| Target (Make/Just) | Taskfile task | Gradle command | Maven command |
|---|---|---|---|
lint | lint | check | verify |
fmt | fmt | spotlessApply | spotless:apply |
lint-checkstyle | lint:checkstyle | checkstyleMain | checkstyle:check |
lint-spotbugs | lint:spotbugs | spotbugsMain | spotbugs:check |
lint-pmd | lint:pmd | pmdMain | pmd:check |
lint-spotless | lint:spotless | spotlessCheck | spotless:check |
db-migrate-liquibase | db:migrate:liquibase | liquibaseUpdate | liquibase:update |
db-migrate-flyway | db:migrate:flyway | flywayMigrate | flyway:migrate |
dev | dev | see §2.2 dev tasks + template DEV_GRADLE_TASK resolver (§2.3 priority) | see §2.2 dev goals + template DEV_MAVEN_GOAL resolver (§2.3 priority) |
- Mode B (generate): Copy the catalog from the template, then delete targets whose tools are absent: e.g. remove
lint-checkstyleifcheckstyle∉linters; removelint-spotbugs/lint-pmdif those ids are missing; removefmtandlint-spotlessifspotless∉linters; removedb-migrate-liquibaseif notjava_build.liquibase; removedb-migrate-flywayif notjava_build.flyway. Always keeplint(=check/verify) unless the project truly has no Java plugin lifecycle (rare). Never substituteverify -DskipTestsorcheck -x testaslint. Fordev, templates already resolve the task/goal from build files; when enhancing, replace a wrong constantbootRun/spring-boot:runwith the correct framework command fromPROJECT_PROFILE(same strings as the template resolver). - Mode A (enhance): Prefer missing catalog targets over ad-hoc names; remove recipes that contradict
java_build/linters.
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):
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 89
- Forks
- 5
- Last commit
- Jul 2026
Advanced
- Catalog kind
- skill
- Gateway key
aif-build-automation- Source
- github.com/artemyurov/tomobar