1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
|
Metadata-Version: 2.1
Name: rich-click
Version: 1.7.4
Summary: Format click help output nicely with rich
Home-page: https://github.com/ewels/rich-click
Author: Phil Ewels
Author-email: phil@ewels.co.uk
Maintainer: Phil Ewels
Maintainer-email: phil@ewels.co.uk
License: MIT
Project-URL: Documentation, https://github.com/ewels/rich-click
Project-URL: Source Code, https://github.com/ewels/rich-click
Project-URL: Issue Tracker, https://github.com/ewels/rich-click/issues/
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click >=7
Requires-Dist: rich >=10.7.0
Requires-Dist: typing-extensions
Requires-Dist: importlib-metadata ; python_version < "3.8"
Provides-Extra: dev
Requires-Dist: mypy ; extra == 'dev'
Requires-Dist: pre-commit ; extra == 'dev'
Requires-Dist: pytest ; extra == 'dev'
Requires-Dist: flake8 ; extra == 'dev'
Requires-Dist: flake8-docstrings ; extra == 'dev'
Requires-Dist: pytest-cov ; extra == 'dev'
Requires-Dist: packaging ; extra == 'dev'
Requires-Dist: types-setuptools ; extra == 'dev'
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/rich-click-logo-darkmode.png">
<img alt="rich-click logo" src="https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/rich-click-logo.png">
</picture>
</p>
<p align="center">
<em>Richly rendered command line interfaces in click.</em>
</p>
<p align="center">
<img src="https://github.com/ewels/rich-click/workflows/Test%20Coverage/badge.svg" alt="Test Coverage badge">
<img src="https://github.com/ewels/rich-click/workflows/Lint%20code/badge.svg" alt="Lint code badge">
</p>
---
**rich-click** is a shim around [click](https://click.palletsprojects.com/) that renders help output nicely using [Rich](https://github.com/Textualize/rich).
- Click is a _"Python package for creating beautiful command line interfaces"_.
- Rich is a _"Python library for rich text and beautiful formatting in the terminal"_.
The intention of `rich-click` is to provide attractive help output from
click, formatted with rich, with minimal customisation required.
## Features
- 🌈 Rich command-line formatting of click help and error messages
- 💫 Nice styles be default, usage is simply `import rich_click as click`
- 💻 CLI tool to run on _other people's_ tools (prefix the command with `rich-click`)
- 🎁 Group commands and options into named panels
- ❌ Well formatted error messages
- 🔢 Easily give custom sort order for options and commands
- 🎨 Extensive customisation of styling and behaviour possible
## Examples
### Simple Example
To use rich-click in your code, replace `import click` with `import rich_click as click` in your existing click CLI:
```python
import rich_click as click
@click.command()
@click.option("--count", default=1, help="Number of greetings.")
@click.option("--name", prompt="Your name", help="The person to greet.")
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
click.echo(f"Hello, {name}!")
if __name__ == '__main__':
hello()
```
![`python examples/11_hello.py --help`](docs/images/hello.svg)
_Screenshot from [`examples/11_hello.py`](examples/11_hello.py)_
### More complex example
![examples/03_groups_sorting.py](docs/images/command_groups.svg)
_Screenshot from [`examples/03_groups_sorting.py`](examples/03_groups_sorting.py)_
## Installation
You can install `rich-click` from the [Python Package Index (PyPI)](https://pypi.org/project/rich-click/) with `pip` or equivalent.
```bash
python -m pip install rich-click
```
Conda users can find `rich-click` on [conda forge](https://anaconda.org/conda-forge/rich-click).
Just set up conda to use conda-forge (see [docs](https://conda-forge.org/docs/user/introduction.html#how-can-i-install-packages-from-conda-forge)) then run:
```bash
conda install rich-click
```
Users on macOS can install `rich-click` via [MacPorts](https://ports.macports.org/port/py-rich-click/).
```bash
sudo port install py-rich-click
```
Note that rich-click requires `click>=7` but formatted subcommands (groups) only work with `click>=8`. With v7 the output simply reverts to default click output.
## Usage
### Import as click
To use `rich-click`, switch out your normal `click` import with `rich-click`, using the same namespace:
```python
import rich_click as click
```
That's it! ✨ Then continue to use `click` as you would normally.
> See [`examples/01_simple.py`](examples/01_simple.py) for an example.
The intention is to maintain most / all of the normal click functionality and arguments.
If you spot something that breaks or is missing once you start using the plugin, please create an issue about it.
### Declarative
If you prefer, you can `RichGroup` or `RichCommand` with the `cls` argument in your click usage instead.
This means that you can continue to use the unmodified `click` package in parallel.
> See [`examples/02_declarative.py`](examples/02_declarative.py) for an example.
### Command-line usage
`rich-click` comes with a CLI tool that allows you to format the click help output from _any_ package.
As long as that tool is using click and isn't already passing custom `cls` objects, it should work.
However, please consider it an experimental feature at this point.
To use, simply prefix to your normal command.
For example, to get richified click help text from a package called `awesometool`, you could run:
```console
$ rich-click awesometool --help
Usage: awesometool [OPTIONS]
..more richified output below..
```
### Patching
In some situations, you might be registering a command from another Click CLI that does not use Rich-Click:
```python
import rich_click as click
from some_library import another_cli
@click.group("my-cli")
def cli():
pass
# `another_cli` will NOT have Rich-Click markup. :(
cli.add_command(another_cli)
```
In this situation, `another_cli` retains its original behavior. In order to make `another_cli` work with Rich-Click, you need to patch `click` before you import `another_cli`. You can patch Click with `rich_click.cli.patch` like this:
```python
import rich_click as click
from rich_click.cli import patch
patch()
from some_library import another_cli # noqa: E402
@click.group("my-cli")
def cli():
pass
# `another_cli` will have Rich-Click markup. :)
cli.add_command(another_cli)
```
## Customisation
There are a large number of customisation options in rich-click.
These can be modified by changing variables in the `click.rich_click` namespace.
Note that most normal click options should still work, such as `show_default=True`, `required=True` and `hidden=True`.
> Note: All images below are auto-generated using another side-project of mine: [rich-codex](https://github.com/ewels/rich-codex). Pretty cool!
### Using Rich markup
In order to be as widely compatible as possible with a simple import, rich-click does _not_ parse rich formatting markup (eg. `[red]`) by default. You need to opt-in to this behaviour.
To use rich markup in your help texts, add the following:
```python
click.rich_click.USE_RICH_MARKUP = True
```
Or alternatively, with the `rich_config` and `RichHelpConfiguration`:
```python
@click.command()
@click.rich_config(help_config=click.RichHelpConfiguration(use_rich_markup=True))
def cli():
...
```
Remember that you'll need to escape any regular square brackets using a back slash in your help texts,
for example: `[dim]\[my-default: foo][\]`
![`python examples/04_rich_markup.py --help`](docs/images/rich_markup.svg "Rich markup example")
> See [`examples/04_rich_markup.py`](examples/04_rich_markup.py) for an example.
### Using Markdown
If you prefer, you can use Markdown text.
You must choose either Markdown or rich markup. If you specify both, Markdown takes preference.
```python
click.rich_click.USE_MARKDOWN = True
```
Or alternatively, with the `RichHelpConfiguration`:
```python
@click.command()
@click.rich_config(help_config=click.RichHelpConfiguration(use_markdown=True))
def cli():
...
```
![`python examples/05_markdown.py --help`](docs/images/markdown.svg "Markdown example")
> See [`examples/05_markdown.py`](examples/05_markdown.py) for an example.
### Positional arguments
The default click behaviour is to only show positional arguments in the top usage string,
and not in the list below with the options.
If you prefer, you can tell rich-click to show arguments with `SHOW_ARGUMENTS`.
By default, they will get their own panel but you can tell rich-click to bundle them together with `GROUP_ARGUMENTS_OPTIONS`:
```python
click.rich_click.SHOW_ARGUMENTS = True
click.rich_click.GROUP_ARGUMENTS_OPTIONS = True
```
Or alternatively, with the `RichHelpConfiguration`:
```python
help_config = click.RichHelpConfiguration(
show_arguments=True,
group_arguments_options=True
)
@click.command()
@click.rich_config(help_config=help_config)
def cli():
...
```
![`python examples/06_arguments.py --help`](docs/images/arguments.svg "Positional arguments example")
> See [`examples/06_arguments.py`](examples/06_arguments.py) for an example.
### Metavars and option choices
Metavars are click's way of showing expected input types.
For example, if you have an option that must be an integer, the metavar is `INTEGER`.
If you have a choice, the metavar is a list of the possible values.
By default, rich-click shows metavars in their own column.
However, if you have a long list of choices, this column can be quite wide and result in a lot of white space:
![`python examples/08_metavars_default.py --help`](docs/images/metavars_default.svg "Default metavar display")
It may look better to show metavars appended to the help text, instead of in their own column.
For this, use the following:
```python
click.rich_click.SHOW_METAVARS_COLUMN = False
click.rich_click.APPEND_METAVARS_HELP = True
```
```python
help_config = click.RichHelpConfiguration(
show_metavars_column=False,
append_metavars_help=True
)
@click.command()
@click.rich_config(help_config=help_config)
def cli():
...
```
![`python examples/08_metavars.py --help`](docs/images/metavars_appended.svg "Appended metavar")
> See [`examples/08_metavars.py`](examples/08_metavars.py) for an example.
### Error messages
By default, rich-click gives some nice formatting to error messages:
![`python examples/01_simple.py --hep || true`](docs/images/error.svg "Error message")
You can customise the _Try 'command --help' for help._ message with `ERRORS_SUGGESTION`
using rich-click though, and add some text after the error with `ERRORS_EPILOGUE`.
For example, from [`examples/07_custom_errors.py`](examples/07_custom_errors.py):
```python
click.rich_click.STYLE_ERRORS_SUGGESTION = "magenta italic"
click.rich_click.ERRORS_SUGGESTION = "Try running the '--help' flag for more information."
click.rich_click.ERRORS_EPILOGUE = "To find out more, visit [link=https://mytool.com]https://mytool.com[/link]"
```
![`python examples/07_custom_errors.py --hep || true`](docs/images/custom_error.svg "Custom error message")
> See [`examples/07_custom_errors.py`](examples/07_custom_errors.py) for an example.
### Help width
The default behaviour of rich-click is to use the full width of the terminal for output.
However, if you've carefully crafted your help texts for the default narrow click output, you may find that you now have a lot of whitespace at the side of the panels.
To limit the maximum width of the help output, regardless of the terminal size, set `WIDTH` in characters as follows:
```python
click.rich_click.WIDTH = 128
```
To still use the full width of the terminal up to a certain limit, set `MAX_WIDTH` in characters as follows:
```python
click.rich_click.MAX_WIDTH = 96
```
Setting `MAX_WIDTH` overrides the effect of `WIDTH`
### Styling
Most aspects of rich-click formatting can be customised, from colours to alignment.
For example, to print the option flags in a different colour, you can use:
```python
click.rich_click.STYLE_OPTION = "magenta"
```
To add a blank line between rows of options, you can use:
```python
click.rich_click.STYLE_OPTIONS_TABLE_LEADING = 1
click.rich_click.STYLE_OPTIONS_TABLE_BOX = "SIMPLE"
```
You can make some really ~horrible~ _colourful_ solutions using these styles if you wish:
<!-- RICH-CODEX
extra_env:
TERMINAL_WIDTH: 160
-->
![`python examples/10_table_styles.py --help`](docs/images/style_tables.svg "Rich markup example")
> See [`examples/10_table_styles.py`](examples/10_table_styles.py) for an example.
See the [_Configuration options_](#configuration-options) section below for the full list of available options.
## Groups and sorting
`rich-click` gives functionality to list options and subcommands in groups, printed as separate panels.
It accepts a list of options / commands which means you can also choose a custom sorting order.
- For options (flags), set `click.rich_click.OPTION_GROUPS`
- For subcommands (groups), set `click.rich_click.COMMAND_GROUPS`
![`python examples/03_groups_sorting.py --help`](docs/images/command_groups.svg "Command groups")
When grouping subcommands into more than one group (in above example: 'Main usage' and 'Configuration') you may find that the automatically calculated widths of different groups do not line up, due to varying option name lengths.
You can avoid this by enforcing the alignment of the help text across groups by setting `click.rich_click.STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO = (1, 2)`. This results in a fixed ratio of 1:2 for the width of command name and help text column.
> See [`examples/03_groups_sorting.py`](examples/03_groups_sorting.py) for a full example.
### Options
To group option flags into two sections with custom names, see the following example:
```python
click.rich_click.OPTION_GROUPS = {
"mytool": [
{
"name": "Simple options",
"options": ["--name", "--description", "--version", "--help"],
},
{
"name": "Advanced options",
"options": ["--force", "--yes", "--delete"],
},
]
}
```
If you omit `name` it will use `Commands` (can be configured with `OPTIONS_PANEL_TITLE`).
### Commands
Here we create two groups of commands for the base command of `mytool`.
Any subcommands not listed will automatically be printed in a panel at the end labelled "Commands" as usual.
```python
click.rich_click.COMMAND_GROUPS = {
"mytool": [
{
"name": "Commands for uploading",
"commands": ["sync", "upload"],
},
{
"name": "Download data",
"commands": ["get", "fetch", "download"],
},
]
}
```
If you omit `name` it will use `Commands` (can be configured with `COMMANDS_PANEL_TITLE`).
### Multiple commands
If you use multiple nested subcommands, you can specify their commands using the top-level dictionary keys:
```python
click.rich_click.COMMAND_GROUPS = {
"mytool": [{"commands": ["sync", "auth"]}],
"mytool sync": [
{
"name": "Commands for uploading",
"commands": ["sync", "upload"],
},
{
"name": "Download data",
"commands": ["get", "fetch", "download"],
},
],
"mytool auth":[{"commands": ["login", "logout"]}],
}
```
### Table styling
Typically you would style the option / command tables using the global config options.
However, if you wish you may style tables on a per-group basis using the `table_styles` key:
```python
click.rich_click.COMMAND_GROUPS = {
"mytool": [
{
"commands": ["sync", "auth"],
"table_styles": {
"show_lines": True,
"row_styles": ["magenta", "yellow", "cyan", "green"],
"border_style": "red",
"box": "DOUBLE",
},
},
],
}
```
The available keys are: `show_lines`, `leading`, `box`, `border_style`, `row_styles`, `pad_edge`, `padding`.
## Configuration options
Here is the full list of config options:
```python
# Default styles
STYLE_OPTION = "bold cyan"
STYLE_ARGUMENT = "bold cyan"
STYLE_COMMAND = "bold cyan"
STYLE_SWITCH = "bold green"
STYLE_METAVAR = "bold yellow"
STYLE_METAVAR_APPEND = "dim yellow"
STYLE_METAVAR_SEPARATOR = "dim"
STYLE_HEADER_TEXT = ""
STYLE_EPILOG_TEXT = ""
STYLE_FOOTER_TEXT = ""
STYLE_USAGE = "yellow"
STYLE_USAGE_COMMAND = "bold"
STYLE_DEPRECATED = "red"
STYLE_HELPTEXT_FIRST_LINE = ""
STYLE_HELPTEXT = "dim"
STYLE_OPTION_HELP = ""
STYLE_OPTION_DEFAULT = "dim"
STYLE_OPTION_ENVVAR = "dim yellow"
STYLE_REQUIRED_SHORT = "red"
STYLE_REQUIRED_LONG = "dim red"
STYLE_OPTIONS_PANEL_BORDER = "dim"
ALIGN_OPTIONS_PANEL = "left"
STYLE_OPTIONS_TABLE_SHOW_LINES = False
STYLE_OPTIONS_TABLE_LEADING = 0
STYLE_OPTIONS_TABLE_PAD_EDGE = False
STYLE_OPTIONS_TABLE_PADDING = (0, 1)
STYLE_OPTIONS_TABLE_BOX = ""
STYLE_OPTIONS_TABLE_ROW_STYLES = None
STYLE_OPTIONS_TABLE_BORDER_STYLE = None
STYLE_COMMANDS_PANEL_BORDER = "dim"
ALIGN_COMMANDS_PANEL = "left"
STYLE_COMMANDS_TABLE_SHOW_LINES = False
STYLE_COMMANDS_TABLE_LEADING = 0
STYLE_COMMANDS_TABLE_PAD_EDGE = False
STYLE_COMMANDS_TABLE_PADDING = (0, 1)
STYLE_COMMANDS_TABLE_BOX = ""
STYLE_COMMANDS_TABLE_ROW_STYLES = None
STYLE_COMMANDS_TABLE_BORDER_STYLE = None
STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO = (None, None)
STYLE_ERRORS_PANEL_BORDER = "red"
ALIGN_ERRORS_PANEL = "left"
STYLE_ERRORS_SUGGESTION = "dim"
STYLE_ERRORS_SUGGESTION_COMMAND = "blue"
STYLE_ABORTED = "red"
WIDTH = int(getenv("TERMINAL_WIDTH")) if getenv("TERMINAL_WIDTH") else None
MAX_WIDTH = int(getenv("TERMINAL_WIDTH")) if getenv("TERMINAL_WIDTH") else WIDTH
COLOR_SYSTEM = "auto" # Set to None to disable colors
FORCE_TERMINAL = True if getenv("GITHUB_ACTIONS") or getenv("FORCE_COLOR") or getenv("PY_COLORS") else None
# Fixed strings
HEADER_TEXT = None
FOOTER_TEXT = None
DEPRECATED_STRING = "(Deprecated) "
DEFAULT_STRING = "[default: {}]"
ENVVAR_STRING = "[env var: {}]"
REQUIRED_SHORT_STRING = "*"
REQUIRED_LONG_STRING = "[required]"
RANGE_STRING = " [{}]"
APPEND_METAVARS_HELP_STRING = "({})"
ARGUMENTS_PANEL_TITLE = "Arguments"
OPTIONS_PANEL_TITLE = "Options"
COMMANDS_PANEL_TITLE = "Commands"
ERRORS_PANEL_TITLE = "Error"
ERRORS_SUGGESTION = None # Default: Try 'cmd -h' for help. Set to False to disable.
ERRORS_EPILOGUE = None
ABORTED_TEXT = "Aborted."
# Behaviours
SHOW_ARGUMENTS = False # Show positional arguments
SHOW_METAVARS_COLUMN = True # Show a column with the option metavar (eg. INTEGER)
APPEND_METAVARS_HELP = False # Append metavar (eg. [TEXT]) after the help text
GROUP_ARGUMENTS_OPTIONS = False # Show arguments with options instead of in own panel
OPTION_ENVVAR_FIRST = False # Show env vars before option help text instead of avert
USE_MARKDOWN = False # Parse help strings as markdown
USE_MARKDOWN_EMOJI = True # Parse emoji codes in markdown :smile:
USE_RICH_MARKUP = False # Parse help strings for rich markup (eg. [red]my text[/])
COMMAND_GROUPS = {} # Define sorted groups of panels to display subcommands
OPTION_GROUPS = {} # Define sorted groups of panels to display options and arguments
USE_CLICK_SHORT_HELP = False # Use click's default function to truncate help text
```
Full type annotations of these config options are availble in `src/rich_click/rich_click.py`.
100% of these options are supported in the `RichHelpConfiguration` class, as well.
## Contributing
Contributions and suggestions for new features are welcome, as are bug reports!
Please create a new [issue](https://github.com/ewels/rich-click/issues)
or better still, dive right in with a pull-request.
### Local setup
1. Create a new venv with a python3.7+ interpreter using `python3 -m venv venv`
2. Activate the venv with `source venv/bin/activate`
3. Install our the package as an editable including all dev dependencies with `pip3 install -e ."[dev]"`
4. Install pre-commit with `pre-commit install`
#### Pre-commit
Our pre-commit hooks contain the following hooks:
- [Prettier](https://prettier.io/): formats our markdown and yaml files nicely.
- no relative imports: prevents you from using relative imports.
- [iSort](https://pycqa.github.io/isort/): will automatically sort the imports alphabetically.
- [black](https://black.readthedocs.io/): will automatically format your code to be according to standardized python format.
- [flake8](https://flake8.pycqa.org/): will do linting checks to make sure all your code is correctly styled and used.
- [mypy](http://mypy-lang.org/): static type checker which verifies you are not using objects incorrectly.
As mentioned, some of these tools automatically fix your code while other only highlight potential issues.
Sometimes it will be enough to try to commit a second time and it will pass, while other times it may require
manual changes to your code.
In rare cases it may be difficult or undesirable to change to code to pass the linting rules.
If this happens, it's ok to add a flake8 `# noqa` or mypy `# type: ignore` comment to skip that line.
For details of how to do this, please see the [flake8 docs](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html#in-line-ignoring-errors)
and [mypy docs](https://mypy.readthedocs.io/en/stable/common_issues.html#spurious-errors-and-locally-silencing-the-checker).
## Credits
This package was written by Phil Ewels ([@ewels](http://github.com/ewels/)),
based on initial code by Will McGugan ([@willmcgugan](https://github.com/willmcgugan)).
rich-click is co-maintained by [@dwreeves](http://github.com/dwreeves/).
Furthermore, these contributors helped make the package what it is today:
- [@BrutalSimplicity](https://github.com/BrutalSimplicity)
- [@harens](http://github.com/harens/)
- [@fridex](http://github.com/fridex/)
- [@pawamoy](http://github.com/pawamoy/)
- [@jorrick](http://github.com/harens/)
See the full list of contributors [here](https://github.com/ewels/rich-click/graphs/contributors).
|