Skill: noypearl/yahoo-data-fetcher (version 1.0.2)

--- name: yahoo-data-fetcher description: Fetch real-time stock quotes from Yahoo Finance. user-invocable: true metadata: moltbot: emoji: "📈" requires: bins: ["node"] homepage: https://query1.finance.yahoo.com/v7/finance/quote --- # Yahoo Data Fetcher – Stock Quote Get current stock price data from Yahoo Finance. This skill fetches the latest market quote for one or more stock symbols and returns normalized JSON output. --- ## Command ### `/stock quote` Fetch the latest quote for one or more stock symbols. --- ## Input - `symbols` (string or array of strings) Examples: - `"AAPL"` - `"AAPL MSFT TSLA"` - `"AAPL,MSFT,TSLA"` - `["AAPL", "MSFT"]` - `{ "symbols": ["AAPL", "MSFT"] }` --- ## Output For each symbol: - `symbol` – stock ticker - `price` – latest market price - `change` – absolute price change - `changePercent` – percentage change - `currency` – trading currency - `marketState` – market status (e.g. `REGULAR`, `CLOSED`) Example output: ```json [ { "symbol": "AAPL", "price": 189.12, "change": 1.23, "changePercent": 0.65, "currency": "USD", "marketState": "REGULAR" } ]

path: index.js
content: #!/usr/bin/env node
/**
 * Yahoo Data Fetcher – Stock Quote skill
 * Entry point for Clawdbot
 */

const YAHOO_QUOTE_URL = "https://query1.finance.yahoo.com/v7/finance/quote";

function parseSymbols(argv) {
  if (!argv.length) return [];

  const first = argv[0].trim();

  // JSON input mode
  if (first.startsWith("{") || first.startsWith("[")) {
    const parsed = JSON.parse(first);
    if (Array.isArray(parsed)) return parsed.map(String);
    if (parsed && parsed.symbols) {
      return Array.isArray(parsed.symbols)
        ? parsed.symbols.map(String)
        : [String(parsed.symbols)];
    }
    return [];
  }

  // CLI args mode
  return argv
    .join(" ")
    .split(/[\s,]+/)
    .map(s => s.trim())
    .filter(Boolean);
}

function num(x) {
  return typeof x === "number" && Number.isFinite(x) ? x : null;
}

async function main() {
  const symbols = parseSymbols(process.argv.slice(2));

  if (!symbols.length) {
    console.error(JSON.stringify({
      error: "Missing symbols",
      example: [
        "AAPL",
        "AAPL MSFT TSLA",
        '{ "symbols": ["AAPL", "MSFT"] }'
      ]
    }, null, 2));
    process.exit(2);
  }

  const url = new URL(YAHOO_QUOTE_URL);
  url.searchParams.set("symbols", symbols.join(","));

  const res = await fetch(url.toString(), {
    headers: {
      "User-Agent": "clawdbot-yahoo-data-fetcher/1.0"
    }
  });

  if (!res.ok) {
    const body = await res.text().catch(() => "");
    throw new Error(`Yahoo request failed (${res.status}): ${body.slice(0, 200)}`);
  }

  const json = await res.json();
  const results = json?.quoteResponse?.result ?? [];

  const output = results.map(q => ({
    symbol: q?.symbol ?? null,
    price: num(q?.regularMarketPrice),
    change: num(q?.regularMarketChange),
    changePercent: num(q?.regularMarketChangePercent),
    currency: q?.currency ?? null,
    marketState: q?.marketState ?? null
  }));

  // Handle symbols Yahoo didn't return
  const seen = new Set(output.map(o => o.symbol));
  for (const s of symbols) {
    if (!seen.has(s)) {
      output.push({
        symbol: s,
        price: null,
        change: null,
        changePercent: null,
        currency: null,
        marketState: null
      });
    }
  }

  process.stdout.write(JSON.stringify(output, null, 2) + "\n");
}

main().catch(err => {
  console.error(JSON.stringify({
    error: err.message || String(err)
  }, null, 2));
  process.exit(1);
});

path: package.json
content: name: yahoo-data-fetcher
private: yes
version: 1.0.0
type: module

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)