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,133 @@
1
+ ---
2
+ name: vortexa:init
3
+ description: Set up Vortexa analytics environment in the current project
4
+ argument-hint: ""
5
+ allowed-tools:
6
+ - Read
7
+ - Edit
8
+ - Write
9
+ - Bash
10
+ - Glob
11
+ - Grep
12
+ ---
13
+ <objective>
14
+ Initialize the Vortexa analytics environment in the current project directory. Verifies the global install at ~/.claude/vortexa/, creates a local .env for API key management, sets up CLAUDE.md with context @-imports, verifies .gitignore protection, and validates the Python environment.
15
+ </objective>
16
+
17
+ <process>
18
+
19
+ ## Step 1: Verify Global Install
20
+
21
+ Check that `~/.claude/vortexa/` exists and contains expected files:
22
+ - `~/.claude/vortexa/VERSION` must exist
23
+ - `~/.claude/vortexa/context/guardrails.md` must exist
24
+ - `~/.claude/vortexa/lib/__init__.py` must exist
25
+ - `~/.claude/commands/vortexa/cargo-flows.md` must exist
26
+
27
+ If any are missing, tell the user:
28
+ "Vortexa skills package is not installed or is incomplete. Run: npx @vortexa/claude-skills setup"
29
+ Stop execution.
30
+
31
+ If all present, read VERSION and display: "Vortexa skills v{version} detected."
32
+
33
+ ## Step 2: Create/Verify .env File
34
+
35
+ Read the template from the global install: `~/.claude/vortexa/templates/.env.template`
36
+
37
+ **If .env does NOT exist in the current directory:**
38
+ - Create .env from the template content
39
+ - Tell user: "Created .env file. Please add your Vortexa API key: edit .env and replace 'your_key_here' with your actual key."
40
+
41
+ **If .env EXISTS:**
42
+ - Read it and check if `VORTEXA_API_KEY` is present
43
+ - If present and not `your_key_here`: "Vortexa API key detected in .env -- you're all set."
44
+ - If present but still `your_key_here`: "Found .env but API key is still the placeholder. Please edit .env and add your real API key."
45
+ - If VORTEXA_API_KEY line is missing: Append `VORTEXA_API_KEY=your_key_here` to the .env and tell user to fill it in.
46
+
47
+ ## Step 3: Verify .gitignore Protection
48
+
49
+ Check if `.gitignore` exists in the project root.
50
+
51
+ **If .gitignore does NOT exist:**
52
+ - Create it with `.env` and `.env.*` entries
53
+ - Tell user: "Created .gitignore to protect your API key from being committed."
54
+
55
+ **If .gitignore EXISTS:**
56
+ - Check if it contains `.env`
57
+ - If YES: Good, no action needed
58
+ - If NO: Append `.env` and `.env.*` to .gitignore
59
+ - Tell user: "Updated .gitignore to protect your .env file."
60
+
61
+ ## Step 4: Create/Update Project CLAUDE.md
62
+
63
+ **If no CLAUDE.md exists in the current directory:**
64
+ Create a minimal one:
65
+ ```markdown
66
+ # Project
67
+
68
+ ## Vortexa Analytics Context
69
+ @~/.claude/vortexa/context/guardrails.md
70
+ @~/.claude/vortexa/context/entity-resolution.md
71
+ @~/.claude/vortexa/context/date-units.md
72
+ ```
73
+ Tell user: "Created CLAUDE.md with Vortexa context imports."
74
+
75
+ **If CLAUDE.md exists but has no Vortexa imports:**
76
+ Check if the file contains `vortexa/context/guardrails.md`. If not, append:
77
+ ```markdown
78
+
79
+ ## Vortexa Analytics Context
80
+ @~/.claude/vortexa/context/guardrails.md
81
+ @~/.claude/vortexa/context/entity-resolution.md
82
+ @~/.claude/vortexa/context/date-units.md
83
+ ```
84
+ Tell user: "Added Vortexa context imports to existing CLAUDE.md."
85
+
86
+ **If CLAUDE.md already has Vortexa imports:**
87
+ Tell user: "CLAUDE.md already has Vortexa context configured."
88
+
89
+ ## Step 5: Check Python Environment
90
+
91
+ Run each check and collect results:
92
+
93
+ 1. `python3 --version` (fall back to `python --version`): Require 3.10+
94
+ 2. `python3 -c "import vortexasdk; print(vortexasdk.__version__)"`: Check vortexasdk installed
95
+ 3. `python3 -c "import pandas; print(pandas.__version__)"`: Check pandas installed
96
+ 4. `python3 -c "import plotly; print(plotly.__version__)"`: Check plotly installed
97
+ 5. `python3 -c "from dotenv import load_dotenv; print('ok')"`: Check python-dotenv installed
98
+ 6. `python3 -c "import numpy; print(numpy.__version__)"`: Check numpy installed
99
+ 7. `python3 -c "import dateutil; print(dateutil.__version__)"`: Check python-dateutil installed
100
+
101
+ For any missing package, collect it. At the end, if anything is missing, print:
102
+ ```
103
+ Missing Python dependencies:
104
+ pip install vortexasdk pandas plotly python-dotenv numpy python-dateutil
105
+ ```
106
+
107
+ If all present, print versions found. Do NOT block setup if anything is missing -- warn only.
108
+
109
+ ## Step 6: Verify Context Accessibility
110
+
111
+ Read `~/.claude/vortexa/context/guardrails.md` (first 5 lines) to confirm file access works from the current directory.
112
+
113
+ If read fails: "Warning: Cannot read context files from ~/.claude/vortexa/. Check file permissions."
114
+ If read succeeds: "Context files accessible."
115
+
116
+ ## Step 7: Print Summary
117
+
118
+ ```
119
+ Vortexa Analytics Environment Ready!
120
+
121
+ Package: v{version} (installed at ~/.claude/vortexa/)
122
+ API Key: {status}
123
+ Python: {version}
124
+ Dependencies: {all ok / missing list}
125
+ Context: {accessible / warning}
126
+ CLAUDE.md: {created / updated / already configured}
127
+
128
+ You're ready to go! Try:
129
+ /vortexa:cargo-flows crude exports from Saudi Arabia last month
130
+ /vortexa:explain what is the difference between loading_end and loading_state?
131
+ ```
132
+
133
+ </process>
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: vortexa:oow
3
+ description: "Run oil-on-water breakdown by region using the CM-Authoritative methodology"
4
+ argument-hint: "crude on water by shipping region, last 3 months"
5
+ allowed-tools:
6
+ - Read
7
+ - Edit
8
+ - Write
9
+ - Bash
10
+ - Glob
11
+ - Grep
12
+ ---
13
+
14
+ <objective>
15
+ Build an oil-on-water breakdown by region using the CM-Authoritative methodology: CargoMovements for authoritative OOW periods and quantities, VoyagesSearchEnriched for real-time vessel location enrichment. Uses `cargo_on_water_ts()` from lib/movements.py for data and `oow_area_chart()` from lib/visualization.py for stacked area visualization.
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 before processing the user's query:
27
+ - context/cargo-movements.md -- CargoMovements parameters, activity filters, column naming
28
+ - context/voyages.md -- VoyagesSearchEnriched parameters, event structure, location details
29
+ </execution_context>
30
+
31
+ ## Setup Check
32
+ @commands/vortexa/_check-setup.md
33
+
34
+ <process>
35
+
36
+ ## CM-Authoritative OOW -- MANDATORY
37
+
38
+ The ONLY correct method for location-based OOW analysis is `cargo_on_water_ts()` from lib/movements.py.
39
+
40
+ DO NOT use CTS `cargo_on_water_state` for this skill. CTS shows where cargo was LOADED FROM and GOING TO (origin/destination), NOT where it currently IS on the water.
41
+
42
+ The CM-Authoritative pattern:
43
+ 1. CargoMovements with `cargo_on_water_state` for authoritative OOW periods and quantities
44
+ 2. VoyagesSearchEnriched for real-time vessel location (shipping_region from events)
45
+ 3. Join on cargo_movement_id (16-char truncated) to enrich CM cargoes with voyage location
46
+
47
+ If the generated code imports CargoTimeSeries for the OOW skill, it is WRONG.
48
+
49
+ ## Step 1: Read Endpoint Context
50
+
51
+ Read `context/cargo-movements.md` and `context/voyages.md` (both are needed -- CM for quantities, VSE for location).
52
+
53
+ ## Step 2: Parse the Query
54
+
55
+ Analyze $ARGUMENTS and extract:
56
+
57
+ - **Product**: typically crude, but any product group works
58
+ - **Location layer**: `shipping_region_v2` (default), `region`, or `country`. Controls how granular the location breakdown is.
59
+ - **Time range**: Apply date-units.md convention. Note: longer ranges = longer API queries (CM + VSE both queried).
60
+ - **Unit**: commodity defaults (oil=b for OOW since it's absolute volume, not rate)
61
+ - **Top-N**: how many regions to show in chart (default 8)
62
+ - **Frequency**: for chart display resampling -- daily (default), weekly, monthly
63
+ - **Exclude intra-country**: optional, default false for OOW
64
+
65
+ ## Step 3: Check for Missing Parameters
66
+
67
+ Required (ask if missing):
68
+ - **Product** -- MUST be specified
69
+ - **Time range** -- MUST be specified
70
+
71
+ Optional with smart defaults (mention in confirmation):
72
+ - **Location layer**: default `shipping_region_v2`
73
+ - **Unit**: default `b` (absolute barrels for OOW volumes)
74
+ - **Top-N**: default 8
75
+ - **Frequency**: default `daily`
76
+ - **Exclude intra-country**: default false
77
+
78
+ Ask one parameter at a time. Do NOT bulk-ask.
79
+
80
+ ## Step 4: Resolve Entity IDs
81
+
82
+ Resolve product ID using `lib/entities.py`:
83
+ 1. Check `lib/aliases.json` for common shorthands
84
+ 2. Call `resolve_product(term, layer)` with the correct layer
85
+ 3. If multiple matches: present top 3 candidates. Ask user to pick. NEVER auto-correct.
86
+ 4. If zero matches: tell user the term wasn't found.
87
+
88
+ No geography resolution needed -- OOW covers global flows; the location layer is applied during the CM+VSE join.
89
+
90
+ Use `EntityCache()` from `lib/entities.py` for the session.
91
+
92
+ ## Step 5: Confirm Before Executing (MANDATORY)
93
+
94
+ Present the complete parameter set:
95
+
96
+ ```
97
+ Query: [restate the user's question]
98
+ Analysis: Oil-on-water by region (CM-Authoritative)
99
+ Data: CargoMovements (OOW periods) + VoyagesSearchEnriched (location)
100
+ Product: [name (layer)]
101
+ Time: [start datetime] -> [end datetime]
102
+ Location level: [shipping_region_v2 / region / country]
103
+ Unit: [unit] (absolute volume)
104
+ Chart: Stacked area, Top [N] regions + Other
105
+ Display frequency: [daily/weekly/monthly]
106
+
107
+ Note: This query calls both CM and VSE endpoints -- may take 1-3 minutes for large date ranges.
108
+
109
+ Confirm or adjust?
110
+ ```
111
+
112
+ NEVER execute without confirmation. Wait for user response.
113
+
114
+ ## Step 6: Generate & Execute Code Artifact
115
+
116
+ Generate a self-contained .py file using the data -> viz -> save pipeline:
117
+
118
+ ```python
119
+ """Vortexa OOW: {description}
120
+ Generated by /vortexa:oow
121
+ Date: {date}
122
+ """
123
+ from datetime import datetime
124
+ import os
125
+ import sys; sys.path.insert(0, "lib")
126
+ from entities import resolve_product, EntityCache
127
+ from movements import cargo_on_water_ts
128
+ from visualization import oow_area_chart
129
+
130
+ # Entity Resolution
131
+ cache = EntityCache()
132
+ {product_resolution_code}
133
+
134
+ # OOW by Region (CM-Authoritative)
135
+ ts_wide = cargo_on_water_ts(
136
+ time_min=datetime({time_min}),
137
+ time_max=datetime({time_max}),
138
+ product={product_id},
139
+ unit="{unit}",
140
+ location_layer="{location_layer}",
141
+ exclude_intra_country={exclude_intra},
142
+ verbose=True,
143
+ )
144
+
145
+ print(ts_wide)
146
+
147
+ # Chart
148
+ fig = oow_area_chart(ts_wide, freq="{freq}", top_n={top_n}, title="{title}")
149
+
150
+ os.makedirs("output", exist_ok=True)
151
+ filepath = f"output/{slug}_oow_{datetime.now().strftime('%Y-%m-%d')}.html"
152
+ fig.write_html(filepath, auto_open=True)
153
+ print(f"Chart saved: {filepath}")
154
+
155
+ # Export (uncomment to save)
156
+ # ts_wide.to_csv(f"output/{slug}_oow_{datetime.now().strftime('%Y-%m-%d')}.csv")
157
+ ```
158
+
159
+ After generating, ask: "Save as new file, or add to an existing notebook/script?" Default: new file.
160
+ Run the code to get results.
161
+
162
+ ## Step 7: Present Results
163
+
164
+ - Show the wide DataFrame (Top-N regions + Other + Total by date)
165
+ - Row cap by display frequency: daily=30, weekly=52, monthly=24
166
+ - If capped: "Showing first N of M rows"
167
+ - One-line methodology footnote using CONFIRMED parameters:
168
+ ```
169
+ Methodology: CM-Authoritative OOW | CargoMovements (cargo_on_water_state) + VoyagesSearchEnriched (location) | {product} | {unit} | {location_layer} | Top {N}
170
+ ```
171
+ Use human-readable names for entity filters (not hex IDs).
172
+ - File path to chart HTML
173
+ - Offer: "Export to CSV?" and "Want a summary?"
174
+
175
+ ## Error Handling
176
+
177
+ Report immediately in plain English, no auto-retry:
178
+
179
+ - **401 Unauthorized**: "API key is invalid or expired. Run /vortexa:init to check your setup."
180
+ - **500 Server Error**: "Vortexa API returned a server error. Try again in a few minutes."
181
+ - **Timeout**: "Query timed out. Try narrowing the date range or reducing filters."
182
+ - **Empty results / No cargo movements found**: "No cargo movements found for this query. Try broadening the product filter or date range."
183
+ - **Entity not found**: "The term '{term}' returned no matches. Check spelling or try a broader term."
184
+ - **Multiple entity matches**: "{n} matches found for '{term}': [list]. Which did you mean?"
185
+ - **Long query warning**: Already included in confirmation step (1-3 min for large date ranges).
186
+
187
+ Never auto-retry. Never self-correct. Report the error and let the user decide.
188
+
189
+ </process>
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: vortexa:seasonal
3
+ description: "Run multi-year seasonal pattern analysis for cargo flows with min/max/avg bands and forward-looking current year"
4
+ argument-hint: "crude exports from Middle East, 2020-2026, monthly"
5
+ allowed-tools:
6
+ - Read
7
+ - Edit
8
+ - Write
9
+ - Bash
10
+ - Glob
11
+ - Grep
12
+ ---
13
+
14
+ <objective>
15
+ Run multi-year seasonal pattern analysis using CargoTimeSeries, producing a seasonal overlay chart with historical min/max range band, average line, last year, and forward-looking current year segment. Uses `complete_seasonal_flows()` and `seasonal_charts()` from lib/seasonal.py for data, and `seasonal_chart()` from lib/visualization.py for Plotly output.
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 before processing the user's query:
27
+ - context/cargo-movements.md -- CTS parameters, activity filter rules
28
+ </execution_context>
29
+
30
+ ## Setup Check
31
+ @commands/vortexa/_check-setup.md
32
+
33
+ <process>
34
+
35
+ ## CRITICAL PITFALLS
36
+
37
+ - `complete_seasonal_flows()` handles the 4-year API block limit automatically via `get_search_blocks()`. Do NOT manually split date ranges.
38
+ - `seasonal_charts()` filters out Feb 29 automatically. The viz function handles "Current year" column filtering (non-empty values only).
39
+ - Minimum 3 years for meaningful min/max/avg bands. Warn if user requests < 3 years: "Seasonal analysis with fewer than 3 years produces unreliable min/max bands. Consider extending the range."
40
+
41
+ ## Step 1: Read Endpoint Context
42
+
43
+ Read `context/cargo-movements.md` for CTS parameters and activity filter rules.
44
+
45
+ ## Step 2: Parse the Query
46
+
47
+ Analyze $ARGUMENTS and extract:
48
+
49
+ - **Product**: commodity (crude, LNG, clean products, etc.)
50
+ - **Geography**: origin and/or destination
51
+ - **Activity**: movement stage
52
+ - exports / shipped out / departures = `loading_end`
53
+ - imports / arrivals / received = `unloading_start`
54
+ - on the water / afloat / in transit = `cargo_on_water_state`
55
+ - floating storage = `storing_state`
56
+ - **Year range**: start year to current year. "2020-2026" or "last 5 years" = start year 5 years ago. MINIMUM 3 years for meaningful seasonal pattern.
57
+ - **Unit**: commodity defaults (oil=bpd, LNG=t, LPG=t)
58
+ - **Frequency**: `day` or `month` -- these are the two supported by `seasonal_charts()`. Default to `month` if not specified. Do NOT ask -- just default and mention in confirmation.
59
+ - **Chart type**: multi-year overlay line chart (default). User can request "area chart" override.
60
+ - **Moving average**: optional, if user says "7-day MA" or "30-day smoothing"
61
+
62
+ ## Step 3: Check for Missing Parameters (ASK before confirming)
63
+
64
+ Required (ask if missing):
65
+ - **Product** -- MUST be specified
66
+ - **Activity** -- MUST be specified. If direction ambiguous, ask: "exports from origin or imports to destination?"
67
+ - **Year range** -- MUST be specified. If user says just "seasonal", ask: "What year range should I analyze? e.g., 2020-2026"
68
+
69
+ Optional but commonly specified:
70
+ - **Geography** -- origin and/or destination (optional)
71
+
72
+ Ask one parameter at a time. Do NOT bulk-ask.
73
+
74
+ ## Step 4: Resolve Entity IDs
75
+
76
+ For each entity mentioned (origin, destination, product):
77
+ 1. Check `lib/aliases.json` for common shorthands (ME, AG, USG, ARA, etc.)
78
+ 2. Call `lib/entities.py` resolve functions with the correct entity type and layer:
79
+ - `resolve_geography(term, layer)` for origins/destinations
80
+ - `resolve_product(term, layer)` for products
81
+ - `resolve(term, entity_type, layer, cache)` for the unified resolver with caching
82
+ 3. If multiple matches: present top 3 candidates. Ask user to pick. NEVER auto-correct.
83
+ 4. If zero matches: tell user the term wasn't found, suggest checking spelling or trying a broader term.
84
+
85
+ Use `EntityCache()` from `lib/entities.py` for the session to avoid re-resolving the same entities.
86
+
87
+ ## Step 5: Confirm Before Executing (MANDATORY)
88
+
89
+ Present the complete parameter set:
90
+
91
+ ```
92
+ Query: [restate the user's question]
93
+ Analysis: Seasonal pattern
94
+ Endpoint: CargoTimeSeries (via complete_seasonal_flows)
95
+ Activity: [filter_activity value] ([user's term])
96
+ Origin: [name (layer)] or "not specified"
97
+ Destination: [name (layer)] or "not specified"
98
+ Product: [name (layer)]
99
+ Year range: [start_year] to [current_year] ([N] years)
100
+ Frequency: [day/month]
101
+ Unit: [unit] ([reason])
102
+ Chart: Multi-year overlay with min/max band, average, last year, current year
103
+ Intra: [exclude_intra_country for exports/imports, all otherwise]
104
+
105
+ Confirm or adjust?
106
+ ```
107
+
108
+ NEVER execute without confirmation. Wait for user response.
109
+
110
+ ## Step 6: Generate & Execute Code Artifact
111
+
112
+ Generate a self-contained .py file using the data -> viz -> save pipeline:
113
+
114
+ ```python
115
+ """Vortexa Seasonal: {description}
116
+ Generated by /vortexa:seasonal
117
+ Date: {date}
118
+ """
119
+ from datetime import datetime
120
+ import os
121
+ import sys; sys.path.insert(0, "lib")
122
+ from entities import resolve_geography, resolve_product, EntityCache
123
+ from seasonal import complete_seasonal_flows
124
+ from visualization import seasonal_chart
125
+
126
+ # Entity Resolution
127
+ cache = EntityCache()
128
+ {entity_resolution_code}
129
+
130
+ # Seasonal Analysis
131
+ seasonal_df = complete_seasonal_flows(
132
+ start_y={start_year}, start_m=1, start_d=1,
133
+ product_ids=[{product_id}], product_ids_excl=[],
134
+ freq="{frequency}",
135
+ unit="{unit}",
136
+ activity="{activity}",
137
+ origin_ids=[{origin_ids}], origin_ids_excl=[],
138
+ destination_ids=[{destination_ids}], destination_ids_excl=[],
139
+ ma_period={ma_period_or_None},
140
+ exclude_intra={True_or_False},
141
+ )
142
+
143
+ print(seasonal_df)
144
+
145
+ # Chart
146
+ fig = seasonal_chart(seasonal_df, title="{title}", y_label="{unit_label}")
147
+
148
+ os.makedirs("output", exist_ok=True)
149
+ filepath = f"output/{slug}_seasonal_{datetime.now().strftime('%Y-%m-%d')}.html"
150
+ fig.write_html(filepath, auto_open=True)
151
+ print(f"Chart saved: {filepath}")
152
+
153
+ # Export (uncomment to save)
154
+ # seasonal_df.to_csv(f"output/{slug}_seasonal_{datetime.now().strftime('%Y-%m-%d')}.csv", index=False)
155
+ ```
156
+
157
+ After generating, ask: "Save as new file, or add to an existing notebook/script?" Default: new file.
158
+ Run the code to get results.
159
+
160
+ ## Step 7: Present Results
161
+
162
+ - Show full seasonal DataFrame (12-365 rows depending on freq)
163
+ - One-line methodology footnote using CONFIRMED parameters:
164
+ ```
165
+ Methodology: CargoTimeSeries (seasonal) | {activity} | {key filters} | {freq} | {unit} | {start_year}-{current_year}
166
+ ```
167
+ Use human-readable names for entity filters (not hex IDs).
168
+ - File path to chart HTML
169
+ - Offer: "Export to CSV?" and "Want a summary of these results?"
170
+
171
+ ## Error Handling
172
+
173
+ Report immediately in plain English, no auto-retry:
174
+
175
+ - **401 Unauthorized**: "API key is invalid or expired. Run /vortexa:init to check your setup."
176
+ - **500 Server Error**: "Vortexa API returned a server error. Try again in a few minutes."
177
+ - **Timeout**: "Query timed out. Try narrowing the date range or reducing filters."
178
+ - **Empty results**: "No data found for this query. Check that the entity names, date range, and filters are correct."
179
+ - **Entity not found**: "The term '{term}' returned no matches. Check spelling or try a broader term."
180
+ - **Multiple entity matches**: "{n} matches found for '{term}': [list]. Which did you mean?"
181
+ - **< 3 years**: "Seasonal analysis with fewer than 3 years produces unreliable min/max bands. Consider extending the range."
182
+
183
+ Never auto-retry. Never self-correct. Report the error and let the user decide.
184
+
185
+ </process>