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,369 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple Desktop Notifications for SuperQode.
|
|
3
|
+
|
|
4
|
+
Provides optional desktop notifications for long-running tasks and QE runs.
|
|
5
|
+
Uses OS built-in tools without external dependencies:
|
|
6
|
+
- macOS: osascript (AppleScript)
|
|
7
|
+
- Linux: notify-send (libnotify)
|
|
8
|
+
- Windows: PowerShell toast notifications
|
|
9
|
+
|
|
10
|
+
All notifications are optional and fail silently if the system doesn't support them.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from enum import Enum
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class NotificationLevel(Enum):
|
|
24
|
+
"""Notification urgency level."""
|
|
25
|
+
|
|
26
|
+
INFO = "info"
|
|
27
|
+
SUCCESS = "success"
|
|
28
|
+
WARNING = "warning"
|
|
29
|
+
ERROR = "error"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class NotificationConfig:
|
|
34
|
+
"""Configuration for notifications."""
|
|
35
|
+
|
|
36
|
+
enabled: bool = True
|
|
37
|
+
sound: bool = True # Play sound with notification (macOS only)
|
|
38
|
+
timeout_seconds: int = 5 # Auto-dismiss timeout (Linux only)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Default configuration
|
|
42
|
+
_config = NotificationConfig()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def configure_notifications(
|
|
46
|
+
enabled: bool = True,
|
|
47
|
+
sound: bool = True,
|
|
48
|
+
timeout_seconds: int = 5,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""
|
|
51
|
+
Configure notification behavior.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
enabled: Whether notifications are enabled.
|
|
55
|
+
sound: Whether to play sound with notifications (macOS).
|
|
56
|
+
timeout_seconds: Auto-dismiss timeout in seconds (Linux).
|
|
57
|
+
"""
|
|
58
|
+
global _config
|
|
59
|
+
_config = NotificationConfig(
|
|
60
|
+
enabled=enabled,
|
|
61
|
+
sound=sound,
|
|
62
|
+
timeout_seconds=timeout_seconds,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def is_notifications_supported() -> bool:
|
|
67
|
+
"""
|
|
68
|
+
Check if desktop notifications are supported on this system.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
True if notifications are likely to work, False otherwise.
|
|
72
|
+
"""
|
|
73
|
+
if sys.platform == "darwin":
|
|
74
|
+
return True # macOS always has osascript
|
|
75
|
+
elif sys.platform == "linux":
|
|
76
|
+
try:
|
|
77
|
+
result = subprocess.run(
|
|
78
|
+
["which", "notify-send"],
|
|
79
|
+
capture_output=True,
|
|
80
|
+
timeout=2,
|
|
81
|
+
)
|
|
82
|
+
return result.returncode == 0
|
|
83
|
+
except Exception:
|
|
84
|
+
return False
|
|
85
|
+
elif sys.platform == "win32":
|
|
86
|
+
return True # PowerShell is always available on modern Windows
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def notify(
|
|
91
|
+
title: str,
|
|
92
|
+
message: str,
|
|
93
|
+
level: NotificationLevel = NotificationLevel.INFO,
|
|
94
|
+
subtitle: Optional[str] = None,
|
|
95
|
+
) -> bool:
|
|
96
|
+
"""
|
|
97
|
+
Send a desktop notification.
|
|
98
|
+
|
|
99
|
+
This function is synchronous and blocks briefly. For async contexts,
|
|
100
|
+
use notify_async() instead.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
title: Notification title (short, ~50 chars max).
|
|
104
|
+
message: Notification body text.
|
|
105
|
+
level: Notification urgency level (affects icon/sound on some systems).
|
|
106
|
+
subtitle: Optional subtitle (macOS only).
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
True if notification was sent successfully, False otherwise.
|
|
110
|
+
"""
|
|
111
|
+
if not _config.enabled:
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
if sys.platform == "darwin":
|
|
116
|
+
return _notify_macos(title, message, subtitle, level)
|
|
117
|
+
elif sys.platform == "linux":
|
|
118
|
+
return _notify_linux(title, message, level)
|
|
119
|
+
elif sys.platform == "win32":
|
|
120
|
+
return _notify_windows(title, message, level)
|
|
121
|
+
else:
|
|
122
|
+
return False
|
|
123
|
+
except Exception:
|
|
124
|
+
# Notifications are optional - fail silently
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def notify_async(
|
|
129
|
+
title: str,
|
|
130
|
+
message: str,
|
|
131
|
+
level: NotificationLevel = NotificationLevel.INFO,
|
|
132
|
+
subtitle: Optional[str] = None,
|
|
133
|
+
) -> bool:
|
|
134
|
+
"""
|
|
135
|
+
Send a desktop notification asynchronously.
|
|
136
|
+
|
|
137
|
+
Non-blocking version of notify() for use in async contexts.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
title: Notification title (short, ~50 chars max).
|
|
141
|
+
message: Notification body text.
|
|
142
|
+
level: Notification urgency level.
|
|
143
|
+
subtitle: Optional subtitle (macOS only).
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
True if notification was sent successfully, False otherwise.
|
|
147
|
+
"""
|
|
148
|
+
loop = asyncio.get_event_loop()
|
|
149
|
+
return await loop.run_in_executor(None, notify, title, message, level, subtitle)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _notify_macos(
|
|
153
|
+
title: str,
|
|
154
|
+
message: str,
|
|
155
|
+
subtitle: Optional[str],
|
|
156
|
+
level: NotificationLevel,
|
|
157
|
+
) -> bool:
|
|
158
|
+
"""Send notification on macOS using osascript."""
|
|
159
|
+
# Build AppleScript command
|
|
160
|
+
script_parts = [f'display notification "{_escape_applescript(message)}"']
|
|
161
|
+
script_parts.append(f'with title "{_escape_applescript(title)}"')
|
|
162
|
+
|
|
163
|
+
if subtitle:
|
|
164
|
+
script_parts.append(f'subtitle "{_escape_applescript(subtitle)}"')
|
|
165
|
+
|
|
166
|
+
# Add sound based on level and config
|
|
167
|
+
if _config.sound:
|
|
168
|
+
sound_map = {
|
|
169
|
+
NotificationLevel.INFO: "default",
|
|
170
|
+
NotificationLevel.SUCCESS: "Glass",
|
|
171
|
+
NotificationLevel.WARNING: "Basso",
|
|
172
|
+
NotificationLevel.ERROR: "Sosumi",
|
|
173
|
+
}
|
|
174
|
+
sound = sound_map.get(level, "default")
|
|
175
|
+
script_parts.append(f'sound name "{sound}"')
|
|
176
|
+
|
|
177
|
+
script = " ".join(script_parts)
|
|
178
|
+
|
|
179
|
+
result = subprocess.run(
|
|
180
|
+
["osascript", "-e", script],
|
|
181
|
+
capture_output=True,
|
|
182
|
+
timeout=5,
|
|
183
|
+
)
|
|
184
|
+
return result.returncode == 0
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _notify_linux(title: str, message: str, level: NotificationLevel) -> bool:
|
|
188
|
+
"""Send notification on Linux using notify-send."""
|
|
189
|
+
# Map level to urgency
|
|
190
|
+
urgency_map = {
|
|
191
|
+
NotificationLevel.INFO: "normal",
|
|
192
|
+
NotificationLevel.SUCCESS: "low",
|
|
193
|
+
NotificationLevel.WARNING: "normal",
|
|
194
|
+
NotificationLevel.ERROR: "critical",
|
|
195
|
+
}
|
|
196
|
+
urgency = urgency_map.get(level, "normal")
|
|
197
|
+
|
|
198
|
+
# Map level to icon
|
|
199
|
+
icon_map = {
|
|
200
|
+
NotificationLevel.INFO: "dialog-information",
|
|
201
|
+
NotificationLevel.SUCCESS: "emblem-ok-symbolic",
|
|
202
|
+
NotificationLevel.WARNING: "dialog-warning",
|
|
203
|
+
NotificationLevel.ERROR: "dialog-error",
|
|
204
|
+
}
|
|
205
|
+
icon = icon_map.get(level, "dialog-information")
|
|
206
|
+
|
|
207
|
+
cmd = [
|
|
208
|
+
"notify-send",
|
|
209
|
+
"--urgency",
|
|
210
|
+
urgency,
|
|
211
|
+
"--icon",
|
|
212
|
+
icon,
|
|
213
|
+
"--expire-time",
|
|
214
|
+
str(_config.timeout_seconds * 1000), # Convert to ms
|
|
215
|
+
"--app-name",
|
|
216
|
+
"SuperQode",
|
|
217
|
+
title,
|
|
218
|
+
message,
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
result = subprocess.run(cmd, capture_output=True, timeout=5)
|
|
222
|
+
return result.returncode == 0
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _notify_windows(title: str, message: str, level: NotificationLevel) -> bool:
|
|
226
|
+
"""Send notification on Windows using PowerShell toast."""
|
|
227
|
+
# PowerShell toast notification
|
|
228
|
+
ps_script = f"""
|
|
229
|
+
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
|
|
230
|
+
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom, ContentType = WindowsRuntime] | Out-Null
|
|
231
|
+
|
|
232
|
+
$template = @"
|
|
233
|
+
<toast>
|
|
234
|
+
<visual>
|
|
235
|
+
<binding template="ToastText02">
|
|
236
|
+
<text id="1">{_escape_xml(title)}</text>
|
|
237
|
+
<text id="2">{_escape_xml(message)}</text>
|
|
238
|
+
</binding>
|
|
239
|
+
</visual>
|
|
240
|
+
</toast>
|
|
241
|
+
"@
|
|
242
|
+
|
|
243
|
+
$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
|
|
244
|
+
$xml.LoadXml($template)
|
|
245
|
+
$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
|
|
246
|
+
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("SuperQode").Show($toast)
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
result = subprocess.run(
|
|
250
|
+
["powershell", "-Command", ps_script],
|
|
251
|
+
capture_output=True,
|
|
252
|
+
timeout=10,
|
|
253
|
+
)
|
|
254
|
+
return result.returncode == 0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _escape_applescript(text: str) -> str:
|
|
258
|
+
"""Escape text for AppleScript string."""
|
|
259
|
+
return text.replace("\\", "\\\\").replace('"', '\\"')
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _escape_xml(text: str) -> str:
|
|
263
|
+
"""Escape text for XML."""
|
|
264
|
+
return (
|
|
265
|
+
text.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """)
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# =============================================================================
|
|
270
|
+
# CONVENIENCE FUNCTIONS FOR COMMON NOTIFICATIONS
|
|
271
|
+
# =============================================================================
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def notify_qe_complete(
|
|
275
|
+
findings_count: int,
|
|
276
|
+
duration_seconds: float,
|
|
277
|
+
success: bool = True,
|
|
278
|
+
) -> bool:
|
|
279
|
+
"""
|
|
280
|
+
Notify when a QE run completes.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
findings_count: Number of findings discovered.
|
|
284
|
+
duration_seconds: How long the QE run took.
|
|
285
|
+
success: Whether the run completed successfully.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
True if notification was sent, False otherwise.
|
|
289
|
+
"""
|
|
290
|
+
duration_str = f"{duration_seconds:.1f}s"
|
|
291
|
+
if duration_seconds > 60:
|
|
292
|
+
minutes = int(duration_seconds // 60)
|
|
293
|
+
seconds = int(duration_seconds % 60)
|
|
294
|
+
duration_str = f"{minutes}m {seconds}s"
|
|
295
|
+
|
|
296
|
+
if success:
|
|
297
|
+
if findings_count == 0:
|
|
298
|
+
title = "QE Complete - No Issues!"
|
|
299
|
+
message = f"All checks passed in {duration_str}"
|
|
300
|
+
level = NotificationLevel.SUCCESS
|
|
301
|
+
else:
|
|
302
|
+
title = f"QE Complete - {findings_count} Finding{'s' if findings_count != 1 else ''}"
|
|
303
|
+
message = f"Completed in {duration_str}. Review findings in SuperQode."
|
|
304
|
+
level = NotificationLevel.WARNING
|
|
305
|
+
else:
|
|
306
|
+
title = "QE Run Failed"
|
|
307
|
+
message = f"Run failed after {duration_str}. Check logs for details."
|
|
308
|
+
level = NotificationLevel.ERROR
|
|
309
|
+
|
|
310
|
+
return notify(title, message, level, subtitle="SuperQE")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def notify_task_complete(task_name: str, success: bool = True) -> bool:
|
|
314
|
+
"""
|
|
315
|
+
Notify when a long-running task completes.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
task_name: Name of the task that completed.
|
|
319
|
+
success: Whether the task succeeded.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
True if notification was sent, False otherwise.
|
|
323
|
+
"""
|
|
324
|
+
if success:
|
|
325
|
+
return notify(
|
|
326
|
+
"Task Complete",
|
|
327
|
+
f"{task_name} finished successfully.",
|
|
328
|
+
NotificationLevel.SUCCESS,
|
|
329
|
+
)
|
|
330
|
+
else:
|
|
331
|
+
return notify(
|
|
332
|
+
"Task Failed",
|
|
333
|
+
f"{task_name} encountered an error.",
|
|
334
|
+
NotificationLevel.ERROR,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def notify_agent_ready(agent_name: str) -> bool:
|
|
339
|
+
"""
|
|
340
|
+
Notify when an agent is ready for interaction.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
agent_name: Name of the agent that's ready.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
True if notification was sent, False otherwise.
|
|
347
|
+
"""
|
|
348
|
+
return notify(
|
|
349
|
+
"Agent Ready",
|
|
350
|
+
f"{agent_name} is connected and ready.",
|
|
351
|
+
NotificationLevel.INFO,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def notify_permission_required(action: str) -> bool:
|
|
356
|
+
"""
|
|
357
|
+
Notify when user permission is required.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
action: Description of the action requiring permission.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
True if notification was sent, False otherwise.
|
|
364
|
+
"""
|
|
365
|
+
return notify(
|
|
366
|
+
"Permission Required",
|
|
367
|
+
f"SuperQode needs approval for: {action}",
|
|
368
|
+
NotificationLevel.WARNING,
|
|
369
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""SuperOpt configuration loader (OSS command-based)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from superqode.config.schema import OptimizeConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_optimize_config(project_root: Path) -> OptimizeConfig:
|
|
14
|
+
"""Load SuperOpt config from superqode.yaml."""
|
|
15
|
+
config_path = project_root / "superqode.yaml"
|
|
16
|
+
if not config_path.exists():
|
|
17
|
+
return OptimizeConfig()
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
data: Dict[str, Any] = yaml.safe_load(config_path.read_text()) or {}
|
|
21
|
+
except Exception:
|
|
22
|
+
return OptimizeConfig()
|
|
23
|
+
|
|
24
|
+
optimize_data = data.get("superqode", {}).get("qe", {}).get("optimize", {})
|
|
25
|
+
|
|
26
|
+
if not isinstance(optimize_data, dict):
|
|
27
|
+
return OptimizeConfig()
|
|
28
|
+
|
|
29
|
+
return OptimizeConfig(
|
|
30
|
+
enabled=optimize_data.get("enabled", False),
|
|
31
|
+
command=optimize_data.get("command", optimize_data.get("cmd", "")),
|
|
32
|
+
timeout_seconds=optimize_data.get("timeout_seconds", optimize_data.get("timeout", 300)),
|
|
33
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode Permission System.
|
|
3
|
+
|
|
4
|
+
Rule-based access control for agent operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .rules import (
|
|
8
|
+
PermissionAction,
|
|
9
|
+
PermissionScope,
|
|
10
|
+
PermissionRule,
|
|
11
|
+
PermissionRequest,
|
|
12
|
+
PermissionDecision,
|
|
13
|
+
PermissionManager,
|
|
14
|
+
create_permission_request,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"PermissionAction",
|
|
19
|
+
"PermissionScope",
|
|
20
|
+
"PermissionRule",
|
|
21
|
+
"PermissionRequest",
|
|
22
|
+
"PermissionDecision",
|
|
23
|
+
"PermissionManager",
|
|
24
|
+
"create_permission_request",
|
|
25
|
+
]
|