Skip to content

Add agent command group for non-interactive CLI control - #98

Open
Joacohbc wants to merge 3 commits into
mainfrom
claude/tdah-446-implementation-plan-yesjoi
Open

Add agent command group for non-interactive CLI control#98
Joacohbc wants to merge 3 commits into
mainfrom
claude/tdah-446-implementation-plan-yesjoi

Conversation

@Joacohbc

Copy link
Copy Markdown
Owner

Summary

Introduces a new agent command group that provides a non-interactive facade over the CLI for AI agents and automated tools. The group includes eight subcommands (cli-info, create, connect, exec, forward, copy, list, clean) that wrap or reuse existing human commands with agent-appropriate defaults (no prompts, required flags instead of interactive selection).

Key Changes

  • New service layer (cli/internal/service/agent.go):

    • AgentService.Info() assembles the live catalogue (modules, services, profiles, skills, scripts, assets, paths) as structured data for agents to read before composing commands
    • AgentService.Clean() combines destroy + image cleanup into a single operation
    • Comprehensive JSON-serializable info types (AgentInfo, AgentModuleInfo, AgentServiceInfo, etc.) that expose the CLI's capabilities
  • New command group (cli/internal/cli/commands/agent.go):

    • Eight subcommands with agent-appropriate behavior: non-interactive by default, required arguments instead of prompts
    • Five subcommands reuse existing handlers (connectssh, execshell, forwardport-forward, copycopy, listls) via extracted flag helpers
    • Two new subcommands: cli-info (catalogue dump) and create (generate + build + up in one call)
    • agentDefaults() helper applies agent-specific flag defaults without overriding explicit user values
  • New info command (cli/internal/cli/commands/agent_info.go):

    • Renders the catalogue as human-readable text or JSON
    • Separates selectable vs. automatic modules, marks remote profiles, documents script lifecycle and asset availability
  • New create command (cli/internal/cli/commands/agent_create.go):

    • Combines generation, build, and stack startup
    • Forces --force and --build by default; hides backwards-compatibility flags from --help
    • Adds --no-up to stop after build if needed
  • Flag helper extraction:

    • Extracted flag registration from ssh, shell, port-forward, copy, ls into reusable helpers (addSshFlags, addShellFlags, etc.)
    • Ensures flags are registered once and shared between human and agent commands
  • Domain additions:

    • IsLocalImage() in domain/images.go to identify locally-built images (used by agent clean)
    • CustomScriptNamePattern export in domain/profile.go for agent info
  • Documentation updates:

    • Updated skill markdown (cli/internal/infra/assets/skill-devcontainer-cli.md and skills/devcontainer-cli/SKILL.md) to introduce the agent command group as the primary interface
    • Updated AGENTS.md with architecture notes on the facade pattern

Testing

  • Comprehensive test coverage in cli/internal/service/agent_test.go and cli/internal/cli/commands/agent_test.go
  • Validates catalogue completeness, JSON serialization, module/service selectability rules, path conventions
  • Tests agent clean flow with fake Docker runner
  • Ensures all eight subcommands are registered, documented, and default to non-interactive
  • Verifies flag helpers preserve original command behavior

https://claude.ai/code/session_01AmuL457oWuwXvrM7dRNun4

claude added 3 commits August 14, 2026 03:32
Adds `devcontainer-cli agent`, an agent-facing facade over the CLI: a small
command set that never opens a wizard and never waits for an answer, so an
unattended session cannot hang on a TUI.

- `agent cli-info [--json]` prints the live catalogue — modules, services,
  profiles, skills, how to add custom scripts, copyable assets and the path
  conventions — so an agent composes a create command from the ids this binary
  actually has instead of ones hardcoded in a document that can drift.
- `agent create` is generate + build + up with prompting, overwriting and the
  build question already decided; `--no-up` stops after the build.
- `agent clean` composes destroy with the removal of the image built for the
  project. A pulled image is left alone (other projects on the same profile
  share it) and an image already gone is not an error. `--all` widens the
  sweep past this project.
- `connect`, `exec`, `forward`, `copy` and `list` reuse the existing handlers;
  `exec` requires a command, since `shell` without one opens a login shell that
  an agent can only hang on, and `list --json` returns structured entries.

The flag registration of ssh/shell/port-forward/copy/ls moves into shared
helpers so both spellings of each command are described in exactly one place,
and destroy's target construction moves into destroyTargetFor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmuL457oWuwXvrM7dRNun4
Rewrites the host-side skill so an agent reaching for this CLI finds the
`agent` group first and the plain commands as a documented escape hatch, and
points it at `agent cli-info` as the authority on module/service/profile/skill
ids — the list in the markdown is a summary that can lag, the catalogue dump
cannot.

Fixes an id the document had wrong along the way: `zellij` is an always-on
module, not something to pass to `--with`. The command that reads the catalogue
is what surfaced it.

Also documents the group in the README, in the AGENTS.md command table, and in
a new architecture section covering the four load-bearing properties: the
PreRunE defaults that only apply to unset flags, why `agent exec` refuses an
empty command, why `agent create` registers the full generate flag set, and how
`agent clean` resolves the project image before the destroy drops the catalog
entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmuL457oWuwXvrM7dRNun4
A critical read of the agent facade found that the three failure modes a model
actually hits with this CLI were all untouched by it.

**agent exec ran as root.** There is no USER in the Dockerfile and no user: in
the compose file, and shell leaves --user unset when given a command, so every
`agent exec -- <cmd>` that wrote into the workspace left root-owned files in
the user's real checkout on the host — which they then cannot edit without
sudo. It now defaults to devuser through the existing agentDefaults, so an
explicit --user still wins.

This deliberately inverts shell's own rule, which leaves --user alone so it
keeps working against a database container that has no devuser. Those now fail
with `unable to find user devuser` until --user names the one they have. A loud
failure beats a silently corrupted checkout. shell itself is unchanged, and a
test pins that the two stay different on purpose.

**A failed build exited 0.** saveAndPostProcess downgraded a failed
`docker compose build` to a warning and returned nil, so generation reported
success with no image; `agent create` then handed a caller a project it could
not use, with the real cause scrolled away. It returns the error now — for the
human command too, since a failed build is not a success for a script either.

**Eight recovery hints pointed at the wizard.** Messages like "container is not
running. Run 'devcontainer-cli'" sent a stuck non-interactive caller at the one
command that hangs it. They now name up/start/agent create, and a test walks the
source so the next message added cannot reintroduce it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmuL457oWuwXvrM7dRNun4
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.

2 participants