# start
- [Basic Usage](#basic-usage)
- [Synopsis](#synopsis)
- [Options](#options)
- [Behavior](#behavior)
- [State File](#state-file)
- [Examples](#examples)
- [Troubleshooting](#troubleshooting)
- [Related](#related)
The `start` command spawns `flutter run` as a detached background process, scrapes the VM Service URI from its output, and writes `~/.artisan/state.json` so that every downstream tool (MCP server, `stop`, `restart`, `dusk:snap`, `tinker`, etc.) knows where the running app lives.
## Basic Usage
```bash
dart run artisan start
```
Launches a Chrome web dev session on port `3100` with the VM Service listener on port `8181`. The command exits as soon as the URI is confirmed; the Flutter process continues running in the background.
## Synopsis
```
dart run artisan start [--device=] [--port=] [--vm-service-port=]
[--[no-]dds] [--[no-]profile-static]
```
`start` accepts no positional arguments. All configuration is done via named options and flags declared in `configure(ArgParser)` (see `lib/src/commands/start_command.dart:38`).
## Options
| Option | Type | Default | Description |
|:-------|:-----|:--------|:------------|
| `--device` | string | `chrome` | Flutter device target. Accepts `chrome`, `macos`, `linux`, `windows`, an iOS UDID, or an Android serial number. |
| `--port` | int | `3100` | Web server port passed as `--web-port` to `flutter run`. Ignored for non-Chrome targets. |
| `--vm-service-port` | int | `8181` | VM Service listener port. Recorded in `state.json`; used by `mcp:serve` and connected-mode tools to open the WebSocket. |
| `--dds` | flag | `false` | Enable Dart Development Service. When absent (default), `--no-dds` is forwarded to `flutter run` so dusk and tinker connect directly to the VM Service. |
| `--profile-static` | flag | `false` | Tag the session as a static-profile run. Sets `profile: "static"` in `state.json`; otherwise `"debug"`. |
## Behavior
**FIFO stdin channel.** Before launching Flutter, `start` calls `mkfifo` to create a named pipe at `~/.artisan/flutter-dev.fifo`. A background `tail -f /dev/null` process holds the write end of the FIFO open permanently, preventing `flutter run`'s stdin from receiving EOF when no keystroke sender is active. `flutter run` is then launched with the FIFO as its stdin, so the `reload` and `hot-restart` commands can send `r` or `R` into the pipe and trigger flutter_tools' own handler. This approach works on every device target (web, desktop, mobile) because it routes through `flutter_tools` rather than raw VM Service RPCs, which the Chrome `dwds` bridge rejects.
**Detached spawn and URI scrape.** The shell one-liner run by `start` launches both background processes (`tail` holder and `flutter run`) and echoes their PIDs in `HOLDER=` / `FLUTTER=` format. `start` captures those PIDs from the wrapper's stdout, then polls the log file at `~/.artisan/flutter-dev.log` every 250 ms until it finds a line matching either the web format (`Debug service listening on ws://...`) or the desktop/mobile format (`Dart VM Service on is available at: http://...`). `http://` and `https://` URIs are normalized to their `ws://` and `wss://` equivalents and a `/ws` suffix is appended when missing.
**State file write.** After the URI is confirmed, `start` writes `~/.artisan/state.json` atomically (`.tmp` + rename) with the full process inventory: PIDs, FIFO path, VM Service URI, web port, device target, profile mode, project root, and a UTC `startedAt` timestamp. The MCP server (`mcp:serve`) reads this file at startup to discover the running app; `stop` reads it to send SIGTERM; `status` reads it to report the live process.
## State File
`start` writes `~/.artisan/state.json` atomically after a successful spawn. The schema (from `lib/src/state/state_file.dart:13-24`):
```json
{
"pid": 12345,
"stdinPipe": "/Users/you/.artisan/flutter-dev.fifo",
"stdinHolderPid": 12344,
"vmServiceUri": "ws://127.0.0.1:8181/AbCdEfGhIjK=/ws",
"webPort": 3100,
"vmServicePort": 8181,
"startedAt": "2026-05-19T10:00:00.000Z",
"profile": "debug",
"projectRoot": "/Users/you/Code/my-app",
"device": "chrome",
"chromePid": null,
"tmpProfileDir": null
}
```
Field reference:
| Field | Type | Notes |
|:------|:-----|:------|
| `pid` | int | PID of the `flutter run` process. Used by `stop` to send SIGTERM. |
| `stdinPipe` | string | Absolute path to the FIFO. `reload` and `hot-restart` write `r\n` / `R\n` here. |
| `stdinHolderPid` | int | PID of the `tail -f /dev/null` holder that keeps the FIFO write-end open. Killed alongside `pid` by `stop`. |
| `vmServiceUri` | string | Canonical `ws://host:port//ws` URI. All connected-mode tools open this WebSocket. |
| `webPort` | int | `--web-port` value forwarded to Flutter. Chrome only; ignored for other targets. |
| `vmServicePort` | int | Informational; the port embedded in `vmServiceUri`. |
| `startedAt` | string | ISO 8601 UTC timestamp of the `start` invocation. |
| `profile` | string | `"debug"` or `"static"` (set by `--profile-static`). |
| `projectRoot` | string | Absolute path to the working directory at invocation time. |
| `device` | string | The `--device` value passed to this run. |
| `chromePid` | int or null | Reserved for D6 Chrome capture (V1.x). Always `null` in V1. |
| `tmpProfileDir` | string or null | Reserved for D6 Chrome capture (V1.x). Always `null` in V1. |
## Examples
**Chrome web session (default):**
```bash
dart run artisan start
```
Equivalent to `dart run artisan start --device=chrome --port=3100 --vm-service-port=8181`.
**macOS desktop session:**
```bash
dart run artisan start --device=macos
```
`--port` is ignored for non-Chrome targets. Flutter receives `--no-dds` (the default) so the Dusk and Tinker tools connect directly to the VM Service socket.
**Custom web port with DDS enabled:**
```bash
dart run artisan start --device=chrome --port=4000 --dds
```
Starts the Chrome session on port `4000` and omits `--no-dds`, letting the Dart Development Service run. Use this when your tooling requires DDS (for example, a secondary IDE debugger).
## Troubleshooting
**Port already in use (`--port` collision).** If another process holds the web port, `flutter run` exits immediately and the log file shows `Error: Address already in use`. Pick a free port with `--port=` or stop the occupying process with `lsof -ti: | xargs kill`.
**`mkfifo` permission denied.** `start` creates the FIFO at `~/.artisan/flutter-dev.fifo`. If `~/.artisan/` was created by a previous run with different ownership or mode bits, `mkfifo` fails. Fix with `rm -rf ~/.artisan && dart run artisan start`. Note: `start` is POSIX-only (macOS/Linux). It will not work on Windows.
**VM Service URI never appears (90-second timeout).** If `flutter run` stalls before printing the URI (for example, the Chrome binary is missing, the Flutter SDK is not on `PATH`, or a Dart compilation error occurs), `start` throws `StateError: Timed out after 90s...`. Inspect the log at `~/.artisan/flutter-dev.log` for the underlying Flutter output. Common causes: wrong `--device` value, missing `CHROME_EXECUTABLE` env var for headless environments, or a syntax error in the app's entry point.
## Related
- [stop](stop.md): send SIGTERM to the running Flutter process and delete `state.json`.
- [restart](restart.md): full stop + start cycle; preserves all flags from the original invocation.
- [reload](reload.md): send `r\n` to the FIFO for a hot reload without a full restart.
- [hot-restart](hot-restart.md): send `R\n` to the FIFO for a hot restart that resets app state.
- [mcp:serve](../mcp/mcp-serve.md): start the stdio JSON-RPC MCP server; reads `~/.artisan/state.json` to discover the running app.