{"id":43478233,"url":"https://github.com/discourse/dv","last_synced_at":"2026-05-05T04:10:24.961Z","repository":{"id":309125068,"uuid":"1035243192","full_name":"discourse/dv","owner":"discourse","description":"Discourse Vibe - Agent containers for Discourse Development ","archived":false,"fork":false,"pushed_at":"2026-05-04T02:27:02.000Z","size":890,"stargazers_count":27,"open_issues_count":0,"forks_count":6,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T04:23:15.903Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/discourse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-08-10T00:54:47.000Z","updated_at":"2026-05-04T02:27:06.000Z","dependencies_parsed_at":"2026-01-29T04:05:35.799Z","dependency_job_id":null,"html_url":"https://github.com/discourse/dv","commit_stats":null,"previous_names":["samsaffron/ai-agent-container","samsaffron/dv","discourse/dv"],"tags_count":84,"template":false,"template_full_name":null,"purl":"pkg:github/discourse/dv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/discourse%2Fdv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/discourse%2Fdv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/discourse%2Fdv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/discourse%2Fdv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/discourse","download_url":"https://codeload.github.com/discourse/dv/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/discourse%2Fdv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32634787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"online","status_checked_at":"2026-05-05T02:00:06.033Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-03T08:11:03.685Z","updated_at":"2026-05-05T04:10:24.954Z","avatar_url":"https://github.com/discourse.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Discourse AI Agent Container\n\nA Docker-based development environment for AI agents with Discourse.\n\n## Overview\n\nThis project provides a containerized development environment that includes:\n- Discourse development setup\n- Essential developer tools (vim, ripgrep)\n- Ready-to-use database configuration, fully migrated dev/test databases\n- Various AI helpers preinstalled in the image (Claude, Codex, Aider, Gemini)\n- Multi-agent container management via `dv` top-level commands (`list`, `new`, `select`, `rename`)\n- Plugin bootstrap helpers via `dv new --plugin` and `dv plugin add`\n- Embedded Dockerfile managed by the CLI with safe override mechanisms\n\n## Prerequisites\n\n- Docker installed on your system\n- Go 1.22+\n- Optional: GitHub CLI (`gh`) if you want to use `dv extract`’s default cloning behavior\n\n## Installation\n\n### Using the install script (recommended)\n\nInstall the latest release for macOS or Linux with a single command:\n\n```bash\ncurl -sSfL https://raw.githubusercontent.com/discourse/dv/main/install.sh | sh\n```\n\nThe script downloads the correct binary for your platform and installs it to `~/.local/bin` (create it if missing). After it finishes, run `dv version` to confirm that the binary is on your `PATH`.\n\nTo pin a specific release or control the install location:\n\n```bash\n# install a specific tag\ncurl -sSfL https://raw.githubusercontent.com/discourse/dv/main/install.sh | sh -s -- --version v0.3.0\n\n# install without sudo\ncurl -sSfL https://raw.githubusercontent.com/discourse/dv/main/install.sh | sh -s -- --install-dir ~/.local/bin\n```\n\nYou can also set the `DV_INSTALL_DIR` environment variable to change the default target directory. If `~/.local/bin` (or your custom path) isn’t on your `PATH`, add it in your shell profile, e.g. `export PATH=\"$HOME/.local/bin:$PATH\"`.\n\n`dv` automatically checks for updates once per day in the background. When a newer release is published you’ll see a warning; run `dv upgrade` to install it in place without re-running the shell script. Update metadata is cached at `${XDG_CONFIG_HOME}/dv/update-state.json`.\n\n### Build from source\n\nIf you’re hacking on `dv`, build the binary directly:\n\n```bash\ngo build\n```\n\nThe resulting binary is written to the repository root (run it via `./dv`).\n\n## Quick Start\n\nWith `dv` installed (either via the script or `go build`), run the CLI directly from your shell. If you’re using the locally built binary in this repository, replace `dv` with `./dv` in the commands below.\n\n1. Build the Docker image:\n   ```bash\n   dv build\n   ```\n\n2. Start the container:\n   ```bash\n   dv start\n   ```\n3. Enter the container, or run a one-off command without opening a shell:\n   ```bash\n   dv enter\n   # run a single command\n   dv run -- bin/rails c\n   ```\n4. Extract changes from the container (when ready to create a PR):\n   ```bash\n   dv extract\n   # or extract changes for a specific plugin (with TAB completion)\n   dv extract plugin discourse-akismet\n   ```\n\nOptional: manage multiple named containers (\"agents\") and bootstrap plugin workspaces:\n```bash\ndv new my_project                         # create and select a new agent\ndv new --plugin discourse-kanban kanban   # create an agent with a plugin pre-cloned\ndv plugin add discourse-solved            # add a plugin to the selected running agent\ndv list                                   # show all agents for the selected image\ndv select my_project                      # select an existing agent\ndv rename old new                         # rename an agent\n```\n\n## dv Commands\n\n### dv build\nBuild the Docker image (defaults to tag `ai_agent`).\n\n```bash\ndv build [--no-cache] [--build-arg KEY=VAL] [--rm-existing]\n```\n\nNotes:\n- Uses an embedded `Dockerfile` managed under your XDG config directory. On each build, the CLI ensures the materialized `Dockerfile` matches the embedded version via a SHA file.\n- Override precedence:\n  1) `DV_DOCKERFILE=/absolute/path/to/Dockerfile`\n  2) `${XDG_CONFIG_HOME}/dv/Dockerfile.local`\n  3) Embedded default (materialized to `${XDG_CONFIG_HOME}/dv/Dockerfile`)\n  The command prints which Dockerfile path it used.\n- BuildKit/buildx is enabled by default (`docker buildx build --load`). The CLI automatically falls back to legacy `docker build` if buildx is unavailable.\n- Opt-out controls: `--classic-build` forces legacy `docker build`, and `--builder NAME` targets a specific buildx builder (remote builders, Docker Build Cloud, etc.).\n\n### dv pull\nPull a published image/tag instead of building locally.\n\n```bash\ndv pull [IMAGE_NAME]\n```\n\n### dv image\nManage image definitions, workdirs, ports, and Dockerfile sources.\n\n```bash\ndv image list\ndv image select NAME\ndv image show\n```\n\n### dv start\nCreate or start the container for the selected image (no shell).\n\n```bash\ndv start [--reset] [--name NAME] [--image NAME] [--host-starting-port N] [--container-port N]\n```\n\nNotes:\n- Maps host `4201` → container `4200` by default (Ember CLI dev server). Override with flags.\n- Performs a pre-flight check and picks the next free port if needed.\n\n### dv stop\nStop the selected or specified container.\n\n```bash\ndv stop [--name NAME]\n\n# Restart the container\ndv restart [--name NAME]\n\n# Restart only Discourse services (Pitchfork/Sidekiq)\ndv restart discourse [--name NAME]\n```\n\n### dv reset\nReset the development environment (databases or git state).\n\n```bash\n# Reset databases (default behavior)\ndv reset [--name NAME]\ndv reset db [--name NAME]\n\n# Reset git state (discard local changes, sync with upstream)\ndv reset git [--name NAME]\n```\n\nNotes for `dv reset` / `dv reset db`:\n- Stops Discourse services.\n- Resets the development and test databases.\n- Runs migrations and seeds test data.\n- Restarts services.\n\nNotes for `dv reset git`:\n- Discards local code changes in the container.\n- Syncs with the upstream branch.\n- Reinstalls dependencies and runs migrations.\n\n### dv enter\nAttach to the running container as user `discourse` in the workdir and open an interactive shell.\n\n```bash\ndv enter [--name NAME]\n```\n\nNotes:\n- Copies any configured host files into the container before launching the shell (see `copyRules` under config).\n\n### dv run\nRun a non-interactive command inside the running container (defaults to the `discourse` user).\n\n```bash\ndv run [--name NAME] [--root] -- CMD [ARGS...]\n```\n\nNotes:\n- Same file-copy behavior as `dv enter`; run `dv run -- \u003ccommand\u003e` to execute without opening a shell.\n- Pass `--root` to execute as `root` inside the container.\n\n### dv run-agent (alias: ra)\nRun an AI agent inside the container with a prompt.\n\n```bash\ndv run-agent [--name NAME] AGENT [-- ARGS...|PROMPT ...]\n# alias\ndv ra codex Write a migration to add foo to users\n\n# interactive mode\ndv ra codex\n\n# use a file as the prompt (useful for long instructions)\ndv ra codex ./prompts/long-instructions.txt\ndv ra codex ~/notes/feature-plan.md\n\n# pass raw args directly to the agent (no prompt wrapping)\ndv ra aider -- --yes -m \"Refactor widget\"\n```\n\nNotes:\n- Autocompletes common agents: `codex`, `aider`, `claude`, `gemini`, `crush`, `cursor`, `opencode`, `amp`.\n- If no prompt is provided, an inline TUI opens for multi-line input (Ctrl+D to run, Esc to cancel).\n- You can pass a regular file path as the first argument after the agent (e.g. `dv ra codex ./plan.md`). The file will be read on the host and its contents used as the prompt. If the argument is not a file, the existing prompt behavior is used.\n- Filename/path completion is supported when you start typing a path (e.g. `./`, `../`, `/`, or include a path separator).\n- Agent invocation is rule-based (no runtime discovery). Use `--` to pass raw args unchanged (e.g., `dv ra codex -- --help`).\n\n### dv mail\nRun MailHog and tunnel it to localhost.\n\n```bash\ndv mail [--port 8025] [--host-port 8025]\n```\n\nAllows you to access MailHog from your browser (e.g., http://localhost:8025) to inspect emails sent by Discourse. Press Ctrl+C to stop the process and the tunnel.\n\n### dv tui\nLaunch an interactive TUI to manage containers, images, and run commands.\n\n```bash\ndv tui\n```\n\n### dv import\nPush local commits or uncommitted work from the host repository into the running container.\n\n```bash\ndv import [--base main]\n```\n\n### dv update agents / agent\nRefresh the preinstalled AI agents inside the container, either all at once or a single named agent.\n\n```bash\ndv update agents [--name NAME]\ndv update agent AGENT [--name NAME]\n```\n\nExamples:\n\n```bash\ndv update agent term-llm\ndv update agent codex --name my-container\n```\n\nNotes:\n- Starts the container if needed before running updates.\n- Re-runs the official install scripts or package managers to pull the latest versions.\n- Supported single-agent names include `codex`, `gemini`, `crush`, `copilot`, `opencode`, `amp`, `claude`, `aider`, `cursor`, `droid`, `vibe`, and `term-llm`.\n\n### dv remove\nRemove the container and optionally the image.\n\n```bash\ndv remove [--image] [--name NAME]\n```\n\n### Agent management\nManage multiple containers for the selected image; selection is stored in XDG config. These are the preferred top-level commands; the old `dv agent` group has been removed.\n\n```bash\ndv list\ndv new [NAME]\ndv new --plugin discourse-kanban kanban\ndv new --plugin discourse/discourse-kanban kanban\ndv new --plugin git@github.com:my-org/private-plugin.git private-test\ndv select NAME\ndv rename OLD NEW\n```\n\n`dv new --plugin` creates a normal agent, then clones each requested plugin into the Discourse `plugins/` directory before running provisioning maintenance. `--plugin` is repeatable. Plugin arguments accept:\n\n- `discourse-kanban` shorthand for `https://github.com/discourse/discourse-kanban.git`\n- `owner/repo` shorthand for `https://github.com/owner/repo.git`\n- full HTTPS URLs\n- SSH URLs such as `git@github.com:owner/repo.git`\n\n### dv plugin\nManage plugins in the selected running agent.\n\n```bash\ndv plugin list\ndv plugin add discourse-kanban\ndv plugin add discourse/discourse-kanban\ndv plugin add git@github.com:my-org/private-plugin.git\ndv plugin add --branch main discourse-kanban\ndv plugin add --skip-maintenance discourse-kanban\n```\n\nNotes:\n- `dv plugin add` clones plugins into `/var/www/discourse/plugins/\u003crepo-name\u003e` by default.\n- After cloning, it runs bundle install and database migrations unless `--skip-maintenance` is used.\n- SSH plugin URLs require SSH agent forwarding in the target container. `dv new --plugin git@github.com:owner/repo.git ...` enables this for the new agent automatically; for `dv plugin add git@...`, use an agent that was created with SSH forwarding or use an HTTPS URL.\n- Use `dv plugin --name NAME add ...` to target a specific running agent.\n\n### Templates\nProvision containers with pre-defined configurations using YAML templates. This is useful for setting up specific environments, installing plugins/themes, or applying site settings automatically.\n\n```bash\n# Create a new agent from a local template\ndv new my-feature --template ./templates/stable.yaml\n\n# Create a new agent from a URL\ndv new my-feature --template https://raw.githubusercontent.com/discourse/dv/main/templates/full.yaml\n```\n\nA default template can also be set via `dv config defaultTemplate [PATH]`, which will use the provided template at the path if `dv new` is ran without an explicit `--template` flag.\n\nTemplates support:\n- **Discourse Configuration**: Specify branches, PRs, or custom repos.\n- **Plugins \u0026 Themes**: Automatically clone plugins and install/watch themes.\n- **Site Settings**: Set Discourse settings (title, theme, experimental features) on boot.\n- **Copy Rules**: Sync host files (like `.gitconfig` or API keys) into the container.\n- **Provisioning**: Run arbitrary bash commands via `on_create`.\n- **MCP Servers**: Register Model Context Protocol servers for AI agents.\n\nSee [templates/full.yaml](./templates/full.yaml) for a complete example of all available features.\n\n### dv extract\nCopy modified files from the running container’s `/var/www/discourse` into a local clone and create a new branch at the container’s HEAD.\n\n```bash\ndv extract [--name NAME] [--sync] [--debug]\n```\n\nBy default, the destination is `${XDG_DATA_HOME}/dv/discourse_src`. When a container uses a custom workdir (for example, a theme under `/home/discourse/winter-colors`), the extract target becomes `${XDG_DATA_HOME}/dv/\u003cworkdir-slug\u003e_src` so each workspace mirrors into its own folder.\n\n`--sync` keeps the container and host codebases synchronized after the initial extract by watching for changes in both environments (press `Ctrl+C` to exit). `--debug` adds verbose logging while in sync mode. These flags cannot be combined with `--chdir` or `--echo-cd`.\n\nNote: sync mode requires `inotifywait` to be available inside the container (included in latest Dockerfile used here).\n\nExamples:\n\n```bash\n# Perform a one-off extract\ndv extract\n\n# Start continuous two-way sync with verbose logging\ndv extract --sync --debug\n```\n\n### dv pr\nCheckout a GitHub pull request in the container and reset the development environment.\n\n```bash\ndv pr [--name NAME] [--no-reset] NUMBER\n```\n\nNotes:\n- Fetches and checks out the specified PR into a local branch.\n- Performs a full database reset and migration (development and test databases).\n- Reinstalls dependencies (bundle and pnpm).\n- Seeds test users.\n- Use `--no-reset` to skip DB drop, create, and seed, but still run migrations reinstall deps.\n- Supports TAB completion with PR numbers and titles from GitHub API.\n- Only works with containers using the `discourse` image kind.\n\nExamples:\n```bash\n# Checkout PR #12345\ndv pr 12345\n\n# Checkout without resetting DB\ndv pr --no-reset 12345\n\n# Use TAB completion to search and select a PR\ndv pr \u003cTAB\u003e\n```\n\n### dv branch\nCheckout a git branch in the container and reset the development environment.\n\n```bash\ndv branch [--name NAME] [--no-reset] [--new] BRANCH\n```\n\nNotes:\n- Checks out the specified branch and pulls latest changes.\n- Performs a full database reset and migration (development and test databases).\n- Reinstalls dependencies (bundle and pnpm).\n- Seeds test users.\n- Use `--no-reset` to skip DB drop, create, and seed, but still run migrations reinstall deps.\n- Use `--new` to create a new branch from origin/main (or origin/master) if the branch does not exist on remote.\n- Supports TAB completion(e.g., `dv branch me\u003cTAB\u003e` queries only branches starting with \"me\").\n- Only works with containers using the `discourse` image kind.\n\nExamples:\n```bash\n# Checkout main branch\ndv branch main\n\n# Use TAB completion to list and select a branch\ndv branch \u003cTAB\u003e\n\n# Checkout a feature branch\ndv branch feature/my-feature\n\n# Create a new local branch for development\ndv branch --new my-new-feature\n\n# Quickly switch branches without resetting DB\ndv branch --no-reset main\n```\n\n### dv extract plugin\nExtract changes for a single plugin from the running container. This is useful when a plugin is its own git repository under `/var/www/discourse/plugins`.\n\n```bash\ndv extract plugin \u003cname\u003e [--name NAME] [--chdir] [--echo-cd]\n```\n\nNotes:\n- Requires the container to be running to discover plugins.\n- TAB completion suggests plugin names under `/var/www/discourse/plugins` that are separate git repositories from the core Discourse repo.\n- Destination is `${XDG_DATA_HOME}/dv/\u003cPLUGIN\u003e_src`.\n- If the plugin is a git repo with a remote, dv clones it and checks out a branch/commit matching the container; only modified/untracked files are copied over.\n- If the plugin has no git remote or isn’t a git repo, dv copies the whole directory to `\u003cPLUGIN\u003e_src`.\n- `--chdir` opens a subshell in the extracted directory on completion. `--echo-cd` prints a `cd \u003cpath\u003e` line to stdout (suitable for `eval`).\n\nExamples:\n```bash\n# Autocomplete plugin name\ndv extract plugin \u003cTAB\u003e\n\n# Extract changes for akismet plugin\ndv extract plugin discourse-akismet\n\n# Jump into the extracted repo afterwards\ndv extract plugin discourse-akismet --chdir\n\n# Use in command substitution to cd silently\neval \"$(dv extract plugin discourse-akismet --echo-cd)\"\n```\n\n### dv extract theme\nExtract changes for a theme from `/home/discourse` inside the container.\n\n```bash\ndv extract theme \u003cname\u003e [--name NAME] [--sync] [--debug] [--chdir] [--echo-cd]\n```\n\nNotes:\n- Requires the container to be running to discover themes.\n- TAB completion suggests theme directories under `/home/discourse` that are git repositories.\n- Destination is `${XDG_DATA_HOME}/dv/\u003cTHEME\u003e_src`.\n- `--sync` enables continuous bidirectional synchronization (press Ctrl+C to stop).\n- `--chdir` opens a subshell in the extracted directory on completion. `--echo-cd` prints a `cd \u003cpath\u003e` line to stdout (suitable for `eval`).\n\nExamples:\n```bash\n# Extract a theme\ndv extract theme winter-colors\n\n# Start continuous sync for theme development\ndv extract theme winter-colors --sync\n\n# Jump into the extracted repo afterwards\ndv extract theme winter-colors --chdir\n```\n\n### dv config\nRead/write config stored at `${XDG_CONFIG_HOME}/dv/config.json`.\n\n```bash\ndv config get KEY\ndv config set KEY VALUE\ndv config show\n```\n\n#### AI Configuration (LLMs)\nUse `dv config ai` to launch a TUI for configuring Discourse AI LLM providers (OpenAI, Anthropic, Bedrock, etc.) and models. It automatically detects API keys from your host environment variables.\n\n#### AI Tool Workspace\nUse `dv config ai-tool [NAME]` to scaffold a directory under `/home/discourse/ai-tools` for developing custom Discourse AI tools. It includes `tool.yml` (metadata), `script.js` (logic), and `bin/test` / `bin/sync` helpers.\n\n#### Default Template\nUse `dv config defaultTemplate [PATH]` to set default template to be used when `dv new` is ran without a `--template` flag. See [templates/full.yaml](./templates/full.yaml) for a complete example of all available features for templates.\n\n#### Theme bootstrap\nUse `dv config theme [REPO]` to prepare a theme workspace inside the running container. Running it with no arguments prompts for a name **and** whether you’re building a full theme or component, installs the `discourse_theme` gem, scaffolds a minimal theme under `/home/discourse/\u003cname\u003e`, writes an `AGENTS.md` brief for AI tools, and updates the workdir override so `dv enter` drops you there. Supplying a git URL or `owner/repo` slug clones the existing theme instead of generating a skeleton, while still installing the gem, writing `AGENTS.md`, and configuring the watcher. Each workspace also receives a `theme-watch-\u003cslug\u003e` runit service that runs `discourse_theme watch` with an API key that’s automatically bound to the first admin user; restart it anytime with `sv restart theme-watch-\u003cslug\u003e` inside the container. Pass `--theme-name` (and optionally `--kind theme|component`) to skip the interactive prompts, and `--verbose` if you want to see every helper command that runs (handy when debugging API key or watcher issues).\n\n#### Site Settings\nUse `dv config site_settings FILENAME.yaml` to apply Discourse site settings from a YAML file. Supports 1Password integration via `op://` references for sensitive values.\n\n#### Local proxy (NAME.dv.localhost)\nRun `dv config local-proxy` to build and start a small reverse proxy container (`dv-local-proxy` by default) that maps each new agent to `NAME.dv.localhost` instead of host ports like `localhost:4201`. By default, the proxy listens on localhost only (port 80 for HTTP, 2080 for admin API) for security. Use `--public` to bind to all network interfaces. Use `--https` to enable HTTPS on port 443 via a local mkcert certificate (HTTP will redirect to HTTPS). The proxy registers containers as you create/start them and injects hostname env vars so assets resolve correctly. Stop or remove the proxy container to go back to host-port URLs; only containers created while the proxy is running adopt the hostname.\n\n#### Claude Code Router (CCR)\nUse `dv config ccr` to bootstrap Claude Code Router presets via OpenRouter/OpenAI rankings.\n\n#### Copying host files before enter/run-agent\nUse `copyRules` in your config to copy host files into the container. Each rule sets a host path (supports `~`, env vars, and globs) and a container destination, plus optional `agents` to only copy when that agent is run via `dv run-agent`. Unscoped rules run for `dv enter`/`dv run`; agent-scoped rules skip those commands.\n\n```json\n{\n  \"copyRules\": [\n    { \"host\": \"~/.codex/auth.json\",      \"container\": \"/home/discourse/.codex/auth.json\",      \"agents\": [\"codex\"] },\n    { \"host\": \"~/.gemini/GEMINI.md\",     \"container\": \"/home/discourse/.gemini/GEMINI.md\",     \"agents\": [\"gemini\"] },\n    { \"host\": \"~/.gemini/*.json\",        \"container\": \"/home/discourse/.gemini/\",              \"agents\": [\"gemini\"] },\n    { \"host\": \"~/.gemini/google_account_id\",     \"container\": \"/home/discourse/google_account_id\",     \"agents\": [\"gemini\"] }\n  ]\n}\n```\nThe parent directory inside the container is created if needed, glob patterns are expanded on the host, and ownership is set to `discourse:discourse` so files stay readable by the working user.\n\n### dv data\nPrint the data directory path (`${XDG_DATA_HOME}/dv`).\n\n```bash\ndv data\n```\n\n### dv config completion\nGenerate shell completion scripts (rarely needed). For zsh:\n\n```bash\ndv config completion zsh           # print to stdout\ndv config completion zsh --install # install to ~/.local/share/zsh/site-functions/_dv\n```\n\n### dv upgrade\nDownload and replace the current binary with the latest GitHub release (or a specific tag).\n\n```bash\ndv upgrade           # install the newest release for your platform\ndv upgrade --version v0.3.0\n```\n\nThe command writes the data to the same path as the running executable, so use `sudo dv upgrade` if `dv` lives somewhere like `/usr/local/bin`.\n\n## Environment Variables\n\nAutomatically passed through when set on the host:\n\n- `CURSOR_API_KEY`\n- `MISTRAL_API_KEY`\n- `ANTHROPIC_API_KEY`\n- `OPENAI_API_KEY`\n- `AWS_ACCESS_KEY_ID`\n- `AWS_SECRET_ACCESS_KEY`\n- `AWS_REGION`\n- `CLAUDE_CODE_USE_BEDROCK`\n- `DEEPSEEK_API_KEY`\n- `GEMINI_API_KEY`\n- `AMP_API_KEY`\n- `GH_TOKEN`\n- `OPENROUTER_API_KEY`\n- `FACTORY_API_KEY`\n- `ANTHROPIC_DEFAULT_SONNET_MODEL`\n- `ANTHROPIC_DEFAULT_OPUS_MODEL`\n- `ANTHROPIC_DEFAULT_HAIKU_MODEL`\n\n### Build acceleration toggles\n\nSet these on the host to change how `dv build` (and other build helpers) behave:\n\n- `DV_DISABLE_BUILDX` — force legacy `docker build` even if buildx is available.\n- `DV_BUILDX_BUILDER` (or `DV_BUILDER`) — default builder name used for `docker buildx build`, useful for remote builders.\n\n## Container Details\n\nThe image is based on `discourse/discourse_dev:release` and includes:\n- Full Discourse development environment at `/var/www/discourse`\n- Ruby/Rails stack with bundled dependencies\n- Node.js (pnpm) + Ember CLI dev server\n- Databases created and migrated for dev/test\n- Development tools (vim, ripgrep)\n- Helper tools installed for code agents\n - Playwright and system deps preinstalled\n\n## Logs\n\nRunit services log to the following locations inside the container:\n\n| Service    | Log Path                              |\n|------------|---------------------------------------|\n| pitchfork  | `/var/www/discourse/log/unicorn.log`  |\n| ember-cli  | `/var/www/discourse/log/ember-cli.log`|\n| caddy      | `/var/log/caddy.log`                  |\n| postgresql | `/var/log/postgres/current`           |\n| redis      | `/var/log/redis/current`              |\n\nView logs with:\n```bash\ndv run -- tail -f /var/www/discourse/log/unicorn.log\ndv run -- tail -f /var/www/discourse/log/ember-cli.log\ndv run --root -- tail /var/log/caddy.log\ndv run --root -- tail /var/log/postgres/current\ndv run --root -- tail /var/log/redis/current\n```\n\n## File Structure\n\n```\n.\n├── internal/\n│   └── assets/\n│       ├── Dockerfile      # Embedded container definition used by dv build\n│       └── dockerfile.go   # Embed/resolve logic (env + XDG overrides)\n├── cmd/\n│   └── dv/                 # dv binary entrypoint\n├── internal/\n│   ├── cli/                # dv subcommands (build, run, stop, ...)\n│   ├── config/             # JSON config load/save\n│   ├── docker/             # Docker CLI wrappers\n│   └── xdg/                # XDG path helpers\n├── bin/                    # Legacy bash scripts (being replaced by dv)\n├── README.md\n└── ai-agents.md            # Guidance for AI agents contributing here\n```\n\n## Development Workflow (using dv)\n\n1. Build image:\n   ```bash\n   dv build\n   ```\n2. Develop inside the container:\n   ```bash\n   dv start\n   dv enter\n   # Work with Discourse at /var/www/discourse\n   ```\n3. Extract changes to a local clone and commit:\n   ```bash\n   dv extract\n   # For the default Discourse workdir; custom workdirs land in $(dv data)/\u003cslug\u003e_src\n   cd $(dv data)/discourse_src\n   git add . \u0026\u0026 git commit -m \"Your message\"\n   ```\n\n## Releases\n\nThis project uses automated GitHub releases with cross-platform binary builds for macOS and Linux.\n\n### Creating a Release\n\n1. **Using the release script** (recommended):\n   ```bash\n   ./scripts/release.sh v1.0.0\n   # or automatically bump the patch version based on the latest GitHub release\n   ./scripts/release.sh --auto\n   ```\n\n2. **Manual process**:\n   ```bash\n   git tag -a v1.0.0 -m \"Release v1.0.0\"\n   git push origin v1.0.0\n   ```\n\n### What Happens Automatically\n\nWhen you push a tag starting with `v`, GitHub Actions will:\n\n1. **Build binaries** for:\n   - Linux (amd64, arm64)\n   - macOS (amd64, arm64)\n\n2. **Create a GitHub release** with:\n   - Release notes from git commits\n   - Binary downloads for each platform\n   - Checksums for verification\n\n3. **Archive format**:\n   - Linux: `.tar.gz`\n   - macOS: `.tar.gz`\n   - All platforms include README.md and LICENSE\n\n### Version Information\n\nCheck the version of your `dv` binary:\n```bash\ndv version\n```\n\nThis will show the version, git commit, and build date.\n\n### Release Configuration\n\nThe release process is configured in:\n- `.github/workflows/release.yml` - GitHub Actions workflow\n- `.goreleaser.yml` - GoReleaser configuration for builds and packaging\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiscourse%2Fdv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiscourse%2Fdv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiscourse%2Fdv/lists"}