cli-wizard 2.1.0__tar.gz → 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. {cli_wizard-2.1.0/src/cli_wizard.egg-info → cli_wizard-3.0.0}/PKG-INFO +79 -49
  2. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/README.md +77 -31
  3. cli_wizard-3.0.0/VERSION +1 -0
  4. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/pyproject.toml +40 -14
  5. cli_wizard-3.0.0/src/cli_wizard/cli.py +67 -0
  6. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/commands/bootstrap.py +105 -175
  7. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/commands/common.py +13 -6
  8. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/commands/config.py +10 -9
  9. cli_wizard-3.0.0/src/cli_wizard/commands/generate.py +243 -0
  10. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/config/configuration.py +6 -6
  11. cli_wizard-3.0.0/src/cli_wizard/config/project.py +100 -0
  12. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/config/schema.py +43 -7
  13. cli_wizard-3.0.0/src/cli_wizard/errors.py +137 -0
  14. cli_wizard-3.0.0/src/cli_wizard/generator/generator.py +688 -0
  15. cli_wizard-3.0.0/src/cli_wizard/generator/models.py +220 -0
  16. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/generator/parser.py +75 -28
  17. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/docs.yaml.j2 +1 -1
  18. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/pr-validation.yaml.j2 +5 -3
  19. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/test.yaml.j2 +1 -1
  20. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/CHANGELOG.md.j2 +0 -4
  21. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/DEVELOPMENT.md.j2 +34 -1
  22. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/Makefile.j2 +2 -1
  23. cli_wizard-3.0.0/src/cli_wizard/templates/README.md.j2 +168 -0
  24. cli_wizard-3.0.0/src/cli_wizard/templates/pre-commit-config.yaml.j2 +30 -0
  25. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/pyproject.toml.j2 +34 -18
  26. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/cli.py.j2 +94 -0
  27. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/client.py.j2 +326 -0
  28. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/commands/__init__.py.j2 +4 -0
  29. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/commands/config.py.j2 +146 -0
  30. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/commands/group.py.j2 +185 -0
  31. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/src/{{ PackageName }}/constants.py.j2 +40 -18
  32. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/errors.py.j2 +149 -0
  33. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/log.py.j2 +189 -0
  34. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/options.py.j2 +123 -0
  35. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/output.py.j2 +158 -0
  36. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/profile.py.j2 +283 -0
  37. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/redaction.py.j2 +88 -0
  38. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/runner.py.j2 +77 -0
  39. cli_wizard-3.0.0/src/cli_wizard/templates/src/{{ PackageName }}/state.py.j2 +41 -0
  40. cli_wizard-3.0.0/src/cli_wizard/templates/tests/{{ PackageName }}/cli_test.py.j2 +2049 -0
  41. cli_wizard-3.0.0/src/cli_wizard/templates/tests/{{ PackageName }}/commands_test.py.j2 +73 -0
  42. cli_wizard-3.0.0/src/cli_wizard/templates/tox.ini.j2 +31 -0
  43. {cli_wizard-2.1.0 → cli_wizard-3.0.0/src/cli_wizard.egg-info}/PKG-INFO +79 -49
  44. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard.egg-info/SOURCES.txt +11 -2
  45. cli_wizard-3.0.0/src/cli_wizard.egg-info/requires.txt +6 -0
  46. cli_wizard-2.1.0/VERSION +0 -1
  47. cli_wizard-2.1.0/src/cli_wizard/cli.py +0 -41
  48. cli_wizard-2.1.0/src/cli_wizard/commands/generate.py +0 -263
  49. cli_wizard-2.1.0/src/cli_wizard/generator/generator.py +0 -532
  50. cli_wizard-2.1.0/src/cli_wizard/generator/models.py +0 -126
  51. cli_wizard-2.1.0/src/cli_wizard/templates/README.md.j2 +0 -23
  52. cli_wizard-2.1.0/src/cli_wizard/templates/pre-commit-config.yaml.j2 +0 -23
  53. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/cli.py.j2 +0 -106
  54. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/client.py.j2 +0 -150
  55. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/commands/__init__.py.j2 +0 -14
  56. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/commands/config.py.j2 +0 -384
  57. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/commands/group.py.j2 +0 -272
  58. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/logging.py.j2 +0 -183
  59. cli_wizard-2.1.0/src/cli_wizard/templates/src/{{ PackageName }}/profile.py.j2 +0 -93
  60. cli_wizard-2.1.0/src/cli_wizard/templates/tests/{{ PackageName }}/cli_test.py.j2 +0 -1163
  61. cli_wizard-2.1.0/src/cli_wizard/templates/tox.ini.j2 +0 -27
  62. cli_wizard-2.1.0/src/cli_wizard.egg-info/requires.txt +0 -24
  63. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/LICENSE +0 -0
  64. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/MANIFEST.in +0 -0
  65. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/setup.cfg +0 -0
  66. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/__init__.py +0 -0
  67. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/commands/__init__.py +0 -0
  68. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/config/__init__.py +0 -0
  69. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/constants.py +0 -0
  70. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/generator/__init__.py +0 -0
  71. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/CODEOWNERS.j2 +0 -0
  72. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/ISSUE_TEMPLATE/bug-report.yml.j2 +0 -0
  73. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/ISSUE_TEMPLATE/config.yml.j2 +0 -0
  74. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/ISSUE_TEMPLATE/feature-request.yml.j2 +0 -0
  75. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  76. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/dependabot.yaml +0 -0
  77. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/labeler.yaml +0 -0
  78. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/labels.yaml.j2 +0 -0
  79. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/changelog-enforcer.yaml +0 -0
  80. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/codeql.yaml +0 -0
  81. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/labeler.yaml +0 -0
  82. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/release.yaml.j2 +0 -0
  83. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.github/workflows/sync-labels.yaml +0 -0
  84. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/.gitignore.j2 +0 -0
  85. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/LICENSE.j2 +0 -0
  86. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/MANIFEST.in.j2 +0 -0
  87. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/VERSION.j2 +0 -0
  88. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/cli-wizard.yaml.j2 +0 -0
  89. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard/templates/src/{{ PackageName }}/__init__.py.j2 +0 -0
  90. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard.egg-info/dependency_links.txt +0 -0
  91. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard.egg-info/entry_points.txt +0 -0
  92. {cli_wizard-2.1.0 → cli_wizard-3.0.0}/src/cli_wizard.egg-info/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-wizard
