sweatstack-cli 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 (37) hide show
  1. sweatstack_cli-0.1.0/.claude/settings.local.json +21 -0
  2. sweatstack_cli-0.1.0/.gitignore +10 -0
  3. sweatstack_cli-0.1.0/.python-version +1 -0
  4. sweatstack_cli-0.1.0/DEVELOPMENT.md +403 -0
  5. sweatstack_cli-0.1.0/Makefile +50 -0
  6. sweatstack_cli-0.1.0/PKG-INFO +132 -0
  7. sweatstack_cli-0.1.0/PLAN.md +1130 -0
  8. sweatstack_cli-0.1.0/README.md +102 -0
  9. sweatstack_cli-0.1.0/pyproject.toml +94 -0
  10. sweatstack_cli-0.1.0/src/sweatstack_cli/__init__.py +3 -0
  11. sweatstack_cli-0.1.0/src/sweatstack_cli/api/__init__.py +5 -0
  12. sweatstack_cli-0.1.0/src/sweatstack_cli/api/client.py +192 -0
  13. sweatstack_cli-0.1.0/src/sweatstack_cli/auth/__init__.py +216 -0
  14. sweatstack_cli-0.1.0/src/sweatstack_cli/auth/callback_server.py +216 -0
  15. sweatstack_cli-0.1.0/src/sweatstack_cli/auth/jwt.py +50 -0
  16. sweatstack_cli-0.1.0/src/sweatstack_cli/auth/pkce.py +50 -0
  17. sweatstack_cli-0.1.0/src/sweatstack_cli/auth/tokens.py +178 -0
  18. sweatstack_cli-0.1.0/src/sweatstack_cli/commands/__init__.py +1 -0
  19. sweatstack_cli-0.1.0/src/sweatstack_cli/commands/auth.py +127 -0
  20. sweatstack_cli-0.1.0/src/sweatstack_cli/commands/pages.py +130 -0
  21. sweatstack_cli-0.1.0/src/sweatstack_cli/config.py +32 -0
  22. sweatstack_cli-0.1.0/src/sweatstack_cli/console.py +6 -0
  23. sweatstack_cli-0.1.0/src/sweatstack_cli/exceptions.py +30 -0
  24. sweatstack_cli-0.1.0/src/sweatstack_cli/main.py +72 -0
  25. sweatstack_cli-0.1.0/src/sweatstack_cli/py.typed +0 -0
  26. sweatstack_cli-0.1.0/tests/__init__.py +1 -0
  27. sweatstack_cli-0.1.0/tests/conftest.py +69 -0
  28. sweatstack_cli-0.1.0/tests/test_api/__init__.py +1 -0
  29. sweatstack_cli-0.1.0/tests/test_api/test_client.py +195 -0
  30. sweatstack_cli-0.1.0/tests/test_auth/__init__.py +1 -0
  31. sweatstack_cli-0.1.0/tests/test_auth/test_callback_server.py +166 -0
  32. sweatstack_cli-0.1.0/tests/test_auth/test_jwt.py +92 -0
  33. sweatstack_cli-0.1.0/tests/test_auth/test_pkce.py +66 -0
  34. sweatstack_cli-0.1.0/tests/test_auth/test_tokens.py +173 -0
  35. sweatstack_cli-0.1.0/tests/test_commands/__init__.py +1 -0
  36. sweatstack_cli-0.1.0/tests/test_commands/test_cli.py +65 -0
  37. sweatstack_cli-0.1.0/uv.lock +453 -0
@@ -0,0 +1,21 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(uv pip install:*)",
5
+ "Bash(uv venv:*)",
6
+ "Bash(source .venv/bin/activate:*)",
7
+ "Bash(uv run pytest:*)",
8
+ "Bash(uv run ruff check:*)",
9
+ "Bash(uv run ruff format:*)",
10
+ "Bash(uv run mypy:*)",
11
+ "Bash(uv run sweatstack:*)",
12
+ "Bash(uv sync:*)",
13
+ "Bash(pytest:*)",
14
+ "Bash(ruff check:*)",
15
+ "WebFetch(domain:localhost)",
16
+ "Bash(curl:*)",
17
+ "Bash(python3:*)",
18
+ "Bash(make help:*)"
19
+ ]
20
+ }
21
+ }
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,403 @@
1
+ # SweatStack CLI — Development Guide
2
+
3
+ This document explains how to develop, maintain, and extend the SweatStack CLI.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Clone and install
9
+ git clone https://github.com/sweatstack/sweatstack-cli
10
+ cd sweatstack-cli
11
+
12
+ # Create virtual environment and install
13
+ uv venv
14
+ source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
15
+ uv pip install -e ".[dev]"
16
+
17
+ # Run the CLI
18
+ sweatstack --help
19
+
20
+ # Run tests
21
+ pytest
22
+ ```
23
+
24
+ ## Project Structure
25
+
26
+ ```
27
+ src/sweatstack_cli/
28
+ ├── __init__.py # Package version
29
+ ├── main.py # CLI entry point (Typer app)
30
+ ├── config.py # Environment-based configuration
31
+ ├── console.py # Rich console singleton
32
+ ├── exceptions.py # Exception hierarchy with exit codes
33
+ ├── auth/ # Authentication module
34
+ │ ├── __init__.py # Authenticator class (main API)
35
+ │ ├── pkce.py # PKCE challenge generation
36
+ │ ├── jwt.py # JWT payload decoding
37
+ │ ├── tokens.py # Token storage
38
+ │ └── callback_server.py # Local OAuth callback server
39
+ ├── api/ # API client module
40
+ │ ├── __init__.py # Public exports
41
+ │ └── client.py # Authenticated HTTP client
42
+ └── commands/ # CLI command modules
43
+ ├── __init__.py
44
+ ├── auth.py # login, logout, whoami, status
45
+ └── pages.py # pages deploy, list, delete
46
+ ```
47
+
48
+ ## Architecture
49
+
50
+ ### Design Principles
51
+
52
+ 1. **Minimal dependencies** — Only essential packages: `typer`, `httpx`, `platformdirs`
53
+ 2. **No magic** — Explicit configuration, clear error messages
54
+ 3. **Testable** — Dependency injection, protocol-based abstractions
55
+ 4. **Type safe** — Full type hints, strict mypy configuration
56
+
57
+ ### Authentication Flow
58
+
59
+ The CLI uses OAuth2 PKCE (Proof Key for Code Exchange) for authentication:
60
+
61
+ ```
62
+ User runs `sweatstack login`
63
+
64
+
65
+ Generate PKCE verifier + challenge
66
+
67
+
68
+ Start local HTTP server on port 8400-8499
69
+
70
+
71
+ Open browser to SweatStack OAuth page
72
+
73
+
74
+ User authenticates in browser
75
+
76
+
77
+ Browser redirects to localhost with auth code
78
+
79
+
80
+ Exchange code for tokens (access + refresh)
81
+
82
+
83
+ Store tokens in ~/.../SweatStack/tokens.json
84
+ ```
85
+
86
+ ### Token Storage
87
+
88
+ Tokens are stored in a platform-specific location using `platformdirs`:
89
+
90
+ | Platform | Path |
91
+ |----------|------|
92
+ | macOS | `~/Library/Application Support/SweatStack/SweatStack/tokens.json` |
93
+ | Linux | `~/.local/share/SweatStack/SweatStack/tokens.json` |
94
+ | Windows | `%APPDATA%\SweatStack\SweatStack\tokens.json` |
95
+
96
+ **Important:** This path is shared with the `sweatstack` Python library. Both projects must maintain compatibility with this format:
97
+
98
+ ```json
99
+ {
100
+ "access_token": "eyJ...",
101
+ "refresh_token": "eyJ..."
102
+ }
103
+ ```
104
+
105
+ ## Adding New Commands
106
+
107
+ ### 1. Create a Command Module
108
+
109
+ Create a new file in `src/sweatstack_cli/commands/`:
110
+
111
+ ```python
112
+ # src/sweatstack_cli/commands/activities.py
113
+ """Activity commands."""
114
+
115
+ from __future__ import annotations
116
+
117
+ import typer
118
+
119
+ from sweatstack_cli.api import APIClient
120
+ from sweatstack_cli.console import console
121
+ from sweatstack_cli.exceptions import AuthenticationError
122
+
123
+ app = typer.Typer(
124
+ name="activities",
125
+ help="Manage activities.",
126
+ no_args_is_help=True,
127
+ )
128
+
129
+
130
+ @app.command(name="list")
131
+ def list_activities(
132
+ limit: int = typer.Option(10, "--limit", "-n", help="Number of activities"),
133
+ ) -> None:
134
+ """List recent activities."""
135
+ try:
136
+ client = APIClient()
137
+ activities = client.get("/api/v1/activities", params={"limit": limit})
138
+
139
+ for activity in activities["items"]:
140
+ console.print(f" {activity['id']}: {activity['name']}")
141
+
142
+ except AuthenticationError as e:
143
+ console.print(f"[red]Error:[/red] {e}")
144
+ raise typer.Exit(2)
145
+ ```
146
+
147
+ ### 2. Register in main.py
148
+
149
+ ```python
150
+ # src/sweatstack_cli/main.py
151
+ from sweatstack_cli.commands import activities
152
+
153
+ app.add_typer(activities.app, name="activities")
154
+ ```
155
+
156
+ ### 3. Add Tests
157
+
158
+ ```python
159
+ # tests/test_commands/test_activities.py
160
+ from typer.testing import CliRunner
161
+ from sweatstack_cli.main import app
162
+
163
+ runner = CliRunner()
164
+
165
+ def test_activities_help():
166
+ result = runner.invoke(app, ["activities", "--help"])
167
+ assert result.exit_code == 0
168
+ assert "list" in result.stdout
169
+ ```
170
+
171
+ ## Extending the API Client
172
+
173
+ The `APIClient` class provides authenticated HTTP methods. To add new endpoints:
174
+
175
+ ```python
176
+ # src/sweatstack_cli/api/activities.py
177
+ from __future__ import annotations
178
+
179
+ from sweatstack_cli.api.client import APIClient
180
+
181
+
182
+ class ActivitiesAPI:
183
+ """Activities API wrapper."""
184
+
185
+ def __init__(self, client: APIClient | None = None) -> None:
186
+ self._client = client or APIClient()
187
+
188
+ def list(self, limit: int = 10) -> list[dict]:
189
+ """List activities."""
190
+ response = self._client.get("/api/v1/activities", params={"limit": limit})
191
+ return response["items"]
192
+
193
+ def get(self, activity_id: str) -> dict:
194
+ """Get a single activity."""
195
+ return self._client.get(f"/api/v1/activities/{activity_id}")
196
+ ```
197
+
198
+ ## Testing
199
+
200
+ ### Running Tests
201
+
202
+ ```bash
203
+ # All tests
204
+ pytest
205
+
206
+ # With coverage
207
+ pytest --cov=sweatstack_cli --cov-report=html
208
+
209
+ # Specific test file
210
+ pytest tests/test_auth/test_pkce.py
211
+
212
+ # Specific test
213
+ pytest tests/test_auth/test_pkce.py::TestGeneratePKCE::test_verifier_length_in_range
214
+
215
+ # Verbose output
216
+ pytest -v
217
+ ```
218
+
219
+ ### Test Fixtures
220
+
221
+ Common fixtures are defined in `tests/conftest.py`:
222
+
223
+ - `temp_token_storage` — FileTokenStorage with temp directory
224
+ - `sample_tokens` — Valid TokenPair with future expiry
225
+ - `expired_tokens` — TokenPair with past expiry
226
+ - `clean_env` — Removes SweatStack env vars for isolation
227
+
228
+ ### Mocking HTTP Requests
229
+
230
+ Use `pytest-httpx` for HTTP mocking:
231
+
232
+ ```python
233
+ from pytest_httpx import HTTPXMock
234
+
235
+ def test_api_call(httpx_mock: HTTPXMock):
236
+ httpx_mock.add_response(
237
+ url="https://app.sweatstack.no/api/v1/test",
238
+ json={"data": "value"},
239
+ )
240
+
241
+ client = APIClient(authenticator=mock_auth)
242
+ result = client.get("/api/v1/test")
243
+
244
+ assert result["data"] == "value"
245
+ ```
246
+
247
+ ### Mocking Authentication
248
+
249
+ ```python
250
+ from unittest.mock import Mock
251
+ from sweatstack_cli.auth import Authenticator
252
+ from sweatstack_cli.auth.tokens import TokenPair
253
+
254
+ def test_with_mock_auth(sample_tokens: TokenPair):
255
+ auth = Mock(spec=Authenticator)
256
+ auth.get_valid_tokens.return_value = sample_tokens
257
+
258
+ client = APIClient(authenticator=auth)
259
+ # ... test API calls
260
+ ```
261
+
262
+ ## Code Quality
263
+
264
+ ### Linting and Formatting
265
+
266
+ ```bash
267
+ # Check linting
268
+ ruff check .
269
+
270
+ # Auto-fix issues
271
+ ruff check --fix .
272
+
273
+ # Format code
274
+ ruff format .
275
+ ```
276
+
277
+ ### Type Checking
278
+
279
+ ```bash
280
+ mypy src/sweatstack_cli
281
+ ```
282
+
283
+ ### Pre-commit Checks
284
+
285
+ Consider adding these to CI or as pre-commit hooks:
286
+
287
+ ```bash
288
+ ruff check .
289
+ ruff format --check .
290
+ mypy src/sweatstack_cli
291
+ pytest
292
+ ```
293
+
294
+ ## Environment Variables
295
+
296
+ | Variable | Purpose | Default |
297
+ |----------|---------|---------|
298
+ | `SWEATSTACK_URL` | API base URL | `https://app.sweatstack.no` |
299
+ | `SWEATSTACK_API_KEY` | Access token (CI/CD) | — |
300
+ | `SWEATSTACK_REFRESH_TOKEN` | Refresh token (CI/CD) | — |
301
+
302
+ For CI/CD pipelines, set `SWEATSTACK_API_KEY` and `SWEATSTACK_REFRESH_TOKEN` to bypass interactive login.
303
+
304
+ ## Error Handling
305
+
306
+ ### Exception Hierarchy
307
+
308
+ ```
309
+ CLIError (exit code 1)
310
+ ├── AuthenticationError (exit code 2)
311
+ ├── APIError (exit code 3)
312
+ └── ValidationError (exit code 4)
313
+ ```
314
+
315
+ ### Adding New Exceptions
316
+
317
+ ```python
318
+ # src/sweatstack_cli/exceptions.py
319
+
320
+ class RateLimitError(CLIError):
321
+ """API rate limit exceeded."""
322
+ exit_code = 5
323
+ ```
324
+
325
+ ### Using Exceptions in Commands
326
+
327
+ ```python
328
+ from sweatstack_cli.exceptions import AuthenticationError
329
+
330
+ def my_command():
331
+ try:
332
+ client = APIClient()
333
+ # ...
334
+ except AuthenticationError as e:
335
+ console.print(f"[red]Error:[/red] {e}")
336
+ raise typer.Exit(2)
337
+ ```
338
+
339
+ ## Release Process
340
+
341
+ 1. Update version in `src/sweatstack_cli/__init__.py`
342
+ 2. Update `pyproject.toml` version if needed
343
+ 3. Run full test suite: `pytest`
344
+ 4. Build: `uv build` or `python -m build`
345
+ 5. Publish: `twine upload dist/*`
346
+
347
+ ## Debugging
348
+
349
+ ### Verbose Output
350
+
351
+ The CLI respects Rich's console settings. For debugging:
352
+
353
+ ```python
354
+ from rich.console import Console
355
+ console = Console(force_terminal=True) # Force colors in CI
356
+ ```
357
+
358
+ ### Token Inspection
359
+
360
+ ```bash
361
+ # Check token status
362
+ sweatstack status
363
+
364
+ # View raw token file (careful with secrets!)
365
+ cat ~/Library/Application\ Support/SweatStack/SweatStack/tokens.json | python -m json.tool
366
+ ```
367
+
368
+ ### Local API Testing
369
+
370
+ ```bash
371
+ # Override API URL for local testing
372
+ SWEATSTACK_URL=http://localhost:8000 sweatstack login
373
+ ```
374
+
375
+ ## Compatibility Notes
376
+
377
+ ### Token Storage Compatibility
378
+
379
+ The CLI shares token storage with the `sweatstack` Python library. Changes to the token format must be coordinated:
380
+
381
+ 1. Token file path: `platformdirs.user_data_dir("SweatStack", "SweatStack") / "tokens.json"`
382
+ 2. JSON format: `{"access_token": "...", "refresh_token": "..."}`
383
+ 3. File permissions: `0o600`
384
+
385
+ ### Python Version
386
+
387
+ Requires Python 3.13+. Uses modern features:
388
+ - `type` statement (PEP 695)
389
+ - `X | Y` union syntax
390
+ - `@dataclass(slots=True)`
391
+
392
+ ## Contributing
393
+
394
+ 1. Fork the repository
395
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
396
+ 3. Make changes with tests
397
+ 4. Run checks: `ruff check . && mypy src && pytest`
398
+ 5. Submit a pull request
399
+
400
+ ## Getting Help
401
+
402
+ - GitHub Issues: [github.com/sweatstack/sweatstack-cli/issues](https://github.com/sweatstack/sweatstack-cli/issues)
403
+ - Documentation: [docs.sweatstack.no](https://docs.sweatstack.no)
@@ -0,0 +1,50 @@
1
+ .PHONY: help install dev test lint format typecheck check clean build publish
2
+
3
+ help:
4
+ @echo "SweatStack CLI Development Commands"
5
+ @echo ""
6
+ @echo " make install Install the package"
7
+ @echo " make dev Install with development dependencies"
8
+ @echo " make test Run tests"
9
+ @echo " make lint Run linter"
10
+ @echo " make format Format code"
11
+ @echo " make typecheck Run type checker"
12
+ @echo " make check Run all checks (lint, format, typecheck, test)"
13
+ @echo " make clean Remove build artifacts"
14
+ @echo " make build Build distribution packages"
15
+ @echo " make publish Publish to PyPI"
16
+
17
+ install:
18
+ uv pip install -e .
19
+
20
+ dev:
21
+ uv pip install -e ".[dev]"
22
+
23
+ test:
24
+ uv run pytest
25
+
26
+ lint:
27
+ uv run ruff check .
28
+
29
+ format:
30
+ uv run ruff format .
31
+
32
+ typecheck:
33
+ uv run mypy src/sweatstack_cli
34
+
35
+ check: lint typecheck test
36
+ @echo "All checks passed!"
37
+
38
+ clean:
39
+ rm -rf dist/ build/ *.egg-info src/*.egg-info
40
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
41
+ find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
42
+ find . -type d -name .mypy_cache -exec rm -rf {} + 2>/dev/null || true
43
+ find . -type d -name .ruff_cache -exec rm -rf {} + 2>/dev/null || true
44
+
45
+ build:
46
+ rm -rf dist
47
+ uv build
48
+
49
+ publish: build
50
+ uvx uv-publish
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: sweatstack-cli
3
+ Version: 0.1.0
4
+ Summary: Command-line interface for SweatStack — the sports data platform for developers
5
+ Project-URL: Homepage, https://sweatstack.no
6
+ Project-URL: Documentation, https://docs.sweatstack.no
7
+ Project-URL: Repository, https://github.com/sweatstack/sweatstack-cli
8
+ Author-email: Aart Goossens <aart@goossens.me>
9
+ License: MIT
10
+ Keywords: api,cli,fitness,sports,sweatstack
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.13
20
+ Requires-Dist: httpx>=0.28.0
21
+ Requires-Dist: platformdirs>=4.0
22
+ Requires-Dist: typer>=0.15.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: coverage>=7.0; extra == 'dev'
25
+ Requires-Dist: mypy>=1.14; extra == 'dev'
26
+ Requires-Dist: pytest-httpx>=0.35; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.9; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # SweatStack CLI
32
+
33
+ Command-line interface for [SweatStack](https://sweatstack.no) — the sports data platform for developers.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install sweatstack-cli
39
+ ```
40
+
41
+ Requires Python 3.13+.
42
+
43
+ ## Quick Start
44
+
45
+ ```bash
46
+ # Authenticate with SweatStack
47
+ sweatstack login
48
+
49
+ # Check who you're logged in as
50
+ sweatstack whoami
51
+
52
+ # Deploy a static site
53
+ sweatstack pages deploy ./dist --prod
54
+
55
+ # View authentication status
56
+ sweatstack status
57
+
58
+ # Logout
59
+ sweatstack logout
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ ### Authentication
65
+
66
+ | Command | Description |
67
+ |---------|-------------|
68
+ | `sweatstack login` | Authenticate via browser |
69
+ | `sweatstack login --force` | Force re-authentication |
70
+ | `sweatstack logout` | Remove stored credentials |
71
+ | `sweatstack whoami` | Show current user |
72
+ | `sweatstack status` | Show token status and expiry |
73
+
74
+ ### Pages
75
+
76
+ | Command | Description |
77
+ |---------|-------------|
78
+ | `sweatstack pages deploy <dir>` | Deploy static site |
79
+ | `sweatstack pages deploy --prod` | Deploy to production |
80
+ | `sweatstack pages list` | List all sites |
81
+ | `sweatstack pages delete <name>` | Delete a site |
82
+
83
+ ## CI/CD Usage
84
+
85
+ For automated environments, use environment variables instead of interactive login:
86
+
87
+ ```bash
88
+ export SWEATSTACK_API_KEY="your-access-token"
89
+ export SWEATSTACK_REFRESH_TOKEN="your-refresh-token"
90
+
91
+ sweatstack pages deploy ./dist --prod
92
+ ```
93
+
94
+ ## Configuration
95
+
96
+ | Environment Variable | Description | Default |
97
+ |---------------------|-------------|---------|
98
+ | `SWEATSTACK_URL` | API base URL | `https://app.sweatstack.no` |
99
+ | `SWEATSTACK_API_KEY` | Access token | — |
100
+ | `SWEATSTACK_REFRESH_TOKEN` | Refresh token | — |
101
+
102
+ ## Token Storage
103
+
104
+ Credentials are stored securely in your OS user data directory:
105
+
106
+ - **macOS**: `~/Library/Application Support/SweatStack/SweatStack/tokens.json`
107
+ - **Linux**: `~/.local/share/SweatStack/SweatStack/tokens.json`
108
+ - **Windows**: `%APPDATA%\SweatStack\SweatStack\tokens.json`
109
+
110
+ This location is shared with the [sweatstack Python library](https://github.com/sweatstack/sweatstack-python), so authenticating with either tool works for both.
111
+
112
+ ## Development
113
+
114
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for development setup and contribution guidelines.
115
+
116
+ ```bash
117
+ # Install with dev dependencies
118
+ uv pip install -e ".[dev]"
119
+
120
+ # Run tests
121
+ pytest
122
+
123
+ # Lint and format
124
+ ruff check . && ruff format .
125
+
126
+ # Type check
127
+ mypy src/sweatstack_cli
128
+ ```
129
+
130
+ ## License
131
+
132
+ MIT