vortexa-claude-skills 1.0.0

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.
@@ -0,0 +1,285 @@
1
+ ---
2
+ name: vortexa:voyages
3
+ description: "Query vessel voyages, fleet movements, and voyage timeseries from the Vortexa API"
4
+ argument-hint: "Show me VLCC voyages from Arabian Gulf to China in January 2025"
5
+ allowed-tools:
6
+ - Read
7
+ - Edit
8
+ - Write
9
+ - Bash
10
+ - Glob
11
+ - Grep
12
+ ---
13
+
14
+ <objective>
15
+ Translate natural language voyage questions into correct Vortexa API calls using VoyagesSearchEnriched (individual voyages) or VoyagesTimeseries (aggregate vessel metrics), generating re-runnable Python code artifacts with transparent filter confirmation. This skill handles all vessel voyage queries -- laden/ballast periods, events, port calls, fleet counts, DWT trends, and tonne-miles.
16
+ </objective>
17
+
18
+ <execution_context>
19
+ ## Pre-loaded Context (via CLAUDE.md @imports)
20
+ The following are automatically available -- do NOT re-read them:
21
+ - context/guardrails.md -- NEVER/ALWAYS rules for all Vortexa API calls
22
+ - context/entity-resolution.md -- How to resolve entity names to hex IDs
23
+ - context/date-units.md -- Date parsing rules and unit defaults
24
+
25
+ ## Required Context (Read on demand)
26
+ Read this file before processing the user's query:
27
+ - context/voyages.md -- Full parameter reference, voyage definitions, event types, status filters, V1/V2 differences
28
+ </execution_context>
29
+
30
+ ## Setup Check
31
+ @commands/vortexa/_check-setup.md
32
+
33
+ <process>
34
+ ## 1. Read Endpoint Context
35
+
36
+ Read `context/voyages.md` for full parameter reference, voyage definitions, event types, status filters, and V1/V2 differences.
37
+
38
+ ## 2. Parse the Query
39
+
40
+ Analyze $ARGUMENTS and extract:
41
+ - **Vessel class**: VLCC, Suezmax, Aframax, MR, LR1/LR2, Handysize, etc.
42
+ - **Geography**: Origins and/or destinations -- identify what layer (country, port, shipping_region_v2, region)
43
+ - **Voyage status**: Laden, ballast, or both
44
+ - **Time range**: Apply date-units.md calendar/trailing convention
45
+ - **voyage_date_range_activity**: active / arrivals / departures -- infer from query context or ask
46
+ - **Product filter**: Optional (e.g. crude, LNG, clean products)
47
+ - **Additional filters**: Flag, DWT range, vessel age, scrubber status, risk level, speed, corporate filters
48
+
49
+ ## 3. Check for Breakdown Redirect
50
+
51
+ If the query contains a dimension phrase -- "by origin", "by destination", "by product", "by vessel class", "by vessel type", "by vessel flag", "by DWT", "by grade", "by flag", "split by", "breakdown by", "broken down by" -- this is a breakdown query.
52
+
53
+ **Exclude frequency phrases:** "by month", "by week", "by day", "by quarter", "by year", "monthly", "weekly", "daily" are NOT dimension phrases. These specify frequency, not a split dimension.
54
+
55
+ **Trigger rule:** "by" + a DIMENSION word (country, port, region, product, vessel, class, grade, flag, origin, destination, type, DWT, owner, charterer, terminal). NOT "by" + a TIME word (month, week, day, year, quarter).
56
+
57
+ If a dimension phrase is detected, suggest:
58
+ "This looks like a breakdown query. Try: `/vortexa:breakdown {restate the full query}` for split analysis with Top-N bucketing and wide-format DataFrames."
59
+
60
+ If the user explicitly wants a voyage-specific query (individual voyages, fleet counts) without cargo breakdown, continue to Step 4.
61
+
62
+ ## 4. Route -- VoyagesSearchEnriched or VoyagesTimeseries?
63
+
64
+ Use signal words to determine endpoint:
65
+
66
+ **VoyagesSearchEnriched** (individual voyage records):
67
+ - Signal words: "show voyages", "vessel journey", "voyage details", "laden/ballast", "events", "port calls", "where is", "vessel track", "list vessels", "which ships"
68
+ - Returns individual voyage rows with vessel name, IMO, origin, destination, events, duration
69
+ - This is the DEFAULT if the query is ambiguous
70
+
71
+ **VoyagesTimeseries V1** (aggregate vessel metrics):
72
+ - Signal words: "how many vessels", "vessel count", "DWT trend", "fleet utilisation", "average speed", "wait time", "aggregate"
73
+ - Use for: vessel_count, dwt, cargo_quantity, avg_wait_time, avg_speed, utilisation, cubic_capacity, tonne_miles, avg_distance
74
+ - V1 uses underscore naming: origin_country, vessel_count, tonne_miles
75
+
76
+ **VoyagesTimeseries V2** (voyage-level metric aggregations):
77
+ - Signal words: "tonne-miles", "voyage count", "distance average", "duration average", "by owner", "by time charterer"
78
+ - Use for: voyage-count, tonne-miles-sum, distance-avg, duration-avg, and per-day rates
79
+ - V2 uses hyphen naming: origin-country, voyage-count, tonne-miles-sum
80
+ - V2 has richer corporate breakdowns (vessel owner, time charterer, cargo trader)
81
+
82
+ **NEVER mix V1 and V2 naming conventions in the same query.**
83
+
84
+ **Rule of thumb:** V1 for vessel-perspective metrics (DWT, speed, wait time, utilisation). V2 for voyage-perspective metrics (voyage counts, tonne-miles sums, duration averages) especially when corporate breakdowns are needed.
85
+
86
+ If ambiguous, ask: "Are you looking for individual voyage details or aggregate vessel statistics over time?"
87
+
88
+ ## 5. Check for Missing Parameters (ASK before confirming)
89
+
90
+ Ask targeted questions one at a time. Do NOT bulk-dump all missing params.
91
+
92
+ **For VoyagesSearchEnriched queries:**
93
+ - Voyage status -- ask if not specified: "Laden voyages, ballast voyages, or both?"
94
+ - Time range -- MUST be specified. Ask if missing: "What time period?"
95
+ - voyage_date_range_activity -- infer from context: "active vessels" = active, "arrivals" = arrivals, "departures" = departures. If ambiguous, ask: "Should I look for voyages active during this period, or specifically arrivals/departures?"
96
+ - At least one filter should be present (vessel class, geography, or product). If none specified, ask what to filter by.
97
+
98
+ **For VoyagesTimeseries queries:**
99
+ - Metric -- what to measure (vessel_count, tonne_miles, dwt, etc.). Ask if not clear.
100
+ - Breakdown dimension -- ask if a split is desired: "Would you like this broken down by destination country, vessel class, or another dimension?"
101
+ - Time range -- MUST be specified. Ask if missing.
102
+ - Frequency -- ask if not specified: "What frequency? daily / weekly / monthly?"
103
+
104
+ ## 6. Resolve Entity IDs
105
+
106
+ For each entity (origins, destinations, products, corporates):
107
+ 1. Check lib/aliases.json for common shorthands (AG, MEG, ARA, USG, etc.)
108
+ 2. Call lib/entities.py resolve functions with the correct entity_type and layer
109
+ 3. Multiple matches: present top 3 candidates, ask user to pick. NEVER auto-correct.
110
+ 4. Zero matches: suggest checking spelling or trying broader terms
111
+ 5. For vessel class filters: use `filter_vessel_classes=["oil_vlcc"]` directly -- no ID resolution needed
112
+
113
+ **CRITICAL -- ballast voyage origin/destination inversion:**
114
+ - For ballast voyages, `origins` = last DISCHARGE port (not where the ballast voyage started)
115
+ - For ballast voyages, `destinations` = next LOAD port (not where the ballast voyage ends)
116
+ - This is the OPPOSITE of laden voyage logic
117
+ - When querying ballast voyages with geography filters, always warn the user about this inversion in the confirmation step
118
+
119
+ ## 7. Confirm Before Executing (MANDATORY)
120
+
121
+ Present the complete parameter set:
122
+
123
+ ```
124
+ Query: [restate the user's question]
125
+ Endpoint: VoyagesSearchEnriched / VoyagesTimeseries V1 / VoyagesTimeseries V2
126
+ Voyage Status: [laden / ballast / both]
127
+ Date Range Activity: [active / arrivals / departures]
128
+ Origins: [name (layer)] or "not specified"
129
+ Destinations: [name (layer)] or "not specified"
130
+ Vessel Class: [class] or "not specified"
131
+ Product: [name (layer)] or "not specified"
132
+ Time: [start datetime] -> [end datetime] ([interpretation])
133
+ Metric: [vessel_count / tonne_miles / dwt / etc.] (timeseries only)
134
+ Frequency: [day / week / month / etc.] (timeseries only)
135
+ Breakdown: [split property] or "none" (timeseries only)
136
+ Additional: [any other filters -- flag, DWT range, scrubber, corporate, etc.]
137
+ Note: [ballast inversion warning if querying ballast voyages with geography filters]
138
+
139
+ Confirm or adjust?
140
+ ```
141
+
142
+ NEVER execute without user confirmation.
143
+
144
+ ## 8. Determine Execution Mode
145
+
146
+ **Mode 3 -- Code-aware** (user references an existing file):
147
+ - User mentions a .py file or notebook: read the file first, then extend or fix it
148
+ - Use context/voyages.md to validate and improve existing logic
149
+
150
+ **Mode 1 -- Pre-built function** (query matches a lib/ function pattern):
151
+ - Live laden voyages to a specific destination -> `live_voyages_now()` from lib/voyages.py
152
+ - Voyage count/DWT timeseries with percentile alerts -> `voyages_ts_with_alerts()` from lib/voyages.py
153
+ - Post-discharge ballast destination analysis -> `post_ballast_distribution_and_medians()` from lib/voyages.py
154
+
155
+ **Mode 2 -- Custom SDK code** (no matching lib/ function):
156
+ - VSE queries without a matching lib/ function -> write `VoyagesSearchEnriched().search()` code using voyages.md as reference
157
+ - VoyagesTimeseries queries -> write timeseries code using correct V1 or V2 syntax
158
+ - Add comment: `# Custom query -- built from Vortexa API documentation`
159
+
160
+ Selection order: if user references existing file -> Mode 3. Else if query matches a lib/ function -> Mode 1. Else -> Mode 2.
161
+
162
+ **VOY-02 Building Block Note:**
163
+ VoyagesSearchEnriched is also available as a building block for CM-Authoritative OOW workflows via `cargo_on_water_ts()` in lib/movements.py. That function uses VSE internally for vessel location enrichment, joining on cargo_movement_id. Users who need OOW by region should use Phase 6's `/vortexa:oow` skill, which calls this lib function. For direct VSE queries, use this `/vortexa:voyages` skill with appropriate filters.
164
+
165
+ ## 9. Generate & Execute Code Artifact
166
+
167
+ Write a self-contained .py file:
168
+
169
+ ```python
170
+ """Vortexa Query: {description}
171
+ Generated by /vortexa:voyages
172
+ Date: {date}
173
+ """
174
+ from datetime import datetime
175
+ import sys; sys.path.insert(0, "lib")
176
+ # Entity resolution imports (if needed)
177
+ from entities import resolve, EntityCache
178
+
179
+ # For pre-built functions:
180
+ from voyages import live_voyages_now # or voyages_ts_with_alerts, etc.
181
+
182
+ # For custom SDK code:
183
+ from vortexasdk import VoyagesSearchEnriched # or VoyagesTimeseries, VoyagesTimeseriesV2
184
+
185
+ # Entity Resolution
186
+ cache = EntityCache()
187
+ # {resolve each geography/product entity}
188
+
189
+ # Query Parameters
190
+ # {set up all confirmed parameters}
191
+
192
+ # Execute Query
193
+ df = VoyagesSearchEnriched().search(
194
+ time_min=..., time_max=...,
195
+ voyage_status=...,
196
+ voyage_date_range_activity=...,
197
+ # ... all confirmed filters
198
+ columns="all",
199
+ ).to_df()
200
+
201
+ # Format Results
202
+ # For VSE results, use rename_vse_columns(df) from lib/utils.py for human-readable names
203
+ # For timeseries, parse breakdown using standard pattern
204
+
205
+ # Display
206
+ print(f"Results: {len(df)} rows")
207
+ print(df.head(30))
208
+
209
+ # Export (uncomment to save)
210
+ # df.to_csv("output/{description}_{date}.csv", index=False)
211
+ ```
212
+
213
+ Key rules:
214
+ - For VoyagesSearchEnriched, use `.to_df(columns="all")` to get full detail
215
+ - For VoyagesTimeseries, use `.to_df()` and parse the breakdown array
216
+ - Use `rename_vse_columns(df)` from lib/utils.py for VSE column renaming
217
+ - Code must be self-contained and re-runnable
218
+ - All entity resolution is inline for transparency
219
+
220
+ Ask the user: new file or append to existing? Default: new file `vortexa_query_{slug}.py`.
221
+
222
+ ## 10. Present Results
223
+
224
+ Show a terminal summary:
225
+
226
+ ```
227
+ Results: {n} voyages, {start_date} to {end_date}
228
+
229
+ {brief description of findings -- top destinations, vessel counts, notable patterns}
230
+
231
+ {DataFrame preview with row cap}
232
+
233
+ Full data: {file_path}
234
+ Export to CSV? (y/n)
235
+ ```
236
+
237
+ Row cap by frequency (terminal summary only; full data in code artifact):
238
+ - Daily timeseries: first 30 rows
239
+ - Weekly timeseries: first 52 rows
240
+ - Monthly timeseries: first 24 rows
241
+ - Yearly timeseries / individual voyages: all rows (up to reasonable limit)
242
+
243
+ Always mention total row count if capped.
244
+
245
+ After the DataFrame preview and before the export offer, show a one-line methodology footnote using the CONFIRMED parameters from Step 7:
246
+ ```
247
+ Methodology: {Endpoint} | {voyage_status} | {key filters} | {frequency} | {metric} | {other relevant params}
248
+ ```
249
+ Example: `Methodology: VoyagesSearchEnriched | active | filter_origins=[Arabian/Persian Gulf] | filter_vessel_classes=[oil_vlcc] | size=500`
250
+
251
+ Use human-readable names for entity filters (not hex IDs).
252
+ </process>
253
+
254
+ ## 11. Summary (if triggered)
255
+
256
+ **Smart trigger:** If the user's query contains "analyze", "summarize", "explain results", "what does this show", or "key findings" -- generate the summary automatically.
257
+
258
+ **Post-query offer:** After showing results, always offer: "Want a summary of these results?"
259
+
260
+ **Brief summary (default):**
261
+ Analyze the DataFrame and produce 3-5 bullet points:
262
+ - Top 3-5 contributors by volume with percentages of total
263
+ - Period-over-period change if comparable time periods exist
264
+ - Notable outliers: spikes, drops, record values, trend reversals
265
+ - Each bullet includes specific numbers, not vague descriptions
266
+
267
+ **Extended summary (if user requests "detailed summary" or "full analysis"):**
268
+ - 100-200 word narrative paragraph in market commentary style
269
+ - Includes everything from brief plus trend descriptions and broader context
270
+
271
+ After showing the summary, offer: "Save this summary to a .md file?"
272
+ If yes: create `output/{description}_summary_{date}.md`
273
+
274
+ ## Error Handling
275
+
276
+ Wrap the execution step with these error patterns:
277
+
278
+ - **401 Unauthorized**: "API key is invalid or expired. Run /vortexa:init to check your setup."
279
+ - **500 Server Error**: "Vortexa API returned a server error. Try again in a few minutes."
280
+ - **Timeout**: "Query timed out. Try narrowing the date range or reducing filters."
281
+ - **Empty results**: "No data found. Check entity names, date range, and filters."
282
+ - **Entity not found**: Report clearly -- "The term '{term}' returned no matches. Check spelling or try a broader term."
283
+ - **Multiple entity matches**: Present top 3 candidates with name, layer, and first 8 chars of ID. Ask user to pick.
284
+
285
+ No auto-retry. No auto-correction. Report the error in plain English and let the user decide.
File without changes