CLI Behavior

This section specifies the behavior of each cargo bp subcommand.

Crate sources

r[cli.source.flag] cargo bp --crate-source <path> MUST use a local workspace as the battery pack source, replacing crates.io. The <path> MUST point to a directory containing a Cargo.toml with [workspace].

r[cli.source.discover] When a crate source is specified, cargo bp MUST scan the workspace members for crates whose names end in -battery-pack and make them available as battery packs.

r[cli.source.replace] When --crate-source is specified, it MUST fully replace crates.io. No network requests to crates.io are made.

r[cli.source.multiple] The --crate-source flag MAY be specified multiple times to add multiple local workspaces.

r[cli.source.subcommands] The --crate-source flag MUST be accepted by all subcommands that resolve battery packs: add, new, show, list, status, and sync.

r[cli.source.scope] The --crate-source flag is a per-invocation option that replaces the default crates.io source with local directories. It does not persist across invocations.

Path flag

r[cli.path.flag] cargo bp --path <path> MUST read a battery pack from the given directory. Unlike --crate-source, which adds a searchable workspace, --path identifies a single battery pack directory directly.

r[cli.path.subcommands] The --path flag MUST be accepted by all subcommands that operate on a specific battery pack: add, new, show, check, validate, status, and sync.

r[cli.path.no-resolve] When --path is provided, name resolution is not needed. The battery pack is read directly from the given directory.

Non-interactive mode

r[cli.non-interactive.flag] cargo bp --non-interactive (or -N) MUST suppress interactive prompts and TUI mode. This is a global flag accepted by all subcommands.

r[cli.non-interactive.env] Setting CARGO_BP_NON_INTERACTIVE=true MUST have the same effect as passing --non-interactive. The flag and env var are combined with OR logic.

r[cli.non-interactive.tty] When stdout is not a TTY, cargo bp MUST behave as if --non-interactive were passed.

Name resolution

r[cli.name.resolve] When a battery pack name is given without the -battery-pack suffix, the CLI MUST resolve it by appending -battery-pack. For example, cli resolves to cli-battery-pack.

r[cli.name.exact] If the user provides a full crate name ending in -battery-pack, it MUST be used as-is without further modification.

cargo bp (no arguments)

r[cli.bare.tui] Running cargo bp with no subcommand and no flags MUST print the available subcommands and exit. This is the default clap behavior when a required subcommand is missing.

r[cli.bare.help] Running cargo bp --help MUST print CLI help text and exit.

cargo bp add

r[cli.add.register] cargo bp add <pack> MUST register the battery pack in the project's metadata and add the default crates to the appropriate dependency sections.

r[cli.add.default-crates] When no -F/--features, --no-default-features, or --all-features flags are given, cargo bp add <pack> MUST add the crates from the battery pack's default feature (or all non-optional crates if no default feature exists).

r[cli.add.features] cargo bp add <pack> -F <name> (or --features <name>) MUST add all crates from the named feature. Unless --no-default-features is specified, the default crates are also included.

r[cli.add.features-multiple] Multiple features MAY be specified as a comma-separated list (-F indicators,fancy) or by repeating the flag (-F indicators -F fancy).

r[cli.add.no-default-features] cargo bp add <pack> --no-default-features MUST add no crates by itself. Combined with -F, it adds only the named feature's crates.

r[cli.add.all-features] cargo bp add <pack> --all-features MUST add every crate the battery pack offers, regardless of features or optional status.

r[cli.add.specific-crates] cargo bp add <pack> <crate> [<crate>...] MUST add only the named crates from the battery pack, ignoring defaults and features.

r[cli.add.dep-kind] Each crate MUST be added with the dependency kind matching its section in the battery pack's Cargo.toml (regular, dev, or build), unless the user overrides it.

r[cli.add.target] cargo bp add <pack> --target <level> controls where the battery pack registration is stored. The <level> MUST be one of:

  • workspace — register in [workspace.metadata.battery-pack]
  • package — register in [package.metadata.battery-pack]
  • default — use workspace if a workspace root exists, otherwise package

If --target is not specified, the default behavior MUST be used.

r[cli.add.unknown-crate] When specific crates are named (cargo bp add <pack> <crate>...) and a named crate does not exist in the battery pack, cargo bp MUST report an error for that crate. Other valid crates in the same command MUST still be processed.

r[cli.add.idempotent] Adding a battery pack that is already registered MUST NOT create duplicate entries. If the battery pack is already present, cargo bp add MUST update its version and sync any new crates.

