Back to Discover

typemux-cc

plugin

K-dash

Claude Code plugin: Python type-checker LSP multiplexer. Auto-detects .venv and routes to pyright/ty/pyrefly—no Claude Code restart.

View on GitHub
15 starsMITSynced Aug 2, 2026

Install to Claude Code

/plugin marketplace add K-dash/typemux-cc

README

typemux-cc

Python type-checker LSP multiplexer for Claude Code — pyright, ty, pyrefly

GitHub commit activity License Rust DeepWiki

QuickstartProblems SolvedBackendsInstallationTypical Use CaseArchitecture


Claude Code's official pyright plugin spawns a single LSP backend at startup and holds onto it. If .venv doesn't exist yet — or you create a new one later — it never picks it up. You have to restart Claude Code.

This is especially painful with git worktrees, now common in AI-assisted development: you spin up a fresh worktree, create .venv, and then must restart Claude Code just to get type-checking.

typemux-cc is a Python LSP proxy that fixes this — .venv changes are reflected within your running session, no restarts required.

This is a recurring, unresolved pain in the Claude Code ecosystem: worktree/venv breakage keeps being reported upstream — anthropics/claude-code#31391 (closed as not planned), astral-sh/claude-code-plugins#18 (open), anthropics/claude-code#58365 — while the official pyright plugin remains a single pyright-langserver spawn with no environment handling. typemux-cc exists to solve the environment-lifecycle side of this problem for Python.

Quickstart

# 1. Install a backend (pyright recommended)
npm install -g pyright

# 2. Disable the official pyright plugin
/plugin disable pyright-lsp@claude-plugins-official

# 3. Add marketplace and install
/plugin marketplace add K-dash/typemux-cc
/plugin install typemux-cc@typemux-cc-marketplace

# 4. Restart Claude Code (initial installation only)

For ty/pyrefly, set TYPEMUX_CC_BACKEND in your config.

Problems Solved

  • ⚡ Late .venv creation (worktrees, hooks) — Spin up a git worktree, create .venv later, and typemux-cc picks it up on the next file open. No Claude Code restart needed.
  • 🔄 Multi-project venv switching (monorepos) — typemux-cc keeps a per-.venv backend pool and routes requests to the correct one. Switching between projects is instant.
  • 🔀 Multi-backend support — Not locked into pyright. Choose between pyright, ty, or pyrefly — switch via a single env var.

