hanzo-mcp 0.8.11__py3-none-any.whl → 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hanzo-mcp might be problematic. Click here for more details.

Files changed (166) hide show
  1. hanzo_mcp/__init__.py +1 -3
  2. hanzo_mcp/analytics/posthog_analytics.py +3 -9
  3. hanzo_mcp/bridge.py +9 -25
  4. hanzo_mcp/cli.py +6 -15
  5. hanzo_mcp/cli_enhanced.py +5 -14
  6. hanzo_mcp/cli_plugin.py +3 -9
  7. hanzo_mcp/config/settings.py +6 -20
  8. hanzo_mcp/config/tool_config.py +1 -3
  9. hanzo_mcp/core/base_agent.py +88 -88
  10. hanzo_mcp/core/model_registry.py +238 -210
  11. hanzo_mcp/dev_server.py +5 -15
  12. hanzo_mcp/prompts/__init__.py +2 -6
  13. hanzo_mcp/prompts/project_todo_reminder.py +3 -9
  14. hanzo_mcp/prompts/tool_explorer.py +1 -3
  15. hanzo_mcp/prompts/utils.py +7 -21
  16. hanzo_mcp/server.py +2 -6
  17. hanzo_mcp/tools/__init__.py +26 -27
  18. hanzo_mcp/tools/agent/__init__.py +2 -1
  19. hanzo_mcp/tools/agent/agent.py +10 -30
  20. hanzo_mcp/tools/agent/agent_tool.py +22 -15
  21. hanzo_mcp/tools/agent/claude_desktop_auth.py +3 -9
  22. hanzo_mcp/tools/agent/cli_agent_base.py +7 -24
  23. hanzo_mcp/tools/agent/cli_tools.py +75 -74
  24. hanzo_mcp/tools/agent/code_auth.py +1 -3
  25. hanzo_mcp/tools/agent/code_auth_tool.py +2 -6
  26. hanzo_mcp/tools/agent/critic_tool.py +8 -24
  27. hanzo_mcp/tools/agent/iching_tool.py +12 -36
  28. hanzo_mcp/tools/agent/network_tool.py +7 -18
  29. hanzo_mcp/tools/agent/prompt.py +1 -5
  30. hanzo_mcp/tools/agent/review_tool.py +10 -25
  31. hanzo_mcp/tools/agent/swarm_alias.py +1 -3
  32. hanzo_mcp/tools/agent/unified_cli_tools.py +38 -38
  33. hanzo_mcp/tools/common/batch_tool.py +15 -45
  34. hanzo_mcp/tools/common/config_tool.py +9 -28
  35. hanzo_mcp/tools/common/context.py +1 -3
  36. hanzo_mcp/tools/common/critic_tool.py +1 -3
  37. hanzo_mcp/tools/common/decorators.py +2 -6
  38. hanzo_mcp/tools/common/enhanced_base.py +2 -6
  39. hanzo_mcp/tools/common/fastmcp_pagination.py +4 -12
  40. hanzo_mcp/tools/common/forgiving_edit.py +9 -28
  41. hanzo_mcp/tools/common/mode.py +1 -5
  42. hanzo_mcp/tools/common/paginated_base.py +3 -11
  43. hanzo_mcp/tools/common/paginated_response.py +10 -30
  44. hanzo_mcp/tools/common/pagination.py +3 -9
  45. hanzo_mcp/tools/common/path_utils.py +34 -0
  46. hanzo_mcp/tools/common/permissions.py +14 -13
  47. hanzo_mcp/tools/common/personality.py +983 -701
  48. hanzo_mcp/tools/common/plugin_loader.py +3 -15
  49. hanzo_mcp/tools/common/stats.py +6 -18
  50. hanzo_mcp/tools/common/thinking_tool.py +1 -3
  51. hanzo_mcp/tools/common/tool_disable.py +2 -6
  52. hanzo_mcp/tools/common/tool_list.py +2 -6
  53. hanzo_mcp/tools/common/validation.py +1 -3
  54. hanzo_mcp/tools/compiler/__init__.py +8 -0
  55. hanzo_mcp/tools/compiler/sandboxed_compiler.py +681 -0
  56. hanzo_mcp/tools/config/config_tool.py +7 -13
  57. hanzo_mcp/tools/config/index_config.py +1 -3
  58. hanzo_mcp/tools/config/mode_tool.py +5 -15
  59. hanzo_mcp/tools/database/database_manager.py +3 -9
  60. hanzo_mcp/tools/database/graph.py +1 -3
  61. hanzo_mcp/tools/database/graph_add.py +3 -9
  62. hanzo_mcp/tools/database/graph_query.py +11 -34
  63. hanzo_mcp/tools/database/graph_remove.py +3 -9
  64. hanzo_mcp/tools/database/graph_search.py +6 -20
  65. hanzo_mcp/tools/database/graph_stats.py +11 -33
  66. hanzo_mcp/tools/database/sql.py +4 -12
  67. hanzo_mcp/tools/database/sql_query.py +6 -10
  68. hanzo_mcp/tools/database/sql_search.py +2 -6
  69. hanzo_mcp/tools/database/sql_stats.py +5 -15
  70. hanzo_mcp/tools/editor/neovim_command.py +1 -3
  71. hanzo_mcp/tools/editor/neovim_session.py +7 -13
  72. hanzo_mcp/tools/environment/__init__.py +8 -0
  73. hanzo_mcp/tools/environment/environment_detector.py +594 -0
  74. hanzo_mcp/tools/filesystem/__init__.py +28 -26
  75. hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
  76. hanzo_mcp/tools/filesystem/ast_tool.py +3 -0
  77. hanzo_mcp/tools/filesystem/base.py +20 -12
  78. hanzo_mcp/tools/filesystem/content_replace.py +7 -12
  79. hanzo_mcp/tools/filesystem/diff.py +2 -10
  80. hanzo_mcp/tools/filesystem/directory_tree.py +285 -51
  81. hanzo_mcp/tools/filesystem/edit.py +10 -18
  82. hanzo_mcp/tools/filesystem/find.py +312 -179
  83. hanzo_mcp/tools/filesystem/git_search.py +12 -24
  84. hanzo_mcp/tools/filesystem/multi_edit.py +10 -18
  85. hanzo_mcp/tools/filesystem/read.py +14 -30
  86. hanzo_mcp/tools/filesystem/rules_tool.py +9 -17
  87. hanzo_mcp/tools/filesystem/search.py +1160 -0
  88. hanzo_mcp/tools/filesystem/watch.py +2 -4
  89. hanzo_mcp/tools/filesystem/write.py +7 -10
  90. hanzo_mcp/tools/framework/__init__.py +8 -0
  91. hanzo_mcp/tools/framework/framework_modes.py +714 -0
  92. hanzo_mcp/tools/jupyter/base.py +6 -20
  93. hanzo_mcp/tools/jupyter/jupyter.py +4 -12
  94. hanzo_mcp/tools/llm/consensus_tool.py +8 -24
  95. hanzo_mcp/tools/llm/llm_manage.py +2 -6
  96. hanzo_mcp/tools/llm/llm_tool.py +17 -58
  97. hanzo_mcp/tools/llm/llm_unified.py +18 -59
  98. hanzo_mcp/tools/llm/provider_tools.py +1 -3
  99. hanzo_mcp/tools/lsp/lsp_tool.py +621 -481
  100. hanzo_mcp/tools/mcp/mcp_add.py +1 -3
  101. hanzo_mcp/tools/mcp/mcp_stats.py +1 -3
  102. hanzo_mcp/tools/mcp/mcp_tool.py +9 -23
  103. hanzo_mcp/tools/memory/__init__.py +10 -27
  104. hanzo_mcp/tools/memory/conversation_memory.py +636 -0
  105. hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
  106. hanzo_mcp/tools/memory/memory_tools.py +6 -18
  107. hanzo_mcp/tools/search/find_tool.py +12 -34
  108. hanzo_mcp/tools/search/unified_search.py +24 -78
  109. hanzo_mcp/tools/shell/__init__.py +16 -4
  110. hanzo_mcp/tools/shell/auto_background.py +2 -6
  111. hanzo_mcp/tools/shell/base.py +1 -5
  112. hanzo_mcp/tools/shell/base_process.py +5 -7
  113. hanzo_mcp/tools/shell/bash_session.py +7 -24
  114. hanzo_mcp/tools/shell/bash_session_executor.py +5 -15
  115. hanzo_mcp/tools/shell/bash_tool.py +3 -7
  116. hanzo_mcp/tools/shell/command_executor.py +26 -79
  117. hanzo_mcp/tools/shell/logs.py +4 -16
  118. hanzo_mcp/tools/shell/npx.py +2 -8
  119. hanzo_mcp/tools/shell/npx_tool.py +1 -3
  120. hanzo_mcp/tools/shell/pkill.py +4 -12
  121. hanzo_mcp/tools/shell/process_tool.py +2 -8
  122. hanzo_mcp/tools/shell/processes.py +5 -17
  123. hanzo_mcp/tools/shell/run_background.py +1 -3
  124. hanzo_mcp/tools/shell/run_command.py +1 -3
  125. hanzo_mcp/tools/shell/run_command_windows.py +1 -3
  126. hanzo_mcp/tools/shell/run_tool.py +56 -0
  127. hanzo_mcp/tools/shell/session_manager.py +2 -6
  128. hanzo_mcp/tools/shell/session_storage.py +2 -6
  129. hanzo_mcp/tools/shell/streaming_command.py +7 -23
  130. hanzo_mcp/tools/shell/uvx.py +4 -14
  131. hanzo_mcp/tools/shell/uvx_background.py +2 -6
  132. hanzo_mcp/tools/shell/uvx_tool.py +1 -3
  133. hanzo_mcp/tools/shell/zsh_tool.py +12 -20
  134. hanzo_mcp/tools/todo/todo.py +1 -3
  135. hanzo_mcp/tools/vector/__init__.py +97 -50
  136. hanzo_mcp/tools/vector/ast_analyzer.py +6 -20
  137. hanzo_mcp/tools/vector/git_ingester.py +10 -30
  138. hanzo_mcp/tools/vector/index_tool.py +3 -9
  139. hanzo_mcp/tools/vector/infinity_store.py +7 -27
  140. hanzo_mcp/tools/vector/mock_infinity.py +1 -3
  141. hanzo_mcp/tools/vector/node_tool.py +538 -0
  142. hanzo_mcp/tools/vector/project_manager.py +4 -12
  143. hanzo_mcp/tools/vector/unified_vector.py +384 -0
  144. hanzo_mcp/tools/vector/vector.py +2 -6
  145. hanzo_mcp/tools/vector/vector_index.py +8 -8
  146. hanzo_mcp/tools/vector/vector_search.py +7 -21
  147. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/METADATA +2 -2
  148. hanzo_mcp-0.9.0.dist-info/RECORD +191 -0
  149. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +0 -645
  150. hanzo_mcp/tools/agent/swarm_tool.py +0 -718
  151. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +0 -577
  152. hanzo_mcp/tools/filesystem/batch_search.py +0 -900
  153. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +0 -350
  154. hanzo_mcp/tools/filesystem/find_files.py +0 -369
  155. hanzo_mcp/tools/filesystem/grep.py +0 -467
  156. hanzo_mcp/tools/filesystem/search_tool.py +0 -767
  157. hanzo_mcp/tools/filesystem/symbols_tool.py +0 -515
  158. hanzo_mcp/tools/filesystem/tree.py +0 -270
  159. hanzo_mcp/tools/jupyter/notebook_edit.py +0 -317
  160. hanzo_mcp/tools/jupyter/notebook_read.py +0 -147
  161. hanzo_mcp/tools/todo/todo_read.py +0 -143
  162. hanzo_mcp/tools/todo/todo_write.py +0 -374
  163. hanzo_mcp-0.8.11.dist-info/RECORD +0 -193
  164. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/WHEEL +0 -0
  165. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/entry_points.txt +0 -0
  166. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/top_level.txt +0 -0
