import { readdirSync, readFileSync, existsSync } from "node:fs"; import { join } from "node:path"; import { PACKAGE_ROOT } from "../../../util"; import { type CommandResult, success } from "../../lib/command-result"; import { MODULE_PATHS } from "../../lib/paths"; const MODULES_DIR = join(PACKAGE_ROOT, "src", "modules"); interface ModuleOverview { name: string; commandCount: number; queryCount: number; modelCount: number; overview: string; dependencies: string[]; } function countDocs(dir: string): number { if (!existsSync(dir)) return 0; return readdirSync(dir).filter((f) => f.endsWith(".md")).length; } function extractOverview(readmePath: string): string { if (!existsSync(readmePath)) return ""; const content = readFileSync(readmePath, "utf-8"); const overviewMatch = /## Overview\n\n([\s\S]*?)(?=\n##\s|\n$)/.exec(content); if (!overviewMatch) return ""; const firstParagraph = overviewMatch[1].trim().split("\n\n")[0]; return firstParagraph; } function extractDependencies(readmePath: string): string[] { if (!existsSync(readmePath)) return []; const content = readFileSync(readmePath, "utf-8"); const depsMatch = /## Module Dependencies\n\n([\s\S]*?)(?=\n##\s|$)/.exec(content); if (!depsMatch) return []; const deps: string[] = []; const linePattern = /^- \[([^\]]+)\]/; for (const line of depsMatch[1].trim().split("\n")) { const match = linePattern.exec(line); if (match) deps.push(match[1]); } return deps; } export function getModuleOverviews(): ModuleOverview[] { if (!existsSync(MODULES_DIR)) return []; return readdirSync(MODULES_DIR, { withFileTypes: true }) .filter((d) => d.isDirectory()) .map((d) => { const modDir = join(MODULES_DIR, d.name); const readmePath = join(modDir, "README.md"); return { name: d.name, commandCount: countDocs(join(modDir, MODULE_PATHS.docs.command)), queryCount: countDocs(join(modDir, MODULE_PATHS.docs.query)), modelCount: countDocs(join(modDir, MODULE_PATHS.docs.model)), overview: extractOverview(readmePath), dependencies: extractDependencies(readmePath), }; }) .sort((a, b) => a.name.localeCompare(b.name)); } export function formatModuleOverviews(modules: ModuleOverview[]): string { if (modules.length === 0) return "No modules found."; const lines: string[] = []; for (const mod of modules) { const counts = [ `${mod.commandCount} commands`, `${mod.queryCount} queries`, `${mod.modelCount} models`, ].join(", "); lines.push(`# ${mod.name} (${counts})`); if (mod.overview) { lines.push(mod.overview); } if (mod.dependencies.length > 0) { lines.push(`Dependencies: ${mod.dependencies.join(", ")}`); } else { lines.push("Dependencies: none"); } lines.push(""); } lines.push(`${modules.length} modules`); return lines.join("\n"); } export function runDocModules(format = "text"): CommandResult { const modules = getModuleOverviews(); if (format === "json") { console.log(JSON.stringify(modules, null, 2)); } else { console.log(formatModuleOverviews(modules)); } return success(); }