acra-cli 0.1.1__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 (169) hide show
  1. acra/__init__.py +7 -0
  2. acra/__main__.py +4 -0
  3. acra/agents/__init__.py +0 -0
  4. acra/agents/__pycache__/__init__.cpython-312.pyc +0 -0
  5. acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
  6. acra/agents/__pycache__/llm_utils.cpython-312.pyc +0 -0
  7. acra/agents/coder/__init__.py +0 -0
  8. acra/agents/coder/__pycache__/__init__.cpython-312.pyc +0 -0
  9. acra/agents/coder/__pycache__/coder_agent.cpython-312.pyc +0 -0
  10. acra/agents/coder/__pycache__/coder_chain.cpython-312.pyc +0 -0
  11. acra/agents/coder/coder_agent.py +235 -0
  12. acra/agents/coder/coder_chain.py +204 -0
  13. acra/agents/critic/__init__.py +0 -0
  14. acra/agents/critic/__pycache__/__init__.cpython-312.pyc +0 -0
  15. acra/agents/critic/__pycache__/critic_agent.cpython-312.pyc +0 -0
  16. acra/agents/critic/__pycache__/critic_chain.cpython-312.pyc +0 -0
  17. acra/agents/critic/critic_agent.py +199 -0
  18. acra/agents/critic/critic_chain.py +268 -0
  19. acra/agents/executor/__init__.py +0 -0
  20. acra/agents/executor/__pycache__/__init__.cpython-312.pyc +0 -0
  21. acra/agents/executor/__pycache__/docker_runner.cpython-312.pyc +0 -0
  22. acra/agents/executor/__pycache__/executor_agent.cpython-312.pyc +0 -0
  23. acra/agents/executor/__pycache__/sandbox_runner.cpython-312.pyc +0 -0
  24. acra/agents/executor/docker_runner.py +114 -0
  25. acra/agents/executor/executor_agent.py +142 -0
  26. acra/agents/executor/sandbox_runner.py +144 -0
  27. acra/agents/human/__init__.py +0 -0
  28. acra/agents/human/__pycache__/__init__.cpython-312.pyc +0 -0
  29. acra/agents/human/__pycache__/human_node.cpython-312.pyc +0 -0
  30. acra/agents/human/human_node.py +37 -0
  31. acra/agents/llm.py +263 -0
  32. acra/agents/llm_utils.py +161 -0
  33. acra/agents/memory/__init__.py +0 -0
  34. acra/agents/memory/__pycache__/__init__.cpython-312.pyc +0 -0
  35. acra/agents/memory/__pycache__/memory_agent.cpython-312.pyc +0 -0
  36. acra/agents/memory/__pycache__/memory_manager.cpython-312.pyc +0 -0
  37. acra/agents/memory/__pycache__/retrieval.cpython-312.pyc +0 -0
  38. acra/agents/memory/memory_agent.py +239 -0
  39. acra/agents/memory/memory_manager.py +273 -0
  40. acra/agents/memory/retrieval.py +355 -0
  41. acra/agents/planner/__init__.py +0 -0
  42. acra/agents/planner/__pycache__/__init__.cpython-312.pyc +0 -0
  43. acra/agents/planner/__pycache__/planner_agent.cpython-312.pyc +0 -0
  44. acra/agents/planner/__pycache__/planner_chain.cpython-312.pyc +0 -0
  45. acra/agents/planner/planner_agent.py +97 -0
  46. acra/agents/planner/planner_chain.py +160 -0
  47. acra/agents/researcher/__init__.py +1 -0
  48. acra/agents/researcher/__pycache__/__init__.cpython-312.pyc +0 -0
  49. acra/agents/researcher/__pycache__/researcher_agent.cpython-312.pyc +0 -0
  50. acra/agents/researcher/__pycache__/researcher_chain.cpython-312.pyc +0 -0
  51. acra/agents/researcher/researcher_agent.py +231 -0
  52. acra/agents/researcher/researcher_chain.py +193 -0
  53. acra/brain/__init__.py +6 -0
  54. acra/brain/model_registry.py +19 -0
  55. acra/brain/provider_manager.py +21 -0
  56. acra/cli.py +52 -0
  57. acra/commands/__init__.py +29 -0
  58. acra/commands/ask.py +49 -0
  59. acra/commands/brain.py +58 -0
  60. acra/commands/config.py +25 -0
  61. acra/commands/context.py +30 -0
  62. acra/commands/graph.py +18 -0
  63. acra/commands/keys.py +40 -0
  64. acra/commands/logs.py +13 -0
  65. acra/commands/memory.py +27 -0
  66. acra/commands/plugin.py +18 -0
  67. acra/commands/research.py +65 -0
  68. acra/commands/serve.py +13 -0
  69. acra/commands/session.py +20 -0
  70. acra/commands/utils.py +12 -0
  71. acra/config/__init__.py +36 -0
  72. acra/config/__pycache__/__init__.cpython-312.pyc +0 -0
  73. acra/config/__pycache__/defaults.cpython-312.pyc +0 -0
  74. acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
  75. acra/config/defaults.py +88 -0
  76. acra/config/profile_manager.py +87 -0
  77. acra/execution/__init__.py +1 -0
  78. acra/execution/logs/__init__.py +1 -0
  79. acra/execution/logs/error_logs/__init__.py +1 -0
  80. acra/execution/logs/execution_logs/__init__.py +1 -0
  81. acra/execution/sandbox/__init__.py +1 -0
  82. acra/execution/sandbox/docker_executor.py +209 -0
  83. acra/execution/sandbox/isolated_runner.py +148 -0
  84. acra/execution/sandbox/sandbox_manager.py +178 -0
  85. acra/graph/__init__.py +0 -0
  86. acra/graph/checkpoint.py +85 -0
  87. acra/graph/conditional_edges.py +103 -0
  88. acra/graph/edges.py +85 -0
  89. acra/graph/graph_visualizer.py +175 -0
  90. acra/graph/nodes.py +68 -0
  91. acra/graph/router.py +69 -0
  92. acra/graph/state.py +93 -0
  93. acra/graph/workflow.py +117 -0
  94. acra/memory/__init__.py +1 -0
  95. acra/memory/checkpoints/__init__.py +1 -0
  96. acra/memory/checkpoints/data/__init__.py +1 -0
  97. acra/memory/checkpoints/postgres_checkpoint.py +247 -0
  98. acra/memory/checkpoints/sqlite_checkpoint.py +309 -0
  99. acra/memory/chroma_db/__init__.py +1 -0
  100. acra/memory/conversation_memory/__init__.py +1 -0
  101. acra/memory/conversation_memory/long_term.py +229 -0
  102. acra/memory/conversation_memory/short_term.py +158 -0
  103. acra/memory/storage/__init__.py +1 -0
  104. acra/memory/vector_store/__init__.py +0 -0
  105. acra/memory/vector_store/chroma_store.py +260 -0
  106. acra/memory/vector_store/embeddings.py +43 -0
  107. acra/observability/__init__.py +1 -0
  108. acra/observability/langsmith_tracing.py +91 -0
  109. acra/observability/metrics.py +189 -0
  110. acra/observability/monitoring.py +162 -0
  111. acra/observability/token_tracking.py +131 -0
  112. acra/prompts/__init__.py +1 -0
  113. acra/schemas/__init__.py +0 -0
  114. acra/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
  115. acra/schemas/__pycache__/coder_schema.cpython-312.pyc +0 -0
  116. acra/schemas/__pycache__/critic_schema.cpython-312.pyc +0 -0
  117. acra/schemas/__pycache__/execution_schema.cpython-312.pyc +0 -0
  118. acra/schemas/__pycache__/planner_schema.cpython-312.pyc +0 -0
  119. acra/schemas/__pycache__/researcher_schema.cpython-312.pyc +0 -0
  120. acra/schemas/__pycache__/shared_schema.cpython-312.pyc +0 -0
  121. acra/schemas/coder_schema.py +257 -0
  122. acra/schemas/critic_schema.py +177 -0
  123. acra/schemas/execution_schema.py +74 -0
  124. acra/schemas/planner_schema.py +129 -0
  125. acra/schemas/researcher_schema.py +220 -0
  126. acra/schemas/shared_schema.py +329 -0
  127. acra/tools/__init__.py +1 -0
  128. acra/tools/code_tools/__init__.py +1 -0
  129. acra/tools/code_tools/file_reader.py +156 -0
  130. acra/tools/code_tools/file_writer.py +108 -0
  131. acra/tools/code_tools/python_repl.py +72 -0
  132. acra/tools/code_tools/terminal_runner.py +98 -0
  133. acra/tools/github_tools/__init__.py +1 -0
  134. acra/tools/github_tools/commit_generator.py +91 -0
  135. acra/tools/github_tools/github_search.py +113 -0
  136. acra/tools/github_tools/repo_analyzer.py +138 -0
  137. acra/tools/integration_test_helper.py +370 -0
  138. acra/tools/validation_tools/__init__.py +1 -0
  139. acra/tools/validation_tools/dependency_checker.py +101 -0
  140. acra/tools/validation_tools/environment_config_validator.py +353 -0
  141. acra/tools/validation_tools/http_validator.py +336 -0
  142. acra/tools/validation_tools/response_validator.py +407 -0
  143. acra/tools/validation_tools/security_checker.py +93 -0
  144. acra/tools/validation_tools/syntax_checker.py +75 -0
  145. acra/tools/validation_tools/web_framework_validator.py +382 -0
  146. acra/tools/web_tools/__init__.py +1 -0
  147. acra/tools/web_tools/arxiv_search.py +110 -0
  148. acra/tools/web_tools/serpapi_search.py +106 -0
  149. acra/tools/web_tools/tavily_search.py +96 -0
  150. acra/ui/__init__.py +8 -0
  151. acra/ui/__pycache__/__init__.cpython-312.pyc +0 -0
  152. acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
  153. acra/ui/__pycache__/components.cpython-312.pyc +0 -0
  154. acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
  155. acra/ui/__pycache__/theme.cpython-312.pyc +0 -0
  156. acra/ui/banner.py +40 -0
  157. acra/ui/components.py +32 -0
  158. acra/ui/shell.py +91 -0
  159. acra/ui/theme.py +28 -0
  160. acra/utils/__init__.py +21 -0
  161. acra/utils/context_manager.py +20 -0
  162. acra/utils/keyring_manager.py +83 -0
  163. acra/utils/output_formatter.py +18 -0
  164. acra/utils/session_manager.py +59 -0
  165. acra_cli-0.1.1.dist-info/METADATA +616 -0
  166. acra_cli-0.1.1.dist-info/RECORD +169 -0
  167. acra_cli-0.1.1.dist-info/WHEEL +5 -0
  168. acra_cli-0.1.1.dist-info/licenses/LICENSE +661 -0
  169. acra_cli-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,407 @@