@@ -176,6 +176,9 @@ Examples:
176
176
  return "Error: pattern is required"
177
177
 
178
178
  path = params.get("path", os.getcwd())
179
+
180
+ # Expand path (handles ~, $HOME, etc.)
181
+ path = self.expand_path(path)
179
182
  search_type = params.get("search_type", "content")
180
183
  case_sensitive = params.get("case_sensitive", False)
181
184
  max_count = params.get("max_count", 100)
@@ -256,9 +259,7 @@ Examples:
256
259
  tool_ctx,
257
260
  )
258
261
  elif search_type == "blame":
259
- return await self._search_blame(
260
- abs_path, pattern, case_sensitive, file_pattern, tool_ctx
261
- )
262
+ return await self._search_blame(abs_path, pattern, case_sensitive, file_pattern, tool_ctx)
262
263
  else:
263
264
  return f"Unknown search type: {search_type}"
264
265
 
@@ -307,9 +308,7 @@ Examples:
307
308
  elif result.returncode == 1:
308
309
  return f"No matches found for pattern: {pattern}"
309
310
  else:
310
- raise subprocess.CalledProcessError(
311
- result.returncode, cmd, result.stdout, result.stderr
312
- )
311
+ raise subprocess.CalledProcessError(result.returncode, cmd, result.stdout, result.stderr)
313
312
 
