-
-
Notifications
You must be signed in to change notification settings - Fork 21
feat(setup): treat python -m claude_statusbar.cli as our own statusLine
#40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -110,13 +110,27 @@ def _statusline_config(fast: bool = False, refresh_interval: int = DEFAULT_REFRE | |||||||||
| } | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def _invokes_our_module(cmd: str) -> bool: | ||||||||||
| """True for ``<python> -m claude_statusbar.cli render``. | ||||||||||
|
|
||||||||||
| A legitimate way to run us that skips pip's console-script launcher. On | ||||||||||
| Windows that launcher spawns a second process, roughly doubling status-line | ||||||||||
| latency (~0.9s vs ~0.44s per render on a conda install), so users on slow | ||||||||||
| Python startups may prefer the module form. Recognise it as ours rather | ||||||||||
| than reporting it as a foreign tool. | ||||||||||
| """ | ||||||||||
| return "claude_statusbar" in cmd | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def _is_our_statusline(entry: object) -> bool: | ||||||||||
| """Return True if the existing statusLine entry already points at our CLI.""" | ||||||||||
| if not isinstance(entry, dict): | ||||||||||
| return False | ||||||||||
| cmd = entry.get("command") | ||||||||||
| if not isinstance(cmd, str) or not cmd.strip(): | ||||||||||
| return False | ||||||||||
| if _invokes_our_module(cmd): | ||||||||||
| return True | ||||||||||
| name = _normalize_command_name(Path(cmd.strip().split()[0]).name) # strip args + path + .exe shim | ||||||||||
| return name in OUR_COMMAND_NAMES | ||||||||||
|
|
||||||||||
|
|
@@ -158,7 +172,9 @@ def _existing_uses_render(existing) -> bool: | |||||||||
| if not isinstance(cmd, str): | ||||||||||
| return False | ||||||||||
| parts = cmd.strip().split() | ||||||||||
| return len(parts) >= 2 and parts[1] == "render" | ||||||||||
| # `cs render`, and the module form `<python> -m claude_statusbar.cli render` | ||||||||||
| # — in both, `render` is the last token. | ||||||||||
| return len(parts) >= 2 and parts[-1] == "render" | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def ensure_statusline_configured(fast: Optional[bool] = None) -> Tuple[bool, str]: | ||||||||||
|
|
@@ -218,6 +234,13 @@ def ensure_statusline_configured(fast: Optional[bool] = None) -> Tuple[bool, str | |||||||||
| effective_refresh = DEFAULT_REFRESH_INTERVAL | ||||||||||
| desired = _statusline_config(fast=effective_fast, refresh_interval=effective_refresh) | ||||||||||
|
|
||||||||||
| # The module form is a deliberate choice (see `_invokes_our_module`), not | ||||||||||
| # drift — the daily repair pass must not rewrite it back to the console | ||||||||||
| # script. Keep the command, still refresh refreshInterval. | ||||||||||
| existing_cmd = existing.get("command") | ||||||||||
| if isinstance(existing_cmd, str) and _invokes_our_module(existing_cmd): | ||||||||||
| desired["command"] = existing_cmd | ||||||||||
|
Comment on lines
+241
to
+242
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Honor explicit This runs even when Proposed fix- if isinstance(existing_cmd, str) and _invokes_our_module(existing_cmd):
+ if fast is None and isinstance(existing_cmd, str) and _invokes_our_module(existing_cmd):
desired["command"] = existing_cmd📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| if (existing.get("command") != desired["command"] | ||||||||||
| or existing.get("refreshInterval") != desired["refreshInterval"]): | ||||||||||
| settings["statusLine"] = desired | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1098,3 +1098,67 @@ def test_windows_identity_false_when_no_shell_available(monkeypatch): | |
| ) | ||
|
|
||
| assert _d._process_is_our_daemon(4242) is False | ||
|
|
||
|
|
||
| MODULE_STATUSLINE = "C:/Users/x/miniconda3/python.exe -m claude_statusbar.cli render" | ||
|
|
||
|
|
||
| def test_module_invocation_counts_as_ours(): | ||
| """`<python> -m claude_statusbar.cli render` skips pip's console-script | ||
| launcher, which on Windows spawns a second process and roughly doubles | ||
| per-render latency. It must not be mistaken for a foreign tool.""" | ||
| assert _is_our_statusline({"type": "command", "command": MODULE_STATUSLINE}) is True | ||
|
|
||
| from claude_statusbar.setup import _existing_uses_render | ||
| assert _existing_uses_render({"command": MODULE_STATUSLINE}) is True | ||
|
|
||
|
|
||
| def test_daily_repair_does_not_rewrite_module_invocation(monkeypatch, tmp_path): | ||
| """The once-a-day auto-repair pass would otherwise replace a deliberate | ||
| module invocation with the console script every single day.""" | ||
| from claude_statusbar import setup as _setup | ||
|
|
||
| settings = tmp_path / "settings.json" | ||
| settings.write_text(json.dumps({ | ||
| "statusLine": {"type": "command", "command": MODULE_STATUSLINE, | ||
| "refreshInterval": 1}, | ||
| }), encoding="utf-8") | ||
| monkeypatch.setattr(_setup, "SETTINGS_PATH", settings) | ||
|
|
||
| changed, msg = _setup.ensure_statusline_configured() | ||
|
|
||
| assert changed is False, msg | ||
| after = json.loads(settings.read_text(encoding="utf-8")) | ||
| assert after["statusLine"]["command"] == MODULE_STATUSLINE | ||
|
|
||
|
|
||
| def test_displacement_warning_silent_for_module_invocation(monkeypatch, tmp_path): | ||
| settings = tmp_path / "settings.json" | ||
| settings.write_text(json.dumps({ | ||
| "statusLine": {"type": "command", "command": MODULE_STATUSLINE}, | ||
| }), encoding="utf-8") | ||
| monkeypatch.setattr(render_thin, "_USER_SETTINGS", settings) | ||
|
|
||
| assert render_thin._displacement_suffix() == "" | ||
|
Comment on lines
+1135
to
+1142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Reject foreign commands that merely contain the module name.
🧰 Tools🪛 ast-grep (0.44.1)[info] 1136-1138: use jsonify instead of json.dumps for JSON output (use-jsonify) 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def test_displacement_warning_silent_for_windows_exe_shim(monkeypatch, tmp_path): | ||
| """pip writes `cs.EXE` on Windows; case + extension must not read as foreign.""" | ||
| settings = tmp_path / "settings.json" | ||
| settings.write_text(json.dumps({ | ||
| "statusLine": {"type": "command", | ||
| "command": r"C:\Users\x\Scripts\cs.EXE render"}, | ||
| }), encoding="utf-8") | ||
| monkeypatch.setattr(render_thin, "_USER_SETTINGS", settings) | ||
|
|
||
| assert render_thin._displacement_suffix() == "" | ||
|
|
||
|
|
||
| def test_displacement_warning_still_fires_for_foreign_tool(monkeypatch, tmp_path): | ||
| settings = tmp_path / "settings.json" | ||
| settings.write_text(json.dumps({ | ||
| "statusLine": {"type": "command", "command": "starship prompt"}, | ||
| }), encoding="utf-8") | ||
| monkeypatch.setattr(render_thin, "_USER_SETTINGS", settings) | ||
|
|
||
| assert "starship" in render_thin._displacement_suffix() | ||
Uh oh!
There was an error while loading. Please reload this page.