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/file_viewer.py
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode File Viewer - Beautiful File Content Display
|
|
3
|
+
|
|
4
|
+
A rich file viewer with:
|
|
5
|
+
- Syntax highlighting for 100+ languages
|
|
6
|
+
- Line numbers
|
|
7
|
+
- Search highlighting
|
|
8
|
+
- Scrollable content
|
|
9
|
+
- File info header
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import mimetypes
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Optional, List, Tuple
|
|
19
|
+
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
from rich.panel import Panel
|
|
22
|
+
from rich.syntax import Syntax
|
|
23
|
+
from rich.text import Text
|
|
24
|
+
from rich.table import Table
|
|
25
|
+
from rich.box import ROUNDED, SIMPLE
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Language detection by extension
|
|
29
|
+
LANGUAGE_MAP = {
|
|
30
|
+
# Python
|
|
31
|
+
".py": "python",
|
|
32
|
+
".pyw": "python",
|
|
33
|
+
".pyi": "python",
|
|
34
|
+
".pyx": "python",
|
|
35
|
+
# JavaScript/TypeScript
|
|
36
|
+
".js": "javascript",
|
|
37
|
+
".jsx": "jsx",
|
|
38
|
+
".ts": "typescript",
|
|
39
|
+
".tsx": "tsx",
|
|
40
|
+
".mjs": "javascript",
|
|
41
|
+
".cjs": "javascript",
|
|
42
|
+
# Web
|
|
43
|
+
".html": "html",
|
|
44
|
+
".htm": "html",
|
|
45
|
+
".css": "css",
|
|
46
|
+
".scss": "scss",
|
|
47
|
+
".sass": "sass",
|
|
48
|
+
".less": "less",
|
|
49
|
+
".vue": "vue",
|
|
50
|
+
".svelte": "svelte",
|
|
51
|
+
# Data formats
|
|
52
|
+
".json": "json",
|
|
53
|
+
".yaml": "yaml",
|
|
54
|
+
".yml": "yaml",
|
|
55
|
+
".toml": "toml",
|
|
56
|
+
".xml": "xml",
|
|
57
|
+
".csv": "text",
|
|
58
|
+
# Shell
|
|
59
|
+
".sh": "bash",
|
|
60
|
+
".bash": "bash",
|
|
61
|
+
".zsh": "zsh",
|
|
62
|
+
".fish": "fish",
|
|
63
|
+
".ps1": "powershell",
|
|
64
|
+
".bat": "batch",
|
|
65
|
+
".cmd": "batch",
|
|
66
|
+
# Systems
|
|
67
|
+
".c": "c",
|
|
68
|
+
".h": "c",
|
|
69
|
+
".cpp": "cpp",
|
|
70
|
+
".hpp": "cpp",
|
|
71
|
+
".cc": "cpp",
|
|
72
|
+
".cxx": "cpp",
|
|
73
|
+
".rs": "rust",
|
|
74
|
+
".go": "go",
|
|
75
|
+
".java": "java",
|
|
76
|
+
".kt": "kotlin",
|
|
77
|
+
".kts": "kotlin",
|
|
78
|
+
".scala": "scala",
|
|
79
|
+
".swift": "swift",
|
|
80
|
+
# Other languages
|
|
81
|
+
".rb": "ruby",
|
|
82
|
+
".php": "php",
|
|
83
|
+
".pl": "perl",
|
|
84
|
+
".lua": "lua",
|
|
85
|
+
".r": "r",
|
|
86
|
+
".R": "r",
|
|
87
|
+
".jl": "julia",
|
|
88
|
+
".ex": "elixir",
|
|
89
|
+
".exs": "elixir",
|
|
90
|
+
".erl": "erlang",
|
|
91
|
+
".hs": "haskell",
|
|
92
|
+
".ml": "ocaml",
|
|
93
|
+
".fs": "fsharp",
|
|
94
|
+
".clj": "clojure",
|
|
95
|
+
".lisp": "lisp",
|
|
96
|
+
".scm": "scheme",
|
|
97
|
+
# Config files
|
|
98
|
+
".ini": "ini",
|
|
99
|
+
".cfg": "ini",
|
|
100
|
+
".conf": "ini",
|
|
101
|
+
".env": "dotenv",
|
|
102
|
+
".gitignore": "gitignore",
|
|
103
|
+
".dockerignore": "gitignore",
|
|
104
|
+
".editorconfig": "ini",
|
|
105
|
+
# Documentation
|
|
106
|
+
".md": "markdown",
|
|
107
|
+
".markdown": "markdown",
|
|
108
|
+
".rst": "rst",
|
|
109
|
+
".txt": "text",
|
|
110
|
+
".log": "text",
|
|
111
|
+
# Database
|
|
112
|
+
".sql": "sql",
|
|
113
|
+
".sqlite": "sql",
|
|
114
|
+
# Build/Config
|
|
115
|
+
"Makefile": "makefile",
|
|
116
|
+
"Dockerfile": "dockerfile",
|
|
117
|
+
".dockerfile": "dockerfile",
|
|
118
|
+
"Vagrantfile": "ruby",
|
|
119
|
+
"Gemfile": "ruby",
|
|
120
|
+
"Rakefile": "ruby",
|
|
121
|
+
"CMakeLists.txt": "cmake",
|
|
122
|
+
# Other
|
|
123
|
+
".diff": "diff",
|
|
124
|
+
".patch": "diff",
|
|
125
|
+
".graphql": "graphql",
|
|
126
|
+
".proto": "protobuf",
|
|
127
|
+
".tf": "terraform",
|
|
128
|
+
".hcl": "hcl",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# File type icons
|
|
132
|
+
FILE_ICONS = {
|
|
133
|
+
"python": "🐍",
|
|
134
|
+
"javascript": "📜",
|
|
135
|
+
"typescript": "💠",
|
|
136
|
+
"html": "🌐",
|
|
137
|
+
"css": "🎨",
|
|
138
|
+
"json": "📋",
|
|
139
|
+
"yaml": "⚙️",
|
|
140
|
+
"markdown": "📝",
|
|
141
|
+
"rust": "🦀",
|
|
142
|
+
"go": "🐹",
|
|
143
|
+
"java": "☕",
|
|
144
|
+
"ruby": "💎",
|
|
145
|
+
"php": "🐘",
|
|
146
|
+
"bash": "💻",
|
|
147
|
+
"sql": "🗄️",
|
|
148
|
+
"dockerfile": "🐳",
|
|
149
|
+
"default": "📄",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# SuperQode viewer colors
|
|
153
|
+
VIEWER_COLORS = {
|
|
154
|
+
"header": "#a855f7",
|
|
155
|
+
"border": "#2a2a2a",
|
|
156
|
+
"line_no": "#52525b",
|
|
157
|
+
"highlight": "#f59e0b30",
|
|
158
|
+
"search": "#22c55e40",
|
|
159
|
+
"info": "#71717a",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass
|
|
164
|
+
class FileInfo:
|
|
165
|
+
"""Information about a file."""
|
|
166
|
+
|
|
167
|
+
path: str
|
|
168
|
+
name: str
|
|
169
|
+
extension: str
|
|
170
|
+
language: str
|
|
171
|
+
size: int
|
|
172
|
+
lines: int
|
|
173
|
+
encoding: str = "utf-8"
|
|
174
|
+
is_binary: bool = False
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def detect_language(path: str) -> str:
|
|
178
|
+
"""Detect the programming language from file path."""
|
|
179
|
+
p = Path(path)
|
|
180
|
+
name = p.name
|
|
181
|
+
ext = p.suffix.lower()
|
|
182
|
+
|
|
183
|
+
# Check exact filename matches first
|
|
184
|
+
if name in LANGUAGE_MAP:
|
|
185
|
+
return LANGUAGE_MAP[name]
|
|
186
|
+
|
|
187
|
+
# Check extension
|
|
188
|
+
if ext in LANGUAGE_MAP:
|
|
189
|
+
return LANGUAGE_MAP[ext]
|
|
190
|
+
|
|
191
|
+
return "text"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def get_file_icon(language: str) -> str:
|
|
195
|
+
"""Get an icon for a language."""
|
|
196
|
+
return FILE_ICONS.get(language, FILE_ICONS["default"])
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def get_file_info(path: str) -> FileInfo:
|
|
200
|
+
"""Get information about a file."""
|
|
201
|
+
p = Path(path)
|
|
202
|
+
|
|
203
|
+
if not p.exists():
|
|
204
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
205
|
+
|
|
206
|
+
stat = p.stat()
|
|
207
|
+
ext = p.suffix.lower()
|
|
208
|
+
language = detect_language(path)
|
|
209
|
+
|
|
210
|
+
# Check if binary
|
|
211
|
+
is_binary = False
|
|
212
|
+
try:
|
|
213
|
+
with open(p, "rb") as f:
|
|
214
|
+
chunk = f.read(8192)
|
|
215
|
+
if b"\x00" in chunk:
|
|
216
|
+
is_binary = True
|
|
217
|
+
except Exception:
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
# Count lines
|
|
221
|
+
lines = 0
|
|
222
|
+
if not is_binary:
|
|
223
|
+
try:
|
|
224
|
+
with open(p, "r", encoding="utf-8") as f:
|
|
225
|
+
lines = sum(1 for _ in f)
|
|
226
|
+
except Exception:
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
return FileInfo(
|
|
230
|
+
path=str(p.resolve()),
|
|
231
|
+
name=p.name,
|
|
232
|
+
extension=ext,
|
|
233
|
+
language=language,
|
|
234
|
+
size=stat.st_size,
|
|
235
|
+
lines=lines,
|
|
236
|
+
is_binary=is_binary,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def format_size(size: int) -> str:
|
|
241
|
+
"""Format file size in human-readable format."""
|
|
242
|
+
for unit in ["B", "KB", "MB", "GB"]:
|
|
243
|
+
if size < 1024:
|
|
244
|
+
return f"{size:.1f} {unit}" if unit != "B" else f"{size} {unit}"
|
|
245
|
+
size /= 1024
|
|
246
|
+
return f"{size:.1f} TB"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class FileViewer:
|
|
250
|
+
"""Interactive file viewer with syntax highlighting."""
|
|
251
|
+
|
|
252
|
+
def __init__(self, console: Console):
|
|
253
|
+
self.console = console
|
|
254
|
+
self.current_file: Optional[str] = None
|
|
255
|
+
self.content: Optional[str] = None
|
|
256
|
+
self.info: Optional[FileInfo] = None
|
|
257
|
+
self.scroll_offset: int = 0
|
|
258
|
+
self.search_term: Optional[str] = None
|
|
259
|
+
self.highlight_lines: List[int] = []
|
|
260
|
+
|
|
261
|
+
def open(self, path: str) -> bool:
|
|
262
|
+
"""Open a file for viewing."""
|
|
263
|
+
try:
|
|
264
|
+
self.info = get_file_info(path)
|
|
265
|
+
|
|
266
|
+
if self.info.is_binary:
|
|
267
|
+
self.content = None
|
|
268
|
+
return True
|
|
269
|
+
|
|
270
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
271
|
+
self.content = f.read()
|
|
272
|
+
|
|
273
|
+
self.current_file = path
|
|
274
|
+
self.scroll_offset = 0
|
|
275
|
+
self.search_term = None
|
|
276
|
+
self.highlight_lines = []
|
|
277
|
+
return True
|
|
278
|
+
|
|
279
|
+
except Exception as e:
|
|
280
|
+
self.console.print(f" [red]Error opening file: {e}[/red]")
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
def render(
|
|
284
|
+
self,
|
|
285
|
+
start_line: int = 1,
|
|
286
|
+
end_line: Optional[int] = None,
|
|
287
|
+
show_header: bool = True,
|
|
288
|
+
theme: str = "monokai",
|
|
289
|
+
) -> None:
|
|
290
|
+
"""Render the file content."""
|
|
291
|
+
if self.info is None:
|
|
292
|
+
self.console.print(" [dim]No file open[/dim]")
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
if show_header:
|
|
296
|
+
self._render_header()
|
|
297
|
+
|
|
298
|
+
if self.info.is_binary:
|
|
299
|
+
self.console.print(" [dim]Binary file - cannot display content[/dim]")
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
if self.content is None:
|
|
303
|
+
self.console.print(" [dim]No content to display[/dim]")
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
# Get content slice
|
|
307
|
+
lines = self.content.splitlines()
|
|
308
|
+
total_lines = len(lines)
|
|
309
|
+
|
|
310
|
+
if end_line is None:
|
|
311
|
+
end_line = min(start_line + 50, total_lines) # Default 50 lines
|
|
312
|
+
|
|
313
|
+
start_idx = max(0, start_line - 1)
|
|
314
|
+
end_idx = min(end_line, total_lines)
|
|
315
|
+
|
|
316
|
+
content_slice = "\n".join(lines[start_idx:end_idx])
|
|
317
|
+
|
|
318
|
+
# Create syntax highlighted view
|
|
319
|
+
syntax = Syntax(
|
|
320
|
+
content_slice,
|
|
321
|
+
self.info.language,
|
|
322
|
+
theme=theme,
|
|
323
|
+
line_numbers=True,
|
|
324
|
+
start_line=start_line,
|
|
325
|
+
word_wrap=True,
|
|
326
|
+
highlight_lines=set(self.highlight_lines) if self.highlight_lines else None,
|
|
327
|
+
background_color="#000000",
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
self.console.print(
|
|
331
|
+
Panel(syntax, border_style=VIEWER_COLORS["border"], box=ROUNDED, padding=(0, 1))
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Show position info
|
|
335
|
+
if total_lines > end_idx:
|
|
336
|
+
self.console.print(
|
|
337
|
+
f" [dim]Showing lines {start_line}-{end_idx} of {total_lines}[/dim]"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
def _render_header(self) -> None:
|
|
341
|
+
"""Render the file header."""
|
|
342
|
+
if self.info is None:
|
|
343
|
+
return
|
|
344
|
+
|
|
345
|
+
icon = get_file_icon(self.info.language)
|
|
346
|
+
|
|
347
|
+
header = Text()
|
|
348
|
+
header.append(f" {icon} ", style="bold")
|
|
349
|
+
header.append(self.info.name, style="bold white")
|
|
350
|
+
header.append(" ", style="")
|
|
351
|
+
header.append(f"[{self.info.language}]", style="bold cyan")
|
|
352
|
+
header.append(" ", style="")
|
|
353
|
+
header.append(format_size(self.info.size), style="dim")
|
|
354
|
+
header.append(" ", style="")
|
|
355
|
+
header.append(f"{self.info.lines} lines", style="dim")
|
|
356
|
+
|
|
357
|
+
self.console.print(
|
|
358
|
+
Panel(header, border_style=VIEWER_COLORS["header"], box=ROUNDED, padding=(0, 1))
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
def search(self, term: str) -> List[Tuple[int, str]]:
|
|
362
|
+
"""Search for a term in the file content."""
|
|
363
|
+
if self.content is None:
|
|
364
|
+
return []
|
|
365
|
+
|
|
366
|
+
self.search_term = term
|
|
367
|
+
results = []
|
|
368
|
+
self.highlight_lines = []
|
|
369
|
+
|
|
370
|
+
for i, line in enumerate(self.content.splitlines(), 1):
|
|
371
|
+
if term.lower() in line.lower():
|
|
372
|
+
results.append((i, line.strip()))
|
|
373
|
+
self.highlight_lines.append(i)
|
|
374
|
+
|
|
375
|
+
return results
|
|
376
|
+
|
|
377
|
+
def goto_line(self, line: int) -> None:
|
|
378
|
+
"""Jump to a specific line."""
|
|
379
|
+
if self.content is None:
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
total_lines = len(self.content.splitlines())
|
|
383
|
+
line = max(1, min(line, total_lines))
|
|
384
|
+
self.scroll_offset = line - 1
|
|
385
|
+
self.render(start_line=line)
|
|
386
|
+
|
|
387
|
+
def render_search_results(self, results: List[Tuple[int, str]], max_results: int = 10) -> None:
|
|
388
|
+
"""Render search results."""
|
|
389
|
+
if not results:
|
|
390
|
+
self.console.print(f" [dim]No matches found for '{self.search_term}'[/dim]")
|
|
391
|
+
return
|
|
392
|
+
|
|
393
|
+
header = Text()
|
|
394
|
+
header.append(f" 🔍 ", style="bold")
|
|
395
|
+
header.append(f"{len(results)} match(es)", style="bold white")
|
|
396
|
+
header.append(f" for '{self.search_term}'", style="dim")
|
|
397
|
+
|
|
398
|
+
self.console.print(Panel(header, border_style="#06b6d4", box=ROUNDED, padding=(0, 1)))
|
|
399
|
+
|
|
400
|
+
for line_no, content in results[:max_results]:
|
|
401
|
+
line = Text()
|
|
402
|
+
line.append(f" {line_no:>4}: ", style=VIEWER_COLORS["line_no"])
|
|
403
|
+
|
|
404
|
+
# Highlight search term
|
|
405
|
+
content_lower = content.lower()
|
|
406
|
+
term_lower = self.search_term.lower() if self.search_term else ""
|
|
407
|
+
|
|
408
|
+
if term_lower in content_lower:
|
|
409
|
+
idx = content_lower.index(term_lower)
|
|
410
|
+
line.append(content[:idx], style="")
|
|
411
|
+
line.append(content[idx : idx + len(term_lower)], style="bold yellow on #f59e0b30")
|
|
412
|
+
line.append(content[idx + len(term_lower) :], style="")
|
|
413
|
+
else:
|
|
414
|
+
line.append(content, style="")
|
|
415
|
+
|
|
416
|
+
self.console.print(line)
|
|
417
|
+
|
|
418
|
+
if len(results) > max_results:
|
|
419
|
+
self.console.print(f" [dim]... and {len(results) - max_results} more[/dim]")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def render_file(
|
|
423
|
+
console: Console,
|
|
424
|
+
path: str,
|
|
425
|
+
start_line: int = 1,
|
|
426
|
+
end_line: Optional[int] = None,
|
|
427
|
+
theme: str = "monokai",
|
|
428
|
+
) -> None:
|
|
429
|
+
"""Render a file with syntax highlighting (simple interface)."""
|
|
430
|
+
viewer = FileViewer(console)
|
|
431
|
+
if viewer.open(path):
|
|
432
|
+
viewer.render(start_line, end_line, theme=theme)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def render_file_preview(
|
|
436
|
+
console: Console, path: str, max_lines: int = 20, theme: str = "monokai"
|
|
437
|
+
) -> None:
|
|
438
|
+
"""Render a preview of a file (first N lines)."""
|
|
439
|
+
viewer = FileViewer(console)
|
|
440
|
+
if viewer.open(path):
|
|
441
|
+
viewer.render(1, max_lines, theme=theme)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def render_file_info(console: Console, path: str) -> None:
|
|
445
|
+
"""Render file information without content."""
|
|
446
|
+
try:
|
|
447
|
+
info = get_file_info(path)
|
|
448
|
+
icon = get_file_icon(info.language)
|
|
449
|
+
|
|
450
|
+
table = Table(show_header=False, box=SIMPLE, padding=(0, 2))
|
|
451
|
+
table.add_column("Property", style="dim")
|
|
452
|
+
table.add_column("Value", style="white")
|
|
453
|
+
|
|
454
|
+
table.add_row("Name", f"{icon} {info.name}")
|
|
455
|
+
table.add_row("Path", info.path)
|
|
456
|
+
table.add_row("Language", info.language)
|
|
457
|
+
table.add_row("Size", format_size(info.size))
|
|
458
|
+
table.add_row("Lines", str(info.lines))
|
|
459
|
+
table.add_row("Binary", "Yes" if info.is_binary else "No")
|
|
460
|
+
|
|
461
|
+
console.print(
|
|
462
|
+
Panel(
|
|
463
|
+
table,
|
|
464
|
+
title="[bold]File Info[/bold]",
|
|
465
|
+
border_style=VIEWER_COLORS["header"],
|
|
466
|
+
box=ROUNDED,
|
|
467
|
+
)
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
except Exception as e:
|
|
471
|
+
console.print(f" [red]Error: {e}[/red]")
|
superqode/flash.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode Flash Notifications - Temporary Status Messages
|
|
3
|
+
|
|
4
|
+
Beautiful flash notifications with:
|
|
5
|
+
- Semantic styles (success, warning, error, info)
|
|
6
|
+
- Auto-hide with configurable duration
|
|
7
|
+
- Gradient animations
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Optional, Callable
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
import threading
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.text import Text
|
|
21
|
+
from rich.panel import Panel
|
|
22
|
+
from rich.box import ROUNDED
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FlashStyle(Enum):
|
|
26
|
+
"""Flash notification styles."""
|
|
27
|
+
|
|
28
|
+
DEFAULT = "default"
|
|
29
|
+
SUCCESS = "success"
|
|
30
|
+
WARNING = "warning"
|
|
31
|
+
ERROR = "error"
|
|
32
|
+
INFO = "info"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class FlashMessage:
|
|
37
|
+
"""A flash notification message."""
|
|
38
|
+
|
|
39
|
+
content: str
|
|
40
|
+
style: FlashStyle = FlashStyle.DEFAULT
|
|
41
|
+
duration: float = 3.0
|
|
42
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
43
|
+
dismissed: bool = False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# SuperQode flash colors
|
|
47
|
+
FLASH_COLORS = {
|
|
48
|
+
FlashStyle.DEFAULT: {
|
|
49
|
+
"icon": "💬",
|
|
50
|
+
"color": "#a855f7",
|
|
51
|
+
"bg": "#a855f720",
|
|
52
|
+
"border": "#a855f7",
|
|
53
|
+
},
|
|
54
|
+
FlashStyle.SUCCESS: {
|
|
55
|
+
"icon": "✅",
|
|
56
|
+
"color": "#22c55e",
|
|
57
|
+
"bg": "#22c55e20",
|
|
58
|
+
"border": "#22c55e",
|
|
59
|
+
},
|
|
60
|
+
FlashStyle.WARNING: {
|
|
61
|
+
"icon": "⚠️",
|
|
62
|
+
"color": "#f59e0b",
|
|
63
|
+
"bg": "#f59e0b20",
|
|
64
|
+
"border": "#f59e0b",
|
|
65
|
+
},
|
|
66
|
+
FlashStyle.ERROR: {
|
|
67
|
+
"icon": "❌",
|
|
68
|
+
"color": "#ef4444",
|
|
69
|
+
"bg": "#ef444420",
|
|
70
|
+
"border": "#ef4444",
|
|
71
|
+
},
|
|
72
|
+
FlashStyle.INFO: {
|
|
73
|
+
"icon": "ℹ️",
|
|
74
|
+
"color": "#06b6d4",
|
|
75
|
+
"bg": "#06b6d420",
|
|
76
|
+
"border": "#06b6d4",
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class FlashManager:
|
|
82
|
+
"""Manages flash notifications."""
|
|
83
|
+
|
|
84
|
+
def __init__(self, console: Console, default_duration: float = 3.0):
|
|
85
|
+
self.console = console
|
|
86
|
+
self.default_duration = default_duration
|
|
87
|
+
self.messages: list[FlashMessage] = []
|
|
88
|
+
self.history: list[FlashMessage] = []
|
|
89
|
+
self._timer: Optional[threading.Timer] = None
|
|
90
|
+
|
|
91
|
+
def flash(
|
|
92
|
+
self, content: str, style: FlashStyle = FlashStyle.DEFAULT, duration: Optional[float] = None
|
|
93
|
+
) -> FlashMessage:
|
|
94
|
+
"""Show a flash notification."""
|
|
95
|
+
msg = FlashMessage(content=content, style=style, duration=duration or self.default_duration)
|
|
96
|
+
self.messages.append(msg)
|
|
97
|
+
self._render_flash(msg)
|
|
98
|
+
|
|
99
|
+
# Schedule auto-dismiss
|
|
100
|
+
if msg.duration > 0:
|
|
101
|
+
self._schedule_dismiss(msg)
|
|
102
|
+
|
|
103
|
+
return msg
|
|
104
|
+
|
|
105
|
+
def success(self, content: str, duration: Optional[float] = None) -> FlashMessage:
|
|
106
|
+
"""Show a success flash."""
|
|
107
|
+
return self.flash(content, FlashStyle.SUCCESS, duration)
|
|
108
|
+
|
|
109
|
+
def warning(self, content: str, duration: Optional[float] = None) -> FlashMessage:
|
|
110
|
+
"""Show a warning flash."""
|
|
111
|
+
return self.flash(content, FlashStyle.WARNING, duration)
|
|
112
|
+
|
|
113
|
+
def error(self, content: str, duration: Optional[float] = None) -> FlashMessage:
|
|
114
|
+
"""Show an error flash."""
|
|
115
|
+
return self.flash(content, FlashStyle.ERROR, duration)
|
|
116
|
+
|
|
117
|
+
def info(self, content: str, duration: Optional[float] = None) -> FlashMessage:
|
|
118
|
+
"""Show an info flash."""
|
|
119
|
+
return self.flash(content, FlashStyle.INFO, duration)
|
|
120
|
+
|
|
121
|
+
def dismiss(self, msg: FlashMessage) -> None:
|
|
122
|
+
"""Dismiss a flash message."""
|
|
123
|
+
msg.dismissed = True
|
|
124
|
+
if msg in self.messages:
|
|
125
|
+
self.messages.remove(msg)
|
|
126
|
+
self.history.append(msg)
|
|
127
|
+
|
|
128
|
+
def dismiss_all(self) -> None:
|
|
129
|
+
"""Dismiss all flash messages."""
|
|
130
|
+
for msg in list(self.messages):
|
|
131
|
+
self.dismiss(msg)
|
|
132
|
+
|
|
133
|
+
def _schedule_dismiss(self, msg: FlashMessage) -> None:
|
|
134
|
+
"""Schedule auto-dismiss of a message."""
|
|
135
|
+
|
|
136
|
+
def auto_dismiss():
|
|
137
|
+
if not msg.dismissed:
|
|
138
|
+
self.dismiss(msg)
|
|
139
|
+
|
|
140
|
+
timer = threading.Timer(msg.duration, auto_dismiss)
|
|
141
|
+
timer.daemon = True
|
|
142
|
+
timer.start()
|
|
143
|
+
|
|
144
|
+
def _render_flash(self, msg: FlashMessage) -> None:
|
|
145
|
+
"""Render a flash message."""
|
|
146
|
+
style_config = FLASH_COLORS.get(msg.style, FLASH_COLORS[FlashStyle.DEFAULT])
|
|
147
|
+
|
|
148
|
+
text = Text()
|
|
149
|
+
text.append(f" {style_config['icon']} ", style=f"bold {style_config['color']}")
|
|
150
|
+
text.append(msg.content, style=style_config["color"])
|
|
151
|
+
|
|
152
|
+
self.console.print(text)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def render_flash(console: Console, content: str, style: FlashStyle = FlashStyle.DEFAULT) -> None:
|
|
156
|
+
"""Render a simple flash message (stateless)."""
|
|
157
|
+
style_config = FLASH_COLORS.get(style, FLASH_COLORS[FlashStyle.DEFAULT])
|
|
158
|
+
|
|
159
|
+
text = Text()
|
|
160
|
+
text.append(f" {style_config['icon']} ", style=f"bold {style_config['color']}")
|
|
161
|
+
text.append(content, style=style_config["color"])
|
|
162
|
+
|
|
163
|
+
console.print(text)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def flash_success(console: Console, content: str) -> None:
|
|
167
|
+
"""Show a success flash."""
|
|
168
|
+
render_flash(console, content, FlashStyle.SUCCESS)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def flash_warning(console: Console, content: str) -> None:
|
|
172
|
+
"""Show a warning flash."""
|
|
173
|
+
render_flash(console, content, FlashStyle.WARNING)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def flash_error(console: Console, content: str) -> None:
|
|
177
|
+
"""Show an error flash."""
|
|
178
|
+
render_flash(console, content, FlashStyle.ERROR)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def flash_info(console: Console, content: str) -> None:
|
|
182
|
+
"""Show an info flash."""
|
|
183
|
+
render_flash(console, content, FlashStyle.INFO)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode QE Guidance - System prompts for time-constrained QE.
|
|
3
|
+
|
|
4
|
+
Provides verification-first guidance for QE agents to prevent:
|
|
5
|
+
- False positives (claiming success without proof)
|
|
6
|
+
- Shallow testing (skipping edge cases)
|
|
7
|
+
- Time wasting (long speculative work before feedback)
|
|
8
|
+
|
|
9
|
+
Configuration is driven entirely by superqode.yaml:
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
superqode:
|
|
13
|
+
qe:
|
|
14
|
+
guidance:
|
|
15
|
+
enabled: true
|
|
16
|
+
|
|
17
|
+
# Mode-specific timeouts
|
|
18
|
+
quick_scan:
|
|
19
|
+
timeout_seconds: 60
|
|
20
|
+
verification_first: true
|
|
21
|
+
fail_fast: true
|
|
22
|
+
|
|
23
|
+
deep_qe:
|
|
24
|
+
timeout_seconds: 1800
|
|
25
|
+
exploration_allowed: true
|
|
26
|
+
destructive_testing: true
|
|
27
|
+
|
|
28
|
+
# Anti-patterns to detect
|
|
29
|
+
anti_patterns:
|
|
30
|
+
- skip_verification
|
|
31
|
+
- unconditional_success
|
|
32
|
+
- broad_exception_swallow
|
|
33
|
+
- weaken_tests
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Integrates with:
|
|
37
|
+
- CI: `superqe run --mode quick` (proactive)
|
|
38
|
+
- TUI: Interactive QE sessions (reactive)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from .prompts import (
|
|
42
|
+
QEGuidance,
|
|
43
|
+
GuidanceMode,
|
|
44
|
+
get_qe_system_prompt,
|
|
45
|
+
get_qe_review_prompt,
|
|
46
|
+
get_qe_goal_suffix,
|
|
47
|
+
)
|
|
48
|
+
from .config import GuidanceConfig, load_guidance_config
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"QEGuidance",
|
|
52
|
+
"GuidanceMode",
|
|
53
|
+
"get_qe_system_prompt",
|
|
54
|
+
"get_qe_review_prompt",
|
|
55
|
+
"get_qe_goal_suffix",
|
|
56
|
+
"GuidanceConfig",
|
|
57
|
+
"load_guidance_config",
|
|
58
|
+
]
|