Commitizen's CLI is built declaratively with decli and argparse in commitizen/cli.py. Flags are dicts inside a data["subcommands"] list. End-user documentation: Commands.
- "Add a
--<name>flag tocz <subcommand>." - "Make
--<name>configurable via the config file." - "Change the default of
--<flag>."
commitizen/cli.py— the entire CLI schema. Search for the subcommand name in thesubcommandsblock to find where itsargumentslist lives.commitizen/commands/<subcommand>.py— the command class that receives the parsed arguments viaself.arguments.commitizen/defaults.py:Settings— TypedDict of all settings; required if your flag should also be config-file-readable.tests/test_cli.pyandtests/test_cli/— flag-parsing tests.tests/commands/test_<subcommand>_command.py— behavior tests.docs/commands/<subcommand>.md— user-facing reference for the subcommand.scripts/gen_cli_help_screenshots.py— regenerates--helpSVGs.
-
Add the flag in
commitizen/cli.pyinside the relevant subcommand'sargumentslist. Follow the existing dict shape:{ "name": ["--my-flag", "-m"], # or just "--my-flag" if no short "action": "store_true", # or "store", "store_false", ParseKwargs, ... "default": False, # only when not store_true "help": "<one-line description, period at end>", }Look at neighboring flags in the same subcommand to match style (option grouping, help-text tone).
-
Consume the flag in
commitizen/commands/<subcommand>.py. It will arrive asself.arguments["my_flag"](dashes become underscores). -
Config-file support (optional). If the flag should also be settable in the user's config file:
- Add the key to
commitizen/defaults.py:Settings(and toDEFAULT_SETTINGSif there is a non-Nonedefault). - In the command, fall back to
self.config.settings["my_flag"]when the CLI value isNone. - Document the setting in the relevant
docs/config/<area>.mdpage.
- Add the key to
-
Add tests:
- CLI parsing: extend
tests/test_cli/ortests/test_cli.pywith a case that invokescz <subcommand> --my-flagand asserts the parsed namespace. - Behavior: extend
tests/commands/test_<subcommand>_command.py.
- CLI parsing: extend
-
Update user docs at
docs/commands/<subcommand>.md. If the flag has a corresponding config setting, also updatedocs/config/<area>.md. -
Regenerate the help SVGs so the new flag appears in the rendered docs. See the update-snapshots playbook for the
poe doc:screenshotsworkflow.
uv run pytest tests/test_cli/ tests/test_cli.py tests/commands/test_<subcommand>_command.py -n auto
uv run poe lint
uv run poe doc:build
uv run poe all # final pre-push- Underscores vs dashes — argparse converts
--my-flagtomy_flagin the namespace, butdecliaccepts both. Be consistent with neighboring flags. store_truewith explicitdefault— argparse usesFalseas the implicit default forstore_true; do not setdefaultunless you needNoneto detect "user did not pass the flag" (which matters for config-file fallback).- Mutually exclusive flags — argparse does not enforce mutual exclusion through the
declidict schema; validate in the command class and raisecommitizen.exceptions.InvalidCommandArgumentErrorwith a clear message. - Forgetting the
SettingsTypedDict when adding a config-file key —read_cfgwill accept the value butmypywill flag every read ofself.config.settings["my_flag"]. - Breaking flag removals — see the deprecate-public-api playbook. A flag is user-facing surface; do not remove it without a deprecation window.
- Stale
--helpscreenshots — CI does not regenerate them. Runuv run poe doc:screenshotsafter any flag change and commit the result.
- The flag would change the exit code of an existing success path — that breaks scripts that depend on exit codes. See Exit Codes.
- The flag's behavior overlaps with an existing flag with subtly different semantics — propose a deprecation plan first.
- The flag controls something that is currently determined by config precedence rules (CLI > env > config); make the precedence explicit in the issue.