314
313
  async def _search_commits(
315
314
  self,
@@ -353,16 +352,11 @@ Examples:
353
352
  lines = result.stdout.strip().split("\n")
354
353
  if lines and lines[0]:
355
354
  await tool_ctx.info(f"Found {len(lines)} commits")
356
- return (
357
- f"Found {len(lines)} commits matching '{pattern}':\n\n"
358
- + result.stdout
359
- )
355
+ return f"Found {len(lines)} commits matching '{pattern}':\n\n" + result.stdout
360
356
  else:
361
357
  return f"No commits found matching: {pattern}"
362
358
  else:
363
- raise subprocess.CalledProcessError(
364
- result.returncode, cmd, result.stdout, result.stderr
365
- )
359
+ raise subprocess.CalledProcessError(result.returncode, cmd, result.stdout, result.stderr)
366
360
 
367
361
  async def _search_diff(
368
362
  self,
@@ -387,8 +381,7 @@ Examples:
387
381
  import re
388
382
 
389
383
  case_insensitive_pattern = "".join(
390
- f"[{c.upper()}{c.lower()}]" if c.isalpha() else re.escape(c)
391
- for c in pattern
384
+ f"[{c.upper()}{c.lower()}]" if c.isalpha() else re.escape(c) for c in pattern
392
385
  )
393
386
  search_flag = f"-G{case_insensitive_pattern}"
394
387
 
@@ -415,9 +408,7 @@ Examples:
415
408
 
416
409
  if result.returncode == 0 and result.stdout.strip():
417
410
  # Parse and highlight matching lines
418
- output = self._highlight_diff_matches(
419
- result.stdout, pattern, case_sensitive
420
- )
411
+ output = self._highlight_diff_matches(result.stdout, pattern, case_sensitive)
421
412
  matches = output.count("commit ")
422
413
  await tool_ctx.info(f"Found {matches} commits with changes")
423
414
  return f"Found {matches} commits with changes matching '{pattern}':\n\n{output}"
@@ -505,9 +496,8 @@ Examples:
505
496
 
506
497
  if all_matches:
507
498
  await tool_ctx.info(f"Found {len(all_matches)} matching lines")
508
- return (
509
- f"Found {len(all_matches)} lines matching '{pattern}':\n\n"
510
- + "\n".join(all_matches[:50])
499
+ return f"Found {len(all_matches)} lines matching '{pattern}':\n\n" + "\n".join(
500
+ all_matches[:50]
511
501
  ) # Limit output
512
502
  else:
513
503
  return f"No lines found matching: {pattern}"
@@ -534,9 +524,7 @@ Examples:
534
524
 
535
525
  return f"Found matches for '{pattern}':\n" + "\n".join(output)
536
526
 
537
- def _highlight_diff_matches(
538
- self, diff_output: str, pattern: str, case_sensitive: bool
539
- ) -> str:
527
+ def _highlight_diff_matches(self, diff_output: str, pattern: str, case_sensitive: bool) -> str:
540
528
  """Highlight matching lines in diff output."""
541
529
  lines = diff_output.split("\n")
542
530
  output = []
@@ -159,6 +159,10 @@ If you want to create a new file, use:
159
159
  await tool_ctx.error(path_validation.error_message)
160
160
  return f"Error: {path_validation.error_message}"
161
161
 
162
+ # Expand path first (handles ~, $HOME, etc.)
163
+ expanded_path = self.expand_path(file_path)
164
+ file_path = expanded_path # Use expanded path for all operations
165
+
162
166
  # Validate each edit
163
167
  for i, edit in enumerate(edits):
164
168
  if not isinstance(edit, dict):
@@ -170,15 +174,11 @@ If you want to create a new file, use:
170
174
  expected_replacements = edit.get("expected_replacements", 1)
171
175
 
172
176
  if old_string is None:
173
- await tool_ctx.error(
174
- f"Parameter 'old_string' in edit at index {i} is required but was None"
175
- )
177
+ await tool_ctx.error(f"Parameter 'old_string' in edit at index {i} is required but was None")
176
178
  return f"Error: Parameter 'old_string' in edit at index {i} is required but was None"
177
179
 
178
180
  if new_string is None:
179
- await tool_ctx.error(
180
- f"Parameter 'new_string' in edit at index {i} is required but was None"
181
- )
181
+ await tool_ctx.error(f"Parameter 'new_string' in edit at index {i} is required but was None")
182
182
  return f"Error: Parameter 'new_string' in edit at index {i} is required but was None"
183
183
 
184
184
  if (
@@ -192,12 +192,8 @@ If you want to create a new file, use:
192
192
  return f"Error: Parameter 'expected_replacements' in edit at index {i} must be a non-negative number"
193
193
 
194
194
  if old_string == new_string:
195
- await tool_ctx.error(
196
- f"Edit at index {i}: old_string and new_string are identical"
197
- )
198
- return (
199
- f"Error: Edit at index {i}: old_string and new_string are identical"
200
- )
195
+ await tool_ctx.error(f"Edit at index {i}: old_string and new_string are identical")
196
+ return f"Error: Edit at index {i}: old_string and new_string are identical"
201
197
 
202
198
  await tool_ctx.info(f"Applying {len(edits)} edits to file: {file_path}")
203
199
 
@@ -261,9 +257,7 @@ If you want to create a new file, use:
261
257
 
262
258
  # Check if old_string exists in current content
263
259
  if old_string not in current_content:
264
- edit_index = (
265
- i + 1 if not creation_mode else i + 2
266
- ) # Adjust for display
260
+ edit_index = i + 1 if not creation_mode else i + 2 # Adjust for display
267
261
  await tool_ctx.error(
268
262
  f"Edit {edit_index}: The specified old_string was not found in the file content"
269
263
  )
@@ -274,9 +268,7 @@ If you want to create a new file, use:
274
268
 
275
269
  # Check if the number of occurrences matches expected_replacements
276
270
  if occurrences != expected_replacements:
277
- edit_index = (
278
- i + 1 if not creation_mode else i + 2
279
- ) # Adjust for display
271
+ edit_index = i + 1 if not creation_mode else i + 2 # Adjust for display
280
272
  await tool_ctx.error(
281
273
  f"Edit {edit_index}: Found {occurrences} occurrences of the specified old_string, but expected {expected_replacements}"
282
274
  )
@@ -118,21 +118,17 @@ Usage:
118
118
  await tool_ctx.error("Parameter 'file_path' is required but was None")
119
119
  return "Error: Parameter 'file_path' is required but was None"
120
120
 
121
- await tool_ctx.info(
122
- f"Reading file: {file_path} (offset: {offset}, limit: {limit})"
123
- )
124
-
125
- # Check if path is allowed
126
- if not self.is_path_allowed(file_path):
127
- await tool_ctx.error(
128
- f"Access denied - path outside allowed directories: {file_path}"
129
- )
130
- return (
131
- f"Error: Access denied - path outside allowed directories: {file_path}"
132
- )
121
+ # Expand path first (handles ~, $HOME, etc.)
122
+ expanded_path = self.expand_path(file_path)
123
+ await tool_ctx.info(f"Reading file: {expanded_path} (offset: {offset}, limit: {limit})")
124
+
125
+ # Check if path is allowed (using expanded path)
126
+ if not self.is_path_allowed(expanded_path):
127
+ await tool_ctx.error(f"Access denied - path outside allowed directories: {expanded_path}")
128
+ return f"Error: Access denied - path outside allowed directories: {expanded_path}"
133
129
 
134
130
  try:
135
- file_path_obj = Path(file_path)
131
+ file_path_obj = Path(expanded_path)
136
132
 
137
133
  if not file_path_obj.exists():
138
134
  await tool_ctx.error(f"File does not exist: {file_path}")
@@ -166,10 +162,7 @@ Usage:
166
162
 
167
163
  # Truncate long lines
168
164
  if len(line) > self.MAX_LINE_LENGTH:
169
- line = (
170
- line[: self.MAX_LINE_LENGTH]
171
- + self.LINE_TRUNCATION_INDICATOR
172
- )
165
+ line = line[: self.MAX_LINE_LENGTH] + self.LINE_TRUNCATION_INDICATOR
173
166
 
174
167
  # Add line with line number (1-based)
175
168
  lines.append(f"{i + 1:6d} {line.rstrip()}")
@@ -196,17 +189,12 @@ Usage:
196
189
 
197
190
  # Truncate long lines
198
191
  if len(line) > self.MAX_LINE_LENGTH:
199
- line = (
200
- line[: self.MAX_LINE_LENGTH]
201
- + self.LINE_TRUNCATION_INDICATOR
202
- )
192
+ line = line[: self.MAX_LINE_LENGTH] + self.LINE_TRUNCATION_INDICATOR
203
193
 
204
194
  # Add line with line number (1-based)
205
195
  lines.append(f"{i + 1:6d} {line.rstrip()}")
206
196
 
207
- await tool_ctx.warning(
208
- f"File read with latin-1 encoding: {file_path}"
209
- )
197
+ await tool_ctx.warning(f"File read with latin-1 encoding: {file_path}")
210
198
 
211
199
  except Exception:
212
200
  await tool_ctx.error(f"Cannot read binary file: {file_path}")
@@ -236,9 +224,7 @@ Usage:
236
224
  await tool_ctx.error(f"Error reading file: {str(e)}")
237
225
  return f"Error: {str(e)}"
238
226
 
239
- async def run(
240
- self, ctx: MCPContext, file_path: str, offset: int = 0, limit: int = 2000
241
- ) -> str:
227
+ async def run(self, ctx: MCPContext, file_path: str, offset: int = 0, limit: int = 2000) -> str:
242
228
  """Run method for backwards compatibility with test scripts.
243
229
 
244
230
  Args:
@@ -271,6 +257,4 @@ Usage:
271
257
  offset: Offset = 0,
272
258
  limit: Limit = 2000,
273
259
  ) -> str:
274
- return await tool_self.call(
275
- ctx, file_path=file_path, offset=offset, limit=limit
276
- )
260
+ return await tool_self.call(ctx, file_path=file_path, offset=offset, limit=limit)
@@ -93,6 +93,9 @@ understand project-specific requirements and preferences."""
93
93
 
94
94
  # Extract parameters
95
95
  search_path = params.get("path", ".")
96
+
97
+ # Expand path (handles ~, $HOME, etc.)
98
+ search_path = self.expand_path(search_path)
96
99
 
97
100
  # Validate path
98
101
  path_validation = self.validate_path(search_path)
@@ -142,9 +145,7 @@ understand project-specific requirements and preferences."""
142
145
  found_configs.append(
143
146
  {
144
147
  "path": str(config_path),
145
- "relative_path": str(
146
- config_path.relative_to(start_path)
147
- ),
148
+ "relative_path": str(config_path.relative_to(start_path)),
148
149
  "content": content,
149
150
  "size": len(content),
150
151
  }
@@ -152,9 +153,7 @@ understand project-specific requirements and preferences."""
152
153
 
153
154
  await tool_ctx.info(f"Found configuration: {config_path}")
154
155
  except Exception as e:
155
- await tool_ctx.warning(
156
- f"Could not read {config_path}: {str(e)}"
157
- )
156
+ await tool_ctx.warning(f"Could not read {config_path}: {str(e)}")
158
157
 
159
158
  # Check if we've reached the root or a git repository root
160
159
  if current_path.parent == current_path:
@@ -169,8 +168,7 @@ understand project-specific requirements and preferences."""
169
168
  if (
170
169
  config_path.exists()
171
170
  and config_path.is_file()
172
- and str(config_path)
173
- not in [c["path"] for c in found_configs]
171
+ and str(config_path) not in [c["path"] for c in found_configs]
174
172
  ):
175
173
  try:
176
174
  if self.is_path_allowed(str(config_path)):
@@ -180,21 +178,15 @@ understand project-specific requirements and preferences."""
180
178
  found_configs.append(
181
179
  {
182
180
  "path": str(config_path),
183
- "relative_path": str(
184
- config_path.relative_to(start_path)
185
- ),
181
+ "relative_path": str(config_path.relative_to(start_path)),
186
182
  "content": content,
187
183
  "size": len(content),
188
184
  }
189
185
  )
190
186
 
191
- await tool_ctx.info(
192
- f"Found configuration: {config_path}"
193
- )
187
+ await tool_ctx.info(f"Found configuration: {config_path}")
194
188
  except Exception as e:
195
- await tool_ctx.warning(
196
- f"Could not read {config_path}: {str(e)}"
197
- )
189
+ await tool_ctx.warning(f"Could not read {config_path}: {str(e)}")
198
190
  break
199
191
 
200
192
  # Move to parent directory