3
- Version: 2.1.0
3
+ Version: 3.0.0
4
4
  Summary: Generate modern CLIs from OpenAPI specifications
5
5
  Author-email: Giacomo Marciani <giacomo.marciani@gmail.com>
6
6
  License: MIT
7
- Project-URL: Homepage, https://github.com/gmarciani/cli-wizard
7
+ Project-URL: Homepage, https://gmarciani.github.io/cli-wizard/
8
8
  Project-URL: Repository, https://github.com/gmarciani/cli-wizard
9
9
  Project-URL: Issues, https://github.com/gmarciani/cli-wizard/issues
10
10
  Classifier: Development Status :: 5 - Production/Stable
@@ -23,22 +23,6 @@ Requires-Dist: pydantic~=2.13
23
23
  Requires-Dist: PyYAML~=6.0
24
24
  Requires-Dist: requests~=2.34
25
25
  Requires-Dist: ruff~=0.16.2
26
- Provides-Extra: dev
27
- Requires-Dist: build~=1.5; extra == "dev"
28
- Requires-Dist: mypy~=2.3; extra == "dev"
29
- Requires-Dist: pre-commit~=4.6; extra == "dev"
30
- Requires-Dist: pytest~=9.1; extra == "dev"
31
- Requires-Dist: pytest-cov~=7.1; extra == "dev"
32
- Requires-Dist: tox~=4.58; extra == "dev"
33
- Requires-Dist: twine<8.0,>=6.2; extra == "dev"
34
- Requires-Dist: types-PyYAML~=6.0; extra == "dev"
35
- Requires-Dist: types-requests~=2.33; extra == "dev"
36
- Provides-Extra: docs
37
- Requires-Dist: sphinx~=9.1; extra == "docs"
38
- Requires-Dist: sphinx-click~=6.2; extra == "docs"
39
- Requires-Dist: sphinx-rtd-theme~=3.1; extra == "docs"
40
- Requires-Dist: sphinx-new-tab-link~=0.8; extra == "docs"
41
- Requires-Dist: autodoc_pydantic~=2.2; extra == "docs"
42
26
  Dynamic: license-file
