Files
deepagents/libs/code/deepagents_code/_paths.py
Mason Daugherty 81797312c7 feat(code): dcode doctor diagnostics command (#4148)
Add a `dcode doctor` command that prints install diagnostics (versions,
platform, update status, config locations), with `--json` support.

---

Adds a `dcode doctor` subcommand to `deepagents-code`, inspired by
`claude doctor`. It prints a grouped, tree-style summary of the install
(versions, platform, install method, path), update status, and on-disk
config locations, with a `--json` mode for machine-readable output.

It deliberately runs offline — the update section reads only the local
cache via `get_cached_update_available` and never contacts PyPI — and
reuses existing helpers (`detect_install_method`,
`is_auto_update_enabled`, editable-install detection, config paths) so
the output stays consistent with `dcode --version` and `dcode update`.
All rendered values go through `rich.markup.escape` with
`highlight=False`.

The new command is a leaf subcommand (like `update`), so it is
intentionally not registered in `_HELP_SPECS`. `ui.show_doctor_help`
backs `dcode doctor -h`, and the command is listed in the top-level help
usage.

Made by [Open
SWE](https://openswe.vercel.app/agents/74c251d7-548f-ee57-6e48-c76e1bd66a1a)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-22 18:51:41 -04:00

70 lines
2.2 KiB
Python

"""Filesystem-path classification shared by the diagnostic CLI commands.
`dcode doctor` and `dcode config path` both probe whether config locations
exist. `Path.exists()` can report `False` for `OSError` cases such as EACCES
when a parent directory denies traversal, so a bare `.exists()` can hide the
very permissions problem these commands should diagnose. `classify_path`
centralizes the guard and reports an unreadable path as a distinct state instead
of conflating it with "missing".
"""
from __future__ import annotations
import errno
import logging
from enum import StrEnum
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
logger = logging.getLogger(__name__)
_MISSING_ERRNOS = {errno.ENOENT, errno.ENOTDIR}
class PathState(StrEnum):
"""Whether a probed path exists, is absent, or could not be read.
A `StrEnum` so the value serializes directly to JSON without a custom
encoder.
"""
EXISTS = "exists"
"""The path is present on disk."""
MISSING = "missing"
"""The path is absent (and its parents are readable)."""
UNREADABLE = "unreadable"
"""Existence could not be determined because `Path.stat()` raised.
Typically EACCES when a parent directory denies traversal. Kept distinct
from `MISSING` so diagnostics can flag it as a genuine problem rather than
a not-yet-created path.
"""
def classify_path(path: Path) -> PathState:
"""Classify a path as existing, missing, or unreadable.
Args:
path: Filesystem path to probe.
Returns:
`PathState.EXISTS` for a present path, `PathState.MISSING` for expected
absent-path errors, and `PathState.UNREADABLE` when `Path.stat()`
raises another `OSError` (e.g. a parent directory denies traversal).
The error is logged at debug level so an unreadable path is never
silently indistinguishable from a missing one.
"""
try:
path.stat()
except OSError as exc:
if exc.errno in _MISSING_ERRNOS:
return PathState.MISSING
logger.debug("Could not stat %s", path, exc_info=True)
return PathState.UNREADABLE
else:
return PathState.EXISTS