Add morning-report and stock-lookup skills

Add comprehensive morning report skill with collectors for calendar, email, tasks,
infrastructure status, news, stocks, and weather. Add stock lookup skill for quote queries.
This commit is contained in:
OpenCode Test
2026-01-03 10:54:54 -08:00
parent ae958528a6
commit daa4de8832
13 changed files with 1590 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Weather collector using wttr.in."""
import json
import subprocess
import urllib.request
from pathlib import Path
def fetch_weather(location: str) -> dict:
"""Fetch weather data from wttr.in."""
# Use wttr.in JSON format
url = f"https://wttr.in/{location}?format=j1"
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
try:
with urllib.request.urlopen(req, timeout=5) as resp:
return json.load(resp)
except Exception as e:
return {"error": str(e)}
def format_weather_basic(data: dict, location: str) -> str:
"""Format weather data without LLM - basic fallback."""
if "error" in data:
return f"Weather unavailable: {data['error']}"
try:
current = data["current_condition"][0]
today = data["weather"][0]
temp_f = current.get("temp_F", "?")
desc = current.get("weatherDesc", [{}])[0].get("value", "Unknown")
high = today.get("maxtempF", "?")
low = today.get("mintempF", "?")
return f"{location}: {temp_f}°F, {desc} | High {high}° Low {low}°"
except Exception as e:
return f"Weather parse error: {e}"
def format_weather_with_haiku(data: dict, location: str) -> str:
"""Use Haiku to format weather data nicely."""
if "error" in data:
return f"Weather unavailable: {data['error']}"
try:
current = data["current_condition"][0]
today = data["weather"][0]
# Extract key data
temp_f = current.get("temp_F", "?")
feels_like = current.get("FeelsLikeF", temp_f)
desc = current.get("weatherDesc", [{}])[0].get("value", "Unknown")
humidity = current.get("humidity", "?")
high = today.get("maxtempF", "?")
low = today.get("mintempF", "?")
# Check for precipitation
hourly = today.get("hourly", [])
rain_hours = [h for h in hourly if int(h.get("chanceofrain", 0)) > 50]
# Build context for Haiku
weather_context = f"""Current: {temp_f}°F (feels like {feels_like}°F), {desc}
High: {high}°F, Low: {low}°F
Humidity: {humidity}%
Rain chance >50%: {len(rain_hours)} hours today"""
# Check if claude is available
claude_path = Path.home() / ".local/bin/claude"
if not claude_path.exists():
claude_path = "claude" # Try PATH
prompt = f"""Format this weather data for {location} into a single concise line for a morning report.
Add a brief hint if relevant (e.g., "bring umbrella", "nice day for a walk").
Keep it under 80 characters if possible.
{weather_context}
Output ONLY the formatted weather line, nothing else."""
result = subprocess.run(
[str(claude_path), "--print", "--model", "haiku", "-p", prompt],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
else:
# Fallback to basic format
return format_weather_basic(data, location)
except Exception as e:
# Fallback to basic format
return format_weather_basic(data, location)
def collect(config: dict) -> dict:
"""Main collector entry point."""
location = config.get("weather", {}).get("location", "Seattle,WA,USA")
city_name = location.split(",")[0]
data = fetch_weather(location)
# Try Haiku formatting, fall back to basic
formatted = format_weather_with_haiku(data, city_name)
return {
"section": "Weather",
"icon": "🌤",
"content": formatted,
"raw": data if "error" not in data else None,
"error": data.get("error")
}
if __name__ == "__main__":
# Test
config = {"weather": {"location": "Seattle,WA,USA"}}
result = collect(config)
print(f"## {result['icon']} {result['section']}")
print(result["content"])