rubber-ducky 1.5.1__py3-none-any.whl → 1.5.2__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.
- ducky/ducky.py +74 -29
- {rubber_ducky-1.5.1.dist-info → rubber_ducky-1.5.2.dist-info}/METADATA +1 -1
- {rubber_ducky-1.5.1.dist-info → rubber_ducky-1.5.2.dist-info}/RECORD +7 -7
- {rubber_ducky-1.5.1.dist-info → rubber_ducky-1.5.2.dist-info}/WHEEL +0 -0
- {rubber_ducky-1.5.1.dist-info → rubber_ducky-1.5.2.dist-info}/entry_points.txt +0 -0
- {rubber_ducky-1.5.1.dist-info → rubber_ducky-1.5.2.dist-info}/licenses/LICENSE +0 -0
- {rubber_ducky-1.5.1.dist-info → rubber_ducky-1.5.2.dist-info}/top_level.txt +0 -0
ducky/ducky.py
CHANGED
|
@@ -103,18 +103,43 @@ class ConversationLogger:
|
|
|
103
103
|
handle.write("\n")
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def print_shell_result(result: ShellResult) -> None:
|
|
107
|
-
|
|
106
|
+
def print_shell_result(result: ShellResult, truncate: bool = True) -> None:
|
|
107
|
+
"""Print shell command output with optional truncation.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
result: The ShellResult containing command output
|
|
111
|
+
truncate: If True and output is long (>10 lines), show truncated version
|
|
112
|
+
"""
|
|
113
|
+
# Determine if we should truncate
|
|
114
|
+
stdout_lines = result.stdout.rstrip().split('\n') if result.stdout else []
|
|
115
|
+
stderr_lines = result.stderr.rstrip().split('\n') if result.stderr else []
|
|
116
|
+
total_lines = len(stdout_lines) + len(stderr_lines)
|
|
117
|
+
|
|
118
|
+
should_truncate = truncate and total_lines > 10
|
|
119
|
+
|
|
108
120
|
if result.stdout.strip():
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
if should_truncate:
|
|
122
|
+
# Show first 8 lines of stdout
|
|
123
|
+
show_lines = stdout_lines[:8]
|
|
124
|
+
console.print('\n'.join(show_lines), highlight=False)
|
|
125
|
+
console.print(f"... ({len(stdout_lines) - 8} more lines, use /expand to see full output)", style="dim cyan")
|
|
126
|
+
else:
|
|
127
|
+
console.print(result.stdout.rstrip(), highlight=False)
|
|
128
|
+
|
|
111
129
|
if result.stderr.strip():
|
|
112
|
-
if
|
|
130
|
+
if result.stdout.strip():
|
|
113
131
|
console.print()
|
|
114
132
|
console.print("[stderr]", style="bold red")
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
133
|
+
if should_truncate:
|
|
134
|
+
# Show first 5 lines of stderr
|
|
135
|
+
show_lines = stderr_lines[:5]
|
|
136
|
+
console.print('\n'.join(show_lines), style="red", highlight=False)
|
|
137
|
+
if len(stderr_lines) > 5:
|
|
138
|
+
console.print(f"... ({len(stderr_lines) - 5} more lines)", style="dim red")
|
|
139
|
+
else:
|
|
140
|
+
console.print(result.stderr.rstrip(), style="red", highlight=False)
|
|
141
|
+
|
|
142
|
+
if result.returncode != 0 or (not result.stdout.strip() and not result.stderr.strip()):
|
|
118
143
|
suffix = (
|
|
119
144
|
f"(exit status {result.returncode})"
|
|
120
145
|
if result.returncode != 0
|
|
@@ -128,10 +153,11 @@ async def run_shell_and_print(
|
|
|
128
153
|
command: str,
|
|
129
154
|
logger: ConversationLogger | None = None,
|
|
130
155
|
history: list[dict[str, str]] | None = None,
|
|
131
|
-
) ->
|
|
156
|
+
) -> ShellResult:
|
|
157
|
+
"""Run a shell command and print output. Returns the ShellResult."""
|
|
132
158
|
if not command:
|
|
133
159
|
console.print("No command provided.", style="yellow")
|
|
134
|
-
return
|
|
160
|
+
return ShellResult(command="", stdout="", stderr="", returncode=-1)
|
|
135
161
|
console.print(f"$ {command}", style="bold magenta")
|
|
136
162
|
result = await assistant.run_shell_command(command)
|
|
137
163
|
print_shell_result(result)
|
|
@@ -149,6 +175,7 @@ async def run_shell_and_print(
|
|
|
149
175
|
if not combined_output:
|
|
150
176
|
combined_output.append("(command produced no output)")
|
|
151
177
|
history.append({"role": "assistant", "content": "\n\n".join(combined_output)})
|
|
178
|
+
return result
|
|
152
179
|
|
|
153
180
|
|
|
154
181
|
class RubberDuck:
|
|
@@ -351,6 +378,7 @@ class InlineInterface:
|
|
|
351
378
|
self.code = code
|
|
352
379
|
self._code_sent = False
|
|
353
380
|
self.last_shell_output: str | None = None
|
|
381
|
+
self.last_shell_result: ShellResult | None = None
|
|
354
382
|
self.pending_command: str | None = None
|
|
355
383
|
self.session: PromptSession | None = None
|
|
356
384
|
self.selected_model: str | None = None
|
|
@@ -475,12 +503,7 @@ class InlineInterface:
|
|
|
475
503
|
if not self.last_command:
|
|
476
504
|
console.print("No suggested command available yet.", style="yellow")
|
|
477
505
|
return
|
|
478
|
-
await
|
|
479
|
-
self.assistant,
|
|
480
|
-
self.last_command,
|
|
481
|
-
logger=self.logger,
|
|
482
|
-
history=self.assistant.messages,
|
|
483
|
-
)
|
|
506
|
+
await self._run_shell_command(self.last_command)
|
|
484
507
|
# Add the command to prompt history so user can recall it with up arrow
|
|
485
508
|
if self.session and self.session.history and self.last_command:
|
|
486
509
|
self.session.history.append_string(self.last_command)
|
|
@@ -488,6 +511,29 @@ class InlineInterface:
|
|
|
488
511
|
self.pending_command = None
|
|
489
512
|
self.last_command = None
|
|
490
513
|
|
|
514
|
+
async def _run_shell_command(self, command: str) -> None:
|
|
515
|
+
"""Run a shell command, print output (with truncation), and store result."""
|
|
516
|
+
result = await run_shell_and_print(
|
|
517
|
+
self.assistant,
|
|
518
|
+
command,
|
|
519
|
+
logger=self.logger,
|
|
520
|
+
history=self.assistant.messages,
|
|
521
|
+
)
|
|
522
|
+
# Store the result for expansion later
|
|
523
|
+
self.last_shell_result = result
|
|
524
|
+
|
|
525
|
+
async def _expand_last_output(self) -> None:
|
|
526
|
+
"""Expand and show the full output of the last shell command."""
|
|
527
|
+
if not self.last_shell_result:
|
|
528
|
+
console.print("No previous shell output to expand.", style="yellow")
|
|
529
|
+
return
|
|
530
|
+
|
|
531
|
+
console.print()
|
|
532
|
+
console.print(f"[Full output for: {self.last_shell_result.command}]", style="bold cyan")
|
|
533
|
+
console.print()
|
|
534
|
+
print_shell_result(self.last_shell_result, truncate=False)
|
|
535
|
+
console.print()
|
|
536
|
+
|
|
491
537
|
async def _process_text(self, text: str) -> None:
|
|
492
538
|
stripped = text.strip()
|
|
493
539
|
if not stripped:
|
|
@@ -538,14 +584,13 @@ class InlineInterface:
|
|
|
538
584
|
await self._handle_crumb_command(stripped)
|
|
539
585
|
return
|
|
540
586
|
|
|
587
|
+
if stripped.lower() == "/expand":
|
|
588
|
+
await self._expand_last_output()
|
|
589
|
+
return
|
|
590
|
+
|
|
541
591
|
if stripped.startswith("!"):
|
|
542
592
|
command = stripped[1:].strip()
|
|
543
|
-
await
|
|
544
|
-
self.assistant,
|
|
545
|
-
command,
|
|
546
|
-
logger=self.logger,
|
|
547
|
-
history=self.assistant.messages,
|
|
548
|
-
)
|
|
593
|
+
await self._run_shell_command(command)
|
|
549
594
|
# Add the command to prompt history so user can recall it with up arrow
|
|
550
595
|
if self.session and self.session.history and command:
|
|
551
596
|
self.session.history.append_string(command)
|
|
@@ -595,6 +640,7 @@ class InlineInterface:
|
|
|
595
640
|
("[bold]/crumb add <name> <cmd>[/bold]", "Manually add a crumb"),
|
|
596
641
|
("[bold]/crumb del <name>[/bold]", "Delete a crumb"),
|
|
597
642
|
("[bold]<name>[/bold]", "Invoke a saved crumb"),
|
|
643
|
+
("[bold]/expand[/bold]", "Show full output of last shell command"),
|
|
598
644
|
("[bold]/model[/bold]", "Select a model interactively (local or cloud)"),
|
|
599
645
|
(
|
|
600
646
|
"[bold]/local[/bold]",
|
|
@@ -740,9 +786,13 @@ class InlineInterface:
|
|
|
740
786
|
if explanation:
|
|
741
787
|
self.crumb_manager.update_explanation(name, explanation)
|
|
742
788
|
from rich.text import Text
|
|
789
|
+
|
|
790
|
+
# Strip ANSI codes from explanation
|
|
791
|
+
clean_explanation = re.sub(r'\x1b\[([0-9;]*[mGK])', '', explanation)
|
|
792
|
+
|
|
743
793
|
text = Text()
|
|
744
794
|
text.append("Explanation added: ", style="cyan")
|
|
745
|
-
text.append(
|
|
795
|
+
text.append(clean_explanation)
|
|
746
796
|
console.print(text)
|
|
747
797
|
except Exception as e:
|
|
748
798
|
console.print(f"Could not generate explanation: {e}", style="yellow")
|
|
@@ -786,12 +836,7 @@ class InlineInterface:
|
|
|
786
836
|
|
|
787
837
|
if command and command != "No command":
|
|
788
838
|
# Execute the command
|
|
789
|
-
await
|
|
790
|
-
self.assistant,
|
|
791
|
-
command,
|
|
792
|
-
logger=self.logger,
|
|
793
|
-
history=self.assistant.messages,
|
|
794
|
-
)
|
|
839
|
+
await self._run_shell_command(command)
|
|
795
840
|
|
|
796
841
|
async def _select_model(self, host: str = "") -> None:
|
|
797
842
|
"""Show available models and allow user to select one with arrow keys."""
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
ducky/__init__.py,sha256=2vLhJxOuJ3lnIeg5rmF6xUvybUT5Qhjej6AS0BeBASY,60
|
|
2
2
|
ducky/config.py,sha256=Lh7xTUYh4i8Gxgrl0oTYadZB_72Wy2BKIqLCcDQduOA,2116
|
|
3
3
|
ducky/crumb.py,sha256=7BlyjD81-cZptYxQM97y6gOGdVDBF2qzxW0xbPqbspE,2693
|
|
4
|
-
ducky/ducky.py,sha256=
|
|
4
|
+
ducky/ducky.py,sha256=5B_if1sI06lXvVbDOyPt4nust6DeIOa28v1MC6pjtzg,41167
|
|
5
5
|
examples/POLLING_USER_GUIDE.md,sha256=rMEAczZhpgyJ9BgwHkN-SKwSdyas8nlw_CjpV7SFOLA,10685
|
|
6
6
|
examples/mock-logs/info.txt,sha256=apJqEO__UM1R2_2x9MlQOA7XmxvLvbhRvOy-FAwrINo,258
|
|
7
7
|
examples/mock-logs/mock-logs.sh,sha256=zM2JSaCR1eCQLlMvXDWjFnpxZTqrMpnFRa_SgNLPmBk,1132
|
|
8
|
-
rubber_ducky-1.5.
|
|
9
|
-
rubber_ducky-1.5.
|
|
10
|
-
rubber_ducky-1.5.
|
|
11
|
-
rubber_ducky-1.5.
|
|
12
|
-
rubber_ducky-1.5.
|
|
13
|
-
rubber_ducky-1.5.
|
|
8
|
+
rubber_ducky-1.5.2.dist-info/licenses/LICENSE,sha256=gQ1rCmw18NqTk5GxG96F6vgyN70e1c4kcKUtWDwdNaE,1069
|
|
9
|
+
rubber_ducky-1.5.2.dist-info/METADATA,sha256=AsaHxQ3vt2rl4dxYaZG4kIGsEwv8CB87oFFRit-WlTY,6733
|
|
10
|
+
rubber_ducky-1.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
rubber_ducky-1.5.2.dist-info/entry_points.txt,sha256=WPnVUUNvWdMDcBlCo8JCzkLghGllMX5QVZyQghyq85Q,75
|
|
12
|
+
rubber_ducky-1.5.2.dist-info/top_level.txt,sha256=hid_mDkugR6XIeravFKuzcRPpuN_ylN3ejC_06Fmnb4,15
|
|
13
|
+
rubber_ducky-1.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|