codeui-python 0.1.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.
Files changed (52) hide show
  1. codeui/__init__.py +23 -0
  2. codeui/agent/__init__.py +15 -0
  3. codeui/agent/context.py +207 -0
  4. codeui/agent/contribution.py +224 -0
  5. codeui/agent/edit_tools.py +280 -0
  6. codeui/agent/integrations.py +243 -0
  7. codeui/agent/mcp_server.py +94 -0
  8. codeui/analysis/__init__.py +1 -0
  9. codeui/analysis/api_drift.py +79 -0
  10. codeui/analysis/base.py +42 -0
  11. codeui/analysis/circular.py +116 -0
  12. codeui/analysis/dead_code.py +63 -0
  13. codeui/analysis/duplicates.py +69 -0
  14. codeui/analysis/runner.py +57 -0
  15. codeui/analysis/shadowing.py +49 -0
  16. codeui/analysis/undefined.py +97 -0
  17. codeui/analysis/unreachable.py +28 -0
  18. codeui/analysis/unresolved_import.py +153 -0
  19. codeui/cli/__init__.py +1 -0
  20. codeui/cli/main.py +437 -0
  21. codeui/core/__init__.py +1 -0
  22. codeui/core/cache.py +135 -0
  23. codeui/core/graph.py +435 -0
  24. codeui/core/ir.py +218 -0
  25. codeui/core/override.py +230 -0
  26. codeui/core/repo.py +85 -0
  27. codeui/core/resolver.py +281 -0
  28. codeui/errors.py +98 -0
  29. codeui/lang/__init__.py +1 -0
  30. codeui/lang/base.py +117 -0
  31. codeui/lang/generic.py +90 -0
  32. codeui/lang/go.py +167 -0
  33. codeui/lang/python.py +294 -0
  34. codeui/lang/registry.py +83 -0
  35. codeui/lang/rust.py +185 -0
  36. codeui/lang/ts.py +396 -0
  37. codeui/plugins/__init__.py +1 -0
  38. codeui/plugins/registry.py +31 -0
  39. codeui/report/__init__.py +1 -0
  40. codeui/report/json_emitter.py +24 -0
  41. codeui/report/markdown_emitter.py +33 -0
  42. codeui/report/sarif_emitter.py +56 -0
  43. codeui/server/__init__.py +1 -0
  44. codeui/server/server.py +705 -0
  45. codeui/tracer/__init__.py +1 -0
  46. codeui/tracer/tracer.py +147 -0
  47. codeui_python-0.1.0.dist-info/METADATA +191 -0
  48. codeui_python-0.1.0.dist-info/RECORD +52 -0
  49. codeui_python-0.1.0.dist-info/WHEEL +5 -0
  50. codeui_python-0.1.0.dist-info/entry_points.txt +2 -0
  51. codeui_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  52. codeui_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,280 @@
