ai-coding-assistant 0.5.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 (89) hide show
  1. ai_coding_assistant-0.5.0.dist-info/METADATA +226 -0
  2. ai_coding_assistant-0.5.0.dist-info/RECORD +89 -0
  3. ai_coding_assistant-0.5.0.dist-info/WHEEL +4 -0
  4. ai_coding_assistant-0.5.0.dist-info/entry_points.txt +3 -0
  5. ai_coding_assistant-0.5.0.dist-info/licenses/LICENSE +21 -0
  6. coding_assistant/__init__.py +3 -0
  7. coding_assistant/__main__.py +19 -0
  8. coding_assistant/cli/__init__.py +1 -0
  9. coding_assistant/cli/app.py +158 -0
  10. coding_assistant/cli/commands/__init__.py +19 -0
  11. coding_assistant/cli/commands/ask.py +178 -0
  12. coding_assistant/cli/commands/config.py +438 -0
  13. coding_assistant/cli/commands/diagram.py +267 -0
  14. coding_assistant/cli/commands/document.py +410 -0
  15. coding_assistant/cli/commands/explain.py +192 -0
  16. coding_assistant/cli/commands/fix.py +249 -0
  17. coding_assistant/cli/commands/index.py +162 -0
  18. coding_assistant/cli/commands/refactor.py +245 -0
  19. coding_assistant/cli/commands/search.py +182 -0
  20. coding_assistant/cli/commands/serve_docs.py +128 -0
  21. coding_assistant/cli/repl.py +381 -0
  22. coding_assistant/cli/theme.py +90 -0
  23. coding_assistant/codebase/__init__.py +1 -0
  24. coding_assistant/codebase/crawler.py +93 -0
  25. coding_assistant/codebase/parser.py +266 -0
  26. coding_assistant/config/__init__.py +25 -0
  27. coding_assistant/config/config_manager.py +615 -0
  28. coding_assistant/config/settings.py +82 -0
  29. coding_assistant/context/__init__.py +19 -0
  30. coding_assistant/context/chunker.py +443 -0
  31. coding_assistant/context/enhanced_retriever.py +322 -0
  32. coding_assistant/context/hybrid_search.py +311 -0
  33. coding_assistant/context/ranker.py +355 -0
  34. coding_assistant/context/retriever.py +119 -0
  35. coding_assistant/context/window.py +362 -0
  36. coding_assistant/documentation/__init__.py +23 -0
  37. coding_assistant/documentation/agents/__init__.py +27 -0
  38. coding_assistant/documentation/agents/coordinator.py +510 -0
  39. coding_assistant/documentation/agents/module_documenter.py +111 -0
  40. coding_assistant/documentation/agents/synthesizer.py +139 -0
  41. coding_assistant/documentation/agents/task_delegator.py +100 -0
  42. coding_assistant/documentation/decomposition/__init__.py +21 -0
  43. coding_assistant/documentation/decomposition/context_preserver.py +477 -0
  44. coding_assistant/documentation/decomposition/module_detector.py +302 -0
  45. coding_assistant/documentation/decomposition/partitioner.py +621 -0
  46. coding_assistant/documentation/generators/__init__.py +14 -0
  47. coding_assistant/documentation/generators/dataflow_generator.py +440 -0
  48. coding_assistant/documentation/generators/diagram_generator.py +511 -0
  49. coding_assistant/documentation/graph/__init__.py +13 -0
  50. coding_assistant/documentation/graph/dependency_builder.py +468 -0
  51. coding_assistant/documentation/graph/module_analyzer.py +475 -0
  52. coding_assistant/documentation/writers/__init__.py +11 -0
  53. coding_assistant/documentation/writers/markdown_writer.py +322 -0
  54. coding_assistant/embeddings/__init__.py +0 -0
  55. coding_assistant/embeddings/generator.py +89 -0
  56. coding_assistant/embeddings/store.py +187 -0
  57. coding_assistant/exceptions/__init__.py +50 -0
  58. coding_assistant/exceptions/base.py +110 -0
  59. coding_assistant/exceptions/llm.py +249 -0
  60. coding_assistant/exceptions/recovery.py +263 -0
  61. coding_assistant/exceptions/storage.py +213 -0
  62. coding_assistant/exceptions/validation.py +230 -0
  63. coding_assistant/llm/__init__.py +1 -0
  64. coding_assistant/llm/client.py +277 -0
  65. coding_assistant/llm/gemini_client.py +181 -0
  66. coding_assistant/llm/groq_client.py +160 -0
  67. coding_assistant/llm/prompts.py +98 -0
  68. coding_assistant/llm/together_client.py +160 -0
  69. coding_assistant/operations/__init__.py +13 -0
  70. coding_assistant/operations/differ.py +369 -0
  71. coding_assistant/operations/generator.py +347 -0
  72. coding_assistant/operations/linter.py +430 -0
  73. coding_assistant/operations/validator.py +406 -0
  74. coding_assistant/storage/__init__.py +9 -0
  75. coding_assistant/storage/database.py +363 -0
  76. coding_assistant/storage/session.py +231 -0
  77. coding_assistant/utils/__init__.py +31 -0
  78. coding_assistant/utils/cache.py +477 -0
  79. coding_assistant/utils/hardware.py +132 -0
  80. coding_assistant/utils/keystore.py +206 -0
  81. coding_assistant/utils/logger.py +32 -0
  82. coding_assistant/utils/progress.py +311 -0
  83. coding_assistant/validation/__init__.py +13 -0
  84. coding_assistant/validation/files.py +305 -0
  85. coding_assistant/validation/inputs.py +335 -0
  86. coding_assistant/validation/params.py +280 -0
  87. coding_assistant/validation/sanitizers.py +243 -0
  88. coding_assistant/vcs/__init__.py +5 -0
  89. coding_assistant/vcs/git.py +269 -0
