hanzo-mcp 0.7.6__py3-none-any.whl → 0.8.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 (178) hide show
  1. hanzo_mcp/__init__.py +7 -1
  2. hanzo_mcp/__main__.py +1 -1
  3. hanzo_mcp/analytics/__init__.py +2 -2
  4. hanzo_mcp/analytics/posthog_analytics.py +76 -82
  5. hanzo_mcp/cli.py +31 -36
  6. hanzo_mcp/cli_enhanced.py +94 -72
  7. hanzo_mcp/cli_plugin.py +27 -17
  8. hanzo_mcp/config/__init__.py +2 -2
  9. hanzo_mcp/config/settings.py +112 -88
  10. hanzo_mcp/config/tool_config.py +32 -34
  11. hanzo_mcp/dev_server.py +66 -67
  12. hanzo_mcp/prompts/__init__.py +94 -12
  13. hanzo_mcp/prompts/enhanced_prompts.py +809 -0
  14. hanzo_mcp/prompts/example_custom_prompt.py +6 -5
  15. hanzo_mcp/prompts/project_todo_reminder.py +0 -1
  16. hanzo_mcp/prompts/tool_explorer.py +10 -7
  17. hanzo_mcp/server.py +17 -21
  18. hanzo_mcp/server_enhanced.py +15 -22
  19. hanzo_mcp/tools/__init__.py +56 -28
  20. hanzo_mcp/tools/agent/__init__.py +16 -19
  21. hanzo_mcp/tools/agent/agent.py +82 -65
  22. hanzo_mcp/tools/agent/agent_tool.py +152 -122
  23. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
  24. hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
  25. hanzo_mcp/tools/agent/clarification_tool.py +11 -10
  26. hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
  27. hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
  28. hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
  29. hanzo_mcp/tools/agent/code_auth.py +102 -107
  30. hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
  31. hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
  32. hanzo_mcp/tools/agent/critic_tool.py +86 -73
  33. hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
  34. hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
  35. hanzo_mcp/tools/agent/iching_tool.py +404 -139
  36. hanzo_mcp/tools/agent/network_tool.py +89 -73
  37. hanzo_mcp/tools/agent/prompt.py +2 -1
  38. hanzo_mcp/tools/agent/review_tool.py +101 -98
  39. hanzo_mcp/tools/agent/swarm_alias.py +87 -0
  40. hanzo_mcp/tools/agent/swarm_tool.py +246 -161
  41. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
  42. hanzo_mcp/tools/agent/tool_adapter.py +21 -11
  43. hanzo_mcp/tools/common/__init__.py +1 -1
  44. hanzo_mcp/tools/common/base.py +3 -5
  45. hanzo_mcp/tools/common/batch_tool.py +46 -39
  46. hanzo_mcp/tools/common/config_tool.py +120 -84
  47. hanzo_mcp/tools/common/context.py +1 -5
  48. hanzo_mcp/tools/common/context_fix.py +5 -3
  49. hanzo_mcp/tools/common/critic_tool.py +4 -8
  50. hanzo_mcp/tools/common/decorators.py +58 -56
  51. hanzo_mcp/tools/common/enhanced_base.py +29 -32
  52. hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
  53. hanzo_mcp/tools/common/forgiving_edit.py +91 -87
  54. hanzo_mcp/tools/common/mode.py +15 -17
  55. hanzo_mcp/tools/common/mode_loader.py +27 -24
  56. hanzo_mcp/tools/common/paginated_base.py +61 -53
  57. hanzo_mcp/tools/common/paginated_response.py +72 -79
  58. hanzo_mcp/tools/common/pagination.py +50 -53
  59. hanzo_mcp/tools/common/permissions.py +4 -4
  60. hanzo_mcp/tools/common/personality.py +186 -138
  61. hanzo_mcp/tools/common/plugin_loader.py +54 -54
  62. hanzo_mcp/tools/common/stats.py +65 -47
  63. hanzo_mcp/tools/common/test_helpers.py +31 -0
  64. hanzo_mcp/tools/common/thinking_tool.py +4 -8
  65. hanzo_mcp/tools/common/tool_disable.py +17 -12
  66. hanzo_mcp/tools/common/tool_enable.py +13 -14
  67. hanzo_mcp/tools/common/tool_list.py +36 -28
  68. hanzo_mcp/tools/common/truncate.py +23 -23
  69. hanzo_mcp/tools/config/__init__.py +4 -4
  70. hanzo_mcp/tools/config/config_tool.py +42 -29
  71. hanzo_mcp/tools/config/index_config.py +37 -34
  72. hanzo_mcp/tools/config/mode_tool.py +175 -55
  73. hanzo_mcp/tools/database/__init__.py +15 -12
  74. hanzo_mcp/tools/database/database_manager.py +77 -75
  75. hanzo_mcp/tools/database/graph.py +137 -91
  76. hanzo_mcp/tools/database/graph_add.py +30 -18
  77. hanzo_mcp/tools/database/graph_query.py +178 -102
  78. hanzo_mcp/tools/database/graph_remove.py +33 -28
  79. hanzo_mcp/tools/database/graph_search.py +97 -75
  80. hanzo_mcp/tools/database/graph_stats.py +91 -59
  81. hanzo_mcp/tools/database/sql.py +107 -79
  82. hanzo_mcp/tools/database/sql_query.py +30 -24
  83. hanzo_mcp/tools/database/sql_search.py +29 -25
  84. hanzo_mcp/tools/database/sql_stats.py +47 -35
  85. hanzo_mcp/tools/editor/neovim_command.py +25 -28
  86. hanzo_mcp/tools/editor/neovim_edit.py +21 -23
  87. hanzo_mcp/tools/editor/neovim_session.py +60 -54
  88. hanzo_mcp/tools/filesystem/__init__.py +31 -30
  89. hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
  90. hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
  91. hanzo_mcp/tools/filesystem/base.py +1 -1
  92. hanzo_mcp/tools/filesystem/batch_search.py +316 -224
  93. hanzo_mcp/tools/filesystem/content_replace.py +4 -4
  94. hanzo_mcp/tools/filesystem/diff.py +71 -59
  95. hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
  96. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
  97. hanzo_mcp/tools/filesystem/edit.py +4 -4
  98. hanzo_mcp/tools/filesystem/find.py +173 -80
  99. hanzo_mcp/tools/filesystem/find_files.py +73 -52
  100. hanzo_mcp/tools/filesystem/git_search.py +157 -104
  101. hanzo_mcp/tools/filesystem/grep.py +8 -8
  102. hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
  103. hanzo_mcp/tools/filesystem/read.py +12 -10
  104. hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
  105. hanzo_mcp/tools/filesystem/search_tool.py +263 -207
  106. hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
  107. hanzo_mcp/tools/filesystem/tree.py +35 -33
  108. hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
  109. hanzo_mcp/tools/filesystem/watch.py +37 -36
  110. hanzo_mcp/tools/filesystem/write.py +4 -8
  111. hanzo_mcp/tools/jupyter/__init__.py +4 -4
  112. hanzo_mcp/tools/jupyter/base.py +4 -5
  113. hanzo_mcp/tools/jupyter/jupyter.py +67 -47
  114. hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
  115. hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
  116. hanzo_mcp/tools/llm/__init__.py +5 -7
  117. hanzo_mcp/tools/llm/consensus_tool.py +72 -52
  118. hanzo_mcp/tools/llm/llm_manage.py +101 -60
  119. hanzo_mcp/tools/llm/llm_tool.py +226 -166
  120. hanzo_mcp/tools/llm/provider_tools.py +25 -26
  121. hanzo_mcp/tools/lsp/__init__.py +1 -1
  122. hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
  123. hanzo_mcp/tools/mcp/__init__.py +2 -3
  124. hanzo_mcp/tools/mcp/mcp_add.py +27 -25
  125. hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
  126. hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
  127. hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
  128. hanzo_mcp/tools/memory/__init__.py +39 -21
  129. hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
  130. hanzo_mcp/tools/memory/memory_tools.py +90 -108
  131. hanzo_mcp/tools/search/__init__.py +7 -2
  132. hanzo_mcp/tools/search/find_tool.py +297 -212
  133. hanzo_mcp/tools/search/unified_search.py +366 -314
  134. hanzo_mcp/tools/shell/__init__.py +8 -7
  135. hanzo_mcp/tools/shell/auto_background.py +56 -49
  136. hanzo_mcp/tools/shell/base.py +1 -1
  137. hanzo_mcp/tools/shell/base_process.py +75 -75
  138. hanzo_mcp/tools/shell/bash_session.py +2 -2
  139. hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
  140. hanzo_mcp/tools/shell/bash_tool.py +24 -31
  141. hanzo_mcp/tools/shell/command_executor.py +12 -12
  142. hanzo_mcp/tools/shell/logs.py +43 -33
  143. hanzo_mcp/tools/shell/npx.py +13 -13
  144. hanzo_mcp/tools/shell/npx_background.py +24 -21
  145. hanzo_mcp/tools/shell/npx_tool.py +18 -22
  146. hanzo_mcp/tools/shell/open.py +19 -21
  147. hanzo_mcp/tools/shell/pkill.py +31 -26
  148. hanzo_mcp/tools/shell/process_tool.py +32 -32
  149. hanzo_mcp/tools/shell/processes.py +57 -58
  150. hanzo_mcp/tools/shell/run_background.py +24 -25
  151. hanzo_mcp/tools/shell/run_command.py +5 -5
  152. hanzo_mcp/tools/shell/run_command_windows.py +5 -5
  153. hanzo_mcp/tools/shell/session_storage.py +3 -3
  154. hanzo_mcp/tools/shell/streaming_command.py +141 -126
  155. hanzo_mcp/tools/shell/uvx.py +24 -25
  156. hanzo_mcp/tools/shell/uvx_background.py +35 -33
  157. hanzo_mcp/tools/shell/uvx_tool.py +18 -22
  158. hanzo_mcp/tools/todo/__init__.py +6 -2
  159. hanzo_mcp/tools/todo/todo.py +50 -37
  160. hanzo_mcp/tools/todo/todo_read.py +5 -8
  161. hanzo_mcp/tools/todo/todo_write.py +5 -7
  162. hanzo_mcp/tools/vector/__init__.py +40 -28
  163. hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
  164. hanzo_mcp/tools/vector/git_ingester.py +170 -179
  165. hanzo_mcp/tools/vector/index_tool.py +96 -44
  166. hanzo_mcp/tools/vector/infinity_store.py +283 -228
  167. hanzo_mcp/tools/vector/mock_infinity.py +39 -40
  168. hanzo_mcp/tools/vector/project_manager.py +88 -78
  169. hanzo_mcp/tools/vector/vector.py +59 -42
  170. hanzo_mcp/tools/vector/vector_index.py +30 -27
  171. hanzo_mcp/tools/vector/vector_search.py +64 -45
  172. hanzo_mcp/types.py +6 -4
  173. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
  174. hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
  175. hanzo_mcp-0.7.6.dist-info/RECORD +0 -182
  176. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
  177. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
  178. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
