@robhowley/py-pit-skills 3.1.1
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.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/package.json +20 -0
- package/skills/alembic-migrations/SKILL.md +391 -0
- package/skills/click-cli/SKILL.md +204 -0
- package/skills/click-cli-linter/SKILL.md +192 -0
- package/skills/code-quality/SKILL.md +398 -0
- package/skills/dockerize-service/SKILL.md +280 -0
- package/skills/fastapi-errors/SKILL.md +319 -0
- package/skills/fastapi-init/SKILL.md +356 -0
- package/skills/pydantic-schemas/SKILL.md +500 -0
- package/skills/pytest-service/SKILL.md +216 -0
- package/skills/settings-config/SKILL.md +248 -0
- package/skills/sqlalchemy-models/SKILL.md +433 -0
- package/skills/uv/SKILL.md +310 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rob Howley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# py-pit
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Opinionated Python backend workflows for Claude Code.
|
|
6
|
+
|
|
7
|
+
`py-pit` encodes common backend development workflows as Claude/Pi skills for the modern Python API stack: FastAPI services, uv environments, SQLAlchemy models, Alembic migrations, configuration management, and CLI tooling.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
### Claude Code
|
|
12
|
+
|
|
13
|
+
Add the plugin marketplace, then install py-pit:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/plugin marketplace add robhowley/py-pit-skills
|
|
17
|
+
/plugin install py-pit@py-pit-skills
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### pi
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pi install https://github.com/robhowley/py-pit-skills
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Skills
|
|
27
|
+
|
|
28
|
+
| Skill | Activates when LLM detects |
|
|
29
|
+
|----------------------|--------------------------------------------------------------------------------------------|
|
|
30
|
+
| `fastapi-init` | new FastAPI service, scaffold an API, new microservice |
|
|
31
|
+
| `uv` | dependency management, lockfiles, env setup, migration from pip/poetry |
|
|
32
|
+
| `click-cli` | designing or generating a new Click CLI |
|
|
33
|
+
| `click-cli-linter` | auditing or improving an existing Click CLI |
|
|
34
|
+
| `pydantic-schemas` | request/response schema design, Pydantic v2 models, schema patterns |
|
|
35
|
+
| `code-quality` | linting setup, Ruff, pre-commit, code health tooling |
|
|
36
|
+
| `dockerize-service` | local dev Docker Compose setup, containerizing a project |
|
|
37
|
+
| `settings-config` | environment variable management, pydantic-settings, replacing os.getenv |
|
|
38
|
+
| `sqlalchemy-models` | ORM model design, SQLAlchemy 2.x patterns, relationships, migration-ready schema |
|
|
39
|
+
| `alembic-migrations` | adding Alembic, generating migrations, reviewing autogenerate diffs, safe schema evolution |
|
|
40
|
+
| `fastapi-errors` | FastAPI error handling, exception hierarchy, consistent API error responses |
|
|
41
|
+
| `pytest-service` | writing tests for a FastAPI service, SQLAlchemy test fixtures, DI overrides, test setup |
|
|
42
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@robhowley/py-pit-skills",
|
|
3
|
+
"version": "3.1.1",
|
|
4
|
+
"description": "Opinionated Python backend workflows as agent skills for FastAPI, uv, SQLAlchemy, Alembic, Click, and pytest",
|
|
5
|
+
"keywords": ["pi-package"],
|
|
6
|
+
"author": "robhowley",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/robhowley/py-pit-skills"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"skills/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"pi": {
|
|
18
|
+
"skills": ["./skills"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: alembic-migrations
|
|
3
|
+
description: Production-safe Alembic migration workflows for SQLAlchemy
|
|
4
|
+
2.x projects. Use this skill when adding Alembic, generating
|
|
5
|
+
migrations, reviewing autogenerate diffs, fixing broken migrations, or
|
|
6
|
+
establishing a safe schema evolution workflow in a Python backend.
|
|
7
|
+
disable-model-invocation: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Skill: alembic-migrations
|
|
11
|
+
|
|
12
|
+
## Core position
|
|
13
|
+
|
|
14
|
+
This skill establishes **safe, disciplined database migration
|
|
15
|
+
workflows** using **Alembic** in SQLAlchemy 2.x projects.
|
|
16
|
+
|
|
17
|
+
Autogenerate output is treated as **a draft**, never final truth.
|
|
18
|
+
All migrations must be **reviewable, deterministic, and safe for
|
|
19
|
+
production.**
|
|
20
|
+
|
|
21
|
+
The skill prevents common failures such as:
|
|
22
|
+
|
|
23
|
+
- empty autogenerate revisions
|
|
24
|
+
- rename → drop/create data loss
|
|
25
|
+
- broken env.py metadata wiring
|
|
26
|
+
- migration history divergence
|
|
27
|
+
- unsafe schema changes
|
|
28
|
+
|
|
29
|
+
------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
# Py-pit enforcement rules
|
|
32
|
+
|
|
33
|
+
These rules are **always applied** when generating or reviewing
|
|
34
|
+
migrations.
|
|
35
|
+
|
|
36
|
+
## 1. Model import guarantee
|
|
37
|
+
|
|
38
|
+
Alembic only detects models that are **imported at runtime**.
|
|
39
|
+
|
|
40
|
+
A canonical module must import every ORM model so metadata is complete.
|
|
41
|
+
|
|
42
|
+
Do not assume `app/`. Use the project's existing `Base` definition --
|
|
43
|
+
typically at `{pkg_name}/models/base.py` (see sqlalchemy-models skill).
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
# {pkg_name}/models/base.py
|
|
49
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Base(DeclarativeBase):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# {pkg_name}/models/__init__.py
|
|
57
|
+
from {pkg_name}.models.user import User
|
|
58
|
+
from {pkg_name}.models.order import Order
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Alembic must reference:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from {pkg_name}.models.base import Base
|
|
65
|
+
|
|
66
|
+
target_metadata = Base.metadata
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This prevents **empty migrations**, the most common Alembic failure.
|
|
70
|
+
|
|
71
|
+
------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
## 2. Rename correction rule
|
|
74
|
+
|
|
75
|
+
Alembic **cannot detect column renames**.
|
|
76
|
+
|
|
77
|
+
If autogenerate emits:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
drop_column
|
|
81
|
+
add_column
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
for the same table in one revision, treat it as a **rename**.
|
|
85
|
+
|
|
86
|
+
Replace with:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
op.alter_column("table", "old_name", new_column_name="new_name")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This prevents silent data loss.
|
|
93
|
+
|
|
94
|
+
------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
## 3. Applied migration immutability
|
|
97
|
+
|
|
98
|
+
Never edit a migration that has already been applied.
|
|
99
|
+
|
|
100
|
+
Correct pattern:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
revision A (applied)
|
|
104
|
+
revision B (fix)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Never:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
edit revision A
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Editing applied migrations breaks migration graphs across environments.
|
|
114
|
+
|
|
115
|
+
------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
## 4. Migration round-trip rule
|
|
118
|
+
|
|
119
|
+
Every migration must succeed on a **fresh database** using:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
upgrade head
|
|
123
|
+
downgrade base
|
|
124
|
+
upgrade head
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This guarantees migrations are:
|
|
128
|
+
|
|
129
|
+
- replayable
|
|
130
|
+
- CI safe
|
|
131
|
+
- environment independent
|
|
132
|
+
|
|
133
|
+
------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
# Trigger
|
|
136
|
+
|
|
137
|
+
Use this skill when the user wants to:
|
|
138
|
+
|
|
139
|
+
- add Alembic to a Python project
|
|
140
|
+
- initialize migrations
|
|
141
|
+
- generate migration revisions
|
|
142
|
+
- review autogenerate diffs
|
|
143
|
+
- fix broken migrations
|
|
144
|
+
- establish safe schema evolution workflows
|
|
145
|
+
|
|
146
|
+
------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
# Migration workflow
|
|
149
|
+
|
|
150
|
+
Correct workflow:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
edit models
|
|
154
|
+
↓
|
|
155
|
+
generate revision
|
|
156
|
+
↓
|
|
157
|
+
review migration
|
|
158
|
+
↓
|
|
159
|
+
fix migration
|
|
160
|
+
↓
|
|
161
|
+
run upgrade
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Never:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
generate → immediately run
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Autogenerate output **must be reviewed before execution**.
|
|
171
|
+
|
|
172
|
+
------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
# Step 0 --- Inspect existing project
|
|
175
|
+
|
|
176
|
+
Before generating anything:
|
|
177
|
+
|
|
178
|
+
1. Check whether `alembic/`, `alembic.ini`, and `alembic/env.py` already
|
|
179
|
+
exist. If they do, **modify the existing configuration** rather than
|
|
180
|
+
reinitializing. Never run `alembic init` if `alembic/` already exists.
|
|
181
|
+
2. Identify where `Base` is defined. If the project used the
|
|
182
|
+
sqlalchemy-models skill, it will be at `{pkg_name}/models/base.py`.
|
|
183
|
+
3. Identify which module imports all models (typically
|
|
184
|
+
`{pkg_name}/models/__init__.py`). This is the module env.py must import.
|
|
185
|
+
4. Note the existing package root. For fastapi-init projects it is
|
|
186
|
+
`{pkg_name}/{pkg_name}/` -- never assume `app/`.
|
|
187
|
+
5. Only create new files if the relevant structure is absent.
|
|
188
|
+
|
|
189
|
+
------------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
# Step 1 --- Initialize Alembic
|
|
192
|
+
|
|
193
|
+
If no `alembic/` directory exists, initialize:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
alembic init alembic
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Project layout after initialization:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
project/
|
|
203
|
+
alembic/
|
|
204
|
+
env.py
|
|
205
|
+
versions/
|
|
206
|
+
alembic.ini
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
# Step 2 --- Configure database connection
|
|
212
|
+
|
|
213
|
+
Leave `sqlalchemy.url` in `alembic.ini` blank or as a placeholder.
|
|
214
|
+
Provide the URL at runtime via the application's settings system.
|
|
215
|
+
|
|
216
|
+
Example in `env.py`:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from {pkg_name}.core.config import settings
|
|
220
|
+
|
|
221
|
+
DATABASE_URL = settings.database_url
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
This pattern defers to the settings-config skill and avoids embedding
|
|
225
|
+
credentials in version-controlled files.
|
|
226
|
+
|
|
227
|
+
------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
# Step 3 --- Configure env.py for SQLAlchemy 2.x
|
|
230
|
+
|
|
231
|
+
Provide a minimal complete `env.py`. Replace `{pkg_name}` with the
|
|
232
|
+
actual package name found in Step 0.
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from logging.config import fileConfig
|
|
236
|
+
|
|
237
|
+
from alembic import context
|
|
238
|
+
from sqlalchemy import engine_from_config, pool
|
|
239
|
+
|
|
240
|
+
from {pkg_name}.core.config import settings
|
|
241
|
+
from {pkg_name}.models.base import Base
|
|
242
|
+
import {pkg_name}.models # noqa: F401 — ensures all models are imported
|
|
243
|
+
|
|
244
|
+
config = context.config
|
|
245
|
+
config.set_main_option("sqlalchemy.url", settings.database_url)
|
|
246
|
+
|
|
247
|
+
if config.config_file_name is not None:
|
|
248
|
+
fileConfig(config.config_file_name)
|
|
249
|
+
|
|
250
|
+
target_metadata = Base.metadata
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def run_migrations_offline() -> None:
|
|
254
|
+
url = config.get_main_option("sqlalchemy.url")
|
|
255
|
+
context.configure(
|
|
256
|
+
url=url,
|
|
257
|
+
target_metadata=target_metadata,
|
|
258
|
+
literal_binds=True,
|
|
259
|
+
dialect_opts={"paramstyle": "named"},
|
|
260
|
+
)
|
|
261
|
+
with context.begin_transaction():
|
|
262
|
+
context.run_migrations()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def run_migrations_online() -> None:
|
|
266
|
+
connectable = engine_from_config(
|
|
267
|
+
config.get_section(config.config_ini_section, {}),
|
|
268
|
+
prefix="sqlalchemy.",
|
|
269
|
+
poolclass=pool.NullPool,
|
|
270
|
+
)
|
|
271
|
+
with connectable.connect() as connection:
|
|
272
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
273
|
+
with context.begin_transaction():
|
|
274
|
+
context.run_migrations()
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
if context.is_offline_mode():
|
|
278
|
+
run_migrations_offline()
|
|
279
|
+
else:
|
|
280
|
+
run_migrations_online()
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Naming conventions**: Projects should configure SQLAlchemy
|
|
284
|
+
`naming_convention` on `MetaData`. This is typically defined where
|
|
285
|
+
`Base` is created (see sqlalchemy-models skill) -- not here.
|
|
286
|
+
|
|
287
|
+
**Async note**: Alembic migrations run synchronously even in async
|
|
288
|
+
applications.
|
|
289
|
+
|
|
290
|
+
------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
# Step 4 --- Generate migrations
|
|
293
|
+
|
|
294
|
+
Create a migration revision:
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
alembic revision --autogenerate -m "add users table"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Migration files appear in:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
alembic/versions/
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
------------------------------------------------------------------------
|
|
307
|
+
|
|
308
|
+
# Step 5 --- Review migration output
|
|
309
|
+
|
|
310
|
+
Review the migration carefully before running it.
|
|
311
|
+
|
|
312
|
+
Check for:
|
|
313
|
+
|
|
314
|
+
- rename mis-detections
|
|
315
|
+
- destructive column drops
|
|
316
|
+
- type conversion safety
|
|
317
|
+
- nullable constraint changes
|
|
318
|
+
- missing indexes or constraints
|
|
319
|
+
|
|
320
|
+
Autogenerate is **a starting point, not a final migration.**
|
|
321
|
+
|
|
322
|
+
------------------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
# Step 6 --- Apply migration
|
|
325
|
+
|
|
326
|
+
Apply migrations:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
alembic upgrade head
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Inspect state:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
alembic current
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
View revision history:
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
alembic history
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
------------------------------------------------------------------------
|
|
345
|
+
|
|
346
|
+
# Migration safety heuristics
|
|
347
|
+
|
|
348
|
+
Carefully review migrations involving:
|
|
349
|
+
|
|
350
|
+
- column drops
|
|
351
|
+
- table drops
|
|
352
|
+
- type changes
|
|
353
|
+
- nullability changes
|
|
354
|
+
- constraint removal
|
|
355
|
+
- index removal
|
|
356
|
+
|
|
357
|
+
These operations require deliberate review.
|
|
358
|
+
|
|
359
|
+
------------------------------------------------------------------------
|
|
360
|
+
|
|
361
|
+
# Anti-patterns
|
|
362
|
+
|
|
363
|
+
Avoid:
|
|
364
|
+
|
|
365
|
+
- Blind autogenerate upgrades
|
|
366
|
+
- Empty migrations caused by missing model imports
|
|
367
|
+
- Hardcoded database URLs
|
|
368
|
+
- Editing already-applied migrations
|
|
369
|
+
|
|
370
|
+
------------------------------------------------------------------------
|
|
371
|
+
|
|
372
|
+
# Adjacent skills
|
|
373
|
+
|
|
374
|
+
This skill typically follows:
|
|
375
|
+
|
|
376
|
+
- `sqlalchemy-models` — establishes `Base`, model package structure, and
|
|
377
|
+
the import module that env.py must reference
|
|
378
|
+
- `settings-config` — establishes the `settings` object and
|
|
379
|
+
`database_url` that env.py uses for the connection string
|
|
380
|
+
|
|
381
|
+
------------------------------------------------------------------------
|
|
382
|
+
|
|
383
|
+
# Outcome
|
|
384
|
+
|
|
385
|
+
After applying this skill the project will have:
|
|
386
|
+
|
|
387
|
+
- correctly wired Alembic configuration
|
|
388
|
+
- reliable SQLAlchemy metadata discovery
|
|
389
|
+
- disciplined migration workflows
|
|
390
|
+
- safer schema evolution
|
|
391
|
+
- deterministic revision history
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: click-cli
|
|
3
|
+
description: Design, generate, and improve Python CLIs using the Click library. Focus on idiomatic architecture, command groups, modular layouts, CLI UX conventions, and avoiding common Click anti-patterns.
|
|
4
|
+
disable-model-invocation: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# click-cli
|
|
8
|
+
|
|
9
|
+
Use this skill when designing or implementing Python command-line interfaces with **Click**.
|
|
10
|
+
|
|
11
|
+
This skill should add value **above baseline Click knowledge** by enforcing:
|
|
12
|
+
- good CLI architecture
|
|
13
|
+
- clear command hierarchy
|
|
14
|
+
- consistent argument and option design
|
|
15
|
+
- idiomatic Click usage
|
|
16
|
+
- avoidance of common Click anti-patterns
|
|
17
|
+
|
|
18
|
+
Apply this skill when the user:
|
|
19
|
+
- asks to create or design a Click CLI
|
|
20
|
+
- wants a Python CLI built with Click
|
|
21
|
+
- needs help structuring commands or subcommands
|
|
22
|
+
- wants idiomatic Click code rather than generic CLI code
|
|
23
|
+
- asks for help improving a Click-based command-line tool
|
|
24
|
+
|
|
25
|
+
Do not apply when:
|
|
26
|
+
- the task is unrelated to Python CLIs
|
|
27
|
+
- the user explicitly wants another CLI framework
|
|
28
|
+
- the question is purely about general application design rather than CLI behavior
|
|
29
|
+
|
|
30
|
+
# Invocation heuristics
|
|
31
|
+
|
|
32
|
+
Prefer this skill when the user:
|
|
33
|
+
- mentions `click`
|
|
34
|
+
- asks for command groups, subcommands, options, or arguments
|
|
35
|
+
- wants a CLI architecture recommendation
|
|
36
|
+
- is starting a new Python CLI project
|
|
37
|
+
- wants help fixing non-idiomatic Click code
|
|
38
|
+
|
|
39
|
+
Do not prefer this skill when:
|
|
40
|
+
- the user explicitly wants `argparse`, `typer`, `cement`, or another framework
|
|
41
|
+
- the task is general Python coding with no CLI component
|
|
42
|
+
|
|
43
|
+
# Mission
|
|
44
|
+
|
|
45
|
+
Produce **production-quality Click CLIs** that are:
|
|
46
|
+
1. idiomatic
|
|
47
|
+
2. modular
|
|
48
|
+
3. easy to extend
|
|
49
|
+
4. clear to use from the command line
|
|
50
|
+
5. consistent in help text and option design
|
|
51
|
+
|
|
52
|
+
Do not merely explain Click syntax unless the user explicitly asks for explanation.
|
|
53
|
+
|
|
54
|
+
# Core Click mental model
|
|
55
|
+
|
|
56
|
+
Click is best used to build CLIs around:
|
|
57
|
+
- a root command
|
|
58
|
+
- optional command groups
|
|
59
|
+
- small, composable subcommands
|
|
60
|
+
- explicit arguments and options
|
|
61
|
+
- predictable help output
|
|
62
|
+
|
|
63
|
+
Prefer clear command hierarchy over monolithic single-file command handlers.
|
|
64
|
+
|
|
65
|
+
# The 5 Click invariants
|
|
66
|
+
|
|
67
|
+
Always follow these rules.
|
|
68
|
+
|
|
69
|
+
## 1. Model the CLI before writing code
|
|
70
|
+
|
|
71
|
+
Define:
|
|
72
|
+
- root command
|
|
73
|
+
- subcommands
|
|
74
|
+
- arguments
|
|
75
|
+
- options
|
|
76
|
+
- shared context if needed
|
|
77
|
+
|
|
78
|
+
Do not jump straight into decorators without first clarifying the command structure in the answer.
|
|
79
|
+
|
|
80
|
+
## 2. Use Click-native constructs
|
|
81
|
+
|
|
82
|
+
Prefer:
|
|
83
|
+
- `@click.group()`
|
|
84
|
+
- `@click.command()`
|
|
85
|
+
- `@click.option()`
|
|
86
|
+
- `@click.argument()`
|
|
87
|
+
- `@click.pass_context()` when context is needed
|
|
88
|
+
|
|
89
|
+
Avoid mixing in patterns from other CLI frameworks unless explicitly requested.
|
|
90
|
+
|
|
91
|
+
## 3. Prefer `click.echo()` for user-facing output
|
|
92
|
+
|
|
93
|
+
Correct:
|
|
94
|
+
|
|
95
|
+
click.echo("Done")
|
|
96
|
+
|
|
97
|
+
Avoid defaulting to:
|
|
98
|
+
|
|
99
|
+
print("Done")
|
|
100
|
+
|
|
101
|
+
unless the user explicitly wants raw Python output behavior.
|
|
102
|
+
|
|
103
|
+
## 4. Prefer modular command layouts for multi-command CLIs
|
|
104
|
+
|
|
105
|
+
For anything beyond a very small CLI, prefer structure like:
|
|
106
|
+
|
|
107
|
+
project/
|
|
108
|
+
cli.py
|
|
109
|
+
commands/
|
|
110
|
+
init.py
|
|
111
|
+
run.py
|
|
112
|
+
status.py
|
|
113
|
+
|
|
114
|
+
Avoid placing every command in one large file when the CLI clearly has multiple concerns.
|
|
115
|
+
|
|
116
|
+
## 5. Design the CLI UX deliberately
|
|
117
|
+
|
|
118
|
+
Ensure:
|
|
119
|
+
- option names are consistent
|
|
120
|
+
- help text is useful
|
|
121
|
+
- positional arguments are used sparingly and intentionally
|
|
122
|
+
- subcommands are discoverable
|
|
123
|
+
- command names are short and predictable
|
|
124
|
+
|
|
125
|
+
# Standard operating procedure
|
|
126
|
+
|
|
127
|
+
## Step 1 — Design the CLI shape
|
|
128
|
+
|
|
129
|
+
Identify:
|
|
130
|
+
- root command name
|
|
131
|
+
- subcommands
|
|
132
|
+
- argument vs option boundaries
|
|
133
|
+
- shared/global options
|
|
134
|
+
- whether grouping is needed
|
|
135
|
+
|
|
136
|
+
Return a compact command tree when useful, for example:
|
|
137
|
+
|
|
138
|
+
dataset
|
|
139
|
+
ingest
|
|
140
|
+
validate
|
|
141
|
+
publish
|
|
142
|
+
|
|
143
|
+
## Step 2 — Recommend file layout
|
|
144
|
+
|
|
145
|
+
If the CLI is trivial, a single file may be acceptable.
|
|
146
|
+
|
|
147
|
+
If the CLI has multiple commands or domains, prefer modular layout.
|
|
148
|
+
|
|
149
|
+
## Step 3 — Generate idiomatic Click code
|
|
150
|
+
|
|
151
|
+
Use:
|
|
152
|
+
- typed options where appropriate
|
|
153
|
+
- clear help strings
|
|
154
|
+
- small command handlers
|
|
155
|
+
- `click.echo()` for output
|
|
156
|
+
- context objects only when they provide real value
|
|
157
|
+
|
|
158
|
+
## Step 4 — Validate the design
|
|
159
|
+
|
|
160
|
+
Before finishing, verify:
|
|
161
|
+
- the hierarchy is logical
|
|
162
|
+
- the code matches the designed command structure
|
|
163
|
+
- option names are consistent
|
|
164
|
+
- help text is present where needed
|
|
165
|
+
- the solution uses Click idioms rather than generic CLI habits
|
|
166
|
+
|
|
167
|
+
# Output contract
|
|
168
|
+
|
|
169
|
+
When generating or designing a Click CLI, prefer this structure in your answer:
|
|
170
|
+
|
|
171
|
+
1. CLI Architecture
|
|
172
|
+
2. Recommended File Layout
|
|
173
|
+
3. Click Implementation
|
|
174
|
+
4. Usage Examples
|
|
175
|
+
|
|
176
|
+
If the user asks for only code, still internally follow the same structure and provide the code cleanly.
|
|
177
|
+
|
|
178
|
+
# Anti-pattern detection
|
|
179
|
+
|
|
180
|
+
Call out these problems explicitly when they appear:
|
|
181
|
+
- monolithic all-in-one CLI files
|
|
182
|
+
- `print()` instead of `click.echo()`
|
|
183
|
+
- argparse-style logic mixed into Click code
|
|
184
|
+
- manual help formatting that Click should generate
|
|
185
|
+
- inconsistent option naming
|
|
186
|
+
- excessive positional arguments
|
|
187
|
+
- unnecessary global state
|
|
188
|
+
|
|
189
|
+
# Response style
|
|
190
|
+
|
|
191
|
+
- concise
|
|
192
|
+
- concrete
|
|
193
|
+
- code-first when implementation is requested
|
|
194
|
+
- architecture-aware
|
|
195
|
+
- no unnecessary framework comparisons unless relevant
|
|
196
|
+
|
|
197
|
+
# Completion checklist
|
|
198
|
+
|
|
199
|
+
Before finishing an answer verify:
|
|
200
|
+
- the CLI structure is clear
|
|
201
|
+
- Click-native decorators and patterns are used
|
|
202
|
+
- output uses `click.echo()` where appropriate
|
|
203
|
+
- the layout is modular when the CLI complexity warrants it
|
|
204
|
+
- the answer adds architectural guidance, not just decorator syntax
|