Docs/Skills and code/src/

src/discovery/v2-utils.js

Code64 lines3.2 KBsrc/

export function parseArgs(argv) {
  const out = {};
  for (const arg of argv) {
    if (!arg.startsWith('--')) continue;
    const [key, ...rest] = arg.slice(2).split('=');
    out[key] = rest.length ? rest.join('=') : true;
  }
  return out;
}

export function normalizeQuery(value) {
  return String(value ?? '').replace(/[\r\n\t]+/g, ' ').replace(/\s+/g, ' ').trim();
}

export function uniqueQueries(items, limit = 8) {
  const seen = new Set();
  const result = [];
  for (const item of items) {
    const query = normalizeQuery(typeof item === 'string' ? item : item?.query);
    const key = query.toLocaleLowerCase();
    if (query.length < 5 || query.length > 160 || seen.has(key)) continue;
    seen.add(key);
    result.push({ ...((typeof item === 'object' && item) ? item : {}), query });
    if (result.length >= limit) break;
  }
  return result;
}

export function videoToChannelUrl(video) {
  const url = String(video?.channel_url ?? '').trim();
  if (!url) return null;
  if (/youtube\.com\/(channel\/|@|user\/|c\/)/i.test(url)) return url.replace(/\/$/, '');
  return url.startsWith('http') ? url.replace(/\/$/, '') : null;
}

export function scoreDiscoveryEvidence(row) {
  const views = Math.max(0, Number(row.max_video_views) || 0);
  const videos = Math.max(0, Number(row.found_videos) || 0);
  const subscribers = Number(row.subscriber_count);
  const catalogue = Number(row.video_count);
  const qualification = String(row.classification ?? 'unknown').toLowerCase();
  const subsKnown = Number.isFinite(subscribers) && subscribers > 0;
  const ratio = subsKnown ? views / subscribers : null;
  const catalogueKnown = Number.isFinite(catalogue) && catalogue > 0;
  const subscriberEfficiency = subsKnown && catalogueKnown ? subscribers / catalogue : null;
  const catalogueDrag = subsKnown && catalogueKnown && catalogue > 100 && subscribers < 1000
    ? Math.min(12, Math.max(0, Math.log10(catalogue / 100) * 4 + Math.log10(1000 / (subscribers + 1)) * 2))
    : 0;
  const components = {
    observedReach: Math.min(30, Math.log10(views + 1) * 4.2),
    repeatedEvidence: Math.min(20, videos * 5),
    smallChannelUpside: ratio == null ? null : Math.min(20, Math.max(0, Math.log2(ratio + 1) * 5)),
    subscriberEfficiency: subscriberEfficiency == null ? null : Math.min(15, Math.max(0, Math.log10(subscriberEfficiency + 1) * 6)),
    relevance: qualification === 'core' ? 20 : qualification === 'adjacent' ? 12 : qualification === 'unknown' ? 5 : 0,
    samplePenalty: videos < 2 ? 8 : videos < 4 ? 4 : 0,
    tinyChannelPenalty: (subsKnown && subscribers < 100 && videos < 5) ? 5 : 0,
    catalogueDrag
  };
  const available = Object.entries(components).filter(([k,v]) => !['samplePenalty','tinyChannelPenalty','catalogueDrag'].includes(k) && v != null);
  const positive = available.reduce((sum, [,v]) => sum + v, 0);
  const score = Math.max(0, Math.min(100, positive - components.samplePenalty - components.tinyChannelPenalty - components.catalogueDrag));
  return { score: Math.round(score * 100) / 100, components, viewsPerSubscriber: ratio, subscribersPerCatalogueVideo: subscriberEfficiency, confidence: videos >= 4 && subsKnown && catalogueKnown ? 'moderate' : videos >= 2 ? 'low' : 'very_low' };
}

Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.