Back to Blog
6 min read

Copilot CLI: AI in Your Terminal

Copilot CLI brings AI assistance to the command line. Convert natural language to shell commands, get explanations for complex commands, and troubleshoot errors without leaving the terminal.

Core Features

  1. gh copilot suggest - Natural language to shell command
  2. gh copilot explain - Explain what a command does
  3. Error assistance - Help with command failures

Natural Language to Commands

# Instead of remembering complex syntax:
$ gh copilot suggest "find all Python files modified in the last 7 days"

# Copilot suggests:
# find . -name "*.py" -mtime -7

# With explanation and safety warnings

Building CLI Copilot Features

import os
import subprocess
from dataclasses import dataclass
from enum import Enum

class Shell(Enum):
    BASH = "bash"
    ZSH = "zsh"
    POWERSHELL = "powershell"
    CMD = "cmd"

@dataclass
class CommandSuggestion:
    command: str
    explanation: str
    warnings: list[str]
    confidence: float

class CLICopilot:
    """AI-powered CLI assistant."""

    def __init__(self, client):
        self.client = client
        self.shell = self._detect_shell()
        self.os_type = self._detect_os()

    async def suggest_command(
        self,
        natural_language: str,
        cwd: str = None
    ) -> CommandSuggestion:
        """Convert natural language to shell command."""

        context = self._get_context(cwd)

        prompt = f"""Convert this request to a shell command.

System: {self.os_type}
Shell: {self.shell.value}
Current directory: {context['cwd']}

Request: {natural_language}

Provide:
1. The exact command to run
2. Brief explanation of what it does
3. Any warnings about potential risks
4. Confidence level (0-1)

Format as JSON:
{{"command": "...", "explanation": "...", "warnings": [...], "confidence": 0.9}}"""

        response = await self.client.chat_completion(
            model="gpt-4",
            messages=[
                {"role": "system", "content": "You are a CLI expert. Provide safe, accurate commands."},
                {"role": "user", "content": prompt}
            ],
            temperature=0.1
        )

        import json
        try:
            result = json.loads(response.content)
            return CommandSuggestion(**result)
        except:
            return CommandSuggestion(
                command="",
                explanation="Could not parse response",
                warnings=["Parse error"],
                confidence=0
            )

    async def explain_command(
        self,
        command: str
    ) -> str:
        """Explain what a command does."""

        prompt = f"""Explain this shell command in detail.

Command: {command}

Provide:
1. Overall purpose
2. Each component/flag explained
3. What output to expect
4. Any risks or side effects
5. Common variations

Be thorough but clear."""

        response = await self.client.chat_completion(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}]
        )

        return response.content

    async def troubleshoot_error(
        self,
        command: str,
        error_output: str,
        exit_code: int = None
    ) -> dict:
        """Help troubleshoot command error."""

        prompt = f"""Help troubleshoot this command error.

Command: {command}
Exit code: {exit_code or 'unknown'}
Error output:
{error_output}

System: {self.os_type}

Provide:
1. What the error means
2. Most likely causes
3. Steps to fix
4. Corrected command if applicable
5. How to prevent in future"""

        response = await self.client.chat_completion(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}]
        )

        return {
            "diagnosis": response.content,
            "original_command": command
        }

    def _detect_shell(self) -> Shell:
        """Detect current shell."""
        shell = os.environ.get("SHELL", "")
        if "zsh" in shell:
            return Shell.ZSH
        elif "bash" in shell:
            return Shell.BASH
        elif os.name == "nt":
            return Shell.POWERSHELL
        return Shell.BASH

    def _detect_os(self) -> str:
        """Detect operating system."""
        import platform
        return f"{platform.system()} {platform.release()}"

    def _get_context(self, cwd: str = None) -> dict:
        """Get current context."""
        return {
            "cwd": cwd or os.getcwd(),
            "user": os.environ.get("USER", "unknown")
        }

Command Categories