1
+ import json
2
+ import re
3
+ from typing import Dict, Any, List, Optional, Union
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class APIContractRule:
9
+ """API contract validation rule"""
10
+ field: str
11
+ type: str # "string", "number", "integer", "boolean", "array", "object"
12
+ required: bool = True
13
+ min_value: Optional[Union[int, float]] = None
14
+ max_value: Optional[Union[int, float]] = None
15
+ min_length: Optional[int] = None
16
+ max_length: Optional[int] = None
17
+ pattern: Optional[str] = None # Regex pattern
18
+ enum: Optional[List[Any]] = None # Allowed values
19
+
20
+
21
+ class ResponseValidatorTool:
22
+ """
23
+ Response validation tool for HTTP responses.
24
+
25
+ Responsibilities:
26
+ - Validate response structure
27
+ - Check response types
28
+ - Verify response codes
29
+ - Validate API contracts
30
+ """
31
+
32
+ @staticmethod
33
+ def validate_response_structure(
34
+ response_data: Dict,
35
+ schema: Dict
36
+ ) -> Dict:
37
+ """
38
+ Validate response against a schema.
39
+
40
+ Args:
41
+ response_data: Response data to validate
42
+ schema: JSON schema
43
+
44
+ Returns:
45
+ Validation result with errors
46
+ """
47
+ errors = []
48
+ warnings = []
49
+
50
+ # Check required fields
51
+ required_fields = schema.get("required", [])
52
+ for field in required_fields:
53
+ if field not in response_data:
54
+ errors.append(f"Missing required field: {field}")
55
+
56
+ # Check field types
57
+ properties = schema.get("properties", {})
58
+ for field, field_schema in properties.items():
59
+ if field not in response_data:
60
+ continue
61
+
62
+ field_value = response_data[field]
63
+ expected_type = field_schema.get("type", "string")
64
+
65
+ # Type validation
66
+ type_valid = ResponseValidatorTool._validate_type(
67
+ field_value,
68
+ expected_type
69
+ )
70
+
71
+ if not type_valid:
72
+ errors.append(
73
+ f"Field '{field}' has incorrect type. "
74
+ f"Expected {expected_type}, got {type(field_value).__name__}"
75
+ )
76
+
77
+ # Min/max validation for numbers
78
+ if expected_type in ["number", "integer"]:
79
+ if "minimum" in field_schema:
80
+ if field_value < field_schema["minimum"]:
81
+ errors.append(
82
+ f"Field '{field}' is below minimum: {field_schema['minimum']}"
83
+ )
84
+
85
+ if "maximum" in field_schema:
86
+ if field_value > field_schema["maximum"]:
87
+ errors.append(
88
+ f"Field '{field}' is above maximum: {field_schema['maximum']}"
89
+ )
90
+
91
+ # String length validation
92
+ if expected_type == "string":
93
+ if "minLength" in field_schema:
94
+ if len(field_value) < field_schema["minLength"]:
95
+ errors.append(
96
+ f"Field '{field}' is shorter than minimum: {field_schema['minLength']}"
97
+ )
98
+
99
+ if "maxLength" in field_schema:
100
+ if len(field_value) > field_schema["maxLength"]:
101
+ errors.append(
102
+ f"Field '{field}' is longer than maximum: {field_schema['maxLength']}"
103
+ )
104
+
105
+ # Regex pattern validation
106
+ if "pattern" in field_schema:
107
+ if not re.match(field_schema["pattern"], field_value):
108
+ errors.append(
109
+ f"Field '{field}' does not match pattern: {field_schema['pattern']}"
110
+ )
111
+
112
+ # Enum validation
113
+ if "enum" in field_schema:
114
+ if field_value not in field_schema["enum"]:
115
+ errors.append(
116
+ f"Field '{field}' value '{field_value}' not in allowed values: "
117
+ f"{field_schema['enum']}"
118
+ )
119
+
120
+ # Nested object validation
121
+ if expected_type == "object" and "properties" in field_schema:
122
+ nested_result = ResponseValidatorTool.validate_response_structure(
123
+ field_value,
124
+ field_schema
125
+ )
126
+ errors.extend(nested_result["errors"])
127
+
128
+ return {
129
+ "valid": len(errors) == 0,
130
+ "errors": errors,
131
+ "warnings": warnings,
132
+ "error_count": len(errors)
133
+ }
134
+
135
+ @staticmethod
136
+ def _validate_type(value: Any, expected_type: str) -> bool:
137
+ """
138
+ Validate value type.
139
+
140
+ Args:
141
+ value: Value to validate
142
+ expected_type: Expected type name
143
+
144
+ Returns:
145
+ True if type matches
146
+ """
147
+ type_map = {
148
+ "string": str,
149
+ "number": (int, float),
150
+ "integer": int,
151
+ "boolean": bool,
152
+ "array": list,
153
+ "object": dict,
154
+ "null": type(None)
155
+ }
156
+
157
+ if expected_type not in type_map:
158
+ return True
159
+
160
+ expected = type_map[expected_type]
161
+ return isinstance(value, expected)
162
+
163
+ @staticmethod
164
+ def validate_api_contract(
165
+ response_data: Dict,
166
+ contract_rules: List[APIContractRule]
167
+ ) -> Dict:
168
+ """
169
+ Validate response against API contract rules.
170
+
171
+ Args:
172
+ response_data: Response data
173
+ contract_rules: List of contract rules
174
+
175
+ Returns:
176
+ Validation result
177
+ """
178
+ violations = []
179
+
180
+ for rule in contract_rules:
181
+ if rule.field not in response_data:
182
+ if rule.required:
183
+ violations.append({
184
+ "field": rule.field,
185
+ "violation": "Missing required field",
186
+ "severity": "error"
187
+ })
188
+ continue
189
+
190
+ value = response_data[rule.field]
191
+
192
+ # Type check
193
+ if not ResponseValidatorTool._validate_type(value, rule.type):
194
+ violations.append({
195
+ "field": rule.field,
196
+ "violation": f"Type mismatch: expected {rule.type}, got {type(value).__name__}",
197
+ "severity": "error"
198
+ })
199
+ continue
200
+
201
+ # Value range checks
202
+ if rule.min_value is not None and isinstance(value, (int, float)):
203
+ if value < rule.min_value:
204
+ violations.append({
205
+ "field": rule.field,
206
+ "violation": f"Value below minimum: {rule.min_value}",
207
+ "severity": "error"
208
+ })
209
+
210
+ if rule.max_value is not None and isinstance(value, (int, float)):
211
+ if value > rule.max_value:
212
+ violations.append({
213
+ "field": rule.field,
214
+ "violation": f"Value above maximum: {rule.max_value}",
215
+ "severity": "error"
216
+ })
217
+
218
+ # String length checks
219
+ if rule.min_length is not None and isinstance(value, str):
220
+ if len(value) < rule.min_length:
221
+ violations.append({
222
+ "field": rule.field,
223
+ "violation": f"String shorter than minimum: {rule.min_length}",
224
+ "severity": "error"
225
+ })
226
+
227
+ if rule.max_length is not None and isinstance(value, str):
228
+ if len(value) > rule.max_length:
229
+ violations.append({
230
+ "field": rule.field,
231
+ "violation": f"String longer than maximum: {rule.max_length}",
232
+ "severity": "error"
233
+ })
234
+
235
+ # Pattern matching
236
+ if rule.pattern and isinstance(value, str):
237
+ if not re.match(rule.pattern, value):
238
+ violations.append({
239
+ "field": rule.field,
240
+ "violation": f"Value does not match pattern: {rule.pattern}",
241
+ "severity": "error"
242
+ })
243
+
244
+ # Enum validation
245
+ if rule.enum and value not in rule.enum:
246
+ violations.append({
247
+ "field": rule.field,
248
+ "violation": f"Value not in allowed list: {rule.enum}",
249
+ "severity": "error"
250
+ })
251
+
252
+ return {
253
+ "valid": len(violations) == 0,
254
+ "violations": violations,
255
+ "violation_count": len(violations)
256
+ }
257
+
258
+ @staticmethod
259
+ def extract_json_schema_from_response(
260
+ response_data: Union[Dict, List]
261
+ ) -> Dict:
262
+ """
263
+ Extract JSON schema from response data.
264
+
265
+ Args:
266
+ response_data: Response data
267
+
268
+ Returns:
269
+ Inferred JSON schema
270
+ """
271
+ if isinstance(response_data, dict):
272
+ properties = {}
273
+ required = []
274
+
275
+ for key, value in response_data.items():
276
+ prop_type = ResponseValidatorTool._infer_type(value)
277
+ properties[key] = {"type": prop_type}
278
+
279
+ # All top-level keys are considered required
280
+ required.append(key)
281
+
282
+ return {
283
+ "type": "object",
284
+ "properties": properties,
285
+ "required": required
286
+ }
287
+
288
+ elif isinstance(response_data, list):
289
+ if not response_data:
290
+ return {"type": "array", "items": {}}
291
+
292
+ # Infer from first element
293
+ first_type = ResponseValidatorTool._infer_type(response_data[0])
294
+ return {
295
+ "type": "array",
296
+ "items": {"type": first_type}
297
+ }
298
+
299
+ else:
300
+ return {"type": ResponseValidatorTool._infer_type(response_data)}
301
+
302
+ @staticmethod
303
+ def _infer_type(value: Any) -> str:
304
+ """
305
+ Infer JSON schema type from Python value.
306
+
307
+ Args:
308
+ value: Value to analyze
309
+
310
+ Returns:
311
+ JSON schema type
312
+ """
313
+ if isinstance(value, bool):
314
+ return "boolean"
315
+ elif isinstance(value, int):
316
+ return "integer"
317
+ elif isinstance(value, float):
318
+ return "number"
319
+ elif isinstance(value, str):
320
+ return "string"
321
+ elif isinstance(value, list):
322
+ return "array"
323
+ elif isinstance(value, dict):
324
+ return "object"
325
+ elif value is None:
326
+ return "null"
327
+ else:
328
+ return "string"
329
+
330
+ @staticmethod
331
+ def compare_responses(
332
+ response1: Dict,
333
+ response2: Dict
334
+ ) -> Dict:
335
+ """
336
+ Compare two responses for structural differences.
337
+
338
+ Args:
339
+ response1: First response
340
+ response2: Second response
341
+
342
+ Returns:
343
+ Comparison result with differences
344
+ """
345
+ differences = []
346
+
347
+ # Check for missing keys in response2
348
+ for key in response1.keys():
349
+ if key not in response2:
350
+ differences.append(f"Missing key in second response: {key}")
351
+
352
+ # Check for extra keys in response2
353
+ for key in response2.keys():
354
+ if key not in response1:
355
+ differences.append(f"Extra key in second response: {key}")
356
+
357
+ # Check for value differences
358
+ for key in set(response1.keys()) & set(response2.keys()):
359
+ if response1[key] != response2[key]:
360
+ differences.append(
361
+ f"Value difference for key '{key}': "
362
+ f"{response1[key]} vs {response2[key]}"
363
+ )
364
+
365
+ return {
366
+ "identical": len(differences) == 0,
367
+ "differences": differences,
368
+ "difference_count": len(differences)
369
+ }
370
+
371
+ @staticmethod
372
+ def validate_error_response(error_response: Dict) -> Dict:
373
+ """
374
+ Validate error response structure.
375
+
376
+ Args:
377
+ error_response: Error response data
378
+
379
+ Returns:
380
+ Validation result
381
+ """
382
+ issues = []
383
+ warnings = []
384
+
385
+ # Check for error code
386
+ if "error_code" not in error_response and "code" not in error_response:
387
+ issues.append("Error code not present in response")
388
+
389
+ # Check for error message
390
+ if "error_message" not in error_response and "message" not in error_response:
391
+ issues.append("Error message not present in response")
392
+
393
+ # Check for error details
394
+ if "error_details" not in error_response and "details" not in error_response:
395
+ warnings.append("Consider including error details in error response")
396
+
397
+ # Check for HTTP status code consistency
398
+ if "status" in error_response:
399
+ status = error_response["status"]
400
+ if not (400 <= status <= 599):
401
+ issues.append(f"Invalid HTTP status code: {status}")
402
+
403
+ return {
404
+ "valid": len(issues) == 0,
405
+ "issues": issues,
406
+ "warnings": warnings
407
+ }
@@ -0,0 +1,93 @@
1
+ import re
2
+ from typing import Dict, List
3
+
4
+
5
+ # security checker tool
6
+
7
+ class SecurityCheckerTool:
8
+ """
9
+ Security validation tool.
10
+
11
+ Responsibilities:
12
+ - detect dangerous code patterns
13
+ - identify unsafe execution
14
+ - support autonomous safety checks
15
+ """
16
+
17
+
18
+ # dangerous patterns
19
+
20
+ DANGEROUS_PATTERNS = {
21
+
22
+ "eval_usage": r"\beval\s*\(",
23
+
24
+ "exec_usage": r"\bexec\s*\(",
25
+
26
+ "os_system": r"\bos\.system\s*\(",
27
+
28
+ "subprocess_shell_true": (
29
+ r"shell\s*=\s*True"
30
+ ),
31
+
32
+ "pickle_usage": r"\bpickle\.load",
33
+
34
+ "hardcoded_password": (
35
+ r"password\s*=\s*[\"'].*[\"']"
36
+ ),
37
+
38
+ "hardcoded_api_key": (
39
+ r"api_key\s*=\s*[\"'].*[\"']"
40
+ ),
41
+
42
+ "unsafe_yaml_load": (
43
+ r"yaml\.load\s*\("
44
+ )
45
+ }
46
+
47
+
48
+ # check security
49
+
50
+ @classmethod
51
+ def check_security(
52
+ cls,
53
+ code: str
54
+ ) -> Dict:
55
+ """
56
+ Analyze code for security risks.
57
+ """
58
+
59
+ issues = []
60
+
61
+ for issue_name, pattern in (
62
+ cls.DANGEROUS_PATTERNS.items()
63
+ ):
64
+
65
+ matches = re.findall(
66
+ pattern,
67
+ code
68
+ )
69
+
70
+ if matches:
71
+
72
+ issues.append({
73
+
74
+ "issue": issue_name,
75
+
76
+ "matches_found": len(matches)
77
+ })
78
+
79
+ return {
80
+
81
+ "success": True,
82
+
83
+ "safe": len(issues) == 0,
84
+
85
+ "issues": issues
86
+ }
87
+
88
+
89
+ # global helper
90
+
91
+ security_checker = (
92
+ SecurityCheckerTool()
93
+ )
@@ -0,0 +1,75 @@
1
+ import ast
2
+ from typing import Dict
3
+
4
+
5
+ # syntax checker tool
6
+
7
+ class SyntaxCheckerTool:
8
+ """
9
+ Python syntax validation tool.
10
+
11
+ Responsibilities:
12
+ - validate Python syntax
13
+ - detect parsing failures
14
+ - support autonomous debugging
15
+ """
16
+
17
+
18
+ # check python syntax
19
+
20
+ @staticmethod
21
+ def check_syntax(
22
+ code: str
23
+ ) -> Dict:
24
+ """
25
+ Validate Python syntax.
26
+ """
27
+
28
+ try:
29
+
30
+ ast.parse(code)
31
+
32
+ return {
33
+
34
+ "success": True,
35
+
36
+ "valid": True,
37
+
38
+ "error": None
39
+ }
40
+
41
+ except SyntaxError as e:
42
+
43
+ return {
44
+
45
+ "success": False,
46
+
47
+ "valid": False,
48
+
49
+ "error": {
50
+
51
+ "message": str(e),
52
+
53
+ "line": e.lineno,
54
+
55
+ "offset": e.offset,
56
+
57
+ "text": e.text
58
+ }
59
+ }
60
+
61
+ except Exception as e:
62
+
63
+ return {
64
+
65
+ "success": False,
66
+
67
+ "valid": False,
68
+
69
+ "error": str(e)
70
+ }
71
+
72
+
73
+ # global helper
74
+
75
+ syntax_checker = SyntaxCheckerTool()