diff --git a/Cargo.lock b/Cargo.lock index 7b66911a..ddfd6e11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1931,7 +1931,6 @@ dependencies = [ "libc", "libsqlite3-sys", "matrix-sdk", - "mime_guess", "mockito", "notify", "nutype", @@ -1941,7 +1940,6 @@ dependencies = [ "rand 0.10.1", "regex", "reqwest", - "rust-embed", "serde", "serde_json", "serde_urlencoded", @@ -2978,16 +2976,6 @@ version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbf6f36070878c42c5233846cd3de24cf9016828fd47bc22957a687298bb21fc" -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -4206,40 +4194,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rust-embed" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.117", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" -dependencies = [ - "sha2 0.10.9", - "walkdir", -] - [[package]] name = "rustc-hash" version = "2.1.2" diff --git a/server/Cargo.toml b/server/Cargo.toml index 42579ec8..0006e70b 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,12 +13,10 @@ chrono-tz = { workspace = true } futures = { workspace = true } homedir = { workspace = true } ignore = { workspace = true } -mime_guess = { workspace = true } notify = { workspace = true } poem = { workspace = true, features = ["websocket"] } portable-pty = { workspace = true } reqwest = { workspace = true, features = ["json", "stream", "form"] } -rust-embed = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_urlencoded = { workspace = true } diff --git a/server/src/gateway/mod.rs b/server/src/gateway/mod.rs index f9885bda..cd0178d6 100644 --- a/server/src/gateway/mod.rs +++ b/server/src/gateway/mod.rs @@ -62,13 +62,6 @@ pub fn build_gateway_route(state_arc: Arc) -> impl poem::Endpoint "/gateway/agents/:id/assign", poem::post(gateway_assign_agent_handler), ) - // Serve the embedded React frontend so the gateway has a UI. - .at( - "/assets/*path", - poem::get(crate::http::assets::embedded_asset), - ) - .at("/*path", poem::get(crate::http::assets::embedded_file)) - .at("/", poem::get(crate::http::assets::embedded_index)) .data(state_arc) } diff --git a/server/src/http/assets.rs b/server/src/http/assets.rs deleted file mode 100644 index 8bca8575..00000000 --- a/server/src/http/assets.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Static asset serving — serves the embedded React frontend via `rust-embed`. -use poem::{ - Response, handler, - http::{StatusCode, header}, - web::Path, -}; -use rust_embed::RustEmbed; - -#[derive(RustEmbed)] -#[folder = "../frontend/dist"] -struct EmbeddedAssets; - -fn serve_embedded(path: &str) -> Response { - let normalized = if path.is_empty() { - "index.html" - } else { - path.trim_start_matches('/') - }; - - let is_asset_request = normalized.starts_with("assets/"); - let asset = if is_asset_request { - EmbeddedAssets::get(normalized) - } else { - EmbeddedAssets::get(normalized).or_else(|| { - if normalized == "index.html" { - None - } else { - EmbeddedAssets::get("index.html") - } - }) - }; - - match asset { - Some(content) => { - let body = content.data.into_owned(); - let mime = mime_guess::from_path(normalized) - .first_or_octet_stream() - .to_string(); - - Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, mime) - .body(body) - } - None => Response::builder() - .status(StatusCode::NOT_FOUND) - .body("Not Found"), - } -} - -/// Serve a single embedded asset from the `assets/` folder. -#[handler] -pub fn embedded_asset(Path(path): Path) -> Response { - let asset_path = format!("assets/{path}"); - serve_embedded(&asset_path) -} - -/// Serve an embedded file by path (falls back to `index.html` for SPA routing). -#[handler] -pub fn embedded_file(Path(path): Path) -> Response { - serve_embedded(&path) -} - -/// Serve the embedded SPA entrypoint. -#[handler] -pub fn embedded_index() -> Response { - serve_embedded("index.html") -} - -#[cfg(test)] -mod tests { - use super::*; - use poem::http::StatusCode; - - #[test] - fn non_asset_path_spa_fallback_or_not_found() { - // Non-asset paths fall back to index.html for SPA client-side routing. - // In release builds (with embedded dist/) this returns 200. - // In debug builds without a built frontend dist/ it returns 404. - let response = serve_embedded("__nonexistent_spa_route__.html"); - let status = response.status(); - assert!( - status == StatusCode::OK || status == StatusCode::NOT_FOUND, - "unexpected status: {status}", - ); - } - - #[test] - fn missing_asset_path_prefix_returns_not_found() { - // assets/ prefix: no SPA fallback – returns 404 if the file does not exist - let response = serve_embedded("assets/__nonexistent__.js"); - assert_eq!(response.status(), StatusCode::NOT_FOUND); - } - - #[test] - fn serve_embedded_does_not_panic_on_empty_path() { - // Empty path normalises to index.html; OK in release, 404 in debug without dist/ - let response = serve_embedded(""); - let status = response.status(); - assert!( - status == StatusCode::OK || status == StatusCode::NOT_FOUND, - "unexpected status: {status}", - ); - } - - #[test] - fn embedded_assets_struct_is_iterable() { - // Verifies that rust-embed compiled the EmbeddedAssets struct correctly. - // In debug builds without a built frontend dist/ directory the iterator is empty; that is - // expected. In release builds it will contain all bundled frontend files. - let _files: Vec<_> = EmbeddedAssets::iter().collect(); - // No assertion needed – the test passes as long as it compiles and does not panic. - } - - #[tokio::test] - async fn embedded_index_handler_returns_ok_or_not_found() { - // Route the handler through TestClient; index.html is the SPA entry point. - let app = poem::Route::new().at("/", poem::get(embedded_index)); - let cli = poem::test::TestClient::new(app); - let resp = cli.get("/").send().await; - let status = resp.0.status(); - assert!( - status == StatusCode::OK || status == StatusCode::NOT_FOUND, - "unexpected status: {status}", - ); - } - - #[tokio::test] - async fn embedded_file_handler_with_path_returns_ok_or_not_found() { - // Non-asset paths fall back to index.html (SPA routing) or 404. - let app = poem::Route::new().at("/*path", poem::get(embedded_file)); - let cli = poem::test::TestClient::new(app); - let resp = cli.get("/__spa_route__").send().await; - let status = resp.0.status(); - assert!( - status == StatusCode::OK || status == StatusCode::NOT_FOUND, - "unexpected status: {status}", - ); - } - - #[tokio::test] - async fn embedded_asset_handler_missing_file_returns_not_found() { - // The assets/ prefix disables SPA fallback; missing files must return 404. - let app = poem::Route::new().at("/assets/*path", poem::get(embedded_asset)); - let cli = poem::test::TestClient::new(app); - let resp = cli.get("/assets/__nonexistent__.js").send().await; - assert_eq!(resp.0.status(), StatusCode::NOT_FOUND); - } -} diff --git a/server/src/http/mod.rs b/server/src/http/mod.rs index bc13afcf..07dcfab7 100644 --- a/server/src/http/mod.rs +++ b/server/src/http/mod.rs @@ -1,8 +1,6 @@ //! HTTP server — module declarations for all REST, MCP, WebSocket, and SSE endpoints. /// Server-sent event stream for real-time agent output. pub mod agents_sse; -/// Static asset serving (embedded frontend files). -pub mod assets; /// Shared application context threaded through handlers. pub mod context; /// Server-sent event stream for pipeline/watcher events. @@ -100,10 +98,7 @@ pub fn build_routes( get(oauth::oauth_callback).data(oauth_state.clone()), ) .at("/oauth/status", get(oauth::oauth_status)) - .at("/debug/crdt", get(debug_crdt_handler)) - .at("/assets/*path", get(assets::embedded_asset)) - .at("/", get(assets::embedded_index)) - .at("/*path", get(assets::embedded_file)); + .at("/debug/crdt", get(debug_crdt_handler)); if let Some(buf) = event_buffer { route = route.at("/api/events", get(events::events_handler).data(buf)); diff --git a/website/.gitignore b/website/.gitignore deleted file mode 100644 index c03a1711..00000000 --- a/website/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -.next/ -out/ -next-env.d.ts diff --git a/website/README.md b/website/README.md deleted file mode 100644 index 45c65894..00000000 --- a/website/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Huskies Website - -Static marketing and documentation site, built with Next.js (`output: 'export'`). - -## Development - -```bash -npm install -npm run dev # starts dev server at http://localhost:3000 -``` - -## Build - -```bash -npm install -npm run build # produces out/ directory -``` - -The `out/` directory is a fully static export — plain HTML, CSS, JS, and assets. No Node.js required at runtime. - -## Deploy - -Serve the `out/` directory with any static file server. nginx example: - -```nginx -server { - listen 80; - server_name huskies.dev; - root /path/to/out; - index index.html; - - location / { - try_files $uri $uri.html $uri/ =404; - } -} -``` - -## Pages - -| Route | File | -|-------|------| -| `/` | `app/page.tsx` | -| `/privacy` | `app/privacy/page.tsx` | -| `/docs` | `app/docs/page.tsx` | -| `/docs/quickstart` | `app/docs/quickstart/page.tsx` | -| `/docs/pipeline` | `app/docs/pipeline/page.tsx` | -| `/docs/commands` | `app/docs/commands/page.tsx` | -| `/docs/configuration` | `app/docs/configuration/page.tsx` | -| `/docs/cli` | `app/docs/cli/page.tsx` | -| `/docs/transports` | `app/docs/transports/page.tsx` | - -## Tech - -- Next.js 14 App Router with `output: 'export'` -- TypeScript -- `next/font/google` for self-hosted Bricolage Grotesque and Karla fonts -- No server components with dynamic data, no API routes, no server actions diff --git a/website/app/docs/cli/page.tsx b/website/app/docs/cli/page.tsx deleted file mode 100644 index a7066911..00000000 --- a/website/app/docs/cli/page.tsx +++ /dev/null @@ -1,384 +0,0 @@ -/** CLI reference — flags and subcommands for the huskies binary. */ -import type { Metadata } from 'next' - -/** Page metadata for the CLI reference. */ -export const metadata: Metadata = { - title: 'CLI Reference — Huskies Docs', - description: 'Command-line reference for huskies, huskies init, and related subcommands.', -} - -/** Renders the CLI reference for huskies command-line flags. */ -export default function CliPage() { - return ( - <> -

CLI Reference

-

- Huskies ships as a single binary. Most interaction happens through the web UI or chat transports, - but the CLI is used for initial setup and server control. -

- -

huskies

-

Start the huskies server.

-
-        huskies [OPTIONS]
-      
- -

Options

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FlagDefaultDescription
--port <PORT>3000 - HTTP port to listen on. Set the HUSKIES_PORT environment variable as an - alternative. -
--project <PATH>current dir - Path to the project directory. Huskies looks for .huskies/ here. -
--helpPrint help and exit.
--versionPrint version and exit.
- -

Examples

-
-        {`# Start on the default port
-huskies
-
-# Start on a custom port
-huskies --port 3001
-
-# Specify project directory explicitly
-huskies --project /path/to/project --port 3000
-
-# Using environment variable
-HUSKIES_PORT=3002 huskies`}
-      
- -
- Multiple instances: Each worktree or project can run its own huskies instance on a - different port. Use HUSKIES_PORT to avoid conflicts when running several instances - simultaneously. -
- -

huskies init

-

- Initialise a project directory for use with huskies. Creates the .huskies/ directory - structure, default configuration files, and .mcp.json. -

-
-        huskies init [OPTIONS]
-      
- -

Options

- - - - - - - - - - - - - - - - - - - - - - - - - -
FlagDefaultDescription
--port <PORT>3000 - Port written into .mcp.json for MCP tool discovery. -
--project <PATH>current dirDirectory to initialise. Must be a git repository.
--helpPrint help and exit.
- -

What it creates

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PathDescription
- .huskies/project.toml - Project-wide settings (QA mode, agent limits, timezone, etc.).
- .huskies/agents.toml - Agent definitions for coder, QA, and mergemaster roles.
- .huskies/work/1_backlog/ - Pipeline stage directories (1 through 6).
- .huskies/specs/00_CONTEXT.md - Placeholder project context file for the setup wizard.
- .huskies/specs/tech/STACK.md - Placeholder tech stack file for the setup wizard.
- .mcp.json - - MCP server config so Claude Code discovers huskies' tools automatically. -
- -
- Git required: The project directory must be a git repository. Run{' '} - git init first if needed. -
- -

huskies agent

-

- Spawn a single agent process directly from the command line. This is the command the server uses - internally when you run start <number> in chat — you rarely need to invoke - it manually. -

-
-        huskies agent [OPTIONS]
-      
- -

Options

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FlagDescription
--story <ID> - Story ID slug to work on (e.g. 42_story_add_login). -
--agent <NAME> - Agent name from agents.toml to use (e.g. coder-1,{' '} - qa). -
--worktree <PATH>Path to the git worktree the agent should work in.
--port <PORT>Huskies server port, so the agent can call MCP tools.
--helpPrint help and exit.
- -

Environment variables

- - - - - - - - - - - - - - - - - - - - - -
VariableDescription
- HUSKIES_PORT - - Server port. Overrides the --port flag. -
- ANTHROPIC_API_KEY - - Anthropic API key for agent sessions. Can also be set via the web UI on first use. -
- GITEA_TOKEN - - Gitea API token used by the script/release script when publishing releases. -
- -

Gateway event-push protocol

-

- Project nodes can push pipeline status events to the gateway in real time over a WebSocket - connection. The gateway fans each event out to all connected local subscribers. -

- -

Connecting

-
    -
  1. - Obtain a one-time join token: POST /gateway/tokens →{' '} - {'{"token":"…"}'} -
  2. -
  3. - Open a WebSocket upgrade to{' '} - GET /gateway/events/push?token=TOKEN&project=PROJECT_NAME -
  4. -
  5. - The token is consumed on upgrade. The project name is attached to every event the server - broadcasts downstream. -
  6. -
- -

Sending events

-

- Each message must be a JSON-encoded StoredEvent frame: -

-
-        {`// Stage transition
-{"type":"stage_transition","story_id":"42_story_login","from_stage":"2_current","to_stage":"3_qa","timestamp_ms":1700000000000}
-
-// Merge failure
-{"type":"merge_failure","story_id":"42_story_login","reason":"conflict in src/main.rs","timestamp_ms":1700000001000}
-
-// Story blocked
-{"type":"story_blocked","story_id":"42_story_login","reason":"retry limit exceeded","timestamp_ms":1700000002000}`}
-      
-

- The server does not send frames back. Any other frames received by the project node indicate an - error or server restart — treat them as a disconnect signal. -

- -

Reconnect with exponential back-off

-

- Project nodes must reconnect on any disconnect. Use the following policy to avoid - thundering herds after a gateway restart: -

- - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterValue
Initial delay1 s
Back-off multiplier2× per attempt
Maximum delay60 s
Jitter±10 % of the computed delay
-

Pseudocode:

-
-        {`delay = 1.0          // seconds
-max_delay = 60.0
-
-loop:
-    token = POST /gateway/tokens
-    connect ws:/gateway/events/push?token=TOKEN&project=NAME
-    while connected:
-        send StoredEvent frames
-    // disconnected — wait and retry
-    jitter = delay * (random(0.9, 1.1))
-    sleep(min(jitter, max_delay))
-    delay = min(delay * 2, max_delay)`}
-      
- -
- New token per connection: Each WebSocket upgrade consumes the join token. Request a - fresh token for every reconnect attempt. -
- -

Building from source

-

Standard release build

-
-        {`cargo build --release\n# Output: target/release/huskies`}
-      
- -

Static Linux binary (musl)

-

- Requires cross: cargo install cross. -

-
-        cross build --release --target x86_64-unknown-linux-musl
-      
- -

Docker image

-
-        docker compose -f docker/docker-compose.yml build
-      
- -

Release script

-

- Builds macOS arm64 and Linux amd64 binaries, bumps the version, tags the repo, and publishes a - Gitea release with changelog and binaries attached. -

-
-        script/release 0.8.0
-      
- - ) -} diff --git a/website/app/docs/commands/page.tsx b/website/app/docs/commands/page.tsx deleted file mode 100644 index 9defb66b..00000000 --- a/website/app/docs/commands/page.tsx +++ /dev/null @@ -1,209 +0,0 @@ -/** Bot commands reference — full list of chat transport commands. */ -import type { Metadata } from 'next' - -/** Page metadata for the bot commands reference. */ -export const metadata: Metadata = { - title: 'Bot Commands — Huskies Docs', - description: - 'Full reference of huskies bot commands available in Matrix, Slack, WhatsApp, Discord, and the web UI.', -} - -/** Renders the full bot commands reference page. */ -export default function CommandsPage() { - return ( - <> -

Bot Commands

-

- Commands available in every chat transport (Matrix, Slack, WhatsApp, Discord) and the built-in web - UI. Commands are case-insensitive. Run help in any chat to see the list. -

- -
- How to invoke: In chat rooms, address the bot first (e.g.{' '} - @huskies start 42) or enable ambient mode so it responds to all messages. In the web - UI, type commands directly. -
- -

Pipeline management

-
-
-
status
-
- Show pipeline status and agent availability. Use status <number> for a - detailed triage dump on a specific story. -
-
-
-
start
-
- Start an agent on a story: start <number>. To use the opus model:{' '} - start <number> opus. -
-
-
-
move
-
- Move a work item to a pipeline stage: move <number> <stage>. Stages:{' '} - backlog, current, qa, merge,{' '} - done. -
-
-
-
show
-
- Display the full text of a work item: show <number>. -
-
-
-
delete
-
- Remove a work item from the pipeline: delete <number>. -
-
-
-
unblock
-
- Reset a blocked story: unblock <number>. Clears the blocked flag and resets - the retry count. -
-
-
-
assign
-
- Pre-assign a model to a story before starting: assign <number> <model>{' '} - (e.g. assign 42 opus). -
-
-
-
backlog
-
- Show all items in the backlog with dependency satisfaction status — which are ready to - start and which are still waiting on other stories. -
-
-
-
depends
-
- Set story dependencies: depends <number> [dep1 dep2 ...]. Call with no deps - to clear all dependencies. -
-
-
-
timer
-
- Schedule a deferred agent start: timer <number> HH:MM. List all timers:{' '} - timer list. Cancel: timer cancel <number>. Times are interpreted - in the project timezone. -
-
-
- -

Worktrees

-
-
-
rmtree
-
- Delete the worktree for a story without removing the story from the pipeline:{' '} - rmtree <number>. Useful for freeing disk space on a story that needs to be - restarted. -
-
-
- -

Observability

-
-
-
cost
-
- Show token spend: 24h total, top stories, breakdown by agent type, and all-time total. -
-
-
-
coverage
-
- Show test coverage from the cached baseline. Use coverage run to rerun the full - test suite and regenerate the report. -
-
-
-
git
-
- Show git status for the main repository: current branch, uncommitted changes, and ahead/behind - remote. -
-
-
-
htop
-
- Live system and agent process dashboard. Use htop to start,{' '} - htop 10m to run for 10 minutes, htop stop to stop. -
-
-
-
run_tests
-
- Run the project's test suite (script/test) and show a pass/fail summary with - output. Use run_tests <number> to run tests inside a specific story's - worktree instead of the project root. -
-
-
-
loc
-
- Show top source files by line count: loc (top 10), loc <N> for - N files, or loc <filepath> for a specific file. -
-
-
-
overview
-
- Show an implementation summary for a merged story: overview <number>. -
-
-
-
unreleased
-
- Show stories merged to master since the last release tag. -
-
-
- -

Server management

-
-
-
rebuild
-
Rebuild the huskies server binary and restart the process.
-
-
-
reset
-
- Clear the current Claude Code session and start a fresh context window. -
-
-
- -

Setup & configuration

-
-
-
setup
-
- Show setup wizard progress. Drive the wizard from chat: setup generate,{' '} - setup confirm, setup skip, setup retry. -
-
-
-
ambient
-
- Toggle ambient mode for the current room: ambient on or{' '} - ambient off. In ambient mode the bot responds to all messages, not just addressed - ones. -
-
-
-
help
-
Show the list of available commands.
-
-
- - ) -} diff --git a/website/app/docs/configuration/page.tsx b/website/app/docs/configuration/page.tsx deleted file mode 100644 index 0dac07d7..00000000 --- a/website/app/docs/configuration/page.tsx +++ /dev/null @@ -1,459 +0,0 @@ -/** 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 ( - <> -

Configuration

-

- Huskies is configured via three TOML files in your .huskies/ directory. All files are - created by huskies init with sensible defaults. -

- -

project.toml

-

- Project-wide settings. Lives at .huskies/project.toml. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyTypeDefaultDescription
default_qastring - "server" - - Default QA mode. One of "server" (automated gate run),{' '} - "agent" (spawn a QA agent), or "human" (manual - approval). -
default_coder_modelstring - "sonnet" - - Default model for coder agents. Only agents matching this model are auto-assigned. Use{' '} - "opus" on individual stories for complex tasks. -
max_codersinteger - 3 - - Maximum concurrent coder agents. Stories wait in 2_current/ when all slots are - full. -
max_retriesinteger - 3 - - Maximum retries per story per pipeline stage before marking it as blocked. Set to{' '} - 0 to disable. -
base_branchstringauto-detected - Base branch for merges and agent prompts. When unset, huskies reads the current HEAD branch. -
rate_limit_notificationsbool - false - - Send chat notifications when API soft rate limits are hit. Hard blocks and story-blocked - notifications are always sent. -
timezonestring - "UTC" - - IANA timezone for timer scheduling (e.g. "Europe/London",{' '} - "America/New_York"). Timer HH:MM inputs are interpreted in this - timezone. -
- -

Component setup

-

- The [[component]] 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. -

-
-        {`[[component]]
-name = "frontend"
-path = "frontend"
-setup = ["npm ci", "npm run build"]
-teardown = []
-
-[[component]]
-name = "server"
-path = "."
-setup = ["mkdir -p frontend/dist", "cargo check"]
-teardown = []`}
-      
- -

Story front matter overrides

-

Individual stories can override project defaults using YAML front matter:

-
-        {`---
-name: "My Complex Story"
-qa: agent        # override default_qa
-agent: opus      # use the opus coder agent
----`}
-      
- -

agents.toml

-

- Agent definitions. Lives at .huskies/agents.toml. Each [[agent]] block - defines one agent slot. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyDescription
name - Unique identifier for this agent slot (e.g. "coder-1",{' '} - "qa"). -
stage - Pipeline stage this agent handles. One of "coder",{' '} - "qa", or "mergemaster". -
role - Human-readable description of the agent's responsibilities (shown in status output). -
model - Claude model to use. One of "sonnet" (claude-sonnet-4-6) or{' '} - "opus" (claude-opus-4-6). -
max_turnsMaximum conversation turns before the agent is forcefully stopped.
max_budget_usdMaximum API spend in USD before the agent is stopped.
prompt - The initial user-turn prompt sent to the agent. Supports template variables (see below). -
system_promptThe system prompt sent to the agent session.
- -

Template variables

-

- The following variables are interpolated into prompt and system_prompt at - agent start time: -

- - - - - - - - - - - - - - - - - - - - - -
VariableDescription
{'{{story_id}}'} - The story's ID slug (e.g. 42_story_add_login). -
{'{{worktree_path}}'}Absolute path to the agent's git worktree.
{'{{base_branch}}'} - The base branch name from project.toml. -
- -

Example: adding an opus coder

-
-        {`[[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 ..."`}
-      
-

- To use this agent for a specific story, add agent: opus to the story's front - matter, or run start <number> opus in chat. -

- -

- Project-local agent prompt (.huskies/AGENT.md) -

-

- Place a file at .huskies/AGENT.md in your project root to append project-specific - guidance to every agent's initial prompt at spawn time. -

- -

How it works

-
    -
  • - Huskies reads .huskies/AGENT.md each time an agent is spawned — no caching, no - restart required. -
  • -
  • - The file content is appended after the baked-in agent prompt, so project guidance - refines core instructions without overriding them. -
  • -
  • Applies to all agent roles: coder, QA, mergemaster, and supervisor.
  • -
  • - If the file is missing or empty, agents spawn normally — no warnings, no errors. -
  • -
  • - When the file exists and is non-empty, a single INFO log line is emitted showing the - file path and byte count. -
  • -
- -

Ordering

-
    -
  1. - Baked-in agent prompt (from agents.toml or project.toml) -
  2. -
  3. - Project-local content from .huskies/AGENT.md -
  4. -
  5. Resume context (only on agent restart after a gate failure)
  6. -
- -

Example

-
-        {`# .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.`}
-      
-

- Edit the file at any time — the next agent spawn picks up the latest content automatically. -

- -

bot.toml

-

- Chat transport configuration. Lives at .huskies/bot.toml. This file is gitignored as - it contains credentials. Copy the appropriate example file to get started: -

-
-        cp .huskies/bot.toml.matrix.example .huskies/bot.toml
-      
-

- Only one transport can be active at a time. See the{' '} - Chat transports guide for setup instructions for each platform. -

- -

Common fields

- - - - - - - - - - - - - - - - - - - - - - - - - -
KeyDescription
enabled - Set to true to activate the bot. Set to false to disable without - removing the file. -
transport - Transport type: "matrix", "whatsapp",{' '} - "slack", or "discord". -
display_nameOptional. Bot display name in chat messages.
history_size - Optional. Maximum conversation turns to remember per room/user (default: 20). -
- -

Gateway: aggregated chat stream

-

- When running huskies --gateway, you can configure a single bot that receives pipeline - notifications from all registered projects. Events are prefixed with{' '} - [project-name] so you can tell them apart in one shared room. -

-

- The aggregated stream is configured entirely in the gateway's{' '} - .huskies/bot.toml — no per-project bot config is required and no per-project - files need to change when you add a new project to projects.toml. -

- -

Enabling the aggregated stream

-

- Add or edit <gateway-config-dir>/.huskies/bot.toml and set{' '} - enabled = true. The gateway bot will automatically poll every project listed in{' '} - projects.toml and forward events to the configured rooms. -

-
-        {`# /.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`}
-      
- -

Aggregated stream settings

- - - - - - - - - - - - - - - - - - - - - - - -
KeyTypeDefaultDescription
aggregated_notifications_enabledbool - true - - Set to false to disable the aggregated stream without disabling the gateway bot - entirely. Per-project configs are never consulted. -
aggregated_notifications_poll_interval_secsinteger - 5 - - How often (in seconds) the gateway polls each project's /api/events{' '} - endpoint. Lower values reduce notification latency. -
- -

No-duplicate guarantee

-

- 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. -

- -

Unreachable projects

-

- 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. -

- -

Supported event types

-

- The aggregated stream delivers the following event types, each prefixed with the project name: -

-
    -
  • - Stage transitions — story created, agent started, QA requested, QA - approved/rejected, merge succeeded (all pipeline stage moves) -
  • -
  • - Merge failures — merge failed with a reason -
  • -
  • - Story blocked — story blocked after exceeding retry limit -
  • -
- - ) -} diff --git a/website/app/docs/docs.css b/website/app/docs/docs.css deleted file mode 100644 index 53b71834..00000000 --- a/website/app/docs/docs.css +++ /dev/null @@ -1,431 +0,0 @@ -/* Outer shell */ -.shell { - max-width: 1100px; - margin: 0 auto; - padding: 0 2rem; -} - -@media (max-width: 640px) { - .shell { padding: 0 1.25rem; } -} - -/* Header (docs) */ -.shell header { - padding: 2rem 0; - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid var(--border); - margin-bottom: 0; -} - -.shell header nav a.active { color: var(--cyan); } - -/* Docs layout */ -.docs-layout { - display: grid; - grid-template-columns: 220px 1fr; - gap: 0; - min-height: calc(100vh - 80px); -} - -/* Sidebar */ -.sidebar { - border-right: 1px solid var(--border); - padding: 2.5rem 0 2.5rem 0; - position: sticky; - top: 0; - height: 100vh; - overflow-y: auto; -} - -.sidebar-section { - margin-bottom: 2rem; -} - -.sidebar-heading { - font-family: var(--display); - font-size: 0.6rem; - font-weight: 700; - letter-spacing: 0.14em; - text-transform: uppercase; - color: var(--text-dim); - padding: 0 1.5rem; - margin-bottom: 0.5rem; -} - -.sidebar nav a { - display: block; - padding: 0.4rem 1.5rem; - font-size: 0.83rem; - color: var(--text-secondary); - transition: color 0.15s, background 0.15s; - border-left: 2px solid transparent; -} - -.sidebar nav a:hover { - color: var(--text); - opacity: 1; -} - -.sidebar nav a.active { - color: var(--cyan); - border-left-color: var(--cyan); - background: var(--cyan-dim); -} - -/* Main content */ -.docs-main { - padding: 2.5rem 3rem 4rem; - max-width: 780px; -} - -@media (max-width: 768px) { - .docs-layout { grid-template-columns: 1fr; } - .sidebar { - position: static; - height: auto; - border-right: none; - border-bottom: 1px solid var(--border); - padding: 1.5rem 0; - } - .docs-main { padding: 2rem 0 3rem; } -} - -/* Typography */ -.page-title { - font-family: var(--display); - font-size: 2rem; - font-weight: 800; - letter-spacing: -0.03em; - margin-bottom: 0.5rem; - line-height: 1.2; -} - -.page-subtitle { - font-size: 1rem; - color: var(--text-secondary); - font-weight: 300; - margin-bottom: 3rem; - line-height: 1.7; - max-width: 560px; -} - -h2 { - font-family: var(--display); - font-size: 1.25rem; - font-weight: 700; - letter-spacing: -0.02em; - margin: 3rem 0 1rem; - padding-top: 1rem; - border-top: 1px solid var(--border); - scroll-margin-top: 2rem; -} - -h2:first-of-type { - margin-top: 0; - padding-top: 0; - border-top: none; -} - -h3 { - font-family: var(--display); - font-size: 0.95rem; - font-weight: 600; - margin: 1.8rem 0 0.5rem; -} - -p { - font-size: 0.9rem; - color: var(--text-secondary); - line-height: 1.8; - margin-bottom: 1rem; -} - -p strong { - color: var(--text); - font-weight: 600; -} - -ul, ol { - padding-left: 1.4rem; - margin-bottom: 1rem; -} - -li { - font-size: 0.9rem; - color: var(--text-secondary); - line-height: 1.8; - margin-bottom: 0.2rem; -} - -li strong { - color: var(--text); - font-weight: 600; -} - -/* Code */ -code { - font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; - font-size: 0.8rem; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 4px; - padding: 0.1em 0.4em; - color: var(--cyan); -} - -pre { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 6px; - padding: 1.2rem 1.4rem; - overflow-x: auto; - margin: 1rem 0 1.5rem; -} - -pre code { - background: none; - border: none; - padding: 0; - font-size: 0.82rem; - color: var(--text); - line-height: 1.7; -} - -/* Tables */ -table { - width: 100%; - border-collapse: collapse; - margin: 1rem 0 1.5rem; - font-size: 0.85rem; -} - -th { - text-align: left; - font-family: var(--display); - font-size: 0.72rem; - font-weight: 700; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--text-dim); - padding: 0.6rem 1rem; - border-bottom: 1px solid var(--border); -} - -td { - padding: 0.65rem 1rem; - border-bottom: 1px solid var(--border); - color: var(--text-secondary); - vertical-align: top; - line-height: 1.6; -} - -td:first-child { - font-family: 'SF Mono', 'Fira Code', monospace; - font-size: 0.8rem; - color: var(--cyan); - white-space: nowrap; -} - -tr:last-child td { border-bottom: none; } - -/* Callout / note box */ -.note { - background: var(--cyan-dim); - border: 1px solid rgba(34, 211, 238, 0.2); - border-radius: 6px; - padding: 1rem 1.2rem; - margin: 1.2rem 0; - font-size: 0.85rem; - color: var(--text-secondary); - line-height: 1.7; -} - -.note strong { - color: var(--cyan); - font-weight: 600; -} - -/* Step list */ -.step-list { - list-style: none; - padding: 0; - counter-reset: steps; -} - -.step-list li { - counter-increment: steps; - display: grid; - grid-template-columns: 40px 1fr; - gap: 1rem; - padding: 1.2rem 0; - border-bottom: 1px solid var(--border); - align-items: start; -} - -.step-list li:first-child { border-top: 1px solid var(--border); } - -.step-list li::before { - content: counter(steps, decimal-leading-zero); - font-family: var(--display); - font-size: 0.72rem; - font-weight: 700; - color: var(--text-dim); - padding-top: 0.15rem; -} - -/* Command cards */ -.cmd-grid { - display: flex; - flex-direction: column; - gap: 1px; - background: var(--border); - border: 1px solid var(--border); - border-radius: 6px; - overflow: hidden; - margin: 1rem 0 1.5rem; -} - -.cmd-row { - display: grid; - grid-template-columns: 160px 1fr; - background: var(--surface); - transition: background 0.2s; -} - -.cmd-row:hover { background: var(--surface-hover); } - -.cmd-name { - padding: 0.9rem 1.1rem; - font-family: 'SF Mono', 'Fira Code', monospace; - font-size: 0.8rem; - color: var(--cyan); - border-right: 1px solid var(--border); - display: flex; - align-items: center; -} - -.cmd-desc { - padding: 0.9rem 1.1rem; - font-size: 0.84rem; - color: var(--text-secondary); - line-height: 1.6; - display: flex; - align-items: center; -} - -/* Docs index cards */ -.doc-cards { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1px; - background: var(--border); - border: 1px solid var(--border); - border-radius: 6px; - overflow: hidden; - margin-top: 2rem; -} - -.doc-card { - background: var(--surface); - padding: 1.6rem; - transition: background 0.2s; - text-decoration: none; - display: block; - color: inherit; -} - -.doc-card:hover { background: var(--surface-hover); opacity: 1; } - -.doc-card-title { - font-family: var(--display); - font-size: 0.95rem; - font-weight: 600; - color: var(--text); - margin-bottom: 0.4rem; -} - -.doc-card-desc { - font-size: 0.82rem; - color: var(--text-secondary); - line-height: 1.6; -} - -@media (max-width: 600px) { - .doc-cards { grid-template-columns: 1fr; } - .cmd-row { grid-template-columns: 130px 1fr; } -} - -/* Footer (docs) */ -.shell footer { - padding: 2rem 0; - border-top: 1px solid var(--border); - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.75rem; - color: var(--text-dim); - margin-top: 0; -} - -.shell footer a { - color: var(--text-dim); - font-size: 0.75rem; -} - -.shell footer a:hover { color: var(--text-secondary); } - -/* Pipeline stages (used in pipeline page) */ -.pipeline-stages { - display: flex; - flex-direction: column; - gap: 1px; - background: var(--border); - border: 1px solid var(--border); - border-radius: 6px; - overflow: hidden; - margin: 1.5rem 0; -} - -.stage-row { - display: grid; - grid-template-columns: 48px 140px 1fr; - gap: 0; - background: var(--surface); - transition: background 0.2s; - align-items: stretch; -} - -.stage-row:hover { background: var(--surface-hover); } - -.stage-num { - display: flex; - align-items: center; - justify-content: center; - font-family: var(--display); - font-size: 0.7rem; - font-weight: 700; - color: var(--text-dim); - border-right: 1px solid var(--border); -} - -.stage-name { - display: flex; - align-items: center; - padding: 1rem 1.1rem; - font-family: var(--display); - font-size: 0.88rem; - font-weight: 600; - color: var(--text); - border-right: 1px solid var(--border); -} - -.stage-desc { - padding: 1rem 1.2rem; - font-size: 0.85rem; - color: var(--text-secondary); - line-height: 1.6; -} - -.stage-row.active .stage-name { color: var(--cyan); } diff --git a/website/app/docs/layout.tsx b/website/app/docs/layout.tsx deleted file mode 100644 index b11bd682..00000000 --- a/website/app/docs/layout.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/** Shared layout for all docs pages: header, sidebar, and footer. */ -import type { Metadata } from 'next' -import DocsSidebar from '@/components/DocsSidebar' -import './docs.css' - -/** Default metadata for docs pages. */ -export const metadata: Metadata = { - title: 'Documentation — Huskies', -} - -/** Wraps all doc pages with the shared docs shell (header, sidebar, footer). */ -export default function DocsLayout({ children }: { children: React.ReactNode }) { - return ( - <> -
-
- - huskies - - -
-
- -
-
- -
{children}
-
- - -
- - ) -} diff --git a/website/app/docs/page.tsx b/website/app/docs/page.tsx deleted file mode 100644 index 068a0cc3..00000000 --- a/website/app/docs/page.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/** Docs index — overview and card links to all documentation sections. */ -import type { Metadata } from 'next' -import Link from 'next/link' - -/** Page metadata for the docs index. */ -export const metadata: Metadata = { - title: 'Documentation — Huskies', - description: - 'Huskies documentation: quickstart, configuration, bot commands, pipeline, and more.', -} - -/** Renders the documentation overview page with section cards. */ -export default function DocsIndexPage() { - return ( - <> -

- Documentation -

-

Huskies Docs

-

- Everything you need to set up and run huskies — a story-driven development pipeline that turns - coding agents into a disciplined team. -

- -
- -
Quickstart
-
- Install huskies, run the server, create your first story, and watch an agent implement it. -
- - -
Configuration
-
- Reference for project.toml, agents.toml, and bot.toml. -
- - -
Bot commands
-
- Full list of commands available in Matrix, Slack, WhatsApp, and the web UI. -
- - -
Pipeline stages
-
- How work items move from backlog through QA and merge to done. -
- - -
Chat transports
-
- Connect huskies to Matrix, WhatsApp, Slack, Discord, or the built-in web UI. -
- - -
CLI reference
-
- Command-line flags for huskies, huskies init, and{' '} - huskies agent. -
- -
- -

What is huskies?

-

- Huskies is a story-driven development server. You write stories (feature requests) with acceptance - criteria; huskies spawns coding agents in isolated git worktrees, runs them through quality gates, and - squash-merges the result to your main branch — all without you writing a line of code. -

-

- It ships as a single Rust binary with an embedded React frontend. No separate database or build - infrastructure required. -

- -

How it works

-
    -
  1. -
    - Write a story. Describe the change with acceptance criteria via the web UI, a chat - room (Matrix, WhatsApp, Slack), or by dropping a Markdown file in{' '} - .huskies/work/1_backlog/. -
    -
  2. -
  3. -
    - Agent picks it up. Run start <number> (or configure - auto-start). A coder agent creates a feature branch, implements the code, and writes tests against - your criteria. -
    -
  4. -
  5. -
    - Quality gates run. Linters, tests, and compilation checks run automatically when - the agent exits. Nothing moves forward until everything passes. -
    -
  6. -
  7. -
    - QA review. A QA agent verifies each acceptance criterion, runs your test suite, - and either approves or rejects with detailed findings. -
    -
  8. -
  9. -
    - Merge & land. A merge agent resolves conflicts and squash-merges to your main - branch. The worktree is cleaned up automatically. -
    -
  10. -
- -

Key concepts

-

- Stories are Markdown files with YAML front matter. They live in{' '} - .huskies/work/ and move through pipeline stages as work progresses. -

-

- Agents are Claude Code sessions that run autonomously in git worktrees. Each story gets - its own isolated worktree so multiple stories can be in flight simultaneously. -

-

- MCP tools give Claude Code sessions programmatic access to the pipeline: creating - stories, starting agents, checking status, recording test results. -

- - ) -} diff --git a/website/app/docs/pipeline/page.tsx b/website/app/docs/pipeline/page.tsx deleted file mode 100644 index 89cd83b0..00000000 --- a/website/app/docs/pipeline/page.tsx +++ /dev/null @@ -1,200 +0,0 @@ -/** Pipeline stages reference — describes the six-stage story workflow. */ -import type { Metadata } from 'next' - -/** Page metadata for the pipeline stages guide. */ -export const metadata: Metadata = { - title: 'Pipeline Stages — Huskies Docs', - description: - 'How work items move through the huskies pipeline: backlog, current, QA, merge, done.', -} - -/** Renders the pipeline stages reference page. */ -export default function PipelinePage() { - return ( - <> -

Pipeline Stages

-

- Work items move through six stages from idea to archive. Each stage is a directory under{' '} - .huskies/work/. Moving a file between directories advances the story. -

- -
-
-
1
-
Backlog
-
- 1_backlog/ — New work items awaiting prioritisation. Stories sit here until - you decide to start them. -
-
-
-
2
-
Current
-
- 2_current/ — Work in progress. Run start <number> to - assign a coder agent. Multiple stories can be in current simultaneously (up to{' '} - max_coders). -
-
-
-
3
-
QA
-
- 3_qa/ — Quality review. The server automatically moves stories here when the - coder agent passes all quality gates. A QA agent (or a human) verifies each acceptance criterion. -
-
-
-
4
-
Merge
-
- 4_merge/ — Ready to merge. Stories reach here after QA approval. Run{' '} - start <number> to trigger the mergemaster agent, which squash-merges to your - base branch. -
-
-
-
5
-
Done
-
- 5_done/ — Merged and complete. The mergemaster moves stories here after a - successful merge. Auto-swept to archive after 4 hours. -
-
-
-
6
-
Archived
-
- 6_archived/ — Long-term storage. Stories land here automatically from done. - Use overview <number> to see the implementation summary for any archived - story. -
-
-
- -

Work item types

-

- All work item types move through the same pipeline. They differ in naming convention and workflow: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeFilename patternWhen to use
story - 42_story_add_login.md - New functionality. Requires acceptance criteria and tests.
bug - 43_bug_login_crashes.md - Defect in existing functionality. Write a failing test first.
spike - 44_spike_auth_options.md - Time-boxed research to reduce uncertainty. No production code.
refactor - 45_refactor_extract_auth.md - Code quality improvement. Behaviour must not change.
- -

Story file format

-

Every work item is a Markdown file with YAML front matter:

-
-        {`---
-name: "Short human-readable name"
-qa: agent          # optional: override default_qa
-agent: opus        # optional: request specific agent model
----
-
-# Story 42: Add login endpoint
-
-## User Story
-As a user, I want to log in with email and password so that I can access my account.
-
-## Acceptance Criteria
-- [ ] POST /auth/login accepts email and password
-- [ ] Returns a JWT token on success
-- [ ] Returns 401 on invalid credentials
-- [ ] Rate-limited to 5 attempts per minute per IP
-
-## Out of Scope
-- OAuth / social login
-- Password reset flow`}
-      
- -

Acceptance criteria tracking

-

- Acceptance criteria use Markdown checkboxes (- [ ]). The QA agent reviews each criterion - against the code diff and marks passing criteria as - [x] in the story file. Criteria - that fail are noted in the QA report. -

- -
- Golden rule: No code is written until acceptance criteria are captured in the story. - The agent reads the story file to understand what to build and what to test. -
- -

Filesystem watcher

-

- The server watches .huskies/work/ for changes. When a file is created, moved, or - modified, the watcher auto-commits with a deterministic message and broadcasts a WebSocket update to - the frontend. This means: -

-
    -
  • - You can drag a story between stage folders in your IDE and it advances automatically. -
  • -
  • MCP tools only need to write or move files — the watcher handles git commits.
  • -
  • The pipeline board updates in real time without a manual refresh.
  • -
- -

Blocked stories

-

- A story is marked blocked when it fails the same pipeline stage more than{' '} - max_retries times (default: 3). Blocked stories require manual intervention: -

-
    -
  1. - Run status <number> to see the failure log. -
  2. -
  3. Fix the underlying issue (update the story, fix a build problem, etc.).
  4. -
  5. - Run unblock <number> to reset the retry counter. -
  6. -
  7. - Run start <number> to try again. -
  8. -
- -

Dependencies

-

- Stories can declare dependencies using the depends command. A story with unresolved - dependencies waits in 2_current/ until all its dependencies have reached{' '} - 5_done/. -

-
-        {`depends 45 42 43   # story 45 waits for 42 and 43 to finish\ndepends 45          # clear all dependencies`}
-      
- - ) -} diff --git a/website/app/docs/quickstart/page.tsx b/website/app/docs/quickstart/page.tsx deleted file mode 100644 index 934e1cc4..00000000 --- a/website/app/docs/quickstart/page.tsx +++ /dev/null @@ -1,161 +0,0 @@ -/** Quickstart guide — Docker, binary, and source install instructions. */ -import type { Metadata } from 'next' - -/** Page metadata for the quickstart guide. */ -export const metadata: Metadata = { - title: 'Quickstart — Huskies Docs', - description: 'Get huskies running in minutes: Docker setup, first story, first agent run.', -} - -/** Renders the quickstart installation and setup guide. */ -export default function QuickstartPage() { - return ( - <> -

Quickstart

-

- Get huskies running in your project in a few minutes. This guide covers Docker setup, running from a - binary, your first story, and your first agent run. -

- -

Option A: Docker (recommended)

-

- The easiest way to run huskies is with Docker Compose. This requires Docker and a Claude API key. -

-
    -
  1. -
    - Get the compose file. Download docker-compose.yml from the{' '} - releases page or copy it from - the repository's docker/ directory. -
    -
  2. -
  3. -
    - Set your API key. Create a .env file next to the compose file: -
    ANTHROPIC_API_KEY=sk-ant-...
    -
    -
  4. -
  5. -
    - Mount your project. Edit the compose file to mount your project directory: -
    {`volumes:\n  - /path/to/your/project:/workspace`}
    -
    -
  6. -
  7. -
    - Start the server. -
    docker compose up
    - Open http://localhost:3000 to see the pipeline board. -
    -
  8. -
- -

Option B: Binary

-

- Download the pre-built binary for your platform from the{' '} - releases page and place it - somewhere on your PATH. -

- -

macOS (Apple Silicon)

-
{`curl -L https://code.crashlabs.io/crashlabs/huskies/releases/download/latest/huskies-aarch64-apple-darwin \\
-  -o /usr/local/bin/huskies
-chmod +x /usr/local/bin/huskies`}
- -

Linux (x86-64)

-
{`curl -L https://code.crashlabs.io/crashlabs/huskies/releases/download/latest/huskies-x86_64-unknown-linux-musl \\
-  -o /usr/local/bin/huskies
-chmod +x /usr/local/bin/huskies`}
- -

Option C: Build from source

-

Requires Rust (stable), Node.js, and npm.

-
{`git clone https://code.crashlabs.io/crashlabs/huskies
-cd huskies
-cargo build --release
-# Binary is at target/release/huskies`}
- -

Initialise your project

-

- From your project directory, run the init command. This creates the .huskies/ directory - with the pipeline structure and configuration files. -

-
{`cd /path/to/your/project\nhuskies init --port 3000`}
-

This creates:

-
    -
  • - .huskies/project.toml — project-wide settings -
  • -
  • - .huskies/agents.toml — agent definitions (coder, QA, mergemaster) -
  • -
  • - .huskies/work/ — the 6-stage pipeline directory -
  • -
  • - .mcp.json — MCP server config for Claude Code integration -
  • -
  • - .huskies/specs/ — placeholder spec files for your project context -
  • -
- -
- Claude Code integration: The .mcp.json file automatically registers - huskies' MCP tools with Claude Code. Open a Claude Code session in your project and it will - discover tools like create_story, start_agent, and{' '} - get_pipeline_status automatically. -
- -

Start the server

-
huskies --port 3000
-

- Open http://localhost:3000 to see the pipeline board, agent - status, and chat interface. -

- -

Run the setup wizard

-

- Open a Claude Code session in your project directory (or use the web chat UI), and tell Claude: -

-
help me set up this project with huskies
-

- Claude will walk you through the setup wizard — generating project context ( - specs/00_CONTEXT.md), tech stack docs (specs/tech/STACK.md), and - test/release scripts. Review each step and confirm or ask to retry. -

- -

Create your first story

-

In the chat UI or via a chat transport, type:

-
I want to add a health check endpoint to the API
-

- Claude will create a story file in .huskies/work/1_backlog/ with a user story and - acceptance criteria. Review it, then move it to current: -

-
move <story-number> current
- -

Start an agent

-

- Once a story is in 2_current/, start a coding agent: -

-
start <story-number>
-

- The agent creates an isolated git worktree, implements the feature against the acceptance criteria, - runs quality gates (clippy, tests, biome), and exits. The server automatically advances the story to - QA if all gates pass. -

- -

Review and merge

-

Once QA passes, the story moves to 4_merge/. To merge:

-
start <story-number>
-

- The mergemaster agent resolves any conflicts and squash-merges to your main branch. The worktree is - cleaned up automatically. -

- -
- Tip: Use status in the chat at any time to see the current pipeline - state, active agents, and their progress. -
- - ) -} diff --git a/website/app/docs/transports/page.tsx b/website/app/docs/transports/page.tsx deleted file mode 100644 index 5ed6dfc6..00000000 --- a/website/app/docs/transports/page.tsx +++ /dev/null @@ -1,384 +0,0 @@ -/** Chat transports guide — Matrix, Slack, WhatsApp, Discord, and web UI setup. */ -import type { Metadata } from 'next' -import Link from 'next/link' - -/** Page metadata for the chat transports guide. */ -export const metadata: Metadata = { - title: 'Chat Transports — Huskies Docs', - description: - 'Connect huskies to Matrix, WhatsApp, Slack, Discord, or the built-in web UI.', -} - -/** Renders the chat transports setup guide for all supported platforms. */ -export default function TransportsPage() { - return ( - <> -

Chat Transports

-

- Huskies can be controlled via bot commands in any of five transports. Only one external transport - can be active at a time. The web UI is always available regardless. -

- -
- Configuration: Copy the relevant example file to{' '} - .huskies/bot.toml and fill in your credentials. The file is gitignored. Restart - huskies after changes. -
- -

Web UI

-

- The built-in web interface is always available at http://localhost:<port>. No - configuration required. It provides: -

-
    -
  • Pipeline board showing all work items and their stages
  • -
  • Agent status panel with live output streaming
  • -
  • Chat interface for running commands and talking to Claude
  • -
  • Coverage and cost dashboards
  • -
-

- No bot.toml is required for the web UI. If no transport is configured, huskies runs - in web-only mode. -

- -

Matrix

-

- Matrix uses the Matrix Client-Server API with long-polling sync. No public webhook URL is required - — the bot connects outbound to your homeserver. -

- -

Setup

-
    -
  1. -
    Register a Matrix account for the bot on your homeserver (e.g. @huskies:example.com).
    -
  2. -
  3. -
    Invite the bot account to the rooms you want it to monitor.
    -
  4. -
  5. -
    - Copy the example config and fill in your credentials: -
    -              cp .huskies/bot.toml.matrix.example .huskies/bot.toml
    -            
    -
    -
  6. -
- -

bot.toml fields

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyDescription
homeserver - Your Matrix homeserver URL (e.g. https://matrix.example.com). -
username - Bot account Matrix ID (e.g. @huskies:example.com). -
passwordBot account password.
room_ids - List of room IDs to listen in (e.g. {`["!roomid:example.com"]`}). -
allowed_users - Matrix IDs allowed to interact. Empty list means nobody — always set this. -
ambient_rooms - Rooms where the bot responds to all messages (not just addressed ones). Updated automatically - by ambient on/off. -
- -

Slack

-

- Slack uses event subscriptions over a webhook. You'll need a public HTTPS URL pointing to your - huskies server. -

- -

Setup

-
    -
  1. -
    - Create a Slack App at{' '} - api.slack.com/apps. -
    -
  2. -
  3. -
    - Add OAuth scopes: chat:write, chat:update. -
    -
  4. -
  5. -
    - Subscribe to bot events: message.channels, message.groups,{' '} - message.im. -
    -
  6. -
  7. -
    Install the app to your workspace and copy the bot token.
    -
  8. -
  9. -
    - Set your webhook URL in Event Subscriptions:{' '} - https://your-server/webhook/slack -
    -
  10. -
  11. -
    - Copy the example config: -
    -              cp .huskies/bot.toml.slack.example .huskies/bot.toml
    -            
    -
    -
  12. -
- -

bot.toml fields

- - - - - - - - - - - - - - - - - - - - - -
KeyDescription
slack_bot_token - OAuth bot token starting with xoxb-. -
slack_signing_secretSigning secret from the app's Basic Information page.
slack_channel_ids - List of channel IDs to listen in (e.g. {`["C01ABCDEF"]`}). -
- -

WhatsApp (Meta Cloud API)

-

- Connects huskies to WhatsApp Business via the Meta Cloud API. Requires a Meta Business account and - a public webhook URL. -

- -

Setup

-
    -
  1. -
    - Create a Meta Business App at{' '} - developers.facebook.com. -
    -
  2. -
  3. -
    Add the WhatsApp product and get a Phone Number ID.
    -
  4. -
  5. -
    Generate a permanent access token.
    -
  6. -
  7. -
    - Register your webhook URL in Meta's dashboard:{' '} - https://your-server/webhook/whatsapp -
    -
  8. -
  9. -
    - Copy the example config: -
    -              cp .huskies/bot.toml.whatsapp-meta.example .huskies/bot.toml
    -            
    -
    -
  10. -
- -

bot.toml fields

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyDescription
whatsapp_provider - Set to "meta" for the Meta Cloud API. -
whatsapp_phone_number_idPhone Number ID from the Meta dashboard.
whatsapp_access_tokenPermanent access token.
whatsapp_verify_token - Webhook verify token — must match what you set in Meta's dashboard. -
whatsapp_allowed_phones - Optional. List of phone numbers allowed to interact (e.g.{' '} - {`["+15551234567"]`}). When absent, all numbers are allowed. -
whatsapp_notification_template - Optional. Name of the approved Meta message template for out-of-window notifications - (default: "pipeline_notification"). -
- -

WhatsApp (Twilio)

-

- An alternative WhatsApp integration using Twilio's WhatsApp API. Requires a Twilio account. -

-
-        cp .huskies/bot.toml.whatsapp-twilio.example .huskies/bot.toml
-      
-

- Set whatsapp_provider = "twilio" and fill in your Twilio account SID, auth - token, and phone numbers. The webhook URL is the same:{' '} - https://your-server/webhook/whatsapp. -

- -

Discord

-

- Connects huskies to Discord using the Discord Gateway WebSocket. No public webhook URL required - — the bot connects outbound. -

- -

Setup

-
    -
  1. -
    - Create a Discord Application at{' '} - - discord.com/developers/applications - - . -
    -
  2. -
  3. -
    Go to Bot, create a bot, and copy the token.
    -
  4. -
  5. -
    - Enable Message Content Intent under Privileged Gateway Intents. -
    -
  6. -
  7. -
    - Go to OAuth2 → URL Generator, select the bot scope with permissions: Send - Messages, Read Message History, Manage Messages. -
    -
  8. -
  9. -
    Use the generated URL to invite the bot to your server.
    -
  10. -
  11. -
    - Right-click target channels → Copy Channel ID (requires Developer Mode enabled in Discord - settings). -
    -
  12. -
  13. -
    - Copy the example config: -
    -              cp .huskies/bot.toml.discord.example .huskies/bot.toml
    -            
    -
    -
  14. -
- -

bot.toml fields

- - - - - - - - - - - - - - - - - - - - - -
KeyDescription
discord_bot_tokenBot token from the Discord developer portal.
discord_channel_ids - List of channel IDs to listen in (e.g.{' '} - {`["123456789012345678"]`}). -
discord_allowed_users - Optional. Discord user IDs allowed to interact. When absent, all users in configured - channels can interact. -
- -

Gateway: aggregated notifications

-

- When using huskies --gateway, you can configure the gateway bot to receive - notifications from all registered projects in a single room. Events are prefixed - with [project-name]. -

-

- No additional transport is required — the gateway aggregated stream works with any of the - transports above. Configure the gateway's .huskies/bot.toml with your transport - credentials and set aggregated_notifications_enabled = true (the default). See{' '} - - Configuration → Gateway aggregated stream - {' '} - for the full reference. -

-
- No per-project changes needed: Adding a new project to{' '} - projects.toml does not require editing per-project bot configs — the gateway - picks it up automatically. -
- - ) -} diff --git a/website/app/globals.css b/website/app/globals.css deleted file mode 100644 index 7619da59..00000000 --- a/website/app/globals.css +++ /dev/null @@ -1,416 +0,0 @@ -:root { - --bg: #080c15; - --surface: #0e1420; - --surface-hover: #131a28; - --border: #1a2235; - --text: #e8ecf4; - --text-secondary: #8892a8; - --text-dim: #4a5568; - --cyan: #22d3ee; - --cyan-dim: rgba(34, 211, 238, 0.07); - --cyan-glow: rgba(34, 211, 238, 0.15); - --display: var(--font-display), sans-serif; - --body: var(--font-body), sans-serif; -} - -* { margin: 0; padding: 0; box-sizing: border-box; } -html { scroll-behavior: smooth; } - -body { - font-family: var(--body); - background: var(--bg); - color: var(--text); - line-height: 1.6; - min-height: 100vh; - overflow-x: hidden; - -webkit-font-smoothing: antialiased; -} - -a { color: var(--cyan); text-decoration: none; transition: opacity 0.2s; } -a:hover { opacity: 0.7; } - -/* Animations */ -@keyframes fadeUp { - from { opacity: 0; transform: translateY(18px); } - to { opacity: 1; transform: translateY(0); } -} -@keyframes pulse { - 0%, 100% { box-shadow: 0 0 0 0 var(--cyan-glow); } - 50% { box-shadow: 0 0 12px 4px var(--cyan-glow); } -} - -.reveal { - opacity: 0; - animation: fadeUp 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards; -} -.r1 { animation-delay: 0.05s; } -.r2 { animation-delay: 0.15s; } -.r3 { animation-delay: 0.3s; } -.r4 { animation-delay: 0.5s; } -.r5 { animation-delay: 0.65s; } -.r6 { animation-delay: 0.8s; } -.r7 { animation-delay: 0.95s; } -.r8 { animation-delay: 1.1s; } - -/* Layout */ -.page { - max-width: 960px; - margin: 0 auto; - padding: 0 3rem; -} - -@media (max-width: 640px) { - .page { padding: 0 1.5rem; } - header { - flex-direction: column; - gap: 0.75rem; - } - header nav { - gap: 1rem; - flex-wrap: wrap; - justify-content: center; - } -} - -/* Header */ -header { - padding: 2rem 0; - display: flex; - justify-content: space-between; - align-items: center; -} - -.logo { - font-family: var(--display); - font-size: 1.1rem; - font-weight: 800; - letter-spacing: -0.03em; - color: var(--text) !important; -} - -header nav { - display: flex; - align-items: center; - gap: 2rem; -} - -header nav a { - font-size: 0.82rem; - color: var(--text-secondary); -} - -header nav a:hover { color: var(--text); opacity: 1; } - -.nav-cta { - color: var(--cyan) !important; - font-weight: 500; -} - -.nav-dropdown { - position: relative; -} - -.nav-dropdown-toggle { - cursor: pointer; -} - -/* Invisible bridge between toggle and menu so hover doesn't break */ -.nav-dropdown::after { - content: ''; - position: absolute; - top: 100%; - right: 0; - width: 100%; - height: 0.5rem; -} - -.nav-dropdown-menu { - display: none; - position: absolute; - top: calc(100% + 0.5rem); - right: 0; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 0.5rem; - padding: 0.5rem 0; - min-width: 140px; - z-index: 200; -} - -.nav-dropdown-menu a { - display: block; - padding: 0.4rem 1rem; - font-size: 0.82rem; - color: var(--text-secondary); -} - -.nav-dropdown-menu a:hover { - background: var(--surface-hover); - color: var(--text); - opacity: 1; -} - -.nav-dropdown:hover .nav-dropdown-menu { - display: block; -} - -/* Hero */ -.hero { - padding: 10vh 0 6vh; - text-align: center; -} - -.hero-graphic { - margin-bottom: 2.5rem; - position: relative; - z-index: 1; -} - -.hero-husky { - width: 160px; - height: auto; - filter: drop-shadow(0 0 40px rgba(34, 211, 238, 0.2)); -} - -.hero-kicker { - font-family: var(--display); - font-size: 0.7rem; - font-weight: 600; - letter-spacing: 0.18em; - text-transform: uppercase; - color: var(--cyan); - margin-bottom: 2rem; -} - -.hero h1 { - font-family: var(--display); - font-size: clamp(2.5rem, 6vw, 4.2rem); - font-weight: 800; - line-height: 1.1; - letter-spacing: -0.03em; - margin-bottom: 1.8rem; - max-width: 700px; - margin-left: auto; - margin-right: auto; -} - -.glow { - color: var(--cyan); - text-shadow: 0 0 30px var(--cyan-glow); -} - -.hero-sub { - font-size: 1.05rem; - font-weight: 300; - color: var(--text-secondary); - line-height: 1.8; - max-width: 520px; - margin: 0 auto; -} - -/* Pipeline visualisation */ -.pipeline { - display: flex; - align-items: center; - justify-content: center; - gap: 0; - margin-top: 4rem; - padding: 2rem 0; -} - -.pipe-stage { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.8rem; -} - -.pipe-dot { - width: 12px; - height: 12px; - border-radius: 50%; - border: 2px solid var(--border); - background: var(--surface); - transition: all 0.3s; -} - -.pipe-dot.active { - border-color: var(--cyan); - background: var(--cyan); - animation: pulse 2s ease-in-out infinite; -} - -.pipe-dot.done { - border-color: var(--text-dim); - background: var(--text-dim); -} - -.pipe-label { - font-family: var(--display); - font-size: 0.65rem; - font-weight: 600; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--text-dim); -} - -.pipe-stage:has(.active) .pipe-label { - color: var(--cyan); -} - -.pipe-line { - width: 60px; - height: 1px; - background: var(--border); - margin: 0 0.5rem; - margin-bottom: 2rem; -} - -@media (max-width: 500px) { - .pipe-line { width: 30px; } - .pipe-label { font-size: 0.55rem; } -} - -/* Sections */ -.section-title { - font-family: var(--display); - font-size: 1.6rem; - font-weight: 700; - letter-spacing: -0.02em; - margin-bottom: 2.5rem; -} - -/* How it works */ -.how-section { - padding: 5rem 0; - border-top: 1px solid var(--border); -} - -.steps { - list-style: none; - display: flex; - flex-direction: column; - gap: 0; -} - -.step { - display: grid; - grid-template-columns: 56px 1fr; - gap: 1.5rem; - padding: 1.8rem 0; - border-bottom: 1px solid var(--border); - align-items: start; -} - -.step:first-child { - border-top: 1px solid var(--border); -} - -.step-num { - font-family: var(--display); - font-size: 0.75rem; - font-weight: 700; - color: var(--text-dim); - padding-top: 0.15rem; -} - -.step-body h3 { - font-family: var(--display); - font-size: 1rem; - font-weight: 600; - margin-bottom: 0.4rem; -} - -.step-body p { - font-size: 0.88rem; - font-weight: 300; - color: var(--text-secondary); - line-height: 1.7; -} - -/* Features */ -.features-section { - padding: 5rem 0; - border-top: 1px solid var(--border); -} - -.feature-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1px; - background: var(--border); - border: 1px solid var(--border); -} - -.feature { - background: var(--surface); - padding: 2rem; - transition: background 0.3s; -} - -.feature:hover { - background: var(--surface-hover); -} - -.feature-icon { - color: var(--cyan); - margin-bottom: 1.2rem; - opacity: 0.8; -} - -.feature h3 { - font-family: var(--display); - font-size: 0.95rem; - font-weight: 600; - margin-bottom: 0.5rem; -} - -.feature p { - font-size: 0.82rem; - font-weight: 300; - color: var(--text-secondary); - line-height: 1.7; -} - -@media (max-width: 600px) { - .feature-grid { grid-template-columns: 1fr; } -} - -/* CTA */ -.cta-section { - padding: 5rem 0; - border-top: 1px solid var(--border); - text-align: center; -} - -.cta-section h2 { - font-family: var(--display); - font-size: 1.8rem; - font-weight: 700; - letter-spacing: -0.02em; - margin-bottom: 1rem; -} - -.cta-section p { - font-size: 0.95rem; - color: var(--text-secondary); - font-weight: 300; -} - -/* Footer */ -footer { - padding: 2rem 0; - border-top: 1px solid var(--border); - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.75rem; - color: var(--text-dim); -} - -footer a { - color: var(--text-dim); - font-size: 0.75rem; -} - -footer a:hover { color: var(--text-secondary); } diff --git a/website/app/layout.tsx b/website/app/layout.tsx deleted file mode 100644 index e33ce308..00000000 --- a/website/app/layout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/** Root layout: loads fonts and global CSS for every page. */ -import type { Metadata } from 'next' -import { Bricolage_Grotesque, Karla } from 'next/font/google' -import './globals.css' - -const bricolage = Bricolage_Grotesque({ - weight: ['400', '500', '600', '700', '800'], - subsets: ['latin'], - variable: '--font-display', -}) - -const karla = Karla({ - weight: ['300', '400', '500'], - style: ['normal', 'italic'], - subsets: ['latin'], - variable: '--font-body', -}) - -/** Default page metadata for the site. */ -export const metadata: Metadata = { - title: 'Huskies — Story-Driven Development for AI Agents', - description: - 'Huskies is an autonomous development pipeline that turns user stories into tested, shipped code using AI agents.', -} - -/** Wraps every page with html/body, font CSS variables, and global styles. */ -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - {children} - - - ) -} diff --git a/website/app/page.tsx b/website/app/page.tsx deleted file mode 100644 index 9ed86328..00000000 --- a/website/app/page.tsx +++ /dev/null @@ -1,180 +0,0 @@ -/** Homepage — marketing landing page with hero, features, and CTA. */ -import type { Metadata } from 'next' -import Image from 'next/image' - -/** Page metadata for the homepage. */ -export const metadata: Metadata = { - title: 'Huskies — Story-Driven Development for AI Agents', - description: - 'Huskies is an autonomous development pipeline that turns user stories into tested, shipped code using AI agents.', -} - -/** Renders the huskies marketing homepage. */ -export default function HomePage() { - return ( -
-
- huskies - -
- -
-
- -
-

Story-driven development

-

- Coding agents are huskies,
not labradors. -

-

- They're enthusiastic, sometimes wild, and they'll happily wander off on their own. But put - them in a harness and they'll take you anywhere. Huskies is the harness — a story-driven - pipeline that turns coding agents into a disciplined team. -

- -
-
- - Story -
- -
- - Implement -
- -
- - QA -
- -
- - Merge -
- -
- - Done -
-
-
- -
-

How it works

-
    -
  1. - 01 -
    -

    Write a story

    -

    Describe what you want with acceptance criteria. From your IDE, a chat room, or WhatsApp.

    -
    -
  2. -
  3. - 02 -
    -

    Agent picks it up

    -

    A coder agent creates a feature branch, implements the code, and writes tests against your criteria.

    -
    -
  4. -
  5. - 03 -
    -

    Quality gates run

    -

    Linters, tests, and compilation checks run automatically. Nothing moves forward until everything passes.

    -
    -
  6. -
  7. - 04 -
    -

    Merge & land

    -

    A merge agent resolves conflicts and squash-merges to your main branch. You review and accept.

    -
    -
  8. -
-
- -
-

Features

-
-
-
- - - -
-

The Harness

-

- Stories define the change. Tests define the truth. Code defines the reality. Every agent runs on - rails — nothing ships without acceptance criteria. -

-
-
-
- - - - - - -
-

The Pack

-

- Coder, QA, and merge agents work in parallel across isolated git worktrees. A coordinated pack, not - a lone wolf. Configure agent count, models, and budgets. -

-
-
-
- - - -
-

Chat Anywhere

-

- Control the pipeline from Matrix, WhatsApp, Slack, or the built-in web UI. Create stories, start - agents, check status. -

-
-
-
- - - -
-

You're the Musher

-

- Agents implement, test, and merge independently. You set the direction and approve what ships. Every - story is traceable from request to release. -

-
-
-
- -
-

Interested?

-

- Huskies is built by Crash Labs. Get in touch at{' '} - hello@huskies.dev. -

-
- - -
- ) -} diff --git a/website/app/privacy/page.tsx b/website/app/privacy/page.tsx deleted file mode 100644 index 6dc372bf..00000000 --- a/website/app/privacy/page.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/** Privacy policy page. */ -import type { Metadata } from 'next' - -/** Page metadata for the privacy policy. */ -export const metadata: Metadata = { - title: 'Privacy Policy — Huskies', -} - -/** Renders the privacy policy page. */ -export default function PrivacyPage() { - return ( -
-
-

- - storkit - -

-

Privacy Policy

-
- -
-

- Last updated: 25 March 2026 -

- -

Who we are

-

- Huskies is operated by Libby Labs Ltd (“we”, “us”, “our”), trading - as Crashlabs. Our contact email is{' '} - hello@huskies.dev. -

-
- -
-

What we collect

-

- When you interact with Huskies via WhatsApp, Slack, Matrix, or the web interface, we may collect: -

-

- Messaging data: Your phone number or chat identifier and the content of messages you - send to the bot. This is used solely to process your requests and maintain conversation context. -

-

- Usage data: Basic server logs including timestamps and request metadata. We do not use - analytics trackers on this website. -

-
- -
-

How we use your data

-

We use your data only to provide and improve the Huskies service. Specifically:

-

- - To process commands and respond to your messages.
- - To maintain conversation history within active sessions.
- - To diagnose and fix technical issues. -

-

We do not sell, rent, or share your personal data with third parties for marketing purposes.

-
- -
-

Third-party services

-

- Messages sent via WhatsApp are processed through Meta's WhatsApp Business API or Twilio's - messaging platform, subject to their respective privacy policies. Messages sent via Slack or Matrix pass - through those platforms' infrastructure. -

-
- -
-

Data retention

-

- Conversation history is stored locally on our servers and retained only for the duration needed to - maintain session context. We do not retain message data indefinitely. -

-
- -
-

Your rights

-

- You may request access to, correction of, or deletion of your personal data at any time by contacting - us at hello@huskies.dev. -

-
- -
-

Changes to this policy

-

- We may update this policy from time to time. Changes will be posted on this page with an updated date. -

-
- -
-

- © 2026 Libby Labs Ltd. All rights reserved. · Home -

-
-
- ) -} diff --git a/website/components/DocsSidebar.tsx b/website/components/DocsSidebar.tsx deleted file mode 100644 index 9f15a73e..00000000 --- a/website/components/DocsSidebar.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/** Docs sidebar navigation — highlights the active page via usePathname. */ -'use client' - -import { usePathname } from 'next/navigation' -import Link from 'next/link' - -/** Renders the docs sidebar with active-link highlighting for the current route. */ -export default function DocsSidebar() { - const pathname = usePathname() - - return ( - - ) -} diff --git a/website/next.config.mjs b/website/next.config.mjs deleted file mode 100644 index 1cabe002..00000000 --- a/website/next.config.mjs +++ /dev/null @@ -1,11 +0,0 @@ -/** Next.js configuration for the huskies static marketing site. */ - -/** @type {import('next').NextConfig} */ -const nextConfig = { - output: 'export', - images: { - unoptimized: true, - }, -} - -export default nextConfig diff --git a/website/package-lock.json b/website/package-lock.json deleted file mode 100644 index 130ce8ff..00000000 --- a/website/package-lock.json +++ /dev/null @@ -1,499 +0,0 @@ -{ - "name": "huskies-website", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "huskies-website", - "version": "1.0.0", - "dependencies": { - "next": "14.2.29", - "react": "^18", - "react-dom": "^18" - }, - "devDependencies": { - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "typescript": "^5" - } - }, - "node_modules/@next/env": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.29.tgz", - "integrity": "sha512-UzgLR2eBfhKIQt0aJ7PWH7XRPYw7SXz0Fpzdl5THjUnvxy4kfBk9OU4RNPNiETewEEtaBcExNFNn1QWH8wQTjg==", - "license": "MIT" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.29.tgz", - "integrity": "sha512-wWtrAaxCVMejxPHFb1SK/PVV1WDIrXGs9ki0C/kUM8ubKHQm+3hU9MouUywCw8Wbhj3pewfHT2wjunLEr/TaLA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.29.tgz", - "integrity": "sha512-7Z/jk+6EVBj4pNLw/JQrvZVrAh9Bv8q81zCFSfvTMZ51WySyEHWVpwCEaJY910LyBftv2F37kuDPQm0w9CEXyg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.29.tgz", - "integrity": "sha512-o6hrz5xRBwi+G7JFTHc+RUsXo2lVXEfwh4/qsuWBMQq6aut+0w98WEnoNwAwt7hkEqegzvazf81dNiwo7KjITw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.29.tgz", - "integrity": "sha512-9i+JEHBOVgqxQ92HHRFlSW1EQXqa/89IVjtHgOqsShCcB/ZBjTtkWGi+SGCJaYyWkr/lzu51NTMCfKuBf7ULNw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.29.tgz", - "integrity": "sha512-B7JtMbkUwHijrGBOhgSQu2ncbCYq9E7PZ7MX58kxheiEOwdkM+jGx0cBb+rN5AeqF96JypEppK6i/bEL9T13lA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.29.tgz", - "integrity": "sha512-yCcZo1OrO3aQ38B5zctqKU1Z3klOohIxug6qdiKO3Q3qNye/1n6XIs01YJ+Uf+TdpZQ0fNrOQI2HrTLF3Zprnw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.29.tgz", - "integrity": "sha512-WnrfeOEtTVidI9Z6jDLy+gxrpDcEJtZva54LYC0bSKQqmyuHzl0ego+v0F/v2aXq0am67BRqo/ybmmt45Tzo4A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.29.tgz", - "integrity": "sha512-vkcriFROT4wsTdSeIzbxaZjTNTFKjSYmLd8q/GVH3Dn8JmYjUKOuKXHK8n+lovW/kdcpIvydO5GtN+It2CvKWA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.29.tgz", - "integrity": "sha512-iPPwUEKnVs7pwR0EBLJlwxLD7TTHWS/AoVZx1l9ZQzfQciqaFEr5AlYzA2uB6Fyby1IF18t4PL0nTpB+k4Tzlw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, - "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" - } - }, - "node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", - "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.29.tgz", - "integrity": "sha512-s98mCOMOWLGGpGOfgKSnleXLuegvvH415qtRZXpSp00HeEgdmrxmwL9cgKU+h4XrhB16zEI5d/7BnkS3ATInsA==", - "deprecated": "This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.", - "license": "MIT", - "dependencies": { - "@next/env": "14.2.29", - "@swc/helpers": "0.5.5", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", - "postcss": "8.4.31", - "styled-jsx": "5.1.1" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=18.17.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.29", - "@next/swc-darwin-x64": "14.2.29", - "@next/swc-linux-arm64-gnu": "14.2.29", - "@next/swc-linux-arm64-musl": "14.2.29", - "@next/swc-linux-x64-gnu": "14.2.29", - "@next/swc-linux-x64-musl": "14.2.29", - "@next/swc-win32-arm64-msvc": "14.2.29", - "@next/swc-win32-ia32-msvc": "14.2.29", - "@next/swc-win32-x64-msvc": "14.2.29" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/website/package.json b/website/package.json deleted file mode 100644 index 84302164..00000000 --- a/website/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "huskies-website", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "next": "14.2.29", - "react": "^18", - "react-dom": "^18" - }, - "devDependencies": { - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "typescript": "^5" - } -} diff --git a/website/public/husky.png b/website/public/husky.png deleted file mode 100644 index 25037f5e..00000000 Binary files a/website/public/husky.png and /dev/null differ diff --git a/website/public/husky.svg b/website/public/husky.svg deleted file mode 100644 index 295bba47..00000000 --- a/website/public/husky.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/website/tsconfig.json b/website/tsconfig.json deleted file mode 100644 index 522b6a0f..00000000 --- a/website/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [{ "name": "next" }], - "paths": { - "@/*": ["./*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -}