Skip to content

feat: amp up — spin up a containerised dev instance per worktree #3

Description

@mdawess

Problem Statement

Two related pain points block a smooth per-worktree dev workflow:

  1. Environment variables from ~/.zshrc are not available inside tmux sessions or to docker-compose. When tmux creates a new session it starts a login shell, not an interactive shell, so ~/.zshrc is never sourced and exported vars are absent. Today this means copy-pasting vars into every worktree. The same problem affects other team members: each person already has their secrets in their shell config but amp-spawned processes cannot see them.

  2. There is no single command to stand up a dev instance and know where to reach it. Once docker-compose is running the developer must manually look up the Tailscale IP, cross-reference port mappings, and construct service URLs. There is no handoff to the phone.

Solution

A new amp up command that:

  1. Captures the full shell environment by invoking the user's shell interactively (zsh -i -c "env", bash -i -c "env", etc. — detected from $SHELL), so all vars exported in ~/.zshrc (or equivalent) are available without any additional config files
  2. Runs docker-compose up -d inside the target worktree with that environment injected
  3. Resolves the machine's Tailscale IPv4 address via tailscale ip -4
  4. Constructs service URLs from the Tailscale IP and port mappings configured in .amp.yaml
  5. Fires a notification hook with the service URLs and any configured credential hints

Each team member sets their own secrets in their own shell config once and amp up picks them up automatically — no .env files to copy, no secrets in the repo.

A required_env list in .amp.yaml (committed) documents which vars must be present, so new team members know exactly what to add to their shell config. amp env check validates them before bringing the stack up.

User Stories

  1. As a developer, I want amp up to source my ~/.zshrc automatically so that all my exported vars are available to docker-compose without any extra setup.
  2. As a new team member, I want to know exactly which env vars I need to add to my shell config so that I can get the stack running without asking anyone.
  3. As a developer, I want amp env check to tell me which required vars are missing from my shell environment so that I can fix them before amp up fails mid-run.
  4. As a developer, I want amp up to fail fast with a clear message if required env vars are absent so that I don't get cryptic docker errors.
  5. As a developer, I want amp up to detect my shell from $SHELL automatically so that it works for team members on bash, zsh, or fish without configuration.
  6. As a developer, I want env vars injected into the docker-compose subprocess environment so that services receive them without needing a .env file written into the worktree.
  7. As a developer, I want amp up to resolve my Tailscale IPv4 address automatically so that service URLs are correct without me looking anything up.
  8. As a developer, I want to configure service names and their ports in .amp.yaml so that amp up can construct human-readable URLs (e.g. frontend: 3000http://<tailscale-ip>:3000).
  9. As a developer, I want to receive a notification when amp up completes so that I know the instance is ready from my phone.
  10. As a developer, I want the notification to include the service URLs so that I can open them directly from the notification.
  11. As a developer, I want the notification to optionally include credential hints configured in .amp.yaml so that I can log in immediately.
  12. As a developer, I want amp up to target the current directory's worktree by default, with an optional --branch flag to target a different one.
  13. As a developer, I want amp down to stop the docker-compose stack in a worktree so that I can tear down instances cleanly.
  14. As a developer, I want amp run to also load the shell environment before spawning the agent so that the agent process has the same vars as an interactive session.

Implementation Decisions

Shell env capture

When amp up (and amp run) need the full user environment, they run:

$SHELL -i -c "env"

This starts an interactive shell (which sources ~/.zshrc, ~/.bashrc, etc.), dumps all exported vars, and exits. amp parses the output into a map[string]string and merges it into the subprocess environment before spawning docker-compose or the agent. The capture is done once per invocation and cached in memory — not written to disk.

Fish shell does not support -i -c "env" in the same way; for fish, fish -c "fish_variables; env" is used instead (or the user can configure an explicit shell override in .amp.yaml).

Required env documentation

.amp.yaml gains a required_env key listing var names:

required_env:
  - DATABASE_URL
  - STRIPE_SECRET_KEY
  - AMP_DEV_PASSWORD

This file is committed. amp env check reads it, captures the shell env, and reports any missing keys. amp up runs amp env check automatically and aborts if any are missing.

Service URL construction

.amp.yaml gains an up section:

up:
  services:
    frontend: 3000
    api: 8080
  credentials:
    user: admin@example.com
    password: "{{AMP_DEV_PASSWORD}}"

{{KEY}} placeholders in the credentials block are resolved from the captured shell env at notification time — they are never stored in YAML or on disk.

Tailscale IP resolution

Run tailscale ip -4 as a subprocess. If Tailscale is not running or returns no address, amp up fails with a descriptive error. The IP is stable and does not rotate, so no caching is needed.

Notification payload

The notification hook receives {{urls}}, {{credentials}}, {{branch}}, and {{status}} template variables. {{urls}} is a newline-separated list of <service>: <url> pairs.

Relationship to amp run

amp run gains an implicit shell-env capture step before the agent is spawned. This is always on — if the shell env capture fails (e.g. the user's .zshrc has errors), amp run prints a warning and falls back to the inherited process environment.

Testing Decisions

Good tests assert on observable outputs rather than internal call sequences.

ShellEnvCapture — test that a known-exported var appears in the parsed map; test that the correct shell binary is selected from $SHELL; test that a failed subprocess returns a clear error; test that the fallback to inherited env works when capture fails.

EnvValidator (amp env check) — test that all required keys present passes; that a missing key produces a named error listing the absent var; that an empty required_env list always passes.

TailscaleResolver — test the happy path against a stub subprocess returning a valid IP; test that a non-zero exit or empty output returns a clear error.

ServiceURLBuilder — test that port mappings produce correctly formed URLs given an IP; test that {{KEY}} credential resolution works end-to-end.

amp up orchestration (docker-compose spawn, notification) is exercised by manual end-to-end verification.

Out of Scope

  • Per-worktree env overrides
  • Secret rotation or expiry
  • docker-compose log streaming (use docker-compose logs -f directly)
  • Automatic port conflict detection across running worktrees
  • Any hosted secret store integration (1Password, Vault)

Further Notes

The shell-env capture approach means no new file format to learn and no onboarding step beyond "add these vars to your shell config" — which most developers already do for other tools. The required_env list in .amp.yaml is the single source of truth for what those vars are.

Credential hints in the notification are optional and deliberately minimal. Teams uncomfortable putting even test credentials in .amp.yaml can omit the credentials block; the notification still carries the service URLs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions