Skip to content

Commit f29b575

Browse files
author
Your Name
committed
Add "!!" for running commands with output addition suppressed and "!!!" to background the tui for commands that require input
1 parent 8b6086f commit f29b575

3 files changed

Lines changed: 64 additions & 6 deletions

File tree

cecli/coders/base_coder.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,13 +1693,27 @@ async def preproc_user_input(self, inp):
16931693
inp = inp.strip()
16941694

16951695
if self.commands.is_command(inp):
1696+
run_kwargs = {}
16961697
if inp[0] in "!":
1697-
inp = f"/run {inp[1:]}"
1698+
# Count and strip all leading exclamation marks
1699+
# "!command" -> normal execution
1700+
# "!!command" -> suppress adding output to chat
1701+
# "!!!command" -> background/obstructive mode (TUI suspended)
1702+
num_marks = 0
1703+
while num_marks < len(inp) and inp[num_marks] == "!":
1704+
num_marks += 1
1705+
command_text = inp[num_marks:]
1706+
inp = f"/run {command_text}"
1707+
if num_marks >= 3:
1708+
run_kwargs["background"] = True
1709+
run_kwargs["suppress_add"] = True
1710+
elif num_marks == 2:
1711+
run_kwargs["suppress_add"] = True
16981712

16991713
if self.commands.is_run_command(inp):
17001714
self.commands.cmd_running_event.clear() # Command is running
17011715

1702-
return await self.commands.run(inp, coder=self)
1716+
return await self.commands.run(inp, coder=self, **run_kwargs)
17031717

17041718
await self.check_for_file_mentions(inp)
17051719
inp = await self.check_for_urls(inp)

cecli/commands/core.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,13 @@ def matching_commands(self, inp):
232232
matching_commands = [cmd for cmd in all_commands if cmd.startswith(first_word)]
233233
return matching_commands, first_word, rest_inp
234234

235-
async def run(self, inp, coder=None):
235+
async def run(self, inp, coder=None, **kwargs):
236+
if inp.startswith("!!!"):
237+
return await self.execute(
238+
"run", inp[3:], coder=coder, background=True, suppress_add=True
239+
)
240+
if inp.startswith("!!"):
241+
return await self.execute("run", inp[2:], coder=coder, suppress_add=True)
236242
if inp.startswith("!"):
237243
return await self.execute("run", inp[1:], coder=coder)
238244
res = self.matching_commands(inp)
@@ -241,10 +247,10 @@ async def run(self, inp, coder=None):
241247
matching_commands, first_word, rest_inp = res
242248
if len(matching_commands) == 1:
243249
command = matching_commands[0][1:]
244-
return await self.execute(command, rest_inp, coder=coder)
250+
return await self.execute(command, rest_inp, coder=coder, **kwargs)
245251
elif first_word in matching_commands:
246252
command = first_word[1:]
247-
return await self.execute(command, rest_inp, coder=coder)
253+
return await self.execute(command, rest_inp, coder=coder, **kwargs)
248254
elif len(matching_commands) > 1:
249255
self.io.tool_error(f"Ambiguous command: {', '.join(matching_commands)}")
250256
else:

cecli/commands/run.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ class RunCommand(BaseCommand):
1414
@classmethod
1515
async def execute(cls, io, coder, args, **kwargs):
1616
"""Execute the run command with given parameters."""
17+
suppress_add = kwargs.get("suppress_add", False)
18+
background = kwargs.get("background", False)
1719
add_on_nonzero_exit = kwargs.get("add_on_nonzero_exit", False)
1820

21+
# Background mode: suspend the TUI and run interactively
22+
if background:
23+
return await cls._execute_background(io, coder, args)
24+
1925
should_print = True
2026

2127
if coder.args.tui:
@@ -44,7 +50,10 @@ async def execute(cls, io, coder, args, **kwargs):
4450
token_count = coder.main_model.token_count(combined_output)
4551
k_tokens = token_count / 1000
4652

47-
if add_on_nonzero_exit:
53+
# When suppress_add is True, skip the confirmation and never add
54+
if suppress_add:
55+
add = False
56+
elif add_on_nonzero_exit:
4857
add = exit_status != 0
4958
else:
5059
add = await io.confirm_ask(f"Add {k_tokens:.1f}k tokens of command output to the chat?")
@@ -80,6 +89,35 @@ async def execute(cls, io, coder, args, **kwargs):
8089
# Return None if output wasn't added or command succeeded
8190
return format_command_result(io, "run", "Command executed successfully")
8291

92+
@classmethod
93+
async def _execute_background(cls, io, coder, args):
94+
"""
95+
Execute a command in background/obstructive mode with the TUI suspended.
96+
97+
This allows running interactive commands (e.g., sudo) that require
98+
direct terminal access for user input. The TUI is suspended while
99+
the command runs and is resumed upon completion.
100+
"""
101+
import subprocess
102+
103+
def _run_sync():
104+
"""Run the command synchronously with direct terminal access."""
105+
subprocess.run(
106+
args,
107+
shell=True,
108+
cwd=coder.root,
109+
)
110+
111+
if coder.tui and coder.tui():
112+
# Suspend the TUI and run the command with direct terminal access
113+
coder.tui().run_obstructive(_run_sync)
114+
else:
115+
# Not in TUI mode, run directly
116+
_run_sync()
117+
118+
io.tool_output(f"Background command completed: {args}")
119+
return format_command_result(io, "run", "Command executed in background mode")
120+
83121
@classmethod
84122
def get_completions(cls, io, coder, args) -> List[str]:
85123
"""Get completion options for run command."""

0 commit comments

Comments
 (0)