Skip to content

astro dev --background #1358

@matthewp

Description

@matthewp

Summary

Add built-in background process management to astro dev, enabling AI coding agents to reliably start, monitor, query and stop the dev server.

Background & Motivation

AI coding agents are increasingly the primary interface developers use to build Astro sites. These agents need to interact with the dev server constantly: starting it, checking if edits succeeded, reading errors and stopping it when done.

Today, this is painful. The dev server is designed for humans watching a terminal. Agents struggle with:

  • Long-running processes. Most agents shell out, wait for exit, and read output. A dev server never exits. Agents either hang, or start it and lose track of it.
  • Readiness detection. There is no reliable signal for "the server is ready to accept requests". Agents resort to polling with curl, sleeping for arbitrary durations, or parsing ANSI-coloured output for strings like "Local:".
  • HMR feedback. After editing a file, agents have no way to know whether the HMR update succeeded or failed without making an HTTP request and hoping the error page is parseable. This is the single biggest failure mode observed in agent workflows.
  • Error parsing. Build and runtime errors are formatted for human readability with colours, box-drawing characters and contextual snippets. Agents cannot reliably extract the file, line number and error type from this output.
  • Process lifecycle. Agents frequently start duplicate dev servers, lose track of running instances, or leave zombie processes behind. There is no way to check if a server is already running for the current project.

My bgproc project demonstrates that a thin process management layer with JSON output dramatically improves the agent experience with dev servers. Experiments by @FredKSchott demonstrated that bgproc reduced the time takes by an agent to triage an issue using astro dev by 81%, saved 13 tool calls and thousands of tokens. However, as a generic external wrapper, tools like bgproc are less discoverable and cannot provide framework-specific signals like HMR status, typed error codes or build state, and it relies upon heuristics to find ports that have been opened. Building this capability directly into Astro unlocks a significantly better experience.

Goals

  • Enable agents to start the dev server as a background process and receive structured JSON confirmation when it is ready, without polling or heuristics.
  • Provide a machine-readable feedback loop for file changes: did the HMR update succeed or fail, and if it failed, what was the error?
  • Ensure idempotent, forgiving behaviour: starting when already running reports the existing instance; stopping when not running succeeds silently. Agents are bad at tracking state and the CLI should not punish them for it.
  • Allow agents to query a running dev server (foreground or background) from a separate shell session.
  • Maintain full backward compatibility with the existing human-friendly astro dev experience.

Non-Goals

  • Implementing JSON logging. This is covered in the custom logging proposal and is not strictly required for the background process management features proposed here. However, the two features are complementary and should be designed to work together.
  • This proposal does not cover an Astro MCP server, though it could provide the foundation for one.
  • Background process management requires a POSIX environment (macOS, Linux or WSL). This is consistent with the agent ecosystem: Claude Code, Codex and Cursor CLI all require or strongly recommend a POSIX shell on Windows. The health endpoint would work on any platform regardless.

Example

Starting the dev server (background, agent workflow)

$ astro dev --background
# Blocks until server is ready, streams build logs to stderr
# On success, prints JSON to stdout and detaches:
{"pid": 12345, "url": "http://localhost:4321", "networkUrl": "http://192.168.1.5:4321"}

If the dev server is already running for this project, this is a no-op that returns the existing instance's details:

$ astro dev --background
{"pid": 12345, "url": "http://localhost:4321", "networkUrl": "http://192.168.1.5:4321", "existing": true}

Force restart with --force:

$ astro dev --background --force
# Kills existing instance, starts fresh, blocks until ready
{"pid": 12346, "url": "http://localhost:4321", "networkUrl": "http://192.168.1.5:4321"}

If the server fails to start within the timeout (default 30s):

$ astro dev --background
{"error": "timeout", "message": "Dev server failed to start within 30s", "logs": "..."}
# Exit code 1. Process is killed. No zombie left behind.

Checking status

$ astro dev status
{"running": true, "pid": 12345, "url": "http://localhost:4321", "uptime": 3600, "errors": [], "lastHmrEvent": {"type": "update", "file": "src/pages/index.astro", "at": "2026-02-18T10:23:01Z", "duration": 42}}

When nothing is running:

$ astro dev status
{"running": false}
# Exit code 0 — "not running" is information, not an error.

When the process has died:

$ astro dev status
{"running": false, "lastExit": {"code": 1, "signal": null, "at": "2026-02-18T10:20:00Z"}}
# Stale state is cleaned up automatically.

Reading logs

$ astro dev logs
$ astro dev logs --tail 50
$ astro dev logs --follow
$ astro dev logs --errors       # stderr only
$ astro dev logs --json         # only structured events

Stopping the server

$ astro dev stop
{"stopped": true, "pid": 12345}

Idempotent — stopping when not running succeeds:

$ astro dev stop
{"stopped": false, "reason": "not_running"}
# Exit code 0.

Health endpoint

All running dev server instances (foreground and background) expose a status endpoint:

$ curl -s http://localhost:4321/_astro/status
{"ok": true, "errors": [], "lastHmrUpdate": {"file": "src/pages/index.astro", "at": "...", "duration": 42}, "routes": ["/", "/about", "/blog/[...slug]"]}

This is the primary mechanism for agents to query a foreground dev server from another shell session. astro dev status uses this endpoint when the server is running but was started in foreground mode.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Stage 2: Accepted Proposals, No RFC

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions