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.
- package/CHANGELOG.md +28 -0
- package/VERSION +1 -0
- package/bin/.gitkeep +0 -0
- package/bin/setup.js +302 -0
- package/commands/vortexa/_check-setup.md +9 -0
- package/commands/vortexa/_skill-template.md +100 -0
- package/commands/vortexa/breakdown.md +294 -0
- package/commands/vortexa/cargo-flows.md +247 -0
- package/commands/vortexa/compare.md +315 -0
- package/commands/vortexa/custom.md +214 -0
- package/commands/vortexa/explain.md +124 -0
- package/commands/vortexa/init.md +133 -0
- package/commands/vortexa/oow.md +189 -0
- package/commands/vortexa/seasonal.md +185 -0
- package/commands/vortexa/voyages.md +285 -0
- package/context/.gitkeep +0 -0
- package/context/cargo-movements.md +738 -0
- package/context/date-units.md +188 -0
- package/context/endpoint-template.md +176 -0
- package/context/entity-resolution.md +217 -0
- package/context/guardrails.md +161 -0
- package/context/reference-endpoints.md +651 -0
- package/context/voyages.md +636 -0
- package/lib/__init__.py +4 -0
- package/lib/aliases.json +52 -0
- package/lib/api.py +20 -0
- package/lib/entities.py +254 -0
- package/lib/inventory.py +140 -0
- package/lib/movements.py +242 -0
- package/lib/requirements.txt +6 -0
- package/lib/seasonal.py +200 -0
- package/lib/timeseries.py +271 -0
- package/lib/utils.py +120 -0
- package/lib/vessels.py +192 -0
- package/lib/visualization.py +164 -0
- package/lib/voyages.py +236 -0
- package/package.json +28 -0
- package/templates/.env.template +3 -0
|
@@ -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.
|
package/context/.gitkeep
ADDED
|
File without changes
|