@@ -2,6 +2,7 @@ import { readFile } from 'fs/promises'
22import { accessSync } from 'fs'
33import path from 'path'
44import { fileURLToPath } from 'url'
5+ import { MarkdownAgentParser } from '../utils/MarkdownAgentParser.js'
56import { logger } from '../utils/logger.js'
67
78// Agent schema interface
@@ -53,34 +54,37 @@ export class AgentManager {
5354 }
5455
5556 /**
56- * Load all agent configuration files
57+ * Load all agent configuration files from markdown (.md) format
5758 * Throws error if agents directory doesn't exist or files are malformed
5859 */
5960 async loadAgents ( ) : Promise < AgentConfigs > {
60- // Load all .json files from the agents directory
61+ // Load all .md files from the agents directory
6162 const { readdir } = await import ( 'fs/promises' )
6263 const files = await readdir ( this . agentDir )
63- const agentFiles = files . filter ( file => file . endsWith ( '.json ' ) )
64+ const agentFiles = files . filter ( file => file . endsWith ( '.md ' ) )
6465
6566 const agents : AgentConfigs = { }
6667
6768 for ( const filename of agentFiles ) {
6869 const agentPath = path . join ( this . agentDir , filename )
69- const agentName = path . basename ( filename , '.json' )
7070
7171 try {
7272 const content = await readFile ( agentPath , 'utf-8' )
73- const agentConfig = JSON . parse ( content ) as AgentConfig
73+
74+ // Parse markdown with frontmatter
75+ const parsed = this . parseMarkdownAgent ( content , filename )
76+ const agentConfig = parsed . config
77+ const agentName = parsed . name
7478
7579 // Validate required fields
7680 this . validateAgentConfig ( agentConfig , agentName )
7781
7882 agents [ agentName ] = agentConfig
7983 logger . debug ( `Loaded agent: ${ agentName } ` )
8084 } catch ( error ) {
81- logger . error ( `Failed to load agent ${ agentName } ` , { error } )
85+ logger . error ( `Failed to load agent from ${ filename } ` , { error } )
8286 throw new Error (
83- `Failed to load agent ${ agentName } : ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
87+ `Failed to load agent from ${ filename } : ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
8488 )
8589 }
8690 }
@@ -105,6 +109,63 @@ export class AgentManager {
105109 }
106110 }
107111
112+ /**
113+ * Parse markdown agent file with YAML frontmatter
114+ * @param content - Raw markdown file content
115+ * @param filename - Original filename for error messages
116+ * @returns Parsed agent config and name
117+ */
118+ private parseMarkdownAgent ( content : string , filename : string ) : { config : AgentConfig ; name : string } {
119+ try {
120+ // Parse frontmatter using custom parser
121+ const { data, content : markdownBody } = MarkdownAgentParser . parse ( content )
122+
123+ // Validate frontmatter has required fields
124+ if ( ! data . name ) {
125+ throw new Error ( 'Missing required field: name' )
126+ }
127+ if ( ! data . description ) {
128+ throw new Error ( 'Missing required field: description' )
129+ }
130+ if ( ! data . tools ) {
131+ throw new Error ( 'Missing required field: tools' )
132+ }
133+ if ( ! data . model ) {
134+ throw new Error ( 'Missing required field: model' )
135+ }
136+
137+ // Parse tools from comma-separated string to array
138+ const tools = data . tools
139+ . split ( ',' )
140+ . map ( ( tool : string ) => tool . trim ( ) )
141+ . filter ( ( tool : string ) => tool . length > 0 )
142+
143+ // Validate model and warn if non-standard
144+ const validModels = [ 'sonnet' , 'opus' , 'haiku' ]
145+ if ( ! validModels . includes ( data . model ) ) {
146+ logger . warn (
147+ `Agent ${ data . name } uses model "${ data . model } " which may not be recognized by Claude CLI, and your workflow may fail or produce unexpected results. ` +
148+ `Valid values are: ${ validModels . join ( ', ' ) } `
149+ )
150+ }
151+
152+ // Construct AgentConfig
153+ const config : AgentConfig = {
154+ description : data . description ,
155+ prompt : markdownBody . trim ( ) ,
156+ tools,
157+ model : data . model ,
158+ ...( data . color && { color : data . color } ) ,
159+ }
160+
161+ return { config, name : data . name }
162+ } catch ( error ) {
163+ throw new Error (
164+ `Failed to parse markdown agent ${ filename } : ${ error instanceof Error ? error . message : 'Unknown error' } `
165+ )
166+ }
167+ }
168+
108169 /**
109170 * Format loaded agents for Claude CLI --agents flag
110171 * Returns object suitable for JSON.stringify
0 commit comments