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

@mako10k/lsp-cli

v0.1.3

Published

Lightweight CLI for driving arbitrary LSP servers (MVP: rust-analyzer)

Readme

lsp-cli

A lightweight CLI client to drive arbitrary LSP servers for structural analysis and refactoring (MVP: rust-analyzer).

Table of contents

Quickstart

# Smoke test (rust-analyzer must be in PATH)
# If you use the rustup proxy, run this first:
#   rustup component add rust-analyzer
npx @mako10k/lsp-cli --root . ping

# documentSymbol (line/col are 0-based)
npx @mako10k/lsp-cli --root . --format pretty symbols path/to/file.rs

Development (from source)

npm install
npm run build

# Install the repo locally as a CLI (provides the lsp-cli command)
npm link
lsp-cli --help

Sample (for testing)

A small Rust sample project is included:

  • samples/rust-basic

Examples:

# initialize smoke test
npx @mako10k/lsp-cli --root samples/rust-basic ping

# documentSymbol
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty symbols samples/rust-basic/src/math.rs

# references: the call site of add inside main.rs (0-based)
# Example: the "a" position of "add" on line 9 ("    let x = add(1, 2);")
npx @mako10k/lsp-cli --root samples/rust-basic --format json references samples/rust-basic/src/main.rs 8 12

# definition (right after rust-analyzer initialization, results may be empty; --wait is recommended)
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 definition samples/rust-basic/src/main.rs 8 12

# hover
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 hover samples/rust-basic/src/main.rs 8 12

# signature help (somewhere inside add()
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 signature-help samples/rust-basic/src/main.rs 8 16

# workspace symbols
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 ws-symbols add --limit 20

# implementation
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 implementation samples/rust-basic/src/main.rs 8 12

# type definition
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 type-definition samples/rust-basic/src/main.rs 8 12

Notes

  • Positions (line, col) are 0-based (LSP-compliant).

  • With --server typescript-language-server, it starts TypeScript Language Server via npx (TypeScript is required in the target workspace):

    npx -y typescript-language-server --stdio
  • Applying changes is dry-run by default; files are modified only when --apply is specified.

Command reference

Global options

Global options must appear before the command for reliable parsing.

  • --root <path>: workspace root (default: cwd)
  • --server <name>: server profile name (default: rust-analyzer)
  • --server-cmd <cmd>: override the server command string
  • --config <path>: config file path (default: <root>/.lsp-cli.json or <root>/lsp-cli.config.json)
  • --format <json|pretty>: output format (default: json)
  • --stdin: read command params from stdin as JSON (command-specific)
  • --jq <filter>: pipe JSON output through jq (requires jq in PATH)
  • --wait-ms <n>: wait before some requests (ms; helps rust-analyzer warm-up)
  • --daemon-log <path>: daemon log sink (auto-start); discard|default|<path>

Command index

Core:

  • ping

Read-only navigation:

  • symbols, references, definition, type-definition, implementation, hover, signature-help, ws-symbols

Edits and refactoring (mutating; dry-run by default):

  • rename, code-actions, apply-edits, delete-symbol

Formatting and tokens:

  • format, format-range, completion, document-highlight, inlay-hints, semantic-tokens-full, semantic-tokens-range, semantic-tokens-delta, prepare-rename

Daemon and operations:

  • daemon-status, daemon-stop, daemon-log, events, server-status, server-stop, server-restart, did-change-configuration

Batch / advanced:

  • batch, daemon-request

Experimental (daemon-only; normally use non-suffixed commands):

  • symbols-daemon, references-daemon, definition-daemon, hover-daemon, signature-help-daemon, ws-symbols-daemon

Read-only navigation

Typical arguments are:

  • symbols <file>
  • references <file> <line> <col>
  • definition <file> <line> <col>
  • type-definition <file> <line> <col>
  • implementation <file> <line> <col>
  • hover <file> <line> <col>
  • signature-help <file> <line> <col>
  • ws-symbols <query> [--limit <n>]

