Files

316 lines
14 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Configuration — Huskies Docs</title>
<meta name="description" content="Reference for project.toml, agents.toml, and bot.toml configuration files.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@400;500;600;700;800&family=Karla:ital,wght@0,300;0,400;0,500;1,300;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="docs.css">
</head>
<body>
<div class="shell">
<header class="reveal r1">
<a href="/" class="logo">huskies</a>
<nav>
<a href="/#how">How it works</a>
<a href="/#features">Features</a>
<a href="/docs/" class="active">Docs</a>
<a href="https://code.crashlabs.io/crashlabs/huskies">Source</a>
<a href="https://code.crashlabs.io/crashlabs/huskies/releases">Releases</a>
<a href="mailto:hello@huskies.dev" class="nav-cta">Get in touch</a>
</nav>
</header>
</div>
<div class="shell">
<div class="docs-layout">
<aside class="sidebar reveal r2">
<div class="sidebar-section">
<div class="sidebar-heading">Getting started</div>
<nav>
<a href="/docs/">Overview</a>
<a href="quickstart.html">Quickstart</a>
</nav>
</div>
<div class="sidebar-section">
<div class="sidebar-heading">Reference</div>
<nav>
<a href="configuration.html" class="active">Configuration</a>
<a href="commands.html">Bot commands</a>
<a href="cli.html">CLI</a>
</nav>
</div>
<div class="sidebar-section">
<div class="sidebar-heading">Guides</div>
<nav>
<a href="pipeline.html">Pipeline stages</a>
<a href="transports.html">Chat transports</a>
</nav>
</div>
</aside>
<main class="docs-main reveal r3">
<h1 class="page-title">Configuration</h1>
<p class="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>
<h2 id="project-toml">project.toml</h2>
<p>Project-wide settings. Lives at <code>.huskies/project.toml</code>.</p>
<table>
<thead>
<tr><th>Key</th><th>Type</th><th>Default</th><th>Description</th></tr>
</thead>
<tbody>
<tr>
<td>default_qa</td>
<td>string</td>
<td><code>"server"</code></td>
<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>
<h2 id="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>&#123;&#123;story_id&#125;&#125;</td><td>The story's ID slug (e.g. <code>42_story_add_login</code>).</td></tr>
<tr><td>&#123;&#123;worktree_path&#125;&#125;</td><td>Absolute path to the agent's git worktree.</td></tr>
<tr><td>&#123;&#123;base_branch&#125;&#125;</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 &lt;number&gt; opus</code> in chat.</p>
<h2 id="agent-md">Project-local agent prompt (<code>.huskies/AGENT.md</code>)</h2>
<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>
<h2 id="bot-toml">bot.toml</h2>
<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>
<pre><code>cp .huskies/bot.toml.matrix.example .huskies/bot.toml</code></pre>
<p>Only one transport can be active at a time. See the <a href="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>
</tbody>
</table>
<h2 id="gateway-aggregated-stream">Gateway: aggregated chat stream</h2>
<p>When running <code>huskies --gateway</code>, you can configure a single bot that receives pipeline notifications from <strong>all</strong> registered projects. Events are prefixed with <code>[project-name]</code> so you can tell them apart in one shared room.</p>
<p>The aggregated stream is configured entirely in the <strong>gateway's</strong> <code>.huskies/bot.toml</code> — no per-project bot config is required and no per-project files need to change when you add a new project to <code>projects.toml</code>.</p>
<h3>Enabling the aggregated stream</h3>
<p>Add or edit <code>&lt;gateway-config-dir&gt;/.huskies/bot.toml</code> and set <code>enabled = true</code>. The gateway bot will automatically poll every project listed in <code>projects.toml</code> and forward events to the configured rooms.</p>
<pre><code># &lt;gateway-config-dir&gt;/.huskies/bot.toml
enabled = true
transport = "matrix"
homeserver = "https://matrix.example.com"
username = "@gateway-bot:example.com"
password = "secret"
room_ids = ["!gateway-room:example.com"]
allowed_users = ["@you:example.com"]
# Gateway-specific: poll interval and on/off switch
aggregated_notifications_poll_interval_secs = 5 # default
aggregated_notifications_enabled = true # default</code></pre>
<h3>Aggregated stream settings</h3>
<table>
<thead>
<tr><th>Key</th><th>Type</th><th>Default</th><th>Description</th></tr>
</thead>
<tbody>
<tr>
<td>aggregated_notifications_enabled</td>
<td>bool</td>
<td><code>true</code></td>
<td>Set to <code>false</code> to disable the aggregated stream without disabling the gateway bot entirely. Per-project configs are never consulted.</td>
</tr>
<tr>
<td>aggregated_notifications_poll_interval_secs</td>
<td>integer</td>
<td><code>5</code></td>
<td>How often (in seconds) the gateway polls each project's <code>/api/events</code> endpoint. Lower values reduce notification latency.</td>
</tr>
</tbody>
</table>
<h3>No-duplicate guarantee</h3>
<p>Per-project bots and the gateway aggregated stream send to different rooms — they are independent. Events from a per-project bot go to that project's rooms; events from the gateway stream go to the gateway rooms. The same event will never appear twice in either room.</p>
<h3>Unreachable projects</h3>
<p>If a per-project server is temporarily unreachable, the gateway logs a warning and skips that project for the current poll cycle. All other projects continue to deliver notifications normally. No configuration change is required — the poller retries on the next interval.</p>
<h3>Supported event types</h3>
<p>The aggregated stream delivers the following event types, each prefixed with the project name:</p>
<ul>
<li><strong>Stage transitions</strong> — story created, agent started, QA requested, QA approved/rejected, merge succeeded (all pipeline stage moves)</li>
<li><strong>Merge failures</strong> — merge failed with a reason</li>
<li><strong>Story blocked</strong> — story blocked after exceeding retry limit</li>
</ul>
</main>
</div>
<footer class="reveal r3">
<span>&copy; 2026 Libby Labs Ltd.</span>
<a href="/privacy.html">Privacy Policy</a>
</footer>
</div>
</body>
</html>