Communitygithub.com

kennyzir/7deer_skills

AI 热点雷达 - Descubre nuevas herramientas/palabras clave de IA antes de que se vuelvan virales, registra dominios y construye sitios para arbitraje.

¿Qué es 7deer_skills?

Esta habilidad funciona como un radar de tendencias de IA, diseñado para detectar herramientas y keywords de IA en su etapa más temprana de viralización (ventana de 24-72 horas) antes de que lleguen al público masivo. Su objetivo principal es identificar proyectos emergentes para registrar dominios, construir sitios de herramientas y capturar tráfico de búsqueda. Soporta plataformas como Claude Code y Cursor, ejecutándose cada 12 horas de forma automática (cron a las 9:00 y 21:00) o mediante comandos como "run radar" o "hot scan". El proceso involucra la inyección de cookies de X/Twitter para mantener sesión iniciada, la ejecución de una matriz de búsqueda de 6 capas (L0 a L4) que cubren desde productos virales no etiquetados como IA hasta señales de FOMO en listas de espera, y la verificación de frescura de proyectos (especialmente repos de GitHub) para evitar oportunidades de arbitraje vencidas. Finalmente genera un reporte detallado con los 10 mejores candidatos, incluyendo sugerencias de dominios, plan de acción y análisis de puntos de dolor del usuario.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kennyzir/7deer_skills/tree/HEAD/x-demand-radar

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Radar de tendencias AI (X)

Métricas Clave

Puntuación Final = Popularidad en X × Frescura del Proyecto

  • puntuación_cruda = likes × log(1 + likes_por_hora)
  • Velocidad = likes / (tiempo actual - tiempo de publicación) en horas
  • Verificación de frescura del proyecto: Tras detectar un proyecto en GitHub, se debe navegar a la página del repositorio para validar su fecha de creación.
  • puntuación_final = puntuación_cruda × multiplicador_frescura
    • Proyecto < 7 días: × 1.0
    • Proyecto 7-30 días: × 0.7
    • Proyecto 30-90 días: × 0.3
    • Proyecto > 90 días: Marcado como ⚠️ Proyecto antiguo reciclado, no participa en el ranking.

🔴 Lección Clave: UI-TARS-desktop (bytedance) tiene 29K estrellas pero el repositorio existe desde hace meses. El post en X era nuevo pero el proyecto no → Oportunidad de arbitraje cero. Alta popularidad no implica proyecto nuevo.

Matriz de Búsqueda (6 Capas de Detección)

Cada capa tiene un objetivo distinto, cubriendo toda la cadena: punto caliente salvaje → lanzamiento de producto → viralización → explosión de código abierto → necesidad FOMO.

L0: Virus Salvaje (ventana de 48h) 🆕

Detecta productos/juegos/herramientas que se viralizan sin etiqueta de IA. Es la capa clave para capturar juegos H5, herramientas web y otros puntos calientes no relacionados con IA.

("this is insane" OR "game changer" OR "holy shit" OR "mind blown" OR "this is crazy" OR "cannot believe" OR "blown away") (game OR tool OR app OR website OR "web app" OR builder OR generator OR platform OR "waitlist") min_faves:200 since:SINCE_48H

⚠️ Nota: L0 no requiere palabras clave de IA, se eleva min_faves a 200 para filtrar ruido.

L1: Lanzamiento de Nuevo Producto - IA (ventana de 24h)

Captura la señal más temprana de herramientas de IA "recién lanzadas/de código abierto".

("just launched" OR "just dropped" OR "just shipped" OR "new AI tool" OR "just released" OR "introducing" OR "launching today") (AI OR LLM OR GPT OR agent OR open source) min_faves:30 since:SINCE_24H

L1-b: Lanzamiento de Nuevo Producto General (ventana de 24h) 🆕

Captura nuevos lanzamientos de productos/juegos/herramientas no relacionados con IA.