Edits and refactoring (mutating)

  • rename <file> <line> <col> <newName>: defaults to dry-run; add --apply to modify files.
  • code-actions <file> ...: defaults to dry-run; add --apply to apply selected action.
  • apply-edits: reads WorkspaceEdit JSON from stdin; add --apply to modify files.
  • delete-symbol <file> <symbolName>: dry-run by default; add --apply to modify files.

Formatting and tokens

  • format <file> [--apply]
  • format-range <file> <startLine> <startCol> <endLine> <endCol> [--apply]
  • completion <file> <line> <col>
  • document-highlight <file> <line> <col>
  • inlay-hints <file> <startLine> <startCol> <endLine> <endCol>
  • semantic-tokens-full <file>
  • semantic-tokens-range <file> <startLine> <startCol> <endLine> <endCol>
  • semantic-tokens-delta <file>
  • prepare-rename <file> <line> <col>
  • did-change-configuration --settings '<json>' (or --stdin)

Daemon and operations

  • daemon-status: daemon metadata (pid/startedAt/socketPath)
  • server-status: whether in-daemon LSP is running
  • server-stop / server-restart: stop/restart only the in-daemon LSP session
  • daemon-stop: stop the daemon process itself
  • events --kind diagnostics --since <cursor> --limit <n>: pull-based notifications
  • daemon-log [discard|default|<path>]: get/set daemon log sink

Batch mode

batch reads JSON Lines (one line = one request) from stdin and executes them sequentially within the same LSP session.

Advanced / debug

  • daemon-request --method <name> [--params <json>]: send an arbitrary LSP request via the daemon.

Daemon mode (persistent)

To reduce repeated execution costs (initialize, etc.) for the same --root, the CLI tries to connect to a daemon by default. If it cannot connect, it implicitly starts the daemon and retries; if it still fails, it falls back to the legacy behavior: start LSP as a one-shot process.

There is no explicit daemon start command (auto-start only).

Use case samples

1) Pull diagnostics (after edits)

# run something that triggers diagnostics (e.g. open/format/rename)
npx @mako10k/lsp-cli --root samples/rust-basic symbols samples/rust-basic/src/math.rs > /dev/null

# then pull diagnostics
npx @mako10k/lsp-cli --root samples/rust-basic events --kind diagnostics --since 0

2) "Navigate" (definition → references)

npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 definition samples/rust-basic/src/main.rs 8 12
npx @mako10k/lsp-cli --root samples/rust-basic --format pretty --wait-ms 500 references samples/rust-basic/src/main.rs 8 12

3) Safe refactor (dry-run first)

# preview rename
npx @mako10k/lsp-cli --root samples/rust-basic rename samples/rust-basic/src/main.rs 8 12 new_name

# apply rename
npx @mako10k/lsp-cli --root samples/rust-basic rename --apply samples/rust-basic/src/main.rs 8 12 new_name

Architecture / config / details

Daemon-first + fallback

By default, many commands try: daemon → auto-start daemon → fallback to direct stdio.

Events (pull-based notifications)

The daemon stores notifications like textDocument/publishDiagnostics and exposes them via events.

Dry-run vs --apply

Commands that can modify files are dry-run by default. To actually write files, pass --apply.

Daemon events (pull-based)

The daemon accumulates notifications such as textDocument/publishDiagnostics, and you can fetch them via events.

# Fetch diagnostics (raw JSON)
npx @mako10k/lsp-cli --root samples/rust-basic events --kind diagnostics

# Fetch deltas using a cursor (pass the previous cursor via --since)
npx @mako10k/lsp-cli --root samples/rust-basic events --kind diagnostics --since 0

Daemon server control (stop/restart LSP only)

You can stop or restart only the LSP session inside the daemon, without killing the daemon process itself.

# Check whether the LSP inside the daemon is running
npx @mako10k/lsp-cli --root samples/rust-basic server-status

