elliot-stack 1.0.29 → 1.0.33
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/LICENSE +21 -21
- package/README.md +5 -0
- package/bin/install.cjs +981 -950
- package/hooks/repo-search-nudge.js +32 -32
- package/package.json +1 -1
- package/skills/estack-active-learning-tutor/SKILL.md +339 -339
- package/skills/estack-better-title/SKILL.md +64 -64
- package/skills/estack-better-title/scripts/rename.sh +55 -55
- package/skills/estack-chris-voss/SKILL.md +80 -80
- package/skills/estack-chris-voss/references/elliot-notes.md +120 -120
- package/skills/estack-chris-voss/references/voss-principles.md +210 -210
- package/skills/estack-customer-discovery/SKILL.md +60 -60
- package/skills/estack-flight-planner/SKILL.md +332 -332
- package/skills/estack-flight-planner/references/config_schema.md +156 -156
- package/skills/estack-flight-planner/references/flight_history_schema.md +97 -97
- package/skills/estack-flight-planner/references/shuttle_schedules.md +98 -98
- package/skills/estack-flight-planner/scripts/check_setup.sh +89 -89
- package/skills/estack-flight-planner/scripts/fetch_flights.py +99 -99
- package/skills/estack-flight-planner/scripts/filter_flights.py +265 -265
- package/skills/estack-flight-planner/scripts/pair_shuttles.py +173 -173
- package/skills/estack-github-issue-tracker/SKILL.md +322 -322
- package/skills/estack-github-issue-tracker/bin/tracker-tools.cjs +1358 -1358
- package/skills/estack-github-issue-tracker/references/gh-cli-patterns.md +124 -124
- package/skills/estack-github-issue-tracker/references/result-file-schema.md +156 -156
- package/skills/estack-github-issue-tracker/references/tracker-schema.md +96 -96
- package/skills/estack-github-issue-tracker/tracker-template.md +58 -58
- package/skills/estack-leadership-coach/SKILL.md +235 -0
- package/skills/estack-leadership-coach/adding-references.md +280 -0
- package/skills/estack-leadership-coach/frameworks/delegation/flows/post-mortem.md +120 -0
- package/skills/estack-leadership-coach/frameworks/delegation/flows/pre-delegation.md +138 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/1-intake.md +145 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/2-trm-assessment.md +119 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/3-enrollment.md +132 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/4-build-brief.md +171 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/5-monitoring.md +134 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/6-reverse-delegation.md +118 -0
- package/skills/estack-leadership-coach/frameworks/delegation/phases/7-diagnose.md +200 -0
- package/skills/estack-leadership-coach/references/.source-files/deci-ryan_self-determination-theory__deci-olafsen-ryan-2017-self-determination-theory-in-work-organizations.md +1881 -0
- package/skills/estack-leadership-coach/references/.source-files/deci-ryan_self-determination-theory__gagne-deci-2005-self-determination-theory-and-work-motivation.md +2058 -0
- package/skills/estack-leadership-coach/references/.source-files/deci-ryan_self-determination-theory__selfdeterminationtheory-org-theory-overview-page.md +61 -0
- package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-3-key-insights-into-the-global-workplace-2024.md +57 -0
- package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-managers-account-for-70-percent-of-variance-in-employee-engagement-2015.md +40 -0
- package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-state-of-the-global-workplace-2026-global-data-summary.md +73 -0
- package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-state-of-the-global-workplace-2026-report-landing.md +42 -0
- package/skills/estack-leadership-coach/references/.source-files/hormozi-leila_4-stages__leila-hormozi-the-art-of-delegation-blog-post.md +91 -0
- package/skills/estack-leadership-coach/references/.source-files/oncken-wass_monkeys-hbr-1974__oncken-wass-management-time-whos-got-the-monkey-hbr-classic-1974.md +969 -0
- package/skills/estack-leadership-coach/references/.source-files/sanchez_main-street-millionaire__codie-sanchez-afford-anything-podcast-ep-565-show-notes.md +89 -0
- package/skills/estack-leadership-coach/references/.source-files/sullivan_who-not-how__dan-sullivan-impact-filter-tool-and-guide-booklet.md +565 -0
- package/skills/estack-leadership-coach/references/.source-files/van-edwards_cues__vanessa-van-edwards-lewis-howes-school-of-greatness-ep-1231-show-notes.md +122 -0
- package/skills/estack-leadership-coach/references/.source-files/van-edwards_cues__vanessa-van-edwards-roger-dooley-cues-interview.md +194 -0
- package/skills/estack-leadership-coach/references/deci-ryan_self-determination-theory.md +166 -0
- package/skills/estack-leadership-coach/references/doerr_measure-what-matters.md +154 -0
- package/skills/estack-leadership-coach/references/ferriss_4hww.md +189 -0
- package/skills/estack-leadership-coach/references/gallup_engagement-research.md +105 -0
- package/skills/estack-leadership-coach/references/gerber_e-myth-revisited.md +118 -0
- package/skills/estack-leadership-coach/references/grove_high-output-management.md +95 -0
- package/skills/estack-leadership-coach/references/hormozi-alex_followthrough.md +152 -0
- package/skills/estack-leadership-coach/references/hormozi-leila_4-stages.md +146 -0
- package/skills/estack-leadership-coach/references/oncken-wass_monkeys-hbr-1974.md +128 -0
- package/skills/estack-leadership-coach/references/sanchez_main-street-millionaire.md +196 -0
- package/skills/estack-leadership-coach/references/sullivan_who-not-how.md +137 -0
- package/skills/estack-leadership-coach/references/van-edwards_cues.md +189 -0
- package/skills/estack-migrate-claude-session-history/SKILL.md +226 -0
- package/skills/estack-migrate-claude-session-history/references/path-encoding.md +55 -0
- package/skills/estack-migrate-claude-session-history/references/troubleshooting.md +96 -0
- package/skills/estack-migrate-claude-session-history/scripts/migrate-claude-history.js +1123 -0
- package/skills/estack-migrate-claude-session-history/scripts/test-append-note.js +48 -0
- package/skills/estack-migrate-claude-session-history/scripts/test-validate-migration.py +326 -0
- package/skills/estack-migrate-claude-session-history/scripts/validate-migration.py +493 -0
- package/skills/estack-pdf-to-md/SKILL.md +180 -0
- package/skills/estack-pdf-to-md/scripts/pdf_to_md.py +596 -0
- package/skills/estack-productivity-prioritization-coach/SKILL.md +124 -0
- package/skills/estack-productivity-prioritization-coach/sources/01-tony-robbins-rpm.md +39 -0
- package/skills/estack-productivity-prioritization-coach/sources/02-justin-sung-task-prioritization.md +34 -0
- package/skills/estack-prompt-builder-coach/SKILL.md +81 -81
- package/skills/estack-prompt-builder-coach/definition-of-done-generator.md +42 -42
- package/skills/estack-prompt-builder-coach/prompt-builder.md +37 -37
- package/skills/estack-prompt-builder-coach/task-shaper.md +36 -36
- package/skills/estack-prompt-builder-coach/vague-ask-auditor.md +37 -37
- package/skills/estack-read-claude-session-history/SKILL.md +204 -204
- package/skills/estack-read-claude-session-history/references/jsonl-schema.md +126 -126
- package/skills/estack-read-claude-session-history/references/modes.md +423 -423
- package/skills/estack-read-claude-session-history/references/recipes.md +271 -271
- package/skills/estack-read-claude-session-history/scripts/lib/__init__.py +1 -1
- package/skills/estack-read-claude-session-history/scripts/lib/parser.py +460 -460
- package/skills/estack-read-claude-session-history/scripts/lib/paths.py +234 -234
- package/skills/estack-read-claude-session-history/scripts/lib/search.py +179 -179
- package/skills/estack-read-claude-session-history/scripts/lib/subagents.py +88 -88
- package/skills/estack-read-claude-session-history/scripts/lib/tools.py +144 -144
- package/skills/estack-read-claude-session-history/scripts/read_transcript.py +1776 -1776
- package/skills/estack-read-claude-session-history/scripts/tests/conftest.py +40 -40
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/README.md +20 -20
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/all-noise.jsonl +4 -4
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/basic-session.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-gaps.jsonl +9 -9
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-noise.jsonl +7 -7
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-parallel-a.jsonl +3 -3
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-parallel-b.jsonl +3 -3
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-waiting.jsonl +5 -5
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/interrupted.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/multi-compact.jsonl +8 -8
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/pending-user.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta/subagents/agent-aaa.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.meta.json +1 -1
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent.jsonl +4 -4
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/time-spread.jsonl +6 -6
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/timeline-day-test.jsonl +5 -5
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-zoo.jsonl +10 -10
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/truncated.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/unicode.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-advisor.jsonl +3 -3
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-compact.jsonl +5 -5
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-thinking.jsonl +2 -2
- package/skills/estack-read-claude-session-history/scripts/tests/test_backup_roots.py +56 -56
- package/skills/estack-read-claude-session-history/scripts/tests/test_engagement.py +239 -239
- package/skills/estack-read-claude-session-history/scripts/tests/test_json_format.py +201 -201
- package/skills/estack-read-claude-session-history/scripts/tests/test_modes.py +199 -199
- package/skills/estack-read-claude-session-history/scripts/tests/test_parser.py +195 -195
- package/skills/estack-read-claude-session-history/scripts/tests/test_paths.py +133 -133
- package/skills/estack-read-claude-session-history/scripts/tests/test_search.py +78 -78
- package/skills/estack-read-claude-session-history/scripts/tests/test_subagents.py +43 -43
- package/skills/estack-read-claude-session-history/scripts/tests/test_timeline.py +179 -179
- package/skills/estack-read-claude-session-history/scripts/tests/test_timezone_and_project.py +212 -212
- package/skills/estack-read-claude-session-history/scripts/tests/test_tools.py +80 -80
- package/skills/estack-repo-search/SKILL.md +65 -65
- package/skills/estack-vscode-file-recovery/SKILL.md +188 -0
|
@@ -1,335 +1,335 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: estack-flight-planner
|
|
3
|
-
version: 1.0.3
|
|
4
|
-
description: (flight-planner) Find and rank flights between any two airports with config-driven preferences (budget, airlines, nonstop, time-of-day) and optional ground-shuttle pairing. Uses SerpAPI Google Flights (or WebSearch fallback). Saves preferences to `~/.flight-planner/config.json` and logs every search.
|
|
5
|
-
disable-model-invocation: true
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Flight Planner
|
|
9
|
-
|
|
10
|
-
A deterministic flight search and ranking pipeline. The user supplies their trip (dates + origin + destination) every run; everything else (budget, airlines, time windows, optional shuttle) comes from a saved preferences config so repeat searches are fast.
|
|
11
|
-
|
|
12
|
-
The math (filtering, pricing, shuttle buffer calculation) runs in Python scripts — never eyeballed by the LLM — so results are reproducible. The LLM's job is orchestration and presentation.
|
|
13
|
-
|
|
14
|
-
## Show progress on every question
|
|
15
|
-
|
|
16
|
-
**Every question you ask the user must show how many questions are left in the current phase.** Use a prefix like `[Q 2 of 7]` or end with `(2 questions left after this)`. This applies to Phase 1, Phase 2 wizard, Phase 2 confirmation prompts, and any clarifying follow-ups within a phase.
|
|
17
|
-
|
|
18
|
-
Counting rules:
|
|
19
|
-
- Each value question and each strength question count separately (so a 4-preference wizard with strengths = 8 questions, plus 3 non-strength = 11 total).
|
|
20
|
-
- Skip questions that don't apply (e.g., nonstop strength when user picked "no preference") don't count toward the total — recompute remaining as you go.
|
|
21
|
-
- In **batch mode**, count each line in the batch as one question, and tell the user "this batch has N questions" up front.
|
|
22
|
-
- In **Phase 2 confirmation mode** (returning user), it's effectively 1 question ("are these still your prefs?") — say so.
|
|
23
|
-
- Phase 1 has 1 question (the open "where/when").
|
|
24
|
-
|
|
25
|
-
Example phrasing:
|
|
26
|
-
- One-at-a-time: `[Q 3 of 11] What's your max budget per flight in USD?`
|
|
27
|
-
- Batch header: `Here are 4 questions in one batch — answer in any order:`
|
|
28
|
-
|
|
29
|
-
## Operating mentality — boil the ocean
|
|
30
|
-
|
|
31
|
-
**Don't make the user do work you could do yourself.** When the user gives a vague trip ("this weekend, Indiana to NJ"), do NOT bounce back with "please give me IATA codes and exact dates." Use your tools to fill in everything inferable, then present a complete proposed plan and let the user adjust.
|
|
32
|
-
|
|
33
|
-
For every vague input, before asking a follow-up, exhaust:
|
|
34
|
-
- **`date` command via Bash** for any relative date ("this weekend", "next Friday", "in 3 weeks")
|
|
35
|
-
- **`WebSearch`** for nearby major airports given a city/state/region (e.g., "Indiana" → IND, SBN, FWA; "NJ" → EWR, plus nearby LGA, JFK, PHL)
|
|
36
|
-
- **Config defaults** (`home_airport`, `frequent_destinations`) for likely matches
|
|
37
|
-
- **Flight history** for recent route patterns
|
|
38
|
-
- **Common sense + sanity checks** — e.g., if user says "Indiana" and config has `home_airport: IND`, lead with IND.
|
|
39
|
-
|
|
40
|
-
Then present:
|
|
41
|
-
|
|
42
|
-
> Here's what I worked out — adjust anything that's off:
|
|
43
|
-
> - **Dates:** 2026-05-16 (Sat) — 2026-05-17 (Sun) ← resolved from "this weekend"
|
|
44
|
-
> - **Origin:** IND (Indianapolis) — also nearby: SBN (South Bend), FWA (Fort Wayne)
|
|
45
|
-
> - **Destination:** EWR (Newark) — also nearby: LGA, JFK
|
|
46
|
-
>
|
|
47
|
-
> Want me to expand origin/destination to include the nearby airports, or run with just IND→EWR? Any changes to dates?
|
|
48
|
-
|
|
49
|
-
**The bar is "holy shit, that's done," not "good enough."** Never present a workaround when the real fix is one tool call away. Never offer to "ask more questions later" when you can answer them now. Never leave a dangling assumption — confirm it visibly. Search before asking. Verify before shipping.
|
|
50
|
-
|
|
51
|
-
This applies to every phase, not just Phase 1. If the user later says "actually, just nonstops" without specifying strength, infer `hard` from "just" and confirm in your next message, rather than asking a separate strength question.
|
|
52
|
-
|
|
53
|
-
## Files
|
|
54
|
-
|
|
55
|
-
- `scripts/check_setup.sh` — Deterministic startup check (runs in Phase 0)
|
|
56
|
-
- `scripts/fetch_flights.py` — SerpAPI Google Flights wrapper
|
|
57
|
-
- `scripts/filter_flights.py` — Filter, rank, and cluster-analyze results
|
|
58
|
-
- `scripts/pair_shuttles.py` — Optional: pair flights with a ground shuttle
|
|
59
|
-
- `references/config_schema.md` — Full config.json field reference
|
|
60
|
-
- `references/flight_history_schema.md` — Flight log format reference
|
|
61
|
-
- `references/shuttle_schedules.md` — Template + how-to for users with a local shuttle
|
|
62
|
-
|
|
63
|
-
## Persistent state (not in the skill directory)
|
|
64
|
-
|
|
65
|
-
- `~/.flight-planner/config.json` — User preferences. Created via first-run wizard. Never overwritten by skill installer.
|
|
66
|
-
- `~/.flight-planner/flight_history.json` — Append-only log of searches and selections.
|
|
67
|
-
|
|
68
|
-
`~` expands to `%USERPROFILE%` on Windows and `$HOME` on Mac/Linux.
|
|
69
|
-
|
|
70
|
-
## Workflow — four phases
|
|
71
|
-
|
|
72
|
-
Run these in order every time. Do not skip Phase 2 even if the config looks right.
|
|
73
|
-
|
|
74
|
-
### Phase 0 — Setup check (deterministic, runs on skill load)
|
|
75
|
-
|
|
76
|
-
The fenced command below runs automatically when the skill is invoked. Read its output before doing anything else — it tells you the user's setup state without you having to ask.
|
|
77
|
-
|
|
78
|
-
```!
|
|
79
|
-
bash ~/.claude/skills/estack-flight-planner/scripts/check_setup.sh
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
The output reports:
|
|
83
|
-
- Today's date and local timezone (use this when converting relative dates in Phase 1)
|
|
84
|
-
- Whether `~/.flight-planner/config.json` exists, and if so, all current preferences (with the SerpAPI key masked to "set" or "null")
|
|
85
|
-
- Whether `SERPAPI_KEY` is set in the environment
|
|
86
|
-
- Whether `~/.flight-planner/flight_history.json` exists and how many entries it has
|
|
87
|
-
|
|
88
|
-
**Decision tree based on output:**
|
|
89
|
-
- **Config exists** → Phase 1 (ask trip details), then Phase 2 in confirmation mode (show saved prefs, ask "still right?")
|
|
90
|
-
- **Config missing** → Phase 1 (ask trip details), then Phase 2 in wizard mode (walk through each preference, offer to save at end)
|
|
91
|
-
- **Config exists but `serpapi_key: null` AND no env var** → tell the user up front that you'll use the WebSearch fallback in Phase 3 Step 2, with the caveat about coverage
|
|
92
|
-
|
|
93
|
-
Don't repeat back the setup output to the user verbatim — just internalize it and adapt your behavior.
|
|
94
|
-
|
|
95
|
-
**After Phase 0 finishes, present an overview to the user before Phase 1:**
|
|
96
|
-
|
|
97
|
-
Tell the user, in your own words:
|
|
98
|
-
- What this skill does: finds and ranks flights between any two airports using their preferences.
|
|
99
|
-
- How it works: 4 phases — (1) Trip details (where/when), (2) Preferences (confirm saved config or run a first-run wizard), (3) Run the search pipeline (fetch → filter → rank → optional shuttle pairing), (4) Recommend and log.
|
|
100
|
-
- Where state lives: `~/.flight-planner/config.json` (preferences) and `~/.flight-planner/flight_history.json` (search log).
|
|
101
|
-
- Whether they're in first-run wizard mode or returning-user mode (based on Phase 0 output).
|
|
102
|
-
|
|
103
|
-
**Pacing for Phase 2 wizard (first-run only):** If Phase 0 showed no config, the user will face a multi-question wizard in Phase 2. Right after the overview, ask once: "When we get to your preferences setup, do you want me to ask all questions one at a time, or batch them so you can answer in one message?" Skip this question entirely if a config already exists (returning user — Phase 2 is just confirmation).
|
|
104
|
-
|
|
105
|
-
### Phase 1 — Trip details (one question, then a proposed plan)
|
|
106
|
-
|
|
107
|
-
Per the "boil the ocean" mentality above, **do not ask three separate questions**. Ask ONE open question, then do the work:
|
|
108
|
-
|
|
109
|
-
**The single question:** "Where are you going and when?" (Wait for answer.)
|
|
110
|
-
|
|
111
|
-
The user may answer with anything from "May 16-17 IND→EWR" (already precise) to "this weekend Indiana to NJ" (vague). Either way, the next thing you do is **resolve every inferable detail with your tools**, then present a proposed plan.
|
|
112
|
-
|
|
113
|
-
**Tool steps before you respond:**
|
|
114
|
-
|
|
115
|
-
1. **Resolve dates deterministically with the `date` command via Bash.** Never guess or do calendar math in your head.
|
|
116
|
-
- Today: `date +%Y-%m-%d`
|
|
117
|
-
- Next Friday (Linux/WSL): `date -d 'next Friday' +%Y-%m-%d`
|
|
118
|
-
- Next Friday (macOS): `date -v+Fri +%Y-%m-%d`
|
|
119
|
-
- "+N weeks": `date -d '+N weeks' +%Y-%m-%d` (Linux) / `date -v+Nw +%Y-%m-%d` (macOS)
|
|
120
|
-
- PowerShell fallback: `(Get-Date).AddDays(N).ToString('yyyy-MM-dd')`
|
|
121
|
-
- For "this weekend" / "next weekend", compute both Sat and Sun explicitly.
|
|
122
|
-
|
|
123
|
-
2. **Resolve airports — always WebSearch for common alternates, even when the user gave an exact IATA code.**
|
|
124
|
-
- Check config first: does `home_airport` or `frequent_destinations` match the region? Lead with those.
|
|
125
|
-
- **WebSearch is mandatory for every origin and every destination**, not just vague ones. Queries to run:
|
|
126
|
-
- "major airports near <location>" (when user gave a city/state/region)
|
|
127
|
-
- "airports within 100 miles of <IATA or city>" (to find common alternates even for a specific airport)
|
|
128
|
-
- "alternate airports to <IATA>" (e.g., user says EWR → surface LGA, JFK; user says LAX → surface BUR, LGB, SNA, ONT)
|
|
129
|
-
- Aim for 1 primary + 2–3 nearby alternates per endpoint. Alternates often save significant money on flights.
|
|
130
|
-
- Output IATA + full city name + approximate distance from the user's stated location so they can verify (e.g., `LGA (LaGuardia) — ~15mi from Newark`).
|
|
131
|
-
- **Never skip the alternate search.** A user who said "EWR" may not realize LGA flights to their destination are $80 cheaper — your job is to surface that option.
|
|
132
|
-
|
|
133
|
-
3. **Sanity-check the result yourself** before showing it. Are the dates in the future? Do the airports actually exist? Does the route make geographic sense?
|
|
134
|
-
|
|
135
|
-
**Then present the proposed plan in a single block:**
|
|
136
|
-
|
|
137
|
-
```
|
|
138
|
-
Here's what I've worked out — adjust anything that's off:
|
|
139
|
-
|
|
140
|
-
Dates: 2026-05-16 (Sat), 2026-05-17 (Sun) ← from "this weekend"
|
|
141
|
-
Origin: IND (Indianapolis)
|
|
142
|
-
Also nearby: SBN (South Bend), FWA (Fort Wayne)
|
|
143
|
-
Destination: EWR (Newark)
|
|
144
|
-
Also nearby: LGA (LaGuardia), JFK (Kennedy)
|
|
145
|
-
|
|
146
|
-
Want me to widen origin/destination to include the nearby airports, or
|
|
147
|
-
run with just IND→EWR? Any changes to dates?
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
If the user says "looks good" → proceed to Phase 2 with that route. If they tweak it ("add LGA, drop Sunday") → apply the change and proceed; no need to re-confirm a third time unless something is now ambiguous.
|
|
151
|
-
|
|
152
|
-
Origin and destination are **never saved to config by default**. The user can opt in to saving them as `home_airport` / `frequent_destinations` in Phase 2 if they want.
|
|
153
|
-
|
|
154
|
-
### Phase 2 — Preferences confirmation
|
|
155
|
-
|
|
156
|
-
**If `~/.flight-planner/config.json` exists:**
|
|
157
|
-
|
|
158
|
-
Read it and show a single block:
|
|
159
|
-
|
|
160
|
-
```
|
|
161
|
-
Your saved preferences:
|
|
162
|
-
Budget: $200 (soft)
|
|
163
|
-
Airlines: UA, DL (soft)
|
|
164
|
-
Nonstop: preferred (soft)
|
|
165
|
-
Time priority: 11:00–14:00, 14:00–22:00 (soft)
|
|
166
|
-
SerpAPI key: set
|
|
167
|
-
Shuttle service: none
|
|
168
|
-
|
|
169
|
-
Are these still your preferences? (yes / change <field> / skip)
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
If the user says "yes" → proceed to Phase 3. If they want to tweak a field for this run only, capture the override without writing it to disk. If they want a permanent change, ask "save this change to your config?" before writing.
|
|
173
|
-
|
|
174
|
-
**If no config file exists:**
|
|
175
|
-
|
|
176
|
-
Run the first-run setup wizard. **Strength questions are always separate from value questions** — never bundle "Budget: $200, hard or soft?" into one ask. The pacing the user chose after the overview determines how to sequence:
|
|
177
|
-
|
|
178
|
-
**The four strength-paired preferences:**
|
|
179
|
-
|
|
180
|
-
| # | Preference | Value question | Strength question |
|
|
181
|
-
|---|---|---|---|
|
|
182
|
-
| 1 | Budget | "What's your max budget per flight in USD?" | "Is the $X budget hard (exclude anything over) or soft (include but rank cheaper higher)?" |
|
|
183
|
-
| 2 | Airlines | "Any airline preferences? (IATA codes like UA, DL, AA — or 'none' for any airline)" | "Are <airlines> hard (only show these) or soft (prefer them but show others)?" |
|
|
184
|
-
| 3 | Nonstop | "Required / preferred / no preference for nonstop?" | (Only if not "no preference") "Is that hard (exclude stops) or soft (rank stops lower)?" |
|
|
185
|
-
| 4 | Time-of-day priority | "Priority time windows for departure? (e.g., 11:00-14:00,14:00-22:00 in 24h format — or 'none')" | (Only if not "none") "Is the <windows> priority hard (exclude flights outside) or soft (rank lower but include)?" |
|
|
186
|
-
|
|
187
|
-
**One-at-a-time mode:**
|
|
188
|
-
|
|
189
|
-
For each preference, ask the value question → wait for answer → ask the strength question (echoing the chosen value verbatim) → wait for answer → move to the next preference.
|
|
190
|
-
|
|
191
|
-
**Batch mode:**
|
|
192
|
-
|
|
193
|
-
Send TWO batches:
|
|
194
|
-
|
|
195
|
-
- **Batch A — values.** Ask all four value questions in one message. Wait for all answers.
|
|
196
|
-
- **Batch B — strengths.** Echo each value back and ask its strength in one message. Example:
|
|
197
|
-
```
|
|
198
|
-
Got it. Now strength for each — hard (filter) or soft (rank)?
|
|
199
|
-
|
|
200
|
-
1. Budget = $200 → hard or soft?
|
|
201
|
-
2. Airlines = UA, DL → hard or soft?
|
|
202
|
-
3. Nonstop = preferred → hard or soft?
|
|
203
|
-
4. Times = 11–14, 14–22 → hard or soft?
|
|
204
|
-
```
|
|
205
|
-
Skip lines in Batch B where strength doesn't apply (airlines = "none", nonstop = "no preference", times = "none").
|
|
206
|
-
|
|
207
|
-
**After the strength-paired preferences, ask the remaining non-strength questions** (these don't need a strength companion). One at a time, or one final batch — match the user's chosen pacing:
|
|
208
|
-
|
|
209
|
-
5. **SerpAPI key** — "Do you have a SerpAPI key? (yes — paste it / no — explain how to get one / skip — use WebSearch fallback)". See the SerpAPI walkthrough section below.
|
|
210
|
-
6. **Optional fields** — "Want to save a home airport so we suggest it next time? (IATA code or 'no')". Same for `frequent_destinations`.
|
|
211
|
-
7. **Optional shuttle service** — "Do you use a ground shuttle to your airport that you'd like the skill to pair flights with? (yes / no)". If yes, ask for company name + schedule URL(s). See shuttle setup below.
|
|
212
|
-
|
|
213
|
-
After collecting answers, show the full config and ask "Save this to ~/.flight-planner/config.json?" before writing.
|
|
214
|
-
|
|
215
|
-
### Phase 3 — Execute
|
|
216
|
-
|
|
217
|
-
Run the scripts. Do all math via the scripts, never in your head.
|
|
218
|
-
|
|
219
|
-
**Step 1 — Log search start**
|
|
220
|
-
|
|
221
|
-
Append a `search_started` entry to `~/.flight-planner/flight_history.json` with timestamp, dates, route, and a snapshot of the preferences used (after Phase 2 overrides). See `references/flight_history_schema.md` for the exact format.
|
|
222
|
-
|
|
223
|
-
**Step 2 — Fetch live flight data**
|
|
224
|
-
|
|
225
|
-
If the user has a SerpAPI key:
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
python scripts/fetch_flights.py \
|
|
229
|
-
--dates 2026-05-09,2026-05-10 \
|
|
230
|
-
--routes IND-EWR,ORD-LGA \
|
|
231
|
-
--airlines UA,DL \
|
|
232
|
-
--stops 1
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Pass `--airlines` only if the user has airline preferences. Pass `--stops 1` only if `nonstop_preference` is `required` or `preferred` with `hard` strength. Omit both for "any airline, any stops".
|
|
236
|
-
|
|
237
|
-
The script reads `SERPAPI_KEY` from the environment or accepts `--api-key`. Saves raw JSON to a temp directory and prints the directory path on stdout — capture this for the next step.
|
|
238
|
-
|
|
239
|
-
**If the user has no SerpAPI key:** Use WebSearch to query flight prices for each route × date combination. Tell the user up front: "Without a SerpAPI key, results will be less comprehensive — I'll search the web for each route but won't have structured price/time data." Build a JSON file in the same shape `fetch_flights.py` would produce so the downstream scripts work unchanged. If you're not confident the WebSearch results are reliable, say so and recommend they get a key.
|
|
240
|
-
|
|
241
|
-
**Step 3 — Filter and rank flights**
|
|
242
|
-
|
|
243
|
-
```bash
|
|
244
|
-
python scripts/filter_flights.py \
|
|
245
|
-
--json-dir <temp-dir-from-step-2> \
|
|
246
|
-
--max-price 200 \
|
|
247
|
-
--time-priority "11:00-14:00,14:00-22:00" \
|
|
248
|
-
--from IND,ORD \
|
|
249
|
-
--to EWR,LGA \
|
|
250
|
-
--soft-filters max-price,time-priority
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Pass `--soft-filters` listing every preference whose strength is `soft` — those become rank weights instead of hard filters. Pass all hard preferences as strict filters with no `--soft-filters` entry.
|
|
254
|
-
|
|
255
|
-
The script outputs filtered flights as JSON to stdout. Capture it for step 5.
|
|
256
|
-
|
|
257
|
-
**Step 4 — Handle empty results (constraint relaxation)**
|
|
258
|
-
|
|
259
|
-
If `filter_flights.py` returns `[]`, rerun with `--cluster-analysis`:
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
python scripts/filter_flights.py --json-dir <dir> --cluster-analysis
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Read the report — it shows which constraint(s) eliminated which flight counts plus a price distribution. **Propose specific relaxations to the user**, not generic "try again with looser settings":
|
|
266
|
-
|
|
267
|
-
- "Your $200 hard budget filtered all 23 flights. Cheapest available is $237. Want to raise the budget to $240?"
|
|
268
|
-
- "The 11:00–22:00 window filtered out 15 morning flights. Want to add a 06:00–11:00 priority band?"
|
|
269
|
-
- "Airline filter (UA only) eliminated 18 of 23 flights. Want to drop the airline filter for this search?"
|
|
270
|
-
|
|
271
|
-
Wait for the user to pick a specific relaxation, then rerun step 3 with adjusted args.
|
|
272
|
-
|
|
273
|
-
**Step 5 — Pair shuttles (only if `shuttle_service` is set in config)**
|
|
274
|
-
|
|
275
|
-
Skip this entire step if the user's config has `shuttle_service: null`. Otherwise:
|
|
276
|
-
|
|
277
|
-
Fetch the user's shuttle schedule URLs (from config) with WebFetch in parallel. Build a `shuttles.json` file matching the schema in `references/shuttle_schedules.md`. Then:
|
|
278
|
-
|
|
279
|
-
```bash
|
|
280
|
-
python scripts/pair_shuttles.py \
|
|
281
|
-
--flights-json <filtered-output-from-step-3> \
|
|
282
|
-
--shuttles-json <shuttles-file> \
|
|
283
|
-
--shuttle-costs "IND:30,ORD:60"
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
`--shuttle-costs` is a comma-separated `AIRPORT:USD` list from the user's `shuttle_service.costs` config field. Outputs a markdown ranked plans table. Show it to the user.
|
|
287
|
-
|
|
288
|
-
**Step 6 — Present results**
|
|
289
|
-
|
|
290
|
-
- If pairing was done: show the markdown table from `pair_shuttles.py` directly.
|
|
291
|
-
- If no shuttle: print a flights-only table with Date | Flight | Route | Departs | Arrives | Price | Airline | Stops | Soft-filter notes.
|
|
292
|
-
|
|
293
|
-
Recommend the top row in one or two sentences. Ask which flight they want to book.
|
|
294
|
-
|
|
295
|
-
**Step 7 — Log the selection**
|
|
296
|
-
|
|
297
|
-
When the user confirms a choice, append a `selection_made` entry to `~/.flight-planner/flight_history.json`, linking back to the `search_started` entry from step 1. See `references/flight_history_schema.md`.
|
|
298
|
-
|
|
299
|
-
**Step 8 — Offer booking link**
|
|
300
|
-
|
|
301
|
-
Offer to open the airline's booking page. For most airlines a generic Google Flights link works:
|
|
302
|
-
|
|
303
|
-
```
|
|
304
|
-
https://www.google.com/travel/flights?q=Flights%20from%20<DEP>%20to%20<ARR>%20on%20<DATE>
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
## SerpAPI walkthrough
|
|
308
|
-
|
|
309
|
-
If the user doesn't have a SerpAPI key and asks for help getting one:
|
|
310
|
-
|
|
311
|
-
1. Tell them: "SerpAPI gives the skill structured Google Flights data. Free tier is 100 searches/month — usually enough for personal trip planning. Paid plans start at $50/month."
|
|
312
|
-
2. Walk them to https://serpapi.com/users/sign_up — sign up with email.
|
|
313
|
-
3. After signup, the API key is at https://serpapi.com/manage-api-key.
|
|
314
|
-
4. To set it permanently, walk them through either:
|
|
315
|
-
- Saving it in their `~/.flight-planner/config.json` (`serpapi_key` field), or
|
|
316
|
-
- Setting `SERPAPI_KEY` as an environment variable in their shell profile.
|
|
317
|
-
5. If they don't want a key: confirm they want the WebSearch fallback. Set `serpapi_key: null` in config. Tell them: "I'll use WebSearch each run. Results won't be as complete and prices may be approximations."
|
|
318
|
-
|
|
319
|
-
## Important behaviors
|
|
320
|
-
|
|
321
|
-
**Always run the scripts.** Don't summarize flights from memory. If the conversation has stale flight data from a previous run, re-fetch.
|
|
322
|
-
|
|
323
|
-
**Strength matters.** Hard filters exclude; soft filters rank. Pass soft filters via `--soft-filters` to keep non-matching results visible.
|
|
324
|
-
|
|
325
|
-
**No shuttle data ships with this skill.** Each user provides their own shuttle service info in their config. The skill scrapes their schedule URLs at search time.
|
|
326
|
-
|
|
327
|
-
**Origin and destination are not saved by default.** Only save them if the user explicitly opts in.
|
|
328
|
-
|
|
329
|
-
**Two log entries per search.** Entry 1 on fetch (step 1), entry 2 on selection (step 7). Both are written even if the user abandons mid-search — the search_started entry preserves what they were looking for.
|
|
330
|
-
|
|
331
|
-
**Times in the config are 24-hour HH:MM format.** Display them in 12-hour to the user but store 24-hour.
|
|
332
|
-
|
|
1
|
+
---
|
|
2
|
+
name: estack-flight-planner
|
|
3
|
+
version: 1.0.3
|
|
4
|
+
description: (flight-planner) Find and rank flights between any two airports with config-driven preferences (budget, airlines, nonstop, time-of-day) and optional ground-shuttle pairing. Uses SerpAPI Google Flights (or WebSearch fallback). Saves preferences to `~/.flight-planner/config.json` and logs every search.
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Flight Planner
|
|
9
|
+
|
|
10
|
+
A deterministic flight search and ranking pipeline. The user supplies their trip (dates + origin + destination) every run; everything else (budget, airlines, time windows, optional shuttle) comes from a saved preferences config so repeat searches are fast.
|
|
11
|
+
|
|
12
|
+
The math (filtering, pricing, shuttle buffer calculation) runs in Python scripts — never eyeballed by the LLM — so results are reproducible. The LLM's job is orchestration and presentation.
|
|
13
|
+
|
|
14
|
+
## Show progress on every question
|
|
15
|
+
|
|
16
|
+
**Every question you ask the user must show how many questions are left in the current phase.** Use a prefix like `[Q 2 of 7]` or end with `(2 questions left after this)`. This applies to Phase 1, Phase 2 wizard, Phase 2 confirmation prompts, and any clarifying follow-ups within a phase.
|
|
17
|
+
|
|
18
|
+
Counting rules:
|
|
19
|
+
- Each value question and each strength question count separately (so a 4-preference wizard with strengths = 8 questions, plus 3 non-strength = 11 total).
|
|
20
|
+
- Skip questions that don't apply (e.g., nonstop strength when user picked "no preference") don't count toward the total — recompute remaining as you go.
|
|
21
|
+
- In **batch mode**, count each line in the batch as one question, and tell the user "this batch has N questions" up front.
|
|
22
|
+
- In **Phase 2 confirmation mode** (returning user), it's effectively 1 question ("are these still your prefs?") — say so.
|
|
23
|
+
- Phase 1 has 1 question (the open "where/when").
|
|
24
|
+
|
|
25
|
+
Example phrasing:
|
|
26
|
+
- One-at-a-time: `[Q 3 of 11] What's your max budget per flight in USD?`
|
|
27
|
+
- Batch header: `Here are 4 questions in one batch — answer in any order:`
|
|
28
|
+
|
|
29
|
+
## Operating mentality — boil the ocean
|
|
30
|
+
|
|
31
|
+
**Don't make the user do work you could do yourself.** When the user gives a vague trip ("this weekend, Indiana to NJ"), do NOT bounce back with "please give me IATA codes and exact dates." Use your tools to fill in everything inferable, then present a complete proposed plan and let the user adjust.
|
|
32
|
+
|
|
33
|
+
For every vague input, before asking a follow-up, exhaust:
|
|
34
|
+
- **`date` command via Bash** for any relative date ("this weekend", "next Friday", "in 3 weeks")
|
|
35
|
+
- **`WebSearch`** for nearby major airports given a city/state/region (e.g., "Indiana" → IND, SBN, FWA; "NJ" → EWR, plus nearby LGA, JFK, PHL)
|
|
36
|
+
- **Config defaults** (`home_airport`, `frequent_destinations`) for likely matches
|
|
37
|
+
- **Flight history** for recent route patterns
|
|
38
|
+
- **Common sense + sanity checks** — e.g., if user says "Indiana" and config has `home_airport: IND`, lead with IND.
|
|
39
|
+
|
|
40
|
+
Then present:
|
|
41
|
+
|
|
42
|
+
> Here's what I worked out — adjust anything that's off:
|
|
43
|
+
> - **Dates:** 2026-05-16 (Sat) — 2026-05-17 (Sun) ← resolved from "this weekend"
|
|
44
|
+
> - **Origin:** IND (Indianapolis) — also nearby: SBN (South Bend), FWA (Fort Wayne)
|
|
45
|
+
> - **Destination:** EWR (Newark) — also nearby: LGA, JFK
|
|
46
|
+
>
|
|
47
|
+
> Want me to expand origin/destination to include the nearby airports, or run with just IND→EWR? Any changes to dates?
|
|
48
|
+
|
|
49
|
+
**The bar is "holy shit, that's done," not "good enough."** Never present a workaround when the real fix is one tool call away. Never offer to "ask more questions later" when you can answer them now. Never leave a dangling assumption — confirm it visibly. Search before asking. Verify before shipping.
|
|
50
|
+
|
|
51
|
+
This applies to every phase, not just Phase 1. If the user later says "actually, just nonstops" without specifying strength, infer `hard` from "just" and confirm in your next message, rather than asking a separate strength question.
|
|
52
|
+
|
|
53
|
+
## Files
|
|
54
|
+
|
|
55
|
+
- `scripts/check_setup.sh` — Deterministic startup check (runs in Phase 0)
|
|
56
|
+
- `scripts/fetch_flights.py` — SerpAPI Google Flights wrapper
|
|
57
|
+
- `scripts/filter_flights.py` — Filter, rank, and cluster-analyze results
|
|
58
|
+
- `scripts/pair_shuttles.py` — Optional: pair flights with a ground shuttle
|
|
59
|
+
- `references/config_schema.md` — Full config.json field reference
|
|
60
|
+
- `references/flight_history_schema.md` — Flight log format reference
|
|
61
|
+
- `references/shuttle_schedules.md` — Template + how-to for users with a local shuttle
|
|
62
|
+
|
|
63
|
+
## Persistent state (not in the skill directory)
|
|
64
|
+
|
|
65
|
+
- `~/.flight-planner/config.json` — User preferences. Created via first-run wizard. Never overwritten by skill installer.
|
|
66
|
+
- `~/.flight-planner/flight_history.json` — Append-only log of searches and selections.
|
|
67
|
+
|
|
68
|
+
`~` expands to `%USERPROFILE%` on Windows and `$HOME` on Mac/Linux.
|
|
69
|
+
|
|
70
|
+
## Workflow — four phases
|
|
71
|
+
|
|
72
|
+
Run these in order every time. Do not skip Phase 2 even if the config looks right.
|
|
73
|
+
|
|
74
|
+
### Phase 0 — Setup check (deterministic, runs on skill load)
|
|
75
|
+
|
|
76
|
+
The fenced command below runs automatically when the skill is invoked. Read its output before doing anything else — it tells you the user's setup state without you having to ask.
|
|
77
|
+
|
|
78
|
+
```!
|
|
79
|
+
bash ~/.claude/skills/estack-flight-planner/scripts/check_setup.sh
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The output reports:
|
|
83
|
+
- Today's date and local timezone (use this when converting relative dates in Phase 1)
|
|
84
|
+
- Whether `~/.flight-planner/config.json` exists, and if so, all current preferences (with the SerpAPI key masked to "set" or "null")
|
|
85
|
+
- Whether `SERPAPI_KEY` is set in the environment
|
|
86
|
+
- Whether `~/.flight-planner/flight_history.json` exists and how many entries it has
|
|
87
|
+
|
|
88
|
+
**Decision tree based on output:**
|
|
89
|
+
- **Config exists** → Phase 1 (ask trip details), then Phase 2 in confirmation mode (show saved prefs, ask "still right?")
|
|
90
|
+
- **Config missing** → Phase 1 (ask trip details), then Phase 2 in wizard mode (walk through each preference, offer to save at end)
|
|
91
|
+
- **Config exists but `serpapi_key: null` AND no env var** → tell the user up front that you'll use the WebSearch fallback in Phase 3 Step 2, with the caveat about coverage
|
|
92
|
+
|
|
93
|
+
Don't repeat back the setup output to the user verbatim — just internalize it and adapt your behavior.
|
|
94
|
+
|
|
95
|
+
**After Phase 0 finishes, present an overview to the user before Phase 1:**
|
|
96
|
+
|
|
97
|
+
Tell the user, in your own words:
|
|
98
|
+
- What this skill does: finds and ranks flights between any two airports using their preferences.
|
|
99
|
+
- How it works: 4 phases — (1) Trip details (where/when), (2) Preferences (confirm saved config or run a first-run wizard), (3) Run the search pipeline (fetch → filter → rank → optional shuttle pairing), (4) Recommend and log.
|
|
100
|
+
- Where state lives: `~/.flight-planner/config.json` (preferences) and `~/.flight-planner/flight_history.json` (search log).
|
|
101
|
+
- Whether they're in first-run wizard mode or returning-user mode (based on Phase 0 output).
|
|
102
|
+
|
|
103
|
+
**Pacing for Phase 2 wizard (first-run only):** If Phase 0 showed no config, the user will face a multi-question wizard in Phase 2. Right after the overview, ask once: "When we get to your preferences setup, do you want me to ask all questions one at a time, or batch them so you can answer in one message?" Skip this question entirely if a config already exists (returning user — Phase 2 is just confirmation).
|
|
104
|
+
|
|
105
|
+
### Phase 1 — Trip details (one question, then a proposed plan)
|
|
106
|
+
|
|
107
|
+
Per the "boil the ocean" mentality above, **do not ask three separate questions**. Ask ONE open question, then do the work:
|
|
108
|
+
|
|
109
|
+
**The single question:** "Where are you going and when?" (Wait for answer.)
|
|
110
|
+
|
|
111
|
+
The user may answer with anything from "May 16-17 IND→EWR" (already precise) to "this weekend Indiana to NJ" (vague). Either way, the next thing you do is **resolve every inferable detail with your tools**, then present a proposed plan.
|
|
112
|
+
|
|
113
|
+
**Tool steps before you respond:**
|
|
114
|
+
|
|
115
|
+
1. **Resolve dates deterministically with the `date` command via Bash.** Never guess or do calendar math in your head.
|
|
116
|
+
- Today: `date +%Y-%m-%d`
|
|
117
|
+
- Next Friday (Linux/WSL): `date -d 'next Friday' +%Y-%m-%d`
|
|
118
|
+
- Next Friday (macOS): `date -v+Fri +%Y-%m-%d`
|
|
119
|
+
- "+N weeks": `date -d '+N weeks' +%Y-%m-%d` (Linux) / `date -v+Nw +%Y-%m-%d` (macOS)
|
|
120
|
+
- PowerShell fallback: `(Get-Date).AddDays(N).ToString('yyyy-MM-dd')`
|
|
121
|
+
- For "this weekend" / "next weekend", compute both Sat and Sun explicitly.
|
|
122
|
+
|
|
123
|
+
2. **Resolve airports — always WebSearch for common alternates, even when the user gave an exact IATA code.**
|
|
124
|
+
- Check config first: does `home_airport` or `frequent_destinations` match the region? Lead with those.
|
|
125
|
+
- **WebSearch is mandatory for every origin and every destination**, not just vague ones. Queries to run:
|
|
126
|
+
- "major airports near <location>" (when user gave a city/state/region)
|
|
127
|
+
- "airports within 100 miles of <IATA or city>" (to find common alternates even for a specific airport)
|
|
128
|
+
- "alternate airports to <IATA>" (e.g., user says EWR → surface LGA, JFK; user says LAX → surface BUR, LGB, SNA, ONT)
|
|
129
|
+
- Aim for 1 primary + 2–3 nearby alternates per endpoint. Alternates often save significant money on flights.
|
|
130
|
+
- Output IATA + full city name + approximate distance from the user's stated location so they can verify (e.g., `LGA (LaGuardia) — ~15mi from Newark`).
|
|
131
|
+
- **Never skip the alternate search.** A user who said "EWR" may not realize LGA flights to their destination are $80 cheaper — your job is to surface that option.
|
|
132
|
+
|
|
133
|
+
3. **Sanity-check the result yourself** before showing it. Are the dates in the future? Do the airports actually exist? Does the route make geographic sense?
|
|
134
|
+
|
|
135
|
+
**Then present the proposed plan in a single block:**
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Here's what I've worked out — adjust anything that's off:
|
|
139
|
+
|
|
140
|
+
Dates: 2026-05-16 (Sat), 2026-05-17 (Sun) ← from "this weekend"
|
|
141
|
+
Origin: IND (Indianapolis)
|
|
142
|
+
Also nearby: SBN (South Bend), FWA (Fort Wayne)
|
|
143
|
+
Destination: EWR (Newark)
|
|
144
|
+
Also nearby: LGA (LaGuardia), JFK (Kennedy)
|
|
145
|
+
|
|
146
|
+
Want me to widen origin/destination to include the nearby airports, or
|
|
147
|
+
run with just IND→EWR? Any changes to dates?
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If the user says "looks good" → proceed to Phase 2 with that route. If they tweak it ("add LGA, drop Sunday") → apply the change and proceed; no need to re-confirm a third time unless something is now ambiguous.
|
|
151
|
+
|
|
152
|
+
Origin and destination are **never saved to config by default**. The user can opt in to saving them as `home_airport` / `frequent_destinations` in Phase 2 if they want.
|
|
153
|
+
|
|
154
|
+
### Phase 2 — Preferences confirmation
|
|
155
|
+
|
|
156
|
+
**If `~/.flight-planner/config.json` exists:**
|
|
157
|
+
|
|
158
|
+
Read it and show a single block:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
Your saved preferences:
|
|
162
|
+
Budget: $200 (soft)
|
|
163
|
+
Airlines: UA, DL (soft)
|
|
164
|
+
Nonstop: preferred (soft)
|
|
165
|
+
Time priority: 11:00–14:00, 14:00–22:00 (soft)
|
|
166
|
+
SerpAPI key: set
|
|
167
|
+
Shuttle service: none
|
|
168
|
+
|
|
169
|
+
Are these still your preferences? (yes / change <field> / skip)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If the user says "yes" → proceed to Phase 3. If they want to tweak a field for this run only, capture the override without writing it to disk. If they want a permanent change, ask "save this change to your config?" before writing.
|
|
173
|
+
|
|
174
|
+
**If no config file exists:**
|
|
175
|
+
|
|
176
|
+
Run the first-run setup wizard. **Strength questions are always separate from value questions** — never bundle "Budget: $200, hard or soft?" into one ask. The pacing the user chose after the overview determines how to sequence:
|
|
177
|
+
|
|
178
|
+
**The four strength-paired preferences:**
|
|
179
|
+
|
|
180
|
+
| # | Preference | Value question | Strength question |
|
|
181
|
+
|---|---|---|---|
|
|
182
|
+
| 1 | Budget | "What's your max budget per flight in USD?" | "Is the $X budget hard (exclude anything over) or soft (include but rank cheaper higher)?" |
|
|
183
|
+
| 2 | Airlines | "Any airline preferences? (IATA codes like UA, DL, AA — or 'none' for any airline)" | "Are <airlines> hard (only show these) or soft (prefer them but show others)?" |
|
|
184
|
+
| 3 | Nonstop | "Required / preferred / no preference for nonstop?" | (Only if not "no preference") "Is that hard (exclude stops) or soft (rank stops lower)?" |
|
|
185
|
+
| 4 | Time-of-day priority | "Priority time windows for departure? (e.g., 11:00-14:00,14:00-22:00 in 24h format — or 'none')" | (Only if not "none") "Is the <windows> priority hard (exclude flights outside) or soft (rank lower but include)?" |
|
|
186
|
+
|
|
187
|
+
**One-at-a-time mode:**
|
|
188
|
+
|
|
189
|
+
For each preference, ask the value question → wait for answer → ask the strength question (echoing the chosen value verbatim) → wait for answer → move to the next preference.
|
|
190
|
+
|
|
191
|
+
**Batch mode:**
|
|
192
|
+
|
|
193
|
+
Send TWO batches:
|
|
194
|
+
|
|
195
|
+
- **Batch A — values.** Ask all four value questions in one message. Wait for all answers.
|
|
196
|
+
- **Batch B — strengths.** Echo each value back and ask its strength in one message. Example:
|
|
197
|
+
```
|
|
198
|
+
Got it. Now strength for each — hard (filter) or soft (rank)?
|
|
199
|
+
|
|
200
|
+
1. Budget = $200 → hard or soft?
|
|
201
|
+
2. Airlines = UA, DL → hard or soft?
|
|
202
|
+
3. Nonstop = preferred → hard or soft?
|
|
203
|
+
4. Times = 11–14, 14–22 → hard or soft?
|
|
204
|
+
```
|
|
205
|
+
Skip lines in Batch B where strength doesn't apply (airlines = "none", nonstop = "no preference", times = "none").
|
|
206
|
+
|
|
207
|
+
**After the strength-paired preferences, ask the remaining non-strength questions** (these don't need a strength companion). One at a time, or one final batch — match the user's chosen pacing:
|
|
208
|
+
|
|
209
|
+
5. **SerpAPI key** — "Do you have a SerpAPI key? (yes — paste it / no — explain how to get one / skip — use WebSearch fallback)". See the SerpAPI walkthrough section below.
|
|
210
|
+
6. **Optional fields** — "Want to save a home airport so we suggest it next time? (IATA code or 'no')". Same for `frequent_destinations`.
|
|
211
|
+
7. **Optional shuttle service** — "Do you use a ground shuttle to your airport that you'd like the skill to pair flights with? (yes / no)". If yes, ask for company name + schedule URL(s). See shuttle setup below.
|
|
212
|
+
|
|
213
|
+
After collecting answers, show the full config and ask "Save this to ~/.flight-planner/config.json?" before writing.
|
|
214
|
+
|
|
215
|
+
### Phase 3 — Execute
|
|
216
|
+
|
|
217
|
+
Run the scripts. Do all math via the scripts, never in your head.
|
|
218
|
+
|
|
219
|
+
**Step 1 — Log search start**
|
|
220
|
+
|
|
221
|
+
Append a `search_started` entry to `~/.flight-planner/flight_history.json` with timestamp, dates, route, and a snapshot of the preferences used (after Phase 2 overrides). See `references/flight_history_schema.md` for the exact format.
|
|
222
|
+
|
|
223
|
+
**Step 2 — Fetch live flight data**
|
|
224
|
+
|
|
225
|
+
If the user has a SerpAPI key:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
python scripts/fetch_flights.py \
|
|
229
|
+
--dates 2026-05-09,2026-05-10 \
|
|
230
|
+
--routes IND-EWR,ORD-LGA \
|
|
231
|
+
--airlines UA,DL \
|
|
232
|
+
--stops 1
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Pass `--airlines` only if the user has airline preferences. Pass `--stops 1` only if `nonstop_preference` is `required` or `preferred` with `hard` strength. Omit both for "any airline, any stops".
|
|
236
|
+
|
|
237
|
+
The script reads `SERPAPI_KEY` from the environment or accepts `--api-key`. Saves raw JSON to a temp directory and prints the directory path on stdout — capture this for the next step.
|
|
238
|
+
|
|
239
|
+
**If the user has no SerpAPI key:** Use WebSearch to query flight prices for each route × date combination. Tell the user up front: "Without a SerpAPI key, results will be less comprehensive — I'll search the web for each route but won't have structured price/time data." Build a JSON file in the same shape `fetch_flights.py` would produce so the downstream scripts work unchanged. If you're not confident the WebSearch results are reliable, say so and recommend they get a key.
|
|
240
|
+
|
|
241
|
+
**Step 3 — Filter and rank flights**
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
python scripts/filter_flights.py \
|
|
245
|
+
--json-dir <temp-dir-from-step-2> \
|
|
246
|
+
--max-price 200 \
|
|
247
|
+
--time-priority "11:00-14:00,14:00-22:00" \
|
|
248
|
+
--from IND,ORD \
|
|
249
|
+
--to EWR,LGA \
|
|
250
|
+
--soft-filters max-price,time-priority
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Pass `--soft-filters` listing every preference whose strength is `soft` — those become rank weights instead of hard filters. Pass all hard preferences as strict filters with no `--soft-filters` entry.
|
|
254
|
+
|
|
255
|
+
The script outputs filtered flights as JSON to stdout. Capture it for step 5.
|
|
256
|
+
|
|
257
|
+
**Step 4 — Handle empty results (constraint relaxation)**
|
|
258
|
+
|
|
259
|
+
If `filter_flights.py` returns `[]`, rerun with `--cluster-analysis`:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
python scripts/filter_flights.py --json-dir <dir> --cluster-analysis
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Read the report — it shows which constraint(s) eliminated which flight counts plus a price distribution. **Propose specific relaxations to the user**, not generic "try again with looser settings":
|
|
266
|
+
|
|
267
|
+
- "Your $200 hard budget filtered all 23 flights. Cheapest available is $237. Want to raise the budget to $240?"
|
|
268
|
+
- "The 11:00–22:00 window filtered out 15 morning flights. Want to add a 06:00–11:00 priority band?"
|
|
269
|
+
- "Airline filter (UA only) eliminated 18 of 23 flights. Want to drop the airline filter for this search?"
|
|
270
|
+
|
|
271
|
+
Wait for the user to pick a specific relaxation, then rerun step 3 with adjusted args.
|
|
272
|
+
|
|
273
|
+
**Step 5 — Pair shuttles (only if `shuttle_service` is set in config)**
|
|
274
|
+
|
|
275
|
+
Skip this entire step if the user's config has `shuttle_service: null`. Otherwise:
|
|
276
|
+
|
|
277
|
+
Fetch the user's shuttle schedule URLs (from config) with WebFetch in parallel. Build a `shuttles.json` file matching the schema in `references/shuttle_schedules.md`. Then:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
python scripts/pair_shuttles.py \
|
|
281
|
+
--flights-json <filtered-output-from-step-3> \
|
|
282
|
+
--shuttles-json <shuttles-file> \
|
|
283
|
+
--shuttle-costs "IND:30,ORD:60"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`--shuttle-costs` is a comma-separated `AIRPORT:USD` list from the user's `shuttle_service.costs` config field. Outputs a markdown ranked plans table. Show it to the user.
|
|
287
|
+
|
|
288
|
+
**Step 6 — Present results**
|
|
289
|
+
|
|
290
|
+
- If pairing was done: show the markdown table from `pair_shuttles.py` directly.
|
|
291
|
+
- If no shuttle: print a flights-only table with Date | Flight | Route | Departs | Arrives | Price | Airline | Stops | Soft-filter notes.
|
|
292
|
+
|
|
293
|
+
Recommend the top row in one or two sentences. Ask which flight they want to book.
|
|
294
|
+
|
|
295
|
+
**Step 7 — Log the selection**
|
|
296
|
+
|
|
297
|
+
When the user confirms a choice, append a `selection_made` entry to `~/.flight-planner/flight_history.json`, linking back to the `search_started` entry from step 1. See `references/flight_history_schema.md`.
|
|
298
|
+
|
|
299
|
+
**Step 8 — Offer booking link**
|
|
300
|
+
|
|
301
|
+
Offer to open the airline's booking page. For most airlines a generic Google Flights link works:
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
https://www.google.com/travel/flights?q=Flights%20from%20<DEP>%20to%20<ARR>%20on%20<DATE>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## SerpAPI walkthrough
|
|
308
|
+
|
|
309
|
+
If the user doesn't have a SerpAPI key and asks for help getting one:
|
|
310
|
+
|
|
311
|
+
1. Tell them: "SerpAPI gives the skill structured Google Flights data. Free tier is 100 searches/month — usually enough for personal trip planning. Paid plans start at $50/month."
|
|
312
|
+
2. Walk them to https://serpapi.com/users/sign_up — sign up with email.
|
|
313
|
+
3. After signup, the API key is at https://serpapi.com/manage-api-key.
|
|
314
|
+
4. To set it permanently, walk them through either:
|
|
315
|
+
- Saving it in their `~/.flight-planner/config.json` (`serpapi_key` field), or
|
|
316
|
+
- Setting `SERPAPI_KEY` as an environment variable in their shell profile.
|
|
317
|
+
5. If they don't want a key: confirm they want the WebSearch fallback. Set `serpapi_key: null` in config. Tell them: "I'll use WebSearch each run. Results won't be as complete and prices may be approximations."
|
|
318
|
+
|
|
319
|
+
## Important behaviors
|
|
320
|
+
|
|
321
|
+
**Always run the scripts.** Don't summarize flights from memory. If the conversation has stale flight data from a previous run, re-fetch.
|
|
322
|
+
|
|
323
|
+
**Strength matters.** Hard filters exclude; soft filters rank. Pass soft filters via `--soft-filters` to keep non-matching results visible.
|
|
324
|
+
|
|
325
|
+
**No shuttle data ships with this skill.** Each user provides their own shuttle service info in their config. The skill scrapes their schedule URLs at search time.
|
|
326
|
+
|
|
327
|
+
**Origin and destination are not saved by default.** Only save them if the user explicitly opts in.
|
|
328
|
+
|
|
329
|
+
**Two log entries per search.** Entry 1 on fetch (step 1), entry 2 on selection (step 7). Both are written even if the user abandons mid-search — the search_started entry preserves what they were looking for.
|
|
330
|
+
|
|
331
|
+
**Times in the config are 24-hour HH:MM format.** Display them in 12-hour to the user but store 24-hour.
|
|
332
|
+
|
|
333
333
|
---
|
|
334
334
|
|
|
335
335
|
## Skill Feedback
|