vortexa-claude-skills 1.0.0 → 1.1.0-beta.2

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 (48) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/README.md +65 -0
  3. package/VERSION +1 -1
  4. package/bin/setup.js +62 -1
  5. package/commands/vortexa/_check-setup.md +1 -1
  6. package/commands/vortexa/_wf-ballast-speeds.md +135 -0
  7. package/commands/vortexa/{oow.md → _wf-cow.md} +82 -37
  8. package/commands/vortexa/_wf-diversions.md +158 -0
  9. package/commands/vortexa/_wf-fleet-distribution.md +172 -0
  10. package/commands/vortexa/_wf-floating-storage.md +298 -0
  11. package/commands/vortexa/_wf-idle-vessels.md +151 -0
  12. package/commands/vortexa/_wf-importers-exporters.md +20 -0
  13. package/commands/vortexa/_wf-net-flows.md +196 -0
  14. package/commands/vortexa/_wf-post-ballast.md +166 -0
  15. package/commands/vortexa/_wf-pricing-voyage.md +10 -0
  16. package/commands/vortexa/_wf-regional-freight.md +10 -0
  17. package/commands/vortexa/_wf-top-movers.md +197 -0
  18. package/commands/vortexa/_wf-top-ports.md +196 -0
  19. package/commands/vortexa/_wf-vessel-availability.md +14 -0
  20. package/commands/vortexa/breakdown.md +55 -13
  21. package/commands/vortexa/cargo-flows.md +45 -14
  22. package/commands/vortexa/compare.md +41 -10
  23. package/commands/vortexa/custom.md +186 -120
  24. package/commands/vortexa/explain.md +9 -5
  25. package/commands/vortexa/init.md +1 -1
  26. package/commands/vortexa/seasonal.md +43 -8
  27. package/commands/vortexa/voyages.md +67 -14
  28. package/commands/vortexa/workflows.md +134 -0
  29. package/context/guardrails.md +2 -2
  30. package/lib/aliases.json +13 -3
  31. package/lib/api.py +1 -1
  32. package/lib/ballast_speeds.py +110 -0
  33. package/lib/diversions.py +347 -0
  34. package/lib/entities.py +34 -0
  35. package/lib/fleet_distribution.py +186 -0
  36. package/lib/floating_storage.py +171 -0
  37. package/lib/freight_pricing.py +17 -0
  38. package/lib/idle_vessels.py +233 -0
  39. package/lib/net_flows.py +145 -0
  40. package/lib/notebook.py +63 -0
  41. package/lib/seasonal.py +79 -88
  42. package/lib/top_movers.py +210 -0
  43. package/lib/top_ports.py +121 -0
  44. package/lib/utils.py +17 -1
  45. package/lib/vessel_availability_ts.py +17 -0
  46. package/lib/visualization.py +415 -14
  47. package/lib/voyages.py +35 -14
  48. package/package.json +13 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0-beta.1] - 2026-03-12
4
+
5
+ ### Beta Release
6
+
7
+ **New: Workflow Analytics Engine**
8
+ - 15 curated analytical workflows accessible via `/vortexa:workflows`
9
+ - Covers net flows, top ports, top movers, floating storage, seasonal patterns, fleet distribution, ballast speeds, idle vessel analysis, post-ballast distribution, speed-based diversions, and cargo-on-water
10
+ - Each workflow produces publication-ready charts and exportable data
11
+
12
+ **New: Flagship Custom Analytics Builder**
13
+ - `/vortexa:custom` rebuilt as a 3-stage analytics builder (discuss, plan, execute)
14
+ - Adaptive questioning to identify exactly what you need
15
+ - Parallel data fetching for complex multi-endpoint queries
16
+ - Default output as Jupyter notebooks
17
+
18
+ **New: Intelligent Skill Routing**
19
+ - Ask any commodity/energy question in natural language -- Claude automatically routes to the right skill
20
+ - No need to remember slash command names
21
+
22
+ **Improved: Setup & Installation**
23
+ - Setup validates Python 3.10+ and vortexasdk before installing
24
+ - Clear guidance when prerequisites are missing
25
+ - Package published as vortexa-claude-skills on npm
26
+
27
+ **Improved: Interaction Speed**
28
+ - Faster workflow menu navigation
29
+ - Streamlined category browsing
30
+
3
31
  ## [1.0.0] - 2026-02-27
