# Devcontainer Auto-Discovery **Package:** `cleveragents.resource.handlers.discovery` **Introduced:** v3.8.0 (issue #2615) This module guide covers the devcontainer auto-discovery system, which automatically detects `devcontainer.json` configurations when a `git-checkout` or `fs-directory` resource is linked to a project. It supports both root-level configurations and named configurations for monorepo projects. --- ## Purpose When a `git-checkout` or `fs-directory` resource is registered, the `discover_devcontainers()` function scans the resource location for `devcontainer.json` files. Each valid configuration is returned as a `DevcontainerDiscoveryResult`, which the resource registry uses to create child `devcontainer-instance` resources automatically. --- ## Scan Paths The scanner checks three categories of paths: | Category | Path | `config_name` | |----------|------|---------------| | Root-level (default) | `.devcontainer/devcontainer.json` | `None` | | Root-level (flat) | `.devcontainer.json` | `None` | | Named configuration | `.devcontainer//devcontainer.json` | `""` | Named configurations are discovered by iterating one subdirectory level inside `.devcontainer/`. Each subdirectory that contains a `devcontainer.json` produces a separate result with `config_name` set to the subdirectory name (e.g. `"api"`, `"frontend"`). --- ## Key Classes and Functions ### `DevcontainerDiscoveryResult` ```python from cleveragents.resource.handlers.discovery import DevcontainerDiscoveryResult result = DevcontainerDiscoveryResult( config_path=Path("/workspace/.devcontainer/api/devcontainer.json"), config_data={"name": "API Dev Container", "image": "mcr.microsoft.com/devcontainers/python:3.12"}, parent_location="/workspace", config_name="api", ) ``` | Attribute | Type | Description | |-----------|------|-------------| | `config_path` | `Path` | Absolute path to the `devcontainer.json` file | | `config_data` | `dict[str, object]` | Parsed JSON content of the devcontainer config | | `parent_location` | `str` | Filesystem path of the parent resource | | `config_name` | `str \| None` | Named configuration identifier, or `None` for root-level configs | --- ### `discover_devcontainers` ```python from cleveragents.resource.handlers.discovery import discover_devcontainers results = discover_devcontainers( resource_location="/workspace", resource_type="git-checkout", ) for result in results: print(result.config_name, result.config_path) ``` Scans a resource location for all valid devcontainer configurations. Invalid JSON files are skipped with a warning log rather than raising. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `resource_location` | `str` | Filesystem path of the parent resource | | `resource_type` | `str` | Type name of the parent resource | **Returns:** `list[DevcontainerDiscoveryResult]` — one entry per valid config. **Raises:** - `ValueError` — if `resource_location` is empty or `resource_type` is empty/blank. --- ### `is_trigger_type` ```python from cleveragents.resource.handlers.discovery import is_trigger_type is_trigger_type("git-checkout") # True is_trigger_type("sqlite") # False ``` Returns `True` if the given resource type triggers devcontainer auto-discovery. Currently only `"git-checkout"` and `"fs-directory"` are trigger types. --- ## Monorepo Example Given a monorepo with the following layout: ``` /workspace/ ├── .devcontainer/ │ ├── api/ │ │ └── devcontainer.json → config_name="api" │ └── frontend/ │ └── devcontainer.json → config_name="frontend" └── src/ ``` `discover_devcontainers("/workspace", "git-checkout")` returns two results: ```python results = discover_devcontainers("/workspace", "git-checkout") # results[0].config_name == "api" # results[1].config_name == "frontend" ``` Each result produces a distinct `devcontainer-instance` resource in the registry, allowing plans to target individual containers by name. --- ## Root-Level Example For a single-container project: ``` /workspace/ ├── .devcontainer/ │ └── devcontainer.json → config_name=None └── src/ ``` ```python results = discover_devcontainers("/workspace", "git-checkout") # results[0].config_name is None ``` Root-level configs retain `config_name=None` for full backward compatibility. --- ## Gotchas - **Non-trigger types return an empty list.** Calling `discover_devcontainers()` with a resource type that is not `"git-checkout"` or `"fs-directory"` returns `[]` immediately without scanning the filesystem. - **Invalid JSON is silently skipped.** Files that cannot be parsed as JSON objects are logged at `WARNING` level and excluded from results. - **Only one subdirectory level is scanned.** Named configurations must be directly inside `.devcontainer//devcontainer.json`. Deeper nesting is not supported. - **Sorted order.** Named configurations are returned in lexicographic order of their subdirectory names (via `sorted(dc_dir.iterdir())`). --- ## Related Documentation - [Resource System API](../api/resource.md) — `ResourceHandler` protocol and built-in handlers - [ADR-043 Devcontainer Integration](../adr/ADR-043-devcontainer-integration.md) — design rationale - [Resource System](../api/resource.md#devcontainerhandler) — `DevcontainerHandler` CRUD operations