Building a Visual Wrapper for Claude Code - Why Native macOS Beats the Terminal for Agent Debugging

M
Matthew Diakonov

The Terminal Is Great Until You Need to Debug the Agent

Claude Code is powerful. It reads files, writes code, runs commands, and manages complex multi-file changes from a terminal interface. For a lot of tasks - especially straightforward edits or quick one-shot commands - the terminal is perfect. Fast, keyboard-driven, no distractions.

But when something goes wrong and you need to understand why the agent made a particular sequence of decisions, scrolling through hundreds of lines of interleaved output is slow. You want to see the conversation flow, the tool calls, the files read and modified - all navigable, not just scrollable. That is the gap a visual wrapper fills.

What a SwiftUI Wrapper Actually Gives You

Some developers build native macOS wrappers around Claude Code using SwiftUI. The core value is not aesthetics - it is observability.

A SwiftUI wrapper can render each tool call as a collapsible card instead of a raw JSON block. The agent reads three files before writing a function: in the terminal, those reads are three lines in the scroll. In a visual UI, they are three expandable nodes you can inspect individually. You can see what the agent actually read versus what you expected it to read.

File diffs become inline comparisons rather than unified diff output. Before-and-after in two columns is faster to review than hunting through + and - lines in monospace text. For code review workflows where a human is approving or rejecting each change, this matters.

Apple integrated Claude Code into Xcode in 2025 via the Model Context Protocol, which lets Claude Code capture SwiftUI Previews directly from the CLI. The agent can see what the UI looks like, change a value, verify the preview updated correctly, and continue - a visual feedback loop without leaving the agent workflow. That pattern - letting the agent see its own output - is the same instinct that drives wrapping the CLI in a visual shell.

What Observability Changes About Working with Agents

When you can see exactly what the agent is doing - which files it read, what searches it ran, what it concluded from each step - you develop better intuition for prompting. You start noticing patterns:

  • It always reads the wrong config file first when there are two with similar names
  • It consistently misses a specific import that lives in a non-obvious location
  • It tends to over-engineer simple utility functions when the codebase has existing helpers it did not find

These patterns are there in the terminal output too, but they take real effort to extract. A visual UI makes them visible without hunting.

The feedback loop from observability to better prompting is what separates developers who get consistent results from agents versus those who treat every session as a lottery. The faster you can understand what the agent did wrong, the faster you can write a better prompt.

A Minimal Architecture That Works

You do not need to build a full IDE. The minimal useful wrapper does three things:

  1. Parses the Claude Code stream - Claude Code outputs structured JSONL to stdout in API mode. A SwiftUI app can consume this and render it in real time.
  2. Groups events by conversation turn - tool calls, file reads, writes, and shell executions all get associated with the turn that triggered them.
  3. Surfaces diffs inline - when the agent writes a file, compute the diff against the previous version and render it in a split view.
// Parsing the Claude Code stream
struct AgentEvent: Decodable {
    let type: String        // "text", "tool_use", "tool_result"
    let id: String?
    let name: String?       // tool name when type == "tool_use"
    let input: AnyCodable?  // tool input parameters
    let content: AnyCodable?
}

// Group events by conversation turn for display
class TurnViewModel: ObservableObject {
    @Published var events: [AgentEvent] = []
    @Published var filesModified: [FileDiff] = []
}

The menu bar is worth adding early: a persistent status indicator that shows whether the agent is running, idle, or has an error waiting for review - without requiring the wrapper app to be foregrounded.

The Trade-Off Is Real

Building a native wrapper is real engineering work. You are now maintaining a SwiftUI app in addition to your agent workflows. Every time Claude Code changes its output format, your parser may need updating.

For developers who spend a couple of hours a week with AI coding assistance, the terminal is good enough. For developers running agents for several hours a day, debugging agent decisions in a visual UI pays back the build cost within a week.

The tooling you build around your agent is part of the system. An agent you can observe is an agent you can improve.


Fazm is an open source macOS AI agent. Open source on GitHub.

More on This Topic

Related Posts