superqode 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,417 @@
1
+ """
2
+ Unified Logging Core for SuperQode.
3
+
4
+ Provides structured log entries and a unified logger that works consistently
5
+ across all provider modes (ACP, BYOK, Local/Ollama).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum
12
+ from time import monotonic
13
+ from typing import Any, Callable, Literal, Optional, Protocol
14
+ import uuid
15
+
16
+
17
+ class LogVerbosity(str, Enum):
18
+ """Log verbosity levels."""
19
+
20
+ MINIMAL = "minimal" # Just status, no content
21
+ NORMAL = "normal" # Summarized content
22
+ VERBOSE = "verbose" # Full content (with truncation limits)
23
+
24
+
25
+ LogKind = Literal[
26
+ "user",
27
+ "assistant",
28
+ "thinking",
29
+ "tool_call",
30
+ "tool_update",
31
+ "tool_result",
32
+ "info",
33
+ "warning",
34
+ "error",
35
+ "system",
36
+ "response_delta",
37
+ "response_final",
38
+ "code_block",
39
+ ]
40
+
41
+ LogSource = Literal["acp", "byok", "local", "system"]
42
+
43
+
44
+ @dataclass
45
+ class LogConfig:
46
+ """Configuration for log display behavior."""
47
+
48
+ verbosity: LogVerbosity = LogVerbosity.NORMAL
49
+ show_thinking: bool = True
50
+ show_tool_args: bool = True
51
+ show_tool_result: bool = True
52
+ max_tool_output_chars: int = 2000
53
+ max_thinking_chars: int = 500
54
+ syntax_highlight: bool = True
55
+ code_theme: str = "github-dark"
56
+
57
+ @classmethod
58
+ def minimal(cls) -> LogConfig:
59
+ """Create minimal verbosity config."""
60
+ return cls(
61
+ verbosity=LogVerbosity.MINIMAL,
62
+ show_thinking=False,
63
+ show_tool_args=False,
64
+ show_tool_result=False,
65
+ )
66
+
67
+ @classmethod
68
+ def normal(cls) -> LogConfig:
69
+ """Create normal verbosity config."""
70
+ return cls(
71
+ verbosity=LogVerbosity.NORMAL,
72
+ show_thinking=True,
73
+ show_tool_args=True,
74
+ show_tool_result=True,
75
+ max_tool_output_chars=200,
76
+ )
77
+
78
+ @classmethod
79
+ def verbose(cls) -> LogConfig:
80
+ """Create verbose config."""
81
+ return cls(
82
+ verbosity=LogVerbosity.VERBOSE,
83
+ show_thinking=True,
84
+ show_tool_args=True,
85
+ show_tool_result=True,
86
+ max_tool_output_chars=2000,
87
+ )
88
+
89
+ @classmethod
90
+ def for_source(cls, source: LogSource) -> LogConfig:
91
+ """Get recommended config for a source type."""
92
+ if source == "local":
93
+ # Local models can be verbose, default to less thinking display
94
+ return cls(
95
+ verbosity=LogVerbosity.NORMAL,
96
+ show_thinking=False, # Toggle with Ctrl+T
97
+ show_tool_args=True,
98
+ show_tool_result=True,
99
+ max_tool_output_chars=500,
100
+ )
101
+ elif source == "acp":
102
+ return cls(
103
+ verbosity=LogVerbosity.NORMAL,
104
+ show_thinking=True,
105
+ show_tool_args=True,
106
+ show_tool_result=True,
107
+ )
108
+ else: # byok
109
+ return cls.normal()
110
+
111
+
112
+ @dataclass
113
+ class LogEntry:
114
+ """
115
+ A structured log entry.
116
+
117
+ This is the single source of truth for all log events across providers.
118
+ """
119
+
120
+ kind: LogKind
121
+ source: LogSource
122
+ text: str = ""
123
+ data: dict[str, Any] = field(default_factory=dict)
124
+ agent: str = "Assistant"
125
+ ts: float = field(default_factory=monotonic)
126
+ span_id: Optional[str] = None
127
+ level: int = 0 # 0=always, 1=normal+verbose, 2=verbose only
128
+
129
+ def __post_init__(self):
130
+ if self.span_id is None and self.kind in ("tool_call", "tool_update", "tool_result"):
131
+ self.span_id = str(uuid.uuid4())[:8]
132
+
133
+ @property
134
+ def tool_name(self) -> str:
135
+ """Get tool name from data."""
136
+ return self.data.get("tool_name", "")
137
+
138
+ @property
139
+ def tool_args(self) -> dict:
140
+ """Get tool arguments from data."""
141
+ return self.data.get("args", {})
142
+
143
+ @property
144
+ def tool_result_text(self) -> str:
145
+ """Get tool result text from data."""
146
+ return str(self.data.get("result", ""))
147
+
148
+ @property
149
+ def is_success(self) -> bool:
150
+ """Check if tool result was successful."""
151
+ return self.data.get("ok", True)
152
+
153
+ @property
154
+ def file_path(self) -> str:
155
+ """Extract file path from tool args."""
156
+ args = self.tool_args
157
+ return args.get("path", args.get("file_path", args.get("filePath", "")))
158
+
159
+ @property
160
+ def command(self) -> str:
161
+ """Extract command from tool args."""
162
+ return self.tool_args.get("command", "")
163
+
164
+ @classmethod
165
+ def thinking(
166
+ cls,
167
+ text: str,
168
+ source: LogSource = "byok",
169
+ category: str = "general",
170
+ ) -> LogEntry:
171
+ """Create a thinking log entry."""
172
+ return cls(
173
+ kind="thinking",
174
+ source=source,
175
+ text=text,
176
+ data={"category": category},
177
+ )
178
+
179
+ @classmethod
180
+ def tool_call(
181
+ cls,
182
+ name: str,
183
+ args: dict,
184
+ source: LogSource = "byok",
185
+ span_id: Optional[str] = None,
186
+ ) -> LogEntry:
187
+ """Create a tool call log entry."""
188
+ return cls(
189
+ kind="tool_call",
190
+ source=source,
191
+ text=f"Calling {name}",
192
+ data={"tool_name": name, "args": args},
193
+ span_id=span_id or str(uuid.uuid4())[:8],
194
+ )
195
+
196
+ @classmethod
197
+ def tool_result(
198
+ cls,
199
+ name: str,
200
+ result: Any,
201
+ success: bool = True,
202
+ source: LogSource = "byok",
203
+ span_id: Optional[str] = None,
204
+ ) -> LogEntry:
205
+ """Create a tool result log entry."""
206
+ result_text = str(result) if result else ""
207
+ return cls(
208
+ kind="tool_result",
209
+ source=source,
210
+ text=f"{name} {'completed' if success else 'failed'}",
211
+ data={"tool_name": name, "result": result_text, "ok": success},
212
+ span_id=span_id,
213
+ )
214
+
215
+ @classmethod
216
+ def response(
217
+ cls,
218
+ text: str,
219
+ source: LogSource = "byok",
220
+ agent: str = "Assistant",
221
+ is_final: bool = False,
222
+ ) -> LogEntry:
223
+ """Create a response log entry."""
224
+ return cls(
225
+ kind="response_final" if is_final else "response_delta",
226
+ source=source,
227
+ text=text,
228
+ agent=agent,
229
+ )
230
+
231
+ @classmethod
232
+ def code_block(
233
+ cls,
234
+ code: str,
235
+ language: str = "",
236
+ source: LogSource = "local",
237
+ ) -> LogEntry:
238
+ """Create a code block log entry for proper syntax highlighting."""
239
+ return cls(
240
+ kind="code_block",
241
+ source=source,
242
+ text=code,
243
+ data={"language": language},
244
+ )
245
+
246
+ @classmethod
247
+ def info(cls, text: str, source: LogSource = "system") -> LogEntry:
248
+ """Create an info log entry."""
249
+ return cls(kind="info", source=source, text=text)
250
+
251
+ @classmethod
252
+ def error(cls, text: str, source: LogSource = "system") -> LogEntry:
253
+ """Create an error log entry."""
254
+ return cls(kind="error", source=source, text=text)
255
+
256
+ @classmethod
257
+ def warning(cls, text: str, source: LogSource = "system") -> LogEntry:
258
+ """Create a warning log entry."""
259
+ return cls(kind="warning", source=source, text=text)
260
+
261
+
262
+ class LogSink(Protocol):
263
+ """Protocol for log output destinations."""
264
+
265
+ def emit(self, entry: LogEntry, config: LogConfig) -> None:
266
+ """Emit a log entry."""
267
+ ...
268
+
269
+
270
+ class UnifiedLogger:
271
+ """
272
+ Unified logger that routes events to sinks based on configuration.
273
+
274
+ This is the central routing point for all log events across providers.
275
+ """
276
+
277
+ def __init__(
278
+ self,
279
+ config: Optional[LogConfig] = None,
280
+ sink: Optional[LogSink] = None,
281
+ ):
282
+ self.config = config or LogConfig.normal()
283
+ self._sinks: list[LogSink] = []
284
+ if sink:
285
+ self._sinks.append(sink)
286
+ self._buffer: list[LogEntry] = []
287
+ self._response_buffer: str = ""
288
+ self._on_entry: Optional[Callable[[LogEntry], None]] = None
289
+
290
+ def add_sink(self, sink: LogSink) -> None:
291
+ """Add a log sink."""
292
+ self._sinks.append(sink)
293
+
294
+ def remove_sink(self, sink: LogSink) -> None:
295
+ """Remove a log sink."""
296
+ if sink in self._sinks:
297
+ self._sinks.remove(sink)
298
+
299
+ def set_verbosity(self, verbosity: LogVerbosity) -> None:
300
+ """Change verbosity level."""
301
+ self.config.verbosity = verbosity
302
+ if verbosity == LogVerbosity.MINIMAL:
303
+ self.config.show_thinking = False
304
+ self.config.show_tool_args = False
305
+ self.config.show_tool_result = False
306
+ elif verbosity == LogVerbosity.NORMAL:
307
+ self.config.show_thinking = True
308
+ self.config.show_tool_args = True
309
+ self.config.show_tool_result = True
310
+ self.config.max_tool_output_chars = 200
311
+ else: # verbose
312
+ self.config.show_thinking = True
313
+ self.config.show_tool_args = True
314
+ self.config.show_tool_result = True
315
+ self.config.max_tool_output_chars = 2000
316
+
317
+ def toggle_thinking(self) -> bool:
318
+ """Toggle thinking display. Returns new state."""
319
+ self.config.show_thinking = not self.config.show_thinking
320
+ return self.config.show_thinking
321
+
322
+ def _should_emit(self, entry: LogEntry) -> bool:
323
+ """Check if entry should be emitted based on config."""
324
+ # Check verbosity level
325
+ if entry.level == 2 and self.config.verbosity != LogVerbosity.VERBOSE:
326
+ return False
327
+ if entry.level == 1 and self.config.verbosity == LogVerbosity.MINIMAL:
328
+ return False
329
+
330
+ # Check thinking filter
331
+ if entry.kind == "thinking" and not self.config.show_thinking:
332
+ return False
333
+
334
+ return True
335
+
336
+ def log(self, entry: LogEntry) -> None:
337
+ """Log an entry to all sinks."""
338
+ self._buffer.append(entry)
339
+
340
+ if self._on_entry:
341
+ self._on_entry(entry)
342
+
343
+ if not self._should_emit(entry):
344
+ return
345
+
346
+ for sink in self._sinks:
347
+ try:
348
+ sink.emit(entry, self.config)
349
+ except Exception:
350
+ pass # Don't let sink errors crash logging
351
+
352
+ def thinking(self, text: str, source: LogSource = "byok", category: str = "general") -> None:
353
+ """Log a thinking entry."""
354
+ self.log(LogEntry.thinking(text, source, category))
355
+
356
+ def tool_call(
357
+ self,
358
+ name: str,
359
+ args: dict,
360
+ source: LogSource = "byok",
361
+ span_id: Optional[str] = None,
362
+ ) -> str:
363
+ """Log a tool call. Returns span_id for correlation."""
364
+ entry = LogEntry.tool_call(name, args, source, span_id)
365
+ self.log(entry)
366
+ return entry.span_id or ""
367
+
368
+ def tool_result(
369
+ self,
370
+ name: str,
371
+ result: Any,
372
+ success: bool = True,
373
+ source: LogSource = "byok",
374
+ span_id: Optional[str] = None,
375
+ ) -> None:
376
+ """Log a tool result."""
377
+ self.log(LogEntry.tool_result(name, result, success, source, span_id))
378
+
379
+ def response_chunk(
380
+ self, text: str, source: LogSource = "byok", agent: str = "Assistant"
381
+ ) -> None:
382
+ """Log a response chunk (streaming)."""
383
+ self._response_buffer += text
384
+ self.log(LogEntry.response(text, source, agent, is_final=False))
385
+
386
+ def response_complete(self, source: LogSource = "byok", agent: str = "Assistant") -> str:
387
+ """Complete response streaming. Returns full response."""
388
+ full_response = self._response_buffer
389
+ if full_response:
390
+ self.log(LogEntry.response(full_response, source, agent, is_final=True))
391
+ self._response_buffer = ""
392
+ return full_response
393
+
394
+ def code_block(self, code: str, language: str = "", source: LogSource = "local") -> None:
395
+ """Log a code block with syntax highlighting."""
396
+ self.log(LogEntry.code_block(code, language, source))
397
+
398
+ def info(self, text: str) -> None:
399
+ """Log info message."""
400
+ self.log(LogEntry.info(text))
401
+
402
+ def error(self, text: str) -> None:
403
+ """Log error message."""
404
+ self.log(LogEntry.error(text))
405
+
406
+ def warning(self, text: str) -> None:
407
+ """Log warning message."""
408
+ self.log(LogEntry.warning(text))
409
+
410
+ def get_history(self) -> list[LogEntry]:
411
+ """Get log history."""
412
+ return self._buffer.copy()
413
+
414
+ def clear(self) -> None:
415
+ """Clear log history."""
416
+ self._buffer.clear()
417
+ self._response_buffer = ""
@@ -0,0 +1,26 @@
1
+ """
2
+ SuperQode LSP Integration.
3
+
4
+ Language Server Protocol support for real-time diagnostics,
5
+ code intelligence, and IDE-like features.
6
+ """
7
+
8
+ from .client import (
9
+ LSPClient,
10
+ LSPConfig,
11
+ Diagnostic,
12
+ DiagnosticSeverity,
13
+ Location,
14
+ Position,
15
+ Range,
16
+ )
17
+
18
+ __all__ = [
19
+ "LSPClient",
20
+ "LSPConfig",
21
+ "Diagnostic",
22
+ "DiagnosticSeverity",
23
+ "Location",
24
+ "Position",
25
+ "Range",
26
+ ]