Frozen capabilities on cold startinitialize always answers immediately with empty capabilities, on every startup, regardless of whether a .venv exists yet — the first backend, like every other, is created lazily on the first venv-resolving message (#140: a synchronous pre-spawned-backend handshake here used to permanently wedge Claude Code 2.1.207's LSP client, still reproducible on 2.1.211). That advertisement stays frozen for the session — verified with Claude Code 2.1.207 that the client keeps sending LSP requests regardless, so nothing is gated (see Frozen Empty Capabilities).

Why LSP over text search? In monorepos, grep returns false positives from same-named types across projects. LSP resolves references at the type-system level. See real-world benchmarks.

Supported Backends

BackendCommandStatus
pyrightpyright-langserver --stdio✅ Stable (default if TYPEMUX_CC_BACKEND is not set)
tyty server✅ Stable
pyreflypyrefly lsp✅ Stable

Requirements

Supported OS

PlatformArchitecture
macOSarm64 only
Linuxx86_64 / arm64

[!Note] Windows is currently unsupported (due to path handling differences). Intel macOS users must build from source (prebuilt binaries are arm64 only).

Prerequisites

  • One of the supported LSP backends available in PATH:
    • pyright-langserver (install via npm install -g pyright or pip install pyright)
    • ty (install via pip install ty or uvx ty)
    • pyrefly (install via pip install pyrefly)
  • Git (used to determine .venv search boundary, works without it)

Installation

[!Note] Claude Code restart is required only for initial installation. After installation, .venv creation and switching no longer require restarts.

Prerequisites

1. Install your preferred LSP backend

# pyright (default, recommended)
npm install -g pyright

# ty (by the creators of uv)
pip install ty

# pyrefly (by Meta)
pip install pyrefly

2. Disable Official pyright Plugin

[!Important] You must disable the official pyright plugin. Having both enabled causes conflicts.

/plugin disable pyright-lsp@claude-plugins-official

Method A: From GitHub Marketplace (Recommended)

[!Note] Installation uses GitHub API and curl. It may fail in offline environments or under rate limiting.

# 1. Add marketplace
/plugin marketplace add K-dash/typemux-cc

# 2. Install plugin
/plugin install typemux-cc@typemux-cc-marketplace

# 3. Restart Claude Code (initial installation only)

After installation, verify in ~/.claude/settings.json:

{
  "enabledPlugins": {
    "pyright-lsp@claude-plugins-official": false,
    "typemux-cc@typemux-cc-marketplace": true
  }
}

Update / Uninstall

# Update
/plugin update typemux-cc@typemux-cc-marketplace

# Uninstall
/plugin uninstall typemux-cc@typemux-cc-marketplace
/plugin marketplace remove typemux-cc-marketplace

How the Installer Verifies Binaries

The plugin's SessionStart hook downloads the prebuilt binary for your platform from this repository's GitHub Releases over HTTPS, pinned to the plugin's own version tag (never "latest"). Before the downloaded binary is executed or activated, the installer:

  1. Verifies its SHA256 checksum against the .sha256 asset published alongside it by the release workflow
  2. Confirms the binary reports the expected version

On any failure it keeps the previously working binary and prints an explicit warning, or fails loudly if no binary exists yet. Binaries are built from the tagged commit by GitHub Actions — nothing is built or fetched from anywhere else.

Method B: Local Build (For Developers)

Requires Rust 1.88 or later. Running the full test suite (cargo test / make ci) additionally requires jq — it's used by scripts/check-versions.sh to validate the release version across Cargo.toml, Cargo.lock, and the plugin manifests. Install it with brew install jq (macOS) or apt-get install -y jq (Debian/Ubuntu). make ci fails fast with a clear error if jq is missing.

git clone https://github.com/K-dash/typemux-cc.git
cd typemux-cc
cargo build --release

/plugin marketplace add /path/to/typemux-cc
/plugin install typemux-cc@typemux-cc-marketplace
# Restart Claude Code (initial installation only)

Usage

Automatically starts as a Claude Code plugin — no manual setup required.

Configuration

Settings are stored in ~/.config/typemux-cc/config. The file uses KEY=VALUE format (shell expansion is not supported):

mkdir -p ~/.config/typemux-cc
cat > ~/.config/typemux-cc/config << 'EOF'
# Select backend (pyright, ty, or pyrefly)
TYPEMUX_CC_BACKEND=pyright

# Enable file logging
TYPEMUX_CC_LOG_FILE=/tmp/typemux-cc.log
EOF

Note: export KEY=VALUE syntax is also accepted for compatibility with older config files.

Settings priority: CLI flag > environment variable > config file > default

VariableDescriptionDefault
TYPEMUX_CC_LOG_FILELog file pathNot set (stderr only)
TYPEMUX_CC_BACKENDLSP backend to usepyright
TYPEMUX_CC_MAX_BACKENDSMax concurrent backend processes8
TYPEMUX_CC_BACKEND_TTLBackend TTL in seconds (0 = disabled)1800
TYPEMUX_CC_POOL_SWEEP_INTERVALInterval in seconds between pool sweep ticks (drives both TTL eviction and the venv staleness sweep; must be > 0)60
TYPEMUX_CC_FANOUT_TIMEOUTFan-out timeout in seconds for workspace/symbol (0 = no timeout)5
TYPEMUX_CC_VENV_CHECK_INTERVALInterval in seconds between venv identity checks (0 = disable venv identity tracking)5
TYPEMUX_CC_INIT_HANDSHAKE_TIMEOUTBackend spawn → initialize handshake timeout in seconds10
RUST_LOGLog leveltypemux_cc=debug

An invalid value for a numeric variable above (e.g. TYPEMUX_CC_FANOUT_TIMEOUT=5s) fails startup with an explicit error instead of silently falling back to the default. TYPEMUX_CC_POOL_SWEEP_INTERVAL=0 also fails startup — unlike the other vars, 0 isn't a valid "disable" sentinel here; use --backend-ttl 0 / TYPEMUX_CC_VENV_CHECK_INTERVAL=0 to disable the sweep's individual jobs instead.

Typical Use Case

Git Worktree (AI-Assisted Development)

A common workflow with AI coding agents:

my-project/                    # main worktree
├── .venv/
└── src/main.py

my-project-worktree/           # new worktree (no .venv yet)
└── src/main.py
StepWhat Happens
1. Create worktreegit worktree add ../my-project-worktree feat/new-feature — no .venv exists
2. Create .venvcd ../my-project-worktree && uv sync.venv now exists
3. Open a fileClaude Code opens my-project-worktree/src/main.py → typemux-cc detects the new .venv and spawns a backend automatically

With the official plugin, step 3 would require restarting Claude Code. With typemux-cc, it just works.

Monorepo Structure

my-monorepo/
├── project-a/
│   ├── .venv/          # project-a specific virtual environment
│   └── src/main.py
├── project-b/
│   ├── .venv/          # project-b specific virtual environment
│   └── src/main.py
└── project-c/
    ├── .venv/          # project-c specific virtual environment
    └── src/main.py

Operation Sequence

Claude Code ActionProxy Behavior
1. Session startsinitialize answers instantly with empty capabilities; no backend spawned yet
2. Opens project-a/src/main.pyDetect project-a/.venv → spawn backend (session 1), add to pool
3. Opens project-b/src/main.pyDetect project-b/.venv → spawn backend (session 2), add to pool
4. Returns to project-a/src/main.pyproject-a/.venv already in pool → route to session 1 (no restart)

What Actually Happens

When Claude Code moves from project-a/main.py to project-b/main.py:

  1. Proxy detects different .venv (project-a/.venv → project-b/.venv)
  2. Checks the backend pool — project-b/.venv not found
  3. Spawns new backend with VIRTUAL_ENV=project-b/.venv (session 2)
  4. Session 1 (project-a) stays alive in the pool — no restart
  5. Restores open documents under project-b/ to session 2
  6. Clears diagnostics for documents outside project-b/
  7. All LSP requests for project-b files now use project-b dependencies

When Claude Code returns to project-a/main.py later, session 1 is still in the pool — zero restart overhead.

Backends are evicted only when the pool is full (LRU) or after idle timeout (TTL, default 30 min).

From the user's perspective: Nothing visible happens. LSP just works.

Environment Variables

Each backend process is spawned with VIRTUAL_ENV and PATH set to point at the detected .venv. These are only applied to the child backend process — your shell environment and system PATH are never modified.

Troubleshooting

For self-diagnosis (--doctor), LSP not working, plugin update issues, .venv not switching, and the "Project root is gitignored" warning, see:

docs/TROUBLESHOOTING.md

Known Limitations

ItemLimitationWorkaround
Windows unsupportedPath handling assumes Unix-like systemsUse WSL2
macOS Intel unsupportedPrebuilt is arm64 onlyUse Apple Silicon
Fixed venv nameOnly .venv with pyvenv.cfg — intentionally strict to avoid silently wrong environments (poetry/conda/etc. not supported)Rename to .venv or create a .venv symlink
SymlinksMay fail to detect pyvenv.cfg if .venv is a symlinkUse actual directory
setuptools editable installsNot a typemux-cc bug. All LSP backends (pyright, ty, pyrefly) cannot resolve imports from setuptools-style editable installs that use import hooks (ty#475)Switch build backend to hatchling/flit, or add source paths to extra-paths in backend config
workspace/symbol fan-out latencyWith multiple backends, workspace/symbol fans out to all backends and merges results; response time equals the slowest backend (timeout: 5s default)Adjust via TYPEMUX_CC_FANOUT_TIMEOUT env var
workspace/symbol always returns emptyClaude Code's LSP tool does not pass a query parameter to workspace/symbol requests. The LSP spec requires { query: "search string" }, but the tool interface only exposes operation, filePath, line, character. With an empty query, pyright returns no results. This is a Claude Code limitation, not a typemux-cc bug.Use Grep/Glob for cross-project symbol search until Claude Code adds query support
goToDefinition/findReferences return empty for gitignored paths (including Claude Code's own worktrees)Not a typemux-cc bug — the proxy forwards pyright's valid Location[] response. Claude Code's LSP tool post-filters URI-bearing results (goToDefinition, findReferences, goToImplementation, workspaceSymbol) through git check-ignore run in the session's cwd, silently dropping every location whose path is ignored in that git context. Claude Code also registers **/.claude/worktrees/ in .git/info/exclude, so results inside its own worktrees are always dropped when the session runs at the main repo root. Other worktree layouts (e.g., .worktree/) are affected only when an ignore rule matches them, and the same filter hides definitions resolving into gitignored directories such as .venv/ (third-party sources). hover/documentSymbol results carry no URIs and are unaffected. Verified against Claude Code v2.1.206; reported upstream in anthropics/claude-code#76371.Launch Claude Code directly inside the worktree (paths then resolve relative to the worktree root and no longer match the ignore rules), or keep worktrees in a directory not matched by any gitignore rule

Architecture

For design philosophy, state transitions, and internal implementation details, see:

docs/ARCHITECTURE.md

Privacy

typemux-cc runs entirely locally and collects no data. The only network access is the version-pinned, checksum-verified binary download from GitHub Releases at install time. See PRIVACY.md.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Rendered live from K-dash/typemux-cc's GitHub README — not stored, always reflects the source repo.

1 Plugin

NameDescriptionCategorySource
typemux-ccPython type-checker LSP multiplexer for Claude Code (pyright, ty, pyrefly)./

0 Comments

Login required
Log in to post a comment or update on this repo.

No comments yet — be the first to share an update.