<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marta Karaś</title>
    <description>The latest articles on DEV Community by Marta Karaś (@martakar).</description>
    <link>https://dev.to/martakar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3859272%2Fe2271f71-db72-4e1f-b357-372de9aec1c4.jpeg</url>
      <title>DEV Community: Marta Karaś</title>
      <link>https://dev.to/martakar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martakar"/>
    <language>en</language>
    <item>
      <title>Building an AI-Agent-Friendly CLI: Lessons from deployhq-cli</title>
      <dc:creator>Marta Karaś</dc:creator>
      <pubDate>Thu, 16 Apr 2026 14:10:12 +0000</pubDate>
      <link>https://dev.to/martakar/building-an-ai-agent-friendly-cli-lessons-from-deployhq-cli-5p3</link>
      <guid>https://dev.to/martakar/building-an-ai-agent-friendly-cli-lessons-from-deployhq-cli-5p3</guid>
      <description>&lt;h1&gt;
  
  
  Building an AI-Agent-Friendly CLI: Lessons from deployhq-cli
&lt;/h1&gt;

&lt;p&gt;AI coding agents can run shell commands. That means every CLI tool you ship is potentially an agent tool -- but most CLIs are built for humans staring at a terminal, not for an LLM parsing stdout. When we built &lt;a href="https://github.com/deployhq/deployhq-cli" rel="noopener noreferrer"&gt;deployhq-cli&lt;/a&gt;, we designed it for both from day one. Here is what we learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CLIs Are Natural Agent Tools
&lt;/h2&gt;

&lt;p&gt;Before reaching for SDKs, MCP servers, or custom integrations, consider what a CLI already gives you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured output via &lt;code&gt;--json&lt;/code&gt;.&lt;/strong&gt; An agent does not need to scrape table formatting. One flag and it gets clean JSON it can parse reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exit codes.&lt;/strong&gt; Zero means success, non-zero means failure. An agent can branch on this without parsing error messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-documenting via &lt;code&gt;--help&lt;/code&gt;.&lt;/strong&gt; Every subcommand describes itself. An agent can explore the tool's surface area without external docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composability.&lt;/strong&gt; Pipes, redirects, and shell scripting work the same whether a human or an agent is driving.&lt;/p&gt;

&lt;p&gt;The gap is not capability -- it is discoverability and consistency. A human reads a README and builds a mental model. An agent needs that mental model handed to it in a format it can consume in one shot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SKILL.md Pattern
&lt;/h2&gt;

&lt;p&gt;The single most impactful thing we did was create a &lt;code&gt;SKILL.md&lt;/code&gt; file -- a structured markdown document that teaches any AI agent how to use the CLI.&lt;/p&gt;

&lt;p&gt;Here is the structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# DeployHQ CLI -- Agent Skill Guide&lt;/span&gt;

&lt;span class="gu"&gt;## Identity&lt;/span&gt;
What this tool is and does, in one paragraph.

&lt;span class="gu"&gt;## Authentication&lt;/span&gt;
Three methods, checked in order: env vars, config files, interactive login.

&lt;span class="gu"&gt;## Output Contract&lt;/span&gt;
stdout is ALWAYS data. stderr is ALWAYS human messages.
--json forces JSON. Exit codes: 0 = success, non-zero = failure.

&lt;span class="gu"&gt;## Command Groups&lt;/span&gt;
Table mapping group names to descriptions and reference docs.

&lt;span class="gu"&gt;## Decision Trees&lt;/span&gt;
Step-by-step workflows for common tasks.

&lt;span class="gu"&gt;## Invariants&lt;/span&gt;
Rules that always hold true.

&lt;span class="gu"&gt;## Gotchas&lt;/span&gt;
Things that will trip up an agent if not warned about.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decision trees are the most valuable section. Instead of making the agent figure out that deploying code requires listing projects, then listing servers, then creating a deployment, you spell it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### "Deploy code"&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; &lt;span class="sb"&gt;`dhq projects list --json`&lt;/span&gt; -- find project permalink
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="sb"&gt;`dhq servers list -p &amp;lt;project&amp;gt; --json`&lt;/span&gt; -- find server identifier
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="sb"&gt;`dhq deploy -p &amp;lt;project&amp;gt; -s &amp;lt;server&amp;gt; --json`&lt;/span&gt; -- create deployment
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="sb"&gt;`dhq deployments watch &amp;lt;id&amp;gt; -p &amp;lt;project&amp;gt;`&lt;/span&gt; -- monitor progress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gotchas section prevents the agent from learning things the hard way. For example: &lt;code&gt;dhq env-vars create&lt;/code&gt; will prompt for a value interactively if &lt;code&gt;--value&lt;/code&gt; is omitted. That hangs an agent. The SKILL.md warns: "always pass &lt;code&gt;--value&lt;/code&gt;."&lt;/p&gt;

