generated/claude/skills/makefile/SKILL.md
Use when creating Makefiles for process lifecycle management with PID tracking, logging, and status monitoring. Triggers on: 'use makefile mode', 'makefile', 'create makefile', 'process management', 'background jobs', 'start/stop services'. Full access mode - can create/modify Makefiles.
npx skillsauth add mcouthon/agents makefileInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create and manage Makefiles optimized for AI agent interaction and process lifecycle management.
"Start clean. Stop clean. Log everything. Know your state."
Principles:
.logs/ - nothing lostBefore creating a Makefile, determine:
Standard structure (in order):
# 1. Configuration Variables
# 2. Directory Setup
# 3. Service Lifecycle Targets (run-*, stop-*)
# 4. Combined Operations (run, stop, restart)
# 5. Testing & Quality (test, lint)
# 6. Utility Targets (logs, status, help)
# 7. .PHONY declarations
run-backend:
@mkdir -p .pids .logs
@if lsof -ti:$(BACKEND_PORT) > /dev/null 2>&1; then \
echo "❌ Backend already running on port $(BACKEND_PORT)"; \
exit 1; \
fi
@echo "🚀 Starting backend on port $(BACKEND_PORT)..."
@nohup $(BACKEND_CMD) > .logs/backend.log 2>&1 & echo $$! > .pids/backend.pid
@echo "✅ Backend started (PID: $$(cat .pids/backend.pid))"
stop-backend:
@if [ -f .pids/backend.pid ]; then \
PID=$$(cat .pids/backend.pid); \
if ps -p $$PID > /dev/null 2>&1; then \
echo "🛑 Stopping backend (PID: $$PID)..."; \
kill -TERM -- -$$PID 2>/dev/null || kill $$PID; \
rm .pids/backend.pid; \
echo "✅ Backend stopped"; \
else \
echo "⚠️ Backend process not found, cleaning up PID file"; \
rm .pids/backend.pid; \
fi \
else \
echo "ℹ️ Backend not running"; \
fi
status:
@echo "📊 Service Status:"
@echo ""
@for service in backend frontend; do \
if [ -f .pids/$$service.pid ]; then \
PID=$$(cat .pids/$$service.pid); \
if ps -p $$PID > /dev/null 2>&1; then \
echo "✅ $$service: running (PID: $$PID)"; \
else \
echo "❌ $$service: stopped (stale PID file)"; \
fi \
else \
echo "⚪ $$service: not running"; \
fi; \
done
logs:
@if [ -f .logs/backend.log ] || [ -f .logs/frontend.log ]; then \
tail -n 50 .logs/*.log 2>/dev/null; \
else \
echo "No logs found"; \
fi
logs-follow:
@tail -f .logs/*.log 2>/dev/null
run: run-backend run-frontend
stop: stop-frontend stop-backend # Reverse order for clean shutdown
restart: stop run
test: test-setup
@echo "🧪 Running tests..."
@$(TEST_CMD)
test-setup:
@if [ -n "$(DOCKER_COMPOSE_FILE)" ] && [ -f "$(DOCKER_COMPOSE_FILE)" ]; then \
docker-compose -f $(DOCKER_COMPOSE_FILE) up -d; \
fi
.DEFAULT_GOAL := help
help:
@echo "Available targets:"
@echo ""
@echo " make run Start all services"
@echo " make stop Stop all services"
@echo " make restart Restart all services"
@echo " make status Show service status"
@echo " make logs Show recent logs"
@echo " make logs-follow Follow logs in real-time"
@echo " make test Run all tests"
@echo " make lint Run linters and formatters"
@echo ""
@echo "Individual services:"
@echo " make run-backend Start backend only"
@echo " make run-frontend Start frontend only"
@echo " make stop-backend Stop backend only"
@echo " make stop-frontend Stop frontend only"
| Scenario | Adaptation |
| ------------------- | ------------------------------------------------------------- |
| Multiple backends | Use suffix naming: run-api, run-worker, etc. |
| Database migrations | Add migrate target, make run-backend depend on it |
| Emulators | Treat like any other service with PID tracking |
| Docker Compose | Wrap docker-compose commands, track container IDs |
| Monorepo | Use subdirectory variables: cd $(API_DIR) && ... |
| Multiple test types | Separate targets: test-unit, test-integration, test-e2e |
| Watch modes | Use separate watch targets, don't mix with regular run |
Before completing a Makefile, verify:
.PHONY (or appropriately not).logs/ directory.pids/ directoryhelp target is default (listed first or .DEFAULT_GOAL):= (simple expansion)| Problem | Solution |
| ------------------------------------ | ------------------------------------------- |
| PID file exists but process dead | Check ps -p $PID before using PID file |
| Child processes survive parent kill | Use kill -TERM -- -$PID (process group) |
| Port already in use | Check with lsof -ti:$PORT before start |
| Logs interleaved/unreadable | Separate log files per service |
| Service starts but immediately exits | Redirect stderr: 2>&1, check .logs/ |
| Make variables not evaluated | Use := not =, check $$ vs $ |
| Colors don't show in logs | Use unbuffer or configure service for TTY |
| Can't stop service (permission) | Run make with same user that started it |
A minimal working template for a full-stack app:
# =============================================================================
# Configuration
# =============================================================================
BACKEND_PORT := 3001
FRONTEND_PORT := 3000
BACKEND_CMD := npm run dev --prefix backend
FRONTEND_CMD := npm run dev --prefix frontend
TEST_CMD := npm test
# =============================================================================
# Directory Setup
# =============================================================================
$(shell mkdir -p .pids .logs)
# =============================================================================
# Service Lifecycle
# =============================================================================
run-backend:
@if lsof -ti:$(BACKEND_PORT) > /dev/null 2>&1; then \
echo "❌ Backend already running on port $(BACKEND_PORT)"; \
exit 1; \
fi
@echo "🚀 Starting backend on port $(BACKEND_PORT)..."
@nohup $(BACKEND_CMD) > .logs/backend.log 2>&1 & echo $$! > .pids/backend.pid
@echo "✅ Backend started (PID: $$(cat .pids/backend.pid))"
run-frontend:
@if lsof -ti:$(FRONTEND_PORT) > /dev/null 2>&1; then \
echo "❌ Frontend already running on port $(FRONTEND_PORT)"; \
exit 1; \
fi
@echo "🚀 Starting frontend on port $(FRONTEND_PORT)..."
@nohup $(FRONTEND_CMD) > .logs/frontend.log 2>&1 & echo $$! > .pids/frontend.pid
@echo "✅ Frontend started (PID: $$(cat .pids/frontend.pid))"
stop-backend:
@if [ -f .pids/backend.pid ]; then \
PID=$$(cat .pids/backend.pid); \
if ps -p $$PID > /dev/null 2>&1; then \
echo "🛑 Stopping backend (PID: $$PID)..."; \
kill -TERM -- -$$PID 2>/dev/null || kill $$PID; \
rm .pids/backend.pid; \
echo "✅ Backend stopped"; \
else \
echo "⚠️ Backend not found, cleaning up PID file"; \
rm .pids/backend.pid; \
fi \
else \
echo "ℹ️ Backend not running"; \
fi
stop-frontend:
@if [ -f .pids/frontend.pid ]; then \
PID=$$(cat .pids/frontend.pid); \
if ps -p $$PID > /dev/null 2>&1; then \
echo "🛑 Stopping frontend (PID: $$PID)..."; \
kill -TERM -- -$$PID 2>/dev/null || kill $$PID; \
rm .pids/frontend.pid; \
echo "✅ Frontend stopped"; \
else \
echo "⚠️ Frontend not found, cleaning up PID file"; \
rm .pids/frontend.pid; \
fi \
else \
echo "ℹ️ Frontend not running"; \
fi
# =============================================================================
# Combined Operations
# =============================================================================
run: run-backend run-frontend
stop: stop-frontend stop-backend
restart: stop run
# =============================================================================
# Testing & Quality
# =============================================================================
test:
@echo "🧪 Running tests..."
@$(TEST_CMD)
lint:
@echo "🔍 Running linters..."
@npm run lint 2>&1 || true
# =============================================================================
# Utilities
# =============================================================================
status:
@echo "📊 Service Status:"
@echo ""
@for service in backend frontend; do \
if [ -f .pids/$$service.pid ]; then \
PID=$$(cat .pids/$$service.pid); \
if ps -p $$PID > /dev/null 2>&1; then \
echo "✅ $$service: running (PID: $$PID)"; \
else \
echo "❌ $$service: stopped (stale PID file)"; \
fi \
else \
echo "⚪ $$service: not running"; \
fi; \
done
logs:
@tail -n 50 .logs/*.log 2>/dev/null || echo "No logs found"
logs-follow:
@tail -f .logs/*.log 2>/dev/null
clean:
@rm -rf .pids .logs
@echo "🧹 Cleaned up PID and log files"
# =============================================================================
# Help
# =============================================================================
.DEFAULT_GOAL := help
help:
@echo "Available targets:"
@echo ""
@echo " make run Start all services"
@echo " make stop Stop all services"
@echo " make restart Restart all services"
@echo " make status Show service status"
@echo " make logs Show recent logs (last 50 lines)"
@echo " make logs-follow Follow logs in real-time"
@echo " make test Run tests"
@echo " make lint Run linters"
@echo " make clean Remove PID and log files"
@echo ""
@echo "Individual services:"
@echo " make run-backend Start backend only"
@echo " make run-frontend Start frontend only"
@echo " make stop-backend Stop backend only"
@echo " make stop-frontend Stop frontend only"
# =============================================================================
# .PHONY
# =============================================================================
.PHONY: run run-backend run-frontend stop stop-backend stop-frontend \
restart status logs logs-follow test lint clean help
Remind users to add these to .gitignore:
.pids/
.logs/
development
Systematic debugging with hypothesis-driven investigation. Use when something is broken, tests are failing, unexpected behavior occurs, or errors need investigation. Triggers on: 'this is broken', 'debug', 'why is this failing', 'unexpected error', 'not working', 'bug', 'fix this issue', 'investigate', 'tests failing', 'trace the error', 'use debug mode'. Full access mode - can run commands, add logging, and fix issues.
development
Systematic debugging with hypothesis-driven investigation. Use when something is broken, tests are failing, unexpected behavior occurs, or errors need investigation. Triggers on: 'this is broken', 'debug', 'why is this failing', 'unexpected error', 'not working', 'bug', 'fix this issue', 'investigate', 'tests failing', 'trace the error', 'use debug mode'. Full access mode - can run commands, add logging, and fix issues.
testing
Behavioral testing strategy — deciding what to test and how. Use when writing tests, reviewing test quality, or fixing tests that test mocks instead of behavior. Triggers on: 'use testing mode', 'write tests', 'test strategy', 'tests are brittle', 'tests test mocks', 'improve test quality', 'what should I test'. Full access mode - can write and run tests.
development
Use when finding code smells, auditing TODOs, removing dead code, cleaning up unused imports, or assessing code quality. Triggers on: 'use tech-debt mode', 'tech debt', 'code smells', 'clean up', 'remove dead code', 'delete unused', 'simplify'. Full access mode - can modify files and run tests.