| name | ovid-make |
|---|---|
| description | Use when creating or updating a Makefile for a project. Ensures standard targets exist and asks before modifying any existing target's implementation. |
Creates or updates a project Makefile with standard targets. Never modifies an existing target without explicit user approval.
1. Detect stack (read CLAUDE.md, AGENTS.md, README, package.json, pyproject.toml, Cargo.toml, go.mod, etc.)
2. Check if Makefile exists
3. Creating → build from scratch with all required targets mapped to detected stack
4. Updating → add missing targets; STOP and ask before changing any existing one
Read project files in this order to understand the technology and available commands:
CLAUDE.mdorAGENTS.md— often lists exact commands for test, lint, format, buildREADME.md— frequently documents dev workflow- Language manifest (
package.json,pyproject.toml,Cargo.toml,go.mod,Gemfile, etc.) — reveals available scripts/tasks
Use the commands the project already documents. Do not invent commands that aren't confirmed to exist.
Every Makefile must include at minimum:
| Target | Purpose |
|---|---|
help |
List all targets with descriptions |
all |
Full CI pass (lint + format + test at minimum) |
test |
Run test suite |
cover |
One-shot coverage report |
lint |
Lint (with autofix if available) |
format |
Format code |
Add extra targets (e.g. build, dev, preview) only if the project supports them.
Every target gets a ## description. help extracts them with grep + awk:
.PHONY: all test cover lint format help
all: lint format test ## Lint, format, and test
test: ## Run full test suite
<stack-specific command>
cover: ## Generate code coverage report (one-shot)
<stack-specific command, forced one-shot — see below>
lint: ## Lint with autofix
<stack-specific command>
format: ## Format code
<stack-specific command>
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-10s %s\n", $$1, $$2}'All targets must appear in .PHONY.
If an existing target's implementation would change, stop and tell the user:
"The existing
covertarget runsX. I'd change it toYbecause [reason]. Should I make this change?"
Wait for explicit approval. Adding a brand-new target never requires approval.
Coverage tools often default to watch mode. Force one-shot execution:
- vitest: append
-- --run - jest: append
-- --watchAll=false - pytest-cov / cargo / go test: typically exit on their own; verify before adding flags
- other: check the tool's docs for a non-interactive/CI flag
make test output should be actionable, not overwhelming. Avoid both extremes:
- Too verbose: full test names for passing tests, stack traces for every assertion, watch-mode chatter
- Too silent: a single pass/fail line with no detail on failures
Goal: On success, show a concise summary (total passed/failed/skipped). On failure, show the failing test name, assertion, and enough context to act on it.
Common approaches by stack:
| Stack | Flag / Approach |
|---|---|
| vitest | --reporter=default is usually fine; avoid --reporter=verbose |
| jest | Default is good; avoid --verbose |
| pytest | -q or --tb=short — default is often too verbose |
| cargo test | Default is fine; --quiet if too noisy |
| go test | Default is fine; avoid -v unless debugging |
| prove (Perl) | Default is fine; avoid --verbose |
If the testing tool doesn't support balanced output (e.g., only offers silent vs. firehose), inform the user and ask how they'd like to handle it rather than guessing.