npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vascent/lazysql

v0.1.21

Published

A lazygit-style TUI database manager.

Downloads

3,015

Readme

lazysql

English · 简体中文

npm license platform

A lazygit-style terminal database client (TUI). Keyboard-driven, panel-based, cross-database, and generates SQL from natural language — all without leaving the keyboard.

Work with databases in your terminal the way lazygit works with git: connect, browse, edit, query, manage schema.

lazysql demo

✨ Features

  • 🔌 Multiple data sources: PostgreSQL · MySQL/MariaDB · SQLite · MongoDB · Redis
  • 📊 Browse / edit: pagination · column sort · column filter · row-level edit and delete (parameterized, run inside a real transaction, auto-rollback when affected≠1)
  • ⌨️ SQL editor: multi-line editing · execute · per-connection persistent history (^P/^N) · schema-aware completion (table names / column names scoped by FROM / keywords, toggle with ^T) · collapses to a one-line echo bar (^O) so the grid keeps the screen
  • 📤 Data export: to CSV · JSON · SQL — the current grid view, a whole table, an entire schema/category, or a multi-select of tables (v to mark); streamed to disk at constant memory, cancellable (esc), with a live row count. CSV writes one file per table; JSON and SQL each combine into a single file
  • 🧬 Schema introspection: tables / views / indexes / sequences / triggers / stored procedures / Postgres enums; inspect an object's columns (enum columns show their allowed values) and DDL definition
  • 🛡️ Destructive-operation guard: a WHERE-less UPDATE/DELETE, DROP, or TRUNCATE always pops a centered confirmation dialog echoing the full SQL to be run; when a Postgres DROP fails due to dependents, it offers a CASCADE retry and names the objects that would be dropped along with it
  • 🌳 Object tree: filter objects by name with / or f (live-narrows as you type) · auto-refreshes after a successful DDL (CREATE/DROP/ALTER/…)
  • 🤖 NL→SQL: press ^G, type natural language, and the LLM generates SQL placed into the editor for review (never auto-executed); destructive statements are flagged with a red ⚠
  • 🗂️ Connection management: multi-connection config · create / edit / test connections in-TUI · passwords stored separately from config (optional OS Keychain) · SSH tunnel to databases behind a bastion (key/agent auth, ~/.ssh/config aliases work)
  • 🖱️ Modern terminal UX: mouse / scroll wheel · system-clipboard copy · full-cell inspector (long text wraps by display width, no CJK truncation)

📦 Installation

Ready to run, no Bun required — prebuilt bun --compile native binaries are distributed via npm, one per platform; only the one matching your system gets installed. Supports macOS (Apple Silicon) · Linux (x64 / arm64) · Windows (x64).

Package managers (recommended)

| Method | Command | |------|------| | npm | npm i -g @vascent/lazysql | | bun | bun add -g @vascent/lazysql | | Try without installing | npx @vascent/lazysql --list |

Then just type lazysql.

