huskies: merge 1113 story [huskies-server repo] Convert static website to Next.js with static rendering

This commit is contained in:
dave
2026-05-17 15:46:21 +00:00
parent 0695ad7ae6
commit 6e4fb7fd4b
23 changed files with 3849 additions and 1 deletions
+459
View File
@@ -0,0 +1,459 @@
/** Configuration reference — project.toml, agents.toml, and bot.toml docs. */
import type { Metadata } from 'next'
/** Page metadata for the configuration reference. */
export const metadata: Metadata = {
title: 'Configuration — Huskies Docs',
description: 'Reference for project.toml, agents.toml, and bot.toml configuration files.',
}
/** Renders the configuration reference for all huskies TOML files. */
export default function ConfigurationPage() {
return (
<>
<h1 className="page-title">Configuration</h1>
<p className="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>&quot;server&quot;</code>
</td>
<td>
Default QA mode. One of <code>&quot;server&quot;</code> (automated gate run),{' '}
<code>&quot;agent&quot;</code> (spawn a QA agent), or <code>&quot;human&quot;</code> (manual
approval).
</td>
</tr>
<tr>
<td>default_coder_model</td>
<td>string</td>
<td>
<code>&quot;sonnet&quot;</code>
</td>
<td>
Default model for coder agents. Only agents matching this model are auto-assigned. Use{' '}
<code>&quot;opus&quot;</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>&quot;UTC&quot;</code>
</td>
<td>
IANA timezone for timer scheduling (e.g. <code>&quot;Europe/London&quot;</code>,{' '}
<code>&quot;America/New_York&quot;</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&apos;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>&quot;coder-1&quot;</code>,{' '}
<code>&quot;qa&quot;</code>).
</td>
</tr>
<tr>
<td>stage</td>
<td>
Pipeline stage this agent handles. One of <code>&quot;coder&quot;</code>,{' '}
<code>&quot;qa&quot;</code>, or <code>&quot;mergemaster&quot;</code>.
</td>
</tr>
<tr>
<td>role</td>
<td>
Human-readable description of the agent&apos;s responsibilities (shown in status output).
</td>
</tr>
<tr>
<td>model</td>
<td>
Claude model to use. One of <code>&quot;sonnet&quot;</code> (claude-sonnet-4-6) or{' '}
<code>&quot;opus&quot;</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&apos;s ID slug (e.g. <code>42_story_add_login</code>).
</td>
</tr>
<tr>
<td>{'{{worktree_path}}'}</td>
<td>Absolute path to the agent&apos;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&apos;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&apos;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 &mdash; 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 &mdash; 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 &mdash; 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="/docs/transports">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>&quot;matrix&quot;</code>, <code>&quot;whatsapp&quot;</code>,{' '}
<code>&quot;slack&quot;</code>, or <code>&quot;discord&quot;</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&apos;s</strong>{' '}
<code>.huskies/bot.toml</code> &mdash; 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>{`# <gateway-config-dir>/.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&apos;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 &mdash; they are
independent. Events from a per-project bot go to that project&apos;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 &mdash; 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> &mdash; story created, agent started, QA requested, QA
approved/rejected, merge succeeded (all pipeline stage moves)
</li>
<li>
<strong>Merge failures</strong> &mdash; merge failed with a reason
</li>
<li>
<strong>Story blocked</strong> &mdash; story blocked after exceeding retry limit
</li>
</ul>
</>
)
}