("just launched" OR "just dropped" OR "just shipped" OR "just released" OR "introducing" OR "launching today" OR "new" OR "announcing") (game OR tool OR app OR website OR "web app" OR builder OR platform) min_faves:100 since:SINCE_24H

L2: Propagación Viral (ventana de 48h)

Captura productos de IA que están siendo masivamente compartidos.

("this is insane" OR "game changer" OR "holy shit" OR "mind blown" OR "this is crazy" OR "cannot believe" OR "blown away") (AI OR LLM OR GPT OR agent OR tool) min_faves:100 since:SINCE_48H

L3: Explosión de Estrellas en GitHub (ventana de 48h)

Proyectos de IA de código abierto que explotan repentinamente.

("github.com" OR "open source") (AI OR LLM OR GPT OR agent) ("stars" OR "trending" OR "blew up" OR "blowing up" OR "just hit") min_faves:100 since:SINCE_48H

L4: FOMO / Lista de Espera (ventana de 72h)

Esperar, pedir invitación, largas colas = señal de demanda desbordada.

("waitlist" OR "beta access" OR "invite" OR "early access" OR "can't wait" OR "need this") (AI OR LLM OR tool OR app) min_faves:50 since:SINCE_72H

Flujo de Ejecución

[Cron: Cada 12 horas]
       ↓
[Inyectar cookie de X en el navegador → Estado de sesión iniciada]
       ↓
[Búsqueda por capas L0-L4 → Recopilación con scroll en JS]
       ↓
[Combinar y deduplicar → Calcular puntuación_cruda]
       ↓
[🔍 Paso 2.5: Verificación de frescura del proyecto]
       ↓
[Extracción con IA: palabra clave + nombre del producto + antigüedad del proyecto + resumen en una frase]
       ↓
[Ordenar Top 10 → Generar reporte]
       ↓
[Enviar directamente a Feishu]

Paso 1: Construir URL de Búsqueda

const now = new Date();
const since24h = new Date(now - 24*60*60*1000).toISOString().split('T')[0];
const since48h = new Date(now - 48*60*60*1000).toISOString().split('T')[0];
const since72h = new Date(now - 72*60*60*1000).toISOString().split('T')[0];

const SEARCH_GROUPS = [
  {
    layer: 'L0-VirusSalvaje',
    q: `("this is insane" OR "game changer" OR "holy shit" OR "mind blown" OR "this is crazy" OR "cannot believe" OR "blown away") (game OR tool OR app OR website OR "web app" OR builder OR generator OR platform OR "waitlist") min_faves:200 since:${since48h}`,
    since: since48h
  },
  {
    layer: 'L1-NuevoProducto-IA',
    q: `("just launched" OR "just dropped" OR "just shipped" OR "new AI tool" OR "just released" OR "introducing") (AI OR LLM OR GPT OR agent OR "open source") min_faves:30 since:${since24h}`,
    since: since24h
  },
  {
    layer: 'L1-b-NuevoProductoGeneral',
    q: `("just launched" OR "just dropped" OR "just shipped" OR "just released" OR "introducing" OR "launching today" OR "new" OR "announcing") (game OR tool OR app OR website OR "web app" OR builder OR platform) min_faves:100 since:${since24h}`,
    since: since24h
  },
  {
    layer: 'L2-PropagacionViral',
    q: `("this is insane" OR "game changer" OR "holy shit" OR "mind blown" OR "this is crazy" OR "cannot believe" OR "blown away") (AI OR LLM OR GPT OR agent OR tool) min_faves:100 since:${since48h}`,
    since: since48h
  },
  {
    layer: 'L3-ExplosionGitHub',
    q: `("github.com" OR "open source") (AI OR LLM OR GPT OR agent) ("stars" OR "trending" OR "blew up" OR "blowing up" OR "just hit") min_faves:100 since:${since48h}`,
    since: since48h
  },
  {
    layer: 'L4-FOMO',
    q: `("waitlist" OR "beta access" OR "invite" OR "early access" OR "can't wait" OR "need this") (AI OR LLM OR tool OR app) min_faves:50 since:${since72h}`,
    since: since72h
  }
];

