A command-line interface is the oldest way to tell a computer what to do, and it has quietly become one of the most important ways for AI agents to get work done. Coding agents run tests, read logs and commit changes through a terminal; operations agents query cloud accounts the same way. This page explains what a CLI is, why it suits agents so well, and what can go wrong when you let an agent type commands on your behalf.
What a CLI is
A CLI is a program you drive with text. You type its name, add arguments that say what you want, and it answers with text. There is no window to click through and no hidden state on a screen: the request and the response are both lines of characters.
Three words get mixed up here. The terminal is the window that displays text. The shell (bash, zsh, PowerShell) is the program inside it that reads what you type, expands wildcards and variables, and starts other programs. A CLI is any of those other programs: git, curl, grep, a package manager, a cloud provider's command-line tool. When people say an agent "uses the CLI", they usually mean it can send command lines to a shell and read what comes back.
$ git log --oneline -n 3 -- src/billing a41c2e9 Handle missing invoice address 7be0d13 Round tax per line, not per invoice f09a6c1 Add credit note export
Anatomy of a command
Every command line follows roughly the same grammar, and each part tells an agent something useful.
| Part | Example | What it carries |
|---|---|---|
| Program and subcommand | git log | Which tool, and which of its actions |
| Flags | --oneline -n 3 | Options that change behavior or output format |
| Positional arguments | src/billing | What the action applies to |
| Standard input | < report.csv or a pipe | Data fed into the program |
| Standard output | The listed commits | The result, as text |
| Standard error | fatal: not a git repository | Problems, kept apart from the result |
| Exit code | 0 or non-zero | Whether the program considers the run a success |
The pipe ties it together. grep -r "TODO" src | wc -l sends the output of one program into the next, so small tools combine into answers nobody wrote a dedicated program for. That composability is the main reason the command line has outlived so many graphical tools.
Why agents work well with CLIs
A language model reads and writes text. A CLI accepts and returns text. That match explains most of the appeal, but there are more specific reasons.
- Self-description. Almost every tool answers
--help. An agent that meets an unfamiliar command can read its usage and try again, without anyone writing a schema first. - Familiarity. Common tools such as git, curl and grep appear in an enormous amount of public documentation and code, so models already know their usual flags and error messages.
- A clear success signal. The exit code gives the loop an unambiguous result to check, which is far more dependable than judging whether a screenshot "looks right".
- Composition. Pipes, redirects and small scripts let the agent build the exact query it needs instead of waiting for someone to add a feature.
- Structured output on request. Many modern CLIs offer
--jsonor a format flag, which turns free text into data the agent can parse without guessing. - Nothing new to build. If a system already has a CLI, an agent can use it today. A custom tool integration or a protocol server is extra code to write and maintain.
That last point is why "just give the agent a shell" is so tempting, and why teams now weigh a CLI against an MCP server or a packaged skill for the same job. The comparison of MCP, skills and CLIs covers that choice, and turning a CLI into a skill shows how to wrap one in instructions an agent can follow.
Where CLIs trip agents up
CLIs were designed for people at a keyboard, and some habits of that design work against an agent.
- Interactive prompts. A confirmation such as
Proceed? [y/N], a pager or a login prompt waits for input that never comes, and the run hangs. Prefer non-interactive flags and put a timeout on every command. - Too much output. A verbose build log can flood the agent's context and push the goal out of view. Cap what reaches the model, and keep the full log in a file the agent can search.
- Misleading exit codes.
grepexits non-zero when it finds nothing, which is not an error. Some tools exit zero after printing a warning that matters. - Quoting and platforms. A command that works in bash can break in PowerShell, and a filename with a space can turn one argument into two.
- Output that changes between versions. Parsing human-readable text is fragile; a structured format is more stable when the tool offers one.
The risks of letting an agent run commands
A shell gives whoever types into it the full authority of the account it runs under. Handing it to an agent means anything that account can do, the agent can also do, including things nobody asked for. The risks fall into a few groups.
- Destruction. Deleting files, force-pushing over a shared branch, dropping a table, terminating cloud resources. One mistaken path or wildcard is enough.
- Data leaving. A single
curlcan post a file anywhere. So can a package install script, or a git remote the agent adds on its own. - Secret exposure. Printing environment variables, reading a
.envfile or listing a keys directory copies credentials into the agent's context and its logs. - Injected instructions. The agent reads text it did not write: a README, an issue comment, a web page, a log line. If that text says "run this command", a poorly guarded agent may comply.
- Supply chain. Installing a misspelled package or piping a downloaded script straight into a shell executes someone else's code with your permissions.
- Spend. Cloud and SaaS command-line tools can create resources that keep costing money long after the run ends.
Prefix allowlists are a common false comfort. Allowing everything that starts with git also allows git push --force, and some git options can launch arbitrary programs. Allowing ls lets ls; rm -rf build through unless compound commands are parsed and judged part by part.
Guardrails that hold up
No single control is enough; the useful ones stack.
- Isolation. Run the agent's shell in a container, a virtual machine or a dedicated account with access to one working directory, not your home folder.
- Read-only first. Start with commands that only inspect, and widen access only after you have watched real runs. See start agents read-only.
- Exact allowlists. Approve specific commands and subcommands, parse compound lines, and refuse anything you cannot classify.
- A pause before consequences. Deleting, pushing, deploying, sending, installing and exporting should wait for a person, who sees the exact command line rather than a summary of it.
- Approval per attempt. Saying yes to one push should not mean yes to every push for the rest of the session.
- Secrets by reference. Keep credentials out of the agent's environment where you can, so a stray
envreveals nothing. - Limits and a record. Set timeouts, output caps and a ceiling on the number of commands, and log every command with its exit code.
Written down as policy, it can be as plain as this:
allow git status, git diff, git log read-only, no approval allow npm test inside the repo, 10 min timeout ask git commit, git push show the exact command first ask any network call outside the registry deny rm outside the working directory deny reading .env files, ~/.ssh, ~/.aws
The program that enforces a policy like this is part of the agent harness, not the model. A model can be asked politely to avoid destructive commands; only the harness can actually refuse them.
How GoCux treats command-running work
GoCux offers a customer CLI next to its web workspace. Local tools, and the permissions attached to them, are managed by that CLI on your own machine. The work itself is still a work order with a goal, a scope and acceptance criteria, plus bounds on wall time and tool calls; set the tool-call bound to zero and the work makes no tool calls at all.
When a step falls into one of the consequential action classes, such as destroying something, sending something out or exporting data, the work stops and waits for someone on the team. Destructive steps stay blocked until a signed decision is in place. The held call is shown exactly as the agent asked for it, so the person approving sees the real request, and the decision applies only to that one attempt and is checked again when the step runs. Every step lands on the work order's record. The Task page describes the work order, governance covers the gate and the trail, and development work shows how the same rules apply to code.
Before you give an agent a shell
- Does the agent run in an isolated environment with access to one working directory?
- Is every allowed command listed exactly, with compound lines parsed and judged part by part?
- Do destructive, outbound and install commands pause for a person who sees the full command?
- Does an approval cover one attempt, not the rest of the session?
- Are credentials kept out of the agent's environment and output?
- Does every command have a timeout, and is long output capped and saved to a file?
- Can you read afterwards which commands ran, in what order, and with what exit codes?
- Is text the agent reads from files, issues and web pages treated as data, never as instructions?
If you can answer yes to each, a shell is one of the most capable tools you can give an agent. If not, start with a narrower set of commands and grow it as the record earns your trust.
Bring this workflow into your workspace.
Set the scope. Review the evidence. Approve the steps that need it.