fplkit 2.3.1__tar.gz → 2.4.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.3.1 → fplkit-2.4.0}/.agents/README.md +1 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/TOOLS.md +21 -16
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/SKILL.md +132 -27
- fplkit-2.4.0/.agents/skills/gw-prep/references/entity-normalisation.md +61 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/references/output-template.md +8 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/references/rules.md +2 -0
- fplkit-2.4.0/.agents/skills/gw-prep/scripts/_bootstrap.py +68 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/scripts/bench_order.py +17 -22
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/scripts/extract_classic_squad.py +7 -1
- fplkit-2.4.0/.agents/skills/gw-prep/scripts/normalise_entities.py +117 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/scripts/starting_xi.py +12 -12
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/scripts/transfer_eval.py +14 -17
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/gw-prep/scripts/validate_draft_waivers.py +10 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/squad-builder/SKILL.md +91 -31
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/squad-builder/references/output-template.md +24 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/squad-builder/references/rules.md +3 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/update-gw-prep/SKILL.md +10 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/AGENTS.md +3 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/CHANGELOG.md +14 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/CLAUDE.md +3 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/PKG-INFO +8 -5
- {fplkit-2.3.1 → fplkit-2.4.0}/README.md +7 -4
- {fplkit-2.3.1 → fplkit-2.4.0}/docs/architecture.md +34 -27
- {fplkit-2.3.1 → fplkit-2.4.0}/docs/command-reference.md +102 -25
- {fplkit-2.3.1 → fplkit-2.4.0}/docs/custom-analysis.md +67 -19
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/_version.py +2 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/action/waiver.py +71 -18
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/stats.py +74 -45
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/transfer_eval.py +54 -20
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/common.py +33 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/data/fixture.py +72 -37
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/orchestration/report.py +6 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/core_insights.py +175 -49
- fplkit-2.4.0/fpl_cli/api/historical.py +188 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/historical_types.py +11 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/providers/__init__.py +5 -0
- fplkit-2.4.0/fpl_cli/api/providers/_http.py +241 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/providers/_models.py +14 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/providers/anthropic.py +9 -32
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/providers/openai_compat.py +8 -35
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/vaastav.py +19 -10
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_context.py +38 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_helpers.py +20 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_league_recap_history.py +13 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_plan_grid.py +6 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_review_analysis.py +20 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_review_classic.py +23 -3
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_review_draft.py +23 -3
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/allocate.py +25 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/chips.py +99 -28
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/differentials.py +12 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/doctor_providers.py +60 -29
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/fdr.py +4 -9
- fplkit-2.4.0/fpl_cli/cli/fixtures.py +210 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/league_recap.py +2 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/player.py +31 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/preview.py +10 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/returnees.py +146 -25
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/review.py +15 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/stats.py +23 -27
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/targets.py +11 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/transfer_eval.py +27 -14
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/waivers.py +12 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/config/defaults.yaml +2 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/player.py +153 -21
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/prompts/league_recap.py +50 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/season.py +17 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/fixture_predictions.py +67 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/league_history_counters.py +27 -27
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/league_history_notes.py +79 -22
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/player_prior.py +146 -8
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/returnee_radar.py +97 -5
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/__init__.py +12 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/constants.py +196 -52
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/data_prep.py +3 -21
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/display.py +10 -8
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/evaluation.py +43 -3
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/ownership.py +125 -23
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/shrinkage.py +4 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/value_quality.py +106 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/squad_allocator.py +15 -31
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/team_ratings.py +130 -9
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/templates/gw_preview.md.j2 +1 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/markdown.py +93 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/scripts/backtest_early_season_prior_blend.py +176 -59
- {fplkit-2.3.1 → fplkit-2.4.0}/scripts/calibrate_quality_ceilings.py +5 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/conftest.py +62 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_action.py +442 -29
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_data.py +322 -41
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_core_insights.py +293 -14
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_historical.py +132 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_vaastav.py +59 -19
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_bench_order_script.py +78 -72
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_classic_squad_extraction.py +2 -21
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_allocate.py +40 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_chips.py +162 -36
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_differentials.py +42 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_doctor_providers.py +54 -17
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_fdr.py +24 -10
- fplkit-2.4.0/tests/test_cli_fixtures.py +360 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_format_suppression.py +3 -9
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_league_recap.py +86 -5
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_plan_grid.py +30 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_player.py +85 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_preview.py +47 -10
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_returnees.py +282 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_review.py +247 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_stats.py +80 -30
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_targets.py +49 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_transfer_eval.py +77 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_waivers.py +43 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_common.py +26 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_data_prep_harmonisation.py +17 -9
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_fixture_predictions.py +85 -0
- fplkit-2.4.0/tests/test_gw_prep_script_bootstrap.py +120 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_league_history_counters.py +1 -1
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_league_history_notes.py +40 -2
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_league_recap.py +40 -3
- fplkit-2.4.0/tests/test_normalise_entities.py +163 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_player_prior.py +154 -1
- fplkit-2.4.0/tests/test_player_resolution.py +315 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_providers.py +360 -25
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_report_agent.py +59 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_returnee_radar.py +158 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_scoring.py +774 -40
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_season.py +18 -0
- fplkit-2.4.0/tests/test_skill_fdr_quality_signals.py +342 -0
- fplkit-2.4.0/tests/test_skill_stats_thresholds.py +75 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_squad_allocator.py +51 -46
- fplkit-2.4.0/tests/test_starting_xi_script.py +99 -0
- fplkit-2.4.0/tests/test_stats.py +472 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_team_ratings.py +112 -22
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_transfer_eval_agent.py +122 -0
- fplkit-2.4.0/tests/test_transfer_eval_script.py +102 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_utils_markdown.py +86 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_validate_draft_waivers.py +3 -17
- fplkit-2.3.1/.agents/skills/gw-prep/scripts/_bootstrap.py +0 -25
- fplkit-2.3.1/fpl_cli/api/historical.py +0 -106
- fplkit-2.3.1/fpl_cli/cli/fixtures.py +0 -138
- fplkit-2.3.1/tests/test_cli_fixtures.py +0 -183
- fplkit-2.3.1/tests/test_player_resolution.py +0 -117
- fplkit-2.3.1/tests/test_stats.py +0 -247
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/create-pr/SKILL.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/preview-ingest/SKILL.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/release/SKILL.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.agents/skills/release-notes/SKILL.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.claude/output-styles/fpl-mate.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.claude/settings.json +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.env.example +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.github/workflows/ci.yml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.github/workflows/pr-title.yml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.github/workflows/release.yml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/.gitignore +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/CONTRIBUTING.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/LICENSE +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/cliff.toml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/config/team_ratings_overrides.yaml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/docs/fpl-rules.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/docs/images/fpl-player-demo-v1-0.png +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/action/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/bench_order.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/captain.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/squad_analyzer.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/analysis/starting_xi.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/base.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/data/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/data/price.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/data/scout.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/agents/orchestration/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/contract.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/dataset_fetcher.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/football_data.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/fpl.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/fpl_draft.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/providers/perplexity.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/api/understat.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_banner.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_fines_config.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_json.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_league_recap_data.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_league_recap_types.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/_review_summarisation.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/captain.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/credentials.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/doctor.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/history.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/init.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/intel.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/league.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/league_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/price_changes.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/price_history.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/ratings.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/sell_prices.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/squad.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/status.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/cli/xg.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/config/fixture_predictions.yaml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/config/previews/EXAMPLE.yaml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/config/team_managers.yaml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/constants.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/chip_plan.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/fixture.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/league_history.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/team.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/models/types.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/parsers/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/parsers/recommendations.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/paths.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/prompts/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/prompts/returnees.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/prompts/review.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/prompts/scout.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/scraper/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/scraper/fpl_prices.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/league_history.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/league_history_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/matchup.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/signals.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/scoring/single_gw.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/season_previews.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/team_form.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/services/team_ratings_prior.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/templates/gw_league_recap.md.j2 +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/templates/gw_review.md.j2 +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/files.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/gameweek.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/teams.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/text.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/fpl_cli/utils/time.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/pyproject.toml +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/pyrightconfig.json +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/requirements.lock +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/scripts/calibrate_promoted_prior.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/__init__.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/fixtures/classic_squad_fixture.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/fixtures/recommendations_fixture.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/fixtures/validate_draft_waivers/recs_clean.md +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/fixtures/validate_draft_waivers/squad_grid.json +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/fixtures/validate_draft_waivers/waivers.json +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/fixtures/waiver_ranked_golden.json +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_analysis.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_analysis_squad.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_base.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_data_price.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_agents_orchestration.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_allocate.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_client.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_dataset_fetcher.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_draft.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_football_data.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_api_understat.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_banner.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_captain.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_credentials.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_custom_analysis.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_doctor.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_fdr_blanks.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_format.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_history.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_init.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_intel.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_json.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_json_contract.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_league.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_league_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_price_changes.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_price_history.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_sell_prices.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_settings_warning.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_squad.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_status.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_warnings_stderr.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_cli_xg.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_common_scoring.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_doc_consistency.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_fines_config.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_fpl_prices.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_init.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_init_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_league_history_fines.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_league_history_store.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_matchup_service.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_models.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_paths.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_preseason.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_recommendations_parser.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_report_paths.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_review.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_rolling_pts_per_m.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_scraper.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_season_awareness.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_season_previews.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_sell_prices.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_starting_xi_agent.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_team_ratings_prior.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_utils_gameweek.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_utils_text.py +0 -0
- {fplkit-2.3.1 → fplkit-2.4.0}/tests/test_utils_time.py +0 -0
|
@@ -14,6 +14,7 @@ Skills are showcase examples - they work out of the box but are designed to be c
|
|
|
14
14
|
|
|
15
15
|
- `<!-- ADAPT: ... -->` comments mark sections you'll want to change for your setup
|
|
16
16
|
- Output paths use `[YOUR_OUTPUT_DIR]` placeholders - replace with your preferred location
|
|
17
|
+
- Script invocations use a `[YOUR_PYTHON]` placeholder - replace with the interpreter fpl-cli is installed on. Helper scripts import the `fpl_cli` package, which a standalone `fpl` on `PATH` (uv tool, pipx) does not provide; get it wrong and they exit 1 with a JSON error envelope naming the cause
|
|
17
18
|
- Supplementary data sources (newsletters, external reports) are noted but not required
|
|
18
19
|
- All CLI data gathering uses `--format json` for structured output
|
|
19
20
|
|
|
@@ -31,28 +31,28 @@ in `error`, not on stderr. Full contract:
|
|
|
31
31
|
| `init` | Set up fpl-cli with your FPL IDs and optional features | No | General | No | direct-api |
|
|
32
32
|
| `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 |
|
|
33
33
|
| `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 and that the Core-Insights per-gameweek files parse to records through the parsers the scoring commands use (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 |
|
|
34
|
-
| `fixtures` | Show fixtures for a gameweek | Yes | General | No | direct-api |
|
|
35
|
-
| `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 |
|
|
36
|
-
| `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`. Before
|
|
34
|
+
| `fixtures` | Show fixtures for a gameweek. With custom analysis on, FDR is the venue-aware ATK/DEF mean `fpl fdr` and `fpl preview` show, so one match reads the same on all three; `-m/--mode` picks `difference` (default) or `opponent`, an unrated club scores a neutral 4.0 rather than the FPL API's 1-5 difficulty, and when the ratings cannot support difficulty at all, table mode warns on stderr and JSON adds a `team_ratings_unusable` entry to `metadata.warnings` (always present, empty or not). With it off, the raw FPL API 1-5 difficulty, and an explicitly passed `-m` reports on stderr that it does not apply. JSON `metadata` carries `custom_analysis`, `fdr_scale` (`team_ratings_1_7` / `fpl_api_1_5`) and `fdr_mode` (null when it does not apply) | Yes | General | No | direct-api |
|
|
35
|
+
| `player` | Look up a player's stats, xG, ownership and fixture run. Before GW10 `quality_score` blends last season's pedigree in and JSON `metadata.warnings` says so (`early_season_prior_informed`, or `early_season_small_sample` when history could not be loaded), same as `fpl stats --value`. 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 |
|
|
36
|
+
| `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`. Before GW10, `--value` quality scores blend last season's pts/90 pedigree with this season's observation and the command says so (stderr notice quoting the observation's weight band; JSON `metadata.warnings` code `early_season_prior_informed`); when last season's history cannot be loaded they are pure observation and the notice says that instead (`early_season_small_sample`, before GW6). GK ceilings scale with the calendar-possible sample, reaching full scale at GW6. 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 |
|
|
37
37
|
| `history` | Show historical player performance across seasons | Yes | General | No | direct-api |
|
|
38
38
|
| `league` | Show live league standings for Classic and Draft leagues | No | General | No | direct-api |
|
|
39
|
-
| `fdr` | Analyse fixture difficulty - easy runs, blanks, doubles. `--blanks` mixes confirmed fixtures with predictions; when those predictions are stale, table mode warns on stderr and JSON adds a `fixture_predictions_stale` entry to `metadata.warnings` | Yes | General | No | via-agent |
|
|
39
|
+
| `fdr` | Analyse fixture difficulty - easy runs, blanks, doubles. Three payloads, not one: the default call runs the agent and returns the full analysis plus `data.ratings_warning` (the team-ratings quality verdict, and with `--my-squad` also `data.predictions_stale` / `data.prediction_warnings` / `data.squad_exposure`); with custom analysis off it returns raw 1-5 FPL API difficulty (`data.easy_fixture_runs` only, `metadata.custom_analysis: false`, no ratings and so no warning); `--blanks` bypasses the agent for a schedule-only payload carrying neither the FDR analysis nor `ratings_warning`. `--blanks` mixes confirmed fixtures with predictions; when those predictions are stale, table mode warns on stderr and JSON adds a `fixture_predictions_stale` entry to `metadata.warnings` | Yes | General | No | via-agent |
|
|
40
40
|
| `xg` | Analyse underlying stats: xG, xA, overperformers | Yes | General | No | via-agent |
|
|
41
41
|
| `price-changes` | Show price changes and transfer activity | No | General | No | via-agent |
|
|
42
42
|
| `price-history` | Show price trajectory and transfer momentum | Yes | General | No | direct-api |
|
|
43
|
-
| `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
|
|
43
|
+
| `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`/`_rate_limited` (true when the search provider rate-limited the run even after retrying — the one failure a re-run is likely to clear); `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 |
|
|
44
44
|
| `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 |
|
|
45
45
|
| `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 |
|
|
46
46
|
| `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). A rebuild keeps the name, club and position already recorded for each player instead of restamping them from today's bootstrap, and never drops a code the gameweek already resolved; it reads the earliest recorded row rather than the newest, so a gameweek an older version already restamped is repaired rather than re-copied. Everything the gameweek itself answers (points, cards, pick flags, blank status) is still re-derived, and a `league_history_identity_carried` warning names how many players were kept. 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` (always present; the partition directory on a first capture, `null` after). Fines are ruled *and recorded* against every captured gameweek, backfilled ones included: the detailed replay rules every configured rule, the coarse tier rules only what cohort points support (`last-place`, `below-threshold`) and records that `red-card` went unruled. Season totals print under `Season Fines` on console and as a `# Season Fines` report section, but only at the two milestone gameweeks (GW19 and the finale) - every other week those surfaces stay a this-week view and `league-fines` answers the season question on demand. The `## Season Fines` prompt section and `metadata.season_fines` are ungated: the editorial gets the running totals every week as optional colour (never a required beat), and a scripted consumer sees no calendar-driven appearance and disappearance. Bottom-half gameweeks and green-arrow droughts no longer surface as streaks at all (their run only drives the season count). Streak conditions also keep reset-surviving season occurrence counts ("their sixth gameweek win of the season"), each surfaced on its own rule (see [Command Reference](../docs/command-reference.md#league-recap) for the per-count table): wins and last places on multiples of 3 plus any second-half first, captain blanks and waiver hauls/backfires on multiples of 5, hits on multiples of 3, weeks on top on multiples of 5, bottom-half gameweeks on multiples of 10 in the second half only, and green-arrow droughts on an unbroken run of 5 or 10 (gameweeks spent leading the league never count towards a drought — the leader could not have climbed). Some counts carry same-week peers alongside the milestone — past a fixed floor for counts that stay small, or within a relative window for counts that climb all season. A firing count reaches the report's `## Season Counts` subsection and the editorial's League History section; at the two milestone gameweeks both surfaces carry every nonzero count as the season set-piece. Every line states its span and any not-judged gameweeks beside the number, and every nonzero count is emitted in `metadata.notes_pack.season_count_entries` regardless. `--summarise` with no usable synthesis key no longer aborts the run: the recap, report and ledger capture all proceed, and a `synthesis_provider_unavailable` warning says the editorial was skipped | Yes | General | No | via-agent |
|
|
47
47
|
| `league-fines` | Show who has been fined this season and how much of it was actually ruled, folded out of the league history ledger. Reads local files only - no network - so it works for any season still on disk (`--season 2025-26`). Nothing is re-ruled: a fine is counted exactly as its gameweek recorded it. Counts, never money (`penalty` is free text; settlement and escalation are not modelled). Every gameweek that could not be ruled is named beneath the table - never captured, unreadable, captured before rulings were recorded, captured with no rules configured, or captured at a fidelity that could not rule a given rule. `--format json` emits one entry per recorded manager, fined or not, each with `counts` (zero included), `total`, `fined_gameweeks`, `ruled_gameweeks`, `unruled_gameweeks`, `first_recorded_gameweek`, `last_recorded_gameweek` and `is_fully_ruled`; `metadata` carries `season`, `fpl_format`, `league_id`, `gameweek`, `start_gameweek`, `rule_types`, `total_fines` and `qualifiers`. Exits 1 with the shared error envelope when no league id is configured for the format | Yes | General | No | direct-api |
|
|
48
48
|
| `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 |
|
|
49
|
-
| `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
|
|
50
|
-
| `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
|
|
51
|
-
| `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
|
|
52
|
-
| `allocate` | Select mathematically optimal 15-player squad within budget.
|
|
53
|
-
| `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
|
|
49
|
+
| `differentials` | Find differential picks - high potential, low ownership. Inverted consistency bonus (volatile players score higher, phased in GW6-10). Before GW10 the score's quality baseline carries the prior blend (last season's pedigree weighted by confidence) rather than position-mean shrinkage, so a quiet-starting elite is not ranked below a one-game wonder; the ownership, matchup and consistency bonuses stay pure observation; JSON `metadata.warnings` carries the early-season notice while the blend is live (`early_season_prior_informed`, or `early_season_small_sample` when history could not be loaded), table mode prints it to stderr. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the blend rather than handed last season's standing | Yes | Classic | Yes | via-agent |
|
|
50
|
+
| `targets` | Find transfer targets - high performers across all ownership. Consistency bonus (CV-xGI percentile, phased in GW6-10). Before GW10 the score's quality baseline carries the prior blend (last season's pedigree weighted by confidence) rather than position-mean shrinkage, so a quiet-starting elite is not ranked below a one-game wonder; the matchup and consistency bonuses stay pure observation, and the score agrees with `fpl transfer-eval`'s Outlook column; JSON `metadata.warnings` carries the early-season notice while the blend is live (`early_season_prior_informed`, or `early_season_small_sample` when history could not be loaded), table mode prints it to stderr. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the blend rather than handed last season's standing | Yes | Classic | Yes | via-agent |
|
|
51
|
+
| `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. Before GW10 `quality_score` carries the value family's prior blend (matching `fpl player` and `fpl stats --value`) and `target_score` the target family's (matching `fpl targets`); the single-GW `lineup_score` keeps position-mean shrinkage. JSON `metadata.warnings` carries the early-season notice naming both blended columns, or only `target_score` when no Understat match produced a quality score. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of whichever device applies, rather than pulled toward the position mean or handed last season's standing. A name two players answer to exactly (`Henderson`) exits 1 with an `error` envelope naming both, rather than resolving to whichever the API lists first; `Name (TEAM)` or a player ID picks one | Yes | General | Yes | via-agent |
|
|
52
|
+
| `allocate` | Select mathematically optimal 15-player squad within budget. At `--horizon >= 2` the solver ranks on the value family's GW1-9 prior blend (last season's pedigree weighted by confidence); players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of it rather than handed last season's standing, and stay in the solver pool; JSON `metadata.warnings` carries the early-season notice at `--horizon >= 2` (`early_season_prior_informed`, or `early_season_small_sample` when history could not be loaded) | Yes | Classic | Yes | direct-api |
|
|
53
|
+
| `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. Keeper scores carry the GK signal block (saves/90, defensive quality, clean-sheet rate), read from the main-game player each draft element is joined to on `code` because the draft bootstrap publishes none of it, so draft keepers rank on what separates them rather than on form and ppg alone. Before GW10 the score's quality baseline carries the prior blend (last season's pedigree weighted by confidence) rather than position-mean shrinkage; the matchup, position-need, team-stacking and consistency terms stay pure observation; JSON `metadata.warnings` carries the early-season notice while the blend is live, and reports `early_season_small_sample` both when history could not be loaded and when no draft element found a main-game counterpart to take a prior from. Players ruled out of the gameweek (0% chance, or no minutes from GW6) are held out of the blend rather than handed last season's standing | Yes | Draft | Yes | via-agent |
|
|
54
54
|
| `squad` | Analyse your FPL squad health and fixtures. Exits 1 with an `error` envelope when no entry ID is configured for the active format | Yes | General | No | via-agent |
|
|
55
|
-
| `squad grid` | Show squad fixture difficulty grid. Exits 1 with an `error` envelope when no entry ID is configured for the active format, or when the squad fetch fails | Yes | General | No | via-agent |
|
|
55
|
+
| `squad grid` | Show squad fixture difficulty grid. Exits 1 with an `error` envelope when no entry ID is configured for the active format, or when the squad fetch fails. A `--watch` name that does not resolve is non-fatal either way: unmatched, or matching two players exactly, it warns on stderr and is skipped, and the grid still renders | Yes | General | No | via-agent |
|
|
56
56
|
| `squad sell-prices` | Show squad sell prices and financial breakdown. Nothing cached and no `--refresh`, a failed scrape, draft format, or a scrape the extraction heuristics distrust each exit 1 with an `error` envelope under `--format json`; table mode still renders a suspect scrape under a `Squad Budget (Suspect)` panel, since a reader can judge it and `fpl allocate --sell-prices` cannot | Yes | Classic | No | direct-api |
|
|
57
57
|
| `chips` | View and plan FPL chip usage | Yes | Classic | No | direct-api |
|
|
58
58
|
| `chips timing` | Recommend chip timing based on blank/double GW exposure | Yes | Classic | No | via-agent |
|
|
@@ -85,9 +85,9 @@ Skills write their own outputs (`gw{N}-recommendations.md`, `gw{N}-squad-builder
|
|
|
85
85
|
|
|
86
86
|
| Skill | Path | Purpose | Compatibility |
|
|
87
87
|
|-------|------|---------|--------------|
|
|
88
|
-
| 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 |
|
|
88
|
+
| gw-prep | `skills/gw-prep/` | Gameweek preparation recommendations for classic and draft (embed / rederive / transfer branches; Phase B1 fixture difficulty + rating-quality caveats; Phase B9 preview intel; Phase B10 injury returnee radar; Phase E post-write validation) | Full: Claude Code. Partial: Codex, Cursor, Copilot |
|
|
89
89
|
| update-gw-prep | `skills/update-gw-prep/` | Append GW update to existing recommendations | Full: Claude Code, Codex, Cursor, Copilot |
|
|
90
|
-
| 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 |
|
|
90
|
+
| squad-builder | `skills/squad-builder/` | Build optimal 15-player squad (wildcard, free hit, season start; Phase B fixture difficulty + rating-quality caveats; Phase B3 preview intel gate) | Full: Claude Code, Codex, Cursor, Copilot |
|
|
91
91
|
| 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 |
|
|
92
92
|
| release-notes | `skills/release-notes/` | Draft release notes and suggest the next semver (read-only preview; never tags or publishes) | Full: Claude Code, Codex, Cursor, Copilot |
|
|
93
93
|
| release | `skills/release/` | Cut a release end-to-end: preflight, notes via release-notes, approval gate, publish GitHub release (tag drives PyPI publish + changelog automation) | Full: Claude Code (local), Codex, Cursor, Copilot. Partial: Claude Code web (publish step handed to user) |
|
|
@@ -113,9 +113,14 @@ Python classes in `fpl_cli/agents/` that implement `async run(context) -> AgentR
|
|
|
113
113
|
|
|
114
114
|
**Notes:**
|
|
115
115
|
- BenchOrderAgent and StartingXIAgent have no CLI command - they are invoked by gw-prep skill wrapper scripts in `.agents/skills/gw-prep/scripts/`
|
|
116
|
-
- Every scoring family holds players known not to be playing out of
|
|
116
|
+
- Every scoring family holds players known not to be playing out of its GW1-9 early-season device — the prior blend for the value family (`quality_score`, `fpl allocate`) and the ownership family (`targets`, `differentials`, `waivers`, transfer-eval's Outlook), position-mean shrinkage for the single-GW family (`captain`, `bench-order`, `starting-xi`, transfer-eval's This GW); bench and lineup order on raw scores, so for those two it changes the displayed score only
|
|
117
117
|
- TransferEvalAgent is used by both `transfer-eval` CLI command and gw-prep skill
|
|
118
|
+
- `bench_order.py`, `starting_xi.py` and `transfer_eval.py` (`.agents/skills/gw-prep/scripts/`) take player names, accept the `Name (TEAM)` disambiguator, and exit 1 naming both candidates when a name matches two players exactly (Dean and Jordan Henderson are both `Henderson`) rather than resolving to whichever the API lists first. Pass `Name (CLUB)` from the squad grid's `player`/`team` pair (issue #180). All three share that resolution with the `transfer-eval` CLI command through `resolve_player_or_report` / `resolve_players_or_report` (`fpl_cli/models/player.py`)
|
|
118
119
|
- `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.
|
|
119
120
|
- 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`)
|
|
120
|
-
- `
|
|
121
|
-
-
|
|
121
|
+
- gw-prep Phase B1 and squad-builder Phase B each run the `fpl fdr` analysis call alongside `fpl fdr --blanks --format json`, and read the quality signals both return -- `data.ratings_warning`, `metadata.custom_analysis`, the blanks payload's `metadata.warnings`, plus `data.predictions_stale` / `data.prediction_warnings` where `--my-squad` resolved a squad. `--blanks` bypasses the fixture agent for a schedule-only payload carrying none of the analysis and none of those fields, so the skills that issued only it were promising pFDR they never received (gw-prep's pFDR Overview, squad-builder's per-position `{ATK|DEF} column` blocks) and silently accepting whatever rating quality they got (issue #135). Whatever fires becomes `data_caveat`, inlined into every sub-agent prompt and written to the output's Data Quality section, messages quoted verbatim so the remedy they name (`fpl ratings update`, `fpl init`) survives. gw-prep passes `--my-squad` (it also wants squad blank/double exposure); squad-builder deliberately does not, since it is replacing the 15 and its season-start modes have no squad to resolve. Enforced by `tests/test_skill_fdr_quality_signals.py`
|
|
122
|
+
- gw-prep Phase B8 and squad-builder Phase B derive the `fpl stats --min-minutes` floor from the current gameweek (`min(cap, (N - 1) * 45)`, caps 315/450) rather than hardcoding it. A fixed floor is unreachable until GW8/GW11 and empties the result silently -- `fpl stats` returns a well-formed envelope with `"data": []`. Both also rank on `ep_next` before GW6 -- the positional sorts (xGI, form, total_points, ppg) order mostly on sample that early -- and label each block with the sort and floor it used, so a downstream agent reads the ordering as a projection rather than as observed output; an empty block is annotated rather than left bare. Enforced by `tests/test_skill_stats_thresholds.py`
|
|
123
|
+
- `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; exits 0 whatever the validation finds — the one non-zero exit is the startup guard below.
|
|
124
|
+
- `normalise_entities.py` (`.agents/skills/gw-prep/scripts/`) — decodes HTML-escaped text in a report assembled from sub-agent output and reports the entity references it deliberately leaves alone. JSON stdout; rewrites its `--file` in place; shared by gw-prep Phase D, squad-builder Phase E and update-gw-prep Phase D, all of which warn rather than block on its findings. Contract and warn posture live once, in `.agents/skills/gw-prep/references/entity-normalisation.md`.
|
|
125
|
+
- Every script under `.agents/skills/gw-prep/scripts/` imports the `fpl_cli` package, so it runs on the interpreter fpl-cli is installed on — gw-prep's SKILL.md writes that interpreter as `[YOUR_PYTHON]` (squad-builder and update-gw-prep still spell their `normalise_entities.py` call `python3`, correct only under an activated venv). A standalone `fpl` on `PATH` (uv tool, pipx) provides the command but not the package; `_bootstrap.py` catches that case ahead of the fpl_cli imports and exits 1 with `{"error": true, "messages": [...]}` on stdout, which the phases parsing that stdout surface as a warning instead of a traceback
|
|
126
|
+
- The extractor and the waiver validator 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.
|
|
@@ -25,6 +25,11 @@ Generate transfer/waiver recommendations and squad analysis for the upcoming FPL
|
|
|
25
25
|
cd "$FPL_CLI_DIR" && source .venv/bin/activate
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
<!-- ADAPT: Set your interpreter. `[YOUR_PYTHON]` below is the Python that has fpl-cli installed -- `python3` once the venv above is active, or an absolute path to that venv's binary (e.g. `$FPL_CLI_DIR/.venv/bin/python`) if you invoke the skill without activating it. -->
|
|
29
|
+
**Every script under `scripts/` imports the `fpl_cli` package**, so each must run on the interpreter fpl-cli is installed on. Activate its venv first, or invoke that venv's Python directly. A standalone `fpl` on `PATH` (uv tool, pipx) puts the *command* there but not `fpl_cli` on the system interpreter's import path -- `fpl status` succeeds and `python3 scripts/transfer_eval.py` still dies with `ModuleNotFoundError: No module named 'fpl_cli'`. The script invocations below write this interpreter as `[YOUR_PYTHON]`; substitute it the same way you substitute `[YOUR_OUTPUT_DIR]`.
|
|
30
|
+
|
|
31
|
+
Get it wrong and the scripts say so: each one exits 1 with `{"error": true, "messages": [...]}` on stdout naming the missing package, rather than a traceback.
|
|
32
|
+
|
|
28
33
|
## Execution Strategy
|
|
29
34
|
|
|
30
35
|
**Claude Code:** Launch Phase C sub-agents in parallel (classic + draft simultaneously) using the `Agent tool parameters:` blocks shown in Phase C.
|
|
@@ -106,7 +111,7 @@ Locate a matching squad-builder output file and extract the Classic Squad block
|
|
|
106
111
|
5. Call the extraction helper:
|
|
107
112
|
|
|
108
113
|
```bash
|
|
109
|
-
|
|
114
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/extract_classic_squad.py" --file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-squad-builder.md"
|
|
110
115
|
```
|
|
111
116
|
|
|
112
117
|
Parse stdout as JSON regardless of exit code. If JSON parse fails, treat as extraction-failed with a generic error message.
|
|
@@ -128,13 +133,43 @@ Run all applicable commands below. Every command uses `--format json`. Skip comm
|
|
|
128
133
|
|
|
129
134
|
**Reference preload (every run):** before issuing the CLI commands, read the full contents of `${CLAUDE_SKILL_DIR}/references/rules.md` and `${CLAUDE_SKILL_DIR}/references/output-template.md` into memory. These are inlined verbatim into each Phase C prompt as `{rules_content}` and `{output_template_content}` — sub-agents cannot reach the skill directory on their own, so the orchestrator must pass the text through. If either file is missing, abort with an error naming the expected path.
|
|
130
135
|
|
|
131
|
-
### B1 -- Fixture Difficulty
|
|
136
|
+
### B1 -- Fixture Difficulty and Rating Quality
|
|
137
|
+
|
|
138
|
+
Two commands, because `--blanks` is a schedule-only path: it returns the confirmed and predicted BGW/DGW lists and nothing else -- no `fdr_by_team`, no positional ATK/DEF split, no `ratings_warning`. The pFDR analysis `references/rules.md` asks Phase C sub-agents to perform, and the rating-quality verdict below, both live in the first call.
|
|
132
139
|
|
|
133
140
|
```bash
|
|
141
|
+
fpl fdr --my-squad --format json
|
|
134
142
|
fpl fdr --blanks --format json
|
|
135
143
|
```
|
|
136
144
|
|
|
137
|
-
|
|
145
|
+
If format is `"both"`, also run:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
fpl fdr --my-squad --draft --format json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Store the analysis as `pfdr`, the schedule as `blanks_schedule` and the draft variant as `pfdr_draft`. Inline them under the pFDR lines of every Phase C prompt.
|
|
152
|
+
|
|
153
|
+
`--my-squad` earns three fields the plain call omits: `data.squad_exposure` (which of your own players blank or double inside the window) and the two prediction-quality fields below, all of which the command fills in only once a squad resolves. It auto-selects the draft squad in draft-only mode and the classic squad under `"both"`, which is why the draft variant above is a separate run. Where no entry ID is configured, or no squad has been submitted yet, the command says so on stderr and returns the fixture analysis without those three fields; that is a normal state, not a failure, so carry on with what came back.
|
|
154
|
+
|
|
155
|
+
**Read the quality signals before handing the payload to anyone.** Fixture difficulty is only as good as the team ratings underneath it, and the ways those go wrong are mostly invisible in the output: ratings left over from last season, no ratings at all, ratings that still name relegated clubs, ratings that fail to separate any two teams, pre-season estimates standing in for results. Every one of them yields a pFDR table that looks like ordinary analysis. `fpl fdr` detects all of them and hands the verdict over in the payload -- unread, each run silently accepts whatever rating quality it happened to get.
|
|
156
|
+
|
|
157
|
+
| Payload | Field | Fires when |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `pfdr` | `metadata.custom_analysis` | `false` -- custom analysis is off, so this is raw 1-5 FPL API difficulty (`data.easy_fixture_runs` only): no ATK/DEF split, no team ratings, and therefore no `ratings_warning` to read. Remedy is `fpl init` |
|
|
160
|
+
| `pfdr` | `data.ratings_warning` | non-null |
|
|
161
|
+
| `pfdr` | `data.predictions_stale` | `true` |
|
|
162
|
+
| `pfdr` | `data.prediction_warnings` | non-empty (one entry per prediction file skipped as unreadable, malformed or empty -- a broken user override falling back silently) |
|
|
163
|
+
| `blanks_schedule` | `metadata.warnings` | non-empty; `fixture_predictions_stale` is the code this command raises |
|
|
164
|
+
|
|
165
|
+
**Set `data_caveat` for Phase C** -- one `- ` bullet per signal that fired, empty when none did. **Quote each message verbatim.** They already name their own remedy (`fpl ratings update`, `fpl init`); a paraphrase drops it, and the remedy is the only part the user can act on.
|
|
166
|
+
|
|
167
|
+
> **Data quality:** the fixture ratings behind this run carry warnings:
|
|
168
|
+
> {one `- ` bullet per fired signal, message quoted verbatim}
|
|
169
|
+
> Weight fixture-difficulty reasoning down accordingly. With a ratings warning live, treat pFDR as
|
|
170
|
+
> indicative rather than decisive and lead on form, minutes and preview intel instead.
|
|
171
|
+
|
|
172
|
+
`data_caveat` goes into every Phase C prompt and into the report's `## Notes` section in Phase D, so a reader can see why the recommendations leaned away from fixtures.
|
|
138
173
|
|
|
139
174
|
### B2 -- Captain Candidates (classic only - skip if format is "draft")
|
|
140
175
|
|
|
@@ -160,6 +195,12 @@ If format is `"both"`, also run:
|
|
|
160
195
|
fpl squad grid --draft --format json
|
|
161
196
|
```
|
|
162
197
|
|
|
198
|
+
Each record carries `player` and `team`. Keep the pair together: the C2.5/C3/C4
|
|
199
|
+
scripts below take player names, and a bare surname that two players share
|
|
200
|
+
(Dean and Jordan Henderson) is rejected as ambiguous rather than resolved to
|
|
201
|
+
whichever the API lists first. Pass `"{player} ({team})"` -- e.g.
|
|
202
|
+
`"Henderson (CRY)"` -- for every name sourced from this output.
|
|
203
|
+
|
|
163
204
|
### B5 -- Price Movements
|
|
164
205
|
|
|
165
206
|
```bash
|
|
@@ -184,24 +225,59 @@ fpl player "{player_name}" --format json
|
|
|
184
225
|
|
|
185
226
|
Run the five differentiated queries below in parallel. Each targets a distinct transfer-signal surface that downstream Phase C analysis depends on; a single generic `fpl stats` call sorts by total points and misses most of them.
|
|
186
227
|
|
|
228
|
+
**Derive the minutes floor from `N` before issuing them.** After `N-1` completed gameweeks no player can have logged more than `(N - 1) * 90` minutes, so a fixed floor is arithmetically unreachable in August: it filters out every player and returns a well-formed envelope carrying `"data": []`, which is indistinguishable from "nobody qualifies". Scale the bar with the calendar instead:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
mins_form = min(315, (N - 1) * 45) # cap reached at GW8
|
|
232
|
+
mins_pos = min(450, (N - 1) * 45) # cap reached at GW11
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`(N - 1) * 45` is half the minutes the season has made possible so far. At `N == 1` both are `0` -- the CLI default, which filters nothing. Once the caps bind, behaviour is identical to a fixed threshold.
|
|
236
|
+
|
|
237
|
+
**Before GW6 (`N <= 5`), rank on `ep_next`.** `form` and `expected_goal_involvements` over one or two matches are near-noise and `clean_sheets` is a 0/1 count, so ranking on them early sorts mostly on sample. `ep_next` is FPL's own prior-informed projection for the coming gameweek and is what `fpl stats -v` recommends in this same window. Nothing is lost by the swap: every record carries `form`, `expected_goal_involvements`, `clean_sheets` and `ep_next` whatever the sort, so only the ordering changes.
|
|
238
|
+
|
|
239
|
+
| Placeholder | `N <= 5` | `N >= 6` |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| `{rank_form}` | `ep_next` | `form` |
|
|
242
|
+
| `{rank_mid}` | `ep_next` | `expected_goal_involvements` |
|
|
243
|
+
| `{rank_fwd}` | `ep_next` | `expected_goal_involvements` |
|
|
244
|
+
| `{rank_def}` | `ep_next` | `clean_sheets` |
|
|
245
|
+
|
|
187
246
|
```bash
|
|
188
|
-
#
|
|
189
|
-
fpl stats -s
|
|
247
|
+
# Leaders across positions (minutes-filtered, available)
|
|
248
|
+
fpl stats -s {rank_form} --min-minutes {mins_form} -n 15 --available-only --format json
|
|
190
249
|
|
|
191
|
-
# Transfer momentum (who the market is chasing this week)
|
|
250
|
+
# Transfer momentum (who the market is chasing this week - valid from GW1, no floor)
|
|
192
251
|
fpl stats -s transfers_in_event -n 15 --format json
|
|
193
252
|
|
|
194
|
-
# MID
|
|
195
|
-
fpl stats -p MID -s
|
|
253
|
+
# MID leaders (attacking mids, hauls signal)
|
|
254
|
+
fpl stats -p MID -s {rank_mid} --min-minutes {mins_pos} -n 15 --available-only --format json
|
|
196
255
|
|
|
197
|
-
# FWD
|
|
198
|
-
fpl stats -p FWD -s
|
|
256
|
+
# FWD leaders (strikers, hauls signal)
|
|
257
|
+
fpl stats -p FWD -s {rank_fwd} --min-minutes {mins_pos} -n 10 --available-only --format json
|
|
199
258
|
|
|
200
|
-
# DEF
|
|
201
|
-
fpl stats -p DEF -s
|
|
259
|
+
# DEF leaders (defensive picks, clean-sheet signal)
|
|
260
|
+
fpl stats -p DEF -s {rank_def} --min-minutes {mins_pos} -n 10 --available-only --format json
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Store each result under a distinct key (`stats_form`, `stats_transfer_momentum`, `stats_mid_xgi`, `stats_fwd_xgi`, `stats_def_clean_sheets`) and inline them into Phase C prompts as labelled sections. **Label each block with the sort and floor it actually used**, not with the key name -- `=== fpl stats: MID leaders (sort=ep_next, min-minutes=45) ===` -- because pre-GW6 the sort is not the one the key implies.
|
|
264
|
+
|
|
265
|
+
**An empty block must say why it is empty.** Phase C inlines these blocks verbatim, and a bare `"data": []` gives a sub-agent nothing to distinguish "no player clears the bar" from "the query was malformed", so it can reason from the gap or fill it from weaker sources. Annotate it in the label instead:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
=== fpl stats: DEF leaders — EMPTY (sort=clean_sheets, min-minutes=450): no available player clears the floor at GW{N} ===
|
|
202
269
|
```
|
|
203
270
|
|
|
204
|
-
|
|
271
|
+
With the floor scaled to `N` this is now a real finding rather than an artefact of the arguments, which is exactly why it is worth stating.
|
|
272
|
+
|
|
273
|
+
**Set `stats_caveat` for Phase C.** When `N <= 5`, it is the following line; when `N >= 6` it is empty and the line carrying it is omitted from every Phase C prompt.
|
|
274
|
+
|
|
275
|
+
> **Early-season caveat:** these blocks are ranked on `ep_next` (FPL's projection), not on observed
|
|
276
|
+
> output. The `form`, `expected_goal_involvements` and `clean_sheets` values inside each record come
|
|
277
|
+
> from {N-1} match(es) and are near-noise -- a hot cameo out-reads a quiet elite. The minutes floor
|
|
278
|
+
> is scaled to the gameweek ({mins_form}/{mins_pos} minutes), so these lists admit players a
|
|
279
|
+
> full-season floor would exclude. Weight prior-season pedigree, role and fixtures above these
|
|
280
|
+
> numbers, and do not present a one-match form figure as a trend.
|
|
205
281
|
|
|
206
282
|
### B9 -- Season Preview Intel
|
|
207
283
|
|
|
@@ -247,7 +323,10 @@ watchlist and why.
|
|
|
247
323
|
`--enrich` searches the web for fresher return timing on the players FPL's own news field is silent
|
|
248
324
|
or stale about, which is what supplies a date for the injured majority. It needs a Perplexity API
|
|
249
325
|
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.
|
|
326
|
+
returns the FPL-sourced watchlist -- so this step is never a reason to abort the run. If
|
|
327
|
+
`metadata.enrichment_rate_limited` is true the provider rate-limited the run even after the command's
|
|
328
|
+
own retries; the answers it did get are cached, so re-run the command once before settling for the
|
|
329
|
+
partial payload.
|
|
251
330
|
|
|
252
331
|
Store the payload as `returnee_radar` and inline it into both Phase C prompts as a labelled
|
|
253
332
|
section. C1 renders it as an informational watchlist and is barred from naming its players in
|
|
@@ -301,12 +380,15 @@ Branch on `squad_builder_result` (set in Phase A3; unset on transfer weeks):
|
|
|
301
380
|
> **Data (JSON):**
|
|
302
381
|
> - Status: {A1 output}
|
|
303
382
|
> - Chips: {A1.5 output}
|
|
304
|
-
> - pFDR: {B1
|
|
383
|
+
> - pFDR: {pfdr} (from B1 - fixture-difficulty analysis; add {pfdr_draft} on `both` runs)
|
|
384
|
+
> - Blank/double schedule: {blanks_schedule} (from B1)
|
|
385
|
+
> {data_caveat} (from B1 - omit these lines entirely when `data_caveat` is empty)
|
|
305
386
|
> - Captain candidates: {B2 output}
|
|
306
387
|
> - Squad: {B4 output}
|
|
307
388
|
> - Price movements: {B5 output}
|
|
308
389
|
> - Chip timing: {B6 output}
|
|
309
390
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
391
|
+
> {stats_caveat} (from B8 - omit this line entirely when `stats_caveat` is empty)
|
|
310
392
|
>
|
|
311
393
|
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
312
394
|
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
@@ -379,12 +461,15 @@ Proceed immediately (non-interactive).
|
|
|
379
461
|
> **Data (JSON):**
|
|
380
462
|
> - Status: {A1 output}
|
|
381
463
|
> - Chips: {A1.5 output}
|
|
382
|
-
> - pFDR: {B1
|
|
464
|
+
> - pFDR: {pfdr} (from B1 - fixture-difficulty analysis; add {pfdr_draft} on `both` runs)
|
|
465
|
+
> - Blank/double schedule: {blanks_schedule} (from B1)
|
|
466
|
+
> {data_caveat} (from B1 - omit these lines entirely when `data_caveat` is empty)
|
|
383
467
|
> - Captain candidates: {B2 output}
|
|
384
468
|
> - Squad: {B4 output}
|
|
385
469
|
> - Price movements: {B5 output}
|
|
386
470
|
> - Chip timing: {B6 output}
|
|
387
471
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
472
|
+
> {stats_caveat} (from B8 - omit this line entirely when `stats_caveat` is empty)
|
|
388
473
|
>
|
|
389
474
|
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
390
475
|
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
@@ -425,12 +510,15 @@ Proceed immediately (non-interactive).
|
|
|
425
510
|
> **Data (JSON):**
|
|
426
511
|
> - Status: {A1 output}
|
|
427
512
|
> - Chips: {A1.5 output}
|
|
428
|
-
> - pFDR: {B1
|
|
513
|
+
> - pFDR: {pfdr} (from B1 - fixture-difficulty analysis; add {pfdr_draft} on `both` runs)
|
|
514
|
+
> - Blank/double schedule: {blanks_schedule} (from B1)
|
|
515
|
+
> {data_caveat} (from B1 - omit these lines entirely when `data_caveat` is empty)
|
|
429
516
|
> - Captain candidates: {B2 output}
|
|
430
517
|
> - Squad: {B4 output}
|
|
431
518
|
> - Price movements: {B5 output}
|
|
432
519
|
> - Chip timing: {B6 output}
|
|
433
|
-
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
520
|
+
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
521
|
+
> {stats_caveat} (from B8 - omit this line entirely when `stats_caveat` is empty)
|
|
434
522
|
> - Returning soon: {returnee_radar} (from B10)
|
|
435
523
|
> **Informational only.** Render it as the Returning Soon section of the Classic output template,
|
|
436
524
|
> one row per entry in `data.entries`, quoting only fields present in that payload. The
|
|
@@ -469,12 +557,15 @@ Proceed immediately (non-interactive).
|
|
|
469
557
|
> **Data (JSON):**
|
|
470
558
|
> - Status: {A1 output}
|
|
471
559
|
> - Chips: {A1.5 output}
|
|
472
|
-
> - pFDR: {B1
|
|
560
|
+
> - pFDR: {pfdr} (from B1 - fixture-difficulty analysis; add {pfdr_draft} on `both` runs)
|
|
561
|
+
> - Blank/double schedule: {blanks_schedule} (from B1)
|
|
562
|
+
> {data_caveat} (from B1 - omit these lines entirely when `data_caveat` is empty)
|
|
473
563
|
> - Captain candidates: {B2 output}
|
|
474
564
|
> - Squad: {B4 output}
|
|
475
565
|
> - Price movements: {B5 output}
|
|
476
566
|
> - Chip timing: {B6 output}
|
|
477
567
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
568
|
+
> {stats_caveat} (from B8 - omit this line entirely when `stats_caveat` is empty)
|
|
478
569
|
>
|
|
479
570
|
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
480
571
|
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
@@ -511,7 +602,9 @@ Proceed immediately (non-interactive).
|
|
|
511
602
|
>
|
|
512
603
|
> **Data (JSON):**
|
|
513
604
|
> - Status: {A1 output}
|
|
514
|
-
> - pFDR: {B1
|
|
605
|
+
> - pFDR: {pfdr} (from B1 - fixture-difficulty analysis; add {pfdr_draft} on `both` runs)
|
|
606
|
+
> - Blank/double schedule: {blanks_schedule} (from B1)
|
|
607
|
+
> {data_caveat} (from B1 - omit these lines entirely when `data_caveat` is empty)
|
|
515
608
|
>
|
|
516
609
|
> **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.
|
|
517
610
|
>
|
|
@@ -520,6 +613,7 @@ Proceed immediately (non-interactive).
|
|
|
520
613
|
> - Waivers: {B3 output}
|
|
521
614
|
> - Squad: {B4 output}
|
|
522
615
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
616
|
+
> {stats_caveat} (from B8 - omit this line entirely when `stats_caveat` is empty)
|
|
523
617
|
>
|
|
524
618
|
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
525
619
|
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
@@ -569,7 +663,7 @@ Proceed immediately (non-interactive).
|
|
|
569
663
|
After each sub-agent identifies OUT candidates and an IN shortlist (from squad analysis, `fpl targets`, `fpl waivers`), run the transfer evaluation script for each OUT/shortlist pair:
|
|
570
664
|
|
|
571
665
|
```bash
|
|
572
|
-
|
|
666
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/transfer_eval.py" --out "{out_player_name} ({club})" --in "{comma-separated IN candidates as 'Name (CLUB)'}"
|
|
573
667
|
```
|
|
574
668
|
|
|
575
669
|
The script outputs JSON with Outlook (multi-GW quality) and This GW (lineup impact) deltas for each IN candidate vs the OUT player. Use these scores as the quantitative baseline for transfer/waiver recommendations. Sub-agents may override with qualitative reasons (press conference intel, newsletter signals, season preview intel from B9) using the same `⚡ Override: {reason}` pattern as starting XI overrides. Name the source in the reason.
|
|
@@ -581,10 +675,10 @@ If the script fails (exit 1), fall back to LLM-driven transfer reasoning and not
|
|
|
581
675
|
Run the lineup engine for each active format's squad **before** bench ordering:
|
|
582
676
|
|
|
583
677
|
```bash
|
|
584
|
-
|
|
678
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/starting_xi.py" --squad "{comma-separated 15 squad players from squad grid, each as 'Name (CLUB)'}"
|
|
585
679
|
```
|
|
586
680
|
|
|
587
|
-
<!-- ADAPT: Replace with your squad
|
|
681
|
+
<!-- ADAPT: Replace with your squad players from the squad grid output, each as 'Name (CLUB)' -->
|
|
588
682
|
|
|
589
683
|
Use the script's recommended XI as the default lineup. Sub-agents may override specific picks with stated qualitative reasons (press conference intel, newsletter signals, rotation predictions, season preview intel from B9). Mark any overrides with `⚡ Override: {reason}` in the output, naming the source. If the script fails (exit 1), fall back to manual selection and note the failure.
|
|
590
684
|
|
|
@@ -595,7 +689,7 @@ Use the script's recommended XI as the default lineup. Sub-agents may override s
|
|
|
595
689
|
Using the starting XI from C3 (or the sub-agent's overridden version), run the bench order script:
|
|
596
690
|
|
|
597
691
|
```bash
|
|
598
|
-
|
|
692
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/bench_order.py" --starting "{comma-separated starters as 'Name (CLUB)'}" --bench "{comma-separated bench as 'Name (CLUB)'}"
|
|
599
693
|
```
|
|
600
694
|
|
|
601
695
|
Incorporate the bench ordering output into the relevant sections of each sub-agent's recommendations.
|
|
@@ -611,10 +705,21 @@ Combine the outputs from whichever sub-agents were dispatched into a single reco
|
|
|
611
705
|
|
|
612
706
|
The file should follow the structure defined in `references/output-template.md`, with both Classic and Draft sections populated.
|
|
613
707
|
|
|
708
|
+
**Write `data_caveat` (from B1) into the `## Notes` section**, above any other note. It is one section for the whole file rather than one per format -- the team ratings and fixture predictions it describes are shared by both -- so the orchestrator writes it here rather than either sub-agent. Omit the whole block when `data_caveat` is empty, which is the ordinary case; an empty Data quality heading reads as a clean bill of health that was never issued.
|
|
709
|
+
|
|
710
|
+
**Normalise the file, immediately after writing it and before Phase D1** -- otherwise a sub-agent section that arrived HTML-escaped lands in the report with its markdown broken, and the entities go on to confuse the table parsing in D1 and E:
|
|
711
|
+
|
|
712
|
+
```bash
|
|
713
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/normalise_entities.py" --file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md"
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
Parse stdout as JSON and warn, never block. `references/entity-normalisation.md` carries the contract, the warning template and the failure handling -- read it rather than reproducing them here.
|
|
717
|
+
|
|
614
718
|
Present a brief summary to the user:
|
|
615
719
|
- GW number and deadline
|
|
616
720
|
- Mode (transfer or squad-builder)
|
|
617
721
|
- Key highlights (top captain pick, priority transfer/waiver, chip timing note)
|
|
722
|
+
- Data quality: each `data_caveat` bullet, verbatim, or nothing at all when it is empty. This is the one place the remedy reaches someone who can run it -- a warning that only lands in the file is read after the deadline, if at all
|
|
618
723
|
- Output file path
|
|
619
724
|
|
|
620
725
|
---
|
|
@@ -630,14 +735,14 @@ _Runs after Phase D file write, before Phase E. Not embed-gated._
|
|
|
630
735
|
3. Run:
|
|
631
736
|
|
|
632
737
|
```bash
|
|
633
|
-
|
|
738
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/validate_draft_waivers.py" \
|
|
634
739
|
--recommendations-file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md" \
|
|
635
740
|
--waivers-json /tmp/gw-prep-waivers-{N}.json \
|
|
636
741
|
--squad-grid-json /tmp/gw-prep-squad-grid-{N}.json
|
|
637
742
|
```
|
|
638
743
|
|
|
639
744
|
Parse stdout as JSON: `{"ok": bool, "flags": [...], "warnings": [...]}`.
|
|
640
|
-
If the script cannot be found or exits non-zero unexpectedly, emit a warning and proceed to Phase E (fail-open for infrastructure errors, fail-closed only for confirmed rule violations).
|
|
745
|
+
If the script cannot be found or exits non-zero unexpectedly, emit a warning and proceed to Phase E (fail-open for infrastructure errors, fail-closed only for confirmed rule violations). A non-zero exit whose stdout parses as `{"error": true, ...}` is the script reporting its own startup failure — quote `messages[0]` verbatim in that warning; it names the cause (a wrong interpreter is the common one).
|
|
641
746
|
|
|
642
747
|
4. **Managed block update:** locate the sentinel block in the recommendations file:
|
|
643
748
|
```
|
|
@@ -676,7 +781,7 @@ _Skip unless `squad_builder_result == "embed"`. Transfer and rederive runs do no
|
|
|
676
781
|
1. Run:
|
|
677
782
|
|
|
678
783
|
```bash
|
|
679
|
-
|
|
784
|
+
[YOUR_PYTHON] "${CLAUDE_SKILL_DIR}/scripts/extract_classic_squad.py" --from-recommendations --file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md"
|
|
680
785
|
```
|
|
681
786
|
|
|
682
787
|
Parse stdout as JSON regardless of exit code. If exit is non-zero → emit warning and proceed:
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Entity normalisation (shared post-write step)
|
|
2
|
+
|
|
3
|
+
Every skill that assembles a file out of sub-agent prose runs this step
|
|
4
|
+
immediately after writing that file. Referenced by gw-prep Phase D,
|
|
5
|
+
squad-builder Phase E and update-gw-prep Phase D — this file is the single
|
|
6
|
+
source for the contract below, so a change to the script's output shape is
|
|
7
|
+
edited here, not in three SKILL.md copies.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
Sub-agent sections are concatenated verbatim. A section that arrived
|
|
12
|
+
HTML-escaped somewhere in the return path therefore lands in the file with its
|
|
13
|
+
markdown broken: a `>` blockquote marker renders as literal text instead of
|
|
14
|
+
opening a quote block, a comparison operator in prose reads as `>`, and the
|
|
15
|
+
entities go on to confuse the table parsing in the validation phases that
|
|
16
|
+
follow. Nothing else in the pipeline looks at whether the prose survived
|
|
17
|
+
assembly.
|
|
18
|
+
|
|
19
|
+
## Invocation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
python3 "$FPL_CLI_DIR/.agents/skills/gw-prep/scripts/normalise_entities.py" --file "{path to the file just written}"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Within gw-prep itself, `"${CLAUDE_SKILL_DIR}/scripts/normalise_entities.py"` is
|
|
26
|
+
equivalent and matches the surrounding phases.
|
|
27
|
+
|
|
28
|
+
The script rewrites the file in place, decoding `<`, `>`, `&`, `"` and `'`, and
|
|
29
|
+
costs nothing when nothing was escaped. Decoding is deliberately narrower than
|
|
30
|
+
a blanket unescape: a numeric reference to anything else (`Δ` for Δ)
|
|
31
|
+
survives into `residual` rather than being silently rewritten.
|
|
32
|
+
|
|
33
|
+
## Contract
|
|
34
|
+
|
|
35
|
+
Parse stdout as JSON:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{"ok": true, "changed": false, "unescaped": 0, "residual": [{"line": 12, "entity": " "}]}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Field | Meaning |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `ok` | `false` only when `residual` is non-empty |
|
|
44
|
+
| `changed` | whether the file was rewritten |
|
|
45
|
+
| `unescaped` | characters recovered (length difference, not entity count) |
|
|
46
|
+
| `residual` | entity references left undecoded, with 1-based line numbers |
|
|
47
|
+
|
|
48
|
+
On a file that cannot be read, decoded as UTF-8, or written, it exits 1 with
|
|
49
|
+
`{"error": true, "messages": [...]}` — still JSON on stdout, never a traceback.
|
|
50
|
+
|
|
51
|
+
## Posture: warn, never block
|
|
52
|
+
|
|
53
|
+
An entity is a cosmetic defect, not a rule violation, so this step never stops
|
|
54
|
+
the pipeline.
|
|
55
|
+
|
|
56
|
+
- `ok: true` → silent continue, whether or not it changed anything.
|
|
57
|
+
- `ok: false` → emit an in-chat warning and proceed:
|
|
58
|
+
> ⚠️ `{filename}` still contains {N} HTML entit(y/ies) after normalisation —
|
|
59
|
+
> line {line}: `{entity}`, ... Check those lines render as intended.
|
|
60
|
+
- Script missing, or a non-zero exit → emit a warning naming the failure and
|
|
61
|
+
proceed. Fail-open for infrastructure errors, as gw-prep Phase D1 does.
|
|
@@ -184,9 +184,17 @@ BenchOrderAgent recommended order:
|
|
|
184
184
|
|
|
185
185
|
## Notes
|
|
186
186
|
|
|
187
|
+
### Data quality
|
|
188
|
+
|
|
189
|
+
- {one bullet per B1 quality signal that fired, CLI message quoted verbatim}
|
|
190
|
+
|
|
187
191
|
Any additional context, caveats, or follow-up actions.
|
|
188
192
|
```
|
|
189
193
|
|
|
194
|
+
The **Data quality** sub-section carries `data_caveat` from gw-prep Phase B1 -- the team-ratings and fixture-prediction warnings that `fpl fdr` returned alongside the difficulty analysis. Omit the whole sub-section, heading included, when nothing fired: an empty heading reads as a clean bill of health that was never issued. Quote each message verbatim rather than summarising it; the CLI text names the command that fixes the problem (`fpl ratings update`, `fpl init`) and that is the only actionable part.
|
|
195
|
+
|
|
196
|
+
Every recommendation in this file that leans on fixture difficulty rests on those ratings, so this belongs with the recommendations rather than only in the terminal scrollback the run scrolled past.
|
|
197
|
+
|
|
190
198
|
---
|
|
191
199
|
|
|
192
200
|
## Fallback Banners (Wildcard/Free Hit re-derivation)
|
|
@@ -83,6 +83,8 @@ Momentum Alert bullets must only cite numbers that appear in the CLI/stats outpu
|
|
|
83
83
|
|
|
84
84
|
## pFDR (Positional FDR) Analysis
|
|
85
85
|
|
|
86
|
+
**When the prompt carries a Data quality caveat, pFDR is indicative, not decisive.** Those warnings mean the team ratings under every number below are stale, absent, describing last season's league, or unable to separate any two clubs -- the table still renders, it just is not measuring what it claims. Keep producing the pFDR Overview section (a reader needs to see the fixtures), but do not let a fixture run alone carry a transfer, captaincy or waiver recommendation; lead on form, minutes and preview intel, and say in the Rationale that fixtures were discounted. With no caveat present, weight pFDR normally.
|
|
87
|
+
|
|
86
88
|
For each position (GK, DEF, MID, FWD):
|
|
87
89
|
|
|
88
90
|
1. Identify the 3 best fixture runs over the next 5 GWs (lowest average pFDR).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Shared startup for gw-prep scripts that import the fpl-cli package.
|
|
2
|
+
|
|
3
|
+
Importing this module is itself the interpreter guard. Every script here
|
|
4
|
+
needs `fpl_cli` importable, and a standalone `fpl` on PATH (uv tool, pipx)
|
|
5
|
+
provides the command without putting the package on the system
|
|
6
|
+
interpreter's import path — so `fpl status` works and `python3
|
|
7
|
+
transfer_eval.py` does not. Reaching for the package here, first and alone,
|
|
8
|
+
turns that mistake into the same JSON error envelope the callers already
|
|
9
|
+
parse rather than a traceback from halfway down a script's import list.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
from typing import NoReturn
|
|
17
|
+
|
|
18
|
+
_WRONG_INTERPRETER = (
|
|
19
|
+
"Cannot import the 'fpl_cli' package on this interpreter ({executable}). "
|
|
20
|
+
"These scripts run inside fpl-cli's own environment: activate its venv "
|
|
21
|
+
"('source .venv/bin/activate') or invoke that venv's Python directly. A "
|
|
22
|
+
"standalone 'fpl' on PATH (uv tool, pipx) provides the command but not the "
|
|
23
|
+
"importable package."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def fail(messages: list[str]) -> NoReturn:
|
|
28
|
+
"""Emit the error envelope callers parse from stdout, then exit 1."""
|
|
29
|
+
json.dump({"error": True, "messages": messages}, sys.stdout, indent=2)
|
|
30
|
+
sys.exit(1)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_fpl_cli_missing(exc: ModuleNotFoundError) -> bool:
|
|
34
|
+
"""True when the fpl_cli package itself is absent from this interpreter.
|
|
35
|
+
|
|
36
|
+
Only the top-level miss earns the envelope below. Python names the first
|
|
37
|
+
component it could not find, so an absent package raises with
|
|
38
|
+
name="fpl_cli" while a corrupt install missing one file raises with
|
|
39
|
+
name="fpl_cli.paths" — that one is on the right interpreter, and telling
|
|
40
|
+
its reader to activate a venv would send them somewhere useless. A
|
|
41
|
+
missing dependency (name="pydantic") is the same kind of wrong. Both
|
|
42
|
+
keep their traceback, which says more than this guard could.
|
|
43
|
+
"""
|
|
44
|
+
return exc.name == "fpl_cli"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
from fpl_cli.paths import UserDirError, ensure_legacy_migration, load_env_files
|
|
49
|
+
except ModuleNotFoundError as exc:
|
|
50
|
+
if not is_fpl_cli_missing(exc):
|
|
51
|
+
raise
|
|
52
|
+
fail([_WRONG_INTERPRETER.format(executable=sys.executable), f"Import failed: {exc}"])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def bootstrap_user_dirs() -> None:
|
|
56
|
+
"""Run fpl-cli's legacy-file migration before any agent resolves paths.
|
|
57
|
+
|
|
58
|
+
The CLI does this in its own entry point; scripts importing agents
|
|
59
|
+
directly must do it themselves or a user with files in the legacy
|
|
60
|
+
repo-root dirs silently gets empty settings and regenerated ratings.
|
|
61
|
+
A bad FPL_CLI_* override surfaces here as a clean JSON error instead
|
|
62
|
+
of a traceback mid-agent.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
load_env_files()
|
|
66
|
+
ensure_legacy_migration()
|
|
67
|
+
except UserDirError as exc:
|
|
68
|
+
fail([str(exc)])
|