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

vue-ts-lsp

v0.3.2

Published

Unified Vue + TypeScript LSP proxy for Claude Code

Readme

CI npm version MIT License Node.js >= 20.19.0

vue-ts-lsp

A unified LSP proxy that gives Claude Code full TypeScript intelligence across .ts files and .vue files — including <template> expressions.

Why

Claude Code's LSP integration supports go-to-definition, hover, find-references, document symbols, workspace symbols, call hierarchy, and diagnostics. Getting all of these to work across .ts and .vue files requires two language servers:

  • vtsls — TypeScript language server (handles .ts, .js, and .vue <script> blocks via @vue/typescript-plugin)
  • vue-language-server (v3) — handles .vue <template> expressions, providing type-aware diagnostics and navigation

The problem: in Vue Language Tools v3, the LSP client must forward tsserver/request notifications from vue-language-server to vtsls and return responses. Claude Code's plugin system can't do this — it just spawns a stdio process.

This proxy fills the gap by acting as both the LSP server (to Claude Code) and the LSP client (to both child servers), implementing the v3 request forwarding protocol internally.

What you get

| Scenario | Without Proxy | With Proxy | | ----------------------------------------------- | -------------- | ---------- | | Go-to-definition in .ts | ✅ | ✅ | | Go-to-definition into .vue component | ⚠️ file only | ✅ symbol | | Hover on {{ ref.value }} in <template> | ❌ | ✅ | | Diagnostics for wrong prop type in <template> | ❌ | ✅ | | Find references across .ts and .vue | ⚠️ partial | ✅ | | Symbols in .vue file | ⚠️ script only | ✅ |

Installation

1. Install the language server

npm install -g vue-ts-lsp

This installs the vue-ts-lsp binary along with vtsls, @vue/language-server, and typescript as bundled dependencies.

Verify the binary is in your PATH:

which vue-ts-lsp

2. Install the Claude Code plugin

The plugin tells Claude Code to use vue-ts-lsp for Vue and TypeScript files.

From a marketplace:

claude plugin install vue-ts-lsp@vue-ts-lsp

Manually — create an .lsp.json file in a plugin directory loaded via claude --plugin-dir (Claude Code discovers LSP servers through plugins only — an .lsp.json at your project root is not picked up):

{
    "vue-ts-lsp": {
        "command": "vue-ts-lsp",
        "extensionToLanguage": {
            ".vue": "vue",
            ".ts": "typescript",
            ".tsx": "typescriptreact",
            ".js": "javascript",
            ".jsx": "javascriptreact"
        },
        "startupTimeout": 30000
    }
}

Claude Code also accepts these optional fields in .lsp.json (as of Claude Code 2.1.226; behavior is version-dependent):

| Field | Type | Default | Description | | ----------------------- | ---------- | ------- | ------------------------------------------------------------------------------------------- | | args | string[] | [] | Extra CLI arguments — e.g. ["--log-level=debug"] to enable proxy debug logging | | diagnostics | boolean | true | Set false to keep code navigation but suppress automatic diagnostic injection after edits | | maxRestarts | number | 3 | Crash-recovery attempts for the proxy process before Claude Code gives up | | restartOnCrash | boolean | — | Client-level restart of the whole proxy on crash (implemented since ~2.1.2xx) | | shutdownTimeout | number | — | How long Claude Code waits for a graceful shutdown before killing the server | | env | object | — | Extra environment variables for the server process | | initializationOptions | object | — | Passed through in the LSP initialize request | | settings | object | — | Pushed to the server via workspace/didChangeConfiguration |

Note: On Claude Code 2.1.204 and earlier, restartOnCrash and shutdownTimeout were recognized but rejected ("not yet implemented") and would prevent the server from starting. Both work on 2.1.226+. Client-level restart is complementary to the proxy's own crash recovery for its child servers — the proxy also self-heals the client's no-didOpen-replay behavior after a restart.

Note: The proxy itself also handles .mts/.cts/.mjs/.cjs files; the plugin's extensionToLanguage map advertises only the common five extensions, so files with the rarer extensions get LSP features when reached through cross-file navigation but do not activate the server on open. Extend the map in a manual install if you need that.

Note: This proxy replaces separate vtsls and vue-volar plugins. Do not enable them simultaneously: Claude Code assigns each file extension to the first plugin that claims it, so a competing TypeScript plugin (e.g. typescript-lsp) can capture .ts/.tsx files while this proxy serves .vue — splitting the two sides of cross-file navigation across unrelated servers.

