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,280 @@
1
+ """Parameter validation for CLI commands."""
2
+
3
+ from typing import Optional, List, Any
4
+
5
+ from coding_assistant.exceptions.validation import (
6
+ ValidationError,
7
+ InvalidParameterError
8
+ )
9
+
10
+
11
+ class ParameterValidator:
12
+ """
13
+ Validates CLI command parameters.
14
+
15
+ Ensures parameters are within acceptable ranges and formats.
16
+ """
17
+
18
+ @staticmethod
19
+ def validate_top_k(value: int) -> int:
20
+ """
21
+ Validate top_k parameter for search results.
22
+
23
+ Args:
24
+ value: Number of results to return
25
+
26
+ Returns:
27
+ Validated value
28
+
29
+ Raises:
30
+ InvalidParameterError: If value is invalid
31
+ """
32
+ if not isinstance(value, int):
33
+ raise InvalidParameterError(
34
+ "top_k",
35
+ value,
36
+ "positive integer"
37
+ )
38
+
39
+ if value < 1:
40
+ raise InvalidParameterError(
41
+ "top_k",
42
+ value,
43
+ "positive integer (>= 1)"
44
+ )
45
+
46
+ if value > 100:
47
+ raise InvalidParameterError(
48
+ "top_k",
49
+ value,
50
+ "positive integer (<= 100)",
51
+ suggestion="Use a smaller value (1-100) for better performance"
52
+ )
53
+
54
+ return value
55
+
56
+ @staticmethod
57
+ def validate_max_files(value: int) -> int:
58
+ """
59
+ Validate max_files parameter for indexing.
60
+
61
+ Args:
62
+ value: Maximum number of files to index
63
+
64
+ Returns:
65
+ Validated value
66
+
67
+ Raises:
68
+ InvalidParameterError: If value is invalid
69
+ """
70
+ if not isinstance(value, int):
71
+ raise InvalidParameterError(
72
+ "max_files",
73
+ value,
74
+ "positive integer"
75
+ )
76
+
77
+ if value < 1:
78
+ raise InvalidParameterError(
79
+ "max_files",
80
+ value,
81
+ "positive integer (>= 1)"
82
+ )
83
+
84
+ if value > 10000:
85
+ raise InvalidParameterError(
86
+ "max_files",
87
+ value,
88
+ "positive integer (<= 10000)",
89
+ suggestion="Use a smaller value for better performance. 100-1000 is usually sufficient."
90
+ )
91
+
92
+ return value
93
+
94
+ @staticmethod
95
+ def validate_temperature(value: float) -> float:
96
+ """
97
+ Validate LLM temperature parameter.
98
+
99
+ Args:
100
+ value: Temperature value
101
+
102
+ Returns:
103
+ Validated value
104
+
105
+ Raises:
106
+ InvalidParameterError: If value is invalid
107
+ """
108
+ if not isinstance(value, (int, float)):
109
+ raise InvalidParameterError(
110
+ "temperature",
111
+ value,
112
+ "number between 0.0 and 2.0"
113
+ )
114
+
115
+ value = float(value)
116
+
117
+ if value < 0.0 or value > 2.0:
118
+ raise InvalidParameterError(
119
+ "temperature",
120
+ value,
121
+ "number between 0.0 and 2.0",
122
+ suggestion="Use 0.0 for deterministic, 0.7 for balanced, 1.0+ for creative"
123
+ )
124
+
125
+ return value
126
+
127
+ @staticmethod
128
+ def validate_hybrid_alpha(value: float) -> float:
129
+ """
130
+ Validate hybrid search alpha parameter.
131
+
132
+ Args:
133
+ value: Alpha value (weight for vector search)
134
+
135
+ Returns:
136
+ Validated value
137
+
138
+ Raises:
139
+ InvalidParameterError: If value is invalid
140
+ """
141
+ if not isinstance(value, (int, float)):
142
+ raise InvalidParameterError(
143
+ "hybrid_alpha",
144
+ value,
145
+ "number between 0.0 and 1.0"
146
+ )
147
+
148
+ value = float(value)
149
+
150
+ if value < 0.0 or value > 1.0:
151
+ raise InvalidParameterError(
152
+ "hybrid_alpha",
153
+ value,
154
+ "number between 0.0 and 1.0",
155
+ suggestion="Use 0.0 for keyword-only, 0.5 for balanced, 1.0 for vector-only"
156
+ )
157
+
158
+ return value
159
+
160
+ @staticmethod
161
+ def validate_provider(value: str, allowed_providers: Optional[List[str]] = None) -> str:
162
+ """
163
+ Validate LLM provider parameter.
164
+
165
+ Args:
166
+ value: Provider name
167
+ allowed_providers: List of allowed providers
168
+
169
+ Returns:
170
+ Validated provider name
171
+
172
+ Raises:
173
+ InvalidParameterError: If provider is invalid
174
+ """
175
+ if allowed_providers is None:
176
+ allowed_providers = ['ollama', 'openai', 'claude', 'mock']
177
+
178
+ value_lower = value.lower()
179
+
180
+ if value_lower not in allowed_providers:
181
+ raise InvalidParameterError(
182
+ "provider",
183
+ value,
184
+ f"one of: {', '.join(allowed_providers)}",
185
+ suggestion=f"Choose from: {', '.join(allowed_providers)}"
186
+ )
187
+
188
+ return value_lower
189
+
190
+ @staticmethod
191
+ def validate_file_type(value: str) -> str:
192
+ """
193
+ Validate file type filter parameter.
194
+
195
+ Args:
196
+ value: File type (extension or language)
197
+
198
+ Returns:
199
+ Validated file type
200
+
201
+ Raises:
202
+ InvalidParameterError: If file type is invalid
203
+ """
204
+ common_types = [
205
+ 'python', 'py',
206
+ 'javascript', 'js', 'jsx',
207
+ 'typescript', 'ts', 'tsx',
208
+ 'java',
209
+ 'cpp', 'c', 'h',
210
+ 'csharp', 'cs',
211
+ 'go',
212
+ 'rust', 'rs',
213
+ 'ruby', 'rb',
214
+ 'php',
215
+ 'swift',
216
+ 'kotlin', 'kt'
217
+ ]
218
+
219
+ value_lower = value.lower()
220
+
221
+ # Map common names to extensions
222
+ type_map = {
223
+ 'python': 'py',
224
+ 'javascript': 'js',
225
+ 'typescript': 'ts',
226
+ 'csharp': 'cs',
227
+ 'rust': 'rs',
228
+ 'ruby': 'rb',
229
+ 'kotlin': 'kt'
230
+ }
231
+
232
+ if value_lower in type_map:
233
+ return type_map[value_lower]
234
+
235
+ if value_lower in common_types:
236
+ return value_lower
237
+
238
+ # Allow any extension starting with .
239
+ if value.startswith('.'):
240
+ return value[1:] # Remove dot
241
+
242
+ return value_lower
243
+
244
+ @staticmethod
245
+ def validate_context_size(value: int) -> int:
246
+ """
247
+ Validate context size parameter.
248
+
249
+ Args:
250
+ value: Number of context chunks
251
+
252
+ Returns:
253
+ Validated value
254
+
255
+ Raises:
256
+ InvalidParameterError: If value is invalid
257
+ """
258
+ if not isinstance(value, int):
259
+ raise InvalidParameterError(
260
+ "context_size",
261
+ value,
262
+ "positive integer"
263
+ )
264
+
265
+ if value < 1:
266
+ raise InvalidParameterError(
267
+ "context_size",
268
+ value,
269
+ "positive integer (>= 1)"
270
+ )
271
+
272
+ if value > 50:
273
+ raise InvalidParameterError(
274
+ "context_size",
275
+ value,
276
+ "positive integer (<= 50)",
277
+ suggestion="Use a smaller value (1-20) for better performance"
278
+ )
279
+
280
+ return value
@@ -0,0 +1,243 @@
1
+ """Input sanitization utilities."""
2
+
3
+ import re
4
+ import html
5
+ from typing import Optional
6
+
7
+
8
+ class InputSanitizer:
9
+ """
10
+ Sanitizes user inputs to prevent injection attacks.
11
+
12
+ Provides methods for cleaning and escaping potentially dangerous input.
13
+ """
14
+
15
+ @staticmethod
16
+ def sanitize_query(query: str, max_length: int = 500) -> str:
17
+ """
18
+ Sanitize search query.
19
+
20
+ Args:
21
+ query: User query
22
+ max_length: Maximum query length
23
+
24
+ Returns:
25
+ Sanitized query
26
+ """
27
+ if not query:
28
+ return ""
29
+
30
+ # Strip whitespace
31
+ query = query.strip()
32
+
33
+ # Remove null bytes
34
+ query = query.replace('\x00', '')
35
+
36
+ # Remove control characters except newline and tab
37
+ query = ''.join(char for char in query if ord(char) >= 32 or char in '\n\t')
38
+
39
+ # Normalize whitespace
40
+ query = re.sub(r'\s+', ' ', query)
41
+
42
+ # Truncate if needed
43
+ if len(query) > max_length:
44
+ query = query[:max_length]
45
+
46
+ return query
47
+
48
+ @staticmethod
49
+ def sanitize_file_path(path: str) -> str:
50
+ """
51
+ Sanitize file path.
52
+
53
+ Args:
54
+ path: File path
55
+
56
+ Returns:
57
+ Sanitized path
58
+ """
59
+ if not path:
60
+ return ""
61
+
62
+ # Remove null bytes
63
+ path = path.replace('\x00', '')
64
+
65
+ # Remove dangerous characters
66
+ dangerous_chars = ['<', '>', '|', '&', ';', '`', '$', '(', ')']
67
+ for char in dangerous_chars:
68
+ path = path.replace(char, '')
69
+
70
+ # Normalize path separators
71
+ path = path.replace('\\', '/')
72
+
73
+ # Remove consecutive slashes
74
+ path = re.sub(r'/+', '/', path)
75
+
76
+ return path.strip()
77
+
78
+ @staticmethod
79
+ def sanitize_command(command: str) -> str:
80
+ """
81
+ Sanitize command string.
82
+
83
+ Args:
84
+ command: Command string
85
+
86
+ Returns:
87
+ Sanitized command
88
+ """
89
+ if not command:
90
+ return ""
91
+
92
+ # Remove null bytes
93
+ command = command.replace('\x00', '')
94
+
95
+ # Remove dangerous shell characters
96
+ dangerous_chars = ['|', '&', ';', '`', '$', '(', ')', '<', '>', '\n']
97
+ for char in dangerous_chars:
98
+ command = command.replace(char, '')
99
+
100
+ return command.strip()
101
+
102
+ @staticmethod
103
+ def escape_html(text: str) -> str:
104
+ """
105
+ Escape HTML special characters.
106
+
107
+ Args:
108
+ text: Text to escape
109
+
110
+ Returns:
111
+ HTML-escaped text
112
+ """
113
+ if not text:
114
+ return ""
115
+
116
+ return html.escape(text)
117
+
118
+ @staticmethod
119
+ def sanitize_code(code: str, language: Optional[str] = None) -> str:
120
+ """
121
+ Sanitize code input.
122
+
123
+ Args:
124
+ code: Code string
125
+ language: Programming language (optional)
126
+
127
+ Returns:
128
+ Sanitized code
129
+ """
130
+ if not code:
131
+ return ""
132
+
133
+ # Remove null bytes
134
+ code = code.replace('\x00', '')
135
+
136
+ # Remove BOM if present
137
+ if code.startswith('\ufeff'):
138
+ code = code[1:]
139
+
140
+ # Normalize line endings
141
+ code = code.replace('\r\n', '\n')
142
+ code = code.replace('\r', '\n')
143
+
144
+ return code
145
+
146
+ @staticmethod
147
+ def sanitize_identifier(identifier: str) -> str:
148
+ """
149
+ Sanitize variable/function identifier.
150
+
151
+ Args:
152
+ identifier: Identifier name
153
+
154
+ Returns:
155
+ Sanitized identifier (only alphanumeric and underscore)
156
+ """
157
+ if not identifier:
158
+ return ""
159
+
160
+ # Keep only alphanumeric and underscore
161
+ identifier = re.sub(r'[^a-zA-Z0-9_]', '', identifier)
162
+
163
+ # Ensure doesn't start with number
164
+ if identifier and identifier[0].isdigit():
165
+ identifier = '_' + identifier
166
+
167
+ return identifier
168
+
169
+ @staticmethod
170
+ def sanitize_string(
171
+ value: str,
172
+ max_length: Optional[int] = None,
173
+ allowed_chars: Optional[str] = None
174
+ ) -> str:
175
+ """
176
+ General string sanitization.
177
+
178
+ Args:
179
+ value: String to sanitize
180
+ max_length: Maximum length
181
+ allowed_chars: Regex pattern of allowed characters
182
+
183
+ Returns:
184
+ Sanitized string
185
+ """
186
+ if not value:
187
+ return ""
188
+
189
+ # Remove null bytes
190
+ value = value.replace('\x00', '')
191
+
192
+ # Filter by allowed characters
193
+ if allowed_chars:
194
+ value = ''.join(char for char in value if re.match(allowed_chars, char))
195
+
196
+ # Strip whitespace
197
+ value = value.strip()
198
+
199
+ # Truncate if needed
200
+ if max_length and len(value) > max_length:
201
+ value = value[:max_length]
202
+
203
+ return value
204
+
205
+ @staticmethod
206
+ def remove_ansi_codes(text: str) -> str:
207
+ """
208
+ Remove ANSI color codes from text.
209
+
210
+ Args:
211
+ text: Text with potential ANSI codes
212
+
213
+ Returns:
214
+ Text without ANSI codes
215
+ """
216
+ if not text:
217
+ return ""
218
+
219
+ # Remove ANSI escape sequences
220
+ ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
221
+ return ansi_escape.sub('', text)
222
+
223
+ @staticmethod
224
+ def truncate_string(
225
+ text: str,
226
+ max_length: int,
227
+ suffix: str = "..."
228
+ ) -> str:
229
+ """
230
+ Truncate string to maximum length.
231
+
232
+ Args:
233
+ text: Text to truncate
234
+ max_length: Maximum length
235
+ suffix: Suffix to add when truncated
236
+
237
+ Returns:
238
+ Truncated string
239
+ """
240
+ if not text or len(text) <= max_length:
241
+ return text
242
+
243
+ return text[:max_length - len(suffix)] + suffix
@@ -0,0 +1,5 @@
1
+ """Version control system integration."""
2
+
3
+ from coding_assistant.vcs.git import GitIntegration
4
+
5
+ __all__ = ['GitIntegration']