python-skills 1.0.0__py3-none-any.whl

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 (105) hide show
  1. python_skills/__init__.py +10 -0
  2. python_skills/__main__.py +6 -0
  3. python_skills/adapters/__init__.py +48 -0
  4. python_skills/adapters/agent_skills.py +415 -0
  5. python_skills/adapters/aider_adapter.py +226 -0
  6. python_skills/adapters/base.py +153 -0
  7. python_skills/adapters/claude.py +474 -0
  8. python_skills/adapters/cline.py +332 -0
  9. python_skills/adapters/codex.py +24 -0
  10. python_skills/adapters/continue_adapter.py +198 -0
  11. python_skills/adapters/cursor.py +327 -0
  12. python_skills/adapters/gemini.py +26 -0
  13. python_skills/adapters/goose.py +26 -0
  14. python_skills/adapters/junie.py +25 -0
  15. python_skills/adapters/kiro.py +382 -0
  16. python_skills/adapters/opencode.py +27 -0
  17. python_skills/adapters/roo.py +25 -0
  18. python_skills/adapters/universal.py +203 -0
  19. python_skills/adapters/vscode.py +27 -0
  20. python_skills/adapters/windsurf.py +26 -0
  21. python_skills/adapters/zed.py +27 -0
  22. python_skills/cli.py +326 -0
  23. python_skills/config.py +160 -0
  24. python_skills/detector.py +152 -0
  25. python_skills/installer.py +163 -0
  26. python_skills/markers.py +115 -0
  27. python_skills/skills/__init__.py +14 -0
  28. python_skills/skills/loader.py +171 -0
  29. python_skills/skills/metadata.py +152 -0
  30. python_skills/skills/registry.py +101 -0
  31. python_skills/state.py +204 -0
  32. python_skills-1.0.0.dist-info/METADATA +99 -0
  33. python_skills-1.0.0.dist-info/RECORD +105 -0
  34. python_skills-1.0.0.dist-info/WHEEL +4 -0
  35. python_skills-1.0.0.dist-info/entry_points.txt +2 -0
  36. python_skills-1.0.0.dist-info/licenses/LICENSE +21 -0
  37. skills/advanced_python.md +239 -0
  38. skills/anti_patterns/index.md +406 -0
  39. skills/comprehensions.md +167 -0
  40. skills/control_flow.md +175 -0
  41. skills/data_structures.md +243 -0
  42. skills/debugging/common_bugs.md +222 -0
  43. skills/debugging/inspection_techniques.md +249 -0
  44. skills/debugging/root_cause.md +203 -0
  45. skills/engineering/application_logging.md +195 -0
  46. skills/engineering/cli_apps.md +207 -0
  47. skills/engineering/configuration.md +218 -0
  48. skills/engineering/database.md +240 -0
  49. skills/engineering/dependency_management.md +205 -0
  50. skills/engineering/http_clients.md +267 -0
  51. skills/engineering/modules_packages.md +211 -0
  52. skills/engineering/packaging.md +197 -0
  53. skills/engineering/project_structure.md +155 -0
  54. skills/engineering/pyproject_toml.md +302 -0
  55. skills/engineering/virtual_environments.md +206 -0
  56. skills/functions.md +244 -0
  57. skills/generation/async_concurrency.md +291 -0
  58. skills/generation/error_handling.md +276 -0
  59. skills/generation/protocols_generics.md +243 -0
  60. skills/generation/type_hints.md +290 -0
  61. skills/generation/validation_pipeline.md +274 -0
  62. skills/generation/workflow.md +190 -0
  63. skills/oop.md +228 -0
  64. skills/quality/abstractions.md +154 -0
  65. skills/quality/comments.md +177 -0
  66. skills/quality/documentation.md +176 -0
  67. skills/quality/duplication.md +137 -0
  68. skills/quality/maintainability.md +142 -0
  69. skills/quality/naming.md +171 -0
  70. skills/quality/quality_functions.md +245 -0
  71. skills/quality/readability.md +239 -0
  72. skills/quality/type_annotations.md +192 -0
  73. skills/refactoring/behavior_preservation.md +157 -0
  74. skills/refactoring/incremental.md +187 -0
  75. skills/refactoring/interface_stability.md +199 -0
  76. skills/refactoring/safe_refactoring.md +206 -0
  77. skills/security/auth_boundaries.md +200 -0
  78. skills/security/command_injection.md +207 -0
  79. skills/security/dependency_risks.md +282 -0
  80. skills/security/file_handling.md +156 -0
  81. skills/security/input_validation.md +190 -0
  82. skills/security/path_traversal.md +172 -0
  83. skills/security/secrets.md +171 -0
  84. skills/security/sql_injection.md +188 -0
  85. skills/security/unsafe_deserialization.md +164 -0
  86. skills/stdlib/argparse.md +178 -0
  87. skills/stdlib/collections.md +212 -0
  88. skills/stdlib/datetime.md +187 -0
  89. skills/stdlib/functools.md +238 -0
  90. skills/stdlib/itertools.md +183 -0
  91. skills/stdlib/json.md +162 -0
  92. skills/stdlib/logging.md +185 -0
  93. skills/stdlib/os_sys.md +184 -0
  94. skills/stdlib/pathlib.md +218 -0
  95. skills/stdlib/re.md +171 -0
  96. skills/stdlib/statistics.md +112 -0
  97. skills/stdlib/subprocess.md +211 -0
  98. skills/testing/async_tests.md +249 -0
  99. skills/testing/coverage.md +168 -0
  100. skills/testing/edge_cases.md +197 -0
  101. skills/testing/fixtures_mocks.md +203 -0
  102. skills/testing/organization.md +205 -0
  103. skills/testing/parameterized.md +174 -0
  104. skills/testing/regression_tests.md +165 -0
  105. skills/variables_types.md +107 -0
