#!/usr/bin/env bun /** * Generate project docs: extract component APIs + inject index into AGENTS.md. * * Normalizes agent instruction files (CLAUDE.md, GEMINI.md) into AGENTS.md * as the canonical file, replacing originals with symlinks. * * Usage: bunx @numbered/docs-to-context [project_root] [options] * * Options: * --platform nextjs|shopify Force platform detection * --dirs dir1 dir2 Custom scan directories (Next.js only) * --output path Custom output directory * --ignore cat1 cat2 Skip doc categories (layout, a11y, best-practices, frontend, entities, components) */ import { existsSync, rmSync } from 'node:fs' import { join, resolve } from 'node:path' import { detectPlatform, extract } from './extract' import { inject, normalizeAgentFiles } from './inject' import { log } from './log' // ── Help ──────────────────────────────────────────────────────────────── function showHelp() { console.log(` Usage: bunx @numbered/docs-to-context [project_root] [options] Extracts component APIs and injects a doc index into AGENTS.md. Normalizes CLAUDE.md / GEMINI.md into AGENTS.md (symlinks). Options: --platform Force platform (nextjs, shopify) --dirs Custom component scan dirs (Next.js only) --output Custom output directory -o Alias for --output --ignore ... Skip doc categories: layout, a11y, best-practices, frontend, entities, components --help, -h Show this help message Examples: bunx @numbered/docs-to-context bunx @numbered/docs-to-context ./apps/web --platform nextjs bunx @numbered/docs-to-context --ignore layout entities bunx @numbered/docs-to-context --dirs components src/ui `) process.exit(0) } // ── Arg parsing ──────────────────────────────────────────────────────── const args = process.argv.slice(2) if (args.includes('--help') || args.includes('-h')) showHelp() let projectRoot = '.' let platform: 'nextjs' | 'shopify' | undefined let dirs: string[] | undefined let outputDir: string | undefined const ignore = new Set() const VALID_IGNORE = new Set(['layout', 'a11y', 'best-practices', 'frontend', 'entities', 'components']) for (let i = 0; i < args.length; i++) { const arg = args[i] if (arg === '--platform' && args[i + 1]) { platform = args[++i] as 'nextjs' | 'shopify' } else if (arg === '--dirs') { dirs = [] while (args[i + 1] && !args[i + 1].startsWith('--')) { dirs.push(args[++i]) } } else if (arg === '--ignore') { while (args[i + 1] && !args[i + 1].startsWith('--')) { const cat = args[++i] if (VALID_IGNORE.has(cat)) { ignore.add(cat) } else { log.warn(`Unknown ignore category: ${cat}. Available: ${[...VALID_IGNORE].join(', ')}`) } } } else if ((arg === '--output' || arg === '-o') && args[i + 1]) { outputDir = args[++i] } else if (!arg.startsWith('-')) { projectRoot = arg } } projectRoot = resolve(projectRoot) // ── Detect platform early ────────────────────────────────────────────── if (!platform) { const detected = detectPlatform(projectRoot) if (detected) platform = detected } // ── Run ──────────────────────────────────────────────────────────────── log.banner() log.section('Normalize') const agentsMdPath = normalizeAgentFiles(projectRoot) const contextDir = join(projectRoot, '.context') if (existsSync(contextDir)) { rmSync(contextDir, { recursive: true }) } log.section('Extract') if (!ignore.has('components')) { extract({ projectRoot, platform, dirs, outputDir }) } log.section('Inject') inject({ projectRoot, docsDir: outputDir, platform, ignore, agentsMdPath }) log.done()