3. Verify

Start Claude Code (claude --debug for verbose output) and open a .vue or .ts file. You should see diagnostics, hover information, and go-to-definition working.

Architecture

┌─────────────┐     stdio       ┌──────────────────────────────┐
│ Claude Code │◄───────────────►│  vue-ts-lsp (proxy)          │
│ (LSP client)│                 │                              │
└─────────────┘                 │  ┌──────────┐  ┌───────────┐ │
                                │  │  vtsls   │  │  vue_ls   │ │
                                │  │ (child)  │  │  (child)  │ │
                                │  └────▲─────┘  └─────▲─────┘ │
                                │       │              │       │
                                │       │  tsserver/   │       │
                                │       │◄─request ────┘       │
                                │       │──response──►         │
                                └──────────────────────────────┘

Request routing

The proxy currently exposes the Claude Code request surface implemented in this repository. It does not currently advertise completion, rename, or formatting support.

| File Type | Supported Operation | Routed To | | ---------------------------- | ------------------------------------------------------------------------------- | ------------------------ | | .ts, .js, .tsx, .jsx | definition, implementation, hover, references, document symbols, call hierarchy | vtsls | | .vue | definition, implementation, hover, references, call hierarchy | vtsls | | .vue | document symbols | vue_ls | | .vue | diagnostics | Merged from both servers | | Any | workspace/symbol | vtsls |

For architecture details, see AGENTS.md.

How library definitions work

Go-to-definition on a symbol from node_modules returns a URI under ~/.cache/vue-ts-lsp/definition-mirrors/ rather than the raw node_modules path: the proxy copies the target declaration file into that cache and rewrites the location. This keeps results stable and readable for Claude Code. The cache is safe to delete at any time (it is rebuilt on demand), and its root can be moved with the VUE_TS_LSP_DEFINITION_MIRROR_ROOT environment variable.

TypeScript versions

vtsls ships with its own bundled TypeScript, but the proxy sets autoUseWorkspaceTsdk: true, so when your project has typescript installed, that version's tsserver does the analysis — your diagnostics match your project's compiler. The proxy's own internal AST helpers (used by fallback scans) run on its bundled TypeScript 6, independent of your workspace version. Note: the proxy pins typescript@^6.0.3 deliberately — TypeScript 7 has no programmatic API for vtsls or @vue/typescript-plugin to use.

CLI usage

vue-ts-lsp [--log-level=<error|warn|info|debug>]

| Flag | Description | | ------------- | ------------------------------------------------------------- | | --log-level | Logging verbosity: error (default), warn, info, debug |

All logging goes to stderr and to ~/.cache/vue-ts-lsp/vue-ts-lsp.log. stdout is reserved for the JSON-RPC protocol.

Project config

The proxy optionally reads project-local settings from .claude/vue-ts-lsp.json in the workspace root.

{
    "ignoreDirectories": ["vendor", "public"],
    "logLevel": "debug"
}

| Option | Type | Default | Description | | ------------------- | -------------------------------------- | ------- | ---------------------------------------------------------------------------------------------- | | ignoreDirectories | string[] | [] | Workspace-root-relative directories to skip when the proxy runs local workspace fallback scans | | logLevel | error | warn | info | debug | unset | Sets the proxy log level without needing to pass a CLI flag |

Notes:

  • The config file is optional.
  • ignoreDirectories defaults to an empty list.
  • ignoreDirectories only affects proxy-managed workspace fallback scans; it does not change your TypeScript project configuration.
  • --log-level on the CLI overrides logLevel from .claude/vue-ts-lsp.json.

