aif-build-automation
GitHub分析项目并生成或增强构建自动化文件(Makefile、Taskfile等)。支持从无到有创建,或在现有文件基础上修复缺陷、补充目标。优先遵循项目特定上下文规则,确保构建脚本符合最佳实践。
触发场景
安装
npx skills add lee-to/ai-factory --skill aif-build-automation -g -y
SKILL.md
Frontmatter
{
"name": "aif-build-automation",
"metadata": {
"author": "AI Factory",
"version": "1.0",
"category": "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\".",
"allowed-tools": "Read Edit Glob Grep Write Bash(git *) AskUserQuestion Questions",
"argument-hint": "[makefile|taskfile|justfile|mage]",
"disable-model-invocation": false
}
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_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):
##@ 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→ usedocker composecommands (notdocker-compose) - If compose has profiles → use
--profile dev/--profile production - If
has_multistage→ use--target devfor dev builds, no target (or--target production) for prod - If
docker_profile.depsexist (db, redis, etc.) → addinfra-up/infra-downtargets to start/stop only infrastructure services without the app - If compose detected →
docker-devshould rundocker compose upwith correct profile/services - If no compose but Dockerfile →
docker-devshould rundocker build --target dev+docker runwith 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
- JVM (
java_build/ Gradle or Maven): UsePROJECT_PROFILE.build_entrypointfrom §2.2 Summary for every tool invocation. Quality and DB: use only the canonical target names and task names from the JVM template catalog (Step 5 table); when enhancing, add/remove recipes to matchjava_buildandlinters, not one-off guesses. - 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 (e.g.,
next dev,uvicorn --reload,air; JVM →build_entrypointplus the §2.2 dev task for the detected stack — QuarkusquarkusDev/quarkus:dev, Micronautrun/mn:run, Vert.xvertxRun/vertx:run, Spring BootbootRun/spring-boot:run) - Test command: Match the detected test runner (§2.7)
- Lint command (JVM): After pruning,
lintmust remaincheck/verify; per-tool rows use the Step 5 catalog table - Migration commands (JVM): Use
db-migrate-liquibasevsdb-migrate-flyway(or Taskfiledb:migrate:*) perjava_build - 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, and JVM catalog targets (lint-checkstyle,db-migrate-flyway, …) after template pruningci(aggregate target)
Missing project-specific targets — Based on PROJECT_PROFILE, check for:
- Docker targets (if
has_dockerbut no docker targets in file) - Database: canonical
db-migrate-*/db:migrate:*matchingjava_build - 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)
- JVM:
build/test/checkdelegating to<build_entrypoint>whenjava_buildis set (not only generic shell or wrong ecosystem) - JVM multi-module:
module-build/module-test/module-check(or Taskfilemodule:*) when the repo is a Gradle multi-project or Maven reactor and per-module commands are useful
Quality issues — Check for anti-patterns from best practices:
- JVM: recipes that are not in the Step 5 catalog table (or wrong tool on a recipe, e.g. Liquibase task on a Flyway-only repo) — replace with catalog names or delete
- Targets without descriptions/documentation
- Missing
.PHONYdeclarations (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 / build entrypoint detection matches the repo (Gradle/Maven wrappers, npm/pnpm, etc.)
- Self-documenting help target is included
-
.PHONYdeclarations 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.mdwhen 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.
版本历史
- 2.17.0 当前 2026-07-24 12:20


