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,203 @@
1
+ """
2
+ QE Guidance Configuration - YAML-driven settings for time-constrained QE.
3
+
4
+ All configuration comes from superqode.yaml, aligned with PRD:
5
+ > "SuperQode operationalizes SuperQE—where autonomous agents aggressively test software,
6
+ > propose fixes, prove improvements, and report findings with research-grade rigor."
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional
13
+ import logging
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class GuidanceMode(Enum):
19
+ """QE guidance mode."""
20
+
21
+ QUICK_SCAN = "quick_scan"
22
+ DEEP_QE = "deep_qe"
23
+
24
+
25
+ @dataclass
26
+ class AntiPatternConfig:
27
+ """Configuration for anti-pattern detection."""
28
+
29
+ enabled: bool = True
30
+ patterns: List[str] = field(
31
+ default_factory=lambda: [
32
+ "skip_verification",
33
+ "unconditional_success",
34
+ "broad_exception_swallow",
35
+ "weaken_tests",
36
+ "silent_fallback",
37
+ "guess_expected_output",
38
+ ]
39
+ )
40
+
41
+
42
+ @dataclass
43
+ class ModeGuidanceConfig:
44
+ """Configuration for a specific QE mode."""
45
+
46
+ timeout_seconds: int = 60
47
+ verification_first: bool = True
48
+ fail_fast: bool = False
49
+ exploration_allowed: bool = False
50
+ destructive_testing: bool = False
51
+
52
+ # What the agent should focus on
53
+ focus_areas: List[str] = field(default_factory=list)
54
+
55
+ # What the agent should NOT do
56
+ forbidden_actions: List[str] = field(default_factory=list)
57
+
58
+
59
+ @dataclass
60
+ class GuidanceConfig:
61
+ """
62
+ Complete QE guidance configuration from superqode.yaml.
63
+
64
+ Defines:
65
+ - Mode-specific timeouts and constraints
66
+ - Verification-first workflow requirements
67
+ - Anti-pattern detection rules
68
+ """
69
+
70
+ enabled: bool = True
71
+
72
+ # Mode-specific configurations
73
+ quick_scan: ModeGuidanceConfig = field(
74
+ default_factory=lambda: ModeGuidanceConfig(
75
+ timeout_seconds=60,
76
+ verification_first=True,
77
+ fail_fast=True,
78
+ exploration_allowed=False,
79
+ destructive_testing=False,
80
+ focus_areas=[
81
+ "Run smoke tests first",
82
+ "Validate critical paths",
83
+ "Check for obvious errors",
84
+ "Verify basic functionality",
85
+ ],
86
+ forbidden_actions=[
87
+ "Long-running performance tests",
88
+ "Extensive code generation",
89
+ "Deep exploration without quick feedback",
90
+ ],
91
+ )
92
+ )
93
+
94
+ deep_qe: ModeGuidanceConfig = field(
95
+ default_factory=lambda: ModeGuidanceConfig(
96
+ timeout_seconds=1800,
97
+ verification_first=True,
98
+ fail_fast=False,
99
+ exploration_allowed=True,
100
+ destructive_testing=True,
101
+ focus_areas=[
102
+ "Comprehensive test coverage",
103
+ "Edge case exploration",
104
+ "Security vulnerability scanning",
105
+ "Performance and load testing",
106
+ "Chaos and stress testing",
107
+ ],
108
+ forbidden_actions=[
109
+ "Modifying production code",
110
+ "Committing changes to git",
111
+ "Accessing external networks without approval",
112
+ ],
113
+ )
114
+ )
115
+
116
+ # Anti-pattern detection
117
+ anti_patterns: AntiPatternConfig = field(default_factory=AntiPatternConfig)
118
+
119
+ # QIR (Quality Investigation Report) settings
120
+ qir_format: str = "markdown" # "markdown", "json", "both"
121
+ require_proof: bool = True # Must have verification before success
122
+
123
+ @classmethod
124
+ def from_yaml_dict(cls, data: Dict[str, Any]) -> "GuidanceConfig":
125
+ """Create GuidanceConfig from YAML dict (superqode.qe.guidance section)."""
126
+ if not data:
127
+ return cls()
128
+
129
+ config = cls(
130
+ enabled=data.get("enabled", True),
131
+ qir_format=data.get("qir_format", "markdown"),
132
+ require_proof=data.get("require_proof", True),
133
+ )
134
+
135
+ # Parse quick_scan config
136
+ if "quick_scan" in data:
137
+ qs = data["quick_scan"]
138
+ config.quick_scan = ModeGuidanceConfig(
139
+ timeout_seconds=qs.get("timeout_seconds", 60),
140
+ verification_first=qs.get("verification_first", True),
141
+ fail_fast=qs.get("fail_fast", True),
142
+ exploration_allowed=qs.get("exploration_allowed", False),
143
+ destructive_testing=qs.get("destructive_testing", False),
144
+ focus_areas=qs.get("focus_areas", config.quick_scan.focus_areas),
145
+ forbidden_actions=qs.get("forbidden_actions", config.quick_scan.forbidden_actions),
146
+ )
147
+
148
+ # Parse deep_qe config
149
+ if "deep_qe" in data:
150
+ dq = data["deep_qe"]
151
+ config.deep_qe = ModeGuidanceConfig(
152
+ timeout_seconds=dq.get("timeout_seconds", 1800),
153
+ verification_first=dq.get("verification_first", True),
154
+ fail_fast=dq.get("fail_fast", False),
155
+ exploration_allowed=dq.get("exploration_allowed", True),
156
+ destructive_testing=dq.get("destructive_testing", True),
157
+ focus_areas=dq.get("focus_areas", config.deep_qe.focus_areas),
158
+ forbidden_actions=dq.get("forbidden_actions", config.deep_qe.forbidden_actions),
159
+ )
160
+
161
+ # Parse anti-patterns config
162
+ if "anti_patterns" in data:
163
+ ap = data["anti_patterns"]
164
+ config.anti_patterns = AntiPatternConfig(
165
+ enabled=ap.get("enabled", True),
166
+ patterns=ap.get("patterns", config.anti_patterns.patterns),
167
+ )
168
+
169
+ return config
170
+
171
+ def get_mode_config(self, mode: GuidanceMode) -> ModeGuidanceConfig:
172
+ """Get configuration for a specific mode."""
173
+ if mode == GuidanceMode.QUICK_SCAN:
174
+ return self.quick_scan
175
+ else:
176
+ return self.deep_qe
177
+
178
+
179
+ def load_guidance_config(project_root: Path) -> GuidanceConfig:
180
+ """
181
+ Load guidance configuration from superqode.yaml.
182
+
183
+ Looks for: superqode.qe.guidance section
184
+ """
185
+ import yaml
186
+
187
+ yaml_path = project_root / "superqode.yaml"
188
+ if not yaml_path.exists():
189
+ logger.debug("No superqode.yaml found, using default guidance config")
190
+ return GuidanceConfig()
191
+
192
+ try:
193
+ with open(yaml_path) as f:
194
+ data = yaml.safe_load(f)
195
+
196
+ # Navigate to superqode.qe.guidance
197
+ guidance_data = data.get("superqode", {}).get("qe", {}).get("guidance", {})
198
+
199
+ return GuidanceConfig.from_yaml_dict(guidance_data)
200
+
201
+ except Exception as e:
202
+ logger.warning(f"Failed to load guidance config: {e}")
203
+ return GuidanceConfig()
@@ -0,0 +1,71 @@
1
+ """
2
+ QE Guidance Prompts - Minimal OSS guidance.
3
+
4
+ This provides lightweight, non-proprietary guidance for QE agents.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ from .config import GuidanceConfig, GuidanceMode, ModeGuidanceConfig, load_guidance_config
12
+
13
+
14
+ @dataclass
15
+ class QEGuidance:
16
+ """Minimal QE guidance for OSS."""
17
+
18
+ config: GuidanceConfig
19
+ mode: GuidanceMode
20
+
21
+ @property
22
+ def mode_config(self) -> ModeGuidanceConfig:
23
+ return self.config.get_mode_config(self.mode)
24
+
25
+ def get_system_prompt(self) -> str:
26
+ mode_name = "Quick Scan" if self.mode == GuidanceMode.QUICK_SCAN else "Deep QE"
27
+ timeout = self.mode_config.timeout_seconds
28
+ return (
29
+ f"SYSTEM: SuperQode {mode_name} QE Mode ({timeout}s)\n\n"
30
+ "Focus on finding real issues with evidence. "
31
+ "Prefer concrete reproduction steps and avoid speculation."
32
+ )
33
+
34
+ def get_review_prompt(self) -> str:
35
+ return (
36
+ "SYSTEM: SuperQode QE Review Mode\n\n"
37
+ "Validate that findings are evidence-backed and reproducible. "
38
+ "Call out any unverifiable claims."
39
+ )
40
+
41
+ def get_goal_suffix(self) -> str:
42
+ return (
43
+ "\n\nCOMPLETION GATE:\n"
44
+ "1. Run relevant checks/tests where feasible\n"
45
+ "2. Document evidence for findings\n"
46
+ "3. Report any limitations clearly\n"
47
+ )
48
+
49
+
50
+ def get_qe_system_prompt(
51
+ project_root: Path,
52
+ mode: GuidanceMode = GuidanceMode.QUICK_SCAN,
53
+ ) -> str:
54
+ config = load_guidance_config(project_root)
55
+ guidance = QEGuidance(config=config, mode=mode)
56
+ return guidance.get_system_prompt()
57
+
58
+
59
+ def get_qe_review_prompt(project_root: Path) -> str:
60
+ config = load_guidance_config(project_root)
61
+ guidance = QEGuidance(config=config, mode=GuidanceMode.DEEP_QE)
62
+ return guidance.get_review_prompt()
63
+
64
+
65
+ def get_qe_goal_suffix(
66
+ project_root: Path,
67
+ mode: GuidanceMode = GuidanceMode.QUICK_SCAN,
68
+ ) -> str:
69
+ config = load_guidance_config(project_root)
70
+ guidance = QEGuidance(config=config, mode=mode)
71
+ return guidance.get_goal_suffix()
@@ -0,0 +1,54 @@
1
+ """
2
+ SuperQode Patch Harness - Fast validation for QE artifacts.
3
+
4
+ The harness runs user-defined commands on patches/changes BEFORE they're suggested in QIRs.
5
+ This keeps OSS flexible without shipping proprietary validators.
6
+
7
+ Key principle from PRD:
8
+ > "SuperQode never edits, rewrites, or commits code."
9
+ > "All fixes are suggested, validated, and proven, never auto-applied."
10
+
11
+ The harness VALIDATES suggestions, it doesn't apply them.
12
+
13
+ Configuration is driven entirely by superqode.yaml:
14
+
15
+ ```yaml
16
+ superqode:
17
+ qe:
18
+ harness:
19
+ enabled: true
20
+ timeout_seconds: 30
21
+
22
+ custom_steps:
23
+ - name: "pytest"
24
+ command: "pytest -q"
25
+ - name: "contract-check"
26
+ command: "schemathesis run openapi.yaml"
27
+ ```
28
+ """
29
+
30
+ from .validator import PatchHarness, HarnessFinding, HarnessResult
31
+ from .config import HarnessConfig, ValidationCategory, load_harness_config
32
+ from .accelerator import (
33
+ Accelerator,
34
+ AcceleratorConfig,
35
+ get_accelerator,
36
+ prewarm,
37
+ cached_system_prompt,
38
+ )
39
+
40
+ __all__ = [
41
+ # Validation
42
+ "PatchHarness",
43
+ "HarnessFinding",
44
+ "HarnessResult",
45
+ "ValidationCategory",
46
+ "HarnessConfig",
47
+ "load_harness_config",
48
+ # Performance
49
+ "Accelerator",
50
+ "AcceleratorConfig",
51
+ "get_accelerator",
52
+ "prewarm",
53
+ "cached_system_prompt",
54
+ ]
@@ -0,0 +1,291 @@
1
+ """
2
+ Harness Accelerator - Performance Optimizations for QE.
3
+
4
+ Centralized performance optimizations for the SuperQode harness:
5
+ - Pre-computed tool definitions (compute once, reuse)
6
+ - Message format caching (don't rebuild identical messages)
7
+ - Background prewarming for slow imports
8
+ - Parallel execution utilities
9
+
10
+ Usage:
11
+ from superqode.harness.accelerator import Accelerator
12
+
13
+ # Initialize once during startup
14
+ accel = Accelerator()
15
+ accel.prewarm()
16
+
17
+ # Use cached tool definitions
18
+ tools = accel.get_tool_definitions(tool_registry)
19
+ """
20
+
21
+ import asyncio
22
+ import concurrent.futures
23
+ import hashlib
24
+ import threading
25
+ from dataclasses import dataclass
26
+ from functools import lru_cache
27
+ from typing import Any, Callable, Dict, List, Optional, TypeVar
28
+
29
+ T = TypeVar("T")
30
+
31
+
32
+ @dataclass
33
+ class AcceleratorConfig:
34
+ """Configuration for the accelerator."""
35
+
36
+ prewarm_litellm: bool = True
37
+ cache_tool_defs: bool = True
38
+ cache_messages: bool = True
39
+ max_cache_size: int = 1000
40
+
41
+
42
+ class Accelerator:
43
+ """Centralized performance optimizations for SuperQode.
44
+
45
+ Provides caching, prewarming, and parallel execution utilities
46
+ to minimize latency during QE sessions.
47
+ """
48
+
49
+ _instance: Optional["Accelerator"] = None
50
+ _lock = threading.Lock()
51
+
52
+ def __new__(cls, *args, **kwargs):
53
+ """Singleton pattern - only one accelerator instance."""
54
+ if cls._instance is None:
55
+ with cls._lock:
56
+ if cls._instance is None:
57
+ cls._instance = super().__new__(cls)
58
+ return cls._instance
59
+
60
+ def __init__(self, config: Optional[AcceleratorConfig] = None):
61
+ if hasattr(self, "_initialized"):
62
+ return
63
+
64
+ self._initialized = True
65
+ self.config = config or AcceleratorConfig()
66
+
67
+ # Thread pool for background tasks
68
+ self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
69
+
70
+ # Caches
71
+ self._tool_def_cache: Dict[str, List[Dict]] = {}
72
+ self._message_cache: Dict[str, Any] = {}
73
+
74
+ # Prewarm state
75
+ self._prewarm_complete = threading.Event()
76
+ self._prewarm_started = False
77
+
78
+ def prewarm(self) -> None:
79
+ """Start prewarming in background (non-blocking).
80
+
81
+ Call this during app startup for faster first operations.
82
+ """
83
+ if self._prewarm_started:
84
+ return
85
+
86
+ self._prewarm_started = True
87
+
88
+ def do_prewarm():
89
+ # Prewarm LiteLLM
90
+ if self.config.prewarm_litellm:
91
+ try:
92
+ from superqode.providers.gateway.litellm_gateway import LiteLLMGateway
93
+
94
+ LiteLLMGateway.prewarm()
95
+ except ImportError:
96
+ pass
97
+
98
+ # Prewarm other heavy imports
99
+ try:
100
+ import rich
101
+ import textual
102
+ except ImportError:
103
+ pass
104
+
105
+ self._prewarm_complete.set()
106
+
107
+ self._executor.submit(do_prewarm)
108
+
109
+ async def prewarm_async(self) -> None:
110
+ """Async version - await to ensure prewarming is complete."""
111
+ if self._prewarm_complete.is_set():
112
+ return
113
+
114
+ loop = asyncio.get_event_loop()
115
+ await loop.run_in_executor(self._executor, self._wait_for_prewarm)
116
+
117
+ def _wait_for_prewarm(self) -> None:
118
+ """Wait for prewarming to complete."""
119
+ self.prewarm() # Start if not started
120
+ self._prewarm_complete.wait(timeout=10.0)
121
+
122
+ def is_ready(self) -> bool:
123
+ """Check if prewarming is complete."""
124
+ return self._prewarm_complete.is_set()
125
+
126
+ def get_tool_definitions(self, registry) -> List[Dict]:
127
+ """Get cached tool definitions for a registry.
128
+
129
+ Args:
130
+ registry: A ToolRegistry instance
131
+
132
+ Returns:
133
+ List of tool definitions in OpenAI format
134
+ """
135
+ if not self.config.cache_tool_defs:
136
+ return self._compute_tool_definitions(registry)
137
+
138
+ # Use registry id as cache key
139
+ cache_key = str(id(registry))
140
+
141
+ if cache_key not in self._tool_def_cache:
142
+ self._tool_def_cache[cache_key] = self._compute_tool_definitions(registry)
143
+
144
+ return self._tool_def_cache[cache_key]
145
+
146
+ def _compute_tool_definitions(self, registry) -> List[Dict]:
147
+ """Compute tool definitions from a registry."""
148
+ definitions = []
149
+ for tool in registry.list():
150
+ definitions.append(
151
+ {
152
+ "type": "function",
153
+ "function": {
154
+ "name": tool.name,
155
+ "description": tool.description,
156
+ "parameters": tool.parameters,
157
+ },
158
+ }
159
+ )
160
+ return definitions
161
+
162
+ def cache_message(self, key: str, message: Any) -> None:
163
+ """Cache a message for later retrieval.
164
+
165
+ Args:
166
+ key: Cache key (e.g., message hash)
167
+ message: The message object to cache
168
+ """
169
+ if not self.config.cache_messages:
170
+ return
171
+
172
+ # Limit cache size
173
+ if len(self._message_cache) >= self.config.max_cache_size:
174
+ # Simple eviction - clear half the cache
175
+ keys = list(self._message_cache.keys())[: self.config.max_cache_size // 2]
176
+ for k in keys:
177
+ self._message_cache.pop(k, None)
178
+
179
+ self._message_cache[key] = message
180
+
181
+ def get_cached_message(self, key: str) -> Optional[Any]:
182
+ """Get a cached message.
183
+
184
+ Args:
185
+ key: Cache key
186
+
187
+ Returns:
188
+ Cached message or None if not found
189
+ """
190
+ return self._message_cache.get(key)
191
+
192
+ def message_hash(self, content: str, role: str = "user") -> str:
193
+ """Compute a hash for a message.
194
+
195
+ Args:
196
+ content: Message content
197
+ role: Message role
198
+
199
+ Returns:
200
+ Hash string for caching
201
+ """
202
+ data = f"{role}:{content}".encode("utf-8")
203
+ return hashlib.md5(data).hexdigest()
204
+
205
+ def invalidate_tool_cache(self) -> None:
206
+ """Invalidate all cached tool definitions."""
207
+ self._tool_def_cache.clear()
208
+
209
+ def invalidate_message_cache(self) -> None:
210
+ """Invalidate all cached messages."""
211
+ self._message_cache.clear()
212
+
213
+ def clear_all_caches(self) -> None:
214
+ """Clear all caches."""
215
+ self.invalidate_tool_cache()
216
+ self.invalidate_message_cache()
217
+
218
+ async def run_parallel(
219
+ self,
220
+ tasks: List[Callable[[], T]],
221
+ max_concurrent: int = 10,
222
+ ) -> List[T]:
223
+ """Run multiple async tasks in parallel.
224
+
225
+ Args:
226
+ tasks: List of async callables
227
+ max_concurrent: Maximum concurrent tasks
228
+
229
+ Returns:
230
+ List of results in same order as tasks
231
+ """
232
+ semaphore = asyncio.Semaphore(max_concurrent)
233
+
234
+ async def limited_task(task):
235
+ async with semaphore:
236
+ if asyncio.iscoroutinefunction(task):
237
+ return await task()
238
+ else:
239
+ return task()
240
+
241
+ return await asyncio.gather(*[limited_task(t) for t in tasks])
242
+
243
+ def shutdown(self) -> None:
244
+ """Shutdown the accelerator and release resources."""
245
+ self._executor.shutdown(wait=False)
246
+ self.clear_all_caches()
247
+
248
+
249
+ # Module-level convenience functions
250
+ _accelerator: Optional[Accelerator] = None
251
+
252
+
253
+ def get_accelerator() -> Accelerator:
254
+ """Get the global accelerator instance."""
255
+ global _accelerator
256
+ if _accelerator is None:
257
+ _accelerator = Accelerator()
258
+ return _accelerator
259
+
260
+
261
+ def prewarm() -> None:
262
+ """Prewarm the accelerator (call during startup)."""
263
+ get_accelerator().prewarm()
264
+
265
+
266
+ @lru_cache(maxsize=32)
267
+ def cached_system_prompt(
268
+ prompt_type: str,
269
+ working_dir: str,
270
+ custom_prompt: str = "",
271
+ ) -> str:
272
+ """Get a cached system prompt.
273
+
274
+ Args:
275
+ prompt_type: Type of prompt (e.g., "minimal", "standard")
276
+ working_dir: Working directory path
277
+ custom_prompt: Optional custom prompt to append
278
+
279
+ Returns:
280
+ Cached system prompt string
281
+ """
282
+ from superqode.agent.system_prompts import get_system_prompt, SystemPromptLevel
283
+ from pathlib import Path
284
+
285
+ level = getattr(SystemPromptLevel, prompt_type.upper(), SystemPromptLevel.MINIMAL)
286
+ prompt = get_system_prompt(level=level, working_directory=Path(working_dir))
287
+
288
+ if custom_prompt:
289
+ prompt += f"\n\n{custom_prompt}"
290
+
291
+ return prompt