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,358 @@
1
+ """Smart prompt widget with completion support."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Callable
7
+
8
+ from textual import events, on
9
+ from textual.app import ComposeResult
10
+ from textual.binding import Binding
11
+ from textual.containers import Horizontal, Vertical
12
+ from textual.message import Message
13
+ from textual.reactive import reactive
14
+ from textual.widget import Widget
15
+ from textual.widgets import Input, Static
16
+
17
+ from superqode.widgets.slash_complete import SlashComplete, SlashCommand
18
+
19
+
20
+ class SmartPrompt(Widget):
21
+ """
22
+ Smart input prompt with:
23
+ - Slash command completion (/)
24
+ - Path completion (Tab)
25
+ - History navigation (Up/Down)
26
+ - Multi-line support (Shift+Enter)
27
+ """
28
+
29
+ DEFAULT_CSS = """
30
+ SmartPrompt {
31
+ dock: bottom;
32
+ height: auto;
33
+ max-height: 8;
34
+ padding: 0 1 1 1;
35
+ background: #0a0a0a;
36
+ }
37
+
38
+ SmartPrompt #prompt-row {
39
+ height: auto;
40
+ min-height: 3;
41
+ background: #0a0a0a;
42
+ }
43
+
44
+ SmartPrompt #prompt-prefix {
45
+ height: 3;
46
+ width: auto;
47
+ color: #00ffff;
48
+ text-style: bold;
49
+ padding: 0 1 0 0;
50
+ background: #001a33;
51
+ content-align: center middle;
52
+ }
53
+
54
+ SmartPrompt #prompt-input {
55
+ height: auto;
56
+ min-height: 3;
57
+ background: #1a1a1a;
58
+ border: double #00ffff;
59
+ padding: 0 1;
60
+ color: #ffffff;
61
+ }
62
+
63
+ SmartPrompt #prompt-input:focus {
64
+ border: double #00ff00;
65
+ background: #0a1a0a;
66
+ }
67
+
68
+ SmartPrompt #suggestions-row {
69
+ height: auto;
70
+ max-height: 3;
71
+ margin-top: 1;
72
+ display: none;
73
+ background: #1a1a1a;
74
+ }
75
+
76
+ SmartPrompt #suggestions-row.visible {
77
+ display: block;
78
+ }
79
+
80
+ SmartPrompt .suggestion {
81
+ color: #00ff00;
82
+ padding: 0 1;
83
+ background: #1a1a1a;
84
+ }
85
+
86
+ SmartPrompt .suggestion.selected {
87
+ color: #00ffff;
88
+ text-style: bold;
89
+ }
90
+ """
91
+
92
+ BINDINGS = [
93
+ Binding("enter", "submit", "Submit", show=False),
94
+ Binding("escape", "cancel", "Cancel", show=False),
95
+ Binding("up", "history_prev", "Previous", show=False),
96
+ Binding("down", "history_next", "Next", show=False),
97
+ Binding("tab", "complete", "Complete", show=False),
98
+ ]
99
+
100
+ class Submitted(Message):
101
+ """Message sent when input is submitted."""
102
+
103
+ def __init__(self, value: str) -> None:
104
+ self.value = value
105
+ super().__init__()
106
+
107
+ class SlashTriggered(Message):
108
+ """Message sent when slash command mode is triggered."""
109
+
110
+ def __init__(self, query: str) -> None:
111
+ self.query = query
112
+ super().__init__()
113
+
114
+ # State
115
+ value: reactive[str] = reactive("")
116
+ prefix: reactive[str] = reactive("superqode")
117
+ mode_suffix: reactive[str] = reactive("HOME")
118
+ placeholder: reactive[str] = reactive("Type a message or / for commands...")
119
+
120
+ def __init__(
121
+ self,
122
+ prefix: str = "superqode",
123
+ mode_suffix: str = "HOME",
124
+ placeholder: str = "Type a message or / for commands...",
125
+ on_submit: Callable[[str], None] | None = None,
126
+ **kwargs,
127
+ ) -> None:
128
+ super().__init__(**kwargs)
129
+ self.prefix = prefix
130
+ self.mode_suffix = mode_suffix
131
+ self.placeholder = placeholder
132
+ self._on_submit = on_submit
133
+ self._history: list[str] = []
134
+ self._history_index: int = -1
135
+ self._suggestions: list[str] = []
136
+ self._suggestion_index: int = 0
137
+
138
+ def compose(self) -> ComposeResult:
139
+ with Vertical():
140
+ with Horizontal(id="prompt-row"):
141
+ yield Static(self._get_prefix_text(), id="prompt-prefix")
142
+ yield Input(
143
+ placeholder=self.placeholder,
144
+ id="prompt-input",
145
+ )
146
+ with Horizontal(id="suggestions-row"):
147
+ yield Static("", id="suggestions-display", classes="suggestion")
148
+
149
+ def _get_prefix_text(self) -> str:
150
+ """Get the formatted prefix text."""
151
+ return f"{self.prefix} {self.mode_suffix} >"
152
+
153
+ def on_mount(self) -> None:
154
+ """Focus the input on mount."""
155
+ self.query_one("#prompt-input", Input).focus()
156
+
157
+ def watch_prefix(self, prefix: str) -> None:
158
+ """React to prefix changes."""
159
+ self._update_prefix()
160
+
161
+ def watch_mode_suffix(self, mode_suffix: str) -> None:
162
+ """React to mode suffix changes."""
163
+ self._update_prefix()
164
+
165
+ def _update_prefix(self) -> None:
166
+ """Update the prefix display."""
167
+ try:
168
+ prefix_widget = self.query_one("#prompt-prefix", Static)
169
+ prefix_widget.update(self._get_prefix_text())
170
+ except Exception:
171
+ # Widget not yet composed
172
+ pass
173
+
174
+ @on(Input.Changed, "#prompt-input")
175
+ def on_input_changed(self, event: Input.Changed) -> None:
176
+ """Handle input changes."""
177
+ self.value = event.value
178
+
179
+ # Check for slash command trigger
180
+ if event.value.startswith("/"):
181
+ self.post_message(self.SlashTriggered(event.value))
182
+
183
+ @on(Input.Submitted, "#prompt-input")
184
+ def on_input_submitted(self, event: Input.Submitted) -> None:
185
+ """Handle input submission."""
186
+ value = event.value.strip()
187
+ if value:
188
+ # Add to history
189
+ if not self._history or self._history[-1] != value:
190
+ self._history.append(value)
191
+ self._history_index = -1
192
+
193
+ # Clear input
194
+ input_widget = self.query_one("#prompt-input", Input)
195
+ input_widget.value = ""
196
+
197
+ # Post message
198
+ self.post_message(self.Submitted(value))
199
+
200
+ # Call callback if provided
201
+ if self._on_submit:
202
+ self._on_submit(value)
203
+
204
+ def action_submit(self) -> None:
205
+ """Submit the current input."""
206
+ input_widget = self.query_one("#prompt-input", Input)
207
+ if input_widget.value.strip():
208
+ self.on_input_submitted(Input.Submitted(input_widget, input_widget.value))
209
+
210
+ def action_cancel(self) -> None:
211
+ """Cancel current input / close completions."""
212
+ input_widget = self.query_one("#prompt-input", Input)
213
+ input_widget.value = ""
214
+ self._hide_suggestions()
215
+
216
+ def action_history_prev(self) -> None:
217
+ """Navigate to previous history item."""
218
+ if not self._history:
219
+ return
220
+
221
+ if self._history_index < 0:
222
+ self._history_index = len(self._history) - 1
223
+ elif self._history_index > 0:
224
+ self._history_index -= 1
225
+
226
+ input_widget = self.query_one("#prompt-input", Input)
227
+ input_widget.value = self._history[self._history_index]
228
+ input_widget.cursor_position = len(input_widget.value)
229
+
230
+ def action_history_next(self) -> None:
231
+ """Navigate to next history item."""
232
+ if not self._history or self._history_index < 0:
233
+ return
234
+
235
+ if self._history_index < len(self._history) - 1:
236
+ self._history_index += 1
237
+ input_widget = self.query_one("#prompt-input", Input)
238
+ input_widget.value = self._history[self._history_index]
239
+ input_widget.cursor_position = len(input_widget.value)
240
+ else:
241
+ self._history_index = -1
242
+ input_widget = self.query_one("#prompt-input", Input)
243
+ input_widget.value = ""
244
+
245
+ def action_complete(self) -> None:
246
+ """Trigger tab completion."""
247
+ input_widget = self.query_one("#prompt-input", Input)
248
+ value = input_widget.value
249
+
250
+ # If we have suggestions, cycle through them
251
+ if self._suggestions:
252
+ self._suggestion_index = (self._suggestion_index + 1) % len(self._suggestions)
253
+ # Apply suggestion
254
+ suggestion = self._suggestions[self._suggestion_index]
255
+ input_widget.value = suggestion
256
+ input_widget.cursor_position = len(suggestion)
257
+ self._show_suggestions()
258
+ return
259
+
260
+ # Try path completion
261
+ if " " in value:
262
+ # Complete the last token as a path
263
+ parts = value.rsplit(" ", 1)
264
+ prefix = parts[0] + " "
265
+ path_query = parts[1] if len(parts) > 1 else ""
266
+ completions = self._get_path_completions(path_query)
267
+ if completions:
268
+ self._suggestions = [prefix + c for c in completions]
269
+ self._suggestion_index = 0
270
+ input_widget.value = self._suggestions[0]
271
+ input_widget.cursor_position = len(input_widget.value)
272
+ self._show_suggestions()
273
+
274
+ def _get_path_completions(self, query: str) -> list[str]:
275
+ """Get file path completions for the query."""
276
+ try:
277
+ if not query:
278
+ # List current directory
279
+ cwd = Path.cwd()
280
+ return [p.name + ("/" if p.is_dir() else "") for p in cwd.iterdir()][:10]
281
+
282
+ # Expand path
283
+ path = Path(query).expanduser()
284
+
285
+ if path.is_dir():
286
+ # List directory contents
287
+ return [
288
+ query.rstrip("/") + "/" + p.name + ("/" if p.is_dir() else "")
289
+ for p in path.iterdir()
290
+ ][:10]
291
+
292
+ # Complete partial filename
293
+ parent = path.parent
294
+ prefix = path.name
295
+
296
+ if parent.is_dir():
297
+ matches = [
298
+ str(parent / p.name) + ("/" if p.is_dir() else "")
299
+ for p in parent.iterdir()
300
+ if p.name.lower().startswith(prefix.lower())
301
+ ]
302
+ return matches[:10]
303
+
304
+ except (OSError, PermissionError):
305
+ pass
306
+
307
+ return []
308
+
309
+ def _show_suggestions(self) -> None:
310
+ """Show the suggestions display."""
311
+ if not self._suggestions:
312
+ self._hide_suggestions()
313
+ return
314
+
315
+ suggestions_row = self.query_one("#suggestions-row", Horizontal)
316
+ suggestions_row.add_class("visible")
317
+
318
+ display = self.query_one("#suggestions-display", Static)
319
+ # Show current suggestion index and total
320
+ text = f"Tab: {self._suggestion_index + 1}/{len(self._suggestions)}"
321
+ display.update(text)
322
+
323
+ def _hide_suggestions(self) -> None:
324
+ """Hide the suggestions display."""
325
+ self._suggestions = []
326
+ self._suggestion_index = 0
327
+ suggestions_row = self.query_one("#suggestions-row", Horizontal)
328
+ suggestions_row.remove_class("visible")
329
+
330
+ def set_value(self, value: str) -> None:
331
+ """Set the input value programmatically."""
332
+ input_widget = self.query_one("#prompt-input", Input)
333
+ input_widget.value = value
334
+ input_widget.cursor_position = len(value)
335
+ self.value = value
336
+
337
+ def clear(self) -> None:
338
+ """Clear the input."""
339
+ input_widget = self.query_one("#prompt-input", Input)
340
+ input_widget.value = ""
341
+ self.value = ""
342
+ self._hide_suggestions()
343
+
344
+ def focus_input(self) -> None:
345
+ """Focus the input widget."""
346
+ self.query_one("#prompt-input", Input).focus()
347
+
348
+ def set_mode(self, mode: str) -> None:
349
+ """Set the mode suffix."""
350
+ self.mode_suffix = mode
351
+
352
+ def insert_command(self, command: str) -> None:
353
+ """Insert a slash command into the input."""
354
+ input_widget = self.query_one("#prompt-input", Input)
355
+ input_widget.value = command
356
+ input_widget.cursor_position = len(command)
357
+ self.value = command
358
+ input_widget.focus()