&lt;p&gt;One file. No SDK. No API wrapper. The agent reads it, and it knows how to drive your CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Decisions for Agent-Friendliness
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Breadcrumbs in JSON Output
&lt;/h3&gt;

&lt;p&gt;Every JSON response includes a &lt;code&gt;breadcrumbs&lt;/code&gt; array with suggested next commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deployment abc123 completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breadcrumbs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cmd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dhq deployments logs abc123 -p my-app"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rollback"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cmd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dhq rollback abc123 -p my-app"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the single best feature for agent workflows. The agent does not need to construct the next command from scratch -- it picks from contextually relevant suggestions. After a deployment completes, the breadcrumbs suggest checking logs or rolling back. After creating a project, they suggest connecting a repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-Discovery via &lt;code&gt;dhq commands --json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;An agent can dump the full command catalog at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dhq commands &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns every command, its flags, required arguments, and descriptions. Combined with &lt;code&gt;SKILL.md&lt;/code&gt; for workflow logic and &lt;code&gt;--help&lt;/code&gt; for individual commands, an agent has three layers of discovery: strategic (SKILL.md), catalog (commands), and tactical (help).&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistent Error Shapes
&lt;/h3&gt;

&lt;p&gt;Every error follows the same JSON structure regardless of which command failed. The agent does not need different error-parsing logic per subcommand. Combined with non-zero exit codes, error handling is mechanical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-Interactive Guarantee
&lt;/h3&gt;

&lt;p&gt;The CLI never prompts when all required flags are provided. This is documented as an invariant in SKILL.md. Three commands (&lt;code&gt;dhq init&lt;/code&gt;, &lt;code&gt;dhq hello&lt;/code&gt;, &lt;code&gt;dhq auth login&lt;/code&gt;) are interactive by design -- the rest are fully non-interactive when flags are supplied. An agent knows to avoid the interactive commands and supply all flags explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stderr for Humans, Stdout for Machines
&lt;/h3&gt;

&lt;p&gt;This separation means an agent can safely parse stdout as JSON without worrying about progress spinners, warnings, or decorative output contaminating the data stream. Humans still see friendly output on stderr when running interactively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plugin Ecosystem
&lt;/h2&gt;

&lt;p&gt;A SKILL.md file is useful, but how does an agent find it? We built multiple discovery paths:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin.json&lt;/code&gt; manifest.&lt;/strong&gt; A &lt;code&gt;.claude-plugin/plugin.json&lt;/code&gt; file declares the skill's name, version, description, and location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deployhq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deploy code, manage servers, and automate infrastructure via the DeployHQ CLI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"skills/deployhq"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;dhq setup claude&lt;/code&gt;&lt;/strong&gt; installs the skill directly into a project's Claude Code configuration. One command and the agent has the SKILL.md in its context. Same for Codex, Cursor, and Windsurf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directory listings.&lt;/strong&gt; Sites like &lt;a href="https://skills.sh" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt; and &lt;a href="https://claudepluginhub.com" rel="noopener noreferrer"&gt;claudepluginhub.com&lt;/a&gt;, plus GitHub topics, let users discover agent-ready CLI tools. The &lt;code&gt;plugin.json&lt;/code&gt; manifest is what these directories index.&lt;/p&gt;

&lt;p&gt;The goal is zero-friction adoption: a developer installs the CLI, runs &lt;code&gt;dhq setup claude&lt;/code&gt;, and their agent can deploy code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Workflow Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Deploy to Production
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent discovers available projects&lt;/span&gt;
dhq projects list &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# Agent picks the right server&lt;/span&gt;
dhq servers list &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# Deploy latest code&lt;/span&gt;
dhq deploy &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;-s&lt;/span&gt; production &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# On success, breadcrumbs suggest: view logs, open in browser&lt;/span&gt;
&lt;span class="c"&gt;# On failure, breadcrumbs suggest: view logs, retry, rollback&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Debug a Failed Deployment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check recent deployments&lt;/span&gt;
dhq deployments list &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# Get details on the failed one&lt;/span&gt;
dhq deployments show dep-456 &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# Read the step logs to find the error&lt;/span&gt;
dhq deployments logs dep-456 &lt;span class="nt"&gt;-p&lt;/span&gt; my-app