43
27
 
44
28
  # CLI WIZARD
@@ -47,7 +31,7 @@ Dynamic: license-file
47
31
  <img src="https://raw.githubusercontent.com/gmarciani/cli-wizard/main/resources/brand/banner.png" alt="cli-wizard-banner" width="500">
48
32
 
49
33
  [![PyPI version](https://img.shields.io/pypi/v/cli-wizard.svg)](https://pypi.org/project/cli-wizard)
50
- [![Python versions](https://img.shields.io/pypi/pyversions/cli-wizard.svg)](https://pypi.org/project/cli-wizard)
34
+ [![Python versions](https://img.shields.io/badge/python-3.12%20%7C%203.13%20%7C%203.14-blue.svg)](https://pypi.org/project/cli-wizard)
51
35
  [![License](https://img.shields.io/github/license/gmarciani/cli-wizard.svg)](https://github.com/gmarciani/cli-wizard/blob/main/LICENSE)
52
36
  [![Build status](https://img.shields.io/github/actions/workflow/status/gmarciani/cli-wizard/test.yaml?branch=main)](https://github.com/gmarciani/cli-wizard/actions)
53
37
  [![Tests](https://img.shields.io/github/actions/workflow/status/gmarciani/cli-wizard/test.yaml?branch=main&label=tests)](https://github.com/gmarciani/cli-wizard/actions)
@@ -76,7 +60,7 @@ Generate modern CLIs from OpenAPI specifications.
76
60
  - Automatic command grouping based on OpenAPI tags
77
61
  - Automatic help generation for all commands
78
62
  - Clean, colored terminal output
79
- - `--debug` flag for verbose logging
63
+ - `--debug` flag for verbose logging, with credentials redacted from the output
80
64
  - Built-in API client with configurable base URL and timeout
81
65
  - SSL/TLS support with custom CA certificate bundles
82
66
  - `--ca-file` option to specify custom CA certificates at runtime
@@ -92,7 +76,13 @@ Generate modern CLIs from OpenAPI specifications.
92
76
 
93
77
  ### Developer Experience
94
78
  - Generated projects are pip-installable out of the box
95
- - Auto-generated `pyproject.toml`, `README.md`, and `VERSION`
79
+ - Auto-generated `pyproject.toml` and `VERSION`
80
+ - Auto-generated `README.md` with commands and configuration reference
81
+ - Auto-generated `DEVELOPMENT.md`, `CHANGELOG.md` and `LICENSE`
82
+ - Test suite for the generated CLI, ready to run
83
+ - `tox.ini` with test, lint, type, format and coverage environments, plus a `Makefile` wrapping them
84
+ - Pre-commit configuration running ruff and mypy
85
+ - Optional GitHub setup (`IncludeGithubWorkflows`): test, release, docs, PR validation, CodeQL, changelog and labeler workflows, plus Dependabot, issue and PR templates, and `CODEOWNERS`
96
86
  - Resources (CA certs, splash files) bundled in the package
97
87
  - Profile management for storing credentials and settings
98
88
 
@@ -125,51 +115,74 @@ paths:
125
115
  description: OK
126
116
  ```
127
117
 
128
- ### Step 2: Create a Configuration File
118
+ ### Step 2: Generate the CLI
129
119
 
130
- Create a `cli-wizard.yaml` file with your CLI settings. At minimum, you need `PackageName` and `DefaultBaseUrl`:
120
+ Run the `generate` command, passing the OpenAPI spec and a project name:
121
+
122
+ ```shell
123
+ cli-wizard generate --api openapi.yaml --project-name "My CLI"
124
+ ```
125
+
126
+ This creates a complete Python CLI project in the `my-cli` directory: the
127
+ command name derives from the project name, and every other setting takes its
128
+ default. Pass `--output` to write it elsewhere.
129
+
130
+ ### Step 3: Install the Generated CLI
131
+
132
+ Navigate to the generated project and install it:
133
+
134
+ ```shell
135
+ pip install -e my-cli
136
+ ```
137
+
138
+ ### Step 4: Use Your CLI
139
+
140
+ Your CLI is now ready to use:
141
+
142
+ ```shell
143
+ my-cli --help
144
+ my-cli users list-users
145
+ ```
146
+
147
+ ### Step 5: Customize with a Configuration File
148
+
149
+ To go beyond the defaults, create a `cli-wizard.yaml` file with the settings you
150
+ want to change. Every parameter is optional:
131
151
 
132
152
  ```yaml
133
- # Required parameters
134
- PackageName: "my-cli"
153
+ ProjectName: "My CLI"
135
154
  DefaultBaseUrl: "https://api.example.com"
136
155
 
137
- # Optional: customize the splash screen
156
+ # Customize the splash screen
138
157
  SplashFile: "splash.txt"
139
158
  SplashColor: "#00FFFF"
140
159
 
141
- # Optional: filter which tags to include
160
+ # Filter which tags to include
142
161
  IncludeTags:
143
162
  - Users
144
163
  - Products
145
164
  ```
146
165
 
147
- ### Step 3: Generate the CLI
148
-
149
- Run the `generate` command:
166
+ Then generate from it. The project lands in the current directory, in a
167
+ directory named after `CommandName`:
150
168
 
151
169
  ```shell
152
- cli-wizard generate --openapi openapi.yaml --config cli-wizard.yaml --output my-cli
170
+ cli-wizard generate --configuration cli-wizard.yaml --api openapi.yaml
153
171
  ```
154
172
 
155
- This creates a complete Python CLI project in the `my-cli` directory.
173
+ ### Starting Without a Specification
156
174
 
157
- ### Step 4: Install the Generated CLI
158
-
159
- Navigate to the generated project and install it:
175
+ To start from scratch instead, run `bootstrap`. It prompts for the main
176
+ settings, writes a commented `cli-wizard.yaml`, and generates a basic CLI from
177
+ it, without API commands. The CLI lands in the current directory, in a
178
+ directory named after `CommandName`; pass `--output` to write it elsewhere:
160
179
 
161
180
  ```shell
162
- pip install -e my-cli
181
+ cli-wizard bootstrap --configuration cli-wizard.yaml
163
182
  ```
164
183
 
165
- ### Step 5: Use Your CLI
166
-
167
- Your CLI is now ready to use:
168
-
169
- ```shell
170
- my-cli --help
171
- my-cli users list-users
172
- ```
184
+ Then edit the configuration, point `Api` at your specification, and rebuild
185
+ with `cli-wizard generate --configuration cli-wizard.yaml`.
173
186
 
174
187
  ## Configuration
175
188
 
@@ -180,17 +193,34 @@ See the [examples](examples/) directory for complete configuration examples.
180
193
 
181
194
  ### cli-wizard generate
182
195
 
183
- Generate a CLI from an OpenAPI specification and configuration file.
196
+ Generate a CLI from an OpenAPI specification, with an optional configuration file.
184
197
 
185
198
  ```shell
186
199
  cli-wizard generate [OPTIONS]
187
200
  ```
188
201
 
189
202
  Options:
190
- - `--openapi, -o` - Path to OpenAPI spec file (default: `openapi.yaml`)
191
- - `--config, -c` - Path to config YAML file (default: `cli-wizard.yaml`)
192
- - `--output, -d` - Output directory for generated CLI (default: `cli`)
193
- - `--working-dir, -w` - Working directory for resolving relative paths
203
+ - `--api, -a` - Path to the OpenAPI spec file, YAML or JSON. Without it and without `Api` in the configuration, the CLI is generated without API commands
204
+ - `--configuration, -c` - Path to the `cli-wizard.yaml` configuration file. Without it, every parameter takes its default
205
+ - `--project-name, -p` - Human-readable project name; the generated command name derives from it, unless the configuration sets it
206
+ - `--output, -o` - Output directory (default: a directory named after `CommandName` in the current directory)
207
+ - `--force, -f` - Skip the confirmation prompt when the output directory is not empty
208
+
209
+ ### cli-wizard bootstrap
210
+
211
+ Bootstrap a new CLI project interactively, without an OpenAPI specification.
212
+ It does two things: it writes a configuration file to evolve the project from,
213
+ then generates a basic CLI from that file, without API commands, in the output
214
+ directory.
215
+
216
+ ```shell
217
+ cli-wizard bootstrap [OPTIONS]
218
+ ```
219
+
220
+ Options:
221
+ - `--configuration, -c` - Path for the `cli-wizard.yaml` configuration file (default: `./cli-wizard.yaml`). An existing file is overwritten, after confirmation, without keeping any of its values
222
+ - `--output, -o` - Output directory of the basic CLI (default: a directory named after `CommandName` in the current directory)
223
+ - `--force, -f` - Skip the confirmation prompts to overwrite an existing configuration file or to write into a non-empty directory
194
224
 
195
225
  ## Issues
196
226
 
@@ -4,7 +4,7 @@
4
4
  <img src="https://raw.githubusercontent.com/gmarciani/cli-wizard/main/resources/brand/banner.png" alt="cli-wizard-banner" width="500">
5
5
 
6
6
  [![PyPI version](https://img.shields.io/pypi/v/cli-wizard.svg)](https://pypi.org/project/cli-wizard)
7
- [![Python versions](https://img.shields.io/pypi/pyversions/cli-wizard.svg)](https://pypi.org/project/cli-wizard)
7
+ [![Python versions](https://img.shields.io/badge/python-3.12%20%7C%203.13%20%7C%203.14-blue.svg)](https://pypi.org/project/cli-wizard)
8
8
  [![License](https://img.shields.io/github/license/gmarciani/cli-wizard.svg)](https://github.com/gmarciani/cli-wizard/blob/main/LICENSE)
9
9
  [![Build status](https://img.shields.io/github/actions/workflow/status/gmarciani/cli-wizard/test.yaml?branch=main)](https://github.com/gmarciani/cli-wizard/actions)
10
10
  [![Tests](https://img.shields.io/github/actions/workflow/status/gmarciani/cli-wizard/test.yaml?branch=main&label=tests)](https://github.com/gmarciani/cli-wizard/actions)
@@ -33,7 +33,7 @@ Generate modern CLIs from OpenAPI specifications.
33
33
  - Automatic command grouping based on OpenAPI tags
34
34
  - Automatic help generation for all commands
35
35
  - Clean, colored terminal output
36
- - `--debug` flag for verbose logging
36
+ - `--debug` flag for verbose logging, with credentials redacted from the output
37
37
  - Built-in API client with configurable base URL and timeout
38
38
  - SSL/TLS support with custom CA certificate bundles
39
39
  - `--ca-file` option to specify custom CA certificates at runtime
@@ -49,7 +49,13 @@ Generate modern CLIs from OpenAPI specifications.
49
49
 
50
50
  ### Developer Experience
51
51
  - Generated projects are pip-installable out of the box
52
- - Auto-generated `pyproject.toml`, `README.md`, and `VERSION`
52
+ - Auto-generated `pyproject.toml` and `VERSION`
53
+ - Auto-generated `README.md` with commands and configuration reference
54
+ - Auto-generated `DEVELOPMENT.md`, `CHANGELOG.md` and `LICENSE`
55
+ - Test suite for the generated CLI, ready to run
56
+ - `tox.ini` with test, lint, type, format and coverage environments, plus a `Makefile` wrapping them
57
+ - Pre-commit configuration running ruff and mypy
58
+ - Optional GitHub setup (`IncludeGithubWorkflows`): test, release, docs, PR validation, CodeQL, changelog and labeler workflows, plus Dependabot, issue and PR templates, and `CODEOWNERS`
53
59
  - Resources (CA certs, splash files) bundled in the package
54
60
  - Profile management for storing credentials and settings
55
61
 
@@ -82,51 +88,74 @@ paths:
82
88
  description: OK
83
89
  ```
84
90
 
85
- ### Step 2: Create a Configuration File
91
+ ### Step 2: Generate the CLI
86
92
 
87
- Create a `cli-wizard.yaml` file with your CLI settings. At minimum, you need `PackageName` and `DefaultBaseUrl`:
93
+ Run the `generate` command, passing the OpenAPI spec and a project name:
94
+
95
+ ```shell
96
+ cli-wizard generate --api openapi.yaml --project-name "My CLI"
97
+ ```
98
+
99
+ This creates a complete Python CLI project in the `my-cli` directory: the
100
+ command name derives from the project name, and every other setting takes its
101
+ default. Pass `--output` to write it elsewhere.
102
+
103
+ ### Step 3: Install the Generated CLI
104
+
105
+ Navigate to the generated project and install it:
106
+
107
+ ```shell
108
+ pip install -e my-cli
109
+ ```
110
+
111
+ ### Step 4: Use Your CLI
112
+
113
+ Your CLI is now ready to use:
114
+
115
+ ```shell
116
+ my-cli --help
117
+ my-cli users list-users
118
+ ```
119
+
120
+ ### Step 5: Customize with a Configuration File
121
+
122
+ To go beyond the defaults, create a `cli-wizard.yaml` file with the settings you
123
+ want to change. Every parameter is optional:
88
124
 
89
125
  ```yaml
90
- # Required parameters
91
- PackageName: "my-cli"
126
+ ProjectName: "My CLI"
92
127
  DefaultBaseUrl: "https://api.example.com"
93
128
 
94
- # Optional: customize the splash screen
129
+ # Customize the splash screen
95
130
  SplashFile: "splash.txt"
96
131
  SplashColor: "#00FFFF"
97
132
 
98
- # Optional: filter which tags to include
133
+ # Filter which tags to include
99
134
  IncludeTags:
100
135
  - Users
101
136
  - Products
102
137
  ```
103
138
 
104
- ### Step 3: Generate the CLI
105
-
106
- Run the `generate` command:
139
+ Then generate from it. The project lands in the current directory, in a
140
+ directory named after `CommandName`:
107
141
 
108
142
  ```shell
109
- cli-wizard generate --openapi openapi.yaml --config cli-wizard.yaml --output my-cli
143
+ cli-wizard generate --configuration cli-wizard.yaml --api openapi.yaml
110
144
  ```
111
145
 
112
- This creates a complete Python CLI project in the `my-cli` directory.
146
+ ### Starting Without a Specification
113
147
 
114
- ### Step 4: Install the Generated CLI
115
-
116
- Navigate to the generated project and install it:
148
+ To start from scratch instead, run `bootstrap`. It prompts for the main
149
+ settings, writes a commented `cli-wizard.yaml`, and generates a basic CLI from
150
+ it, without API commands. The CLI lands in the current directory, in a
151
+ directory named after `CommandName`; pass `--output` to write it elsewhere:
117
152
 
118
153
  ```shell
119
- pip install -e my-cli
154
+ cli-wizard bootstrap --configuration cli-wizard.yaml
120
155
  ```
121
156
 
122
- ### Step 5: Use Your CLI
123
-
124
- Your CLI is now ready to use:
125
-
126
- ```shell
127
- my-cli --help
128
- my-cli users list-users
129
- ```
157
+ Then edit the configuration, point `Api` at your specification, and rebuild
158
+ with `cli-wizard generate --configuration cli-wizard.yaml`.
130
159
 
131
160
  ## Configuration
132
161
 
@@ -137,17 +166,34 @@ See the [examples](examples/) directory for complete configuration examples.
137
166
 
138
167
  ### cli-wizard generate
139
168
 
140
- Generate a CLI from an OpenAPI specification and configuration file.
169
+ Generate a CLI from an OpenAPI specification, with an optional configuration file.
141
170
 
142
171
  ```shell
143
172
  cli-wizard generate [OPTIONS]
144
173
  ```
145
174
 
146
175
  Options:
147
- - `--openapi, -o` - Path to OpenAPI spec file (default: `openapi.yaml`)
148
- - `--config, -c` - Path to config YAML file (default: `cli-wizard.yaml`)
149
- - `--output, -d` - Output directory for generated CLI (default: `cli`)
150
- - `--working-dir, -w` - Working directory for resolving relative paths
176
+ - `--api, -a` - Path to the OpenAPI spec file, YAML or JSON. Without it and without `Api` in the configuration, the CLI is generated without API commands
177
+ - `--configuration, -c` - Path to the `cli-wizard.yaml` configuration file. Without it, every parameter takes its default
178
+ - `--project-name, -p` - Human-readable project name; the generated command name derives from it, unless the configuration sets it
179
+ - `--output, -o` - Output directory (default: a directory named after `CommandName` in the current directory)
180
+ - `--force, -f` - Skip the confirmation prompt when the output directory is not empty
181
+
182
+ ### cli-wizard bootstrap
183
+
184
+ Bootstrap a new CLI project interactively, without an OpenAPI specification.
185
+ It does two things: it writes a configuration file to evolve the project from,
186
+ then generates a basic CLI from that file, without API commands, in the output
187
+ directory.
188
+
189
+ ```shell
190
+ cli-wizard bootstrap [OPTIONS]
191
+ ```
192
+
193
+ Options:
194
+ - `--configuration, -c` - Path for the `cli-wizard.yaml` configuration file (default: `./cli-wizard.yaml`). An existing file is overwritten, after confirmation, without keeping any of its values
195
+ - `--output, -o` - Output directory of the basic CLI (default: a directory named after `CommandName` in the current directory)
196
+ - `--force, -f` - Skip the confirmation prompts to overwrite an existing configuration file or to write into a non-empty directory
151
197
 
152
198
  ## Issues
153
199
 
@@ -0,0 +1 @@
1
+ 3.0.0
@@ -30,15 +30,32 @@ dependencies = [
30
30
  "ruff~=0.16.2",
31
31
  ]
32
32
 
33
- [project.optional-dependencies]
33
+ [project.scripts]
34
+ cli-wizard = "cli_wizard.cli:main"
35
+
36
+ [project.urls]
37
+ Homepage = "https://gmarciani.github.io/cli-wizard/"
38
+ Repository = "https://github.com/gmarciani/cli-wizard"
39
+ Issues = "https://github.com/gmarciani/cli-wizard/issues"
40
+
41
+ # Local-only tooling (PEP 735). Groups are never published, so none of this
42
+ # reaches anyone installing cli-wizard from an index. There are no extras: the
43
+ # built distributions carry no tests, so a published test extra would only
44
+ # install pytest for a consumer with nothing to run it against.
45
+ [dependency-groups]
34
46
  dev = [
47
+ {include-group = "test"},
48
+ {include-group = "docs"},
35
49
  "build~=1.5",
36
- "mypy~=2.3",
37
50
  "pre-commit~=4.6",
38
- "pytest~=9.1",
39
- "pytest-cov~=7.1",
40
51
  "tox~=4.58",
41
52
  "twine>=6.2,<8.0",
53
+ ]
54
+ # mypy and the stubs too: the suite type checks the code it generates.
55
+ test = [
56
+ "mypy~=2.3",
57
+ "pytest~=9.1",
58
+ "pytest-cov~=7.1",
42
59
  "types-PyYAML~=6.0",
43
60
  "types-requests~=2.33",
44
61
  ]
@@ -50,14 +67,6 @@ docs = [
50
67
  "autodoc_pydantic~=2.2",
51
68
  ]
52
69
 
53
- [project.scripts]
54
- cli-wizard = "cli_wizard.cli:main"
55
-
56
- [project.urls]
57
- Homepage = "https://github.com/gmarciani/cli-wizard"
58
- Repository = "https://github.com/gmarciani/cli-wizard"
59
- Issues = "https://github.com/gmarciani/cli-wizard/issues"
60
-
61
70
  [tool.setuptools.packages.find]
62
71
  where = ["src"]
63
72
  include = ["cli_wizard*"]
@@ -74,18 +83,35 @@ cli_wizard = [
74
83
  [tool.setuptools.dynamic]
75
84
  version = {file = "VERSION"}
76
85
 
86
+ # Ruff targets the oldest supported version: it emits syntax valid for the
87
+ # target, and newer syntax would be a SyntaxError on 3.12.
77
88
  [tool.ruff]
78
89
  line-length = 88
79
90
  target-version = "py312"
80
91
 
81
92
  [tool.ruff.lint]
82
- select = ["E", "F", "W", "I"]
93
+ # E/W pycodestyle, F pyflakes, I isort, B bugbear, S bandit, UP pyupgrade
94
+ select = ["E", "F", "W", "I", "B", "S", "UP"]
83
95
 
96
+ [tool.ruff.lint.per-file-ignores]
97
+ # Tests assert by design.
98
+ "tests/**" = ["S101"]
99
+
100
+ # mypy checks against the default interpreter, the newest supported version.
84
101
  [tool.mypy]
85
- python_version = "3.12"
102
+ python_version = "3.14"
86
103
  warn_return_any = true
87
104
  warn_unused_configs = true
88
105
  disallow_untyped_defs = true
89
106
 
90
107
  [tool.coverage.run]
91
108
  omit = ["*/__init__.py"]
109
+
110
+ [tool.coverage.report]
111
+ fail_under = 90
112
+
113
+ [tool.pytest.ini_options]
114
+ # Import cli_wizard from the checkout being tested. The editable install names
115
+ # one checkout in its .pth file, so a run started from a git worktree would
116
+ # otherwise import that other checkout.
117
+ pythonpath = ["src"]
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2026, Giacomo Marciani
2
+ # Licensed under the MIT License
3
+
4
+ """Main CLI module for CLI Wizard."""
5
+
6
+ import logging
7
+ import traceback
8
+ from typing import Any
9
+
10
+ import click
11
+
12
+ from cli_wizard.commands.bootstrap import bootstrap
13
+ from cli_wizard.commands.common import configure_logging, debug_option
14
+ from cli_wizard.commands.config import config
15
+ from cli_wizard.commands.generate import generate
16
+ from cli_wizard.constants import __version__
17
+ from cli_wizard.errors import UnexpectedError, reported
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class RootGroup(click.Group):
23
+ """The root group, reporting what Click raises on its own as JSON too.
24
+
25
+ Click's main() shows a ClickException and exits with its code; wrapping the
26
+ parsing and the dispatch turns everything else into one of cli-wizard's
27
+ errors first, so every failure is the one JSON document on stdout.
28
+ """
29
+
30
+ def make_context(
31
+ self,
32
+ info_name: str | None,
33
+ args: list[str],
34
+ parent: click.Context | None = None,
35
+ **extra: Any,
36
+ ) -> click.Context:
37
+ with reported():
38
+ return super().make_context(info_name, args, parent, **extra)
39
+
40
+ def invoke(self, ctx: click.Context) -> Any:
41
+ try:
42
+ with reported():
43
+ return super().invoke(ctx)
44
+ except UnexpectedError as e:
45
+ # The traceback the bug came with, kept for --debug
46
+ logger.debug("".join(traceback.format_exception(e.__cause__)))
47
+ raise
48
+
49
+
50
+ @click.group(cls=RootGroup, help="CLI Wizard - Generate modern CLI from OpenAPI.")
51
+ @click.version_option(version=__version__, prog_name="cli-wizard")
52
+ @debug_option
53
+ @click.pass_context
54
+ def main(ctx: click.Context, debug: bool) -> None:
55
+ """Main CLI entry point."""
56
+ ctx.ensure_object(dict)
57
+ ctx.obj["debug"] = debug
58
+ configure_logging(debug)
59
+
60
+
61
+ main.add_command(bootstrap)
62
+ main.add_command(config)
63
+ main.add_command(generate)
64
+
65
+
66
+ if __name__ == "__main__":
67
+ main()