# Stop only the LSP (daemon stays alive)
npx @mako10k/lsp-cli --root samples/rust-basic server-stop

# Restart LSP (re-run initialize)
npx @mako10k/lsp-cli --root samples/rust-basic server-restart

Daemon stop (stop the daemon process)

npx @mako10k/lsp-cli --root samples/rust-basic daemon-stop

apply-edits (apply/dry-run WorkspaceEdit)

You can supply a WorkspaceEdit from stdin and preview/apply it (reuses the existing WorkspaceEdit application logic).

# dry-run (preview)
cat workspaceEdit.json | npx @mako10k/lsp-cli apply-edits

# apply (modifies files)
cat workspaceEdit.json | npx @mako10k/lsp-cli apply-edits --apply

Batch mode (JSONL)

Reads JSON Lines from stdin (one line = one request) and executes them sequentially within the same LSP session.

cat <<'JSONL' | npx @mako10k/lsp-cli --root . --server typescript-language-server --format json batch
{"cmd":"references","file":"src/index.ts","line":0,"col":0}
{"cmd":"definition","file":"src/index.ts","line":0,"col":0}
JSONL

To apply edits/refactors, add batch --apply (explicit opt-in for safety):

cat <<'JSONL' | npx @mako10k/lsp-cli --root . --server typescript-language-server --format json batch --apply
{"cmd":"rename","file":"src/index.ts","line":0,"col":0,"newName":"renamed","apply":true}
JSONL

Structured edit (delete symbol)

Deletes a symbol block by name using documentSymbol (dry-run example):

npx @mako10k/lsp-cli --server typescript-language-server --root . --format pretty \
  delete-symbol src/servers/typescriptLanguageServer.ts typescriptLanguageServerProfile
  • With --jq '<filter>', JSON output is piped through jq for formatting/extraction (jq must be in PATH).
  • For <file>, pass - to read the file path from stdin.
  • With --stdin, command input is read as JSON from stdin.

Protocol support

  • See PROTOCOL_SUPPORT.md for a feature-by-feature comparison of LSP capabilities vs what lsp-cli implements.

Changelog

  • See CHANGELOG.md.

Config file (server profiles)

  • By default it searches:
    • <root>/.lsp-cli.json
    • <root>/lsp-cli.config.json
  • You can specify explicitly with --config <path> (relative paths are treated as relative to <root>).

The config supports:

  • presets: reusable server config snippets (referenced by preset).
  • servers: custom server profiles and overrides. command is required for custom servers unless provided via a preset.
  • augment: extra overrides merged into both built-in and custom profiles. When augmenting built-ins, command is optional.
  • Per-server fields include args, initializationOptions, languageIdByExt/defaultLanguageId, cwd, env, waitMs, and warmup.

Example: .lsp-cli.json

{
  "presets": {
    "ra-default": {
      "command": "rust-analyzer",
      "args": [],
      "defaultLanguageId": "rust",
      "languageIdByExt": {
        ".rs": "rust"
      }
    }
  },
  "augment": {
    "rust-analyzer": {
      "initializationOptions": {
        "cargo": {
          "buildScripts": { "enable": true }
        }
      }
    },
    "typescript-language-server": {
      "env": {
        "TSS_LOG": "-level verbose"
      }
    }
  },
  "servers": {
    "my-ra": {
      "preset": "ra-default",
      "cwd": "samples/rust-basic"
    }
  }
}

Usage:

npx @mako10k/lsp-cli --root samples/rust-basic --config .lsp-cli.json ping

Examples (stdin / jq)

# Pass a file path via stdin
printf '%s\n' samples/rust-basic/src/math.rs \
  | npx @mako10k/lsp-cli --root samples/rust-basic --jq 'length' symbols -

# Pass references input via JSON stdin
printf '{"file":"samples/rust-basic/src/main.rs","line":8,"col":12}' \
  | npx @mako10k/lsp-cli --root samples/rust-basic --stdin --jq '.[0]' references