Skip to content

feat: add dcode doctor and dcode update subcommands#3

Merged
rosstaco merged 7 commits into
mainfrom
feat/doctor-and-update
Apr 30, 2026
Merged

feat: add dcode doctor and dcode update subcommands#3
rosstaco merged 7 commits into
mainfrom
feat/doctor-and-update

Conversation

@rosstaco
Copy link
Copy Markdown
Owner

Summary

Adds two new subcommands to dcode:

  • dcode doctor [PATH] — diagnoses the local environment (editor, devcontainer extension, container runtime, git, WSL setup, target devcontainer, dcode version, install method) and prints a "what would dcode <PATH> do" plan summary. Read-only — never patches settings.json or spawns the editor. Exit code: 0 if no failing checks, 1 otherwise. Warnings do not fail.
  • dcode update — shells out to uv tool upgrade dcode. Detects when uv is missing or dcode wasn't installed via uv tool and prints actionable hints instead of forwarding the raw error.
  • dcode update --check — checks GitHub Releases for a newer version without installing. Exit 0 if up-to-date or ahead (dev build), 1 if behind, 2 on network error.

Output is rendered with rich — colored status glyphs, per-section panels, and a clickable release URL on supporting terminals. Auto-disables color on non-TTY and when NO_COLOR=1.

Commits

# Type Subject
1 refactor split cli into core and wsl modules
2 refactor split test_cli into test_core, test_wsl, conftest
3 feat add update subcommand
4 feat add doctor subcommand
5 docs document doctor and update subcommands
6 feat prettify doctor and update output with rich
7 feat render each doctor section in its own panel

The two refactors land first as pure code-moves so the feature commits read against a clean per-module structure. Each commit passes the full test suite individually.

Module layout (new)

src/dcode/
├── __init__.py        (unchanged)
├── __main__.py        (unchanged)
├── cli.py             (argparse + subcommand dispatch)
├── core.py            (run_dcode, find_devcontainer, resolve_worktree, build_uri, …)
├── wsl.py             (is_wsl, build_uri_wsl, settings.json patcher, …)
├── doctor.py          (NEW — 13 checks + plan-summary + driver)
├── update.py          (NEW — uv tool detection + upgrade + --check)
├── version_check.py   (NEW — GitHub Releases API, version comparator)
└── _rich.py           (NEW — Console singleton + status-glyph styles)

New runtime dependency

  • rich >= 13.0 (added to pyproject.toml). Pure-Python, widely used. Auto-disables color on non-TTY / NO_COLOR.

No other new deps. The GitHub API call uses stdlib urllib.request.

Tests

  • Baseline: 39 tests
  • After this PR: 156 tests (+117)
  • All checks return (status, message, hint) tuples → 95% of doctor tests assert on the data, not the rendering, so they're robust against future UI changes.
  • No real network or subprocess in the suite — everything mocked via unittest.mock.patch against urllib.request.urlopen, subprocess.run, shutil.which.
  • Two new tests assert that NO_COLOR=1 produces zero ANSI escape sequences in doctor and update --check.

Verification on this machine

  • uv run pytest -q156 passed
  • uv run ruff checkAll checks passed!
  • python -m dcode doctor → 7 ok / 2 warn / 0 fail (only code-insiders and the absent .devcontainer/ warn — both expected on this repo). Plan-summary prints. Exit 0.
  • python -m dcode doctor /tmp → confirms [PATH] argument routes correctly.
  • python -m dcode update --check → correctly reports the local hatch-vcs dev version as ahead of v0.4.2 (the dev-build vs released-version case is covered explicitly in the version comparator and unit-tested).

Release-please / version bump

Multiple feat: commits → release-please should propose a minor bump from 0.4.2 → 0.5.0. Conventional Commit messages are intact for changelog generation.

Notable design choices

  • Per-check functions return tuples, not direct prints. The renderer is the only thing that touches stderr, which made the 53 doctor tests trivial and decoupled rendering from logic.
  • argparse subcommand routing — Python 3.13 argparse misparses dcode ./somepath as an invalid subcommand when subparsers are mixed with a top-level positional. Solved with a _looks_like_subcommand peek over sys.argv[1:] against a _SUBCOMMANDS = ("update", "doctor") tuple. The escape hatch dcode ./doctor (open a folder literally named doctor) works as documented in the README.
  • doctor is read-only — even on WSL, where the existing dcode <path> flow auto-patches settings.json, doctor only reports what would change.
  • Plan-summary smart editor fallback — if only code-insiders is installed, the plan replays as dcode -i instead of the default dcode path. If neither editor exists, the plan-summary section says "no editor available" and is omitted from the exit-code calculation.

Acceptance against the original plan

All criteria from project/plans/2026-04-29-doctor-update/plan.md §10 satisfied. Real-output excerpts captured in project/plans/2026-04-29-doctor-update/implementation-notes.md under "Commit 3/4 validation" and "Rich UI validation" sections.

rosstaco and others added 7 commits April 29, 2026 19:09
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rosstaco rosstaco merged commit 34a5f1e into main Apr 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant