Skip to content

Latest commit

Β 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

windward logo

windward

One sentence in. Five bookable trips out. Never books anything.

CI status Python 3.11+ MIT license No extra cost β€” uses your existing Claude plan PRs welcome

Quickstart Β Β β€’Β Β  How it works Β Β β€’Β Β  Providers Β Β β€’Β Β  Costs Β Β β€’Β Β  Contributing

windward's trip viewer β€” ranked plan cards with budget bars, itineraries, and a hotel map

Every run ships a self-contained HTML viewer: ranked cards, budget bars, day-by-day itineraries, and a map pinning each hotel.

What is windward?

Windward is an agentic, budget-bounded, plan-only trip planner. You give it one natural-language request:

windward "vacation for 2, budget 3k total, anytime in august, want insane landscapes"

It answers with 4–5 complete, priced, bookable trip plans β€” the exact flight, the exact hotel, a day-by-day itinerary, a budget breakdown against your ceiling, and deep links to book each piece. The last click is always yours: windward never books, pays, or touches PII. That's a design decision, not a roadmap gap.

Under the hood it's four small LLM agents wrapped around a fully deterministic pipeline β€” the interesting parts (pruning, costing, ranking) are plain, testable code.

Features

  • πŸ’Έ Budget is a hard constraint, not a suggestion. Plans are assembled bottom-up β€” flights + lodging + per-diem + buffer β€” and must clear your ceiling with margin. A critic agent re-verifies every sum before you see it.
  • πŸ€– LLM at the edges, code in the middle. Parse β†’ brainstorm β†’ write β†’ critique are agents; prune β†’ cost β†’ rank is deterministic code. Cheap, reproducible, and it won't melt a provider's rate limit.
  • πŸ†“ Zero API keys to start. Free Google Flights data via fli, keyless hotel data via trvl, agents on your existing Claude subscription.
  • πŸ—ΊοΈ A real deliverable. Each run writes Markdown, JSON, and a self-contained HTML viewer with a map β€” artifacts you can save, share, or diff.
  • πŸ”₯ Deal detection. Standout-cheap fares get flagged in every run for free; an opt-in mode hunts fares below their historical range.
  • 🍜 Taste profiles. Ingest your Beli restaurant lists via vision into per-person and group taste profiles that can flavor itineraries.
  • ♻️ Reproducible runs. Run directories are keyed by a hash of the brief; provider responses cache to SQLite with a TTL. Same request, same data.
  • 🧒 Hard caps everywhere. Max provider calls, concurrency, quota ceilings β€” config.py bounds spend and API pressure by design.

🧭 How it works

prompt
  β†’ [LLM]  brief parser       structure the request, infer origin      (cheap)
  β†’ [gate] elicit             ask: origin? long-haul? ceiling?         (pre-spend)
  β†’ [LLM]  brainstormer       ~40 in-season, vibe-matched destinations
  β†’ [code] coarse prune       1 flight call each, drop pricey β†’ ~12
  β†’ [code] cost assembly      + hotel + per-diem buffer, keep ≀ budget β†’ ~6
  β†’ [code] rank               vibe Γ— headroom Γ— season β†’ top 5
  β†’ [code] exact pricing      lock specific flights + hotels
  β†’ [LLM]  itinerary writer   day-by-day plans
  β†’ [LLM]  critic             verify sums ≀ budget, no hallucinations
  β†’ present                   ranked plans + booking deep links

Only four nodes are LLM calls β€” the two ends. The spine is deterministic on purpose.

πŸ“ Repository layout
src/windward/
β”œβ”€β”€ orchestrator.py      the deterministic state machine
β”œβ”€β”€ cli.py               entry points (windward, windward-deals, windward-beli)
β”œβ”€β”€ models.py            typed contracts between every stage
β”œβ”€β”€ agents/
β”‚   β”œβ”€β”€ runtime.py       claude -p wrapper: env scrub + billing guard
β”‚   β”œβ”€β”€ llm_agents.py    the four agents
β”‚   └── prompts/*.md     their prompts + JSON contracts
β”œβ”€β”€ engine/              coarse prune, cost assembly, rank, budget, taste, deals
└── providers/           swappable adapters behind base.py interfaces
πŸ“¦ Run artifacts

Each run is saved to runs/<month><vibe><pax>pax_<hash>/ (local only, gitignored):

  • run.json β€” the structured brief + metadata
  • summary.md β€” ranked index of all options
  • plan-NN-<dest>.{json,md} β€” each trip as a saveable file
  • index.html β€” the self-contained visual viewer (Leaflet map, budget bars, itineraries)

The directory is keyed by a hash of the brief, so re-running the same scenario overwrites in place instead of piling up copies.

πŸš€ Quickstart

# 1. Claude Code, logged in with a Pro/Max subscription (NOT an API key)
npm install -g @anthropic-ai/claude-code
claude login

# 2. Install
git clone https://github.com/sohan-shingade/windward && cd windward
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"

# 3. (Optional but recommended) keyless hotel data
go install github.com/MikkoParkkola/trvl/cmd/trvl@latest
#   or: brew install MikkoParkkola/tap/trvl

# 4. Confirm you're on subscription auth, not API billing
./scripts/preflight.sh

# 5. Plan a trip
windward "vacation for 2, budget 3k total, anytime in august, want insane landscapes"

Structured flags override the parsed brief; multi-origin checks every airport and keeps the cheapest:

windward "adventurous, insane landscapes, backpacking" \
  --pax 2 --budget 2000 --month 2026-08 --nights 4 --origins SFO,OAK,SJC --open

--open launches the HTML viewer when the run finishes.

πŸ”Œ Providers

Layer Default Alternatives
Flights fli β€” free Google Flights, no key SerpApi google_flights (needs SERPAPI_API_KEY)
Hotels trvl β€” free, keyless CLI SerpApi google_hotels β†’ labeled flat-rate estimate
Agents claude -p on your subscription implement AgentRuntime for API-key billing

Hotel preference is trvl β†’ SerpApi β†’ flat estimate; force one with WINDWARD_HOTELS=trvl|serpapi|flat. One SerpApi key (free tier ~250 searches/mo) powers both engines β€” copy .env.example to .env and set SERPAPI_API_KEY if you want it.

Note

Provider responses cache to a local SQLite DB (~/.cache/windward/providers.db) with a TTL, so identical requests reproduce the same flight and hotel data. Force fresh data with WINDWARD_CACHE=off. The optional trvl binary is PolyForm Noncommercial β€” fine for personal, plan-only use.

πŸ’° What it costs: nothing extra

Windward is free, and it's built to run at zero marginal cost:

  • Flight and hotel data is free. The default providers (fli, trvl) need no API key and charge nothing. SerpApi is optional, and its free tier covers normal use.
  • The AI agents use the Claude subscription you already have. They run through Claude Code (claude -p) on your existing Pro/Max login β€” the same flat plan you use for chatting or coding. No per-token API charges, no separate account, no credit card.

You are never billed by windward for anything. The only requirement is a Claude Pro/Max subscription for the agent steps (if you don't have one, you can implement the AgentRuntime interface to point the agents at any API instead).

Two built-in guards make sure agent calls can't accidentally land on metered API billing:

  • The runtime strips ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, and ANTHROPIC_BASE_URL from every agent subprocess, so a key sitting in your shell can't silently switch you from subscription to pay-per-token.
  • The runtime reads total_cost_usd from every agent result and aborts the run if it is ever positive (fail_on_api_billing=True) β€” a belt-and-suspenders check against CLI billing quirks.

Tip

First run? ./scripts/preflight.sh confirms you're on subscription auth (in an interactive claude session, /status should show CLAUDE_CODE_OAUTH_TOKEN).

Important

Running claude -p on your own subscription for your own local use is fine under the ToS. Subscription OAuth may not power a product served to other people β€” if you productize this, swap ClaudeCLIRuntime for an API-key runtime.

πŸ”₯ Deal detection

  • Relative (free, always on): within a normal run, any fare ≀65% of the median gets tagged πŸ”₯ deal in the output, summary, and viewer. No extra API calls.

  • Absolute (opt-in): windward-deals scans aspirational destinations from your origins for fares below their typical historical range (SerpApi price_insights) β€” real error-fare hunting. It spends metered quota, so it's off by default:

    windward-deals --origins SFO,OAK --month 2026-10 --nights 7 --enable

    A hard cap (deal_hunt_max_calls, default 24) clamps targets Γ— origins so a scan can't blow your quota. Results save to runs/deals_*/.

🍜 Beli taste profiles

Turn your Beli restaurant lists into a taste profile that can flavor trip planning. Record a slow scroll of a list (or grab screenshots); a vision agent extracts places and cuisine tags, deduplicates, and scores them by your rating pattern.

# ingest your "been" list from a screen recording
windward-beli alice --video recordings/beli_been.mov --list-type been
# alice: 47 places -> 12 cuisines, 0 on wishlist
# wrote profiles/alice/beli_snapshot.json + taste.json

# second pass for the wishlist β€” appends to the same snapshot, dedup handles overlap
windward-beli alice --video recordings/beli_wants.mov --list-type want_to_try

# screenshots instead of video
windward-beli alice --shots shots/ --list-type been

# merge profiles into a group (consensus scores, shared wants, union of dislikes)
windward-beli group --merge alice,bob,charlie

Profiles land in profiles/ (local only, gitignored). Video ingestion needs the ffmpeg system binary; scroll slowly with brief pauses so mpdecimate can dedupe frames.

πŸ§ͺ Testing

pytest          # FAST: offline, no network or claude. Deterministic logic + provider
                # parsers run against recorded real responses in tests/fixtures/.
pytest -m live  # LIVE: genuine end-to-end (real fli + SerpApi + claude -p). Skips
                # unless creds + network are present; keep ANTHROPIC_API_KEY unset.

Regenerate fixtures with python scripts/record_fixtures.py (needs creds + network).

Contributing

PRs welcome! Ground rules (see CLAUDE.md for the full contributor context):

  • 🚫 Plan-only, forever. No booking, payments, or PII capture.
  • 🧠 Keep the spine deterministic. LLM calls belong in agents/ only.
  • πŸ”Œ Vendor isolation. Concrete providers stay behind the providers/base.py interfaces.
  • 🧒 Don't raise the caps quietly. The limits in config.py protect rate limits and spend.
  • βœ… Add tests next to new engine logic; pytest must pass offline with no credentials.

Released under the MIT License.
The optional trvl hotel binary is separately licensed (PolyForm Noncommercial) and is not distributed with this project.

About

🧭 Agentic, budget-bounded, plan-only trip planner β€” one prompt in, 4-5 complete trip plans out (exact flights + hotels + itineraries + booking links). Never books anything.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages