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.
- package/CHANGELOG.md +30 -2
- package/README.md +65 -0
- package/VERSION +1 -1
- package/bin/setup.js +62 -1
- package/commands/vortexa/_check-setup.md +1 -1
- package/commands/vortexa/_wf-ballast-speeds.md +135 -0
- package/commands/vortexa/{oow.md → _wf-cow.md} +82 -37
- package/commands/vortexa/_wf-diversions.md +158 -0
- package/commands/vortexa/_wf-fleet-distribution.md +172 -0
- package/commands/vortexa/_wf-floating-storage.md +298 -0
- package/commands/vortexa/_wf-idle-vessels.md +151 -0
- package/commands/vortexa/_wf-importers-exporters.md +20 -0
- package/commands/vortexa/_wf-net-flows.md +196 -0
- package/commands/vortexa/_wf-post-ballast.md +166 -0
- package/commands/vortexa/_wf-pricing-voyage.md +10 -0
- package/commands/vortexa/_wf-regional-freight.md +10 -0
- package/commands/vortexa/_wf-top-movers.md +197 -0
- package/commands/vortexa/_wf-top-ports.md +196 -0
- package/commands/vortexa/_wf-vessel-availability.md +14 -0
- package/commands/vortexa/breakdown.md +55 -13
- package/commands/vortexa/cargo-flows.md +45 -14
- package/commands/vortexa/compare.md +41 -10
- package/commands/vortexa/custom.md +186 -120
- package/commands/vortexa/explain.md +9 -5
- package/commands/vortexa/init.md +1 -1
- package/commands/vortexa/seasonal.md +43 -8
- package/commands/vortexa/voyages.md +67 -14
- package/commands/vortexa/workflows.md +134 -0
- package/context/guardrails.md +2 -2
- package/lib/aliases.json +13 -3
- package/lib/api.py +1 -1
- package/lib/ballast_speeds.py +110 -0
- package/lib/diversions.py +347 -0
- package/lib/entities.py +34 -0
- package/lib/fleet_distribution.py +186 -0
- package/lib/floating_storage.py +171 -0
- package/lib/freight_pricing.py +17 -0
- package/lib/idle_vessels.py +233 -0
- package/lib/net_flows.py +145 -0
- package/lib/notebook.py +63 -0
- package/lib/seasonal.py +79 -88
- package/lib/top_movers.py +210 -0
- package/lib/top_ports.py +121 -0
- package/lib/utils.py +17 -1
- package/lib/vessel_availability_ts.py +17 -0
- package/lib/visualization.py +415 -14
- package/lib/voyages.py +35 -14
- 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
|
|
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
|
|
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.
|
|
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(`\
|
|
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
|
|
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:
|
|
3
|
-
description: "
|
|
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
|
-
|
|
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
|
|
36
|
+
## CM-Authoritative COW -- MANDATORY
|
|
37
37
|
|
|
38
|
-
The ONLY correct method for location-based
|
|
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
|
|
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
|
|
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**:
|
|
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
|
|
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
|
|
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`
|
|
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 --
|
|
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:
|
|
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] (
|
|
104
|
-
Chart:
|
|
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
|
-
|
|
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
|
|
120
|
-
Generated by /vortexa:
|
|
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
|
|
126
|
-
|
|
127
|
-
from
|
|
128
|
-
from
|
|
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
|
-
|
|
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}
|
|
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
|
-
|
|
160
|
-
|
|
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
|
|
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
|
-
-
|
|
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
|
|