@@ -26,15 +26,13 @@ class BaseMicroagent(BaseModel):
2626 type : MicroagentType
2727
2828 PATH_TO_THIRD_PARTY_MICROAGENT_NAME : ClassVar [dict [str , str ]] = {
29- ' .cursorrules' : ' cursorrules' ,
30- ' agents.md' : ' agents' ,
31- ' agent.md' : ' agents' ,
29+ " .cursorrules" : " cursorrules" ,
30+ " agents.md" : " agents" ,
31+ " agent.md" : " agents" ,
3232 }
3333
3434 @classmethod
35- def _handle_third_party (
36- cls , path : Path , file_content : str
37- ) -> Union ['RepoMicroagent' , None ]:
35+ def _handle_third_party (cls , path : Path , file_content : str ) -> Union ["RepoMicroagent" , None ]:
3836 # Determine the agent name based on file type
3937 microagent_name = cls .PATH_TO_THIRD_PARTY_MICROAGENT_NAME .get (path .name .lower ())
4038
@@ -56,7 +54,7 @@ def load(
5654 path : Union [str , Path ],
5755 microagent_dir : Path | None = None ,
5856 file_content : str | None = None ,
59- ) -> ' BaseMicroagent' :
57+ ) -> " BaseMicroagent" :
6058 """Load a microagent from a markdown file with frontmatter.
6159
6260 The agent's name is derived from its path relative to the microagent_dir.
@@ -68,21 +66,19 @@ def load(
6866 derived_name = None
6967 if microagent_dir is not None :
7068 # Special handling for files which are not in microagent_dir
71- derived_name = cls .PATH_TO_THIRD_PARTY_MICROAGENT_NAME .get (
72- path .name .lower ()
73- ) or str (path .relative_to (microagent_dir ).with_suffix ('' ))
69+ derived_name = cls .PATH_TO_THIRD_PARTY_MICROAGENT_NAME .get (path .name .lower ()) or str (path .relative_to (microagent_dir ).with_suffix ("" ))
7470
7571 # Only load directly from path if file_content is not provided
7672 if file_content is None :
7773 with open (path ) as f :
7874 file_content = f .read ()
7975
8076 # Legacy repo instructions are stored in .openhands_instructions
81- if path .name == ' .openhands_instructions' :
77+ if path .name == " .openhands_instructions" :
8278 return RepoMicroagent (
83- name = ' repo_legacy' ,
79+ name = " repo_legacy" ,
8480 content = file_content ,
85- metadata = MicroagentMetadata (name = ' repo_legacy' ),
81+ metadata = MicroagentMetadata (name = " repo_legacy" ),
8682 source = str (path ),
8783 type = MicroagentType .REPO_KNOWLEDGE ,
8884 )
@@ -100,31 +96,24 @@ def load(
10096 metadata_dict = loaded .metadata or {}
10197
10298 # Ensure version is always a string (YAML may parse numeric versions as integers)
103- if ' version' in metadata_dict and not isinstance (metadata_dict [' version' ], str ):
104- metadata_dict [' version' ] = str (metadata_dict [' version' ])
99+ if " version" in metadata_dict and not isinstance (metadata_dict [" version" ], str ):
100+ metadata_dict [" version" ] = str (metadata_dict [" version" ])
105101
106102 try :
107103 metadata = MicroagentMetadata .model_validate (metadata_dict )
108104
109105 # Validate MCP tools configuration if present
110106 if metadata .mcp_tools :
111107 if metadata .mcp_tools .sse_servers :
112- logger .warning (
113- f'Microagent { metadata .name } has SSE servers. Only stdio servers are currently supported.'
114- )
108+ logger .warning (f"Microagent { metadata .name } has SSE servers. Only stdio servers are currently supported." )
115109
116110 if not metadata .mcp_tools .stdio_servers :
117- raise MicroagentValidationError (
118- f'Microagent { metadata .name } has MCP tools configuration but no stdio servers. '
119- 'Only stdio servers are currently supported.'
120- )
111+ raise MicroagentValidationError (f"Microagent { metadata .name } has MCP tools configuration but no stdio servers. Only stdio servers are currently supported." )
121112 except Exception as e :
122113 # Provide more detailed error message for validation errors
123- error_msg = f'Error validating microagent metadata in { path .name } : { str (e )} '
124- if 'type' in metadata_dict and metadata_dict ['type' ] not in [
125- t .value for t in MicroagentType
126- ]:
127- valid_types = ', ' .join ([f'"{ t .value } "' for t in MicroagentType ])
114+ error_msg = f"Error validating microagent metadata in { path .name } : { str (e )} "
115+ if "type" in metadata_dict and metadata_dict ["type" ] not in [t .value for t in MicroagentType ]:
116+ valid_types = ", " .join ([f'"{ t .value } "' for t in MicroagentType ])
128117 error_msg += f'. Invalid "type" value: "{ metadata_dict ["type" ]} ". Valid types are: { valid_types } '
129118 raise MicroagentValidationError (error_msg ) from e
130119
@@ -143,7 +132,7 @@ def load(
143132 if metadata .inputs :
144133 inferred_type = MicroagentType .TASK
145134 # Add a trigger for the agent name if not already present
146- trigger = f' /{ metadata .name } '
135+ trigger = f" /{ metadata .name } "
147136 if not metadata .triggers or trigger not in metadata .triggers :
148137 if not metadata .triggers :
149138 metadata .triggers = [trigger ]
@@ -158,7 +147,7 @@ def load(
158147
159148 if inferred_type not in subclass_map :
160149 # This should theoretically not happen with the logic above
161- raise ValueError (f' Could not determine microagent type for: { path } ' )
150+ raise ValueError (f" Could not determine microagent type for: { path } " )
162151
163152 # Use derived_name if available (from relative path), otherwise fallback to metadata.name
164153 agent_name = derived_name if derived_name is not None else metadata .name
@@ -186,7 +175,7 @@ class KnowledgeMicroagent(BaseMicroagent):
186175 def __init__ (self , ** data ):
187176 super ().__init__ (** data )
188177 if self .type not in [MicroagentType .KNOWLEDGE , MicroagentType .TASK ]:
189- raise ValueError (' KnowledgeMicroagent must have type KNOWLEDGE or TASK' )
178+ raise ValueError (" KnowledgeMicroagent must have type KNOWLEDGE or TASK" )
190179
191180 def match_trigger (self , message : str ) -> str | None :
192181 """Match a trigger in the message.
@@ -220,9 +209,7 @@ class RepoMicroagent(BaseMicroagent):
220209 def __init__ (self , ** data ):
221210 super ().__init__ (** data )
222211 if self .type != MicroagentType .REPO_KNOWLEDGE :
223- raise ValueError (
224- f'RepoMicroagent initialized with incorrect type: { self .type } '
225- )
212+ raise ValueError (f"RepoMicroagent initialized with incorrect type: { self .type } " )
226213
227214
228215class TaskMicroagent (KnowledgeMicroagent ):
@@ -235,9 +222,7 @@ class TaskMicroagent(KnowledgeMicroagent):
235222 def __init__ (self , ** data ):
236223 super ().__init__ (** data )
237224 if self .type != MicroagentType .TASK :
238- raise ValueError (
239- f'TaskMicroagent initialized with incorrect type: { self .type } '
240- )
225+ raise ValueError (f"TaskMicroagent initialized with incorrect type: { self .type } " )
241226
242227 # Append a prompt to ask for missing variables
243228 self ._append_missing_variables_prompt ()
@@ -256,7 +241,7 @@ def extract_variables(self, content: str) -> list[str]:
256241
257242 Variables are in the format ${variable_name}.
258243 """
259- pattern = r' \$\{([a-zA-Z_][a-zA-Z0-9_]*)\}'
244+ pattern = r" \$\{([a-zA-Z_][a-zA-Z0-9_]*)\}"
260245 matches = re .findall (pattern , content )
261246 return matches
262247
@@ -267,7 +252,7 @@ def requires_user_input(self) -> bool:
267252 """
268253 # Check if the content contains any variables
269254 variables = self .extract_variables (self .content )
270- logger .debug (f' This microagent requires user input: { variables } ' )
255+ logger .debug (f" This microagent requires user input: { variables } " )
271256 return len (variables ) > 0
272257
273258 @property
@@ -296,18 +281,18 @@ def load_microagents_from_dir(
296281 knowledge_agents = {}
297282
298283 # Load all agents from microagents directory
299- logger .debug (f' Loading agents from { microagent_dir } ' )
284+ logger .debug (f" Loading agents from { microagent_dir } " )
300285
301286 # Always check for .cursorrules and AGENTS.md files in repo root, regardless of whether microagents_dir exists
302287 special_files = []
303288 repo_root = microagent_dir .parent .parent
304289
305290 # Check for .cursorrules
306- if (repo_root / ' .cursorrules' ).exists ():
307- special_files .append (repo_root / ' .cursorrules' )
291+ if (repo_root / " .cursorrules" ).exists ():
292+ special_files .append (repo_root / " .cursorrules" )
308293
309294 # Check for AGENTS.md (case-insensitive)
310- for agents_filename in [' AGENTS.md' , ' agents.md' , ' AGENT.md' , ' agent.md' ]:
295+ for agents_filename in [" AGENTS.md" , " agents.md" , " AGENT.md" , " agent.md" ]:
311296 agents_path = repo_root / agents_filename
312297 if agents_path .exists ():
313298 special_files .append (agents_path )
@@ -316,7 +301,7 @@ def load_microagents_from_dir(
316301 # Collect .md files from microagents directory if it exists
317302 md_files = []
318303 if microagent_dir .exists ():
319- md_files = [f for f in microagent_dir .rglob (' *.md' ) if f .name != ' README.md' ]
304+ md_files = [f for f in microagent_dir .rglob (" *.md" ) if f .name != " README.md" ]
320305
321306 # Process all files in one loop
322307 for file in chain (special_files , md_files ):
@@ -329,15 +314,12 @@ def load_microagents_from_dir(
329314 knowledge_agents [agent .name ] = agent
330315 except MicroagentValidationError as e :
331316 # For validation errors, include the original exception
332- error_msg = f' Error loading microagent from { file } : { str (e )} '
317+ error_msg = f" Error loading microagent from { file } : { str (e )} "
333318 raise MicroagentValidationError (error_msg ) from e
334319 except Exception as e :
335320 # For other errors, wrap in a ValueError with detailed message
336- error_msg = f' Error loading microagent from { file } : { str (e )} '
321+ error_msg = f" Error loading microagent from { file } : { str (e )} "
337322 raise ValueError (error_msg ) from e
338323
339- logger .debug (
340- f'Loaded { len (repo_agents ) + len (knowledge_agents )} microagents: '
341- f'{ [* repo_agents .keys (), * knowledge_agents .keys ()]} '
342- )
324+ logger .debug (f"Loaded { len (repo_agents ) + len (knowledge_agents )} microagents: { [* repo_agents .keys (), * knowledge_agents .keys ()]} " )
343325 return repo_agents , knowledge_agents
0 commit comments