// Utilities for defining safe Commander placeholder descriptors. import type { Command } from "commander"; import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js"; import type { NamedCommandDescriptor } from "./command-group-descriptors.js"; /** Minimal descriptor shape used before a command is fully registered. */ type CommandDescriptorLike = Pick; const SAFE_COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; /** Normalize and validate a command descriptor name for safe Commander registration. */ export function normalizeCommandDescriptorName(name: string): string | null { const normalized = name.trim(); return SAFE_COMMAND_NAME_PATTERN.test(normalized) ? normalized : null; } function assertSafeCommandDescriptorName(name: string): string { const normalized = normalizeCommandDescriptorName(name); if (!normalized) { throw new Error(`Invalid CLI command name: ${JSON.stringify(name.trim())}`); } return normalized; } /** Strip unsafe terminal content from descriptor descriptions. */ export function sanitizeCommandDescriptorDescription(description: string): string { return sanitizeForLog(description).trim(); } /** Merge descriptor groups while keeping the first descriptor for each command name. */ export function collectUniqueCommandDescriptors( descriptorGroups: readonly (readonly TDescriptor[])[], ): TDescriptor[] { const seen = new Set(); const descriptors: TDescriptor[] = []; for (const group of descriptorGroups) { for (const descriptor of group) { if (seen.has(descriptor.name)) { continue; } seen.add(descriptor.name); descriptors.push(descriptor); } } return descriptors; } /** Add safe placeholder commands to Commander without duplicating existing command names. */ export function addCommandDescriptorsToProgram( program: Command, descriptors: readonly CommandDescriptorLike[], existingCommands: Set = new Set(), ): Set { for (const descriptor of descriptors) { const name = assertSafeCommandDescriptorName(descriptor.name); if (existingCommands.has(name)) { continue; } program .command(name, { hidden: descriptor.hidden }) .description(sanitizeCommandDescriptorDescription(descriptor.description)); existingCommands.add(name); } return existingCommands; }