Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/claude_statusbar/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,16 +343,56 @@ def _cmdline_is_our_daemon(cmdline: str) -> bool:
)


_WIN_CMDLINE_TIMEOUT_S = 8.0


def _win_process_cmdline(pid: int) -> Optional[str]:
"""Command line of `pid` on Windows, or None if it can't be read.

Windows has no /proc, and `ps` is worse than useless there: when Git Bash
is on PATH its MSYS `ps` only lists MSYS processes, so a natively spawned
daemon reads as gone and the caller concludes "not ours".

CIM is the reliable source. `wmic` would be faster but is deprecated and
already absent from current Windows 11 builds. ~0.5s per call, which only
the stop / install / drift-restart paths pay — never a render.
"""
import subprocess
script = (
f"$p = Get-CimInstance Win32_Process -Filter 'ProcessId={int(pid)}';"
"if ($p) { $p.CommandLine }"
)
for exe in ("powershell", "pwsh"):
try:
out = subprocess.run(
[exe, "-NoProfile", "-NonInteractive", "-Command", script],
capture_output=True,
text=True,
timeout=_WIN_CMDLINE_TIMEOUT_S,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
)
except (OSError, subprocess.TimeoutExpired):
continue # not installed / hung — try the next interpreter
if out.returncode == 0 and out.stdout.strip():
return out.stdout
return None


def _process_is_our_daemon(pid: int) -> bool:
"""Verify the PID actually belongs to *our* daemon, not a recycled PID.

Linux: read /proc/<pid>/cmdline directly (cheap, no fork).
Windows: query CIM (see `_win_process_cmdline`).
macOS / fallback: shell out to `ps -o command= -p <pid>` (~10ms — only
runs on stop/install paths, never on the per-render hot path).

Returns False on any error (better to assume not-ours and skip than to
accidentally SIGTERM an unrelated user process).
"""
if sys.platform == "win32":
cmdline = _win_process_cmdline(pid)
return _cmdline_is_our_daemon(cmdline) if cmdline else False

Comment on lines 381 to +395

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm spawn_if_dead's call sites and whether it's on the interactive render path.
rg -n 'spawn_if_dead' --type=py -C3

Repository: leeguooooo/claude-code-usage-bar

Length of output: 170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map daemon.py, then inspect spawn_if_dead, render-related call sites, and the PID verification path.
ast-grep outline src/claude_statusbar/daemon.py --view expanded || true
printf '\n--- spawn_if_dead matches ---\n'
rg -n 'spawn_if_dead|_process_is_our_daemon|_win_process_cmdline|rendered\.meta\.json|cs render|render' src/claude_statusbar/daemon.py -C 3

Repository: leeguooooo/claude-code-usage-bar

Length of output: 17063


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant sections around the reported lines and the spawn_if_dead implementation.
sed -n '340,460p' src/claude_statusbar/daemon.py
printf '\n---\n'
sed -n '800,860p' src/claude_statusbar/daemon.py

Repository: leeguooooo/claude-code-usage-bar

Length of output: 6736


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files:\n'
git ls-files | sed -n '1,200p'

printf '\n--- references ---\n'
rg -n '_process_is_our_daemon|spawn_if_dead|rendered\.meta\.json|cs render|render' src -C 2 || true

printf '\n--- daemon.py slices ---\n'
wc -l src/claude_statusbar/daemon.py
sed -n '360,450p' src/claude_statusbar/daemon.py
printf '\n---\n'
sed -n '800,860p' src/claude_statusbar/daemon.py

Repository: leeguooooo/claude-code-usage-bar

Length of output: 50391


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Trace where render_thin calls spawn_if_dead and whether it's only on stale-meta fallback.
rg -n 'spawn_if_dead|rendered\.meta\.json|stale|_is_fresh|_signal_outdated_daemon' src/claude_statusbar/render_thin.py -C 4

Repository: leeguooooo/claude-code-usage-bar

Length of output: 6056


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the thin client control flow around the stale-meta check.
sed -n '1,260p' src/claude_statusbar/render_thin.py

Repository: leeguooooo/claude-code-usage-bar

