feat: add dcode shell subcommand#5
Merged
Merged
Conversation
Open an interactive shell inside the project's running devcontainer with
behavior that mirrors VS Code's integrated terminal as closely as
possible.
Features
* New `dcode shell [path] [--shell EXECUTABLE]` subcommand
* Locates the running container by `devcontainer.local_folder` +
`devcontainer.config_file` Docker labels (the same labels VS Code
Dev Containers and `@devcontainers/cli` set on every container)
* Resolves the shell via VS Code's full settings priority chain
(workspace `.vscode/settings.json` > `devcontainer.json`
`customizations.vscode.settings` > host user-level `settings.json`),
with deep-merge of `terminal.integrated.profiles.linux` across layers
and `null`-as-delete semantics. Falls back to the container's login
shell from `getent passwd` (rejecting `nologin`/`false`), then
`/bin/bash`, then `/bin/sh`
* Forwards the SSH agent socket: detects via `docker inspect` env or
by probing `/tmp/vscode-ssh-auth*.sock` (newest, validated via
`test -S`) and exports it via `docker exec -e SSH_AUTH_SOCK=...`
* Honors `remoteUser` then `containerUser` from `devcontainer.json` for
the `docker exec -u` flag
* Probes the working directory with `test -d` before passing `-w`,
using `<workspaceFolder>/<rel_path>` for worktrees and falling back
cleanly when the path doesn't exist in the container
* Worktree-aware: uses the main repo path for container lookup so all
worktrees of a repo share one container (consistent with the URI
logic in `dcode <path>`)
* Cross-platform: macOS, Linux, and WSL (with Windows-side label
conversion for both `local_folder` and `config_file`)
* Replaces the Python process with `os.execvp("docker", ...)` so TTY,
Ctrl-C, and resize behavior all inherit naturally
* Distinct, actionable error messages for: no `devcontainer.json`,
container missing / stopped / ambiguous, Docker CLI/daemon
unavailable, and non-interactive (no TTY) contexts
* Interactive prompt to auto-start a stopped container (`docker start
<id>` after Y/n confirmation; declines or non-TTY contexts exit
cleanly with guidance)
Limitations (documented in README)
* GPG agent forwarding is not yet supported
* `remoteEnv` is not applied (warning emitted when present)
* `${env:VAR}` and similar substitution in profile args/env is not
resolved (warning emitted on first encounter)
* Devcontainer config inheritance (`extends`, image-label metadata,
Docker Compose service `user`) is not merged
* Requires an interactive terminal
Implementation
* New module `src/dcode/shell.py` (~640 lines) with `find_container`,
`resolve_terminal_profile`, `find_ssh_socket`, `probe_workdir`,
`detect_login_shell`, `run_shell`, plus `ContainerLookup` and
`ResolvedShell` dataclasses
* `src/dcode/cli.py`: new `shell` subparser with `shell_path`
positional (avoids namespace collision with the legacy top-level
`path`) and `--shell` flag (literal executable; whitespace rejected)
* `src/dcode/wsl.py`: `_get_windows_vscode_settings_path` renamed to
public `get_windows_vscode_settings_path` (alias preserved for
backward compatibility)
Tests
* New `tests/test_shell.py`: 76 tests across 9 classes
* `tests/test_cli.py`: +13 tests in `TestShellDispatch`
* All 245 tests pass; ruff clean
To open a folder literally named `shell`, use `dcode ./shell` (mirrors
the existing `dcode ./doctor` workaround).
Release-As: 0.5.0
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
dcode shell [path] [--shell EXECUTABLE]— opens an interactive shell inside the project's running devcontainer, mirroring VS Code's integrated terminal as closely as possible so things likegit pushand SSH key auth Just Work without re-configuring anything inside the container.Behavior
devcontainer.local_folder+devcontainer.config_fileDocker labels (the same labels VS Code's Dev Containers extension and@devcontainers/cliset on every container they create). Worktree-aware — uses the main repo path so all worktrees of a repo share one container..vscode/settings.json>devcontainer.jsoncustomizations.vscode.settings> host usersettings.json.terminal.integrated.profiles.linuxis deep-merged across layers;nulldeletes a profile (matches VS Code semantics). Falls back to the container's login shell fromgetent passwd(rejectingnologin/false), then/bin/bash, then/bin/sh.docker inspectenv or by probing/tmp/vscode-ssh-auth*.sock(newest, validated withtest -S) and forwards viadocker exec -e SSH_AUTH_SOCK=....remoteUserthencontainerUserfromdevcontainer.json; otherwise the container's image USER applies.<workspaceFolder>/<rel_path>withtest -dbefore passing-w; falls back cleanly if the path doesn't exist in the container.docker startthe container before exec'ing.os.execvp("docker", ...)so TTY, Ctrl-C, and resize all inherit naturally.local_folderandconfig_file).Error messages
Distinct, actionable stderr for: missing
devcontainer.json, container missing / stopped / ambiguous (multiple matches), Docker CLI/daemon unavailable, and non-interactive (no TTY) contexts.Limitations (documented in README)
These are intentional v1 deferrals — separate follow-ups will track each:
GPG_AGENT_INFOvs direct path vsgpgconf— wasn't verified upstream during research; forwarding incorrectly could silently break commit signing)remoteEnvfromdevcontainer.jsonis not applied to the exec session (a one-line warning is emitted when present)${env:VAR},${localEnv:VAR}) in terminal profile values is not resolvedextends, image-label metadata, Docker Compose serviceuser) is not mergedImplementation
src/dcode/shell.pyfind_container,resolve_terminal_profile,find_ssh_socket,probe_workdir,detect_login_shell,run_shell+ContainerLookup/ResolvedShelldataclassessrc/dcode/cli.pyshellsubparser withshell_pathpositional (avoids namespace collision with legacy top-levelpath) and--shellflag (literal executable; whitespace rejected viaparser.error)src/dcode/wsl.py_get_windows_vscode_settings_pathrenamed to publicget_windows_vscode_settings_path(alias preserved for backward-compat)tests/test_shell.pytests/test_cli.pyTestShellDispatchREADME.md### dcode shellsection under## 🛠 CommandsVerification
uv run ruff check— cleanuv run pytest— 245 passed, 0 failed (was 156 before this PR; +89 new tests)Release
Release-As: 0.5.0footer in the commit overrides the calculated version (would otherwise be 0.4.4 due tobump-patch-for-minor-pre-major: true). Adding a meaningful new subcommand warrants a minor bump.Try it
Container stopped? You'll be prompted to start it. SSH key not working? Open the folder in VS Code first to start the agent relay, then re-run
dcode shell.