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.
- {rubber_ducky-1.6.4/rubber_ducky.egg-info → rubber_ducky-1.6.5}/PKG-INFO +1 -1
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/ducky/ducky.py +59 -19
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/pyproject.toml +1 -1
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5/rubber_ducky.egg-info}/PKG-INFO +1 -1
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/LICENSE +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/MANIFEST.in +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/README.md +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/ducky/__init__.py +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/ducky/config.py +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/ducky/crumb.py +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/examples/POLLING_USER_GUIDE.md +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/examples/mock-logs/info.txt +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/examples/mock-logs/mock-logs.sh +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/rubber_ducky.egg-info/SOURCES.txt +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/rubber_ducky.egg-info/dependency_links.txt +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/rubber_ducky.egg-info/entry_points.txt +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/rubber_ducky.egg-info/requires.txt +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/rubber_ducky.egg-info/top_level.txt +0 -0
- {rubber_ducky-1.6.4 → rubber_ducky-1.6.5}/setup.cfg +0 -0
|
@@ -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
|
|
242
|
-
"
|
|
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
|
-
|
|
292
|
-
if not
|
|
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
|
-
|
|
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
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|