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,118 @@
1
+ """
2
+ SuperQode App Models - Agent data structures and helpers.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import concurrent.futures
9
+ import shutil
10
+ from dataclasses import dataclass
11
+ from enum import Enum
12
+
13
+ from .constants import AGENT_COLORS, AGENT_ICONS, THEME
14
+
15
+
16
+ class AgentStatus(Enum):
17
+ """Status of an agent installation."""
18
+
19
+ INSTALLED = "installed"
20
+ AVAILABLE = "available"
21
+ RECOMMENDED = "recommended"
22
+
23
+
24
+ @dataclass
25
+ class AgentInfo:
26
+ """Information about a coding agent."""
27
+
28
+ identity: str
29
+ name: str
30
+ short_name: str
31
+ description: str
32
+ author: str
33
+ status: AgentStatus
34
+
35
+ @property
36
+ def color(self) -> str:
37
+ return AGENT_COLORS.get(self.short_name, THEME["purple"])
38
+
39
+ @property
40
+ def icon(self) -> str:
41
+ return AGENT_ICONS.get(self.short_name, "🤖")
42
+
43
+ @property
44
+ def is_ready(self) -> bool:
45
+ return self.status == AgentStatus.INSTALLED
46
+
47
+ @property
48
+ def status_icon(self) -> str:
49
+ if self.is_ready:
50
+ return "✅"
51
+ elif self.status == AgentStatus.RECOMMENDED:
52
+ return "⭐"
53
+ return "○"
54
+
55
+
56
+ def check_installed(name: str) -> bool:
57
+ """Check if an agent is installed on the system."""
58
+ # Map agent short names to their CLI commands
59
+ cmd_map = {
60
+ # 14 Official ACP Agents
61
+ "gemini": "gemini",
62
+ "claude": "claude",
63
+ "claude-code": "claude",
64
+ "codex": "codex",
65
+ "junie": "junie",
66
+ "goose": "goose",
67
+ "kimi": "kimi",
68
+ "opencode": "opencode",
69
+ "stakpak": "stakpak",
70
+ "vtcode": "vtcode",
71
+ "auggie": "auggie",
72
+ "code-assistant": "code-assistant",
73
+ "cagent": "cagent",
74
+ "fast-agent": "fast-agent",
75
+ "llmling-agent": "llmling-agent",
76
+ }
77
+ return shutil.which(cmd_map.get(name, name)) is not None
78
+
79
+
80
+ def load_agents_sync() -> list[AgentInfo]:
81
+ """Load agents list synchronously."""
82
+ agents = []
83
+ try:
84
+ from superqode.agents.discovery import read_agents
85
+
86
+ def _read():
87
+ loop = asyncio.new_event_loop()
88
+ asyncio.set_event_loop(loop)
89
+ try:
90
+ return loop.run_until_complete(read_agents())
91
+ finally:
92
+ loop.close()
93
+
94
+ with concurrent.futures.ThreadPoolExecutor() as ex:
95
+ agent_map = ex.submit(_read).result(timeout=10)
96
+
97
+ for identity, data in agent_map.items():
98
+ short = data.get("short_name", "").lower()
99
+ installed = check_installed(short)
100
+ recommended = data.get("recommended", False)
101
+
102
+ agents.append(
103
+ AgentInfo(
104
+ identity=identity,
105
+ name=data.get("name", "Unknown"),
106
+ short_name=short,
107
+ description=data.get("description", "")[:80],
108
+ author=data.get("author_name", ""),
109
+ status=AgentStatus.INSTALLED
110
+ if installed
111
+ else (AgentStatus.RECOMMENDED if recommended else AgentStatus.AVAILABLE),
112
+ )
113
+ )
114
+ except Exception:
115
+ pass
116
+
117
+ agents.sort(key=lambda a: (0 if a.is_ready else 1, a.name))
118
+ return agents
@@ -0,0 +1,125 @@
1
+ """
2
+ SuperQode Command Suggester - Autocompletion for commands.
3
+
4
+ Optimized for zero-latency typing - returns immediately without blocking.
5
+ """
6
+
7
+ from typing import List, Tuple, Optional
8
+ from textual.suggester import Suggester
9
+
10
+ from .constants import COMMANDS
11
+
12
+
13
+ def get_role_suggestions(mode: str, partial_role: str) -> List[Tuple[str, bool, str]]:
14
+ """Get role suggestions for a mode matching partial_role.
15
+
16
+ Args:
17
+ mode: Mode name ('qe', 'dev', 'devops')
18
+ partial_role: Partial role name to match (empty string returns all roles)
19
+
20
+ Returns:
21
+ List of (role_name, enabled, description) tuples, sorted by enabled first, then alphabetically
22
+ """
23
+ try:
24
+ from superqode.config import load_config
25
+
26
+ config = load_config()
27
+ mode_config = config.team.modes.get(mode)
28
+ if not mode_config or not mode_config.roles:
29
+ return []
30
+
31
+ partial_lower = partial_role.lower()
32
+ suggestions = []
33
+
34
+ for role_name, role_config in mode_config.roles.items():
35
+ # Use prefix match for more predictable autocomplete behavior
36
+ # Empty partial_role matches all roles
37
+ if not partial_lower or role_name.lower().startswith(partial_lower):
38
+ enabled = role_config.enabled
39
+ desc = role_config.description or ""
40
+ suggestions.append((role_name, enabled, desc))
41
+
42
+ # Sort: enabled first, then alphabetically
43
+ suggestions.sort(key=lambda x: (not x[1], x[0]))
44
+ return suggestions
45
+ except Exception:
46
+ return []
47
+
48
+
49
+ class CommandSuggester(Suggester):
50
+ """Autocomplete for : commands.
51
+
52
+ Performance optimizations:
53
+ - Pre-computed command lists
54
+ - Fast early-exit checks
55
+ - Non-blocking async implementation
56
+ - Minimal processing per keystroke
57
+ """
58
+
59
+ def __init__(self):
60
+ super().__init__()
61
+ # Pre-compute lowercase commands for faster matching
62
+ self._commands_lower = tuple(cmd.lower() for cmd in COMMANDS)
63
+ self._commands = tuple(COMMANDS)
64
+ # Pre-filter commands starting with ':' for even faster lookup
65
+ self._colon_commands = tuple(cmd for cmd in COMMANDS if cmd.startswith(":"))
66
+ self._colon_commands_lower = tuple(cmd.lower() for cmd in self._colon_commands)
67
+
68
+ async def get_suggestion(self, value: str) -> str | None:
69
+ """Get suggestion for command autocomplete.
70
+
71
+ Returns immediately to avoid any blocking or delay.
72
+ Designed for zero-latency typing experience.
73
+
74
+ Supports:
75
+ - Basic commands: :help, :clear, etc.
76
+ - Mode commands with roles: :qe <role>, :dev <role>, :devops <role>
77
+ """
78
+ # Ultra-fast path: not a command (most common case)
79
+ if not value:
80
+ return None
81
+
82
+ # Fast path: doesn't start with ':'
83
+ if not value.startswith(":"):
84
+ return None
85
+
86
+ # Fast path: too short (just ':')
87
+ if len(value) < 2:
88
+ return None
89
+
90
+ value_lower = value.lower()
91
+
92
+ # Check for mode commands with role patterns: :qe <role>, :dev <role>, :devops <role>
93
+ mode_patterns = [("qe", ":qe"), ("dev", ":dev"), ("devops", ":devops")]
94
+ for mode_name, mode_prefix in mode_patterns:
95
+ if value_lower == mode_prefix or value_lower.startswith(mode_prefix + " "):
96
+ # Determine the prefix with space
97
+ prefix_with_space = mode_prefix + " "
98
+ # Extract partial role name (everything after the mode prefix and space)
99
+ if len(value) > len(prefix_with_space):
100
+ partial_role = value[len(prefix_with_space) :]
101
+ else:
102
+ # User typed just ":qe" or ":qe " - return first enabled role
103
+ partial_role = ""
104
+
105
+ # Get role suggestions
106
+ try:
107
+ suggestions = get_role_suggestions(mode_name, partial_role)
108
+ if suggestions:
109
+ role_name, enabled, _ = suggestions[0]
110
+ status = "" if enabled else " [DISABLED]"
111
+ # Keep the space in prefix - don't strip it!
112
+ return f"{prefix_with_space}{role_name}{status}"
113
+ except Exception:
114
+ # If role suggestions fail, fall through to normal command matching
115
+ pass
116
+ # No suggestions found - let normal command matching continue
117
+ break
118
+
119
+ # Fast matching - use pre-filtered colon commands
120
+ # Find first command that starts with the value
121
+ for i, cmd_lower in enumerate(self._colon_commands_lower):
122
+ if cmd_lower.startswith(value_lower) and cmd_lower != value_lower:
123
+ return self._colon_commands[i]
124
+
125
+ return None