rubber-ducky 1.5.1__tar.gz → 1.5.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rubber-ducky
3
- Version: 1.5.1
3
+ Version: 1.5.2
4
4
  Summary: Quick CLI do-it-all tool. Use natural language to spit out bash commands
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -103,18 +103,43 @@ class ConversationLogger:
103
103
  handle.write("\n")
104
104
 
105
105
 
106
- def print_shell_result(result: ShellResult) -> None:
107
- printed = False
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
- console.print(result.stdout.rstrip(), highlight=False)
110
- printed = True
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 printed:
130
+ if result.stdout.strip():
113
131
  console.print()
114
132
  console.print("[stderr]", style="bold red")
115
- console.print(result.stderr.rstrip(), style="red", highlight=False)
116
- printed = True
117
- if result.returncode != 0 or not printed:
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
- ) -> None:
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 run_shell_and_print(
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 run_shell_and_print(
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(explanation)
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 run_shell_and_print(
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "rubber-ducky"
3
- version = "1.5.1"
3
+ version = "1.5.2"
4
4
  description = "Quick CLI do-it-all tool. Use natural language to spit out bash commands"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rubber-ducky
3
- Version: 1.5.1
3
+ Version: 1.5.2
4
4
  Summary: Quick CLI do-it-all tool. Use natural language to spit out bash commands
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes
File without changes