superqode 0.1.5__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 (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,135 @@
1
+ """
2
+ QIR Templates for different QE modes and scenarios.
3
+ """
4
+
5
+ from enum import Enum
6
+ from typing import Dict, List
7
+
8
+
9
+ class QRTemplate(Enum):
10
+ """Pre-defined QIR templates for different scenarios."""
11
+
12
+ QUICK_SCAN = "quick_scan"
13
+ DEEP_QE = "deep_qe"
14
+ SECURITY_AUDIT = "security_audit"
15
+ PERFORMANCE_REVIEW = "performance_review"
16
+ REGRESSION_CHECK = "regression_check"
17
+
18
+
19
+ # Template configurations
20
+ TEMPLATES: Dict[QRTemplate, Dict] = {
21
+ QRTemplate.QUICK_SCAN: {
22
+ "name": "Quick Scan Report",
23
+ "sections": ["executive_summary", "findings", "recommendations"],
24
+ "findings_limit": 10,
25
+ "include_evidence": False,
26
+ "include_patches": False,
27
+ "methodology_notes": [
28
+ "Time-boxed shallow analysis",
29
+ "Focus on high-risk paths",
30
+ "Static analysis and linting",
31
+ ],
32
+ },
33
+ QRTemplate.DEEP_QE: {
34
+ "name": "Deep QE Investigation Report",
35
+ "sections": [
36
+ "executive_summary",
37
+ "scope",
38
+ "methodology",
39
+ "findings",
40
+ "root_cause",
41
+ "suggested_fixes",
42
+ "generated_tests",
43
+ "benchmarks",
44
+ "recommendations",
45
+ "appendix",
46
+ ],
47
+ "findings_limit": None,
48
+ "include_evidence": True,
49
+ "include_patches": True,
50
+ "methodology_notes": [
51
+ "Full codebase exploration",
52
+ "Destructive testing enabled",
53
+ "Test generation for uncovered code",
54
+ "Security vulnerability scanning",
55
+ "Performance profiling",
56
+ ],
57
+ },
58
+ QRTemplate.SECURITY_AUDIT: {
59
+ "name": "Security Audit Report",
60
+ "sections": [
61
+ "executive_summary",
62
+ "scope",
63
+ "methodology",
64
+ "findings",
65
+ "suggested_fixes",
66
+ "recommendations",
67
+ "appendix",
68
+ ],
69
+ "findings_limit": None,
70
+ "include_evidence": True,
71
+ "include_patches": True,
72
+ "methodology_notes": [
73
+ "OWASP Top 10 vulnerability check",
74
+ "Dependency vulnerability scan",
75
+ "Authentication/authorization review",
76
+ "Input validation analysis",
77
+ "Secrets detection",
78
+ ],
79
+ "severity_filter": ["critical", "high"], # Focus on security-relevant
80
+ },
81
+ QRTemplate.PERFORMANCE_REVIEW: {
82
+ "name": "Performance Review Report",
83
+ "sections": [
84
+ "executive_summary",
85
+ "scope",
86
+ "methodology",
87
+ "benchmarks",
88
+ "findings",
89
+ "recommendations",
90
+ ],
91
+ "findings_limit": None,
92
+ "include_evidence": True,
93
+ "include_patches": False,
94
+ "methodology_notes": [
95
+ "Load testing",
96
+ "Memory profiling",
97
+ "CPU profiling",
98
+ "Database query analysis",
99
+ "Network latency measurement",
100
+ ],
101
+ },
102
+ QRTemplate.REGRESSION_CHECK: {
103
+ "name": "Regression Check Report",
104
+ "sections": ["executive_summary", "findings", "recommendations"],
105
+ "findings_limit": None,
106
+ "include_evidence": True,
107
+ "include_patches": False,
108
+ "methodology_notes": [
109
+ "Existing test suite execution",
110
+ "Flaky test detection",
111
+ "Coverage comparison",
112
+ "Performance regression detection",
113
+ ],
114
+ },
115
+ }
116
+
117
+
118
+ def get_template(template: QRTemplate) -> Dict:
119
+ """Get configuration for a QIR template."""
120
+ return TEMPLATES.get(template, TEMPLATES[QRTemplate.DEEP_QE])
121
+
122
+
123
+ def get_template_by_mode(mode: str) -> Dict:
124
+ """Get template configuration by mode name."""
125
+ mode_map = {
126
+ "quick_scan": QRTemplate.QUICK_SCAN,
127
+ "quick": QRTemplate.QUICK_SCAN,
128
+ "deep_qe": QRTemplate.DEEP_QE,
129
+ "deep": QRTemplate.DEEP_QE,
130
+ "security": QRTemplate.SECURITY_AUDIT,
131
+ "performance": QRTemplate.PERFORMANCE_REVIEW,
132
+ "regression": QRTemplate.REGRESSION_CHECK,
133
+ }
134
+ template = mode_map.get(mode.lower(), QRTemplate.DEEP_QE)
135
+ return TEMPLATES[template]
@@ -0,0 +1,41 @@
1
+ """
2
+ Safety warning system for SuperQode.
3
+
4
+ Provides warnings about destructive QE actions, token consumption,
5
+ and sandbox environment recommendations.
6
+ """
7
+
8
+ from .warnings import (
9
+ SafetyWarning,
10
+ WarningType,
11
+ WarningSeverity,
12
+ get_safety_warnings,
13
+ get_production_warnings,
14
+ show_safety_warnings,
15
+ get_warning_acknowledgment,
16
+ should_skip_warnings,
17
+ mark_warnings_acknowledged,
18
+ )
19
+
20
+ from .sandbox import (
21
+ SandboxDetector,
22
+ SandboxStatus,
23
+ detect_sandbox_environment,
24
+ get_sandbox_recommendations,
25
+ )
26
+
27
+ __all__ = [
28
+ # Warning system
29
+ "SafetyWarning",
30
+ "WarningType",
31
+ "WarningSeverity",
32
+ "show_safety_warnings",
33
+ "get_warning_acknowledgment",
34
+ "should_skip_warnings",
35
+ "mark_warnings_acknowledged",
36
+ # Sandbox detection
37
+ "SandboxDetector",
38
+ "SandboxStatus",
39
+ "detect_sandbox_environment",
40
+ "get_sandbox_recommendations",
41
+ ]
@@ -0,0 +1,413 @@
1
+ """
2
+ Sandbox environment detection for SuperQode.
3
+
4
+ Detects whether QE sessions are being run in isolated, safe environments
5
+ and provides recommendations for safe execution.
6
+ """
7
+
8
+ import os
9
+ import subprocess
10
+ import platform
11
+ from pathlib import Path
12
+ from enum import Enum
13
+ from typing import List, Dict, Any, Optional
14
+
15
+ from rich.console import Console
16
+
17
+ _console = Console()
18
+
19
+
20
+ class SandboxStatus(Enum):
21
+ """Status of sandbox environment detection."""
22
+
23
+ SAFE = "safe"
24
+ WARNING = "warning"
25
+ DANGEROUS = "dangerous"
26
+ UNKNOWN = "unknown"
27
+
28
+
29
+ class SandboxDetector:
30
+ """Detects sandbox environment characteristics."""
31
+
32
+ def __init__(self, target_path: Optional[Path] = None):
33
+ self.target_path = target_path or Path.cwd()
34
+ self.detections = {}
35
+
36
+ def detect_all(self) -> Dict[str, Any]:
37
+ """Run all sandbox detection checks."""
38
+ self.detections = {
39
+ "git_status": self._check_git_status(),
40
+ "container": self._check_container_environment(),
41
+ "virtual_env": self._check_virtual_environment(),
42
+ "filesystem": self._check_filesystem_safety(),
43
+ "system_load": self._check_system_load(),
44
+ }
45
+
46
+ return self.detections
47
+
48
+ def get_overall_status(self) -> SandboxStatus:
49
+ """Get overall sandbox safety status."""
50
+ if not self.detections:
51
+ self.detect_all()
52
+
53
+ # Critical indicators of danger
54
+ if self.detections.get("git_status", {}).get("has_uncommitted_changes"):
55
+ return SandboxStatus.DANGEROUS
56
+
57
+ if self.detections.get("filesystem", {}).get("is_production_like"):
58
+ return SandboxStatus.DANGEROUS
59
+
60
+ # Warning indicators
61
+ if not self.detections.get("container", {}).get("is_container"):
62
+ return SandboxStatus.WARNING
63
+
64
+ if not self.detections.get("virtual_env", {}).get("is_venv"):
65
+ return SandboxStatus.WARNING
66
+
67
+ # Safe indicators
68
+ if self.detections.get("container", {}).get("is_container") and not self.detections.get(
69
+ "git_status", {}
70
+ ).get("has_uncommitted_changes"):
71
+ return SandboxStatus.SAFE
72
+
73
+ return SandboxStatus.UNKNOWN
74
+
75
+ def _check_git_status(self) -> Dict[str, Any]:
76
+ """Check git repository status."""
77
+ try:
78
+ # Check if we're in a git repository
79
+ result = subprocess.run(
80
+ ["git", "rev-parse", "--git-dir"],
81
+ cwd=self.target_path,
82
+ capture_output=True,
83
+ text=True,
84
+ timeout=5,
85
+ )
86
+
87
+ if result.returncode != 0:
88
+ return {"is_git_repo": False, "has_uncommitted_changes": False, "is_clean": False}
89
+
90
+ # Check for uncommitted changes
91
+ status_result = subprocess.run(
92
+ ["git", "status", "--porcelain"],
93
+ cwd=self.target_path,
94
+ capture_output=True,
95
+ text=True,
96
+ timeout=5,
97
+ )
98
+
99
+ has_changes = bool(status_result.stdout.strip())
100
+
101
+ return {
102
+ "is_git_repo": True,
103
+ "has_uncommitted_changes": has_changes,
104
+ "is_clean": not has_changes,
105
+ "changes_count": len(status_result.stdout.strip().split("\n"))
106
+ if has_changes
107
+ else 0,
108
+ }
109
+
110
+ except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
111
+ return {
112
+ "is_git_repo": False,
113
+ "has_uncommitted_changes": False,
114
+ "is_clean": False,
115
+ "error": "git not available",
116
+ }
117
+
118
+ def _check_container_environment(self) -> Dict[str, Any]:
119
+ """Check if running in a container environment."""
120
+ indicators = {
121
+ "docker": self._check_docker_container(),
122
+ "podman": self._check_podman_container(),
123
+ "kubernetes": self._check_kubernetes_pod(),
124
+ "wsl": self._check_wsl_environment(),
125
+ }
126
+
127
+ is_container = any(indicators.values())
128
+
129
+ return {"is_container": is_container, "indicators": indicators}
130
+
131
+ def _check_docker_container(self) -> bool:
132
+ """Check if running in Docker container."""
133
+ try:
134
+ # Check for Docker-specific files
135
+ if Path("/.dockerenv").exists():
136
+ return True
137
+
138
+ # Check cgroup for docker
139
+ if Path("/proc/1/cgroup").exists():
140
+ with open("/proc/1/cgroup", "r") as f:
141
+ content = f.read()
142
+ if "docker" in content.lower():
143
+ return True
144
+
145
+ return False
146
+
147
+ except (OSError, IOError):
148
+ return False
149
+
150
+ def _check_podman_container(self) -> bool:
151
+ """Check if running in Podman container."""
152
+ try:
153
+ if Path("/run/.containerenv").exists():
154
+ return True
155
+
156
+ if Path("/proc/1/cgroup").exists():
157
+ with open("/proc/1/cgroup", "r") as f:
158
+ content = f.read()
159
+ if "podman" in content.lower():
160
+ return True
161
+
162
+ return False
163
+
164
+ except (OSError, IOError):
165
+ return False
166
+
167
+ def _check_kubernetes_pod(self) -> bool:
168
+ """Check if running in Kubernetes pod."""
169
+ try:
170
+ # Check for Kubernetes service account token
171
+ if Path("/var/run/secrets/kubernetes.io/serviceaccount/token").exists():
172
+ return True
173
+
174
+ # Check environment variables
175
+ if os.environ.get("KUBERNETES_SERVICE_HOST"):
176
+ return True
177
+
178
+ return False
179
+
180
+ except OSError:
181
+ return False
182
+
183
+ def _check_wsl_environment(self) -> bool:
184
+ """Check if running in WSL environment."""
185
+ try:
186
+ # Check for WSL-specific files
187
+ if Path("/proc/version").exists():
188
+ with open("/proc/version", "r") as f:
189
+ content = f.read()
190
+ if "microsoft" in content.lower() or "wsl" in content.lower():
191
+ return True
192
+
193
+ # Check uname
194
+ result = subprocess.run(["uname", "-r"], capture_output=True, text=True, timeout=2)
195
+
196
+ if "microsoft" in result.stdout.lower() or "wsl" in result.stdout.lower():
197
+ return True
198
+
199
+ return False
200
+
201
+ except (OSError, IOError, subprocess.SubprocessError):
202
+ return False
203
+
204
+ def _check_virtual_environment(self) -> Dict[str, Any]:
205
+ """Check if running in a virtual environment."""
206
+ is_venv = os.environ.get("VIRTUAL_ENV") is not None
207
+ is_conda = os.environ.get("CONDA_DEFAULT_ENV") is not None
208
+ is_poetry = os.environ.get("POETRY_ACTIVE") is not None
209
+
210
+ return {
211
+ "is_venv": is_venv,
212
+ "is_conda": is_conda,
213
+ "is_poetry": is_poetry,
214
+ "has_any_venv": is_venv or is_conda or is_poetry,
215
+ "venv_path": os.environ.get("VIRTUAL_ENV"),
216
+ "conda_env": os.environ.get("CONDA_DEFAULT_ENV"),
217
+ }
218
+
219
+ def _check_filesystem_safety(self) -> Dict[str, Any]:
220
+ """Check filesystem safety indicators."""
221
+ path = self.target_path
222
+
223
+ # Check for production-like indicators
224
+ production_indicators = [
225
+ "production" in str(path).lower(),
226
+ "prod" in str(path).lower(),
227
+ "live" in str(path).lower(),
228
+ "main" in str(path).lower() and "master" in str(path).lower(),
229
+ path == Path.home(), # Running in home directory
230
+ Path(path / "node_modules").exists(), # Large npm project
231
+ Path(path / ".git").exists()
232
+ and any(
233
+ f.suffix in [".py", ".js", ".ts", ".java", ".cpp", ".c"]
234
+ for f in path.glob("*")
235
+ if f.is_file()
236
+ ), # Looks like active development
237
+ ]
238
+
239
+ is_production_like = any(production_indicators)
240
+
241
+ # Check write permissions
242
+ try:
243
+ test_file = path / ".superqode_safety_test"
244
+ test_file.write_text("test")
245
+ test_file.unlink()
246
+ has_write_permission = True
247
+ except (OSError, IOError):
248
+ has_write_permission = False
249
+
250
+ return {
251
+ "is_production_like": is_production_like,
252
+ "production_indicators": production_indicators,
253
+ "has_write_permission": has_write_permission,
254
+ "target_path": str(path),
255
+ }
256
+
257
+ def _check_system_load(self) -> Dict[str, Any]:
258
+ """Check system load and resources."""
259
+ try:
260
+ # Get system info
261
+ system = platform.system().lower()
262
+
263
+ if system == "linux":
264
+ # Check load average
265
+ with open("/proc/loadavg", "r") as f:
266
+ loadavg = f.read().strip().split()
267
+ load_1min = float(loadavg[0])
268
+ load_5min = float(loadavg[1])
269
+ load_15min = float(loadavg[2])
270
+
271
+ # Get CPU count
272
+ cpu_count = os.cpu_count() or 1
273
+ high_load = load_1min > cpu_count * 0.8
274
+
275
+ return {
276
+ "system": system,
277
+ "load_1min": load_1min,
278
+ "load_5min": load_5min,
279
+ "load_15min": load_15min,
280
+ "cpu_count": cpu_count,
281
+ "high_load": high_load,
282
+ }
283
+
284
+ else:
285
+ return {"system": system, "load_info": "not available"}
286
+
287
+ except (OSError, IOError, ValueError):
288
+ return {"system": platform.system().lower(), "load_info": "error reading system info"}
289
+
290
+
291
+ def detect_sandbox_environment(target_path: Optional[Path] = None) -> Dict[str, Any]:
292
+ """Convenience function to detect sandbox environment."""
293
+ detector = SandboxDetector(target_path)
294
+ return detector.detect_all()
295
+
296
+
297
+ def get_sandbox_recommendations(detections: Dict[str, Any]) -> List[str]:
298
+ """Get recommendations based on sandbox detection results."""
299
+ recommendations = []
300
+
301
+ # Git status recommendations
302
+ git_status = detections.get("git_status", {})
303
+ if git_status.get("has_uncommitted_changes"):
304
+ recommendations.append(
305
+ "⚠️ Git repository has uncommitted changes. Consider committing or stashing before QE."
306
+ )
307
+
308
+ if not git_status.get("is_git_repo"):
309
+ recommendations.append(
310
+ "💡 Consider initializing a git repository for better change tracking during QE."
311
+ )
312
+
313
+ # Container recommendations
314
+ container = detections.get("container", {})
315
+ if not container.get("is_container"):
316
+ recommendations.append("🐳 Consider running QE in a Docker container for better isolation.")
317
+
318
+ # Virtual environment recommendations
319
+ venv = detections.get("virtual_env", {})
320
+ if not venv.get("has_any_venv"):
321
+ recommendations.append(
322
+ "📦 Consider using a virtual environment (venv, conda) for dependency isolation."
323
+ )
324
+
325
+ # Filesystem recommendations
326
+ fs = detections.get("filesystem", {})
327
+ if fs.get("is_production_like"):
328
+ recommendations.append(
329
+ "🚨 Production-like environment detected. Use sandbox environments for QE testing."
330
+ )
331
+
332
+ # System load recommendations
333
+ sys_load = detections.get("system_load", {})
334
+ if sys_load.get("high_load"):
335
+ recommendations.append("⚡ System load is high. QE sessions may impact system performance.")
336
+
337
+ # Always include general recommendations
338
+ if not recommendations:
339
+ recommendations.extend(
340
+ [
341
+ "✅ Environment looks suitable for QE testing.",
342
+ "💡 For maximum safety, consider using git worktrees or Docker containers.",
343
+ ]
344
+ )
345
+
346
+ return recommendations
347
+
348
+
349
+ def display_sandbox_status(detections: Dict[str, Any], console: Optional[Console] = None) -> None:
350
+ """Display sandbox detection results."""
351
+ if console is None:
352
+ console = _console
353
+
354
+ from rich.table import Table
355
+ from rich.panel import Panel
356
+
357
+ # Create status table
358
+ table = Table(title="Sandbox Environment Detection")
359
+ table.add_column("Check", style="cyan", no_wrap=True)
360
+ table.add_column("Status", style="green")
361
+ table.add_column("Details", style="white")
362
+
363
+ # Git status
364
+ git = detections.get("git_status", {})
365
+ git_status = (
366
+ "✅ Clean"
367
+ if git.get("is_clean")
368
+ else "⚠️ Has Changes"
369
+ if git.get("has_uncommitted_changes")
370
+ else "❓ Not a Git Repo"
371
+ )
372
+ git_details = (
373
+ f"Repository: {git.get('is_git_repo', False)}, Changes: {git.get('changes_count', 0)}"
374
+ )
375
+ table.add_row("Git Status", git_status, git_details)
376
+
377
+ # Container
378
+ container = detections.get("container", {})
379
+ container_status = "✅ Container" if container.get("is_container") else "⚠️ Host System"
380
+ container_details = ", ".join([k for k, v in container.get("indicators", {}).items() if v])
381
+ if not container_details:
382
+ container_details = "Not detected"
383
+ table.add_row("Container", container_status, container_details)
384
+
385
+ # Virtual Environment
386
+ venv = detections.get("virtual_env", {})
387
+ venv_status = "✅ Virtual Env" if venv.get("has_any_venv") else "⚠️ System Python"
388
+ venv_details = []
389
+ if venv.get("is_venv"):
390
+ venv_details.append("venv")
391
+ if venv.get("is_conda"):
392
+ venv_details.append("conda")
393
+ if venv.get("is_poetry"):
394
+ venv_details.append("poetry")
395
+ venv_details = ", ".join(venv_details) if venv_details else "None detected"
396
+ table.add_row("Virtual Env", venv_status, venv_details)
397
+
398
+ # Filesystem
399
+ fs = detections.get("filesystem", {})
400
+ fs_status = "🚨 Production Risk" if fs.get("is_production_like") else "✅ Development Safe"
401
+ fs_details = f"Path: {fs.get('target_path', 'unknown')}"
402
+ table.add_row("Filesystem", fs_status, fs_details)
403
+
404
+ console.print(table)
405
+
406
+ # Show recommendations
407
+ recommendations = get_sandbox_recommendations(detections)
408
+ if recommendations:
409
+ rec_panel = Panel(
410
+ "\n".join(recommendations), title="💡 Recommendations", border_style="blue"
411
+ )
412
+ console.print()
413
+ console.print(rec_panel)