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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.0] - 2026-02-27
|
|
4
|
+
|
|
5
|
+
### Initial Release
|
|
6
|
+
|
|
7
|
+
**9 Skills:**
|
|
8
|
+
- `/vortexa:init` -- Environment setup (API key, Python validation)
|
|
9
|
+
- `/vortexa:cargo-flows` -- Cargo flow queries via CargoTimeSeries and CargoMovements
|
|
10
|
+
- `/vortexa:breakdown` -- Dimensional breakdowns with stacked bar charts
|
|
11
|
+
- `/vortexa:voyages` -- Vessel voyage queries via VoyagesSearchEnriched and VoyagesTimeseries
|
|
12
|
+
- `/vortexa:custom` -- Open-ended Vortexa API queries and code improvement
|
|
13
|
+
- `/vortexa:explain` -- Plain-English Vortexa data model explanations
|
|
14
|
+
- `/vortexa:seasonal` -- Multi-year seasonal pattern analysis with min/max/avg bands
|
|
15
|
+
- `/vortexa:oow` -- Oil-on-water breakdown by region (CM-Authoritative methodology)
|
|
16
|
+
- `/vortexa:compare` -- Period-over-period and region-over-region comparison analytics
|
|
17
|
+
|
|
18
|
+
**Foundation:**
|
|
19
|
+
- 6 context documents covering Cargo Movements, Voyages, Reference Endpoints, guardrails, entity resolution, date/unit handling
|
|
20
|
+
- 11 Python library modules with entity resolution, timeseries, movements, voyages, seasonal analysis, visualization
|
|
21
|
+
- Vortexa dark Plotly theme with branded colour palette
|
|
22
|
+
- Endpoint template pattern for adding new API endpoints
|
|
23
|
+
|
|
24
|
+
**Distribution:**
|
|
25
|
+
- One-command install: `npx @vortexa/claude-skills setup`
|
|
26
|
+
- Per-directory init: `/vortexa:init` for environment setup
|
|
27
|
+
- Cross-platform: Mac, Windows, Linux
|
|
28
|
+
- Automatic update: `npx @vortexa/claude-skills update`
|
package/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.0
|
package/bin/.gitkeep
ADDED
|
File without changes
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
|
|
8
|
+
const packageDir = path.resolve(__dirname, '..');
|
|
9
|
+
const version = fs.readFileSync(path.join(packageDir, 'VERSION'), 'utf8').trim();
|
|
10
|
+
|
|
11
|
+
function getTargetDir() {
|
|
12
|
+
const home = os.homedir();
|
|
13
|
+
if (!home) {
|
|
14
|
+
console.error('Could not determine home directory. Set the HOME environment variable and try again.');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
return path.join(home, '.claude');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ensureDir(dir) {
|
|
21
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hashFile(filepath) {
|
|
25
|
+
const content = fs.readFileSync(filepath);
|
|
26
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function rewritePaths(content, targetDir) {
|
|
30
|
+
const vortexaDir = path.join(targetDir, 'vortexa');
|
|
31
|
+
const commandsDir = path.join(targetDir, 'commands', 'vortexa');
|
|
32
|
+
|
|
33
|
+
content = content.replace(/@context\//g, `@${vortexaDir}/context/`);
|
|
34
|
+
content = content.replace(/@commands\/vortexa\//g, `@${commandsDir}/`);
|
|
35
|
+
|
|
36
|
+
content = content.replace(/`context\//g, `\`${vortexaDir}/context/`);
|
|
37
|
+
|
|
38
|
+
content = content.replace(/sys\.path\.insert\(0,\s*"lib"\)/g,
|
|
39
|
+
'sys.path.insert(0, os.path.expanduser("~/.claude/vortexa"))');
|
|
40
|
+
|
|
41
|
+
content = content.replace(/`templates\//g, `\`${vortexaDir}/templates/`);
|
|
42
|
+
|
|
43
|
+
return content;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function copyDir(srcDir, destDir, options = {}) {
|
|
47
|
+
const { rewrite = false, targetDir = null } = options;
|
|
48
|
+
const installedFiles = [];
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(srcDir)) return installedFiles;
|
|
51
|
+
|
|
52
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (entry.name.startsWith('.') && entry.name !== '.env.template') continue;
|
|
55
|
+
if (entry.name === '__pycache__') continue;
|
|
56
|
+
|
|
57
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
58
|
+
const destPath = path.join(destDir, entry.name);
|
|
59
|
+
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
ensureDir(destPath);
|
|
62
|
+
const subFiles = copyDir(srcPath, destPath, options);
|
|
63
|
+
installedFiles.push(...subFiles);
|
|
64
|
+
} else {
|
|
65
|
+
let content = fs.readFileSync(srcPath);
|
|
66
|
+
|
|
67
|
+
if (rewrite && targetDir && entry.name.endsWith('.md')) {
|
|
68
|
+
content = rewritePaths(content.toString('utf8'), targetDir);
|
|
69
|
+
fs.writeFileSync(destPath, content, 'utf8');
|
|
70
|
+
} else {
|
|
71
|
+
fs.writeFileSync(destPath, content);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
installedFiles.push(destPath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return installedFiles;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildManifest(files, ver, targetDir) {
|
|
82
|
+
const fileHashes = {};
|
|
83
|
+
for (const filepath of files) {
|
|
84
|
+
const relative = path.relative(targetDir, filepath);
|
|
85
|
+
fileHashes[relative] = hashFile(filepath);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
version: ver,
|
|
90
|
+
installed_at: new Date().toISOString(),
|
|
91
|
+
file_count: files.length,
|
|
92
|
+
files: fileHashes
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readSettings(settingsPath) {
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
99
|
+
} catch {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function writeSettings(settingsPath, settings) {
|
|
105
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function mergeSkillRegistrations(targetDir) {
|
|
109
|
+
const settingsPath = path.join(targetDir, 'settings.json');
|
|
110
|
+
const settings = readSettings(settingsPath);
|
|
111
|
+
|
|
112
|
+
if (!settings.customCommands) {
|
|
113
|
+
settings.customCommands = {};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const commandsDir = path.join(targetDir, 'commands', 'vortexa');
|
|
117
|
+
if (!fs.existsSync(commandsDir)) return;
|
|
118
|
+
|
|
119
|
+
const skills = fs.readdirSync(commandsDir)
|
|
120
|
+
.filter(f => f.endsWith('.md') && !f.startsWith('_'))
|
|
121
|
+
.map(f => {
|
|
122
|
+
const name = f.replace('.md', '');
|
|
123
|
+
return { name: `vortexa:${name}`, file: f };
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
let added = 0;
|
|
127
|
+
for (const skill of skills) {
|
|
128
|
+
const key = skill.name;
|
|
129
|
+
const skillPath = path.join(commandsDir, skill.file);
|
|
130
|
+
|
|
131
|
+
if (!settings.customCommands[key]) {
|
|
132
|
+
settings.customCommands[key] = skillPath;
|
|
133
|
+
added++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
writeSettings(settingsPath, settings);
|
|
138
|
+
return { total: skills.length, added };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readManifest(manifestPath) {
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function countFilesByType(files) {
|
|
150
|
+
const counts = { md: 0, py: 0, json: 0, other: 0 };
|
|
151
|
+
for (const f of files) {
|
|
152
|
+
const ext = path.extname(f).slice(1);
|
|
153
|
+
if (ext in counts) {
|
|
154
|
+
counts[ext]++;
|
|
155
|
+
} else {
|
|
156
|
+
counts.other++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return counts;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function setup(targetDir) {
|
|
163
|
+
const commandsSrc = path.join(packageDir, 'commands', 'vortexa');
|
|
164
|
+
const contextSrc = path.join(packageDir, 'context');
|
|
165
|
+
const libSrc = path.join(packageDir, 'lib');
|
|
166
|
+
const templatesSrc = path.join(packageDir, 'templates');
|
|
167
|
+
const versionSrc = path.join(packageDir, 'VERSION');
|
|
168
|
+
const changelogSrc = path.join(packageDir, 'CHANGELOG.md');
|
|
169
|
+
|
|
170
|
+
const commandsDest = path.join(targetDir, 'commands', 'vortexa');
|
|
171
|
+
const vortexaDir = path.join(targetDir, 'vortexa');
|
|
172
|
+
const contextDest = path.join(vortexaDir, 'context');
|
|
173
|
+
const libDest = path.join(vortexaDir, 'lib');
|
|
174
|
+
const templatesDest = path.join(vortexaDir, 'templates');
|
|
175
|
+
|
|
176
|
+
ensureDir(targetDir);
|
|
177
|
+
ensureDir(commandsDest);
|
|
178
|
+
ensureDir(vortexaDir);
|
|
179
|
+
ensureDir(contextDest);
|
|
180
|
+
ensureDir(libDest);
|
|
181
|
+
ensureDir(templatesDest);
|
|
182
|
+
|
|
183
|
+
const allFiles = [];
|
|
184
|
+
|
|
185
|
+
const commandFiles = copyDir(commandsSrc, commandsDest, { rewrite: true, targetDir });
|
|
186
|
+
allFiles.push(...commandFiles);
|
|
187
|
+
|
|
188
|
+
const contextFiles = copyDir(contextSrc, contextDest);
|
|
189
|
+
allFiles.push(...contextFiles);
|
|
190
|
+
|
|
191
|
+
const libFiles = copyDir(libSrc, libDest);
|
|
192
|
+
allFiles.push(...libFiles);
|
|
193
|
+
|
|
194
|
+
const templateFiles = copyDir(templatesSrc, templatesDest);
|
|
195
|
+
allFiles.push(...templateFiles);
|
|
196
|
+
|
|
197
|
+
if (fs.existsSync(versionSrc)) {
|
|
198
|
+
const versionDest = path.join(vortexaDir, 'VERSION');
|
|
199
|
+
fs.copyFileSync(versionSrc, versionDest);
|
|
200
|
+
allFiles.push(versionDest);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (fs.existsSync(changelogSrc)) {
|
|
204
|
+
const changelogDest = path.join(vortexaDir, 'CHANGELOG.md');
|
|
205
|
+
fs.copyFileSync(changelogSrc, changelogDest);
|
|
206
|
+
allFiles.push(changelogDest);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const manifest = buildManifest(allFiles, version, targetDir);
|
|
210
|
+
const manifestPath = path.join(targetDir, 'vortexa-manifest.json');
|
|
211
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
212
|
+
|
|
213
|
+
const skillReg = mergeSkillRegistrations(targetDir);
|
|
214
|
+
|
|
215
|
+
const commandCount = commandFiles.length;
|
|
216
|
+
const contextCount = contextFiles.length;
|
|
217
|
+
const libCount = libFiles.length;
|
|
218
|
+
const templateCount = templateFiles.length;
|
|
219
|
+
|
|
220
|
+
console.log(`\nVortexa Claude Skills v${version} installed successfully!\n`);
|
|
221
|
+
console.log('Files installed:');
|
|
222
|
+
console.log(` ~/.claude/commands/vortexa/ (${commandCount} skill files)`);
|
|
223
|
+
console.log(` ~/.claude/vortexa/context/ (${contextCount} context docs)`);
|
|
224
|
+
console.log(` ~/.claude/vortexa/lib/ (${libCount} Python modules)`);
|
|
225
|
+
console.log(` ~/.claude/vortexa/templates/ (${templateCount} template${templateCount !== 1 ? 's' : ''})`);
|
|
226
|
+
if (skillReg) {
|
|
227
|
+
console.log(` ~/.claude/settings.json (${skillReg.total} skills registered${skillReg.added > 0 ? `, ${skillReg.added} new` : ''})`);
|
|
228
|
+
}
|
|
229
|
+
console.log('');
|
|
230
|
+
console.log('Next steps:');
|
|
231
|
+
console.log(' 1. Open Claude Code in any project directory');
|
|
232
|
+
console.log(' 2. Run /vortexa:init to set up your environment');
|
|
233
|
+
console.log(' 3. Run /vortexa:cargo-flows to start querying!');
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function update(targetDir) {
|
|
238
|
+
const manifestPath = path.join(targetDir, 'vortexa-manifest.json');
|
|
239
|
+
const existing = readManifest(manifestPath);
|
|
240
|
+
|
|
241
|
+
if (!existing) {
|
|
242
|
+
console.log('No existing installation found. Running setup instead.\n');
|
|
243
|
+
setup(targetDir);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const installedVersion = existing.version;
|
|
248
|
+
|
|
249
|
+
if (installedVersion === version) {
|
|
250
|
+
console.log(`Already up to date (v${version}).`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log(`Updating from v${installedVersion} to v${version}...\n`);
|
|
255
|
+
|
|
256
|
+
setup(targetDir);
|
|
257
|
+
|
|
258
|
+
const changelogPath = path.join(packageDir, 'CHANGELOG.md');
|
|
259
|
+
if (fs.existsSync(changelogPath)) {
|
|
260
|
+
const changelog = fs.readFileSync(changelogPath, 'utf8');
|
|
261
|
+
const versionHeader = new RegExp(`^##\\s+.*${version.replace(/\./g, '\\.')}`, 'm');
|
|
262
|
+
const match = changelog.match(versionHeader);
|
|
263
|
+
|
|
264
|
+
if (match) {
|
|
265
|
+
const start = match.index;
|
|
266
|
+
const nextHeader = changelog.indexOf('\n## ', start + 1);
|
|
267
|
+
const section = nextHeader !== -1
|
|
268
|
+
? changelog.slice(start, nextHeader).trim()
|
|
269
|
+
: changelog.slice(start).trim();
|
|
270
|
+
|
|
271
|
+
console.log('\nChangelog:');
|
|
272
|
+
console.log(section);
|
|
273
|
+
console.log('');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log(`Updated from v${installedVersion} to v${version}!`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function main() {
|
|
281
|
+
const subcommand = process.argv[2] || 'setup';
|
|
282
|
+
const targetDir = getTargetDir();
|
|
283
|
+
|
|
284
|
+
switch (subcommand) {
|
|
285
|
+
case 'setup':
|
|
286
|
+
setup(targetDir);
|
|
287
|
+
break;
|
|
288
|
+
case 'update':
|
|
289
|
+
update(targetDir);
|
|
290
|
+
break;
|
|
291
|
+
default:
|
|
292
|
+
setup(targetDir);
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
main();
|
|
299
|
+
} catch (err) {
|
|
300
|
+
console.error(`Error: ${err.message}`);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Setup Detection
|
|
2
|
+
|
|
3
|
+
Before executing any Vortexa analytics command, verify both the global install and local environment:
|
|
4
|
+
|
|
5
|
+
1. Check that `~/.claude/vortexa/VERSION` exists. If not: "Vortexa skills package is not installed. Run: npx @vortexa/claude-skills setup"
|
|
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
|
+
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
|
+
|
|
9
|
+
If any check fails, stop execution and display the relevant message. Do not attempt to run the query.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
# SKILL TEMPLATE -- Copy this file and customize for each new /vortexa: data skill
|
|
3
|
+
# Rename to {skill-name}.md (no underscore prefix) to register as /vortexa:{skill-name}
|
|
4
|
+
name: vortexa:{skill-name}
|
|
5
|
+
description: "{One-line description of what this skill does}"
|
|
6
|
+
argument-hint: "{Example natural language query}"
|
|
7
|
+
allowed-tools:
|
|
8
|
+
- Read
|
|
9
|
+
- Edit
|
|
10
|
+
- Write
|
|
11
|
+
- Bash
|
|
12
|
+
- Glob
|
|
13
|
+
- Grep
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<objective>
|
|
17
|
+
{What this skill does in 1-2 sentences. Be specific about which Vortexa endpoint(s) it uses.}
|
|
18
|
+
</objective>
|
|
19
|
+
|
|
20
|
+
<execution_context>
|
|
21
|
+
## Pre-loaded Context (via CLAUDE.md @imports)
|
|
22
|
+
The following are automatically available -- do NOT re-read them:
|
|
23
|
+
- context/guardrails.md -- NEVER/ALWAYS rules for all Vortexa API calls
|
|
24
|
+
- context/entity-resolution.md -- How to resolve entity names to hex IDs
|
|
25
|
+
- context/date-units.md -- Date parsing rules and unit defaults
|
|
26
|
+
|
|
27
|
+
## Required Context (Read on demand)
|
|
28
|
+
Read these files before processing the user's query:
|
|
29
|
+
- context/cargo-movements.md (for cargo flow / timeseries queries)
|
|
30
|
+
- context/voyages.md (for vessel voyage queries)
|
|
31
|
+
- context/reference-endpoints.md (for entity lookups when disambiguation needed)
|
|
32
|
+
|
|
33
|
+
Only read the endpoint doc(s) relevant to this skill.
|
|
34
|
+
</execution_context>
|
|
35
|
+
|
|
36
|
+
## Setup Check
|
|
37
|
+
@commands/vortexa/_check-setup.md
|
|
38
|
+
|
|
39
|
+
<process>
|
|
40
|
+
## 1. Parse the Query
|
|
41
|
+
Analyze $ARGUMENTS and identify:
|
|
42
|
+
- **Product**: What commodity? (crude, LNG, clean products, etc.)
|
|
43
|
+
- **Geography**: Origin and/or destination? What layer? (country, port, region)
|
|
44
|
+
- **Activity**: What movement stage? (exports=loading_end, imports=unloading_start, on water, etc.)
|
|
45
|
+
- **Time range**: What period? Apply date-units.md calendar/trailing convention
|
|
46
|
+
- **Unit**: What measurement? Apply date-units.md commodity defaults
|
|
47
|
+
- **Frequency**: Daily/weekly/monthly? Ask if not specified
|
|
48
|
+
- **Vessel filters**: Any class, flag, or size constraints?
|
|
49
|
+
- **Other**: Intra movements, confidence level, STS inclusion, etc.
|
|
50
|
+
|
|
51
|
+
## 2. Resolve Entity IDs
|
|
52
|
+
For each entity (origin, destination, product, vessel, corporate):
|
|
53
|
+
1. Identify the entity type and layer
|
|
54
|
+
2. Call the correct SDK search method (see entity-resolution.md)
|
|
55
|
+
3. If multiple matches: present disambiguation options to user
|
|
56
|
+
4. If zero matches: suggest alternatives based on fuzzy matching
|
|
57
|
+
|
|
58
|
+
## 3. Confirm Before Executing (MANDATORY)
|
|
59
|
+
Present the parsed query to the user:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Query: [restate user's natural language question]
|
|
63
|
+
Endpoint: [CargoTimeSeries / CargoMovements / VoyagesSearchEnriched / etc.]
|
|
64
|
+
Activity: [filter_activity value] ([user's term])
|
|
65
|
+
Origin: [name] ([layer])
|
|
66
|
+
Destination: [name] ([layer])
|
|
67
|
+
Product: [name] ([layer])
|
|
68
|
+
Time: [start datetime] -> [end datetime]
|
|
69
|
+
Unit: [unit] ([commodity default or user-specified])
|
|
70
|
+
Frequency: [value or "Please specify: daily/weekly/monthly?"]
|
|
71
|
+
Intra: [exclude_intra_country for exports/imports, all otherwise]
|
|
72
|
+
Additional: [any other filters applied]
|
|
73
|
+
|
|
74
|
+
Confirm or adjust?
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
NEVER execute an API call without showing this confirmation.
|
|
78
|
+
Wait for user to confirm or request adjustments.
|
|
79
|
+
|
|
80
|
+
## 4. Execute Query
|
|
81
|
+
Build and run the Python API call using confirmed parameters.
|
|
82
|
+
Use the lib/ modules for SDK interaction.
|
|
83
|
+
|
|
84
|
+
## 5. Present Results
|
|
85
|
+
|
|
86
|
+
### Data Variant (default for query skills)
|
|
87
|
+
1. Display DataFrame with human-readable column names
|
|
88
|
+
2. Provide natural language summary of key findings (top values, trends, notable changes)
|
|
89
|
+
3. Offer: "Export to CSV?"
|
|
90
|
+
|
|
91
|
+
### Analytics Variant (for analysis skills)
|
|
92
|
+
1. Display Plotly chart
|
|
93
|
+
2. Display supporting DataFrame
|
|
94
|
+
3. Provide natural language summary
|
|
95
|
+
4. Offer: "Export to CSV?"
|
|
96
|
+
|
|
97
|
+
### Knowledge Variant (for /vortexa:explain)
|
|
98
|
+
1. Provide text explanation only
|
|
99
|
+
2. No data/charts
|
|
100
|
+
</process>
|