Agent-facing update detection: cached check + recommendation#2
Conversation
Lets the /autosentry skill (Claude, Cursor, Codex, OpenCode, Zed, Gemini)
detect a newer release on every invocation and recommend the upgrade
command, without hammering PyPI or upgrading unprompted.
updater.py:
- check() caches the PyPI "latest version" lookup on disk (XDG-aware,
24h TTL); use_cache=False forces a live query. Best-effort — a corrupt
or unwritable cache is just a miss.
- detect_install_method() recognizes Homebrew (Cellar) installs and
perform_update() runs `brew upgrade autosentry` for them, falling back
to install.sh only for --pre / pinned --version.
cli update command:
- --check now exits 0 and prints a recommendation ("→ update available —
run autosentry update", or `brew upgrade autosentry` for Homebrew) so
agent triage chains don't abort on a non-zero exit.
- --json emits {"current","latest","is_outdated"}; --no-cache forces live.
skills: AGENTS.md and the per-tool wrappers run `autosentry update --check`
during triage and surface the recommendation.
Tests: caching (hit/miss/ttl/allow_pre/corrupt), brew detection, and the
CLI --check/--json behavior. Full suite green (206).
Note: src/autosentry/cli.py is dead code — shadowed by the cli/ package —
and is untouched here; flagging separately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR adds disk-caching for PyPI update checks, Homebrew install detection and a brew upgrade path, extends the CLI with ChangesAgent-driven update detection with caching
Sequence DiagramsequenceDiagram
participant Agent
participant CLI as autosentry update --check
participant Updater
participant Cache as Disk Cache
participant PyPI
Agent->>CLI: invoke --check
CLI->>Updater: check(use_cache=True, ttl=86400)
Updater->>Cache: read cached result
alt Cache hit & TTL valid
Cache-->>Updater: return cached UpdateCheck
else Cache miss or stale
Updater->>PyPI: fetch_latest_version()
PyPI-->>Updater: latest version
Updater->>Cache: write result (best-effort)
Cache-->>Updater: written
end
Updater-->>CLI: UpdateCheck { current, latest, is_outdated }
alt --json output
CLI-->>Agent: JSON { current, latest, is_outdated }
else User-facing
alt is_outdated
CLI-->>Agent: "→ update available\nRun: autosentry update (or brew upgrade)"
else up-to-date
CLI-->>Agent: (silent)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/autosentry/cli/commands/update.py (1)
22-27: ⚡ Quick winConsider adding test coverage for the Homebrew recommendation path.
The
_recommended_command()helper and its integration into the check-mode output work correctly, but the current tests don't verify the Homebrew-specific recommendation ("brew upgrade autosentry"). Consider adding a test that mocksdetect_install_methodto return"brew"and asserts the output contains"brew upgrade autosentry".🧪 Example test to add
Add this to
tests/test_cli.py:def test_update_check_outdated_recommends_brew_when_homebrew_install( runner: CliRunner, monkeypatch ): from autosentry.updater import UpdateCheck def fake_check(**_kwargs): return UpdateCheck(current="0.1.0", latest="0.2.0", is_outdated=True) def fake_detect(): return "brew" monkeypatch.setattr("autosentry.cli.commands.update.updater_check", fake_check) monkeypatch.setattr("autosentry.cli.commands.update.detect_install_method", fake_detect) result = runner.invoke(app, ["update", "--check"]) assert result.exit_code == 0 out = _plain(result.output) assert "brew upgrade autosentry" in outAlso applies to: 82-89
🤖 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/autosentry/cli/commands/update.py` around lines 22 - 27, Add a unit test that asserts the Homebrew-specific recommendation by mocking detect_install_method to return "brew" and the updater_check to report an outdated version; specifically, add a test (e.g., test_update_check_outdated_recommends_brew_when_homebrew_install) in tests/test_cli.py that monkeypatches autosentry.cli.commands.update.detect_install_method to return "brew" and autosentry.cli.commands.update.updater_check (or the updater_check import used in that module) to return an UpdateCheck with is_outdated=True, then invoke the CLI (runner.invoke(app, ["update","--check"])) and assert the output contains "brew upgrade autosentry" to cover the _recommended_command() brew path.tests/test_updater.py (1)
111-117: ⚡ Quick winAdd regression coverage for valid-JSON wrong-shape cache content.
Current corruption coverage misses cases like
[]/null/"str"that are valid JSON but invalid schema.Suggested test addition
def test_check_treats_corrupt_cache_as_miss(tmp_path, monkeypatch): @@ monkeypatch.setattr(updater, "fetch_latest_version", _counting_fetch([])) assert check().latest == "9.9.9" + + +def test_check_treats_non_object_cache_as_miss(tmp_path, monkeypatch): + monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path)) + cache = tmp_path / "autosentry" / "update-check.json" + cache.parent.mkdir(parents=True) + cache.write_text("[]", encoding="utf-8") + monkeypatch.setattr(updater, "fetch_latest_version", _counting_fetch([])) + assert check().latest == "9.9.9"🤖 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_updater.py` around lines 111 - 117, The existing test test_check_treats_corrupt_cache_as_miss only covers syntactically invalid JSON; extend it to also cover valid-JSON but wrong-shape cache contents (e.g. [], null, "str", and an object missing expected fields) by parametrizing the test to write each of those payloads into the same cache path, then call check() and assert it falls back to the network result (with monkeypatched updater.fetch_latest_version as in the test) so check().latest == "9.9.9"; keep using the same helpers (tmp_path, monkeypatch) and the same cache path logic to locate the code that reads and validates the cache in check().
🤖 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/autosentry/updater.py`:
- Around line 132-137: The cache loading code in function check() assumes the
JSON payload is a dict and will raise AttributeError for valid JSON types like
lists or strings; after json.loads(...) verify that the loaded value (data) is
an instance of dict and treat non-dict payloads as a cache miss by returning
None so subsequent calls to data.get don't fail; update the branch around
_cache_path(), json.loads, and the following use of data.get (referencing
_cache_path() and the local variable data and the check() function) to perform
an isinstance(data, dict) guard and return None when it is not a dict.
---
Nitpick comments:
In `@src/autosentry/cli/commands/update.py`:
- Around line 22-27: Add a unit test that asserts the Homebrew-specific
recommendation by mocking detect_install_method to return "brew" and the
updater_check to report an outdated version; specifically, add a test (e.g.,
test_update_check_outdated_recommends_brew_when_homebrew_install) in
tests/test_cli.py that monkeypatches
autosentry.cli.commands.update.detect_install_method to return "brew" and
autosentry.cli.commands.update.updater_check (or the updater_check import used
in that module) to return an UpdateCheck with is_outdated=True, then invoke the
CLI (runner.invoke(app, ["update","--check"])) and assert the output contains
"brew upgrade autosentry" to cover the _recommended_command() brew path.
In `@tests/test_updater.py`:
- Around line 111-117: The existing test test_check_treats_corrupt_cache_as_miss
only covers syntactically invalid JSON; extend it to also cover valid-JSON but
wrong-shape cache contents (e.g. [], null, "str", and an object missing expected
fields) by parametrizing the test to write each of those payloads into the same
cache path, then call check() and assert it falls back to the network result
(with monkeypatched updater.fetch_latest_version as in the test) so
check().latest == "9.9.9"; keep using the same helpers (tmp_path, monkeypatch)
and the same cache path logic to locate the code that reads and validates the
cache in check().
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ad52e412-bbea-4892-aaeb-6691ffc76417
📒 Files selected for processing (13)
CHANGELOG.mdREADME.mdsrc/autosentry/cli/commands/update.pysrc/autosentry/templates/skills/AGENTS.mdsrc/autosentry/templates/skills/claude.mdsrc/autosentry/templates/skills/codex.mdsrc/autosentry/templates/skills/cursor.mdsrc/autosentry/templates/skills/gemini.tomlsrc/autosentry/templates/skills/opencode.mdsrc/autosentry/templates/skills/zed.mdsrc/autosentry/updater.pytests/test_cli.pytests/test_updater.py
_read_cache only guarded JSON parse errors and key mismatches; valid JSON of the wrong shape (e.g. `[]`) would hit data.get() and raise AttributeError, crashing check() instead of missing gracefully. Add an isinstance(data, dict) guard and a regression test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lets the
/autosentryskill detect a newer release and recommend the upgrade command across Claude, Cursor, Codex, OpenCode, Zed, and Gemini — inspired by GSD's idempotent self-update, adapted to autosentry's skill model.What
updater.pycheck()caches the PyPI "latest version" on disk (~/.cache/autosentry, XDG-aware, 24h TTL).use_cache=Falseforces a live query. Cache is best-effort — corrupt/unwritable → cache miss, never an error.brew upgrade autosentryfor them (falls back toinstall.shonly for--pre/pinned--version, which brew can't honor against the tap).autosentry updatecommand--checknow exits 0 and prints a recommendation (→ update available — run autosentry update, orbrew upgrade autosentry) so agent triage chains don't abort on a non-zero exit.--json→{"current","latest","is_outdated"};--no-cacheforces a live query.Skills —
AGENTS.mdand the per-tool wrappers runautosentry update --checkduring triage and surface the recommendation, explicitly without upgrading unprompted (it can restart the CLI mid-session).Tests
New coverage for caching (hit / miss / TTL expiry /
allow_prekeying / corrupt cache), Homebrew detection, and the CLI--check/--jsonoutput. Full suite green (206 passed), ruff + pyrefly clean.Heads-up (separate from this PR)
src/autosentry/cli.pyis dead code — it's shadowed by thecli/package that's actually imported, which is why it only hadinit/run/status/incidents/dispatcherwhile the real CLI hasupdate,skills,doctor, etc. Left untouched here; worth deleting in a follow-up.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
autosentry update --checknow caches PyPI lookups for a day (use--no-cacheto bypass), always exits 0, and prints a recommendation if an update is available.--jsonproduces machine-readable output (current/latest/is_outdated).Documentation
Tests