@@ -2,6 +2,7 @@ import { promises as fs } from 'node:fs';
22import * as path from 'node:path' ;
33
44import { debug } from '../../shared/logging/logger.js' ;
5+ import type { ChainedPathEntry , ConditionalChainedPath } from '../../shared/agents/config/types.js' ;
56
67/**
78 * Represents a chained prompt loaded from a .md file
@@ -87,21 +88,16 @@ function filenameToName(filename: string): string {
8788}
8889
8990/**
90- * Load chained prompts from a folder
91- * Files are sorted by filename (01-first.md, 02-second.md, etc.)
92- *
93- * @param chainedPromptsPath - Absolute or relative path to folder containing .md files
94- * @param projectRoot - Project root for resolving relative paths
95- * @returns Array of ChainedPrompt objects sorted by filename
91+ * Load chained prompts from a single folder
9692 */
97- export async function loadChainedPrompts (
98- chainedPromptsPath : string ,
93+ async function loadPromptsFromFolder (
94+ folderPath : string ,
9995 projectRoot : string
10096) : Promise < ChainedPrompt [ ] > {
10197 // Resolve path
102- const absolutePath = path . isAbsolute ( chainedPromptsPath )
103- ? chainedPromptsPath
104- : path . resolve ( projectRoot , chainedPromptsPath ) ;
98+ const absolutePath = path . isAbsolute ( folderPath )
99+ ? folderPath
100+ : path . resolve ( projectRoot , folderPath ) ;
105101
106102 // Check if directory exists
107103 try {
@@ -147,3 +143,60 @@ export async function loadChainedPrompts(
147143 debug ( `Loaded ${ prompts . length } chained prompts from ${ absolutePath } ` ) ;
148144 return prompts ;
149145}
146+
147+ /**
148+ * Type guard for conditional path entry
149+ */
150+ function isConditionalPath ( entry : ChainedPathEntry ) : entry is ConditionalChainedPath {
151+ return typeof entry === 'object' && entry !== null && 'path' in entry ;
152+ }
153+
154+ /**
155+ * Check if all conditions are met (AND logic)
156+ */
157+ function meetsConditions ( entry : ChainedPathEntry , selectedConditions : string [ ] ) : boolean {
158+ if ( typeof entry === 'string' ) return true ;
159+ if ( ! entry . conditions ?. length ) return true ;
160+ return entry . conditions . every ( c => selectedConditions . includes ( c ) ) ;
161+ }
162+
163+ /**
164+ * Extract path string from entry
165+ */
166+ function getPath ( entry : ChainedPathEntry ) : string {
167+ return typeof entry === 'string' ? entry : entry . path ;
168+ }
169+
170+ /**
171+ * Load chained prompts from one or more folders
172+ * 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
174+ *
175+ * @param chainedPromptsPath - Path or array of paths to folder(s) containing .md files
176+ * @param projectRoot - Project root for resolving relative paths
177+ * @param selectedConditions - User-selected conditions for filtering conditional paths
178+ * @returns Array of ChainedPrompt objects sorted by filename within each folder
179+ */
180+ export async function loadChainedPrompts (
181+ chainedPromptsPath : ChainedPathEntry | ChainedPathEntry [ ] ,
182+ projectRoot : string ,
183+ selectedConditions : string [ ] = [ ]
184+ ) : Promise < ChainedPrompt [ ] > {
185+ const entries = Array . isArray ( chainedPromptsPath ) ? chainedPromptsPath : [ chainedPromptsPath ] ;
186+ const allPrompts : ChainedPrompt [ ] = [ ] ;
187+
188+ for ( const entry of entries ) {
189+ if ( ! meetsConditions ( entry , selectedConditions ) ) {
190+ const pathStr = getPath ( entry ) ;
191+ const conditions = isConditionalPath ( entry ) ? entry . conditions : [ ] ;
192+ debug ( `Skipped chained path: ${ pathStr } (unmet conditions: ${ conditions ?. join ( ', ' ) } )` ) ;
193+ continue ;
194+ }
195+
196+ const folderPath = getPath ( entry ) ;
197+ const prompts = await loadPromptsFromFolder ( folderPath , projectRoot ) ;
198+ allPrompts . push ( ...prompts ) ;
199+ }
200+
201+ return allPrompts ;
202+ }
0 commit comments