import { z, type ZodType } from "zod"; export interface Stream { chunk(data: unknown): void; } export interface HandlerDef { input: I; output: O; fn: (input: z.infer, stdin: string, stream?: Stream) => Promise>; } export function handler( input: I, output: O, fn: (input: z.infer, stdin: string, stream?: Stream) => Promise>, ): HandlerDef { return { input, output, fn: async (raw: z.infer, stdin: string, stream?: Stream) => { const parsed = await input.parseAsync(raw); return fn(parsed, stdin, stream); }, }; } // === Command Groups (sub-commands) === export interface SubcommandDef { description: string; handler: HandlerDef; } export interface GroupDef { __type: "group"; description: string; commands: Record; } export function isGroupDef(value: unknown): value is GroupDef { return ( value !== null && typeof value === "object" && (value as GroupDef).__type === "group" ); } export function commandGroup( description: string, commands: Record, ): GroupDef { const mapped: Record = {}; for (const [name, [desc, h]] of Object.entries(commands)) { mapped[name] = { description: desc, handler: h }; } return { __type: "group", description, commands: mapped }; }