faostat-skills 0.2.2 → 0.2.4

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.
@@ -9,7 +9,7 @@
9
9
  "source": {
10
10
  "source": "npm",
11
11
  "package": "faostat-skills",
12
- "version": "^0.2.0"
12
+ "version": "0.2.4"
13
13
  },
14
14
  "description": "AI skills for analyzing UN FAOSTAT food & agriculture data — country profiles, trade analysis, climate assessments, commodity briefings, maps, infographics, and more."
15
15
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "faostat-skills",
3
3
  "description": "Analysis skills for UN FAOSTAT food and agriculture data. Country profiles, commodity briefings, trade analysis, climate assessments, and data visualizations. Works with any AI assistant that supports the SKILL.md format.",
4
- "version": "0.1.0",
4
+ "version": "0.2.4",
5
5
  "author": {
6
6
  "name": "Griffiths Obli-Laryea"
7
7
  },
package/README.md CHANGED
@@ -17,7 +17,10 @@ AI-powered analysis skills for the [UN FAOSTAT](https://www.fao.org/faostat/en/#
17
17
  /plugin marketplace add berba-q/faostat-skills
18
18
 
19
19
  # 2. Install
20
- claude plugin install faostat-skills@faostat
20
+ /plugin install faostat-skills@faostat
21
+
22
+ # 3. Activate
23
+ /reload-plugins
21
24
  ```
22
25
 
23
26
  **OpenAI Codex**
@@ -61,10 +64,7 @@ Claude Code uses a marketplace model. Add this repo as a marketplace source once
61
64
  claude plugin install faostat-skills@faostat
62
65
  ```
63
66
 
64
- To update to a new version later:
65
- ```bash
66
- claude plugin update faostat-skills@faostat
67
- ```
67
+ Updates are automatic Claude Code checks for new versions at startup and updates in the background. Run `/reload-plugins` to activate the latest version in your current session.
68
68
 
69
69
  ### OpenAI Codex
70
70
 
@@ -168,30 +168,38 @@ Key domains used by these skills:
168
168
  ```
169
169
  FAOSTAT Skills (this repo) FAOSTAT MCP Server (separate)
170
170
  ┌────────────────────────┐ ┌────────────────────────┐
171
- 9 platform-agnostic │ uses │ 21 MCP tools │
171
+ 14 platform-agnostic │ uses │ 21 MCP tools │
172
172
  │ SKILL.md workflows │────────→ │ Published on PyPI │
173
- │ │ │ v1.2.2 · stable
173
+ │ │ │ faostat-mcp · stable
174
174
  └────────────────────────┘ └────────────────────────┘
175
175
  ```
176
176
 
177
+ **Two distribution channels — keep both in sync:**
178
+ - **Claude Code**: served from GitHub (`berba-q/faostat-skills`) via the marketplace. Push to GitHub → users get the update automatically at next startup.
179
+ - **npm / Codex**: served from npm (`faostat-skills`). Run `npm publish` → Codex users update with `npm install -g faostat-skills@latest`.
180
+
177
181
  Skills orchestrate the MCP server's tools into multi-step analysis workflows. They encode FAOSTAT domain expertise so users can easily interact with the FAOSTAT data.
178
182
 
179
183
  ## Project Structure
180
184
 
181
185
  ```
182
186
  faostat-skills/
183
- ├── skills/ ← Core: platform-agnostic SKILL.md files
184
- │ ├── country-profile/
187
+ ├── skills/ ← Core: 14 platform-agnostic SKILL.md files
188
+ │ ├── country-profile/ ← Tier 1: analysis
185
189
  │ ├── compare/
186
190
  │ ├── commodity/
187
191
  │ ├── trade/
188
192
  │ ├── trends/
189
193
  │ ├── climate/
190
- │ ├── viz/
194
+ │ ├── explore/
195
+ │ ├── viz/ ← Tier 2: outputs
196
+ │ ├── map/
197
+ │ ├── infographic/
191
198
  │ ├── story/
192
- └── explore/
199
+ ├── analytical-brief/
200
+ │ ├── scientific-paper/
201
+ │ └── export-dataset/ ← Tier 3: utilities
193
202
  ├── .claude-plugin/ ← Claude Code packaging
194
- ├── .agents/skills/ ← OpenAI Codex packaging (symlink)
195
203
  ├── commands/ ← Claude Code slash commands
196
204
  ├── README.md
197
205
  └── LICENSE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "faostat-skills",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Analysis skills for UN FAOSTAT food and agriculture data. Country profiles, commodity briefings, trade analysis, climate assessments, and data visualizations.",
5
5
  "author": "Griffiths Obli-Laryea",
6
6
  "license": "MIT",
@@ -30,5 +30,8 @@
30
30
  ".claude-plugin/",
31
31
  "README.md",
32
32
  "LICENSE"
33
- ]
33
+ ],
34
+ "scripts": {
35
+ "version": "node scripts/sync-version.mjs && git add .claude-plugin/plugin.json marketplace.json .claude-plugin/marketplace.json"
36
+ }
34
37
  }
@@ -30,6 +30,7 @@ All 6 invariants from the FAOSTAT skill suite apply unchanged:
30
30
  4. **TCL for national trade aggregates, TM only for partner breakdowns.** Never sum TM rows to reconstruct national totals.
31
31
  5. **China composite default (user preference, Apr 2026).** Default to composite `China` (area 351) for country rankings, top-N lists and country-level analysis. Offer `China, mainland` (41) as an opt-in — do not substitute 41 unless the user asks for 41 explicitly. Flag the choice in the Methodology sheet, and include the caveat whenever a China number is quoted: FAOSTAT's own publications default to 41, so a brief using 351 will show a marginally larger "China" value than the FAO data-portal default. (Map figures inside the brief are an exception: they use the disaggregation path — area 41 on the CHN polygon with HKG / MAC / TWN rendered separately — because choropleths cannot sensibly render 351. See the map skill.)
32
32
  6. **`faostat_get_rankings` HTTP-500 fallback.** Reconstruct rankings from `faostat_get_data` + client-side sort; document fallback in the Methodology sheet.
33
+ 7. **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
33
34
 
34
35
  ## Workflow
35
36
 
@@ -64,7 +65,7 @@ For each figure and table, plan a specific `faostat_get_data` call. Log every ca
64
65
  - Use comma-separated year lists.
65
66
  - Use `response_format='compact'`.
66
67
  - Pass `show_unit=True`.
67
- - For trade aggregates, use **TCL** elements (2610 import quantity, 2910 export quantity, 2612 import value, 2912 export value) not TM sums.
68
+ - For trade aggregates, use **TCL** elements — not TM sums. Resolve element codes at runtime: e.g. `faostat_search_codes(domain_code='TCL', dimension_id='element', query='import quantity')` e.g. 2610; similarly for export quantity (~2910), import value (~2612), export value (~2912). Treat any numeric code here as a verified hint to validate against, not a value to pass directly.
68
69
  - For region figures, pull the 6 continental area codes (Africa 5100, Americas 5200, Asia 5300, Europe 5400, Oceania 5500, World 5000) directly — don't compute them client-side.
69
70
  - For top-N country figures, **don't** rely on `faostat_get_rankings`; pull `faostat_get_data` for the domain and element (no area filter) and sort client-side, so an HTTP-500 from `get_rankings` doesn't block the brief.
70
71
  - For China in country rankings and country-level analysis, keep composite `China` (area 351) and drop 41 (mainland), unless the user opted into 41 explicitly. (Map figures inside the brief use the disaggregation path — area 41 + HKG 96 + MAC 128 + TWN 214 — because a choropleth cannot render 351 sensibly.)
@@ -28,13 +28,13 @@ Before starting, verify that the FAOSTAT MCP tools are available: `faostat_searc
28
28
 
29
29
  ## Element Filter Reference
30
30
 
31
- Every `faostat_get_data` call below specifies an `element` filter. Without one, emissions domains return dozens of sub-elements per year and the payload explodes. If an element code is unfamiliar, resolve via `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='...')`.
31
+ Every `faostat_get_data` call below specifies an `element` filter. Without one, emissions domains return dozens of sub-elements per year and the payload explodes.
32
+
33
+ **Rule:** For GT, GF, ET, RL, RFN, RP — the codes below are stable and may be used directly. For **EM domain elements, always resolve at runtime** via `faostat_search_codes(domain_code='EM', dimension_id='element', query='<metric>')` — never hardcode them. The verified codes shown are reference hints only.
32
34
 
33
35
  | Metric | Domain | Filter code |
34
36
  |--------|--------|-------------|
35
37
  | Emissions (CO2eq) | GT | `724313` |
36
- | Share in total emissions (%) | EM | `7231` |
37
- | Emissions per capita (kg CO2eq/person) | EM | `723112` |
38
38
  | Temperature change (°C) | ET | `7271` |
39
39
  | Net forest conversion (area, ha) | GF | `6646` |
40
40
  | Forest land emissions (CO2eq) | GF | `724313` |
@@ -43,6 +43,23 @@ Every `faostat_get_data` call below specifies an `element` filter. Without one,
43
43
  | N fertilizer use (tonnes) | RFN | `5157` |
44
44
  | Pesticide use (tonnes) | RP | `5157` |
45
45
 
46
+ **EM domain — resolve at runtime (hints only):**
47
+
48
+ | Metric | Element query | Verified code |
49
+ |--------|--------------|---------------|
50
+ | Emissions per capita | `query='per capita'` | `7279` |
51
+ | Share of agrifood in national total (CO2eq AR5) | `query='share CO2eq'` | `726313` |
52
+ | Emissions per value of agricultural production | `query='per value'` | `72791` |
53
+
54
+ **EM domain item codes (for filtering the agrifood breakdown):**
55
+
56
+ | Item | Code |
57
+ |------|------|
58
+ | Agrifood systems (broadest total) | `6518` |
59
+ | Farm gate | `6996` |
60
+ | Land-use change | `6516` |
61
+ | Pre- and post-production | `6517` |
62
+
46
63
  Year-range syntax: use explicit comma-separated lists (`year='2014,2015,...,2023'`). Colon ranges (`'2014:2023'`) have returned empty in practice.
47
64
 
48
65
  ## Sub-workflow Detection
@@ -74,12 +91,18 @@ If the question does not clearly match one sub-workflow, present the five option
74
91
  )
