Add aqua doctor config diagnostic/repair tool#118
Conversation
Startup no longer writes to ~/.aqua/config.json — enabled_tools becomes a sparse map where an absent key means "use the shipped default", so shipping a new default never clobbers a user's explicit choice and the file no longer gets re-polluted with defaults on every run. - `aqua doctor` / MCP tool `doctor` diagnose (default) and, with `--fix` / fix=true, repair the config: drop orphaned enabled_tools keys (the source of the "Unknown tool" startup warnings), prune entries that match the current shipped default, and remove unknown top-level keys - doctor reads the config as raw JSON so it can repair a file that would otherwise break config loading entirely - startup warnings now point at `aqua doctor --fix` instead of asking the user to hand-edit config.json - Config.from_dict drops unknown top-level keys instead of crashing, and load_config_with_merge survives an unreadable/corrupt config file — both used to take down the whole CLI/MCP server before doctor could run - doctor is always registered on the CLI (like `serve`) and gated on MCP like every other tool; the MCP tool coerces a stringly-typed fix argument so a non-compliant client can't accidentally trigger a write
develop's "Enable SideSwap tools by default" emptied _SHIPPED_DISABLED, so no tool ships disabled-by-default anymore and the old helper that picked a real disabled-by-default tool had nothing to find. Force a tool's default to False via monkeypatch instead, so the test no longer depends on which tools (if any) ship disabled.
No logic changes — just tightening module/function docstrings and multi-line comments introduced by the doctor feature down to their essential what+why, per the repo's concise-documentation convention.
- Generalize cli/output.py's run_tool to return its result, so cli/doctor.py no longer hand-rolls the error envelope - Hoist KNOWN_CONFIG_KEYS to storage.py as the single source of truth, shared by Config.from_dict and doctor - Drop the vestigial _merge_with_defaults helper in features.py now that its changed flag has no consumer - Have doctor's --fix path reuse the diagnosis findings instead of re-deriving which keys are removable - Fix a stale stat() call and doc/schema wording that still described the crash Config.from_dict no longer has
coelhogonzalo
left a comment
There was a problem hiding this comment.
LGTM. I generated a PR with changes I found relevant after review
Feel free to edit it but no issue is big enough to block this being merged.
marinate305
left a comment
There was a problem hiding this comment.
This directly solves the config warning noise we were hitting in testing — the aqua doctor --fix workflow is a much better UX than asking users to hand-edit JSON. The crash path fixes are important too. coelhogonzalo's follow-up in #121 looks non-blocking. Approving.
TomasCast
left a comment
There was a problem hiding this comment.
Looks good, just a minor comment that may be considered
| # Never crash on a broken config — aqua doctor (which fixes it) must still run. | ||
| try: | ||
| config = storage.load_config() | ||
| except (OSError, ValueError) as exc: # ValueError ⊇ json.JSONDecodeError |
There was a problem hiding this comment.
If a JSON root that is not an object ([], null, "str") will cause Config.from_dict to raise a TypeError, that is not being catched on this except clause.
Purpose
Adds an
aqua doctor(CLI + MCP) command that diagnoses and repairs~/.aqua/config.json, so future feature adds/removals stop greeting users with a wall ofWARNING: Unknown tool in enabled_tools: '...'on every single run.Description
Config drift has been quietly annoying users since we started shipping and retiring tools — every release meant another pile of orphaned keys and a startup log nobody could self-serve fix (short of manually editing JSON). This PR makes the config sparse: only genuine user overrides get persisted, everything else just inherits the shipped default.
Main Changes
aqua doctor [--fix]/ MCP tooldoctor(fix=false)— diagnoses by default, repairs with--fix/fix=true: drops orphaned tool keys (the actual source of the warnings), prunes entries that match the current shipped default, and removes unknown top-level keys.load_config_with_mergemerges shipped defaults in memory only and never writes to disk anymore.doctorcould even run to fix them.aqua doctor --fixinstead of asking users to hand-editconfig.json.tests/test_doctor.pyplus CLI/MCP/server coverage, including aTOOL_SCHEMAS↔TOOLSdrift guard.Checklist
Screenshots