Skip to content
diegopachecoPublic

About

dev-cli: all in one dev-cli

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

devcli

devcli is an all-in-one terminal tool for developers, written in Go. One binary includes:

  • a btop-style system dashboard
  • a process killer
  • a Podman/Docker container manager
  • colored JVM thread dumps for Java, Scala, Kotlin and Clojure
  • query consoles for MySQL, Postgres, SQLite, Cassandra, Redis, Loki, Grafana and Prometheus

Every console shares one editor: syntax highlighting, line numbers, auto-complete as you type, history, and results shown as tables or colored JSON.

It opens with an ASCII-art splash, finds your running database containers and asks whether to connect to them. Cmd-K searches everything and jumps there. Every data source also has a one-shot mode for scripts: devcli -sql --postgres "select 1".

The design is in design-doc.md.

How it Works?

  • main.go parses the flags. With no mode it opens the TUI. With a mode (-sql, -redis, -loki, …) it runs once, prints the result and exits.
  • One tview event loop owns the screen. Background workers collect data and hand it back through QueueUpdateDraw, so only the UI goroutine changes what is drawn.
  • Container discovery: at start, podman inspect runs on every running container. Each image name maps to a data store, with its port and credentials read from the container. A prompt then asks which containers each console should use.
  • Cmd-K palette: a native Go overlay. It fuzzy-matches tabs, commands, discovered containers, containers, JVMs, processes, the live completion words of every console (tables, keys, labels, metrics) and query history. Enter goes to the selected item.
  • Dashboard: streams top -l 0 -s 1 for CPU and memory. It reads exact byte counters from netstat -ib and ioreg for network and disk rates, and draws braille graphs and gradient meters.
  • Consoles: all eight share one Console component and one Backend interface.
    • MySQL, Postgres and SQLite use database/sql; Cassandra uses gocql.
    • Redis uses a hand-written RESP2 client.
    • Loki, Grafana and Prometheus use plain net/http.
  • One-shot output: the same renderers produce tview color tags, which a small converter turns into 24-bit ANSI on a terminal or plain text in a pipe. The ASCII banner goes to stderr, so stdout stays clean for jq.
  • Grafana charts in the terminal: chart <uid> sends each panel's own queries through POST /api/ds/query and draws the returned frames as color-tagged text. Time series become braille line charts, and stat, gauge and log panels get their own views. Charts use the width of the results pane (redrawn on resize) or of the terminal in one-shot mode.

Architecture

devcli architecture

The SVG source uses the Caveat font, a wobble filter, pastel boxes and solid arrows.

