codegraph-engine 3.0.0__1-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.
Files changed (130) hide show
  1. codegraph/__init__.py +37 -0
  2. codegraph/agent.py +26 -0
  3. codegraph/agent_brain.py +2212 -0
  4. codegraph/agent_capabilities.py +4058 -0
  5. codegraph/agent_rules.py +531 -0
  6. codegraph/api_drift.py +455 -0
  7. codegraph/architecture.py +371 -0
  8. codegraph/async_queue/__init__.py +50 -0
  9. codegraph/async_queue/detectors/__init__.py +48 -0
  10. codegraph/async_queue/detectors/celery.py +160 -0
  11. codegraph/async_queue/detectors/kafka.py +168 -0
  12. codegraph/async_queue/detectors/rabbitmq.py +116 -0
  13. codegraph/async_queue/detectors/redis.py +130 -0
  14. codegraph/async_queue/detectors/sqs.py +135 -0
  15. codegraph/async_queue/models.py +161 -0
  16. codegraph/async_queue/otel_models.py +74 -0
  17. codegraph/async_queue/otel_parser.py +337 -0
  18. codegraph/async_queue/reconciler.py +427 -0
  19. codegraph/async_queue/scanner.py +291 -0
  20. codegraph/async_queue/traversal.py +447 -0
  21. codegraph/audit.py +106 -0
  22. codegraph/binding_resolver.py +616 -0
  23. codegraph/branch_comparison.py +197 -0
  24. codegraph/cache.py +95 -0
  25. codegraph/change_impact.py +547 -0
  26. codegraph/cli.py +2436 -0
  27. codegraph/cli_output.py +205 -0
  28. codegraph/config.py +45 -0
  29. codegraph/constraints.py +238 -0
  30. codegraph/context.py +2182 -0
  31. codegraph/context_freshness.py +351 -0
  32. codegraph/database/__init__.py +72 -0
  33. codegraph/database/extractor.py +3651 -0
  34. codegraph/database/interrogation.py +1494 -0
  35. codegraph/database/models.py +310 -0
  36. codegraph/database/schema_drift.py +862 -0
  37. codegraph/epistemic.py +200 -0
  38. codegraph/errors.py +282 -0
  39. codegraph/evidence/__init__.py +15 -0
  40. codegraph/evidence/citations.py +418 -0
  41. codegraph/evidence_contract.py +486 -0
  42. codegraph/frameworks.py +960 -0
  43. codegraph/frameworks_nestjs.py +502 -0
  44. codegraph/frameworks_nextjs.py +337 -0
  45. codegraph/frameworks_react.py +457 -0
  46. codegraph/freshness.py +335 -0
  47. codegraph/frontend_html.py +109 -0
  48. codegraph/git.py +362 -0
  49. codegraph/git_state.py +545 -0
  50. codegraph/graph/__init__.py +46 -0
  51. codegraph/graph/models.py +56 -0
  52. codegraph/graph/traversal.py +1993 -0
  53. codegraph/indexing/__init__.py +24 -0
  54. codegraph/indexing/classifier.py +564 -0
  55. codegraph/indexing/indexer.py +2578 -0
  56. codegraph/indexing/models.py +427 -0
  57. codegraph/indexing/parser.py +3404 -0
  58. codegraph/indexing/pool.py +136 -0
  59. codegraph/indexing/scanner.py +211 -0
  60. codegraph/indexing/telemetry.py +326 -0
  61. codegraph/indexing/templates.py +418 -0
  62. codegraph/indexing/test_framework.py +116 -0
  63. codegraph/installer.py +1702 -0
  64. codegraph/interrogation.py +2355 -0
  65. codegraph/llm/__init__.py +3 -0
  66. codegraph/llm/base.py +15 -0
  67. codegraph/llm/context.py +20 -0
  68. codegraph/mcp/__init__.py +3 -0
  69. codegraph/mcp/server.py +1476 -0
  70. codegraph/mcp_diagnostics.py +882 -0
  71. codegraph/memory/__init__.py +3 -0
  72. codegraph/memory/store.py +46 -0
  73. codegraph/models.py +310 -0
  74. codegraph/monorepo.py +925 -0
  75. codegraph/observability.py +262 -0
  76. codegraph/optimizer.py +1014 -0
  77. codegraph/planner.py +417 -0
  78. codegraph/process_lifecycle.py +1153 -0
  79. codegraph/py.typed +1 -0
  80. codegraph/query_expansion.py +199 -0
  81. codegraph/ranking.py +408 -0
  82. codegraph/refactor/__init__.py +36 -0
  83. codegraph/refactor/models.py +114 -0
  84. codegraph/refactor/renamer.py +844 -0
  85. codegraph/refactor/transactions.py +414 -0
  86. codegraph/resolver.py +3968 -0
  87. codegraph/resources/__init__.py +46 -0
  88. codegraph/resources/cache.py +117 -0
  89. codegraph/resources/coalescer.py +83 -0
  90. codegraph/resources/debouncer.py +98 -0
  91. codegraph/resources/governor.py +219 -0
  92. codegraph/resources/monitor.py +102 -0
  93. codegraph/resources/policy.py +123 -0
  94. codegraph/retrieval_policy.py +320 -0
  95. codegraph/roblox.py +2174 -0
  96. codegraph/route_composer.py +363 -0
  97. codegraph/route_topology.py +172 -0
  98. codegraph/runtime/__init__.py +51 -0
  99. codegraph/runtime/collector.py +155 -0
  100. codegraph/runtime/db_watcher.py +191 -0
  101. codegraph/runtime/ingestor.py +692 -0
  102. codegraph/runtime/models.py +130 -0
  103. codegraph/runtime/port_inspector.py +304 -0
  104. codegraph/runtime/reconciliation.py +492 -0
  105. codegraph/runtime/runner.py +353 -0
  106. codegraph/runtime/trace_linker.py +175 -0
  107. codegraph/search/__init__.py +25 -0
  108. codegraph/search/hybrid.py +846 -0
  109. codegraph/search/semantic.py +28 -0
  110. codegraph/security/__init__.py +51 -0
  111. codegraph/security/paths.py +189 -0
  112. codegraph/security/redaction.py +633 -0
  113. codegraph/semantic_conflicts.py +270 -0
  114. codegraph/semantic_decorators.py +372 -0
  115. codegraph/structural_diff.py +542 -0
  116. codegraph/styles.py +144 -0
  117. codegraph/symbol_history.py +277 -0
  118. codegraph/target_resolver.py +348 -0
  119. codegraph/task.py +637 -0
  120. codegraph/tool_selection_eval.py +1158 -0
  121. codegraph/tsconfig.py +236 -0
  122. codegraph/ui/__init__.py +6 -0
  123. codegraph/ui/server.py +755 -0
  124. codegraph/watcher.py +323 -0
  125. codegraph_engine-3.0.0.dist-info/METADATA +859 -0
  126. codegraph_engine-3.0.0.dist-info/RECORD +130 -0
  127. codegraph_engine-3.0.0.dist-info/WHEEL +6 -0
  128. codegraph_engine-3.0.0.dist-info/entry_points.txt +2 -0
  129. codegraph_engine-3.0.0.dist-info/licenses/LICENSE +21 -0
  130. codegraph_engine-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,155 @@