Template merging

r[cli.add.template-flag] cargo bp add <pack> --template <name> (or -t <name>) MUST render the named template and merge the output into the current project directory. This does not register the battery pack or add its curated crates; it only applies the template files.

r[cli.add.template-project-name] When merging a template, cargo bp MUST infer project_name from the current Cargo.toml [package].name. If no Cargo.toml exists or it has no [package].name, the current directory name MUST be used as a fallback.

r[cli.add.template-define] cargo bp add <pack> -t <name> --define <key>=<value> (or -d) MUST set the named placeholder to the given value, skipping the prompt for that placeholder. Multiple -d flags MAY be provided.

r[cli.add.template-merge-toml] When a template produces a .toml file and the target file already exists, cargo bp MUST merge using TOML-aware logic: for Cargo.toml, dependencies are synced (version upgraded if behind, features unioned, never removed); for all .toml files, sections and keys are recursively merged (inserted if absent, left alone if present). The user's existing formatting MUST be preserved.

r[cli.add.template-merge-yaml] When a template produces a .yml or .yaml file and the target file already exists, cargo bp MUST merge using YAML-aware logic: top-level mapping keys are merged additively. For known GitHub Actions keys (jobs, on, permissions), child maps are also merged additively. Existing keys are never removed.

r[cli.add.template-merge-plain] When a template produces any other file and the target file already exists, cargo bp MUST prompt the user to skip, overwrite, or view a diff.

r[cli.add.template-overwrite] cargo bp add <pack> -t <name> --overwrite MUST force overwrite all plain file conflicts without prompting. Structured file merges (TOML, YAML) MUST still use merge logic.

r[cli.add.template-non-interactive] In non-interactive mode, conflicts with files that are not .toml, .yml, or .yaml MUST be skipped unless --overwrite is passed. TOML and YAML merges MUST still apply.

r[cli.add.template-hints] If the template's bp-template.toml declares [[hints]] entries, cargo bp MUST print them after the merge summary.

r[cli.add.template-git-dirty] Before applying template files, cargo bp MUST check for uncommitted git changes. In interactive mode, it MUST warn and prompt for confirmation. In non-interactive mode, it MUST refuse unless --overwrite is passed. If the directory is not a git repository, the check MUST be skipped.

r[cli.add.template-batch] When prompting for conflict resolution, cargo bp MUST offer batch options. For TOML and YAML merge prompts: "accept all" and "skip all". For other file prompts: "overwrite all" and "skip all". Batch options apply to all remaining conflicts without further prompting.

r[cli.add.template-edit] When prompting for structured merge conflicts (TOML, YAML), cargo bp MUST offer an "edit" option that opens the merged result in $VISUAL, $EDITOR, or vi (in that order). After editing, the updated diff MUST be shown and the user MUST be returned to the accept/skip/edit prompt.

cargo bp new

r[cli.new.template] cargo bp new <pack> MUST create a new project from the battery pack's template using the built-in template engine.

r[cli.new.name-flag] cargo bp new <pack> --name <name> MUST pass the project name to the template engine, skipping the name prompt.

r[cli.new.name-prompt] If --name is not provided, the CLI MUST prompt the user for a project name.

r[cli.new.template-select] If the battery pack has multiple templates and --template is not provided, the CLI MUST prompt the user to select one.

r[cli.new.template-flag] cargo bp new <pack> --template <name> MUST use the specified template without prompting.

r[cli.new.define-flag] cargo bp new <pack> --define <key>=<value> (or -d) MUST set the named placeholder to the given value, skipping the prompt for that placeholder. Multiple -d flags MAY be provided.

r[cli.new.non-interactive] In non-interactive mode, cargo bp new MUST fail with an error if --name is not provided. Template placeholders without a default or --define override MUST also cause an error.

cargo bp status

r[cli.status.list] cargo bp status MUST list all installed battery packs with their registered versions.

r[cli.status.version-warn] For each installed battery pack, cargo bp status MUST display a warning for each dependency whose version is older than what the battery pack recommends. Dependencies with equal or newer versions MUST NOT produce a warning.

r[cli.status.no-project] If run outside a Rust project, cargo bp status MUST report that no project was found.

cargo bp sync

r[cli.sync.update-versions] cargo bp sync MUST update dependency versions that are older than what the installed battery packs recommend. Versions that are equal to or newer than recommended MUST be left unchanged.

