Problem Statement
Two related pain points block a smooth per-worktree dev workflow:
-
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.
-
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:
- 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
- Runs
docker-compose up -d inside the target worktree with that environment injected
- Resolves the machine's Tailscale IPv4 address via
tailscale ip -4
- Constructs service URLs from the Tailscale IP and port mappings configured in
.amp.yaml
- 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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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: 3000 → http://<tailscale-ip>:3000).
- As a developer, I want to receive a notification when
amp up completes so that I know the instance is ready from my phone.
- As a developer, I want the notification to include the service URLs so that I can open them directly from the notification.
- As a developer, I want the notification to optionally include credential hints configured in
.amp.yaml so that I can log in immediately.
- 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.
- As a developer, I want
amp down to stop the docker-compose stack in a worktree so that I can tear down instances cleanly.
- 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:
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.
Problem Statement
Two related pain points block a smooth per-worktree dev workflow:
Environment variables from
~/.zshrcare 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~/.zshrcis 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.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 upcommand that: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 filesdocker-compose up -dinside the target worktree with that environment injectedtailscale ip -4.amp.yamlEach team member sets their own secrets in their own shell config once and
amp uppicks them up automatically — no.envfiles to copy, no secrets in the repo.A
required_envlist 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 checkvalidates them before bringing the stack up.User Stories
amp upto source my~/.zshrcautomatically so that all my exported vars are available to docker-compose without any extra setup.amp env checkto tell me which required vars are missing from my shell environment so that I can fix them beforeamp upfails mid-run.amp upto fail fast with a clear message if required env vars are absent so that I don't get cryptic docker errors.amp upto detect my shell from$SHELLautomatically so that it works for team members on bash, zsh, or fish without configuration..envfile written into the worktree.amp upto resolve my Tailscale IPv4 address automatically so that service URLs are correct without me looking anything up..amp.yamlso thatamp upcan construct human-readable URLs (e.g.frontend: 3000→http://<tailscale-ip>:3000).amp upcompletes so that I know the instance is ready from my phone..amp.yamlso that I can log in immediately.amp upto target the current directory's worktree by default, with an optional--branchflag to target a different one.amp downto stop the docker-compose stack in a worktree so that I can tear down instances cleanly.amp runto 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(andamp run) need the full user environment, they run:This starts an interactive shell (which sources
~/.zshrc,~/.bashrc, etc.), dumps all exported vars, and exits.ampparses the output into amap[string]stringand 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.yamlgains arequired_envkey listing var names:This file is committed.
amp env checkreads it, captures the shell env, and reports any missing keys.amp uprunsamp env checkautomatically and aborts if any are missing.Service URL construction
.amp.yamlgains anupsection:{{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 -4as a subprocess. If Tailscale is not running or returns no address,amp upfails 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 runamp rungains 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.zshrchas errors),amp runprints 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 emptyrequired_envlist 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 uporchestration (docker-compose spawn, notification) is exercised by manual end-to-end verification.Out of Scope
docker-compose logs -fdirectly)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_envlist in.amp.yamlis 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.yamlcan omit the credentials block; the notification still carries the service URLs.