🇧🇷 Português · 🇬🇧 English
Every environment variable, config file, and knob in Squire. All values have sensible defaults; you only need to set what you want to change.
Precedence order (strongest to weakest):
- Command-line argument (e.g.,
squire run --quiet) - Environment variable (e.g.,
SQUIRE_DAILY_USD_BUDGET=15) - Config file (e.g.,
budget.json) - Hardcoded default (in
config.py)
Insight — why env vars > files? 12-factor principle: env vars are portable across dev/staging/prod without editing files in the repo. The file is a convenient fallback for values that rarely change (budget caps) or are personal (paths).
All start with SQUIRE_. Source: config.py.
| Variable | Default | Effect |
|---|---|---|
SQUIRE_STATE_ROOT |
(required — no default) | Root of persistent state (all JSONs) |
Important
SQUIRE_STATE_ROOT is the only required variable. If not set,
import config fails immediately. The squire bash script (CLI)
injects a reasonable default (/home/ai-debian/squire-state) before
delegating to Python.
| Variable | Default | Effect |
|---|---|---|
SQUIRE_LITELLM_URL |
http://localhost:4000/v1 |
LiteLLM gateway base URL |
SQUIRE_LITELLM_MODEL |
journal-synth |
Default model (LiteLLM alias) |
SQUIRE_LITELLM_KEY |
sk-local |
API key (local placeholder — LiteLLM doesn't enforce) |
SQUIRE_MODEL_LOW |
same as LITELLM_MODEL |
Model for effort=low tasks |
SQUIRE_MODEL_MEDIUM |
same as LITELLM_MODEL |
Model for effort=medium tasks |
SQUIRE_MODEL_HIGH |
same as LITELLM_MODEL |
Model for effort=high tasks |
| Variable | Default | Effect |
|---|---|---|
SQUIRE_INNER_MAX_ATTEMPTS |
10 |
Attempts per round (per-task override in tasks.json) |
SQUIRE_INNER_TIMEOUT |
1200 |
Timeout (s) for one backend call |
| Variable | Default | Effect |
|---|---|---|
SQUIRE_CODING_BACKEND |
opencode |
Default backend (override in project.json via coding_backend) |
SQUIRE_OPENCODE_BIN |
opencode |
Opencode binary path/name (searches $PATH if relative) |
SQUIRE_CRUSH_BIN |
crush |
Crush binary path/name |
SQUIRE_AIDER_BIN |
aider |
Deprecated. Aider was discontinued; this var has no effect anymore. |
| Variable | Default | Effect |
|---|---|---|
SQUIRE_CLAUDE_BIN |
claude |
Claude Code CLI binary |
SQUIRE_CC_MAX_CALLS |
10 |
Max calls per window (secondary rate limit) |
SQUIRE_CC_WINDOW_MIN |
30 |
Window duration in minutes |
| Variable | Default | Effect |
|---|---|---|
SQUIRE_MAX_HOMOLOG |
5 |
Max rounds per task (default — override in tasks.json) |
SQUIRE_LOOP_DETECT |
3 |
Consecutive rejections with same pattern → forced escalation |
SQUIRE_NO_PROGRESS |
3 |
Cycles without modified files → forced escalation |
| Variable | Default | Effect |
|---|---|---|
SQUIRE_LOCK_TTL |
60 |
Session lock TTL (minutes). Heartbeat renews during operation. |
SQUIRE_HEARTBEAT |
300 |
Heartbeat interval (s) (renews lock + writes checkpoint) |
| Variable | Default | Effect |
|---|---|---|
SQUIRE_DAILY_USD_BUDGET |
0 |
Global daily USD cap (0 = no limit) |
SQUIRE_PER_TASK_USD_CAP |
0 |
Default per-task USD cap (Task.max_usd overrides) |
SQUIRE_ESTIMATED_CALL_USD |
0.05 |
Estimated call cost before knowing the real one (used in can_afford) |
See Cost and Budget for the complete system.
Persisted by squire budget set. Env vars take precedence.
{
"daily_usd": 10.0,
"per_task_usd": 2.0
}Project metadata. Schema: models.Project.
{
"id": "orchestrator-dashboard",
"name": "Orchestrator Dashboard",
"description": "Next.js panel showing squire project state",
"repo_path": "/home/ai-debian/projects/orchestrator-dashboard",
"stack": ["typescript", "nextjs", "tailwind"],
"status": "implementing",
"created_at": "2026-03-24T18:32:00Z",
"updated_at": "2026-05-11T14:24:33Z",
"current_task_id": "task-004",
"coding_backend": "opencode"
}coding_backend— overridesSQUIRE_CODING_BACKENDfor this project. Valid:"litellm","opencode","crush".repo_path— absolute path to the code repo. Auto-snapshot and auto-commit operate here.stack— informational (no behavior effect yet; future: hint for the mechanical gate).current_task_id— updated by squire as the cursor advances.
Backlog. Complete schema in Tasks.
Persisted after every state transition. Don't edit manually — use
squire reset or squire unblock to mess with the cursor. Schema:
models.Checkpoint.
Aggregated daily counters. Auto-reset when UTC day rolls.
{
"daily_claude_code_calls": 14,
"daily_local_llm_calls": 287,
"date": "2026-05-11",
"cost_estimate_usd": 1.247,
"daily_tokens": 24381,
"cost_by_model": {
"claude-opus-4-7": 1.247,
"journal-synth": 0.0
},
"daily_calls_unknown_cost": 0,
"projects_touched_today": ["orchestrator-dashboard"],
"tasks_completed_today": 4,
"approval_first_try_rate": 0.75
}Reset with squire budget reset.
List of alerts (warnings / criticals) needing human attention.
Auto-populated by squire in cases like: task hit max_homologation_attempts,
per-task budget exceeded, lock corruption.
Env var template for you to copy to .env:
# Required
SQUIRE_STATE_ROOT=/home/ai-debian/squire-state
# Optional (defaults shown)
# SQUIRE_LITELLM_URL=http://localhost:4000/v1
# SQUIRE_LITELLM_MODEL=journal-synth
# SQUIRE_CODING_BACKEND=opencode
# Budget (recommended in production)
# SQUIRE_DAILY_USD_BUDGET=10.0
# SQUIRE_PER_TASK_USD_CAP=2.0Full $SQUIRE_STATE_ROOT/ layout:
$SQUIRE_STATE_ROOT/
├── session.lock ← global lock
├── budget.json ← persisted USD caps
├── global-stats.json ← daily counters
├── alerts.json ← active alerts
├── rate.json ← legacy, currently unused
└── projects/
└── <project-id>/
├── project.json
├── tasks.json
├── checkpoint.json
├── history.json
└── progress.txt ← long-term memory
In production (on Unraid), typical STATE_ROOT is /mnt/user/data/squire/
(mounted volume). In dev, any writable directory works.
The MODEL_PRICING_PER_1M table in config.py:73 maps
model names to (USD/1M input tokens, USD/1M output tokens). Defaults
reflect Anthropic's public pricing as of 2026-Q1:
| Model | Input ($/1M) | Output ($/1M) |
|---|---|---|
claude-opus-4-7 |
15.00 | 75.00 |
claude-opus-4-6 |
15.00 | 75.00 |
claude-sonnet-4-6 |
3.00 | 15.00 |
claude-sonnet-4-5 |
3.00 | 15.00 |
claude-haiku-4-5 |
1.00 | 5.00 |
journal-synth |
0.00 | 0.00 |
To extend: edit the dict in config.py directly. No per-model env
var (bad UX with 20+ vars). Matching is exact with prefix-match fallback —
e.g., claude-opus-4-7[1m] matches claude-opus-4-7 via the prefix rule.
Unknown models cost (0, 0) — squire still records tokens but treats as
free. If Claude Code reports total_cost_usd in JSON, that value takes
precedence over the table computation
(homologator.py:_extract_usage_from_claude_json).
- Cost and Budget — how the daily/per-task USD cap works
- State and Recovery — format and purpose of each JSON
- CLI — commands that read/write these configs