This document explains how boatmanapp integrates with boatmanmode for automated Linear ticket execution.
BoatmanApp is the desktop UI that provides:
- Chat interface with Claude
- Firefighter mode for production incident investigation
- Manual investigation of Linear tickets with Bugsnag/Datadog integration
BoatmanMode is the CLI tool that provides:
- Automated ticket execution with full workflow (plan → implement → test → peer review → PR)
- Git worktree isolation
- Multi-agent coordination
- Iterative refinement based on peer review feedback
BoatmanApp calls the boatman CLI binary as a subprocess rather than importing the Go modules directly.
Why?
- BoatmanMode's packages are in
internal/which can't be imported by external modules (Go restriction) - Clean separation of concerns - UI vs automation engine
- No version coupling - each can be updated independently
- Can use boatmanmode as a standalone tool or via the UI
How?
// boatmanmode/integration.go
cmd := exec.CommandContext(ctx, boatmanmodePath,
"execute",
"--ticket", ticketID,
"--repo", repoPath,
)StreamExecution() accepts a MessageCallback that routes messages through the desktop session:
type MessageCallback func(role, content string)
func (i *Integration) StreamExecution(ctx context.Context, sessionID, input, mode string,
outputChan chan<- string, onMessage MessageCallback) (map[string]interface{}, error)The callback handles two roles:
"assistant"— regular CLI output text, routed viasession.AddBoatmanMessage()"claude_stream"— raw Claude stream-json lines, routed viasession.ProcessExternalStreamLine()
This means all boatmanmode messages flow through the session's message system with proper agent attribution, appearing in the chat UI via the agent:message Wails channel.
result, err := ExecuteLinearTicketWithBoatmanMode(
linearAPIKey,
ticketID,
projectPath,
)What happens:
- Calls
boatman execute --ticket TICKET_ID --repo REPO_PATH - BoatmanMode:
- Fetches ticket from Linear
- Creates isolated git worktree
- Plans implementation (parallel agent)
- Validates plan (preflight agent)
- Implements code changes
- Runs tests
- Peer reviews code (using
peer-reviewskill) - Refactors based on feedback (loop until pass)
- Creates PR with
gh - Updates Linear ticket
- Returns result (success/failure, PR URL, test results)
err := StreamLinearTicketExecution(
linearAPIKey,
ticketID,
projectPath,
)
// Messages now flow through agent:message (primary channel):
EventsOn("agent:message", (data) => {
// Messages include metadata.agent with agentId, agentType, status
addMessage(data.sessionId, data.message);
})
// Structured events for task tracking:
EventsOn("boatmanmode:event", (data) => {
HandleBoatmanModeEvent(data.sessionId, data.event.type, data.event);
})
// boatmanmode:output is now a fallback (no-op logger):
EventsOn("boatmanmode:output", (data) => {
console.log("Fallback:", data.message);
})What happens:
- Same as above, but streams real-time output to the UI
- User can watch the execution progress live
- Shows all agent activity: file reads, edits, test runs, peer review feedback
- Each boatmanmode phase creates a separate agent tab in the AgentLogsPanel
- Claude's raw stream events are forwarded via
claude_streamfor full visibility - Messages are attributed to the correct agent and appear in the chat UI
tickets, err := FetchLinearTicketsForBoatmanMode(
linearAPIKey,
projectPath,
)What happens:
- Calls
boatman list-tickets --repo REPO_PATH --labels firefighter,triage,boatmanmode - Returns list of tickets suitable for automated execution
- UI displays them in the ticket list
The integration looks for the boatman binary in two places:
- In PATH:
exec.LookPath("boatman") - Default location:
~/workspace/handshake/boatmanmode/boatman
For production, ensure boatman is in the PATH:
# Option 1: Add to PATH
export PATH="$PATH:~/workspace/handshake/boatmanmode"
# Option 2: Create symlink
ln -s ~/workspace/handshake/boatmanmode/boatman /usr/local/bin/boatman
# Option 3: Install globally
cd ~/workspace/handshake/boatmanmode
go install ./cmd/boatmanmodeBoatmanApp passes API keys to boatmanmode via environment variables (set by the CLI flags).
Required:
LINEAR_API_KEY- from user preferences or FirefighterSettingsCLAUDE_API_KEY- from user preferences
Current behavior:
- User clicks "Firefighter" button
- Sees Linear triage queue in sidebar
- Clicks "Investigate" on a ticket
- Firefighter agent (in chat):
- Fetches Bugsnag errors
- Queries Datadog logs
- Analyzes git history
- Generates investigation report
- Updates Linear ticket
- User reviews report and decides next steps
This is useful for:
- Understanding what went wrong
- Root cause analysis
- Creating incident reports
- Manual fixes
New behavior:
- User clicks "Auto-Execute with BoatmanMode" button on ticket
- BoatmanMode (subprocess):
- Creates worktree
- Plans fix
- Implements code
- Runs tests
- Peer reviews
- Refactors until passing
- Creates PR
- User reviews the PR
This is useful for:
- Straightforward bug fixes
- Well-defined feature requests
- Tickets with clear acceptance criteria
- Reducing manual coding work
| Use Case | Tool | Why |
|---|---|---|
| "What caused this production error?" | Firefighter Mode | Investigates across systems |
| "Fix this straightforward bug" | BoatmanMode | Automates the fix |
| "Unclear what's broken" | Firefighter Mode | Gathers context first |
| "Implement feature XYZ" | BoatmanMode | Writes code automatically |
| "Need postmortem report" | Firefighter Mode | Generates investigation docs |
| "Simple refactor task" | BoatmanMode | Automates implementation |
Add an "Auto-Execute" button next to the "Investigate" button:
<button onClick={() => handleAutoExecute(ticket)}>
<PlayCircle className="w-3 h-3" />
Auto-Execute with BoatmanMode
</button>Boatmanmode messages now flow through the session message system with proper agent attribution:
agent:message(primary) - All session messages including boatmanmode output. Messages includemetadata.agentwithagentId,agentType,description, andstatus.boatmanmode:event- Structured events for task tracking (agent_started, agent_completed, etc.)boatmanmode:output(fallback) - Raw text output, now a no-op logger since messages route throughagent:message
The AgentLogsPanel component groups messages by agent. Boatmanmode phases (Execution, Planning, Review, Refactor, Testing) each get their own tab with color-coded labels:
- Execution: Orange
- Planning: Yellow
- Review: Pink
- Refactor: Indigo
- Testing: Teal
Each tab shows a status indicator:
- Pulsing cyan dot = active
- Solid green dot = completed
When a message is attributed to a non-main agent, a purple badge appears next to "Claude" in the message header showing the agent type and description (truncated to 30 characters).
Allow user to:
- Pause execution
- Provide feedback during peer review
- Adjust plan before implementation
- Cancel if going wrong direction
Execute multiple tickets in parallel:
Queue: TICKET-123, TICKET-124, TICKET-125
Execute all in separate worktrees
Show progress for each
Let users configure:
- Which peer review skill to use
- Test frameworks
- PR template
- Commit message format
Show estimated token usage before execution:
This ticket will use approximately:
- Planning: 5K tokens (~$0.10)
- Implementation: 20K tokens (~$0.40)
- Review: 10K tokens (~$0.20)
Total: ~$0.70
Track all automated executions:
- Success rate
- Average time
- Common failure reasons
- Cost per ticket
┌──────────────────────────────────────────────────────────────────────┐
│ BoatmanApp (UI) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Firefighter │ │ BoatmanMode │ │
│ │ Mode │ │ Integration │ │
│ │ │ │ │ │
│ │ • Investigate │ │ • Auto-Execute │ │
│ │ • Report │ │ • Stream Output │ │
│ │ • Manual Fix │ │ • Fetch Tickets │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ │ MCP Tools │ Subprocess │
│ │ (Bugsnag, │ exec.Command │
│ │ Datadog, ▼ │
│ │ Linear) ┌──────────────────┐ │
│ │ │ boatman CLI │ │
│ │ │ (boatmanmode) │ │
│ │ └────────┬─────────┘ │
│ │ │ │
│ │ JSON events to stdout │
│ │ (agent_started, claude_stream, etc.) │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────────┐ │
│ │ │ integration.go │ │
│ │ │ Parses events │ │
│ │ │ + MessageCallback│ │
│ │ └────────┬─────────┘ │
│ │ │ │
│ │ ┌───────────────┼───────────────┐ │
│ │ │ │ │ │
│ │ ▼ ▼ ▼ │
│ │ boatmanmode: app.go routes Session routes │
│ │ event (Wails) to session messages via │
│ │ agent methods agent:message │
│ │ │
│ │ RegisterBoatmanAgent() │
│ │ SetCurrentAgent() │
│ │ AddBoatmanMessage() │
│ │ CompleteCurrentAgent() │
│ │ ProcessExternalStreamLine() │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────────┐ │
│ │ │ Frontend │ │
│ │ │ AgentLogsPanel │ │
│ │ │ MessageBubble │ │
│ │ │ (agent badges) │ │
│ │ └──────────────────┘ │
└───────────┼─────────────────────────────────────────────────────────┘
│
▼
┌──────────────┐ ┌──────────────┐
│ Bugsnag │ │ Linear │
│ Datadog │ │ API │
│ APIs │ │ │
└──────────────┘ └──────┬───────┘
│
▼
┌──────────────┐
│ Git Worktree│
│ Claude API │
│ GitHub API │
└──────────────┘
-
Build boatmanmode:
cd ~/workspace/handshake/boatmanmode go build -o boatman ./cmd/boatmanmode
-
Build boatmanapp:
cd /Users/pmiddleton/workspace/personal/boatmanapp wails dev -
Test execution:
- Create a test Linear ticket
- Add labels: "firefighter", "boatmanmode"
- Click "Auto-Execute with BoatmanMode" in UI
- Watch live output
When boatmanmode adds new CLI commands, update the integration:
// boatmanmode/integration.go
// Add new method
func (i *Integration) YourNewCommand(ctx context.Context, args...) error {
cmd := exec.CommandContext(ctx, i.boatmanmodePath,
"your-command",
"--arg", value,
)
// ...
}Then expose it in app.go:
func (a *App) YourNewCommand(args...) error {
bmIntegration, err := bmintegration.NewIntegration(...)
return bmIntegration.YourNewCommand(ctx, args...)
}# Check if in PATH
which boatman
# If not, add to PATH or use full path
export PATH="$PATH:~/workspace/handshake/boatmanmode"Check boatmanmode logs:
tail -f ~/.boatman/logs/execution.logEnsure boatmanmode supports --stream flag or implement polling:
// Poll for status instead of streaming
ticker := time.NewTicker(2 * time.Second)
for range ticker.C {
status := checkStatus()
emit("boatmanmode:output", status)
}BoatmanMode integration gives BoatmanApp users:
- ✅ Automated ticket execution (not just investigation)
- ✅ Full implementation workflow (plan → code → test → review → PR)
- ✅ Live progress streaming
- ✅ Choice between manual investigation vs automated fix
- ✅ Best of both worlds: UI convenience + CLI automation power
The integration is clean (subprocess), flexible (no version coupling), and user-friendly (streaming output, real-time feedback).