Length of output: 10203


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrowly check whether render_thin invokes spawn_if_dead.
rg -n 'spawn_if_dead|rendered\.meta\.json|_is_fresh|_signal_outdated_daemon' src/claude_statusbar/render_thin.py -C 3 || true

Repository: leeguooooo/claude-code-usage-bar

Length of output: 2643


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the spawn helper and the render fallback call site to see whether the CIM lookup is synchronous.
sed -n '320,430p' src/claude_statusbar/render_thin.py

Repository: leeguooooo/claude-code-usage-bar

Length of output: 4514


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show just the async-spawn helper and its usage points.
rg -n '_spawn_daemon_async|spawn_if_dead|threading\.Thread|start_new_session' src/claude_statusbar/render_thin.py -C 3

Repository: leeguooooo/claude-code-usage-bar

Length of output: 1628


Keep the Windows CIM probe off the cs render fallback path. spawn_if_dead() is still reached from render_thin._spawn_daemon_async(), so a stale or missing daemon can make cs render block on _process_is_our_daemon() before falling back inline. On Windows that can add up to 16s of powershell/pwsh timeout latency to a status-line tick; reserve the identity check for explicit daemon-management commands, or skip it on this render-triggered path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/claude_statusbar/daemon.py` around lines 381 - 395, Prevent the Windows
`_win_process_cmdline` CIM probe from running when `_process_is_our_daemon` is
invoked through `render_thin._spawn_daemon_async` and `spawn_if_dead`. Keep the
identity check for explicit daemon-management commands, while allowing the
render fallback to avoid the potentially blocking Windows probe.

proc_path = f"/proc/{pid}/cmdline"
try:
with open(proc_path, "rb") as f:
Expand Down
53 changes: 53 additions & 0 deletions tests/test_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -1045,3 +1045,56 @@ def test_release_pidfile_still_cleans_its_own_file(monkeypatch, tmp_path: Path):
assert _d._acquire_pidfile() is True
_d._release_pidfile()
assert not _d.pid_path().exists()


def test_windows_identity_uses_cim_not_ps(monkeypatch):
"""On Windows there is no /proc, and a Git-Bash `ps` on PATH only lists
MSYS processes — a natively spawned daemon reads as gone, so `cs daemon
stop` refuses to stop it and the drift-kill guard never restarts it onto
upgraded code."""
import subprocess as _sp

calls = []

def fake_run(cmd, **kw):
calls.append(cmd[0])
if cmd[0] == "ps":
raise AssertionError("ps must not be consulted on win32")
return _sp.CompletedProcess(
cmd, 0,
stdout=('"C:\Python\python.exe" -m claude_statusbar.cli '
'daemon _run --render-interval 1.0\n'),
stderr="",
)
Comment on lines +1063 to +1068

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

# Show the relevant sections with line numbers
sed -n '1058,1072p;1078,1088p' tests/test_daemon.py

# Probe Python's handling of the exact string literals used in the review comment
python3 - <<'PY'
import warnings

snippets = [
    '"C:\\Python\\python.exe" -m claude_statusbar.cli daemon _run --render-interval 1.0\n',
    "C:\\Windows\\explorer.exe\n",
    '"C:\Python\python.exe" -m claude_statusbar.cli daemon _run --render-interval 1.0\n',
    "C:\Windows\explorer.exe\n",
]

for s in snippets:
    print("SNIPPET:", repr(s))
    try:
        compile(f"x = {s!r}", "<snippet>", "exec")
        print("compile: ok")
    except Exception as e:
        print("compile:", type(e).__name__, e)

# Specifically compile literal source containing invalid escapes to see warnings/errors.
sources = [
    'x = "C:\\Python\\python.exe" -m claude_statusbar.cli daemon _run --render-interval 1.0\n',
    'x = "C:\\Windows\\explorer.exe\n"',
    'x = "C:\\Python\\python.exe"\n',
    'x = "C:\\Windows\\explorer.exe"\n',
]
for src in sources:
    print("\nSOURCE:", src.encode("unicode_escape").decode())
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        try:
            compile(src, "<src>", "exec")
            print("compile: ok")
        except Exception as e:
            print("compile:", type(e).__name__, e)
        for item in w:
            print("warning:", item.category.__name__, str(item.message))
PY

Repository: leeguooooo/claude-code-usage-bar

Length of output: 1877


🏁 Script executed:

set -euo pipefail

printf '\n-- targeted source --\n'
nl -ba tests/test_daemon.py | sed -n '1058,1072p;1078,1088p'

printf '\n-- python probe --\n'
python3 - <<'PY'
import warnings

# Build the exact source text without letting this script's parser interpret it.
good1 = '"C:\\\\Python\\\\python.exe" -m claude_statusbar.cli daemon _run --render-interval 1.0\n'
good2 = 'C:\\\\Windows\\\\explorer.exe\n'
bad1 = '"C:\\Python\\python.exe" -m claude_statusbar.cli daemon _run --render-interval 1.0\n'
bad2 = 'C:\\Windows\\explorer.exe\n'

for label, text in [('good1', good1), ('good2', good2), ('bad1', bad1), ('bad2', bad2)]:
    print(label, repr(text))
    src = 'x = ' + repr(text) + '\n'
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter('always')
        try:
            compile(src, '<src>', 'exec')
            print('  compile: ok')
        except Exception as e:
            print('  compile:', type(e).__name__, e)
        for item in w:
            print('  warning:', item.category.__name__, str(item.message))

# Compile exact raw source code containing invalid escapes
raw_sources = [
    'x = "C:\\Python\\python.exe" -m claude_statusbar.cli daemon _run --render-interval 1.0\n',
    'x = "C:\\Windows\\explorer.exe\\n"\n',
    'x = "C:\\Python\\python.exe"\n',
    'x = "C:\\Windows\\explorer.exe"\n',
]
for src in raw_sources:
    print('\nRAW SRC:', src.encode('unicode_escape').decode())
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter('always')
        try:
            compile(src, '<raw>', 'exec')
            print('  compile: ok')
        except Exception as e:
            print('  compile:', type(e).__name__, e)
        for item in w:
            print('  warning:', item.category.__name__, str(item.message))
PY

Repository: leeguooooo/claude-code-usage-bar

Length of output: 233


🏁 Script executed:

set -euo pipefail

python3 - <<'PY'
from pathlib import Path

path = Path("tests/test_daemon.py")
lines = path.read_text().splitlines()
for n in [1063, 1064, 1065, 1066, 1067, 1068, 1082, 1083, 1084, 1085]:
    print(f"{n}: {lines[n-1]!r}")
PY

printf '\n-- warning-mode probe --\n'
python3 -Wd - <<'PY'
s1 = "C:\Python\python.exe"
s2 = "C:\Windows\explorer.exe"
print(repr(s1))
print(repr(s2))
PY

Repository: leeguooooo/claude-code-usage-bar

Length of output: 819


Escape the Windows path literals.
"C:\Python\python.exe" and "C:\Windows\explorer.exe" use invalid escapes; write them as C:\\Python\\python.exe and C:\\Windows\\explorer.exe (or raw strings) to avoid parser warnings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_daemon.py` around lines 1063 - 1068, Update the CompletedProcess
mock in the daemon test to escape backslashes in the Windows executable path
literals, including the Python and explorer paths, using doubled backslashes or
raw strings while preserving the expected command output.


monkeypatch.setattr(sys, "platform", "win32")
monkeypatch.setattr(_sp, "run", fake_run)

assert _d._process_is_our_daemon(4242) is True
assert calls and calls[0] in ("powershell", "pwsh")


def test_windows_identity_false_when_pid_is_someone_else(monkeypatch):
import subprocess as _sp

monkeypatch.setattr(sys, "platform", "win32")
monkeypatch.setattr(
_sp, "run",
lambda cmd, **kw: _sp.CompletedProcess(
cmd, 0, stdout="C:\Windows\explorer.exe\n", stderr=""),
)

assert _d._process_is_our_daemon(4242) is False


def test_windows_identity_false_when_no_shell_available(monkeypatch):
"""No powershell and no pwsh — stay conservative, never SIGTERM."""
import subprocess as _sp

monkeypatch.setattr(sys, "platform", "win32")
monkeypatch.setattr(
_sp, "run",
lambda cmd, **kw: (_ for _ in ()).throw(FileNotFoundError(cmd[0])),
)

assert _d._process_is_our_daemon(4242) is False
Loading