r[cli.sync.add-features] cargo bp sync MUST add any Cargo features that the battery pack specifies but are missing from the user's dependency entry. Existing user-added features MUST be preserved.

r[cli.sync.add-crates] cargo bp sync MUST add any crates that belong to the user's active features but are missing from the user's dependencies. Existing crates MUST NOT be removed.

cargo bp list

r[cli.list.query] cargo bp list MUST query crates.io for crates with the battery-pack keyword.

r[cli.list.filter] cargo bp list <filter> MUST filter results by name pattern.

r[cli.list.interactive] If running in a TTY, cargo bp list SHOULD display results in the interactive TUI.

r[cli.list.non-interactive] In non-interactive mode, cargo bp list MUST print results as plain text.

cargo bp check

r[cli.check.purpose] cargo bp check MUST validate that installed battery packs match the project's current dependencies and warn about version drift.

r[cli.check.version-drift] cargo bp check MUST compare the user's current dependency versions against the versions recommended by installed battery packs and warn when user versions are older than recommended versions.

r[cli.check.output] cargo bp check MUST display the status of each installed battery pack with clear indicators (✅ for up-to-date, ⚠️ for outdated versions).

r[cli.check.no-packs] If no battery packs are installed, cargo bp check MUST display "No battery packs installed." and exit successfully.

cargo bp validate

r[cli.validate.purpose] cargo bp validate MUST check whether a battery pack crate conforms to the battery pack format specification (format.* rules).

r[cli.validate.default-path] If --path is not provided, cargo bp validate MUST validate the battery pack in the current directory.

r[cli.validate.checks] cargo bp validate MUST check all applicable format.* rules, including both data-level checks (from the parsed Cargo.toml) and filesystem-level checks (on-disk structure).

r[cli.validate.severity] Violations of MUST rules MUST be reported as errors. Violations of SHOULD rules MUST be reported as warnings.

r[cli.validate.rule-id] Each diagnostic MUST include the spec rule ID in its output (e.g., error[format.crate.name]: ...).

r[cli.validate.clean] When a battery pack passes all checks with no diagnostics, cargo bp validate MUST print <name> is valid and exit successfully.

r[cli.validate.warnings-only] When a battery pack has warnings but no errors, cargo bp validate MUST print <name> is valid (<N> warning(s)) and exit successfully.

r[cli.validate.errors] When a battery pack has one or more errors, cargo bp validate MUST exit with a non-zero status.

r[cli.validate.workspace-error] If the target Cargo.toml is a workspace manifest (contains [workspace] but no [package]), cargo bp validate MUST report a clear error directing the user to run from a battery pack crate directory or use --path.

r[cli.validate.no-package] If the target Cargo.toml has no [package] section and is not a workspace manifest, cargo bp validate MUST report a clear error indicating the file is not a battery pack crate.

r[cli.validate.templates] cargo bp validate MUST generate each declared template into a temporary directory, then run cargo check and cargo test on the result. If any template fails to compile or its tests fail, validation MUST fail.

r[cli.validate.templates.patch] When validating templates, cargo bp validate MUST patch crates-io dependencies with local workspace packages so that validation runs against the current source.

r[cli.validate.templates.cache] Compiled artifacts from template validation SHOULD be cached in <target_dir>/bp-validate/ so that subsequent runs are faster.

r[cli.validate.templates.none] If the battery pack declares no templates, template validation MUST be skipped.

cargo bp show

r[cli.show.details] cargo bp show <pack> MUST display the battery pack's name, version, description, curated crates, features, templates, and examples.

r[cli.show.hidden] cargo bp show MUST NOT display hidden dependencies.

r[cli.show.interactive] If running in a TTY, cargo bp show SHOULD display results in the interactive TUI.

r[cli.show.non-interactive] In non-interactive mode, cargo bp show MUST print results as plain text.

r[cli.show.template-preview] cargo bp show <pack> --template <name> MUST render the named template and display the resulting files. In a TTY, the output SHOULD be shown in the interactive TUI preview screen. With --non-interactive, the rendered files MUST be printed to stdout. Placeholders without a default MUST fall back to <name> so the preview always succeeds. The project name MUST default to my-project.

r[cli.show.define-flag] cargo bp show <pack> -t <name> --define <key>=<value> (or -d) MUST set the named placeholder to the given value in the rendered preview. Multiple -d flags MAY be provided.