feat(update): toggle auto-update via /update auto, /settings, and info - #133
Conversation
Add an in-app way to turn silent startup auto-updates on or off: - `/update auto on|off` (and `/update auto` reports the effective state) - an effective-state-aware row in the interactive `/settings` panel - auto-update status in `pythinker info` All surfaces show the effective state: an external override (PYTHINKER_CLI_NO_AUTO_UPDATE or a source checkout) is surfaced as the reason and renders the /settings row read-only, so the toggle is never a silent no-op. Extract the pure policy resolver into a shell-free `update_policy` module so `pythinker info` reports status without importing the shell stack, and add `create=False` to get_share_dir/get_config_file so the read-only info path no longer materializes ~/.pythinker as a side effect.
|
Warning Review limit reached
More reviews will be available in 41 minutes and 59 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThis PR adds a complete auto-update toggle system: a new policy module establishes environment/config/source-checkout precedence, the shell update logic is refactored to use that canonical module, and three user-facing interfaces (CLI ChangesAuto-update toggle feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with 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.
Inline comments:
In `@src/pythinker_code/cli/info.py`:
- Around line 42-43: The broad except in _auto_update_info currently swallows
all exceptions; change it to only catch expected errors (e.g.,
FileNotFoundError, json.JSONDecodeError, KeyError, or any domain-specific
Policy/Config error your code raises) and add a logged diagnostic (use the
module logger or logging.getLogger(__name__) and call logger.exception or
logger.error with the exception) before returning the unknowns; leave the
fallback return (None, None, None) but do not suppress unexpected
exceptions—either let them propagate or re-raise after logging if they are truly
unexpected.
In `@src/pythinker_code/update_policy.py`:
- Around line 36-41: The is_running_from_source_checkout() block currently
swallows all exceptions when importing pythinker_code; instead, limit the except
to the expected failure modes (e.g., ImportError, AttributeError,
FileNotFoundError, TypeError) and log the failure before returning False so
callers know why detection failed: update the try/except around the import and
Path resolution in is_running_from_source_checkout() to catch those specific
exceptions, call logging.getLogger(__name__).debug()/error(...) with the
exception details and context (including the exception message and that
import/path detection failed), and only return False for those caught cases
while allowing truly unexpected exceptions to propagate. Ensure you reference
the same symbols (is_running_from_source_checkout, pythinker_code, package_path)
when making the change.
In `@tests/ui_and_conv/test_update_auto_slash.py`:
- Around line 56-66: The test currently mocks internal helpers (load_config,
save_config, shell_slash.console.print) but should instead exercise observable
behavior: create a real temp config file containing config_for_save at
config_path (don’t mock load_config/save_config), call _run_update(app, "auto
on"), then reopen/read the persisted config file and assert its auto_update is
True, assert the in-memory/runtime config (app or returned config) reflects
auto_update True, and assert the user-facing message was printed by
capturing/patching shell_slash.console.print only for output verification; apply
the same replacement strategy to the other test blocks mentioned (lines 83-90,
102-124, 136-143).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: df5f8107-52d6-4448-8a71-0c8526ef7b60
⛔ Files ignored due to path filters (1)
docs/en/reference/slash-commands.mdis excluded by!docs/**
📒 Files selected for processing (13)
CHANGELOG.mdsrc/pythinker_code/cli/info.pysrc/pythinker_code/config.pysrc/pythinker_code/share.pysrc/pythinker_code/ui/shell/selectors/settings.pysrc/pythinker_code/ui/shell/slash.pysrc/pythinker_code/ui/shell/update.pysrc/pythinker_code/update_policy.pytests/cli/test_info.pytests/ui_and_conv/test_settings_selector.pytests/ui_and_conv/test_shell_update.pytests/ui_and_conv/test_silent_auto_update.pytests/ui_and_conv/test_update_auto_slash.py
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
… excepts - Repoint consumers (shell __init__, /update, /settings) and tests to import auto_update_enabled / auto_update_override_reason directly from the canonical `update_policy` module, and drop the unused re-export shims from ui/shell/update.py (resolves "unused import" findings). - Narrow the broad `except Exception` in `info._auto_update_info` to (OSError, ValueError, ImportError) and log the degraded path instead of silently swallowing (C03); ConfigError/pydantic errors are ValueError. - Narrow `update_policy.is_running_from_source_checkout` to (ImportError, AttributeError, OSError). - Strengthen the /update auto persist tests to assert real on-disk persistence via load_config rather than mock call-coupling.
Summary
Closes the gap surfaced while releasing 0.43.0: silent startup auto-updates (shipped in #130) could only be turned off by editing config or setting an env var — there was no in-app control. This adds three surfaces, all effective-state aware.
What's added
/update auto on|off— toggle silent startup auto-updates;/update auto(no arg) reports the effective state. Persists to theauto_updateconfig field; no reload (auto-update is consulted only at startup, so the running config is mirrored in place)./settingsrow — the interactive panel now exposes Auto-update. When an external override forces it off, the row is read-only and shows the reason, so the panel never offers a no-op toggle.pythinker info— reportsauto-update: <effective> (config auto_update=<...>[; <override>]), in both text and--json.All three resolve the effective state via the precedence in
auto_update_enabled: thePYTHINKER_CLI_NO_AUTO_UPDATEkill-switch and source-checkout detection outrank the config field and are surfaced as the reason.Design notes
auto_update_enabled,auto_update_override_reason, and the two primitives) into a new shell-freeupdate_policymodule.pythinker infoimports from there, so the lightweight CLI no longer pulls the shell stack (aiohttp/console).ui/shell/update.pyre-exports the names for back-compat.create=Falsetoget_share_dir()/get_config_file()so the read-onlyinfopath resolves the config path without materializing~/.pythinkeras a side effect (defaultcreate=Truepreserves all existing behavior). Guarded by a regression test.Tests
/update autopersist/no-op/usage/no-config-file/override-status (test_update_auto_slash.py)/settingsselector: live toggle, read-only under override, apply path (test_settings_selector.py)auto_update_override_reasonprecedence (test_silent_auto_update.py)pythinker infoline formatting + no share-dir side effect (test_info.py)auto_update_enabledprecedence test to the new module.make check-pythinker-codegreen (ruff + pyright); focused + regression suites pass (312+ tests).Notes
Targets
mainpost-0.43.0, so this ships in the next release (0.44.0).## UnreleasedCHANGELOG entry added; docs updated (slash-commands.md).Summary by CodeRabbit
/update auto [on|off]to toggle automatic background updates on or off./update autodisplays the current auto-update status and any active overrides.pythinker inforeports auto-update configuration and override status.