@@ -0,0 +1,195 @@
1
+ # Engineering: Logging (Application)
2
+
3
+ **Purpose**: Application-level logging setup and patterns.
4
+
5
+ **When to use**: Setting up logging for applications (not libraries).
6
+
7
+ ---
8
+
9
+ ## Core Rules
10
+
11
+ ### Application vs Library Logging
12
+ - **Application**: Configures logging (handlers, formatters, levels)
13
+ - **Library**: Only uses `logging.getLogger(__name__)`, adds `NullHandler`
14
+
15
+ ### Structured Logging (JSON)
16
+ ```python
17
+ import logging
18
+ import json
19
+ import sys
20
+ from datetime import datetime
21
+
22
+ class JSONFormatter(logging.Formatter):
23
+ def format(self, record: logging.LogRecord) -> str:
24
+ log_data = {
25
+ "timestamp": datetime.fromtimestamp(record.created).isoformat(),
26
+ "level": record.levelname,
27
+ "logger": record.name,
28
+ "message": record.getMessage(),
29
+ "module": record.module,
30
+ "function": record.funcName,
31
+ "line": record.lineno,
32
+ }
33
+
34
+ # Add extra fields
35
+ for key, value in record.__dict__.items():
36
+ if key not in {"name", "msg", "args", "created", "filename", "funcName",
37
+ "levelname", "levelno", "lineno", "module", "msecs",
38
+ "message", "name", "pathname", "process", "processName",
39
+ "relativeCreated", "thread", "threadName", "exc_info",
40
+ "exc_text", "stack_info", "getMessage"}:
41
+ log_data[key] = value
42
+
43
+ if record.exc_info:
44
+ log_data["exception"] = self.formatException(record.exc_info)
45
+
46
+ return json.dumps(log_data, default=str)
47
+
48
+ # Setup
49
+ handler = logging.StreamHandler(sys.stdout)
50
+ handler.setFormatter(JSONFormatter())
51
+ root = logging.getLogger()
52
+ root.addHandler(handler)
53
+ root.setLevel(logging.INFO)
54
+ ```
55
+
56
+ ### Structlog (Better Structured Logging)
57
+ ```python
58
+ import structlog
59
+
60
+ structlog.configure(
61
+ processors=[
62
+ structlog.contextvars.merge_contextvars,
63
+ structlog.processors.add_log_level,
64
+ structlog.processors.TimeStamper(fmt="iso"),
65
+ structlog.dev.ConsoleRenderer() if dev else structlog.processors.JSONRenderer(),
66
+ ],
67
+ wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
68
+ context_class=dict,
69
+ logger_factory=structlog.stdlib.LoggerFactory(),
70
+ cache_logger_on_first_use=True,
71
+ )
72
+
73
+ log = structlog.get_logger()
74
+
75
+ # Usage
76
+ log.info("user_login", user_id=123, ip="1.2.3.4")
77
+ log.error("db_failed", error=str(e), query="SELECT ...")
78
+ ```
79
+
80
+ ### Context Injection
81
+ ```python
82
+ import contextvars
83
+
84
+ request_id_var: contextvars.ContextVar[str | None] = contextvars.ContextVar("request_id", default=None)
85
+
86
+ class RequestIDFilter(logging.Filter):
87
+ def filter(self, record: logging.LogRecord) -> bool:
88
+ record.request_id = request_id_var.get()
89
+ return True
90
+
91
+ # In middleware
92
+ request_id_var.set("req-123")
93
+ log.info("processing") # Includes request_id
94
+ ```
95
+
96
+ ### Log Levels Guide
97
+ | Level | Use Case |
98
+ |-------|----------|
99
+ | DEBUG | Detailed diagnostic (dev only) |
100
+ | INFO | General operations (requests, startup) |
101
+ | WARNING | Unexpected but handled (retry, fallback) |
102
+ | ERROR | Operation failed (5xx, failed request) |
103
+ | CRITICAL | System may stop (OOM, disk full) |
104
+
105
+ ---
106
+
107
+ ## Decision Rules
108
+
109
+ | Situation | Setup |
110
+ |-----------|-------|
111
+ | Simple app | `logging.basicConfig` + JSON formatter |
112
+ | Production service | structlog + JSON + log aggregation |
113
+ | Library | `getLogger(__name__)` + `NullHandler` |
114
+ | CLI tool | Rich handler for pretty output |
115
+
116
+ ---
117
+
118
+ ## Preferred Patterns
119
+
120
+ ```python
121
+ # Centralized setup (call once at startup)
122
+ def setup_logging(
123
+ level: str = "INFO",
124
+ json_format: bool = False,
125
+ dev_mode: bool = False,
126
+ ) -> None:
127
+ import logging
128
+ import sys
129
+
130
+ # Clear existing
131
+ root = logging.getLogger()
132
+ for h in root.handlers[:]:
133
+ root.removeHandler(h)
134
+
135
+ handler = logging.StreamHandler(sys.stdout)
136
+
137
+ if json_format and not dev_mode:
138
+ handler.setFormatter(JSONFormatter())
139
+ elif dev_mode:
140
+ import rich.logging
141
+ handler = rich.logging.RichHandler(rich_tracebacks=True)
142
+ else:
143
+ handler.setFormatter(logging.Formatter(
144
+ "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
145
+ ))
146
+
147
+ root.addHandler(handler)
148
+ root.setLevel(level)
149
+
150
+ # Quiet noisy libraries
151
+ logging.getLogger("httpx").setLevel(logging.WARNING)
152
+ logging.getLogger("httpcore").setLevel(logging.WARNING)
153
+ logging.getLogger("asyncio").setLevel(logging.WARNING)
154
+
155
+ # Usage in app
156
+ log = logging.getLogger(__name__)
157
+
158
+ def process_order(order_id: str):
159
+ log.info("order_started", order_id=order_id)
160
+ try:
161
+ do_work()
162
+ log.info("order_completed", order_id=order_id)
163
+ except Exception as e:
164
+ log.exception("order_failed", order_id=order_id, error=str(e))
165
+ raise
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Avoid
171
+
172
+ - `print()` in production code
173
+ - Logging sensitive data (passwords, tokens, PII)
174
+ - Excessive DEBUG in production
175
+ - No log rotation (use `RotatingFileHandler` or external)
176
+ - Blocking I/O in logging (use `QueueHandler` + `QueueListener` for high volume)
177
+
178
+ ---
179
+
180
+ ## Validation Considerations
181
+
182
+ - Test log output format
183
+ - Verify log levels in different environments
184
+ - Check structured fields are present
185
+ - Ensure no PII in logs
186
+ - Test high-volume logging doesn't block
187
+
188
+ ---
189
+
190
+ ## Related Skills
191
+
192
+ - `stdlib/logging.md`
193
+ - `engineering/configuration.md`
194
+ - `security/secrets.md`
195
+ - `generation/error_handling.md`
@@ -0,0 +1,207 @@
1
+ # Engineering: CLI Applications
2
+
3
+ **Purpose**: Building command-line interfaces with Python.
4
+
5
+ **When to use**: Creating CLI tools, scripts, or application entry points.
6
+
7
+ ---
8
+
9
+ ## Core Rules
10
+
11
+ ### Entry Points
12
+ ```toml
13
+ # pyproject.toml
14
+ [project.scripts]
15
+ my-cli = "mypackage.cli:main"
16
+ my-other = "mypackage.other:run"
17
+ ```
18
+
19
+ ### Click (Recommended for Complex CLIs)
20
+ ```python
21
+ import click
22
+ from pathlib import Path
23
+
24
+ @click.group()
25
+ @click.version_option()
26
+ @click.option("-v", "--verbose", count=True)
27
+ @click.pass_context
28
+ def cli(ctx: click.Context, verbose: int):
29
+ """My CLI tool."""
30
+ ctx.ensure_object(dict)
31
+ ctx.obj["verbose"] = verbose
32
+
33
+ @cli.command()
34
+ @click.argument("input", type=click.Path(exists=True, path_type=Path))
35
+ @click.option("-o", "--output", type=click.Path(path_type=Path))
36
+ @click.pass_context
37
+ def process(ctx: click.Context, input: Path, output: Path | None):
38
+ """Process a file."""
39
+ verbose = ctx.obj["verbose"]
40
+ if verbose:
41
+ click.echo(f"Processing {input}")
42
+ # ...
43
+
44
+ @cli.command()
45
+ @click.option("--force", is_flag=True)
46
+ def clean(force: bool):
47
+ """Clean cache."""
48
+ if not force:
49
+ click.confirm("Delete all cache?", abort=True)
50
+ # ...
51
+
52
+ if __name__ == "__main__":
53
+ cli()
54
+ ```
55
+
56
+ ### Typer (Modern, Type-Hint Based)
57
+ ```python
58
+ import typer
59
+ from pathlib import Path
60
+ from typing_extensions import Annotated
61
+
62
+ app = typer.Typer()
63
+
64
+ @app.command()
65
+ def process(
66
+ input: Annotated[Path, typer.Argument(exists=True, help="Input file")],
67
+ output: Annotated[Path | None, typer.Option("-o", "--output")] = None,
68
+ verbose: Annotated[int, typer.Option("-v", "--verbose", count=True)] = 0,
69
+ ):
70
+ """Process a file."""
71
+ if verbose:
72
+ typer.echo(f"Processing {input}")
73
+
74
+ @app.command()
75
+ def clean(force: Annotated[bool, typer.Option("--force", help="Skip confirmation")] = False):
76
+ """Clean cache."""
77
+ if not force:
78
+ typer.confirm("Delete all cache?", abort=True)
79
+
80
+ if __name__ == "__main__":
81
+ app()
82
+ ```
83
+
84
+ ### argparse (Stdlib Only)
85
+ ```python
86
+ import argparse
87
+ from pathlib import Path
88
+
89
+ def create_parser() -> argparse.ArgumentParser:
90
+ parser = argparse.ArgumentParser(
91
+ prog="my-cli",
92
+ description="Description",
93
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
94
+ )
95
+ parser.add_argument("input", type=Path)
96
+ parser.add_argument("-o", "--output", type=Path)
97
+ parser.add_argument("-v", "--verbose", action="count", default=0)
98
+ return parser
99
+
100
+ def main(argv: list[str] | None = None) -> int:
101
+ parser = create_parser()
102
+ args = parser.parse_args(argv)
103
+ # ...
104
+ return 0
105
+
106
+ if __name__ == "__main__":
107
+ sys.exit(main())
108
+ ```
109
+
110
+ ### Rich (Beautiful Output)
111
+ ```python
112
+ from rich.console import Console
113
+ from rich.table import Table
114
+ from rich.progress import Progress
115
+
116
+ console = Console()
117
+
118
+ console.print("[bold green]Success![/bold green]")
119
+ console.print("[red]Error:[/red] Something failed")
120
+
121
+ table = Table(title="Results")
122
+ table.add_column("Name")
123
+ table.add_column("Status")
124
+ table.add_row("Item 1", "[green]OK[/green]")
125
+ table.add_row("Item 2", "[red]FAIL[/red]")
126
+ console.print(table)
127
+
128
+ with Progress() as progress:
129
+ task = progress.add_task("Processing...", total=100)
130
+ for i in range(100):
131
+ progress.update(task, advance=1)
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Decision Rules
137
+
138
+ | Complexity | Tool |
139
+ |------------|------|
140
+ | Simple (1-3 commands) | `argparse` (stdlib) |
141
+ | Multiple commands, options | `click` |
142
+ | Modern, type-hint focused | `typer` |
143
+ | Beautiful output needed | `rich` (with any) |
144
+ | No dependencies allowed | `argparse` |
145
+
146
+ ---
147
+
148
+ ## Preferred Patterns
149
+
150
+ ```python
151
+ # Common structure
152
+ # src/mypackage/cli.py
153
+
154
+ def main(argv: list[str] | None = None) -> int:
155
+ """Entry point for setuptools console_scripts."""
156
+ try:
157
+ # Parse args, run logic
158
+ return 0
159
+ except KeyboardInterrupt:
160
+ return 130
161
+ except Exception as e:
162
+ logger.exception("Fatal error")
163
+ return 1
164
+
165
+ if __name__ == "__main__":
166
+ sys.exit(main())
167
+ ```
168
+
169
+ ### Subcommand Organization
170
+ ```
171
+ cli.py # Main entry, creates command group
172
+ commands/
173
+ __init__.py # Imports and registers subcommands
174
+ process.py # @cli.command() or @app.command()
175
+ clean.py
176
+ config.py
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Avoid
182
+
183
+ - Logic in `__main__.py` (use entry point function)
184
+ - Global state in CLI module
185
+ - Printing directly (use `click.echo`, `typer.echo`, `console.print`)
186
+ - No `--help` / `--version`
187
+ - Exit codes not meaningful (0=success, 1=error, 130=interrupt)
188
+
189
+ ---
190
+
191
+ ## Validation Considerations
192
+
193
+ - `my-cli --help` works
194
+ - `my-cli --version` shows version
195
+ - Invalid args show usage + exit 2
196
+ - Tests for each command
197
+ - Shell completion (`click`/`typer` support)
198
+
199
+ ---
200
+
201
+ ## Related Skills
202
+
203
+ - `stdlib/argparse.md`
204
+ - `engineering/configuration.md`
205
+ - `engineering/packaging.md`
206
+ - `generation/error_handling.md`
207
+ - `testing/organization.md`
@@ -0,0 +1,218 @@
1
+ # Engineering: Configuration
2
+
3
+ **Purpose**: Application configuration management patterns.
4
+
5
+ **When to use**: Any application needing configuration (CLI, server, library).
6
+
7
+ ---
8
+
9
+ ## Core Rules
10
+
11
+ ### Configuration Sources (Priority Order)
12
+ 1. **Defaults** — Code defaults (lowest)
13
+ 2. **Config file** — TOML/YAML/JSON file
14
+ 3. **Environment variables** — Override config file
15
+ 4. **CLI arguments** — Highest priority (for apps)
16
+
17
+ ### Layered Configuration
18
+ ```python
19
+ from dataclasses import dataclass, field
20
+ from typing import Optional
21
+ import os
22
+ from pathlib import Path
23
+
24
+ @dataclass
25
+ class Config:
26
+ # Required (no default)
27
+ database_url: str
28
+
29
+ # Optional with defaults
30
+ host: str = "0.0.0.0"
31
+ port: int = 8000
32
+ debug: bool = False
33
+ log_level: str = "INFO"
34
+
35
+ # Nested config
36
+ redis: "RedisConfig" = field(default_factory=RedisConfig)
37
+
38
+ @dataclass
39
+ class RedisConfig:
40
+ host: str = "localhost"
41
+ port: int = 6379
42
+ db: int = 0
43
+ password: Optional[str] = None
44
+ ```
45
+
46
+ ### Environment Variable Mapping
47
+ ```python
48
+ import os
49
+ from dataclasses import fields
50
+
51
+ def load_from_env(config: Config, prefix: str = "APP_") -> Config:
52
+ """Update config from environment variables."""
53
+ for f in fields(config):
54
+ env_key = f"{prefix}{f.name.upper()}"
55
+ if env_key in os.environ:
56
+ value = os.environ[env_key]
57
+ # Type conversion
58
+ if f.type == bool:
59
+ value = value.lower() in ("1", "true", "yes", "on")
60
+ elif f.type == int:
61
+ value = int(value)
62
+ elif f.type == list[str]:
63
+ value = value.split(",")
64
+ setattr(config, f.name, value)
65
+ return config
66
+ ```
67
+
68
+ ### Config File Loading (TOML)
69
+ ```python
70
+ import tomllib # Python 3.11+
71
+
72
+ def load_config_file(path: Path) -> dict:
73
+ with path.open("rb") as f:
74
+ return tomllib.load(f)
75
+
76
+ # For older Python: pip install tomli
77
+ ```
78
+
79
+ ### Complete Loader
80
+ ```python
81
+ def load_config(
82
+ config_path: Path | None = None,
83
+ env_prefix: str = "APP_",
84
+ cli_overrides: dict | None = None,
85
+ ) -> Config:
86
+ # 1. Defaults
87
+ config = Config(database_url="") # Will be validated
88
+
89
+ # 2. Config file
90
+ if config_path and config_path.exists():
91
+ file_config = load_config_file(config_path)
92
+ apply_dict(config, file_config)
93
+
94
+ # 3. Environment
95
+ load_from_env(config, env_prefix)
96
+
97
+ # 4. CLI overrides
98
+ if cli_overrides:
99
+ apply_dict(config, cli_overrides)
100
+
101
+ # 5. Validate
102
+ validate_config(config)
103
+
104
+ return config
105
+
106
+ def apply_dict(config: Config, data: dict) -> None:
107
+ for key, value in data.items():
108
+ if hasattr(config, key):
109
+ setattr(config, key, value)
110
+ elif hasattr(config, key.replace("-", "_")):
111
+ setattr(config, key.replace("-", "_"), value)
112
+
113
+ def validate_config(config: Config) -> None:
114
+ if not config.database_url:
115
+ raise ValueError("database_url is required")
116
+ if not 1 <= config.port <= 65535:
117
+ raise ValueError("Invalid port")
118
+ ```
119
+
120
+ ### Pydantic Settings (Alternative)
121
+ ```python
122
+ from pydantic_settings import BaseSettings, SettingsConfigDict
123
+
124
+ class Settings(BaseSettings):
125
+ database_url: str
126
+ host: str = "0.0.0.0"
127
+ port: int = 8000
128
+ debug: bool = False
129
+
130
+ model_config = SettingsConfigDict(
131
+ env_file=".env",
132
+ env_file_encoding="utf-8",
133
+ env_prefix="APP_",
134
+ case_sensitive=False,
135
+ extra="ignore",
136
+ )
137
+
138
+ settings = Settings() # Auto-loads from env + .env
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Decision Rules
144
+
145
+ | Need | Approach |
146
+ |------|----------|
147
+ | Simple app | Dataclass + env vars |
148
+ | Complex validation | Pydantic Settings |
149
+ | Multiple environments | Config files per env + env vars |
150
+ | Secrets | Environment variables only (never files) |
151
+ | Feature flags | Config + env override |
152
+
153
+ ---
154
+
155
+ ## Preferred Patterns
156
+
157
+ ```python
158
+ # Central config instance
159
+ _config: Config | None = None
160
+
161
+ def get_config() -> Config:
162
+ global _config
163
+ if _config is None:
164
+ _config = load_config()
165
+ return _config
166
+
167
+ # Reset for testing
168
+ def reset_config() -> None:
169
+ global _config
170
+ _config = None
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Secrets Management
176
+
177
+ ```python
178
+ # NEVER hardcode secrets
179
+ # NEVER commit .env files with real secrets
180
+ # Use: Environment variables, secret managers (AWS Secrets, Vault, etc.)
181
+
182
+ # .env.example (committed)
183
+ DATABASE_URL=postgresql://user:pass@localhost/db
184
+ API_KEY=
185
+
186
+ # .env (gitignored, local only)
187
+ DATABASE_URL=postgresql://prod:secret@db/prod
188
+ API_KEY=sk-live-...
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Avoid
194
+
195
+ - Global mutable config (use frozen dataclass or singleton getter)
196
+ - Config file with secrets
197
+ - Complex inheritance in config
198
+ - Silent failures on missing required config
199
+ - Type confusion (env vars are strings)
200
+
201
+ ---
202
+
203
+ ## Validation Considerations
204
+
205
+ - Test config loading with various sources
206
+ - Validate required fields
207
+ - Test type conversion edge cases
208
+ - Ensure secrets never logged
209
+
210
+ ---
211
+
212
+ ## Related Skills
213
+
214
+ - `engineering/pyproject_toml.md`
215
+ - `engineering/cli_apps.md`
216
+ - `security/secrets.md`
217
+ - `generation/type_hints.md`
218
+ - `generation/error_handling.md`