class CommandCategorizer:
    """Categorize and validate commands."""

    DANGEROUS_PATTERNS = [
        r"rm\s+-rf\s+/",
        r"rm\s+-rf\s+\*",
        r"mkfs",
        r"dd\s+if=",
        r">\s*/dev/sd",
        r"chmod\s+-R\s+777",
        r"curl.*\|\s*(ba)?sh",
    ]

    CATEGORIES = {
        "file_operations": ["ls", "cd", "cp", "mv", "rm", "mkdir", "touch", "cat", "find"],
        "text_processing": ["grep", "sed", "awk", "sort", "uniq", "cut", "tr", "wc"],
        "network": ["curl", "wget", "ssh", "scp", "ping", "netstat", "nc"],
        "process": ["ps", "kill", "top", "htop", "bg", "fg", "nohup"],
        "package": ["apt", "yum", "brew", "pip", "npm", "cargo"],
        "git": ["git"],
        "docker": ["docker", "docker-compose"],
        "kubernetes": ["kubectl", "helm"]
    }

    def analyze_command(self, command: str) -> dict:
        """Analyze a command for safety and category."""
        import re

        # Check for dangerous patterns
        dangers = []
        for pattern in self.DANGEROUS_PATTERNS:
            if re.search(pattern, command):
                dangers.append(pattern)

        # Determine category
        first_word = command.split()[0] if command else ""
        category = "other"
        for cat, commands in self.CATEGORIES.items():
            if first_word in commands:
                category = cat
                break

        return {
            "command": command,
            "category": category,
            "is_dangerous": len(dangers) > 0,
            "danger_patterns": dangers,
            "requires_confirmation": len(dangers) > 0 or "sudo" in command
        }

Interactive CLI Session

class InteractiveCLICopilot:
    """Interactive CLI session with AI assistance."""

    def __init__(self, copilot: CLICopilot):
        self.copilot = copilot
        self.history = []

    async def run_session(self):
        """Run interactive session."""
        print("CLI Copilot - Type 'help' for commands, 'exit' to quit")

        while True:
            try:
                user_input = input("\n> ").strip()

                if user_input.lower() == "exit":
                    break
                elif user_input.lower() == "help":
                    self._print_help()
                elif user_input.startswith("?"):
                    # Natural language query
                    query = user_input[1:].strip()
                    await self._handle_query(query)
                elif user_input.startswith("!"):
                    # Explain command
                    command = user_input[1:].strip()
                    await self._explain_command(command)
                else:
                    # Execute command
                    await self._execute_and_assist(user_input)

            except KeyboardInterrupt:
                print("\nUse 'exit' to quit")

    async def _handle_query(self, query: str):
        """Handle natural language query."""
        print("Thinking...")

        suggestion = await self.copilot.suggest_command(query)

        print(f"\nSuggested command: {suggestion.command}")
        print(f"Explanation: {suggestion.explanation}")

        if suggestion.warnings:
            print(f"Warnings: {', '.join(suggestion.warnings)}")

        confirm = input("\nExecute? [y/N]: ").strip().lower()
        if confirm == "y":
            await self._execute_and_assist(suggestion.command)

    async def _explain_command(self, command: str):
        """Explain a command."""
        print("Analyzing command...")
        explanation = await self.copilot.explain_command(command)
        print(f"\n{explanation}")

    async def _execute_and_assist(self, command: str):
        """Execute command and assist with errors."""
        import subprocess

        try:
            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
                text=True,
                timeout=30
            )

            if result.stdout:
                print(result.stdout)

            if result.returncode != 0:
                print(f"Command failed with exit code {result.returncode}")
                if result.stderr:
                    print(result.stderr)

                help_prompt = input("Get help troubleshooting? [y/N]: ").strip().lower()
                if help_prompt == "y":
                    diagnosis = await self.copilot.troubleshoot_error(
                        command,
                        result.stderr or result.stdout,
                        result.returncode
                    )
                    print(f"\n{diagnosis['diagnosis']}")

            self.history.append({
                "command": command,
                "success": result.returncode == 0
            })

        except subprocess.TimeoutExpired:
            print("Command timed out after 30 seconds")

    def _print_help(self):
        """Print help message."""
        print("""
CLI Copilot Commands:
  ? <request>   - Convert natural language to command
  ! <command>   - Explain what a command does
  <command>     - Execute command with error assistance
  help          - Show this help
  exit          - Exit CLI Copilot

Examples:
  ? find all log files larger than 100MB
  ! tar -czvf archive.tar.gz /path/to/dir
  ls -la
""")

Integration with zsh/bash

# Add to .zshrc or .bashrc for quick access

# Copilot suggest shortcut
copilot_suggest() {
    local query="$*"
    gh copilot suggest "$query"
}
alias '??'='copilot_suggest'

# Copilot explain shortcut
copilot_explain() {
    local cmd="$*"
    gh copilot explain "$cmd"
}
alias '?!'='copilot_explain'

# Usage:
# ?? "find all files modified today"
# ?! "tar -czvf archive.tar.gz ."

Copilot CLI removes the friction of command-line work. Whether you’re learning new tools or working with unfamiliar systems, AI assistance is just a keystroke away.

Michael John Pena

Michael John Pena

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.