codegraph-cli 2.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_cli/__init__.py +4 -0
- codegraph_cli/agents.py +191 -0
- codegraph_cli/bug_detector.py +386 -0
- codegraph_cli/chat_agent.py +352 -0
- codegraph_cli/chat_session.py +220 -0
- codegraph_cli/cli.py +330 -0
- codegraph_cli/cli_chat.py +367 -0
- codegraph_cli/cli_diagnose.py +133 -0
- codegraph_cli/cli_refactor.py +230 -0
- codegraph_cli/cli_setup.py +470 -0
- codegraph_cli/cli_test.py +177 -0
- codegraph_cli/cli_v2.py +267 -0
- codegraph_cli/codegen_agent.py +265 -0
- codegraph_cli/config.py +31 -0
- codegraph_cli/config_manager.py +341 -0
- codegraph_cli/context_manager.py +500 -0
- codegraph_cli/crew_agents.py +123 -0
- codegraph_cli/crew_chat.py +159 -0
- codegraph_cli/crew_tools.py +497 -0
- codegraph_cli/diff_engine.py +265 -0
- codegraph_cli/embeddings.py +241 -0
- codegraph_cli/graph_export.py +144 -0
- codegraph_cli/llm.py +642 -0
- codegraph_cli/models.py +47 -0
- codegraph_cli/models_v2.py +185 -0
- codegraph_cli/orchestrator.py +49 -0
- codegraph_cli/parser.py +800 -0
- codegraph_cli/performance_analyzer.py +223 -0
- codegraph_cli/project_context.py +230 -0
- codegraph_cli/rag.py +200 -0
- codegraph_cli/refactor_agent.py +452 -0
- codegraph_cli/security_scanner.py +366 -0
- codegraph_cli/storage.py +390 -0
- codegraph_cli/templates/graph_interactive.html +257 -0
- codegraph_cli/testgen_agent.py +316 -0
- codegraph_cli/validation_engine.py +285 -0
- codegraph_cli/vector_store.py +293 -0
- codegraph_cli-2.0.0.dist-info/METADATA +318 -0
- codegraph_cli-2.0.0.dist-info/RECORD +43 -0
- codegraph_cli-2.0.0.dist-info/WHEEL +5 -0
- codegraph_cli-2.0.0.dist-info/entry_points.txt +2 -0
- codegraph_cli-2.0.0.dist-info/licenses/LICENSE +21 -0
- codegraph_cli-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Data models for v2.0 code generation features."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Location:
|
|
11
|
+
"""Represents a location in source code."""
|
|
12
|
+
file_path: str
|
|
13
|
+
line: int
|
|
14
|
+
column: int = 0
|
|
15
|
+
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
return f"{self.file_path}:{self.line}"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Range:
|
|
22
|
+
"""Represents a range in source code."""
|
|
23
|
+
start: Location
|
|
24
|
+
end: Location
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
if self.start.file_path == self.end.file_path:
|
|
28
|
+
return f"{self.start.file_path}:{self.start.line}-{self.end.line}"
|
|
29
|
+
return f"{self.start} to {self.end}"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class FileChange:
|
|
34
|
+
"""Represents changes to a single file."""
|
|
35
|
+
file_path: str
|
|
36
|
+
change_type: Literal["create", "modify", "delete"]
|
|
37
|
+
original_content: Optional[str] = None
|
|
38
|
+
new_content: Optional[str] = None
|
|
39
|
+
diff: str = ""
|
|
40
|
+
|
|
41
|
+
def __post_init__(self):
|
|
42
|
+
"""Validate change type constraints."""
|
|
43
|
+
if self.change_type == "create" and self.original_content is not None:
|
|
44
|
+
raise ValueError("Create changes should not have original_content")
|
|
45
|
+
if self.change_type == "delete" and self.new_content is not None:
|
|
46
|
+
raise ValueError("Delete changes should not have new_content")
|
|
47
|
+
if self.change_type == "modify" and (self.original_content is None or self.new_content is None):
|
|
48
|
+
raise ValueError("Modify changes must have both original and new content")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class CodeProposal:
|
|
53
|
+
"""Represents proposed code changes."""
|
|
54
|
+
id: str
|
|
55
|
+
description: str
|
|
56
|
+
changes: List[FileChange]
|
|
57
|
+
impact_summary: str = ""
|
|
58
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def num_files_changed(self) -> int:
|
|
62
|
+
"""Number of files affected."""
|
|
63
|
+
return len(self.changes)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def num_files_created(self) -> int:
|
|
67
|
+
"""Number of new files."""
|
|
68
|
+
return sum(1 for c in self.changes if c.change_type == "create")
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def num_files_modified(self) -> int:
|
|
72
|
+
"""Number of modified files."""
|
|
73
|
+
return sum(1 for c in self.changes if c.change_type == "modify")
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def num_files_deleted(self) -> int:
|
|
77
|
+
"""Number of deleted files."""
|
|
78
|
+
return sum(1 for c in self.changes if c.change_type == "delete")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class RefactorPlan:
|
|
83
|
+
"""Represents a refactoring operation."""
|
|
84
|
+
refactor_type: str # "extract-function", "rename", "extract-service"
|
|
85
|
+
description: str
|
|
86
|
+
source_locations: List[Location]
|
|
87
|
+
target_location: Location
|
|
88
|
+
call_sites: List[Location]
|
|
89
|
+
changes: List[FileChange]
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def num_call_sites(self) -> int:
|
|
93
|
+
"""Number of call sites that will be updated."""
|
|
94
|
+
return len(self.call_sites)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class TestCase:
|
|
99
|
+
"""Represents a generated test."""
|
|
100
|
+
name: str
|
|
101
|
+
target_function: str
|
|
102
|
+
test_code: str
|
|
103
|
+
description: str
|
|
104
|
+
test_type: Literal["unit", "integration"] = "unit"
|
|
105
|
+
coverage_impact: float = 0.0
|
|
106
|
+
|
|
107
|
+
def __str__(self) -> str:
|
|
108
|
+
return f"Test: {self.name} for {self.target_function}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class ApplyResult:
|
|
113
|
+
"""Result of applying changes."""
|
|
114
|
+
success: bool
|
|
115
|
+
files_changed: List[str]
|
|
116
|
+
backup_id: Optional[str] = None
|
|
117
|
+
error: Optional[str] = None
|
|
118
|
+
|
|
119
|
+
def __str__(self) -> str:
|
|
120
|
+
if self.success:
|
|
121
|
+
return f"✅ Applied changes to {len(self.files_changed)} files"
|
|
122
|
+
return f"❌ Failed: {self.error}"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class ValidationResult:
|
|
127
|
+
"""Result of validating code changes."""
|
|
128
|
+
valid: bool
|
|
129
|
+
errors: List[str] = field(default_factory=list)
|
|
130
|
+
warnings: List[str] = field(default_factory=list)
|
|
131
|
+
|
|
132
|
+
def __str__(self) -> str:
|
|
133
|
+
if self.valid:
|
|
134
|
+
return "✅ Validation passed"
|
|
135
|
+
return f"❌ Validation failed: {', '.join(self.errors)}"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass
|
|
139
|
+
class ChatMessage:
|
|
140
|
+
"""Represents a single message in a chat conversation."""
|
|
141
|
+
role: Literal["user", "assistant", "system"]
|
|
142
|
+
content: str
|
|
143
|
+
timestamp: str
|
|
144
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
145
|
+
|
|
146
|
+
def __str__(self) -> str:
|
|
147
|
+
return f"[{self.role}] {self.content[:100]}"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class ChatSession:
|
|
152
|
+
"""Represents a chat session with conversation history."""
|
|
153
|
+
id: str
|
|
154
|
+
project_name: str
|
|
155
|
+
messages: List[ChatMessage] = field(default_factory=list)
|
|
156
|
+
pending_proposals: List[CodeProposal] = field(default_factory=list)
|
|
157
|
+
created_at: str = ""
|
|
158
|
+
updated_at: str = ""
|
|
159
|
+
|
|
160
|
+
def add_message(self, role: str, content: str, timestamp: str, metadata: Optional[Dict] = None):
|
|
161
|
+
"""Add a message to the conversation."""
|
|
162
|
+
self.messages.append(ChatMessage(
|
|
163
|
+
role=role,
|
|
164
|
+
content=content,
|
|
165
|
+
timestamp=timestamp,
|
|
166
|
+
metadata=metadata or {}
|
|
167
|
+
))
|
|
168
|
+
self.updated_at = timestamp
|
|
169
|
+
|
|
170
|
+
def clear_history(self):
|
|
171
|
+
"""Clear all messages from this session."""
|
|
172
|
+
self.messages.clear()
|
|
173
|
+
|
|
174
|
+
def clear_proposals(self):
|
|
175
|
+
"""Clear all pending proposals."""
|
|
176
|
+
self.pending_proposals.clear()
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def message_count(self) -> int:
|
|
180
|
+
"""Number of messages in conversation."""
|
|
181
|
+
return len(self.messages)
|
|
182
|
+
|
|
183
|
+
def get_recent_messages(self, n: int = 3) -> List[ChatMessage]:
|
|
184
|
+
"""Get the N most recent messages."""
|
|
185
|
+
return self.messages[-n:] if len(self.messages) >= n else self.messages
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""MCP-style orchestrator coordinating specialized agents."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List
|
|
7
|
+
|
|
8
|
+
from .agents import GraphAgent, RAGAgent, SummarizationAgent
|
|
9
|
+
from .embeddings import HashEmbeddingModel
|
|
10
|
+
from .llm import LocalLLM
|
|
11
|
+
from .models import ImpactReport, SearchResult
|
|
12
|
+
from .rag import RAGRetriever
|
|
13
|
+
from .storage import GraphStore
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MCPOrchestrator:
|
|
17
|
+
"""Coordinates graph, retrieval, and summarization agents."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
store: GraphStore,
|
|
22
|
+
llm_model: str = "qwen2.5-coder:7b",
|
|
23
|
+
llm_provider: str = "ollama",
|
|
24
|
+
llm_api_key: str | None = None,
|
|
25
|
+
llm_endpoint: str | None = None,
|
|
26
|
+
):
|
|
27
|
+
self.store = store
|
|
28
|
+
self.embedding_model = HashEmbeddingModel()
|
|
29
|
+
self.graph_agent = GraphAgent(store, self.embedding_model)
|
|
30
|
+
self.rag_agent = RAGAgent(RAGRetriever(store, self.embedding_model))
|
|
31
|
+
self.summarization_agent = SummarizationAgent(
|
|
32
|
+
store,
|
|
33
|
+
LocalLLM(model=llm_model, provider=llm_provider, api_key=llm_api_key, endpoint=llm_endpoint),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def index(self, project_root: Path) -> Dict[str, int]:
|
|
37
|
+
return self.graph_agent.index_project(project_root)
|
|
38
|
+
|
|
39
|
+
def search(self, query: str, top_k: int = 5) -> List[SearchResult]:
|
|
40
|
+
return self.rag_agent.semantic_search(query, top_k=top_k)
|
|
41
|
+
|
|
42
|
+
def impact(self, symbol: str, hops: int = 2) -> ImpactReport:
|
|
43
|
+
return self.summarization_agent.impact_analysis(symbol=symbol, hops=hops)
|
|
44
|
+
|
|
45
|
+
def graph(self, symbol: str, depth: int = 2) -> str:
|
|
46
|
+
return self.graph_agent.ascii_neighbors(symbol=symbol, depth=depth)
|
|
47
|
+
|
|
48
|
+
def rag_context(self, query: str, top_k: int = 6) -> str:
|
|
49
|
+
return self.rag_agent.context_for_query(query, top_k=top_k)
|