4
32
 
5
33
  ### Initial Release
@@ -22,7 +50,7 @@
22
50
  - Endpoint template pattern for adding new API endpoints
23
51
 
24
52
  **Distribution:**
25
- - One-command install: `npx @vortexa/claude-skills setup`
53
+ - One-command install: `npx vortexa-claude-skills setup`
26
54
  - Per-directory init: `/vortexa:init` for environment setup
27
55
  - Cross-platform: Mac, Windows, Linux
28
- - Automatic update: `npx @vortexa/claude-skills update`
56
+ - Automatic update: `npx vortexa-claude-skills update`
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # vortexa-claude-skills
2
+
3
+ Natural language analytics for Vortexa's commodity and energy data -- powered by Claude Code.
4
+
5
+ ## What it does
6
+
7
+ A set of Claude Code skills that let you query Vortexa's API using plain English. Ask about cargo flows, vessel voyages, seasonal patterns, fleet analytics, and more -- get structured data and publication-ready charts without writing code. Includes 9 interactive skills and 15 curated analytical workflows covering flows, freight, on-water, and LNG markets.
8
+
9
+ ## Prerequisites
10
+
11
+ - [Claude Code](https://claude.ai/code)
12
+ - Node.js 18+
13
+ - Python 3.10+
14
+ - `vortexasdk` Python package (`pip install vortexasdk`)
15
+ - Vortexa API key
16
+
17
+ ## Install
18
+
19
+ ```
20
+ npx vortexa-claude-skills setup
21
+ ```
22
+
23
+ This installs skill files to `~/.claude/` and registers them with Claude Code.
24
+
25
+ ## Quick Start
26
+
27
+ 1. Open Claude Code in any project directory
28
+ 2. Run `/vortexa:init` to set up your environment (API key, etc.)
29
+ 3. Start asking questions: "How much crude oil was exported from Saudi Arabia last month?"
30
+
31
+ ## Available Skills
32
+
33
+ | Skill | Description |
34
+ |---|---|
35
+ | `/vortexa:init` | Set up your Vortexa analytics environment |
36
+ | `/vortexa:cargo-flows` | Query cargo flows, exports, and imports |
37
+ | `/vortexa:breakdown` | Break down flows by origin, destination, product, or vessel class |
38
+ | `/vortexa:voyages` | Query vessel voyages and fleet movements |
39
+ | `/vortexa:custom` | Flagship analytics builder -- discuss, plan, and build any analysis |
40
+ | `/vortexa:explain` | Get plain-English explanations of Vortexa data and methodology |
41
+ | `/vortexa:seasonal` | Multi-year seasonal pattern analysis with min/max/avg bands |
42
+ | `/vortexa:compare` | Period-over-period or region-over-region comparisons |
43
+ | `/vortexa:workflows` | Access 15 curated analytical workflows |
44
+
45
+ ## Workflows
46
+
47
+ Run `/vortexa:workflows` to browse 15 curated analytical workflows organized by category:
48
+
49
+ - **Flows** -- Net flows, top ports, top movers
50
+ - **On Water** -- Floating storage, cargo on water
51
+ - **Voyages** -- Fleet distribution, ballast speeds, idle vessels, post-ballast distribution, diversions
52
+ - **Freight Pricing** -- Regional freight rates, pricing voyages
53
+ - **LNG** -- Vessel availability, importers and exporters
54
+
55
+ Each workflow produces publication-ready charts and exportable data.
56
+
57
+ ## Update
58
+
59
+ ```
60
+ npx vortexa-claude-skills update
61
+ ```
62
+
63
+ ## License
64
+
65
+ UNLICENSED -- Internal use only.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.1.0-beta.2
package/bin/setup.js CHANGED
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
6
  const crypto = require('crypto');
7
+ const { execSync } = require('child_process');
7
8
 
8
9
  const packageDir = path.resolve(__dirname, '..');
9
10
  const version = fs.readFileSync(path.join(packageDir, 'VERSION'), 'utf8').trim();
@@ -159,6 +160,62 @@ function countFilesByType(files) {
159
160
  return counts;
160
161
  }
161
162
 
163
+ function checkPrerequisites() {
164
+ const issues = [];
165
+
166
+ // 1. Check Python 3.10+
167
+ let python_version;
168
+ try {
169
+ const raw = execSync('python3 --version', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
170
+ const match = raw.match(/Python\s+(\d+)\.(\d+)\.(\d+)/);
171
+ if (!match) {
172
+ issues.push('Could not parse Python version. Install Python 3.10+ from https://python.org/downloads/');
173
+ } else {
174
+ const major = parseInt(match[1], 10);
175
+ const minor = parseInt(match[2], 10);
176
+ if (major < 3 || (major === 3 && minor < 10)) {
177
+ issues.push(`Python ${match[1]}.${match[2]}.${match[3]} found, but 3.10+ is required. Install from https://python.org/downloads/`);
178
+ } else {
179
+ python_version = `${match[1]}.${match[2]}.${match[3]}`;
180
+ }
181
+ }
182
+ } catch (err) {
183
+ issues.push('python3 not found. Install Python 3.10+ from https://python.org/downloads/');
184
+ }
185
+
186
+ // 2. Check vortexasdk
187
+ let sdk_version;
188
+ try {
189
+ sdk_version = execSync('python3 -c "import vortexasdk; print(vortexasdk.__version__)"', {
190
+ encoding: 'utf8',
191
+ stdio: ['pipe', 'pipe', 'pipe']
192
+ }).trim();
193
+ } catch (err) {
194
+ issues.push('vortexasdk not installed. Run: pip install vortexasdk');
195
+ }
196
+
197
+ if (issues.length > 0) {
198
+ console.log('\nPrerequisite warnings:');
199
+ for (const issue of issues) {
200
+ console.log(` - ${issue}`);
201
+ }
202
+ console.log('\nSkills have been installed, but queries will not work until these are resolved.\n');
203
+ } else {
204
+ console.log(`Prerequisites OK: Python ${python_version}, vortexasdk ${sdk_version}`);
205
+ }
206
+ }
207
+
208
+ function checkEnvGuidance() {
209
+ const env_path = path.join(os.homedir(), '.claude', 'vortexa', '.env');
210
+ if (!fs.existsSync(env_path)) {
211
+ console.log('Note: You\'ll need a .env file with your Vortexa API key.');
212
+ console.log('Run /vortexa:init in Claude Code to set this up, or create ~/.claude/vortexa/.env with:');
213
+ console.log('');
214
+ console.log(' VORTEXA_API_KEY=your_key_here');
215
+ console.log('');
216
+ }
217
+ }
218
+
162
219
  function setup(targetDir) {
163
220
  const commandsSrc = path.join(packageDir, 'commands', 'vortexa');
164
221
  const contextSrc = path.join(packageDir, 'context');
@@ -217,7 +274,7 @@ function setup(targetDir) {
217
274
  const libCount = libFiles.length;
218
275
  const templateCount = templateFiles.length;
219
276
 
220
- console.log(`\nVortexa Claude Skills v${version} installed successfully!\n`);
277
+ console.log(`\nvortexa-claude-skills v${version} installed successfully!\n`);
221
278
  console.log('Files installed:');
222
279
  console.log(` ~/.claude/commands/vortexa/ (${commandCount} skill files)`);
223
280
  console.log(` ~/.claude/vortexa/context/ (${contextCount} context docs)`);
@@ -232,6 +289,8 @@ function setup(targetDir) {
232
289
  console.log(' 2. Run /vortexa:init to set up your environment');
233
290
  console.log(' 3. Run /vortexa:cargo-flows to start querying!');
234
291
  console.log('');
292
+
293
+ checkEnvGuidance();
235
294
  }
236
295
 
237
296
  function update(targetDir) {
@@ -292,6 +351,8 @@ function main() {
292
351
  setup(targetDir);
293
352
  break;
294
353
  }
354
+
355
+ checkPrerequisites();
295
356
  }
296
357
 
297
358
  try {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Before executing any Vortexa analytics command, verify both the global install and local environment:
4
4
 
5
- 1. Check that `~/.claude/vortexa/VERSION` exists. If not: "Vortexa skills package is not installed. Run: npx @vortexa/claude-skills setup"
5
+ 1. Check that `~/.claude/vortexa/VERSION` exists. If not: "Vortexa skills package is not installed. Run: npx vortexa-claude-skills setup"
6
6
  2. Check that `.env` exists in the current project directory. If not: "Vortexa environment not configured in this project. Run /vortexa:init first."
7
7
  3. Check that `.env` contains `VORTEXA_API_KEY` and it is not `your_key_here`. If not: "API key not configured. Edit .env and add your Vortexa API key."
8
8
 
@@ -0,0 +1,135 @@
1
+ ---
2
+ name: vortexa:_wf-ballast-speeds
3
+ description: "Seasonal ballast speed patterns with min/max/avg bands"
4
+ argument-hint: "VLCC ballast speeds to AG, 2019-2026, daily"
5
+ allowed-tools:
6
+ - Read
7
+ - Edit
8
+ - Write
9
+ - Bash
10
+ - Glob
11
+ - Grep
12
+ - AskUserQuestion
13
+ ---
14
+
15
+ <objective>
16
+ Seasonal ballast speed analysis using VoyagesTimeseries with avg_speed metric. Produces the same seasonal overlay chart as /vortexa:seasonal (min/max band, average, last year, current year) but for ballast voyage speeds instead of cargo flows. Uses `complete_seasonal_voyages()` from lib/ballast_speeds.py and `seasonal_chart()` from lib/visualization.py.
17
+ </objective>
18
+
19
+ <execution_context>
20
+ ## Pre-loaded Context (via CLAUDE.md @imports)
21
+ - context/guardrails.md, context/entity-resolution.md, context/date-units.md
22
+
23
+ ## Required Context (Read on demand)
24
+ - context/voyages.md -- VoyagesTimeseries parameters
25
+ </execution_context>
26
+
27
+ ## Setup Check
28
+ @commands/vortexa/_check-setup.md
29
+
30
+ <process>
31
+
32
+ ## CRITICAL PITFALLS
33
+ - Minimum 3 years for meaningful seasonal bands.
34
+ - breakdown_property='avg_speed' with breakdown_unit_operator='avg' is the standard for speed patterns.
35
+ - voyage_status='ballast' is essential -- without it, you get mixed laden/ballast speeds.
36
+
37
+ ## Step 1: Read Endpoint Context
38
+ Read `context/voyages.md` for VoyagesTimeseries parameters.
39
+
40
+ ## Step 2: Parse the Query
41
+ Extract: product (last cargo), destination, origin (optional), vessel class (optional), frequency (day/month, default: day), moving average (optional), start year.
42
+
43
+ ## Step 3: Check for Missing Parameters
44
+ Required (ask one at a time): product, destination, start year.
45
+ Optional: origin, vessel class, frequency (day), MA period.
46
+
47
+ ## Step 4: Resolve Entity IDs
48
+ Resolve origin, destination, product via lib/entities.py. Use EntityCache().
49
+
50
+ ## Step 5: Confirm Before Executing (MANDATORY)
51
+ ```
52
+ Query: [restate]
53
+ Analysis: Seasonal Ballast Speeds
54
+ Endpoint: VoyagesTimeseries (avg_speed, avg operator)
55
+ Voyage status: ballast
56
+ Destination: [name (layer)]
57
+ Origin: [name (layer)] or "not specified"
58
+ Product: [name (layer)] (last cargo filter)
59
+ Vessel class: [class] or "all"
60
+ Year range: [start_year] to [current_year]
61
+ Frequency: [day/month]
62
+ Moving average: [period or "none"]
63
+
64
+ Confirm or adjust?
65
+ ```
66
+ Use AskUserQuestion:
67
+ - Question: "Does this look correct?"
68
+ - Header: "Confirm"
69
+ - Options:
70
+ 1. Label: "Confirm", Description: "Run the analysis as configured"
71
+ 2. Label: "Adjust", Description: "Change one or more parameters before running"
72
+
73
+ ## Step 6: Generate & Execute Code Artifact
74
+ ```python
75
+ """Vortexa Seasonal Ballast Speeds: {description}
76
+ Generated by /vortexa:workflows (Seasonal Ballast Speeds)
77
+ """
78
+ import os, sys, multiprocessing
79
+ from datetime import datetime
80
+ from dotenv import load_dotenv
81
+
82
+ if sys.platform == "darwin" and sys.version_info >= (3, 13):
83
+ multiprocessing.set_start_method("fork", force=True)
84
+ load_dotenv(override=True)
85
+ sys.path.insert(0, os.path.expanduser("~/.claude/vortexa"))
86
+
87
+ from lib.entities import resolve_geography, resolve_product, EntityCache
88
+ from lib.ballast_speeds import complete_seasonal_voyages
89
+ from lib.visualization import seasonal_chart
90
+
91
+ cache = EntityCache()
92
+ {entity_resolution_code}
93
+
94
+ seasonal_df = complete_seasonal_voyages(
95
+ start_y={start_year}, start_m=1, start_d=1,
96
+ destination_ids=[{destination_ids}],
97
+ origin_ids={origin_ids_or_None},
98
+ product_ids=[{product_id}],
99
+ vessel_classes={vessel_classes_or_None},
100
+ voyage_status="ballast",
101
+ frequency="{frequency}",
102
+ breakdown_property="avg_speed",
103
+ breakdown_unit_operator="avg",
104
+ ma_period={ma_period_or_None},
105
+ )
106
+
107
+ print(seasonal_df)
108
+
109
+ fig = seasonal_chart(seasonal_df, title="{title}", y_label="Avg Speed (knots)")
110
+ os.makedirs("output", exist_ok=True)
111
+ filepath = f"output/{slug}_ballast_speeds_{datetime.now().strftime('%Y-%m-%d')}.html"
112
+ fig.write_html(filepath, auto_open=True)
113
+ print(f"Chart saved: {filepath}")
114
+ ```
115
+
116
+ ## Step 7: Present Results
117
+ - Seasonal DataFrame summary
118
+ - Chart path
119
+ - Methodology: `VoyagesTimeseries (avg_speed, avg) | ballast | {destination} | {product} | {freq} | {start_year}-{current_year}`
120
+ - Use AskUserQuestion:
121
+ - Question: "What would you like to do next?"
122
+ - Header: "Next"
123
+ - Options:
124
+ 1. Label: "Laden speeds", Description: "Compare with laden voyage speeds"
125
+ 2. Label: "Idle analysis", Description: "Run idle vessel analysis for this fleet"
126
+ 3. Label: "Export CSV", Description: "Save speed data to CSV"
127
+ 4. Label: "Save as notebook", Description: "Convert to Jupyter notebook (.ipynb)"
128
+ 5. Label: "Done", Description: "Analysis complete"
129
+
130
+ If "Save as notebook": call `output_choice()` from `lib/notebook.py` with the code string to generate the .ipynb.
131
+
132
+ ## Error Handling
133
+ Report errors in plain English. < 3 years warning for seasonal bands.
134
+
135
+ </process>
@@ -1,7 +1,6 @@
1
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"
2
+ name: "Cargo-On-Water (COW)"
3
+ description: "Track where cargo volumes are physically located on the water, broken down by shipping region"
5
4
  allowed-tools:
6
5
  - Read
7
6
  - Edit
@@ -9,10 +8,11 @@ allowed-tools:
9
8
  - Bash
10
9
  - Glob
11
10
  - Grep
11
+ - AskUserQuestion
12
12
  ---
13
13
 
14
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.
15
+ Cargo-On-Water (COW) workflow using the CM-Authoritative methodology: CargoMovements for authoritative OOW periods and quantities, VoyagesSearchEnriched for real-time vessel location enrichment. Product-agnostic (crude, LNG, LPG, CPP, DPP). Uses `cargo_on_water_ts()` from lib/movements.py for data. Offers two visualization options: stacked area chart via `oow_area_chart()` or animated dual-basin heatmap via `cow_animated_heatmap()` from lib/visualization.py.
16
16
  </objective>
17
17
 
18
18
  <execution_context>
@@ -33,18 +33,18 @@ Read before processing the user's query:
33
33
 
34
34
  <process>
35
35
 
36
- ## CM-Authoritative OOW -- MANDATORY
36
+ ## CM-Authoritative COW -- MANDATORY
37
37
 
38
- The ONLY correct method for location-based OOW analysis is `cargo_on_water_ts()` from lib/movements.py.
38
+ The ONLY correct method for location-based cargo-on-water analysis is `cargo_on_water_ts()` from lib/movements.py.
39
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.
40
+ DO NOT use CTS `cargo_on_water_state` for this workflow. CTS shows where cargo was LOADED FROM and GOING TO (origin/destination), NOT where it currently IS on the water.
41
41
 
42
42
  The CM-Authoritative pattern:
43
43
  1. CargoMovements with `cargo_on_water_state` for authoritative OOW periods and quantities
44
44
  2. VoyagesSearchEnriched for real-time vessel location (shipping_region from events)
45
45
  3. Join on cargo_movement_id (16-char truncated) to enrich CM cargoes with voyage location
46
46
 
47
- If the generated code imports CargoTimeSeries for the OOW skill, it is WRONG.
47
+ If the generated code imports CargoTimeSeries for the COW workflow, it is WRONG.
48
48
 
49
49
  ## Step 1: Read Endpoint Context
50
50
 
@@ -54,23 +54,23 @@ Read `context/cargo-movements.md` and `context/voyages.md` (both are needed -- C
54
54
 
55
55
  Analyze $ARGUMENTS and extract:
56
56
 
57
- - **Product**: typically crude, but any product group works
57
+ - **Product**: any commodity -- crude, LNG, LPG, CPP, DPP, etc. This workflow is product-agnostic.
58
58
  - **Location layer**: `shipping_region_v2` (default), `region`, or `country`. Controls how granular the location breakdown is.
59
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)
60
+ - **Unit**: commodity defaults -- `b` for crude/CPP/DPP (absolute volume, not rate), `cbm` for LNG, `t` for LPG. See date-units.md.
61
61
  - **Top-N**: how many regions to show in chart (default 8)
62
62
  - **Frequency**: for chart display resampling -- daily (default), weekly, monthly
63
- - **Exclude intra-country**: optional, default false for OOW
63
+ - **Exclude intra-country**: optional, default false for COW
64
64
 
65
65
  ## Step 3: Check for Missing Parameters
66
66
 
67
- Required (ask if missing):
67
+ Required (ask one at a time if missing):
68
68
  - **Product** -- MUST be specified
69
69
  - **Time range** -- MUST be specified
70
70
 
71
71
  Optional with smart defaults (mention in confirmation):
72
72
  - **Location layer**: default `shipping_region_v2`
73
- - **Unit**: default `b` (absolute barrels for OOW volumes)
73
+ - **Unit**: commodity default (`b` for crude/CPP/DPP, `cbm` for LNG, `t` for LPG)
74
74
  - **Top-N**: default 8
75
75
  - **Frequency**: default `daily`
76
76
  - **Exclude intra-country**: default false
@@ -85,7 +85,7 @@ Resolve product ID using `lib/entities.py`:
85
85
  3. If multiple matches: present top 3 candidates. Ask user to pick. NEVER auto-correct.
86
86
  4. If zero matches: tell user the term wasn't found.
87
87
 
88
- No geography resolution needed -- OOW covers global flows; the location layer is applied during the CM+VSE join.
88
+ No geography resolution needed -- COW covers global flows; the location layer is applied during the CM+VSE join.
89
89
 
90
90
  Use `EntityCache()` from `lib/entities.py` for the session.
91
91
 
@@ -95,43 +95,62 @@ Present the complete parameter set:
95
95
 
96
96
  ```
97
97
  Query: [restate the user's question]
98
- Analysis: Oil-on-water by region (CM-Authoritative)
98
+ Analysis: Cargo-On-Water by region (CM-Authoritative)
99
99
  Data: CargoMovements (OOW periods) + VoyagesSearchEnriched (location)
100
100
  Product: [name (layer)]
101
101
  Time: [start datetime] -> [end datetime]
102
102
  Location level: [shipping_region_v2 / region / country]
103
- Unit: [unit] (absolute volume)
104
- Chart: Stacked area, Top [N] regions + Other
103
+ Unit: [unit] ([commodity default reason])
104
+ Chart: To be selected after data generation
105
105
  Display frequency: [daily/weekly/monthly]
106
+ Top-N: [N] regions
106
107
 
107
108
  Note: This query calls both CM and VSE endpoints -- may take 1-3 minutes for large date ranges.
108
109
 
109
110
  Confirm or adjust?
110
111
  ```
111
112
 
112
- NEVER execute without confirmation. Wait for user response.
113
+ Use AskUserQuestion:
114
+ - Question: "Does this look correct?"
115
+ - Header: "Confirm"
116
+ - Options:
117
+ 1. Label: "Confirm", Description: "Run the cargo-on-water analysis"
118
+ 2. Label: "Adjust filters", Description: "Change product, time range, or other parameters"
119
+
120
+ NEVER execute without confirmation. NEVER require the user to type "confirm" -- always use AskUserQuestion with clickable options.
113
121
 
114
122
  ## Step 6: Generate & Execute Code Artifact
115
123
 
116
- Generate a self-contained .py file using the data -> viz -> save pipeline:
124
+ Generate a self-contained .py file using the data -> viz -> save pipeline.
125
+
126
+ **Python command:** Use `python3` to execute generated scripts (macOS/Linux). On Windows, `python` is standard. If `python3` fails, fall back to `python`.
127
+
128
+ **Environment loading:** Always use `load_dotenv(override=True)` so the `.env` file takes precedence over any stale environment variables.
117
129
 
118
130
  ```python
119
- """Vortexa OOW: {description}
120
- Generated by /vortexa:oow
131
+ """Vortexa COW: {description}
132
+ Generated by /vortexa:workflows (Cargo-On-Water)
121
133
  Date: {date}
122
134
  """
123
- from datetime import datetime
124
135
  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
136
+ import sys
137
+ import multiprocessing
138
+ from datetime import datetime
139
+ from dotenv import load_dotenv
140
+
141
+ if sys.platform == "darwin" and sys.version_info >= (3, 13):
142
+ multiprocessing.set_start_method("fork", force=True)
143
+
144
+ load_dotenv(override=True)
145
+
146
+ sys.path.insert(0, os.path.expanduser("~/.claude/vortexa"))
147
+ from lib.entities import resolve_product, EntityCache
148
+ from lib.movements import cargo_on_water_ts
149
+ from lib.visualization import oow_area_chart, cow_animated_heatmap
129
150
 
130
- # Entity Resolution
131
151
  cache = EntityCache()
132
152
  {product_resolution_code}
133
153
 
134
- # OOW by Region (CM-Authoritative)
135
154
  ts_wide = cargo_on_water_ts(
136
155
  time_min=datetime({time_min}),
137
156
  time_max=datetime({time_max}),
@@ -143,21 +162,37 @@ ts_wide = cargo_on_water_ts(
143
162
  )
144
163
 
145
164
  print(ts_wide)
165
+ ```
166
+
167
+ After the data is generated and printed, present the user with a visualization choice using AskUserQuestion:
146
168
 
147
- # Chart
169
+ - Question: "Which visualization would you like?"
170
+ - Header: "Chart type"
171
+ - Options:
172
+ 1. Label: "Stacked Area Chart", Description: "Cleaner timeseries view -- top N regions stacked over time"
173
+ 2. Label: "Animated Heatmap", Description: "Dual-basin Atlantic/Pacific grid with day-over-day changes, play/pause, date slider"
174
+
175
+ Then append the user's chosen visualization code to the script and run it:
176
+
177
+ **If Stacked Area Chart:**
178
+ ```python
148
179
  fig = oow_area_chart(ts_wide, freq="{freq}", top_n={top_n}, title="{title}")
149
180
 
150
181
  os.makedirs("output", exist_ok=True)
151
- filepath = f"output/{slug}_oow_{datetime.now().strftime('%Y-%m-%d')}.html"
182
+ filepath = f"output/{slug}_cow_area_{datetime.now().strftime('%Y-%m-%d')}.html"
152
183
  fig.write_html(filepath, auto_open=True)
153
184
  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
185
  ```
158
186
 
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.
187
+ **If Animated Heatmap:**
188
+ ```python
189
+ fig = cow_animated_heatmap(ts_wide, title="{title}", speed_ms=200, unit_label="{unit}")
190
+
191
+ os.makedirs("output", exist_ok=True)
192
+ filepath = f"output/{slug}_cow_heatmap_{datetime.now().strftime('%Y-%m-%d')}.html"
193
+ fig.write_html(filepath, auto_open=True)
194
+ print(f"Chart saved: {filepath}")
195
+ ```
161
196
 
162
197
  ## Step 7: Present Results
163
198
 
@@ -166,11 +201,21 @@ Run the code to get results.
166
201
  - If capped: "Showing first N of M rows"
167
202
  - One-line methodology footnote using CONFIRMED parameters:
168
203
  ```
169
- Methodology: CM-Authoritative OOW | CargoMovements (cargo_on_water_state) + VoyagesSearchEnriched (location) | {product} | {unit} | {location_layer} | Top {N}
204
+ Methodology: CM-Authoritative COW | CargoMovements (cargo_on_water_state) + VoyagesSearchEnriched (location) | {product} | {unit} | {location_layer} | Top {N}
170
205
  ```
171
206
  Use human-readable names for entity filters (not hex IDs).
172
207
  - File path to chart HTML
173
- - Offer: "Export to CSV?" and "Want a summary?"
208
+ - Use AskUserQuestion:
209
+ - Question: "What would you like to do with these results?"
210
+ - Header: "Next step"
211
+ - Options:
212
+ 1. Label: "Export to CSV", Description: "Save COW data to CSV"
213
+ 2. Label: "Switch visualization", Description: "Try the other chart type (area/heatmap)"
214
+ 3. Label: "Show summary", Description: "Summarize key COW findings by region"
215
+ 4. Label: "Save as notebook", Description: "Convert to Jupyter notebook (.ipynb)"
216
+ 5. Label: "Done", Description: "Analysis complete"
217
+
218
+ If "Save as notebook": call `output_choice()` from `lib/notebook.py` with the code string to generate the .ipynb.
174
219
 
175
220
  ## Error Handling
176
221