Features

  • ASCII splash: a gradient DEVCLI banner with a spinner while containers are scanned. Any key skips it.

  • Container discovery + connect prompt: finds running Postgres, MySQL, Cassandra, Redis, Loki, Grafana and Prometheus containers, even with custom ports and passwords. Space toggles, Enter connects. You don't have to type connection strings for your own containers.

  • Cmd-K search anything: jump to a tab, run a command, connect a container, open a JVM's thread dump, filter to a process, or insert a table, key, label or metric into its console. Esc clears the search first, a second Esc closes.

  • Cmd-/ shortcuts: grouped by area, each group with its own icon and color, laid out in columns that fit the screen and scroll inside the modal. Has a search box, a match count, and a message when nothing matches.

  • Cmd-1..9 / Cmd-0, F1..F12: jump to any tab, even while typing in an editor.

  • One-shot mode: -sql --postgres|--mysql|--sqlite, -cassandra, -redis, -loki, -grafana, -prometheus, -ps, -containers, -threads [PID], -discover. Also -json, -target, -timeout and -q, a colored --help, the query from stdin, and exit code 1 on errors.

  • Dashboard (btop style): CPU history, memory, wired, compressed and swap, volumes, disk and network rates, top processes.

  • Processes: filter, sort, and SIGTERM/SIGKILL through a dialog where Cancel is selected by default. Before signaling, devcli checks the PID still belongs to the same command.

  • Containers: running containers first; stop, start, kill, remove, and a shell inside the container with the TUI suspended.

  • Threads: JVMs tagged Java, Scala, Kotlin or Clojure; state colors, a deadlock banner, and frame names that read like source code.

  • MySQL / Postgres / SQLite / Cassandra: multi-statement queries, Enter runs once the statement ends with ;. Completion knows live tables and columns.

  • Redis: any command. JSON values pretty print, hashes render as objects, keys complete.

  • all in MySQL, Postgres, SQLite, Cassandra, Loki, Grafana and Prometheus: type all and press Enter (no ; needed) to see everything the connected server offers:

    • the commands of that console;
    • ready queries built from the server's own tables, labels, metrics, datasources and dashboards. They are printed full width, and Cmd-K lists them so Enter loads one into the editor;
    • Postgres: server, databases, schemas, tables with estimated rows and size, columns, indexes, constraints, sequences, routines, triggers, extensions, roles, sessions and functions;
    • MySQL: server, databases, tables with engine, rows and size, columns, indexes, constraints, routines, triggers, events, users, sessions and functions;
    • SQLite: server, attached databases, tables and views, columns, indexes, foreign keys, triggers, every pragma and functions;
    • Cassandra: cluster, peers, keyspaces, tables, columns, indexes, materialized views, user types, user functions, aggregates, virtual tables and CQL functions;
    • Loki: labels with their values, streams, pipeline stages and functions;
    • Grafana: health, datasources, folders, dashboards and alert rules;
    • Prometheus: metrics with type and help, labels, targets and functions.

    Typing a bare function name such as avg_over_time shows how to call it instead of an empty result or a parse error.

  • Loki: LogQL with labels, values, :range and :limit, and a colored log view.

  • Grafana: search, dashboard, datasources, alerts, query DS EXPR, get /api/....

  • Grafana charts: chart UID draws every panel of a dashboard, and chart UID latency (a panel id or part of a title) draws one:

    • timeseries panels become braille line charts with a value axis, a time axis, one color per series, and a legend with last, min and max. Missing samples and nulls stay gaps;
    • stat panels show the last value in big digits with its unit and a sparkline;
    • gauge, bar gauge, bar chart and pie chart panels become heat-colored bars against the panel max, or 100 for percent;
    • logs panels use the Loki log view, table panels a table;
    • panels inside collapsed rows are included, hidden targets are skipped, and a target's datasource overrides the panel's;
    • the time range comes from the dashboard, with about 300 points per chart; units such as bytes, percent, percentunit, s and ms are formatted like Grafana.

    query DS EXPR also draws a line chart when the result is numeric. devcli -grafana "chart devcli-metrics" prints the charts in any terminal.

  • Prometheus: PromQL instant or :range queries, metrics, labels, values, targets, alerts, rules. Metric names complete.

  • Java 25 sample: sample/java25 is a real multi-threaded app to thread dump, run with scripts/sample-java.sh start|dump|stop.

  • Sample data: scripts/sample-all.sh start|stop starts and fills every data store, and runs the Java 25 sample and a Clojure JVM.

  • macOS install: scripts/install-macos.sh builds and installs one devcli into ~/.local/bin; scripts/uninstall-macos.sh removes it.

Stack

  • Go 1.26: one binary; standard library for HTTP, JSON, flags, process execution and signals.
  • tview + tcell: layout, tables, mouse, paste, the kitty keyboard protocol (which delivers Cmd as a modifier), and a simulation screen for tests and screenshots.
  • go-sql-driver/mysql, lib/pq, mattn/go-sqlite3, gocql: the four database wire protocols.
  • Hand-written code instead of libraries: the RESP2 client; the Loki, Grafana and Prometheus clients; lexers, fuzzy search, completion, the ordered JSON colorizer, the ANSI converter and container discovery.
  • Podman + podman-compose: MySQL 9, Postgres 18, Cassandra 5, Redis 8, Loki 3.5, Grafana and Prometheus for local testing.
  • Playwright (npx): only used to photograph the captured terminal screens for this README.

