@@ -87,6 +87,34 @@ function filenameToName(filename: string): string {
8787 return filename . replace ( / \. m d $ / i, '' ) ;
8888}
8989
90+ /**
91+ * Load a single chained prompt from a file
92+ */
93+ async function loadPromptFromFile (
94+ filePath : string ,
95+ projectRoot : string
96+ ) : Promise < ChainedPrompt | null > {
97+ const absolutePath = path . isAbsolute ( filePath )
98+ ? filePath
99+ : path . resolve ( projectRoot , filePath ) ;
100+
101+ try {
102+ const rawContent = await fs . readFile ( absolutePath , 'utf-8' ) ;
103+ const { frontmatter, body } = parseFrontmatter ( rawContent ) ;
104+ const filename = path . basename ( absolutePath ) ;
105+
106+ debug ( `Loaded chained prompt from file: ${ absolutePath } ` ) ;
107+ return {
108+ name : frontmatter . name || filenameToName ( filename ) ,
109+ label : frontmatter . description || filenameToLabel ( filename ) ,
110+ content : body . trim ( ) ,
111+ } ;
112+ } catch ( err ) {
113+ debug ( `Failed to read chained prompt file ${ absolutePath } : ${ err } ` ) ;
114+ return null ;
115+ }
116+ }
117+
90118/**
91119 * Load chained prompts from a single folder
92120 */
@@ -144,6 +172,37 @@ async function loadPromptsFromFolder(
144172 return prompts ;
145173}
146174
175+ /**
176+ * Load chained prompts from a path (file or folder)
177+ */
178+ async function loadPromptsFromPath (
179+ inputPath : string ,
180+ projectRoot : string
181+ ) : Promise < ChainedPrompt [ ] > {
182+ const absolutePath = path . isAbsolute ( inputPath )
183+ ? inputPath
184+ : path . resolve ( projectRoot , inputPath ) ;
185+
186+ try {
187+ const stat = await fs . stat ( absolutePath ) ;
188+
189+ if ( stat . isFile ( ) && absolutePath . endsWith ( '.md' ) ) {
190+ // Single file
191+ const prompt = await loadPromptFromFile ( absolutePath , projectRoot ) ;
192+ return prompt ? [ prompt ] : [ ] ;
193+ } else if ( stat . isDirectory ( ) ) {
194+ // Folder
195+ return loadPromptsFromFolder ( absolutePath , projectRoot ) ;
196+ } else {
197+ debug ( `chainedPromptsPath is neither a .md file nor directory: ${ absolutePath } ` ) ;
198+ return [ ] ;
199+ }
200+ } catch {
201+ debug ( `chainedPromptsPath does not exist: ${ absolutePath } ` ) ;
202+ return [ ] ;
203+ }
204+ }
205+
147206/**
148207 * Type guard for conditional path entry
149208 */
@@ -168,11 +227,11 @@ function getPath(entry: ChainedPathEntry): string {
168227}
169228
170229/**
171- * Load chained prompts from one or more folders
230+ * Load chained prompts from one or more paths (files or folders)
172231 * Files are sorted by filename within each folder (01-first.md, 02-second.md, etc.)
173- * When multiple folders are provided, prompts are loaded in folder order
232+ * When multiple paths are provided, prompts are loaded in path order
174233 *
175- * @param chainedPromptsPath - Path or array of paths to folder(s) containing .md files
234+ * @param chainedPromptsPath - Path or array of paths to file(s) or folder(s) containing .md files
176235 * @param projectRoot - Project root for resolving relative paths
177236 * @param selectedConditions - User-selected conditions for filtering conditional paths
178237 * @returns Array of ChainedPrompt objects sorted by filename within each folder
@@ -193,8 +252,8 @@ export async function loadChainedPrompts(
193252 continue ;
194253 }
195254
196- const folderPath = getPath ( entry ) ;
197- const prompts = await loadPromptsFromFolder ( folderPath , projectRoot ) ;
255+ const inputPath = getPath ( entry ) ;
256+ const prompts = await loadPromptsFromPath ( inputPath , projectRoot ) ;
198257 allPrompts . push ( ...prompts ) ;
199258 }
200259
0 commit comments