When installing with bun add -g, if lazysql reports command not found, Bun's global directory ~/.bun/bin isn't on your PATHbun add -g usually prints a hint to add it (npm i -g generally doesn't have this issue).

From source (to try unreleased changes)

To run the latest unreleased code, or use your local edits right away:

git clone https://github.com/Yangeyu/lazysql && cd lazysql
bun install
bun link            # register a global lazysql → ~/.bun/bin/lazysql (symlinked to this repo)

bun link puts the command in Bun's global directory ~/.bun/bin; make sure it's on your PATH (command -v lazysql returns a path). If not, add one line — exactly the same config bun add -g needs:

echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc

The symlink points at the repo, so git pull or code edits take effect immediately — no reinstall; undo with bun unlink inside the repo.

Upgrade

npm i -g @vascent/lazysql@latest      # bun: bun add -g @vascent/lazysql@latest

A from-source install upgrades via git pull — the symlink picks it up immediately.

Uninstall

npm un -g @vascent/lazysql            # bun: bun remove -g @vascent/lazysql; from source: bun unlink

Uninstalling only removes the program itself. Config and history (connections, passwords, SQL history) stay in ~/.config/lazysql/; to wipe them too:

rm -rf ~/.config/lazysql

On macOS, if you ever set LAZYSQL_SECRETS=keychain, passwords live in the system keychain and aren't removed by deleting ~/.config/lazysql — search for lazysql in Keychain Access and delete the matching entries manually.

🚀 Usage

lazysql                   # open the default (first saved) connection
lazysql <name>            # open a saved connection by id / name
lazysql <file.db>         # open an ad-hoc SQLite file
lazysql --list            # list saved connections and exit

The first run auto-creates ~/.config/lazysql/connections.yml. Inside the TUI: press ? for the full keymap, ` to switch connections, and n to create one — see the Keybindings section below for the rest.

⌨️ Keybindings

vim-style, panel-based. Grouped by context below; the full list is in-app via ? (the footer and help both render from the same keymap table, so they never drift).

Global

| Key | Action | |----|------| | ` | Switch connection (back to the picker) | | : | Open the SQL editor (expands it if collapsed) | | ^O | Expand / collapse the SQL editor — collapsed it is a one-line echo of the SQL behind the grid | | ^G | Ask in natural language (opens the SQL editor) | | tab | Toggle focus between tree ↔ results | | ^H / ^L | Focus the tree / the results grid directly | | ^⇧- / ^⇧+ | Shrink / widen the connections sidebar | | ! | Reopen the current error details | | F12 | Toggle the runtime debug console (logs and unhandled errors) | | ? | Toggle help | | q | Quit |

Failures (a refused write, a lost connection, a failed export…) pop an error dialog with the full message, the driver's error code and its detail. esc / dismisses it; ! reopens the retained details.

Sidebar (tree)

| Key | Action | |----|------| | k / j · / | Move selection | | / space | Expand / collapse / open object | | / l | Expand | | / h | Collapse / jump to parent | | a | Clean SELECT * browse of the selected table | | / or f | Filter objects by name — live-narrows the tree ( keep · esc clear) | | v | Mark / unmark a table for a batch export (multi-select) | | X | Export — marked tables, else all tables under the node (schema / category), else this one | | esc | Clear the tree filter, else all export marks | | g / G | Jump to first / last | | D | View the object's DDL / structure | | d | Draft a DROP into the editor | | r | Refresh connection and object tree | | n / e | New / edit connection | | x | Remove the selected connection |

Results grid

| Key | Action | |----|------| | k / j | Move row cursor | | h / l · / | Move column cursor · horizontal scroll for wide tables | | g / G | Jump to first / last row | | ^U / ^D | Half page up / down | | | Inspect the full cell value | | y | Copy the focused cell's full value to the clipboard | | a | Browse the selected table (SELECT *) | | s | Cycle sort (asc → desc → none) | | / | Filter by column substring | | esc | Undo the latest applied filter — restore its previous page, sort and focused cell | | d | Delete row ( inspect, then e edits the cell) | | X | Export the view — a browsed table to CSV / JSON / SQL (filtered & sorted), a query result to CSV / JSON | | n / p | Next / previous page | | D | Toggle Data / DDL tab |

DDL / structure tab

| Key | Action | |----|------| | / k/j | Scroll the structure / DDL | | g / G | Jump to the first / last line | | D | Toggle Data / DDL tab |

SQL editor

| Key | Action | |----|------| | | Run the query (results show in the grid) | | ⇧⏎ | Insert a newline — compose multi-line SQL | | tab | Accept completion, otherwise move to the next panel | | ^P / ^N | Previous / next history entry | | ^T | Toggle schema-aware completion | | ^G | Generate SQL from natural language | | ^C | Clear the draft | | ^O | Collapse the editor to its one-line echo bar (the draft is kept, flagged (draft)) | | esc | Back to the results grid (the editor stays open) |

Confirmation dialog

| Key | Action | |----|------| | y | Apply the pending write / run the export | | n | Cancel | | f | Cycle export format (CSV / JSON / SQL) — export confirm only |

Cell inspector

| Key | Action | |----|------| | j / k · / | Scroll the value | | e | Edit the cell in place (^S save · esc back) | | y | Copy the full value to the clipboard | | q / esc / | Close |

New / edit connection form

| Key | Action | |----|------| | / | Move between the driver, fields and buttons | | / | Cycle the driver / focused button (on that row) | | ^R | Show / hide password | | ^T | Test the connection (without saving) — reports the visible object count | | | Save — or press the focused button | | esc | Cancel |

Port/DB fields accept digits only; required fields are validated on save (the error names the field). Everything is also clickable.

Paste a connection URL (postgres://, mysql://, mongodb://, redis://) into the URL row and press to fill the whole form — driver, host, port, user, password and database are split out automatically; a name you typed is kept. mongodb+srv and rediss URLs are not supported by the form (use the url option in connections.yml).

For a database behind a bastion, fill the SSH row with user@host[:port] (or a ~/.ssh/config alias) and optionally an SSH key path — see SSH tunnel below.

⚙️ Configuration

All under ~/.config/lazysql/:

| File | Contents | |------|------| | connections.yml | Connection config, no passwords, editable by hand | | secrets.json | Passwords (chmod 600) | | config.yml | App settings (incl. the NL→SQL llm: block) | | history.json | Per-connection SQL history (capped at 100 entries each) |

Passwords default to secrets.json; on macOS, set LAZYSQL_SECRETS=keychain to use the system keychain instead (zero native dependencies).

SSH tunnel

Reach a database behind a bastion by adding an ssh: block to a connection (or filling the SSH rows in the in-TUI form). lazysql runs the system ssh for a local port forward, so your ~/.ssh/config, keys and agent all apply — host can simply be a config alias:

connections:
  - id: prod
    name: prod
    driver: postgres
    options:
      host: db.internal        # as seen FROM the bastion
      port: 5432
      user: app
      database: app
    ssh:
      host: bastion.example.com   # or a ~/.ssh/config Host alias
      user: ubuntu                # optional
      port: 22                    # optional
      keyFile: ~/.ssh/id_ed25519  # optional (-i); agent/config keys work without it

Applies to PostgreSQL / MySQL / MongoDB / Redis, with discrete host/port options only — a url/connectionString option can't be tunneled (its embedded host can't be rewritten). Auth is key/agent only: the TUI owns the terminal, so an interactive SSH password prompt can't be answered (BatchMode is forced — a connection that would prompt fails fast with ssh's error instead of hanging).

