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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- 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
|