| name | seo-tour |
| description | Draft a tight-SEO Tour JSON file ready for Admin → Tours → Import JSON. Use when the user gives raw facts about a trek/tour/safari package (place, duration, price, itinerary, etc.) and wants it turned into importable content, or runs /seo-tour.
|
SEO Tour Generator
Turn raw facts about a tour into a JSON file matching boilerplates/tour.json,
with SEO fields written tight enough to pass straight into the DB without
truncation or weak copy.
Step 1: Gather facts
Read whatever the user supplied in the skill args. If critical facts are
missing (duration, price, region, difficulty, at least a skeleton itinerary),
ask for them — don't invent itinerary days or pricing.
Step 2: Read the template
Read boilerplates/tour.json for the exact field shape. Also check
boilerplates/README.md for the content-block type reference.
Step 3: Write the JSON
Build one object (or array of objects for multiple tours) with all _note*
keys removed. Respect these hard DB limits — exceeding them causes
DataError on import, so truncate/tighten during drafting, don't rely on
the importer's defensive truncation to save bad copy:
| Field | Model limit | SEO target |
|---|
title | 200 chars | descriptive, keyword near the front |
place_name | 200 chars | |
excerpt | 300 chars | meaningful summary, not filler |
seo.meta_title | 60 chars | under 60, focus keyword included |
seo.meta_description | 155 chars | under 155, keyword + a reason to click |
seo.focus_keyword | 100 chars | one primary search phrase, not a sentence |
seo.secondary_keywords | 300 chars | comma-separated LSI terms |
seo.og_title | 95 chars | |
seo.og_description | 200 chars | |
image_alt_text | 150 chars | describes the image, includes keyword if natural |
itinerary.items[].title | 200 chars | |
gallery alt_text | 150 chars | |
SEO writing rules:
meta_title: front-load the focus keyword, end with the brand only if room (| Visit Kili).
meta_description: state the unique value (duration, difficulty, price tier) and end with an implicit CTA.
focus_keyword: what someone actually types into Google, e.g. "7 day Machame route Kilimanjaro", not a generic phrase.
content_blocks: write at least a heading + paragraph pair per major itinerary phase (acclimatization, summit night, descent) — thin content underperforms on tour pages.
- Slugs: leave
"slug": "" unless the user gives an existing slug to update (matching a legacy URL for SEO continuity).
related_tours: only reference slugs you've confirmed exist (ask the user, or check Tour.objects.values_list('slug', flat=True) via python manage.py shell if uncertain).
Step 4: Validate before writing the file
Count characters on every SEO field and excerpt yourself — don't guess.
If anything is over limit, rewrite it tighter rather than truncating mid-word.
Step 5: Save and hand off
Write the finished JSON to boilerplates/generated/<slug-or-title>.json
(create the generated/ folder if it doesn't exist).
Tell the user:
- Where the file was saved.
- To upload it at Admin → Tours → Import JSON, with Dry run checked first to validate, then re-upload unchecked to commit.
- That
related_tours/tags must already exist in the DB or they'll be silently skipped.