Interactive wizards are great for humans and terrible for machines. A CI job, a Docker build, or a setup script cannot answer prompts, and a tool that blocks on input in one of those contexts is broken. That is why pyrpc dev ships a fully non-interactive mode behind a single flag: --yes.
Fully explicit: --yes --module --client
When you supply both values on the command line, nothing is detected and nothing is asked:
if yes and module and client:
# Fully non-interactive: all values supplied on the command line.
cfg = {"module": module, "framework": "Other", "client": client}
if cfg_path is None:
cfg_path = _write_config(cfg)
console.print(f" [green]✓[/green] pyrpc.json created")This is the deterministic form for CI: pyrpc dev --yes --module main --client ../frontend. The same command run a hundred times produces the same config. It writespyrpc.json only if one does not already exist, a pre-existing config is respected and read instead.
Auto-detected: --yes alone
With --yes but no explicit flags, the CLI auto-detects everything it can:
- Module, first match of
main.py,server.py,app.py,app/main.py. - Client + framework, via
_find_frontend_projects. Exactly one project → it is used. Several projects → hard error listing them and pointing at--client. None → no client configured, framework"Other".
The multiple-projects case deserves emphasis. Rather than silently pick the first frontend it finds, a guess that would generate types into the wrong app, the CLI refuses and tells you exactly how to disambiguate:
elif len(detected_projects) > 1:
console.print("[red]✗ Multiple TypeScript projects found.[/red]\n")
for p, _ in detected_projects:
console.print(f" • {p}")
console.print("\n[dim]Specify which client to use:[/dim]\n")
console.print(" [cyan]pyrpc dev --client <path>[/cyan]\n")
raise typer.Exit(1)Non-interactive mode still refuses to guess in ambiguous situations, it just reports the ambiguity in a machine-readable way (a non-zero exit) instead of asking a human.
The result is a summary line, not a dialog
After detection, --yes prints a compact line that captures the decision:
if resolved_client is not None:
console.print(f" [dim]module={cfg['module']} client={cfg['client']}[/dim]")
else:
console.print(f" [dim]module={cfg['module']} (no client configured)[/dim]")With no client configured, type generation is skipped at startup and the dev console shows○ no clients configured, skipping type generation. This is a legitimate state: a pure-API server, or a frontend that will be configured later.
Where non-interactive mode shines
- CI, regenerate types in a build step with
--yes --module main --client ./frontend, deterministic every run. - Container image builds, a Dockerfile layer that pre-generates types cannot hang on a prompt;
--yesguarantees it cannot. - Scripted onboarding, repo setup scripts, devcontainer post-create hooks, and Makefile targets can configure pyRPC without user input.
--yes rounds out the setup matrix: the wizard for humans, explicit flags for the deterministic case, and auto-detection for the “I just want it to work” case. Each path ends in the same pyrpc.json.
Read the full changelogfor the complete list of changes.

pyRPC