Paso 2: Recolectar y Calcular Puntuación de Calor

// Ejecutar en la consola del navegador
async function collectAndScore(sinceHours) {
  let allPosts = [];
  let prevCount = 0;
  let scrolls = 0;
  const maxScrolls = 12;

  while (scrolls < maxScrolls) {
    const articles = document.querySelectorAll('article');
    articles.forEach((a) => {
      const textEl = a.querySelector('[data-testid="tweetText"]') || a.querySelector('[lang]');
      const text = textEl?.innerText || '';
      if (!text || text.length < 30) return;

      const likesEl = a.querySelector('[data-testid="like"] span');
      let likes = 0;
      if (likesEl) {
        const txt = likesEl.innerText.replace(/,/g, '');
        if (txt.includes('K')) likes = Math.round(parseFloat(txt.replace('K','')) * 1000);
        else if (txt.includes('M')) likes = Math.round(parseFloat(txt.replace('M','')) * 1000000);
        else likes = parseInt(txt) || 0;
      }

      // Extraer autor
      const allLinks = Array.from(a.querySelectorAll('a[role="link"]'));
      const authorLink = allLinks.find(l => l.href && l.href.includes('x.com/') && !l.href.includes('/status/') && !l.href.includes('search') && !l.href.includes('hashtag'));
      const author = authorLink?.href?.split('/').pop() || '';

      // Extraer tiempo
      const timeEl = a.querySelector('time');
      const time = timeEl?.getAttribute('datetime') || '';

      // Extraer enlace
      const statusLink = allLinks.find(l => l.href && l.href.includes('/status/'));
      const link = statusLink?.href || '';

      // Extraer número de retweets
      const repostEl = a.querySelector('[data-testid="retweet"] span, [data-testid="repost"] span');
      let reposts = 0;
      if (repostEl) {
        const txt = repostEl.innerText.replace(/,/g, '');
        if (txt.includes('K')) reposts = Math.round(parseFloat(txt.replace('K','')) * 1000);
        else reposts = parseInt(txt) || 0;
      }

      if (!allPosts.some(p => p.text === text)) {
        allPosts.push({ text: text.substring(0, 400), likes, reposts, author, time, link });
      }
    });

    if (allPosts.length === prevCount && scrolls > 2) break;
    prevCount = allPosts.length;

    window.scrollBy(0, 600);
    await new Promise(r => setTimeout(r, 1500));
    scrolls++;
  }

  // Calcular puntuación de calor
  const now = new Date();
  return allPosts.map(p => {
    const postTime = new Date(p.time);
    const hoursAgo = Math.max(0.1, (now - postTime) / (1000 * 60 * 60));
    const likesPerHour = p.likes / hoursAgo;
    const hotScore = Math.round(p.likes * Math.log(1 + likesPerHour));
    return { ...p, hoursAgo: Math.round(hoursAgo * 10) / 10, likesPerHour: Math.round(likesPerHour), hotScore };
  }).sort((a, b) => b.hotScore - a.hotScore);
}

Paso 2.5: Verificación de Frescura del Proyecto (🔴 Obligatorio)

⚠️ Popularidad en X no implica proyecto nuevo. Un proyecto antiguo puede ser muy popular en X si se retoma, pero la ventana de arbitraje ya se cerró.

Primero revisar la lista de proyectos antiguos conocidos: references/known-stale-repos.md — los que coincidan se excluyen directamente, sin necesidad de navegar a GitHub.

Para cada señal que contenga un enlace de GitHub, es obligatorio navegar a la página del repositorio para verificar la fecha de creación:

// Ejecutar en la página del repositorio de GitHub
const createdEl = document.querySelector('relative-time');
const createdAt = createdEl?.getAttribute('datetime') || '';
const daysSinceCreated = (new Date() - new Date(createdAt)) / (1000 * 60 * 60 * 24);

