Building a CLI Assistant Agent: Natural Language Command Line Interactions
Build an AI agent that translates natural language into shell commands, explains what each command does, asks for confirmation before executing dangerous operations, and learns from command history.
Why a CLI Assistant Agent
The command line is powerful but has a steep learning curve. Developers frequently search the internet for the right flags to pass to git, docker, kubectl, or ffmpeg. A CLI assistant agent lets you describe what you want in plain English, translates it into the correct command, explains what it will do, and optionally executes it after confirmation.
Unlike static cheatsheets, the agent understands your current context — your OS, installed tools, and working directory — to produce commands that actually work.
The CLI Agent Core
The agent maps natural language to shell commands, classifying each as safe or dangerous before execution.
import os
import subprocess
import shutil
from dataclasses import dataclass
from openai import OpenAI
client = OpenAI()
@dataclass
class CommandResult:
command: str
explanation: str
is_dangerous: bool
output: str | None = None
error: str | None = None
executed: bool = False
class CLIAssistantAgent:
def __init__(self, model: str = "gpt-4o"):
self.model = model
self.history: list[dict] = []
self.dangerous_patterns = [
"rm -rf", "mkfs", "dd if=", "> /dev/",
"chmod 777", ":(){ :|:& };:",
"DROP TABLE", "DELETE FROM",
"--force", "--hard",
]
def get_system_context(self) -> str:
import platform
shell = os.environ.get("SHELL", "unknown")
cwd = os.getcwd()
tools = {}
for tool in ["git", "docker", "kubectl", "python", "node"]:
tools[tool] = shutil.which(tool) is not None
return (
f"OS: {platform.system()} {platform.release()}\n"
f"Shell: {shell}\n"
f"CWD: {cwd}\n"
f"Available tools: {tools}"
)
Translating Natural Language to Commands
The core translation step uses the system context to produce environment-specific commands.
import json
def translate(self, user_request: str) -> CommandResult:
context = self.get_system_context()
history_context = ""
if self.history:
recent = self.history[-5:]
history_context = "Recent commands:\n" + "\n".join(
f"- {h['request']} -> {h['command']}" for h in recent
)
response = client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": f"""You are a CLI assistant.
Translate user requests into shell commands.
System info:
{context}
{history_context}
Return JSON with:
- "command": the shell command to execute
- "explanation": plain English explanation of what it does
- "is_dangerous": boolean, true if the command modifies or deletes data
IMPORTANT: Use tools available on this system. Adapt commands
for the detected OS (e.g., use gsed on macOS if needed).
Return ONLY valid JSON."""},
{"role": "user", "content": user_request},
],
temperature=0,
response_format={"type": "json_object"},
)
data = json.loads(response.choices[0].message.content)
result = CommandResult(
command=data["command"],
explanation=data["explanation"],
is_dangerous=data.get("is_dangerous", False),
)
for pattern in self.dangerous_patterns:
if pattern in result.command:
result.is_dangerous = True
break
return result
Notice the double check: the LLM classifies danger, and then the agent applies its own pattern-based check. This defense-in-depth approach prevents the LLM from accidentally marking a destructive command as safe.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
Safe Execution with Confirmation
Dangerous commands require explicit user confirmation before running.
def execute(self, result: CommandResult, force: bool = False) -> CommandResult:
if result.is_dangerous and not force:
result.error = "BLOCKED: Dangerous command requires confirmation"
return result
try:
proc = subprocess.run(
result.command,
shell=True,
capture_output=True,
text=True,
timeout=30,
cwd=os.getcwd(),
)
result.output = proc.stdout
result.error = proc.stderr if proc.returncode != 0 else None
result.executed = True
except subprocess.TimeoutExpired:
result.error = "Command timed out after 30 seconds"
except Exception as e:
result.error = str(e)
self.history.append({
"request": "executed",
"command": result.command,
"success": result.error is None,
})
return result
Building an Interactive Loop
The agent runs as a REPL that continuously accepts requests.
def run_interactive(self):
print("CLI Assistant (type 'exit' to quit)")
print("-" * 40)
while True:
user_input = input("\n> ").strip()
if user_input.lower() in ("exit", "quit"):
break
if not user_input:
continue
result = self.translate(user_input)
print(f"\nCommand: {result.command}")
print(f"Explanation: {result.explanation}")
if result.is_dangerous:
print("\n[WARNING] This command modifies or deletes data.")
confirm = input("Execute? (y/N): ").strip().lower()
if confirm != "y":
print("Cancelled.")
continue
result = self.execute(result, force=True)
else:
result = self.execute(result)
if result.output:
print(f"\nOutput:\n{result.output}")
if result.error:
print(f"\nError:\n{result.error}")
agent = CLIAssistantAgent()
agent.run_interactive()
FAQ
How do I prevent command injection if the user input is malicious?
The agent should never interpolate user input directly into shell commands. The LLM generates the full command as a string, and the dangerous-pattern checker blocks known attack vectors like ; rm -rf / or backtick injection. For additional safety, run commands in a restricted subprocess with limited permissions and environment variables.
Can the agent compose multi-step commands like pipes and redirects?
Yes. The LLM naturally understands piping, redirection, and command chaining. A request like "find all Python files larger than 1MB and sort by size" produces find . -name '*.py' -size +1M -exec ls -lh {} + | sort -k5 -h. The explanation breaks down each step so the user understands what each part does.
How does command history improve the agent's responses?
The last five commands are included in the prompt context. This allows the agent to understand follow-up requests like "now do the same but only for .js files" or "run that again with verbose output." The agent resolves these references against recent history.
#CLITools #AIAgents #Python #Shell #DeveloperProductivity #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.