core: improve directory validation error messages to help users fix invalid directory names

This commit is contained in:
Dax Raad
2025-10-09 22:40:23 -04:00
parent 096710a8cc
commit 9b52d33889
2 changed files with 25 additions and 17 deletions

View File

@@ -10,6 +10,8 @@ export function FormatError(input: unknown) {
`Config file at ${input.data.path} is not valid JSON(C)` + (input.data.message ? `: ${input.data.message}` : "") `Config file at ${input.data.path} is not valid JSON(C)` + (input.data.message ? `: ${input.data.message}` : "")
) )
} }
if (Config.DirectoryError.isInstance(input))
return `Directory "${input.data.dir}" in ${input.data.path} is not valid. Did you mean "${input.data.suggestion}"?`
if (Config.InvalidError.isInstance(input)) if (Config.InvalidError.isInstance(input))
return [ return [
`Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""), `Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""),

View File

@@ -62,7 +62,7 @@ export namespace Config {
] ]
for (const dir of directories) { for (const dir of directories) {
await assertValid(dir).catch(() => {}) await assertValid(dir)
installDependencies(dir) installDependencies(dir)
result.command = mergeDeep(result.command ?? {}, await loadCommand(dir)) result.command = mergeDeep(result.command ?? {}, await loadCommand(dir))
result.agent = mergeDeep(result.agent, await loadAgent(dir)) result.agent = mergeDeep(result.agent, await loadAgent(dir))
@@ -120,23 +120,20 @@ export namespace Config {
} }
}) })
const INVALID_DIRS = new Bun.Glob(`{${["agents", "commands", "plugins", "tools"].join(",")}}/`)
async function assertValid(dir: string) { async function assertValid(dir: string) {
const ALLOWED_DIRS = new Set(["agent", "command", "mode", "plugin", "tool", "themes"]) const invalid = await Array.fromAsync(
const UNEXPECTED_DIR_GLOB = new Bun.Glob("*/") INVALID_DIRS.scan({
for await (const item of UNEXPECTED_DIR_GLOB.scan({ onlyFiles: false,
absolute: true, cwd: dir,
followSymlinks: true, }),
dot: true, )
cwd: dir, for (const item of invalid) {
onlyFiles: false, throw new DirectoryError({
})) { path: dir,
const dirname = path.basename(item) dir: item,
if (!ALLOWED_DIRS.has(dirname)) { suggestion: item.substring(0, item.length - 1),
throw new InvalidError({ })
path: dir,
message: `Unexpected directory "${dirname}" found in "${dir}". Only ${ALLOWED_DIRS.values().toArray().join(", ")} directories are allowed.`,
})
}
} }
} }
@@ -714,6 +711,15 @@ export namespace Config {
}), }),
) )
export const DirectoryError = NamedError.create(
"ConfigDirectoryError",
z.object({
path: z.string(),
dir: z.string(),
suggestion: z.string(),
}),
)
export const InvalidError = NamedError.create( export const InvalidError = NamedError.create(
"ConfigInvalidError", "ConfigInvalidError",
z.object({ z.object({