Skip to content

symfonic.diagnostics.cli

cli

Typer command for environment-only and scoped native project diagnostics.

doctor

doctor(project_path: Annotated[Path | None, Argument(help='Adopter project root containing ``app/setup/agent.py``. A live project requires --scope. Without a project, only the process environment is inspected; no agent is built.')] = None, scope: Annotated[str | None, Option('--scope', help='Tenant path, optionally with principal and session, for a live project audit.')] = None, strict: Annotated[bool, Option('--strict', help='Promote WARN findings to exit code 2.')] = False, no_network: Annotated[bool | None, Option('--no-network/--network', help='For PROJECT_PATH, skip project import and composition as well as network probes; those checks report UNKNOWN. Default honours CI=true. --network composes adopter code, which may do I/O.')] = None, json_output: Annotated[bool, Option('--json', help='Emit a single JSON document on stdout instead of the human-readable report.  Suppresses all stderr progress.')] = False, offline: Annotated[bool, Option('--offline', help='Run the migration inspection instead of the live audit: no agent is constructed, nothing in PROJECT_PATH is imported, and no socket is opened.')] = False, config: Annotated[Path | None, Option('--config', help='JSON or TOML file holding the raw adopter configuration to inspect (--offline only).')] = None, env_file: Annotated[Path | None, Option('--env-file', help='KEY=value file holding the environment the inspected deployment runs with. Without it the platform-security checks are recorded as not inspected rather than answered from this machine (--offline only).')] = None, migration_report: Annotated[Path | None, Option('--migration-report', help='Write the Markdown migration inspection report to this path (--offline only).')] = None, package_version: Annotated[str | None, Option('--package-version', help='symfonic-core version of the inspected deployment (--offline only).')] = None) -> None

Inspect native project composition or the process environment.

Source code in src/symfonic/diagnostics/cli.py
def doctor(
    project_path: Annotated[
        Path | None,
        typer.Argument(
            help="Adopter project root containing ``app/setup/agent.py``. "
            "A live project requires --scope. Without a project, only "
            "the process environment is inspected; no agent is built.",
        ),
    ] = None,
    scope: Annotated[
        str | None,
        typer.Option(
            "--scope",
            help="Tenant path, optionally with principal and session, for a live project audit.",
        ),
    ] = None,
    strict: Annotated[
        bool,
        typer.Option(
            "--strict",
            help="Promote WARN findings to exit code 2.",
        ),
    ] = False,
    no_network: Annotated[
        bool | None,
        typer.Option(
            "--no-network/--network",
            help=(
                "For PROJECT_PATH, skip project import and composition as well "
                "as network probes; those checks report UNKNOWN. Default "
                "honours CI=true. --network composes adopter code, which may do I/O."
            ),
        ),
    ] = None,
    json_output: Annotated[
        bool,
        typer.Option(
            "--json",
            help=(
                "Emit a single JSON document on stdout instead of the "
                "human-readable report.  Suppresses all stderr progress."
            ),
        ),
    ] = False,
    offline: Annotated[
        bool,
        typer.Option(
            "--offline",
            help=(
                "Run the migration inspection instead of the live audit: no "
                "agent is constructed, nothing in PROJECT_PATH is imported, and "
                "no socket is opened."
            ),
        ),
    ] = False,
    config: Annotated[
        Path | None,
        typer.Option(
            "--config",
            help=(
                "JSON or TOML file holding the raw adopter configuration to "
                "inspect (--offline only)."
            ),
        ),
    ] = None,
    env_file: Annotated[
        Path | None,
        typer.Option(
            "--env-file",
            help=(
                "KEY=value file holding the environment the inspected deployment "
                "runs with. Without it the platform-security checks are recorded "
                "as not inspected rather than answered from this machine "
                "(--offline only)."
            ),
        ),
    ] = None,
    migration_report: Annotated[
        Path | None,
        typer.Option(
            "--migration-report",
            help=(
                "Write the Markdown migration inspection report to this path "
                "(--offline only)."
            ),
        ),
    ] = None,
    package_version: Annotated[
        str | None,
        typer.Option(
            "--package-version",
            help=(
                "symfonic-core version of the inspected deployment "
                "(--offline only)."
            ),
        ),
    ] = None,
) -> None:
    """Inspect native project composition or the process environment."""
    _reject_offline_only_options(
        offline,
        config=config,
        env_file=env_file,
        migration_report=migration_report,
        package_version=package_version,
    )
    if scope is not None and (offline or project_path is None):
        typer.echo("error: --scope requires a live PROJECT_PATH")
        raise SystemExit(2)
    if offline:
        _run_offline_mode(
            project_path,
            config=config,
            env_file=env_file,
            migration_report=migration_report,
            package_version=package_version,
            strict=strict,
            json_output=json_output,
        )
        return

    effective_no_network = resolve_no_network(explicit_flag=no_network)
    if project_path is not None:
        if not scope:
            typer.echo("error: live PROJECT_PATH requires --scope")
            raise SystemExit(2)
        from symfonic.diagnostics.native_cli import inspect_project

        report = inspect_project(
            project_path, scope=scope, no_network=effective_no_network,
            json_output=json_output,
        )
        if json_output:
            typer.echo(json.dumps(report.to_json_dict(strict=strict), indent=2))
        else:
            _render_human(report)
        raise SystemExit(report.exit_code(strict=strict))

    from symfonic.diagnostics.native_cli import inspect_environment

    report = inspect_environment(no_network=effective_no_network, json_output=json_output)

    if json_output:
        typer.echo(json.dumps(report.to_json_dict(strict=strict), indent=2))
    else:
        _render_human(report)

    raise SystemExit(report.exit_code(strict=strict))