{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "generated-command",
  "title": "Generated Command",
  "description": "A component that generates CLI commands based on tool configuration and parameter values with copy and save functionality.",
  "dependencies": [
    "react",
    "lucide-react",
    "sonner"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "registry/commandly/generated-command.tsx",
      "content": "import { ParameterValue, Tool, Command } from \"@/components/commandly/types/flat\";\nimport { generateCommand } from \"@/components/commandly/utils/flat\";\nimport { Button } from \"@/components/ui/button\";\nimport { CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Label } from \"@/components/ui/label\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { cn } from \"@/lib/utils\";\nimport { TerminalIcon, CopyIcon, SaveIcon } from \"lucide-react\";\nimport {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n  type ComponentProps,\n  type ReactNode,\n} from \"react\";\nimport { toast } from \"sonner\";\n\ninterface GeneratedCommandProps {\n  tool: Tool;\n  selectedCommand?: Command | null;\n  parameterValues: Record<string, ParameterValue>;\n  onSaveCommand?: (command: string) => void;\n  useLongFlag?: boolean;\n  children?: ReactNode;\n}\n\ninterface GeneratedCommandContextValue {\n  generatedCommand: string;\n  useLongFlag: boolean;\n  setUseLongFlag: (value: boolean) => void;\n  supportsFlagPreference: boolean;\n  onCopyCommand: () => void;\n  onSaveCommand?: (command: string) => void;\n}\n\nconst GeneratedCommandContext = createContext<GeneratedCommandContextValue | null>(null);\n\nfunction useGeneratedCommandContext() {\n  const context = useContext(GeneratedCommandContext);\n\n  if (!context) {\n    throw new Error(\"GeneratedCommand compound components must be used within GeneratedCommand.\");\n  }\n\n  return context;\n}\n\nfunction GeneratedCommandRoot({\n  tool,\n  selectedCommand: providedCommand,\n  parameterValues,\n  onSaveCommand,\n  useLongFlag = false,\n  children,\n}: GeneratedCommandProps) {\n  const [prefersLongFlag, setPrefersLongFlag] = useState(useLongFlag);\n  const supportsFlagPreference = useMemo(\n    () =>\n      tool.parameters.some(\n        (parameter) =>\n          parameter.parameterType !== \"Argument\" &&\n          Boolean(parameter.shortFlag) &&\n          Boolean(parameter.longFlag),\n      ),\n    [tool.parameters],\n  );\n\n  useEffect(() => {\n    setPrefersLongFlag(useLongFlag);\n  }, [useLongFlag]);\n\n  const generatedCommand = useMemo(\n    () =>\n      generateCommand(tool, parameterValues, {\n        selectedCommand: providedCommand,\n        useLongFlag: prefersLongFlag,\n      }),\n    [tool, parameterValues, providedCommand, prefersLongFlag],\n  );\n\n  const copyCommand = useCallback(() => {\n    navigator.clipboard.writeText(generatedCommand);\n    toast(\"Command copied!\");\n  }, [generatedCommand]);\n\n  const contextValue = useMemo(\n    () => ({\n      generatedCommand,\n      useLongFlag: prefersLongFlag,\n      setUseLongFlag: setPrefersLongFlag,\n      supportsFlagPreference,\n      onCopyCommand: copyCommand,\n      onSaveCommand,\n    }),\n    [generatedCommand, prefersLongFlag, supportsFlagPreference, copyCommand, onSaveCommand],\n  );\n\n  return (\n    <GeneratedCommandContext.Provider value={contextValue}>\n      {generatedCommand ? (\n        (children ?? (\n          <div className=\"min-w-0 space-y-4\">\n            <GeneratedCommandOutput />\n            <GeneratedCommandActions />\n          </div>\n        ))\n      ) : (\n        <GeneratedCommandEmptyState />\n      )}\n    </GeneratedCommandContext.Provider>\n  );\n}\n\nfunction GeneratedCommandToolbar({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      className={cn(\"flex flex-wrap items-center justify-end gap-2\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction GeneratedCommandHeader({\n  children,\n  className,\n  ...props\n}: ComponentProps<typeof CardHeader>) {\n  return (\n    <CardHeader\n      className={cn(\"flex flex-row items-center justify-between gap-3 space-y-0\", className)}\n      {...props}\n    >\n      <CardTitle className=\"flex items-center gap-2\">\n        <TerminalIcon className=\"h-5 w-5\" />\n        Generated Command\n      </CardTitle>\n      {children}\n    </CardHeader>\n  );\n}\n\nfunction GeneratedCommandFlagPreference({\n  className,\n  ...props\n}: Omit<ComponentProps<typeof Switch>, \"checked\" | \"onCheckedChange\">) {\n  const { supportsFlagPreference, useLongFlag, setUseLongFlag } = useGeneratedCommandContext();\n\n  if (!supportsFlagPreference) return null;\n\n  return (\n    <Label className=\"shrink-0 gap-2 text-xs text-muted-foreground\">\n      <span className=\"font-mono tracking-[0.12em] uppercase\">Long flags</span>\n      <Switch\n        checked={useLongFlag}\n        onCheckedChange={setUseLongFlag}\n        aria-label=\"Long flags\"\n        className={className}\n        {...props}\n      />\n    </Label>\n  );\n}\n\nfunction GeneratedCommandOutput({ className, ...props }: ComponentProps<\"div\">) {\n  const { generatedCommand } = useGeneratedCommandContext();\n\n  return (\n    <div\n      className={cn(\"overflow-x-auto rounded bg-muted p-4 font-mono text-sm\", className)}\n      {...props}\n    >\n      <div className=\"min-w-max whitespace-nowrap\">{generatedCommand}</div>\n    </div>\n  );\n}\n\nfunction GeneratedCommandActions({ className, ...props }: ComponentProps<\"div\">) {\n  const { generatedCommand, onCopyCommand, onSaveCommand } = useGeneratedCommandContext();\n\n  return (\n    <div\n      className={cn(\"flex flex-col gap-2 sm:flex-row\", className)}\n      {...props}\n    >\n      <Button\n        onClick={onCopyCommand}\n        variant=\"outline\"\n        className=\"w-full sm:flex-1\"\n      >\n        <CopyIcon className=\"mr-2 h-4 w-4\" />\n        Copy Command\n      </Button>\n      {onSaveCommand && (\n        <Button\n          onClick={() => onSaveCommand(generatedCommand)}\n          variant=\"outline\"\n          className=\"w-full sm:flex-1\"\n        >\n          <SaveIcon className=\"mr-2 h-4 w-4\" />\n          Save Command\n        </Button>\n      )}\n    </div>\n  );\n}\n\nfunction GeneratedCommandEmptyState() {\n  return (\n    <div className=\"py-8 text-center\">\n      <TerminalIcon className=\"mx-auto mb-4 h-12 w-12 text-muted-foreground\" />\n      <p className=\"text-muted-foreground\">Configure parameters to generate the command.</p>\n    </div>\n  );\n}\n\nexport const GeneratedCommand = Object.assign(GeneratedCommandRoot, {\n  Header: GeneratedCommandHeader,\n  Toolbar: GeneratedCommandToolbar,\n  FlagPreference: GeneratedCommandFlagPreference,\n  Output: GeneratedCommandOutput,\n  Actions: GeneratedCommandActions,\n  EmptyState: GeneratedCommandEmptyState,\n});\n",
      "type": "registry:component",
      "target": "components/commandly/generated-command.tsx"
    },
    {
      "path": "registry/commandly/types/flat.ts",
      "content": "export interface ToolInfo {\n  /** A brief human-readable description of what the tool does. */\n  description?: string;\n  /** The version string of the tool (e.g. \"1.0.0\"). */\n  version?: string;\n  /** The homepage or documentation URL for the tool. */\n  url?: string;\n}\n\nexport interface Command {\n  /** Unique identifier for this command within the tool. */\n  key: string;\n  /** Key of the parent command; used to represent subcommand nesting. */\n  parentCommandKey?: string;\n  /** Human-readable display name of the command. */\n  name: string;\n  /** Brief description of what this command does. */\n  description?: string;\n  /** Whether this command opens an interactive session or prompt. */\n  interactive?: boolean;\n  /** Display sort position relative to sibling commands. */\n  sortOrder?: number;\n}\n\nexport interface ParameterEnumValue {\n  /** The raw value passed to the CLI for this choice. */\n  value: string;\n  /** Human-readable label shown to the user for this enum choice. */\n  displayName: string;\n  /** Description of what this enum value does or represents. */\n  description?: string;\n  /** Whether this is the default selection when no value is provided. */\n  isDefault?: boolean;\n  /** Display sort position relative to sibling enum values. */\n  sortOrder?: number;\n}\n\nexport interface ParameterEnumValues {\n  /** The list of allowed enum choices. */\n  values: ParameterEnumValue[];\n  /** Whether the user can select multiple values at once. */\n  allowMultiple?: boolean;\n  /** Separator character used when joining multiple selected values. */\n  separator?: string;\n}\n\nexport type ParameterValidationType =\n  | \"min_length\"\n  | \"max_length\"\n  | \"min_value\"\n  | \"max_value\"\n  | \"regex\";\n\nexport interface ParameterValidation {\n  /** Unique identifier for this validation rule. */\n  key: string;\n  /** The type of validation to apply. */\n  validationType: ParameterValidationType;\n  /** The value to validate against (e.g. the max length number, or a regex pattern). */\n  validationValue: string;\n  /** The error message to display when validation fails. */\n  errorMessage: string;\n}\n\nexport type ParameterDependencyType = \"requires\" | \"conflicts_with\";\n\nexport interface ParameterDependency {\n  /** Unique identifier for this dependency rule. */\n  key: string;\n  /** Key of the parameter that owns this dependency. */\n  parameterKey: string;\n  /** Key of the parameter this dependency references. */\n  dependsOnParameterKey: string;\n  /** Whether this parameter requires or conflicts with the referenced parameter. */\n  dependencyType: ParameterDependencyType;\n  /** Optional value that the referenced parameter must have for this dependency to apply. */\n  conditionValue?: string;\n}\n\nexport type ParameterValue = string | number | boolean | string[];\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ToolMetadata {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ParameterMetadata {\n  /** Arbitrary tags for categorising or filtering parameters. */\n  tags?: string[];\n}\n\nexport type ParameterType = \"Flag\" | \"Option\" | \"Argument\";\n\nexport type ParameterDataType = \"String\" | \"Number\" | \"Boolean\" | \"Enum\";\n\nexport interface Parameter {\n  /** Unique identifier for this parameter within the tool. */\n  key: string;\n  /** Human-readable display name of the parameter. */\n  name: string;\n  /** Key of the command this parameter belongs to; omit for global parameters. */\n  commandKey?: string;\n  /** Brief description of what this parameter does or accepts. */\n  description?: string;\n  /** Optional grouping label for organising related parameters in the UI. */\n  group?: string;\n  /** Additional metadata such as tags. */\n  metadata?: ParameterMetadata;\n  /** Whether this is a boolean flag, a key-value option, or a positional argument. */\n  parameterType: ParameterType;\n  /** The data type of the parameter's value. */\n  dataType: ParameterDataType;\n  /** Whether the user must provide this parameter. */\n  isRequired?: boolean;\n  /** Whether this parameter can be specified multiple times. */\n  isRepeatable?: boolean;\n  /** Whether this parameter applies to all commands rather than a single command. */\n  isGlobal?: boolean;\n  /** The single-character short flag (e.g. \"-v\"). */\n  shortFlag?: string;\n  /** The long-form flag or option name (e.g. \"--verbose\"). */\n  longFlag?: string;\n  /** Zero-based position index for positional arguments. */\n  position?: number;\n  /** Display sort position relative to sibling parameters. */\n  sortOrder?: number;\n  /** Separator character used when the option accepts multiple values in one argument (e.g. \",\"). */\n  arraySeparator?: string;\n  /** Separator between key and value for key=value style options (e.g. \"=\"). */\n  keyValueSeparator?: string;\n  /** Allowed enum choices when dataType is \"Enum\". */\n  enum?: ParameterEnumValues;\n  /** Validation rules applied to this parameter's value. */\n  validations?: ParameterValidation[];\n  /** Dependencies on other parameters (requires or conflicts-with relationships). */\n  dependencies?: ParameterDependency[];\n}\n\nexport type ExclusionType = \"mutual_exclusive\" | \"required_one_of\";\n\nexport interface ExclusionGroup {\n  /** Unique identifier for this exclusion group. */\n  key?: string;\n  /** Key of the command this exclusion group belongs to; omit for global groups. */\n  commandKey?: string;\n  /** Human-readable name for this exclusion group. */\n  name: string;\n  /** Whether parameters in this group are mutually exclusive or one is required. */\n  exclusionType: ExclusionType;\n  /** Keys of the parameters that participate in this exclusion group. */\n  parameterKeys: string[];\n}\n\nexport interface Tool {\n  /** Unique binary name for the tool that it can be invoked from the command line (e.g. \"httpx\"). */\n  binaryName: string;\n  /** Human-readable display name for the tool (e.g. \"HTTPx\"). */\n  displayName: string;\n  /** Whether the root tool invocation opens an interactive session or prompt. */\n  interactive?: boolean;\n  /** General information about the tool such as description, version, and URL. */\n  info?: ToolInfo;\n  /** List of all commands and subcommands defined for this tool. */\n  commands: Command[];\n  /** Flat list of all parameters across all commands and global scope. */\n  parameters: Parameter[];\n  /** Groups of parameters with mutual exclusion or required-one-of constraints. */\n  exclusionGroups?: ExclusionGroup[];\n  /** Arbitrary metadata attached to the tool. */\n  metadata?: ToolMetadata;\n}\n",
      "type": "registry:file",
      "target": "components/commandly/types/flat.ts"
    },
    {
      "path": "registry/commandly/types/nested.ts",
      "content": "import type {\n  ExclusionType,\n  ParameterDataType,\n  ParameterDependencyType,\n  ParameterEnumValues,\n  ParameterMetadata,\n  ParameterType,\n  ParameterValidationType,\n  ToolInfo,\n  ToolMetadata,\n} from \"@/components/commandly/types/flat\";\n\nexport interface NestedParameterValidation {\n  /** The type of validation to apply. */\n  validationType: ParameterValidationType;\n  /** The value to validate against (e.g. max length number or regex pattern). */\n  validationValue: string;\n  /** The error message to display when validation fails. */\n  errorMessage: string;\n}\n\nexport interface NestedParameterDependency {\n  /** Name of the parameter this dependency references. */\n  dependsOnParameter: string;\n  /** Whether this parameter requires or conflicts with the referenced parameter. */\n  dependencyType: ParameterDependencyType;\n  /** Optional value that the referenced parameter must have for this dependency to apply. */\n  conditionValue?: string;\n}\n\nexport interface NestedParameter {\n  /** Human-readable display name of the parameter. */\n  name: string;\n  /** Brief description of what this parameter does or accepts. */\n  description?: string;\n  /** Optional grouping label for organising related parameters in the UI. */\n  group?: string;\n  /** Whether this is a boolean flag, a key-value option, or a positional argument. */\n  parameterType: ParameterType;\n  /** The data type of the parameter's value. */\n  dataType: ParameterDataType;\n  /** Additional metadata such as tags. */\n  metadata?: ParameterMetadata;\n  /** Whether the user must provide this parameter. */\n  isRequired?: boolean;\n  /** Whether this parameter can be specified multiple times. */\n  isRepeatable?: boolean;\n  /** Whether this parameter applies to all commands rather than a single command. */\n  isGlobal?: boolean;\n  /** The single-character short flag (e.g. \"-v\"). */\n  shortFlag?: string;\n  /** The long-form flag or option name (e.g. \"--verbose\"). */\n  longFlag?: string;\n  /** Zero-based position index for positional arguments. */\n  position?: number;\n  /** Display sort position relative to sibling parameters. */\n  sortOrder?: number;\n  /** Separator character used when the option accepts multiple values in one argument (e.g. \",\"). */\n  arraySeparator?: string;\n  /** Separator between key and value for key=value style options (e.g. \"=\"). */\n  keyValueSeparator?: string;\n  /** Allowed enum choices when dataType is \"Enum\". */\n  enum?: ParameterEnumValues;\n  /** Validation rules applied to this parameter's value. */\n  validations?: NestedParameterValidation[];\n  /** Dependencies on other parameters (requires or conflicts-with relationships). */\n  dependencies?: NestedParameterDependency[];\n}\n\nexport interface NestedCommand {\n  /** Human-readable display name of the command. */\n  name: string;\n  /** Brief description of what this command does. */\n  description?: string;\n  /** Whether this command opens an interactive session or prompt. */\n  interactive?: boolean;\n  /** Display sort position relative to sibling commands. */\n  sortOrder?: number;\n  /** Parameters that belong directly to this command. */\n  parameters: NestedParameter[];\n  /** Nested subcommands of this command. */\n  subcommands: NestedCommand[] /** Groups of parameters with mutual exclusion or required-one-of constraints scoped to this command. */;\n  exclusionGroups?: NestedExclusionGroup[];\n}\n\nexport interface NestedExclusionGroup {\n  /** Human-readable name for this exclusion group. */\n  name: string;\n  /** Whether parameters in this group are mutually exclusive or one is required. */\n  exclusionType: ExclusionType;\n  /** Names of the parameters that participate in this exclusion group. */\n  parameters: string[];\n}\n\nexport interface NestedTool {\n  /** Unique binary name for the tool that it can be invoked from the command line (e.g. \"httpx\"). */\n  binaryName: string;\n  /** Human-readable display name for the tool (e.g. \"HTTPx\"). */\n  displayName: string;\n  /** Whether the root tool invocation opens an interactive session or prompt. */\n  interactive?: boolean;\n  /** General information about the tool such as description, version, and URL. */\n  info?: ToolInfo;\n  /** The homepage or documentation URL for the tool. */\n  url?: string;\n  /** Parameters that belong to the root invocation when no commands exist. */\n  rootParameters: NestedParameter[];\n  /** Parameters that apply to all commands globally. */\n  globalParameters: NestedParameter[];\n  /** Hierarchical list of commands and their nested subcommands. */\n  commands: NestedCommand[];\n  /** Groups of parameters with mutual exclusion or required-one-of constraints. */\n  exclusionGroups?: NestedExclusionGroup[] | null;\n  /** Arbitrary metadata attached to the tool. */\n  metadata?: ToolMetadata;\n}\n",
      "type": "registry:file",
      "target": "components/commandly/types/nested.ts"
    },
    {
      "path": "registry/commandly/utils/flat.ts",
      "content": "import { SCHEMA_URL } from \"@/components/ai-chat/tool-rules\";\nimport type {\n  Command,\n  ExclusionGroup,\n  Parameter,\n  ParameterMetadata,\n  ParameterValue,\n  Tool,\n} from \"@/components/commandly/types/flat\";\n\nexport const slugify = (text: string): string => {\n  return text\n    .toString()\n    .toLowerCase()\n    .trim()\n    .replace(/\\s+/g, \"-\") // Replace spaces with -\n    .replace(/[^\\w-]+/g, \"\") // Remove all non-word chars\n    .replace(/--+/g, \"-\") // Replace multiple - with single -\n    .replace(/^-+/, \"\") // Trim - from start of text\n    .replace(/-+$/, \"\"); // Trim - from end of text\n};\n\nexport const getCommandPath = (command: Command, tool: Tool): string => {\n  const allCommands = tool.commands;\n  const findCommandPath = (\n    targetKey: string,\n    commands: Command[],\n    path: string[] = [],\n  ): string[] | null => {\n    for (const cmd of commands) {\n      if (cmd.name === targetKey) {\n        return [...path, cmd.name];\n      }\n\n      const childCommands = allCommands.filter((c) => c.parentCommandKey === cmd.key);\n      if (childCommands.length > 0) {\n        const subPath = findCommandPath(targetKey, childCommands, [...path, cmd.name]);\n        if (subPath) {\n          return subPath;\n        }\n      }\n    }\n    return null;\n  };\n\n  const rootCommands = tool.commands.filter((c) => !c.parentCommandKey);\n  const path = findCommandPath(command.name, rootCommands);\n\n  if (!path) return command.name;\n\n  return path.join(\" \");\n};\n\nexport const getAllSubcommands = (commandKey: string, commands: Command[]): Command[] => {\n  const result: Command[] = [];\n\n  const findSubcommands = (parentKey: string) => {\n    commands.forEach((cmd) => {\n      if (cmd.parentCommandKey === parentKey) {\n        result.push(cmd);\n        findSubcommands(cmd.key);\n      }\n    });\n  };\n\n  findSubcommands(commandKey);\n  return result;\n};\n\nexport interface GenerateCommandOptions {\n  selectedCommand?: Command | null;\n  useLongFlag?: boolean;\n}\n\nfunction getPreferredFlag(param: Parameter, useLongFlag: boolean): string | undefined {\n  return useLongFlag ? param.longFlag || param.shortFlag : param.shortFlag || param.longFlag;\n}\n\nexport function generateCommand(\n  tool: Tool,\n  parameterValues: Record<string, ParameterValue>,\n  options: GenerateCommandOptions = {},\n): string {\n  const hasCommands = tool.commands.length > 0;\n  const selectedCommand =\n    options.selectedCommand === undefined ? (tool.commands[0] ?? null) : options.selectedCommand;\n  const useLongFlag = options.useLongFlag ?? false;\n\n  let command = tool.binaryName;\n\n  if (hasCommands && selectedCommand) {\n    const commandPath = getCommandPath(selectedCommand, tool);\n    if (tool.binaryName !== commandPath) {\n      command = `${tool.binaryName} ${commandPath}`;\n    }\n  }\n\n  const parametersWithValues: Array<{\n    param: Parameter;\n    value: ParameterValue;\n  }> = [];\n  const globalParameters = tool.parameters?.filter((param) => param.isGlobal) ?? [];\n  const rootParameters =\n    hasCommands && selectedCommand\n      ? []\n      : (tool.parameters?.filter((param) => !param.commandKey && !param.isGlobal) ?? []);\n  const currentParameters = selectedCommand\n    ? (tool.parameters?.filter(\n        (param) => param.commandKey === selectedCommand.key && !param.isGlobal,\n      ) ?? [])\n    : [];\n\n  [...globalParameters, ...rootParameters, ...currentParameters].forEach((param) => {\n    const value = parameterValues[param.key];\n    if (value !== undefined && value !== \"\" && value !== false) {\n      parametersWithValues.push({ param, value });\n    }\n  });\n\n  const positionalParams = parametersWithValues\n    .filter(({ param }) => param.parameterType === \"Argument\")\n    .sort((a, b) => (a.param.position || 0) - (b.param.position || 0));\n\n  parametersWithValues.forEach(({ param, value }) => {\n    if (param.parameterType === \"Flag\") {\n      if (value === true) {\n        const flag = getPreferredFlag(param, useLongFlag);\n        if (flag) command += ` ${flag}`;\n      } else if (param.isRepeatable && typeof value === \"number\" && value > 0) {\n        const flag = getPreferredFlag(param, useLongFlag);\n        if (flag) command += ` ${flag}`.repeat(value);\n      }\n      return;\n    }\n\n    if (param.parameterType === \"Option\") {\n      const flag = getPreferredFlag(param, useLongFlag);\n      if (!flag) return;\n\n      const separator = param.keyValueSeparator ?? \" \";\n      if (Array.isArray(value)) {\n        const entries = value.filter((entry) => entry !== \"\");\n        if (entries.length === 0) return;\n\n        if (param.arraySeparator) {\n          command += ` ${flag}${separator}${entries.join(param.arraySeparator)}`;\n          return;\n        }\n\n        entries.forEach((entry) => {\n          command += ` ${flag}${separator}${entry}`;\n        });\n        return;\n      }\n\n      command += ` ${flag}${separator}${value}`;\n    }\n  });\n\n  positionalParams.forEach(({ value }) => {\n    if (!Array.isArray(value)) {\n      command += ` ${value}`;\n    }\n  });\n\n  return command;\n}\n\nexport const exportToStructuredJSON = (tool: Tool) => {\n  return {\n    $schema: SCHEMA_URL,\n    name: tool.binaryName,\n    displayName: tool.displayName,\n    info: tool.info,\n    commands: tool.commands.map((cmd) => ({ ...cmd })),\n    parameters: tool.parameters.map(({ metadata: _metadata, ...param }) => param),\n    exclusionGroups: tool.exclusionGroups,\n    metadata: tool.metadata,\n  };\n};\n\nfunction isEmptyArray(value: unknown): boolean {\n  return Array.isArray(value) && value.length === 0;\n}\n\nfunction isEmptyObject(value: unknown): boolean {\n  return (\n    value != null &&\n    typeof value === \"object\" &&\n    !Array.isArray(value) &&\n    Object.keys(value).length === 0\n  );\n}\n\nfunction cleanParameter(param: Parameter): Parameter {\n  const cleaned = { ...param };\n\n  if (cleaned.isRequired === false) delete cleaned.isRequired;\n  if (cleaned.isRepeatable === false) delete cleaned.isRepeatable;\n  if (cleaned.isGlobal === false) delete cleaned.isGlobal;\n  if (cleaned.keyValueSeparator === \" \") delete cleaned.keyValueSeparator;\n  if (cleaned.arraySeparator === \",\" && cleaned.isRepeatable !== true)\n    delete cleaned.arraySeparator;\n\n  if (!cleaned.enum || isEmptyArray(cleaned.enum.values)) delete cleaned.enum;\n  if (isEmptyArray(cleaned.validations)) delete cleaned.validations;\n  if (isEmptyArray(cleaned.dependencies)) delete cleaned.dependencies;\n\n  if (cleaned.metadata) {\n    const meta = { ...cleaned.metadata } as ParameterMetadata;\n    if (isEmptyArray(meta.tags)) delete meta.tags;\n    if (isEmptyObject(meta)) {\n      delete cleaned.metadata;\n    } else {\n      cleaned.metadata = meta;\n    }\n  }\n\n  return cleaned;\n}\n\nfunction cleanCommand(cmd: Command): Command {\n  const cleaned = { ...cmd };\n  if (cleaned.interactive === false) delete cleaned.interactive;\n  return cleaned;\n}\n\nfunction cleanExclusionGroup(group: ExclusionGroup): ExclusionGroup {\n  return { ...group };\n}\n\nexport interface FixToolOptions {\n  addSchema?: boolean;\n  removeMetadata?: boolean;\n}\n\nexport function fixTool(tool: Tool, options?: FixToolOptions): Tool {\n  const addSchema = options?.addSchema ?? false;\n  const removeMetadata = options?.removeMetadata ?? false;\n\n  const cleaned: Record<string, unknown> = { ...tool };\n\n  if (addSchema) {\n    cleaned[\"$schema\"] = SCHEMA_URL;\n  }\n\n  if (cleaned.interactive === false) delete cleaned.interactive;\n  if (isEmptyObject(cleaned.metadata) || removeMetadata) delete cleaned.metadata;\n  if (isEmptyArray(cleaned.exclusionGroups)) delete cleaned.exclusionGroups;\n\n  if (\"description\" in cleaned && cleaned.info == null) {\n    cleaned.info = { description: cleaned.description as string };\n    delete cleaned.description;\n  }\n\n  if (\"version\" in cleaned && typeof cleaned.version === \"string\") {\n    if (cleaned.info && typeof cleaned.info === \"object\") {\n      (cleaned.info as Record<string, unknown>).version = cleaned.version;\n    } else {\n      cleaned.info = { version: cleaned.version as string };\n    }\n    delete cleaned.version;\n  }\n\n  if (Array.isArray(cleaned.commands)) {\n    cleaned.commands = (cleaned.commands as Command[]).map(cleanCommand);\n  }\n\n  if (Array.isArray(cleaned.parameters)) {\n    cleaned.parameters = (cleaned.parameters as Parameter[]).map((p) => {\n      const fixed = cleanParameter(p);\n      if (removeMetadata) delete fixed.metadata;\n      return fixed;\n    });\n  }\n\n  if (\n    Array.isArray(cleaned.exclusionGroups) &&\n    (cleaned.exclusionGroups as ExclusionGroup[]).length > 0\n  ) {\n    cleaned.exclusionGroups = (cleaned.exclusionGroups as ExclusionGroup[]).map(\n      cleanExclusionGroup,\n    );\n  }\n\n  return cleaned as unknown as Tool;\n}\n",
      "type": "registry:file",
      "target": "components/commandly/utils/flat.ts"
    },
    {
      "path": "registry/commandly/utils/nested.ts",
      "content": "import type { Tool, Command, Parameter } from \"@/components/commandly/types/flat\";\nimport {\n  NestedCommand,\n  NestedExclusionGroup,\n  NestedParameter,\n  NestedTool,\n} from \"@/components/commandly/types/nested\";\n\nexport const convertToNestedStructure = (tool: Tool): NestedTool => {\n  const globalParameters = tool.parameters.filter((p) => p.isGlobal);\n\n  const convertParameter = (param: Parameter): NestedParameter => {\n    const { ...rest } = param;\n    return {\n      ...rest,\n      validations: param.validations?.map((v) => {\n        return {\n          validationType: v.validationType,\n          validationValue: v.validationValue,\n          errorMessage: v.errorMessage,\n        };\n      }),\n      metadata: param.metadata,\n      dataType: param.dataType,\n      dependencies: param.dependencies?.map((dep) => {\n        const dependsOnParam = tool.parameters.find((p) => p.key === dep.dependsOnParameterKey);\n        return {\n          dependsOnParameter: dependsOnParam?.longFlag || \"\",\n          dependencyType: dep.dependencyType,\n          conditionValue: dep.conditionValue,\n        };\n      }),\n    };\n  };\n\n  const buildNestedCommands = (commands: Command[], parentKey?: string): NestedCommand[] => {\n    return commands\n      .filter((cmd) => cmd.parentCommandKey === parentKey)\n      .map((cmd) => {\n        const commandParameters = tool.parameters.filter(\n          (p) => p.commandKey === cmd.key && !p.isGlobal,\n        );\n        const commandExclusionGroups = tool.exclusionGroups\n          ?.filter((g) => g.commandKey === cmd.key)\n          .map((group) => ({\n            name: group.name,\n            exclusionType: group.exclusionType,\n            parameters: group.parameterKeys.map((pk) => {\n              const param = tool.parameters.find((p) => p.key === pk);\n              return param?.longFlag || \"\";\n            }),\n          }));\n        return {\n          name: cmd.name,\n          description: cmd.description,\n          interactive: cmd.interactive,\n          sortOrder: cmd.sortOrder ?? 0,\n          parameters: commandParameters.map(convertParameter),\n          subcommands: buildNestedCommands(commands, cmd.key),\n          ...(commandExclusionGroups?.length ? { exclusionGroups: commandExclusionGroups } : {}),\n        };\n      });\n  };\n\n  const nestedExclusionGroups: NestedExclusionGroup[] | undefined = tool.exclusionGroups\n    ?.filter((g) => !g.commandKey)\n    .map((group) => ({\n      name: group.name,\n      exclusionType: group.exclusionType,\n      parameters: group.parameterKeys.map((pk) => {\n        const param = tool.parameters.find((p) => p.key === pk);\n        return param?.longFlag || \"\";\n      }),\n    }));\n\n  const rootParameters =\n    tool.commands.length === 0 ? tool.parameters.filter((p) => !p.commandKey && !p.isGlobal) : [];\n\n  return {\n    binaryName: tool.binaryName,\n    url: tool.info?.url,\n    displayName: tool.displayName,\n    interactive: tool.interactive,\n    info: tool.info,\n    metadata: tool.metadata,\n    rootParameters: rootParameters.map(convertParameter),\n    globalParameters: globalParameters.map(convertParameter),\n    commands: buildNestedCommands(tool.commands),\n    exclusionGroups: nestedExclusionGroups,\n  };\n};\n",
      "type": "registry:file",
      "target": "components/commandly/utils/nested.ts"
    }
  ],
  "type": "registry:component"
}