elliot-stack 1.0.36 → 1.0.38

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 (83) hide show
  1. package/LICENSE +21 -21
  2. package/bin/install.cjs +981 -981
  3. package/hooks/repo-search-nudge.js +32 -32
  4. package/package.json +1 -1
  5. package/skills/estack-active-learning-tutor/SKILL.md +339 -339
  6. package/skills/estack-better-title/SKILL.md +64 -64
  7. package/skills/estack-better-title/scripts/rename.sh +55 -55
  8. package/skills/estack-chris-voss/SKILL.md +80 -80
  9. package/skills/estack-chris-voss/references/elliot-notes.md +120 -120
  10. package/skills/estack-chris-voss/references/voss-principles.md +210 -210
  11. package/skills/estack-customer-discovery/SKILL.md +60 -60
  12. package/skills/estack-flight-planner/SKILL.md +332 -332
  13. package/skills/estack-flight-planner/references/config_schema.md +156 -156
  14. package/skills/estack-flight-planner/references/flight_history_schema.md +97 -97
  15. package/skills/estack-flight-planner/references/shuttle_schedules.md +98 -98
  16. package/skills/estack-flight-planner/scripts/check_setup.sh +89 -89
  17. package/skills/estack-flight-planner/scripts/fetch_flights.py +99 -99
  18. package/skills/estack-flight-planner/scripts/filter_flights.py +265 -265
  19. package/skills/estack-flight-planner/scripts/pair_shuttles.py +173 -173
  20. package/skills/estack-github-issue-tracker/SKILL.md +322 -322
  21. package/skills/estack-github-issue-tracker/bin/tracker-tools.cjs +1358 -1358
  22. package/skills/estack-github-issue-tracker/references/gh-cli-patterns.md +124 -124
  23. package/skills/estack-github-issue-tracker/references/result-file-schema.md +156 -156
  24. package/skills/estack-github-issue-tracker/references/tracker-schema.md +96 -96
  25. package/skills/estack-github-issue-tracker/tracker-template.md +58 -58
  26. package/skills/estack-leadership-coach/SKILL.md +1 -1
  27. package/skills/estack-leadership-coach/adding-references.md +1 -1
  28. package/skills/estack-migrate-claude-session-history/SKILL.md +15 -2
  29. package/skills/estack-pdf-to-md/SKILL.md +1 -2
  30. package/skills/estack-prompt-builder-coach/SKILL.md +81 -81
  31. package/skills/estack-prompt-builder-coach/definition-of-done-generator.md +42 -42
  32. package/skills/estack-prompt-builder-coach/prompt-builder.md +37 -37
  33. package/skills/estack-prompt-builder-coach/task-shaper.md +36 -36
  34. package/skills/estack-prompt-builder-coach/vague-ask-auditor.md +37 -37
  35. package/skills/estack-read-claude-session-history/SKILL.md +228 -204
  36. package/skills/estack-read-claude-session-history/references/jsonl-schema.md +126 -126
  37. package/skills/estack-read-claude-session-history/references/modes.md +455 -423
  38. package/skills/estack-read-claude-session-history/references/recipes.md +300 -271
  39. package/skills/estack-read-claude-session-history/scripts/lib/__init__.py +1 -1
  40. package/skills/estack-read-claude-session-history/scripts/lib/parser.py +460 -460
  41. package/skills/estack-read-claude-session-history/scripts/lib/paths.py +234 -234
  42. package/skills/estack-read-claude-session-history/scripts/lib/search.py +179 -179
  43. package/skills/estack-read-claude-session-history/scripts/lib/subagents.py +88 -88
  44. package/skills/estack-read-claude-session-history/scripts/lib/tools.py +144 -144
  45. package/skills/estack-read-claude-session-history/scripts/read_transcript.py +1914 -1776
  46. package/skills/estack-read-claude-session-history/scripts/tests/conftest.py +40 -40
  47. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/README.md +20 -20
  48. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/all-noise.jsonl +4 -4
  49. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/basic-session.jsonl +2 -2
  50. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-gaps.jsonl +9 -9
  51. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-noise.jsonl +7 -7
  52. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-parallel-a.jsonl +3 -3
  53. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-parallel-b.jsonl +3 -3
  54. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/engagement-waiting.jsonl +5 -5
  55. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/interrupted.jsonl +2 -2
  56. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/multi-compact.jsonl +8 -8
  57. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/pending-user.jsonl +2 -2
  58. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta/subagents/agent-aaa.jsonl +2 -2
  59. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta.jsonl +2 -2
  60. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.jsonl +2 -2
  61. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.meta.json +1 -1
  62. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent.jsonl +4 -4
  63. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/time-spread.jsonl +6 -6
  64. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/timeline-day-test.jsonl +5 -5
  65. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-usage-parent/subagents/agent-sub1.jsonl +3 -0
  66. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-usage-parent.jsonl +3 -0
  67. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-zoo.jsonl +10 -10
  68. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/truncated.jsonl +2 -2
  69. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/unicode.jsonl +2 -2
  70. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-advisor.jsonl +3 -3
  71. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-compact.jsonl +5 -5
  72. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-thinking.jsonl +2 -2
  73. package/skills/estack-read-claude-session-history/scripts/tests/test_backup_roots.py +56 -56
  74. package/skills/estack-read-claude-session-history/scripts/tests/test_engagement.py +239 -239
  75. package/skills/estack-read-claude-session-history/scripts/tests/test_json_format.py +201 -201
  76. package/skills/estack-read-claude-session-history/scripts/tests/test_modes.py +323 -199
  77. package/skills/estack-read-claude-session-history/scripts/tests/test_parser.py +195 -195
  78. package/skills/estack-read-claude-session-history/scripts/tests/test_paths.py +133 -133
  79. package/skills/estack-read-claude-session-history/scripts/tests/test_search.py +78 -78
  80. package/skills/estack-read-claude-session-history/scripts/tests/test_subagents.py +43 -43
  81. package/skills/estack-read-claude-session-history/scripts/tests/test_timeline.py +179 -179
  82. package/skills/estack-read-claude-session-history/scripts/tests/test_timezone_and_project.py +212 -212
  83. package/skills/estack-read-claude-session-history/scripts/tests/test_tools.py +80 -80
@@ -1,156 +1,156 @@
1
- # Config Schema — `~/.flight-planner/config.json`
2
-
3
- The skill stores user preferences in this file. It's outside `~/.claude/skills/` so the e-stack installer never overwrites it.
4
-
5
- ## Full schema
6
-
7
- ```json
8
- {
9
- "serpapi_key": "abc123 or null",
10
-
11
- "budget_usd": 200,
12
- "budget_strength": "soft",
13
-
14
- "airline_preferences": ["UA", "DL"],
15
- "airline_preference_strength": "soft",
16
-
17
- "nonstop_preference": "preferred",
18
- "nonstop_strength": "soft",
19
-
20
- "time_priority_bands": ["11:00-14:00", "14:00-22:00"],
21
- "time_priority_strength": "soft",
22
-
23
- "home_airport": null,
24
- "frequent_destinations": [],
25
-
26
- "shuttle_service": null
27
- }
28
- ```
29
-
30
- ## Field reference
31
-
32
- ### `serpapi_key`
33
- - Type: string or null
34
- - Your SerpAPI key for Google Flights queries. If null, the skill falls back to WebSearch (less comprehensive — see SKILL.md).
35
- - Get one at https://serpapi.com/manage-api-key.
36
-
37
- ### `budget_usd`
38
- - Type: int
39
- - Max flight price in USD.
40
-
41
- ### `budget_strength`
42
- - Type: `"hard"` or `"soft"`
43
- - `hard` = filter out flights above budget. `soft` = include them but rank cheaper ones higher.
44
-
45
- ### `airline_preferences`
46
- - Type: array of IATA airline codes (e.g., `["UA", "DL"]`)
47
- - Empty array = any airline.
48
-
49
- ### `airline_preference_strength`
50
- - Type: `"hard"` or `"soft"`
51
- - `hard` = only show flights on these airlines. `soft` = prefer them but show others too.
52
-
53
- ### `nonstop_preference`
54
- - Type: `"required"`, `"preferred"`, or `"no_preference"`
55
-
56
- ### `nonstop_strength`
57
- - Type: `"hard"` or `"soft"`
58
- - Combined with `nonstop_preference`:
59
- - `required` + `hard` = filter out anything with stops
60
- - `required` + `soft` = treat stopovers as a downside but still show them
61
- - `preferred` + `soft` = rank nonstops first, show stops second (typical setup)
62
- - `no_preference` = strength is ignored
63
-
64
- ### `time_priority_bands`
65
- - Type: array of `"HH:MM-HH:MM"` ranges in 24-hour time
66
- - Departures inside the first band rank highest; second band second; outside all bands rank lowest.
67
-
68
- ### `time_priority_strength`
69
- - Type: `"hard"` or `"soft"`
70
- - `hard` = filter out flights outside all bands. `soft` = include but rank lower.
71
-
72
- ### `home_airport`
73
- - Type: IATA code or null
74
- - Optional. If set, the skill suggests it when asking for origin in Phase 1 (but still asks).
75
-
76
- ### `frequent_destinations`
77
- - Type: array of IATA codes
78
- - Optional. Surfaced as suggestions when asking for destination.
79
-
80
- ### `shuttle_service`
81
- - Type: object or null
82
- - If non-null, the skill pairs flights with ground shuttle runs. See `references/shuttle_schedules.md` for the full sub-schema.
83
-
84
- ## Example configs
85
-
86
- ### Frequent flier, one home airport, picky about times
87
-
88
- ```json
89
- {
90
- "serpapi_key": "sk_xxx",
91
- "budget_usd": 350,
92
- "budget_strength": "soft",
93
- "airline_preferences": ["DL"],
94
- "airline_preference_strength": "hard",
95
- "nonstop_preference": "required",
96
- "nonstop_strength": "hard",
97
- "time_priority_bands": ["07:00-10:00", "17:00-20:00"],
98
- "time_priority_strength": "soft",
99
- "home_airport": "JFK",
100
- "frequent_destinations": ["LAX", "SFO", "SEA"],
101
- "shuttle_service": null
102
- }
103
- ```
104
-
105
- ### Casual traveler, cost-sensitive, flexible
106
-
107
- ```json
108
- {
109
- "serpapi_key": null,
110
- "budget_usd": 250,
111
- "budget_strength": "hard",
112
- "airline_preferences": [],
113
- "airline_preference_strength": "soft",
114
- "nonstop_preference": "preferred",
115
- "nonstop_strength": "soft",
116
- "time_priority_bands": [],
117
- "time_priority_strength": "soft",
118
- "home_airport": null,
119
- "frequent_destinations": [],
120
- "shuttle_service": null
121
- }
122
- ```
123
-
124
- ### Suburban user with a shuttle service
125
-
126
- ```json
127
- {
128
- "serpapi_key": "sk_xxx",
129
- "budget_usd": 200,
130
- "budget_strength": "soft",
131
- "airline_preferences": ["UA"],
132
- "airline_preference_strength": "soft",
133
- "nonstop_preference": "preferred",
134
- "nonstop_strength": "soft",
135
- "time_priority_bands": ["11:00-14:00", "14:00-22:00"],
136
- "time_priority_strength": "soft",
137
- "home_airport": null,
138
- "frequent_destinations": ["EWR", "LGA"],
139
- "shuttle_service": {
140
- "name": "Acme Airport Express",
141
- "schedule_urls": ["https://acme.example.com/schedule"],
142
- "costs": {"ORD": 60, "IND": 30},
143
- "home_timezone": "America/New_York",
144
- "airport_timezones": {"ORD": "America/Chicago", "IND": "America/New_York"}
145
- }
146
- }
147
- ```
148
-
149
- ## Strength semantics recap
150
-
151
- | Strength | Behavior |
152
- |---|---|
153
- | `hard` | Filter applied strictly — non-matching flights are excluded from output |
154
- | `soft` | Filter applied as a rank weight — non-matching flights still shown, flagged with `soft_filter_violations` and sorted below matching ones |
155
-
156
- When hard filters return zero results, the skill runs `filter_flights.py --cluster-analysis` to see which constraint(s) eliminated which flight counts, then proposes specific relaxations.
1
+ # Config Schema — `~/.flight-planner/config.json`
2
+
3
+ The skill stores user preferences in this file. It's outside `~/.claude/skills/` so the e-stack installer never overwrites it.
4
+
5
+ ## Full schema
6
+
7
+ ```json
8
+ {
9
+ "serpapi_key": "abc123 or null",
10
+
11
+ "budget_usd": 200,
12
+ "budget_strength": "soft",
13
+
14
+ "airline_preferences": ["UA", "DL"],
15
+ "airline_preference_strength": "soft",
16
+
17
+ "nonstop_preference": "preferred",
18
+ "nonstop_strength": "soft",
19
+
20
+ "time_priority_bands": ["11:00-14:00", "14:00-22:00"],
21
+ "time_priority_strength": "soft",
22
+
23
+ "home_airport": null,
24
+ "frequent_destinations": [],
25
+
26
+ "shuttle_service": null
27
+ }
28
+ ```
29
+
30
+ ## Field reference
31
+
32
+ ### `serpapi_key`
33
+ - Type: string or null
34
+ - Your SerpAPI key for Google Flights queries. If null, the skill falls back to WebSearch (less comprehensive — see SKILL.md).
35
+ - Get one at https://serpapi.com/manage-api-key.
36
+
37
+ ### `budget_usd`
38
+ - Type: int
39
+ - Max flight price in USD.
40
+
41
+ ### `budget_strength`
42
+ - Type: `"hard"` or `"soft"`
43
+ - `hard` = filter out flights above budget. `soft` = include them but rank cheaper ones higher.
44
+
45
+ ### `airline_preferences`
46
+ - Type: array of IATA airline codes (e.g., `["UA", "DL"]`)
47
+ - Empty array = any airline.
48
+
49
+ ### `airline_preference_strength`
50
+ - Type: `"hard"` or `"soft"`
51
+ - `hard` = only show flights on these airlines. `soft` = prefer them but show others too.
52
+
53
+ ### `nonstop_preference`
54
+ - Type: `"required"`, `"preferred"`, or `"no_preference"`
55
+
56
+ ### `nonstop_strength`
57
+ - Type: `"hard"` or `"soft"`
58
+ - Combined with `nonstop_preference`:
59
+ - `required` + `hard` = filter out anything with stops
60
+ - `required` + `soft` = treat stopovers as a downside but still show them
61
+ - `preferred` + `soft` = rank nonstops first, show stops second (typical setup)
62
+ - `no_preference` = strength is ignored
63
+
64
+ ### `time_priority_bands`
65
+ - Type: array of `"HH:MM-HH:MM"` ranges in 24-hour time
66
+ - Departures inside the first band rank highest; second band second; outside all bands rank lowest.
67
+
68
+ ### `time_priority_strength`
69
+ - Type: `"hard"` or `"soft"`
70
+ - `hard` = filter out flights outside all bands. `soft` = include but rank lower.
71
+
72
+ ### `home_airport`
73
+ - Type: IATA code or null
74
+ - Optional. If set, the skill suggests it when asking for origin in Phase 1 (but still asks).
75
+
76
+ ### `frequent_destinations`
77
+ - Type: array of IATA codes
78
+ - Optional. Surfaced as suggestions when asking for destination.
79
+
80
+ ### `shuttle_service`
81
+ - Type: object or null
82
+ - If non-null, the skill pairs flights with ground shuttle runs. See `references/shuttle_schedules.md` for the full sub-schema.
83
+
84
+ ## Example configs
85
+
86
+ ### Frequent flier, one home airport, picky about times
87
+
88
+ ```json
89
+ {
90
+ "serpapi_key": "sk_xxx",
91
+ "budget_usd": 350,
92
+ "budget_strength": "soft",
93
+ "airline_preferences": ["DL"],
94
+ "airline_preference_strength": "hard",
95
+ "nonstop_preference": "required",
96
+ "nonstop_strength": "hard",
97
+ "time_priority_bands": ["07:00-10:00", "17:00-20:00"],
98
+ "time_priority_strength": "soft",
99
+ "home_airport": "JFK",
100
+ "frequent_destinations": ["LAX", "SFO", "SEA"],
101
+ "shuttle_service": null
102
+ }
103
+ ```
104
+
105
+ ### Casual traveler, cost-sensitive, flexible
106
+
107
+ ```json
108
+ {
109
+ "serpapi_key": null,
110
+ "budget_usd": 250,
111
+ "budget_strength": "hard",
112
+ "airline_preferences": [],
113
+ "airline_preference_strength": "soft",
114
+ "nonstop_preference": "preferred",
115
+ "nonstop_strength": "soft",
116
+ "time_priority_bands": [],
117
+ "time_priority_strength": "soft",
118
+ "home_airport": null,
119
+ "frequent_destinations": [],
120
+ "shuttle_service": null
121
+ }
122
+ ```
123
+
124
+ ### Suburban user with a shuttle service
125
+
126
+ ```json
127
+ {
128
+ "serpapi_key": "sk_xxx",
129
+ "budget_usd": 200,
130
+ "budget_strength": "soft",
131
+ "airline_preferences": ["UA"],
132
+ "airline_preference_strength": "soft",
133
+ "nonstop_preference": "preferred",
134
+ "nonstop_strength": "soft",
135
+ "time_priority_bands": ["11:00-14:00", "14:00-22:00"],
136
+ "time_priority_strength": "soft",
137
+ "home_airport": null,
138
+ "frequent_destinations": ["EWR", "LGA"],
139
+ "shuttle_service": {
140
+ "name": "Acme Airport Express",
141
+ "schedule_urls": ["https://acme.example.com/schedule"],
142
+ "costs": {"ORD": 60, "IND": 30},
143
+ "home_timezone": "America/New_York",
144
+ "airport_timezones": {"ORD": "America/Chicago", "IND": "America/New_York"}
145
+ }
146
+ }
147
+ ```
148
+
149
+ ## Strength semantics recap
150
+
151
+ | Strength | Behavior |
152
+ |---|---|
153
+ | `hard` | Filter applied strictly — non-matching flights are excluded from output |
154
+ | `soft` | Filter applied as a rank weight — non-matching flights still shown, flagged with `soft_filter_violations` and sorted below matching ones |
155
+
156
+ When hard filters return zero results, the skill runs `filter_flights.py --cluster-analysis` to see which constraint(s) eliminated which flight counts, then proposes specific relaxations.
@@ -1,97 +1,97 @@
1
- # Flight History Schema — `~/.flight-planner/flight_history.json`
2
-
3
- Append-only log of every flight search. Used as passive context so the skill can recognize patterns over time ("you've been booking the ~$180 range lately").
4
-
5
- ## File format
6
-
7
- A single JSON array of entry objects. The file is created the first time the skill writes to it; until then the file does not exist.
8
-
9
- ```json
10
- [
11
- { entry 1 },
12
- { entry 2 },
13
- ...
14
- ]
15
- ```
16
-
17
- To append safely: read the array, push the new entry, write the file back. If the file is missing or corrupt, start a fresh array.
18
-
19
- ## Entry types
20
-
21
- There are two entry types. Each search produces one of each (under normal flow).
22
-
23
- ### `search_started`
24
-
25
- Written immediately after the user confirms Phase 2 preferences and before `fetch_flights.py` runs. Captures what they were looking for, even if they abandon the search before booking.
26
-
27
- ```json
28
- {
29
- "type": "search_started",
30
- "id": "2026-05-11T14:23:07-04:00-3f9a",
31
- "timestamp": "2026-05-11T14:23:07-04:00",
32
- "dates": ["2026-05-15", "2026-05-16"],
33
- "origins": ["IND", "ORD"],
34
- "destinations": ["EWR", "LGA"],
35
- "config_snapshot": {
36
- "budget_usd": 200,
37
- "budget_strength": "soft",
38
- "airline_preferences": ["UA", "DL"],
39
- "airline_preference_strength": "soft",
40
- "nonstop_preference": "preferred",
41
- "nonstop_strength": "soft",
42
- "time_priority_bands": ["11:00-14:00", "14:00-22:00"],
43
- "time_priority_strength": "soft",
44
- "had_shuttle": false
45
- }
46
- }
47
- ```
48
-
49
- **Fields:**
50
- - `id` — unique identifier for this search. Format: ISO timestamp + 4-char random hex.
51
- - `timestamp` — ISO 8601 with timezone offset
52
- - `dates` — list of YYYY-MM-DD strings the user searched
53
- - `origins`, `destinations` — list of IATA codes used for this search (Phase 1 input)
54
- - `config_snapshot` — preferences as-applied to this search (after any Phase 2 overrides). Don't include `serpapi_key`.
55
-
56
- ### `selection_made`
57
-
58
- Written after the user picks a flight from the ranked output. Links back to the `search_started` entry.
59
-
60
- ```json
61
- {
62
- "type": "selection_made",
63
- "timestamp": "2026-05-11T14:31:42-04:00",
64
- "search_started_id": "2026-05-11T14:23:07-04:00-3f9a",
65
- "chosen_flight": {
66
- "date": "2026-05-15",
67
- "flight": "UA 1234",
68
- "from": "IND",
69
- "to": "EWR",
70
- "departs": "13:45",
71
- "arrives": "16:20",
72
- "price": 178,
73
- "stops": 0,
74
- "airline": "United"
75
- },
76
- "chosen_shuttle": null,
77
- "total_cost": 178,
78
- "options_shown_count": 7
79
- }
80
- ```
81
-
82
- **Fields:**
83
- - `search_started_id` — id of the matching `search_started` entry
84
- - `chosen_flight` — the flight dict from `filter_flights.py` output
85
- - `chosen_shuttle` — the shuttle dict from `pair_shuttles.py` if pairing was used, else null
86
- - `total_cost` — flight + shuttle USD
87
- - `options_shown_count` — how many options the user could pick from
88
-
89
- ## How the skill uses the log
90
-
91
- - **Pattern surfacing (passive):** When relevant, the skill can read recent entries to note things like "Your last 3 trips picked flights between $160–$200 — flagging the $245 option as out-of-pattern."
92
- - **Abandoned-search detection:** A `search_started` with no matching `selection_made` within the same day suggests an abandoned search. The skill should not bring these up unprompted.
93
- - **Privacy:** The log lives on the user's local disk, unencrypted. Don't put the SerpAPI key or any other secret in `config_snapshot`.
94
-
95
- ## Pruning
96
-
97
- The file grows by 2 entries per booking. Pruning is the user's responsibility — they can delete it or keep it indefinitely. The skill does not prune.
1
+ # Flight History Schema — `~/.flight-planner/flight_history.json`
2
+
3
+ Append-only log of every flight search. Used as passive context so the skill can recognize patterns over time ("you've been booking the ~$180 range lately").
4
+
5
+ ## File format
6
+
7
+ A single JSON array of entry objects. The file is created the first time the skill writes to it; until then the file does not exist.
8
+
9
+ ```json
10
+ [
11
+ { entry 1 },
12
+ { entry 2 },
13
+ ...
14
+ ]
15
+ ```
16
+
17
+ To append safely: read the array, push the new entry, write the file back. If the file is missing or corrupt, start a fresh array.
18
+
19
+ ## Entry types
20
+
21
+ There are two entry types. Each search produces one of each (under normal flow).
22
+
23
+ ### `search_started`
24
+
25
+ Written immediately after the user confirms Phase 2 preferences and before `fetch_flights.py` runs. Captures what they were looking for, even if they abandon the search before booking.
26
+
27
+ ```json
28
+ {
29
+ "type": "search_started",
30
+ "id": "2026-05-11T14:23:07-04:00-3f9a",
31
+ "timestamp": "2026-05-11T14:23:07-04:00",
32
+ "dates": ["2026-05-15", "2026-05-16"],
33
+ "origins": ["IND", "ORD"],
34
+ "destinations": ["EWR", "LGA"],
35
+ "config_snapshot": {
36
+ "budget_usd": 200,
37
+ "budget_strength": "soft",
38
+ "airline_preferences": ["UA", "DL"],
39
+ "airline_preference_strength": "soft",
40
+ "nonstop_preference": "preferred",
41
+ "nonstop_strength": "soft",
42
+ "time_priority_bands": ["11:00-14:00", "14:00-22:00"],
43
+ "time_priority_strength": "soft",
44
+ "had_shuttle": false
45
+ }
46
+ }
47
+ ```
48
+
49
+ **Fields:**
50
+ - `id` — unique identifier for this search. Format: ISO timestamp + 4-char random hex.
51
+ - `timestamp` — ISO 8601 with timezone offset
52
+ - `dates` — list of YYYY-MM-DD strings the user searched
53
+ - `origins`, `destinations` — list of IATA codes used for this search (Phase 1 input)
54
+ - `config_snapshot` — preferences as-applied to this search (after any Phase 2 overrides). Don't include `serpapi_key`.
55
+
56
+ ### `selection_made`
57
+
58
+ Written after the user picks a flight from the ranked output. Links back to the `search_started` entry.
59
+
60
+ ```json
61
+ {
62
+ "type": "selection_made",
63
+ "timestamp": "2026-05-11T14:31:42-04:00",
64
+ "search_started_id": "2026-05-11T14:23:07-04:00-3f9a",
65
+ "chosen_flight": {
66
+ "date": "2026-05-15",
67
+ "flight": "UA 1234",
68
+ "from": "IND",
69
+ "to": "EWR",
70
+ "departs": "13:45",
71
+ "arrives": "16:20",
72
+ "price": 178,
73
+ "stops": 0,
74
+ "airline": "United"
75
+ },
76
+ "chosen_shuttle": null,
77
+ "total_cost": 178,
78
+ "options_shown_count": 7
79
+ }
80
+ ```
81
+
82
+ **Fields:**
83
+ - `search_started_id` — id of the matching `search_started` entry
84
+ - `chosen_flight` — the flight dict from `filter_flights.py` output
85
+ - `chosen_shuttle` — the shuttle dict from `pair_shuttles.py` if pairing was used, else null
86
+ - `total_cost` — flight + shuttle USD
87
+ - `options_shown_count` — how many options the user could pick from
88
+
89
+ ## How the skill uses the log
90
+
91
+ - **Pattern surfacing (passive):** When relevant, the skill can read recent entries to note things like "Your last 3 trips picked flights between $160–$200 — flagging the $245 option as out-of-pattern."
92
+ - **Abandoned-search detection:** A `search_started` with no matching `selection_made` within the same day suggests an abandoned search. The skill should not bring these up unprompted.
93
+ - **Privacy:** The log lives on the user's local disk, unencrypted. Don't put the SerpAPI key or any other secret in `config_snapshot`.
94
+
95
+ ## Pruning
96
+
97
+ The file grows by 2 entries per booking. Pruning is the user's responsibility — they can delete it or keep it indefinitely. The skill does not prune.