specli 0.1.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 (129) hide show
  1. specli-0.1.0/.gitattributes +2 -0
  2. specli-0.1.0/.gitignore +80 -0
  3. specli-0.1.0/CHANGELOG.md +41 -0
  4. specli-0.1.0/CONTRIBUTING.md +315 -0
  5. specli-0.1.0/LICENSE +21 -0
  6. specli-0.1.0/PKG-INFO +882 -0
  7. specli-0.1.0/README.md +845 -0
  8. specli-0.1.0/docs/auth.md +315 -0
  9. specli-0.1.0/docs/configuration.md +221 -0
  10. specli-0.1.0/docs/getting-started.md +252 -0
  11. specli-0.1.0/docs/path-rules.md +336 -0
  12. specli-0.1.0/docs/plugins.md +369 -0
  13. specli-0.1.0/docs/skill-generation.md +241 -0
  14. specli-0.1.0/examples/README.md +92 -0
  15. specli-0.1.0/examples/uspto-openapi.json +252 -0
  16. specli-0.1.0/plugins/example_plugin/__init__.py +1 -0
  17. specli-0.1.0/plugins/example_plugin/plugin.py +46 -0
  18. specli-0.1.0/pyproject.toml +65 -0
  19. specli-0.1.0/specli.json +3 -0
  20. specli-0.1.0/src/specli/__init__.py +25 -0
  21. specli-0.1.0/src/specli/__main__.py +6 -0
  22. specli-0.1.0/src/specli/app.py +343 -0
  23. specli-0.1.0/src/specli/auth/__init__.py +36 -0
  24. specli-0.1.0/src/specli/auth/base.py +132 -0
  25. specli-0.1.0/src/specli/auth/credential_store.py +187 -0
  26. specli-0.1.0/src/specli/auth/manager.py +156 -0
  27. specli-0.1.0/src/specli/cache/__init__.py +15 -0
  28. specli-0.1.0/src/specli/cache/cache.py +158 -0
  29. specli-0.1.0/src/specli/client/__init__.py +27 -0
  30. specli-0.1.0/src/specli/client/async_client.py +448 -0
  31. specli-0.1.0/src/specli/client/response.py +72 -0
  32. specli-0.1.0/src/specli/client/sync_client.py +519 -0
  33. specli-0.1.0/src/specli/commands/__init__.py +16 -0
  34. specli-0.1.0/src/specli/commands/auth.py +442 -0
  35. specli-0.1.0/src/specli/commands/config.py +147 -0
  36. specli-0.1.0/src/specli/commands/init.py +140 -0
  37. specli-0.1.0/src/specli/commands/inspect.py +222 -0
  38. specli-0.1.0/src/specli/config.py +474 -0
  39. specli-0.1.0/src/specli/enrichment/__init__.py +83 -0
  40. specli-0.1.0/src/specli/enrichment/enricher.py +243 -0
  41. specli-0.1.0/src/specli/enrichment/scanner.py +589 -0
  42. specli-0.1.0/src/specli/enrichment/strings.py +304 -0
  43. specli-0.1.0/src/specli/exceptions.py +103 -0
  44. specli-0.1.0/src/specli/exit_codes.py +40 -0
  45. specli-0.1.0/src/specli/generator/__init__.py +32 -0
  46. specli-0.1.0/src/specli/generator/command_tree.py +710 -0
  47. specli-0.1.0/src/specli/generator/param_mapper.py +258 -0
  48. specli-0.1.0/src/specli/generator/path_rules.py +280 -0
  49. specli-0.1.0/src/specli/models.py +405 -0
  50. specli-0.1.0/src/specli/output.py +539 -0
  51. specli-0.1.0/src/specli/parser/__init__.py +29 -0
  52. specli-0.1.0/src/specli/parser/extractor.py +436 -0
  53. specli-0.1.0/src/specli/parser/loader.py +248 -0
  54. specli-0.1.0/src/specli/parser/resolver.py +170 -0
  55. specli-0.1.0/src/specli/plugins/__init__.py +33 -0
  56. specli-0.1.0/src/specli/plugins/api_key/__init__.py +14 -0
  57. specli-0.1.0/src/specli/plugins/api_key/plugin.py +133 -0
  58. specli-0.1.0/src/specli/plugins/api_key_gen/__init__.py +18 -0
  59. specli-0.1.0/src/specli/plugins/api_key_gen/plugin.py +199 -0
  60. specli-0.1.0/src/specli/plugins/base.py +147 -0
  61. specli-0.1.0/src/specli/plugins/basic/__init__.py +14 -0
  62. specli-0.1.0/src/specli/plugins/basic/plugin.py +74 -0
  63. specli-0.1.0/src/specli/plugins/bearer/__init__.py +14 -0
  64. specli-0.1.0/src/specli/plugins/bearer/plugin.py +61 -0
  65. specli-0.1.0/src/specli/plugins/browser_login/__init__.py +21 -0
  66. specli-0.1.0/src/specli/plugins/browser_login/plugin.py +738 -0
  67. specli-0.1.0/src/specli/plugins/completion/__init__.py +13 -0
  68. specli-0.1.0/src/specli/plugins/completion/plugin.py +184 -0
  69. specli-0.1.0/src/specli/plugins/device_code/__init__.py +15 -0
  70. specli-0.1.0/src/specli/plugins/device_code/plugin.py +416 -0
  71. specli-0.1.0/src/specli/plugins/hooks.py +135 -0
  72. specli-0.1.0/src/specli/plugins/manager.py +218 -0
  73. specli-0.1.0/src/specli/plugins/manual_token/__init__.py +15 -0
  74. specli-0.1.0/src/specli/plugins/manual_token/plugin.py +149 -0
  75. specli-0.1.0/src/specli/plugins/oauth2_auth_code/__init__.py +23 -0
  76. specli-0.1.0/src/specli/plugins/oauth2_auth_code/plugin.py +384 -0
  77. specli-0.1.0/src/specli/plugins/oauth2_client_credentials/__init__.py +19 -0
  78. specli-0.1.0/src/specli/plugins/oauth2_client_credentials/plugin.py +189 -0
  79. specli-0.1.0/src/specli/plugins/openid_connect/__init__.py +16 -0
  80. specli-0.1.0/src/specli/plugins/openid_connect/plugin.py +177 -0
  81. specli-0.1.0/src/specli/plugins/skill/__init__.py +18 -0
  82. specli-0.1.0/src/specli/plugins/skill/generator.py +317 -0
  83. specli-0.1.0/src/specli/plugins/skill/plugin.py +97 -0
  84. specli-0.1.0/src/specli/plugins/skill/templates/auth_setup.md.j2 +54 -0
  85. specli-0.1.0/src/specli/plugins/skill/templates/reference.md.j2 +38 -0
  86. specli-0.1.0/src/specli/plugins/skill/templates/skill.md.j2 +40 -0
  87. specli-0.1.0/src/specli/py.typed +0 -0
  88. specli-0.1.0/tests/__init__.py +0 -0
  89. specli-0.1.0/tests/conftest.py +204 -0
  90. specli-0.1.0/tests/fixtures/complex_auth.json +256 -0
  91. specli-0.1.0/tests/fixtures/petstore_3.0.json +235 -0
  92. specli-0.1.0/tests/fixtures/petstore_3.1.json +240 -0
  93. specli-0.1.0/tests/test_auth/__init__.py +0 -0
  94. specli-0.1.0/tests/test_auth/test_api_key_gen.py +176 -0
  95. specli-0.1.0/tests/test_auth/test_auth_plugins.py +558 -0
  96. specli-0.1.0/tests/test_auth/test_browser_login.py +501 -0
  97. specli-0.1.0/tests/test_auth/test_credential_store.py +160 -0
  98. specli-0.1.0/tests/test_auth/test_device_code.py +520 -0
  99. specli-0.1.0/tests/test_auth/test_manual_token.py +135 -0
  100. specli-0.1.0/tests/test_auth/test_oauth2_plugins.py +811 -0
  101. specli-0.1.0/tests/test_cache/__init__.py +0 -0
  102. specli-0.1.0/tests/test_cache/test_cache.py +294 -0
  103. specli-0.1.0/tests/test_client/__init__.py +0 -0
  104. specli-0.1.0/tests/test_client/test_response.py +225 -0
  105. specli-0.1.0/tests/test_client/test_sync_client.py +898 -0
  106. specli-0.1.0/tests/test_config.py +648 -0
  107. specli-0.1.0/tests/test_enrichment/__init__.py +0 -0
  108. specli-0.1.0/tests/test_enrichment/test_enricher.py +254 -0
  109. specli-0.1.0/tests/test_enrichment/test_scanner.py +325 -0
  110. specli-0.1.0/tests/test_enrichment/test_strings.py +325 -0
  111. specli-0.1.0/tests/test_generator/__init__.py +0 -0
  112. specli-0.1.0/tests/test_generator/test_command_tree.py +786 -0
  113. specli-0.1.0/tests/test_generator/test_param_mapper.py +330 -0
  114. specli-0.1.0/tests/test_generator/test_path_rules.py +696 -0
  115. specli-0.1.0/tests/test_integration/__init__.py +0 -0
  116. specli-0.1.0/tests/test_integration/test_build.py +411 -0
  117. specli-0.1.0/tests/test_integration/test_completion.py +51 -0
  118. specli-0.1.0/tests/test_integration/test_dynamic_commands.py +674 -0
  119. specli-0.1.0/tests/test_integration/test_init_flow.py +588 -0
  120. specli-0.1.0/tests/test_integration/test_uspto_example.py +275 -0
  121. specli-0.1.0/tests/test_output.py +964 -0
  122. specli-0.1.0/tests/test_parser/__init__.py +0 -0
  123. specli-0.1.0/tests/test_parser/test_extractor.py +701 -0
  124. specli-0.1.0/tests/test_parser/test_loader.py +316 -0
  125. specli-0.1.0/tests/test_parser/test_resolver.py +350 -0
  126. specli-0.1.0/tests/test_plugins/__init__.py +0 -0
  127. specli-0.1.0/tests/test_plugins/test_plugins.py +706 -0
  128. specli-0.1.0/tests/test_skill/__init__.py +0 -0
  129. specli-0.1.0/tests/test_skill/test_skill_generator.py +766 -0
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,80 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ *.manifest
29
+ *.spec
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ .coverage
40
+ .coverage.*
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ *.py,cover
46
+ .hypothesis/
47
+ .pytest_cache/
48
+
49
+ # Translations
50
+ *.mo
51
+ *.pot
52
+
53
+ # Environments
54
+ .env
55
+ .venv
56
+ env/
57
+ venv/
58
+ ENV/
59
+
60
+ # IDE
61
+ .vscode/
62
+ .idea/
63
+ *.swp
64
+ *.swo
65
+ *~
66
+
67
+ # mypy
68
+ .mypy_cache/
69
+ dmypy.json
70
+ dmypy.txt
71
+
72
+ # ruff
73
+ .ruff_cache/
74
+
75
+ # OS
76
+ .DS_Store
77
+ Thumbs.db
78
+
79
+ # Project-specific
80
+ *.log
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-02-25
9
+
10
+ ### Added
11
+ - OpenAPI 3.0/3.1 spec parsing from URL, file, or stdin (JSON and YAML)
12
+ - `$ref` resolution with circular reference detection (internal refs only)
13
+ - Dynamic Typer CLI generation from spec operations
14
+ - Path rules engine with auto-strip prefix, keep, skip_segments, strip_prefix, and collapse
15
+ - HTTP method to CLI verb mapping (GET list/get, POST create, PUT update, DELETE delete)
16
+ - Parameter mapping: path params to positional arguments, query/header/cookie to `--options`
17
+ - `--body` option with `@filename` file reference support
18
+ - Built-in auth plugins: API key (header/query/cookie), bearer token, HTTP basic
19
+ - Credential source resolution: `env:VAR`, `file:/path`, `prompt`, `keyring:service:account`
20
+ - Plugin system with entry-point-based discovery and enable/disable configuration
21
+ - Pre-request, post-response, and error hooks via `HookRunner`
22
+ - Synchronous httpx client with auth injection, retry with exponential backoff, and dry-run mode
23
+ - Asynchronous httpx client mirroring the sync API
24
+ - Rich/JSON/plain output modes with automatic TTY detection
25
+ - stdout/stderr discipline following clig.dev conventions
26
+ - `NO_COLOR` and `TERM=dumb` environment variable support
27
+ - Pager support (`$PAGER` with `less -FIRX` fallback)
28
+ - XDG-compliant configuration on Linux/BSD (`~/.config/specli/`)
29
+ - macOS/Windows fallback to `~/.specli/`
30
+ - Atomic config file writes (temp file + rename)
31
+ - Profile system with per-API configuration (spec URL, base URL, auth, path rules, request settings)
32
+ - Config precedence chain: CLI flags > env vars > project config > user config > defaults
33
+ - Project-local config via `./specli.json`
34
+ - Built-in commands: `init`, `auth` (login, add, list, test, remove), `config` (show, set, reset), `inspect` (paths, schemas, auth, info)
35
+ - Plugins: `build` (compile/generate), `completion` (install/show), `skill` (generate)
36
+ - Claude Code skill generation with Jinja2 templates (SKILL.md, api-reference.md, auth-setup.md)
37
+ - Structured exit codes (0-10) following clig.dev conventions
38
+ - Custom exception hierarchy with per-type exit codes
39
+ - Crash log writing to `$XDG_DATA_HOME/specli/logs/`
40
+ - Global `--version`, `--profile`, `--json`, `--plain`, `--no-color`, `--quiet`, `--verbose`, `--dry-run`, `--force`, `--no-input`, `--output` flags
41
+ - Comprehensive test suite with 725+ tests
@@ -0,0 +1,315 @@
1
+ # Contributing to specli
2
+
3
+ Thank you for your interest in contributing. This guide covers the development workflow, project structure, and conventions used in the codebase.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ # Clone the repository
9
+ git clone https://github.com/aft/specli.git
10
+ cd specli
11
+
12
+ # Create a virtual environment
13
+ python -m venv .venv
14
+ source .venv/bin/activate # Linux/macOS
15
+ # .venv\Scripts\activate # Windows
16
+
17
+ # Install in editable mode with dev dependencies
18
+ pip install -e ".[dev]"
19
+ ```
20
+
21
+ ### Optional extras
22
+
23
+ ```bash
24
+ # Keyring support (for system credential storage)
25
+ pip install -e ".[keyring]"
26
+
27
+ # JWT support
28
+ pip install -e ".[jwt]"
29
+ ```
30
+
31
+ ## Running Tests
32
+
33
+ The test suite uses pytest and contains 725+ tests across unit, integration, and edge-case scenarios.
34
+
35
+ ```bash
36
+ # Run all tests
37
+ pytest
38
+
39
+ # Run with verbose output
40
+ pytest -v
41
+
42
+ # Run with coverage report
43
+ pytest --cov=specli --cov-report=term-missing
44
+
45
+ # Run a specific test file
46
+ pytest tests/test_config.py -v
47
+
48
+ # Run a specific test function
49
+ pytest tests/test_generator/test_path_rules.py::test_auto_strip_prefix -v
50
+
51
+ # Run only integration tests
52
+ pytest tests/test_integration/ -v
53
+ ```
54
+
55
+ ## Project Structure
56
+
57
+ ```
58
+ specli/
59
+ src/specli/ # Source code (src layout)
60
+ __init__.py # Package init, __version__
61
+ __main__.py # python -m specli support
62
+ app.py # Typer app factory and entry point
63
+ config.py # XDG config, profiles, atomic writes
64
+ models.py # All Pydantic models (single source of truth)
65
+ output.py # Output system (Rich/JSON/plain, stdout/stderr)
66
+ exit_codes.py # Exit code constants (clig.dev conventions)
67
+ exceptions.py # Exception hierarchy
68
+ parser/ # OpenAPI spec loading and extraction
69
+ loader.py # URL, file, stdin loading (JSON + YAML)
70
+ resolver.py # $ref resolution with cycle detection
71
+ extractor.py # Extract operations, params, security schemes
72
+ generator/ # CLI command generation
73
+ command_tree.py # Build Typer command tree from parsed spec
74
+ param_mapper.py # Map OpenAPI params to Typer arguments/options
75
+ path_rules.py # Path transformation rules engine
76
+ client/ # HTTP clients
77
+ sync_client.py # Synchronous httpx client
78
+ async_client.py # Async httpx client
79
+ response.py # Response formatting bridge
80
+ auth/ # Authentication system
81
+ base.py # AuthPlugin ABC and AuthResult
82
+ manager.py # Auth plugin registry
83
+ plugins/ # Built-in auth plugins
84
+ api_key.py # API key (header, query, cookie)
85
+ bearer.py # Bearer token
86
+ basic.py # HTTP Basic
87
+ plugins/ # Plugin system
88
+ base.py # Plugin ABC with hook methods
89
+ hooks.py # HookRunner and HookContext
90
+ manager.py # Entry-point discovery, enable/disable
91
+ skill/ # Claude Code skill generation
92
+ generator.py # Skill file generator
93
+ templates/ # Jinja2 templates (skill.md.j2, etc.)
94
+ commands/ # Built-in CLI commands
95
+ init.py # specli init
96
+ auth.py # specli auth (login, add, list, test, remove)
97
+ config.py # specli config (show, set, reset)
98
+ inspect.py # specli inspect (paths, schemas, auth, info)
99
+ generate_skill.py # specli generate-skill
100
+ tests/ # Test suite
101
+ conftest.py # Shared fixtures
102
+ test_config.py # Config unit tests
103
+ test_output.py # Output system tests
104
+ test_parser/ # Parser tests (loader, resolver, extractor)
105
+ test_generator/ # Generator tests (command_tree, param_mapper, path_rules)
106
+ test_client/ # Client tests (sync_client, response)
107
+ test_auth/ # Auth plugin tests
108
+ test_plugins/ # Plugin system tests
109
+ test_skill/ # Skill generator tests
110
+ test_integration/ # Integration tests (init flow, dynamic commands)
111
+ ```
112
+
113
+ ## Key Design Decisions
114
+
115
+ ### Single models file
116
+
117
+ All Pydantic models live in `src/specli/models.py`. Every other module imports from there. This prevents circular imports and makes the data contract easy to find.
118
+
119
+ ### Output discipline
120
+
121
+ The output system follows [clig.dev](https://clig.dev) conventions:
122
+
123
+ - **stdout** is for primary data only (API responses, JSON, tables)
124
+ - **stderr** is for all diagnostics (progress, status, warnings, errors)
125
+ - The `OutputManager` class handles format selection, color detection, and pager support
126
+ - Module-level convenience functions (`info()`, `error()`, `debug()`, etc.) delegate to the global `OutputManager` instance
127
+
128
+ ### Exit codes
129
+
130
+ Exit codes are defined in `exit_codes.py` and follow a structured scheme. Each exception class in `exceptions.py` carries its own exit code.
131
+
132
+ | Code | Meaning |
133
+ |------|---------|
134
+ | 0 | Success |
135
+ | 1 | Generic failure |
136
+ | 2 | Invalid usage / bad arguments |
137
+ | 3 | Auth failure |
138
+ | 4 | Not found |
139
+ | 5 | Server error |
140
+ | 6 | Connection error |
141
+ | 7 | Spec parse error |
142
+ | 10 | Plugin error |
143
+
144
+ ### Atomic config writes
145
+
146
+ Config and profile files are written atomically using `tempfile + os.replace`. This prevents corruption if the process is interrupted mid-write.
147
+
148
+ ## How to Create a Plugin
149
+
150
+ Plugins are discovered via Python entry points. Here is the complete process:
151
+
152
+ ### 1. Create a package with a Plugin subclass
153
+
154
+ ```python
155
+ # mypackage/plugin.py
156
+ from specli.plugins.base import Plugin
157
+ from specli.models import GlobalConfig
158
+ from typing import Any
159
+
160
+
161
+ class MyPlugin(Plugin):
162
+ @property
163
+ def name(self) -> str:
164
+ return "my-plugin"
165
+
166
+ @property
167
+ def version(self) -> str:
168
+ return "1.0.0"
169
+
170
+ @property
171
+ def description(self) -> str:
172
+ return "Adds custom logging to every request"
173
+
174
+ def on_init(self, config: GlobalConfig) -> None:
175
+ # Called once when the plugin is loaded
176
+ print(f"MyPlugin initialized with config: {config.default_profile}")
177
+
178
+ def on_pre_request(
179
+ self, method: str, url: str, headers: dict[str, str], params: dict[str, Any]
180
+ ) -> dict[str, Any]:
181
+ print(f"Request: {method} {url}")
182
+ # Must return headers and params (possibly modified)
183
+ return {"headers": headers, "params": params}
184
+
185
+ def on_post_response(
186
+ self, status_code: int, headers: dict[str, str], body: Any
187
+ ) -> Any:
188
+ print(f"Response: {status_code}")
189
+ return body # Return (possibly modified) body
190
+
191
+ def on_error(self, error: Exception) -> None:
192
+ print(f"Error: {error}")
193
+
194
+ def cleanup(self) -> None:
195
+ # Called on shutdown
196
+ pass
197
+ ```
198
+
199
+ ### 2. Register the entry point
200
+
201
+ In your package's `pyproject.toml`:
202
+
203
+ ```toml
204
+ [project.entry-points."specli.plugins"]
205
+ my-plugin = "mypackage.plugin:MyPlugin"
206
+ ```
207
+
208
+ ### 3. Install and use
209
+
210
+ ```bash
211
+ pip install mypackage
212
+ specli api pets list # Plugin hooks fire automatically
213
+ ```
214
+
215
+ ## How to Create an Auth Plugin
216
+
217
+ Auth plugins handle credential resolution for a specific authentication type.
218
+
219
+ ```python
220
+ # mypackage/oauth2_plugin.py
221
+ from specli.auth.base import AuthPlugin, AuthResult
222
+ from specli.config import resolve_credential
223
+ from specli.models import AuthConfig
224
+
225
+
226
+ class OAuth2Plugin(AuthPlugin):
227
+ @property
228
+ def auth_type(self) -> str:
229
+ return "oauth2_client_credentials"
230
+
231
+ def authenticate(self, auth_config: AuthConfig) -> AuthResult:
232
+ client_id = resolve_credential(auth_config.client_id_source or "prompt")
233
+ client_secret = resolve_credential(auth_config.client_secret_source or "prompt")
234
+ # Exchange credentials for a token (implementation omitted)
235
+ token = self._get_token(client_id, client_secret, auth_config.token_url)
236
+ return AuthResult(headers={"Authorization": f"Bearer {token}"})
237
+
238
+ def validate_config(self, auth_config: AuthConfig) -> list[str]:
239
+ errors = []
240
+ if not auth_config.token_url:
241
+ errors.append("OAuth2 requires 'token_url'")
242
+ return errors
243
+
244
+ def _get_token(self, client_id: str, client_secret: str, token_url: str | None) -> str:
245
+ # Token exchange logic here
246
+ ...
247
+ ```
248
+
249
+ Register the auth plugin in your application code or via a regular plugin's `on_init` hook:
250
+
251
+ ```python
252
+ from specli.auth.manager import AuthManager
253
+
254
+ manager = AuthManager()
255
+ manager.register(OAuth2Plugin())
256
+ ```
257
+
258
+ ## Code Style
259
+
260
+ ### Tools
261
+
262
+ - **Formatter**: [ruff](https://github.com/astral-sh/ruff) (line length 100)
263
+ - **Linter**: ruff
264
+ - **Type checker**: mypy (strict mode, Python 3.10 target)
265
+
266
+ ### Conventions
267
+
268
+ - All public functions and methods must have type annotations
269
+ - All modules use `from __future__ import annotations` for PEP 604 union syntax
270
+ - Docstrings use Google style (Args/Returns/Raises sections)
271
+ - Private functions are prefixed with `_`
272
+ - Constants are `UPPER_SNAKE_CASE`
273
+ - No version numbers in file names
274
+
275
+ ### Running quality checks
276
+
277
+ ```bash
278
+ # Lint
279
+ ruff check src/ tests/
280
+
281
+ # Format check
282
+ ruff format --check src/ tests/
283
+
284
+ # Type check
285
+ mypy src/specli/
286
+ ```
287
+
288
+ ## Pull Request Process
289
+
290
+ 1. **Create a branch** from `main` with a descriptive name (e.g., `feat/oauth2-auth`, `fix/path-rules-edge-case`)
291
+ 2. **Write tests** for any new functionality or bug fixes
292
+ 3. **Run the full test suite** and ensure all 725+ tests pass
293
+ 4. **Run linting and type checks** (`ruff check`, `mypy`)
294
+ 5. **Update documentation** if your change affects user-facing behavior
295
+ 6. **Write a clear PR description** explaining what changed and why
296
+ 7. **Keep commits focused** -- one logical change per commit
297
+
298
+ ### Commit message style
299
+
300
+ ```
301
+ feat: add OAuth2 client credentials auth plugin
302
+ fix: handle empty path segments in path rules
303
+ docs: add plugin development guide
304
+ test: add edge cases for $ref circular resolution
305
+ ```
306
+
307
+ ## Reporting Issues
308
+
309
+ When filing a bug report, please include:
310
+
311
+ - Python version (`python --version`)
312
+ - specli version (`specli --version`)
313
+ - The OpenAPI spec (or a minimal reproduction) that triggers the issue
314
+ - Full command invocation and output
315
+ - The crash log path if one was generated (printed in the error message)
specli-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cem Baspinar
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.