Files
OpenCode Test daa4de8832 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.
2026-01-03 10:54:54 -08:00

109 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Stocks collector using stock-lookup skill."""
import json
import subprocess
import sys
from pathlib import Path
def get_quotes(symbols: list) -> list:
"""Fetch quotes using stock-lookup skill."""
script = Path.home() / ".claude/skills/stock-lookup/scripts/quote.py"
try:
result = subprocess.run(
[sys.executable, str(script), "--json"] + symbols,
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
return json.loads(result.stdout)
else:
return [{"symbol": s, "error": "fetch failed"} for s in symbols]
except Exception as e:
return [{"symbol": s, "error": str(e)} for s in symbols]
def format_stocks_with_haiku(quotes: list) -> str:
"""Use Haiku to format stock data nicely."""
# Build context
lines = []
for q in quotes:
if "error" in q:
lines.append(f"{q.get('symbol', '?')}: error - {q['error']}")
else:
price = q.get("price", 0)
prev = q.get("previous_close", price)
if prev and prev > 0:
change = ((price - prev) / prev) * 100
direction = "+" if change >= 0 else ""
lines.append(f"{q['symbol']}: ${price:.2f} ({direction}{change:.1f}%)")
else:
lines.append(f"{q['symbol']}: ${price:.2f}")
stock_data = "\n".join(lines)
prompt = f"""Format these stock quotes into a compact single line for a morning dashboard.
Use arrow indicators (▲▼) for direction. Keep it concise.
{stock_data}
Output ONLY the formatted stock line, nothing else."""
try:
result = subprocess.run(
["claude", "--print", "--model", "haiku", "-p", prompt],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except Exception:
pass
# Fallback to basic format
parts = []
for q in quotes:
if "error" in q:
parts.append(f"{q.get('symbol', '?')} ⚠️")
else:
price = q.get("price", 0)
prev = q.get("previous_close", price)
if prev and prev > 0:
change = ((price - prev) / prev) * 100
arrow = "" if change >= 0 else ""
parts.append(f"{q['symbol']} ${price:.2f} {'+' if change >= 0 else ''}{change:.1f}% {arrow}")
else:
parts.append(f"{q['symbol']} ${price:.2f}")
return " ".join(parts)
def collect(config: dict) -> dict:
"""Main collector entry point."""
watchlist = config.get("stocks", {}).get("watchlist", ["NVDA", "AAPL", "MSFT"])
quotes = get_quotes(watchlist)
formatted = format_stocks_with_haiku(quotes)
errors = [q.get("error") for q in quotes if "error" in q]
return {
"section": "Stocks",
"icon": "📈",
"content": formatted,
"raw": quotes,
"error": errors[0] if errors else None
}
if __name__ == "__main__":
config = {"stocks": {"watchlist": ["CRWV", "NVDA", "MSFT"]}}
result = collect(config)
print(f"## {result['icon']} {result['section']}")
print(result["content"])