Imported from colonyops/hive (
AGENTS.md). Install upstream withnpx skills add colonyops/hive. Copyright stays with the author.
Agent Instructions
Project Overview
Hive is a CLI/TUI for managing multiple AI agent sessions in isolated git environments. Instead of manually managing worktrees, hive handles cloning, recycling, and spawning terminal sessions with your preferred AI tool.
Key capabilities:
- Session Management - Create, recycle, and prune isolated git clones
- Terminal Integration - Real-time status monitoring of AI agents in tmux
- Inter-agent Messaging - Pub/sub communication between sessions
- Context Directories - Shared storage per repository via
.hivesymlinks
Architecture
Core Concepts
Session - An isolated git environment for an AI agent
- Isolated git clone in a dedicated directory
- Lifecycle states: active, recycled, corrupted
- Unique ID and display name
- Metadata for terminal integration
Agent - The AI tool instance (Claude, Aider, Codex) running within a session
- Detected via terminal output patterns
- Status monitoring: Active, Idle, Waiting, Error, Missing
- Future: Multiple agents per session
Terminal Integration - Real-time status monitoring (tmux)
- Maps hive sessions to tmux sessions
- Captures terminal output for status detection
- Shows live preview of agent work
Messaging - Inter-agent communication via pub/sub
- Inbox convention:
agent.<session-id>.inbox - Topics support wildcards for broadcast
- Persistent message storage
Code Structure
internal/
├── commands/ # CLI command handlers (urfave/cli/v3)
├── core/
│ ├── config/ # Configuration loading, validation, defaults
│ ├── git/ # Git operations (clone, pull, status)
│ └── session/ # Session model and Store interface
├── hive/ # Service layer - orchestrates all operations
├── integration/
│ └── terminal/ # Terminal status monitoring (tmux)
├── store/
│ └── jsonfile/ # JSON file session storage implementation
├── tui/ # Bubble Tea TUI (tree view, modals, keybindings)
├── messaging/ # Pub/sub messaging between agents
├── printer/ # Output formatting utilities
└── styles/ # Shared lipgloss styles
Key Files
| File | Purpose |
|---|---|
main.go |
CLI entry point, global flags, command registration |
internal/hive/service.go |
Service layer - coordinates sessions, git, rules |
internal/core/config/config.go |
Config structs, loading, defaults |
internal/core/config/validate.go |
Template data structs, validation |
internal/tui/model.go |
TUI model, update loop, view rendering |
internal/tui/tree_view.go |
Session tree with status indicators |
internal/integration/terminal/detector.go |
AI agent status detection patterns |
Development
Commands
mise run start # Run with global config (supports CLI args)
mise run dev # Run with dev config (supports CLI args)
mise run dev -- new # Example: run 'hive new' with dev config
mise run build # Build with goreleaser
mise run test # Run tests with go test
mise run lint # Run golangci-lint
mise run check # tidy + lint + test (full validation)
mise run coverage # Generate coverage report
mise container # Build and launch an ephemeral Docker container with hive pre-installed
Manual Testing
Use mise container to manually test hive end-to-end. It builds the current branch and drops you into an isolated Docker container with hive installed and tmux available — no need to install a local binary or worry about polluting your dev environment.
mise container
# Inside the container (hive is aliased to 'hv'):
hv new --remote <url> "my-session"
hv ls
This is the preferred way to test CLI/TUI behavior, session creation, branch templates, tmux integration, and anything that requires a real git environment. Do NOT attempt to test by manually building and replacing a binary in your PATH.
Environment
Dev environment uses config.dev.yaml and .data/ for isolation:
HIVE_LOG_LEVEL=debug
HIVE_LOG_FILE=./dev.log
HIVE_CONFIG=./config.dev.yaml
HIVE_DATA_DIR=./.data
Code Generation
go-enum
Enum types use // ENUM(...) comments processed by go-enum. Generated files (*_enum.go) are committed and must never be edited manually.
mise run generate:enums # regenerate after changing ENUM comments
mise run generate # all generators (go-enum + sqlc)
Defining an enum:
// ENUM(epic, task)
type ItemType string
This generates constants (ItemTypeEpic, ItemTypeTask), ParseItemType, IsValid, MarshalText/UnmarshalText, and ItemTypeValues. String values match the ENUM comment exactly (lowercase).
When adding a new value, update the ENUM(...) comment, run mise run generate:enums, then update any switch statements or criterio.OneOf(...) validators that enumerate the values. Also add the source file to sources in mise.toml under [tasks."generate:enums"] if it's a new file.
sqlc
Queries live in internal/data/db/queries/. Generated files (queries*.sql.go, models.go) are committed and must never be edited manually.
mise run generate # regenerates after SQL or sqlc.yaml changes
sqlc generate # directly
Adding a query: write the annotated SQL (:one, :many, :exec), run generation, then call via s.db.Queries().FunctionName(ctx, ...).
Type overrides: when a column stores a domain enum, add an override to sqlc.yaml so the generated code uses the Go type directly instead of string. The go-enum type satisfies the required interfaces via MarshalText/UnmarshalText.
Always commit the generated *.sql.go and models.go alongside the SQL changes in the same commit.
Code Patterns
Integration Tests
Integration tests live in test/integration/ and require a compiled binary. They use the integration build tag and are excluded from the standard mise run test run.
CRITICAL: NEVER run integration tests directly on the host (e.g. go test -tags integration ./test/integration/... or mise run test -- -tags integration run outside a container). These tests spawn real tmux sessions and subprocesses that have crashed host tmux/dev environments. Integration tests exercise tmux session lifecycle and are only safe inside the isolated Docker environment.
Always run integration tests via the Docker-based task instead:
mise run integration # builds the project and runs integration tests inside Docker
Use mise container for interactive manual testing in the same isolated environment (see "Manual Testing" above).
Key rules:
- Every test calls
NewHarness(t)which creates isolateddataDirandhomeDirper test — no shared state between tests. - Use
h.RunStdout(...)(noth.Run) when parsing JSON output — it separates stdout from stderr (migration logs etc. go to stderr). - Use
h.RunJSONLines(...)to get[]map[string]anyfrom JSONL output directly. - Use
h.RunWithStdin(input, ...)to pipe JSON to stdin (for bulk create etc.). - Avoid
h.Run(...)for structured output; use it only when testing error messages or combined output. - Tests that need a real hive session (e.g.
hc next) must create one withh.CreateSession(t)first. - Do not test formatting/cosmetic output — test field values and structural correctness only.
What belongs in integration tests vs unit tests:
- Integration: end-to-end CLI flag wiring, stdin/stdout behavior, multi-command workflows, session detection
- Unit: business logic, validation rules, store behavior (using real SQLite via
db.Open(t.TempDir(), ...)), service orchestration
Bubble Tea (TUI)
Standard Model/Update/View pattern. Key messages:
sessionsLoadedMsg- Sessions fetched from storegitStatusBatchCompleteMsg- Git status for all sessionsterminalPollTickMsg- Terminal status polling tickactionCompleteMsg- Keybinding action finished
Configuration
Two validation phases:
- Basic (
Validate()) - Struct validation, required fields - Deep (
ValidateDeep()) - File access, template syntax, regex patterns
Templates
Commands support Go templates with shq function for shell quoting:
spawn:
- my-script {{ .Name | shq }} {{ .Path | shq }}
Available variables vary by context - see internal/core/config/validate.go for *TemplateData structs.
Error Handling
Never silently discard errors. If an error cannot be presented to the user (e.g., in background polling, cache refresh, or TUI status fetching), log it at an appropriate level (debug for expected/transient failures, warn for configuration problems). Prefer degraded behavior with logging over silent fallbacks — for example, show a StatusMissing indicator instead of dropping an item from the UI.
Keybinding Precedence
The TUI dispatches keystrokes through three layers, in this order:
- Layer 1: hardcoded, non-overridable -
ctrl+c,esc,tab, andshift+tabin normal-mode handling, plus modal-lifecycle dismissal keys (esc/q) inside dialogs and modals. - Layer 2: configurable via
KeybindingResolver- every other user-overridable key. Default bindings live indefaultViewsConfig(internal/core/config/config_views.go), and user config incfg.Views.{Global,Sessions,Tasks,Review}.Keybindingsoverrides those defaults becausemaps.Copy(merged, user)overlays user values onto the merged map. - Layer 3: bubbles list internals - the underlying list component claims keys like
g,G,j,k,h,l,u,d,f,b,/,?,q, andesc. This is intentionally out of scope for hive keybinding configuration: the resolver consumes configured bindings before the list sees them.
When adding a new overridable key, do not add a new if keyStr == "X" block in view code. Register an action.Type in internal/core/action/type.go, add a default UserCommand in defaultUserCommands (internal/core/config/config.go), bind it in defaultViewsConfig, and dispatch it from internal/tui/model_handlers.go.
Session States
(new) ──► active ──► recycled ──► (deleted)
│ │
└──► corrupted ──► (deleted)
Honeycomb (hc) — Multi-Agent Task Coordination
hive hc is the built-in task coordination system for multi-agent workflows. A conductor creates epics and tasks; workers claim and complete them.
Quick Reference
# Conductor: create work (simple)
echo '{"title":"Epic","type":"epic","children":[{"title":"Task","type":"task"}]}' | hive hc create
# Conductor: create work with blocker dependencies (ref/blockers are local labels, not stored)
echo '{
"title": "Auth System",
"type": "epic",
"children": [
{"ref": "jwt", "title": "JWT middleware", "type": "task"},
{"title": "Login endpoint", "type": "task", "blockers": ["jwt"]}
]
}' | hive hc create
# Worker: claim next task
hive hc next <epic-id> --assign
# Worker: record progress
hive hc comment <id> "implemented X"
hive hc comment <id> "CHECKPOINT: stopping here, Y still needed"
# Worker: complete task
hive hc update <id> --status done
# Worker: manage blockers after creation
hive hc update <id> --add-blocker <blocker-id> # mark task as blocked by another
hive hc update <id> --remove-blocker <blocker-id> # remove a blocker
# Get context for an epic (markdown for AI consumption)
hive hc context <epic-id>
# List tasks
hive hc list # open items (default)
hive hc list --all # all items regardless of status
hive hc list <epic-id> # open items under an epic
hive hc list --status done # filter by specific status
hive hc list --session <session-id> # filter by session
Key Commands
| Command | Purpose |
|---|---|
hive hc create [title] |
Single item (positional) or bulk tree (stdin JSON) |
hive hc list [epic-id] |
List open items (use --all for everything) |
hive hc show <id> |
Item + comments as JSON lines |
hive hc update <id> |
Update status (--status), assign (--assign/--unassign), manage blockers (--add-blocker/--remove-blocker) |
hive hc next <epic-id> |
Next actionable leaf task; --assign to claim |
hive hc comment <id> <msg> |
Add a comment |
hive hc comment <id> "CHECKPOINT: msg" |
Handoff checkpoint |
hive hc context <epic-id> |
Epic context block; --json for JSON output |
hive hc prune |
Remove old completed items |
See .claude/skills/hc/SKILL.md for full agent usage guide.
Landing the Plane (Session Completion)
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds