Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .devcontainer/agents.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# The agents of this repository (the agents Feature): task agents -- <name> inside,
# task devcontainer:agent -- <name> from the workstation; task agents:list shows them.
agents:
# The host session, driven from the workstation and from the phone. On-demand with
# --remote-control until a background session is shown to be reachable through Remote
# Control (the agents proposal, decision D2 and fact 5); then it becomes resident.
- name: host
cli: claude
kind: on-demand
folder: .
args: [--remote-control]
3 changes: 3 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
},
"./features/gh": {},
"./features/claude-code": {},
// The agent list is .devcontainer/agents.yml: `task agents -- host` inside,
// `task devcontainer:agent -- host` from the workstation.
"./features/agents": {},
"./features/devhost": {}
},
// Host side, before every start: stage the Features, and make sure
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- go-task
- devcontainer-cli
- claude-code
- agents
- coder-cli
- openbao
- velero
Expand Down Expand Up @@ -63,6 +64,7 @@ jobs:
- go-task
- devcontainer-cli
- claude-code
- agents
- coder-cli
- openbao
- velero
Expand Down
8 changes: 8 additions & 0 deletions LEXICON.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ The domain words of the project itself are not here; they live with the project'
the **workstation**, never "the host".
- **Inter** (interface agent): an agent that works with an engineer, directly or through another
inter; the host is the main one.
- **Resident agent**: an agent kind in the agent list: it runs continuously in the background, and
starting it attaches to it (starting it first if it is not running). The host becomes one once
Remote Control reaches a background session; until then it is on-demand. Not to be confused with
a back, which no person attaches to.
- **On-demand agent**: an agent kind in the agent list: starting it opens a new session, and it
can resume an earlier one when asked. A designer or a general Claude instance is one.
- **Agent list**: the file a repository keeps naming its agents, each with its kind and CLI;
`.devcontainer/agents.yml` by default, with a configurable path relative to the repository root.
- **Back** (background agent): an agent that never works with a person. After each turn, and after
each con's job, it reads what was done and keeps one record current, so no inter spends a turn
on upkeep. It reports only through a quiet notice. Each runs on its own model, inside the
Expand Down
36 changes: 25 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ installing Node.js, and a one-line bootstrap that wires both into any repo.
| --- | --- |
| [`go-task`](src/go-task) | [Task](https://taskfile.dev) (`task`), sha256-verified from GitHub releases, with bash/zsh completions. |
| [`devcontainer-cli`](src/devcontainer-cli) | `@devcontainers/cli` as `devcontainer`, on a private Node.js under `/opt/devcontainer-cli` that is never put on PATH. |
| [`claude-code`](src/claude-code) | [Claude Code](https://code.claude.com/docs): the native release binary (no Node.js, version pinnable) unless a `claude` is already there (the official Anthropic Feature), plus the `claude-host` launcher (a running session of the folder is never started twice), `claude-host-pin` (opt-in SessionStart hook keeping the pin current), `claude-fleet` (several sessions in one terminal) and the state (`CLAUDE_CONFIG_DIR`) in a volume it owns. |
| [`claude-code`](src/claude-code) | [Claude Code](https://code.claude.com/docs): the native release binary (no Node.js, version pinnable) unless a `claude` is already there (the official Anthropic Feature), plus `agent-adapter-claude` (Claude Code's adapter for the `agents` Feature) and the state (`CLAUDE_CONFIG_DIR`) in a volume it owns; the `claude-host` launcher, `claude-host-pin` hook and `claude-fleet` still work in 1.7.0 but are deprecated (claude-code 2.0 removes them). |
| [`agents`](src/agents) | One list of a repository's agents (`.devcontainer/agents.yml`: name, CLI, folder, args, kind) and one verb for every agent CLI: `task agents -- <name>` makes a `resident` agent run in the background and attaches, or opens a new session of an `on-demand` one; `agents:up` starts the residents with the container; each CLI Feature supplies the adapter. |
| [`git`](src/git) | git augmented for the container: a worktree-aware wrapper that scopes `GIT_DIR` to the checkout and maps a `.git` pointer under the host's `.git-dirs` onto one shared mount, `safe.directory` and `core.hooksPath` at post-create, the global config in a volume it owns. |
| [`gh`](src/gh) | [GitHub CLI](https://cli.github.com) from GitHub releases, checksum-verified, unless the official Feature installed it; its login (`GH_CONFIG_DIR`) in a volume it owns, git's GitHub credentials taken from it. |
| [`ssh-agent`](src/ssh-agent) | `SSH_AUTH_SOCK` at a fixed path in every process, so git commits can be signed with an SSH key: the launcher mounts the host's agent socket (linux, macOS, a WSL distro), the private key never entering the container; the Windows engines get a plain explanation and an opt-in, temporary key mode; `ssh-agent:*` tasks for keys, dedicated-key warnings, rotation and retirement (`key:rotate`, `key:retire`, `key:age`) and git signing setup. |
Expand Down Expand Up @@ -75,14 +76,17 @@ gap to close here (see [Contributing from a consuming repo](#contributing-from-a
worktrees stay out of the checkout too (`PYTHONPYCACHEPREFIX`, a volume
over `.claude/worktrees`), or a synced checkout carries every build
artefact.
- **One session per role, found by UUID.** `claude-host` resumes the session
in `HOST_SESSION` (from `.devcontainer/.env`) wherever it runs;
`task devcontainer:claude` does it from the host, `task claude-code:host`
inside, `DIR=<sub-folder>` for a session per sub-folder. A session that is
running in the folder (`claude agents --json`, an entry with a pid) is never
started a second time (claude-host refuses and names it; `/clear` gives a
session a new id, which the opt-in `claude-host-pin` SessionStart hook writes back to `.env`); several agents in one terminal are
`task devcontainer:claude:fleet` (below), never a second copy of one.
- **One list of agents, one verb.** A repository names its agents in
`.devcontainer/agents.yml` (the `agents` Feature): a name, a CLI, a folder,
extra args and a kind. `task agents -- <name>` inside, `task
devcontainer:agent -- <name>` from the host, does the right thing for the
kind: a `resident` agent is made to run in the background and attached, an
`on-demand` one opens a new session (`--resume` for its latest). Each CLI
Feature supplies an adapter (`agent-adapter-claude` from `claude-code`); the
adapter finds a session by its name and folder and acts on it by id, so no
session id is pinned anywhere and nothing starts a second copy of a running
agent. `claude-host`, `claude-fleet`, `HOST_SESSION` and `SESSIONS` are
deprecated and go with claude-code 2.0.
- **Settings are plain variables, three ways.** Command-line var, then the
`.env` files, then the exported environment, with the same names in the
tasks, the PowerShell scripts and their bash twins.
Expand Down Expand Up @@ -268,8 +272,10 @@ Each Feature stores its persistent state (logins, config, transcripts) in a name
| `devcontainer:ssh-key-import FILE=<host path>` | Opt-in key mode: stream a private key file of the host (and its `.pub`) into the running container's `ssh-agent` key volume through `devcontainer exec` on stdin (never an argument), mode 0600, then load it; prints the fingerprint, never the key; warns (never blocks) when FILE has a default name or its public key also opens this host. Refuses when FILE is missing, no container runs, or key mode is off there. Host only. |
| `devcontainer:host` | Print the host profile (`HOST_TYPE` and why, engine, `DCF_HOST_*`) and the host mounts `up` would add or skip; starts nothing. |
| `devcontainer:exec CMD='...'` / `shell` / `pwsh` | Run a command (base64-safe), an interactive bash, or PowerShell in the container. |
| `devcontainer:claude [DIR=<sub-folder>] [SESSION=<uuid>] [-- <claude args>]` | Start or resume the Claude Code host session inside (needs the `claude-code` Feature): `HOST_SESSION` from `.devcontainer/.env`, `HOST_SESSION_<DIR>` for a sub-folder. Runs directly when already inside the container. |
| `devcontainer:claude:fleet [-- --plan\|--no-view\|--no-wait\|--stop-duplicates]` | Several Claude Code sessions in one terminal (`claude-fleet` from the `claude-code` Feature): `SESSIONS` (include `vars:`; one line per session, `name\|session-variable\|dir\|extra claude args`) is read against `claude agents --json`; a session counts as running when its id (from `.devcontainer/.env`, then the container's environment) or its folder is live, the missing ones start in the background with `claude-host --bg` in their folder, then `claude agents` opens. It first waits for sessions restored after a rebuild to settle (`-- --no-wait` skips that); several live sessions for one agent are reported, and `-- --stop-duplicates` stops the extras with `claude stop` (never rm). `-- --plan` prints what it would do and starts nothing; `-- --no-view` skips the view. |
| `devcontainer:agent [AGENTS_FILE=<path>] -- <name> [--resume\|--new] [<CLI args>]` | Start an agent of the repository's agent list inside (needs the `agents` Feature and the adapter of the agent's CLI): a `resident` runs in the background and is attached, an `on-demand` agent opens a new session in this terminal. Runs directly when already inside the container. |
| `devcontainer:agent:list [-- --json]` | The agents with their kind, CLI, state and session id. |
| `devcontainer:claude [DIR=<sub-folder>] [SESSION=<uuid>] [-- <claude args>]` | Deprecated (prints one line; goes with claude-code 2.0): use `devcontainer:agent`. Start or resume the Claude Code host session inside (needs the `claude-code` Feature): `HOST_SESSION` from `.devcontainer/.env`, `HOST_SESSION_<DIR>` for a sub-folder. Runs directly when already inside the container. |
| `devcontainer:claude:fleet [-- --plan\|--no-view\|--no-wait\|--stop-duplicates]` | Deprecated (prints one line; goes with claude-code 2.0): list the agents in `agents.yml` instead. Several Claude Code sessions in one terminal (`claude-fleet` from the `claude-code` Feature): `SESSIONS` (include `vars:`; one line per session, `name\|session-variable\|dir\|extra claude args`) is read against `claude agents --json`; a session counts as running when its id (from `.devcontainer/.env`, then the container's environment) or its folder is live, the missing ones start in the background with `claude-host --bg` in their folder, then `claude agents` opens. It first waits for sessions restored after a rebuild to settle (`-- --no-wait` skips that); several live sessions for one agent are reported, and `-- --stop-duplicates` stops the extras with `claude stop` (never rm). `-- --plan` prints what it would do and starts nothing; `-- --no-view` skips the view. |
| `devcontainer:config` | Resolved configuration. |
| `devcontainer:<anything>` | Passthrough to that CLI subcommand, args after `--`. |
| `devcontainer:reset-storage` | wslc.exe only: reset WSL Containers' storage after a read-only BuildKit error. |
Expand Down Expand Up @@ -306,6 +312,14 @@ Where there is no host folder (a cluster), `cloud-folder` 1.1.0 syncs it with rc
`cloud-folder-setup shutdown-check` to your `shutdown:check` so a stop waits for a running sync (see the Feature's
NOTES).

**One verb for every agent (`agents` 1.0.0, claude-code 1.7.0, devcontainer-cli 1.13.0):** add
`ghcr.io/nabil-becker/devcontainer-features/agents:1` to the `features`, write `.devcontainer/agents.yml` (one entry per
agent: `name`, `cli`, `kind` resident or on-demand, `folder`, `args`) and start them with `task devcontainer:agent --
<name>` (inside: `task agents -- <name>`). That replaces `devcontainer:claude`, `devcontainer:claude:fleet` with
`SESSIONS`, the `HOST_SESSION...` pins in `.devcontainer/.env`, the `claude-host-pin` hook and any join script of your
own; they keep working, with a deprecation line, until claude-code 2.0. Step by step:
[docs/migrations/agents.md](docs/migrations/agents.md).

All of them also answer to `dc:` (the include's alias in the generated
Taskfile). Each task dispatches per platform (Task's per-command
`platforms`):
Expand Down
13 changes: 10 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ stays. Ids are short descriptive slugs (two to four kebab-case words naming the
- [ ] **gh-ssh-key-format-check Check the real output of gh ssh-key list** (engineers). `REVOKE=1` finds the old key's id by parsing `gh ssh-key list`, which only the stubs have produced; run one real rotation on a throwaway key, fix the parse if the format differs, and file what it shows. Added 2026-10-24.
- [x] **fleet-reconcile-live-sessions The fleet waits for restored sessions, reports and stops duplicates** (host). Requested by a consuming repository running four agents in one container (2026-10-24): `claude-fleet` waits until the restored session list is stable before reconciling (`--no-wait` skips it), reads ids from `.devcontainer/.env` before the environment, reports every agent with more than one live session and, only with `--stop-duplicates`, stops the extras with `claude stop` (never rm); `claude-host`'s refusal names `claude attach <id>`. claude-code 1.6.0, devcontainer-cli 1.11.0. Added 2026-10-24. Done 2026-10-24 (branch `feature/fleet-reconcile-live-sessions/1`).
- [x] **release-claude-code-1-6-1 Release claude-code 1.6.1 after the pin-hook and attach fixes merge** (the Operator). 1.6.1 carries the pin-hook and attach-advice fixes found on the workstation; the host reviews the con's changes, the Operator says "commit" and opens the pull request, and merges it once validate, lint, tests and DCO pass. The host does not start the release; it says when one is due. Added 2026-10-24. Done 2026-10-24.
- [ ] **cloud-folder-build Build the cloud-folder Feature** (host). A central SharePoint folder in the container through rclone, decided 2026-10-27 (`docs/proposals/cloud-folder.md`, section 7): slice 1 (`host` mode, `status`) is in progress; slice 2 (`sync`, sign-in, the shutdown check) and slice 3 (`mount`, FUSE granted per machine) remain. The office can adopt after slice 1. Added 2026-10-27.
- [ ] **cloud-folder-build Build the cloud-folder Feature** (host). A central SharePoint folder in the container through rclone, decided 2026-10-27 (`docs/proposals/cloud-folder.md`, section 7): slices 1 (`host`, #82) and 2 (`sync`, #83) are released as cloud-folder 1.1.0 (devcontainer-cli 1.12.0); slice 3 (`mount`, `auto` with FUSE, the launcher granting FUSE on request) remains. The office can adopt now. Added 2026-10-27.

### Devhost and the way of working

Expand Down Expand Up @@ -271,8 +271,15 @@ stays. Ids are short descriptive slugs (two to four kebab-case words naming the
- [ ] **psscriptanalyzer-tracked-files The PowerShell lint checks tracked files only** (host). `task lint` fails on a workstation checkout because the PSScriptAnalyzer step scans the git-ignored `material.ignore/` folder and reports 32 problems in files git never sees; the earlier `lint-ignore-local-folders` fix covered yamllint only. Build the analyzer's file list from tracked and non-ignored files, as the YAML check does, with a test. Added 2026-10-27.
- [ ] **cloud-folder-ci-check Read the first CI run of the cloud-folder tests** (engineers). The Feature's container tests (`test/cloud-folder`) and the collection-wide test have not run with Docker anywhere but CI; read the first CI run and file what it shows. Added 2026-10-27.
- [ ] **cloud-folder-workstation-up Run cloud-folder once on the workstation** (the Operator). One real `up` with a cloud folder whose path has spaces, on Docker Desktop and on wslc, to check the launcher's mount and `cloud-folder:status` on real hosts. Added 2026-10-27.
- [ ] **release-cloud-folder Release cloud-folder 1.0.0 and devcontainer-cli 1.12.0 after the step 1 pull request merges** (the Operator). The host does not start the release. Until it runs, consumers cannot pull cloud-folder or name it in `installsAfter`. Added 2026-10-27.
- [ ] **cloud-folder-step-2-rebase Rebase cloud-folder step 2 onto main** (host). The step 2 branch `feature/cloud-folder-feature/2` was cut from step 1 before #82 was squash-merged, so it carries the old history; rebase it onto `origin/main` once its subagent has finished, before any commit on it. Added 2026-10-27.
- [x] **release-cloud-folder Release cloud-folder 1.0.0 and devcontainer-cli 1.12.0 after the step 1 pull request merges** (the Operator). The host does not start the release. Until it runs, consumers cannot pull cloud-folder or name it in `installsAfter`. Added 2026-10-27. Done 2026-10-27.
- [x] **cloud-folder-step-2-rebase Rebase cloud-folder step 2 onto main** (host). The step 2 branch `feature/cloud-folder-feature/2` was cut from step 1 before #82 was squash-merged, so it carries the old history; rebase it onto `origin/main` once its subagent has finished, before any commit on it. Added 2026-10-27. Done 2026-10-27.
- [x] **merge-cloud-folder-step-2 Review and merge the cloud-folder step 2 pull request** (the Operator). Step 2 (`sync` mode with rclone, cloud-folder 1.1.0) is committed on its rebased branch and its pull request is open; review it, and merge once validate, lint, tests and DCO pass. The host does not merge. Added 2026-10-27. Done 2026-10-27.
- [x] **release-cloud-folder-1-1-0 Release cloud-folder 1.1.0 after the step 2 pull request merges** (the Operator). Step 2 (`sync` mode) ships as cloud-folder 1.1.0 once #83 is in; consumers cannot pull it until the release runs. The host does not start the release. Added 2026-10-27. Done 2026-10-27.
- [ ] **cloud-folder-sync-live-check Check cloud-folder sync on a real sign-in** (the Operator). After the 1.1.0 release: one real sign-in, a file each way between the container and the SharePoint folder, and a stop in the middle of a sync, to see what the sync does when it is interrupted. Added 2026-10-27.
- [ ] **agents-lexicon-terms Land the agent-model terms on the agents branch** (host). The librarian's three terms (resident agent, on-demand agent, agent list) were taken out of the step 2 commit: they came ahead of the Operator's decision and their ontology entries are missing, so the guard would refuse them. The saved patch in the session scratchpad goes onto the agents branch with those entries. Added 2026-10-27.
- [ ] **agents-commit Commit and open the pull request for the agents build** (the Operator). The `agents` 1.0.0 Feature, the Claude adapter (claude-code 1.7.0), the host task (devcontainer-cli 1.13.0) and this repository's `.devcontainer/agents.yml` pass their unit tests and the repository checks in a worktree, but nothing is committed. The host commits and opens the pull request only on the Operator's word. Added 2026-10-27.
- [ ] **agents-ci-check Read the first CI run of the agents tests** (engineers). The container tests of the `agents` Feature have not run with Docker anywhere but CI; read the first CI run and file what it shows. Added 2026-10-27.
- [ ] **release-agents Release agents 1.0.0, claude-code 1.7.0 and devcontainer-cli 1.13.0 after the agents pull request merges** (the Operator). The host does not start the release. Once it runs, rebuild the devhost here so the launcher and `task agents` carry the change. Added 2026-10-27.

## Next

Expand Down
Loading
Loading