Skip to content

Configuration

Maina works with zero configuration. Everything below is optional and lets you customize behavior as your needs grow.

Create maina.config.ts in your project root:

import { defineConfig } from 'maina';
export default defineConfig({
models: {
mechanical: 'google/gemini-2.5-flash',
standard: 'anthropic/claude-sonnet-4',
architectural: 'anthropic/claude-sonnet-4',
local: 'ollama/qwen3-coder-8b',
},
provider: 'openrouter',
budget: {
daily: 5.00,
perTask: 0.50,
alertAt: 0.80,
},
});

Every AI call uses a specific tier. You choose which model fills each tier.

TierUsed forDefault example
mechanicalTests, commit messages, slop detection, context compressiongoogle/gemini-2.5-flash
standardReviews, plans, design docs, explanationsanthropic/claude-sonnet-4
architecturalDesign review, architecture decisions, prompt evolutionanthropic/claude-sonnet-4
localOffline use via Ollama — slop detection, commit messagesollama/qwen3-coder-8b

Models are specified as provider/model-name strings. Maina uses OpenRouter by default, giving access to 300+ models through a unified interface.

With no API key, Maina still works:

  • Deterministic verification (Biome, Semgrep, Trivy, etc.) runs normally
  • Point the local tier to Ollama for offline AI features
  • AI-dependent features gracefully degrade

The constitution is your project’s non-negotiable rules. Generated by maina init and stored at .maina/constitution.md.

# Project Constitution
## Stack
- Runtime: Bun
- Language: TypeScript strict
- Lint/Format: Biome 2.x (NOT ESLint/Prettier)
- Test: bun:test (NOT Jest)
- Error handling: Result<T, E> pattern. Never throw.
## Architecture
- All DB access through repository layer
- API responses: { data, error, meta } envelope
- Feature modules are self-contained packages
## Verification
- All commits pass: biome check + tsc --noEmit + bun test
- PR reviews require spec compliance before code quality
- Mutation testing score > 80% on changed code
- No console.log in production code

The constitution is:

  • Injected as preamble into every AI call
  • Stable DNA — not subject to A/B testing or prompt evolution
  • Editable — update it directly when your project fundamentals change
  • Per-project — different repos have different constitutions

Drop markdown files in .maina/prompts/ to control AI behavior per task:

.maina/prompts/
review.md # What to focus on during code reviews
tests.md # How your team writes tests
commit.md # Commit message style and conventions
plan.md # Planning document structure

Edit prompts with the CLI:

Terminal window
maina prompt edit review # Opens .maina/prompts/review.md in $EDITOR
maina prompt list # Shows all prompts with version hashes and accept rates

Custom prompts:

  • Override defaults — Maina ships built-in prompts for each task; your custom prompts take priority
  • Are versioned — every prompt is hashed, with usage and accept rates tracked per version
  • Evolve from feedbackmaina learn analyzes accept/reject patterns and proposes improvements
  • Support A/B testing — improved prompts are tested at 80/20 split against the previous version
VariableDescriptionRequired
MAINA_API_KEYOpenRouter API key for AI featuresNo (deterministic verification works without it)
OPENROUTER_API_KEYAlternative name for the API keyNo
MAINA_MODEL_MECHANICALOverride the mechanical model tierNo
MAINA_MODEL_STANDARDOverride the standard model tierNo
MAINA_MODEL_ARCHITECTURALOverride the architectural model tierNo
MAINA_MODEL_LOCALOverride the local model tierNo
EDITOREditor used by maina prompt editNo (defaults to vi)

Environment variables override config file values. This is useful for CI or per-developer overrides.

Shell scripts in .maina/hooks/ execute at lifecycle boundaries:

.maina/hooks/
pre-commit.sh # Before verification gates
post-commit.sh # After successful commit
pre-verify.sh # Before verification pipeline
post-verify.sh # After verification completes
pre-review.sh # Before AI review
post-learn.sh # After prompt evolution

Hooks receive JSON on stdin with event context. An exit code of 2 blocks the action. This enables custom integrations without a plugin API.