&lt;span class="c"&gt;# Roll back to the last good state&lt;/span&gt;
dhq rollback dep-456 &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rollback Under Pressure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One command -- rollback creates a reverse deployment&lt;/span&gt;
dhq rollback dep-456 &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# Monitor the rollback&lt;/span&gt;
dhq deployments watch &amp;lt;rollback-id&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt; my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In each case, the agent follows the decision tree from SKILL.md, uses &lt;code&gt;--json&lt;/code&gt; for parsing, and follows breadcrumbs for next steps. No guesswork.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Make YOUR CLI Agent-Friendly
&lt;/h2&gt;

&lt;p&gt;Here are the concrete steps, roughly in priority order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add &lt;code&gt;--json&lt;/code&gt; to every command.&lt;/strong&gt; This is table stakes. If your output is only human-readable tables, agents cannot use your tool reliably. Bonus: let users select fields with &lt;code&gt;--json field1,field2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Separate data from decoration.&lt;/strong&gt; Stdout should be pure data (tables or JSON). Progress bars, warnings, and color codes go to stderr. This lets agents pipe stdout safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Write a SKILL.md.&lt;/strong&gt; One markdown file with: what the tool does, how to authenticate, what the output contract is, decision trees for common workflows, and gotchas. This is the highest-leverage documentation you can write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Add breadcrumbs to JSON output.&lt;/strong&gt; After every operation, include a &lt;code&gt;breadcrumbs&lt;/code&gt; array with contextually relevant next commands. This turns your CLI from a bag of commands into a guided workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Never prompt when flags are provided.&lt;/strong&gt; If all required arguments are supplied via flags, the command should run without interaction. Document which commands are interactive and which are not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Add a &lt;code&gt;commands&lt;/code&gt; subcommand.&lt;/strong&gt; Let agents dump your full command catalog as JSON at runtime. This is the self-discovery escape hatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Use consistent exit codes and error shapes.&lt;/strong&gt; Zero for success, non-zero for failure. Errors should follow one JSON schema across all commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Ship a plugin manifest.&lt;/strong&gt; A &lt;code&gt;plugin.json&lt;/code&gt; with your tool's metadata lets agent ecosystems index and auto-discover your CLI.&lt;/p&gt;

&lt;p&gt;None of these changes hurt the human experience. Structured output, consistent errors, and self-documentation make a CLI better for everyone. The agent-friendliness comes for free once you commit to these patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;The best agent integration is often the simplest one. You do not need an MCP server, a custom SDK, or a SaaS integration layer. A well-structured CLI with &lt;code&gt;--json&lt;/code&gt; output, a SKILL.md file, and breadcrumbs in responses gives any AI agent everything it needs to drive your tool competently.&lt;/p&gt;

&lt;p&gt;Build for machines and humans at the same time. They want the same things: clear contracts, predictable behavior, and good error messages. The only difference is that machines read JSON and humans read tables.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/deployhq/deployhq-cli" rel="noopener noreferrer"&gt;deployhq-cli&lt;/a&gt; is open source under MIT. Try &lt;code&gt;dhq setup claude&lt;/code&gt; to see the skill pattern in action.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ai</category>
      <category>cli</category>
      <category>claude</category>
    </item>
    <item>
      <title>Building a CLI for Humans and AI Agents</title>
      <dc:creator>Marta Karaś</dc:creator>
      <pubDate>Fri, 03 Apr 2026 10:27:25 +0000</pubDate>
      <link>https://dev.to/martakar/building-a-cli-for-humans-and-ai-agents-1lpj</link>
      <guid>https://dev.to/martakar/building-a-cli-for-humans-and-ai-agents-1lpj</guid>
      <description>&lt;p&gt;Most CLIs are built for humans. Some are built for scripts. We built one for both — and for AI agents too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/deployhq/deployhq-cli" rel="noopener noreferrer"&gt;DeployHQ CLI&lt;/a&gt; (&lt;code&gt;dhq&lt;/code&gt;) is a Go command-line tool for the DeployHQ deployment platform. It manages projects, servers, deployments, and releases. But what makes it different is that it was designed from day one to work equally well when a human is typing, when a shell script is piping, or when an AI agent is orchestrating.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Output Contract
&lt;/h2&gt;

&lt;p&gt;The single most important design decision: &lt;strong&gt;stdout is always data, stderr is always human text.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Human sees a nice table on stderr, data goes to stdout&lt;/span&gt;
dhq projects list

