fplkit 2.1.0__tar.gz → 2.2.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.
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/TOOLS.md +12 -9
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/SKILL.md +89 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/references/output-template.md +24 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/references/rules.md +5 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/scripts/validate_draft_waivers.py +53 -11
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/release/SKILL.md +21 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/squad-builder/SKILL.md +1 -1
- fplkit-2.2.0/.github/workflows/ci.yml +64 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/AGENTS.md +4 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/CHANGELOG.md +40 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/CLAUDE.md +4 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/CONTRIBUTING.md +12 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/PKG-INFO +8 -1
- {fplkit-2.1.0 → fplkit-2.2.0}/README.md +7 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/docs/architecture.md +34 -14
- {fplkit-2.1.0 → fplkit-2.2.0}/docs/command-reference.md +77 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/docs/custom-analysis.md +50 -15
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/_version.py +2 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/action/waiver.py +30 -4
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/bench_order.py +8 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/captain.py +8 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/squad_analyzer.py +1 -1
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/starting_xi.py +26 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/stats.py +10 -3
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/transfer_eval.py +15 -4
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/common.py +2 -2
- fplkit-2.2.0/fpl_cli/api/contract.py +77 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/core_insights.py +81 -21
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/football_data.py +18 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/understat.py +35 -4
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/vaastav.py +45 -5
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/__init__.py +2 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/allocate.py +2 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/doctor.py +100 -18
- fplkit-2.2.0/fpl_cli/cli/doctor_providers.py +710 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/player.py +2 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/ratings.py +118 -9
- fplkit-2.2.0/fpl_cli/cli/returnees.py +798 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/sell_prices.py +17 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/stats.py +11 -7
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/config/defaults.yaml +18 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/types.py +16 -0
- fplkit-2.2.0/fpl_cli/prompts/returnees.py +104 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/scraper/fpl_prices.py +49 -9
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/player_prior.py +9 -5
- fplkit-2.2.0/fpl_cli/services/returnee_radar.py +1655 -0
- fplkit-2.2.0/fpl_cli/services/scoring/__init__.py +252 -0
- fplkit-2.2.0/fpl_cli/services/scoring/constants.py +493 -0
- fplkit-2.2.0/fpl_cli/services/scoring/data_prep.py +498 -0
- fplkit-2.2.0/fpl_cli/services/scoring/display.py +57 -0
- fplkit-2.2.0/fpl_cli/services/scoring/evaluation.py +333 -0
- fplkit-2.2.0/fpl_cli/services/scoring/ownership.py +241 -0
- fplkit-2.2.0/fpl_cli/services/scoring/shrinkage.py +213 -0
- fplkit-2.2.0/fpl_cli/services/scoring/signals.py +708 -0
- fplkit-2.2.0/fpl_cli/services/scoring/single_gw.py +550 -0
- fplkit-2.2.0/fpl_cli/services/scoring/value_quality.py +217 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/squad_allocator.py +12 -4
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/team_ratings.py +67 -12
- fplkit-2.2.0/fpl_cli/services/team_ratings_prior.py +651 -0
- fplkit-2.2.0/scripts/calibrate_promoted_prior.py +283 -0
- fplkit-2.2.0/scripts/calibrate_quality_ceilings.py +669 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/conftest.py +15 -2
- fplkit-2.2.0/tests/fixtures/validate_draft_waivers/recs_clean.md +28 -0
- fplkit-2.2.0/tests/fixtures/validate_draft_waivers/waivers.json +19 -0
- fplkit-2.2.0/tests/fixtures/waiver_ranked_golden.json +1053 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_action.py +267 -17
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_analysis.py +1 -1
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_analysis_squad.py +1 -1
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_core_insights.py +69 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_football_data.py +14 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_understat.py +63 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_vaastav.py +84 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_allocate.py +4 -4
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_doctor.py +48 -0
- fplkit-2.2.0/tests/test_cli_doctor_providers.py +359 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_player.py +4 -2
- fplkit-2.2.0/tests/test_cli_returnees.py +868 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_sell_prices.py +49 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_common_scoring.py +4 -4
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_data_prep_harmonisation.py +17 -9
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_player_prior.py +7 -7
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_preseason.py +185 -2
- fplkit-2.2.0/tests/test_returnee_radar.py +1467 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_rolling_pts_per_m.py +1 -1
- fplkit-2.1.0/tests/test_player_scoring.py → fplkit-2.2.0/tests/test_scoring.py +630 -93
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_scraper.py +157 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_squad_allocator.py +63 -2
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_starting_xi_agent.py +53 -1
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_stats.py +60 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_team_ratings.py +435 -0
- fplkit-2.2.0/tests/test_team_ratings_prior.py +1048 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_transfer_eval_agent.py +1 -1
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_validate_draft_waivers.py +184 -0
- fplkit-2.1.0/.github/workflows/ci.yml +0 -40
- fplkit-2.1.0/fpl_cli/services/player_scoring.py +0 -2787
- fplkit-2.1.0/fpl_cli/services/team_ratings_prior.py +0 -351
- fplkit-2.1.0/tests/fixtures/validate_draft_waivers/recs_clean.md +0 -20
- fplkit-2.1.0/tests/fixtures/validate_draft_waivers/waivers.json +0 -12
- fplkit-2.1.0/tests/test_team_ratings_prior.py +0 -435
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/README.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/create-pr/SKILL.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/scripts/_bootstrap.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/scripts/bench_order.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/scripts/extract_classic_squad.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/scripts/starting_xi.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/gw-prep/scripts/transfer_eval.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/preview-ingest/SKILL.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/release-notes/SKILL.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/squad-builder/references/output-template.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/squad-builder/references/rules.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.agents/skills/update-gw-prep/SKILL.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.claude/output-styles/fpl-mate.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.claude/settings.json +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.env.example +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.github/workflows/pr-title.yml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.github/workflows/release.yml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/.gitignore +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/LICENSE +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/cliff.toml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/config/team_ratings_overrides.yaml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/docs/fpl-rules.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/docs/images/fpl-player-demo-v1-0.png +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/action/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/analysis/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/base.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/data/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/data/fixture.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/data/price.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/data/scout.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/orchestration/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/agents/orchestration/report.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/dataset_fetcher.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/fpl.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/fpl_draft.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/historical.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/historical_types.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/providers/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/providers/_models.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/providers/anthropic.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/providers/openai_compat.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/api/providers/perplexity.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_banner.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_context.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_fines.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_fines_config.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_helpers.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_json.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_league_recap_data.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_league_recap_history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_league_recap_types.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_plan_grid.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_review_analysis.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_review_classic.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_review_draft.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/_review_summarisation.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/captain.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/chips.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/credentials.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/differentials.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/fdr.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/fixtures.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/init.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/intel.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/league.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/league_recap.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/preview.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/price_changes.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/price_history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/review.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/squad.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/status.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/targets.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/transfer_eval.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/waivers.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/cli/xg.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/config/fixture_predictions.yaml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/config/previews/EXAMPLE.yaml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/config/team_managers.yaml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/constants.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/chip_plan.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/fixture.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/league_history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/player.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/models/team.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/parsers/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/parsers/recommendations.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/paths.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/prompts/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/prompts/league_recap.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/prompts/review.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/prompts/scout.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/scraper/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/season.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/fixture_predictions.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/league_history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/league_history_counters.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/league_history_notes.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/matchup.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/season_previews.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/services/team_form.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/templates/gw_league_recap.md.j2 +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/templates/gw_preview.md.j2 +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/templates/gw_review.md.j2 +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/files.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/gameweek.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/markdown.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/teams.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/text.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/fpl_cli/utils/time.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/pyproject.toml +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/pyrightconfig.json +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/requirements.lock +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/__init__.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/fixtures/classic_squad_fixture.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/fixtures/recommendations_fixture.md +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/fixtures/validate_draft_waivers/squad_grid.json +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_base.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_data.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_data_price.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_agents_orchestration.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_allocate.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_client.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_dataset_fetcher.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_draft.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_api_historical.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_bench_order_script.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_classic_squad_extraction.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_banner.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_captain.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_chips.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_credentials.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_custom_analysis.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_differentials.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_fdr.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_fdr_blanks.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_fixtures.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_format.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_format_suppression.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_init.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_intel.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_json.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_league.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_league_recap.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_plan_grid.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_preview.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_price_changes.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_price_history.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_review.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_settings_warning.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_squad.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_stats.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_status.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_targets.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_transfer_eval.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_waivers.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_warnings_stderr.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_cli_xg.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_common.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_doc_consistency.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_fines.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_fines_config.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_fixture_predictions.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_fpl_prices.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_init.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_init_fines.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_league_history_counters.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_league_history_notes.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_league_history_store.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_league_recap.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_matchup_service.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_models.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_paths.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_player_resolution.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_providers.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_recommendations_parser.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_report_agent.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_report_paths.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_review.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_season.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_season_awareness.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_season_previews.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_sell_prices.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_utils_gameweek.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_utils_markdown.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_utils_text.py +0 -0
- {fplkit-2.1.0 → fplkit-2.2.0}/tests/test_utils_time.py +0 -0
|
@@ -26,7 +26,7 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
26
26
|
|---------|-------------|------|--------|-------------|---------|
|
|
27
27
|
| `init` | Set up fpl-cli with your FPL IDs and optional features | No | General | No | direct-api |
|
|
28
28
|
| `status` | Show FPL gameweek status and upcoming deadlines. JSON `metadata` carries `gameweek`, `format` and `season` (hyphenated label, e.g. `2026-27`) - `season` is the directory segment skills must use when writing alongside fpl-cli's own reports | Yes | General | No | direct-api |
|
|
29
|
-
| `doctor` | Check the setup for dead IDs, stale data, and config problems: resolves each configured ID against the live API (reporting the team/league name back so a wrong-but-valid ID is visible), verifies per-team data files (team ratings, managers, season preview intel, finances, player prior) describe the current season's clubs, and reports which config/data/cache directories are in use. Distinguishes broken (needs a fix today) from stale (self-corrects). Exits non-zero when something is broken | Yes | General | No | direct-api |
|
|
29
|
+
| `doctor` | Check the setup for dead IDs, stale data, and config problems: resolves each configured ID against the live API (reporting the team/league name back so a wrong-but-valid ID is visible), verifies per-team data files (team ratings, managers, season preview intel, finances, player prior) describe the current season's clubs, and reports which config/data/cache directories are in use. With `--providers`, probes the external data sources instead: live shape-and-volume checks against the FPL and Draft APIs, the vaastav and Core-Insights datasets, Understat, and football-data.org, including that every club resolves across sources (the drift that otherwise surfaces as plausible but wrong output). Distinguishes broken (needs a fix today) from stale (self-corrects). Exits non-zero when something is broken | Yes | General | No | direct-api |
|
|
30
30
|
| `fixtures` | Show fixtures for a gameweek | Yes | General | No | direct-api |
|
|
31
31
|
| `player` | Look up a player's stats, xG, ownership and fixture run. JSON includes `ep_next` (predicted pts next GW) and `ep_this` (current GW), emitting `null` when FPL has no projection; panel shows `xPts` (ep_next) when available, omits the segment when FPL has no projection. `--detail` (`-d`) shows GW-by-GW performance and, for FWD/MID with sufficient history, xGI sustainability (per-match GI-xGI divergence -> form modifier). With `custom_analysis`: JSON adds `info.adjusted_npxg_per_90` (fixture-adjusted) and `info.raw_npxg_per_90` (Understat season avg); panel shows `adj. npxG/90: X.XXX (raw: Y.YYY)` | Yes | General | No | direct-api |
|
|
32
32
|
| `stats` | List players with filtering and sorting. `--value` adds quality/value per £m columns; `--window N` sets rolling lookback (3-10) for `rolling_pts_per_m`. Sortable by `ep_next`/`ep_this` (FPL predicted points; `ep_this` is only meaningful before the gameweek's first kickoff — FPL rolls it forward to `ep_next` as each player's match finishes); both in JSON output (emit `null` when FPL has no projection; table renders `—`). Players with no projection sort to the bottom in either direction | Yes | General | No | direct-api |
|
|
@@ -36,15 +36,16 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
36
36
|
| `xg` | Analyse underlying stats: xG, xA, overperformers | Yes | General | No | via-agent |
|
|
37
37
|
| `price-changes` | Show price changes and transfer activity | No | General | No | via-agent |
|
|
38
38
|
| `price-history` | Show price trajectory and transfer momentum | Yes | General | No | direct-api |
|
|
39
|
+
| `returnees` | Track injured and suspended players due back soon, so a returnee can be claimed in draft before they are fit or planned around in classic. Reads the FPL availability news, filters to returns inside a window (`--window N`, 1-38) and through a source-aware quality bar, and diffs against the stored watchlist for week-over-week changes. Most flagged players carry no parseable return date, so date-unknown entries are the norm rather than the exception; `--enrich` searches the web for fresher timing on those and on stale-news entries (needs a Perplexity key; skips with a note without one) and shows it beside the FPL news, never over it. `--all` bypasses the quality bar and deliberately does not persist the snapshot. JSON `metadata` carries `window`, `escalation_window`, `stash_upgrade_margin`, `transitions_available`, `quality_bar_available`, `quality_bar_applied` and `enrichment_requested`/`_available`/`_note`/`_count`; `data` is `{entries, departures}`, each entry carrying `quality.basis`/`quality.meets_stash`, `expected_return`/`return_gameweek`/`return_source`, `escalation_eligible`/`escalation_basis`, `transition`, and `enrichment` (null unless enriched) | Yes | General | No | direct-api |
|
|
39
40
|
| `preview` | Run full pre-gameweek analysis and generate report. `--save` writes to `<output dir>/<season>/gw{N}-preview.md`; `--scout` writes to `<research dir>/ai-scout-reports/<season>/gw{N}-scout-preview[-referenced].md` | No | General | No | via-agent |
|
|
40
41
|
| `review` | Review a completed gameweek - squad performance and standings. `--save` writes to `<output dir>/<season>/gw{N}-review.md`; `--compare-recs` reads `gw{N}-recommendations.md` from the same season directory | No | General | No | via-agent |
|
|
41
42
|
| `league-recap` | Recap a completed gameweek - awards, standings, and banter. Saves to `<output dir>/<season>/gw{N}-league-recap[-draft].md`. Records every run into the league history ledger (`<data dir>/league_history/`) as a side effect, backfilling classic gaps at a coarse tier automatically; `--backfill-detail` rebuilds earlier gameweeks in full (one request per manager per gameweek). Coverage gaps and capture warnings go to stderr; the command exits 0 even when the store is unreadable. `--format json` emits one row per manager (the captured ledger row shape) plus `metadata.coverage`, `season_phase`, `notes_pack`, `synthesis_summary`, `warnings` (always present, each entry a stable `code` plus rewritable `message` - codes listed in [Command Reference](../docs/command-reference.md#league-history)) and `first_capture_store_path` | Yes | General | No | via-agent |
|
|
42
|
-
| `captain` | Analyse and rank captain options for next gameweek. JSON candidates include `adjusted_npxg_per_90` and `raw_npxg_per_90` when fixture adjustment is active. Consistency tiebreaker (CV-xGI percentile) phased in GW6-10 | Yes | Classic | Yes | via-agent |
|
|
43
|
-
| `differentials` | Find differential picks - high potential, low ownership. Inverted consistency bonus (volatile players score higher, phased in GW6-10) | Yes | Classic | Yes | via-agent |
|
|
44
|
-
| `targets` | Find transfer targets - high performers across all ownership. Consistency bonus (CV-xGI percentile, phased in GW6-10) | Yes | Classic | Yes | via-agent |
|
|
45
|
-
| `transfer-eval` | Compare transfer OUT player against IN candidates. JSON includes `adjusted_npxg_per_90` and `raw_npxg_per_90` per player when fixture adjustment is active | Yes | General | Yes | via-agent |
|
|
46
|
-
| `allocate` | Select mathematically optimal 15-player squad within budget | Yes | Classic | Yes | direct-api |
|
|
47
|
-
| `waivers` | Show waiver recommendations for your draft league | Yes | Draft | Yes | via-agent |
|
|
43
|
+
| `captain` | Analyse and rank captain options for next gameweek. JSON candidates include `adjusted_npxg_per_90` and `raw_npxg_per_90` when fixture adjustment is active. Consistency tiebreaker (CV-xGI percentile) phased in GW6-10. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the GW1-9 shrinkage rather than pulled toward the position mean | Yes | Classic | Yes | via-agent |
|
|
44
|
+
| `differentials` | Find differential picks - high potential, low ownership. Inverted consistency bonus (volatile players score higher, phased in GW6-10). Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the GW1-9 shrinkage rather than pulled toward the position mean | Yes | Classic | Yes | via-agent |
|
|
45
|
+
| `targets` | Find transfer targets - high performers across all ownership. Consistency bonus (CV-xGI percentile, phased in GW6-10). Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the GW1-9 shrinkage rather than pulled toward the position mean | Yes | Classic | Yes | via-agent |
|
|
46
|
+
| `transfer-eval` | Compare transfer OUT player against IN candidates. JSON includes `adjusted_npxg_per_90` and `raw_npxg_per_90` per player when fixture adjustment is active. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the GW1-9 shrinkage rather than pulled toward the position mean | Yes | General | Yes | via-agent |
|
|
47
|
+
| `allocate` | Select mathematically optimal 15-player squad within budget. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the GW1-9 shrinkage rather than pulled toward the position mean; they stay in the solver pool | Yes | Classic | Yes | direct-api |
|
|
48
|
+
| `waivers` | Show waiver recommendations for your draft league. JSON `data` carries `top_targets` and `targets_by_position` (ranked claims) plus `pool` — the full unowned roster, one `{id, player_name, position, team_short}` row each, unranked and untruncated, so a consumer can answer "is this player claimable at all" without inferring it from the ranked lists. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the GW1-9 shrinkage rather than pulled toward the position mean | Yes | Draft | Yes | via-agent |
|
|
48
49
|
| `squad` | Analyse your FPL squad health and fixtures | Yes | General | No | via-agent |
|
|
49
50
|
| `squad grid` | Show squad fixture difficulty grid | Yes | General | No | via-agent |
|
|
50
51
|
| `squad sell-prices` | Show squad sell prices and financial breakdown | Yes | Classic | No | direct-api |
|
|
@@ -54,7 +55,7 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
54
55
|
| `chips remove` | Remove a planned chip from a gameweek | No | Classic | No | direct-api |
|
|
55
56
|
| `chips sync` | Sync chip usage from FPL API | No | Classic | No | direct-api |
|
|
56
57
|
| `ratings` | Display team ratings | No | General | Yes | direct-api |
|
|
57
|
-
| `ratings update` | Recalculate ratings from fixture results | No | General | Yes | direct-api |
|
|
58
|
+
| `ratings update` | Recalculate ratings from fixture results (shrunk toward the previous-season prior before GW12; seeds from the prior when no results exist yet) | No | General | Yes | direct-api |
|
|
58
59
|
| `intel` | Show season preview intel collected per team, with coverage and per-gameweek decay. JSON `metadata` carries `coverage.usable_as` (`full` / `negative_filter_only` / `none`), `section_confidence`, `sections_live`/`sections_expired`, `decay_schedule`, `team_set_warning` and `warnings`. `-g/--gameweek` ages the payload to any gameweek; `--show-decay` prints the expiry schedule | Yes | General | No | direct-api |
|
|
59
60
|
| `intel schema` | Print the preview file format with every field explained | No | General | No | direct-api |
|
|
60
61
|
| `intel init` | Scaffold an empty preview file per Premier League team (`--force` overwrites). Stubs never count toward coverage | No | General | No | direct-api |
|
|
@@ -79,7 +80,7 @@ Skills write their own outputs (`gw{N}-recommendations.md`, `gw{N}-squad-builder
|
|
|
79
80
|
|
|
80
81
|
| Skill | Path | Purpose | Compatibility |
|
|
81
82
|
|-------|------|---------|--------------|
|
|
82
|
-
| gw-prep | `skills/gw-prep/` | Gameweek preparation recommendations for classic and draft (embed / rederive / transfer branches; Phase B9 preview intel; Phase E post-write validation) | Full: Claude Code. Partial: Codex, Cursor, Copilot |
|
|
83
|
+
| gw-prep | `skills/gw-prep/` | Gameweek preparation recommendations for classic and draft (embed / rederive / transfer branches; Phase B9 preview intel; Phase B10 injury returnee radar; Phase E post-write validation) | Full: Claude Code. Partial: Codex, Cursor, Copilot |
|
|
83
84
|
| update-gw-prep | `skills/update-gw-prep/` | Append GW update to existing recommendations | Full: Claude Code, Codex, Cursor, Copilot |
|
|
84
85
|
| squad-builder | `skills/squad-builder/` | Build optimal 15-player squad (wildcard, free hit, season start; Phase B3 preview intel gate) | Full: Claude Code, Codex, Cursor, Copilot |
|
|
85
86
|
| preview-ingest | `skills/preview-ingest/` | Convert season preview prose into structured per-team intel files, resolve player codes, verify coverage | Full: Claude Code (parallel per team), Codex, Cursor, Copilot |
|
|
@@ -107,7 +108,9 @@ Python classes in `fpl_cli/agents/` that implement `async run(context) -> AgentR
|
|
|
107
108
|
|
|
108
109
|
**Notes:**
|
|
109
110
|
- BenchOrderAgent and StartingXIAgent have no CLI command - they are invoked by gw-prep skill wrapper scripts in `.agents/skills/gw-prep/scripts/`
|
|
111
|
+
- Every scoring family holds players known not to be playing out of the GW1-9 shrinkage; bench and lineup order on raw scores, so for those two it changes the displayed score only
|
|
110
112
|
- TransferEvalAgent is used by both `transfer-eval` CLI command and gw-prep skill
|
|
111
113
|
- `extract_classic_squad.py` (`.agents/skills/gw-prep/scripts/`) — deterministic Classic Squad block extractor used by gw-prep Phase A3 + Phase E. JSON stdout; read-only; emits TypedDict-annotated payloads.
|
|
114
|
+
- gw-prep Phase B10 runs `fpl returnees --enrich --format json` and inlines the payload into both Phase C prompts, rendering a Returning Soon section in the classic and draft output templates. In draft a row can escalate into a stash claim; in classic the watchlist is informational and transfer recommendations may not name a tracked returnee (`references/rules.md`)
|
|
112
115
|
- `validate_draft_waivers.py` (`.agents/skills/gw-prep/scripts/`) — cross-checks the Draft waiver table against the live waiver pool and squad grid (gw-prep Phase D1). JSON stdout; read-only; always exits 0.
|
|
113
116
|
- Both scripts locate markdown sections via `fpl_cli.utils.markdown` (`HeadingMatcher`, `find_section`, `section_body`, `leaf_body`, `fence_flags`), tolerating LLM heading drift (qualifiers, case, leading annotations, opt-in aliases) without matching a different heading that shares a prefix. Also used by `fpl_cli/prompts/review.py` for the GW Narrative section boundary.
|
|
@@ -13,7 +13,7 @@ compatibility:
|
|
|
13
13
|
copilot: fallback (sequential execution)
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
<!-- CLI commands composed: status, chips, chips sync, chips timing, fdr, captain, waivers, squad grid, squad sell-prices, price-history, player, stats, intel -->
|
|
16
|
+
<!-- CLI commands composed: status, chips, chips sync, chips timing, fdr, captain, waivers, squad grid, squad sell-prices, price-history, player, stats, intel, returnees -->
|
|
17
17
|
|
|
18
18
|
# Gameweek Preparation
|
|
19
19
|
|
|
@@ -231,6 +231,31 @@ Read `metadata.coverage.usable_as` and store it as `intel_gate`:
|
|
|
231
231
|
With partial coverage the written-up teams carry annotations and the rest carry nothing, so absence
|
|
232
232
|
of a flag would read as absence of merit. Store the payload as `intel` for Phase C.
|
|
233
233
|
|
|
234
|
+
### B10 -- Injury Returnee Radar
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
fpl returnees --enrich --format json
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Flagged players -- injured, suspended, unavailable, doubtful -- whose expected return lands inside
|
|
241
|
+
`metadata.window` gameweeks and who clear the radar's quality bar. Each entry in `data.entries`
|
|
242
|
+
carries the return estimate and where it came from (`expected_return`, `return_gameweek`,
|
|
243
|
+
`return_source`, `escalation_basis`), a quality verdict (`quality.passed`, `quality.meets_stash`)
|
|
244
|
+
and what moved since the previous run (`transition`). `data.departures` says who left the
|
|
245
|
+
watchlist and why.
|
|
246
|
+
|
|
247
|
+
`--enrich` searches the web for fresher return timing on the players FPL's own news field is silent
|
|
248
|
+
or stale about, which is what supplies a date for the injured majority. It needs a Perplexity API
|
|
249
|
+
key; without one the command skips enrichment, records why in `metadata.enrichment_note` and still
|
|
250
|
+
returns the FPL-sourced watchlist -- so this step is never a reason to abort the run.
|
|
251
|
+
|
|
252
|
+
Store the payload as `returnee_radar` and inline it into both Phase C prompts as a labelled
|
|
253
|
+
section. C1 renders it as an informational watchlist and is barred from naming its players in
|
|
254
|
+
transfer recommendations; C2 renders the subset still in the waiver pool and may escalate an entry
|
|
255
|
+
to a stash claim. The Returning Soon section quotes this payload, which is inlined into the prompt
|
|
256
|
+
like every other cited source -- so its numbers are grounded the same way every other cited number
|
|
257
|
+
is.
|
|
258
|
+
|
|
234
259
|
<!-- ADAPT: Add your own further supplementary data sources here. Examples:
|
|
235
260
|
- `fpl preview --save --scout` generates a GW preview with fixture analysis and scout insights.
|
|
236
261
|
Read the saved file and inject its content into Phase C sub-agents as additional context.
|
|
@@ -288,6 +313,13 @@ Branch on `squad_builder_result` (set in Phase A3; unset on transfer weeks):
|
|
|
288
313
|
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
289
314
|
> only to downgrade. A player with no intel is not a worse player.
|
|
290
315
|
>
|
|
316
|
+
> - Returning soon: {returnee_radar} (from B10)
|
|
317
|
+
> **Informational only.** Render it as the Returning Soon section of the Classic output template,
|
|
318
|
+
> one row per entry in `data.entries`, quoting only fields present in that payload. The
|
|
319
|
+
> injury/suspension rule in the analysis rules above governs what may be recommended while a
|
|
320
|
+
> player is flagged, and its classic branch is what bars transfer recommendations -- inline
|
|
321
|
+
> late-change swaps included -- from naming a tracked returnee. Do not restate or relax it here.
|
|
322
|
+
>
|
|
291
323
|
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
292
324
|
>
|
|
293
325
|
> **Embedded Classic Squad (from squad-builder — insert this as the `### Classic Squad` section):**
|
|
@@ -302,7 +334,7 @@ Branch on `squad_builder_result` (set in Phase A3; unset on transfer weeks):
|
|
|
302
334
|
>
|
|
303
335
|
> For Phase C2.5 (`transfer_eval.py`): on embed mode, the OUT-candidate pool is the 15 players in the Player column of the embedded Starting XI and Bench tables (not the current `fpl squad grid`). Use this to evaluate whether any late-breaking swaps are justified.
|
|
304
336
|
>
|
|
305
|
-
> **Sections to produce:** Chip Timing (from B6), Momentum Alerts (from B5/B8), pFDR Overview (from B1), and the `### Classic Squad` block (embedded verbatim or swap-edited with trailing note). **Suppress:** Captain Pick top-3 table (captain is inside the embedded block), standalone Bench Order section (bench order is inside the embedded block), and Transfer Recommendations section (any swaps are applied inline in the block).
|
|
337
|
+
> **Sections to produce:** Chip Timing (from B6), Momentum Alerts (from B5/B8), Returning Soon (from B10), pFDR Overview (from B1), and the `### Classic Squad` block (embedded verbatim or swap-edited with trailing note). **Suppress:** Captain Pick top-3 table (captain is inside the embedded block), standalone Bench Order section (bench order is inside the embedded block), and Transfer Recommendations section (any swaps are applied inline in the block).
|
|
306
338
|
>
|
|
307
339
|
> **User workflow note:** The `### Classic Squad` block is the final 15-player squad the user should enter into FPL. If a trailing `> Late change:` note is present, it explains the swap — the user can revert it in the FPL site before saving if they disagree.
|
|
308
340
|
>
|
|
@@ -359,6 +391,13 @@ Proceed immediately (non-interactive).
|
|
|
359
391
|
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
360
392
|
> only to downgrade. A player with no intel is not a worse player.
|
|
361
393
|
>
|
|
394
|
+
> - Returning soon: {returnee_radar} (from B10)
|
|
395
|
+
> **Informational only.** Render it as the Returning Soon section of the Classic output template,
|
|
396
|
+
> one row per entry in `data.entries`, quoting only fields present in that payload. The
|
|
397
|
+
> injury/suspension rule in the analysis rules above governs what may be recommended while a
|
|
398
|
+
> player is flagged, and its classic branch is what bars transfer recommendations -- inline
|
|
399
|
+
> late-change swaps included -- from naming a tracked returnee. Do not restate or relax it here.
|
|
400
|
+
>
|
|
362
401
|
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
363
402
|
>
|
|
364
403
|
> Apply squad-builder rules from `references/rules.md` for full squad selection. Prepend the fallback banner (Variant A/B/C — see `references/output-template.md` for exact wording) on the line immediately before the Classic section heading in the output file. Do not include `squad_builder_mode` in the frontmatter — the squad was re-derived, not embedded from squad-builder.
|
|
@@ -391,7 +430,13 @@ Proceed immediately (non-interactive).
|
|
|
391
430
|
> - Squad: {B4 output}
|
|
392
431
|
> - Price movements: {B5 output}
|
|
393
432
|
> - Chip timing: {B6 output}
|
|
394
|
-
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
433
|
+
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)>
|
|
434
|
+
> - Returning soon: {returnee_radar} (from B10)
|
|
435
|
+
> **Informational only.** Render it as the Returning Soon section of the Classic output template,
|
|
436
|
+
> one row per entry in `data.entries`, quoting only fields present in that payload. The
|
|
437
|
+
> injury/suspension rule in the analysis rules above governs what may be recommended while a
|
|
438
|
+
> player is flagged, and its classic branch is what bars transfer recommendations -- inline
|
|
439
|
+
> late-change swaps included -- from naming a tracked returnee. Do not restate or relax it here.
|
|
395
440
|
>
|
|
396
441
|
> **Bench Boost instructions:**
|
|
397
442
|
>
|
|
@@ -436,6 +481,13 @@ Proceed immediately (non-interactive).
|
|
|
436
481
|
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
437
482
|
> only to downgrade. A player with no intel is not a worse player.
|
|
438
483
|
>
|
|
484
|
+
> - Returning soon: {returnee_radar} (from B10)
|
|
485
|
+
> **Informational only.** Render it as the Returning Soon section of the Classic output template,
|
|
486
|
+
> one row per entry in `data.entries`, quoting only fields present in that payload. The
|
|
487
|
+
> injury/suspension rule in the analysis rules above governs what may be recommended while a
|
|
488
|
+
> player is flagged, and its classic branch is what bars transfer recommendations -- inline
|
|
489
|
+
> late-change swaps included -- from naming a tracked returnee. Do not restate or relax it here.
|
|
490
|
+
>
|
|
439
491
|
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
440
492
|
>
|
|
441
493
|
> Produce the **Classic** section of the output template.
|
|
@@ -463,6 +515,8 @@ Proceed immediately (non-interactive).
|
|
|
463
515
|
>
|
|
464
516
|
> **WAIVER POOL IS AUTHORITATIVE:** `fpl waivers` output is the only source for available players. All other data (stats, form tables, squad context) is for analysis only. Never recommend a claim not present in the waivers output — Phase D1 will flag pool misses as a warning. Cross-position recommendations (e.g. dropping a MID to claim a DEF) are structurally illegal and will be blocked by Phase D1.
|
|
465
517
|
>
|
|
518
|
+
> **`data.pool` is an availability roster, not a shortlist.** It lists every unowned player in the league — one `{id, player_name, position, team_short}` row each, unranked and untruncated — so membership answers only "may this player be claimed at all". Ranked claims come from `data.top_targets` and `data.targets_by_position`; presence in `data.pool` is never on its own a reason to recommend someone. The single claim allowed to rest on pool membership alone is a stash claim for a tracked returnee, which the waiver scoring suppresses by design and which therefore cannot reach the ranked targets.
|
|
519
|
+
>
|
|
466
520
|
> - Waivers: {B3 output}
|
|
467
521
|
> - Squad: {B4 output}
|
|
468
522
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
@@ -472,6 +526,38 @@ Proceed immediately (non-interactive).
|
|
|
472
526
|
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
473
527
|
> only to downgrade. A player with no intel is not a worse player.
|
|
474
528
|
>
|
|
529
|
+
> - Returning soon: {returnee_radar} (from B10)
|
|
530
|
+
> Render the Returning Soon section of the Draft output template from this payload, restricted to
|
|
531
|
+
> tracked returnees whose `id` appears in the waivers `data.pool` list. A returnee missing from
|
|
532
|
+
> the pool is already owned by a rival and is not actionable — leave it out rather than listing it
|
|
533
|
+
> as unavailable. Quote only fields present in this payload.
|
|
534
|
+
>
|
|
535
|
+
> **STASH CLAIMS:** a Returning Soon row escalates into the Waiver Recommendations table only when
|
|
536
|
+
> **all four** gates below hold. Any one failing means watchlist only — no claim.
|
|
537
|
+
>
|
|
538
|
+
> 1. **Elite by prior:** `quality.meets_stash` is `true`.
|
|
539
|
+
> 2. **Returning soon enough:** `escalation_eligible` is `true`. The command has already applied
|
|
540
|
+
> `metadata.escalation_window` and the citation gate, so do not re-derive the timing yourself:
|
|
541
|
+
> an enrichment date counts here only when it arrived cited, and `escalation_basis` names which
|
|
542
|
+
> source the verdict rests on (`fpl-news` or `ai-search`).
|
|
543
|
+
> 3. **Claimable and position-for-position:** the returnee's `id` is in the waivers `data.pool`, and
|
|
544
|
+
> the player dropped for them plays the returnee's own position.
|
|
545
|
+
> 4. **Beats the incumbent by the configured margin:** take the drop candidate your squad analysis
|
|
546
|
+
> already ranks lowest at that position, run C2.5's `transfer_eval.py --out "{that player}"
|
|
547
|
+
> --in "{returnee}"`, and require `outlook_delta` to exceed the radar's
|
|
548
|
+
> `metadata.stash_upgrade_margin`, which already carries the effective configured value; both
|
|
549
|
+
> are quality points on the same 0-100 scale. If the script errors, or the margin is missing
|
|
550
|
+
> from the metadata, or the delta does not clear it, do not escalate. A drop-priority ordering
|
|
551
|
+
> always yields a lowest-ranked player and so can never answer "no" — the margin is what makes
|
|
552
|
+
> this gate refusable.
|
|
553
|
+
>
|
|
554
|
+
> Write every stash claim as a stash, never as a straight upgrade: it spends a roster slot until the
|
|
555
|
+
> player is fit again, and it is bought to lock the asset before a rival can claim them. Each stash
|
|
556
|
+
> row's Rationale must carry the expected return (`expected_return`, or `return_gameweek` where the
|
|
557
|
+
> date is unknown) and name the provenance of that date from `escalation_basis`. The
|
|
558
|
+
> injury/suspension rule in the analysis rules above is the single authority on what may be
|
|
559
|
+
> recommended while a player is flagged.
|
|
560
|
+
>
|
|
475
561
|
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
476
562
|
>
|
|
477
563
|
> Additionally enforce: every waiver swap must be position-for-position (MID out → MID in, DEF out → DEF in, etc.). Cross-position swaps are illegal under FPL Draft rules.
|
|
@@ -76,6 +76,18 @@ For each transfer, include:
|
|
|
76
76
|
- Form summary
|
|
77
77
|
- Price trend
|
|
78
78
|
|
|
79
|
+
### Returning Soon
|
|
80
|
+
|
|
81
|
+
_Informational only. Omit the section when the radar payload has no entries._
|
|
82
|
+
|
|
83
|
+
| Player | Team | Pos | Quality | Expected Return | Chance | Change |
|
|
84
|
+
|--------|------|-----|---------|-----------------|--------|--------|
|
|
85
|
+
| | | | | | | |
|
|
86
|
+
|
|
87
|
+
One row per entry in the `fpl returnees` payload (`data.entries`). Quality = `quality.basis` plus whether it clears the stash bar (`quality.meets_stash`). Expected Return = `expected_return`, or `return_gameweek` where only a gameweek is known, or `Unknown`. Chance = `chance_of_playing`. Change = `transition` since the previous run, blank when nothing moved.
|
|
88
|
+
|
|
89
|
+
Every number here comes from the radar payload inlined into the sub-agent prompt, so the Momentum Alerts grounding rule is satisfied by citing it — but nothing beyond those fields may be asserted. No transfer recommendation above may name a tracked returnee (see the injury/suspension rule in `references/rules.md`).
|
|
90
|
+
|
|
79
91
|
### Classic Squad
|
|
80
92
|
|
|
81
93
|
_Embed-mode only: the orchestrator replaces this placeholder at runtime with the `{embedded_classic_squad_block}` extracted from `gw{N}-squad-builder.md`. Do not populate this section manually — it is produced by the C1 sub-agent._
|
|
@@ -124,6 +136,18 @@ Cross-referenced against current squad holdings.
|
|
|
124
136
|
|
|
125
137
|
Outlook = multi-GW quality delta (target score). This GW = lineup impact delta. Both from `transfer_eval.py`.
|
|
126
138
|
|
|
139
|
+
### Returning Soon
|
|
140
|
+
|
|
141
|
+
_Tracked returnees still unowned in the league. Omit the section when nothing qualifies._
|
|
142
|
+
|
|
143
|
+
| Player | Team | Pos | Quality | Expected Return | Chance | Change | Verdict |
|
|
144
|
+
|--------|------|-----|---------|-----------------|--------|--------|---------|
|
|
145
|
+
| | | | | | | | |
|
|
146
|
+
|
|
147
|
+
Rows are the `fpl returnees` payload (`data.entries`) intersected with the waivers `data.pool` by `id` — a returnee already owned by a rival is not listed at all. Columns as Classic. Verdict = `Stash` when every escalation gate in SKILL.md Phase C2 is met, in which case the player also appears in Waiver Recommendations above with their expected return and its `escalation_basis` in the Rationale; otherwise `Watch`.
|
|
148
|
+
|
|
149
|
+
Every number here comes from the radar payload inlined into the sub-agent prompt, so the Momentum Alerts grounding rule is satisfied by citing it — but nothing beyond those fields may be asserted.
|
|
150
|
+
|
|
127
151
|
### Starting XI
|
|
128
152
|
|
|
129
153
|
Recommended lineup with formation (via lineup engine).
|
|
@@ -45,7 +45,7 @@ When `mode = "squad-builder"`:
|
|
|
45
45
|
4. **Fixture run** -- as with classic, weight the next 3 gameweeks of fixtures rather than just the immediate one.
|
|
46
46
|
5. **Maximum 5 waiver suggestions** -- for each: player to drop, player to claim, positional context, and fixture rationale.
|
|
47
47
|
6. **Position-for-position only** -- FPL Draft squads are locked at 2 GK / 5 DEF / 5 MID / 3 FWD. Every waiver swap must be position-for-position (e.g. MID out → MID in). Cross-position swaps are structurally illegal and will be rejected by Phase D1.
|
|
48
|
-
7. **Waiver pool discipline** -- only recommend players returned by `fpl waivers --format json`. Players appearing in other data sources (e.g. `fpl stats`) are not necessarily available; never recommend a claim not in the waiver pool.
|
|
48
|
+
7. **Waiver pool discipline** -- only recommend players returned by `fpl waivers --format json`. Players appearing in other data sources (e.g. `fpl stats`) are not necessarily available; never recommend a claim not in the waiver pool. Within that output, `data.pool` establishes availability only -- it is the full unowned roster, unranked and untruncated. Ranked claims come from `data.top_targets` and `data.targets_by_position`; the one claim that may rest on `data.pool` membership alone is a stash claim for a tracked returnee, whom the waiver scoring suppresses by design.
|
|
49
49
|
|
|
50
50
|
## Transfer/Waiver Evaluation Script
|
|
51
51
|
|
|
@@ -63,7 +63,10 @@ For all formats:
|
|
|
63
63
|
1. **Recent form** -- last 4 GW average points, minutes played (flag rotation risks below 60 mins average).
|
|
64
64
|
2. **Expected stats** -- where available, prefer xG/xA-based analysis over raw goals/assists. Flag players significantly over- or under-performing their expected stats.
|
|
65
65
|
3. **Set-piece involvement** -- note players on corners, free kicks, and penalties as these provide floor-raising opportunities.
|
|
66
|
-
4. **Injury/suspension flags** -- check player availability. Never recommend a player flagged as injured or suspended without explicitly noting the risk.
|
|
66
|
+
4. **Injury/suspension flags** -- check player availability. Never recommend a player flagged as injured or suspended without explicitly noting the risk. A **tracked returnee** -- any entry in `data.entries` of the `fpl returnees` payload, excluding those whose `status` is `"d"` with a non-zero `chance_of_playing` -- is held to a tighter rule than that:
|
|
67
|
+
- **Draft:** a tracked returnee may be recommended only as an explicit stash claim -- a roster slot spent until the player is fit, to lock the asset before a rival claims them. Never present one as a straight upgrade for this gameweek. Every stash claim must carry the expected return (`expected_return`, or `return_gameweek` where the date is unknown) and name the provenance of that date from `escalation_basis`. SKILL.md Phase C2 holds the gates a watchlist row must clear before it becomes a claim.
|
|
68
|
+
- **Classic:** the returnee watchlist is informational. Transfer recommendations -- including any inline late-change swap on an embedded squad -- must not name a tracked returnee. Classic has no scarcity race: a returnee can be bought the week they are actually back.
|
|
69
|
+
- **Unchanged for everyone else:** a doubtful (`d`) player with a non-zero chance of playing is not a tracked returnee. They may still be recommended with the risk explicitly noted, exactly as before.
|
|
67
70
|
|
|
68
71
|
## Momentum Alerts
|
|
69
72
|
|
|
@@ -25,7 +25,7 @@ import json
|
|
|
25
25
|
import re
|
|
26
26
|
import sys
|
|
27
27
|
import unicodedata
|
|
28
|
-
from typing import TypedDict
|
|
28
|
+
from typing import NotRequired, TypedDict, cast
|
|
29
29
|
|
|
30
30
|
from fpl_cli.utils.markdown import HeadingMatcher, find_section, leaf_body
|
|
31
31
|
|
|
@@ -58,6 +58,7 @@ class PoolEntry(TypedDict):
|
|
|
58
58
|
player_name: str
|
|
59
59
|
position: str
|
|
60
60
|
team_short: str
|
|
61
|
+
id: NotRequired[int]
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
class SquadEntry(TypedDict):
|
|
@@ -125,24 +126,65 @@ def _shape_check_squad(data: dict) -> bool:
|
|
|
125
126
|
return False
|
|
126
127
|
|
|
127
128
|
|
|
129
|
+
def _entries_shape_ok(entries: object) -> bool:
|
|
130
|
+
"""Return True if entries is a list of name/position records (empty is fine)."""
|
|
131
|
+
if not isinstance(entries, list):
|
|
132
|
+
return False
|
|
133
|
+
if not entries:
|
|
134
|
+
return True
|
|
135
|
+
sample = entries[0]
|
|
136
|
+
return isinstance(sample, dict) and "player_name" in sample and "position" in sample
|
|
137
|
+
|
|
138
|
+
|
|
128
139
|
def _shape_check_waivers(data: dict) -> bool:
|
|
129
|
-
"""Return True if waivers JSON has the expected shape.
|
|
140
|
+
"""Return True if waivers JSON has the expected shape.
|
|
141
|
+
|
|
142
|
+
`pool` is the full unowned roster and is what membership is checked against;
|
|
143
|
+
`top_targets` is the ranked subset older waivers JSON carried on its own.
|
|
144
|
+
Either satisfies the guard, so a cached file still validates.
|
|
145
|
+
|
|
146
|
+
A `pool` key that is present but unusable deliberately fails the file rather
|
|
147
|
+
than falling through to `top_targets`. The two are not interchangeable: a
|
|
148
|
+
flagged player is kept out of `top_targets` by the availability factor no
|
|
149
|
+
matter how strong the case for claiming them, so checking membership
|
|
150
|
+
against the ranked subset reports every legitimate injury claim as a miss.
|
|
151
|
+
Failing here warns once and suppresses the pool check for the run, which
|
|
152
|
+
beats a page of false positives on exactly the claims this validates.
|
|
153
|
+
"""
|
|
130
154
|
try:
|
|
131
|
-
|
|
132
|
-
if
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
return True
|
|
136
|
-
sample = targets[0]
|
|
137
|
-
return "player_name" in sample and "position" in sample
|
|
155
|
+
payload = data["data"]
|
|
156
|
+
if "pool" in payload:
|
|
157
|
+
return _entries_shape_ok(payload["pool"])
|
|
158
|
+
return _entries_shape_ok(payload["top_targets"])
|
|
138
159
|
except (KeyError, TypeError):
|
|
139
160
|
return False
|
|
140
161
|
|
|
141
162
|
|
|
142
163
|
def build_pool(waivers_data: dict) -> dict[tuple[str, str], PoolEntry]:
|
|
143
|
-
"""Build {(position, normalised_name) -> entry} from waivers JSON.
|
|
164
|
+
"""Build {(position, normalised_name) -> entry} from waivers JSON.
|
|
165
|
+
|
|
166
|
+
Reads the full unowned `pool` roster, so a claim for a flagged player is
|
|
167
|
+
resolvable: the availability factor in the waiver score keeps such a player
|
|
168
|
+
out of `top_targets` no matter how strong the case for claiming them. Falls
|
|
169
|
+
back to `top_targets` when `pool` is missing or empty -- an older cached
|
|
170
|
+
file then validates as it always did rather than reporting every claim as a
|
|
171
|
+
miss.
|
|
172
|
+
"""
|
|
173
|
+
payload = waivers_data.get("data", {})
|
|
174
|
+
entries = payload.get("pool")
|
|
175
|
+
if not isinstance(entries, list) or not entries:
|
|
176
|
+
entries = payload.get("top_targets")
|
|
177
|
+
if not isinstance(entries, list):
|
|
178
|
+
# The shape check clears a file on either key, so the fallback can still
|
|
179
|
+
# land on a malformed value. Reporting no pool costs a missed check;
|
|
180
|
+
# raising here would exit non-zero and break the orchestrator contract.
|
|
181
|
+
entries = []
|
|
182
|
+
|
|
144
183
|
pool: dict[tuple[str, str], PoolEntry] = {}
|
|
145
|
-
for
|
|
184
|
+
for raw in entries:
|
|
185
|
+
if not isinstance(raw, dict):
|
|
186
|
+
continue
|
|
187
|
+
entry = cast(PoolEntry, raw)
|
|
146
188
|
pos = entry.get("position", "")
|
|
147
189
|
key = (pos, normalise(entry.get("player_name", "")))
|
|
148
190
|
pool[key] = entry
|
|
@@ -144,6 +144,27 @@ Actions run.
|
|
|
144
144
|
repo), update the secret, re-run the job.
|
|
145
145
|
- **PyPI publish fails with OIDC errors**: check the `pypi` environment on
|
|
146
146
|
the repo and the trusted-publisher config on the fplkit PyPI project.
|
|
147
|
+
- **PyPI publish fails on distribution metadata**
|
|
148
|
+
(`InvalidDistribution: Invalid distribution metadata: '2.5' is not a
|
|
149
|
+
valid metadata version`): toolchain drift, not a bad tag. `pyproject.toml`
|
|
150
|
+
requires `hatchling>=1.21` with no upper bound, so the build emits
|
|
151
|
+
whatever `Metadata-Version` a current hatchling produces, while
|
|
152
|
+
`pypa/gh-action-pypi-publish` is pinned to a SHA whose bundled twine and
|
|
153
|
+
packaging predate it. Bump the action pin rather than capping hatchling —
|
|
154
|
+
PyPI accepts the newer metadata, so the stale half is the action (v1.13.0
|
|
155
|
+
ships twine 6.1.0 / packaging 25.0 and rejects 2.5; v1.14.2 ships twine
|
|
156
|
+
7.0.0 / packaging 26.2 and accepts it). Prove it either way by running
|
|
157
|
+
`twine check` on the built wheel under the pins in each action tag's
|
|
158
|
+
`requirements/runtime.txt`. This fails at twine's client-side check
|
|
159
|
+
*before* any upload, so PyPI never receives the version.
|
|
160
|
+
- **Fixing `release.yml` itself needs a retag, not a re-run**: a
|
|
161
|
+
`release`-triggered run uses the workflow file from the *tagged commit*,
|
|
162
|
+
so re-running a failed job replays the same broken workflow. Land the fix
|
|
163
|
+
on main, then `gh release delete vX.Y.Z --cleanup-tag` and re-create the
|
|
164
|
+
release against main so the tag moves onto the fix. Only safe while the
|
|
165
|
+
version is still free on PyPI — check `pip index versions fplkit` first.
|
|
166
|
+
Once PyPI has accepted an upload that number is gone permanently, and the
|
|
167
|
+
fix has to ship as the next version instead.
|
|
147
168
|
- **Version source drift**: never reintroduce a hardcoded `version =
|
|
148
169
|
"X.Y.Z"` in pyproject.toml — `dynamic = ["version"]` + hatch-vcs is the
|
|
149
170
|
single source of truth. And `fallback-version` belongs under
|
|
@@ -309,7 +309,7 @@ Include all of this in the prompt field, populated with position-specific data:
|
|
|
309
309
|
Run `fpl player "{name}" -f -H` **in parallel** for all candidates in your list.
|
|
310
310
|
For season-start modes with no current-season data, also use `fpl history` data passed in context.
|
|
311
311
|
|
|
312
|
-
7. **Scoring:** Score each candidate against the mode-specific criteria from rules. Use `quality_score` and `quality_per_m` when available (null for players without Understat data — don't penalise). **Both fields are elite-within-position: never rank or compare candidates across positions by `quality_score` or `quality_per_m`.** A
|
|
312
|
+
7. **Scoring:** Score each candidate against the mode-specific criteria from rules. Use `quality_score` and `quality_per_m` when available (null for players without Understat data — don't penalise). **Both fields are elite-within-position: never rank or compare candidates across positions by `quality_score` or `quality_per_m`.** A GK showing 90 is "top of the GK pool", not "better than a MID showing 85" — every position is normalised against its own calibrated ceiling, MID and FWD included. Within a position the numbers are trustworthy: elite players in every position read 80+, so a MID at 55 genuinely is mid-tier among MIDs. Only compare within the position you are currently filling.
|
|
313
313
|
|
|
314
314
|
8. **Return format:** Return a structured ranked list. Per candidate:
|
|
315
315
|
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
# Date-derived logic (the July season cutover) can break main with no
|
|
10
|
+
# commit behind it, which push/pull_request triggers never surface.
|
|
11
|
+
- cron: "0 6 * * 1"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
check:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Lint
|
|
31
|
+
run: ruff check fpl_cli/ scripts/
|
|
32
|
+
|
|
33
|
+
- name: Type check
|
|
34
|
+
run: pyright fpl_cli/ scripts/
|
|
35
|
+
|
|
36
|
+
- name: Test
|
|
37
|
+
run: pytest
|
|
38
|
+
|
|
39
|
+
- name: Build package
|
|
40
|
+
run: pip install hatch && hatch build
|
|
41
|
+
|
|
42
|
+
provider-probe:
|
|
43
|
+
# The hermetic test suite pins our assumption of each provider's schema,
|
|
44
|
+
# not the schema -- upstream can drift with no commit behind it, which is
|
|
45
|
+
# exactly what the Monday cron exists for. This job is the one place CI
|
|
46
|
+
# talks to the live providers: it fails only on "broken" (shape drift),
|
|
47
|
+
# never on transient unreachability, which the probe reports as unchecked.
|
|
48
|
+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- uses: actions/setup-python@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: "3.11"
|
|
56
|
+
|
|
57
|
+
- name: Install package
|
|
58
|
+
run: pip install .
|
|
59
|
+
|
|
60
|
+
- name: Probe providers
|
|
61
|
+
env:
|
|
62
|
+
# Optional: without it the football-data checks report skipped.
|
|
63
|
+
FOOTBALL_DATA_API_KEY: ${{ secrets.FOOTBALL_DATA_API_KEY }}
|
|
64
|
+
run: fpl doctor --providers --format json
|
|
@@ -4,13 +4,13 @@ fpl-cli - CLI tool for Fantasy Premier League analysis (classic + draft). Distri
|
|
|
4
4
|
## Setup & Dev
|
|
5
5
|
```bash
|
|
6
6
|
source .venv/bin/activate && pip install -e ".[dev]" # local only — web sessions install globally via setup script
|
|
7
|
-
ruff check fpl_cli/
|
|
8
|
-
pyright fpl_cli/
|
|
7
|
+
ruff check fpl_cli/ scripts/ # Lint
|
|
8
|
+
pyright fpl_cli/ scripts/ # Type check
|
|
9
9
|
python3 -m pytest tests/ # Tests
|
|
10
10
|
```
|
|
11
11
|
Run tests as `python3 -m pytest`, never bare `pytest`: in web sessions the `pytest` on PATH is a uv tool shim with its own interpreter and none of the project's dependencies, so it fails at `import pydantic`. If pytest then rejects `--disable-socket` / `--allow-unix-socket`, the dev extra is incomplete — `pip install pytest-socket`.
|
|
12
12
|
|
|
13
|
-
Entry point: `fpl_cli/cli/__init__.py:main` (Click). Config: `fpl_cli/config/defaults.yaml`, shipped inside the package and resolved as `SHIPPED_CONFIG_DIR` (`paths.py`), + `settings.yaml` in the user config dir (overrides, deep-merged via `platformdirs`; `~/Library/Application Support/fpl-cli/` on macOS). Repo-root `config/` holds only examples and `team_ratings_overrides.yaml` — nothing there is loaded as defaults. All three writable dirs have env overrides: `FPL_CLI_CONFIG_DIR` (settings, managers, overrides; an optional `fixture_predictions.yaml` here overrides the shipped copy; an optional `previews/` dir holds season preview intel read by `fpl intel`), `FPL_CLI_DATA_DIR` (generated data: team ratings, priors, chip plan, sell prices, the append-only `league_history/` ledger and its rebuildable `league_history_counters/` projection — the ledger partitions by season instead of being discarded at rollover, the one store that outlives a season), `FPL_CLI_CACHE_DIR` (disposable). Overrides must be absolute — a relative one resolves against the cwd, so it is rejected with `UserDirError` rather than silently giving a different dir per invocation. Resolve them by calling `user_config_dir()` / `user_data_dir()` / `user_cache_dir()` at point of use — binding one to a module-level constant freezes the override at import time (before `.env` loads); `tests/test_paths.py` enforces this. Ephemeral environments (Claude Code on the web) must set config + data to a persistent workspace or generated data dies with the container; cache can stay local.
|
|
13
|
+
Entry point: `fpl_cli/cli/__init__.py:main` (Click). Config: `fpl_cli/config/defaults.yaml`, shipped inside the package and resolved as `SHIPPED_CONFIG_DIR` (`paths.py`), + `settings.yaml` in the user config dir (overrides, deep-merged via `platformdirs`; `~/Library/Application Support/fpl-cli/` on macOS). Repo-root `config/` holds only examples and `team_ratings_overrides.yaml` — nothing there is loaded as defaults. All three writable dirs have env overrides: `FPL_CLI_CONFIG_DIR` (settings, managers, overrides; an optional `fixture_predictions.yaml` here overrides the shipped copy; an optional `previews/` dir holds season preview intel read by `fpl intel`), `FPL_CLI_DATA_DIR` (generated data: team ratings, priors, chip plan, sell prices, the returnee radar's `returnee_snapshot.json` week-over-week baseline (season-stamped, discarded on mismatch like `player_prior.yaml`), the append-only `league_history/` ledger and its rebuildable `league_history_counters/` projection — the ledger partitions by season instead of being discarded at rollover, the one store that outlives a season), `FPL_CLI_CACHE_DIR` (disposable). Overrides must be absolute — a relative one resolves against the cwd, so it is rejected with `UserDirError` rather than silently giving a different dir per invocation. Resolve them by calling `user_config_dir()` / `user_data_dir()` / `user_cache_dir()` at point of use — binding one to a module-level constant freezes the override at import time (before `.env` loads); `tests/test_paths.py` enforces this. Ephemeral environments (Claude Code on the web) must set config + data to a persistent workspace or generated data dies with the container; cache can stay local.
|
|
14
14
|
|
|
15
15
|
## Architecture
|
|
16
16
|
Agents inherit `fpl_cli/agents/base.py:Agent`, implement `async run(context: dict | None) -> AgentResult`. Organised in `agents/{data,analysis,action,orchestration}/`. AgentResult statuses: SUCCESS, PARTIAL, FAILED, PENDING_APPROVAL.
|
|
@@ -76,6 +76,7 @@ For a complete inventory of CLI commands, analysis agents, and skills with JSON
|
|
|
76
76
|
- Draft has no captains, no budget, no transfers and no chips — acquisition is via waivers and free agents
|
|
77
77
|
|
|
78
78
|
## Rules
|
|
79
|
+
- This repo is public: never put real manager or league names, entry/league IDs, or FPL account details into commits, PR titles or bodies, issues, or CHANGELOG.md. Use placeholders when quoting generated reports from the configured `reports.output_dir`. Footballer and club names are fine — they are public data. Before pushing, check the branch for a pre-existing leak and flag it rather than pushing over it.
|
|
79
80
|
- Verify before asserting: don't state that a command, file, function, or data point exists without checking first (read the file, run the command, grep for the name)
|
|
80
81
|
- Find-and-replace: review each replacement in context - don't blindly replace substrings in unrelated identifiers
|
|
81
82
|
- Removing/replacing X: new implementation must have zero dependencies on X
|
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
|
+
## [2.1.0] - 2026-08-23
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- survive a keyring backend that panics below Python
|
|
9
|
+
- validate pre-season teams and guard pFDR against unrated seasons
|
|
10
|
+
- consistent non-zero exit codes on command failure, friendly pre-season squad 404 (#47)
|
|
11
|
+
- drop stale committed team_ratings.yaml seed
|
|
12
|
+
- rank promoted teams against the PL, not the Championship
|
|
13
|
+
- config dirs must be absolute, and gated commands name their gate (#46)
|
|
14
|
+
- layer team_managers.yaml over the shipped copy per club
|
|
15
|
+
- make team ratings and manager config season-aware (#56)
|
|
16
|
+
- stop gw-prep squad validation failing when a heading names the formation
|
|
17
|
+
- keep the new-signing flag alive until real minutes exist, not until the window shuts
|
|
18
|
+
- stop gw-prep validators mislocating sections on drifted headings
|
|
19
|
+
- stop review inventing fines, positions and rolled transfers at gameweek 1
|
|
20
|
+
- keep the status classic section alive pre-season and stop last-place fines misreading a partial league table
|
|
21
|
+
- report exact classic league size and rank in status instead of page-one figures
|
|
22
|
+
- keep league recap accurate for large leagues and gameweek 1
|
|
23
|
+
- cap the managers named in a tied league recap bench-haul award
|
|
24
|
+
- pin PuLP below 4.0 so squad allocation keeps working (#80)
|
|
25
|
+
- stop a new season's reports overwriting the previous season's (#90)
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
- add FPL_CLI_DATA_DIR override so the data dir survives ephemeral environments
|
|
30
|
+
- user config dir copy of fixture_predictions.yaml overrides shipped copy
|
|
31
|
+
- add `fpl intel` for season preview intel with per-gameweek decay
|
|
32
|
+
- add preview-ingest skill for turning preview prose into intel files
|
|
33
|
+
- wire season preview intel into squad-builder
|
|
34
|
+
- wire season preview intel into gw-prep and update-gw-prep
|
|
35
|
+
- point-in-time headline numbers and league positions for replays (#75)
|
|
36
|
+
- record every gameweek to a durable league history store (#76)
|
|
37
|
+
- surface streaks and season phase in every output (#77)
|
|
38
|
+
- add `fpl doctor` to surface stale config and dead IDs (#98)
|
|
39
|
+
|
|
40
|
+
### Performance
|
|
41
|
+
|
|
42
|
+
- stop fpl player and fpl stats fetching data they discard (#96)
|
|
43
|
+
|
|
4
44
|
## [2.0.0] - 2026-05-25
|
|
5
45
|
|
|
6
46
|
### Bug Fixes
|
|
@@ -4,13 +4,13 @@ fpl-cli - CLI tool for Fantasy Premier League analysis (classic + draft). Distri
|
|
|
4
4
|
## Setup & Dev
|
|
5
5
|
```bash
|
|
6
6
|
source .venv/bin/activate && pip install -e ".[dev]" # local only — web sessions install globally via setup script
|
|
7
|
-
ruff check fpl_cli/
|
|
8
|
-
pyright fpl_cli/
|
|
7
|
+
ruff check fpl_cli/ scripts/ # Lint
|
|
8
|
+
pyright fpl_cli/ scripts/ # Type check
|
|
9
9
|
python3 -m pytest tests/ # Tests
|
|
10
10
|
```
|
|
11
11
|
Run tests as `python3 -m pytest`, never bare `pytest`: in web sessions the `pytest` on PATH is a uv tool shim with its own interpreter and none of the project's dependencies, so it fails at `import pydantic`. If pytest then rejects `--disable-socket` / `--allow-unix-socket`, the dev extra is incomplete — `pip install pytest-socket`.
|
|
12
12
|
|
|
13
|
-
Entry point: `fpl_cli/cli/__init__.py:main` (Click). Config: `fpl_cli/config/defaults.yaml`, shipped inside the package and resolved as `SHIPPED_CONFIG_DIR` (`paths.py`), + `settings.yaml` in the user config dir (overrides, deep-merged via `platformdirs`; `~/Library/Application Support/fpl-cli/` on macOS). Repo-root `config/` holds only examples and `team_ratings_overrides.yaml` — nothing there is loaded as defaults. All three writable dirs have env overrides: `FPL_CLI_CONFIG_DIR` (settings, managers, overrides; an optional `fixture_predictions.yaml` here overrides the shipped copy; an optional `previews/` dir holds season preview intel read by `fpl intel`), `FPL_CLI_DATA_DIR` (generated data: team ratings, priors, chip plan, sell prices, the append-only `league_history/` ledger and its rebuildable `league_history_counters/` projection — the ledger partitions by season instead of being discarded at rollover, the one store that outlives a season), `FPL_CLI_CACHE_DIR` (disposable). Overrides must be absolute — a relative one resolves against the cwd, so it is rejected with `UserDirError` rather than silently giving a different dir per invocation. Resolve them by calling `user_config_dir()` / `user_data_dir()` / `user_cache_dir()` at point of use — binding one to a module-level constant freezes the override at import time (before `.env` loads); `tests/test_paths.py` enforces this. Ephemeral environments (Claude Code on the web) must set config + data to a persistent workspace or generated data dies with the container; cache can stay local.
|
|
13
|
+
Entry point: `fpl_cli/cli/__init__.py:main` (Click). Config: `fpl_cli/config/defaults.yaml`, shipped inside the package and resolved as `SHIPPED_CONFIG_DIR` (`paths.py`), + `settings.yaml` in the user config dir (overrides, deep-merged via `platformdirs`; `~/Library/Application Support/fpl-cli/` on macOS). Repo-root `config/` holds only examples and `team_ratings_overrides.yaml` — nothing there is loaded as defaults. All three writable dirs have env overrides: `FPL_CLI_CONFIG_DIR` (settings, managers, overrides; an optional `fixture_predictions.yaml` here overrides the shipped copy; an optional `previews/` dir holds season preview intel read by `fpl intel`), `FPL_CLI_DATA_DIR` (generated data: team ratings, priors, chip plan, sell prices, the returnee radar's `returnee_snapshot.json` week-over-week baseline (season-stamped, discarded on mismatch like `player_prior.yaml`), the append-only `league_history/` ledger and its rebuildable `league_history_counters/` projection — the ledger partitions by season instead of being discarded at rollover, the one store that outlives a season), `FPL_CLI_CACHE_DIR` (disposable). Overrides must be absolute — a relative one resolves against the cwd, so it is rejected with `UserDirError` rather than silently giving a different dir per invocation. Resolve them by calling `user_config_dir()` / `user_data_dir()` / `user_cache_dir()` at point of use — binding one to a module-level constant freezes the override at import time (before `.env` loads); `tests/test_paths.py` enforces this. Ephemeral environments (Claude Code on the web) must set config + data to a persistent workspace or generated data dies with the container; cache can stay local.
|
|
14
14
|
|
|
15
15
|
## Architecture
|
|
16
16
|
Agents inherit `fpl_cli/agents/base.py:Agent`, implement `async run(context: dict | None) -> AgentResult`. Organised in `agents/{data,analysis,action,orchestration}/`. AgentResult statuses: SUCCESS, PARTIAL, FAILED, PENDING_APPROVAL.
|
|
@@ -76,6 +76,7 @@ For a complete inventory of CLI commands, analysis agents, and skills with JSON
|
|
|
76
76
|
- Draft has no captains, no budget, no transfers and no chips — acquisition is via waivers and free agents
|
|
77
77
|
|
|
78
78
|
## Rules
|
|
79
|
+
- This repo is public: never put real manager or league names, entry/league IDs, or FPL account details into commits, PR titles or bodies, issues, or CHANGELOG.md. Use placeholders when quoting generated reports from the configured `reports.output_dir`. Footballer and club names are fine — they are public data. Before pushing, check the branch for a pre-existing leak and flag it rather than pushing over it.
|
|
79
80
|
- Verify before asserting: don't state that a command, file, function, or data point exists without checking first (read the file, run the command, grep for the name)
|
|
80
81
|
- Find-and-replace: review each replacement in context - don't blindly replace substrings in unrelated identifiers
|
|
81
82
|
- Removing/replacing X: new implementation must have zero dependencies on X
|