75
92
  ```
76
93
  Identify total agrifood emissions trend and the breakdown by source category (farm gate, land use, pre/post production — these come through as different `item` codes within GT).
77
- 4. Pull emissions indicators from the **EM** domain:
94
+ 4. Resolve EM element codes at runtime (do not hardcode):
95
+ ```
96
+ faostat_search_codes(domain_code='EM', dimension_id='element', query='per capita')
97
+ faostat_search_codes(domain_code='EM', dimension_id='element', query='share CO2eq')
98
+ ```
99
+ Use the returned codes (e.g. `7279` for per capita, `726313` for share) in the data call:
78
100
  ```
79
101
  faostat_get_data(
80
102
  domain_code='EM',
81
103
  area='<area_code>',
82
- element='7231,723112', # Share in total (%), per capita (kg CO2eq)
104
+ item='6518', # Agrifood systems total
105
+ element='<per_capita_code>,<share_code>',
83
106
  year='2019,2020,2021,2022,2023',
84
107
  response_format='compact'
85
108
  )
@@ -119,12 +142,17 @@ If the question does not clearly match one sub-workflow, present the five option
119
142
  response_format='compact'
120
143
  )
121
144
  ```
122
- 4. Pull emissions indicators from the **EM** domain for all entities:
145
+ 4. Resolve EM element codes at runtime, then pull indicators for all entities:
146
+ ```
147
+ faostat_search_codes(domain_code='EM', dimension_id='element', query='per capita')
148
+ faostat_search_codes(domain_code='EM', dimension_id='element', query='share CO2eq')
149
+ ```
123
150
  ```