@@ -1,51 +1,52 @@
1
1
  """Database manager for project-specific SQLite and graph databases."""
2
2
 
3
- import sqlite3
4
3
  import os
5
- import json
4
+ import sqlite3
5
+ from typing import Dict, List, Optional
6
6
  from pathlib import Path
7
- from typing import Dict, Any, Optional, Tuple, List
8
- from datetime import datetime
9
7
 
10
8
  from hanzo_mcp.tools.common.permissions import PermissionManager
11
9
 
12
10
 
13
11
  class ProjectDatabase:
14
12
  """Manages SQLite and graph databases for a project."""
15
-
13
+
16
14
  def __init__(self, project_path: str):
17
15
  self.project_path = Path(project_path)
18
16
  self.db_dir = self.project_path / ".hanzo" / "db"
19
17
  self.db_dir.mkdir(parents=True, exist_ok=True)
20
-
18
+
21
19
  # SQLite database path
22
20
  self.sqlite_path = self.db_dir / "project.db"
23
21
  self.graph_path = self.db_dir / "graph.db"
24
-
22
+
25
23
  # Initialize databases
26
24
  self._init_sqlite()
27
25
  self._init_graph()
28
-
26
+
29
27
  # Keep graph in memory for performance
30
28
  self.graph_conn = sqlite3.connect(":memory:")
