rubber-ducky 1.6.4__tar.gz → 1.6.5__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.6.4
3
+ Version: 1.6.5
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
@@ -238,8 +238,9 @@ class RubberDuck:
238
238
 
239
239
  if effective_command_mode:
240
240
  instruction = (
241
- "Return a single bash command that accomplishes the task. Unless user wants something els"
242
- "Do not include explanations or formatting other than the command itself."
241
+ "Return a single bash command that accomplishes the task wrapped in <command></command> tags. "
242
+ "You can write explanatory text before or after the command tags for the user, "
243
+ "but the command itself must be within the <command></command> tags and nothing else."
243
244
  )
244
245
  user_content = (
245
246
  f"{user_content}\n\n{instruction}" if user_content else instruction
@@ -288,10 +289,20 @@ class RubberDuck:
288
289
  )
289
290
 
290
291
  def _extract_command(self, content: str) -> str | None:
291
- lines = content.strip().splitlines()
292
- if not lines:
292
+ content = content.strip()
293
+ if not content:
293
294
  return None
294
295
 
296
+ # First, try to extract command from <command></command> tags
297
+ command_match = re.search(r"<command>(.*?)</command>", content, re.DOTALL)
298
+ if command_match:
299
+ command = command_match.group(1).strip()
300
+ # Strip backticks if present
301
+ command = self._strip_backticks(command)
302
+ return command or None
303
+
304
+ # Fallback to code block detection
305
+ lines = content.splitlines()
295
306
  command_lines: List[str] = []
296
307
  in_block = False
297
308
 
@@ -316,9 +327,20 @@ class RubberDuck:
316
327
 
317
328
  # Join all command lines with newlines for multi-line commands
318
329
  command = "\n".join(command_lines)
330
+ # Strip backticks if present
331
+ command = self._strip_backticks(command)
319
332
 
320
333
  return command or None
321
334
 
335
+ def _strip_backticks(self, command: str) -> str:
336
+ """Strip surrounding backticks from a command string."""
337
+ command = command.strip()
338
+ if command.startswith("`"):
339
+ command = command[1:]
340
+ if command.endswith("`"):
341
+ command = command[:-1]
342
+ return command.strip()
343
+
322
344
  async def list_models(self, host: str = "") -> list[str]:
323
345
  """List available Ollama models."""
324
346
  # Set the host temporarily for this operation
@@ -1095,11 +1117,12 @@ async def run_single_prompt(
1095
1117
  code: str | None = None,
1096
1118
  logger: ConversationLogger | None = None,
1097
1119
  suppress_suggestion: bool = False,
1120
+ command_mode: bool | None = None,
1098
1121
  ) -> AssistantResult:
1099
1122
  if logger:
1100
1123
  logger.log_user(prompt)
1101
1124
  try:
1102
- result = await rubber_ducky.send_prompt(prompt=prompt, code=code)
1125
+ result = await rubber_ducky.send_prompt(prompt=prompt, code=code, command_mode=command_mode)
1103
1126
  except Exception as e:
1104
1127
  error_msg = str(e)
1105
1128
  if "unauthorized" in error_msg.lower() or "401" in error_msg:
@@ -1126,7 +1149,9 @@ async def run_single_prompt(
1126
1149
  raise
1127
1150
 
1128
1151
  content = result.content or "(No content returned.)"
1129
- console.print(content, style="dim", highlight=False)
1152
+ # Strip <command>...</command> tags from display output
1153
+ display_content = re.sub(r"<command>.*?</command>", "", content, flags=re.DOTALL).strip()
1154
+ console.print(display_content, highlight=False)
1130
1155
  if logger:
1131
1156
  logger.log_assistant(content, result.command)
1132
1157
  if result.command and not suppress_suggestion:
@@ -1235,20 +1260,35 @@ async def ducky() -> None:
1235
1260
 
1236
1261
  if piped_prompt is not None:
1237
1262
  if piped_prompt:
1238
- result = await run_single_prompt(
1239
- rubber_ducky, piped_prompt, code=code, logger=logger
1240
- )
1241
- if (
1242
- result.command
1243
- and sys.stdout.isatty()
1244
- and confirm("Run suggested command?")
1245
- ):
1246
- await run_shell_and_print(
1247
- rubber_ducky,
1248
- result.command,
1249
- logger=logger,
1250
- history=rubber_ducky.messages,
1263
+ # Check if user also provided a command-line prompt
1264
+ if args.single_prompt:
1265
+ # Combine piped content with user prompt
1266
+ # User prompt is the instruction, piped content is context
1267
+ user_prompt = " ".join(args.single_prompt)
1268
+ combined_prompt = (
1269
+ f"Context from stdin:\n```\n{piped_prompt}\n```\n\n"
1270
+ f"User request: {user_prompt}"
1251
1271
  )
1272
+ # Disable command_mode for this scenario - user wants an explanation, not a command
1273
+ result = await run_single_prompt(
1274
+ rubber_ducky, combined_prompt, code=code, logger=logger, command_mode=False
1275
+ )
1276
+ else:
1277
+ # Only piped input - proceed with command mode (default behavior)
1278
+ result = await run_single_prompt(
1279
+ rubber_ducky, piped_prompt, code=code, logger=logger
1280
+ )
1281
+ if (
1282
+ result.command
1283
+ and sys.stdout.isatty()
1284
+ and confirm("Run suggested command?")
1285
+ ):
1286
+ await run_shell_and_print(
1287
+ rubber_ducky,
1288
+ result.command,
1289
+ logger=logger,
1290
+ history=rubber_ducky.messages,
1291
+ )
1252
1292
  else:
1253
1293
  console.print("No input received from stdin.", style="yellow")
1254
1294
  return
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rubber-ducky"
3
- version = "1.6.4"
3
+ version = "1.6.5"
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.6.4
3
+ Version: 1.6.5
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