@@ -0,0 +1,269 @@
1
+ """Git integration for context retrieval."""
2
+
3
+ from typing import List, Dict, Optional
4
+ from pathlib import Path
5
+ from datetime import datetime, timedelta
6
+ import git
7
+
8
+
9
+ class GitIntegration:
10
+ """Git repository integration for enhanced context."""
11
+
12
+ def __init__(self, repo_path: str):
13
+ """Initialize Git integration.
14
+
15
+ Args:
16
+ repo_path: Path to git repository
17
+
18
+ Raises:
19
+ git.exc.InvalidGitRepositoryError: If not a git repository
20
+ """
21
+ self.repo_path = Path(repo_path)
22
+ try:
23
+ self.repo = git.Repo(repo_path, search_parent_directories=True)
24
+ except git.exc.InvalidGitRepositoryError:
25
+ self.repo = None
26
+
27
+ def is_git_repo(self) -> bool:
28
+ """Check if path is a git repository.
29
+
30
+ Returns:
31
+ True if git repo, False otherwise
32
+ """
33
+ return self.repo is not None
34
+
35
+ def get_recent_changes(self, days: int = 7, max_files: int = 20) -> List[Dict]:
36
+ """Get files changed in last N days.
37
+
38
+ Args:
39
+ days: Number of days to look back
40
+ max_files: Maximum number of files to return
41
+
42
+ Returns:
43
+ List of dicts with file change information
44
+ """
45
+ if not self.is_git_repo():
46
+ return []
47
+
48
+ since = datetime.now() - timedelta(days=days)
49
+
50
+ try:
51
+ # Get commits since date
52
+ commits = list(self.repo.iter_commits(
53
+ all=True,
54
+ since=since.isoformat()
55
+ ))
56
+
57
+ # Track file changes
58
+ file_changes = {}
59
+
60
+ for commit in commits:
61
+ # Skip merge commits
62
+ if len(commit.parents) > 1:
63
+ continue
64
+
65
+ for file_path in commit.stats.files.keys():
66
+ if file_path not in file_changes:
67
+ file_changes[file_path] = {
68
+ 'file': file_path,
69
+ 'commits': [],
70
+ 'total_changes': 0,
71
+ 'last_modified': commit.committed_datetime,
72
+ 'last_author': commit.author.name
73
+ }
74
+
75
+ stats = commit.stats.files[file_path]
76
+ file_changes[file_path]['commits'].append({
77
+ 'sha': commit.hexsha[:7],
78
+ 'message': commit.message.split('\n')[0], # First line only
79
+ 'author': commit.author.name,
80
+ 'date': commit.committed_datetime.isoformat(),
81
+ 'insertions': stats.get('insertions', 0),
82
+ 'deletions': stats.get('deletions', 0),
83
+ })
84
+ file_changes[file_path]['total_changes'] += (
85
+ stats.get('insertions', 0) + stats.get('deletions', 0)
86
+ )
87
+
88
+ # Sort by total changes (most active files first)
89
+ sorted_files = sorted(
90
+ file_changes.values(),
91
+ key=lambda x: x['total_changes'],
92
+ reverse=True
93
+ )
94
+
95
+ return sorted_files[:max_files]
96
+
97
+ except Exception as e:
98
+ # Git operations can fail for various reasons
99
+ return []
100
+
101
+ def get_uncommitted_changes(self) -> List[str]:
102
+ """Get list of files with uncommitted changes.
103
+
104
+ Returns:
105
+ List of file paths
106
+ """
107
+ if not self.is_git_repo():
108
+ return []
109
+
110
+ try:
111
+ # Get both staged and unstaged changes
112
+ changed_files = set()
113
+
114
+ # Unstaged changes
115
+ changed_files.update(item.a_path for item in self.repo.index.diff(None))
116
+
117
+ # Staged changes
118
+ changed_files.update(item.a_path for item in self.repo.index.diff('HEAD'))
119
+
120
+ # Untracked files
121
+ changed_files.update(self.repo.untracked_files)
122
+
123
+ return list(changed_files)
124
+
125
+ except Exception:
126
+ return []
127
+
128
+ def get_file_history(self, file_path: str, limit: int = 5) -> List[Dict]:
129
+ """Get commit history for a specific file.
130
+
131
+ Args:
132
+ file_path: Path to file (relative to repo root)
133
+ limit: Maximum number of commits
134
+
135
+ Returns:
136
+ List of commit dicts
137
+ """
138
+ if not self.is_git_repo():
139
+ return []
140
+
141
+ try:
142
+ commits = list(self.repo.iter_commits(paths=file_path, max_count=limit))
143
+
144
+ history = []
145
+ for commit in commits:
146
+ history.append({
147
+ 'sha': commit.hexsha[:7],
148
+ 'message': commit.message.split('\n')[0],
149
+ 'author': commit.author.name,
150
+ 'date': commit.committed_datetime.isoformat(),
151
+ 'email': commit.author.email
152
+ })
153
+
154
+ return history
155
+
156
+ except Exception:
157
+ return []
158
+
159
+ def get_current_branch(self) -> Optional[str]:
160
+ """Get current branch name.
161
+
162
+ Returns:
163
+ Branch name or None
164
+ """
165
+ if not self.is_git_repo():
166
+ return None
167
+
168
+ try:
169
+ return self.repo.active_branch.name
170
+ except Exception:
171
+ return None
172
+
173
+ def get_diff(self, file_path: Optional[str] = None) -> str:
174
+ """Get diff for uncommitted changes.
175
+
176
+ Args:
177
+ file_path: Optional specific file path
178
+
179
+ Returns:
180
+ Diff string
181
+ """
182
+ if not self.is_git_repo():
183
+ return ""
184
+
185
+ try:
186
+ if file_path:
187
+ # Get diff for specific file
188
+ diff = self.repo.git.diff(file_path)
189
+ else:
190
+ # Get all diffs
191
+ diff = self.repo.git.diff()
192
+
193
+ return diff
194
+
195
+ except Exception:
196
+ return ""
197
+
198
+ def get_recent_commit_messages(self, count: int = 10) -> List[str]:
199
+ """Get recent commit messages for context.
200
+
201
+ Args:
202
+ count: Number of commits
203
+
204
+ Returns:
205
+ List of commit messages
206
+ """
207
+ if not self.is_git_repo():
208
+ return []
209
+
210
+ try:
211
+ commits = list(self.repo.iter_commits(max_count=count))
212
+ return [commit.message.split('\n')[0] for commit in commits]
213
+ except Exception:
214
+ return []
215
+
216
+ def get_contributors(self, limit: int = 10) -> List[Dict]:
217
+ """Get repository contributors.
218
+
219
+ Args:
220
+ limit: Maximum number of contributors
221
+
222
+ Returns:
223
+ List of contributor dicts
224
+ """
225
+ if not self.is_git_repo():
226
+ return []
227
+
228
+ try:
229
+ # Count commits per author
230
+ author_counts = {}
231
+ for commit in self.repo.iter_commits():
232
+ author = commit.author.name
233
+ if author not in author_counts:
234
+ author_counts[author] = {
235
+ 'name': author,
236
+ 'email': commit.author.email,
237
+ 'commits': 0,
238
+ 'last_commit': commit.committed_datetime
239
+ }
240
+ author_counts[author]['commits'] += 1
241
+
242
+ # Sort by commit count
243
+ sorted_contributors = sorted(
244
+ author_counts.values(),
245
+ key=lambda x: x['commits'],
246
+ reverse=True
247
+ )
248
+
249
+ return sorted_contributors[:limit]
250
+
251
+ except Exception:
252
+ return []
253
+
254
+ def is_ignored(self, file_path: str) -> bool:
255
+ """Check if file is in .gitignore.
256
+
257
+ Args:
258
+ file_path: Path to file
259
+
260
+ Returns:
261
+ True if ignored, False otherwise
262
+ """
263
+ if not self.is_git_repo():
264
+ return False
265
+
266
+ try:
267
+ return file_path in self.repo.ignored(file_path)
268
+ except Exception:
269
+ return False