Docs/Skills and code/skill winning-video-teardown

.claude/skills/winning-video-teardown/scripts/report.js

Code48 lines4.5 KBSkill: winning-video-teardown

What the file says about itself

Assembles the per-video teardowns into one readable markdown file (teardowns.md) in the run directory. node report.js --run <runDir>

#!/usr/bin/env node
// Assembles the per-video teardowns into one readable markdown file (teardowns.md) in the run directory.
//   node report.js --run <runDir>

const fs = require('fs');
const path = require('path');

const run = path.resolve(process.argv[process.argv.indexOf('--run') + 1] || '');
if (!fs.existsSync(path.join(run, 'manifest.json'))) { console.error('usage: node report.js --run <runDir>'); process.exit(1); }
const manifest = JSON.parse(fs.readFileSync(path.join(run, 'manifest.json'), 'utf8'));
const compact = (x) => (x == null ? 'n/a' : new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 }).format(x));
const ev = (list) => (list || []).slice(0, 2).map((e) => `> [${e.ts}] "${e.quote}" (${e.gloss})`).join('\n');

const out = ['# Video teardowns', '', `${manifest.cases.length} videos. Each section says what the video does well and how, with the exact moments quoted.`, ''];
for (const group of [...new Set(manifest.cases.map((c) => c.group))]) {
  out.push(`# ${group[0].toUpperCase() + group.slice(1)} videos`, '');
  for (const c of manifest.cases.filter((x) => x.group === group)) {
    if (!fs.existsSync(c.output)) { out.push(`## ${c.cid}: ${c.title}`, '', '(analysis missing)', ''); continue; }
    const a = JSON.parse(fs.readFileSync(c.output, 'utf8'));
    const p = a.packaging, s = a.script;
    out.push(`## ${c.cid}: [${c.title}](${c.url})`, '',
      `${c.channel} | ${compact(c.subscribers)} subscribers | ${compact(c.views)} views | ${c.outlier ?? ''} | ${Math.round(c.duration_sec / 60)} min | ${c.language}`, '',
      `**Why it works:** ${a.one_line}`, '',
      '### Packaging', '',
      `- **Promise:** ${p.title.promise} (specificity: ${p.title.specificity})`,
      `- **Gap it opens:** ${p.title.info_gap?.type ?? 'n/a'}${p.title.info_gap?.what_is_withheld ? ': ' + p.title.info_gap.what_is_withheld : ''}`,
      `- **Formula:** ${p.title.formula}`,
      ...p.title.mechanisms.map((m) => `- **${m.id}:** ${m.how}`),
      p.thumbnail?.seen ? `- **Thumbnail:** ${p.thumbnail.description} ${(p.thumbnail.mechanisms || []).map((m) => `**${m.id}** ${m.how}`).join(' ')} Division of labour: ${p.thumbnail.division_of_labour}` : '- **Thumbnail:** not analysed',
      `- **Does the video earn the promise?** ${p.promise_delivery.delivered} (at ${p.promise_delivery.delivered_at}); clickbait risk: ${p.promise_delivery.clickbait_risk}. ${p.promise_delivery.why}`, '',
      '### What it does well, and how', '',
      ...a.doing_well.flatMap((d, i) => [`${i + 1}. **${d.move}.** ${d.how}`, ev(d.evidence), '']),
      '### Hook (first minute)', '', s.hook.summary, '', `Time to the promise: ${s.hook.time_to_promise_sec} s.`, '',
      '### Structure', '', ...s.structure.map((x) => `- ${x.from} to ${x.to}: ${x.function}${x.note ? ' (' + x.note + ')' : ''}`), '',
      ...(s.hook.first_payoff_sec != null ? [`Opening: first payoff at ${s.hook.first_payoff_sec} s; ${s.hook.figures_in_first_60s ?? '?'} figures in the first 60 s; destination hint: ${s.hook.destination_hint?.present ? s.hook.destination_hint.what : 'none'}; extra promises: ${(s.hook.extra_promises || []).map((p) => `${p.promise} (seeded ${p.seeded_at}, paid ${p.paid_at})`).join('; ') || 'none'}.`, ''] : []),
      ...(s.junctions ? ['### How the stretches connect', '', ...s.junctions.map((j) => `- ${j.at} **${j.link}**: ${j.from} -> ${j.to}${j.evidence?.[0] ? ` ("${j.evidence[0].quote}")` : ''}`), ''] : []),
      ...(s.numbers ? ['### Figures and stakes', '', ...(s.numbers.hero_figures || []).map((h) => `- ${h.ts} ${h.what}: framed by ${h.anchored_by}`), ...(s.numbers.naked_examples || []).map((n) => `- naked at ${n.ts}: "${n.quote}" (${n.why})`), `- Load: ${s.numbers.load_note}`, ''] : []),
      '### Open loops', '', ...(s.loops.length ? s.loops.map((l) => `- ${l.opened_at} -> ${l.closed_at ?? 'never closed'}: ${l.question}${l.how_kept_alive ? ' (kept alive by: ' + l.how_kept_alive + ')' : ''}`) : ['- none']), '',
      `### Voice and pacing`, '', s.voice, '', ...(s.pacing_devices || []).map((x) => `- ${x}`), '',
      `**Ending:** ${s.ending?.type}: ${s.ending?.what_it_does}`, '',
      '### Weak points', '', ...(a.weak_points.length ? a.weak_points.map((w) => `- ${w.where}: ${w.what} (${w.risk})`) : ['- none found']), '',
      '### Moves to copy', '', ...a.transferable_moves.map((m) => `- ${m}`), '',
      `_Analyst confidence: ${a.confidence}. ${a.transcript_quality_notes || ''}_`, '', '---', '');
  }
}
fs.writeFileSync(path.join(run, 'teardowns.md'), out.join('\n'), 'utf8');
console.log(`Wrote ${path.join(run, 'teardowns.md')}`);

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.