Skip to main content
Pentagon is a native macOS application built with Swift and SwiftUI. It manages multiple Claude Code processes, each running in its own terminal, and coordinates them through an event-driven architecture based on file watching and hook injection.

Tech Stack

ComponentTechnologyRole
LanguageSwiftApplication logic, concurrency with actors
UI FrameworkSwiftUI + @ObservableReactive interface, state management via AppState
TerminalSwiftTermTerminal emulation for each agent’s Claude Code session
LayoutNSSplitViewControllerThree-panel split view (sidebar, canvas, detail)
File WatchingDispatchSourceEvent-driven monitoring of agent files — no polling
Process ManagementFoundation.ProcessSpawning Claude Code processes via PTY
Auto-UpdatesSparkleBackground update checks and installation

Process Management

Each agent runs as an independent Claude Code process:
Foundation.Process → PTY → SwiftTerm terminal view
  1. SpawnFoundation.Process launches claude with the agent’s working directory, model, and permission flags.
  2. PTY bridge — A pseudo-terminal connects the process to a SwiftTerm terminal view, providing full terminal emulation (colors, cursor movement, scrollback).
  3. Display — The SwiftTerm view renders inside the Detail Panel when an agent desk is selected.
Agents are fully isolated. Each process has its own environment, working directory, and terminal session. Killing one agent has no effect on others.

Event-Driven File Watching

Pentagon uses DispatchSource.makeFileSystemObjectSource to monitor agent directories for changes. There is no polling. When a file changes in ~/.pentagon/agents/{id}/, the corresponding DispatchSource fires a callback. Pentagon watches these files per agent:
FileTriggers
status.jsonStatusEngine state transition — updates desk ring color and animation
report.jsonUpdates the agent’s self-reported summary in the Detail Panel
TASKS.mdTaskEngine parses checkboxes and updates progress indicators
MEMORY.mdRefreshes the Memory tab in the Detail Panel
SOUL.mdRefreshes the Soul tab in the Detail Panel
heartbeat-config.jsonHeartbeatScheduler picks up new restart configuration
This design means Pentagon reacts to changes instantly, whether they come from hook scripts, the agent itself, or manual edits on disk.

Hook Injection

Pentagon injects hooks into Claude Code so that agent activity is reported back to the app. This is the primary mechanism for status tracking.

How It Works

  1. HookInjector generates five shell scripts in ~/.pentagon/hooks/:
    • session-start.sh
    • agent-stop.sh
    • tool-activity.sh
    • post-tool.sh
    • session-end.sh
  2. Each script writes a status.json file atomically (write to temp file, then mv) to the agent’s directory at ~/.pentagon/agents/{id}/status.json.
  3. HookInjector creates a .claude/settings.local.json file in the agent’s working directory that registers these scripts as Claude Code hooks.
  4. Pentagon’s DispatchSource watcher detects the status.json change and feeds the event into the StatusEngine.

Hook Events

Hook EventScriptSignal
SessionStartsession-start.shsessionStarted
Stopagent-stop.shstopRequested
PreToolUse (sync)tool-activity.shtoolUsed
PostToolUse (async)post-tool.shtoolUsed
SessionEndsession-end.shsessionEnded

Claude Code Integration

CLI Discovery

Pentagon searches for the claude binary in this order:
  1. /usr/local/bin/claude
  2. /opt/homebrew/bin/claude
  3. ~/.npm-global/bin/claude
  4. $PATH lookup

Environment

Environment variables from your shell profile are passed through to Claude Code processes. Pentagon adds agent-specific configuration (model, permissions) via command-line flags.

System Prompt

Pentagon generates a system prompt for each agent that includes:
  • The agent’s name and identity from SOUL.md
  • Current tasks from TASKS.md
  • Memory context from MEMORY.md
  • Pod and team context if the agent belongs to a group

Git Worktree Management

The GitWorktree Swift helper manages branch isolation across agents:
  • First agent in a repository works on your existing branch. No worktree is created.
  • Subsequent agents in the same repository each get an isolated git worktree with a dedicated branch.
  • When an agent is deleted, its worktree is cleaned up automatically.
This prevents file conflicts when multiple agents work on the same codebase simultaneously.

StatusEngine

The StatusEngine is a centralized state machine that determines every agent’s current status. All state transitions flow through a single function: StatusEngine.apply(). Inputs to the StatusEngine come from two sources:
  • Hook signals — parsed from status.json written by hook scripts
  • User actions — pause, resume, respawn commands from the UI
The StatusEngine enforces deterministic transitions. Given the same current state and signal, the result is always the same. See the StatusEngine reference for the full state diagram and transition table.