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
superqode/core/roles.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified Role Definitions for SuperQode.
|
|
3
|
+
|
|
4
|
+
Provides a single interface for all role types across the platform:
|
|
5
|
+
- dev: Development roles (fullstack, frontend, backend, etc.)
|
|
6
|
+
- qe: Quality Engineering roles (smoke, regression, security, etc.)
|
|
7
|
+
- devops: DevOps roles (deployment, infrastructure, monitoring)
|
|
8
|
+
|
|
9
|
+
Each role can operate in either BYOK or ACP mode.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Dict, List, Literal, Optional, Any
|
|
15
|
+
|
|
16
|
+
from ..__init__ import __version__
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RoleCategory(Enum):
|
|
20
|
+
"""Role category (top-level grouping)."""
|
|
21
|
+
|
|
22
|
+
DEV = "dev"
|
|
23
|
+
QE = "qe"
|
|
24
|
+
DEVOPS = "devops"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class QERoleType(Enum):
|
|
28
|
+
"""Type of QE role."""
|
|
29
|
+
|
|
30
|
+
EXECUTION = "execution" # Runs existing tests (smoke, sanity, regression)
|
|
31
|
+
DETECTION = "detection" # Proactive issue detection (security, api, e2e)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class Role:
|
|
36
|
+
"""
|
|
37
|
+
Unified role definition.
|
|
38
|
+
|
|
39
|
+
Represents a role in the SuperQode system, whether it's a development,
|
|
40
|
+
QE, or DevOps role. Each role can operate in BYOK or ACP mode.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
category: RoleCategory
|
|
44
|
+
name: str
|
|
45
|
+
description: str
|
|
46
|
+
job_description: str = ""
|
|
47
|
+
|
|
48
|
+
# Execution mode
|
|
49
|
+
mode: Literal["byok", "acp"] = "byok"
|
|
50
|
+
|
|
51
|
+
# BYOK settings
|
|
52
|
+
provider: Optional[str] = None
|
|
53
|
+
model: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
# ACP settings
|
|
56
|
+
agent: Optional[str] = None
|
|
57
|
+
|
|
58
|
+
# QE-specific
|
|
59
|
+
qe_type: Optional[QERoleType] = None
|
|
60
|
+
|
|
61
|
+
# Common settings
|
|
62
|
+
enabled: bool = True
|
|
63
|
+
mcp_servers: List[str] = field(default_factory=list)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def display_name(self) -> str:
|
|
67
|
+
"""Get display name like 'dev.fullstack'."""
|
|
68
|
+
return f"{self.category.value}.{self.name}"
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def command(self) -> str:
|
|
72
|
+
"""Get TUI command like ':<mode> <role>'."""
|
|
73
|
+
return f":{self.category.value} {self.name}"
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def is_byok(self) -> bool:
|
|
77
|
+
"""Check if role uses BYOK mode."""
|
|
78
|
+
return self.mode == "byok"
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def is_acp(self) -> bool:
|
|
82
|
+
"""Check if role uses ACP mode."""
|
|
83
|
+
return self.mode == "acp"
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def is_execution_role(self) -> bool:
|
|
87
|
+
"""Check if this is an execution QE role."""
|
|
88
|
+
return self.qe_type == QERoleType.EXECUTION
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def is_detection_role(self) -> bool:
|
|
92
|
+
"""Check if this is a detection QE role."""
|
|
93
|
+
return self.qe_type == QERoleType.DETECTION
|
|
94
|
+
|
|
95
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
96
|
+
"""Convert to dictionary representation."""
|
|
97
|
+
result = {
|
|
98
|
+
"category": self.category.value,
|
|
99
|
+
"name": self.name,
|
|
100
|
+
"display_name": self.display_name,
|
|
101
|
+
"description": self.description,
|
|
102
|
+
"job_description": self.job_description,
|
|
103
|
+
"mode": self.mode,
|
|
104
|
+
"enabled": self.enabled,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if self.mode == "byok":
|
|
108
|
+
result["provider"] = self.provider
|
|
109
|
+
result["model"] = self.model
|
|
110
|
+
else:
|
|
111
|
+
result["agent"] = self.agent
|
|
112
|
+
|
|
113
|
+
if self.qe_type:
|
|
114
|
+
result["qe_type"] = self.qe_type.value
|
|
115
|
+
|
|
116
|
+
if self.mcp_servers:
|
|
117
|
+
result["mcp_servers"] = self.mcp_servers
|
|
118
|
+
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# =============================================================================
|
|
123
|
+
# Default Role Definitions
|
|
124
|
+
# =============================================================================
|
|
125
|
+
|
|
126
|
+
DEFAULT_DEV_ROLES = {
|
|
127
|
+
"fullstack": Role(
|
|
128
|
+
category=RoleCategory.DEV,
|
|
129
|
+
name="fullstack",
|
|
130
|
+
description="Full-stack development",
|
|
131
|
+
job_description="Implement features, fix bugs, refactor code across the entire stack",
|
|
132
|
+
mode="acp",
|
|
133
|
+
agent="claude-code",
|
|
134
|
+
),
|
|
135
|
+
"frontend": Role(
|
|
136
|
+
category=RoleCategory.DEV,
|
|
137
|
+
name="frontend",
|
|
138
|
+
description="Frontend/UI specialist",
|
|
139
|
+
job_description="Build and maintain user interfaces, ensure great UX",
|
|
140
|
+
mode="acp",
|
|
141
|
+
agent="claude-code",
|
|
142
|
+
),
|
|
143
|
+
"backend": Role(
|
|
144
|
+
category=RoleCategory.DEV,
|
|
145
|
+
name="backend",
|
|
146
|
+
description="Backend/API specialist",
|
|
147
|
+
job_description="Design and implement APIs, services, and data layers",
|
|
148
|
+
mode="acp",
|
|
149
|
+
agent="claude-code",
|
|
150
|
+
),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
DEFAULT_QE_ROLES = {
|
|
154
|
+
"smoke": Role(
|
|
155
|
+
category=RoleCategory.QE,
|
|
156
|
+
name="smoke",
|
|
157
|
+
description="Fast critical path validation",
|
|
158
|
+
job_description="Run smoke tests to validate critical paths work",
|
|
159
|
+
mode="byok",
|
|
160
|
+
qe_type=QERoleType.EXECUTION,
|
|
161
|
+
),
|
|
162
|
+
"sanity": Role(
|
|
163
|
+
category=RoleCategory.QE,
|
|
164
|
+
name="sanity",
|
|
165
|
+
description="Core functionality check",
|
|
166
|
+
job_description="Verify core functionality and recent changes",
|
|
167
|
+
mode="byok",
|
|
168
|
+
qe_type=QERoleType.EXECUTION,
|
|
169
|
+
),
|
|
170
|
+
"regression": Role(
|
|
171
|
+
category=RoleCategory.QE,
|
|
172
|
+
name="regression",
|
|
173
|
+
description="Full test suite execution",
|
|
174
|
+
job_description="Run full regression suite with flake detection",
|
|
175
|
+
mode="byok",
|
|
176
|
+
qe_type=QERoleType.EXECUTION,
|
|
177
|
+
),
|
|
178
|
+
"security": Role(
|
|
179
|
+
category=RoleCategory.QE,
|
|
180
|
+
name="security",
|
|
181
|
+
description="Security vulnerability scanning",
|
|
182
|
+
job_description="Proactively detect security vulnerabilities and suggest fixes",
|
|
183
|
+
mode="acp",
|
|
184
|
+
agent="claude-code",
|
|
185
|
+
qe_type=QERoleType.DETECTION,
|
|
186
|
+
),
|
|
187
|
+
"api": Role(
|
|
188
|
+
category=RoleCategory.QE,
|
|
189
|
+
name="api",
|
|
190
|
+
description="API contract and behavior testing",
|
|
191
|
+
job_description="Validate API contracts, test edge cases, check error handling",
|
|
192
|
+
mode="acp",
|
|
193
|
+
agent="claude-code",
|
|
194
|
+
qe_type=QERoleType.DETECTION,
|
|
195
|
+
),
|
|
196
|
+
"e2e": Role(
|
|
197
|
+
category=RoleCategory.QE,
|
|
198
|
+
name="e2e",
|
|
199
|
+
description="End-to-end workflow validation",
|
|
200
|
+
job_description="Test complete user workflows and integration points",
|
|
201
|
+
mode="acp",
|
|
202
|
+
agent="claude-code",
|
|
203
|
+
qe_type=QERoleType.DETECTION,
|
|
204
|
+
),
|
|
205
|
+
"performance": Role(
|
|
206
|
+
category=RoleCategory.QE,
|
|
207
|
+
name="performance",
|
|
208
|
+
description="Performance bottleneck detection",
|
|
209
|
+
job_description="Identify performance issues and optimization opportunities",
|
|
210
|
+
mode="acp",
|
|
211
|
+
agent="claude-code",
|
|
212
|
+
qe_type=QERoleType.DETECTION,
|
|
213
|
+
),
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
DEFAULT_DEVOPS_ROLES = {
|
|
217
|
+
"deployment": Role(
|
|
218
|
+
category=RoleCategory.DEVOPS,
|
|
219
|
+
name="deployment",
|
|
220
|
+
description="Deployment readiness validation",
|
|
221
|
+
job_description="Validate deployment configurations and readiness",
|
|
222
|
+
mode="acp",
|
|
223
|
+
agent="claude-code",
|
|
224
|
+
),
|
|
225
|
+
"infrastructure": Role(
|
|
226
|
+
category=RoleCategory.DEVOPS,
|
|
227
|
+
name="infrastructure",
|
|
228
|
+
description="Infrastructure and config validation",
|
|
229
|
+
job_description="Review infrastructure code and configurations",
|
|
230
|
+
mode="acp",
|
|
231
|
+
agent="claude-code",
|
|
232
|
+
),
|
|
233
|
+
"monitoring": Role(
|
|
234
|
+
category=RoleCategory.DEVOPS,
|
|
235
|
+
name="monitoring",
|
|
236
|
+
description="Observability and alerting setup",
|
|
237
|
+
job_description="Set up and validate monitoring, logging, and alerting",
|
|
238
|
+
mode="acp",
|
|
239
|
+
agent="claude-code",
|
|
240
|
+
),
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def get_default_roles() -> Dict[str, Dict[str, Role]]:
|
|
245
|
+
"""Get all default roles organized by category."""
|
|
246
|
+
return {
|
|
247
|
+
"dev": DEFAULT_DEV_ROLES,
|
|
248
|
+
"qe": DEFAULT_QE_ROLES,
|
|
249
|
+
"devops": DEFAULT_DEVOPS_ROLES,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def get_role(category: str, name: str) -> Optional[Role]:
|
|
254
|
+
"""Get a specific role by category and name."""
|
|
255
|
+
defaults = get_default_roles()
|
|
256
|
+
category_roles = defaults.get(category, {})
|
|
257
|
+
return category_roles.get(name)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def list_all_roles() -> List[Role]:
|
|
261
|
+
"""List all available roles."""
|
|
262
|
+
roles = []
|
|
263
|
+
for category_roles in get_default_roles().values():
|
|
264
|
+
roles.extend(category_roles.values())
|
|
265
|
+
return roles
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def list_roles_by_category(category: str) -> List[Role]:
|
|
269
|
+
"""List roles in a specific category."""
|
|
270
|
+
defaults = get_default_roles()
|
|
271
|
+
return list(defaults.get(category, {}).values())
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def list_qe_execution_roles() -> List[Role]:
|
|
275
|
+
"""List QE roles that run existing tests."""
|
|
276
|
+
return [r for r in DEFAULT_QE_ROLES.values() if r.qe_type == QERoleType.EXECUTION]
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def list_qe_detection_roles() -> List[Role]:
|
|
280
|
+
"""List QE roles that detect issues proactively."""
|
|
281
|
+
return [r for r in DEFAULT_QE_ROLES.values() if r.qe_type == QERoleType.DETECTION]
|
superqode/danger.py
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode Danger Detection - Command Safety Analysis
|
|
3
|
+
|
|
4
|
+
Analyzes shell commands to detect potentially dangerous operations.
|
|
5
|
+
Uses a unique visual style with gradient warnings.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import IntEnum
|
|
9
|
+
from functools import lru_cache
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Iterable, NamedTuple, Sequence, Tuple
|
|
12
|
+
|
|
13
|
+
# Commands that are generally safe (read-only operations)
|
|
14
|
+
SAFE_COMMANDS = {
|
|
15
|
+
# Display & Output
|
|
16
|
+
"echo",
|
|
17
|
+
"cat",
|
|
18
|
+
"less",
|
|
19
|
+
"more",
|
|
20
|
+
"head",
|
|
21
|
+
"tail",
|
|
22
|
+
"tac",
|
|
23
|
+
"nl",
|
|
24
|
+
"bat",
|
|
25
|
+
# File & Directory Information
|
|
26
|
+
"ls",
|
|
27
|
+
"tree",
|
|
28
|
+
"pwd",
|
|
29
|
+
"file",
|
|
30
|
+
"stat",
|
|
31
|
+
"du",
|
|
32
|
+
"df",
|
|
33
|
+
"exa",
|
|
34
|
+
"lsd",
|
|
35
|
+
# Search & Find
|
|
36
|
+
"find",
|
|
37
|
+
"locate",
|
|
38
|
+
"which",
|
|
39
|
+
"whereis",
|
|
40
|
+
"type",
|
|
41
|
+
"grep",
|
|
42
|
+
"egrep",
|
|
43
|
+
"fgrep",
|
|
44
|
+
"rg",
|
|
45
|
+
"ag",
|
|
46
|
+
"fd",
|
|
47
|
+
"fzf",
|
|
48
|
+
# Text Processing (read-only)
|
|
49
|
+
"wc",
|
|
50
|
+
"sort",
|
|
51
|
+
"uniq",
|
|
52
|
+
"cut",
|
|
53
|
+
"paste",
|
|
54
|
+
"column",
|
|
55
|
+
"tr",
|
|
56
|
+
"diff",
|
|
57
|
+
"cmp",
|
|
58
|
+
"comm",
|
|
59
|
+
# System Information
|
|
60
|
+
"whoami",
|
|
61
|
+
"who",
|
|
62
|
+
"w",
|
|
63
|
+
"id",
|
|
64
|
+
"hostname",
|
|
65
|
+
"uname",
|
|
66
|
+
"uptime",
|
|
67
|
+
"date",
|
|
68
|
+
"cal",
|
|
69
|
+
"env",
|
|
70
|
+
"printenv",
|
|
71
|
+
# Process Information
|
|
72
|
+
"ps",
|
|
73
|
+
"top",
|
|
74
|
+
"htop",
|
|
75
|
+
"pgrep",
|
|
76
|
+
"jobs",
|
|
77
|
+
"pstree",
|
|
78
|
+
# Network (read-only)
|
|
79
|
+
"ping",
|
|
80
|
+
"traceroute",
|
|
81
|
+
"nslookup",
|
|
82
|
+
"dig",
|
|
83
|
+
"host",
|
|
84
|
+
"netstat",
|
|
85
|
+
"ss",
|
|
86
|
+
"ifconfig",
|
|
87
|
+
"ip",
|
|
88
|
+
# View compressed files
|
|
89
|
+
"zcat",
|
|
90
|
+
"zless",
|
|
91
|
+
# History & Help
|
|
92
|
+
"history",
|
|
93
|
+
"man",
|
|
94
|
+
"help",
|
|
95
|
+
"info",
|
|
96
|
+
"apropos",
|
|
97
|
+
"whatis",
|
|
98
|
+
# Checksums
|
|
99
|
+
"md5sum",
|
|
100
|
+
"sha256sum",
|
|
101
|
+
"sha1sum",
|
|
102
|
+
"cksum",
|
|
103
|
+
"sum",
|
|
104
|
+
"md5",
|
|
105
|
+
"shasum",
|
|
106
|
+
# Other Safe
|
|
107
|
+
"bc",
|
|
108
|
+
"expr",
|
|
109
|
+
"test",
|
|
110
|
+
"sleep",
|
|
111
|
+
"true",
|
|
112
|
+
"false",
|
|
113
|
+
"yes",
|
|
114
|
+
"seq",
|
|
115
|
+
"basename",
|
|
116
|
+
"dirname",
|
|
117
|
+
"realpath",
|
|
118
|
+
"readlink",
|
|
119
|
+
# Dev tools (read-only)
|
|
120
|
+
"git status",
|
|
121
|
+
"git log",
|
|
122
|
+
"git diff",
|
|
123
|
+
"git show",
|
|
124
|
+
"git branch",
|
|
125
|
+
"npm list",
|
|
126
|
+
"pip list",
|
|
127
|
+
"cargo tree",
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# Commands that can modify the filesystem
|
|
131
|
+
UNSAFE_COMMANDS = {
|
|
132
|
+
# File/Directory Creation
|
|
133
|
+
"mkdir",
|
|
134
|
+
"touch",
|
|
135
|
+
"mktemp",
|
|
136
|
+
"mkfifo",
|
|
137
|
+
"mknod",
|
|
138
|
+
# File/Directory Deletion
|
|
139
|
+
"rm",
|
|
140
|
+
"rmdir",
|
|
141
|
+
"shred",
|
|
142
|
+
# File/Directory Moving/Copying
|
|
143
|
+
"mv",
|
|
144
|
+
"cp",
|
|
145
|
+
"rsync",
|
|
146
|
+
"scp",
|
|
147
|
+
"install",
|
|
148
|
+
# File Modification
|
|
149
|
+
"sed",
|
|
150
|
+
"awk",
|
|
151
|
+
"tee",
|
|
152
|
+
"nano",
|
|
153
|
+
"vim",
|
|
154
|
+
"vi",
|
|
155
|
+
"emacs",
|
|
156
|
+
"code",
|
|
157
|
+
# Permissions/Ownership
|
|
158
|
+
"chmod",
|
|
159
|
+
"chown",
|
|
160
|
+
"chgrp",
|
|
161
|
+
"chattr",
|
|
162
|
+
"setfacl",
|
|
163
|
+
# Linking
|
|
164
|
+
"ln",
|
|
165
|
+
"link",
|
|
166
|
+
"unlink",
|
|
167
|
+
# Archive/Compression
|
|
168
|
+
"tar",
|
|
169
|
+
"zip",
|
|
170
|
+
"unzip",
|
|
171
|
+
"gzip",
|
|
172
|
+
"gunzip",
|
|
173
|
+
"bzip2",
|
|
174
|
+
"bunzip2",
|
|
175
|
+
"xz",
|
|
176
|
+
"unxz",
|
|
177
|
+
"7z",
|
|
178
|
+
"rar",
|
|
179
|
+
"unrar",
|
|
180
|
+
# Download Tools
|
|
181
|
+
"wget",
|
|
182
|
+
"curl",
|
|
183
|
+
"fetch",
|
|
184
|
+
"aria2c",
|
|
185
|
+
# Low-level Disk
|
|
186
|
+
"dd",
|
|
187
|
+
"truncate",
|
|
188
|
+
"fallocate",
|
|
189
|
+
# File Splitting
|
|
190
|
+
"split",
|
|
191
|
+
"csplit",
|
|
192
|
+
# Sync
|
|
193
|
+
"sync",
|
|
194
|
+
# System Administration
|
|
195
|
+
"useradd",
|
|
196
|
+
"userdel",
|
|
197
|
+
"usermod",
|
|
198
|
+
"groupadd",
|
|
199
|
+
"groupdel",
|
|
200
|
+
"passwd",
|
|
201
|
+
"mount",
|
|
202
|
+
"umount",
|
|
203
|
+
"mkfs",
|
|
204
|
+
"fdisk",
|
|
205
|
+
"parted",
|
|
206
|
+
"swapon",
|
|
207
|
+
"swapoff",
|
|
208
|
+
# Package managers (can install/remove)
|
|
209
|
+
"npm install",
|
|
210
|
+
"pip install",
|
|
211
|
+
"cargo install",
|
|
212
|
+
"brew install",
|
|
213
|
+
"apt",
|
|
214
|
+
"yum",
|
|
215
|
+
"dnf",
|
|
216
|
+
"pacman",
|
|
217
|
+
# Other Dangerous
|
|
218
|
+
"patch",
|
|
219
|
+
"git checkout",
|
|
220
|
+
"git reset",
|
|
221
|
+
"git clean",
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class DangerLevel(IntEnum):
|
|
226
|
+
"""The danger level of a command."""
|
|
227
|
+
|
|
228
|
+
SAFE = 0 # Command is known to be generally safe
|
|
229
|
+
UNKNOWN = 1 # We don't know about this command
|
|
230
|
+
DANGEROUS = 2 # Command can modify filesystem
|
|
231
|
+
DESTRUCTIVE = 3 # Command modifies files outside project
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class CommandInfo(NamedTuple):
|
|
235
|
+
"""Information about a command's danger level."""
|
|
236
|
+
|
|
237
|
+
command: str
|
|
238
|
+
level: DangerLevel
|
|
239
|
+
target_path: Path | None
|
|
240
|
+
reason: str
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# Visual styles for each danger level
|
|
244
|
+
DANGER_STYLES = {
|
|
245
|
+
DangerLevel.SAFE: {
|
|
246
|
+
"icon": "✅",
|
|
247
|
+
"color": "#22c55e",
|
|
248
|
+
"bg": "#22c55e20",
|
|
249
|
+
"label": "Safe",
|
|
250
|
+
"border": "green",
|
|
251
|
+
},
|
|
252
|
+
DangerLevel.UNKNOWN: {
|
|
253
|
+
"icon": "❓",
|
|
254
|
+
"color": "#f59e0b",
|
|
255
|
+
"bg": "#f59e0b20",
|
|
256
|
+
"label": "Unknown",
|
|
257
|
+
"border": "yellow",
|
|
258
|
+
},
|
|
259
|
+
DangerLevel.DANGEROUS: {
|
|
260
|
+
"icon": "⚠️",
|
|
261
|
+
"color": "#f97316",
|
|
262
|
+
"bg": "#f9731620",
|
|
263
|
+
"label": "Dangerous",
|
|
264
|
+
"border": "orange1",
|
|
265
|
+
},
|
|
266
|
+
DangerLevel.DESTRUCTIVE: {
|
|
267
|
+
"icon": "🚨",
|
|
268
|
+
"color": "#ef4444",
|
|
269
|
+
"bg": "#ef444420",
|
|
270
|
+
"label": "DESTRUCTIVE",
|
|
271
|
+
"border": "red",
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@lru_cache(maxsize=512)
|
|
277
|
+
def analyze_command(
|
|
278
|
+
project_dir: str,
|
|
279
|
+
working_dir: str,
|
|
280
|
+
command: str,
|
|
281
|
+
) -> Tuple[DangerLevel, str, Path | None]:
|
|
282
|
+
"""
|
|
283
|
+
Analyze a shell command for potential dangers.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
project_dir: The project root directory
|
|
287
|
+
working_dir: Current working directory
|
|
288
|
+
command: The shell command to analyze
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Tuple of (danger_level, reason, target_path)
|
|
292
|
+
"""
|
|
293
|
+
if not command or not command.strip():
|
|
294
|
+
return DangerLevel.SAFE, "Empty command", None
|
|
295
|
+
|
|
296
|
+
command = command.strip()
|
|
297
|
+
parts = command.split()
|
|
298
|
+
if not parts:
|
|
299
|
+
return DangerLevel.SAFE, "Empty command", None
|
|
300
|
+
|
|
301
|
+
base_cmd = parts[0]
|
|
302
|
+
project_path = Path(project_dir).resolve()
|
|
303
|
+
current_path = Path(working_dir).resolve()
|
|
304
|
+
|
|
305
|
+
# Check for safe commands first
|
|
306
|
+
if base_cmd in SAFE_COMMANDS:
|
|
307
|
+
return DangerLevel.SAFE, f"'{base_cmd}' is a read-only command", None
|
|
308
|
+
|
|
309
|
+
# Check for known unsafe commands
|
|
310
|
+
is_unsafe = base_cmd in UNSAFE_COMMANDS
|
|
311
|
+
|
|
312
|
+
# Look for file paths in arguments
|
|
313
|
+
target_path = None
|
|
314
|
+
for arg in parts[1:]:
|
|
315
|
+
if arg.startswith("-"):
|
|
316
|
+
continue
|
|
317
|
+
try:
|
|
318
|
+
# Try to resolve the path
|
|
319
|
+
if arg.startswith("/"):
|
|
320
|
+
target_path = Path(arg).resolve()
|
|
321
|
+
elif arg.startswith("~"):
|
|
322
|
+
target_path = Path(arg).expanduser().resolve()
|
|
323
|
+
else:
|
|
324
|
+
target_path = (current_path / arg).resolve()
|
|
325
|
+
break
|
|
326
|
+
except (OSError, ValueError):
|
|
327
|
+
continue
|
|
328
|
+
|
|
329
|
+
# Determine danger level
|
|
330
|
+
if is_unsafe:
|
|
331
|
+
if target_path:
|
|
332
|
+
try:
|
|
333
|
+
# Check if path is outside project
|
|
334
|
+
target_path.relative_to(project_path)
|
|
335
|
+
return DangerLevel.DANGEROUS, f"'{base_cmd}' can modify files", target_path
|
|
336
|
+
except ValueError:
|
|
337
|
+
return (
|
|
338
|
+
DangerLevel.DESTRUCTIVE,
|
|
339
|
+
f"'{base_cmd}' targets files outside project!",
|
|
340
|
+
target_path,
|
|
341
|
+
)
|
|
342
|
+
return DangerLevel.DANGEROUS, f"'{base_cmd}' can modify the filesystem", None
|
|
343
|
+
|
|
344
|
+
# Unknown command
|
|
345
|
+
return DangerLevel.UNKNOWN, f"Unknown command '{base_cmd}'", target_path
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def get_danger_display(level: DangerLevel) -> dict:
|
|
349
|
+
"""Get display properties for a danger level."""
|
|
350
|
+
return DANGER_STYLES.get(level, DANGER_STYLES[DangerLevel.UNKNOWN])
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def format_danger_message(
|
|
354
|
+
command: str,
|
|
355
|
+
level: DangerLevel,
|
|
356
|
+
reason: str,
|
|
357
|
+
target_path: Path | None = None,
|
|
358
|
+
) -> str:
|
|
359
|
+
"""Format a danger warning message."""
|
|
360
|
+
style = DANGER_STYLES[level]
|
|
361
|
+
icon = style["icon"]
|
|
362
|
+
label = style["label"]
|
|
363
|
+
|
|
364
|
+
msg = f"{icon} [{label}] {reason}"
|
|
365
|
+
if target_path:
|
|
366
|
+
msg += f"\n 📁 Target: {target_path}"
|
|
367
|
+
return msg
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# Quick check functions
|
|
371
|
+
def is_safe(command: str, project_dir: str = ".", working_dir: str = ".") -> bool:
|
|
372
|
+
"""Check if a command is safe to run."""
|
|
373
|
+
level, _, _ = analyze_command(project_dir, working_dir, command)
|
|
374
|
+
return level == DangerLevel.SAFE
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def is_destructive(command: str, project_dir: str = ".", working_dir: str = ".") -> bool:
|
|
378
|
+
"""Check if a command is destructive (modifies files outside project)."""
|
|
379
|
+
level, _, _ = analyze_command(project_dir, working_dir, command)
|
|
380
|
+
return level == DangerLevel.DESTRUCTIVE
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def requires_approval(command: str, project_dir: str = ".", working_dir: str = ".") -> bool:
|
|
384
|
+
"""Check if a command requires user approval."""
|
|
385
|
+
level, _, _ = analyze_command(project_dir, working_dir, command)
|
|
386
|
+
return level >= DangerLevel.DANGEROUS
|