reddit

reddit is a skill for Claude Code, Codex from carsteneu/yesmem. It costs 135 tokens per session (3,986 once invoked), scanned A, original, Apache-2.0.

A Reddit research tool that reads posts and nested comments from Reddit's website, then extracts outside links. Reddit is an online discussion site organized into topic-based communities called subreddits.

In plain words
What is it for?
Use it to fetch a post, search subreddits by topic, classify results, or combine several communities into a topic summary.
Why use it?
It provides structured post and comment data when the unauthenticated Reddit JSON interface is unavailable. It also saves fetched data for later use and can search across communities.

Skill for Claude CodeCodex

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add skills/carsteneu/yesmem/reddit
Any agent
npx skills add carsteneu/yesmem --skill reddit
Clone the repo
git clone --depth 1 https://github.com/carsteneu/yesmem

Made for: Claude Code, Codex.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for reddit

README.md
[![agentmods](https://agentmods.dev/badge/skills/carsteneu/yesmem/reddit.svg)](https://agentmods.dev/skills/carsteneu/yesmem/reddit)
Your own site
<a href="https://agentmods.dev/skills/carsteneu/yesmem/reddit"><img src="https://agentmods.dev/badge/skills/carsteneu/yesmem/reddit.svg" alt="Measured on agentmods" height="20"></a>
Per session 135 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 3,986 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 1 finding. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00135 $0.03986
Opus 5 $0.00068 $0.01993
Sonnet 5 $0.00027 $0.00797
Haiku 4.5 $0.00014 $0.00399

Measured 5d ago against content hash 3674ac216150, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-05, from the pricing page.

Security

Grade A, and why

reddit scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 5d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Makes network callslowCapability

Not a fault in itself. Listed so you know the mod talks to something, and to what.

description: "Fetch a Reddit post URL via old.reddit.com HTML scraping — the .json API is blocked for unauthenticated requests since May 2026. Returns structured data: post metadata + nested comments (with depth) + uniqu
skills/bundled-skills/reddit/SKILL.md · 290 lines

How it starts

The opening of the file, as written. The whole thing — 290 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Purpose

Fetch a Reddit post URL via old.reddit.com HTML scraping (the public .json API has been blocked for unauthenticated requests since ~May 2026). Returns structured data: post metadata + nested comments (with depth) + unique external links categorized (github/reddit/external). Persists into cap_store tables. Uses cap-blob-put to bypass the sh() 30KB wall.

Also search across subreddits with LLM classification and multi-subreddit topic research with synthesis.

Quick Usage

// Fetch a single post with comments
await yesmem_execute_cap({name:"reddit", fn:"reddit_fetch", args:'{"url":"<reddit-url>","max_comments":30}'})

// Search across subreddits
await yesmem_execute_cap({name:"reddit", fn:"reddit_search", args:'{"query":"<topic>","limit":25,"sort":"relevance","t":"week"}'})

// Multi-subreddit topic research with synthesis
await yesmem_execute_cap({name:"reddit", fn:"reddit_research", args:'{"topic":"<topic>","limit":10,"fetch_top":5,"synthesize":true}'})

Functions

reddit_fetch({url, max_comments})

Fetches a single Reddit post, parses comments with depth, extracts external links.

reddit_search({query, limit, sort, t, subreddit, after, classify})

Searches Reddit listings or subreddits. Returns structured posts. Optional LLM classification via haiku().

reddit_research({topic, subreddits, limit, score_min, fetch_top, synthesize})

Multi-subreddit research: searches across 7+ subreddits, fetches top posts with comments, classifies, and synthesizes findings.

Script

async ({url, max_comments}) => {
  if (!url || typeof url !== 'string') return {error: 'url required (string)'};
  url = url.replace(/^reddit:/i, '').trim().replace(/\/$/, '');
  if (!/^https?:\/\/(www\.|old\.)?reddit\.com\//i.test(url)) return {error: 'not a reddit URL', given: url};
  
  const oldUrl = url.replace(/^https?:\/\/(www\.)?reddit\.com/, 'https://old.reddit.com');
  const key = 'url:' + url;
  
  const curlCmd = `curl -sL -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" -H "Accept: text/html,application/xhtml+xml" -H "Accept-Language: en-US,en;q=0.9" --max-time 20 ${JSON.stringify(oldUrl)} | yesmem cap-blob-put --cap reddit --key ${JSON.stringify(key)}`;
  
  const putRes = await sh(curlCmd, 25000);
  if (!putRes || !putRes.includes('"status":"ok"')) return {error: 'cap-blob-put failed', detail: String(putRes).slice(0,400)};
  
  let rows = [];
  for (let i = 0; i < 50; i++) {
    const r = await mcp__yesmem__cap_store({capability: 'reddit', action: 'query', table: 'blobs', where: 'key=? AND chunk_idx=?', args: JSON.stringify([key, i]), limit: 1});
    const parsed = typeof r === 'string' ? JSON.parse(r) : r;
    const arr = Array.isArray(parsed) ? parsed : (parsed.rows || []);
    if (!arr.length) break;
    rows.push(arr[0]);
  }
  if (!rows.length) return {error: 'blob empty after put', key};
  const html = rows.map(r => r.data || '').join('');
  
  // Helper: extract data-* attribute value
  const getAttr = (str, name) => {
    const m = str.match(new RegExp('data-' + name + '="([^"]*)"'));
    return m ? m[1] : '';
  };
  
  // Helper: clean HTML entities and Reddit markdown escapes
  const cleanText = (text) => {
    return text
      .replace(/<[^>]+>/g, '')
      .replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
      .replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'")
      .replace(/&nbsp;/g, ' ')
      .replace(/\\([()_*\[\]#])/g, '$1')  // Reddit markdown escapes: \( \) \_ \* etc.
      .replace(/\\n/g, '\n').trim();
  };
  
  // === PARSE POST ===
  const t3Match = html.match(/<div class="[^"]*thing id-t3_(\w+)[^"]*"([^>]*)>/);
  if (!t3Match) return {error: 'could not find post (t3_ thing) in HTML'};
  const postId = t3Match[1];
  const postFullname = 't3_' + postId;
  const t3Attrs = t3Match[2];
  
  const postAuthor = getAttr(t3Attrs, 'author') || '[deleted]';
  const postScore = parseInt(getAttr(t3Attrs, 'score')) || 0;
  const postNumComments = parseInt(getAttr(t3Attrs, 'comments-count')) || 0;
  const postTimestamp = parseInt(getAttr(t3Attrs, 'timestamp')) || 0;
  const postSubreddit = (getAttr(t3Attrs, 'subreddit-prefixed') || '').replace('r/', '');
  const postPermalinkRaw = getAttr(t3Attrs, 'permalink');
  const postPermalink = 'https://reddit.com' + (postPermalinkRaw || '/r/' + postSubreddit + '/comments/' + postId + '/');
  
  const titleMatch = html.match(/<a class="title may-blank[^"]*"[^>]*>([^<]+)<\/a>/);
  const postTitle = titleMatch ? cleanText(titleMatch[1]) : (html.match(/<title>([^<]+)/) || ['',''])[1].replace(' : ' + postSubreddit, '').trim();
  
  const bodyRe = new RegExp('id="form-' + postFullname + '[^"]*"[^>]*>.*?<div class="md">([\\s\\S]*?)<\/div>\\s*<\/div>\\s*<\/form>');
  const bodyMatch = html.match(bodyRe);
  let postBody = bodyMatch ? cleanText(bodyMatch[1]) : '';
  
  let finalScore = postScore;
  if (!finalScore) {
    const sf = html.match(/<span class="number">(\d+)<\/span>/);
    if (sf) finalScore = parseInt(sf[1]);
  }
  
  const fetchedAt = Math.floor(Date.now()/1000);
  
  // === PARSE COMMENTS ===
  const commentRegex = /<div class="[^"]*thing id-(t1_\w+)[^"]*"([^>]*)>/g;
  const commentData = [];
  let cm;
  while ((cm = commentRegex.exec(html)) !== null) {
    const fullname = cm[1];
    const cattrs = cm[2];
    const author = getAttr(cattrs, 'author') || '[deleted]';
    const pos = cm.index;
    
    const chunk = html.slice(pos, pos + 3000);
    const parentMatch = chunk.match(/<a href="#(\w+)"[^>]*data-event-action="parent"/);
    let parentShortId = parentMatch ? parentMatch[1] : '';
    
    const scoreMatch = chunk.match(/<span class="score likes" title="(-?\d+)"/);
    const score = scoreMatch ? parseInt(scoreMatch[1]) : 0;
    
    const timeMatch = chunk.match(/<time[^>]*datetime="([^"]+)"/);
    let createdUtc = timeMatch ? Math.floor(new Date(timeMatch[1]).getTime() / 1000) : 0;
    
    const mdMatch = chunk.match(/<div class="md">([\s\S]*?)<\/div>\s*<\/div>\s*<\/form>/);
    let body = mdMatch ? cleanText(mdMatch[1]) : '';
    
    commentData.push({fullname, author, score, body, created_utc: createdUtc, parentShortId});
  }
  
  // Build short_id → fullname map for depth calculation
  const shortToFull = new Map();
  for (const c of commentData) shortToFull.set(c.fullname.replace('t1_', ''), c.fullname);
  shortToFull.set(postId, postFullname);
  
  for (const c of commentData) {
    c.parent_id = (c.parentShortId && shortToFull.has(c.parentShortId)) ? shortToFull.get(c.parentShortId) : postFullname;
    let depth = 0, current = c.parentShortId;
    const visited = new Set();
    while (current && current !== postId && shortToFull.has(current) && !visited.has(current)) {
      visited.add(current);
      depth++;
      const pc = commentData.find(x => x.fullname === shortToFull.get(current));
      current = pc ? pc.parentShortId : null;
    }
    c.depth = depth;
  }
  
  // === CAP_STORE PERSISTENCE ===
  await mcp__yesmem__cap_store({capability:'reddit',action:'create_table',table:'posts',columns:JSON.stringify([{name:'permalink',type:'TEXT'},{name:'subreddit',type:'TEXT'},{name:'author',type:'TEXT'},{name:'title',type:'TEXT'},{name:'body',type:'TEXT'},{name:'score',type:'INTEGER'},{name:'num_comments',type:'INTEGER'},{name:'created_utc',type:'INTEGER'},{name:'external_url',type:'TEXT'},{name:'fetched_at',type:'INTEGER'}])});
  await mcp__yesmem__cap_store({capability:'reddit',action:'create_table',table:'comments',columns:JSON.stringify([{name:'post_permalink',type:'TEXT'},{name:'comment_id',type:'TEXT'},{name:'depth',type:'INTEGER'},{name:'author',type:'TEXT'},{name:'score',type:'INTEGER'},{name:'body',type:'TEXT'},{name:'created_utc',type:'INTEGER'},{name:'parent_id',type:'TEXT'},{name:'fetched_at',type:'INTEGER'}])});
  await mcp__yesmem__cap_store({capability:'reddit',action:'create_table',table:'links',columns:JSON.stringify([{name:'post_permalink',type:'TEXT'},{name:'target_url',type:'TEXT'},{name:'kind',type:'TEXT'},{name:'source_kind',type:'TEXT'},{name:'source_author',type:'TEXT'},{name:'source_comment_id',type:'TEXT'},{name:'fetched_at',type:'INTEGER'}])});
  await mcp__yesmem__cap_store({capability:'reddit',action:'delete',table:'posts',where:'permalink=?',args:JSON.stringify([postPermalink])});
  await mcp__yesmem__cap_store({capability:'reddit',action:'delete',table:'comments',where:'post_permalink=?',args:JSON.stringify([postPermalink])});
  await mcp__yesmem__cap_store({capability:'reddit',action:'delete',table:'links',where:'post_permalink=?',args:JSON.stringify([postPermalink])});
  
  const postCreatedUtc = Math.floor(postTimestamp / 1000);
  await mcp__yesmem__cap_store({capability:'reddit',action:'upsert',table:'posts',data:JSON.stringify({permalink:postPermalink,subreddit:postSubreddit,author:postAuthor,title:postTitle,body:postBody,score:finalScore,num_comments:postNumComments,created_utc:postCreatedUtc,external_url:'',fetched_at:fetchedAt})});
  
  // === LINK EXTRACTION ===
  const categorize = (u) => {
    const m = u.match(/^https?:\/\/([^\/?#:]+)/i);
    if (!m) return 'external';
    const host = m[1].toLowerCase();
    if (host === 'github.com' || host.endsWith('.github.com') || host === 'gist.github.com') return 'github';
    if (host === 'reddit.com' || host.endsWith('.reddit.com') || host === 'redd.it') return 'reddit';
    return 'external';
  };
  const linkSet = new Set();
  const linkRows = [];
  const urlRe = /https?:\/\/[^\s\)\]\>"'\<]+/g;
  const collect = (text, sourceKind, author, cid) => {
    if (!text) return;
    const m = text.match(urlRe);
    if (!m) return;
    for (const u of m) {
      const cleaned = u.replace(/[.,;:!?'")\]>]*$/, '').replace(/\\([()_*\[\]#])/g, '$1');
      if (linkSet.has(cleaned)) continue;
      linkSet.add(cleaned);
      linkRows.push({post_permalink:postPermalink,target_url:cleaned,kind:categorize(cleaned),source_kind:sourceKind,source_author:author||'',source_comment_id:cid||'',fetched_at:fetchedAt});
    }
  };
  collect(postBody, 'post_body', postAuthor, '');
  
  const cap = typeof max_comments === 'number' && max_comments > 0 ? max_comments : 0;
  const outputComments = [];
  const commentRows = [];
  for (const c of commentData) {
    if (cap && outputComments.length >= cap) break;
    if (!c.body) continue;
    outputComments.push({author:c.author,score:c.score,depth:c.depth,body:c.body});
    commentRows.push({post_permalink:postPermalink,comment_id:c.fullname,depth:c.depth,author:c.author,score:c.score,body:c.body,created_utc:c.created_utc,parent_id:c.parent_id,fetched_at:fetchedAt});
    collect(c.body, 'comment', c.author, c.fullname);
  }
  
  for (const row of commentRows) {
    await mcp__yesmem__cap_store({capability:'reddit',action:'upsert',table:'comments',data:JSON.stringify(row)});
  }
  for (const row of linkRows) {
    await mcp__yesmem__cap_store({capability:'reddit',action:'upsert',table:'links',data:JSON.stringify(row)});
  }
  
  return {
    post: {title:postTitle,author:postAuthor,score:finalScore,subreddit:postSubreddit,permalink:postPermalink,body:postBody},
    comments: outputComments,
    links: Array.from(linkSet),
    stats: {comment_count:outputComments.length,link_count:linkSet.size,reported_comments:postNumComments},
    stored: {posts:1, comments:commentRows.length, links:linkRows.length}
  };
}

Read the full file on GitHub · 290 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 5d ago First seen · 290 lines · 135 tokens per session scan A 3674ac216150

Subscribe to this mod's changes

reddit is a skill published in the GitHub repository carsteneu/yesmem (41 stars, last pushed 3d ago), licensed Apache-2.0. It adds 135 tokens to every session and 3,986 once invoked, about $0.0007 per session on Opus 5. A static security scan graded it A with 1 finding (makes network calls). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other skills, from other repositories

web-access

Use this skill whenever the user needs to access information from the internet — searching for current information, fetching public web pages, browsing login-gated sites (微博/小红书/B站/飞书/Twitter), comparing products, researching topics, gathering documentation, or summarizing news. This skill orchestrates four…

desirecore/market · 255 tokens

web-search-tool

Web search tool. Queries the public internet via the Brave Search API. Use when: researching current events, finding documentation, fact-checking, or fetching ranked search results.

xuiltul/animaworks · 38 tokens

forgetful-recall

Recall past knowledge before working — prior decisions, solved problems, preferences, project history. Use at the start of any task, when the user references earlier work, when re-entering a project after time away, or before proposing an approach that may already have history. Covers query shaping, scoping…

ScottRBK/forgetful · 78 tokens

opencli-usage

Use when running OpenCLI commands to interact with websites (Bilibili, Twitter, Reddit, Xiaohongshu, etc.), desktop apps (Cursor, Notion), or public APIs (HackerNews, arXiv). Covers installation, command reference, and output formats for 100+ adapters.

zxfccmm4/Obsidian-OpenCode-Knowledge · 66 tokens

reddapi

The original reddapi.dev Reddit search skill (vector search, semantic search, trends, subreddit discovery), no Reddit OAuth or app registration needed. This is the same engine now packaged as reddit-research with added market-research playbooks and a fuller pitch on semantic vs keyword search; reddapi is kept live…

lignertys/reddit-research-skills · 143 tokens

qiaomu-opencli-usage

Use when running OpenCLI commands to interact with websites (Bilibili, Twitter, Reddit, Xiaohongshu, etc.), desktop apps (Cursor, Notion), or public APIs (HackerNews, arXiv). Covers installation, command reference, and output formats for 79+ adapters.

joeseesun/qiaomu-opencli-skills · 69 tokens