A tunneled MongoDB connection is pinned with directConnection=true — replica-set member addresses only resolve from the far side of the tunnel. If the SSH link dies, keepalives make ssh give up within ~90s rather than hold a dead forward open; press r to reconnect.

NL→SQL (LLM)

The provider is isolated behind the SqlGenerator port; without an API key, ^G is silently disabled. Pin it in config.yml, or override temporarily with environment variables (env wins). The API key is read only from the environment, never written to config.yml.

config.yml:

llm:
  provider: alibaba        # anthropic | alibaba | openai | deepseek | moonshot
  model: qwen3.7-plus      # optional, overrides the default model
  # baseUrl: https://...   # optional, overrides the default base URL (e.g. an overseas endpoint)

| Provider | id | API key env var | Default model | |----------|----|------------------|----------| | Alibaba Cloud (Qwen) | alibaba | DASHSCOPE_API_KEY | qwen3.7-plus | | OpenAI | openai | OPENAI_API_KEY | gpt-4o | | DeepSeek | deepseek | DEEPSEEK_API_KEY | deepseek-chat | | Moonshot AI (Kimi) | moonshot | MOONSHOT_API_KEY | kimi-k2.6 | | Anthropic (Claude) | anthropic | ANTHROPIC_API_KEY | claude-opus-4-8 |

Moonshot defaults to the mainland-China host (api.moonshot.cn); from outside China set baseUrl/LAZYSQL_LLM_BASE_URL to https://api.moonshot.ai/v1.

When no provider is set explicitly, it's auto-detected by "which API key exists" (Qwen first, Anthropic last). Each run can also override with LAZYSQL_LLM_PROVIDER / LAZYSQL_LLM_MODEL / LAZYSQL_LLM_BASE_URL.

export DASHSCOPE_API_KEY=sk-xxx && lazysql              # Qwen by default
ANTHROPIC_API_KEY=sk-ant-xxx LAZYSQL_LLM_PROVIDER=anthropic lazysql  # temporarily switch to Claude

🏗️ Architecture

Tech stack: TypeScript (strict) · Bun · OpenTUI (@opentui/react + React 19) · Zustand · yaml. LLM via @anthropic-ai/sdk + any OpenAI-compatible backend (Qwen / OpenAI / DeepSeek / Kimi).

Clean / Hexagonal layering, built around a capability-segmented DataSource port — the UI asks a data source "which capabilities do you support" (Queryable / Browsable / RowEditable / Transactional / …) rather than "what type are you", so adding a database = adding one adapter, with zero changes to the core. Every layer boundary is crossed via a Result<T,E> + port handshake; inner layers never depend on outer ones.

src/
  domain/         pure business rules / entities / value objects (no IO)
  application/    ports (outbound interfaces) + usecases (use-case orchestration)
  adapters/       datasource · llm · persistence · clipboard (real IO)
  presentation/   app · components · keymap · tree (TUI, unidirectional data flow + Zustand)
  shared/         cross-layer pure utilities (e.g. Result)

The source of truth for the design is docs/ARCHITECTURE.md; key decisions are recorded in docs/adr/ (capability model, TUI framework, NL→SQL provider strategy, key dispatch, etc.).

🛠️ Development

Requires Bun. SQLite uses Bun's built-in bun:sqlite — no native dependency.

bun install        # install dependencies
bun run seed       # generate the sample DB data/sample.db
bun start          # run from source (the launch-arg forms from Usage work here too, e.g. bun start <name>)

Tests

bun run typecheck   # strict type check
bun test            # unit + five-engine adapter contracts + persistence + headless TUI integration

The objective judge of "done" is bun run typecheck && bun test all green. Every data-source adapter passes the same contract test suite (the executable acceptance of LSP); contract tests need a reachable real database and skip automatically when unreachable, so a machine without Docker won't fail. To run the full contracts, start the matching containers:

# PostgreSQL
docker run -d --name lazysql-pg -e POSTGRES_PASSWORD=lazysql \
  -e POSTGRES_USER=lazysql -e POSTGRES_DB=lazysql -p 5432:5432 postgres:16-alpine
# MySQL / MariaDB
docker run -d --name lazysql-mysql -e MARIADB_ROOT_PASSWORD=lazysql -p 3306:3306 mariadb:11
# MongoDB
docker run -d --name lazysql-mongo -p 27017:27017 mongo:7
# Redis
docker run -d --name lazysql-redis -p 6379:6379 redis:7-alpine

Contributors: please read CLAUDE.md (working rules: layering, naming, commit conventions) and docs/ARCHITECTURE.md first. Commits follow Conventional Commits (enforced by .githooks + commitlint); on bun install, the prepare script points core.hooksPath at .githooks.

📄 License

MIT © yangwb