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
@@ -1,369 +0,0 @@
1
- """Find files using ffind library."""
2
-
3
- import os
4
- from typing import Unpack, Optional, Annotated, TypedDict, final, override
5
-
6
- from pydantic import Field
7
- from mcp.server.fastmcp import Context as MCPContext
8
-
9
- from hanzo_mcp.tools.common.base import BaseTool
10
- from hanzo_mcp.tools.common.context import create_tool_context
11
- from hanzo_mcp.tools.common.permissions import PermissionManager
12
-
13
- try:
14
- import ffind
15
-
16
- FFIND_AVAILABLE = True
17
- except ImportError:
18
- FFIND_AVAILABLE = False
19
-
20
-
21
- Pattern = Annotated[
22
- str,
23
- Field(
24
- description="File name pattern to search for (supports wildcards)",
25
- min_length=1,
26
- ),
27
- ]
28
-
29
- Path_ = Annotated[
30
- Optional[str],
31
- Field(
32
- description="Directory to search in (defaults to current directory)",
33
- default=None,
34
- ),
35
- ]
36
-
37
- Recursive = Annotated[
38
- bool,
39
- Field(
40
- description="Search recursively in subdirectories",
41
- default=True,
42
- ),
43
- ]
44
-
45
- IgnoreCase = Annotated[
46
- bool,
47
- Field(
48
- description="Case-insensitive search",
49
- default=True,
50
- ),
51
- ]
52
-
53
- Hidden = Annotated[
54
- bool,
55
- Field(
56
- description="Include hidden files in search",
57
- default=False,
58
- ),
59
- ]
60
-
61
- DirsOnly = Annotated[
62
- bool,
63
- Field(
64
- description="Only find directories",
65
- default=False,
66
- ),
67
- ]
68
-
69
- FilesOnly = Annotated[
70
- bool,
71
- Field(
72
- description="Only find files (not directories)",
73
- default=True,
74
- ),
75
- ]
76
-
77
- MaxResults = Annotated[
78
- int,
79
- Field(
80
- description="Maximum number of results",
81
- default=100,
82
- ),
83
- ]
84
-
85
-
86
- class FindFilesParams(TypedDict, total=False):
87
- """Parameters for find files tool."""
88
-
89
- pattern: str
90
- path: Optional[str]
91
- recursive: bool
92
- ignore_case: bool
93
- hidden: bool
94
- dirs_only: bool
95
- files_only: bool
96
- max_results: int
97
-
98
-
99
- @final
100
- class FindFilesTool(BaseTool):
101
- """Tool for finding files using ffind."""
102
-
103
- def __init__(self, permission_manager: PermissionManager):
104
- """Initialize the find files tool.
105
-
106
- Args:
107
- permission_manager: Permission manager for access control
108
- """
109
- self.permission_manager = permission_manager
110
-
111
- @property
112
- @override
113
- def name(self) -> str:
114
- """Get the tool name."""
115
- return "find_files"
116
-
117
- @property
118
- @override
119
- def description(self) -> str:
120
- """Get the tool description."""
121
- return """Find files by name pattern using efficient search.
122
-
123
- Uses the ffind library for fast file searching with support for:
124
- - Wildcards (* and ?)
125
- - Case-insensitive search
126
- - Hidden files
127
- - Directory vs file filtering
128
-
129
- Examples:
130
- - find_files --pattern "*.py" # Find all Python files
131
- - find_files --pattern "test_*" # Find files starting with test_
132
- - find_files --pattern "README.*" # Find README files
133
- - find_files --pattern "*config*" --hidden # Include hidden config files
134
- - find_files --pattern "src" --dirs-only # Find directories named src
135
-
136
- For content search, use 'grep' instead.
137
- For database search, use 'sql_search' or 'vector_search'.
138
- """
139
-
140
- @override
141
- async def call(
142
- self,
143
- ctx: MCPContext,
144
- **params: Unpack[FindFilesParams],
145
- ) -> str:
146
- """Find files matching pattern.
147
-
148
- Args:
149
- ctx: MCP context
150
- **params: Tool parameters
151
-
152
- Returns:
153
- List of matching files
154
- """
155
- tool_ctx = create_tool_context(ctx)
156
- await tool_ctx.set_tool_info(self.name)
157
-
158
- # Extract parameters
159
- pattern = params.get("pattern")
160
- if not pattern:
161
- return "Error: pattern is required"
162
-
163
- search_path = params.get("path") or os.getcwd()
164
- recursive = params.get("recursive", True)
165
- ignore_case = params.get("ignore_case", True)
166
- hidden = params.get("hidden", False)
167
- dirs_only = params.get("dirs_only", False)
168
- files_only = params.get("files_only", True)
169
- max_results = params.get("max_results", 100)
170
-
171
- # Validate path
172
- search_path = os.path.abspath(search_path)
173
- if not self.permission_manager.has_permission(search_path):
174
- return f"Error: No permission to access {search_path}"
175
-
176
- if not os.path.exists(search_path):
177
- return f"Error: Path does not exist: {search_path}"
178
-
179
- await tool_ctx.info(f"Searching for '{pattern}' in {search_path}")
180
-
181
- # If ffind is not available, fall back to basic implementation
182
- if not FFIND_AVAILABLE:
183
- return await self._find_files_fallback(
184
- pattern,
185
- search_path,
186
- recursive,
187
- ignore_case,
188
- hidden,
189
- dirs_only,
190
- files_only,
191
- max_results,
192
- )
193
-
194
- try:
195
- # Use ffind for efficient searching
196
- results = []
197
- count = 0
198
-
199
- # Configure ffind options
200
- options = {
201
- "pattern": pattern,
202
- "path": search_path,
203
- "recursive": recursive,
204
- "ignore_case": ignore_case,
205
- "hidden": hidden,
206
- }
207
-
208
- # Search with ffind
209
- for filepath in ffind.find(**options):
210
- # Check if it matches our criteria
211
- is_dir = os.path.isdir(filepath)
212
-
213
- if dirs_only and not is_dir:
214
- continue
215
- if files_only and is_dir:
216
- continue
217
-
218
- # Make path relative for cleaner output
219
- try:
220
- rel_path = os.path.relpath(filepath, search_path)
221
- except ValueError:
222
- rel_path = filepath
223
-
224
- results.append(rel_path)
225
- count += 1
226
-
227
- if count >= max_results:
228
- break
229
-
230
- if not results:
231
- return f"No files found matching '{pattern}'"
232
-
233
- # Format output
234
- output = [f"Found {len(results)} file(s) matching '{pattern}':"]
235
- output.append("")
236
-
237
- for filepath in sorted(results):
238
- output.append(filepath)
239
-
240
- if count >= max_results:
241
- output.append(f"\n... (showing first {max_results} results)")
242
-
243
- return "\n".join(output)
244
-
245
- except Exception as e:
246
- await tool_ctx.error(f"Error during search: {str(e)}")
247
- # Fall back to basic implementation
248
- return await self._find_files_fallback(
249
- pattern,
250
- search_path,
251
- recursive,
252
- ignore_case,
253
- hidden,
254
- dirs_only,
255
- files_only,
256
- max_results,
257
- )
258
-
259
- async def _find_files_fallback(
260
- self,
261
- pattern: str,
262
- search_path: str,
263
- recursive: bool,
264
- ignore_case: bool,
265
- hidden: bool,
266
- dirs_only: bool,
267
- files_only: bool,
268
- max_results: int,
269
- ) -> str:
270
- """Fallback implementation when ffind is not available."""
271
-
272
- results = []
273
- count = 0
274
-
275
- # Convert pattern for case-insensitive matching
276
- if ignore_case:
277
- pattern = pattern.lower()
278
-
279
- try:
280
- if recursive:
281
- # Walk directory tree
282
- for root, dirs, files in os.walk(search_path):
283
- # Skip hidden directories if not requested
284
- if not hidden:
285
- dirs[:] = [d for d in dirs if not d.startswith(".")]
286
-
287
- # Check directories
288
- if not files_only:
289
- for dirname in dirs:
290
- if self._match_pattern(dirname, pattern, ignore_case):
291
- filepath = os.path.join(root, dirname)
292
- rel_path = os.path.relpath(filepath, search_path)
293
- results.append(rel_path + "/")
294
- count += 1
295
- if count >= max_results:
296
- break
297
-
298
- # Check files
299
- if not dirs_only:
300
- for filename in files:
301
- if not hidden and filename.startswith("."):
302
- continue
303
-
304
- if self._match_pattern(filename, pattern, ignore_case):
305
- filepath = os.path.join(root, filename)
306
- rel_path = os.path.relpath(filepath, search_path)
307
- results.append(rel_path)
308
- count += 1
309
- if count >= max_results:
310
- break
311
-
312
- if count >= max_results:
313
- break
314
- else:
315
- # Only search in the specified directory
316
- for entry in os.listdir(search_path):
317
- if not hidden and entry.startswith("."):
318
- continue
319
-
320
- filepath = os.path.join(search_path, entry)
321
- is_dir = os.path.isdir(filepath)
322
-
323
- if dirs_only and not is_dir:
324
- continue
325
- if files_only and is_dir:
326
- continue
327
-
328
- if self._match_pattern(entry, pattern, ignore_case):
329
- results.append(entry + "/" if is_dir else entry)
330
- count += 1
331
- if count >= max_results:
332
- break
333
-
334
- if not results:
335
- return f"No files found matching '{pattern}' (using fallback search)"
336
-
337
- # Format output
338
- output = [
339
- f"Found {len(results)} file(s) matching '{pattern}' (using fallback search):"
340
- ]
341
- output.append("")
342
-
343
- for filepath in sorted(results):
344
- output.append(filepath)
345
-
346
- if count >= max_results:
347
- output.append(f"\n... (showing first {max_results} results)")
348
-
349
- output.append(
350
- "\nNote: Install 'ffind' for faster searching: pip install ffind"
351
- )
352
-
353
- return "\n".join(output)
354
-
355
- except Exception as e:
356
- return f"Error searching for files: {str(e)}"
357
-
358
- def _match_pattern(self, filename: str, pattern: str, ignore_case: bool) -> bool:
359
- """Check if filename matches pattern."""
360
- import fnmatch
361
-
362
- if ignore_case:
363
- return fnmatch.fnmatch(filename.lower(), pattern)
364
- else:
365
- return fnmatch.fnmatch(filename, pattern)
366
-
367
- def register(self, mcp_server) -> None:
368
- """Register this tool with the MCP server."""
369
- pass