RoadTrip Navigator
Turn "start + days" or "an existing route" into a road trip you can
actually drive: paced into days, with overnight stops, fuel/charging, park
reservations, seasonal road risks, and a map-first single-file HTML page.
North American road trips revolve around the car, not flights: how many
hours do we drive today, where do we sleep, will we make it on the fuel/charge
we have, and is the road even open. That focus is what this skill adds on top
of a generic "list of attractions."
When to use
Use this skill whenever the request is about driving a multi-stop trip in the
US / Canada / Mexico (see read-when triggers). If the user only wants a single
city guide or a flight itinerary, this is not the right skill.
Two entry modes
Detect the mode up front (see scripts/helper.py for the heuristic):
- Light mode (plan it for me): user gives a start, a rough region or
destination, day count, and party/vehicle. → Run the full 7-step workflow,
designing the route yourself.
- Heavy mode (verify my route): user pastes/links/screenshots an existing
route. → Skip route invention. Parse their route into the schema, then
verify and fill gaps: driving segmentation, overnight realism, fuel/charge
coverage, reservation countdown, seasonal closures, and produce the page.
When unsure which mode, ask one short question. Otherwise infer and proceed.
The five things that make this more than a list
These are where pure-model answers fail and where this skill earns its keep:
- Daily driving segmentation (the core). Slice the whole route into days
under a sane daily drive limit, place an overnight at each segment end, and
validate each day: drive ≤ limit, arrive before dark, no stop hits a
closed gate, fatigue buffer. This is the road-trip equivalent of
connection-checking — most AI itineraries skip it.
- Reservation countdown. Recreation.gov campgrounds often release ~6 months
out; popular timed-entry a few days out; in-park lodges up to ~13 months out.
From the departure date, work backwards into a "book by" to-do list.
- Fuel / charge planning. Gas: flag long empty stretches ("next fuel in
X mi"). EV: plan a charging corridor against the vehicle's range and note
whether each leg makes it, charger power, and a backup.
- Seasonal road conditions & closures. Mountain passes that close in winter
(Going-to-the-Sun, Tioga Pass, Trail Ridge Rd), wildfire/hurricane/snow
season. If the travel date hits one, down-rank or reroute and say so.
- Timezones & borders. Correct arrival times across timezone lines; for
border crossings, flag documents / vehicle papers / insurance / wait times.
Workflow (7 steps)
Run scripts/helper.py "<user request>" first — it parses slots, guesses the
entry mode, picks the trip region (for HTML theming), and prints what's still
missing. Use its output to drive the steps below.
Step 1 — Collect requirements (slot filling)
Required: start, travel date, days, party makeup, vehicle (gas/EV/RV + range).
Optional: destination/region, budget, preferences (scenic vs. fast, hike
intensity, loop vs. one-way, border crossing). Only ask follow-ups for missing
required slots; fill the rest with sensible defaults and proceed.
Validate place names before planning. Slot presence is not slot truth: a
made-up start like "ABC" parses fine and would otherwise flow straight into a
fabricated route. Run every user-supplied place — start, destination, named
waypoints; in heavy mode each day's from/to towns — through
python3 tools/places_client.py "<name>" and branch on its verdict:
match → adopt the returned canonical name + coordinates; did-you-mean →
confirm the intended place with the user (one short question, same spirit as
the required-slot follow-ups); no-match → stop and ask — never plan a
route around a place you could not verify; unverified (offline) → use your
own judgment and ask about any name you don't recognize. A match with
outsideNA: true is a real place outside US/Canada/Mexico — tell the user
it's beyond this skill's coverage instead of calling it fake.
Step 2 — Route / destination planning (if not given)
Decide loop vs. one-way first (affects one-way drop fees and pacing).
For region-level input ("the Southwest", "Pacific Northwest"): search
candidates → seasonal & closure check → shortlist. Compute rough total miles /
driving days for the shortlist and drop any "can't be driven in N days" option.
Present two candidate routes before committing (light mode only). Once the
shortlist is down to viable options, draft exactly two genuinely distinct
routes yourself — e.g. a faster direct corridor vs. a scenic detour, or two
different geographic loops — each with a short label, a one-line summary, and
rough total miles/driving days. Show both to the user and ask them to pick
(or say "surprise me") before moving to Step 3. This is a single short
question, same spirit as the required-slot follow-up in Step 1 — don't
draft a full itinerary for either option first. If the conversation is
one-shot and no reply is possible, pick the better-rated option yourself,
proceed, and note the alternative you didn't take. Skip this entirely in
heavy mode (the user already supplied a route) or once the user has already
chosen. Carry both options into scripts/helper.compare_routes() to populate
routeOptions[] (Phase-3 module below) so the rendered page shows the
comparison table with the chosen route flagged.
Step 3 — Daily driving segmentation (core; see five-things #1)
- Split by a daily drive limit (default: relaxed adults ≤ 4–5h;
with kids/seniors ≤ 3–4h; user-adjustable).
- Put an overnight at each segment end (has lodging, supplies, good for the
next morning).
- Validate: arrive before dark, no stop hits a closed gate, long legs
have a fuel/charge point mid-way.
- If infeasible: cut miles / add a night / pick a closer overnight town.
- Surface risks explicitly in the day, e.g. "no fast charger for 180 mi on this
leg — charge to full before leaving."
Rule of thumb: plan by daylight, not by odometer — a day that ends after
dark fails at the trailhead, not on the map.
Step 4 — Parallel research (sub-agents)
Fan out (one concern per sub-agent, run concurrently): weather (per day),
lodging/campgrounds (price + booking difficulty), fuel/charging points,
attractions & tickets/permits, food, scenic byways & hikes, Reddit real-world
gotchas. Delegation rule: instruct each sub-agent to hit official APIs first
(NPS / NWS / Recreation.gov / Open Charge Map) and fall back to web search only
on failure. See reference.md for the tool routing table and tools/.
Step 5 — Reservation countdown (see five-things #2)
From the departure date, generate a "book by" to-do list:
campgrounds (Recreation.gov, ~T-6 months), timed-entry / wilderness permits
(per park rule, T-X days), popular in-park lodges (up to ~T-13 months),
one-way car/RV rental (lock price early). Render at the top of the page as
Attractions / Restaurants / Hotels tabs, each with its own deadline timeline.
Populate bookingCountdown[] and set each item's optional category to
attraction, restaurant, or hotel (use the closest category for legacy tasks).
Every planned stay must also be present in lodging[]; the Hotels tab renders that
complete list once and merges any matching hotel deadline from bookingCountdown[].
The Attractions and Restaurants tabs likewise render the complete visitable stops[]
and daily meal list, then merge matching deadlines instead of hiding items without one.
An unmatched attraction or meal is labeled as needing no advance booking; an unmatched
stay is labeled with an unknown deadline because lodging still needs to be reserved.
Every park, hike, scenic stop, and tour must carry an admission object whose status
is free, included, paid, or unknown. Add a structured per-stop price for paid
admission only when supportable; never derive it from an aggregate budget line. The view
renders these as Free, Included in park pass, a concrete amount, or Price unavailable.
Include a structured price when known: hotel per night, restaurant per person,
and attraction ticket/permit price when required. Use amount 0 for a free reservation;
never invent an exact live price when it cannot be supported.
Step 6 — Budget (with reliability grading)
Tag every line verified / reference(~) / estimate(≈). Road-trip specifics:
fuel = total miles ÷ MPG × gas price (or EV charging cost); tolls; park entry or
the America the Beautiful annual pass; one-way drop fee; campground; lodging;
food. Force a bottom disclaimer: prices are dynamic, confirm before departure.
Step 7 — Generate the single-file HTML (map-first)
- Write the data to
tripData.json first (data/view separation — editable,
re-renderable).
- Render:
python3 assets/generate.py tripData.json -o trip.html
→ Leaflet map (numbered stops + ordered polyline) + one-tap mobile nav
(Google/Apple deep links) + daily timeline + reservation to-do + budget.
Responsive (mobile single-column / desktop multi-column) + print friendly.
- Validate before delivering (plan §9): the generator already does a light
schema check and a JSON parse of the injected data. Optionally syntax-check
the inline JS, then open/preview.
- Full-page disclaimer: AI-assembled, may be out of date, verify with official
sources.
Output contract
- Always produce both
tripData.json and the rendered trip.html.
- Units: miles, °F, MPG, USD by default; switch to km/°C/local currency on
Canadian/Mexican legs and note the change. A trip entirely within China
prices its budget in CNY (¥) — never converted into USD.
- Never invent a precise reservation availability, live charger occupancy, or
minute-level traffic — point to the official app / Recreation.gov / nav.
Honesty boundaries (Phase 1)
Do not promise: exact live fuel/electricity prices, live charger occupancy,
minute-level traffic, live campground availability, or replacing turn-by-turn
navigation. For these, tell the user to confirm via the official app /
Recreation.gov / their navigation app in real time. The page's job is to be
right the morning you leave, not merely impressive the night it was generated.
Files
reference.md — tripData schema, reliability grading, tool routing table.
AGENTS.md ("Worked examples") — typical prompts and expected outputs.
assets/generate.py — tripData.json → single-file HTML.
assets/template.html — the HTML/JS renderer (Leaflet map + timeline).
assets/tripData.example.json / assets/preview.html — Southwest 7-day demo.
assets/tripData.tahoe.json / assets/preview-tahoe.html — Sunnyvale→Tahoe
3-day demo (mountain theme, state-park reservations, Sierra snow risk).
assets/tripData.pnw.json / assets/preview-pnw.html — Seattle→Vancouver→
Whistler EV cross-border demo (exercises all three Phase-3 modules below).
Phase-3 modules (implemented)
These render as extra sections when their data is present (see reference.md):
- Multi-route comparison —
scripts/helper.compare_routes(options, party)
→ routeOptions[]. Feeds from the Step 2 two-route pick above; it auto-rates
drive intensity and renders a comparison table with the chosen route flagged.
- Cross-border —
tools/border_client.trip_section([("US","CA",rental),...])
→ crossBorder. Per-crossing documents / insurance / customs / unit-switch
checklist for US↔CA↔MX. Note the key asymmetry it encodes: US insurance is
usually valid in Canada but never in Mexico (buy Mexican insurance).
- Duty-free exemption —
tools/customs_client.personal_exemption(residence, hours_abroad, used_within_30_days=False) → the per-person allowance quoted in
crossBorder customs notes. Encodes the 24h/48h tiers (US: USD 800 at 48h+,
once per 30 days, else USD 200; CA: 0 / CAD 200 / CAD 800; MX land: USD 300)
with EN + 中文 note strings — quote the tool, never recall these amounts.
- EV charging corridor —
tools/charging_client.corridor(legs, usableRange, winter_derate=...) → evPlan. Simulates state-of-charge leg by leg, sets a
recommended charge-to at each stop, and flags legs that won't make the buffer.
Pass winter_derate (e.g. 0.25) for cold-weather range loss.
scripts/helper.py — input parsing, entry-mode + region detection, slot check.
tools/*.py — per-source clients, each with a web-search fallback
(incl. border_client.py and charging_client.corridor()).
1---2name: roadtrip-navigator3description: Generate North American road-trip itineraries as a map-first, offline-friendly single-file HTML page. Plans around daily driving segments, overnight stops, fuel/EV-charging, national-park reservations (Recreation.gov / NPS), seasonal road closures, and timezone/border crossings — for executable, decision-ready trips. Two entry modes: give a start + region/destination + days and it plans the whole route, or hand it an existing route and it verifies, fills gaps, and produces the page.4---56# RoadTrip Navigator78Turn **"start + days"** or **"an existing route"** into a road trip you can9actually drive: paced into days, with overnight stops, fuel/charging, park10reservations, seasonal road risks, and a map-first single-file HTML page.1112North American road trips revolve around the **car**, not flights: *how many13hours do we drive today, where do we sleep, will we make it on the fuel/charge14we have, and is the road even open.* That focus is what this skill adds on top15of a generic "list of attractions."1617## When to use1819Use this skill whenever the request is about driving a multi-stop trip in the20US / Canada / Mexico (see `read-when` triggers). If the user only wants a single21city guide or a flight itinerary, this is not the right skill.2223## Two entry modes2425Detect the mode up front (see `scripts/helper.py` for the heuristic):2627- **Light mode (plan it for me):** user gives a start, a rough region or28 destination, day count, and party/vehicle. → Run the full 7-step workflow,29 designing the route yourself.30- **Heavy mode (verify my route):** user pastes/links/screenshots an existing31 route. → Skip route invention. Parse their route into the schema, then32 *verify and fill gaps*: driving segmentation, overnight realism, fuel/charge33 coverage, reservation countdown, seasonal closures, and produce the page.3435When unsure which mode, ask one short question. Otherwise infer and proceed.3637## The five things that make this more than a list3839These are where pure-model answers fail and where this skill earns its keep:40411. **Daily driving segmentation (the core).** Slice the whole route into days42 under a sane daily drive limit, place an overnight at each segment end, and43 *validate* each day: drive ≤ limit, arrive before dark, no stop hits a44 closed gate, fatigue buffer. This is the road-trip equivalent of45 connection-checking — most AI itineraries skip it.462. **Reservation countdown.** Recreation.gov campgrounds often release ~6 months47 out; popular timed-entry a few days out; in-park lodges up to ~13 months out.48 From the departure date, work backwards into a "book by" to-do list.493. **Fuel / charge planning.** Gas: flag long empty stretches ("next fuel in50 X mi"). EV: plan a charging corridor against the vehicle's range and note51 whether each leg makes it, charger power, and a backup.524. **Seasonal road conditions & closures.** Mountain passes that close in winter53 (Going-to-the-Sun, Tioga Pass, Trail Ridge Rd), wildfire/hurricane/snow54 season. If the travel date hits one, down-rank or reroute and say so.555. **Timezones & borders.** Correct arrival times across timezone lines; for56 border crossings, flag documents / vehicle papers / insurance / wait times.5758## Workflow (7 steps)5960> Run `scripts/helper.py "<user request>"` first — it parses slots, guesses the61> entry mode, picks the trip region (for HTML theming), and prints what's still62> missing. Use its output to drive the steps below.6364### Step 1 — Collect requirements (slot filling)65Required: **start, travel date, days, party makeup, vehicle (gas/EV/RV + range)**.66Optional: destination/region, budget, preferences (scenic vs. fast, hike67intensity, loop vs. one-way, border crossing). Only ask follow-ups for missing68**required** slots; fill the rest with sensible defaults and proceed.6970**Validate place names before planning.** Slot presence is not slot truth: a71made-up start like "ABC" parses fine and would otherwise flow straight into a72fabricated route. Run every user-supplied place — start, destination, named73waypoints; in heavy mode each day's from/to towns — through74`python3 tools/places_client.py "<name>"` and branch on its verdict:75`match` → adopt the returned canonical name + coordinates; `did-you-mean` →76confirm the intended place with the user (one short question, same spirit as77the required-slot follow-ups); `no-match` → **stop and ask — never plan a78route around a place you could not verify**; `unverified` (offline) → use your79own judgment and ask about any name you don't recognize. A `match` with80`outsideNA: true` is a real place outside US/Canada/Mexico — tell the user81it's beyond this skill's coverage instead of calling it fake.8283### Step 2 — Route / destination planning (if not given)84Decide **loop vs. one-way** first (affects one-way drop fees and pacing).85For region-level input ("the Southwest", "Pacific Northwest"): search86candidates → seasonal & closure check → shortlist. Compute rough total miles /87driving days for the shortlist and drop any "can't be driven in N days" option.8889**Present two candidate routes before committing (light mode only).** Once the90shortlist is down to viable options, draft **exactly two** genuinely distinct91routes yourself — e.g. a faster direct corridor vs. a scenic detour, or two92different geographic loops — each with a short label, a one-line summary, and93rough total miles/driving days. Show both to the user and ask them to pick94(or say "surprise me") before moving to Step 3. This is a single short95question, same spirit as the required-slot follow-up in Step 1 — don't96draft a full itinerary for either option first. If the conversation is97one-shot and no reply is possible, pick the better-rated option yourself,98proceed, and note the alternative you didn't take. Skip this entirely in99heavy mode (the user already supplied a route) or once the user has already100chosen. Carry both options into `scripts/helper.compare_routes()` to populate101`routeOptions[]` (Phase-3 module below) so the rendered page shows the102comparison table with the chosen route flagged.103104### Step 3 — Daily driving segmentation (core; see five-things #1)1051. Split by a **daily drive limit** (default: relaxed adults ≤ 4–5h;106 with kids/seniors ≤ 3–4h; user-adjustable).1072. Put an **overnight** at each segment end (has lodging, supplies, good for the108 next morning).1093. Validate: arrive **before dark**, no stop hits a **closed gate**, long legs110 have a fuel/charge point mid-way.1114. If infeasible: cut miles / add a night / pick a closer overnight town.1125. Surface risks explicitly in the day, e.g. "no fast charger for 180 mi on this113 leg — charge to full before leaving."114115Rule of thumb: **plan by daylight, not by odometer** — a day that ends after116dark fails at the trailhead, not on the map.117118### Step 4 — Parallel research (sub-agents)119Fan out (one concern per sub-agent, run concurrently): weather (per day),120lodging/campgrounds (price + booking difficulty), fuel/charging points,121attractions & tickets/permits, food, scenic byways & hikes, Reddit real-world122gotchas. **Delegation rule: instruct each sub-agent to hit official APIs first123(NPS / NWS / Recreation.gov / Open Charge Map) and fall back to web search only124on failure.** See `reference.md` for the tool routing table and `tools/`.125126### Step 5 — Reservation countdown (see five-things #2)127From the departure date, generate a "book by" to-do list:128campgrounds (Recreation.gov, ~T-6 months), timed-entry / wilderness permits129(per park rule, T-X days), popular in-park lodges (up to ~T-13 months),130one-way car/RV rental (lock price early). Render at the top of the page as131Attractions / Restaurants / Hotels tabs, each with its own deadline timeline.132Populate `bookingCountdown[]` and set each item's optional `category` to133`attraction`, `restaurant`, or `hotel` (use the closest category for legacy tasks).134Every planned stay must also be present in `lodging[]`; the Hotels tab renders that135complete list once and merges any matching hotel deadline from `bookingCountdown[]`.136The Attractions and Restaurants tabs likewise render the complete visitable `stops[]`137and daily `meal` list, then merge matching deadlines instead of hiding items without one.138An unmatched attraction or meal is labeled as needing no advance booking; an unmatched139stay is labeled with an unknown deadline because lodging still needs to be reserved.140Every park, hike, scenic stop, and tour must carry an `admission` object whose `status`141is `free`, `included`, `paid`, or `unknown`. Add a structured per-stop price for paid142admission only when supportable; never derive it from an aggregate budget line. The view143renders these as Free, Included in park pass, a concrete amount, or Price unavailable.144Include a structured `price` when known: hotel per night, restaurant per person,145and attraction ticket/permit price when required. Use amount `0` for a free reservation;146never invent an exact live price when it cannot be supported.147148### Step 6 — Budget (with reliability grading)149Tag every line **verified / reference(~) / estimate(≈)**. Road-trip specifics:150fuel = total miles ÷ MPG × gas price (or EV charging cost); tolls; park entry or151the **America the Beautiful** annual pass; one-way drop fee; campground; lodging;152food. Force a bottom disclaimer: prices are dynamic, confirm before departure.153154### Step 7 — Generate the single-file HTML (map-first)1551. Write the data to **`tripData.json`** first (data/view separation — editable,156 re-renderable).1572. Render: `python3 assets/generate.py tripData.json -o trip.html`158 → Leaflet map (numbered stops + ordered polyline) + one-tap mobile nav159 (Google/Apple deep links) + daily timeline + reservation to-do + budget.160 Responsive (mobile single-column / desktop multi-column) + print friendly.1613. **Validate before delivering** (plan §9): the generator already does a light162 schema check and a JSON parse of the injected data. Optionally syntax-check163 the inline JS, then open/preview.1644. Full-page disclaimer: AI-assembled, may be out of date, verify with official165 sources.166167## Output contract168169- Always produce **both** `tripData.json` and the rendered `trip.html`.170- Units: miles, °F, MPG, USD by default; switch to km/°C/local currency on171 Canadian/Mexican legs and note the change. A trip entirely within China172 prices its budget in CNY (¥) — never converted into USD.173- Never invent a precise reservation availability, live charger occupancy, or174 minute-level traffic — point to the official app / Recreation.gov / nav.175176## Honesty boundaries (Phase 1)177178Do **not** promise: exact live fuel/electricity prices, live charger occupancy,179minute-level traffic, live campground availability, or replacing turn-by-turn180navigation. For these, tell the user to confirm via the official app /181Recreation.gov / their navigation app in real time. The page's job is to be182right the morning you leave, not merely impressive the night it was generated.183184## Files185186- `reference.md` — tripData schema, reliability grading, tool routing table.187- `AGENTS.md` ("Worked examples") — typical prompts and expected outputs.188- `assets/generate.py` — `tripData.json` → single-file HTML.189- `assets/template.html` — the HTML/JS renderer (Leaflet map + timeline).190- `assets/tripData.example.json` / `assets/preview.html` — Southwest 7-day demo.191- `assets/tripData.tahoe.json` / `assets/preview-tahoe.html` — Sunnyvale→Tahoe192 3-day demo (mountain theme, state-park reservations, Sierra snow risk).193- `assets/tripData.pnw.json` / `assets/preview-pnw.html` — Seattle→Vancouver→194 Whistler EV cross-border demo (exercises all three Phase-3 modules below).195196## Phase-3 modules (implemented)197198These render as extra sections when their data is present (see `reference.md`):199200- **Multi-route comparison** — `scripts/helper.compare_routes(options, party)`201 → `routeOptions[]`. Feeds from the Step 2 two-route pick above; it auto-rates202 drive intensity and renders a comparison table with the chosen route flagged.203- **Cross-border** — `tools/border_client.trip_section([("US","CA",rental),...])`204 → `crossBorder`. Per-crossing documents / insurance / customs / unit-switch205 checklist for US↔CA↔MX. Note the key asymmetry it encodes: US insurance is206 usually valid in **Canada** but **never in Mexico** (buy Mexican insurance).207- **Duty-free exemption** — `tools/customs_client.personal_exemption(residence,208 hours_abroad, used_within_30_days=False)` → the per-person allowance quoted in209 `crossBorder` customs notes. Encodes the 24h/48h tiers (US: USD 800 at 48h+,210 once per 30 days, else USD 200; CA: 0 / CAD 200 / CAD 800; MX land: USD 300)211 with EN + 中文 note strings — quote the tool, never recall these amounts.212- **EV charging corridor** — `tools/charging_client.corridor(legs, usableRange,213 winter_derate=...)` → `evPlan`. Simulates state-of-charge leg by leg, sets a214 recommended charge-to at each stop, and flags legs that won't make the buffer.215 Pass `winter_derate` (e.g. 0.25) for cold-weather range loss.216- `scripts/helper.py` — input parsing, entry-mode + region detection, slot check.217- `tools/*.py` — per-source clients, each with a web-search fallback218 (incl. `border_client.py` and `charging_client.corridor()`).