Contracts/APIs

devcli has no HTTP server. Its contract is the command line, environment variables and keys.

devcli --help
Command Effect
devcli Open the TUI: splash, container discovery prompt, dashboard
devcli -sql --postgres QUERY / -sql --mysql QUERY / --sqlite QUERY Run SQL (--sqllite also works)
devcli -cassandra QUERY Run CQL (-cql also works)
devcli -redis COMMAND Run Redis commands, one per line
devcli -loki LOGQL Query Loki
devcli -grafana COMMAND Grafana API commands
devcli -prometheus PROMQL Query Prometheus
devcli -ps [FILTER] / -containers / -threads [PID] Processes, containers, JVMs or one thread dump
devcli -discover Running data containers, their connect targets and the flag to use
-json -target URL -timeout 30s -no-color -q JSON output, target override, timeout, plain output, no banner
-tab NAME -no-discover -capture DIR -version TUI start tab, skip discovery, HTML screen capture, version

Every option works with one or two dashes, and options go before the query. When no query is given and stdin is not a terminal, the query is read from stdin. Exit codes: 0 ok, 1 query or connection error, 2 invalid flags.

devcli -sql --postgres "select kind, count(*) from events group by kind"
devcli -json --sqlite "select * from hosts" | jq '.[].meta.arch'
echo "HGETALL session:9f2c" | devcli -q -redis
devcli -loki '{app="api"} |= "error"'
devcli -prometheus 'sum by (job) (up)'
devcli -threads "$(pgrep -f DevcliJvm)"
Variable Default
DEVCLI_MYSQL mysql://root:[email protected]:3306/devcli
DEVCLI_POSTGRES postgres://postgres:[email protected]:5432/devcli?sslmode=disable
DEVCLI_SQLITE devcli.db
DEVCLI_CASSANDRA 127.0.0.1:9042/devcli (host[,host]:port/keyspace)
DEVCLI_REDIS redis://127.0.0.1:6380/0 (redis://[user:pass@]host:port/db)
DEVCLI_LOKI http://127.0.0.1:3100
DEVCLI_GRAFANA http://admin:[email protected]:3000; DEVCLI_GRAFANA_TOKEN sends a bearer token
DEVCLI_PROMETHEUS http://127.0.0.1:9090
Scope Keys
Global Cmd-K / Ctrl-K search, Cmd-/ / Ctrl-/ / ? shortcuts, Cmd-1..9 Cmd-0 / F1..F12 tabs, Ctrl-N / Ctrl-P next / previous, Ctrl-Q quit (also from dialogs)
Palette type to search, ↑↓ move, Enter go, Esc clear then close
Connect prompt ↑↓ move, Space toggle, a all, Enter / y connect, Esc / n skip
Editor Ctrl-R run, Enter run or newline, Tab complete, ↑↓ popup or history, Ctrl-L clear, Ctrl-T table/JSON, Ctrl-E connection, F5 reload words, Esc cancel query
Processes / filter, o sort, x SIGTERM, K SIGKILL, r refresh
Containers Enter/e shell, s stop, S start, k kill, d remove, r refresh
Threads Enter dump, Tab focus dump, / filter, s state filter, r refresh

Cmd keys in macOS terminals. tcell turns on the kitty keyboard protocol, so a terminal that forwards Cmd sends it to devcli as a modifier. Most terminals keep Cmd-K for themselves (it clears the screen), so either use Ctrl-K or unbind the key. In Ghostty:

keybind = cmd+k=unbind
keybind = cmd+slash=unbind

In iTerm2 or Terminal.app, map Cmd-K to send the hex code 0x0b, which is Ctrl-K.

Backends talk to these server APIs:

