@scrylog/cli
v0.1.4
Published
Terminal dashboard for monitoring tmux sessions running AI coding agents
Downloads
479
Readme
scrylog
scrylog is a Bun-based terminal dashboard and daemon for monitoring AI coding sessions in tmux and as standalone processes.
It ships four commands:
| Command | Purpose |
| --- | --- |
| scrylog | TUI dashboard and operator CLI |
| scrylogd | Foreground daemon process |
| scrylog-mcp | stdio MCP server that proxies daemon data |
| oc | OpenCode launcher and attach helper |
Features
tmuxmonitoring for OpenCode, Codex, Copilot, and shell panes- OpenCode integration in
hooksorssemode - Claude transcript collection
- Codex transcript collection and hook-based state tracking
- Local daemon with Unix socket IPC on macOS and Linux, TCP fallback on Windows
- Optional TCP REST API exposure (enabled by default on Windows)
- Optional MCP HTTP exposure with bearer token support
- User-service installation on macOS and Linux
- Interactive setup wizard and diagnostics
Requirements
- Bun
>= 1.3.0 tmuxif you wanttmuxmonitoring- OpenCode if you want OpenCode monitoring
pson Unix orwmicon Windows for process auto-discovery
Platform Support
| Tier | Platform | Status | Notes |
| --- | --- | --- | --- |
| Tier 1 | macOS | Tested daily | Full feature set including Keychain OAuth and Safari cookies |
| Tier 2 | Linux / WSL 2 | Compatible | Claude OAuth via ~/.claude/.credentials.json, Chrome/Chromium cookies, systemd service when available |
| Tier 3 | Windows (native) | Experimental / not recommended | TCP IPC, wmic process scan, degraded service status. Chrome cookie decryption not available (DPAPI not implemented). Primary use is ad-hoc debugging, not daily monitoring. |
WSL 2 Notes
- The daemon runs as a Linux process and uses the same paths as native Linux.
- Service installation requires systemd. If
systemctl --useris not available,scrylog daemon statusreportsplatform: wsl(WSL) orplatform: linux-no-systemd(native Linux) with guidance. tmuxmonitoring works inside WSL but cannot see Windows-native processes.- Browser cookie extraction works for Chrome/Chromium running inside WSL, not for the Windows host browser.
Linux Notes
- Linux without systemd (containers, minimal distros) reports
platform: linux-no-systemd— the daemon runs but service management commands throw descriptive errors. - OpenCode port discovery falls back to
/proc/<pid>/net/tcpwhenlsofis unavailable.
Install
Main package
bun install -g @scrylog/cliThis installs the user-facing commands scrylog, scrylogd, scrylog-mcp, scrylog-claude-hook, scrylog-codex-hook, and oc.
OpenCode plugin for hooks mode
Install this only if you want opencode.mode: "hooks":
bun install -g @scrylog/opencode-pluginFrom source
git clone https://github.com/scrylog/scrylog.git
cd scrylog
bun installQuick Start
Simplest setup
Run the setup wizard:
scrylog setupThe wizard can:
- create the config file
- optionally configure OpenCode
- enable or disable the TCP API
- enable or disable CORS for browser access
- enable or disable MCP HTTP
- generate an MCP bearer token
- install the daemon as a user service
Validated wizard paths:
- skip OpenCode entirely and keep defaults
- configure OpenCode
hooksmode - configure OpenCode
ssemode with explicit instance URLs
Launch the dashboard
scrylogIf the daemon is not running, scrylog prints an error with the commands to start it.
Recommended local setup
For most users on macOS or Linux:
- Install
@scrylog/cli. - Run
scrylog setup. - Keep the TCP API disabled unless you explicitly want HTTP access from outside local IPC.
- Keep MCP HTTP disabled unless you explicitly need remote MCP over HTTP.
- Install the daemon as a user service from the wizard.
- Launch the dashboard with
scrylog.
Operator CLI
scrylog is both the TUI entrypoint and the operator CLI.
TUI
scrylog
scrylog --config ~/.config/scrylog/config.jsonKeyboard shortcuts:
| Key | Action |
| --- | --- |
| ↑ / k | Move selection up |
| ↓ / j | Move selection down |
| Enter | Open session detail |
| t | Attach to selected tmux session |
| / | Search |
| f | Cycle filters |
| a | Show actionable sessions only |
| r | Refresh now |
| Esc | Close detail |
| q / Ctrl+C | Quit |
Setup
scrylog setupDaemon service management
scrylog daemon install
scrylog daemon uninstall
scrylog daemon start
scrylog daemon stop
scrylog daemon restart
scrylog daemon statusSupported service managers:
- macOS:
launchduser agent - Linux:
systemd --user - WSL without systemd: reported as
platform: wslwith guidance - Windows: reported as
platform: unsupported— runscrylogdmanually or use Task Scheduler
Typical service workflow:
scrylog daemon install
scrylog daemon start
scrylog daemon statusTo stop automatic startup later:
scrylog daemon stop
scrylog daemon uninstallConfig commands
scrylog config path
scrylog config showToken commands
scrylog token generate
scrylog token rotate
scrylog token clearDiagnostics and logs
scrylog doctor
scrylog logsForeground Daemon
Use scrylogd when you want to run the daemon manually in the foreground:
scrylogd
scrylogd --config ~/.config/scrylog/config.json
scrylogd --log-level debug
scrylogd --log-format json
scrylogd --no-log-fileUse the foreground daemon mainly for:
- debugging startup issues
- verifying listener behavior manually
- local development
Logging options:
| Flag | Purpose |
| --- | --- |
| --log-level debug|info|warn|error | Override the configured minimum log level |
| --log-format pretty|json | Override stderr output format for this process |
| --no-log-file | Disable daemon JSONL file logging for this process |
For normal daily use on macOS and Linux, prefer scrylog daemon install so the daemon starts automatically with your user session.
Configuration
Config is loaded from the first path that exists:
--config <path>SCRYLOG_CONFIG~/.config/scrylog/config.json~/.scrylog.json- built-in defaults
Example config:
{
"daemon": {
"pollIntervalMs": 10000,
"socketPath": null,
"commandTimeoutMs": 5000,
"networkTimeoutMs": 10000,
"logLevel": "info",
"logFormat": "auto",
"logFileEnabled": true,
"logFilePath": "~/.scrylog/logs/scrylogd.log",
"logFileMaxBytes": 5242880,
"logFileMaxFiles": 3
},
"api": {
"tcpEnabled": false,
"host": "127.0.0.1",
"port": 7788
},
"cors": {
"enabled": false,
"origins": [],
"methods": ["GET", "POST", "OPTIONS"],
"allowedHeaders": ["Content-Type", "Authorization"],
"exposedHeaders": [],
"credentials": false,
"maxAge": 600
},
"opencode": {
"mode": "hooks",
"instances": [],
"autoDiscover": true
},
"claude": {
"claudeDir": "/Users/you/.claude",
"hooksEnabled": false,
"hooksDir": "/tmp/scrylog-claude-hooks"
},
"codex": {
"codexDir": "/Users/you/.codex",
"hooksEnabled": false,
"hooksDir": "/tmp/scrylog-codex-hooks"
},
"tmux": {
"previewLines": 50,
"detailLines": 200
},
"tui": {
"refreshMs": 5000,
"showStandalone": true,
"usePushUpdates": true,
"standaloneMaxAgeHours": 6
},
"mcp": {
"stdioEnabled": true,
"httpEnabled": false,
"host": "127.0.0.1",
"port": 7789,
"bearerToken": null
},
"usage": {
"enabled": true,
"pollIntervalMs": 300000,
"providers": ["claude", "opencode-go", "codex"]
},
"retention": {
"maxSessions": 500,
"standaloneMaxAgeHours": 168,
"localCommandTtlHours": 72,
"cacheMaxEntries": 1000,
"transcriptMaxBytes": 10485760
}
}Notes:
- On macOS and Linux, local clients use the Unix socket even when
api.tcpEnabledisfalse. api.tcpEnabledcontrols TCP exposure of the REST API, not local daemon IPC.mcp.httpEnabledcontrols the HTTP MCP listener.scrylog-mcpuses local daemon access and is separate from MCP HTTP.cors.enabledcontrols whether the API and MCP HTTP handlers include CORS headers. Enable it when consuming the API from a browser (e.g. a web dashboard).cors.originsaccepts an array of allowed origins. Use["*"]to allow any origin.daemon.logFormat: "auto"uses pretty stderr output in a terminal and JSONL when stderr is not a TTY.daemon.socketPathoverrides the local Unix socket on macOS/Linux. Leave it unset for the per-user default/tmp/scrylog-<uid>.sock.daemon.commandTimeoutMsbounds daemon-owned commands such asps,tmux,git,lsof, andpgrep.daemon.networkTimeoutMsbounds daemon-owned usage/integration network requests.usage.enabled: falsedisables periodic Claude/OpenCode usage probes without disabling session monitoring.retentionlimits in-memory state. Activeworkingandwaiting-inputsessions are preserved even when old.- The daemon log file is always JSONL and rotates by size:
scrylogd.log,scrylogd.1.log,scrylogd.2.logby default. - The current CLI preserves an existing legacy config path when updating config, so older
~/.scrylog.jsonusers are not forced onto a new file immediately.
Long-Running Operation
scrylogd is a monitor for active and recent sessions, not a permanent historical archive. Defaults are tuned for unattended daemon usage:
| Setting | Default | Effect |
| --- | ---: | --- |
| retention.maxSessions | 500 | Prunes oldest inactive sessions above this count |
| retention.standaloneMaxAgeHours | 168 | Prunes inactive standalone sessions older than 7 days |
| retention.cacheMaxEntries | 1000 | Bounds internal path/project caches |
| retention.transcriptMaxBytes | 10485760 | Parses only a bounded tail for very large Claude transcripts |
| daemon.commandTimeoutMs | 5000 | Prevents external commands from blocking indefinitely |
Expected daemon RSS depends on active sessions and transcript size:
| Scenario | Expected RSS | | --- | ---: | | No active sessions | 35-70 MB | | Normal local usage | 50-120 MB | | Heavy tmux/Claude/OpenCode usage | 100-250 MB |
If you want longer history, increase retention.maxSessions or retention.standaloneMaxAgeHours. Setting age to 0 disables age-based pruning, but count-based pruning can still apply.
Run diagnostics after changing operational settings:
scrylog doctorDoctor reports the effective socket path, log path, retention limits, timeout values, and whether usage collection is enabled.
Daemon Logs
scrylogd writes structured logs to stderr and, by default, to:
~/.scrylog/logs/scrylogd.logThe file format is JSONL for bug reports and tooling. Terminal stderr output defaults to pretty formatting when interactive.
Default file rotation keeps three files total:
| File | Purpose |
| --- | --- |
| scrylogd.log | Active log file |
| scrylogd.1.log | Previous active file |
| scrylogd.2.log | Oldest retained file |
Example JSONL entry:
{"ts":"2026-05-05T18:42:31.123Z","level":"info","source":"daemon","event":"daemon.started","msg":"Started"}Logs sanitize home-directory paths, bearer tokens, authorization values, and URL query strings before writing.
The daemon's own file logs are capped by daemon.logFileMaxBytes * daemon.logFileMaxFiles (15 MB by default). Stderr logs written by systemd --user or launchd are retained according to the operating system's service logging policy.
Exposure Recommendations
Recommended defaults for a local machine:
{
"api": {
"tcpEnabled": false
},
"cors": {
"enabled": false
},
"mcp": {
"httpEnabled": false
}
}Enable api.tcpEnabled only when you want TCP access to the REST API.
Enable cors.enabled when a browser-based client needs to consume the API directly.
Enable mcp.httpEnabled only when you want MCP over HTTP. If you enable it, also set or generate mcp.bearerToken.
OpenCode Integration
OpenCode setup is optional. If you skip it in scrylog setup, defaults remain unchanged until you configure it later.
scrylog supports two mutually exclusive OpenCode modes.
hooks mode
Recommended mode when you run OpenCode inside tmux.
Use it when you want:
- immediate session updates from OpenCode events
- permission prompt visibility
- the best available pane correlation in
tmux
Requirements:
scrylog@scrylog/opencode-plugin- OpenCode config with the plugin enabled
Setup:
- Install the plugin.
bun install -g @scrylog/opencode-plugin- Set
opencode.modetohooksand register the plugin.
scrylog setup
scrylog opencode hooks installAlternatively, add the plugin to ~/.config/opencode/opencode.json manually:
{
"plugin": ["@scrylog/opencode-plugin"]
}- Start
scrylogdor install the service.
Recommended command flow:
scrylog setup
scrylog daemon install
scrylog daemon statusWhat it allows:
- webhook-driven OpenCode updates
- better
tmuxcorrelation when the plugin writes@opencode-session - child session visibility when OpenCode emits parent IDs
What it does not allow:
- heuristic
opencode attachcorrelation insidetmux - complete exact-session tracking without an upstream OpenCode signal
sse mode
Use this when OpenCode exposes an HTTP endpoint, such as:
opencode --port 4096opencode serveoc
Setup:
- Set
opencode.modetosse. - Optionally configure
opencode.instances. - Leave
opencode.autoDiscoverenabled if you want local process discovery. - Start OpenCode with
opencode --port 4096,opencode serve, oroc.
What it allows:
- standalone OpenCode monitoring through the SDK
- explicit instance URLs
oclaunch and attach workflows
What it does not allow:
- OpenCode
tmuxrows - plugin-driven updates
Important behavior:
- in
ssemode, OpenCode is shown as standalone only - the daemon ignores webhook payloads from the plugin in
ssemode
oc
oc is the OpenCode helper included in scrylog. It is mainly intended for sse mode.
oc
oc --continue
OPENCODE_LOCAL_PORT=4097 oc
OPENCODE_HOST=mac-mini.my-tailnet.ts.net OPENCODE_LOCAL_PORT=4096 ocHow it works:
- scans running processes for OpenCode instances exposing a port
- probes each candidate with
GET /global/health - does one of the following:
- no live instance: launches
opencode --port 4096 - one live instance: runs
opencode attach <url> - multiple live instances: prompts you to choose one
- no live instance: launches
Environment variables:
| Variable | Default | Purpose |
| --- | --- | --- |
| OPENCODE_LOCAL_PORT | 4096 | Force a specific port |
| OPENCODE_HOST | 127.0.0.1 | Force a specific host |
HTTP API
The daemon always exposes local IPC on Unix socket on macOS and Linux. The TCP HTTP API is optional.
Enable it with:
{
"api": {
"tcpEnabled": true,
"host": "127.0.0.1",
"port": 7788
}
}Endpoints:
| Method | Path | Returns |
| --- | --- | --- |
| GET | /api/health | daemon health, timestamps, collector status |
| GET | /api/status | visible counts, collector status, generated timestamp |
| GET | /api/sessions | visible session tree plus counts |
| GET | /api/sessions/:id | full session detail |
| GET | /api/sessions/:id/children | visible child sessions |
| GET | /api/sessions/:id/messages | recent messages when available |
| GET | /api/config | active config with sensitive fields redacted |
| GET | /api/events | SSE stream of store events |
| POST | /api/webhook/opencode | OpenCode plugin webhook |
All error responses follow a consistent format:
{ "error": "Session 'abc' not found", "code": "NOT_FOUND" }Known codes: NOT_FOUND, UNAUTHORIZED, VALIDATION_ERROR, INTERNAL_ERROR.
Example:
curl http://127.0.0.1:7788/api/health
curl http://127.0.0.1:7788/api/sessionsMCP
stdio MCP
Use scrylog-mcp for local MCP clients:
scrylog-mcpExample MCP client config:
{
"mcpServers": {
"scrylog": {
"command": "scrylog-mcp"
}
}
}Available tools:
list_sessionsget_active_sessionsget_sessionget_session_messagesget_session_children
Available resources:
sessions://allsessions://activeconfig://current
MCP HTTP
MCP HTTP is disabled by default.
Enable it with:
{
"mcp": {
"httpEnabled": true,
"host": "127.0.0.1",
"port": 7789,
"bearerToken": "your-secret-token"
}
}If bearerToken is set, clients must send:
Authorization: Bearer <token>
For streamable HTTP clients, include:
Accept: application/json, text/event-stream
The server uses stateful MCP HTTP sessions:
- send
initialize - read the
mcp-session-idresponse header - send
notifications/initializedwith the samemcp-session-id - send later MCP requests with the same
mcp-session-id
MCP HTTP Security
Generate a token from the operator CLI:
scrylog token generateRotate it later with:
scrylog token rotateClear it with:
scrylog token clearService Installation
You can keep the daemon running automatically with the operator CLI:
scrylog daemon install
scrylog daemon start
scrylog daemon statusmacOS uses a LaunchAgent under ~/Library/LaunchAgents/.
Linux uses systemd --user under ~/.config/systemd/user/.
The service lifecycle was validated with:
- install
- start
- restart
- stop
- uninstall
If you want to inspect the current service state or listener reachability, run:
scrylog daemon status
scrylog doctorTroubleshooting
scrylog cannot connect to the daemon
Start it manually:
scrylogd --log-level debugOr inspect the service:
scrylog daemon status
scrylog logsIf you normally use the user service, also try:
scrylog daemon restartOpenCode is missing in hooks mode
Check:
opencode.modeishooks@scrylog/opencode-pluginis installed- OpenCode config includes
"plugin": ["@scrylog/opencode-plugin"] - the daemon is running
OpenCode is missing in sse mode
Check:
opencode.modeissse- OpenCode is exposing a reachable HTTP endpoint
opencode.instancesis correct orautoDiscoveris enabledGET /global/healthsucceeds on the OpenCode endpoint
MCP HTTP returns 401 Unauthorized
Send the configured bearer token.
MCP HTTP returns No valid session ID provided
Start with initialize, then reuse the returned mcp-session-id.
I want a guided setup again
Run:
scrylog setupValidated User Flows
These user-facing flows were exercised after implementation:
- operator CLI help and subcommands
- daemon foreground startup
- daemon user-service install, start, restart, stop, uninstall on macOS
scrylogTUI launch and quit- setup wizard skip,
hooks, andsseflows - MCP token generate, rotate, and clear
- TCP API enable and disable behavior
- MCP HTTP enable, disable, and bearer-token enforcement
scrylog-mcpstdio startup and tool discovery
Development And Release
Development Hooks
When developing from this checkout, use local hook paths instead of the published package. This is only for local development and manual testing; normal users should follow the published package setup above.
Claude Code hook commands should point to this checkout:
bun run /Volumes/Files/Workspace/Projects/other/scrylog/src/hooks/claude.tsUse that command for every Claude hook event that scrylog tracks.
Codex hook commands should point to this checkout:
bun run /Volumes/Files/Workspace/Projects/other/scrylog/src/hooks/codex.tsUse that command for every Codex hook event that scrylog tracks.
For OpenCode local plugin testing, create or update:
~/.config/opencode/plugins/scrylog.tswith:
export { server } from "file:///Volumes/Files/Workspace/Projects/other/scrylog/packages/opencode-plugin/src/index.ts"For package usage, prefer the published plugin configuration:
{
"plugin": ["@scrylog/opencode-plugin"]
}Validation:
bun run check-types
bun run testPublish-readiness checks:
bun run release:checkPublic packages are versioned with Changesets and validated through Turborepo. The release workflow creates a version PR on main; merging that PR publishes @scrylog/cli and @scrylog/opencode-plugin to npm.
OpenSpec
Design and behavior artifacts live in openspec/.
