[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-82283":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":13,"forks30d":13,"starsTrendScore":18,"compositeScore":13,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":39,"readmeContent":40,"aiSummary":41,"trendingCount":13,"starSnapshotCount":13,"syncStatus":42,"lastSyncTime":43,"discoverSource":44},82283,"narwhal","Nonanti\u002Fnarwhal","Nonanti","Multi-driver TUI database client with a built-in MCP server. Six databases (postgres, mysql, sqlite, duckdb, clickhouse, mssql), vim editing, Lua + WASM plugins, schema diff, audit log.","https:\u002F\u002Fcrates.io\u002Fcrates\u002Fnarwhaldb",null,"Rust",38,0,1,4,9,11,12,"Other",false,"main",true,[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"cli","clickhouse","database","duckdb","mcp","model-context-protocol","mysql","postgres","ratatui","rust","sql","sqlite","terminal","tui","vim","2026-06-12 02:04:24","# narwhal\n\n[![CI](https:\u002F\u002Fgithub.com\u002FNonanti\u002Fnarwhal\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002FNonanti\u002Fnarwhal\u002Factions\u002Fworkflows\u002Fci.yml)\n[![License: MIT OR Apache-2.0](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT%20OR%20Apache--2.0-blue.svg)](#licence)\n[![Version](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fversion-1.1.0-brightgreen)](.\u002FCHANGELOG.md)\n[![Rust 1.75+](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Frust-1.75%2B-orange.svg)](.\u002Frust-toolchain.toml)\n\n> Multi-driver TUI database client with a built-in MCP server.\n> Postgres \u002F MySQL \u002F SQLite \u002F DuckDB \u002F ClickHouse, vim editing, Lua plugins.\n\n![narwhal demo](.\u002Fdocs\u002Fimg\u002Fdemo.gif)\n\n## Why narwhal\n\n- **Built-in MCP server.** Run `narwhal mcp` and any\n  [Model Context Protocol](https:\u002F\u002Fmodelcontextprotocol.io\u002F) client (Claude\n  Desktop, Cursor, your own agent) gets `list_connections`,\n  `describe_schema`, `describe_table`, `run_query`, `explain_query` over\n  stdio. Read-only by default, with a three-layer SQL guard and a\n  workspace ACL (`.narwhal\u002Fworkspace.toml`) so an agent can only see\n  the connections you explicitly listed.\n- **One TUI, five databases.** Postgres, MySQL, SQLite, DuckDB,\n  ClickHouse. No driver-juggling, no context-switching between `psql`,\n  `mysql`, and DataGrip.\n- **Vim editing + auto-pair + completion.** Modal input (Normal,\n  Insert, Visual), schema-aware tab-completion, alias-resolved column\n  hints, a proper `:` command palette.\n- **Lua plugin runtime.** The bits that should be yours, stay yours.\n  Write a `.lua` file, drop it in `~\u002F.config\u002Fnarwhal\u002Fplugins\u002F`, and it\n  is live.\n- **SSH tunnels, `~\u002F.pgpass`, OS keyring.** The auth ergonomics you\n  already configured for `psql` work here too. Set\n  `ssh_host=jump.example.com` and the connect path forwards a loopback\n  port for you.\n\n## Install\n\n### Cargo (any platform with Rust)\n\n```sh\ncargo install narwhaldb\n```\n\n> The crates.io name is `narwhaldb` (the bare `narwhal` slot is held\n> by an unrelated 2018 docker library); the installed binary is still\n> just `narwhal`.\n\nFor users without a Rust toolchain, `cargo-binstall` fetches the\nprebuilt binary instead of compiling:\n\n```sh\ncargo binstall narwhaldb\n```\n\n### Homebrew (macOS, Linuxbrew)\n\n```sh\nbrew tap Nonanti\u002Ftap\nbrew install narwhal\n```\n\n### Arch Linux (AUR)\n\n```sh\nyay -S narwhal      # or: paru -S narwhal\n```\n\n### Nix\n\n```sh\nnix run github:Nonanti\u002Fnarwhal\n```\n\nOr add the flake to your inputs and reference the default package.\n\n### Pre-built binaries\n\nDownload native tarballs (with SHA-256 checksums) from the\n[latest GitHub Release](https:\u002F\u002Fgithub.com\u002FNonanti\u002Fnarwhal\u002Freleases):\n\n- `x86_64-unknown-linux-gnu`\n- `aarch64-apple-darwin` (Apple Silicon)\n\nIntel Mac users: build from source with `cargo install narwhaldb` for\nnow; a prebuilt `x86_64-apple-darwin` tarball is on the roadmap once\nthe macos-13 runner backlog clears.\n\n```sh\ncurl -LO https:\u002F\u002Fgithub.com\u002FNonanti\u002Fnarwhal\u002Freleases\u002Flatest\u002Fdownload\u002Fnarwhal-1.1.0-x86_64-unknown-linux-gnu.tar.gz\ntar -xzf narwhal-1.1.0-*.tar.gz\nmv narwhal-1.1.0-*\u002Fnarwhal ~\u002F.local\u002Fbin\u002F\n```\n\n### Build from source\n\n```sh\ngit clone https:\u002F\u002Fgithub.com\u002FNonanti\u002Fnarwhal.git\ncd narwhal\ncargo build --release\n# binary at target\u002Frelease\u002Fnarwhal\n```\n\n## Quick start\n\n1. **Run `narwhal`.** The TUI opens with an empty editor and a sidebar.\n2. **Hit `:add`.** The connection wizard appears. Pick a driver, fill in host + database (or use `:url postgres:\u002F\u002Fuser:pass@host\u002Fdb` to skip the form).\n3. **`:open \u003Cname>`.** The saved entry connects; the sidebar fills with schemas and tables.\n4. **F6 to run.** The whole buffer executes; results appear in the lower pane. Press **F1** any time for the full keymap reference.\n\n### `connections.toml` schema\n\nNamed connections live in `~\u002F.config\u002Fnarwhal\u002Fconnections.toml`.  One\n`[[connection]]` block per database; `[connection.params]` carries the\ndriver-specific options.  The field names match the\n[`ConnectionParams`](.\u002Fcrates\u002Fnarwhal-core\u002Fsrc\u002Fconnection.rs) struct —\nin particular `username` (not `user`) is the canonical name.\n\n```toml\n# Local SQLite — the file path is the only required param.\n[[connection]]\nid     = \"00000000-0000-0000-0000-000000000001\"\nname   = \"smoke\"\ndriver = \"sqlite\"\n\n[connection.params]\npath = \"\u002Ftmp\u002Fnarwhal-smoke.db\"\n\n# Postgres on a non-default port, no TLS — typical local docker setup.\n[[connection]]\nid     = \"00000000-0000-0000-0000-000000000002\"\nname   = \"demo-pg\"\ndriver = \"postgres\"\n\n[connection.params]\nhost     = \"127.0.0.1\"\nport     = 5433\nusername = \"postgres\"        # NOTE: `username`, not `user`\npassword = \"narwhal\"\ndatabase = \"demo\"\nssl_mode = \"disable\"         # disable | prefer (default) | require | verify-ca | verify-full\n```\n\nFile-local drivers (`sqlite`, `duckdb`) tolerate the default `prefer`\nso pre-TLS configs still load; the wire layer ignores it.  Network\ndrivers (`postgres`, `mysql`, `clickhouse`) accept any of the five\n`ssl_mode` values plus optional `ssl_root_cert`, `ssl_cert`, `ssl_key`\npaths for mutual TLS.\n\n### Connection safety: color, read-only, write confirmation (v1.1)\n\nThree optional fields on `[[connection]]` keep you out of trouble on\nproduction databases:\n\n```toml\n[[connection]]\nname           = \"prod-pg\"\ndriver         = \"postgres\"\ncolor          = \"red\"           # red | yellow | green | blue | magenta | cyan\nconfirm_writes = true            # type YES before any mutating statement\nread_only      = true            # syntactic guard rejects non-reads\n\n[connection.params]\n# …\n```\n\n- **`color`** — the active connection's name is tinted in the status\n  bar with the chosen accent. Set `color = \"red\"` on every production\n  connection and \"am I on prod?\" becomes a glance, not a guess.\n- **`confirm_writes`** — before an `INSERT`\u002F`UPDATE`\u002F`DELETE`\u002FDDL\n  statement reaches the driver, narwhal opens a modal that previews\n  the first statement and demands the user type `YES` exactly. Esc\n  cancels. Bare reads run without prompting.\n- **`read_only`** — every batch is checked against the same syntactic\n  allow-list MCP's `run_query` tool uses (`SELECT`, `WITH`, `SHOW`,\n  `EXPLAIN`, `DESCRIBE`, `DESC`, `PRAGMA`, `VALUES`, `TABLE`). Writes\n  are rejected before they reach the network.\n\n### `config.toml` (settings)\n\nA `~\u002F.config\u002Fnarwhal\u002Fconfig.toml` lets you override the renderer\ntheme and a few editor toggles. Missing fields fall back to their\ndefaults so a one-line file is enough.\n\n```toml\ntheme = \"dark\"           # \"dark\" (default) | \"light\" | \"high-contrast\"\n\n[editor]\ntab_width    = 4         # reserved, v1.1 will honour this\nuse_spaces   = true      # reserved, v1.1 will honour this\nline_numbers = true      # reserved, v1.1 will honour this\n\n[keybindings]\nvim_mode = true          # reserved, v1.1 will allow opt-out\n```\n\nv1.0 wires only the `theme` field; the rest are persisted and\nload-validated so the file stays stable across upgrades. The renderer\nwarns at start-up if the file is malformed instead of silently\nfalling back to defaults.\n\n### SSH tunnels\n\nAny network connection can prepend an SSH local-port-forward by\nadding `ssh` fields to the params block. The forward is opened\nimplicitly on `:open` and torn down when the session closes.\n\n```toml\n[connection.params]\nhost     = \"db.internal\"     # resolved on the bastion side\nport     = 5432\nusername = \"alice\"\ndatabase = \"prod\"\n\n[connection.params.ssh]\nhost      = \"bastion.example.com\"\nuser      = \"alice\"\nport      = 22               # optional — defaults to 22\n# key_path  = \"~\u002F.ssh\u002Fid_ed25519\"  # optional — defaults to ssh-agent \u002F ~\u002F.ssh\u002Fconfig\n# jump_host = \"jump.example.com\"   # optional — maps to `ssh -J`\n```\n\nThe spawned `ssh` subprocess inherits `~\u002F.ssh\u002Fconfig`, the agent,\n`Match` blocks, `IdentityAgent`, and FIDO2 keys for free — narwhal\ndeliberately shells out to OpenSSH rather than embedding its own\nclient. URL form: `?ssh_host=bastion&ssh_user=alice` on a\n`:url postgres:\u002F\u002F...` invocation.\n\n### TLS defaults changed (v0.2)\n\n**Breaking change:** `ssl_mode = prefer` and `ssl_mode = require` now\nperform full CA chain verification instead of accepting any server\ncertificate. Self-signed certificates will be rejected unless the CA\nis explicitly trusted via `ssl_root_cert`.\n\nIf you were relying on the previous insecure behaviour:\n\n- **Self-signed servers:** add `ssl_root_cert = \"\u002Fpath\u002Fto\u002Fca.pem\"` to\n  the connection params, or set `ssl_mode = \"disable\"` if TLS is not\n  needed.\n- **Hostname mismatch:** use `ssl_mode = \"require\"` or\n  `ssl_mode = \"verify-ca\"` (chain verified, hostname skipped).\n- **Full verification:** `ssl_mode = \"verify-full\"` (unchanged).\n\nQuery-string TLS params (`?sslmode=...`, `?sslrootcert=...`, etc.)\nare now parsed into dedicated struct fields instead of being left in\nthe generic `options` map.\n\n## MCP server: talk to your databases through an AI agent\n\nnarwhal ships a built-in [Model Context Protocol](https:\u002F\u002Fmodelcontextprotocol.io)\nserver so any MCP-capable AI assistant (Claude Desktop, Cursor, Continue,\nAider, ...) can browse the connections you already configured and inspect\ntheir schema.\n\n```sh\nnarwhal mcp   # runs the JSON-RPC stdio server\n```\n\nWire it into Claude Desktop:\n\n```jsonc\n\u002F\u002F ~\u002F.config\u002FClaude\u002Fclaude_desktop_config.json\n{\n  \"mcpServers\": {\n    \"narwhal\": {\n      \"command\": \"narwhal\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n\nThe v0 tool surface:\n\n| Tool | What it does |\n|------|--------------|\n| `list_connections` | List configured connections — driver, target, SSH flag. No IO, no credentials loaded. Honours the workspace ACL. |\n| `describe_schema`  | Schema \u002F table \u002F view tree for one connection. |\n| `describe_table`   | Full structure of one table — columns, indexes, foreign keys, unique constraints, engine-native DDL. |\n| `run_query`        | Execute a single statement. **Read-only by default** — syntactic guard + `BEGIN\u002FROLLBACK` sandwich + row limit (default 1 000). `read_only=false` opts out, subject to the workspace ACL. |\n| `explain_query`    | Driver-native EXPLAIN with the right dialect prefix. Optional `analyze=true` runs the statement for real cardinalities (PG \u002F MySQL \u002F DuckDB). |\n\nEvery database-touching call is audit-logged to\n`~\u002F.local\u002Fshare\u002Fnarwhal\u002Fhistory.jsonl` with `source: \"mcp\"` so you can\n`jq 'select(.source == \"mcp\")'` to isolate agent traffic.\n\n### Workspace scoping: `.narwhal\u002Fworkspace.toml`\n\nA repo-local file (discovered by walking up from `pwd`, same idiom as\n`.git`) declares what the MCP server may expose when narwhal runs from\ninside that directory tree. Commit it next to your code so an agent\nlaunched against your project can only reach the databases you list.\n\n```toml\n# .narwhal\u002Fworkspace.toml\n\n# Connection names from connections.toml that the agent may target.\n# Empty \u002F omitted = all of them.\nallowed_connections = [\"staging\", \"test\"]\n\n# When false, run_query rejects read_only=false. Default true.\nallow_writes = false\n```\n\nDisallowed connections appear to the agent exactly as a misspelled\nname would: the `list_connections` result hides them, `describe_*` \u002F\n`run_query` calls answer with the same \"unknown connection\" tool-level\nerror, and the agent retries against the visible set automatically.\n\n## Keymap\n\n### Global\n\n| Keys | Action |\n|------|--------|\n| F5 \u002F Alt-Enter \u002F Ctrl-; | Run statement under cursor |\n| F6 | Run whole buffer |\n| F7 | Stream cursor statement |\n| F4 \u002F Ctrl-C | Cancel running query |\n| Ctrl-W | Cycle pane focus |\n| Ctrl-T | New editor tab |\n| Ctrl-N \u002F :goto \u002F :g | Fuzzy navigator — jump to any table\u002Fview |\n| :diff `\u003Ca>` `\u003Cb>` | Schema diff: emit ALTER TABLE migration SQL |\n| :lint | Run linter (SELECT *, UPDATE\u002FDELETE no WHERE, cartesian …) |\n| :tpl `\u003Cname>` | Insert a built-in template: sel \u002F ins \u002F upd \u002F del \u002F join \u002F with |\n| :history `[pattern]` | Open Ctrl-R modal (optionally pre-filtered) |\n| :submit \u002F :revert | Flush \u002F drop the pending-mutation queue |\n| :filter `[expr\\|clear]` | Set \u002F clear the result filter |\n| :sort `\u003CN\\|clear>` | Toggle the result sort on column N |\n| Ctrl-Tab \u002F Ctrl-Shift-Tab | Cycle tabs |\n| ? \u002F F1 | Help |\n| :q | Quit |\n| :refresh | Re-fetch schema tree for active connection |\n\n### Editor\n\n| Keys | Action |\n|------|--------|\n| i \u002F a | Enter insert mode |\n| Esc | Back to normal mode |\n| Tab \u002F Ctrl-Space | Completion |\n| ↑ ↓ \u002F Shift-Tab | Cycle popup items |\n| Enter \u002F Tab (in popup) | Accept completion |\n| h j k l \u002F arrows | Move cursor |\n| w \u002F b | Word forward \u002F backward |\n| 0 \u002F $ | Line start \u002F end |\n| v \u002F V | Visual \u002F visual-line mode |\n\n### Sidebar\n\n| Keys | Action |\n|------|--------|\n| j \u002F k \u002F ↑ \u002F ↓ | Navigate |\n| Enter | Describe table |\n| o | Preview table data |\n| d | Inject DDL into editor |\n\n### Results\n\n| Keys | Action |\n|------|--------|\n| h j k l \u002F arrows | Move selection |\n| Enter | Open cell popup |\n| e | Edit cell value |\n| y \u002F Y | Yank cell \u002F row to clipboard |\n| \u002F | Filter rows |\n| n \u002F N | Next \u002F prev search match |\n| g \u002F G | Jump to first \u002F last row |\n| :next \u002F :prev | Page through results |\n\n### Snippets\n\n| Keys | Action |\n|------|--------|\n| :save \\\u003Cname\\> | Save editor buffer as a named snippet |\n| :load \\\u003Cname\\> | Load a snippet into a new tab |\n| :rm-snippet \\\u003Cname\\> | Delete a saved snippet |\n| :snippets | Browse saved snippets |\n\n## Plugins\n\nPlugins are Lua scripts that auto-load from `~\u002F.config\u002Fnarwhal\u002Fplugins\u002F*.lua`\n(or the platform equivalent under `$XDG_CONFIG_HOME`). They get a `narwhal`\nglobal with these entry points:\n\n```lua\nnarwhal.register_command(name, description, handler)\n    -- handler(arg : string)\n    --   return \"...\"                 -> status bar message\n    --   return { sql = \"...\" }       -> append to editor buffer\n    --   return { sql = \"...\", append = false }\n    --                                -> replace editor buffer\n    --   return nil | false           -> silent\n\nnarwhal.register_transform(handler)\n    -- handler(result : table)\n    --   mutate in place; return value ignored\n\nnarwhal.sql_run(sql : string) -> result\n    -- Run SQL on the active connection synchronously\n\nnarwhal.editor_text          : string (read-only)\n    -- Current editor buffer content during command dispatch\n```\n\n### Sample plugins\n\nSix working samples live in [`examples\u002Fplugins\u002F`](.\u002Fexamples\u002Fplugins\u002F):\n\n| File | What it does |\n|------|-------------|\n| `uppercase.lua` | Result transform that uppercases every TEXT cell |\n| `format_json.lua` | Pretty-prints cells that parse as JSON |\n| `row_count.lua` | `:rc \u003Ctable>` — count rows via `narwhal.sql_run` |\n| `query_snippet.lua` | `:top \u003Ctable>` — inject `SELECT * FROM … LIMIT 10` |\n| `csv_export.lua` | `:csv-export \u003Ctable> \u003Cpath>` — dump to CSV |\n| `explain_cost.lua` | `:explain-cost` \u002F `:explain-sqlite` — wrap buffer in EXPLAIN |\n\nLoad on demand: `:plug-load \u002Fpath\u002Fto\u002Ffile.lua`. List everything: `:plug-list`.\n\nFor the full API reference, see [`narwhal-plugin-lua` docs](.\u002Fcrates\u002Fnarwhal-plugin-lua\u002Fsrc\u002Flib.rs)\nand the [plugin examples README](.\u002Fexamples\u002Fplugins\u002FREADME.md).\n\n### Security model\n\nPlugins are **trusted code that runs with your privileges**. They can run\narbitrary SQL, inject into the editor, and read every result row. Only\ninstall scripts from sources you'd trust as a shell script. There is no\nsandbox — by design, so auditing a plugin is just reading a short `.lua`\nfile.\n\nBuilt-in command names (`run`, `open`, `begin`, `quit`, …) are reserved;\na plugin that tries to shadow one is rejected at load time. During a\n`:begin` transaction, `narwhal.sql_run` is refused entirely.\n\n## Headless `exec` mode: one-shot SQL from the shell\n\nThe same binary doubles as a `psql -c` \u002F `mysql -e` muadili. Use it in\nCI smoke checks, `cron` jobs, or shell pipelines:\n\n```sh\nnarwhal exec --conn prod 'SELECT count(*) FROM users'\n\n# Choose a format for the consumer (default: table)\nnarwhal exec -c prod -f json 'SELECT id, email FROM users' | jq '.[].email'\nnarwhal exec -c prod -f csv  'SELECT * FROM orders' > orders.csv\nnarwhal exec -c prod -f tsv  'SELECT id, name FROM users' | column -t\n\n# Limit rows to keep large queries snappy\nnarwhal exec -c prod -l 100 'SELECT * FROM events'\n\n# Writes are sandboxed by default (BEGIN ... ROLLBACK). `--write` opts out:\nnarwhal exec -c prod --write \"UPDATE users SET banned = true WHERE id = 42\"\n```\n\nFormats: `table` (default, ASCII grid), `csv` (RFC 4180), `json`\n(array-of-objects), `tsv` (pipe-friendly, no quoting). Connection\nresolution is the same as the TUI — keyring first, `~\u002F.pgpass`\u002Fenv\nfallback. Every call is audit-logged to `history.jsonl` with\n`source: \"exec\"` so it can be grepped alongside MCP traffic.\n\n## Transactions\n\n| Command | Action |\n|---------|--------|\n| `:begin [iso]` | Open a pinned connection; `iso` accepts `ru`\u002F`rc`\u002F`rr`\u002F`s` short forms |\n| `:commit` | Commit and close the pinned connection |\n| `:rollback` | Rollback and close the pinned connection |\n| `:savepoint NAME` | Create a named savepoint (drivers that support them) |\n| `:release NAME` | Release a savepoint |\n| `:rollback-to NAME` | Rollback to a savepoint |\n\nA **TX** badge on the status bar reminds you that you're inside a\ntransaction.\n\n## Architecture\n\n```\n              narwhal (bin)\n                   │  entry point + CLI\n                   ▼\n             narwhal-app                       narwhal-mcp\n              orchestrator                       MCP server\n           │     │      │                          │\n           ▼     ▼      ▼                          ▼\n   narwhal-tui  narwhal-commands           narwhal-driver-registry\n     render     completion \u002F export \u002F             (feature-gated\n                wizard \u002F dispatch \u002F                 driver lookup)\n                snippets \u002F DDL \u002F …                       │\n           │         │                                    ▼\n           └─────────┼────────────────┐         narwhal-driver-*\n                    ▼                 │          postgres · sqlite\n              narwhal-domain          │          mysql · duckdb\n              pure model state        │          clickhouse\n           (editor buffer, schema     │\n            listings, no IO,          │\n            no rendering)             │\n                   │                  ▼\n                   └────────►   narwhal-pool · narwhal-sql ·\n                                narwhal-history · narwhal-config ·\n                                narwhal-vim\n                                       │\n                                       ▼\n                                  narwhal-core\n                            driver trait, value model,\n                            schema types, error type\n```\n\nDependency direction: every arrow points downward. View (`narwhal-tui`)\nand commands (`narwhal-commands`) read domain state by reference; only\n`narwhal-app` is allowed to mutate it in response to user input.\nConcrete drivers are reached exclusively through\n`narwhal-driver-registry`, so cargo features are the only place where\ndatabase engines come and go.\n\nFor the full layer map see [`docs\u002FARCHITECTURE.md`](docs\u002FARCHITECTURE.md);\nfor the code-style contract see [`docs\u002FSTYLE.md`](docs\u002FSTYLE.md).\n\n## Safety\n\n- Every destructive operation goes through the `:` command line, never a hotkey.\n- Statements are journaled to `~\u002F.local\u002Fshare\u002Fnarwhal\u002Fhistory.jsonl` before execution.\n- Passwords prefer the OS keyring; an in-memory fallback is used only when the keyring isn't available. `:forget \u003Cname>` wipes the cached entry.\n\n## Building\n\n### Nix\n\n```sh\nnix develop\ncargo build --release\n```\n\nThe dev shell pulls in `cmake`, `clang`, and `libcxx` for the bundled\nDuckDB C++ build, and pre-sets `LIBCLANG_PATH` for bindgen.\n\n### Other systems\n\nAny toolchain at or above the version pinned in `rust-toolchain.toml`,\nplus the usual native build deps for DuckDB (cmake, a C++17 compiler).\n\n```sh\ncargo build --release\n```\n\n### Benchmarks\n\nCriterion harnesses live under `crates\u002F*\u002Fbenches\u002F`. Run them all with\n`cargo bench --workspace` or one at a time:\n\n```sh\ncargo bench -p narwhal-sql --bench splitter\ncargo bench -p narwhal-tui --bench sort\ncargo bench -p narwhal-tui --bench editor_motion\ncargo bench -p narwhal-history --bench append\n```\n\nThe pre\u002Fpost-optimisation numbers are recorded in\n[`docs\u002Fperf-after-phase-2.md`](.\u002Fdocs\u002Fperf-after-phase-2.md); current\nheadline numbers on a Linux box are ~900 MiB\u002Fs for the statement\nsplitter, ~38 µs for a 5 000-line `w` motion, and ~1.15 ms to sort\n2 000 JSON cells.\n\n## Contributing\n\nA few ground rules so PRs land smoothly:\n\n- `cargo fmt --all`, `cargo clippy --workspace --all-targets -- -D warnings`,\n  `cargo test --workspace`, and `RUSTDOCFLAGS=\"-D warnings\" cargo doc\n  --workspace --no-deps` all pass in CI; please run them locally too.\n- New behaviour ships with a regression test under the relevant\n  crate's `tests\u002F` directory.\n- Commit messages follow Conventional Commits\n  (`feat:`, `fix:`, `refactor:`, `docs:`, `chore:`).\n\n## Licence\n\nDual-licensed under the [MIT](.\u002FLICENSE-MIT) and\n[Apache 2.0](.\u002FLICENSE-APACHE) licences. Contributions are accepted under\nthe same terms.\n\n---\n\nSee [`docs\u002FRELEASING.md`](.\u002Fdocs\u002FRELEASING.md) for the release\nchecklist and [`CHANGELOG.md`](.\u002FCHANGELOG.md) for the version history.\n","narwhal 是一个支持多种数据库的终端用户界面（TUI）客户端，内置了MCP服务器。它支持五种主流数据库（Postgres、MySQL、SQLite、DuckDB 和 ClickHouse），并提供了Vim编辑模式和Lua插件扩展功能。其核心特点包括通过单一界面管理多个数据库，减少在不同工具间切换的需求；集成的MCP服务器允许与任何遵循Model Context Protocol的客户端进行交互，提供数据库连接、查询执行等操作；同时，它还支持SSH隧道、`.pgpass`文件及操作系统密钥环认证方式，确保了良好的安全性和便捷性。适用于需要高效管理和操作多类型数据库的开发者或运维人员使用场景。",2,"2026-06-11 04:08:16","CREATED_QUERY"]