codegraph-py 1.0.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/__init__.py +12 -0
- codegraph/__main__.py +6 -0
- codegraph/cli.py +911 -0
- codegraph/codegraph.py +618 -0
- codegraph/context/__init__.py +216 -0
- codegraph/db/__init__.py +11 -0
- codegraph/db/connection.py +163 -0
- codegraph/db/queries.py +752 -0
- codegraph/db/schema.sql +151 -0
- codegraph/directory.py +160 -0
- codegraph/errors.py +101 -0
- codegraph/extraction/__init__.py +956 -0
- codegraph/extraction/languages/__init__.py +64 -0
- codegraph/extraction/languages/base.py +132 -0
- codegraph/extraction/languages/c_cfg.py +53 -0
- codegraph/extraction/languages/cpp_cfg.py +60 -0
- codegraph/extraction/languages/dart_cfg.py +53 -0
- codegraph/extraction/languages/go_cfg.py +70 -0
- codegraph/extraction/languages/java_cfg.py +62 -0
- codegraph/extraction/languages/javascript_cfg.py +84 -0
- codegraph/extraction/languages/kotlin_cfg.py +52 -0
- codegraph/extraction/languages/lua_cfg.py +65 -0
- codegraph/extraction/languages/python_cfg.py +75 -0
- codegraph/extraction/languages/ruby_cfg.py +48 -0
- codegraph/extraction/languages/rust_cfg.py +68 -0
- codegraph/extraction/languages/scala_cfg.py +59 -0
- codegraph/extraction/languages/swift_cfg.py +64 -0
- codegraph/extraction/languages/typescript_cfg.py +89 -0
- codegraph/extraction/tree_sitter_extractor.py +689 -0
- codegraph/graph/__init__.py +685 -0
- codegraph/installer/__init__.py +6 -0
- codegraph/mcp/__init__.py +668 -0
- codegraph/project_config.py +191 -0
- codegraph/resolution/__init__.py +337 -0
- codegraph/search/__init__.py +653 -0
- codegraph/sync/__init__.py +204 -0
- codegraph/types.py +334 -0
- codegraph/ui/__init__.py +11 -0
- codegraph/utils.py +218 -0
- codegraph_py-1.0.0.dist-info/METADATA +238 -0
- codegraph_py-1.0.0.dist-info/RECORD +44 -0
- codegraph_py-1.0.0.dist-info/WHEEL +5 -0
- codegraph_py-1.0.0.dist-info/entry_points.txt +2 -0
- codegraph_py-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeGraph File Synchronization
|
|
3
|
+
|
|
4
|
+
File watcher for auto-syncing the graph on file changes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
import threading
|
|
12
|
+
from typing import Callable, List, Optional, Set, Dict, Any
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class PendingFile:
|
|
18
|
+
"""A file with pending changes."""
|
|
19
|
+
path: str
|
|
20
|
+
modified_at: float
|
|
21
|
+
change_type: str # 'added', 'modified', 'removed'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class WatchOptions:
|
|
26
|
+
"""Options for the file watcher."""
|
|
27
|
+
debounce_ms: int = 2000
|
|
28
|
+
ignore_patterns: Optional[List[str]] = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LockUnavailableError(Exception):
|
|
32
|
+
"""Raised when a lock cannot be acquired."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FileWatcher:
|
|
37
|
+
"""Watches a project directory for file changes using polling."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, project_root: str,
|
|
40
|
+
on_change: Optional[Callable[[List[PendingFile]], None]] = None,
|
|
41
|
+
options: Optional[WatchOptions] = None):
|
|
42
|
+
self._project_root = project_root
|
|
43
|
+
self._on_change = on_change
|
|
44
|
+
self._options = options or WatchOptions()
|
|
45
|
+
self._running = False
|
|
46
|
+
self._thread: Optional[threading.Thread] = None
|
|
47
|
+
self._lock = threading.Lock()
|
|
48
|
+
self._known_files: Dict[str, float] = {}
|
|
49
|
+
self._timer: Optional[threading.Timer] = None
|
|
50
|
+
self._pending: List[PendingFile] = []
|
|
51
|
+
|
|
52
|
+
def start(self) -> None:
|
|
53
|
+
"""Start watching for file changes."""
|
|
54
|
+
if self._running:
|
|
55
|
+
return
|
|
56
|
+
self._running = True
|
|
57
|
+
|
|
58
|
+
# Snapshot current state
|
|
59
|
+
self._scan_files()
|
|
60
|
+
|
|
61
|
+
self._thread = threading.Thread(target=self._watch_loop, daemon=True)
|
|
62
|
+
self._thread.start()
|
|
63
|
+
|
|
64
|
+
def stop(self) -> None:
|
|
65
|
+
"""Stop watching."""
|
|
66
|
+
self._running = False
|
|
67
|
+
if self._timer:
|
|
68
|
+
self._timer.cancel()
|
|
69
|
+
self._timer = None
|
|
70
|
+
|
|
71
|
+
def _scan_files(self) -> None:
|
|
72
|
+
"""Scan project files and record their mtimes."""
|
|
73
|
+
self._known_files.clear()
|
|
74
|
+
for root, dirs, files in os.walk(self._project_root):
|
|
75
|
+
# Skip hidden directories and .codegraph
|
|
76
|
+
dirs[:] = [d for d in dirs
|
|
77
|
+
if not d.startswith('.') and d != '.codegraph'
|
|
78
|
+
and d != 'node_modules' and d != '__pycache__']
|
|
79
|
+
|
|
80
|
+
for f in files:
|
|
81
|
+
# Skip hidden files
|
|
82
|
+
if f.startswith('.'):
|
|
83
|
+
continue
|
|
84
|
+
filepath = os.path.join(root, f)
|
|
85
|
+
try:
|
|
86
|
+
mtime = os.path.getmtime(filepath)
|
|
87
|
+
relpath = os.path.relpath(filepath, self._project_root)
|
|
88
|
+
self._known_files[relpath.replace('\\', '/')] = mtime
|
|
89
|
+
except OSError:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
def _watch_loop(self) -> None:
|
|
93
|
+
"""Main watch loop - polls for file changes."""
|
|
94
|
+
while self._running:
|
|
95
|
+
try:
|
|
96
|
+
self._check_changes()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
time.sleep(1.0) # Poll every second
|
|
100
|
+
|
|
101
|
+
def _check_changes(self) -> None:
|
|
102
|
+
"""Check for file changes and trigger debounced callback."""
|
|
103
|
+
current: Dict[str, float] = {}
|
|
104
|
+
pending: List[PendingFile] = []
|
|
105
|
+
|
|
106
|
+
for root, dirs, files in os.walk(self._project_root):
|
|
107
|
+
dirs[:] = [d for d in dirs
|
|
108
|
+
if not d.startswith('.') and d != '.codegraph'
|
|
109
|
+
and d != 'node_modules' and d != '__pycache__']
|
|
110
|
+
|
|
111
|
+
for f in files:
|
|
112
|
+
if f.startswith('.'):
|
|
113
|
+
continue
|
|
114
|
+
filepath = os.path.join(root, f)
|
|
115
|
+
try:
|
|
116
|
+
mtime = os.path.getmtime(filepath)
|
|
117
|
+
relpath = os.path.relpath(filepath, self._project_root)
|
|
118
|
+
normalized = relpath.replace('\\', '/')
|
|
119
|
+
current[normalized] = mtime
|
|
120
|
+
except OSError:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
# Check for added/modified
|
|
124
|
+
for path, mtime in current.items():
|
|
125
|
+
if path not in self._known_files:
|
|
126
|
+
pending.append(PendingFile(path, mtime, 'added'))
|
|
127
|
+
elif self._known_files[path] != mtime:
|
|
128
|
+
pending.append(PendingFile(path, mtime, 'modified'))
|
|
129
|
+
|
|
130
|
+
# Check for removed
|
|
131
|
+
for path in self._known_files:
|
|
132
|
+
if path not in current:
|
|
133
|
+
pending.append(PendingFile(path, time.time(), 'removed'))
|
|
134
|
+
|
|
135
|
+
if pending:
|
|
136
|
+
self._known_files = current
|
|
137
|
+
self._debounce_callback(pending)
|
|
138
|
+
|
|
139
|
+
def _debounce_callback(self, pending: List[PendingFile]) -> None:
|
|
140
|
+
"""Debounce and fire change callback."""
|
|
141
|
+
if self._timer:
|
|
142
|
+
self._timer.cancel()
|
|
143
|
+
|
|
144
|
+
self._pending.extend(pending)
|
|
145
|
+
|
|
146
|
+
self._timer = threading.Timer(
|
|
147
|
+
self._options.debounce_ms / 1000.0,
|
|
148
|
+
self._fire_callback
|
|
149
|
+
)
|
|
150
|
+
self._timer.daemon = True
|
|
151
|
+
self._timer.start()
|
|
152
|
+
|
|
153
|
+
def _fire_callback(self) -> None:
|
|
154
|
+
"""Fire the change callback with pending files."""
|
|
155
|
+
if self._pending and self._on_change:
|
|
156
|
+
try:
|
|
157
|
+
self._on_change(self._pending)
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
160
|
+
self._pending = []
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class LockFile:
|
|
164
|
+
"""Simple file-based lock for cross-process synchronization."""
|
|
165
|
+
|
|
166
|
+
def __init__(self, lock_path: str):
|
|
167
|
+
self.lock_path = lock_path
|
|
168
|
+
self._fd = None
|
|
169
|
+
|
|
170
|
+
def acquire(self, timeout: float = 5.0) -> bool:
|
|
171
|
+
"""Acquire the lock."""
|
|
172
|
+
deadline = time.time() + timeout
|
|
173
|
+
while time.time() < deadline:
|
|
174
|
+
try:
|
|
175
|
+
self._fd = os.open(
|
|
176
|
+
self.lock_path,
|
|
177
|
+
os.O_CREAT | os.O_EXCL | os.O_RDWR,
|
|
178
|
+
0o644
|
|
179
|
+
)
|
|
180
|
+
os.write(self._fd, str(os.getpid()).encode())
|
|
181
|
+
return True
|
|
182
|
+
except OSError:
|
|
183
|
+
time.sleep(0.1)
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
def release(self) -> None:
|
|
187
|
+
"""Release the lock."""
|
|
188
|
+
if self._fd is not None:
|
|
189
|
+
try:
|
|
190
|
+
os.close(self._fd)
|
|
191
|
+
except OSError:
|
|
192
|
+
pass
|
|
193
|
+
self._fd = None
|
|
194
|
+
try:
|
|
195
|
+
os.unlink(self.lock_path)
|
|
196
|
+
except OSError:
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
def __enter__(self):
|
|
200
|
+
self.acquire()
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
def __exit__(self, *args):
|
|
204
|
+
self.release()
|
codegraph/types.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeGraph Type Definitions
|
|
3
|
+
|
|
4
|
+
Core types for the semantic knowledge graph system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import dataclasses
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# Enums & Constants
|
|
17
|
+
# =============================================================================
|
|
18
|
+
|
|
19
|
+
NODE_KINDS = [
|
|
20
|
+
'file', 'module', 'class', 'struct', 'interface', 'trait', 'protocol',
|
|
21
|
+
'function', 'method', 'property', 'field', 'variable', 'constant',
|
|
22
|
+
'enum', 'enum_member', 'type_alias', 'namespace', 'parameter',
|
|
23
|
+
'import', 'export', 'route', 'component',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
EDGE_KINDS = [
|
|
27
|
+
'contains', 'calls', 'imports', 'exports', 'extends', 'implements',
|
|
28
|
+
'references', 'type_of', 'returns', 'instantiates', 'overrides', 'decorates',
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
LANGUAGES = [
|
|
32
|
+
'typescript', 'javascript', 'tsx', 'jsx', 'arkts',
|
|
33
|
+
'python', 'go', 'rust', 'java', 'c', 'cpp', 'csharp', 'razor',
|
|
34
|
+
'php', 'ruby', 'swift', 'kotlin', 'dart', 'svelte', 'vue', 'astro',
|
|
35
|
+
'liquid', 'pascal', 'scala', 'lua', 'luau', 'objc', 'r', 'solidity',
|
|
36
|
+
'nix', 'yaml', 'twig', 'xml', 'properties', 'cfml', 'cfscript',
|
|
37
|
+
'cfquery', 'cobol', 'vbnet', 'erlang', 'terraform', 'unknown',
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class NodeKind(str, Enum):
|
|
42
|
+
"""Types of nodes in the knowledge graph."""
|
|
43
|
+
FILE = 'file'
|
|
44
|
+
MODULE = 'module'
|
|
45
|
+
CLASS = 'class'
|
|
46
|
+
STRUCT = 'struct'
|
|
47
|
+
INTERFACE = 'interface'
|
|
48
|
+
TRAIT = 'trait'
|
|
49
|
+
PROTOCOL = 'protocol'
|
|
50
|
+
FUNCTION = 'function'
|
|
51
|
+
METHOD = 'method'
|
|
52
|
+
PROPERTY = 'property'
|
|
53
|
+
FIELD = 'field'
|
|
54
|
+
VARIABLE = 'variable'
|
|
55
|
+
CONSTANT = 'constant'
|
|
56
|
+
ENUM = 'enum'
|
|
57
|
+
ENUM_MEMBER = 'enum_member'
|
|
58
|
+
TYPE_ALIAS = 'type_alias'
|
|
59
|
+
NAMESPACE = 'namespace'
|
|
60
|
+
PARAMETER = 'parameter'
|
|
61
|
+
IMPORT = 'import'
|
|
62
|
+
EXPORT = 'export'
|
|
63
|
+
ROUTE = 'route'
|
|
64
|
+
COMPONENT = 'component'
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class EdgeKind(str, Enum):
|
|
68
|
+
"""Types of edges (relationships) between nodes."""
|
|
69
|
+
CONTAINS = 'contains'
|
|
70
|
+
CALLS = 'calls'
|
|
71
|
+
IMPORTS = 'imports'
|
|
72
|
+
EXPORTS = 'exports'
|
|
73
|
+
EXTENDS = 'extends'
|
|
74
|
+
IMPLEMENTS = 'implements'
|
|
75
|
+
REFERENCES = 'references'
|
|
76
|
+
TYPE_OF = 'type_of'
|
|
77
|
+
RETURNS = 'returns'
|
|
78
|
+
INSTANTIATES = 'instantiates'
|
|
79
|
+
OVERRIDES = 'overrides'
|
|
80
|
+
DECORATES = 'decorates'
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Language(str, Enum):
|
|
84
|
+
"""Supported programming languages."""
|
|
85
|
+
TYPESCRIPT = 'typescript'
|
|
86
|
+
JAVASCRIPT = 'javascript'
|
|
87
|
+
TSX = 'tsx'
|
|
88
|
+
JSX = 'jsx'
|
|
89
|
+
ARKTS = 'arkts'
|
|
90
|
+
PYTHON = 'python'
|
|
91
|
+
GO = 'go'
|
|
92
|
+
RUST = 'rust'
|
|
93
|
+
JAVA = 'java'
|
|
94
|
+
C = 'c'
|
|
95
|
+
CPP = 'cpp'
|
|
96
|
+
CSHARP = 'csharp'
|
|
97
|
+
RAZOR = 'razor'
|
|
98
|
+
PHP = 'php'
|
|
99
|
+
RUBY = 'ruby'
|
|
100
|
+
SWIFT = 'swift'
|
|
101
|
+
KOTLIN = 'kotlin'
|
|
102
|
+
DART = 'dart'
|
|
103
|
+
SVELTE = 'svelte'
|
|
104
|
+
VUE = 'vue'
|
|
105
|
+
ASTRO = 'astro'
|
|
106
|
+
LIQUID = 'liquid'
|
|
107
|
+
PASCAL = 'pascal'
|
|
108
|
+
SCALA = 'scala'
|
|
109
|
+
LUA = 'lua'
|
|
110
|
+
LUAU = 'luau'
|
|
111
|
+
OBJC = 'objc'
|
|
112
|
+
R = 'r'
|
|
113
|
+
SOLIDITY = 'solidity'
|
|
114
|
+
NIX = 'nix'
|
|
115
|
+
TERRAFORM = 'terraform'
|
|
116
|
+
UNKNOWN = 'unknown'
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# =============================================================================
|
|
120
|
+
# Core Graph Types
|
|
121
|
+
# =============================================================================
|
|
122
|
+
|
|
123
|
+
@dataclass
|
|
124
|
+
class Node:
|
|
125
|
+
"""A node in the knowledge graph representing a code symbol."""
|
|
126
|
+
id: str
|
|
127
|
+
kind: str
|
|
128
|
+
name: str
|
|
129
|
+
qualified_name: str
|
|
130
|
+
file_path: str
|
|
131
|
+
language: str
|
|
132
|
+
start_line: int
|
|
133
|
+
end_line: int
|
|
134
|
+
start_column: int
|
|
135
|
+
end_column: int
|
|
136
|
+
docstring: Optional[str] = None
|
|
137
|
+
signature: Optional[str] = None
|
|
138
|
+
visibility: Optional[str] = None
|
|
139
|
+
is_exported: bool = False
|
|
140
|
+
is_async: bool = False
|
|
141
|
+
is_static: bool = False
|
|
142
|
+
is_abstract: bool = False
|
|
143
|
+
decorators: Optional[List[str]] = None
|
|
144
|
+
type_parameters: Optional[List[str]] = None
|
|
145
|
+
return_type: Optional[str] = None
|
|
146
|
+
updated_at: int = 0
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass
|
|
150
|
+
class Edge:
|
|
151
|
+
"""An edge representing a relationship between two nodes."""
|
|
152
|
+
id: Optional[int] = None
|
|
153
|
+
source: str = ''
|
|
154
|
+
target: str = ''
|
|
155
|
+
kind: str = ''
|
|
156
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
157
|
+
line: Optional[int] = None
|
|
158
|
+
column: Optional[int] = None
|
|
159
|
+
provenance: Optional[str] = None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class FileRecord:
|
|
164
|
+
"""Metadata about a tracked file."""
|
|
165
|
+
path: str
|
|
166
|
+
content_hash: str
|
|
167
|
+
language: str
|
|
168
|
+
size: int
|
|
169
|
+
modified_at: int
|
|
170
|
+
indexed_at: int
|
|
171
|
+
node_count: int = 0
|
|
172
|
+
errors: Optional[List[ExtractionError]] = None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass
|
|
176
|
+
class ExtractionError:
|
|
177
|
+
"""Error during code extraction."""
|
|
178
|
+
message: str
|
|
179
|
+
file_path: Optional[str] = None
|
|
180
|
+
line: Optional[int] = None
|
|
181
|
+
column: Optional[int] = None
|
|
182
|
+
severity: str = 'error' # 'error' | 'warning'
|
|
183
|
+
code: Optional[str] = None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@dataclass
|
|
187
|
+
class UnresolvedReference:
|
|
188
|
+
"""A reference that couldn't be resolved during extraction."""
|
|
189
|
+
id: Optional[int] = None
|
|
190
|
+
from_node_id: str = ''
|
|
191
|
+
reference_name: str = ''
|
|
192
|
+
reference_kind: str = ''
|
|
193
|
+
line: int = 0
|
|
194
|
+
column: int = 0
|
|
195
|
+
file_path: Optional[str] = None
|
|
196
|
+
language: Optional[str] = None
|
|
197
|
+
candidates: Optional[List[str]] = None
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass
|
|
201
|
+
class ExtractionResult:
|
|
202
|
+
"""Result from parsing a source file."""
|
|
203
|
+
nodes: List[Node] = field(default_factory=list)
|
|
204
|
+
edges: List[Edge] = field(default_factory=list)
|
|
205
|
+
unresolved_references: List[UnresolvedReference] = field(default_factory=list)
|
|
206
|
+
errors: List[ExtractionError] = field(default_factory=list)
|
|
207
|
+
duration_ms: float = 0.0
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# =============================================================================
|
|
211
|
+
# Query Types
|
|
212
|
+
# =============================================================================
|
|
213
|
+
|
|
214
|
+
@dataclass
|
|
215
|
+
class Subgraph:
|
|
216
|
+
"""A subgraph containing a subset of the knowledge graph."""
|
|
217
|
+
nodes: Dict[str, Node] = field(default_factory=dict)
|
|
218
|
+
edges: List[Edge] = field(default_factory=list)
|
|
219
|
+
roots: List[str] = field(default_factory=list)
|
|
220
|
+
confidence: Optional[str] = None # 'high' | 'low'
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass
|
|
224
|
+
class TraversalOptions:
|
|
225
|
+
"""Options for graph traversal."""
|
|
226
|
+
max_depth: Optional[int] = None
|
|
227
|
+
edge_kinds: Optional[List[str]] = None
|
|
228
|
+
node_kinds: Optional[List[str]] = None
|
|
229
|
+
direction: str = 'outgoing' # 'outgoing' | 'incoming' | 'both'
|
|
230
|
+
limit: Optional[int] = None
|
|
231
|
+
include_start: bool = True
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@dataclass
|
|
235
|
+
class SearchOptions:
|
|
236
|
+
"""Options for searching the graph."""
|
|
237
|
+
kinds: Optional[List[str]] = None
|
|
238
|
+
languages: Optional[List[str]] = None
|
|
239
|
+
include_patterns: Optional[List[str]] = None
|
|
240
|
+
exclude_patterns: Optional[List[str]] = None
|
|
241
|
+
limit: int = 20
|
|
242
|
+
offset: int = 0
|
|
243
|
+
case_sensitive: bool = False
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass
|
|
247
|
+
class SearchResult:
|
|
248
|
+
"""A search result with relevance scoring."""
|
|
249
|
+
node: Node
|
|
250
|
+
score: float
|
|
251
|
+
highlights: Optional[List[str]] = None
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@dataclass
|
|
255
|
+
class SegmentMatch:
|
|
256
|
+
"""A symbol whose name-segments match prose words from a prompt."""
|
|
257
|
+
name: str
|
|
258
|
+
kind: str
|
|
259
|
+
file_path: str
|
|
260
|
+
start_line: int
|
|
261
|
+
matched_words: List[str] = field(default_factory=list)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# =============================================================================
|
|
265
|
+
# Context Types
|
|
266
|
+
# =============================================================================
|
|
267
|
+
|
|
268
|
+
@dataclass
|
|
269
|
+
class Context:
|
|
270
|
+
"""Context information for code understanding."""
|
|
271
|
+
focal: Optional[Node] = None
|
|
272
|
+
ancestors: List[Node] = field(default_factory=list)
|
|
273
|
+
children: List[Node] = field(default_factory=list)
|
|
274
|
+
incoming_refs: List[Dict[str, Any]] = field(default_factory=list)
|
|
275
|
+
outgoing_refs: List[Dict[str, Any]] = field(default_factory=list)
|
|
276
|
+
types: List[Node] = field(default_factory=list)
|
|
277
|
+
imports: List[Node] = field(default_factory=list)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@dataclass
|
|
281
|
+
class CodeBlock:
|
|
282
|
+
"""A block of code with context."""
|
|
283
|
+
content: str
|
|
284
|
+
file_path: str
|
|
285
|
+
start_line: int
|
|
286
|
+
end_line: int
|
|
287
|
+
language: str
|
|
288
|
+
node: Optional[Node] = None
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# =============================================================================
|
|
292
|
+
# Statistics
|
|
293
|
+
# =============================================================================
|
|
294
|
+
|
|
295
|
+
@dataclass
|
|
296
|
+
class GraphStats:
|
|
297
|
+
"""Statistics about the knowledge graph."""
|
|
298
|
+
node_count: int = 0
|
|
299
|
+
edge_count: int = 0
|
|
300
|
+
file_count: int = 0
|
|
301
|
+
nodes_by_kind: Dict[str, int] = field(default_factory=dict)
|
|
302
|
+
edges_by_kind: Dict[str, int] = field(default_factory=dict)
|
|
303
|
+
files_by_language: Dict[str, int] = field(default_factory=dict)
|
|
304
|
+
db_size_bytes: int = 0
|
|
305
|
+
last_updated: int = 0
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# =============================================================================
|
|
309
|
+
# Task Context Types
|
|
310
|
+
# =============================================================================
|
|
311
|
+
|
|
312
|
+
@dataclass
|
|
313
|
+
class BuildContextOptions:
|
|
314
|
+
"""Options for building task context."""
|
|
315
|
+
max_nodes: int = 50
|
|
316
|
+
max_code_blocks: int = 10
|
|
317
|
+
max_code_block_size: int = 2000
|
|
318
|
+
include_code: bool = True
|
|
319
|
+
format: str = 'markdown' # 'markdown' | 'json'
|
|
320
|
+
search_limit: int = 5
|
|
321
|
+
traversal_depth: int = 2
|
|
322
|
+
min_score: float = 0.3
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@dataclass
|
|
326
|
+
class TaskContext:
|
|
327
|
+
"""Full context for a task, ready for AI agent consumption."""
|
|
328
|
+
query: str
|
|
329
|
+
subgraph: Optional[Subgraph] = None
|
|
330
|
+
entry_points: List[Node] = field(default_factory=list)
|
|
331
|
+
code_blocks: List[CodeBlock] = field(default_factory=list)
|
|
332
|
+
related_files: List[str] = field(default_factory=list)
|
|
333
|
+
summary: str = ''
|
|
334
|
+
stats: Optional[Dict[str, int]] = None
|