124
151
  faostat_get_data(
125
152
  domain_code='EM',
126
153
  area='<code1>,<code2>,...',
127
- element='7231,723112',
154
+ item='6518', # Agrifood systems total
155
+ element='<per_capita_code>,<share_code>',
128
156
  year='2019,2020,2021,2022,2023',
129
157
  response_format='compact'
130
158
  )
@@ -213,6 +241,7 @@ If the question does not clearly match one sub-workflow, present the five option
213
241
  - **Always pass an `element` filter to `faostat_get_data`.** Emissions domains return dozens of sub-elements per year and the payload explodes without one.
214
242
  - **Always pass an explicit comma-separated `year` list.** Colon ranges like `'2014:2023'` have returned empty in practice.
215
243
  - Always include the source attribution line at the end of any output.
244
+ - **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
216
245
 
217
246
  ## Error Handling and Reliability Notes
218
247
 
@@ -75,10 +75,11 @@ faostat_get_rankings(
75
75
  **If `faostat_get_rankings` returns an HTTP 500 or other error** (this happens — the endpoint is intermittently unreliable), fall back to pulling all countries via `faostat_get_data` and sorting client-side:
76
76
 
77
77
  ```
78
+ # Resolve element at runtime: faostat_search_codes(domain_code='QCL', dimension_id='element', query='production') → e.g. 2510
78
79
  faostat_get_data(
79
80
  domain_code='QCL',
80
81
  item='<item_code>',
81
- element='2510', # FILTER code for Production
82
+ element='<resolved_production_code>',
82
83
  year='<recent_year>',
83
84
  response_format='compact',
84
85
  limit=300, # cover all reporter countries
@@ -92,6 +93,8 @@ Then sort the returned rows by value descending and take the top 10. Exclude reg
92
93
 
93
94
  **Important code-system note:** `faostat_get_rankings` uses DISPLAY element codes (Production = `5510`). `faostat_get_data` uses FILTER element codes (Production = `2510`, Area harvested = `2312`, Yield = `2413`). These are different code systems — don't mix them.
94
95
 
96
+ > Element codes above are verified hints. Resolve at runtime via `faostat_search_codes` before use.
97
+
95
98
  ### Step 4: Pull annual yield, production, and area series for the top 5
96
99
 
97
100
  For each of the top 5 producers from Step 3, pull a full 10-year annual series rather than just endpoints. This is worth the extra rows because year-to-year volatility (droughts, disease outbreaks, policy shocks) is part of the story and a 2-point comparison can hide it.
@@ -99,19 +102,24 @@ For each of the top 5 producers from Step 3, pull a full 10-year annual series r
99
102
  Three calls, each for the top-5 area codes as a comma-separated list:
100
103
 
101
104
  ```
105
+ # Resolve elements at runtime:
106
+ # faostat_search_codes(domain_code='QCL', dimension_id='element', query='production') → e.g. 2510
107
+ # faostat_search_codes(domain_code='QCL', dimension_id='element', query='area harvested') → e.g. 2312
108
+ # faostat_search_codes(domain_code='QCL', dimension_id='element', query='yield') → e.g. 2413
109
+
102
110
  # Production
103
111
  faostat_get_data(domain_code='QCL', area='<top5_area_codes>', item='<item_code>',
104
- element='2510', year='2014:2023', response_format='compact',
112
+ element='<resolved_production_code>', year='2014:2023', response_format='compact',
105
113
  limit=100, show_unit=True)
106
114
 
107
115
  # Area harvested
108
116
  faostat_get_data(domain_code='QCL', area='<top5_area_codes>', item='<item_code>',
109
- element='2312', year='2014:2023', response_format='compact',
117
+ element='<resolved_area_harvested_code>', year='2014:2023', response_format='compact',
110
118
  limit=100, show_unit=True)
111
119
 
112
120
  # Yield
113
121
  faostat_get_data(domain_code='QCL', area='<top5_area_codes>', item='<item_code>',
114
- element='2413', year='2014:2023', response_format='compact',
122
+ element='<resolved_yield_code>', year='2014:2023', response_format='compact',
115
123
  limit=100, show_unit=True)
116
124
  ```
117
125
 
@@ -122,15 +130,19 @@ Update the year window if you used a different reference year in Step 3 (aim for
122
130
  **Use the `TCL` domain** (Trade — Crops and Livestock Products) for aggregate country-level import and export totals. TCL gives you each country's total exports and total imports of the commodity as single rows — which is what you need for a top-exporters and top-importers table.
123
131
 
124
132
  ```
125
- # Top exporters by value (or switch to quantity element 5910)
133
+ # Resolve elements at runtime:
134
+ # faostat_search_codes(domain_code='TCL', dimension_id='element', query='export value') → e.g. 5922
135
+ # faostat_search_codes(domain_code='TCL', dimension_id='element', query='import value') → e.g. 5622
136
+
137
+ # Top exporters by value (or switch to quantity element resolved via faostat_search_codes)
126
138
  faostat_get_data(domain_code='TCL', item='<item_code>',
127
- element='5922', # Export Value (USD 1000)
139
+ element='<resolved_export_value_code>',
128
140
  year='<recent_year>', response_format='compact',
129
141
  limit=200, show_unit=True)
130
142
 
131
143
  # Top importers
132
144
  faostat_get_data(domain_code='TCL', item='<item_code>',
133
- element='5622', # Import Value (USD 1000)
145
+ element='<resolved_import_value_code>',
134
146
  year='<recent_year>', response_format='compact',
135
147
  limit=200, show_unit=True)
136
148
  ```
@@ -148,8 +160,9 @@ Based on the classification from Step 2, pull one additional dataset:
148
160
  **Food staple — Consumption & Food Security:** pull Food Balance Sheet data via the `FBS` domain. Look for food supply (kg/capita/yr), calories (kcal/capita/day), and protein (g/capita/day) at the world level plus the top 5 producer/consumer countries.
149
161
 
150
162
  ```
163
+ # Resolve element at runtime: faostat_search_codes(domain_code='FBS', dimension_id='element', query='food supply') → e.g. 645 / 664 / 674
151
164
  faostat_get_data(domain_code='FBS', item='<fbs_item_code>',
152
- element='<645 or 664 or 674>', year='<recent_year>',
165
+ element='<resolved_food_supply_code>', year='<recent_year>',
153
166
  response_format='compact', limit=50, show_unit=True)
154
167
  ```
155
168
 
@@ -158,8 +171,9 @@ You'll need to resolve the FBS item code separately via `faostat_search_codes(do
158
171
  **Cash crop — Price Dynamics:** pull producer-price series via the `PP` domain for the top 5 producers over the last 5 years. Report year-over-year changes and note any recent shocks.
159
172
 
160
173
  ```
174
+ # Resolve element at runtime: faostat_search_codes(domain_code='PP', dimension_id='element', query='producer price') → e.g. 5532
161
175
  faostat_get_data(domain_code='PP', area='<top5_area_codes>', item='<item_code>',
162
- element='5532', # Producer Price, USD/tonne
176
+ element='<resolved_producer_price_code>',
163
177
  year='<5yr_range>', response_format='compact',
164
178
  limit=100, show_unit=True)
165
179
  ```
@@ -217,6 +231,10 @@ End the briefing with a methodology footer and source line:
217
231
 
218
232
  Do not omit the source line — it's how users verify figures and know when the data was pulled.
219
233
 
234
+ ## Important Rules
235
+
236
+ **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
237
+
220
238
  ## Error handling and reliability notes
221
239
 
222
240
  - **`faostat_get_rankings` returns HTTP 500 intermittently.** This is the most common failure you'll hit. The Step 3 fallback (pull all countries with `faostat_get_data` and sort client-side) is the documented workaround. Don't spend many retries on the rankings endpoint — try once, fall back, and move on.
@@ -34,6 +34,9 @@ Determine from the user's message (or ask if unclear):
34
34
  - Agrifood emissions: **GT**; temperature change: **ET**; land use: **RL**
35
35
 
36
36
  Map the metric to the correct FAOSTAT element FILTER codes:
37
+
38
+ > Element codes below are verified hints. Resolve at runtime via `faostat_search_codes` before use.
39
+
37
40
  - Production (QCL): `'2510'`
38
41
  - Yield (QCL): `'2413'`
39
42
  - Area harvested (QCL): `'2312'`
@@ -60,7 +63,7 @@ For each entity combination, call `faostat_get_data` with:
60
63
  - `domain_code='<domain>'`
61
64
  - `area='<area_code>'` (or comma-separated codes if comparing multiple countries for one item)
62
65
  - `item='<item_code>'` (or comma-separated codes if comparing multiple items)
63
- - `element='<element_filter_code>'`
66
+ - `element='<element_filter_code>'` — resolve first: `faostat_search_codes(domain_code='<domain>', dimension_id='element', query='<metric name>')` → use the returned FILTER code; reference hints in Step 1 to validate
64
67
  - `year='2014,2015,2016,2017,2018,2019,2020,2021,2022,2023'` — use an **explicit comma-separated year list**. Colon ranges like `'2014:2023'` have returned empty in practice; avoid them.
65
68
  - `response_format='compact'` (saves tokens for multi-entity queries)
66
69
  - `limit=200` (increase limit for multi-year, multi-entity queries)
@@ -103,6 +106,10 @@ End with:
103
106
 
104
107
  > Source: FAOSTAT (FAO), accessed [current date].
105
108
 
109
+ ## Important Rules
110
+
111
+ **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
112
+
106
113
  ## Error Handling
107
114
 
108
115
  - If data is missing for some years for an entity, note the gap and compute metrics on available data only.
@@ -29,6 +29,8 @@ If any tool is missing, inform the user: "This skill requires the FAOSTAT MCP se
29
29
 
30
30
  ## Element Code Reference
31
31
 
32
+ > Element codes below are verified hints. Resolve at runtime via `faostat_search_codes` before use.
33
+
32
34
  - **Production quantity (QCL)**: filter `2510`, display `5510`
33
35
  - **Import quantity (TCL)**: filter `2610`; **Export quantity (TCL)**: filter `2910`
34
36
  - **Import value (TCL, USD 1000)**: filter `2612`; **Export value (TCL, USD 1000)**: filter `2912`
@@ -61,7 +63,7 @@ Store the confirmed `area` code for all subsequent queries.
61
63
  Call `faostat_get_data` with:
62
64
  - `domain_code='QCL'`
63
65
  - `area='<resolved_area_code>'`
64
- - `element='2510'` (Production — this is the FILTER code)
66
+ - `element='<resolved_production_code>'` — resolve at runtime: `faostat_search_codes(domain_code='QCL', dimension_id='element', query='production quantity')` → e.g. `2510` (Production — this is the FILTER code)
65
67
  - `year='<most_recent_year>'` (try the current year minus 2; if no data, try minus 3)
66
68
  - `response_format='compact'`
67
69
  - `limit=20`
@@ -74,7 +76,7 @@ Sort the results by value descending and identify the **top 5 crops by productio
74
76
  Call `faostat_get_data` with specific element filters so the payload stays small:
75
77
  - `domain_code='FS'`
76
78
  - `area='<resolved_area_code>'`
77
- - `element='210041,210011'` (prevalence of undernourishment, number undernourished). Add other FS element codes if needed — resolve via `faostat_search_codes(domain_code='FS', dimension_id='element', query='...')`.
79
+ - `element='<resolved_fs_codes>'` — resolve at runtime: `faostat_search_codes(domain_code='FS', dimension_id='element', query='undernourishment')` → e.g. `210041,210011` (prevalence of undernourishment, number undernourished). Add other FS element codes if needed — resolve via `faostat_search_codes(domain_code='FS', dimension_id='element', query='...')`.
78
80
  - `year='2014,2015,2016,2017,2018,2019,2020,2021,2022,2023'` (use explicit comma list — colon ranges like `'2014:2023'` have returned empty in practice)
79
81
  - `response_format='compact'`
80
82
 
@@ -91,7 +93,7 @@ If data is sparse, note which indicators are unavailable.
91
93
  Call `faostat_get_data` with:
92
94
  - `domain_code='FBS'`
93
95
  - `area='<resolved_area_code>'`
94
- - `element='664,674,684,645'` (kcal/cap/day, protein g/cap/day, fat g/cap/day, food kg/cap/yr)
96
+ - `element='<resolved_fbs_codes>'` — resolve at runtime: `faostat_search_codes(domain_code='FBS', dimension_id='element', query='food supply kcal')` and similar queries for protein/fat → e.g. `664,674,684,645` (kcal/cap/day, protein g/cap/day, fat g/cap/day, food kg/cap/yr)
95
97
  - `year='<latest 2-3 years available — FBS typically lags QCL by 1 year>'`
96
98
  - `item='2901'` (Grand Total for headline metrics); for item-specific breakdown (e.g., wheat contribution) resolve via `faostat_search_codes`
97
99
  - `response_format='compact'`
@@ -106,11 +108,16 @@ Extract:
106
108
  ### Step 6: Pull Trade Summary (TCL, not TM)
107
109
 
108
110
  Use **TCL** (country-level aggregate trade) for total imports and exports:
109
- ```
111
+ ```python
112
+ # Resolve elements at runtime:
113
+ # faostat_search_codes(domain_code='TCL', dimension_id='element', query='import quantity') → e.g. 2610
114
+ # faostat_search_codes(domain_code='TCL', dimension_id='element', query='export quantity') → e.g. 2910
115
+ # faostat_search_codes(domain_code='TCL', dimension_id='element', query='import value') → e.g. 2612
116
+ # faostat_search_codes(domain_code='TCL', dimension_id='element', query='export value') → e.g. 2912
110
117
  faostat_get_data(
111
118
  domain_code='TCL',
112
119
  area='<resolved_area_code>',
113
- element='2610,2910,2612,2912', # import qty, export qty, import value, export value
120
+ element='<resolved_import_qty>,<resolved_export_qty>,<resolved_import_val>,<resolved_export_val>',
114
121
  year='<latest 3 years>',
115
122
  response_format='compact',
116
123
  limit=500
@@ -149,6 +156,10 @@ End the report with:
149
156
 
150
157
  > Source: FAOSTAT (FAO), accessed [current date]. Data may reflect reporting lags of 1-3 years.
151
158
 
159
+ ## Important Rules
160
+
161
+ **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
162
+
152
163
  ## Error Handling
153
164
 
154
165
  - If a domain returns no data for the country, note it in the report and continue with available data.
@@ -110,6 +110,10 @@ If the user wants to query, use `faostat_search_codes` to resolve any entity nam
110
110
 
111
111
  **Important:** Element FILTER codes differ from DISPLAY codes. When calling `faostat_get_data`, use the filter code for the `element` parameter. When calling `faostat_get_rankings`, use the DISPLAY code. If unsure, use `faostat_search_codes` with `dimension_id='element'` to find the correct code.
112
112
 
113
+ ## Important Rules
114
+
115
+ **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
116
+
113
117
  ### Step 7 — Attribution
114
118
 
115
119
  When presenting any data, always include:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: faostat-export-dataset
3
- description: Use when the user wants a clean, documented tabular export of FAOSTAT data — "export FAOSTAT", "download the data", "give me the CSV / xlsx of [indicator]", "I want the raw numbers", "tabular data pull", "export to spreadsheet". The deliverable is a bundle: one multi-sheet .xlsx (README / Data_tidy / Data_wide / Methodology / Sources) + one tidy-long .csv mirror + a data-dictionary .md. No prose, no charts, no narrative — just the data with full provenance. Do NOT use for reports, briefs, papers, infographics, or visualisations — those skills embed their own xlsx appendices. Use this skill when the data itself IS the deliverable.
3
+ description: Use when the user wants a clean, documented tabular export of FAOSTAT data — "export FAOSTAT", "download the data", "give me the CSV / xlsx of [indicator]", "I want the raw numbers", "tabular data pull", "export to spreadsheet". The deliverable is a bundle one multi-sheet .xlsx (README / Data_tidy / Data_wide / Methodology / Sources) + one tidy-long .csv mirror + a data-dictionary .md. No prose, no charts, no narrative — just the data with full provenance. Do NOT use for reports, briefs, papers, infographics, or visualisations — those skills embed their own xlsx appendices. Use this skill when the data itself IS the deliverable.
4
4
  ---
5
5
 
6
6
  # FAOSTAT Data Export
@@ -24,12 +24,14 @@ Cross-skill invariants (all six — violations are skill bugs):
24
24
  5. **China composite default (Apr 2026 user preference).** Default: composite `China` (area 351). `China, mainland` (41) is an opt-in; full disaggregation (41 + 96 + 128 + 214) is also opt-in. Record the choice in the README sheet with the FAOSTAT-default-41 caveat.
25
25
  6. **`faostat_get_rankings` HTTP-500 fallback.** On failure, reconstruct by pulling `faostat_get_data` across all reporting countries and sorting client-side. Note the fallback in Methodology.
26
26
 
27
+ 7. **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='<dom>', dimension_id='element', query='<metric name>')` for elements; `faostat_search_codes(domain_code='<dom>', dimension_id='item', query='<item name>')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.
28
+
27
29
  Export-specific invariants:
28
30
 
29
- 7. **Every value traces to an API call.** The Methodology sheet logs one row per `faostat_get_data` call with domain / area / element / item / year list / timestamp / rows-returned. No value appears in the export that did not come from a documented call.
30
- 8. **FAO-native units kept as-is.** No auto-conversion. Values stay in FAOSTAT's native units (kt CO₂eq, tonnes, USD 1000, etc.). Unit is a column in tidy-long and a header row in wide. If the user explicitly asks for normalised units, add normalised columns **alongside** the native ones — never in place of.
31
- 9. **No FAO branding.** No FAO logo, "Food and Agriculture Organization of the United Nations" masthead, ISSN, "FAO Statistics Division" stamp, or "Required citation: FAO. …" line. CC-BY-4.0 attribution to FAOSTAT (source, licence, access date) IS kept — that's a property of the source data.
32
- 10. **Both shapes in the xlsx.** `Data_tidy` (long) and `Data_wide` (years-as-columns pivot) are both always present. The CSV mirror is tidy-long only. A wide CSV is produced only if the user asks.
31
+ 8. **Every value traces to an API call.** The Methodology sheet logs one row per `faostat_get_data` call with domain / area / element / item / year list / timestamp / rows-returned. No value appears in the export that did not come from a documented call.
32
+ 9. **FAO-native units kept as-is.** No auto-conversion. Values stay in FAOSTAT's native units (kt CO₂eq, tonnes, USD 1000, etc.). Unit is a column in tidy-long and a header row in wide. If the user explicitly asks for normalised units, add normalised columns **alongside** the native ones — never in place of.
33
+ 10. **No FAO branding.** No FAO logo, "Food and Agriculture Organization of the United Nations" masthead, ISSN, "FAO Statistics Division" stamp, or "Required citation: FAO. …" line. CC-BY-4.0 attribution to FAOSTAT (source, licence, access date) IS kept — that's a property of the source data.
34
+ 11. **Both shapes in the xlsx.** `Data_tidy` (long) and `Data_wide` (years-as-columns pivot) are both always present. The CSV mirror is tidy-long only. A wide CSV is produced only if the user asks.
33
35
 
34
36
  ## Workflow
35
37
 
@@ -38,7 +40,7 @@ Export-specific invariants:
38
40
  Required from the user (prompt via `AskUserQuestion` in Cowork, inline otherwise):
39
41
 
40
42
  - **Domain** — e.g., GT, QCL, TCL, ET, PP, FBS, FS.
41
- - **Element(s)** — FILTER code(s).
43
+ - **Element(s)** — FILTER code(s). If the user supplies a name rather than a code, resolve via `faostat_search_codes` (invariant 7) before use.
42
44
  - **Year range** — list of years (will be passed comma-separated).
43
45
  - **Area scope** — World, specific regions, specific country list, or "all reporting countries". Default if unspecified: all reporting countries plus regional aggregates (5000/5100/5200/5300/5400/5500).
44
46
 
@@ -51,7 +53,7 @@ Optional:
51
53
 
52
54
  ### Step 2 — Resolve codes
53
55
 
54
- If the user gave names rather than codes, call `faostat_search_codes(domain_code=…, dimension_id='item'|'area', query=…)`. Print the resolved codes back in a short confirmation block before the heavy pull so mismatches surface early.
56
+ Always resolve element and item codes at runtime before the data pull (invariant 7). Call `faostat_search_codes(domain_code=…, dimension_id='element'|'item'|'area', query=…)` for every numeric code needed — even when the user supplies a code directly, verify it matches. Print the resolved codes back in a short confirmation block before the heavy pull so mismatches surface early.
55
57
 
56
58
  ### Step 3 — Pull data
57
59