Summary
On Windows, canonical_omind_exe() never engages its intended stable pin and silently falls back to shutil.which("omind"). Because the fallback pins whatever omind launcher happens to be first on PATH, omind setup/doctor can bake a path to an orphaned pip shim into the MCP server entry (and every omind hook). When that shim points at a Python interpreter that no longer has the omind package installed, the MCP server dies on launch and Claude Code shows omi (CONNECTION_CLOSED): "Connection closed" on every new session.
This is the exact "pin whatever's first on PATH, then freeze it forever" fragility the function's docstring says it exists to avoid — it just doesn't hold on Windows.
Root cause
src/omind/provision.py:
CANONICAL_OMIND_EXE = Path.home() / ".local" / "bin" / "omind" # no extension
def canonical_omind_exe() -> str:
if CANONICAL_OMIND_EXE.exists():
return str(CANONICAL_OMIND_EXE)
return shutil.which("omind") or "omind"
Two layers:
- Extension gap (concrete bug). On Windows the launcher is
omind.exe, but CANONICAL_OMIND_EXE is the extension-less name omind, so .exists() is always False and the canonical pin never engages. It always falls through to which().
- Location mismatch + no liveness check (design).
~/.local/bin is a POSIX convention; on Windows pip/uv install the launcher to %APPDATA%\Python\PythonXY\Scripts, and nothing keeps ~/.local/bin current. which() can therefore resolve a stale/orphaned shim, and nothing (including doctor) verifies the resolved exe actually runs before baking it into config/hooks.
Reproduction / evidence
Windows 11, Python 3.14, omind installed editable into the user site (%APPDATA%\Python\Python314), with an older orphaned launcher still present at ~/.local/bin\omind.exe (which is first on PATH):
>>> from pathlib import Path; p = Path.home()/'.local'/'bin'/'omind'
>>> p.exists()
False
>>> import shutil; shutil.which('omind')
'C:\\Users\\<user>\\.local\\bin\\omind.EXE'
Running the pinned shim directly:
C:\Users\<user>\.local\bin\omind.EXE ->
Traceback (most recent call last):
File "<frozen runpy>", line 203, in _run_module_as_main
...
File "C:\Users\<user>\.local\bin\omind.EXE\__main__.py", line 4, in <module>
from omind.cli import main
ModuleNotFoundError: No module named 'omind'
The working launcher at %APPDATA%\Python\Python314\Scripts\omind.exe runs fine — so the package is installed; the wrong (orphaned) launcher was pinned.
Resulting ~/.claude.json entry (broken):
"omi": {
"type": "stdio",
"command": "C:\\Users\\<user>\\.local\\bin\\omind.EXE",
"args": ["node", "--vault", "...", "--folder", "OMI"]
}
Impact
- MCP server
omi fails to connect on every new Claude Code session on affected Windows boxes.
- Manually repointing the config command at the working launcher is not durable: the next
omind setup/doctor/self-update re-runs canonical_omind_exe(), re-resolves to the orphaned shim, and re-breaks it.
- The same resolution feeds omind hook commands, so hooks are exposed to the same stale-pin failure.
Suggested fix
- Make canonical resolution extension-aware on Windows (probe
omind.exe / honor PATHEXT, not just a bare omind).
- Treat the pip/uv Scripts dir as the canonical location on Windows (e.g. via
sysconfig user scripts path) rather than ~/.local/bin.
- Add a liveness check: verify the resolved launcher actually runs / imports
omind before baking it into the MCP entry and hooks; re-resolve if it doesn't.
- Have
doctor flag a baked-but-dead command (a pinned path whose process exits non-zero / can't import omind) and offer to re-register.
Regression coverage would want a Windows case for canonical_omind_exe() asserting the .exe/Scripts-dir path is chosen and that an orphaned/dead shim is not pinned.
Environment
- OS: Windows 11 Pro (10.0.26200)
- Python: 3.14.7
- omind: 9.4.0 (editable install)
- Claude Code MCP server name:
omi
Summary
On Windows,
canonical_omind_exe()never engages its intended stable pin and silently falls back toshutil.which("omind"). Because the fallback pins whateveromindlauncher happens to be first onPATH,omind setup/doctorcan bake a path to an orphaned pip shim into the MCP server entry (and every omind hook). When that shim points at a Python interpreter that no longer has theomindpackage installed, the MCP server dies on launch and Claude Code showsomi (CONNECTION_CLOSED): "Connection closed"on every new session.This is the exact "pin whatever's first on PATH, then freeze it forever" fragility the function's docstring says it exists to avoid — it just doesn't hold on Windows.
Root cause
src/omind/provision.py:Two layers:
omind.exe, butCANONICAL_OMIND_EXEis the extension-less nameomind, so.exists()is alwaysFalseand the canonical pin never engages. It always falls through towhich().~/.local/binis a POSIX convention; on Windows pip/uv install the launcher to%APPDATA%\Python\PythonXY\Scripts, and nothing keeps~/.local/bincurrent.which()can therefore resolve a stale/orphaned shim, and nothing (includingdoctor) verifies the resolved exe actually runs before baking it into config/hooks.Reproduction / evidence
Windows 11, Python 3.14,
omindinstalled editable into the user site (%APPDATA%\Python\Python314), with an older orphaned launcher still present at~/.local/bin\omind.exe(which is first onPATH):Running the pinned shim directly:
The working launcher at
%APPDATA%\Python\Python314\Scripts\omind.exeruns fine — so the package is installed; the wrong (orphaned) launcher was pinned.Resulting
~/.claude.jsonentry (broken):Impact
omifails to connect on every new Claude Code session on affected Windows boxes.omind setup/doctor/self-update re-runscanonical_omind_exe(), re-resolves to the orphaned shim, and re-breaks it.Suggested fix
omind.exe/ honorPATHEXT, not just a bareomind).sysconfiguser scripts path) rather than~/.local/bin.omindbefore baking it into the MCP entry and hooks; re-resolve if it doesn't.doctorflag a baked-but-dead command (a pinned path whose process exits non-zero / can't importomind) and offer to re-register.Regression coverage would want a Windows case for
canonical_omind_exe()asserting the.exe/Scripts-dir path is chosen and that an orphaned/dead shim is not pinned.Environment
omi