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.
Files changed (128) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +5 -0
  3. package/bin/install.cjs +981 -950
  4. package/hooks/repo-search-nudge.js +32 -32
  5. package/package.json +1 -1
  6. package/skills/estack-active-learning-tutor/SKILL.md +339 -339
  7. package/skills/estack-better-title/SKILL.md +64 -64
  8. package/skills/estack-better-title/scripts/rename.sh +55 -55
  9. package/skills/estack-chris-voss/SKILL.md +80 -80
  10. package/skills/estack-chris-voss/references/elliot-notes.md +120 -120
  11. package/skills/estack-chris-voss/references/voss-principles.md +210 -210
  12. package/skills/estack-customer-discovery/SKILL.md +60 -60
  13. package/skills/estack-flight-planner/SKILL.md +332 -332
  14. package/skills/estack-flight-planner/references/config_schema.md +156 -156
  15. package/skills/estack-flight-planner/references/flight_history_schema.md +97 -97
  16. package/skills/estack-flight-planner/references/shuttle_schedules.md +98 -98
  17. package/skills/estack-flight-planner/scripts/check_setup.sh +89 -89
  18. package/skills/estack-flight-planner/scripts/fetch_flights.py +99 -99
  19. package/skills/estack-flight-planner/scripts/filter_flights.py +265 -265
  20. package/skills/estack-flight-planner/scripts/pair_shuttles.py +173 -173
  21. package/skills/estack-github-issue-tracker/SKILL.md +322 -322
  22. package/skills/estack-github-issue-tracker/bin/tracker-tools.cjs +1358 -1358
  23. package/skills/estack-github-issue-tracker/references/gh-cli-patterns.md +124 -124
  24. package/skills/estack-github-issue-tracker/references/result-file-schema.md +156 -156
  25. package/skills/estack-github-issue-tracker/references/tracker-schema.md +96 -96
  26. package/skills/estack-github-issue-tracker/tracker-template.md +58 -58
  27. package/skills/estack-leadership-coach/SKILL.md +235 -0
  28. package/skills/estack-leadership-coach/adding-references.md +280 -0
  29. package/skills/estack-leadership-coach/frameworks/delegation/flows/post-mortem.md +120 -0
  30. package/skills/estack-leadership-coach/frameworks/delegation/flows/pre-delegation.md +138 -0
  31. package/skills/estack-leadership-coach/frameworks/delegation/phases/1-intake.md +145 -0
  32. package/skills/estack-leadership-coach/frameworks/delegation/phases/2-trm-assessment.md +119 -0
  33. package/skills/estack-leadership-coach/frameworks/delegation/phases/3-enrollment.md +132 -0
  34. package/skills/estack-leadership-coach/frameworks/delegation/phases/4-build-brief.md +171 -0
  35. package/skills/estack-leadership-coach/frameworks/delegation/phases/5-monitoring.md +134 -0
  36. package/skills/estack-leadership-coach/frameworks/delegation/phases/6-reverse-delegation.md +118 -0
  37. package/skills/estack-leadership-coach/frameworks/delegation/phases/7-diagnose.md +200 -0
  38. 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
  39. 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
  40. package/skills/estack-leadership-coach/references/.source-files/deci-ryan_self-determination-theory__selfdeterminationtheory-org-theory-overview-page.md +61 -0
  41. package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-3-key-insights-into-the-global-workplace-2024.md +57 -0
  42. 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
  43. package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-state-of-the-global-workplace-2026-global-data-summary.md +73 -0
  44. package/skills/estack-leadership-coach/references/.source-files/gallup_engagement-research__gallup-state-of-the-global-workplace-2026-report-landing.md +42 -0
  45. package/skills/estack-leadership-coach/references/.source-files/hormozi-leila_4-stages__leila-hormozi-the-art-of-delegation-blog-post.md +91 -0
  46. 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
  47. package/skills/estack-leadership-coach/references/.source-files/sanchez_main-street-millionaire__codie-sanchez-afford-anything-podcast-ep-565-show-notes.md +89 -0
  48. package/skills/estack-leadership-coach/references/.source-files/sullivan_who-not-how__dan-sullivan-impact-filter-tool-and-guide-booklet.md +565 -0
  49. 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
  50. package/skills/estack-leadership-coach/references/.source-files/van-edwards_cues__vanessa-van-edwards-roger-dooley-cues-interview.md +194 -0
  51. package/skills/estack-leadership-coach/references/deci-ryan_self-determination-theory.md +166 -0
  52. package/skills/estack-leadership-coach/references/doerr_measure-what-matters.md +154 -0
  53. package/skills/estack-leadership-coach/references/ferriss_4hww.md +189 -0
  54. package/skills/estack-leadership-coach/references/gallup_engagement-research.md +105 -0
  55. package/skills/estack-leadership-coach/references/gerber_e-myth-revisited.md +118 -0
  56. package/skills/estack-leadership-coach/references/grove_high-output-management.md +95 -0
  57. package/skills/estack-leadership-coach/references/hormozi-alex_followthrough.md +152 -0
  58. package/skills/estack-leadership-coach/references/hormozi-leila_4-stages.md +146 -0
  59. package/skills/estack-leadership-coach/references/oncken-wass_monkeys-hbr-1974.md +128 -0
  60. package/skills/estack-leadership-coach/references/sanchez_main-street-millionaire.md +196 -0
  61. package/skills/estack-leadership-coach/references/sullivan_who-not-how.md +137 -0
  62. package/skills/estack-leadership-coach/references/van-edwards_cues.md +189 -0
  63. package/skills/estack-migrate-claude-session-history/SKILL.md +226 -0
  64. package/skills/estack-migrate-claude-session-history/references/path-encoding.md +55 -0
  65. package/skills/estack-migrate-claude-session-history/references/troubleshooting.md +96 -0
  66. package/skills/estack-migrate-claude-session-history/scripts/migrate-claude-history.js +1123 -0
  67. package/skills/estack-migrate-claude-session-history/scripts/test-append-note.js +48 -0
  68. package/skills/estack-migrate-claude-session-history/scripts/test-validate-migration.py +326 -0
  69. package/skills/estack-migrate-claude-session-history/scripts/validate-migration.py +493 -0
  70. package/skills/estack-pdf-to-md/SKILL.md +180 -0
  71. package/skills/estack-pdf-to-md/scripts/pdf_to_md.py +596 -0
  72. package/skills/estack-productivity-prioritization-coach/SKILL.md +124 -0
  73. package/skills/estack-productivity-prioritization-coach/sources/01-tony-robbins-rpm.md +39 -0
  74. package/skills/estack-productivity-prioritization-coach/sources/02-justin-sung-task-prioritization.md +34 -0
  75. package/skills/estack-prompt-builder-coach/SKILL.md +81 -81
  76. package/skills/estack-prompt-builder-coach/definition-of-done-generator.md +42 -42
  77. package/skills/estack-prompt-builder-coach/prompt-builder.md +37 -37
  78. package/skills/estack-prompt-builder-coach/task-shaper.md +36 -36
  79. package/skills/estack-prompt-builder-coach/vague-ask-auditor.md +37 -37
  80. package/skills/estack-read-claude-session-history/SKILL.md +204 -204
  81. package/skills/estack-read-claude-session-history/references/jsonl-schema.md +126 -126
  82. package/skills/estack-read-claude-session-history/references/modes.md +423 -423
  83. package/skills/estack-read-claude-session-history/references/recipes.md +271 -271
  84. package/skills/estack-read-claude-session-history/scripts/lib/__init__.py +1 -1
  85. package/skills/estack-read-claude-session-history/scripts/lib/parser.py +460 -460
  86. package/skills/estack-read-claude-session-history/scripts/lib/paths.py +234 -234
  87. package/skills/estack-read-claude-session-history/scripts/lib/search.py +179 -179
  88. package/skills/estack-read-claude-session-history/scripts/lib/subagents.py +88 -88
  89. package/skills/estack-read-claude-session-history/scripts/lib/tools.py +144 -144
  90. package/skills/estack-read-claude-session-history/scripts/read_transcript.py +1776 -1776
  91. package/skills/estack-read-claude-session-history/scripts/tests/conftest.py +40 -40
  92. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/README.md +20 -20
  93. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/all-noise.jsonl +4 -4
  94. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/basic-session.jsonl +2 -2
  95. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-gaps.jsonl +9 -9
  96. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-noise.jsonl +7 -7
  97. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-parallel-a.jsonl +3 -3
  98. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-parallel-b.jsonl +3 -3
  99. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-waiting.jsonl +5 -5
  100. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/interrupted.jsonl +2 -2
  101. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/multi-compact.jsonl +8 -8
  102. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/pending-user.jsonl +2 -2
  103. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta/subagents/agent-aaa.jsonl +2 -2
  104. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta.jsonl +2 -2
  105. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.jsonl +2 -2
  106. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.meta.json +1 -1
  107. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent.jsonl +4 -4
  108. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/time-spread.jsonl +6 -6
  109. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/timeline-day-test.jsonl +5 -5
  110. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-zoo.jsonl +10 -10
  111. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/truncated.jsonl +2 -2
  112. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/unicode.jsonl +2 -2
  113. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-advisor.jsonl +3 -3
  114. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-compact.jsonl +5 -5
  115. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-thinking.jsonl +2 -2
  116. package/skills/estack-read-claude-session-history/scripts/tests/test_backup_roots.py +56 -56
  117. package/skills/estack-read-claude-session-history/scripts/tests/test_engagement.py +239 -239
  118. package/skills/estack-read-claude-session-history/scripts/tests/test_json_format.py +201 -201
  119. package/skills/estack-read-claude-session-history/scripts/tests/test_modes.py +199 -199
  120. package/skills/estack-read-claude-session-history/scripts/tests/test_parser.py +195 -195
  121. package/skills/estack-read-claude-session-history/scripts/tests/test_paths.py +133 -133
  122. package/skills/estack-read-claude-session-history/scripts/tests/test_search.py +78 -78
  123. package/skills/estack-read-claude-session-history/scripts/tests/test_subagents.py +43 -43
  124. package/skills/estack-read-claude-session-history/scripts/tests/test_timeline.py +179 -179
  125. package/skills/estack-read-claude-session-history/scripts/tests/test_timezone_and_project.py +212 -212
  126. package/skills/estack-read-claude-session-history/scripts/tests/test_tools.py +80 -80
  127. package/skills/estack-repo-search/SKILL.md +65 -65
  128. package/skills/estack-vscode-file-recovery/SKILL.md +188 -0
