fplkit 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/TOOLS.md +25 -9
- fplkit-2.1.0/.agents/skills/create-pr/SKILL.md +189 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/SKILL.md +69 -16
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/references/output-template.md +8 -4
- fplkit-2.1.0/.agents/skills/gw-prep/scripts/_bootstrap.py +25 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/scripts/bench_order.py +3 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/scripts/extract_classic_squad.py +126 -118
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/scripts/starting_xi.py +3 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/scripts/transfer_eval.py +3 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/scripts/validate_draft_waivers.py +18 -27
- fplkit-2.1.0/.agents/skills/preview-ingest/SKILL.md +162 -0
- fplkit-2.1.0/.agents/skills/release/SKILL.md +151 -0
- fplkit-2.1.0/.agents/skills/release-notes/SKILL.md +98 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/squad-builder/SKILL.md +95 -13
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/squad-builder/references/output-template.md +11 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/squad-builder/references/rules.md +24 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/update-gw-prep/SKILL.md +25 -8
- {fplkit-2.0.0 → fplkit-2.1.0}/.github/workflows/ci.yml +5 -0
- fplkit-2.1.0/.github/workflows/pr-title.yml +30 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.github/workflows/release.yml +8 -3
- {fplkit-2.0.0 → fplkit-2.1.0}/.gitignore +0 -3
- fplkit-2.1.0/AGENTS.md +93 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/CHANGELOG.md +15 -1
- fplkit-2.1.0/CLAUDE.md +93 -0
- fplkit-2.1.0/CONTRIBUTING.md +99 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/PKG-INFO +37 -7
- {fplkit-2.0.0 → fplkit-2.1.0}/README.md +33 -4
- {fplkit-2.0.0 → fplkit-2.1.0}/cliff.toml +14 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/docs/architecture.md +70 -25
- {fplkit-2.0.0 → fplkit-2.1.0}/docs/command-reference.md +308 -18
- {fplkit-2.0.0 → fplkit-2.1.0}/docs/custom-analysis.md +14 -4
- {fplkit-2.0.0 → fplkit-2.1.0}/docs/fpl-rules.md +5 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/_version.py +2 -2
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/common.py +4 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/data/fixture.py +9 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/orchestration/report.py +62 -12
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/core_insights.py +2 -2
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/dataset_fetcher.py +3 -21
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/fpl_draft.py +7 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/providers/anthropic.py +1 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/__init__.py +15 -4
- fplkit-2.1.0/fpl_cli/cli/_context.py +321 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_fines.py +15 -7
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_helpers.py +21 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_league_recap_data.py +674 -67
- fplkit-2.1.0/fpl_cli/cli/_league_recap_history.py +1076 -0
- fplkit-2.1.0/fpl_cli/cli/_league_recap_types.py +198 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_review_analysis.py +2 -2
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_review_classic.py +23 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_review_summarisation.py +163 -20
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/allocate.py +43 -23
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/captain.py +2 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/chips.py +1 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/differentials.py +2 -5
- fplkit-2.1.0/fpl_cli/cli/doctor.py +612 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/fdr.py +7 -7
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/init.py +18 -21
- fplkit-2.1.0/fpl_cli/cli/intel.py +534 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/league.py +9 -2
- fplkit-2.1.0/fpl_cli/cli/league_recap.py +547 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/player.py +30 -21
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/preview.py +6 -3
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/price_changes.py +2 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/ratings.py +2 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/review.py +36 -9
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/sell_prices.py +5 -3
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/squad.py +39 -10
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/status.py +117 -32
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/targets.py +2 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/waivers.py +2 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/xg.py +2 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/config/defaults.yaml +1 -1
- fplkit-2.1.0/fpl_cli/config/fixture_predictions.yaml +5 -0
- fplkit-2.1.0/fpl_cli/config/previews/EXAMPLE.yaml +97 -0
- {fplkit-2.0.0 → fplkit-2.1.0/fpl_cli}/config/team_managers.yaml +10 -10
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/models/chip_plan.py +7 -4
- fplkit-2.1.0/fpl_cli/models/league_history.py +415 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/models/player.py +8 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/models/team.py +18 -8
- fplkit-2.1.0/fpl_cli/paths.py +244 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/prompts/league_recap.py +68 -4
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/prompts/review.py +59 -16
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/scraper/fpl_prices.py +14 -8
- fplkit-2.1.0/fpl_cli/season.py +162 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/fixture_predictions.py +124 -20
- fplkit-2.1.0/fpl_cli/services/league_history.py +343 -0
- fplkit-2.1.0/fpl_cli/services/league_history_counters.py +679 -0
- fplkit-2.1.0/fpl_cli/services/league_history_notes.py +648 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/player_prior.py +11 -16
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/player_scoring.py +3 -0
- fplkit-2.1.0/fpl_cli/services/season_previews.py +953 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/team_ratings.py +270 -70
- fplkit-2.1.0/fpl_cli/services/team_ratings_prior.py +351 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/templates/gw_league_recap.md.j2 +23 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/templates/gw_review.md.j2 +9 -1
- fplkit-2.1.0/fpl_cli/utils/files.py +37 -0
- fplkit-2.1.0/fpl_cli/utils/gameweek.py +10 -0
- fplkit-2.1.0/fpl_cli/utils/markdown.py +292 -0
- fplkit-2.1.0/fpl_cli/utils/teams.py +51 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/pyproject.toml +14 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/conftest.py +103 -4
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_action.py +90 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_analysis.py +20 -2
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_analysis_squad.py +8 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_data.py +21 -4
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_allocate.py +73 -27
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_core_insights.py +5 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_draft.py +33 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_understat.py +10 -3
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_bench_order_script.py +17 -6
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_classic_squad_extraction.py +633 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_captain.py +7 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_chips.py +17 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_custom_analysis.py +73 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_differentials.py +7 -0
- fplkit-2.1.0/tests/test_cli_doctor.py +437 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_fdr.py +54 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_fdr_blanks.py +43 -7
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_init.py +5 -1
- fplkit-2.1.0/tests/test_cli_intel.py +344 -0
- fplkit-2.1.0/tests/test_cli_league.py +67 -0
- fplkit-2.1.0/tests/test_cli_league_recap.py +1941 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_player.py +136 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_preview.py +67 -0
- fplkit-2.1.0/tests/test_cli_price_changes.py +57 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_review.py +208 -2
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_sell_prices.py +42 -1
- fplkit-2.1.0/tests/test_cli_settings_warning.py +99 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_squad.py +109 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_stats.py +8 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_status.py +332 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_targets.py +7 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_waivers.py +7 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_warnings_stderr.py +2 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_xg.py +7 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_common.py +37 -0
- fplkit-2.1.0/tests/test_doc_consistency.py +89 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_fines.py +16 -1
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_fixture_predictions.py +183 -7
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_init.py +51 -0
- fplkit-2.1.0/tests/test_league_history_counters.py +935 -0
- fplkit-2.1.0/tests/test_league_history_notes.py +701 -0
- fplkit-2.1.0/tests/test_league_history_store.py +588 -0
- fplkit-2.1.0/tests/test_league_recap.py +2876 -0
- fplkit-2.1.0/tests/test_paths.py +454 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_player_prior.py +7 -7
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_player_scoring.py +25 -0
- fplkit-2.1.0/tests/test_preseason.py +359 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_providers.py +5 -5
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_report_agent.py +50 -0
- fplkit-2.1.0/tests/test_report_paths.py +212 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_review.py +257 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_scraper.py +30 -23
- fplkit-2.1.0/tests/test_season.py +219 -0
- fplkit-2.1.0/tests/test_season_awareness.py +382 -0
- fplkit-2.1.0/tests/test_season_previews.py +763 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_team_ratings.py +129 -17
- fplkit-2.1.0/tests/test_team_ratings_prior.py +435 -0
- fplkit-2.1.0/tests/test_utils_gameweek.py +18 -0
- fplkit-2.1.0/tests/test_utils_markdown.py +238 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_validate_draft_waivers.py +179 -0
- fplkit-2.0.0/AGENTS.md +0 -70
- fplkit-2.0.0/CLAUDE.md +0 -70
- fplkit-2.0.0/config/team_ratings.yaml +0 -119
- fplkit-2.0.0/fpl_cli/cli/_context.py +0 -171
- fplkit-2.0.0/fpl_cli/cli/_league_recap_types.py +0 -120
- fplkit-2.0.0/fpl_cli/cli/league_recap.py +0 -297
- fplkit-2.0.0/fpl_cli/config/fixture_predictions.yaml +0 -10
- fplkit-2.0.0/fpl_cli/paths.py +0 -130
- fplkit-2.0.0/fpl_cli/season.py +0 -88
- fplkit-2.0.0/fpl_cli/services/team_ratings_prior.py +0 -291
- fplkit-2.0.0/tests/test_league_recap.py +0 -1425
- fplkit-2.0.0/tests/test_paths.py +0 -182
- fplkit-2.0.0/tests/test_season.py +0 -95
- fplkit-2.0.0/tests/test_team_ratings_prior.py +0 -194
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/README.md +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.agents/skills/gw-prep/references/rules.md +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.claude/output-styles/fpl-mate.md +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.claude/settings.json +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/.env.example +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/LICENSE +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/config/team_ratings_overrides.yaml +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/docs/images/fpl-player-demo-v1-0.png +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/action/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/action/waiver.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/bench_order.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/captain.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/squad_analyzer.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/starting_xi.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/stats.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/analysis/transfer_eval.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/base.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/data/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/data/price.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/data/scout.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/agents/orchestration/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/football_data.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/fpl.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/historical.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/historical_types.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/providers/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/providers/_models.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/providers/openai_compat.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/providers/perplexity.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/understat.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/api/vaastav.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_banner.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_fines_config.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_json.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_plan_grid.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/_review_draft.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/credentials.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/fixtures.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/history.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/price_history.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/stats.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/cli/transfer_eval.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/constants.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/models/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/models/fixture.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/models/types.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/parsers/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/parsers/recommendations.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/prompts/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/prompts/scout.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/scraper/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/matchup.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/squad_allocator.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/services/team_form.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/templates/gw_preview.md.j2 +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/utils/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/utils/text.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/fpl_cli/utils/time.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/pyrightconfig.json +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/requirements.lock +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/__init__.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/fixtures/classic_squad_fixture.md +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/fixtures/recommendations_fixture.md +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/fixtures/validate_draft_waivers/recs_clean.md +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/fixtures/validate_draft_waivers/squad_grid.json +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/fixtures/validate_draft_waivers/waivers.json +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_base.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_data_price.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_agents_orchestration.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_client.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_dataset_fetcher.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_football_data.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_historical.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_api_vaastav.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_allocate.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_banner.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_credentials.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_fixtures.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_format.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_format_suppression.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_history.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_json.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_plan_grid.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_price_history.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_cli_transfer_eval.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_common_scoring.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_data_prep_harmonisation.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_fines_config.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_fpl_prices.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_init_fines.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_matchup_service.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_models.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_player_resolution.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_recommendations_parser.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_rolling_pts_per_m.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_sell_prices.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_squad_allocator.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_starting_xi_agent.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_stats.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_transfer_eval_agent.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_utils_text.py +0 -0
- {fplkit-2.0.0 → fplkit-2.1.0}/tests/test_utils_time.py +0 -0
|
@@ -25,19 +25,20 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
25
25
|
| Command | Description | JSON | Format | Experimental | Pattern |
|
|
26
26
|
|---------|-------------|------|--------|-------------|---------|
|
|
27
27
|
| `init` | Set up fpl-cli with your FPL IDs and optional features | No | General | No | direct-api |
|
|
28
|
-
| `status` | Show FPL gameweek status and upcoming deadlines | Yes | General | No | direct-api |
|
|
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
30
|
| `fixtures` | Show fixtures for a gameweek | Yes | General | No | direct-api |
|
|
30
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 |
|
|
31
|
-
| `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); 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 |
|
|
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 |
|
|
32
33
|
| `history` | Show historical player performance across seasons | Yes | General | No | direct-api |
|
|
33
34
|
| `league` | Show live league standings for Classic and Draft leagues | No | General | No | direct-api |
|
|
34
35
|
| `fdr` | Analyse fixture difficulty - easy runs, blanks, doubles | Yes | General | No | via-agent |
|
|
35
36
|
| `xg` | Analyse underlying stats: xG, xA, overperformers | Yes | General | No | via-agent |
|
|
36
37
|
| `price-changes` | Show price changes and transfer activity | No | General | No | via-agent |
|
|
37
38
|
| `price-history` | Show price trajectory and transfer momentum | Yes | General | No | direct-api |
|
|
38
|
-
| `preview` | Run full pre-gameweek analysis and generate report | No | General | No | via-agent |
|
|
39
|
-
| `review` | Review a completed gameweek - squad performance and standings | No | General | No | via-agent |
|
|
40
|
-
| `league-recap` | Recap a completed gameweek - awards, standings, and banter |
|
|
39
|
+
| `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
|
+
| `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
|
+
| `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 |
|
|
41
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 |
|
|
42
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 |
|
|
43
44
|
| `targets` | Find transfer targets - high performers across all ownership. Consistency bonus (CV-xGI percentile, phased in GW6-10) | Yes | Classic | Yes | via-agent |
|
|
@@ -54,24 +55,37 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
54
55
|
| `chips sync` | Sync chip usage from FPL API | No | Classic | No | direct-api |
|
|
55
56
|
| `ratings` | Display team ratings | No | General | Yes | direct-api |
|
|
56
57
|
| `ratings update` | Recalculate ratings from fixture results | No | General | Yes | direct-api |
|
|
58
|
+
| `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
|
+
| `intel schema` | Print the preview file format with every field explained | No | General | No | direct-api |
|
|
60
|
+
| `intel init` | Scaffold an empty preview file per Premier League team (`--force` overwrites). Stubs never count toward coverage | No | General | No | direct-api |
|
|
61
|
+
| `intel show` | Show one team's preview, aged to the current gameweek | Yes | General | No | direct-api |
|
|
62
|
+
| `intel resolve` | Match preview player names to FPL `element_code`s; `--write` saves them back with comments preserved (never touching an existing code), `--all` re-resolves coded players and with `--write` saves corrections over them. Ambiguity is reported, never guessed | Yes | General | No | direct-api |
|
|
57
63
|
| `credentials set` | Store FPL email and password in system keyring | No | Classic | No | direct-api |
|
|
58
64
|
| `credentials clear` | Remove FPL credentials from system keyring | No | Classic | No | direct-api |
|
|
59
65
|
|
|
60
66
|
**Column key:**
|
|
61
67
|
- **JSON** - supports `--format json` output
|
|
62
68
|
- **Format** - Classic (classic league only), Draft (draft league only), General (both)
|
|
63
|
-
- **Experimental** - requires `custom_analysis: true` in settings; hidden from `--help` by default
|
|
69
|
+
- **Experimental** - requires `custom_analysis: true` in settings; hidden from `--help` by default, and invoking one while it is off reports the toggle and the settings.yaml being read
|
|
64
70
|
- **Pattern** - `direct-api` (API client only), `via-agent` (uses analysis agent), `mixed` (both patterns in subcommands)
|
|
65
71
|
|
|
72
|
+
**Report paths are season-partitioned.** Every command that saves a report writes to `<dir>/<season>/gw{N}-*.md`, where `<season>` is the hyphenated label (`2026-27`). Report filenames carry a gameweek but no season, so a flat directory lets one season's GW21 file overwrite the previous season's. This applies to an explicit `--output` too. Any skill or script writing alongside these reports must use the same season directory, and must take the label from `fpl status --format json` (`metadata.season`) rather than hardcoding it — a hardcoded label silently rots at the July rollover.
|
|
73
|
+
|
|
66
74
|
## Skills
|
|
67
75
|
|
|
68
76
|
Agent playbooks in `.agents/skills/`. Each has a `SKILL.md` entry point. Claude Code discovers them via the `.claude/skills/` symlink.
|
|
69
77
|
|
|
78
|
+
Skills write their own outputs (`gw{N}-recommendations.md`, `gw{N}-squad-builder.md`, `season-start-squad.md`) into the same `<output dir>/<season>/` directory, for the same reason.
|
|
79
|
+
|
|
70
80
|
| Skill | Path | Purpose | Compatibility |
|
|
71
81
|
|-------|------|---------|--------------|
|
|
72
|
-
| gw-prep | `skills/gw-prep/` | Gameweek preparation recommendations for classic and draft (embed / rederive / transfer branches; Phase E post-write validation) | Full: Claude Code. Partial: Codex, Cursor, Copilot |
|
|
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 |
|
|
73
83
|
| update-gw-prep | `skills/update-gw-prep/` | Append GW update to existing recommendations | Full: Claude Code, Codex, Cursor, Copilot |
|
|
74
|
-
| squad-builder | `skills/squad-builder/` | Build optimal 15-player squad (wildcard, free hit, season start) | Full: Claude Code, Codex, Cursor, Copilot |
|
|
84
|
+
| 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
|
+
| 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 |
|
|
86
|
+
| 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 |
|
|
87
|
+
| 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) |
|
|
88
|
+
| create-pr | `skills/create-pr/` | Open a PR for the current branch: conventional-commit title (the changelog line) + why-first prose body in the fpl-cli PR description style | Full: Claude Code, Codex, Cursor, Copilot |
|
|
75
89
|
|
|
76
90
|
## Analysis Agents
|
|
77
91
|
|
|
@@ -94,4 +108,6 @@ Python classes in `fpl_cli/agents/` that implement `async run(context) -> AgentR
|
|
|
94
108
|
**Notes:**
|
|
95
109
|
- BenchOrderAgent and StartingXIAgent have no CLI command - they are invoked by gw-prep skill wrapper scripts in `.agents/skills/gw-prep/scripts/`
|
|
96
110
|
- TransferEvalAgent is used by both `transfer-eval` CLI command and gw-prep skill
|
|
97
|
-
- `extract_classic_squad.py` (`.agents/skills/gw-prep/scripts/`) — deterministic Classic Squad block extractor used by gw-prep Phase
|
|
111
|
+
- `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.
|
|
112
|
+
- `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
|
+
- 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.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-pr
|
|
3
|
+
description: >
|
|
4
|
+
Use when the user asks to create a pull request or PR for the current
|
|
5
|
+
branch — "create a PR", "open a PR", "raise a PR", "push and create a
|
|
6
|
+
PR", "ship this branch". Writes the title as the changelog line
|
|
7
|
+
(conventional commits) and the body in the fpl-cli PR description style:
|
|
8
|
+
first-person prose, why-first, no boilerplate sections. Also the style
|
|
9
|
+
reference whenever a session writes a PR title or body outside the skill.
|
|
10
|
+
model: sonnet
|
|
11
|
+
compatibility:
|
|
12
|
+
claude-code: full (gh locally, GitHub MCP tools on the web)
|
|
13
|
+
codex: full (gh)
|
|
14
|
+
cursor: full (gh)
|
|
15
|
+
copilot: full (gh)
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Create Pull Request
|
|
19
|
+
|
|
20
|
+
Opens a PR for the current branch against main: pushes the branch, writes
|
|
21
|
+
a conventional-commit title, and a prose description in the fpl-cli PR
|
|
22
|
+
description style. Solo repo — there are no reviewers, squads, or labels
|
|
23
|
+
to assign, and the PR is created ready (not draft) unless asked otherwise.
|
|
24
|
+
|
|
25
|
+
## Title: the changelog line
|
|
26
|
+
|
|
27
|
+
PRs squash-merge with the title as the commit subject on main, and
|
|
28
|
+
git-cliff publishes `feat:`/`fix:`/`refactor:`/`perf:` subjects verbatim in
|
|
29
|
+
CHANGELOG.md and the GitHub release notes. The `PR Title` CI check rejects
|
|
30
|
+
anything non-conventional. So:
|
|
31
|
+
|
|
32
|
+
- Format `type(scope)?!?: subject` — types as in `cliff.toml` (`feat`,
|
|
33
|
+
`fix`, `refactor`, `perf` are changelog-visible; `chore`, `docs`, `ci`,
|
|
34
|
+
`test`, `style` are skipped). `!` marks a breaking CLI change and drives
|
|
35
|
+
a major bump.
|
|
36
|
+
- A changelog-visible title must read as the standalone user-facing change
|
|
37
|
+
("fix: pin PuLP below 4.0 so squad allocation keeps working"), never the
|
|
38
|
+
implementation ("fix: address review feedback").
|
|
39
|
+
- Keep it ≤ 72 characters — GitHub appends ` (#N)` on squash.
|
|
40
|
+
- No issue references in the title; `Closes #N` lives in the body, so the
|
|
41
|
+
changelog line stays clean.
|
|
42
|
+
- One purpose per PR, so one title can describe it. If no single
|
|
43
|
+
conventional subject covers the diff, the branch wants splitting, not a
|
|
44
|
+
vaguer title.
|
|
45
|
+
|
|
46
|
+
## Description: the fpl-cli PR description style
|
|
47
|
+
|
|
48
|
+
The body is **flowing first-person prose**, as if explaining the change to
|
|
49
|
+
a colleague. No `## Summary`/`## Changes`/`## Testing` headings, no
|
|
50
|
+
file-by-file changelog, no checklists.
|
|
51
|
+
|
|
52
|
+
### Tone
|
|
53
|
+
|
|
54
|
+
First person, conversational, proper sentence casing. Reads like an
|
|
55
|
+
explanation, not a filled-in template.
|
|
56
|
+
|
|
57
|
+
Good: "The changelog job pushes straight to main, which the new ruleset
|
|
58
|
+
would block — so it now authenticates as the repo admin, who is a bypass
|
|
59
|
+
actor."
|
|
60
|
+
Bad: "## Summary\nThis PR updates the changelog job's authentication."
|
|
61
|
+
|
|
62
|
+
### Content
|
|
63
|
+
|
|
64
|
+
- **Open with why, not what.** Start with the problem or context that
|
|
65
|
+
motivated the change. The diff already shows what changed.
|
|
66
|
+
- **Explain the approach when it is non-obvious.** Natural phrasing ("the
|
|
67
|
+
general strategy is"), not a formal section.
|
|
68
|
+
- **`Closes #N`** inline when the PR resolves an issue — GitHub closes it
|
|
69
|
+
on merge. The branch name often carries the number (`claude/issue-46-…`).
|
|
70
|
+
- **Use italics for caveats, scope notes, and verification gaps.**
|
|
71
|
+
Deliberate omissions, known limitations, and anything you could not
|
|
72
|
+
verify — and why — go in `_italics_`. In this repo that is usually
|
|
73
|
+
behaviour needing live FPL API data, LLM provider keys, or the scraper's
|
|
74
|
+
FPL credentials, none of which a web session has.
|
|
75
|
+
- **Include stats when relevant.** Test counts, performance numbers,
|
|
76
|
+
migration sizes — plainly stated, at the end.
|
|
77
|
+
|
|
78
|
+
### Structure and length
|
|
79
|
+
|
|
80
|
+
No fixed sections. Bullets only where you are genuinely listing items
|
|
81
|
+
(rules, scenarios, per-command effects) — never as the default shape.
|
|
82
|
+
|
|
83
|
+
Pick the budget before writing, and stay inside it:
|
|
84
|
+
|
|
85
|
+
| Change | Body |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Dependency bump, config/data refresh, docs, cleanup, test-only | 1–2 sentences |
|
|
88
|
+
| Bug fix, small feature, new flag, refactor | 2–3 sentences |
|
|
89
|
+
| New agent, new data source, scoring change, or a strategy the diff cannot show | up to 3 short paragraphs |
|
|
90
|
+
|
|
91
|
+
Break the body up at every length: at 2–3 sentences, one sentence per
|
|
92
|
+
line; beyond that, paragraphs of two-three sentences with blank lines
|
|
93
|
+
between. Then one editing pass — delete every sentence that only tells the
|
|
94
|
+
reviewer something the diff or the title already tells them.
|
|
95
|
+
|
|
96
|
+
### What to leave out
|
|
97
|
+
|
|
98
|
+
- No "Test Plan"/"How to verify"/"Acceptance Criteria" sections
|
|
99
|
+
- No "Changes" lists that duplicate the diff
|
|
100
|
+
- No empty validation ("great improvement to the codebase")
|
|
101
|
+
- No restating the title or narrating commits
|
|
102
|
+
- No extra AI-attribution lines — remote Claude Code sessions append a
|
|
103
|
+
"Generated with Claude Code" footer automatically; that platform footer
|
|
104
|
+
stays, and nothing more gets added
|
|
105
|
+
|
|
106
|
+
## Workflow
|
|
107
|
+
|
|
108
|
+
### Step 1: Branch and push
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
BRANCH=$(git branch --show-current) # never main
|
|
112
|
+
git push -u origin "$BRANCH"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Target is always `main`. If the branch's previous PR was already merged,
|
|
116
|
+
restart the branch from `origin/main` first and carry only unmerged
|
|
117
|
+
commits.
|
|
118
|
+
|
|
119
|
+
### Step 2: Gather the why
|
|
120
|
+
|
|
121
|
+
If the branch or session references an issue, read it (`gh issue view N`,
|
|
122
|
+
or the GitHub MCP `issue_read` tool) for motivation. `git log
|
|
123
|
+
origin/main..HEAD --oneline` shows scope — use it to understand intent,
|
|
124
|
+
never to write a commit list into the body.
|
|
125
|
+
|
|
126
|
+
### Step 3: Write title and body
|
|
127
|
+
|
|
128
|
+
Title per the rules above; body inside the length budget. Sanity-check the
|
|
129
|
+
title against the lint regex:
|
|
130
|
+
`^(feat|fix|refactor|perf|chore|docs|ci|test|style)(\([a-zA-Z0-9._/ -]+\))?!?: .+`
|
|
131
|
+
|
|
132
|
+
### Step 4: Create the PR
|
|
133
|
+
|
|
134
|
+
Local session:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
gh pr create --title "<title>" --base main --body "$(cat <<'EOF'
|
|
138
|
+
<body prose>
|
|
139
|
+
EOF
|
|
140
|
+
)"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Web/remote session: the GitHub MCP `create_pull_request` tool (load via
|
|
144
|
+
ToolSearch) with the same title/body, `base: main`.
|
|
145
|
+
|
|
146
|
+
Pass `--draft` (or `draft: true`) only if the user asked to open it as a
|
|
147
|
+
draft to keep working on the branch.
|
|
148
|
+
|
|
149
|
+
### Step 5: Report
|
|
150
|
+
|
|
151
|
+
Print the PR URL. If the user asked for auto-merge, arm it (`gh pr merge
|
|
152
|
+
--auto --squash`, or the MCP `enable_pr_auto_merge` tool) — it merges once
|
|
153
|
+
the required `check` and `lint` checks pass.
|
|
154
|
+
|
|
155
|
+
## Stacked PRs
|
|
156
|
+
|
|
157
|
+
When this branch builds on another open PR, target that PR's branch
|
|
158
|
+
instead of main and end the body with `Stacked on #N.` Sanity-check with
|
|
159
|
+
`git log origin/<target>..HEAD --oneline` — only this PR's commits should
|
|
160
|
+
appear. When the base PR squash-merges and its branch auto-deletes, GitHub
|
|
161
|
+
retargets this PR to main and the diff inherits the base PR's commits:
|
|
162
|
+
rebase onto main before review continues.
|
|
163
|
+
|
|
164
|
+
## Common Mistakes
|
|
165
|
+
|
|
166
|
+
- Issue number in the title (belongs in the body as `Closes #N`)
|
|
167
|
+
- A changelog-visible title describing the implementation or the review
|
|
168
|
+
process instead of the user-facing change
|
|
169
|
+
- Narrating the diff or padding the body to look thorough
|
|
170
|
+
- Describing only what changed and never why — even a two-sentence body
|
|
171
|
+
carries the motivation
|
|
172
|
+
- Bundling unrelated changes so no single title can describe the PR
|
|
173
|
+
- Forgetting to push before creating the PR
|
|
174
|
+
- Adding attribution lines beyond the automatic platform footer
|
|
175
|
+
|
|
176
|
+
## Validation
|
|
177
|
+
|
|
178
|
+
- The branch is pushed and the PR targets `main` (or a stacked base per
|
|
179
|
+
the rules above), created ready unless the user asked for a draft.
|
|
180
|
+
- The title matches the lint regex, is ≤ 72 characters, carries no issue
|
|
181
|
+
ref, and — if changelog-visible — reads as the standalone user-facing
|
|
182
|
+
change.
|
|
183
|
+
- The body is why-first first-person prose within its length budget: no
|
|
184
|
+
boilerplate headings, no diff narration, no empty validation, and no
|
|
185
|
+
sentence restating the title or the diff.
|
|
186
|
+
- `Closes #N` appears when the PR resolves an issue; caveats and
|
|
187
|
+
verification gaps are in italics; any stats are plainly stated at the
|
|
188
|
+
end.
|
|
189
|
+
- The PR URL was reported to the user.
|
|
@@ -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 -->
|
|
16
|
+
<!-- CLI commands composed: status, chips, chips sync, chips timing, fdr, captain, waivers, squad grid, squad sell-prices, price-history, player, stats, intel -->
|
|
17
17
|
|
|
18
18
|
# Gameweek Preparation
|
|
19
19
|
|
|
@@ -48,6 +48,9 @@ Extract:
|
|
|
48
48
|
- `deadline` -- the transfer deadline timestamp
|
|
49
49
|
- `phase` -- current status (e.g. "Fixture day 1 of 2", "Between gameweeks")
|
|
50
50
|
- `metadata.format` -- `"classic"`, `"draft"`, or `"both"`. This determines which sub-agents to dispatch and which Phase B commands to run. If format is not present (no entry IDs configured), ask the user.
|
|
51
|
+
- `metadata.season` -- the hyphenated season label (e.g. `"2026-27"`). Referred to below as `{season}`.
|
|
52
|
+
|
|
53
|
+
**Every file this skill reads or writes lives under `[YOUR_OUTPUT_DIR]/{season}/`.** Report filenames carry the gameweek but no season, so a flat output directory lets 2026-27's GW21 file overwrite 2025-26's. Take `{season}` from this command rather than hardcoding it -- a hardcoded label silently rots at the July rollover, which is the failure this partition exists to prevent. `fpl` writes its own reports to the same season directory.
|
|
51
54
|
|
|
52
55
|
### A1.5 -- Chip Status
|
|
53
56
|
|
|
@@ -82,7 +85,7 @@ _Skip unless `mode == "squad-builder"` AND `active_chip ∈ {wildcard, freehit}`
|
|
|
82
85
|
|
|
83
86
|
Locate a matching squad-builder output file and extract the Classic Squad block for embedding. Sets `squad_builder_result = "embed"` on success, or `"rederive"` with a `squad_builder_reason` code on failure. Resolving mode in Phase A (before Phase B) lets sub-agents know up-front which Phase B outputs will actually feed them, and prints the rederive warning banner before any data-gather commands run. All steps are synchronous and must complete before Phase B begins.
|
|
84
87
|
|
|
85
|
-
1. Look for `[YOUR_OUTPUT_DIR]/gw{N}-squad-builder.md`.
|
|
88
|
+
1. Look for `[YOUR_OUTPUT_DIR]/{season}/gw{N}-squad-builder.md`.
|
|
86
89
|
- Not found → `squad_builder_result = "rederive"`, `squad_builder_reason = "file-missing"`. Done.
|
|
87
90
|
2. Parse the file's YAML frontmatter. Required fields: `mode`, `gameweek`.
|
|
88
91
|
- Missing or malformed → `squad_builder_result = "rederive"`, `squad_builder_reason = "frontmatter-malformed"`. Done.
|
|
@@ -103,7 +106,7 @@ Locate a matching squad-builder output file and extract the Classic Squad block
|
|
|
103
106
|
5. Call the extraction helper:
|
|
104
107
|
|
|
105
108
|
```bash
|
|
106
|
-
python3 "${CLAUDE_SKILL_DIR}/scripts/extract_classic_squad.py" --file "[YOUR_OUTPUT_DIR]/gw{N}-squad-builder.md"
|
|
109
|
+
python3 "${CLAUDE_SKILL_DIR}/scripts/extract_classic_squad.py" --file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-squad-builder.md"
|
|
107
110
|
```
|
|
108
111
|
|
|
109
112
|
Parse stdout as JSON regardless of exit code. If JSON parse fails, treat as extraction-failed with a generic error message.
|
|
@@ -200,7 +203,35 @@ fpl stats -p DEF -s clean_sheets --min-minutes 450 -n 10 --available-only --form
|
|
|
200
203
|
|
|
201
204
|
Store each result under a distinct key (e.g. `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.
|
|
202
205
|
|
|
203
|
-
|
|
206
|
+
### B9 -- Season Preview Intel
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
fpl intel --format json
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Hand-curated pre-season notes on minutes, injuries, role and set-piece duty. Optional: most setups
|
|
213
|
+
have none, and every downstream step is unchanged when there is nothing to read.
|
|
214
|
+
|
|
215
|
+
**This is an early-season source by design.** Each section expires at the point real data
|
|
216
|
+
supersedes it -- projected XIs once actual minutes exist, everything eventually. Do not assume the
|
|
217
|
+
schedule: the response's `metadata.decay_schedule` carries the live expiry table and
|
|
218
|
+
`metadata.sections_live` what still counts at this gameweek. Once `sections_live` is empty, expect
|
|
219
|
+
an empty payload -- that is the decay working, not a failure. Its value is concentrated in the
|
|
220
|
+
opening gameweeks, when `starting_xi.py` and `bench_order.py` are ranking players who have not
|
|
221
|
+
played yet.
|
|
222
|
+
|
|
223
|
+
Read `metadata.coverage.usable_as` and store it as `intel_gate`:
|
|
224
|
+
|
|
225
|
+
| `intel_gate` | What sub-agents may do with intel |
|
|
226
|
+
|---|---|
|
|
227
|
+
| `full` | Support **or** oppose a pick |
|
|
228
|
+
| `negative_filter_only` | Only downgrade: injuries, rotation risk, "not nailed on". Never promote. |
|
|
229
|
+
| `none` | Ignore entirely; omit the intel block from every Phase C prompt |
|
|
230
|
+
|
|
231
|
+
With partial coverage the written-up teams carry annotations and the rest carry nothing, so absence
|
|
232
|
+
of a flag would read as absence of merit. Store the payload as `intel` for Phase C.
|
|
233
|
+
|
|
234
|
+
<!-- ADAPT: Add your own further supplementary data sources here. Examples:
|
|
204
235
|
- `fpl preview --save --scout` generates a GW preview with fixture analysis and scout insights.
|
|
205
236
|
Read the saved file and inject its content into Phase C sub-agents as additional context.
|
|
206
237
|
- Newsletter extracts (e.g. community tips, model projections) saved as markdown files
|
|
@@ -252,7 +283,12 @@ Branch on `squad_builder_result` (set in Phase A3; unset on transfer weeks):
|
|
|
252
283
|
> - Chip timing: {B6 output}
|
|
253
284
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
254
285
|
>
|
|
255
|
-
>
|
|
286
|
+
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
287
|
+
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
288
|
+
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
289
|
+
> only to downgrade. A player with no intel is not a worse player.
|
|
290
|
+
>
|
|
291
|
+
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
256
292
|
>
|
|
257
293
|
> **Embedded Classic Squad (from squad-builder — insert this as the `### Classic Squad` section):**
|
|
258
294
|
>
|
|
@@ -262,7 +298,7 @@ Branch on `squad_builder_result` (set in Phase A3; unset on transfer weeks):
|
|
|
262
298
|
>
|
|
263
299
|
> Default: insert the embedded `### Classic Squad` block above **verbatim** as the Classic Squad section of the recommendations file. This is the expected path for nearly every embed-mode run.
|
|
264
300
|
>
|
|
265
|
-
> Late-changes path (use only when clearly warranted): if Phase B data reveals a material change squad-builder could not have seen (a starter ruled out after the file was written, an injury confirmed, a price move materially shifting affordability), you may apply a swap inline. To apply a swap: replace the relevant Starting XI or Bench row, update Captain/Vice if affected, adjust the Budget table (subtract OUT price, add IN price, update Total and Remaining) and Team Exposure table accordingly. Immediately after the block's `#### Alternatives` section, append a blockquote note: `> Late change: {OUT name} → {IN name} — {one-line reason, named source e.g. "ruled out per Thursday presser"}`. One note per swap. **Default is verbatim pass-through. Only deviate for a specific named reason. Never rewrite tables to "improve" formatting or fix perceived typos in squad-builder's output.**
|
|
301
|
+
> Late-changes path (use only when clearly warranted): if Phase B data reveals a material change squad-builder could not have seen (a starter ruled out after the file was written, an injury confirmed, a price move materially shifting affordability), you may apply a swap inline. To apply a swap: replace the relevant Starting XI or Bench row, update Captain/Vice if affected, adjust the Budget table (subtract OUT price, add IN price, update Total and Remaining) and Team Exposure table accordingly. Immediately after the block's `#### Alternatives` section, append a blockquote note: `> Late change: {OUT name} → {IN name} — {one-line reason, named source e.g. "ruled out per Thursday presser"}`. One note per swap. **Default is verbatim pass-through. Only deviate for a specific named reason. Never rewrite tables to "improve" formatting or fix perceived typos in squad-builder's output, and never fold an annotation (formation, notes) into a heading line — if squad-builder wrote `#### Starting XI`, keep the heading exactly as `#### Starting XI`; the formation belongs only on the `**Formation:**` line beneath it.**
|
|
266
302
|
>
|
|
267
303
|
> 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.
|
|
268
304
|
>
|
|
@@ -284,7 +320,7 @@ Branch on `squad_builder_result` (set in Phase A3; unset on transfer weeks):
|
|
|
284
320
|
Before dispatching, print the in-chat warning (variant by `squad_builder_reason`):
|
|
285
321
|
|
|
286
322
|
**Variant A** (`squad_builder_reason == "file-missing"`):
|
|
287
|
-
> ⚠️ **Wildcard/Free Hit detected for GW{N}, but no squad-builder file was found.** Expected `gw{N}-squad-builder.md` in `[YOUR_OUTPUT_DIR]`. Re-derivation will run (weaker squad selection). To use squad-builder output, run `/squad-builder --{wildcard|freehit}` first, then re-run `/gw-prep`.
|
|
323
|
+
> ⚠️ **Wildcard/Free Hit detected for GW{N}, but no squad-builder file was found.** Expected `gw{N}-squad-builder.md` in `[YOUR_OUTPUT_DIR]/{season}`. Re-derivation will run (weaker squad selection). To use squad-builder output, run `/squad-builder --{wildcard|freehit}` first, then re-run `/gw-prep`.
|
|
288
324
|
|
|
289
325
|
**Variant B** (`squad_builder_reason ∈ {"gameweek-mismatch", "mode-mismatch"}`):
|
|
290
326
|
> ⚠️ **Squad-builder file found but does not match this run.** Found `gw{N}-squad-builder.md` with `mode: {file.mode}` / `gameweek: {file.gameweek}`. Expected mode `{active_chip}` / gameweek `{N}`. Re-derivation will run. To use squad-builder output, run `/squad-builder --{wildcard|freehit}` for GW{N}, then re-run `/gw-prep`.
|
|
@@ -318,7 +354,12 @@ Proceed immediately (non-interactive).
|
|
|
318
354
|
> - Chip timing: {B6 output}
|
|
319
355
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
320
356
|
>
|
|
321
|
-
>
|
|
357
|
+
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
358
|
+
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
359
|
+
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
360
|
+
> only to downgrade. A player with no intel is not a worse player.
|
|
361
|
+
>
|
|
362
|
+
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
322
363
|
>
|
|
323
364
|
> 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.
|
|
324
365
|
>
|
|
@@ -390,7 +431,12 @@ Proceed immediately (non-interactive).
|
|
|
390
431
|
> - Chip timing: {B6 output}
|
|
391
432
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
392
433
|
>
|
|
393
|
-
>
|
|
434
|
+
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
435
|
+
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
436
|
+
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
437
|
+
> only to downgrade. A player with no intel is not a worse player.
|
|
438
|
+
>
|
|
439
|
+
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
394
440
|
>
|
|
395
441
|
> Produce the **Classic** section of the output template.
|
|
396
442
|
|
|
@@ -421,7 +467,12 @@ Proceed immediately (non-interactive).
|
|
|
421
467
|
> - Squad: {B4 output}
|
|
422
468
|
> - Stats: {stats_form}, {stats_transfer_momentum}, {stats_mid_xgi}, {stats_fwd_xgi}, {stats_def_clean_sheets} (from B8)
|
|
423
469
|
>
|
|
424
|
-
>
|
|
470
|
+
> - Preview intel: {intel} (from B9 - omit this line entirely when `intel_gate` is `none`)
|
|
471
|
+
> Gate: {intel_gate}. Governs **minutes and role only**, never how good a player is. Attribute it
|
|
472
|
+
> ("Transfer Flow projects him starting"), never assert it. Under `negative_filter_only` use it
|
|
473
|
+
> only to downgrade. A player with no intel is not a worse player.
|
|
474
|
+
>
|
|
475
|
+
> <!-- ADAPT: Add your own further supplementary data sources here (newsletters, external reports) -->
|
|
425
476
|
>
|
|
426
477
|
> 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.
|
|
427
478
|
>
|
|
@@ -435,7 +486,7 @@ After each sub-agent identifies OUT candidates and an IN shortlist (from squad a
|
|
|
435
486
|
python3 "${CLAUDE_SKILL_DIR}/scripts/transfer_eval.py" --out "{out_player_name}" --in "{comma-separated IN candidate names}"
|
|
436
487
|
```
|
|
437
488
|
|
|
438
|
-
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) using the same `⚡ Override: {reason}` pattern as starting XI overrides.
|
|
489
|
+
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.
|
|
439
490
|
|
|
440
491
|
If the script fails (exit 1), fall back to LLM-driven transfer reasoning and note the failure.
|
|
441
492
|
|
|
@@ -449,7 +500,9 @@ python3 "${CLAUDE_SKILL_DIR}/scripts/starting_xi.py" --squad "{comma-separated 1
|
|
|
449
500
|
|
|
450
501
|
<!-- ADAPT: Replace with your squad player names from the squad grid output -->
|
|
451
502
|
|
|
452
|
-
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). Mark any overrides with `⚡ Override: {reason}` in the output. If the script fails (exit 1), fall back to manual selection and note the failure.
|
|
503
|
+
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.
|
|
504
|
+
|
|
505
|
+
**Preview intel is at its most useful here in GW1-3**, when the lineup engine is ranking players with no minutes on the board and a projected XI is the only nailed-on signal available. It feeds this override channel deliberately rather than the scoring inside `starting_xi.py`: an override is visible and attributed in the output, whereas a scoring input would move picks invisibly. Respect `intel_gate` -- under `negative_filter_only`, intel may bench a player but never promote one into the XI.
|
|
453
506
|
|
|
454
507
|
### C4 -- Bench Ordering (skip if `squad_builder_result == "embed"` OR `mode == "benchboost"` — embed has bench order inside block, bench boost has no bench ordering)
|
|
455
508
|
|
|
@@ -467,8 +520,8 @@ Incorporate the bench ordering output into the relevant sections of each sub-age
|
|
|
467
520
|
|
|
468
521
|
Combine the outputs from whichever sub-agents were dispatched into a single recommendations file. If only one format is active, the file contains only that format's section.
|
|
469
522
|
|
|
470
|
-
<!-- ADAPT: Set your output directory -->
|
|
471
|
-
**Output path:** `[YOUR_OUTPUT_DIR]/gw{N}-recommendations.md`
|
|
523
|
+
<!-- ADAPT: Set your output directory. Keep the `/{season}/` segment -- it is what stops a new season overwriting the last one's reports. -->
|
|
524
|
+
**Output path:** `[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md`
|
|
472
525
|
|
|
473
526
|
The file should follow the structure defined in `references/output-template.md`, with both Classic and Draft sections populated.
|
|
474
527
|
|
|
@@ -492,7 +545,7 @@ _Runs after Phase D file write, before Phase E. Not embed-gated._
|
|
|
492
545
|
|
|
493
546
|
```bash
|
|
494
547
|
cd "$FPL_CLI_DIR" && source .venv/bin/activate && python "$FPL_CLI_DIR/.agents/skills/gw-prep/scripts/validate_draft_waivers.py" \
|
|
495
|
-
--recommendations-file "[YOUR_OUTPUT_DIR]/gw{N}-recommendations.md" \
|
|
548
|
+
--recommendations-file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md" \
|
|
496
549
|
--waivers-json /tmp/gw-prep-waivers-{N}.json \
|
|
497
550
|
--squad-grid-json /tmp/gw-prep-squad-grid-{N}.json
|
|
498
551
|
```
|
|
@@ -537,7 +590,7 @@ _Skip unless `squad_builder_result == "embed"`. Transfer and rederive runs do no
|
|
|
537
590
|
1. Run:
|
|
538
591
|
|
|
539
592
|
```bash
|
|
540
|
-
python3 "${CLAUDE_SKILL_DIR}/scripts/extract_classic_squad.py" --from-recommendations --file "[YOUR_OUTPUT_DIR]/gw{N}-recommendations.md"
|
|
593
|
+
python3 "${CLAUDE_SKILL_DIR}/scripts/extract_classic_squad.py" --from-recommendations --file "[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md"
|
|
541
594
|
```
|
|
542
595
|
|
|
543
596
|
Parse stdout as JSON regardless of exit code. If exit is non-zero → emit warning and proceed:
|
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Template structure for the gameweek recommendations file.
|
|
4
4
|
|
|
5
|
-
**Output path:** `[YOUR_OUTPUT_DIR]/gw{N}-recommendations.md`
|
|
5
|
+
**Output path:** `[YOUR_OUTPUT_DIR]/{season}/gw{N}-recommendations.md`
|
|
6
|
+
|
|
7
|
+
`{season}` is the hyphenated season label from `fpl status --format json` (`metadata.season`). The filename carries the gameweek but no season, so the directory segment is what stops a new season's GW21 file overwriting the previous season's.
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
9
11
|
## File Structure
|
|
10
12
|
|
|
11
|
-
**Frontmatter fields** added at the top of the output file. `squad_builder_mode` is load-bearing for `/update-gw-prep`'s Phase C detection rule ("if `squad_builder_mode: true`, switch to squad-builder output format") — omit it on non-embed-mode runs to preserve that detection. `mode` is a forward-compat machine-readable enum for future consumers; always written.
|
|
13
|
+
**Frontmatter fields** added at the top of the output file. `squad_builder_mode` is load-bearing for `/update-gw-prep`'s Phase C detection rule ("if `squad_builder_mode: true`, switch to squad-builder output format") — omit it on non-embed-mode runs to preserve that detection. `mode` is a forward-compat machine-readable enum for future consumers; always written. `season` and `gameweek` let a reader confirm which season's file it opened without inferring it from the path.
|
|
12
14
|
|
|
13
15
|
```markdown
|
|
14
16
|
---
|
|
17
|
+
season: {season} # Always present. Hyphenated label from `fpl status --format json` (metadata.season).
|
|
18
|
+
gameweek: {N} # Always present. Together with season this identifies the file independently of its path.
|
|
15
19
|
squad_builder_mode: true # Only on embed-mode wildcard/freehit runs. Omit on rederive or transfer runs.
|
|
16
20
|
mode: wildcard | freehit | benchboost | transfer # Always present. Enum value matches the active chip or "transfer".
|
|
17
21
|
phase_e_ok: true | false # Embed-mode only. Written by Phase E after post-write validation. Omit on transfer and rederive runs.
|
|
@@ -163,12 +167,12 @@ Any additional context, caveats, or follow-up actions.
|
|
|
163
167
|
|
|
164
168
|
## Fallback Banners (Wildcard/Free Hit re-derivation)
|
|
165
169
|
|
|
166
|
-
When Phase
|
|
170
|
+
When Phase A3 detects a wildcard/freehit week but cannot use the squad-builder file (`squad_builder_result == "rederive"`), prepend the appropriate banner on the line immediately before the Classic section heading in the output file. Choose the variant matching `squad_builder_reason`.
|
|
167
171
|
|
|
168
172
|
`Season Start Classic`, `Season Start Draft`, and `Re-draft` mode files correctly land in Variant B's mode-mismatch branch by design — those modes cannot match an active wildcard/freehit chip.
|
|
169
173
|
|
|
170
174
|
**Variant A** (`squad_builder_reason == "file-missing"`):
|
|
171
|
-
> ⚠️ **Wildcard/Free Hit detected for GW{N}, but no squad-builder file was found.** Expected `gw{N}-squad-builder.md` in `[YOUR_OUTPUT_DIR]`. Re-derivation has run (weaker squad selection). To use squad-builder output next time, run `/squad-builder --{wildcard|freehit}` first, then re-run `/gw-prep`.
|
|
175
|
+
> ⚠️ **Wildcard/Free Hit detected for GW{N}, but no squad-builder file was found.** Expected `gw{N}-squad-builder.md` in `[YOUR_OUTPUT_DIR]/{season}`. Re-derivation has run (weaker squad selection). To use squad-builder output next time, run `/squad-builder --{wildcard|freehit}` first, then re-run `/gw-prep`.
|
|
172
176
|
|
|
173
177
|
**Variant B** (`squad_builder_reason ∈ {"gameweek-mismatch", "mode-mismatch"}`):
|
|
174
178
|
> ⚠️ **Squad-builder file found but does not match this run.** Found `gw{N}-squad-builder.md` with `mode: {file.mode}` / `gameweek: {file.gameweek}`. Expected mode `{active_chip}` / gameweek `{N}`. Re-derivation has run. To use squad-builder output, run `/squad-builder --{wildcard|freehit}` for GW{N}, then re-run `/gw-prep`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Shared startup for gw-prep scripts that import fpl-cli agents directly."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from fpl_cli.paths import UserDirError, ensure_legacy_migration, load_env_files
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def bootstrap_user_dirs() -> None:
|
|
12
|
+
"""Run fpl-cli's legacy-file migration before any agent resolves paths.
|
|
13
|
+
|
|
14
|
+
The CLI does this in its own entry point; scripts importing agents
|
|
15
|
+
directly must do it themselves or a user with files in the legacy
|
|
16
|
+
repo-root dirs silently gets empty settings and regenerated ratings.
|
|
17
|
+
A bad FPL_CLI_* override surfaces here as a clean JSON error instead
|
|
18
|
+
of a traceback mid-agent.
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
load_env_files()
|
|
22
|
+
ensure_legacy_migration()
|
|
23
|
+
except UserDirError as exc:
|
|
24
|
+
json.dump({"error": True, "messages": [str(exc)]}, sys.stdout, indent=2)
|
|
25
|
+
sys.exit(1)
|
|
@@ -15,6 +15,8 @@ import asyncio
|
|
|
15
15
|
import json
|
|
16
16
|
import sys
|
|
17
17
|
|
|
18
|
+
from _bootstrap import bootstrap_user_dirs
|
|
19
|
+
|
|
18
20
|
from fpl_cli.agents.analysis.bench_order import BenchOrderAgent
|
|
19
21
|
from fpl_cli.api.fpl import FPLClient
|
|
20
22
|
from fpl_cli.models.player import resolve_player
|
|
@@ -62,6 +64,7 @@ async def _run(starting_names: list[str], bench_names: list[str]) -> None:
|
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
def main() -> None:
|
|
67
|
+
bootstrap_user_dirs()
|
|
65
68
|
parser = argparse.ArgumentParser(description="Bench order analysis")
|
|
66
69
|
parser.add_argument(
|
|
67
70
|
"--starting", required=True,
|