Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/skills/forge.md
Original file line number Diff line number Diff line change
Expand Up @@ -1096,10 +1096,10 @@ metadata:
requires:
bins: [curl] # binaries that must be in PATH
env:
required: [WEATHER_API_KEY]
required: []
one_of: []
optional: []
egress_domains: [api.openweathermap.org]
egress_domains: [wttr.in]
denied_tools: [http_request] # tools this skill must NOT use
timeout_hint: 60 # suggested execution timeout in seconds
---
Expand Down
7 changes: 3 additions & 4 deletions forge-cli/internal/tui/steps/egress_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,9 @@ func inferSource(domain string, ctx *tui.WizardContext) string {

// Skill domains
skillDomains := map[string]string{
"api.github.com": "github skill",
"github.com": "github skill",
"api.openweathermap.org": "weather skill",
"api.weatherapi.com": "weather skill",
"api.github.com": "github skill",
"github.com": "github skill",
"wttr.in": "weather skill",
}
if src, ok := skillDomains[domain]; ok {
return src
Expand Down
16 changes: 13 additions & 3 deletions forge-skills/local/embedded/weather/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ metadata:
requires:
bins:
- curl
- jq
env:
required: []
one_of: []
optional: []
egress_domains:
- api.openweathermap.org
- api.weatherapi.com
- wttr.in
---
## Tool: weather_current

Expand All @@ -27,9 +27,19 @@ Get current weather for a location.
**Input:** location (string) - City name or coordinates
**Output:** Current temperature, conditions, humidity, and wind speed

```bash
curl -s "https://wttr.in/${location}?format=j1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ```bash block is documentation — the runtime won't execute it. For weather_current to be a callable tool, add `scripts/weather-current.sh` (tool name with `_`→`-`) next to this SKILL.md. The script receives the tool's JSON input as its first argument, so it should parse `$1` with `jq`, pull `.location`, URL-encode it (`jq -rn --arg s "$LOCATION" '$s|@uri'`), curl wttr.in, check the HTTP status, and emit the shaped fields you list just below (`temp_C`, `weatherDesc`, `humidity`, `windspeedKmph`) rather than the full j1 blob. You'll also want `jq` added to `bins` above.

```
Relevant fields in the response: `current_condition[0].temp_C`, `current_condition[0].weatherDesc[0].value`, `current_condition[0].humidity`, `current_condition[0].windspeedKmph`.

## Tool: weather_forecast

Get weather forecast for a location.

**Input:** location (string), days (integer: 1-7)
**Input:** location (string), days (integer: 1-3)
**Output:** Daily forecast with high/low temperatures and conditions

```bash
curl -s "https://wttr.in/${location}?format=j1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here — this needs a scripts/weather-forecast.sh to become runnable. One extra thing for the forecast: the tool declares a days input but this curl ignores it (?format=j1 returns a fixed window). Have the script read .days (clamp to 1–3, wttr.in's free cap) and slice .weather[0:$days] so the documented input actually shapes the output. Passing days through jq --argjson keeps it out of the shell.

```
The `weather` array in the response contains up to 3 days of forecast (today + 2 days ahead). Each entry has `date`, `maxtempC`, `mintempC`, and `hourly[].weatherDesc[0].value`. wttr.in's free JSON endpoint caps at 3 days — for a longer forecast window, `open-meteo.com` would be needed instead.
29 changes: 29 additions & 0 deletions forge-skills/local/embedded/weather/scripts/weather-current.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail

input="$1"

location=$(jq -r '.location // empty' <<< "$input" 2>/dev/null) || location=""

if [[ -z "$location" ]]; then
jq -n '{error: "location is required"}'
exit 0
fi

encoded_location=$(jq -rn --arg s "$location" '$s|@uri')

response=$(curl -s -w "\n%{http_code}" "https://wttr.in/${encoded_location}?format=j1")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional (Low): under set -e, if curl fails at the transport layer (DNS/connection), this assignment exits the script before the http_code != 200 branch — so a network failure produces no output rather than a shaped {error: ...}. HTTP 4xx/5xx are handled correctly (curl -s returns 0 for those). If you want transport errors to also emit JSON:

response=$(curl -s -w "\n%{http_code}" "https://wttr.in/${encoded_location}?format=j1") \
  || { jq -n '{error: "network request failed"}'; exit 0; }

Same line in weather-forecast.sh. Non-blocking.

http_code=$(tail -n1 <<< "$response")
body=$(sed '$d' <<< "$response")

if [[ "$http_code" != "200" ]]; then
jq -n --arg code "$http_code" '{error: "wttr.in request failed with status \($code)"}'
exit 0
fi

echo "$body" | jq '{
temperature_c: .current_condition[0].temp_C,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional (Low, cosmetic): wttr.in's j1 returns temp_C, humidity, windspeedKmph as JSON strings ("18"), so this emits string values. If the tool's consumers expect numbers, wrap with | tonumber (e.g. temperature_c: (.current_condition[0].temp_C | tonumber)). Take it or leave it depending on how the output is consumed.

condition: .current_condition[0].weatherDesc[0].value,
humidity: .current_condition[0].humidity,
wind_speed_kmph: .current_condition[0].windspeedKmph
}' 2>/dev/null || jq -n '{error: "failed to parse weather data"}'
38 changes: 38 additions & 0 deletions forge-skills/local/embedded/weather/scripts/weather-forecast.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail

input="$1"

location=$(jq -r '.location // empty' <<< "$input" 2>/dev/null) || location=""
days=$(jq -r '.days // 3' <<< "$input" 2>/dev/null) || days=3

if [[ -z "$location" ]]; then
jq -n '{error: "location is required"}'
exit 0
fi

if ! [[ "$days" =~ ^[0-9]+$ ]]; then
days=3
fi
if (( days < 1 )); then days=1; fi
if (( days > 3 )); then days=3; fi

encoded_location=$(jq -rn --arg s "$location" '$s|@uri')

response=$(curl -s -w "\n%{http_code}" "https://wttr.in/${encoded_location}?format=j1")
http_code=$(tail -n1 <<< "$response")
body=$(sed '$d' <<< "$response")

if [[ "$http_code" != "200" ]]; then
jq -n --arg code "$http_code" '{error: "wttr.in request failed with status \($code)"}'
exit 0
fi

echo "$body" | jq --argjson days "$days" '{
forecast: [.weather[0:$days][] | {
date: .date,
max_temp_c: .maxtempC,
min_temp_c: .mintempC,
condition: .hourly[0].weatherDesc[0].value
}]
}' 2>/dev/null || jq -n '{error: "failed to parse forecast data"}'
2 changes: 1 addition & 1 deletion forge-skills/local/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ metadata:
bins:
- curl
egress_domains:
- api.openweathermap.org
- wttr.in
---
## Tool: weather_current
Get current weather.
Expand Down
Loading