31
29
  self._init_graph_schema(self.graph_conn)
32
30
  self._load_graph_from_disk()
33
-
31
+
34
32
  def _init_sqlite(self):
35
33
  """Initialize SQLite database with common tables."""
36
34
  conn = sqlite3.connect(self.sqlite_path)
37
35
  try:
38
36
  # Create metadata table
39
- conn.execute('''
37
+ conn.execute(
38
+ """
40
39
  CREATE TABLE IF NOT EXISTS metadata (
41
40
  key TEXT PRIMARY KEY,
42
41
  value TEXT,
43
42
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
44
43
  )
45
- ''')
46
-
44
+ """
45
+ )
46
+
47
47
  # Create files table
48
- conn.execute('''
48
+ conn.execute(
49
+ """
49
50
  CREATE TABLE IF NOT EXISTS files (
50
51
  path TEXT PRIMARY KEY,
51
52
  content TEXT,
@@ -54,10 +55,12 @@ class ProjectDatabase:
54
55
  hash TEXT,
55
56
  metadata TEXT
56
57
  )
57
- ''')
58
-
58
+ """
59
+ )
60
+
59
61
  # Create symbols table
60
- conn.execute('''
62
+ conn.execute(
63
+ """
61
64
  CREATE TABLE IF NOT EXISTS symbols (
62
65
  id INTEGER PRIMARY KEY AUTOINCREMENT,
63
66
  file_path TEXT,
@@ -69,17 +72,18 @@ class ProjectDatabase:
69
72
  signature TEXT,
70
73
  FOREIGN KEY (file_path) REFERENCES files(path)
71
74
  )
72
- ''')
73
-
75
+ """
76
+ )
77
+
74
78
  # Create index for fast searches
