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
codeui/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ """codeui: Universal Code-Intelligence Library. Demo Version 18661"""
2
+ from codeui.core.ir import Location, Symbol, SymbolKind, Visibility, EdgeKind, Edge, Finding, Severity
3
+ from codeui.core.graph import Graph
4
+ from codeui.core.override import Override, OverrideKind, OverrideStore
5
+ from codeui.core.repo import get_library_repo_url
6
+
7
+ __version__ = "0.1.0"
8
+
9
+ __all__ = [
10
+ "Location",
11
+ "Symbol",
12
+ "SymbolKind",
13
+ "Visibility",
14
+ "EdgeKind",
15
+ "Edge",
16
+ "Finding",
17
+ "Severity",
18
+ "Graph",
19
+ "Override",
20
+ "OverrideKind",
21
+ "OverrideStore",
22
+ "get_library_repo_url",
23
+ ]
@@ -0,0 +1,15 @@
1
+ """Agent integration and dynamic context builder package for codeui."""
2
+ from codeui.agent.context import ContextBuilder, ContextBundle, TaskSpec
3
+ from codeui.agent.contribution import ContributionManager
4
+ from codeui.agent.edit_tools import EditProposal, EditTools
5
+ from codeui.agent.integrations import CodeUITools
6
+
7
+ __all__ = [
8
+ "ContextBuilder",
9
+ "ContextBundle",
10
+ "TaskSpec",
11
+ "ContributionManager",
12
+ "EditProposal",
13
+ "EditTools",
14
+ "CodeUITools",
15
+ ]
@@ -0,0 +1,207 @@
1
+ """Dynamic context builder for AI agents based on graph structure."""
2
+ import json
3
+ from dataclasses import dataclass, field, asdict
4
+ from typing import Any, Dict, List, Optional, Set, Tuple
5
+ from codeui.core.graph import Graph
6
+ from codeui.core.ir import Edge, Finding, Symbol
7
+ from codeui.errors import SymbolNotFoundError, FileNotFoundInGraphError
8
+
9
+ @dataclass
10
+ class TaskSpec:
11
+ """Specification of an AI agent coding task.
12
+ Example:
13
+ >>> spec = TaskSpec("Fix bug in main", ["sym_1"], ["app.py"])
14
+ >>> spec.query
15
+ 'Fix bug in main'
16
+ """
17
+ query: str
18
+ target_symbols: List[str] = field(default_factory=list)
19
+ target_files: List[str] = field(default_factory=list)
20
+
21
+ @dataclass
22
+ class ContextBundle:
23
+ """Compact context payload for LLMs built via graph structure.
24
+ Example:
25
+ >>> bundle = ContextBundle({"sym_1": "def f(): pass"}, {"sym_2": "def g():"}, [], [], {"reason": "test"})
26
+ >>> bundle.manifest["reason"]
27
+ 'test'
28
+ """
29
+ symbol_bodies: Dict[str, str]
30
+ cross_signatures: Dict[str, str]
31
+ edge_summaries: List[Dict[str, Any]]
32
+ findings: List[Dict[str, Any]]
33
+ manifest: Dict[str, Any]
34
+
35
+ def to_dict(self) -> Dict[str, Any]:
36
+ """Convert context bundle to dictionary.
37
+ Example:
38
+ >>> bundle = ContextBundle({}, {}, [], [], {})
39
+ >>> "symbol_bodies" in bundle.to_dict()
40
+ True
41
+ """
42
+ return asdict(self)
43
+
44
+ def to_json(self) -> str:
45
+ """Convert context bundle to deterministic JSON string.
46
+ Example:
47
+ >>> bundle = ContextBundle({}, {}, [], [], {})
48
+ >>> isinstance(bundle.to_json(), str)
49
+ True
50
+ """
51
+ return json.dumps(self.to_dict(), sort_keys=True, indent=2)
52
+
53
+ class ContextBuilder:
54
+ """Builds structured dynamic context bundles from graph traversal.
55
+ Example:
56
+ >>> g = Graph()
57
+ >>> builder = ContextBuilder(g)
58
+ >>> bundle = builder.for_symbol("sym_1", 1)
59
+ >>> bundle.manifest["status"]
60
+ 'empty'
61
+ """
62
+ def __init__(self, graph: Graph) -> None:
63
+ self.graph = graph
64
+
65
+ @staticmethod
66
+ def _comment_prefix(file_id: str) -> str:
67
+ lower = file_id.lower()
68
+ if lower.endswith((".py", ".pyi", ".sh", ".bash", ".zsh", ".yaml", ".yml", ".toml", ".rb")):
69
+ return "#"
70
+ if lower.endswith((".sql", ".lua")):
71
+ return "--"
72
+ return "//"
73
+
74
+ def for_symbol(self, symbol_id: str, depth: int = 1, seen_hashes: Set[str] | None = None) -> ContextBundle:
75
+ """Build context bundle starting from a symbol outwards by N hops.
76
+ Example:
77
+ >>> g = Graph()
78
+ >>> builder = ContextBuilder(g)
79
+ >>> bundle = builder.for_symbol("sym_1", 1)
80
+ >>> "symbol_id" in bundle.manifest
81
+ True
82
+ """
83
+ seen_hashes = seen_hashes or set()
84
+ if not self.graph.has_symbol(symbol_id):
85
+ return ContextBundle(
86
+ symbol_bodies={},
87
+ cross_signatures={},
88
+ edge_summaries=[],
89
+ findings=[],
90
+ manifest={"symbol_id": symbol_id, "status": "empty", "reason": "symbol_not_found"},
91
+ )
92
+ syms, edges = self.graph.get_subgraph(symbol_id, depth=depth)
93
+ target_sym = self.graph.get_symbol(symbol_id)
94
+ bodies: Dict[str, str] = {}
95
+ signatures: Dict[str, str] = {}
96
+ for s in syms:
97
+ if s.content_hash in seen_hashes:
98
+ continue
99
+ f_id = s.location.file_id if s.location else ""
100
+ cm = self._comment_prefix(f_id)
101
+ if s.id == symbol_id:
102
+ bodies[s.id] = f"{cm} {f_id}:{s.location.start_line if s.location else 1}\n{s.signature or s.name}"
103
+ else:
104
+ signatures[s.id] = s.signature or f"{s.kind} {s.name}"
105
+
106
+ edge_summaries = [
107
+ {"source": e.source_id, "target": e.target_id, "kind": str(e.kind)}
108
+ for e in edges
109
+ ]
110
+
111
+ relevant_findings = [
112
+ f.to_dict() for f in self.graph.get_findings()
113
+ if f.symbol_id in {s.id for s in syms} or f.location.file_id == target_sym.location.file_id
114
+ ]
115
+
116
+ manifest = {
117
+ "symbol_id": symbol_id,
118
+ "depth": depth,
119
+ "symbols_included": len(syms),
120
+ "edges_included": len(edges),
121
+ "findings_included": len(relevant_findings),
122
+ "status": "success",
123
+ }
124
+ return ContextBundle(
125
+ symbol_bodies=bodies,
126
+ cross_signatures=signatures,
127
+ edge_summaries=edge_summaries,
128
+ findings=relevant_findings,
129
+ manifest=manifest,
130
+ )
131
+
132
+ def for_file(self, file_id: str, depth: int = 1, seen_hashes: Set[str] | None = None) -> ContextBundle:
133
+ """Build context bundle for all symbols in a file.
134
+ Example:
135
+ >>> g = Graph()
136
+ >>> builder = ContextBuilder(g)
137
+ >>> bundle = builder.for_file("app.py", 1)
138
+ >>> bundle.manifest["file_id"]
139
+ 'app.py'
140
+ """
141
+ seen_hashes = seen_hashes or set()
142
+ symbols = self.graph.get_symbols_by_file(file_id)
143
+ bodies: Dict[str, str] = {}
144
+ cm = self._comment_prefix(file_id)
145
+ for s in symbols:
146
+ if s.content_hash not in seen_hashes:
147
+ bodies[s.id] = f"{cm} {file_id}:{s.location.start_line if s.location else 1}\n{s.signature or s.name}"
148
+ relevant_findings = [
149
+ f.to_dict() for f in self.graph.get_findings()
150
+ if f.location.file_id == file_id
151
+ ]
152
+ return ContextBundle(
153
+ symbol_bodies=bodies,
154
+ cross_signatures={},
155
+ edge_summaries=[],
156
+ findings=relevant_findings,
157
+ manifest={"file_id": file_id, "symbols_count": len(symbols), "status": "success"},
158
+ )
159
+
160
+ def for_task(self, task: TaskSpec, depth: int = 1) -> ContextBundle:
161
+ """Build context bundle tailored to a task specification.
162
+ Example:
163
+ >>> g = Graph()
164
+ >>> builder = ContextBuilder(g)
165
+ >>> spec = TaskSpec("Fix issue")
166
+ >>> bundle = builder.for_task(spec)
167
+ >>> "query" in bundle.manifest
168
+ True
169
+ """
170
+ combined_bodies: Dict[str, str] = {}
171
+ combined_sigs: Dict[str, str] = {}
172
+ combined_edges: List[Dict[str, Any]] = []
173
+ combined_findings: List[Dict[str, Any]] = []
174
+ for sym_id in task.target_symbols:
175
+ b = self.for_symbol(sym_id, depth=depth)
176
+ combined_bodies.update(b.symbol_bodies)
177
+ combined_sigs.update(b.cross_signatures)
178
+ combined_edges.extend(b.edge_summaries)
179
+ combined_findings.extend(b.findings)
180
+ for file_id in task.target_files:
181
+ b = self.for_file(file_id, depth=depth)
182
+ combined_bodies.update(b.symbol_bodies)
183
+ combined_findings.extend(b.findings)
184
+ return ContextBundle(
185
+ symbol_bodies=combined_bodies,
186
+ cross_signatures=combined_sigs,
187
+ edge_summaries=combined_edges,
188
+ findings=combined_findings,
189
+ manifest={"query": task.query, "symbols": task.target_symbols, "files": task.target_files},
190
+ )
191
+
192
+ def for_defect(self, finding_id: str) -> ContextBundle:
193
+ """Build context bundle targeting a specific defect finding.
194
+ Example:
195
+ >>> g = Graph()
196
+ >>> builder = ContextBuilder(g)
197
+ >>> bundle = builder.for_defect("f1")
198
+ >>> bundle.manifest["finding_id"]
199
+ 'f1'
200
+ """
201
+ matching = [f for f in self.graph.get_findings() if f.id == finding_id]
202
+ if not matching:
203
+ return ContextBundle({}, {}, [], [], {"finding_id": finding_id, "status": "not_found"})
204
+ f = matching[0]
205
+ if f.symbol_id:
206
+ return self.for_symbol(f.symbol_id, depth=1)
207
+ return self.for_file(f.location.file_id, depth=1)
@@ -0,0 +1,224 @@
1
+ """AI agent feedback and repository contribution workflow for codeui."""
2
+ import os
3
+ import re
4
+ import shutil
5
+ import subprocess
6
+ import tempfile
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Optional, Union
9
+ from codeui.core.repo import clone_git_repo, get_library_repo_url
10
+ from codeui.errors import ContributionError, RepositoryCloneError
11
+
12
+ class ContributionManager:
13
+ """Manager for AI agents to clone codeui, apply patches, and push modifications.
14
+ Example:
15
+ >>> mgr = ContributionManager()
16
+ >>> mgr.get_repo_url()
17
+ 'https://github.com/AAlsubari/codeUI'
18
+ """
19
+ def __init__(
20
+ self,
21
+ repo_url: Optional[str] = None,
22
+ token: Optional[str] = None,
23
+ ) -> None:
24
+ self.repo_url = repo_url or get_library_repo_url()
25
+ self.token = token or os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
26
+
27
+ def get_repo_url(self) -> str:
28
+ """Get the target GitHub repository URL.
29
+ Example:
30
+ >>> ContributionManager().get_repo_url()
31
+ 'https://github.com/AAlsubari/codeUI'
32
+ """
33
+ return self.repo_url
34
+
35
+ def _authenticated_url(self, token: Optional[str] = None) -> str:
36
+ auth_token = token or self.token
37
+ url = self.repo_url.strip()
38
+ if auth_token and url.startswith("https://"):
39
+ return url.replace("https://", f"https://x-access-token:{auth_token}@")
40
+ return url
41
+
42
+ def clone_repository(
43
+ self,
44
+ target_dir: Optional[Union[str, Path]] = None,
45
+ token: Optional[str] = None,
46
+ branch: Optional[str] = None,
47
+ ) -> Path:
48
+ """Clone the codeui library repository into target or temporary directory.
49
+ Example:
50
+ >>> mgr = ContributionManager()
51
+ >>> isinstance(mgr.get_repo_url(), str)
52
+ True
53
+ """
54
+ auth_url = self._authenticated_url(token)
55
+ dest = Path(target_dir) if target_dir else None
56
+ cloned_path, _ = clone_git_repo(auth_url, dest_dir=dest)
57
+ if branch:
58
+ git_bin = shutil.which("git") or "git"
59
+ res = subprocess.run([git_bin, "checkout", branch], cwd=cloned_path, capture_output=True, text=True)
60
+ if res.returncode != 0:
61
+ subprocess.run([git_bin, "checkout", "-b", branch], cwd=cloned_path, capture_output=True, text=True)
62
+ return cloned_path
63
+
64
+ def create_feature_branch(self, repo_dir: Union[str, Path], branch_name: str) -> str:
65
+ """Create and checkout a new branch in the cloned repository.
66
+ Example:
67
+ >>> mgr = ContributionManager()
68
+ >>> mgr.clean_branch_name("Fix Bug #1")
69
+ 'agent/fix-bug-1'
70
+ """
71
+ git_bin = shutil.which("git") or "git"
72
+ p = Path(repo_dir)
73
+ safe_branch = self.clean_branch_name(branch_name)
74
+ res = subprocess.run([git_bin, "checkout", "-b", safe_branch], cwd=p, capture_output=True, text=True)
75
+ if res.returncode != 0:
76
+ res_co = subprocess.run([git_bin, "checkout", safe_branch], cwd=p, capture_output=True, text=True)
77
+ if res_co.returncode != 0:
78
+ raise ContributionError(f"Failed to create branch '{safe_branch}': {res.stderr.strip()}")
79
+ return safe_branch
80
+
81
+ @staticmethod
82
+ def clean_branch_name(name: str) -> str:
83
+ """Convert descriptive name into a git-safe branch identifier.
84
+ Example:
85
+ >>> ContributionManager.clean_branch_name("Fix AST Edge Leak!")
86
+ 'agent/fix-ast-edge-leak'
87
+ """
88
+ raw = name.strip().lower()
89
+ if raw.startswith("agent/"):
90
+ raw = raw[6:]
91
+ slug = re.sub(r"[^a-zA-Z0-9_-]+", "-", raw).strip("-")
92
+ if not slug:
93
+ slug = "patch"
94
+ return f"agent/{slug}"
95
+
96
+ def apply_changes(self, repo_dir: Union[str, Path], file_changes: Dict[str, str]) -> List[str]:
97
+ """Apply modified or new file contents to the repository on disk.
98
+ Example:
99
+ >>> import tempfile
100
+ >>> with tempfile.TemporaryDirectory() as td:
101
+ ... mgr = ContributionManager()
102
+ ... mgr.apply_changes(td, {"test.txt": "hello"})
103
+ ['test.txt']
104
+ """
105
+ p = Path(repo_dir)
106
+ applied: List[str] = []
107
+ for rel_path, content in file_changes.items():
108
+ target = p / rel_path.lstrip("/\\")
109
+ target.parent.mkdir(parents=True, exist_ok=True)
110
+ target.write_text(content, encoding="utf-8")
111
+ applied.append(rel_path)
112
+ return applied
113
+
114
+ def commit_changes(
115
+ self,
116
+ repo_dir: Union[str, Path],
117
+ message: str,
118
+ author_name: str = "CodeUI Agent",
119
+ author_email: str = "agent@codeui.dev",
120
+ ) -> str:
121
+ """Stage all modifications and commit with structured commit message.
122
+ Example:
123
+ >>> mgr = ContributionManager()
124
+ >>> mgr.get_repo_url()
125
+ 'https://github.com/AAlsubari/codeUI'
126
+ """
127
+ git_bin = shutil.which("git") or "git"
128
+ p = Path(repo_dir)
129
+ subprocess.run([git_bin, "config", "user.name", author_name], cwd=p, capture_output=True, text=True)
130
+ subprocess.run([git_bin, "config", "user.email", author_email], cwd=p, capture_output=True, text=True)
131
+ subprocess.run([git_bin, "add", "-A"], cwd=p, capture_output=True, text=True)
132
+
133
+ res = subprocess.run([git_bin, "commit", "-m", message], cwd=p, capture_output=True, text=True)
134
+ if res.returncode != 0 and "nothing to commit" not in res.stdout.lower() and "nothing to commit" not in res.stderr.lower():
135
+ raise ContributionError(f"Git commit failed: {res.stderr.strip() or res.stdout.strip()}")
136
+ return message
137
+
138
+ def push_changes(
139
+ self,
140
+ repo_dir: Union[str, Path],
141
+ remote: str = "origin",
142
+ branch: Optional[str] = None,
143
+ token: Optional[str] = None,
144
+ ) -> Dict[str, Any]:
145
+ """Push committed changes to remote repository.
146
+ Example:
147
+ >>> mgr = ContributionManager()
148
+ >>> mgr.get_repo_url()
149
+ 'https://github.com/AAlsubari/codeUI'
150
+ """
151
+ git_bin = shutil.which("git") or "git"
152
+ p = Path(repo_dir)
153
+ auth_url = self._authenticated_url(token)
154
+
155
+ curr_branch = branch
156
+ if not curr_branch:
157
+ res_b = subprocess.run([git_bin, "rev-parse", "--abbrev-ref", "HEAD"], cwd=p, capture_output=True, text=True)
158
+ curr_branch = res_b.stdout.strip() or "main"
159
+
160
+ subprocess.run([git_bin, "remote", "set-url", remote, auth_url], cwd=p, capture_output=True, text=True)
161
+ res_push = subprocess.run([git_bin, "push", "-u", remote, curr_branch], cwd=p, capture_output=True, text=True)
162
+
163
+ if res_push.returncode != 0:
164
+ return {
165
+ "success": False,
166
+ "branch": curr_branch,
167
+ "error": res_push.stderr.strip() or res_push.stdout.strip(),
168
+ "message": "Push failed, local commit retained.",
169
+ }
170
+ return {
171
+ "success": True,
172
+ "branch": curr_branch,
173
+ "error": None,
174
+ "message": f"Successfully pushed branch {curr_branch} to {self.repo_url}",
175
+ }
176
+
177
+ def submit_contribution(
178
+ self,
179
+ title: str,
180
+ description: str,
181
+ file_changes: Dict[str, str],
182
+ branch_name: Optional[str] = None,
183
+ token: Optional[str] = None,
184
+ push: bool = True,
185
+ workspace_dir: Optional[Union[str, Path]] = None,
186
+ ) -> Dict[str, Any]:
187
+ """Execute complete contribution flow: clone, branch, edit, commit, and push.
188
+ Example:
189
+ >>> mgr = ContributionManager()
190
+ >>> isinstance(mgr.get_repo_url(), str)
191
+ True
192
+ """
193
+ temp_dir_obj = None
194
+ if workspace_dir:
195
+ work_path = Path(workspace_dir).resolve()
196
+ work_path.mkdir(parents=True, exist_ok=True)
197
+ else:
198
+ temp_dir_obj = tempfile.TemporaryDirectory(prefix="codeui_contrib_")
199
+ work_path = Path(temp_dir_obj.name).resolve()
200
+
201
+ push_result: Dict[str, Any] = {"success": False, "message": "Push disabled by caller"}
202
+ try:
203
+ cloned_repo = self.clone_repository(target_dir=work_path, token=token)
204
+ target_branch = self.create_feature_branch(cloned_repo, branch_name or title)
205
+ applied_files = self.apply_changes(cloned_repo, file_changes)
206
+
207
+ commit_msg = f"{title}\n\n{description}\n\nSubmitted by codeui Agent"
208
+ self.commit_changes(cloned_repo, commit_msg)
209
+
210
+ if push:
211
+ push_result = self.push_changes(cloned_repo, branch=target_branch, token=token)
212
+
213
+ return {
214
+ "status": "pushed" if push_result.get("success") else "committed_locally",
215
+ "repo_url": self.repo_url,
216
+ "branch": target_branch,
217
+ "changed_files": applied_files,
218
+ "commit_message": commit_msg,
219
+ "push_result": push_result,
220
+ "cloned_dir": str(cloned_repo),
221
+ }
222
+ finally:
223
+ if temp_dir_obj and not push_result.get("success"):
224
+ pass