Known limitations

  • Forwarded diagnostics preserve the downstream document version when available, and the proxy prevents known older versions and cached pre-edit results from being merged into newer diagnostics. vtsls does not report a version, so its publishes use the document version at arrival; a late unversioned result can still be attributed to the current edit. A diagnostic can also land one turn late if the type check outruns Claude Code's read of it.
  • Cross-file diagnostics after changing exported function signatures, store APIs, or narrowed unions are improved but not guaranteed to appear immediately in every dependent file, especially in large mixed workspaces.
  • If your workflow depends on catching downstream breakage after API changes, do not treat "no diagnostic appeared" as proof that all callers are valid. Prefer findReferences, a targeted typecheck, or project-specific verification hooks for high-confidence changes.
  • Go-to-definition can be sensitive to the exact character position in complex TypeScript expressions such as keyof typeof .... If a lookup misses unexpectedly, retry with the cursor placed directly on the referenced symbol.
  • Template definition support is strongest for standard bound expressions such as :prop="value". Member expressions inside v-model (e.g. v-model="charge.name") resolve for hover and definition (covered by the smoke suite); what remains unreliable is shorthand boolean props and resolving the v-model directive itself to the child component's prop declaration.
  • textDocument/implementation follows downstream TypeScript and Vue language-server behavior. Interfaces or structural types backed by object literals may legitimately return no implementation result.
  • Project hooks that rewrite files after Edit or Write tool calls, such as running Prettier, can add an extra mutation step and make diagnostic timing harder to reason about.
  • After reverting a cross-file type break, dependent-file diagnostics may occasionally clear a beat later than the edit that fixed them.
  • ignoreDirectories can reduce noisy fallback scanning in projects with generated, vendored, or public asset trees, but it only affects proxy fallback scans, not TypeScript's own project graph.
  • Workspace reference fallbacks match identifier spelling and can include unrelated symbols with the same name. Their alias resolution reads root tsconfig.json and jsconfig.json, including inherited settings, but does not discover separate configurations through project references.

Troubleshooting

| Issue | Solution | | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Server not starting | Verify which vue-ts-lsp returns a path. Check claude --debug output for initialization errors. | | No diagnostics or hover | Ensure your project has a tsconfig.json and typescript is installed in the project. | | Slow startup | The first initialization can take time on large projects. Increase startupTimeout in .lsp.json if needed. | | Server crashes repeatedly | Check ~/.cache/vue-ts-lsp/vue-ts-lsp.log for errors. The proxy auto-restarts up to 3 times per 30 seconds. | | Debug logging | Set --log-level=debug in the .lsp.json args, or set "logLevel": "debug" in .claude/vue-ts-lsp.json, to see full JSON-RPC payloads in the log file. | | tsserver-level debugging | Set the VUE_TS_LSP_TSSERVER_LOG environment variable (terse, normal, or verbose) to make tsserver write its own log files. Off by default. | | Cross-file diagnostics missing in noisy workspaces | Add project-specific ignores in .claude/vue-ts-lsp.json, for example "ignoreDirectories": ["vendor", "public"], to keep fallback scans focused on real source files. | | Go-to-definition misses on a valid TS symbol | Retry with the cursor placed directly on the symbol token. Complex type expressions can be character-position-sensitive. | | Template prop or v-model definition does not resolve | Prefer testing the bound script symbol or a standard :prop="..." binding first. Shorthand boolean attrs and directive targets are less reliable today. | | Go-to-implementation returns no result for an interface | This can be expected for structural typing or object-literal implementations. Use references or definitions instead if you need to trace usage sites. |

Contributing

See CONTRIBUTING.md for development guidelines and how to submit changes.

git clone https://github.com/tylersatre/vue-ts-lsp.git
cd vue-ts-lsp
npm install

| Command | Description | | ------------------------------- | ------------------------------------------------------------------------------------- | | npm run build | Build with tsup (src/index.ts → dist/index.js) | | npm run format | Format the repository with Prettier | | npm run format:check | Check repository formatting with Prettier | | npm run typecheck | Type-check source and tests | | npm test | Run unit, integration, and smoke tests (vitest; smoke self-skips without the fixture) | | npm run install:smoke-fixture | Install smoke test fixture dependencies |

Note: The repo includes .claude/settings.json which enables the typescript-lsp plugin for Claude Code. This gives contributors TypeScript LSP support while working on this project. If it conflicts with your setup, you can override it in .claude/settings.local.json.

Releasing

This project uses changesets for versioning and npm publishing. Do not create GitHub releases manually — the CI handles it.

1. Add a changeset with your PR

When submitting a PR with user-facing changes, add a changeset:

npx changeset

You'll be prompted to select the version bump type (major, minor, or patch) and a summary. Commit the generated .changeset/*.md file with your PR.

2. Merge to main

When the PR merges, the Release workflow runs automatically and does one of two things:

  • Pending changesets exist: the changesets/action opens (or updates) a "Version Packages" PR that bumps package.json, updates CHANGELOG.md, and removes consumed changeset files.
  • No pending changesets (i.e., the "Version Packages" PR was just merged): the action runs npm run release, which builds the package and publishes to npm, then creates a GitHub release.

License

MIT