75
- conn.execute('CREATE INDEX IF NOT EXISTS idx_files_path ON files(path)')
76
- conn.execute('CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name)')
77
- conn.execute('CREATE INDEX IF NOT EXISTS idx_symbols_type ON symbols(type)')
78
-
79
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_files_path ON files(path)")
80
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name)")
81
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_symbols_type ON symbols(type)")
82
+
79
83
  conn.commit()
80
84
  finally:
81
85
  conn.close()
82
-
86
+
83
87
  def _init_graph(self):
84
88
  """Initialize graph database on disk."""
85
89
  conn = sqlite3.connect(self.graph_path)
@@ -88,21 +92,24 @@ class ProjectDatabase:
88
92
  conn.commit()
89
93
  finally:
90
94
  conn.close()
91
-
95
+
92
96
  def _init_graph_schema(self, conn: sqlite3.Connection):
93
97
  """Initialize graph database schema."""
94
98
  # Nodes table
95
- conn.execute('''
99
+ conn.execute(
100
+ """
96
101
  CREATE TABLE IF NOT EXISTS nodes (
97
102
  id TEXT PRIMARY KEY,
98
103
  type TEXT NOT NULL,
99
104
  properties TEXT,
100
105
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
101
106
  )
102
- ''')
103
-
107
+ """
108
+ )
109
+
104
110
  # Edges table
105
- conn.execute('''
111
+ conn.execute(
112
+ """
106
113
  CREATE TABLE IF NOT EXISTS edges (
107
114
  source TEXT NOT NULL,
108
115
  target TEXT NOT NULL,
@@ -114,72 +121,67 @@ class ProjectDatabase:
114
121
  FOREIGN KEY (source) REFERENCES nodes(id),
115
122
  FOREIGN KEY (target) REFERENCES nodes(id)
116
123
  )
