Skip to content

Local configuration

base-cli does not read machine-local or project configuration implicitly. Standalone applications can accept an explicit --config file through the generic profile, or provide their own load_user_config and load_config callbacks for application-owned configuration sources.

Command-line --config values are strict: the path is expanded and must be an existing, readable regular file before profile and runtime startup. By contrast, base_cli.config.load_yaml_file(path) keeps its optional-file behavior for profile-discovered configuration; pass required=True when a consumer-owned call site represents an explicit user request.

The consumer owns the configuration schema, merge semantics, and operational choice of whether to back up or synchronize its machine-local files.

Applications that prefer conventional policy can opt into CliProfile.batteries_included("tool") after installing the base-cli[yaml] extra. It loads optional platform-aware user, project, environment, and explicit YAML layers with documented precedence and records the winning source for each key in Context.config_provenance. Its reserved lifecycle keys are validated separately as Context.framework_config; consumer-owned keys remain in Context.config. CliProfile.generic() remains the convention-free default.

For direct use, BatteriesIncludedConfigLoader accepts an optional cli_name. When no user_config_dir is supplied, that identity selects an isolated directory below the platform's default config root (for example, ~/.config/tool on Linux). An already-safe identity such as MyTool, Alpha-Tool, or acme.tools keeps its spelling so existing configuration directories continue to be read. Identities that need normalization, such as Alpha Tool or a path-like value, use a readable filesystem-safe slug plus a stable identity digest; this keeps them distinct from already-safe names. Consumers with an existing configuration-root policy should pass user_config_dir explicitly; that path remains authoritative. Existing directories created by an older normalized policy are not merged, moved, or deleted automatically; a consumer that needs migration must copy them under its chosen policy.

Trust of discovered project configuration

CliProfile.batteries_included() validates implicit project configuration before loading it. On POSIX, the file and directories between the working directory and the discovered project must be owned by the invoking user or root and must not be writable by group/other. Symlinks and Windows reparse points are refused, including project environment files. Refusals raise ConfigurationError naming the path. Windows ACL ownership/write permissions are not evaluated; applications using shared Windows workspaces must provide their own discovery/trust policy.

Discovery checks the current directory and at most 32 ancestors, stops at .git (including worktree marker files), and never crosses a filesystem boundary. max_project_ancestor_depth=0 restricts discovery to the current directory; project_boundary_marker changes the marker or accepts None to disable markers. verify_discovered_config=False explicitly opts out of permission/reparse checks for knowingly shared workspaces. It does not disable depth/filesystem limits. Custom discovery callbacks own discovery boundaries; their project files still receive the loader's trust checks. CliProfile.generic() remains unchanged.

All YAML files are limited to 1 MiB of UTF-8 input, 64 container levels, and 100,000 visited values (including alias expansion); recursive aliases are refused. Explicit --config is an intentional file choice and bypasses discovery trust, but still uses these parsing bounds.