(config: HostConfig)
| 118 | const CLI_REGEX = /^[a-z][a-z0-9_-]*$/; |
| 119 | |
| 120 | export function validateHostConfig(config: HostConfig): string[] { |
| 121 | const errors: string[] = []; |
| 122 | |
| 123 | if (!NAME_REGEX.test(config.name)) { |
| 124 | errors.push(`name '${config.name}' must be lowercase alphanumeric with hyphens`); |
| 125 | } |
| 126 | if (!config.displayName) { |
| 127 | errors.push('displayName is required'); |
| 128 | } |
| 129 | if (!CLI_REGEX.test(config.cliCommand)) { |
| 130 | errors.push(`cliCommand '${config.cliCommand}' contains invalid characters`); |
| 131 | } |
| 132 | if (config.cliAliases) { |
| 133 | for (const alias of config.cliAliases) { |
| 134 | if (!CLI_REGEX.test(alias)) { |
| 135 | errors.push(`cliAlias '${alias}' contains invalid characters`); |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | if (!PATH_REGEX.test(config.globalRoot)) { |
| 140 | errors.push(`globalRoot '${config.globalRoot}' contains invalid characters`); |
| 141 | } |
| 142 | if (!PATH_REGEX.test(config.localSkillRoot)) { |
| 143 | errors.push(`localSkillRoot '${config.localSkillRoot}' contains invalid characters`); |
| 144 | } |
| 145 | if (!PATH_REGEX.test(config.hostSubdir)) { |
| 146 | errors.push(`hostSubdir '${config.hostSubdir}' contains invalid characters`); |
| 147 | } |
| 148 | if (!['allowlist', 'denylist'].includes(config.frontmatter.mode)) { |
| 149 | errors.push(`frontmatter.mode must be 'allowlist' or 'denylist'`); |
| 150 | } |
| 151 | if (!['real-dir-symlink', 'symlink-generated'].includes(config.install.linkingStrategy)) { |
| 152 | errors.push(`install.linkingStrategy must be 'real-dir-symlink' or 'symlink-generated'`); |
| 153 | } |
| 154 | |
| 155 | return errors; |
| 156 | } |
| 157 | |
| 158 | export function validateAllConfigs(configs: HostConfig[]): string[] { |
| 159 | const errors: string[] = []; |
no test coverage detected