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,291 @@
1
+ """Repository scanner and reconciliation engine for asynchronous message queues and tasks.
2
+
3
+ Epistemic Invariant:
4
+ Static topic matching within the same repository produces STATIC_QUEUE_LINKED edges.
5
+ It NEVER claims to be runtime proof (OTEL_DISTRIBUTED_VERIFIED).
6
+ The graph structure maintains distinct entities:
7
+ Producer -> PRODUCES_TO_QUEUE -> Queue -> CONSUMES_FROM_QUEUE -> Consumer
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import ast
12
+ import hashlib
13
+ import sqlite3
14
+ from pathlib import Path
15
+
16
+ from codegraph.async_queue.detectors import run_all_detectors
17
+ from codegraph.async_queue.models import (
18
+ AsyncConsumerRecord,
19
+ AsyncProducerRecord,
20
+ AsyncQueueEntity,
21
+ AsyncQueueEvidenceClass,
22
+ )
23
+ from codegraph.indexing.models import normalize_module
24
+
25
+ ASYNC_SCHEMA = """
26
+ CREATE TABLE IF NOT EXISTS async_queues (
27
+ queue_id TEXT PRIMARY KEY,
28
+ system TEXT NOT NULL,
29
+ destination TEXT NOT NULL,
30
+ service TEXT NOT NULL DEFAULT '',
31
+ environment TEXT NOT NULL DEFAULT '',
32
+ is_dlq INTEGER NOT NULL DEFAULT 0,
33
+ metadata_json TEXT NOT NULL DEFAULT '{}'
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS async_producers (
37
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
38
+ queue_id TEXT NOT NULL,
39
+ system TEXT NOT NULL,
40
+ destination TEXT NOT NULL,
41
+ caller_canonical_id TEXT NOT NULL,
42
+ file_path TEXT NOT NULL,
43
+ line INTEGER NOT NULL,
44
+ call_snippet TEXT NOT NULL DEFAULT '',
45
+ confidence TEXT NOT NULL DEFAULT 'HIGH',
46
+ metadata_json TEXT NOT NULL DEFAULT '{}',
47
+ FOREIGN KEY(queue_id) REFERENCES async_queues(queue_id) ON DELETE CASCADE
48
+ );
49
+
50
+ CREATE TABLE IF NOT EXISTS async_consumers (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ queue_id TEXT NOT NULL,
53
+ system TEXT NOT NULL,
54
+ destination TEXT NOT NULL,
55
+ handler_canonical_id TEXT NOT NULL,
56
+ file_path TEXT NOT NULL,
57
+ line INTEGER NOT NULL,
58
+ handler_snippet TEXT NOT NULL DEFAULT '',
59
+ confidence TEXT NOT NULL DEFAULT 'HIGH',
60
+ metadata_json TEXT NOT NULL DEFAULT '{}',
61
+ FOREIGN KEY(queue_id) REFERENCES async_queues(queue_id) ON DELETE CASCADE
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS async_links (
65
+ link_id TEXT PRIMARY KEY,
66
+ queue_id TEXT NOT NULL,
67
+ system TEXT NOT NULL,
68
+ destination TEXT NOT NULL,
69
+ producer_id TEXT NOT NULL,
70
+ producer_file TEXT NOT NULL,
71
+ producer_line INTEGER NOT NULL,
72
+ consumer_id TEXT NOT NULL,
73
+ consumer_file TEXT NOT NULL,
74
+ consumer_line INTEGER NOT NULL,
75
+ evidence_class TEXT NOT NULL DEFAULT 'STATIC_QUEUE_LINKED',
76
+ confidence TEXT NOT NULL DEFAULT 'HIGH',
77
+ trace_count INTEGER NOT NULL DEFAULT 1,
78
+ avg_latency_ms REAL NOT NULL DEFAULT 0.0,
79
+ p50_ms REAL NOT NULL DEFAULT 0.0,
80
+ p95_ms REAL NOT NULL DEFAULT 0.0,
81
+ p99_ms REAL NOT NULL DEFAULT 0.0,
82
+ error_count INTEGER NOT NULL DEFAULT 0,
83
+ metadata_json TEXT NOT NULL DEFAULT '{}',
84
+ FOREIGN KEY(queue_id) REFERENCES async_queues(queue_id) ON DELETE CASCADE
85
+ );
86
+
87
+ CREATE INDEX IF NOT EXISTS idx_async_producers_queue ON async_producers(queue_id);
88
+ CREATE INDEX IF NOT EXISTS idx_async_producers_caller ON async_producers(caller_canonical_id);
89
+ CREATE INDEX IF NOT EXISTS idx_async_consumers_queue ON async_consumers(queue_id);
90
+ CREATE INDEX IF NOT EXISTS idx_async_consumers_handler ON async_consumers(handler_canonical_id);
91
+ CREATE INDEX IF NOT EXISTS idx_async_links_queue ON async_links(queue_id);
92
+ CREATE INDEX IF NOT EXISTS idx_async_links_producer ON async_links(producer_id);
93
+ CREATE INDEX IF NOT EXISTS idx_async_links_consumer ON async_links(consumer_id);
94
+ """
95
+
96
+
97
+ def ensure_async_tables(con: sqlite3.Connection) -> None:
98
+ """Ensure async queue storage tables and indexes exist."""
99
+ con.executescript(ASYNC_SCHEMA)
100
+
101
+
102
+ def scan_file_for_async(
103
+ file_path: Path,
104
+ repo_root: Path,
105
+ ) -> tuple[list[AsyncProducerRecord], list[AsyncConsumerRecord]]:
106
+ """Scan a single Python file for async messaging producers and consumers."""
107
+ rel_path = file_path.relative_to(repo_root).as_posix()
108
+ module_name = normalize_module(rel_path, "python")
109
+ try:
110
+ content = file_path.read_text(encoding="utf-8", errors="replace")
111
+ tree = ast.parse(content, filename=rel_path)
112
+ except Exception:
113
+ return [], []
114
+
115
+ return run_all_detectors(tree, content, rel_path, module_name)
116
+
117
+
118
+ class AsyncQueueScanner:
119
+ """Repository-level scanner and graph reconciler for async queues."""
120
+
121
+ def __init__(self, repository: Path) -> None:
122
+ self.repository = repository.resolve()
123
+
124
+ def scan_repository(
125
+ self,
126
+ con: sqlite3.Connection,
127
+ ) -> dict[str, int]:
128
+ """Scan all repository Python files, persist entities, and link matching queues."""
129
+ ensure_async_tables(con)
130
+
131
+ producers: list[AsyncProducerRecord] = []
132
+ consumers: list[AsyncConsumerRecord] = []
133
+
134
+ # Find all Python source files
135
+ for py_file in self.repository.rglob("*.py"):
136
+ if ".git" in py_file.parts or "__pycache__" in py_file.parts or ".codegraph" in py_file.parts:
137
+ continue
138
+ prods, cons = scan_file_for_async(py_file, self.repository)
139
+ producers.extend(prods)
140
+ consumers.extend(cons)
141
+
142
+ # Clear existing static async tables
143
+ con.execute("DELETE FROM async_links WHERE evidence_class='STATIC_QUEUE_LINKED'")
144
+ con.execute("DELETE FROM async_consumers")
145
+ con.execute("DELETE FROM async_producers")
146
+ con.execute("DELETE FROM async_queues")
147
+ con.execute("DELETE FROM graph_edges WHERE relationship IN ('PRODUCES_TO_QUEUE', 'CONSUMES_FROM_QUEUE', 'STATIC_QUEUE_LINKED')")
148
+
149
+ # Collect distinct queues
150
+ queues_by_id: dict[str, AsyncQueueEntity] = {}
151
+ for p in producers:
152
+ if p.queue_id not in queues_by_id:
153
+ queues_by_id[p.queue_id] = AsyncQueueEntity(
154
+ queue_id=p.queue_id,
155
+ system=p.system,
156
+ destination=p.destination,
157
+ )
158
+ for c in consumers:
159
+ if c.queue_id not in queues_by_id:
160
+ queues_by_id[c.queue_id] = AsyncQueueEntity(
161
+ queue_id=c.queue_id,
162
+ system=c.system,
163
+ destination=c.destination,
164
+ )
165
+
166
+ # Insert async_queues
167
+ for q in queues_by_id.values():
168
+ con.execute(
169
+ "INSERT INTO async_queues (queue_id, system, destination, service, environment, is_dlq) "
170
+ "VALUES (?, ?, ?, ?, ?, ?)",
171
+ (q.queue_id, str(q.system), q.destination, q.service, q.environment, 1 if q.is_dead_letter_queue else 0),
172
+ )
173
+
174
+ # Insert async_producers
175
+ for p in producers:
176
+ con.execute(
177
+ "INSERT INTO async_producers (queue_id, system, destination, caller_canonical_id, file_path, line, call_snippet, confidence) "
178
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
179
+ (p.queue_id, str(p.system), p.destination, p.caller_canonical_id, p.file_path, p.line, p.call_snippet, p.confidence),
180
+ )
181
+
182
+ # Insert async_consumers
183
+ for c in consumers:
184
+ con.execute(
185
+ "INSERT INTO async_consumers (queue_id, system, destination, handler_canonical_id, file_path, line, handler_snippet, confidence) "
186
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
187
+ (c.queue_id, str(c.system), c.destination, c.handler_canonical_id, c.file_path, c.line, c.handler_snippet, c.confidence),
188
+ )
189
+
190
+ # Reconcile STATIC_QUEUE_LINKED edges (Same repository topic/queue matching)
191
+ # Groups: fan-out (1 producer -> N consumers) and fan-in (M producers -> 1 consumer)
192
+ prods_by_queue: dict[str, list[AsyncProducerRecord]] = {}
193
+ for p in producers:
194
+ prods_by_queue.setdefault(p.queue_id, []).append(p)
195
+
196
+ cons_by_queue: dict[str, list[AsyncConsumerRecord]] = {}
197
+ for c in consumers:
198
+ cons_by_queue.setdefault(c.queue_id, []).append(c)
199
+
200
+ links_count = 0
201
+ graph_edges_to_insert: list[tuple[str, str, str, str, str, int, int, str, str]] = []
202
+
203
+ for q_id, q_prods in prods_by_queue.items():
204
+ q_cons = cons_by_queue.get(q_id, [])
205
+
206
+ # 1. PRODUCES_TO_QUEUE edges: Producer -> Queue
207
+ for p in q_prods:
208
+ graph_edges_to_insert.append((
209
+ p.caller_canonical_id,
210
+ q_id,
211
+ "PRODUCES_TO_QUEUE",
212
+ "HIGH",
213
+ p.file_path,
214
+ p.line,
215
+ p.line,
216
+ p.call_snippet,
217
+ "STATIC_QUEUE_LINKED",
218
+ ))
219
+
220
+ # 2. CONSUMES_FROM_QUEUE edges: Queue -> Consumer
221
+ for c in q_cons:
222
+ graph_edges_to_insert.append((
223
+ q_id,
224
+ c.handler_canonical_id,
225
+ "CONSUMES_FROM_QUEUE",
226
+ "HIGH",
227
+ c.file_path,
228
+ c.line,
229
+ c.line,
230
+ c.handler_snippet,
231
+ "STATIC_QUEUE_LINKED",
232
+ ))
233
+
234
+ # 3. Direct bridge links: Producer -> Consumer across queue boundary
235
+ for p in q_prods:
236
+ for c in q_cons:
237
+ link_seed = f"{p.caller_canonical_id}:{q_id}:{c.handler_canonical_id}:{p.file_path}:{p.line}"
238
+ link_id = hashlib.sha256(link_seed.encode("utf-8")).hexdigest()[:16]
239
+
240
+ con.execute(
241
+ "INSERT OR REPLACE INTO async_links ("
242
+ "link_id, queue_id, system, destination, producer_id, producer_file, producer_line, "
243
+ "consumer_id, consumer_file, consumer_line, evidence_class, confidence"
244
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
245
+ (
246
+ link_id,
247
+ q_id,
248
+ str(p.system),
249
+ p.destination,
250
+ p.caller_canonical_id,
251
+ p.file_path,
252
+ p.line,
253
+ c.handler_canonical_id,
254
+ c.file_path,
255
+ c.line,
256
+ AsyncQueueEvidenceClass.STATIC_QUEUE_LINKED.value,
257
+ "HIGH",
258
+ ),
259
+ )
260
+ links_count += 1
261
+
262
+ # Direct STATIC_QUEUE_LINKED edge in graph_edges
263
+ graph_edges_to_insert.append((
264
+ p.caller_canonical_id,
265
+ c.handler_canonical_id,
266
+ "STATIC_QUEUE_LINKED",
267
+ "HIGH",
268
+ p.file_path,
269
+ p.line,
270
+ c.line,
271
+ f"Matched queue '{q_id}' ({p.system})",
272
+ "STATIC_QUEUE_LINKED",
273
+ ))
274
+
275
+ # Insert graph edges with ON CONFLICT IGNORE
276
+ if graph_edges_to_insert:
277
+ con.executemany(
278
+ "INSERT OR IGNORE INTO graph_edges ("
279
+ "source, target, relationship, confidence, file, start_line, end_line, evidence, evidence_class"
280
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
281
+ graph_edges_to_insert,
282
+ )
283
+
284
+ con.commit()
285
+
286
+ return {
287
+ "queues": len(queues_by_id),
288
+ "producers": len(producers),
289
+ "consumers": len(consumers),
290
+ "links": links_count,
291
+ }