11import fs from "fs/promises"
22import * as path from "path"
3- import { ExpertData } from "../../../webview-ui/src/types/experts"
3+ import * as vscode from "vscode"
4+ import { ExpertData , ExpertDataSchema } from "../../../webview-ui/src/types/experts"
45import { fileExistsAtPath , createDirectoriesForFile } from "../../utils/fs"
6+ import { GlobalFileNames } from "../../global-constants"
57
68export class ExpertManager {
79 /**
@@ -14,11 +16,17 @@ export class ExpertManager {
1416 throw new Error ( "No workspace path provided" )
1517 }
1618
19+ // Validate expert data with Zod schema
20+ const validationResult = ExpertDataSchema . safeParse ( expert )
21+ if ( ! validationResult . success ) {
22+ throw new Error ( `Invalid expert data: ${ validationResult . error . message } ` )
23+ }
24+
1725 // Create a sanitized folder name from the expert name
1826 const sanitizedName = expert . name . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, "_" ) . toLowerCase ( )
1927
2028 // Create the expert directory
21- const expertDir = path . join ( workspacePath , ".hai- experts" , sanitizedName )
29+ const expertDir = path . join ( workspacePath , GlobalFileNames . experts , sanitizedName )
2230 await createDirectoriesForFile ( path . join ( expertDir , "placeholder.txt" ) )
2331
2432 // Create metadata file
@@ -45,7 +53,7 @@ export class ExpertManager {
4553 return [ ]
4654 }
4755
48- const expertsDir = path . join ( workspacePath , ".hai- experts" )
56+ const expertsDir = path . join ( workspacePath , GlobalFileNames . experts )
4957 if ( ! ( await fileExistsAtPath ( expertsDir ) ) ) {
5058 return [ ]
5159 }
@@ -62,22 +70,31 @@ export class ExpertManager {
6270 try {
6371 // Read metadata
6472 const metadataPath = path . join ( expertDir , "metadata.json" )
65- if ( await fileExistsAtPath ( metadataPath ) ) {
73+ const promptPath = path . join ( expertDir , "prompt.md" )
74+
75+ if ( ( await fileExistsAtPath ( metadataPath ) ) && ( await fileExistsAtPath ( promptPath ) ) ) {
6676 const metadataContent = await fs . readFile ( metadataPath , "utf-8" )
6777 const metadata = JSON . parse ( metadataContent )
6878
6979 // Read prompt
70- const promptPath = path . join ( expertDir , "prompt.md" )
71- const promptContent = ( await fileExistsAtPath ( promptPath ) )
72- ? await fs . readFile ( promptPath , "utf-8" )
73- : ""
80+ const promptContent = await fs . readFile ( promptPath , "utf-8" )
7481
75- experts . push ( {
82+ const expertData = {
7683 name : metadata . name ,
7784 isDefault : metadata . isDefault ,
7885 prompt : promptContent ,
7986 createdAt : metadata . createdAt ,
80- } )
87+ }
88+
89+ // Validate expert data with Zod schema
90+ const validationResult = ExpertDataSchema . safeParse ( expertData )
91+ if ( validationResult . success ) {
92+ experts . push ( expertData )
93+ } else {
94+ vscode . window . showWarningMessage (
95+ `Invalid expert data for ${ folder } : ${ validationResult . error . issues . map ( ( issue ) => issue . message ) . join ( ", " ) } ` ,
96+ )
97+ }
8198 }
8299 } catch ( error ) {
83100 console . error ( `Failed to read expert from ${ folder } :` , error )
@@ -102,7 +119,12 @@ export class ExpertManager {
102119 throw new Error ( "No workspace path provided" )
103120 }
104121
105- const expertsDir = path . join ( workspacePath , ".hai-experts" )
122+ // Validate expert name
123+ if ( ! expertName || typeof expertName !== "string" ) {
124+ throw new Error ( "Expert name must be a non-empty string" )
125+ }
126+
127+ const expertsDir = path . join ( workspacePath , GlobalFileNames . experts )
106128 if ( ! ( await fileExistsAtPath ( expertsDir ) ) ) {
107129 return
108130 }
@@ -145,7 +167,12 @@ export class ExpertManager {
145167 throw new Error ( "No workspace path provided" )
146168 }
147169
148- const expertsDir = path . join ( workspacePath , ".hai-experts" )
170+ // Validate expert name
171+ if ( ! expertName || typeof expertName !== "string" ) {
172+ throw new Error ( "Expert name must be a non-empty string" )
173+ }
174+
175+ const expertsDir = path . join ( workspacePath , GlobalFileNames . experts )
149176 if ( ! ( await fileExistsAtPath ( expertsDir ) ) ) {
150177 return null
151178 }
0 commit comments