The CLI is the main entrypoint for end users and for the interactive bot menu.

openenv.cli

openenv.cli

Command line interface for OpenClawenv.

build_parser()

Create the CLI parser.

Source code in src/openenv/cli.py
def build_parser() -> argparse.ArgumentParser:
    """Create the CLI parser."""
    parser = argparse.ArgumentParser(prog="clawopenenv", description="OpenClawenv CLI")
    subparsers = parser.add_subparsers(dest="command", required=True)

    init_parser = subparsers.add_parser("init", help=f"Create a starter {DEFAULT_MANIFEST_FILENAME}")
    init_parser.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest output path")
    init_parser.add_argument(
        "--force",
        action="store_true",
        help="Overwrite an existing manifest file",
    )

    validate_parser = subparsers.add_parser("validate", help=f"Validate {DEFAULT_MANIFEST_FILENAME}")
    validate_parser.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest path")

    lock_parser = subparsers.add_parser("lock", help=f"Generate {DEFAULT_LOCKFILE_FILENAME}")
    lock_parser.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest path")
    lock_parser.add_argument("--output", default=DEFAULT_LOCKFILE_FILENAME, help="Lockfile output path")

    scan_parser = subparsers.add_parser("scan", help="Run skill-scanner against inline skills")
    scan_parser.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest path")
    scan_parser.add_argument(
        "--scanner-bin",
        default="skill-scanner",
        help="Path to the skill-scanner executable",
    )
    scan_parser.add_argument(
        "--keep-artifacts",
        action="store_true",
        help=f"Keep the materialized skill directory in {DEFAULT_SCAN_ARTIFACTS_DIRNAME}",
    )
    scan_parser.add_argument(
        "scanner_args",
        nargs=argparse.REMAINDER,
        help="Additional arguments passed to skill-scanner after --",
    )

    export_parser = subparsers.add_parser("export", help="Export generated artifacts")
    export_subparsers = export_parser.add_subparsers(dest="export_command", required=True)
    dockerfile_parser = export_subparsers.add_parser(
        "dockerfile",
        help="Render the deterministic Dockerfile",
    )
    dockerfile_parser.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest path")
    dockerfile_parser.add_argument("--lock", default=DEFAULT_LOCKFILE_FILENAME, help="Lockfile path")
    dockerfile_parser.add_argument("--output", help="Optional Dockerfile output path")
    compose_parser = export_subparsers.add_parser(
        "compose",
        help="Render the docker-compose file for the bot image",
    )
    compose_parser.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest path")
    compose_parser.add_argument("--lock", default=DEFAULT_LOCKFILE_FILENAME, help="Lockfile path")
    compose_parser.add_argument("--tag", help="Docker image tag to reference")
    compose_parser.add_argument("--output", help="Optional compose output path")

    build_parser_cmd = subparsers.add_parser("build", help="Build the Docker image")
    build_parser_cmd.add_argument("--path", default=DEFAULT_MANIFEST_FILENAME, help="Manifest path")
    build_parser_cmd.add_argument("--lock", default=DEFAULT_LOCKFILE_FILENAME, help="Lockfile path")
    build_parser_cmd.add_argument("--tag", help="Docker image tag")
    build_parser_cmd.add_argument(
        "--scan-format",
        default=DEFAULT_SKILL_SCAN_FORMAT,
        help="Build-time skill scan format passed to the Dockerfile",
    )
    build_parser_cmd.add_argument(
        "--scan-policy",
        default=DEFAULT_SKILL_SCAN_POLICY,
        help="Build-time skill scan policy passed to the Dockerfile",
    )
    build_parser_cmd.add_argument(
        "--scan-fail-on-severity",
        default=DEFAULT_SKILL_SCAN_FAIL_ON_SEVERITY,
        help="Build-time skill scan severity threshold passed to the Dockerfile",
    )

    return parser

main(argv=None)

Program entry point.

Source code in src/openenv/cli.py
def main(argv: list[str] | None = None) -> int:
    """Program entry point."""
    _configure_logging()
    argv = sys.argv[1:] if argv is None else argv
    if not argv:
        return interactive_menu(Path.cwd())
    parser = build_parser()
    args = parser.parse_args(argv)
    try:
        if args.command == "init":
            return _handle_init(args)
        if args.command == "validate":
            return _handle_validate(args)
        if args.command == "lock":
            return _handle_lock(args)
        if args.command == "scan":
            return _handle_scan(args)
        if args.command == "export" and args.export_command == "dockerfile":
            return _handle_export_dockerfile(args)
        if args.command == "export" and args.export_command == "compose":
            return _handle_export_compose(args)
        if args.command == "build":
            return _handle_build(args)
        parser.error("unknown command")
    except CommandError as exc:
        logger.error("error: {}", exc)
        return exc.exit_code or 1
    except OpenEnvError as exc:
        logger.error("error: {}", exc)
        return 1
    return 0

openenv.__main__

openenv.__main__