1
+ """Zero-Heat, Push-Based OpenTelemetry (OTLP) & Dev Telemetry Collector.
2
+
3
+ Listens for OTLP HTTP traces from localhost dev servers:
4
+ - POST /v1/traces (Standard OpenTelemetry protobuf-in-JSON or OTLP JSON)
5
+ - POST /api/telemetry (Lightweight JSON / JSONL dev events)
6
+ - GET /healthz (Health probe)
7
+
8
+ Efficiency & Anti-Heat Invariants:
9
+ 1. Purely event-driven socket select; 0.0% CPU when no requests are being processed.
10
+ 2. In-memory bounded queue with ring-buffer semantics (capped at max_events=1,000) to prevent memory ballooning.
11
+ 3. Strict secrecy redaction: Authorization, Cookie, and passwords stripped before SQLite persistence.
12
+ 4. Graceful shutdown: Closes sockets cleanly without leaking ports.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import sqlite3
18
+ import threading
19
+ from http.server import BaseHTTPRequestHandler, HTTPServer
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ from codegraph.runtime.ingestor import ingest_runtime_traces
24
+
25
+
26
+ class TelemetryRequestHandler(BaseHTTPRequestHandler):
27
+ """Zero-overhead HTTP request handler for OTLP spans and dev telemetry."""
28
+
29
+ server: TelemetryServer # Type annotation for custom server
30
+
31
+ def log_message(self, format: str, *args: Any) -> None:
32
+ """Suppress standard stderr access logging to prevent noisy terminal output."""
33
+ return
34
+
35
+ def do_GET(self) -> None:
36
+ if self.path in ("/healthz", "/health"):
37
+ self.send_response(200)
38
+ self.send_header("Content-Type", "application/json")
39
+ self.end_headers()
40
+ self.wfile.write(b'{"status":"ok","engine":"codegraph-runtime-collector"}')
41
+ return
42
+ self.send_response(404)
43
+ self.end_headers()
44
+
45
+ def do_POST(self) -> None:
46
+ if self.path not in ("/v1/traces", "/api/telemetry", "/telemetry"):
47
+ self.send_response(404)
48
+ self.end_headers()
49
+ return
50
+
51
+ content_length = int(self.headers.get("Content-Length", 0))
52
+ if content_length <= 0 or content_length > 10 * 1024 * 1024: # Max 10MB payload
53
+ self.send_response(400)
54
+ self.end_headers()
55
+ return
56
+
57
+ try:
58
+ body_bytes = self.rfile.read(content_length)
59
+ body_text = body_bytes.decode("utf-8", errors="replace")
60
+ parsed_json = json.loads(body_text)
61
+
62
+ # Ingest into CodeGraph SQLite database
63
+ with self.server.db_lock:
64
+ ingest_runtime_traces(
65
+ self.server.db_connection,
66
+ parsed_json,
67
+ repository=self.server.repo_root,
68
+ format="otel" if "/traces" in self.path else "json",
69
+ max_events=1000,
70
+ )
71
+
72
+ self.send_response(200)
73
+ self.send_header("Content-Type", "application/json")
74
+ self.end_headers()
75
+ self.wfile.write(b'{"partialSuccess":{}}')
76
+ except Exception as exc:
77
+ self.send_response(500)
78
+ self.send_header("Content-Type", "application/json")
79
+ self.end_headers()
80
+ err_msg = json.dumps({"error": str(exc)}).encode("utf-8")
81
+ self.wfile.write(err_msg)
82
+
83
+
84
+ class TelemetryServer(HTTPServer):
85
+ """Custom HTTPServer maintaining db connection and repo references."""
86
+
87
+ def __init__(
88
+ self,
89
+ server_address: tuple[str, int],
90
+ db_connection: sqlite3.Connection,
91
+ repo_root: Path,
92
+ ) -> None:
93
+ super().__init__(server_address, TelemetryRequestHandler)
94
+ self.db_connection = db_connection
95
+ self.repo_root = repo_root
96
+ self.db_lock = threading.Lock()
97
+
98
+
99
+ class RuntimeCollectorDaemon:
100
+ """Manages lifecycle of background OTLP collector."""
101
+
102
+ def __init__(
103
+ self,
104
+ db_connection: sqlite3.Connection,
105
+ repo_root: Path,
106
+ port: int = 4318,
107
+ host: str = "127.0.0.1",
108
+ ) -> None:
109
+ self.db_connection = db_connection
110
+ self.repo_root = repo_root
111
+ self.port = port
112
+ self.host = host
113
+ self._server: TelemetryServer | None = None
114
+ self._thread: threading.Thread | None = None
115
+ self._is_running = False
116
+
117
+ @property
118
+ def is_running(self) -> bool:
119
+ return self._is_running
120
+
121
+ def start(self) -> bool:
122
+ """Start the collector in a low-priority background daemon thread."""
123
+ if self._is_running:
124
+ return True
125
+ try:
126
+ self._server = TelemetryServer(
127
+ (self.host, self.port),
128
+ self.db_connection,
129
+ self.repo_root,
130
+ )
131
+ self._thread = threading.Thread(
132
+ target=self._server.serve_forever,
133
+ name="codegraph-otlp-collector",
134
+ daemon=True,
135
+ )
136
+ self._thread.start()
137
+ self._is_running = True
138
+ return True
139
+ except OSError:
140
+ # Port might be in use or unavailable; fail closed gracefully without crashing
141
+ self._is_running = False
142
+ return False
143
+
144
+ def stop(self) -> None:
145
+ """Shutdown the collector cleanly."""
146
+ if not self._is_running or not self._server:
147
+ return
148
+ try:
149
+ self._server.shutdown()
150
+ self._server.server_close()
151
+ except Exception:
152
+ pass
153
+ finally:
154
+ self._is_running = False
155
+ self._server = None
@@ -0,0 +1,191 @@
1
+ """Automatic Database Schema Sync & Migration Drift Watcher.
2
+
3
+ Continuously tracks:
4
+ 1. Migration directory updates (Prisma, Drizzle, Alembic, raw SQL migrations).
5
+ 2. Live database schema version cookies (PRAGMA schema_version, _prisma_migrations, alembic_version).
6
+ 3. Schema drift detection: flags discrepancy between static ORM models and database state.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import sqlite3
12
+ import time
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class SchemaDriftItem:
20
+ kind: str # MISSING_COLUMN | MISSING_TABLE | TYPE_MISMATCH | NULLABILITY_MISMATCH
21
+ table_name: str
22
+ column_name: str = ""
23
+ db_type: str = ""
24
+ model_type: str = ""
25
+ message: str = ""
26
+
27
+ def as_dict(self) -> dict[str, Any]:
28
+ return {
29
+ "kind": self.kind,
30
+ "table_name": self.table_name,
31
+ "column_name": self.column_name,
32
+ "db_type": self.db_type,
33
+ "model_type": self.model_type,
34
+ "message": self.message,
35
+ }
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class SchemaDriftReport:
40
+ is_drifted: bool
41
+ schema_version: str
42
+ migration_hash: str
43
+ drifts: tuple[SchemaDriftItem, ...] = ()
44
+ migration_files_count: int = 0
45
+ latest_migration_name: str = ""
46
+
47
+ def as_dict(self) -> dict[str, Any]:
48
+ return {
49
+ "is_drifted": self.is_drifted,
50
+ "schema_version": self.schema_version,
51
+ "migration_hash": self.migration_hash,
52
+ "drifts": [d.as_dict() for d in self.drifts],
53
+ "migration_files_count": self.migration_files_count,
54
+ "latest_migration_name": self.latest_migration_name,
55
+ }
56
+
57
+
58
+ def compute_migration_directory_state(repo_root: Path) -> tuple[str, int, str]:
59
+ """Scan migration directories and compute combined hash, count, and latest migration name."""
60
+ migration_patterns = (
61
+ "**/migrations/*.sql",
62
+ "**/prisma/migrations/**/*.sql",
63
+ "**/drizzle/*.sql",
64
+ "**/alembic/versions/*.py",
65
+ )
66
+ migration_files: list[Path] = []
67
+ for pattern in migration_patterns:
68
+ migration_files.extend(repo_root.glob(pattern))
69
+
70
+ migration_files = [f for f in migration_files if "node_modules" not in f.parts and ".git" not in f.parts]
71
+ if not migration_files:
72
+ return "", 0, ""
73
+
74
+ migration_files.sort(key=lambda f: f.name)
75
+ hasher = hashlib.sha256()
76
+ for f in migration_files:
77
+ try:
78
+ hasher.update(f.name.encode())
79
+ hasher.update(str(f.stat().st_mtime_ns).encode())
80
+ except OSError:
81
+ pass
82
+
83
+ latest_name = migration_files[-1].name
84
+ return hasher.hexdigest()[:16], len(migration_files), latest_name
85
+
86
+
87
+ def check_sqlite_schema_version(db_path: Path) -> int:
88
+ """Read PRAGMA schema_version from SQLite database."""
89
+ if not db_path.exists():
90
+ return 0
91
+ try:
92
+ con = sqlite3.connect(str(db_path), timeout=5.0)
93
+ cur = con.execute("PRAGMA schema_version")
94
+ val = cur.fetchone()
95
+ con.close()
96
+ return int(val[0]) if val else 0
97
+ except Exception:
98
+ return 0
99
+
100
+
101
+ _DRIFT_CACHE: dict[str, tuple[float, SchemaDriftReport]] = {}
102
+ _DRIFT_CACHE_TTL_SEC = 5.0
103
+
104
+
105
+ def detect_schema_drift(
106
+ con: sqlite3.Connection,
107
+ repo_root: Path,
108
+ ) -> SchemaDriftReport:
109
+ """Compare indexed DB models against migration state with 5-second TTL cache (Zero idle CPU)."""
110
+ global _DRIFT_CACHE
111
+ cache_key = str(repo_root)
112
+ now = time.monotonic()
113
+ if cache_key in _DRIFT_CACHE:
114
+ last_t, cached_report = _DRIFT_CACHE[cache_key]
115
+ if now - last_t < _DRIFT_CACHE_TTL_SEC:
116
+ return cached_report
117
+
118
+ mig_hash, mig_count, latest_mig = compute_migration_directory_state(repo_root)
119
+
120
+ # 1. Fetch tables and columns registered in CodeGraph
121
+ db_tables_rows = con.execute("SELECT table_name, name, dialect FROM db_entities WHERE kind = 'Table'").fetchall()
122
+ indexed_tables = {str(r["table_name"] or r["name"]).lower(): str(r["dialect"]) for r in db_tables_rows}
123
+
124
+ indexed_cols: dict[str, set[str]] = {}
125
+ col_rows = con.execute("SELECT table_name, name, data_type FROM db_entities WHERE kind = 'Column'").fetchall()
126
+ for r in col_rows:
127
+ t_name = str(r["table_name"] or "").lower()
128
+ if t_name not in indexed_cols:
129
+ indexed_cols[t_name] = set()
130
+ c_name = str(r["name"] or "").lower()
131
+ if c_name:
132
+ indexed_cols[t_name].add(c_name)
133
+
134
+ drifts: list[SchemaDriftItem] = []
135
+
136
+ # 2. If a local SQLite database exists in repo (e.g. dev.db, local.db, test.db), check live tables
137
+ local_dbs = list(repo_root.glob("*.db")) + list(repo_root.glob("*.sqlite")) + list(repo_root.glob("*.sqlite3"))
138
+ local_dbs = [d for d in local_dbs if not d.name.startswith(".codegraph")]
139
+
140
+ schema_version_str = "0"
141
+ if local_dbs:
142
+ target_db = local_dbs[0]
143
+ schema_version_str = str(check_sqlite_schema_version(target_db))
144
+ try:
145
+ live_con = sqlite3.connect(str(target_db), timeout=5.0)
146
+ live_tables = [
147
+ row[0].lower()
148
+ for row in live_con.execute(
149
+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
150
+ ).fetchall()
151
+ ]
152
+ for lt in live_tables:
153
+ if lt not in indexed_tables and not lt.startswith(("_prisma", "alembic")):
154
+ drifts.append(
155
+ SchemaDriftItem(
156
+ kind="MISSING_TABLE",
157
+ table_name=lt,
158
+ message=f"Table '{lt}' exists in live database but has no corresponding code model.",
159
+ )
160
+ )
161
+ else:
162
+ # check columns
163
+ live_cols = [
164
+ crow[1].lower()
165
+ for crow in live_con.execute(f"PRAGMA table_info('{lt}')").fetchall()
166
+ ]
167
+ known_c = indexed_cols.get(lt, set())
168
+ for lc in live_cols:
169
+ if lc not in known_c:
170
+ drifts.append(
171
+ SchemaDriftItem(
172
+ kind="MISSING_COLUMN",
173
+ table_name=lt,
174
+ column_name=lc,
175
+ message=f"Column '{lt}.{lc}' exists in live database but is missing in code model.",
176
+ )
177
+ )
178
+ live_con.close()
179
+ except Exception:
180
+ pass
181
+
182
+ report = SchemaDriftReport(
183
+ is_drifted=len(drifts) > 0,
184
+ schema_version=schema_version_str,
185
+ migration_hash=mig_hash,
186
+ drifts=tuple(drifts),
187
+ migration_files_count=mig_count,
188
+ latest_migration_name=latest_mig,
189
+ )
190
+ _DRIFT_CACHE[cache_key] = (now, report)
191
+ return report