Skip to content

Commit 5f73eb5

Browse files
committed
refactor: modernize indexing and error handling
- Improved Indexer: Refactored to use GitPython (git.Repo) for commit/diff operations. - Enhanced doctor.py: Added OSError handling and logging in _path_size. - MCP Server Improvements: Added type annotations and removed type: ignores in call_tool. - Service Layer Cleanup: Standardized top-level imports and improved reload error handling. - Maintenance: Updated .gitignore for SQLite temporary files.
1 parent c394b7d commit 5f73eb5

5 files changed

Lines changed: 35 additions & 26 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ docs_test/
213213
.knowcode/
214214
knowcode_index/
215215
knowledge.db
216+
knowledge.db-shm
217+
knowledge.db-wal
216218
.DS_Store
217219
tests/test_sample-prose
218220
tests/.DS_Store

src/knowcode/doctor.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,10 +686,16 @@ def _path_size(path: Path) -> int:
686686
return path.stat().st_size
687687

688688
total = 0
689+
import logging
690+
logger = logging.getLogger(__name__)
691+
689692
for child in path.rglob("*"):
690-
with contextlib.suppress(OSError):
693+
try:
691694
if child.is_file():
692695
total += child.stat().st_size
696+
except OSError as e:
697+
logger.warning("Skipped file size calculation for %s: %s", child, e)
698+
693699
return total
694700

695701

src/knowcode/indexing/indexer.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,12 @@ def index_directory(self, root_dir: str | Path) -> int:
8787
total_chunks += 1
8888

8989
# Store current commit hash for future incremental indexing
90-
import subprocess
9190
try:
92-
res = subprocess.run(["git", "rev-parse", "HEAD"], cwd=str(root_path), capture_output=True, text=True, check=True)
93-
self.manifest["last_indexed_commit"] = res.stdout.strip()
91+
import git
92+
repo = git.Repo(str(root_path), search_parent_directories=True)
93+
self.manifest["last_indexed_commit"] = repo.head.commit.hexsha
9494
except Exception as e:
95-
import logging
96-
logging.getLogger(__name__).warning("Ignored exception: %s", e)
95+
logger.warning("Failed to get git commit for manifest: %s", e)
9796

9897
return total_chunks
9998

