<pclass="page-subtitle">Huskies is configured via three TOML files in your <code>.huskies/</code> directory. All files are created by <code>huskies init</code> with sensible defaults.</p>
<h2id="project-toml">project.toml</h2>
<p>Project-wide settings. Lives at <code>.huskies/project.toml</code>.</p>
<td>Default QA mode. One of <code>"server"</code> (automated gate run), <code>"agent"</code> (spawn a QA agent), or <code>"human"</code> (manual approval).</td>
</tr>
<tr>
<td>default_coder_model</td>
<td>string</td>
<td><code>"sonnet"</code></td>
<td>Default model for coder agents. Only agents matching this model are auto-assigned. Use <code>"opus"</code> on individual stories for complex tasks.</td>
</tr>
<tr>
<td>max_coders</td>
<td>integer</td>
<td><code>3</code></td>
<td>Maximum concurrent coder agents. Stories wait in <code>2_current/</code> when all slots are full.</td>
</tr>
<tr>
<td>max_retries</td>
<td>integer</td>
<td><code>3</code></td>
<td>Maximum retries per story per pipeline stage before marking it as blocked. Set to <code>0</code> to disable.</td>
</tr>
<tr>
<td>base_branch</td>
<td>string</td>
<td>auto-detected</td>
<td>Base branch for merges and agent prompts. When unset, huskies reads the current HEAD branch.</td>
</tr>
<tr>
<td>rate_limit_notifications</td>
<td>bool</td>
<td><code>false</code></td>
<td>Send chat notifications when API soft rate limits are hit. Hard blocks and story-blocked notifications are always sent.</td>
</tr>
<tr>
<td>timezone</td>
<td>string</td>
<td><code>"UTC"</code></td>
<td>IANA timezone for timer scheduling (e.g. <code>"Europe/London"</code>, <code>"America/New_York"</code>). Timer HH:MM inputs are interpreted in this timezone.</td>
</tr>
</tbody>
</table>
<h3>Component setup</h3>
<p>The <code>[[component]]</code> sections define how to build and verify each part of your project. The server runs setup commands before accepting a story's QA and teardown commands after merging.</p>
<pre><code>[[component]]
name = "frontend"
path = "frontend"
setup = ["npm ci", "npm run build"]
teardown = []
[[component]]
name = "server"
path = "."
setup = ["mkdir -p frontend/dist", "cargo check"]
teardown = []</code></pre>
<h3>Story front matter overrides</h3>
<p>Individual stories can override project defaults using YAML front matter:</p>
<pre><code>---
name: "My Complex Story"
qa: agent # override default_qa
agent: opus # use the opus coder agent
---</code></pre>
<h2id="agents-toml">agents.toml</h2>
<p>Agent definitions. Lives at <code>.huskies/agents.toml</code>. Each <code>[[agent]]</code> block defines one agent slot.</p>
<table>
<thead>
<tr><th>Key</th><th>Description</th></tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>Unique identifier for this agent slot (e.g. <code>"coder-1"</code>, <code>"qa"</code>).</td>
</tr>
<tr>
<td>stage</td>
<td>Pipeline stage this agent handles. One of <code>"coder"</code>, <code>"qa"</code>, or <code>"mergemaster"</code>.</td>
</tr>
<tr>
<td>role</td>
<td>Human-readable description of the agent's responsibilities (shown in status output).</td>
</tr>
<tr>
<td>model</td>
<td>Claude model to use. One of <code>"sonnet"</code> (claude-sonnet-4-6) or <code>"opus"</code> (claude-opus-4-6).</td>
</tr>
<tr>
<td>max_turns</td>
<td>Maximum conversation turns before the agent is forcefully stopped.</td>
</tr>
<tr>
<td>max_budget_usd</td>
<td>Maximum API spend in USD before the agent is stopped.</td>
</tr>
<tr>
<td>prompt</td>
<td>The initial user-turn prompt sent to the agent. Supports template variables (see below).</td>
</tr>
<tr>
<td>system_prompt</td>
<td>The system prompt sent to the agent session.</td>
</tr>
</tbody>
</table>
<h3>Template variables</h3>
<p>The following variables are interpolated into <code>prompt</code> and <code>system_prompt</code> at agent start time:</p>
<table>
<thead>
<tr><th>Variable</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>{{story_id}}</td><td>The story's ID slug (e.g. <code>42_story_add_login</code>).</td></tr>
<tr><td>{{worktree_path}}</td><td>Absolute path to the agent's git worktree.</td></tr>
<tr><td>{{base_branch}}</td><td>The base branch name from <code>project.toml</code>.</td></tr>
</tbody>
</table>
<h3>Example: adding an opus coder</h3>
<pre><code>[[agent]]
name = "coder-opus"
stage = "coder"
role = "Senior engineer for complex tasks."
model = "opus"
max_turns = 80
max_budget_usd = 20.00
prompt = "You are working on story {{story_id}} ..."
system_prompt = "You are a senior full-stack engineer ..."</code></pre>
<p>To use this agent for a specific story, add <code>agent: opus</code> to the story's front matter, or run <code>start <number> opus</code> in chat.</p>
<p>Place a file at <code>.huskies/AGENT.md</code> in your project root to append project-specific guidance to every agent's initial prompt at spawn time.</p>
<h3>How it works</h3>
<ul>
<li>Huskies reads <code>.huskies/AGENT.md</code> each time an agent is spawned — no caching, no restart required.</li>
<li>The file content is appended <em>after</em> the baked-in agent prompt, so project guidance refines core instructions without overriding them.</li>
<li>Applies to all agent roles: coder, QA, mergemaster, and supervisor.</li>
<li>If the file is missing or empty, agents spawn normally — no warnings, no errors.</li>
<li>When the file exists and is non-empty, a single <code>INFO</code> log line is emitted showing the file path and byte count.</li>
</ul>
<h3>Ordering</h3>
<ol>
<li>Baked-in agent prompt (from <code>agents.toml</code> or <code>project.toml</code>)</li>
<li>Project-local content from <code>.huskies/AGENT.md</code></li>
<li>Resume context (only on agent restart after a gate failure)</li>
</ol>
<h3>Example</h3>
<pre><code># .huskies/AGENT.md
## Documentation
Docs live in `website/docs/*.html`, not Markdown files.
Edit the relevant .html file when a story asks for documentation.
## Quality gates
Run `cargo clippy -- -D warnings` before committing. Zero warnings allowed.</code></pre>
<p>Edit the file at any time — the next agent spawn picks up the latest content automatically.</p>
<p>Chat transport configuration. Lives at <code>.huskies/bot.toml</code>. This file is gitignored as it contains credentials. Copy the appropriate example file to get started:</p>
<p>Only one transport can be active at a time. See the <ahref="transports.html">Chat transports</a> guide for setup instructions for each platform.</p>
<h3>Common fields</h3>
<table>
<thead>
<tr><th>Key</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>enabled</td><td>Set to <code>true</code> to activate the bot. Set to <code>false</code> to disable without removing the file.</td></tr>
<tr><td>transport</td><td>Transport type: <code>"matrix"</code>, <code>"whatsapp"</code>, <code>"slack"</code>, or <code>"discord"</code>.</td></tr>
<tr><td>display_name</td><td>Optional. Bot display name in chat messages.</td></tr>
<tr><td>history_size</td><td>Optional. Maximum conversation turns to remember per room/user (default: 20).</td></tr>