1
+ """Edit tools allowing agents to propose and apply non-destructive overrides."""
2
+ import json
3
+ from dataclasses import dataclass, asdict
4
+ from datetime import datetime, timezone
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Union
7
+ from codeui.core.graph import Graph
8
+ from codeui.core.ir import SymbolKind
9
+ from codeui.core.override import Conflict, Override, OverrideKind, OverrideStore
10
+ from codeui.errors import SymbolNotFoundError, FileNotFoundInGraphError
11
+
12
+ @dataclass
13
+ class EditProposal:
14
+ """Dry-run proposal report for edit tools.
15
+ Example:
16
+ >>> p = EditProposal("s1", "rename_symbol", ["s1"], ["a.py"], "diff preview", [])
17
+ >>> p.target_id
18
+ 's1'
19
+ """
20
+ target_id: str
21
+ kind: str
22
+ affected_symbols: List[str]
23
+ affected_files: List[str]
24
+ diff_preview: str
25
+ conflicts: List[Conflict]
26
+ new_value: Any = None
27
+
28
+ def to_dict(self) -> Dict[str, Any]:
29
+ """Convert EditProposal to dictionary.
30
+ Example:
31
+ >>> p = EditProposal("s1", "rename", [], [], "", [])
32
+ >>> p.to_dict()["target_id"]
33
+ 's1'
34
+ """
35
+ return asdict(self)
36
+
37
+ class EditTools:
38
+ """Provides agent interface to dry-run, apply code overrides, and edit project files directly on disk.
39
+ Example:
40
+ >>> g = Graph()
41
+ >>> store = OverrideStore()
42
+ >>> tools = EditTools(g, store)
43
+ >>> tools.diff()
44
+ ''
45
+ """
46
+ def __init__(
47
+ self,
48
+ graph: Graph,
49
+ override_store: Optional[OverrideStore] = None,
50
+ project_root: Optional[Union[str, Path]] = None,
51
+ direct_disk: bool = True,
52
+ ) -> None:
53
+ self.graph = graph
54
+ self.override_store = override_store if override_store is not None else OverrideStore()
55
+ self.project_root = Path(project_root).resolve() if project_root else None
56
+ self.direct_disk = direct_disk
57
+
58
+ def rename_symbol(self, symbol_id: str, new_name: str) -> EditProposal:
59
+ """Propose renaming a symbol and updating references.
60
+ Example:
61
+ >>> from codeui.core.ir import Symbol, Location, Visibility
62
+ >>> g = Graph()
63
+ >>> g.add_symbol(Symbol("s1", "f", "a.f", SymbolKind.FUNCTION, "python", Location("a.py", 1, 0, 2, 0), None, None, Visibility.PUBLIC, (), "h"))
64
+ >>> store = OverrideStore()
65
+ >>> tools = EditTools(g, store)
66
+ >>> prop = tools.rename_symbol("s1", "new_name")
67
+ >>> prop.target_id
68
+ 's1'
69
+ """
70
+ if not self.graph.has_symbol(symbol_id):
71
+ raise SymbolNotFoundError(symbol_id)
72
+ sym = self.graph.get_symbol(symbol_id)
73
+ affected_files = [sym.location.file_id]
74
+ incoming = self.graph.get_incoming_edges(symbol_id)
75
+ affected_symbols = [symbol_id] + [e.source_id for e in incoming]
76
+ diff_preview = f"--- a/{sym.location.file_id}\n+++ b/{sym.location.file_id}\n- {sym.name}\n+ {new_name}\n"
77
+ return EditProposal(
78
+ target_id=symbol_id,
79
+ kind="rename_symbol",
80
+ affected_symbols=affected_symbols,
81
+ affected_files=affected_files,
82
+ diff_preview=diff_preview,
83
+ conflicts=[],
84
+ new_value=new_name,
85
+ )
86
+
87
+ def retype_symbol(self, symbol_id: str, new_kind: SymbolKind) -> EditProposal:
88
+ """Propose retyping a symbol's SymbolKind.
89
+ Example:
90
+ >>> from codeui.core.ir import Symbol, Location, Visibility
91
+ >>> g = Graph()
92
+ >>> g.add_symbol(Symbol("s1", "f", "a.f", SymbolKind.FUNCTION, "python", Location("a.py", 1, 0, 2, 0), None, None, Visibility.PUBLIC, (), "h"))
93
+ >>> store = OverrideStore()
94
+ >>> tools = EditTools(g, store)
95
+ >>> prop = tools.retype_symbol("s1", SymbolKind.METHOD)
96
+ >>> prop.kind
97
+ 'retype_symbol'
98
+ """
99
+ if not self.graph.has_symbol(symbol_id):
100
+ raise SymbolNotFoundError(symbol_id)
101
+ sym = self.graph.get_symbol(symbol_id)
102
+ return EditProposal(
103
+ target_id=symbol_id,
104
+ kind="retype_symbol",
105
+ affected_symbols=[symbol_id],
106
+ affected_files=[sym.location.file_id],
107
+ diff_preview=f"Change kind of {sym.name} from {sym.kind} to {new_kind}",
108
+ conflicts=[],
109
+ new_value=new_kind,
110
+ )
111
+
112
+ def replace_symbol_body(self, symbol_id: str, new_body: str) -> EditProposal:
113
+ """Propose replacing the implementation body of a single symbol.
114
+ Example:
115
+ >>> from codeui.core.ir import Symbol, Location, Visibility
116
+ >>> g = Graph()
117
+ >>> g.add_symbol(Symbol("s1", "f", "a.f", SymbolKind.FUNCTION, "python", Location("a.py", 1, 0, 2, 0), None, None, Visibility.PUBLIC, (), "h"))
118
+ >>> store = OverrideStore()
119
+ >>> tools = EditTools(g, store)
120
+ >>> prop = tools.replace_symbol_body("s1", "def foo(): pass")
121
+ >>> prop.target_id
122
+ 's1'
123
+ """
124
+ if not self.graph.has_symbol(symbol_id):
125
+ raise SymbolNotFoundError(symbol_id)
126
+ sym = self.graph.get_symbol(symbol_id)
127
+ return EditProposal(
128
+ target_id=symbol_id,
129
+ kind="replace_symbol_body",
130
+ affected_symbols=[symbol_id],
131
+ affected_files=[sym.location.file_id],
132
+ diff_preview=f"Replace body of {sym.name} with:\n{new_body}",
133
+ conflicts=[],
134
+ new_value=new_body,
135
+ )
136
+
137
+ def replace_file(self, file_id: str, new_content: str, original_content: str = "") -> EditProposal:
138
+ """Propose replacing entire file content without mutating disk.
139
+ Example:
140
+ >>> g = Graph()
141
+ >>> store = OverrideStore()
142
+ >>> tools = EditTools(g, store)
143
+ >>> prop = tools.replace_file("app.py", "x = 1", "x = 0")
144
+ >>> prop.affected_files
145
+ ['app.py']
146
+ """
147
+ diff = f"--- a/{file_id}\n+++ b/{file_id}\n{new_content}"
148
+ return EditProposal(
149
+ target_id=file_id,
150
+ kind="replace_file",
151
+ affected_symbols=[],
152
+ affected_files=[file_id],
153
+ diff_preview=diff,
154
+ conflicts=[],
155
+ new_value=new_content,
156
+ )
157
+
158
+ def write_file_to_disk(self, file_path: str, new_content: str) -> Path:
159
+ """Write content directly to project file on disk.
160
+ Example:
161
+ >>> import tempfile
162
+ >>> with tempfile.TemporaryDirectory() as td:
163
+ ... tools = EditTools(Graph(), project_root=td)
164
+ ... p = tools.write_file_to_disk("sub/app.py", "x = 1")
165
+ ... p.exists()
166
+ True
167
+ """
168
+ root = (self.project_root if self.project_root is not None else Path(".")).resolve()
169
+ clean_rel = file_path.split("::")[0].replace("module::", "").replace("\\", "/").lstrip("/")
170
+
171
+ target_path: Optional[Path] = None
172
+ cand_direct = (root / clean_rel).resolve()
173
+ if cand_direct.exists() and cand_direct.is_file():
174
+ target_path = cand_direct
175
+ elif root.name and clean_rel.startswith(root.name + "/"):
176
+ sub_cand = (root / clean_rel[len(root.name):].lstrip("/")).resolve()
177
+ if sub_cand.exists() and sub_cand.is_file():
178
+ target_path = sub_cand
179
+
180
+ if target_path is None and self.graph:
181
+ for known_file in self.graph.get_all_files():
182
+ k_clean = known_file.replace("\\", "/").lstrip("./")
183
+ if k_clean == clean_rel or k_clean.endswith("/" + clean_rel) or clean_rel.endswith("/" + k_clean):
184
+ cand_k = (root / k_clean).resolve()
185
+ if cand_k.exists() and cand_k.is_file():
186
+ target_path = cand_k
187
+ break
188
+
189
+ if target_path is None:
190
+ target_path = cand_direct
191
+
192
+ target_path.parent.mkdir(parents=True, exist_ok=True)
193
+ target_path.write_text(new_content, encoding="utf-8")
194
+ if self.override_store:
195
+ self.override_store.revert(clean_rel)
196
+ try:
197
+ rel = str(target_path.relative_to(root)).replace("\\", "/")
198
+ self.override_store.revert(rel)
199
+ except Exception:
200
+ pass
201
+ return target_path
202
+
203
+ def apply_to_disk(self, proposal: EditProposal, author: str = "agent") -> Path:
204
+ """Apply an EditProposal directly to project files on disk.
205
+ Example:
206
+ >>> import tempfile
207
+ >>> with tempfile.TemporaryDirectory() as td:
208
+ ... tools = EditTools(Graph(), project_root=td)
209
+ ... prop = tools.replace_file("test.py", "val = 10")
210
+ ... path = tools.apply_to_disk(prop)
211
+ ... path.read_text()
212
+ 'val = 10'
213
+ """
214
+ content = proposal.new_value if proposal.new_value is not None else (
215
+ proposal.diff_preview.split("\n", 2)[-1] if "\n" in proposal.diff_preview else proposal.diff_preview
216
+ )
217
+ return self.write_file_to_disk(proposal.target_id, content)
218
+
219
+ def apply(self, proposal: EditProposal, author: str = "agent", write_to_disk: Optional[bool] = None) -> None:
220
+ """Apply an EditProposal into OverrideStore or directly to project file on disk.
221
+ Example:
222
+ >>> g = Graph()
223
+ >>> store = OverrideStore()
224
+ >>> tools = EditTools(g, store, direct_disk=False)
225
+ >>> prop = tools.replace_file("app.py", "x = 1")
226
+ >>> tools.apply(prop)
227
+ >>> store.get("app.py") is not None
228
+ True
229
+ """
230
+ should_write_disk = write_to_disk if write_to_disk is not None else (self.direct_disk and self.project_root is not None)
231
+ if should_write_disk and proposal.kind == "replace_file":
232
+ content = proposal.new_value if proposal.new_value is not None else (
233
+ proposal.diff_preview.split("\n", 2)[-1] if "\n" in proposal.diff_preview else proposal.diff_preview
234
+ )
235
+ self.write_file_to_disk(proposal.target_id, content)
236
+ return
237
+
238
+ now = datetime.now(timezone.utc).isoformat()
239
+ if proposal.kind == "replace_file":
240
+ content = proposal.new_value if proposal.new_value is not None else (
241
+ proposal.diff_preview.split("\n", 2)[-1] if "\n" in proposal.diff_preview else proposal.diff_preview
242
+ )
243
+ self.override_store.set_file_override(
244
+ file_path=proposal.target_id,
245
+ original_content="",
246
+ new_content=content,
247
+ author=author,
248
+ )
249
+ else:
250
+ val = proposal.new_value if proposal.new_value is not None else proposal.diff_preview
251
+ override = Override(
252
+ target_id=proposal.target_id,
253
+ kind=OverrideKind(proposal.kind),
254
+ original_hash="",
255
+ new_value=val,
256
+ author=author,
257
+ created_at=now,
258
+ )
259
+ self.override_store.set(override)
260
+
261
+ def revert(self, target_id: str) -> None:
262
+ """Revert override for target ID.
263
+ Example:
264
+ >>> g = Graph()
265
+ >>> store = OverrideStore()
266
+ >>> tools = EditTools(g, store)
267
+ >>> tools.revert("app.py")
268
+ """
269
+ self.override_store.revert(target_id)
270
+
271
+ def diff(self) -> str:
272
+ """Generate unified diff across all applied overrides.
273
+ Example:
274
+ >>> g = Graph()
275
+ >>> store = OverrideStore()
276
+ >>> tools = EditTools(g, store)
277
+ >>> tools.diff()
278
+ ''
279
+ """
280
+ return self.override_store.to_diff()
@@ -0,0 +1,243 @@
1
+ """LangChain, LlamaIndex, and MCP tool integration helpers."""
2
+ from pathlib import Path
3
+ from typing import Any, Dict, List, Optional, Union
4
+ from codeui.agent.context import ContextBuilder
5
+ from codeui.agent.contribution import ContributionManager
6
+ from codeui.agent.edit_tools import EditProposal, EditTools
7
+ from codeui.core.graph import Graph
8
+ from codeui.core.override import OverrideStore
9
+ from codeui.core.repo import get_library_repo_url
10
+ from codeui.tracer.tracer import Tracer
11
+
12
+ class CodeUITools:
13
+ """Unified integration tools exposing codeui capabilities to AI agents.
14
+ Example:
15
+ >>> g = Graph()
16
+ >>> tools = CodeUITools(g)
17
+ >>> "matches" in tools.find_symbol("main")
18
+ True
19
+ """
20
+ def __init__(
21
+ self,
22
+ graph: Graph,
23
+ override_store: Optional[OverrideStore] = None,
24
+ project_root: Optional[Union[str, Path]] = None,
25
+ direct_disk: bool = True,
26
+ ) -> None:
27
+ self.graph = graph
28
+ self.override_store = override_store or OverrideStore()
29
+ self.project_root = Path(project_root).resolve() if project_root else None
30
+ self.direct_disk = direct_disk
31
+ self.tracer = Tracer(graph)
32
+ self.context_builder = ContextBuilder(graph)
33
+ self.edit_tools = EditTools(
34
+ graph=graph,
35
+ override_store=self.override_store,
36
+ project_root=self.project_root,
37
+ direct_disk=self.direct_disk,
38
+ )
39
+
40
+ def find_symbol(self, query: str) -> Dict[str, Any]:
41
+ """Search for symbols by name or qualified name.
42
+ Example:
43
+ >>> g = Graph()
44
+ >>> tools = CodeUITools(g)
45
+ >>> tools.find_symbol("f")["matches"]
46
+ []
47
+ """
48
+ matches = [
49
+ s.to_dict() for s in self.graph.get_all_symbols()
50
+ if query.lower() in s.name.lower() or query.lower() in s.qualified_name.lower()
51
+ ]
52
+ return {"query": query, "matches": matches, "count": len(matches)}
53
+
54
+ def get_context(self, symbol_id: str, depth: int = 1) -> Dict[str, Any]:
55
+ """Retrieve graph-based context bundle for symbol.
56
+ Example:
57
+ >>> g = Graph()
58
+ >>> tools = CodeUITools(g)
59
+ >>> "manifest" in tools.get_context("sym_1")
60
+ True
61
+ """
62
+ bundle = self.context_builder.for_symbol(symbol_id, depth=depth)
63
+ return bundle.to_dict()
64
+
65
+ def find_defects(self, severity: str | None = None) -> Dict[str, Any]:
66
+ """Fetch all defects filtered by optional severity.
67
+ Example:
68
+ >>> g = Graph()
69
+ >>> tools = CodeUITools(g)
70
+ >>> tools.find_defects()["count"]
71
+ 0
72
+ """
73
+ findings = self.graph.get_findings()
74
+ if severity:
75
+ findings = [f for f in findings if str(f.severity) == severity]
76
+ return {"findings": [f.to_dict() for f in findings], "count": len(findings)}
77
+
78
+ def trace_call_chain(self, symbol_id: str, direction: str = "forward", depth: int = 3) -> Dict[str, Any]:
79
+ """Trace symbol call chain forward or backward.
80
+ Example:
81
+ >>> g = Graph()
82
+ >>> tools = CodeUITools(g)
83
+ >>> tools.trace_call_chain("sym_1")["chain"]
84
+ []
85
+ """
86
+ if direction == "backward":
87
+ chain = [s.to_dict() for s in self.tracer.backward(symbol_id, depth=depth)]
88
+ else:
89
+ chain = [s.to_dict() for s in self.tracer.forward(symbol_id, depth=depth)]
90
+ return {"symbol_id": symbol_id, "direction": direction, "chain": chain}
91
+
92
+ def propose_rename(self, symbol_id: str, new_name: str) -> Dict[str, Any]:
93
+ """Propose renaming a symbol and preview diff.
94
+ Example:
95
+ >>> from codeui.core.ir import Location, Symbol, SymbolKind, Visibility
96
+ >>> g = Graph()
97
+ >>> loc = Location("a.py", 1, 0, 1, 0)
98
+ >>> g.add_symbol(Symbol("s1", "f", "a.f", SymbolKind.FUNCTION, "python", loc, None, None, Visibility.PUBLIC, (), "h"))
99
+ >>> tools = CodeUITools(g)
100
+ >>> tools.propose_rename("s1", "new_f")["target_id"]
101
+ 's1'
102
+ """
103
+ proposal = self.edit_tools.rename_symbol(symbol_id, new_name)
104
+ return proposal.to_dict()
105
+
106
+ def propose_replace_file(self, file_id: str, new_content: str, original_content: str = "") -> Dict[str, Any]:
107
+ """Propose replacing file content.
108
+ Example:
109
+ >>> g = Graph()
110
+ >>> tools = CodeUITools(g)
111
+ >>> tools.propose_replace_file("a.py", "x = 1")["target_id"]
112
+ 'a.py'
113
+ """
114
+ proposal = self.edit_tools.replace_file(file_id, new_content, original_content)
115
+ return proposal.to_dict()
116
+
117
+ def write_file_direct(self, file_id: str, new_content: str) -> Dict[str, Any]:
118
+ """Directly write content to a project file on disk.
119
+ Example:
120
+ >>> import tempfile
121
+ >>> with tempfile.TemporaryDirectory() as td:
122
+ ... tools = CodeUITools(Graph(), project_root=td)
123
+ ... res = tools.write_file_direct("main.py", "print('hello')")
124
+ ... res["status"]
125
+ 'saved_to_disk'
126
+ """
127
+ path = self.edit_tools.write_file_to_disk(file_id, new_content)
128
+ return {"status": "saved_to_disk", "file_id": file_id, "path": str(path)}
129
+
130
+ def apply_proposal(
131
+ self,
132
+ proposal: Dict[str, Any],
133
+ author: str = "agent",
134
+ direct_disk: Optional[bool] = None,
135
+ ) -> Dict[str, Any]:
136
+ """Apply an edit proposal into the active override store or directly to project file on disk.
137
+ Example:
138
+ >>> g = Graph()
139
+ >>> tools = CodeUITools(g, direct_disk=False)
140
+ >>> prop = tools.propose_replace_file("a.py", "x = 1")
141
+ >>> tools.apply_proposal(prop)["status"]
142
+ 'applied'
143
+ """
144
+ prop_obj = EditProposal(
145
+ target_id=proposal["target_id"],
146
+ kind=proposal["kind"],
147
+ affected_symbols=proposal.get("affected_symbols", []),
148
+ affected_files=proposal.get("affected_files", []),
149
+ diff_preview=proposal.get("diff_preview", ""),
150
+ conflicts=[],
151
+ new_value=proposal.get("new_value"),
152
+ )
153
+ self.edit_tools.apply(prop_obj, author=author, write_to_disk=direct_disk)
154
+ status = "written_to_disk" if (direct_disk or (direct_disk is None and self.direct_disk and self.project_root is not None)) else "applied"
155
+ return {"status": status, "target_id": proposal["target_id"]}
156
+
157
+ def revert_override(self, target_id: str) -> Dict[str, Any]:
158
+ """Revert active override for target symbol or file.
159
+ Example:
160
+ >>> g = Graph()
161
+ >>> tools = CodeUITools(g)
162
+ >>> tools.revert_override("a.py")["status"]
163
+ 'reverted'
164
+ """
165
+ self.edit_tools.revert(target_id)
166
+ return {"status": "reverted", "target_id": target_id}
167
+
168
+ def diff(self) -> str:
169
+ """Get unified diff across all active overrides.
170
+ Example:
171
+ >>> g = Graph()
172
+ >>> tools = CodeUITools(g)
173
+ >>> tools.get_diff()
174
+ ''
175
+ """
176
+ return self.edit_tools.diff()
177
+
178
+ def get_diff(self) -> str:
179
+ """Get unified diff across all active overrides.
180
+ Example:
181
+ >>> g = Graph()
182
+ >>> tools = CodeUITools(g)
183
+ >>> tools.get_diff()
184
+ ''
185
+ """
186
+ return self.edit_tools.diff()
187
+
188
+ def get_repository_url(self) -> str:
189
+ """Retrieve the official codeui GitHub repository URL.
190
+ Example:
191
+ >>> g = Graph()
192
+ >>> tools = CodeUITools(g)
193
+ >>> tools.get_repository_url()
194
+ 'https://github.com/AAlsubari/codeUI'
195
+ """
196
+ return get_library_repo_url()
197
+
198
+ def clone_library_repository(
199
+ self,
200
+ target_dir: Optional[Union[str, Path]] = None,
201
+ token: Optional[str] = None,
202
+ branch: Optional[str] = None,
203
+ ) -> Dict[str, Any]:
204
+ """Clone the codeui repository for inspection, bug fixing, or feature addition.
205
+ Example:
206
+ >>> g = Graph()
207
+ >>> tools = CodeUITools(g)
208
+ >>> tools.get_repository_url()
209
+ 'https://github.com/AAlsubari/codeUI'
210
+ """
211
+ mgr = ContributionManager(token=token)
212
+ cloned_path = mgr.clone_repository(target_dir=target_dir, token=token, branch=branch)
213
+ return {
214
+ "status": "cloned",
215
+ "repo_url": mgr.get_repo_url(),
216
+ "cloned_dir": str(cloned_path),
217
+ }
218
+
219
+ def submit_library_contribution(
220
+ self,
221
+ title: str,
222
+ description: str,
223
+ file_changes: Dict[str, str],
224
+ branch_name: Optional[str] = None,
225
+ token: Optional[str] = None,
226
+ push: bool = True,
227
+ ) -> Dict[str, Any]:
228
+ """Clone codeui, apply modifications (bug fixes / features), commit, and push to GitHub.
229
+ Example:
230
+ >>> g = Graph()
231
+ >>> tools = CodeUITools(g)
232
+ >>> tools.get_repository_url()
233
+ 'https://github.com/AAlsubari/codeUI'
234
+ """
235
+ mgr = ContributionManager(token=token)
236
+ return mgr.submit_contribution(
237
+ title=title,
238
+ description=description,
239
+ file_changes=file_changes,
240
+ branch_name=branch_name,
241
+ token=token,
242
+ push=push,
243
+ )
@@ -0,0 +1,94 @@
1
+ """Model Context Protocol (MCP) server exposing codeui edit and analysis tools."""
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Any, Dict, Optional, Union
5
+ from codeui.agent.integrations import CodeUITools
6
+ from codeui.core.graph import Graph
7
+
8
+ class MCPServer:
9
+ """Model Context Protocol (MCP) server handler for AI agent tool invocations.
10
+ Example:
11
+ >>> g = Graph()
12
+ >>> server = MCPServer(g)
13
+ >>> resp = server.handle_request({"method": "tools/list", "id": 1})
14
+ >>> resp["result"]["tools"][0]["name"]
15
+ 'find_symbol'
16
+ """
17
+ def __init__(self, graph: Graph, project_root: Optional[Union[str, Path]] = None, direct_disk: bool = True) -> None:
18
+ self.graph = graph
19
+ self.tools = CodeUITools(graph, project_root=project_root, direct_disk=direct_disk)
20
+
21
+ def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
22
+ """Process JSON-RPC style MCP protocol request.
23
+ Example:
24
+ >>> server = MCPServer(Graph())
25
+ >>> resp = server.handle_request({"method": "unknown", "id": 1})
26
+ >>> "error" in resp
27
+ True
28
+ """
29
+ req_id = request.get("id")
30
+ method = request.get("method")
31
+ params = request.get("params", {})
32
+
33
+ if method == "tools/list":
34
+ return {
35
+ "jsonrpc": "2.0",
36
+ "id": req_id,
37
+ "result": {
38
+ "tools": [
39
+ {"name": "find_symbol", "description": "Search for code symbols by name"},
40
+ {"name": "get_context", "description": "Get graph context bundle for symbol"},
41
+ {"name": "find_defects", "description": "Get defect findings"},
42
+ {"name": "trace_call_chain", "description": "Trace symbol forward/backward call chain"},
43
+ {"name": "propose_rename", "description": "Propose renaming a symbol and preview diff"},
44
+ {"name": "propose_replace_file", "description": "Propose replacing entire file content"},
45
+ {"name": "apply_proposal", "description": "Apply edit proposal into active override store or directly to disk"},
46
+ {"name": "write_file_to_disk", "description": "Directly write new content to project file on disk"},
47
+ {"name": "revert_override", "description": "Revert active override for target"},
48
+ {"name": "get_diff", "description": "Get unified diff across all active overrides"},
49
+ ]
50
+ },
51
+ }
52
+ elif method == "tools/call":
53
+ tool_name = params.get("name")
54
+ args = params.get("arguments", {})
55
+ if tool_name == "find_symbol":
56
+ res = self.tools.find_symbol(args.get("query", ""))
57
+ elif tool_name == "get_context":
58
+ res = self.tools.get_context(args.get("symbol_id", ""), args.get("depth", 1))
59
+ elif tool_name == "find_defects":
60
+ res = self.tools.find_defects(args.get("severity"))
61
+ elif tool_name == "trace_call_chain":
62
+ res = self.tools.trace_call_chain(args.get("symbol_id", ""), args.get("direction", "forward"), args.get("depth", 3))
63
+ elif tool_name == "propose_rename":
64
+ res = self.tools.propose_rename(args.get("symbol_id", ""), args.get("new_name", ""))
65
+ elif tool_name == "propose_replace_file":
66
+ res = self.tools.propose_replace_file(args.get("file_id", ""), args.get("new_content", ""), args.get("original_content", ""))
67
+ elif tool_name == "apply_proposal":
68
+ res = self.tools.apply_proposal(
69
+ args.get("proposal", {}),
70
+ author=args.get("author", "agent"),
71
+ direct_disk=args.get("direct_disk"),
72
+ )
73
+ elif tool_name == "write_file_to_disk":
74
+ res = self.tools.write_file_direct(args.get("file_id", ""), args.get("content", ""))
75
+ elif tool_name == "revert_override":
76
+ res = self.tools.revert_override(args.get("target_id", ""))
77
+ elif tool_name == "get_diff":
78
+ res = {"diff": self.tools.get_diff()}
79
+ else:
80
+ return {
81
+ "jsonrpc": "2.0",
82
+ "id": req_id,
83
+ "error": {"code": -32601, "message": f"Tool not found: {tool_name}"},
84
+ }
85
+ return {
86
+ "jsonrpc": "2.0",
87
+ "id": req_id,
88
+ "result": {"content": [{"type": "text", "text": json.dumps(res, sort_keys=True)}]},
89
+ }
90
+ return {
91
+ "jsonrpc": "2.0",
92
+ "id": req_id,
93
+ "error": {"code": -32601, "message": f"Method not found: {method}"},
94
+ }
@@ -0,0 +1 @@
1
+ """Analysis engine and defect detection modules for codeui."""