&lt;span class="c"&gt;# Pipe it and get JSON automatically — no flag needed&lt;/span&gt;
dhq projects list | jq &lt;span class="s1"&gt;'.data[].name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When stdout is a TTY, you get a formatted table. When it's piped, you get JSON. No &lt;code&gt;--json&lt;/code&gt; flag required (though you can use it explicitly).&lt;/p&gt;




&lt;h2&gt;
  
  
  Field Selection with &lt;code&gt;--json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Most CLIs give you &lt;code&gt;--json&lt;/code&gt; and dump everything. We give you &lt;code&gt;--json &amp;lt;fields&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Only get what you need&lt;/span&gt;
dhq deployments list &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;--json&lt;/span&gt; identifier,status,timestamps.duration

&lt;span class="c"&gt;# An agent requesting just the deployment ID and status&lt;/span&gt;
dhq deploy &lt;span class="nt"&gt;-p&lt;/span&gt; my-app &lt;span class="nt"&gt;-s&lt;/span&gt; prod &lt;span class="nt"&gt;--use-latest&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt; identifier,status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters for agents because tokens cost money. A full deployment object might be 50+ fields. An agent checking status needs two.&lt;/p&gt;




&lt;h2&gt;
  
  
  Breadcrumbs: Teaching Agents What To Do Next
&lt;/h2&gt;

&lt;p&gt;Every JSON response includes a &lt;code&gt;breadcrumbs&lt;/code&gt; array — suggested next commands based on what just happened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breadcrumbs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dhq deployments logs abc123 -p my-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dhq deployments show abc123 -p my-app --json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An AI agent doesn't need to memorize your API. It reads the breadcrumbs and knows what's possible next. This turns a stateless CLI into a guided workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agent Detection
&lt;/h2&gt;

&lt;p&gt;The CLI detects when it's being used by an agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEPLOYHQ_AGENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1      &lt;span class="c"&gt;# Explicit agent mode&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1          &lt;span class="c"&gt;# Auto-detected&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;                &lt;span class="c"&gt;# Auto-detected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In agent mode, the CLI adjusts its behavior — suppressing interactive prompts, defaulting to structured output, and including richer metadata.&lt;/p&gt;




&lt;h2&gt;
  
  
  JSONL Capture
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;DEPLOYHQ_OUTPUT_FILE=log.jsonl&lt;/code&gt; and every command's output gets appended as a JSONL line. Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent audit trails&lt;/li&gt;
&lt;li&gt;CI pipeline debugging&lt;/li&gt;
&lt;li&gt;Replaying what happened during an automated workflow&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Non-Interactive Auth
&lt;/h2&gt;

&lt;p&gt;Three env vars, no browser popup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEPLOYHQ_ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myaccount
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEPLOYHQ_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;me@example.com
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEPLOYHQ_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is table stakes for CI, but it matters even more for agents. An AI agent can't click "Authorize" in a browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exit Codes That Mean Something
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; — success&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; — failure (with structured error in JSON)&lt;/li&gt;
&lt;li&gt;Empty results return &lt;code&gt;0&lt;/code&gt; with empty &lt;code&gt;data&lt;/code&gt; — that's not an error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple, but surprisingly rare. Many CLIs return 0 on errors or don't include error details in their JSON output.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Go SDK Inside
&lt;/h2&gt;

&lt;p&gt;The CLI wraps a public Go SDK at &lt;code&gt;pkg/sdk/&lt;/code&gt; with 97 methods. The SDK has zero internal imports — it's designed to be extractable as a standalone module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"account"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListProjects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're building Go tooling around DeployHQ, you can use the SDK directly without shelling out to the CLI.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;p&gt;Building for agents changed how we think about CLIs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Structured output isn't optional&lt;/strong&gt; — if your CLI can't produce JSON, agents can't use it reliably&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field selection saves tokens&lt;/strong&gt; — agents pay per token, don't make them parse 50 fields to get 2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breadcrumbs beat documentation&lt;/strong&gt; — agents don't read man pages, but they read JSON responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-interactive is non-negotiable&lt;/strong&gt; — if it needs a human to click something, agents can't use it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Piped detection is free UX&lt;/strong&gt; — detect TTY vs pipe and adjust automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The future of CLIs isn't human OR machine. It's both.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/deployhq/deployhq-cli" rel="noopener noreferrer"&gt;https://github.com/deployhq/deployhq-cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install: &lt;code&gt;brew install deployhq/tap/dhq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DeployHQ: &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;https://www.deployhq.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>devops</category>
      <category>cli</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
