tripkit 1.1.0 → 1.2.0
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.
- package/CHANGELOG.md +17 -5
- package/README.md +4 -1
- package/agent/AGENT-SKILL.md +114 -68
- package/examples/new-england-fall-2026.yaml +471 -0
- package/examples/nyc-long-weekend-2026.yaml +283 -0
- package/examples/oregon-spring-2026.yaml +2 -0
- package/examples/southwest-parks-2026.yaml +463 -0
- package/package.json +6 -2
- package/renderers/html/tripkit-renderer.html +74 -15
- package/schema/tripkit.schema.yaml +7 -0
- package/scripts/check-skill-coverage.js +66 -0
- package/validate.js +67 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# Southwest National Parks Loop 2026
|
|
3
|
+
# 5-day Utah / Arizona red-rock loop from Las Vegas
|
|
4
|
+
# Showcases: heavy hiking, desert palette, multi-park logistics
|
|
5
|
+
# ============================================================
|
|
6
|
+
|
|
7
|
+
trip:
|
|
8
|
+
title: "Southwest Parks Loop 2026"
|
|
9
|
+
subtitle: "Zion · Bryce · Page · Grand Canyon"
|
|
10
|
+
dates: "May 2–6, 2026"
|
|
11
|
+
total_days: 5
|
|
12
|
+
total_miles: "~900 mi"
|
|
13
|
+
total_stops: 13
|
|
14
|
+
travelers:
|
|
15
|
+
adults: 2
|
|
16
|
+
children: 0
|
|
17
|
+
origin: "Las Vegas, NV (fly in / fly out)"
|
|
18
|
+
origin_lat: 36.084
|
|
19
|
+
origin_lng: -115.154
|
|
20
|
+
vehicle: "Rental SUV"
|
|
21
|
+
|
|
22
|
+
days:
|
|
23
|
+
# ========== DAY 1 ==========
|
|
24
|
+
- number: 1
|
|
25
|
+
title: "Las Vegas → Zion"
|
|
26
|
+
date: "Saturday, May 2"
|
|
27
|
+
status: upcoming
|
|
28
|
+
color: "#c25e1f"
|
|
29
|
+
|
|
30
|
+
summary:
|
|
31
|
+
drive: "3 hrs"
|
|
32
|
+
hike: "1–2 hrs"
|
|
33
|
+
miles: "~165 mi"
|
|
34
|
+
|
|
35
|
+
weather:
|
|
36
|
+
high: "82°F"
|
|
37
|
+
low: "55°F"
|
|
38
|
+
sky: "Sunny, dry"
|
|
39
|
+
rain_chance: "0%"
|
|
40
|
+
note: "Bring a hat and 2L of water minimum — desert sun is no joke."
|
|
41
|
+
|
|
42
|
+
meals:
|
|
43
|
+
breakfast: "In-N-Out near LAS rental car return — quick fuel"
|
|
44
|
+
lunch: "Valley of Fire visitor center picnic area"
|
|
45
|
+
dinner: "Oscar's Cafe, Springdale — patio overlooks the canyon at sunset"
|
|
46
|
+
|
|
47
|
+
lodging:
|
|
48
|
+
name: "Best Western Plus Zion Canyon Inn & Suites"
|
|
49
|
+
location: "Springdale, Utah"
|
|
50
|
+
price_estimate: "~$220/night"
|
|
51
|
+
confirmation: "XXXXX1234"
|
|
52
|
+
booked: true
|
|
53
|
+
lat: 37.1924
|
|
54
|
+
lng: -112.9870
|
|
55
|
+
notes: "Walk to Zion shuttle stop. Pool, free breakfast, mountain views."
|
|
56
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=Best+Western+Plus+Zion+Canyon+Inn+Springdale+UT"
|
|
57
|
+
|
|
58
|
+
tips:
|
|
59
|
+
- "Park the rental in Springdale — Zion's canyon is shuttle-only Mar–Nov."
|
|
60
|
+
- "Watchman Trail is the perfect golden-hour hike — start at 5 PM, summit by sunset."
|
|
61
|
+
- "Refill water bottles at every visitor center; spigots are everywhere."
|
|
62
|
+
|
|
63
|
+
stops:
|
|
64
|
+
- name: "Valley of Fire State Park"
|
|
65
|
+
lat: 36.4307
|
|
66
|
+
lng: -114.5147
|
|
67
|
+
type: scenic
|
|
68
|
+
label: "Detour"
|
|
69
|
+
description: "60-min detour off I-15. Mars-red Aztec sandstone formations, petroglyphs, the Fire Wave. Drive the loop and stop at Mouse's Tank."
|
|
70
|
+
duration: "1.5–2 hrs"
|
|
71
|
+
parking_fee: "$15 NV non-resident"
|
|
72
|
+
kid_friendly: true
|
|
73
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=36.4307,-114.5147"
|
|
74
|
+
|
|
75
|
+
- name: "Zion Visitor Center"
|
|
76
|
+
lat: 37.2002
|
|
77
|
+
lng: -112.9871
|
|
78
|
+
type: scenic
|
|
79
|
+
label: "Stop"
|
|
80
|
+
description: "Pick up the shuttle here. Grab a Zion map, fill water bottles, check Angel's Landing permit status board."
|
|
81
|
+
duration: "20–30 min"
|
|
82
|
+
parking_fee: "$35 park fee (7-day) or America the Beautiful pass"
|
|
83
|
+
kid_friendly: true
|
|
84
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.2002,-112.9871"
|
|
85
|
+
|
|
86
|
+
- name: "Watchman Trail (sunset)"
|
|
87
|
+
lat: 37.1996
|
|
88
|
+
lng: -112.9871
|
|
89
|
+
type: hike
|
|
90
|
+
label: "Sunset"
|
|
91
|
+
description: "3.3-mile out-and-back from the visitor center. Modest 370 ft climb to a viewpoint over the West Temple and Watchman peak. Light up like fire at sunset."
|
|
92
|
+
duration: "2 hrs"
|
|
93
|
+
parking_fee: "Included with park fee"
|
|
94
|
+
kid_friendly: true
|
|
95
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.1996,-112.9871"
|
|
96
|
+
|
|
97
|
+
# ========== DAY 2 ==========
|
|
98
|
+
- number: 2
|
|
99
|
+
title: "Zion full day"
|
|
100
|
+
date: "Sunday, May 3"
|
|
101
|
+
status: upcoming
|
|
102
|
+
color: "#a04020"
|
|
103
|
+
|
|
104
|
+
summary:
|
|
105
|
+
drive: "Shuttle only"
|
|
106
|
+
hike: "5–7 hrs"
|
|
107
|
+
miles: "0"
|
|
108
|
+
|
|
109
|
+
weather:
|
|
110
|
+
high: "84°F"
|
|
111
|
+
low: "58°F"
|
|
112
|
+
sky: "Sunny, light wind"
|
|
113
|
+
rain_chance: "0%"
|
|
114
|
+
note: "Start hikes by 7 AM — the canyon walls trap heat by 11."
|
|
115
|
+
|
|
116
|
+
meals:
|
|
117
|
+
breakfast: "Hotel breakfast or Deep Creek Coffee, Springdale"
|
|
118
|
+
lunch: "Zion Lodge Red Rock Grill (sit on the lawn under cottonwoods)"
|
|
119
|
+
dinner: "Bit & Spur — Mexican, locally famous since the 80s"
|
|
120
|
+
|
|
121
|
+
lodging:
|
|
122
|
+
name: "Best Western Plus Zion Canyon Inn & Suites"
|
|
123
|
+
location: "Springdale, Utah"
|
|
124
|
+
price_estimate: "~$220/night"
|
|
125
|
+
confirmation: "XXXXX1234"
|
|
126
|
+
booked: true
|
|
127
|
+
lat: 37.1924
|
|
128
|
+
lng: -112.9870
|
|
129
|
+
notes: "Second night — keep the same room"
|
|
130
|
+
|
|
131
|
+
alerts:
|
|
132
|
+
- "🪨 Angel's Landing requires a permit (lottery + day-before). If you didn't get one, do Observation Point via East Mesa Trail instead — same view, no permit, less crowded."
|
|
133
|
+
|
|
134
|
+
tips:
|
|
135
|
+
- "Take the first shuttle (6 AM) to beat heat and crowds for Angel's Landing."
|
|
136
|
+
- "The Narrows from the bottom (Riverside Walk → Mystery Falls) is wadeable in May with rented neoprene boots from Zion Outfitter."
|
|
137
|
+
- "Emerald Pools loop is a perfect afternoon recovery hike — shaded, gentle, gorgeous."
|
|
138
|
+
|
|
139
|
+
stops:
|
|
140
|
+
- name: "Angel's Landing"
|
|
141
|
+
lat: 37.2581
|
|
142
|
+
lng: -112.9514
|
|
143
|
+
type: hike
|
|
144
|
+
label: "Big hike"
|
|
145
|
+
description: "5.4 miles, 1,488 ft climb. Walter's Wiggles switchbacks then the chain section ridge. Permit required. One of the most exposed hikes in any US park — not for vertigo."
|
|
146
|
+
duration: "4–5 hrs"
|
|
147
|
+
reservation_required: true
|
|
148
|
+
reservation_url: "https://www.recreation.gov/permits/angels-landing"
|
|
149
|
+
kid_friendly: false
|
|
150
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.2581,-112.9514"
|
|
151
|
+
|
|
152
|
+
- name: "Emerald Pools Loop"
|
|
153
|
+
lat: 37.2497
|
|
154
|
+
lng: -112.9588
|
|
155
|
+
type: hike
|
|
156
|
+
label: "Easy"
|
|
157
|
+
description: "Lower (paved, 1.2 mi) → Middle → Upper. Hanging gardens, seep springs, water curtain over an alcove. Best in the afternoon when sun hits the upper bowl."
|
|
158
|
+
duration: "1.5–2 hrs"
|
|
159
|
+
kid_friendly: true
|
|
160
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.2497,-112.9588"
|
|
161
|
+
|
|
162
|
+
- name: "Riverside Walk → Narrows mouth"
|
|
163
|
+
lat: 37.2856
|
|
164
|
+
lng: -112.9479
|
|
165
|
+
type: scenic
|
|
166
|
+
label: "Stroll"
|
|
167
|
+
description: "Paved 2-mile out-and-back to where the Virgin River disappears between 1,000 ft cliffs. Wade in 50 ft if you brought water shoes."
|
|
168
|
+
duration: "1–1.5 hrs"
|
|
169
|
+
kid_friendly: true
|
|
170
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.2856,-112.9479"
|
|
171
|
+
|
|
172
|
+
# ========== DAY 3 ==========
|
|
173
|
+
- number: 3
|
|
174
|
+
title: "Zion → Bryce → Page"
|
|
175
|
+
date: "Monday, May 4"
|
|
176
|
+
status: upcoming
|
|
177
|
+
color: "#d97a3c"
|
|
178
|
+
|
|
179
|
+
summary:
|
|
180
|
+
drive: "5 hrs"
|
|
181
|
+
hike: "2 hrs"
|
|
182
|
+
miles: "~270 mi"
|
|
183
|
+
|
|
184
|
+
weather:
|
|
185
|
+
high: "72°F (Bryce 8000 ft)"
|
|
186
|
+
low: "38°F"
|
|
187
|
+
sky: "Sunny, breezy at altitude"
|
|
188
|
+
rain_chance: "10%"
|
|
189
|
+
note: "Bryce is 4,000 ft higher than Zion — bring a fleece even in May."
|
|
190
|
+
|
|
191
|
+
meals:
|
|
192
|
+
breakfast: "Hotel breakfast"
|
|
193
|
+
lunch: "Bryce Canyon Lodge dining room — cafeteria + sit-down side"
|
|
194
|
+
dinner: "El Tapatio, Page — best Mexican in town, walk from hotel"
|
|
195
|
+
|
|
196
|
+
lodging:
|
|
197
|
+
name: "Best Western View of Lake Powell"
|
|
198
|
+
location: "Page, Arizona"
|
|
199
|
+
price_estimate: "~$165/night"
|
|
200
|
+
confirmation: "XXXXX5678"
|
|
201
|
+
booked: true
|
|
202
|
+
lat: 36.9097
|
|
203
|
+
lng: -111.4587
|
|
204
|
+
notes: "Hilltop with Lake Powell views. Pool, free breakfast, easy Antelope Canyon shuttle pickup."
|
|
205
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=Best+Western+View+of+Lake+Powell+Page+AZ"
|
|
206
|
+
|
|
207
|
+
tips:
|
|
208
|
+
- "Drive Hwy 12 — one of America's prettiest scenic byways. Slot in 30 min for the Hogsback ridge between Boulder and Escalante."
|
|
209
|
+
- "At Bryce, walk the rim from Sunrise to Sunset Point (1 mi) for the best free view of the amphitheater."
|
|
210
|
+
- "Time the Page arrival for sunset at Horseshoe Bend tomorrow morning — too crowded after 9 AM."
|
|
211
|
+
|
|
212
|
+
stops:
|
|
213
|
+
- name: "Bryce Canyon — Sunrise & Sunset Points"
|
|
214
|
+
lat: 37.6248
|
|
215
|
+
lng: -112.1620
|
|
216
|
+
type: scenic
|
|
217
|
+
label: "Rim walk"
|
|
218
|
+
description: "Two of the most-photographed viewpoints on the rim. 1-mile flat connector along the edge looks down on Thor's Hammer and the Silent City of hoodoos."
|
|
219
|
+
duration: "30–45 min"
|
|
220
|
+
kid_friendly: true
|
|
221
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.6248,-112.1620"
|
|
222
|
+
|
|
223
|
+
- name: "Navajo Loop / Queen's Garden"
|
|
224
|
+
lat: 37.6238
|
|
225
|
+
lng: -112.1640
|
|
226
|
+
type: hike
|
|
227
|
+
label: "Hoodoo hike"
|
|
228
|
+
description: "Best 3-mile hike in any US park, full stop. Drop down Wall Street switchbacks, weave through hoodoos, come back up Queen's Garden. Do it counter-clockwise."
|
|
229
|
+
duration: "2 hrs"
|
|
230
|
+
kid_friendly: true
|
|
231
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=37.6238,-112.1640"
|
|
232
|
+
|
|
233
|
+
# ========== DAY 4 ==========
|
|
234
|
+
- number: 4
|
|
235
|
+
title: "Page → Grand Canyon South Rim"
|
|
236
|
+
date: "Tuesday, May 5"
|
|
237
|
+
status: upcoming
|
|
238
|
+
color: "#7d3c1a"
|
|
239
|
+
|
|
240
|
+
summary:
|
|
241
|
+
drive: "3 hrs"
|
|
242
|
+
hike: "1.5 hrs"
|
|
243
|
+
miles: "~140 mi"
|
|
244
|
+
|
|
245
|
+
weather:
|
|
246
|
+
high: "75°F (rim)"
|
|
247
|
+
low: "42°F"
|
|
248
|
+
sky: "Clear"
|
|
249
|
+
rain_chance: "5%"
|
|
250
|
+
note: "Sunset at Hopi Point is the picture you've seen on every postcard."
|
|
251
|
+
|
|
252
|
+
meals:
|
|
253
|
+
breakfast: "Hotel breakfast — early start for Horseshoe Bend"
|
|
254
|
+
lunch: "Cameron Trading Post (en route) — Navajo tacos in a 1916 stone post"
|
|
255
|
+
dinner: "El Tovar Dining Room (book ahead) or Bright Angel Restaurant"
|
|
256
|
+
|
|
257
|
+
lodging:
|
|
258
|
+
name: "Best Western Premier Grand Canyon Squire Inn"
|
|
259
|
+
location: "Tusayan, Arizona"
|
|
260
|
+
price_estimate: "~$280/night"
|
|
261
|
+
confirmation: "XXXXX9012"
|
|
262
|
+
booked: true
|
|
263
|
+
lat: 35.9684
|
|
264
|
+
lng: -112.1218
|
|
265
|
+
notes: "1 mile south of the park entrance. Bowling alley, two pools, free shuttle into the park."
|
|
266
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=Best+Western+Premier+Grand+Canyon+Squire+Inn+Tusayan+AZ"
|
|
267
|
+
|
|
268
|
+
alerts:
|
|
269
|
+
- "📅 Antelope Canyon entry is by Navajo guide only — book the Lower Canyon tour 4–6 weeks ahead through Ken's Tours or Dixie Ellis'."
|
|
270
|
+
|
|
271
|
+
tips:
|
|
272
|
+
- "Horseshoe Bend opens at sunrise — be there by 6:30 AM to avoid tour bus crush."
|
|
273
|
+
- "Stop at the Watchtower (Desert View) on the way in — eastern entrance to GCNP saves 30 min vs going through Tusayan first."
|
|
274
|
+
- "Sunset shuttle from Bright Angel Lodge to Hopi Point runs every 15 min, free."
|
|
275
|
+
|
|
276
|
+
stops:
|
|
277
|
+
- name: "Horseshoe Bend"
|
|
278
|
+
lat: 36.8625
|
|
279
|
+
lng: -111.5103
|
|
280
|
+
type: scenic
|
|
281
|
+
label: "Sunrise"
|
|
282
|
+
description: "1.5-mile round trip from the parking lot to the overlook. The Colorado River carves a 270° loop around a sandstone fin 1,000 ft below. Bring a wide-angle lens."
|
|
283
|
+
duration: "1 hr"
|
|
284
|
+
parking_fee: "$10 City of Page"
|
|
285
|
+
kid_friendly: true
|
|
286
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=36.8625,-111.5103"
|
|
287
|
+
|
|
288
|
+
- name: "Lower Antelope Canyon"
|
|
289
|
+
lat: 36.8619
|
|
290
|
+
lng: -111.4133
|
|
291
|
+
type: scenic
|
|
292
|
+
label: "Slot canyon"
|
|
293
|
+
description: "Guided tour through a slot canyon carved by flash floods. Wavy walls glow orange and purple in shafts of sunlight. Easier than Upper for photos (less crowded, cheaper)."
|
|
294
|
+
duration: "1.5 hrs"
|
|
295
|
+
reservation_required: true
|
|
296
|
+
reservation_url: "https://lowerantelope.com"
|
|
297
|
+
parking_fee: "Included in tour"
|
|
298
|
+
kid_friendly: true
|
|
299
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=36.8619,-111.4133"
|
|
300
|
+
|
|
301
|
+
- name: "Mather Point (sunset)"
|
|
302
|
+
lat: 36.0613
|
|
303
|
+
lng: -112.1080
|
|
304
|
+
type: scenic
|
|
305
|
+
label: "Sunset"
|
|
306
|
+
description: "First view of the Grand Canyon for most arriving visitors, and still the best for impact. 5-min walk from the visitor center."
|
|
307
|
+
duration: "30–45 min"
|
|
308
|
+
kid_friendly: true
|
|
309
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=36.0613,-112.1080"
|
|
310
|
+
|
|
311
|
+
# ========== DAY 5 ==========
|
|
312
|
+
- number: 5
|
|
313
|
+
title: "Grand Canyon → Las Vegas"
|
|
314
|
+
date: "Wednesday, May 6"
|
|
315
|
+
status: upcoming
|
|
316
|
+
color: "#5c2a14"
|
|
317
|
+
|
|
318
|
+
summary:
|
|
319
|
+
drive: "4.5 hrs"
|
|
320
|
+
hike: "1.5–2 hrs"
|
|
321
|
+
miles: "~280 mi"
|
|
322
|
+
|
|
323
|
+
weather:
|
|
324
|
+
high: "78°F"
|
|
325
|
+
low: "44°F"
|
|
326
|
+
sky: "Clear, gusty PM"
|
|
327
|
+
rain_chance: "0%"
|
|
328
|
+
note: "Last day — pace it for the LAS evening flight."
|
|
329
|
+
|
|
330
|
+
meals:
|
|
331
|
+
breakfast: "Bright Angel Lodge — sunrise window seat if you can get one"
|
|
332
|
+
lunch: "Hoover Dam visitor center cafeteria, or push through to Boulder City for In-N-Out"
|
|
333
|
+
dinner: "Vegas — depends on flight time"
|
|
334
|
+
|
|
335
|
+
lodging:
|
|
336
|
+
name: "Home"
|
|
337
|
+
location: "Returning home"
|
|
338
|
+
booked: true
|
|
339
|
+
lat: 36.0840
|
|
340
|
+
lng: -115.1537
|
|
341
|
+
notes: "Flight out of LAS — return rental by 7 PM"
|
|
342
|
+
|
|
343
|
+
tips:
|
|
344
|
+
- "Bright Angel Trail down to 1.5-mile Resthouse and back is a perfect taste of below-the-rim without committing to Plateau Point."
|
|
345
|
+
- "Hoover Dam adds 20 min off Hwy 93 — totally worth it. Don't bother with the dam tour, just walk across the bridge."
|
|
346
|
+
- "Drop the rental 2 hrs before your flight — LAS rental return shuttle runs slow."
|
|
347
|
+
|
|
348
|
+
stops:
|
|
349
|
+
- name: "Bright Angel Trail (1.5-mile Resthouse)"
|
|
350
|
+
lat: 36.0578
|
|
351
|
+
lng: -112.1438
|
|
352
|
+
type: hike
|
|
353
|
+
label: "Half-day"
|
|
354
|
+
description: "Out-and-back to the 1.5-mile Resthouse — 3 miles, 1,100 ft of elevation (mostly down on the way in, all up on the way out). Mules pass you. Real canyon experience."
|
|
355
|
+
duration: "2 hrs"
|
|
356
|
+
kid_friendly: true
|
|
357
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=36.0578,-112.1438"
|
|
358
|
+
|
|
359
|
+
- name: "Hoover Dam"
|
|
360
|
+
lat: 36.0162
|
|
361
|
+
lng: -114.7377
|
|
362
|
+
type: scenic
|
|
363
|
+
label: "Engineering"
|
|
364
|
+
description: "Walk across the Mike O'Callaghan–Pat Tillman Memorial Bridge for the best photo of the dam. Free, 15 min, blows minds."
|
|
365
|
+
duration: "30–45 min"
|
|
366
|
+
parking_fee: "$10 dam parking, or free at the bridge lot"
|
|
367
|
+
kid_friendly: true
|
|
368
|
+
navigate_url: "https://www.google.com/maps/dir/?api=1&destination=36.0162,-114.7377"
|
|
369
|
+
|
|
370
|
+
# ============================================================
|
|
371
|
+
# ROUTES — explicit polylines that follow real road geometry
|
|
372
|
+
# (I-15, US-89, UT-9, UT-12, US-93). Auto-generation works fine
|
|
373
|
+
# without this block, but hand-curated waypoints look smoother.
|
|
374
|
+
# ============================================================
|
|
375
|
+
routes:
|
|
376
|
+
# Day 1: Vegas → Valley of Fire → I-15 → St. George → Springdale
|
|
377
|
+
- day: 1
|
|
378
|
+
color: "#c25e1f"
|
|
379
|
+
width: 4
|
|
380
|
+
points:
|
|
381
|
+
- [36.084, -115.154]
|
|
382
|
+
- [36.430, -114.515]
|
|
383
|
+
- [37.097, -113.568]
|
|
384
|
+
- [37.175, -113.283]
|
|
385
|
+
- [37.192, -112.987]
|
|
386
|
+
|
|
387
|
+
# Day 2: Zion canyon shuttle loop
|
|
388
|
+
- day: 2
|
|
389
|
+
color: "#a04020"
|
|
390
|
+
width: 4
|
|
391
|
+
points:
|
|
392
|
+
- [37.192, -112.987]
|
|
393
|
+
- [37.258, -112.951]
|
|
394
|
+
- [37.250, -112.959]
|
|
395
|
+
- [37.286, -112.948]
|
|
396
|
+
- [37.192, -112.987]
|
|
397
|
+
|
|
398
|
+
# Day 3: Springdale → Bryce → Kanab → Page
|
|
399
|
+
- day: 3
|
|
400
|
+
color: "#d97a3c"
|
|
401
|
+
width: 4
|
|
402
|
+
points:
|
|
403
|
+
- [37.192, -112.987]
|
|
404
|
+
- [37.342, -112.642]
|
|
405
|
+
- [37.524, -112.227]
|
|
406
|
+
- [37.624, -112.162]
|
|
407
|
+
- [37.524, -112.227]
|
|
408
|
+
- [37.045, -112.527]
|
|
409
|
+
- [36.910, -111.459]
|
|
410
|
+
|
|
411
|
+
# Day 4: Page → Horseshoe → Antelope → Cameron → Grand Canyon → Tusayan
|
|
412
|
+
- day: 4
|
|
413
|
+
color: "#7d3c1a"
|
|
414
|
+
width: 4
|
|
415
|
+
points:
|
|
416
|
+
- [36.910, -111.459]
|
|
417
|
+
- [36.862, -111.510]
|
|
418
|
+
- [36.862, -111.413]
|
|
419
|
+
- [35.875, -111.413]
|
|
420
|
+
- [36.061, -112.108]
|
|
421
|
+
- [35.968, -112.122]
|
|
422
|
+
|
|
423
|
+
# Day 5: Tusayan → Bright Angel → Williams → Kingman → Hoover → Vegas
|
|
424
|
+
- day: 5
|
|
425
|
+
color: "#5c2a14"
|
|
426
|
+
width: 4
|
|
427
|
+
points:
|
|
428
|
+
- [35.968, -112.122]
|
|
429
|
+
- [36.058, -112.144]
|
|
430
|
+
- [35.250, -112.190]
|
|
431
|
+
- [35.189, -114.053]
|
|
432
|
+
- [36.016, -114.738]
|
|
433
|
+
- [36.084, -115.154]
|
|
434
|
+
|
|
435
|
+
theme:
|
|
436
|
+
font_family: "DM Sans, sans-serif"
|
|
437
|
+
accent_color: "#c25e1f"
|
|
438
|
+
map_style: "terrain"
|
|
439
|
+
|
|
440
|
+
agent_context:
|
|
441
|
+
preferences:
|
|
442
|
+
pace: "moderate"
|
|
443
|
+
budget: "mid-range"
|
|
444
|
+
accommodation_chain: "Best Western"
|
|
445
|
+
interests:
|
|
446
|
+
- "national parks"
|
|
447
|
+
- "hiking"
|
|
448
|
+
- "geology"
|
|
449
|
+
- "photography"
|
|
450
|
+
constraints:
|
|
451
|
+
max_drive_per_day: "5 hours"
|
|
452
|
+
must_see:
|
|
453
|
+
- "Angel's Landing or Observation Point"
|
|
454
|
+
- "Bryce hoodoos"
|
|
455
|
+
- "Horseshoe Bend at sunrise"
|
|
456
|
+
- "Grand Canyon South Rim sunset"
|
|
457
|
+
iteration_log:
|
|
458
|
+
- date: "2026-04-15"
|
|
459
|
+
change: "Original plan included North Rim — dropped because it doesn't open until May 15."
|
|
460
|
+
- date: "2026-04-18"
|
|
461
|
+
change: "Swapped Upper Antelope for Lower Antelope — half the price, better photos, easier ladders."
|
|
462
|
+
- date: "2026-04-22"
|
|
463
|
+
change: "Added Hoover Dam to Day 5 — only adds 20 min and makes the LAS approach more interesting."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Open-source framework for AI-assisted trip planning with beautiful interactive visualizers",
|
|
5
5
|
"main": "convert.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,9 +9,13 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"convert.js",
|
|
11
11
|
"validate.js",
|
|
12
|
+
"scripts/check-skill-coverage.js",
|
|
12
13
|
"renderers/html/tripkit-renderer.html",
|
|
13
14
|
"schema/tripkit.schema.yaml",
|
|
14
15
|
"examples/oregon-spring-2026.yaml",
|
|
16
|
+
"examples/southwest-parks-2026.yaml",
|
|
17
|
+
"examples/nyc-long-weekend-2026.yaml",
|
|
18
|
+
"examples/new-england-fall-2026.yaml",
|
|
15
19
|
"agent/questionnaire.yaml",
|
|
16
20
|
"agent/AGENT-SKILL.md",
|
|
17
21
|
"README.md",
|
|
@@ -21,7 +25,7 @@
|
|
|
21
25
|
"scripts": {
|
|
22
26
|
"convert": "node convert.js",
|
|
23
27
|
"example": "node convert.js examples/oregon-spring-2026.yaml examples/oregon-spring-2026.html",
|
|
24
|
-
"test": "node convert.js examples/oregon-spring-2026.yaml /tmp/tripkit-test.html && test -s /tmp/tripkit-test.html && echo 'OK'",
|
|
28
|
+
"test": "node convert.js examples/oregon-spring-2026.yaml /tmp/tripkit-test.html && test -s /tmp/tripkit-test.html && node scripts/check-skill-coverage.js && echo 'OK'",
|
|
25
29
|
"prepublishOnly": "npm test"
|
|
26
30
|
},
|
|
27
31
|
"keywords": [
|
|
@@ -94,6 +94,10 @@ body{font-family:var(--font);background:var(--bg);color:var(--text)}
|
|
|
94
94
|
border:2px solid #1b5e3b;font-family:var(--font);cursor:pointer;transition:transform .12s;letter-spacing:-.02em}
|
|
95
95
|
.hm:hover{transform:scale(1.15)}
|
|
96
96
|
.pp-conf{font-size:11px;font-weight:600;color:var(--accent);background:var(--accent-soft);padding:4px 8px;border-radius:4px;display:inline-block;margin-top:6px;letter-spacing:.02em}
|
|
97
|
+
.endpin{display:flex;align-items:center;justify-content:center;width:28px;height:34px;font-size:11px;font-weight:700;color:#fff;font-family:var(--font);border-radius:14px 14px 14px 2px;transform:rotate(-45deg);box-shadow:0 2px 6px rgba(0,0,0,.35);border:2px solid #fff;cursor:pointer}
|
|
98
|
+
.endpin span{transform:rotate(45deg);letter-spacing:.02em}
|
|
99
|
+
.endpin.start{background:#1b5e3b}
|
|
100
|
+
.endpin.end{background:#7a1f1f}
|
|
97
101
|
@media(max-width:480px){
|
|
98
102
|
.hero{height:140px}.hero-text{padding:12px 14px}.hero-text h1{font-size:20px}
|
|
99
103
|
.day-nav{padding:8px 10px;gap:1px}.day-btn{padding:5px 8px;font-size:10px}
|
|
@@ -329,15 +333,25 @@ const TRIP_DATA = {};
|
|
|
329
333
|
L.polyline(r.points, { color: r.color || '#666', weight: r.width || 4, opacity: 0.8 }).addTo(map);
|
|
330
334
|
});
|
|
331
335
|
} else {
|
|
332
|
-
// Auto-generate routes
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
336
|
+
// Auto-generate routes. For each day, the polyline runs:
|
|
337
|
+
// prev day's lodging (if any) → today's stops → today's lodging (if any)
|
|
338
|
+
// This stitches consecutive days together so the map reads as one continuous trip.
|
|
339
|
+
const isAnchorLodging = (l) => l && l.lat && l.lng && l.name !== 'Home';
|
|
340
|
+
days.forEach((d, di) => {
|
|
341
|
+
const pts = [];
|
|
342
|
+
const prevLodging = di > 0 ? days[di - 1].lodging : null;
|
|
343
|
+
if (isAnchorLodging(prevLodging)) pts.push([prevLodging.lat, prevLodging.lng]);
|
|
344
|
+
(d.stops || []).forEach(s => { if (s.lat && s.lng) pts.push([s.lat, s.lng]); });
|
|
345
|
+
if (isAnchorLodging(d.lodging)) {
|
|
346
|
+
const last = pts[pts.length - 1];
|
|
347
|
+
if (!last || last[0] !== d.lodging.lat || last[1] !== d.lodging.lng) {
|
|
348
|
+
pts.push([d.lodging.lat, d.lodging.lng]);
|
|
339
349
|
}
|
|
340
350
|
}
|
|
351
|
+
if (pts.length > 1) {
|
|
352
|
+
L.polyline(pts, { color: '#fff', weight: 7, opacity: 0.4 }).addTo(map);
|
|
353
|
+
L.polyline(pts, { color: d.color || '#666', weight: 4, opacity: 0.8 }).addTo(map);
|
|
354
|
+
}
|
|
341
355
|
});
|
|
342
356
|
}
|
|
343
357
|
|
|
@@ -369,8 +383,34 @@ const TRIP_DATA = {};
|
|
|
369
383
|
markers.push(dm);
|
|
370
384
|
});
|
|
371
385
|
|
|
386
|
+
// --- Start / End pins (origin & destination) ---
|
|
387
|
+
function endpoint(lat, lng, kind, name) {
|
|
388
|
+
const labelTxt = kind === 'start' ? 'A' : 'B';
|
|
389
|
+
const titleTxt = kind === 'start' ? 'Start' : 'End';
|
|
390
|
+
const icon = L.divIcon({
|
|
391
|
+
className: '',
|
|
392
|
+
html: `<div class="endpin ${kind}"><span>${labelTxt}</span></div>`,
|
|
393
|
+
iconSize: [28, 34], iconAnchor: [4, 32]
|
|
394
|
+
});
|
|
395
|
+
const m = L.marker([lat, lng], { icon, zIndexOffset: 800 }).addTo(map);
|
|
396
|
+
m.bindPopup(`<div class="pp-body" style="padding:14px">
|
|
397
|
+
<div class="pp-day">${titleTxt}</div>
|
|
398
|
+
<div class="pp-name">${name || titleTxt}</div>
|
|
399
|
+
</div>`, { maxWidth: 240 });
|
|
400
|
+
}
|
|
401
|
+
if (Number.isFinite(trip.origin_lat) && Number.isFinite(trip.origin_lng)) {
|
|
402
|
+
endpoint(trip.origin_lat, trip.origin_lng, 'start', trip.origin);
|
|
403
|
+
const destLat = Number.isFinite(trip.destination_lat) ? trip.destination_lat : trip.origin_lat;
|
|
404
|
+
const destLng = Number.isFinite(trip.destination_lng) ? trip.destination_lng : trip.origin_lng;
|
|
405
|
+
// Suppress the "End" pin if it's at the same coords as the Start (round trip).
|
|
406
|
+
if (Math.abs(destLat - trip.origin_lat) > 0.001 || Math.abs(destLng - trip.origin_lng) > 0.001) {
|
|
407
|
+
endpoint(destLat, destLng, 'end', trip.destination || trip.origin);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
372
411
|
// --- Hotel markers ---
|
|
373
|
-
|
|
412
|
+
// Build per-night list, then dedupe by lat/lng so multi-night stays render as one marker.
|
|
413
|
+
const nights = days
|
|
374
414
|
.filter(d => d.lodging && d.lodging.lat && d.lodging.lng && d.lodging.name !== 'Home')
|
|
375
415
|
.map((d, i) => ({
|
|
376
416
|
name: d.lodging.name,
|
|
@@ -384,11 +424,24 @@ const TRIP_DATA = {};
|
|
|
384
424
|
navigate_url: d.lodging.navigate_url
|
|
385
425
|
}));
|
|
386
426
|
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
427
|
+
const hotelGroups = new Map();
|
|
428
|
+
nights.forEach(n => {
|
|
429
|
+
const key = n.lat.toFixed(4) + ',' + n.lng.toFixed(4);
|
|
430
|
+
if (!hotelGroups.has(key)) hotelGroups.set(key, []);
|
|
431
|
+
hotelGroups.get(key).push(n);
|
|
432
|
+
});
|
|
433
|
+
const hotels = Array.from(hotelGroups.values());
|
|
434
|
+
|
|
435
|
+
const hotelLabel = theme.hotel_label
|
|
436
|
+
|| (data.agent_context?.preferences?.accommodation_chain
|
|
437
|
+
? data.agent_context.preferences.accommodation_chain.split(' ')[0].substring(0, 3).toUpperCase()
|
|
438
|
+
: '🏨');
|
|
439
|
+
|
|
440
|
+
hotels.forEach(group => {
|
|
441
|
+
const h = group[0];
|
|
442
|
+
const ns = group.map(g => g.night);
|
|
443
|
+
const nightLabel = ns.length === 1 ? `Night ${ns[0]}` : `Nights ${ns[0]}–${ns[ns.length - 1]}`;
|
|
444
|
+
const dateLabel = group.length === 1 ? h.day : `${h.day} – ${group[group.length - 1].day}`;
|
|
392
445
|
const icon = L.divIcon({
|
|
393
446
|
className: '',
|
|
394
447
|
html: `<div class="hm">${hotelLabel}</div>`,
|
|
@@ -396,7 +449,7 @@ const TRIP_DATA = {};
|
|
|
396
449
|
});
|
|
397
450
|
const m = L.marker([h.lat, h.lng], { icon, zIndexOffset: 1000 }).addTo(map);
|
|
398
451
|
let popupHTML = `<div class="pp-body" style="padding:16px">
|
|
399
|
-
<div class="pp-day"
|
|
452
|
+
<div class="pp-day">${nightLabel} · ${dateLabel}</div>
|
|
400
453
|
<div class="pp-name">${h.name}</div>
|
|
401
454
|
<div style="font-size:12px;color:var(--muted);margin-bottom:6px">${h.loc}</div>`;
|
|
402
455
|
if (h.booked && h.conf) popupHTML += `<div class="pp-conf">Conf# ${h.conf}</div>`;
|
|
@@ -421,7 +474,13 @@ const TRIP_DATA = {};
|
|
|
421
474
|
legendEl.querySelector('.legend-toggle')?.addEventListener('click', () => legendEl.classList.toggle('collapsed'));
|
|
422
475
|
|
|
423
476
|
// --- Fit bounds ---
|
|
424
|
-
|
|
477
|
+
// Include stops, hotels, and origin/destination pins so the initial fit covers everything.
|
|
478
|
+
const allPts = [
|
|
479
|
+
...days.flatMap(d => (d.stops || []).filter(s => s.lat && s.lng).map(s => [s.lat, s.lng])),
|
|
480
|
+
...days.flatMap(d => (d.lodging && d.lodging.lat && d.lodging.lng && d.lodging.name !== 'Home') ? [[d.lodging.lat, d.lodging.lng]] : []),
|
|
481
|
+
];
|
|
482
|
+
if (Number.isFinite(trip.origin_lat) && Number.isFinite(trip.origin_lng)) allPts.push([trip.origin_lat, trip.origin_lng]);
|
|
483
|
+
if (Number.isFinite(trip.destination_lat) && Number.isFinite(trip.destination_lng)) allPts.push([trip.destination_lat, trip.destination_lng]);
|
|
425
484
|
const fullBounds = L.latLngBounds(allPts).pad(0.06);
|
|
426
485
|
map.fitBounds(fullBounds);
|
|
427
486
|
|
|
@@ -18,6 +18,10 @@ trip:
|
|
|
18
18
|
children: integer
|
|
19
19
|
ages: string # e.g. "Kids: 14, 17"
|
|
20
20
|
origin: string # e.g. "Folsom, CA"
|
|
21
|
+
origin_lat: number # Optional — if set with origin_lng, renders a "Start" pin on the map
|
|
22
|
+
origin_lng: number
|
|
23
|
+
destination_lat: number # Optional — if absent, defaults to origin coords (round trip)
|
|
24
|
+
destination_lng: number # Set explicitly only if the trip ends somewhere different from where it started
|
|
21
25
|
vehicle: string # e.g. "SUV", "Sedan", "RV"
|
|
22
26
|
|
|
23
27
|
# ============================================================
|
|
@@ -102,6 +106,9 @@ theme:
|
|
|
102
106
|
map_style: string # "terrain" | "satellite" | "topo" | "street"
|
|
103
107
|
dark_mode: boolean # Default: false
|
|
104
108
|
hero_style: string # "photo" | "gradient" | "minimal"
|
|
109
|
+
hotel_label: string # Optional 1–4 char label for hotel markers (e.g. "MAR", "BW", "INN").
|
|
110
|
+
# Falls back to first 3 chars of agent_context.preferences.accommodation_chain,
|
|
111
|
+
# then 🏨 emoji.
|
|
105
112
|
|
|
106
113
|
# ============================================================
|
|
107
114
|
# AGENT CONTEXT — metadata for the planning agent
|