korb 0.2.2__tar.gz → 0.2.3__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.
- {korb-0.2.2 → korb-0.2.3}/.coverage +0 -0
- {korb-0.2.2 → korb-0.2.3}/CHANGELOG.md +16 -0
- {korb-0.2.2 → korb-0.2.3}/PKG-INFO +18 -3
- {korb-0.2.2 → korb-0.2.3}/README.md +17 -2
- {korb-0.2.2 → korb-0.2.3}/korb/__init__.py +1 -1
- {korb-0.2.2 → korb-0.2.3}/korb/__main__.py +36 -0
- {korb-0.2.2 → korb-0.2.3/korb}/skills/SKILL_LEAGUE_TOP_N_ANALYSIS.md +1 -2
- {korb-0.2.2 → korb-0.2.3/korb}/skills/SKILL_TEAM_ANALYSIS.md +4 -5
- korb-0.2.3/korb/skills/__init__.py +28 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_cli.py +38 -1
- {korb-0.2.2 → korb-0.2.3}/.github/workflows/release.yml +0 -0
- {korb-0.2.2 → korb-0.2.3}/.github/workflows/test.yml +0 -0
- {korb-0.2.2 → korb-0.2.3}/.gitignore +0 -0
- {korb-0.2.2 → korb-0.2.3}/LICENSE +0 -0
- {korb-0.2.2 → korb-0.2.3}/files/.gitkeep +0 -0
- {korb-0.2.2 → korb-0.2.3}/korb/core.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/korb/predict.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/korb/py.typed +0 -0
- {korb-0.2.2 → korb-0.2.3}/korb/schedule.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/korb/standings.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/korb/team.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/pyproject.toml +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/__init__.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/conftest.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/fixtures/ergebnisse_minimal.html +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/fixtures/spielplan_finalized.html +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/fixtures/spielplan_minimal.html +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_core.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_download.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_predict.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_schedule.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_standings.py +0 -0
- {korb-0.2.2 → korb-0.2.3}/tests/test_team.py +0 -0
|
Binary file
|
|
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
6
6
|
|
|
7
|
+
## [0.2.3] — 2026-04-10
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `skill` subcommand — prints built-in AI skill prompts to stdout.
|
|
12
|
+
- `skill --list` / `-l` flag — lists available skill names and filenames.
|
|
13
|
+
- `korb/skills/` package with `SKILL_MAP` and `get_skill_text()` using `importlib.resources`.
|
|
14
|
+
- Two bundled skills: `analysis` (team deep-dive) and `prediction` (league top-N forecast).
|
|
15
|
+
- Skill markdown files now shipped inside the `korb` package (included in pip distributions).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Migrated `skills/` from project root into `korb/skills/` for proper wheel inclusion.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
7
23
|
## [0.2.2] — 2026-04-09
|
|
8
24
|
|
|
9
25
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: korb
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A CLI toolkit for DBB basketball league analysis
|
|
5
5
|
Project-URL: Homepage, https://github.com/malvavisc0/korb
|
|
6
6
|
Project-URL: Repository, https://github.com/malvavisc0/korb
|
|
@@ -47,6 +47,7 @@ A zero-dependency Python CLI that parses HTML from the **DBB** (Deutscher Basket
|
|
|
47
47
|
- 🔮 **Predictions** — efficiency-model-based final standings forecast
|
|
48
48
|
- 🥇 **Top N** — quick leaderboard with ASCII bar chart
|
|
49
49
|
- 📥 **Download** — fetch fresh HTML data directly from basketball-bund.net
|
|
50
|
+
- 🤖 **Skills** — built-in AI skill prompts for team analysis & league prediction
|
|
50
51
|
- 🔧 **JSON output** — pipe-friendly `--json` flag for all commands
|
|
51
52
|
|
|
52
53
|
---
|
|
@@ -161,6 +162,19 @@ Uses a multiplicative efficiency model with recency weighting, recent form blend
|
|
|
161
162
|
uv run korb --ligaid 12345 top -n 5
|
|
162
163
|
```
|
|
163
164
|
|
|
165
|
+
### `skill` — Print AI skill prompts
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# List available skills
|
|
169
|
+
uv run korb skill --list
|
|
170
|
+
|
|
171
|
+
# Print a specific skill prompt
|
|
172
|
+
uv run korb skill analysis
|
|
173
|
+
uv run korb skill prediction
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Ships two built-in skills: `analysis` (team deep-dive) and `prediction` (league top-N forecast).
|
|
177
|
+
|
|
164
178
|
### `--download` — Fetch fresh data before any command
|
|
165
179
|
|
|
166
180
|
```bash
|
|
@@ -192,18 +206,19 @@ $ uv run korb --help
|
|
|
192
206
|
|
|
193
207
|
usage: korb [-h] [--version] [--results RESULTS] [--schedule SCHEDULE]
|
|
194
208
|
[--json] [--ligaid LIGAID] [--download]
|
|
195
|
-
{standings,team,schedule,predict,top,download} ...
|
|
209
|
+
{standings,team,schedule,predict,top,download,skill} ...
|
|
196
210
|
|
|
197
211
|
Basketball league analysis tools
|
|
198
212
|
|
|
199
213
|
positional arguments:
|
|
200
|
-
{standings,team,schedule,predict,top,download}
|
|
214
|
+
{standings,team,schedule,predict,top,download,skill}
|
|
201
215
|
standings Display league standings
|
|
202
216
|
team Display results for a team
|
|
203
217
|
schedule Display game schedule
|
|
204
218
|
predict Predict final standings
|
|
205
219
|
top Show top teams from standings
|
|
206
220
|
download Download results & schedule HTML
|
|
221
|
+
skill Print a skill prompt or list available skills
|
|
207
222
|
|
|
208
223
|
options:
|
|
209
224
|
-h, --help show this help message and exit
|
|
@@ -22,6 +22,7 @@ A zero-dependency Python CLI that parses HTML from the **DBB** (Deutscher Basket
|
|
|
22
22
|
- 🔮 **Predictions** — efficiency-model-based final standings forecast
|
|
23
23
|
- 🥇 **Top N** — quick leaderboard with ASCII bar chart
|
|
24
24
|
- 📥 **Download** — fetch fresh HTML data directly from basketball-bund.net
|
|
25
|
+
- 🤖 **Skills** — built-in AI skill prompts for team analysis & league prediction
|
|
25
26
|
- 🔧 **JSON output** — pipe-friendly `--json` flag for all commands
|
|
26
27
|
|
|
27
28
|
---
|
|
@@ -136,6 +137,19 @@ Uses a multiplicative efficiency model with recency weighting, recent form blend
|
|
|
136
137
|
uv run korb --ligaid 12345 top -n 5
|
|
137
138
|
```
|
|
138
139
|
|
|
140
|
+
### `skill` — Print AI skill prompts
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# List available skills
|
|
144
|
+
uv run korb skill --list
|
|
145
|
+
|
|
146
|
+
# Print a specific skill prompt
|
|
147
|
+
uv run korb skill analysis
|
|
148
|
+
uv run korb skill prediction
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Ships two built-in skills: `analysis` (team deep-dive) and `prediction` (league top-N forecast).
|
|
152
|
+
|
|
139
153
|
### `--download` — Fetch fresh data before any command
|
|
140
154
|
|
|
141
155
|
```bash
|
|
@@ -167,18 +181,19 @@ $ uv run korb --help
|
|
|
167
181
|
|
|
168
182
|
usage: korb [-h] [--version] [--results RESULTS] [--schedule SCHEDULE]
|
|
169
183
|
[--json] [--ligaid LIGAID] [--download]
|
|
170
|
-
{standings,team,schedule,predict,top,download} ...
|
|
184
|
+
{standings,team,schedule,predict,top,download,skill} ...
|
|
171
185
|
|
|
172
186
|
Basketball league analysis tools
|
|
173
187
|
|
|
174
188
|
positional arguments:
|
|
175
|
-
{standings,team,schedule,predict,top,download}
|
|
189
|
+
{standings,team,schedule,predict,top,download,skill}
|
|
176
190
|
standings Display league standings
|
|
177
191
|
team Display results for a team
|
|
178
192
|
schedule Display game schedule
|
|
179
193
|
predict Predict final standings
|
|
180
194
|
top Show top teams from standings
|
|
181
195
|
download Download results & schedule HTML
|
|
196
|
+
skill Print a skill prompt or list available skills
|
|
182
197
|
|
|
183
198
|
options:
|
|
184
199
|
-h, --help show this help message and exit
|
|
@@ -26,6 +26,7 @@ from .schedule import (
|
|
|
26
26
|
print_schedule,
|
|
27
27
|
)
|
|
28
28
|
from .standings import calculate_standings, print_table
|
|
29
|
+
from .skills import SKILL_MAP, get_skill_text
|
|
29
30
|
from .team import get_team_results, print_bars, print_metrics, print_results
|
|
30
31
|
|
|
31
32
|
|
|
@@ -352,6 +353,22 @@ def cmd_download(args: argparse.Namespace) -> None:
|
|
|
352
353
|
_download(args.ligaid)
|
|
353
354
|
|
|
354
355
|
|
|
356
|
+
def cmd_skill(args: argparse.Namespace) -> None:
|
|
357
|
+
"""Handle 'skill' subcommand."""
|
|
358
|
+
if args.list_skills:
|
|
359
|
+
for name, filename in SKILL_MAP.items():
|
|
360
|
+
print(f" {name:12s} {filename}")
|
|
361
|
+
return
|
|
362
|
+
if args.name is None:
|
|
363
|
+
print("Error: pass a skill name or --list", file=sys.stderr)
|
|
364
|
+
sys.exit(1)
|
|
365
|
+
try:
|
|
366
|
+
print(get_skill_text(args.name))
|
|
367
|
+
except ValueError as exc:
|
|
368
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
369
|
+
sys.exit(1)
|
|
370
|
+
|
|
371
|
+
|
|
355
372
|
def main() -> None:
|
|
356
373
|
"""Entry point for CLI."""
|
|
357
374
|
parser = argparse.ArgumentParser(
|
|
@@ -478,6 +495,25 @@ def main() -> None:
|
|
|
478
495
|
)
|
|
479
496
|
p_dl.set_defaults(func=cmd_download)
|
|
480
497
|
|
|
498
|
+
p_sk = subs.add_parser(
|
|
499
|
+
"skill",
|
|
500
|
+
help="Print a skill prompt or list available skills",
|
|
501
|
+
)
|
|
502
|
+
p_sk.add_argument(
|
|
503
|
+
"name",
|
|
504
|
+
nargs="?",
|
|
505
|
+
default=None,
|
|
506
|
+
help="Skill name: analysis or prediction",
|
|
507
|
+
)
|
|
508
|
+
p_sk.add_argument(
|
|
509
|
+
"--list",
|
|
510
|
+
"-l",
|
|
511
|
+
action="store_true",
|
|
512
|
+
dest="list_skills",
|
|
513
|
+
help="List available skill names and descriptions",
|
|
514
|
+
)
|
|
515
|
+
p_sk.set_defaults(func=cmd_skill)
|
|
516
|
+
|
|
481
517
|
args = parser.parse_args()
|
|
482
518
|
|
|
483
519
|
# Pre-command download hook
|
|
@@ -66,11 +66,11 @@ uv run korb --json --ligaid <LIGA_ID> team "<TEAM_NAME>"
|
|
|
66
66
|
|
|
67
67
|
Important: in JSON mode, the CLI returns **all matching games** and does **not** apply `--last-k` slicing or `--metrics` printing logic.
|
|
68
68
|
|
|
69
|
-
So to emulate
|
|
69
|
+
So to emulate "last 5 games":
|
|
70
70
|
|
|
71
71
|
1. Take `team_json["results"]`.
|
|
72
72
|
2. Treat it as **newest-first**.
|
|
73
|
-
3. Use the first 5 items as the
|
|
73
|
+
3. Use the first 5 items as the "last 5". If there are fewer than 5 games, use all available.
|
|
74
74
|
|
|
75
75
|
Compute:
|
|
76
76
|
|
|
@@ -92,7 +92,7 @@ uv run korb --json --ligaid <LIGA_ID> schedule --pending
|
|
|
92
92
|
uv run korb --json --ligaid <LIGA_ID> predict
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
From `predict_json["standings"]` extract the team
|
|
95
|
+
From `predict_json["standings"]` extract the team's **predicted rank** as `index_in_list + 1`.
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
@@ -117,5 +117,4 @@ Write the paragraph in the selected `LANGUAGE`.
|
|
|
117
117
|
|
|
118
118
|
## Output
|
|
119
119
|
|
|
120
|
-
Return the paragraph directly. Do **not** save to a file.
|
|
121
|
-
|
|
120
|
+
Return the paragraph directly. Do **not** save to a file.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Skill definitions shipped with the korb package."""
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
|
|
5
|
+
SKILL_MAP: dict[str, str] = {
|
|
6
|
+
"analysis": "SKILL_TEAM_ANALYSIS.md",
|
|
7
|
+
"prediction": "SKILL_LEAGUE_TOP_N_ANALYSIS.md",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_skill_text(name: str) -> str:
|
|
12
|
+
"""Return the markdown text of a named skill.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name: Skill key from SKILL_MAP (e.g. "analysis", "prediction").
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
The full markdown content of the skill file.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError: If *name* is not in SKILL_MAP.
|
|
22
|
+
"""
|
|
23
|
+
if name not in SKILL_MAP:
|
|
24
|
+
raise ValueError(
|
|
25
|
+
f"unknown skill: {name!r}. Choose from: {', '.join(SKILL_MAP)}"
|
|
26
|
+
)
|
|
27
|
+
filename = SKILL_MAP[name]
|
|
28
|
+
return files(__package__).joinpath(filename).read_text(encoding="utf-8")
|
|
@@ -4,7 +4,13 @@ import argparse
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from korb.__main__ import
|
|
7
|
+
from korb.__main__ import (
|
|
8
|
+
cmd_predict,
|
|
9
|
+
cmd_schedule,
|
|
10
|
+
cmd_skill,
|
|
11
|
+
cmd_standings,
|
|
12
|
+
cmd_team,
|
|
13
|
+
)
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
def _make_args(**kwargs) -> argparse.Namespace:
|
|
@@ -167,3 +173,34 @@ class TestCmdPredict:
|
|
|
167
173
|
cmd_predict(args)
|
|
168
174
|
captured = capsys.readouterr()
|
|
169
175
|
assert "Predicted" in captured.out
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class TestCmdSkill:
|
|
179
|
+
def test_analysis_skill(self, capsys):
|
|
180
|
+
args = _make_args(name="analysis", list_skills=False)
|
|
181
|
+
cmd_skill(args)
|
|
182
|
+
out = capsys.readouterr().out
|
|
183
|
+
assert "Team Analysis" in out
|
|
184
|
+
|
|
185
|
+
def test_prediction_skill(self, capsys):
|
|
186
|
+
args = _make_args(name="prediction", list_skills=False)
|
|
187
|
+
cmd_skill(args)
|
|
188
|
+
out = capsys.readouterr().out
|
|
189
|
+
assert "League Prediction" in out
|
|
190
|
+
|
|
191
|
+
def test_list_skills(self, capsys):
|
|
192
|
+
args = _make_args(name=None, list_skills=True)
|
|
193
|
+
cmd_skill(args)
|
|
194
|
+
out = capsys.readouterr().out
|
|
195
|
+
assert "analysis" in out
|
|
196
|
+
assert "prediction" in out
|
|
197
|
+
|
|
198
|
+
def test_no_args(self):
|
|
199
|
+
args = _make_args(name=None, list_skills=False)
|
|
200
|
+
with pytest.raises(SystemExit):
|
|
201
|
+
cmd_skill(args)
|
|
202
|
+
|
|
203
|
+
def test_invalid_skill(self):
|
|
204
|
+
args = _make_args(name="nonexistent", list_skills=False)
|
|
205
|
+
with pytest.raises(SystemExit):
|
|
206
|
+
cmd_skill(args)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|