feat(cli): volunteer unwired status line via stderr hint (#188) - #197
Merged
Merged
Conversation
luongnv89
deleted the
feat/188-context-stats-never-volunteers-that-the
branch
September 2, 2026 20:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #188
Summary
The
context-statsCLI now volunteers — on its own, once per invocation — that the Claude Code status line is installed but unwired: when~/.claude/settings.jsoncarries no effectivestatusLineblock, everycontext-statscommand prints a single line to stderr (! statusLine is not wired into ~/.claude/settings.json — the status line will never run. Fix: context-stats doctor --fix). The hint is stderr-only, never raises, never changes the exit code, and leavesgraph/export/reportstdout byte-identical. It is suppressed when the wiring is healthy, or on demand via a newsuppress_setup_hintconfig key and theCONTEXT_STATS_SUPPRESS_SETUP_HINTenv var. This closes the #186 residual gap for upgraders, who never re-read install docs and had no signal that the status line would never run.Approach
Option 2 — Balanced: extract the wiring predicate, hook it into the CLI, mirror the config key across both copies.
The statusLine check doctor already performs was extracted into a cheap, subprocess-free
_effective_statusline()predicate incli/doctor.py(check_settingsnow delegates to it — one source of truth for "is the status line wired?"). The CLI entrymain()incli/context_stats.pycalls_maybe_warn_setup_hint()betweenparse_args()and action dispatch: lazy-imports the predicate, suppresses on thesuppress_setup_hintkey or env var, and writes exactly one stderr line when unwired. Per the CLAUDE.md sync-point parity contract, the key is mirrored into the standaloneread_config()and both conf templates (kept byte-identical bytest_config_colors.py).Decision Record
install.sh's jq-gated block, which every pip/uv install and every upgrade skips; wheels cannot run post-install hooks;claude-statuslineonly runs once wiring already exists. The one process guaranteed to run while unwired is thecontext-statsCLI, whose every invocation was silent — so the installed-but-inactive state was never volunteered, anddoctoronly helped users who already knew to run it.Configand the standaloneread_configthat the parity contract pushes against, costing more in carve-out documentation than the mirror saves; Option 3 — overbuilds beyond AC#1–6: touching install.sh/check-install.sh/README widens the blast radius with an untested shell surface while the startup hook already puts the message on the one process guaranteed to run while unwired.Config.load()can emit a[statusline] warning:stderr line on commands that never otherwise loaded config — never raises, never changes exit code.Analyzed at:
feat/188-context-stats-never-volunteers-that-the @ 6c613cd(2026-09-02)Changes
src/claude_statusline/cli/doctor.py_effective_statusline()(pure file-reads, never raises) from the effective-block resolution;check_settingsdelegates to itsrc/claude_statusline/cli/context_stats.py_setup_hint_suppressed()(env var + read-only conf key check),_maybe_warn_setup_hint(args)(stderr-only, never-raise, help-guarded), called inmain()betweenparse_args()and dispatchsrc/claude_statusline/core/config.pysuppress_setup_hint: boolfield + tolerant parse +to_dict+_MINIMAL_CONFIG_FALLBACKscripts/statusline.pyread_config()defaults +_BOOL_CONFIG_KEYS(renderer parses, never branches)src/claude_statusline/data/statusline.conf.default+examples/statusline.confsuppress_setup_hintcomment block (byte-identical pair)docs/configuration.mdCONTEXT_STATS_SUPPRESS_SETUP_HINTenv vardocs/troubleshooting.mdtests/python/test_cli_entry.pyTestSetupHint: 23 tests (unwired→hint, wired→silent, missing/malformed→silent, suppression via key + env, no hint on help/version, byte-identical stdout per action, no conf-file side effect)tests/python/test_doctor.pyTestEffectiveStatusline: 7 predicate tests on thefake_homefixturetests/python/test_parity.pysuppress_setup_hintin the config-parsing parity setTest Results
ruff check .andmypy src scriptscleanAcceptance Criteria Verification
test_hint_shows_when_unwiredetc. —_maybe_warn_setup_hint()writes exactly one line tosys.stderr; exercised for graph/export/report/sessions/doctortest_no_hint_when_wired—_effective_statusline()returns a block → silentoutunchanged while hint appears onerr(graph/export/report no-hint + hint cases); existing golden/export/report tests still greentest_*_missing*/test_*_malformed*/test_*_unreadable*— silent + exit 0; broken HOME never raisestest_suppressed_by_conf/test_suppressed_by_env;docs/configuration.md+docs/troubleshooting.mdupdatedTestSetupHint(23) +TestEffectiveStatusline(7) cover the full matrix