@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 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
+ ![version](https://img.shields.io/github/v/tag/robhowley/py-pit-skills)
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