let freshnessLabel;
if (daysSinceCreated < 7) freshnessLabel = '🆕 Nuevo';
else if (daysSinceCreated < 30) freshnessLabel = '📈 En ascenso';
else if (daysSinceCreated < 90) freshnessLabel = '⚠️ Proyecto antiguo reciclado';
else freshnessLabel = '💀 Anticuado (Excluido)';

const freshnessMultiplier = daysSinceCreated < 7 ? 1.0 : daysSinceCreated < 30 ? 0.7 : daysSinceCreated < 90 ? 0.3 : 0;
const finalScore = Math.round(rawHotScore * freshnessMultiplier);

Regla de filtro: Los proyectos con freshnessMultiplier === 0 se excluyen directamente, no aparecen en el reporte.

Para proyectos no basados en GitHub (como lanzamientos de nuevos productos, APIs, listas de espera), el multiplicador de frescura predeterminado es 0.8 (dando cierto margen a proyectos no open source), pero si se puede extraer la fecha de lanzamiento de la página, se usará la fecha real.

Paso 3: Reglas de Filtro

// Excluir temas: crypto, guerra, NSFW, meme coin
const excludePatterns = [
  /crypto|bitcoin|nft|token.*sale/i,
  /war|killed|attack|bomb/i,
  /nsfw|onlyfans|porn/i,
  /ai.*girlfriend|ai.*waifu/i,
  /meme.*coin/i
];

const filtered = allPosts
  .filter(p => p.hotScore >= 200)  // Umbral de puntuación de calor
  .filter(p => !excludePatterns.some(r => r.test(p.text)))
  .slice(0, 15);

Paso 4: Extracción de Palabras Clave con IA

Ejecutar el siguiente prompt para cada post:

Extrae las palabras clave de tendencia del siguiente post de X (sin limitarse a IA):

Post: {{ text }}
Publicado por: @{{ author }}
Likes: {{ likes }} | Retweets: {{ reposts }}
Publicado hace: {{ hoursAgo }} horas | Velocidad: {{ likesPerHour }} likes/hora

Salida:
- **Palabra clave / Nombre del producto**: [Extraer 1-3 palabras clave principales]
- **Resumen**: [Qué hace esta herramienta/proyecto]
- **Puntuación de calor**: [hotScore]
- **Señal de arbitraje**: Fuerte/Media/Débil (velocidad >500 likes/h = Fuerte, 100-500 = Media, <100 = Débil)

Paso 5: Formato del Reporte de Salida

Estructura de salida JSON (demand-radar.json)

Cada señal debe contener todos los siguientes campos:

{
  "date": "YYYY-MM-DD",
  "signals": [{
    "rank": 1,
    "keyword": "Nombre del producto/proyecto",
    "description": "Descripción en una frase de qué hace",
    "likes": 188, "velocity": 109, "hotScore": 884,
    "freshness": "🆕 Nuevo", "finalScore": 884, "signal": "🟡 Media",
    "author": "@handle",
    "tweetLink": "https://x.com/user/status/xxx",
    "hoursAgo": 1.7,
    "github": "owner/repo", "stars": "2.8K",
    "sourceLayer": "L1 Lanzamiento de Nuevo Producto-IA",
    "arbitrageDirection": "Dirección de arbitraje + dominio recomendado",
    "domainSuggestions": ["dominio1.com", "dominio2.com"],
    "actionPlan": "Plan de acción concreto: qué hacer, cómo hacerlo, por qué ahora es el momento",
    "painPoint": "Punto de dolor del usuario: por qué alguien necesita esto"
  }]
}

🔴 Regla clave: Cada señal debe incluir tweetLink (enlace al post original), arbitrageDirection (dirección de arbitraje), domainSuggestions (sugerencias de dominio), actionPlan (plan de acción), painPoint (punto de dolor). Las señales que falten alguno de estos campos se consideran incompletas y no aparecerán en el reporte.

🔍 Radar de Tendencias AI | {{DATE}} {{TIME}}

> **Fuente de datos: Captura en tiempo real de X/Twitter @claw0x**

## 📰 Barra lateral de Noticias de Hoy

| Tema | Hora | Número de Posts |
|------|------|-----------------|
| {{topic}} | {{age}} | {{posts}} |

## 🔥 Top 10 de Popularidad

| # | Palabra clave | Likes | Velocidad (likes/h) | Puntuación de calor | Frescura | Puntuación final | Señal de arbitraje |
|---|---------------|-------|---------------------|--------------------|---------|-----------------|-------------------|
| 1 | xxx | 5.2K | 1,200 | 36,000 | 🆕 0.8 días | 36,000 | 🔴 Fuerte |

### 🚀 Acción Inmediata (Señal de arbitraje ≥ Media)
1. **{{keyword}}** — {{summary}}
   - @{{author}} | {{likes}} likes | {{likesPerHour}} likes/hora | {{hoursAgo}}h atrás
   - GitHub: `{{owner/repo}}` | Creado hace {{daysAgo}} días | {{stars}} estrellas
   - Dominio: {{domain}}.com ({{status}})
   - Dirección de arbitraje: {{arbitrageDirection}}

## ⚠️ Excluidos (Proyectos Antiguos Reciclados)

| Proyecto | Razón | Estrellas | Antigüedad |
|----------|-------|-----------|------------|
| UI-TARS-desktop (ByteDance) | Agente de automatización de escritorio, retomado en X | 30.9K | >12 meses |

---

📊 Parámetros de Escaneo
- Capas de búsqueda: L0 Salvaje / L1 Nuevo-IA / L1-b Nuevo-General / L2 Viral / L3 GitHub / L4 FOMO
- Rango de tiempo: {{since}} - {{until}}
- Posts originales: {{raw}} → Deduplicados {{deduped}} → Filtrados {{filtered}} → Seleccionados {{top}}
- Verificación GitHub: Excluidos {{excluded}} proyectos antiguos
- Estado del navegador: Sesión iniciada @claw0x

## 🔮 Perspectivas Clave

(Listar 2-4 tendencias clave detectadas en el escaneo de hoy)

Paso 6: Despliegue (git push → CF Pages)

cd /Volumes/sunzi/Code/ludusAI
# Guardar instantánea JSON por día para las páginas de detalle /radar
cp public/data/demand-radar.json "public/data/radar/YYYY-MM-DD.json" 2>/dev/null || true
# Construir manifiesto
ls public/data/radar/*.json | python3 -c "
import json,os,sys
m=[]
for f in sys.stdin.read().strip().split():
  d=json.load(open(f))
  t=d.get('top10',[])
  m.append({'date':os.path.basename(f).replace('.json',''),'topKeywords':[x.get('keyword','') for x in t[:3]],'signalCount':len(t),'newsTopics':[n.get('topic','') for n in d.get('news',[])[:2]],'insights':d.get('insights',[])[:3],'strongSignal':sum(1 for x in t if 'Fuerte' in x.get('signal','')),'mediumSignal':sum(1 for x in t if 'Media' in x.get('signal','')),'raw':d.get('scanParams',{}).get('raw',0),'top':d.get('scanParams',{}).get('top',0),'excluded':d.get('scanParams',{}).get('githubExcluded',0),'layers':d.get('scanParams',{}).get('layers',[])})
m.sort(key=lambda x:x['date'],reverse=True)
json.dump(m,open('public/data/radar-manifest.json','w'),ensure_ascii=False,indent=2)
print(f'manifesto: {len(m)} reportes')
"
git add daily-reports/YYYY-MM-DD/x-demand-radar.md public/data/demand-radar.json public/data/radar/YYYY-MM-DD.json public/data/radar-manifest.json
git commit -m "radar: YYYY-MM-DD HH:MM CST - <resumen de señal superior>"
git pull --rebase && git push  # ⚠️ Obligatorio hacer rebase para evitar rechazo por non-fast-forward

⚠️ Es obligatorio usar send_message para enviar a Feishu, no depender de cron announce.

Inyección de Cookie (Antes de Cada Ejecución)

⚠️ Ver detalles completos en references/x-auth-and-collection.md — Incluye flujo completo de inyección, template de IIFE asíncrono, análisis de K/M, fórmula de hotScore.

// Primero navegar a x.com, luego inyectar, y finalmente verificar inicio de sesión en x.com/home
document.cookie = "auth_token=06dec19f563932f39d96f93e9d8c005d3de9b7a9; domain=.x.com; path=/; secure; SameSite=None";

Configuración

  • Cuenta X: @claw0x (navegador con sesión iniciada)
  • Frecuencia de ejecución: Cada 12 horas (9:00 + 21:00 CST)
  • Validez de la cookie: El usuario debe actualizar auth_token periódicamente
  • Destino de salida: daily-reports/YYYY-MM-DD/x-demand-radar.md + public/data/demand-radar.json
  • Despliegue: git push → CF Pages construye automáticamente

Errores Comunes (Pitfalls)

  1. La cookie no persiste entre sesiones: Cada vez que Hermes se inicia es una nueva instancia del navegador, la cookie debe reinyectarse. El primer paso de cada ejecución del cron es inyectar la cookie → verificar inicio de sesión.
  2. browser_console no puede usar return directamente: Debe envolverse en un IIFE (async function() { ... })(), de lo contrario lanza error Illegal return statement. Ver detalles en references/x-auth-and-collection.md.
  3. La escasez de resultados de búsqueda es normal: Con min_faves≥100 y consultas booleanas complejas, obtener entre 4 y 16 resultados por capa es normal. L3 (GitHub) es especialmente escaso. No reducir el umbral min_faves, o el ruido abrumará la señal.
  4. La barra lateral de Noticias de Hoy es una fuente gratuita de señales: Cada página de búsqueda muestra temas de noticias del día y el número de posts a la derecha. Son puntos calientes deterministas y deben incluirse en el reporte.
  5. El envío debe hacerse directamente a Feishu: No depender de cron announce. Enviar el mensaje inmediatamente después de escribir los archivos de resultados.
  6. Los términos de búsqueda son globales: Todas las consultas se hacen en inglés, sin limitación geográfica. El reporte se centra en tendencias globales (sin limitarse a IA).
  7. execute_code no soporta JSON en línea extenso: Incrustar datos JSON directamente en una cadena de Python puede causar errores de análisis como SyntaxError: leading zeros. La forma correcta es usar write_file para escribir en un archivo temporal y luego usar open() para leer: with open('/tmp/data.json') as f: data = json.load(f). De la misma manera, read_file devuelve texto con prefijo de número de línea (ej. 1|{...}), no se puede usar directamente json.loads(), se debe usar open() nativo.
  8. git push puede ser rechazado (non-fast-forward): El repositorio remoto puede tener otras instancias del cron o commits manuales anteriores al push actual. Ejecutar siempre git pull --rebase && git push, nunca solo git push.
  9. El texto truncado al recolectar con JS puede perder URLs de GitHub: text.substring(0, 300) puede cortar el enlace de GitHub al final. Si el post menciona GitHub pero no se extrajo el enlace github.com, es necesario navegar al post original (browser_navigate al campo link) para extraer la URL completa, o buscar el nombre del repositorio en GitHub.
  10. La capa L0 genera bastante ruido (verificado): min_faves:200 no frena suficientes posts coloquiales con "game changer" (política, deportes, vida cotidiana). En la primera ejecución, de 19 resultados de L0 solo 4 tenían valor potencial como producto. Sugerencia: Si en dos ejecuciones consecutivas la relación señal/ruido de L0 es <20%, elevar min_faves a 500, o añadir términos de exclusión en la consulta L0 como -(political OR sports OR religion).

Fuentes de Señal Complementarias (Opcional)

Skills relacionados