Provides top 3 surf spots in a region ranked by wave height, wind direction, and wind speed using Surfline data and a defined scoring rubric.
# Surf Region Ranking Skill Use this skill when the user asks for the best surf spots in a region based on wave height, wind direction, and wind speed. ## Goal For a user-specified region: 1. Run the surfline_region_report.py script to gather surf, wind, and tide data per spot in the specified region. 2. Score every spot in the region inside the agent (not in Go). 3. Return the top 3 spots by total score with explanations of their grades. Category scores are: - Wave height `/10` - Wind direction `/10` - Wind speed `/10` Total score is `/30`. ## Important architecture rule The Python script is **data collection only**. It must not be treated as a scorer or ranker. - Script responsibility: fetch and output spot data - Agent responsibility: score spots and choose top 3 ## Required script command Preferred (preset regions): - `python3 surfline_region_report.py --region <region> --hours <hours> --output json` Custom spot IDs: - `python3 surfline_region_report.py --spots "<spotId1,spotId2,...>" --hours <hours> --output json` Default `hours` to `24` unless the user asks otherwise. ## Regions supported by preset - `north-orange-county` - `south-orange-county` - `san-diego` - `santa-cruz` If the user gives an unsupported region, ask for Surfline spot IDs for that region. ## Parse JSON output from the script Read `spots[]` from script output. Each spot includes: - `spotId` - `avgSurfMinFt` - `avgSurfMaxFt` - `avgPrimarySwellFt` - `avgWindMph` - `avgWindDirectionDeg` - `avgTideFt` - `validHours` Use `avgSurfMaxFt` as the main wave-height comparison value. ## Scoring rubric (agent-side, 0-10 each) Compute scores for all valid spots in the selected region. ### 1) Wave height score (0-10, relative within region) Let `waveMax = avgSurfMaxFt`. - `waveScore = 10 * (waveMax - minWaveMax) / (maxWaveMax - minWaveMax)` - If all spots have identical `waveMax`, assign `waveScore = 5` for all - Clamp to `[0, 10]` Interpretation: bigger rideable waves in the same region score higher. ### 2) Wind speed score (0-10, lower is better) Let `windSpeed = avgWindMph`. - `<= 4 mph`: `10` - `> 4 and <= 8`: `8` - `> 8 and <= 12`: `6` - `> 12 and <= 16`: `4` - `> 16 and <= 20`: `2` - `> 20`: `0` ### 3) Wind direction score (0-10) Default directional rubric unless user provides local break orientation: - `315-360` or `0-45`: offshore/cross-offshore -> `9` - `46-90` or `271-314`: side-shore -> `6` - `91-270`: onshore/cross-onshore -> `2` If user provides local direction guidance for a break, that guidance overrides defaults. ## Total score - `totalScore = waveScore + windSpeedScore + windDirectionScore` - Max total = `30` - Round all category and total scores to 1 decimal place ## Required response format Return only the top 3 spots sorted by `totalScore` descending. For each ranked spot include: - Spot ID (or spot name if available) - Wave score `/10` and `avgSurfMaxFt` - Wind direction score `/10` and `avgWindDirectionDeg` plus label - Wind speed score `/10` and `avgWindMph` - Total score `/30` - 1-3 sentence explanation of why the spot earned the rank After the list, include one short sentence naming the #1 recommendation. ## Behavior rules - Always score all valid spots before picking top 3. - If fewer than 3 valid spots exist, return all and explain why. - If script fetch fails for a spot, exclude it and mention exclusion. - Do not invent missing values; only use data returned by the script.
don't have the plugin yet? install it then click "run inline in claude" again.
extracted implicit decision logic (unsupported regions, script failures, identical wave heights, user overrides) into explicit decision points; added edge cases (network timeouts, reduced forecast windows, missing spot data); clarified architecture boundary between script (data collection only) and agent (scoring and ranking); standardized scoring procedures with exact formulas and lookup tables; documented all inputs and parameters with defaults; specified output format precisely.
this skill ranks the top 3 surf spots in a user-specified region by evaluating wave height, wind speed, and wind direction against a defined scoring rubric. use it when a surfer asks for the best breaks in an area right now. the skill fetches live surfline data, scores every valid spot in the region on the agent side (not in the data collection script), and returns ranked recommendations with transparent scoring and reasoning.
surfline data source:
surfline_region_report.py script (local python script, no auth required if script is pre-authenticated)north-orange-county, south-orange-county, san-diego, santa-cruzuser parameters:
region (string): preset region name or "custom"spots (string, optional): comma-separated surfline spot IDs if region is unsupportedhours (integer, optional): forecast window in hours; default to 24 if not specifiedlocal_break_orientation (string, optional): user-provided wind direction preference for a specific break (overrides default directional rubric)time context:
validate region input:
region stringnorth-orange-county, south-orange-county, san-diego, santa-cruz), proceed to step 2fetch spot data from surfline:
python3 surfline_region_report.py --region <region> --hours <hours> --output json (preset) OR python3 surfline_region_report.py --spots "<spotId1,spotId2,...>" --hours <hours> --output json (custom)spots[] array; each spot object includes spotId, avgSurfMinFt, avgSurfMaxFt, avgPrimarySwellFt, avgWindMph, avgWindDirectionDeg, avgTideFt, validHours2b. handle custom spot IDs:
--spots flag instead of --regionparse and validate spot data:
spots[] arrayspotId, avgSurfMaxFt, avgWindMph, avgWindDirectionDegcompute wave height score (0-10, relative within region):
avgSurfMaxFt for all spotsminWaveMax and maxWaveMax across all spotsminWaveMax == maxWaveMax (all spots identical wave height), assign waveScore = 5.0 to all spotswaveScore = 10 * (avgSurfMaxFt - minWaveMax) / (maxWaveMax - minWaveMax)[0.0, 10.0]waveScore for each spotcompute wind speed score (0-10, lower is better):
avgWindMph for all spots<= 4 mph: 10.0> 4 and <= 8: 8.0> 8 and <= 12: 6.0> 12 and <= 16: 4.0> 16 and <= 20: 2.0> 20: 0.0windSpeedScore for each spotcompute wind direction score (0-10):
avgWindDirectionDeg for all spots, optional local_break_orientation parameterlocal_break_orientation for that specific spot315-360 or 0-45: offshore/cross-offshore -> 9.046-90 or 271-314: side-shore -> 6.091-270: onshore/cross-onshore -> 2.0windDirectionScore and directional label for each spotcompute total score:
waveScore, windSpeedScore, windDirectionScore for all spotstotalScore = waveScore + windSpeedScore + windDirectionScore30.0totalScore descendingselect and format top 3:
totalScore (or fewer if fewer than 3 valid spots exist)generate final recommendation:
if region is unsupported:
if script fetch fails (network timeout, API error, malformed response):
if fewer than 3 valid spots exist in the region:
if all spots have identical wave height:
waveScore = 5.0 to all (not relative ranking)if user provides local_break_orientation for a specific spot:
if user requests forecast window outside 24 hours (e.g., 48, 72):
--hours <user_value> to scriptif spot data returns validHours < hours_requested:
return a markdown-formatted response containing:
ranked spot list (top 3 or fewer):
### #N. [Spot Name/ID]**Wave:** [score]/10 ([avgSurfMaxFt] ft max)**Wind Direction:** [score]/10 ([avgWindDirectionDeg]°, [label])**Wind Speed:** [score]/10 ([avgWindMph] mph)**Total Score:** [totalScore]/30final recommendation (single sentence):
**#1 Pick:** [Spot Name/ID] is your best bet right now.exclusions note (if applicable):
metadata (optional but recommended):
file location: response is delivered in-channel or to user directly; no file save required unless user specifies export
the skill worked if: