Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
![License: MIT](https://img.shields.io/badge/license-MIT-blue)
[![Product Hunt](https://img.shields.io/badge/Product%20Hunt-Launch-ff6154?logo=producthunt&logoColor=white)](https://www.producthunt.com/products/lazy-agent)

A terminal UI, macOS menu bar app, and HTTP API for monitoring all your coding agents — [Claude Code](https://claude.ai/code), [Cursor](https://cursor.com/), [pi](https://github.com/badlogic/pi-mono), and [OpenCode](https://opencode.ai/) — from a single place. No lock-in, no server, purely observational.
A terminal UI, macOS menu bar app, and HTTP API for monitoring all your coding agents — [Claude Code](https://claude.ai/code), [Cursor](https://cursor.com/), [Codex](https://developers.openai.com/codex/), [pi](https://github.com/badlogic/pi-mono), and [OpenCode](https://opencode.ai/) — from a single place. No lock-in, no server, purely observational.

Inspired by [lazygit](https://github.com/jesseduffield/lazygit), [lazyworktree](https://github.com/chmouel/lazyworktree), and [pixel-agents](https://github.com/pablodelucca/pixel-agents).

Expand Down Expand Up @@ -33,10 +33,11 @@ lazyagent watches session data from coding agents to determine what each session
- **Claude Code CLI** — reads JSONL from `~/.claude/projects/*/`
- **Claude Code Desktop** — same JSONL files, enriched with session metadata (title, permissions) from `~/Library/Application Support/Claude/claude-code-sessions/`
- **Cursor** — reads SQLite from `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb`
- **Codex CLI** — reads JSONL from `~/.codex/sessions/YYYY/MM/DD/*.jsonl`
- **pi coding agent** — reads JSONL from `~/.pi/agent/sessions/*/`
- **OpenCode** — reads SQLite from `~/.local/share/opencode/opencode.db`

Use `--agent claude`, `--agent pi`, `--agent opencode`, `--agent cursor`, or `--agent all` (default) to control which agents are monitored. Agents can also be enabled/disabled in the config file. Pi sessions are marked with a **π** prefix, Cursor with **C**, OpenCode with **O**, and Desktop sessions with a **D** prefix in the session list.
Use `--agent claude`, `--agent pi`, `--agent opencode`, `--agent cursor`, `--agent codex`, or `--agent all` (default) to control which agents are monitored. Agents can also be enabled/disabled in the config file. Pi sessions are marked with a **π** prefix, Cursor with **C**, Codex with **X**, OpenCode with **O**, and Desktop sessions with a **D** prefix in the session list.

From the JSONL stream it detects activity states with color-coded labels:

Expand Down Expand Up @@ -119,6 +120,7 @@ lazyagent --agent claude Monitor only Claude Code sessions
lazyagent --agent pi Monitor only pi coding agent sessions
lazyagent --agent opencode Monitor only OpenCode sessions
lazyagent --agent cursor Monitor only Cursor sessions
lazyagent --agent codex Monitor only Codex CLI sessions
lazyagent --agent all Monitor all agents (default)
lazyagent --api Start the HTTP API (http://127.0.0.1:7421)
lazyagent --api --host :8080 Start the HTTP API on a custom address
Expand Down Expand Up @@ -234,6 +236,7 @@ lazyagent reads `~/.config/lazyagent/config.json` (created automatically with de
"notify_after_sec": 30,
"agents": {
"claude": true,
"codex": true,
"cursor": true,
"opencode": true,
"pi": true
Expand All @@ -260,9 +263,10 @@ lazyagent/
├── main.go # Entry point: dispatches --tui / --tray / --api / --agent
├── internal/
│ ├── core/ # Shared: watcher, activity, session, config, helpers
│ │ └── provider.go # SessionProvider interface + Multi/Live/Pi/OpenCode/Cursor providers
│ │ └── provider.go # SessionProvider interface + Multi/Live/Pi/OpenCode/Cursor/Codex providers
│ ├── model/ # Shared types (Session, ToolCall, etc.)
│ ├── claude/ # Claude Code JSONL parsing, desktop metadata, session discovery
│ ├── codex/ # Codex CLI JSONL parsing and session discovery
│ ├── cursor/ # Cursor IDE session discovery from state.vscdb
│ ├── pi/ # pi coding agent JSONL parsing, session discovery
│ ├── opencode/ # OpenCode SQLite parsing, session discovery
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/lib/SessionDetail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
<dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 text-[12px]">
{#if detail.agent}
<dt class="text-subtext">Agent</dt>
<dd class="text-text">{detail.agent === "pi" ? "π pi" : detail.agent}</dd>
<dd class="text-text">{detail.agent === "pi" ? "π pi" : detail.agent === "codex" ? "X codex" : detail.agent}</dd>
{/if}
{#if detail.source === "desktop"}
<dt class="text-subtext">Source</dt>
Expand All @@ -135,6 +135,9 @@
{:else if detail.agent === "claude"}
<dt class="text-subtext">Source</dt>
<dd class="text-text">CLI</dd>
{:else if detail.agent === "codex"}
<dt class="text-subtext">Source</dt>
<dd class="text-text">Codex CLI</dd>
{/if}
{#if detail.model}
<dt class="text-subtext">Model</dt>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/SessionList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
{#if session.agent === "pi"}<span class="text-activity-spawning">π</span>
{:else if session.agent === "opencode"}<span class="text-subtext">O</span>
{:else if session.agent === "cursor"}<span class="text-subtext">C</span>
{:else if session.agent === "codex"}<span class="text-subtext">X</span>
{:else if session.source === "desktop"}<span class="text-accent">D</span>
{/if}
{session.customName || session.agentName || session.shortName}
Expand Down
Loading