@@ -106,16 +105,16 @@ def index_incremental(self, root_dir: str | Path) -> int:
106105
Returns:
107106
Number of new chunks added.
108107
"""
109-
import subprocess
110108
root_path = Path(root_dir).resolve()
111109

112110
# Determine last indexed commit
113111
last_commit = self.manifest.get("last_indexed_commit")
114112

115113
# Get current commit
116114
try:
117-
res = subprocess.run(["git", "rev-parse", "HEAD"], cwd=str(root_path), capture_output=True, text=True, check=True)
118-
current_commit = res.stdout.strip()
115+
import git
116+
repo = git.Repo(str(root_path), search_parent_directories=True)
117+
current_commit = repo.head.commit.hexsha
119118
except Exception as e:
120119
logger.warning(f"Failed to get current git commit: {e}. Incremental indexer falling back to full index.")
121120
return self.index_directory(root_dir)
@@ -131,20 +130,20 @@ def index_incremental(self, root_dir: str | Path) -> int:
131130

132131
# Get changed files
133132
try:
134-
diff_res = subprocess.run(
135-
["git", "diff", "--name-only", last_commit, current_commit],
136-
cwd=str(root_path), capture_output=True, text=True, check=True
137-
)
138-
untracked_res = subprocess.run(
139-
["git", "ls-files", "--others", "--exclude-standard"],
140-
cwd=str(root_path), capture_output=True, text=True, check=True
141-
)
133+
changed_files_rel = []
134+
diff = repo.commit(last_commit).diff(current_commit)
135+
for d in diff:
136+
if d.b_path:
137+
changed_files_rel.append(d.b_path)
138+
139+
untracked = repo.untracked_files
140+
changed_files_rel.extend(untracked)
142141
except Exception as e:
143142
logger.warning(f"Failed to get git diff: {e}. Falling back to full index.")
144143
self.manifest["last_indexed_commit"] = current_commit
145144
return self.index_directory(root_dir)
146145

147-
changed_files_rel = diff_res.stdout.splitlines() + untracked_res.stdout.splitlines()
146+
changed_files_rel = list(set(changed_files_rel))
148147
changed_files = [str(root_path / f) for f in set(changed_files_rel) if f.strip()]
149148

150149
if not changed_files:

src/knowcode/mcp/server.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ def handle_tool_call(self, name: str, arguments: dict[str, Any]) -> str:
330330
logging.getLogger(__name__).warning("Ignored telemetry OS error: %s", e)
331331

332332
try:
333+
result: dict[str, Any] | list[dict[str, Any]]
334+
333335
if name == "search_codebase":
334336
if "query" not in arguments or not isinstance(arguments["query"], str):
335337
raise ValueError("search_codebase requires 'query' as a string")
@@ -343,7 +345,7 @@ def handle_tool_call(self, name: str, arguments: dict[str, Any]) -> str:
343345
elif name == "get_entity_context":
344346
if "entity_id" not in arguments or not isinstance(arguments["entity_id"], str):
345347
raise ValueError("get_entity_context requires 'entity_id' as a string")
346-
result = self.get_entity_context( # type: ignore
348+
result = self.get_entity_context(
347349
entity_id=arguments["entity_id"],
348350
task_type=str(arguments.get("task_type", "general")),
349351
max_tokens=int(arguments.get("max_tokens", 2000)),
@@ -359,7 +361,7 @@ def handle_tool_call(self, name: str, arguments: dict[str, Any]) -> str:
359361
elif name == "retrieve_context_for_query":
360362
if "query" not in arguments or not isinstance(arguments["query"], str):
361363
raise ValueError("retrieve_context_for_query requires 'query' as a string")
362-
result = self.retrieve_context_for_query( # type: ignore
364+
result = self.retrieve_context_for_query(
363365
query=arguments["query"],
364366
task_type=str(arguments.get("task_type", "auto")),
365367
max_tokens=int(arguments.get("max_tokens", 4000)),
@@ -368,7 +370,7 @@ def handle_tool_call(self, name: str, arguments: dict[str, Any]) -> str:
368370
verbosity=str(arguments.get("verbosity", "minimal")),
369371
)
370372
else:
371-
result = {"error": f"Unknown tool: {name}"} # type: ignore
373+
result = {"error": f"Unknown tool: {name}"}
372374

373375
return json.dumps(result, separators=(',', ':'))
374376
except KnowCodePrerequisiteError as e:

src/knowcode/service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
from __future__ import annotations
44

5+
import os
56
import re
7+
import shutil
8+
import logging
69
from pathlib import Path
710
from typing import TYPE_CHECKING, Any, Optional
811

@@ -284,8 +287,6 @@ def retrieve_context_for_query(
284287

285288
def get_freshness_metadata(self) -> dict[str, Any]:
286289
"""Compute freshness metadata for the knowledge store and index."""
287-
import os
288-
289290
store_file = self._store_file()
290291
last_store_rebuild = 0.0
291292
if store_file.exists():
@@ -346,7 +347,6 @@ def _build_index(self, directory: str | Path, index_path: str | Path, incrementa
346347

347348
if not incremental:
348349
# Clear/initialize directory
349-
import shutil
350350
if resolved_index_path.exists():
351351
shutil.rmtree(resolved_index_path)
352352

@@ -688,9 +688,9 @@ def reload(self) -> None:
688688
try:
689689
# Force reload by accessing the property
690690
_ = self.store
691-
except FileNotFoundError:
691+
except FileNotFoundError as e:
692692
# If the file is gone, keep _store as None
693-
pass
693+
logging.getLogger(__name__).warning("Knowledge store file not found during reload: %s", e)
694694

695695
def get_entity_details(self, entity_id: str) -> Optional[dict[str, Any]]:
696696
"""Get detailed information about an entity as a dictionary.

0 commit comments

Comments
 (0)