117
- ''')
118
-
124
+ """
125
+ )
126
+
119
127
  # Indexes for graph traversal
120
- conn.execute('CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source)')
121
- conn.execute('CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target)')
122
- conn.execute('CREATE INDEX IF NOT EXISTS idx_edges_relationship ON edges(relationship)')
123
- conn.execute('CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type)')
124
-
128
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source)")
129
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target)")
130
+ conn.execute(
131
+ "CREATE INDEX IF NOT EXISTS idx_edges_relationship ON edges(relationship)"
132
+ )
133
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type)")
134
+
125
135
  def _load_graph_from_disk(self):
126
136
  """Load graph from disk into memory."""
127
137
  disk_conn = sqlite3.connect(self.graph_path)
128
138
  try:
129
139
  # Copy nodes
130
- nodes = disk_conn.execute('SELECT * FROM nodes').fetchall()
140
+ nodes = disk_conn.execute("SELECT * FROM nodes").fetchall()
131
141
  self.graph_conn.executemany(
132
- 'INSERT OR REPLACE INTO nodes VALUES (?, ?, ?, ?)',
133
- nodes
142
+ "INSERT OR REPLACE INTO nodes VALUES (?, ?, ?, ?)", nodes
134
143
  )
135
-
144
+
136
145
  # Copy edges
137
- edges = disk_conn.execute('SELECT * FROM edges').fetchall()
146
+ edges = disk_conn.execute("SELECT * FROM edges").fetchall()
138
147
  self.graph_conn.executemany(
139
- 'INSERT OR REPLACE INTO edges VALUES (?, ?, ?, ?, ?, ?)',
140
- edges
148
+ "INSERT OR REPLACE INTO edges VALUES (?, ?, ?, ?, ?, ?)", edges
141
149
  )
142
-
150
+
143
151
  self.graph_conn.commit()
144
152
  finally:
145
153
  disk_conn.close()
146
-
154
+
147
155
  def _save_graph_to_disk(self):
148
156
  """Save in-memory graph to disk."""
149
157
  disk_conn = sqlite3.connect(self.graph_path)
150
158
  try:
151
159
  # Clear existing data
152
- disk_conn.execute('DELETE FROM edges')
153
- disk_conn.execute('DELETE FROM nodes')
154
-
160
+ disk_conn.execute("DELETE FROM edges")
161
+ disk_conn.execute("DELETE FROM nodes")
162
+
155
163
  # Copy nodes
156
- nodes = self.graph_conn.execute('SELECT * FROM nodes').fetchall()
157
- disk_conn.executemany(
158
- 'INSERT INTO nodes VALUES (?, ?, ?, ?)',
159
- nodes
160
- )
161
-
164
+ nodes = self.graph_conn.execute("SELECT * FROM nodes").fetchall()
165
+ disk_conn.executemany("INSERT INTO nodes VALUES (?, ?, ?, ?)", nodes)
166
+
162
167
  # Copy edges
163
- edges = self.graph_conn.execute('SELECT * FROM edges').fetchall()
164
- disk_conn.executemany(
165
- 'INSERT INTO edges VALUES (?, ?, ?, ?, ?, ?)',
166
- edges
167
- )
168
-
168
+ edges = self.graph_conn.execute("SELECT * FROM edges").fetchall()
169
+ disk_conn.executemany("INSERT INTO edges VALUES (?, ?, ?, ?, ?, ?)", edges)
170
+
169
171
  disk_conn.commit()
170
172
  finally:
171
173
  disk_conn.close()
172
-
174
+
173
175
  def get_sqlite_connection(self) -> sqlite3.Connection:
174
176
  """Get SQLite connection."""
175
177
  conn = sqlite3.connect(self.sqlite_path)
176
178
  conn.row_factory = sqlite3.Row
177
179
  return conn
178
-
180
+
179
181
  def get_graph_connection(self) -> sqlite3.Connection:
180
182
  """Get in-memory graph connection."""
181
183
  return self.graph_conn
182
-
184
+
183
185
  def close(self):
184
186
  """Close connections and save graph to disk."""
185
187
  self._save_graph_to_disk()
@@ -188,59 +190,59 @@ class ProjectDatabase:
188
190
 
189
191
  class DatabaseManager:
190
192
  """Manages databases for multiple projects."""
191
-
193
+
192
194
  def __init__(self, permission_manager: PermissionManager):
193
195
  self.permission_manager = permission_manager
194
196
  self.projects: Dict[str, ProjectDatabase] = {}
195
197
  self.search_paths: List[str] = []
196
-
198
+
197
199
  def add_search_path(self, path: str):
198
200
  """Add a path to search for projects."""
199
201
  if path not in self.search_paths:
200
202
  self.search_paths.append(path)
201
-
203
+
202
204
  def get_project_db(self, project_path: str) -> ProjectDatabase:
203
205
  """Get or create database for a project."""
204
206
  project_path = os.path.abspath(project_path)
205
-
207
+
206
208
  # Check permissions
207
209
  if not self.permission_manager.has_permission(project_path):
208
210
  raise PermissionError(f"No permission to access: {project_path}")
209
-
211
+
210
212
  # Create database if not exists
211
213
  if project_path not in self.projects:
212
214
  self.projects[project_path] = ProjectDatabase(project_path)
213
-
215
+
214
216
  return self.projects[project_path]
215
-
217
+
216
218
  def get_project_for_path(self, file_path: str) -> Optional[ProjectDatabase]:
217
219
  """Find the project database for a given file path."""
218
220
  file_path = os.path.abspath(file_path)
219
-
221
+
220
222
  # Check if file is in a known project
221
223
  for project_path in self.projects:
222
224
  if file_path.startswith(project_path):
223
225
  return self.projects[project_path]
224
-
226
+
225
227
  # Search up the directory tree for a project
226
228
  current = Path(file_path)
227
229
  if current.is_file():
228
230
  current = current.parent
229
-
231
+
230
232
  while current != current.parent:
231
233
  # Check for project markers
232
234
  if (current / ".git").exists() or (current / "LLM.md").exists():
233
235
  return self.get_project_db(str(current))
234
236
  current = current.parent
235
-
237
+
236
238
  # No project found, use the directory of the file
237
239
  if Path(file_path).is_file():
238
240
  return self.get_project_db(str(Path(file_path).parent))
239
241
  else:
240
242
  return self.get_project_db(file_path)
241
-
243
+
242
244
  def close_all(self):
243
245
  """Close all project databases."""
244
246
  for db in self.projects.values():
245
247
  db.close()
246
- self.projects.clear()
248
+ self.projects.clear()