You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/blog/building-opencode-command-center.md
+12-12Lines changed: 12 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,11 +8,11 @@ draft: false
8
8
9
9
## The Problem
10
10
11
-
I run a lot of AI agents. On a busy day I might have eight or more OpenCode sessions going in separate tmux panes. Each one is working on something different. Some are humming along. Some are stuck waiting for me to approve a tool call. Some crashed ten minutes ago and I haven't noticed.
11
+
I run a lot of AI agents. On a busy day I might have eight or more OpenCode sessions going in separate tmux panes. Each one is working on something different with a slew of sub-agents of their own. Some are humming along. Some are stuck waiting for me to approve a tool call. Some crashed ten minutes ago and I haven't noticed. Some are hanging on waiting for a trickle of free usage because I overdid it with OpenAI, Anthropic, and Google models.
12
12
13
-
My workflow was: cycle through each tmux pane, glance at the bottom of the terminal, figure out the state, move on. This stops working past three or four agents. By the time I check the last one, the first might be stuck again.
13
+
I cycle through each tmux pane, glance at the bottom of the terminal, figure out the state, move on. This stops working past three or four agents. By the time I check the last one, the first might be stuck again.
14
14
15
-
I wanted a single screen that showed me everything. A GUI was out. I'm already in the terminal. I don't want to context-switch to a browser. So I built `occc` (OpenCode Command Center), a TUI in Rust using Ratatui. I built the whole thing in a day, with AI agents helping write it. Yes, I used AI agents to build a tool for monitoring AI agents. I'm aware of the recursion.
15
+
I wanted a single screen that showed me everything. A GUI was out. I'm already in the terminal. I don't want to context-switch to a browser so I built `occc` (OpenCode Command Center), a TUI in Rust using Ratatui. I built it with AI agents helping write it. Yes, I used AI agents to build a tool for monitoring AI agents. I'm aware of the recursion. There's something poetic about it.
16
16
17
17
## Architecture
18
18
@@ -32,19 +32,19 @@ The DB and tmux feed the polling engine, which pushes updates into the event loo
32
32
33
33
## Two Data Sources
34
34
35
-
OpenCode stores all its session data in a SQLite database at `~/.local/share/opencode/opencode.db`. Messages, model info, tool calls, todos. It's a rich source of context for understanding what an agent has been doing.
35
+
OpenCode stores all its session data in a local SQLite database. Messages, model info, tool calls, todos. It's a rich source of context for understanding what an agent has been doing.
36
36
37
-
But the DB has a blind spot. It doesn't reflect what's happening right now. If an agent is blocked on a permission prompt, the database still shows the last completed message. The session looks active when it's actually waiting for you.
37
+
But the DB has a blind spot. It doesn't reflect what's happening right now. If an agent is blocked on a permission prompt, the database still shows the last completed message. The session looks active when it's actually waiting...just waiting...
38
38
39
39
The fix: also read the tmux pane content with `tmux capture-pane`. The pane shows exactly what's on screen at this moment. It's the live view.
40
40
41
-
Combined, the DB gives you context (what happened) and the pane gives you state (what's happening now). Neither one is enough on its own.
41
+
Combined, the DB gives gave me context (what happened) and the pane gives me state (what's happening now). Neither one is enough on its own.
42
42
43
43
## Async Polling
44
44
45
-
The TUI has to stay responsive. If the main thread blocks on a database query or waits for eight `tmux capture-pane` calls to finish, the UI freezes. That's not acceptable.
45
+
The TUI has to stay responsive. If the main thread blocks on a database query or waits for eight (or more) `tmux capture-pane` calls to finish, the UI freezes. That's not acceptable.
46
46
47
-
Polling runs on a background thread at 500ms intervals (configurable with `--poll-interval`). It queries the DB, captures all the tmux panes, and sends a single update to the main thread through a channel. The main thread handles keyboard input and renders at ~60fps. It picks up new data whenever it arrives, but never waits for it.
47
+
Polling runs on a background thread at 500ms intervals (configurable with `--poll-interval` and was good enough for me). It queries the DB, captures all the tmux panes, and sends a single update to the main thread through a channel. The main thread handles keyboard input and renders at ~60fps. It picks up new data whenever it arrives, but never waits for it.
48
48
49
49
I took this pattern from tmuxcc, one of three similar projects I studied before writing any code. The others were recon and ATM. Studying prior art before building saved me from a lot of wrong turns.
50
50
@@ -54,14 +54,14 @@ The hardest question was: how do you detect agent status without modifying OpenC
54
54
55
55
The answer is simple. Look at the last few lines of the terminal. OpenCode renders a status bar at the bottom of the pane. The status bar tells you everything.
56
56
57
-
If you see "esc interrupt", the agent is working. If you see "Confirm" and "Cancel", the agent is blocked on a permission prompt and needs your attention. If you see a `>` prompt, the agent is idle.
57
+
If you see "esc interrupt", the agent is working. If you see "Confirm" and "Cancel", the agent is blocked on a permission prompt and needs your attention. If you see a `>` prompt, the agent is idle. This is brittle but it is simple and works at the moment.
58
58
59
-
The detection logic scans the bottom five non-empty lines of pane content, bottom-up. It's simple, reliable, and version-independent. It mirrors what you'd do manually: glance at the bottom of the terminal and read the status bar.
59
+
The detection logic scans the bottom five non-empty lines of pane content, bottom-up. It's simple, reliable, and version-independent. It mirrors what I do manually, glance at the bottom of the terminal and read the status bar.
60
60
61
61
No structured event parsing. No hooks. Just text matching on five lines of terminal output.
62
62
63
63
## Using It
64
64
65
-
I keep `occc` running in its own tmux pane now. One glance tells me which agents need attention. I press `a` to approve a blocked agent without leaving the pane. It's a small tool that removed a real friction point from my workflow.
65
+
I keep `occc` running in its own tmux pane now. One glance tells me which agents need attention. I press `a` to approve a blocked agent without leaving the pane. It's a small tool that removed a real friction point from my workflow but it still needs to be refined.
66
66
67
-
Building it with AI agents was fitting. They wrote most of the boilerplate while I focused on the architecture and the polling design. The recursive irony wasn't lost on me, but mostly it just worked.
67
+
Building it with AI agents was fitting. They wrote most of the boilerplate while I focused on the architecture and the polling design.
0 commit comments