@@ -1,98 +1,98 @@
1
- # Shuttle Schedules — Template & Format Reference
2
-
3
- This skill optionally pairs flights with a ground shuttle service. Pairing is **off by default** — the skill only runs it if the user's config has a `shuttle_service` block set.
4
-
5
- This file describes:
6
- 1. How to configure your shuttle service in `~/.flight-planner/config.json`
7
- 2. The JSON format the pairing script (`scripts/pair_shuttles.py`) expects
8
- 3. How the skill builds that JSON from your shuttle company's website each run
9
-
10
- ## 1. Config block
11
-
12
- Add this to `~/.flight-planner/config.json` if you regularly use a shuttle:
13
-
14
- ```json
15
- "shuttle_service": {
16
- "name": "Acme Airport Express",
17
- "schedule_urls": [
18
- "https://acmeairport.example.com/schedules/ord",
19
- "https://acmeairport.example.com/schedules/mdw"
20
- ],
21
- "costs": {
22
- "ORD": 60,
23
- "MDW": 55
24
- },
25
- "home_timezone": "America/New_York",
26
- "airport_timezones": {
27
- "ORD": "America/Chicago",
28
- "MDW": "America/Chicago"
29
- }
30
- }
31
- ```
32
-
33
- **Fields:**
34
-
35
- - `name` — display name for the company
36
- - `schedule_urls` — one or more URLs the skill will fetch with WebFetch each run. Should be public schedule pages.
37
- - `costs` — one-way USD cost per destination airport (IATA code)
38
- - `home_timezone` — IANA timezone string for the city/town the shuttle picks you up from
39
- - `airport_timezones` — IANA timezone string per destination airport
40
-
41
- Set `shuttle_service: null` (or omit the field entirely) if you don't use a shuttle.
42
-
43
- ## 2. JSON the pairing script expects
44
-
45
- After fetching schedule URLs, the skill assembles this JSON and saves it as `shuttles.json`:
46
-
47
- ```json
48
- {
49
- "shuttles": [
50
- {
51
- "company": "Acme Airport Express",
52
- "from": "Home",
53
- "to": "ORD",
54
- "pickup_location": "Downtown station",
55
- "departs_local": "06:00",
56
- "arrives_local": "08:30"
57
- },
58
- {
59
- "company": "Acme Airport Express",
60
- "from": "Home",
61
- "to": "ORD",
62
- "pickup_location": "Downtown station",
63
- "departs_local": "10:00",
64
- "arrives_local": "12:30"
65
- }
66
- ]
67
- }
68
- ```
69
-
70
- **Field rules:**
71
-
72
- - `from` — descriptive label for the pickup region (not used for matching)
73
- - `to` — destination airport IATA code (used to match flights)
74
- - `departs_local` — pickup time in **home timezone**, HH:MM 24-hour
75
- - `arrives_local` — arrival time in **destination airport's local timezone**, HH:MM 24-hour
76
- - `pickup_location` — optional, shown in output
77
-
78
- The pairing script uses `--tz-offsets` to translate `arrives_local` into the home timezone for buffer math. The skill computes those offsets from `home_timezone` and `airport_timezones` in your config.
79
-
80
- ## 3. Extracting schedules from a website
81
-
82
- Most shuttle companies post fixed weekly schedules on their site. To turn a schedule page into the JSON above:
83
-
84
- 1. Use WebFetch on each URL in `schedule_urls`. Prompt: "extract every shuttle run with pickup time, destination airport, and arrival time."
85
- 2. For each run, build one object with `from`, `to`, `departs_local`, `arrives_local`.
86
- 3. Append all objects to a single `"shuttles"` array.
87
-
88
- If the company has multiple runs to the same airport on different days, treat each as a separate entry. If schedules change on weekends, fetch the relevant day's schedule when the user's flight date is a weekend.
89
-
90
- ## 4. Cost overrides for a single run
91
-
92
- If the user wants to override shuttle costs for one run only (e.g., a friend is driving them for free), the skill passes `--shuttle-costs "ORD:0"` instead of the config value. This doesn't modify the saved config.
93
-
94
- ## 5. Tightness tuning
95
-
96
- `--min-buffer-min` (default 90) sets the floor for "viable" — flights with less buffer than this between shuttle arrival and flight departure are marked TOO_TIGHT. Increase to 120 if your airport has long security lines, decrease to 60 if you trust your shuttle and have TSA PreCheck.
97
-
98
- `--max-wait-min` (default 240) caps how much pre-flight wait time is acceptable. Pairings beyond this get a LONG_WAIT label but still appear in the output.
1
+ # Shuttle Schedules — Template & Format Reference
2
+
3
+ This skill optionally pairs flights with a ground shuttle service. Pairing is **off by default** — the skill only runs it if the user's config has a `shuttle_service` block set.
4
+
5
+ This file describes:
6
+ 1. How to configure your shuttle service in `~/.flight-planner/config.json`
7
+ 2. The JSON format the pairing script (`scripts/pair_shuttles.py`) expects
8
+ 3. How the skill builds that JSON from your shuttle company's website each run
9
+
10
+ ## 1. Config block
11
+
12
+ Add this to `~/.flight-planner/config.json` if you regularly use a shuttle:
13
+
14
+ ```json
15
+ "shuttle_service": {
16
+ "name": "Acme Airport Express",
17
+ "schedule_urls": [
18
+ "https://acmeairport.example.com/schedules/ord",
19
+ "https://acmeairport.example.com/schedules/mdw"
20
+ ],
21
+ "costs": {
22
+ "ORD": 60,
23
+ "MDW": 55
24
+ },
25
+ "home_timezone": "America/New_York",
26
+ "airport_timezones": {
27
+ "ORD": "America/Chicago",
28
+ "MDW": "America/Chicago"
29
+ }
30
+ }
31
+ ```
32
+
33
+ **Fields:**
34
+
35
+ - `name` — display name for the company
36
+ - `schedule_urls` — one or more URLs the skill will fetch with WebFetch each run. Should be public schedule pages.
37
+ - `costs` — one-way USD cost per destination airport (IATA code)
38
+ - `home_timezone` — IANA timezone string for the city/town the shuttle picks you up from
39
+ - `airport_timezones` — IANA timezone string per destination airport
40
+
41
+ Set `shuttle_service: null` (or omit the field entirely) if you don't use a shuttle.
42
+
43
+ ## 2. JSON the pairing script expects
44
+
45
+ After fetching schedule URLs, the skill assembles this JSON and saves it as `shuttles.json`:
46
+
47
+ ```json
48
+ {
49
+ "shuttles": [
50
+ {
51
+ "company": "Acme Airport Express",
52
+ "from": "Home",
53
+ "to": "ORD",
54
+ "pickup_location": "Downtown station",
55
+ "departs_local": "06:00",
56
+ "arrives_local": "08:30"
57
+ },
58
+ {
59
+ "company": "Acme Airport Express",
60
+ "from": "Home",
61
+ "to": "ORD",
62
+ "pickup_location": "Downtown station",
63
+ "departs_local": "10:00",
64
+ "arrives_local": "12:30"
65
+ }
66
+ ]
67
+ }
68
+ ```
69
+
70
+ **Field rules:**
71
+
72
+ - `from` — descriptive label for the pickup region (not used for matching)
73
+ - `to` — destination airport IATA code (used to match flights)
74
+ - `departs_local` — pickup time in **home timezone**, HH:MM 24-hour
75
+ - `arrives_local` — arrival time in **destination airport's local timezone**, HH:MM 24-hour
76
+ - `pickup_location` — optional, shown in output
77
+
78
+ The pairing script uses `--tz-offsets` to translate `arrives_local` into the home timezone for buffer math. The skill computes those offsets from `home_timezone` and `airport_timezones` in your config.
79
+
80
+ ## 3. Extracting schedules from a website
81
+
82
+ Most shuttle companies post fixed weekly schedules on their site. To turn a schedule page into the JSON above:
83
+
84
+ 1. Use WebFetch on each URL in `schedule_urls`. Prompt: "extract every shuttle run with pickup time, destination airport, and arrival time."
85
+ 2. For each run, build one object with `from`, `to`, `departs_local`, `arrives_local`.
86
+ 3. Append all objects to a single `"shuttles"` array.
87
+
88
+ If the company has multiple runs to the same airport on different days, treat each as a separate entry. If schedules change on weekends, fetch the relevant day's schedule when the user's flight date is a weekend.
89
+
90
+ ## 4. Cost overrides for a single run
91
+
92
+ If the user wants to override shuttle costs for one run only (e.g., a friend is driving them for free), the skill passes `--shuttle-costs "ORD:0"` instead of the config value. This doesn't modify the saved config.
93
+
94
+ ## 5. Tightness tuning
95
+
96
+ `--min-buffer-min` (default 90) sets the floor for "viable" — flights with less buffer than this between shuttle arrival and flight departure are marked TOO_TIGHT. Increase to 120 if your airport has long security lines, decrease to 60 if you trust your shuttle and have TSA PreCheck.
97
+
98
+ `--max-wait-min` (default 240) caps how much pre-flight wait time is acceptable. Pairings beyond this get a LONG_WAIT label but still appear in the output.
@@ -1,89 +1,89 @@
1
- #!/usr/bin/env bash
2
- # Deterministic setup check. Run at skill load time via the ```! fence in SKILL.md.
3
- # Outputs a human-readable report of:
4
- # - whether ~/.flight-planner/config.json exists and its contents (key masked)
5
- # - whether ~/.flight-planner/flight_history.json exists and its entry count
6
- # - today's date (for resolving relative dates in Phase 1)
7
- # - whether SERPAPI_KEY env var is set
8
- # No side effects. Safe to run on every skill invocation.
9
-
10
- set -u
11
-
12
- CONFIG_DIR="$HOME/.flight-planner"
13
- CONFIG_FILE="$CONFIG_DIR/config.json"
14
- HISTORY_FILE="$CONFIG_DIR/flight_history.json"
15
-
16
- echo "=== Flight Planner Setup ==="
17
- echo "Today: $(date +%Y-%m-%d) (timezone: $(date +%Z))"
18
- echo ""
19
-
20
- # --- Config ---
21
- if [ -f "$CONFIG_FILE" ]; then
22
- echo "Config: $CONFIG_FILE (exists)"
23
- echo ""
24
- echo "--- Saved preferences ---"
25
- # Mask serpapi_key value: show as "set" or "null" but never the actual key
26
- python -c "
27
- import json, sys
28
- try:
29
- with open(r'''$CONFIG_FILE''', encoding='utf-8') as f:
30
- c = json.load(f)
31
- except Exception as e:
32
- print(f' ERROR reading config: {e}')
33
- sys.exit(0)
34
-
35
- key = c.get('serpapi_key')
36
- print(' SerpAPI key: ' + ('set' if key else 'null (will use WebSearch fallback)'))
37
- print(' Budget: \$' + str(c.get('budget_usd', '?')) + ' (' + str(c.get('budget_strength', '?')) + ')')
38
- airlines = c.get('airline_preferences') or []
39
- print(' Airlines: ' + (', '.join(airlines) if airlines else 'any') + ' (' + str(c.get('airline_preference_strength', '?')) + ')')
40
- np = c.get('nonstop_preference', '?')
41
- ns = c.get('nonstop_strength', '?')
42
- print(' Nonstop: ' + str(np) + ' (' + str(ns) + ')')
43
- bands = c.get('time_priority_bands') or []
44
- print(' Time priority: ' + (', '.join(bands) if bands else 'none') + ' (' + str(c.get('time_priority_strength', '?')) + ')')
45
- print(' Home airport: ' + str(c.get('home_airport') or 'not set'))
46
- freq = c.get('frequent_destinations') or []
47
- print(' Frequent destinations: ' + (', '.join(freq) if freq else 'not set'))
48
- shuttle = c.get('shuttle_service')
49
- if shuttle:
50
- name = shuttle.get('name', '?')
51
- costs = shuttle.get('costs', {}) or {}
52
- cost_str = ', '.join(f'{k}: \${v}' for k, v in costs.items()) if costs else 'no costs configured'
53
- print(' Shuttle service: ' + str(name) + ' (' + cost_str + ')')
54
- else:
55
- print(' Shuttle service: none (pairing step will be skipped)')
56
- " 2>&1 || echo " (python not available; cannot parse config — read the file directly)"
57
- else
58
- echo "Config: $CONFIG_FILE (NOT FOUND)"
59
- echo ""
60
- echo "First-run setup is needed. The skill will walk the user through Phase 2"
61
- echo "to create this file."
62
- fi
63
-
64
- echo ""
65
-
66
- # --- Environment SERPAPI_KEY ---
67
- if [ -n "${SERPAPI_KEY:-}" ]; then
68
- echo "SERPAPI_KEY: set in environment (will be used if config key is null)"
69
- else
70
- echo "SERPAPI_KEY: not set in environment"
71
- fi
72
-
73
- echo ""
74
-
75
- # --- Flight history ---
76
- if [ -f "$HISTORY_FILE" ]; then
77
- count=$(python -c "
78
- import json
79
- try:
80
- with open(r'''$HISTORY_FILE''', encoding='utf-8') as f:
81
- data = json.load(f)
82
- print(len(data) if isinstance(data, list) else 0)
83
- except Exception:
84
- print(0)
85
- " 2>/dev/null || echo "?")
86
- echo "History: $HISTORY_FILE ($count entries)"
87
- else
88
- echo "History: $HISTORY_FILE (not created yet — first search will create it)"
89
- fi
1
+ #!/usr/bin/env bash
2
+ # Deterministic setup check. Run at skill load time via the ```! fence in SKILL.md.
3
+ # Outputs a human-readable report of:
4
+ # - whether ~/.flight-planner/config.json exists and its contents (key masked)
5
+ # - whether ~/.flight-planner/flight_history.json exists and its entry count
6
+ # - today's date (for resolving relative dates in Phase 1)
7
+ # - whether SERPAPI_KEY env var is set
8
+ # No side effects. Safe to run on every skill invocation.
9
+
10
+ set -u
11
+
12
+ CONFIG_DIR="$HOME/.flight-planner"
13
+ CONFIG_FILE="$CONFIG_DIR/config.json"
14
+ HISTORY_FILE="$CONFIG_DIR/flight_history.json"
15
+
16
+ echo "=== Flight Planner Setup ==="
17
+ echo "Today: $(date +%Y-%m-%d) (timezone: $(date +%Z))"
18
+ echo ""
19
+
20
+ # --- Config ---
21
+ if [ -f "$CONFIG_FILE" ]; then
22
+ echo "Config: $CONFIG_FILE (exists)"
23
+ echo ""
24
+ echo "--- Saved preferences ---"
25
+ # Mask serpapi_key value: show as "set" or "null" but never the actual key
26
+ python -c "
27
+ import json, sys
28
+ try:
29
+ with open(r'''$CONFIG_FILE''', encoding='utf-8') as f:
30
+ c = json.load(f)
31
+ except Exception as e:
32
+ print(f' ERROR reading config: {e}')
33
+ sys.exit(0)
34
+
35
+ key = c.get('serpapi_key')
36
+ print(' SerpAPI key: ' + ('set' if key else 'null (will use WebSearch fallback)'))
37
+ print(' Budget: \$' + str(c.get('budget_usd', '?')) + ' (' + str(c.get('budget_strength', '?')) + ')')
38
+ airlines = c.get('airline_preferences') or []
39
+ print(' Airlines: ' + (', '.join(airlines) if airlines else 'any') + ' (' + str(c.get('airline_preference_strength', '?')) + ')')
40
+ np = c.get('nonstop_preference', '?')
41
+ ns = c.get('nonstop_strength', '?')
42
+ print(' Nonstop: ' + str(np) + ' (' + str(ns) + ')')
43
+ bands = c.get('time_priority_bands') or []
44
+ print(' Time priority: ' + (', '.join(bands) if bands else 'none') + ' (' + str(c.get('time_priority_strength', '?')) + ')')
45
+ print(' Home airport: ' + str(c.get('home_airport') or 'not set'))
46
+ freq = c.get('frequent_destinations') or []
47
+ print(' Frequent destinations: ' + (', '.join(freq) if freq else 'not set'))
48
+ shuttle = c.get('shuttle_service')
49
+ if shuttle:
50
+ name = shuttle.get('name', '?')
51
+ costs = shuttle.get('costs', {}) or {}
52
+ cost_str = ', '.join(f'{k}: \${v}' for k, v in costs.items()) if costs else 'no costs configured'
53
+ print(' Shuttle service: ' + str(name) + ' (' + cost_str + ')')
54
+ else:
55
+ print(' Shuttle service: none (pairing step will be skipped)')
56
+ " 2>&1 || echo " (python not available; cannot parse config — read the file directly)"
57
+ else
58
+ echo "Config: $CONFIG_FILE (NOT FOUND)"
59
+ echo ""
60
+ echo "First-run setup is needed. The skill will walk the user through Phase 2"
61
+ echo "to create this file."
62
+ fi
63
+
64
+ echo ""
65
+
66
+ # --- Environment SERPAPI_KEY ---
67
+ if [ -n "${SERPAPI_KEY:-}" ]; then
68
+ echo "SERPAPI_KEY: set in environment (will be used if config key is null)"
69
+ else
70
+ echo "SERPAPI_KEY: not set in environment"
71
+ fi
72
+
73
+ echo ""
74
+
75
+ # --- Flight history ---
76
+ if [ -f "$HISTORY_FILE" ]; then
77
+ count=$(python -c "
78
+ import json
79
+ try:
80
+ with open(r'''$HISTORY_FILE''', encoding='utf-8') as f:
81
+ data = json.load(f)
82
+ print(len(data) if isinstance(data, list) else 0)
83
+ except Exception:
84
+ print(0)
85
+ " 2>/dev/null || echo "?")
86
+ echo "History: $HISTORY_FILE ($count entries)"
87
+ else
88
+ echo "History: $HISTORY_FILE (not created yet — first search will create it)"
89
+ fi
@@ -1,99 +1,99 @@
1
- """
2
- Fetch flight data from SerpAPI Google Flights.
3
-
4
- Saves raw JSON responses to a platform-aware temp directory, one file per
5
- route x date combination. Prints the temp directory path on stdout so the
6
- caller can pipe it to filter_flights.py.
7
-
8
- Usage:
9
- python fetch_flights.py --dates 2026-05-09,2026-05-10 --routes IND-EWR,ORD-LGA
10
- python fetch_flights.py --dates 2026-05-09 --routes IND-EWR --airlines UA,DL --stops 1
11
- python fetch_flights.py --dates 2026-05-09 --routes IND-EWR --output-dir /tmp/x
12
-
13
- Args:
14
- --airlines Optional comma-separated IATA airline codes (e.g. UA,DL). Omit for any airline.
15
- --stops Optional. 0=any (default), 1=nonstop only, 2=one-stop-max.
16
-
17
- Auth:
18
- Reads SERPAPI_KEY from environment. Override with --api-key.
19
- """
20
- import argparse
21
- import os
22
- import sys
23
- import tempfile
24
- import urllib.parse
25
- import urllib.request
26
- from pathlib import Path
27
-
28
- ENDPOINT = "https://serpapi.com/search"
29
- BASE_PARAMS = {
30
- "engine": "google_flights",
31
- "type": "2", # one-way
32
- "sort_by": "2", # price
33
- "currency": "USD",
34
- }
35
-
36
-
37
- def default_output_dir() -> Path:
38
- base = Path(tempfile.gettempdir()) / "estack-flight-planner"
39
- base.mkdir(parents=True, exist_ok=True)
40
- return base
41
-
42
-
43
- def fetch_one(api_key: str, dep: str, arr: str, date: str, out_path: Path,
44
- airlines: str = None, stops: str = None) -> int:
45
- params = {**BASE_PARAMS, "departure_id": dep, "arrival_id": arr,
46
- "outbound_date": date, "api_key": api_key}
47
- if airlines:
48
- params["include_airlines"] = airlines
49
- if stops and stops != "0":
50
- params["stops"] = stops
51
- url = f"{ENDPOINT}?{urllib.parse.urlencode(params)}"
52
- with urllib.request.urlopen(url, timeout=60) as resp:
53
- data = resp.read()
54
- out_path.write_bytes(data)
55
- return len(data)
56
-
57
-
58
- def main() -> int:
59
- p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
60
- p.add_argument("--dates", required=True, help="Comma-separated YYYY-MM-DD")
61
- p.add_argument("--routes", required=True, help="Comma-separated DEP-ARR (e.g. IND-EWR,ORD-LGA)")
62
- p.add_argument("--airlines", default=None,
63
- help="Optional comma-separated IATA codes (e.g. UA,DL). Omit for any airline.")
64
- p.add_argument("--stops", default="0",
65
- help="0=any (default), 1=nonstop only, 2=one-stop-max")
66
- p.add_argument("--output-dir", default=None, help="Override temp dir")
67
- p.add_argument("--api-key", default=None, help="SerpAPI key (else uses $SERPAPI_KEY)")
68
- args = p.parse_args()
69
-
70
- api_key = args.api_key or os.environ.get("SERPAPI_KEY")
71
- if not api_key:
72
- print("ERROR: provide --api-key or set SERPAPI_KEY env var", file=sys.stderr)
73
- return 2
74
-
75
- out_dir = Path(args.output_dir) if args.output_dir else default_output_dir()
76
- out_dir.mkdir(parents=True, exist_ok=True)
77
-
78
- dates = [d.strip() for d in args.dates.split(",") if d.strip()]
79
- routes = [tuple(r.strip().split("-")) for r in args.routes.split(",") if r.strip()]
80
-
81
- total = 0
82
- for dep, arr in routes:
83
- for date in dates:
84
- fname = out_dir / f"{dep}_{arr}_{date}.json"
85
- try:
86
- size = fetch_one(api_key, dep, arr, date, fname,
87
- airlines=args.airlines, stops=args.stops)
88
- print(f" {dep}->{arr} {date}: {size} bytes", file=sys.stderr)
89
- total += 1
90
- except Exception as e:
91
- print(f" {dep}->{arr} {date}: FAILED ({e})", file=sys.stderr)
92
-
93
- print(f"Fetched {total} files to:", file=sys.stderr)
94
- print(out_dir) # stdout = the path, for piping
95
- return 0
96
-
97
-
98
- if __name__ == "__main__":
99
- sys.exit(main())
1
+ """
2
+ Fetch flight data from SerpAPI Google Flights.
3
+
4
+ Saves raw JSON responses to a platform-aware temp directory, one file per
5
+ route x date combination. Prints the temp directory path on stdout so the
6
+ caller can pipe it to filter_flights.py.
7
+
8
+ Usage:
9
+ python fetch_flights.py --dates 2026-05-09,2026-05-10 --routes IND-EWR,ORD-LGA
10
+ python fetch_flights.py --dates 2026-05-09 --routes IND-EWR --airlines UA,DL --stops 1
11
+ python fetch_flights.py --dates 2026-05-09 --routes IND-EWR --output-dir /tmp/x
12
+
13
+ Args:
14
+ --airlines Optional comma-separated IATA airline codes (e.g. UA,DL). Omit for any airline.
15
+ --stops Optional. 0=any (default), 1=nonstop only, 2=one-stop-max.
16
+
17
+ Auth:
18
+ Reads SERPAPI_KEY from environment. Override with --api-key.
19
+ """
20
+ import argparse
21
+ import os
22
+ import sys
23
+ import tempfile
24
+ import urllib.parse
25
+ import urllib.request
26
+ from pathlib import Path
27
+
28
+ ENDPOINT = "https://serpapi.com/search"
29
+ BASE_PARAMS = {
30
+ "engine": "google_flights",
31
+ "type": "2", # one-way
32
+ "sort_by": "2", # price
33
+ "currency": "USD",
34
+ }
35
+
36
+
37
+ def default_output_dir() -> Path:
38
+ base = Path(tempfile.gettempdir()) / "estack-flight-planner"
39
+ base.mkdir(parents=True, exist_ok=True)
40
+ return base
41
+
42
+
43
+ def fetch_one(api_key: str, dep: str, arr: str, date: str, out_path: Path,
44
+ airlines: str = None, stops: str = None) -> int:
45
+ params = {**BASE_PARAMS, "departure_id": dep, "arrival_id": arr,
46
+ "outbound_date": date, "api_key": api_key}
47
+ if airlines:
48
+ params["include_airlines"] = airlines
49
+ if stops and stops != "0":
50
+ params["stops"] = stops
51
+ url = f"{ENDPOINT}?{urllib.parse.urlencode(params)}"
52
+ with urllib.request.urlopen(url, timeout=60) as resp:
53
+ data = resp.read()
54
+ out_path.write_bytes(data)
55
+ return len(data)
56
+
57
+
58
+ def main() -> int:
59
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
60
+ p.add_argument("--dates", required=True, help="Comma-separated YYYY-MM-DD")
61
+ p.add_argument("--routes", required=True, help="Comma-separated DEP-ARR (e.g. IND-EWR,ORD-LGA)")
62
+ p.add_argument("--airlines", default=None,
63
+ help="Optional comma-separated IATA codes (e.g. UA,DL). Omit for any airline.")
64
+ p.add_argument("--stops", default="0",
65
+ help="0=any (default), 1=nonstop only, 2=one-stop-max")
66
+ p.add_argument("--output-dir", default=None, help="Override temp dir")
67
+ p.add_argument("--api-key", default=None, help="SerpAPI key (else uses $SERPAPI_KEY)")
68
+ args = p.parse_args()
69
+
70
+ api_key = args.api_key or os.environ.get("SERPAPI_KEY")
71
+ if not api_key:
72
+ print("ERROR: provide --api-key or set SERPAPI_KEY env var", file=sys.stderr)
73
+ return 2
74
+
75
+ out_dir = Path(args.output_dir) if args.output_dir else default_output_dir()
76
+ out_dir.mkdir(parents=True, exist_ok=True)
77
+
78
+ dates = [d.strip() for d in args.dates.split(",") if d.strip()]
79
+ routes = [tuple(r.strip().split("-")) for r in args.routes.split(",") if r.strip()]
80
+
81
+ total = 0
82
+ for dep, arr in routes:
83
+ for date in dates:
84
+ fname = out_dir / f"{dep}_{arr}_{date}.json"
85
+ try:
86
+ size = fetch_one(api_key, dep, arr, date, fname,
87
+ airlines=args.airlines, stops=args.stops)
88
+ print(f" {dep}->{arr} {date}: {size} bytes", file=sys.stderr)
89
+ total += 1
90
+ except Exception as e:
91
+ print(f" {dep}->{arr} {date}: FAILED ({e})", file=sys.stderr)
92
+
93
+ print(f"Fetched {total} files to:", file=sys.stderr)
94
+ print(out_dir) # stdout = the path, for piping
95
+ return 0
96
+
97
+
98
+ if __name__ == "__main__":
99
+ sys.exit(main())