Backend Calls
Prometheus GET /api/v1/status/buildinfo, /api/v1/query, /api/v1/query_range, /api/v1/labels, /api/v1/label/<name>/values, /api/v1/targets, /api/v1/alerts, /api/v1/rules
Loki GET /loki/api/v1/labels, /loki/api/v1/label/<name>/values, /loki/api/v1/query_range
Grafana GET /api/health, /api/search, /api/dashboards/uid/<uid>, /api/datasources, /api/folders, /api/v1/provisioning/alert-rules, /api/annotations, POST /api/ds/query
Redis RESP2 over TCP, AUTH / SELECT from the URL, SCAN for completion
SQL / CQL information_schema.columns, sqlite_master + pragma_table_info, system_schema.columns
Discovery podman ps -q + podman inspect (or docker): ImageName, Config.Env, NetworkSettings.Ports, State.Running

Key data structures and design decisions

Structure Purpose
backend.Backend Connect, Execute, Words, RunsOnEnter, Language: everything a console or one-shot run needs from a data store
backend.Result Columns/Rows tables, Logs log views, Value JSON, Text pre-rendered text, plus title, message, elapsed
discover.Found Kind, container, image, host port and a ready connect URL built from the container's env
ui.PaletteItem Kind, title, detail and the Run action; the palette only scores and draws them
ui.ShortcutGroup Name, icon, color and rows; one table feeds the modal and its filter
cli.Options The parsed mode, dialect, query and output options
syntax.Language / syntax.Object Per-language lexing rules; ordered JSON objects
sys.*, jvm.Dump Parsed command output and thread dumps, each with a pure parser covered by tests
  • One renderer, two outputs. The TUI and one-shot mode use the same table, JSON and log renderers. For one-shot output, cli.Colorize turns the tview tags into ANSI escapes or strips them.
  • Discovery asks first. Containers are only suggested, and nothing connects until you press Enter. Only one container of each kind can be checked, because a console holds one connection. If the scan finishes after you have started working, devcli shows a status-line hint instead of popping the dialog, and the containers stay available in Cmd-K.
  • Dialogs never strand the UI. Every dialog (splash, connect prompt, palette, shortcuts, confirm) sits in a layer that swallows all mouse events, and keys always go to the top dialog. A click on a tab behind a dialog can no longer switch the tab underneath and leave a dialog that ignores Esc.
  • Connection generations. Picking a container while a console is still connecting to its default bumps a counter. The late result of the first connect is then ignored, and the backend is never used by two goroutines at once.
  • Shortcuts that work while typing. Cmd/Ctrl shortcuts are handled before the editor. Plain q, ? and digits only act outside editors, and Ctrl-Q quits from anywhere.
  • Few libraries. Drivers only where a binary protocol needs one; everything else is hand-written.
  • Safe destructive actions. Kill and remove use a dialog with Cancel selected by default, and a reused PID is refused.

How to run the app/tests

Requirements: macOS, Go 1.26, Podman with podman-compose, JDK 25 (java, jcmd), clojure, sqlite3, curl.

./scripts/setup.sh
./scripts/sample-all.sh start
./scripts/ui.sh
./scripts/test-all.sh
./scripts/sample-all.sh stop

Install the binary on your PATH:

./scripts/install-macos.sh
devcli --help
./scripts/uninstall-macos.sh

Each step of install-macos.sh, and why:

  1. Runs uninstall-macos.sh first, so only one version is ever installed.
  2. Rebuilds bin/devcli and copies it into ~/.local/bin, or DEVCLI_INSTALL_DIR. The copy goes through a temporary file and a rename, so macOS never runs a half-written, invalidly signed binary.
  3. Checks that the installed binary runs.
  4. Adds the directory to ~/.zshrc (or ~/.bash_profile) only when it is not already on PATH.
  5. Warns when another devcli on PATH would shadow it.

uninstall-macos.sh stops running devcli processes and removes the binary, and it is safe to run twice.

Java 25 thread dump sample

sample/java25/src is a multi-file Java 25 program: no build tool, launched with java sample/java25/src/DevcliJvm.java. It uses compact source files with void main(), import module, IO.println, ScopedValue, records, sealed interfaces and pattern-matching switch. Its threads are always doing something worth dumping:

Threads State in a dump What they do
order-producer, order-worker-1..4 TIMED_WAITING, WAITING (parking), RUNNABLE a producer fills a BlockingQueue; workers take orders and hash paid orders inside a ScopedValue
ledger-writer, inventory-writer BLOCKED, deadlocked two synchronized monitors taken in opposite order
payment-capture, payment-refund WAITING (parking), deadlocked two ReentrantLocks taken in opposite order
report-exporter, report-reader-1..3 RUNNABLE, BLOCKED one thread holds a monitor for 1.5 s while readers pile up
cpu-hasher RUNNABLE burns about half a core computing SHA-256
cache-refresher, event-listener TIMED_WAITING, WAITING (on object monitor) a scheduler refreshes a cache and notifyAlls a waiting listener
HTTP-Dispatcher, stats-poller, HttpClient-* RUNNABLE a JDK HttpServer on an ephemeral loopback port, polled with HttpClient every 250 ms
ForkJoinPool-1-worker-* WAITING (parking) carrier threads of 500 virtual threads
./scripts/sample-java.sh start
./scripts/sample-java.sh dump
./scripts/sample-java.sh status
./scripts/sample-java.sh stop

start requires Java 25 or newer, and waits until the app has started its threads. dump prints the colored dump through devcli -threads, or jcmd when the binary is not built. The dump shows both deadlocks, and the parser flags all four threads, including the ReentrantLock pair.

sample-all.sh start starts the containers and waits until each one answers a real query. It then loads sample data (safe to run again):

Store Data
Postgres and MySQL users with JSON profiles, orders, 200 events
Cassandra devcli.users, devcli.events
Redis JSON strings, a hash, list, sorted set, set and stream
SQLite .run/devcli.db with hosts and 100 latency samples
Loki api, worker and web log lines
Prometheus scrapes itself, Loki and Grafana
Grafana Loki and Prometheus datasources; devcli-logs (logs, errors per minute) and devcli-metrics (stat, gauge, timeseries and bar gauge panels over Prometheus) dashboards
JVMs the Java 25 sample (scripts/sample-java.sh) and a Clojure loop

test-all.sh runs these steps:

  1. bash -n on every script.
  2. go vet.
  3. Race-enabled unit tests: lexers, fuzzy search, palette navigation, Cmd-K/Cmd-digit routing, shortcut filtering, the connect prompt, discovery parsing, the ANSI converter, flag parsing, one-shot output, the Redis protocol, all parsers, all on a real in-memory SQLite, Loki, Grafana and Prometheus against fake servers, Grafana panel queries and frame shaping, and chart rendering (axes, gaps, big digits, gauges, units, redraw on resize).
  4. Integration tests against the running stack, including every one-shot mode, all on Postgres, MySQL, SQLite and Cassandra with every ready query executed, and charts drawn from both provisioned Grafana dashboards.

Printscreens

These screens are real terminal cells rendered by devcli -capture against the local stack, photographed with npx playwright. Machine-specific values (processes, containers, CPU) change on every run.

./bin/devcli -capture .run/capture
for page in .run/capture/*.html; do
  npx playwright screenshot --browser chromium --viewport-size '1560,990' "file://$PWD/$page" "printscreens/$(basename "${page%.html}").png"
done

1. Splash

The gradient ASCII banner, version and tagline. The spinner line reports the container scan, here 7 data containers found. Any key skips the splash, and it closes by itself after a moment.

Splash

2. Dashboard

On the top row, the CPU panel draws a heat-colored braille history, with user/sys split, model, cores, uptime, load and process/thread counts. The middle row holds memory meters, volume usage with disk rates, and network graphs. The bottom panel lists top processes by CPU.

Dashboard

3. Processes

All processes sorted by CPU (▼ marks the sort column). The filter row accepts text or a PID, and the bottom panel shows the selected process. x / K open the confirm dialog.

Processes

4. Containers

Running containers first, with state dots, published ports and status. Enter opens a shell inside the selected container.

Containers

5. Threads

The JVM list shows the Java 25 sample and the Clojure loop, with language badges. The dump shows per-state counts and a red DEADLOCK banner with both deadlocks: the monitor pair and the ReentrantLock pair. Deadlocked threads are marked ☠, lock lines are orange, JDK frames dimmed and application frames bright.

Threads

6. MySQL

A multi-line join with highlighted keywords, functions and operators. The result table colors numbers orange, JSON documents purple and NULL dim.

MySQL

7. Postgres

A jsonb query in JSON view (Ctrl-T): rows become ordered objects, the profile document is expanded, and short arrays stay on one line.

Postgres

8. SQLite

A join over hosts and metrics. The editor holds the next query, and the completion popup offers the live column meta and table metrics for met.

SQLite

9. Cassandra

CQL over devcli.users. The set<text> column renders as a JSON array.

Cassandra

10. Redis

Three commands in one run: a JSON string pretty printed, HGETALL as an object, and a sorted set with scores.

Redis

11. Loki

Every env="dev" stream except cache hits, merged newest first. Levels are colored, labels are purple, and JSON lines are colorized inline.

Loki

12. Grafana

chart devcli-metrics draws the provisioned Prometheus dashboard in the results pane: targets up and the average scrape duration as big-digit stats with sparklines, a percent gauge per job, and CPU and goroutine line charts with a legend. all lists the Grafana commands and ready commands such as chart devcli-logs and query prometheus up.

Grafana

13. Prometheus

all lists the Prometheus commands first, then ready queries built from live metrics (also in Cmd-K, where Enter loads one): up, a counter rate, a gauge average and a histogram p95. Every metric with its type and help, the labels with their values, the scrape targets and the PromQL functions follow; scroll the results pane with PgUp/PgDn.

Prometheus

14. Connect prompt

Shown after the splash. Each running data container is listed with its kind, name and connect URL, password masked. The first container of each kind is pre-checked; Enter connects the checked ones and Esc skips.

Connect prompt

15. Cmd-K palette

Searching post ranks the Postgres tab first, then the connect action for devcli-postgres, every Postgres container, a matching process, Prometheus metric names that can be inserted, and actions for the current console. Matched letters are underlined in cyan, and Enter goes to the selected item.

Palette

16. Cmd-/ shortcuts

Every shortcut grouped by area, each group with its own icon and color, flowing in columns with a search box and a match count.

Shortcuts

Scripts

All scripts live in scripts/ and run from any directory of the repository.

Script What it does
./scripts/setup.sh Checks tools, downloads Go modules, builds bin/devcli, pulls images
./scripts/sample-all.sh start Starts every container, waits for each service, loads sample data, starts the sample JVMs
./scripts/sample-all.sh stop Stops the sample JVMs and every container
./scripts/sample-java.sh start|stop|status|dump Runs the Java 25 thread dump sample, reports it, dumps it, stops it
./scripts/install-macos.sh Uninstalls any previous copy, rebuilds, installs devcli into ~/.local/bin
./scripts/uninstall-macos.sh Removes the installed devcli
./scripts/start-all.sh Checks the binary and runs sample-all.sh start
./scripts/status.sh Shows every service port as UP or DOWN, plus the sample JVMs
./scripts/test-all.sh Runs bash checks, vet, race-enabled unit tests and integration tests
./scripts/ui.sh Opens the devcli TUI wired to the local stack
./scripts/stop-all.sh Runs sample-all.sh stop
./scripts/sql-console.sh Opens a native console: mysql, postgres, cassandra, redis, sqlite or prometheus

Ports are declared in scripts/ports.env.

./scripts/setup.sh
./scripts/start-all.sh
./scripts/status.sh
./scripts/ui.sh
./scripts/stop-all.sh

About

dev-cli: all in one dev-cli

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages