Files
justvitamin/scripts/build-competitor-comparison.ts
Omair Saleh 056c47581f feat: editorial review dashboard + elite-grade pilot batch (5 SKUs)
Ships the second dashboard surface — a Pattern Library + Preview Theatre — that
presents the 4-section PDP pilot batch back to Umar, compliance, and the board
in an editorial format. Adds the full data layer that drives it: 5 source-backed
per-SKU drafts at QA 100/100, 15 competitor PDP semantic extracts, PubMed
evidence packs, EFSA claims library extension, JV brand voice guide, hand-curated
product FAQs, and the Matrixify-ready CSV exports for Lewis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 18:50:09 +08:00

41 lines
2.1 KiB
TypeScript

#!/usr/bin/env bun
import { intelPath, listProducts, loadCompetitorConfig, readJson, readPilotDoc, sourcePath, sourceStatus, writeJson, now, slug } from './lib/intel-utils'
const competitors = loadCompetitorConfig()
for (const product of listProducts()) {
const sku = product.sku
const { aspects, sw } = readPilotDoc(sku)
const seededBenefits = product.metafields?.pdpProductBenefits || []
const rows = competitors.map((c: any, index: number) => {
const doc = readJson(sourcePath('competitor-pdps', `${slug(c.brand)}.json`), null)
const okPages = (doc?.pages || []).filter((p: any) => p.ok)
return {
rank: index + 1,
brand: c.brand,
domain: c.domain,
sourceCoverage: okPages.length ? `${okPages.length}/${(doc?.pages || []).length} URLs fetched` : 'pending crawl/import',
priceSignal: '',
format: '',
dose: '',
claims: '',
whitespace: okPages.length ? 'Homepage/PDP probe captured. Needs product-specific extraction before final comparison.' : 'Pending competitor PDP/Amazon/Trustpilot evidence. Do not use for final copy yet.',
status: okPages.length ? 'source_probe_available' : 'needs_ingestion'
}
})
const jvSignals = (sw.items || []).length
? sw.items.slice(0, 5).map((item: any) => ({ rank: item.rank, signal: item.insight, source: 'Feefo strengths/weaknesses' }))
: seededBenefits.slice(0, 5).map((benefit: string, index: number) => ({ rank: index + 1, signal: benefit, source: 'product content benefits seed; needs competitor validation' }))
writeJson(intelPath(sku, 'competitor-comparison.json'), {
sku,
name: aspects.name || product.name || sku,
generatedAt: now(),
generatedBy: 'build-competitor-comparison.ts',
n: rows.length,
sourceStatus: { competitorPdp: sourceStatus('competitor-pdps'), amazon: sourceStatus('amazon'), trustpilot: sourceStatus('trustpilot') },
coverage: (sw.items || []).length ? 'pilot_feefo_plus_competitor_probes' : 'catalogue_queue_seeded_from_product_content_and_competitor_probes',
competitors: rows,
jvSignals
})
console.log(`${sku}: wrote competitor comparison for ${rows.length} brands`)
}