codegraph-engine 2.1.7__py3-none-any.whl → 2.2.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.
codegraph/mcp/server.py CHANGED
@@ -173,6 +173,8 @@ def create_server(
173
173
  repository: Path,
174
174
  settings: Settings | None = None,
175
175
  profile: str = "full",
176
+ host: str = "127.0.0.1",
177
+ port: int = 8765,
176
178
  ) -> Any:
177
179
  """Build an MCP FastMCP server lazily so normal CLI use needs no MCP dependency."""
178
180
  try:
@@ -184,7 +186,12 @@ def create_server(
184
186
 
185
187
  indexer = Indexer(repository, settings)
186
188
  memory = MemoryStore(indexer.db_path)
187
- app = FastMCP("CodeGraph MCP", instructions=render_agent_rules("antigravity"))
189
+ app = FastMCP(
190
+ "CodeGraph MCP",
191
+ instructions=render_agent_rules("antigravity"),
192
+ host=host,
193
+ port=port,
194
+ )
188
195
  max_read_bytes = indexer.settings.max_read_bytes
189
196
 
190
197
  # -----------------------------------------------------------------------
@@ -486,25 +493,79 @@ def create_server(
486
493
  return git_analyze_change_impact(con, indexer.repository, since=since, until=until)
487
494
 
488
495
  def get_repository_status() -> dict[str, object]:
489
- """Get repository indexing status, freshness, symbol counts, and health."""
496
+ """Get repository indexing status, freshness, symbol counts, and health (automatically hot-reloads in-memory caches when `.codegraph.sqlite3` is created or updated by `codegraph init` / `codegraph index`)."""
497
+ from codegraph.resources.cache import get_graph_cache, get_parse_cache
498
+
499
+ get_graph_cache().clear()
500
+ get_parse_cache().clear()
501
+ if not indexer.db_path.exists():
502
+ return {
503
+ "status": "error",
504
+ "repository": str(indexer.repository),
505
+ "generation": 0,
506
+ "freshness": "NOT_INDEXED",
507
+ "freshness_detail": "No CodeGraph index exists for this repository.",
508
+ "files": 0,
509
+ "files_indexed": 0,
510
+ "symbols": 0,
511
+ "symbols_indexed": 0,
512
+ "graph_edges": 0,
513
+ "framework_routes": 0,
514
+ "reloaded": True,
515
+ "error": {
516
+ "code": "INDEX_NOT_FOUND",
517
+ "message": "No CodeGraph index exists for this repository.",
518
+ "next_action": {
519
+ "command": "codegraph init",
520
+ "reason": "Initialize the repository before querying it.",
521
+ },
522
+ },
523
+ }
490
524
  with indexer.session() as con:
491
525
  report = check_freshness(indexer.repository, con)
492
- file_count = con.execute("SELECT count(*) FROM files").fetchone()[0]
493
- symbol_count = con.execute("SELECT count(*) FROM symbols").fetchone()[0]
494
- edge_count = con.execute("SELECT count(*) FROM graph_edges").fetchone()[0]
495
- route_count = con.execute("SELECT count(*) FROM framework_routes").fetchone()[0]
526
+ file_count = int(con.execute("SELECT count(*) FROM files").fetchone()[0])
527
+ symbol_count = int(con.execute("SELECT count(*) FROM symbols").fetchone()[0])
528
+ edge_count = int(con.execute("SELECT count(*) FROM graph_edges").fetchone()[0])
529
+ route_count = int(con.execute("SELECT count(*) FROM framework_routes").fetchone()[0])
530
+ if file_count == 0:
531
+ return {
532
+ "status": "error",
533
+ "repository": str(indexer.repository),
534
+ "generation": report.index_generation,
535
+ "freshness": "NOT_INDEXED",
536
+ "freshness_detail": "No files are indexed in this repository.",
537
+ "files": 0,
538
+ "files_indexed": 0,
539
+ "symbols": 0,
540
+ "symbols_indexed": 0,
541
+ "graph_edges": 0,
542
+ "framework_routes": 0,
543
+ "reloaded": True,
544
+ "error": {
545
+ "code": "INDEX_NOT_FOUND",
546
+ "message": "No CodeGraph index exists for this repository.",
547
+ "next_action": {
548
+ "command": "codegraph init",
549
+ "reason": "Initialize the repository before querying it.",
550
+ },
551
+ },
552
+ }
496
553
  return {
554
+ "status": "ok",
497
555
  "repository": str(indexer.repository),
498
556
  "generation": report.index_generation,
499
557
  "freshness": report.status.value,
500
558
  "freshness_detail": report.detail,
559
+ "files": file_count,
501
560
  "files_indexed": file_count,
561
+ "symbols": symbol_count,
502
562
  "symbols_indexed": symbol_count,
503
563
  "graph_edges": edge_count,
504
564
  "framework_routes": route_count,
505
565
  "modified_files": report.modified_files,
506
566
  "deleted_files": report.deleted_files,
507
567
  "parse_failed_files": report.parse_failed_files,
568
+ "reloaded": True,
508
569
  }
509
570
 
510
571
  def trace_call(
@@ -1024,12 +1085,41 @@ def create_server(
1024
1085
  else:
1025
1086
  selected_names = set(all_tools.keys())
1026
1087
 
1088
+ _last_db_sig: list[tuple[int, int, int, int] | None] = [None]
1089
+
1090
+ def _compute_db_sig() -> tuple[int, int, int, int] | None:
1091
+ db_file = indexer.db_path
1092
+ if not db_file.exists():
1093
+ return None
1094
+ try:
1095
+ st = db_file.stat()
1096
+ wal_file = Path(f"{db_file}-wal")
1097
+ wal_mtime = wal_file.stat().st_mtime_ns if wal_file.exists() else 0
1098
+ return (st.st_ino, st.st_mtime_ns, st.st_size, wal_mtime)
1099
+ except OSError:
1100
+ return None
1101
+
1102
+ _last_db_sig[0] = _compute_db_sig()
1103
+
1104
+ def _ensure_hot_reloaded() -> bool:
1105
+ """Detect if `.codegraph.sqlite3` was created, replaced, or updated on disk (e.g. via `codegraph init`)."""
1106
+ cur_sig = _compute_db_sig()
1107
+ if cur_sig != _last_db_sig[0]:
1108
+ _last_db_sig[0] = cur_sig
1109
+ from codegraph.resources.cache import get_graph_cache, get_parse_cache
1110
+
1111
+ get_graph_cache().clear()
1112
+ get_parse_cache().clear()
1113
+ return True
1114
+ return False
1115
+
1027
1116
  for tool_name in sorted(selected_names):
1028
1117
  target_fn: Any = all_tools[tool_name]
1029
1118
 
1030
1119
  def _make_guarded(fn: Any) -> Any:
1031
1120
  @functools.wraps(fn)
1032
1121
  def _guarded(*args: Any, **kwargs: Any) -> Any:
1122
+ _ensure_hot_reloaded()
1033
1123
  with indexer.governor.task_scope(TaskPriority.INTERACTIVE_HIGH):
1034
1124
  return fn(*args, **kwargs)
1035
1125
 
@@ -1151,4 +1241,5 @@ def create_server(
1151
1241
  "2. Run `get_context` with intent='REVIEW'."
1152
1242
  )
1153
1243
 
1244
+ app._codegraph_indexer = indexer # type: ignore[attr-defined]
1154
1245
  return app
@@ -111,14 +111,17 @@ class MCPDoctorReport:
111
111
  "discovered_tools": list(self.discovered_tools),
112
112
  }
113
113
 
114
- def format_human(self) -> str:
114
+ def format_human(self, *, unicode_override: bool | None = None) -> str:
115
+ from codegraph.cli_output import get_cli_symbols
116
+
117
+ sym = get_cli_symbols(unicode_override=unicode_override)
115
118
  lines = [
116
119
  "CodeGraph MCP Doctor",
117
120
  "--------------------",
118
121
  "",
119
122
  ]
120
123
  for chk in self.checks:
121
- mark = "✓" if chk.passed else "✗"
124
+ mark = sym.ok if chk.passed else sym.error
122
125
  lines.append(f"{mark} {chk.name}")
123
126
  return "\n".join(lines)
124
127
 
codegraph/monorepo.py CHANGED
@@ -29,7 +29,7 @@ from codegraph.indexing.classifier import FileCategory, classify_file
29
29
  from codegraph.security import is_sensitive, safe_path
30
30
 
31
31
  try:
32
- import yaml # type: ignore[import-untyped]
32
+ import yaml
33
33
  _HAS_YAML = True
34
34
  except ImportError:
35
35
  _HAS_YAML = False