BACK TO BLOG

How to design a CLI login flow for coding agents

Design a CLI login for coding agents with a ticket flow that pauses, resumes in a new process, and returns the next command in JSON. Learn how Dosu built it.

Taylor DolezalTaylor Dolezal/Sep 1, 2026/9 min read
How to design a CLI login flow for coding agents

Where did the browser go

Most CLI login commands assume a person is present for the login step. The login step typically opens a browser, shows some sort of confirmation screen, and waits for you to authenticate. A coding agent can also run those CLI-driven login commands, though agents might be running this from a remote workspace where neither you nor anyone else could ever reach the browser tab.

Your CLI and agent-driven login flow is similar to how you sign in to streaming platforms on a smart TV. Your TV shows a code and a URL. You approve the request on your phone, then the TV provides you a list of all the shows you can watch. RFC 8628 is the spec that defines this split for clients without a suitable browser. Any spec-compliant client handles the login protocol while a person approves access on another device.

A coding agent occupies the TV's side of that exchange. The agent can run the request command and poll the check command until the login finishes, though you still make the decision to approve access. The agent can't stand in the room with you. The model's next tool call depends on the terminal output alone, so that output has to describe enough of the login flow to work with you.

When logins outlive your commands

Coding agents work in turns. Your agent runs a command, reads the response, talks to you, then decides which tool to call next. A login command that keeps running blocks the terminal at the exact moment the agent's next turn has to return a message to you. So where does the login live while the agent is off asking you to click?

At Dosu, we used RFC 8628's separation of tasks, but we didn't need the entire spec. We shaped our own ticket protocol similar to Netlify's CLI so the approval step transmits during the web app's existing sign-in. Our requests create a ticket, bind your browser approval to it, and exchange the ticket identifier for a CLI session. login --request runs the first of those and returns a ticket, a random string that expires in ten minutes, so the request command can end before you've signed in. Later, login --check runs the exchange from a fresh process, and Dosu answers pending while you haven't approved yet, expired after the ten minutes pass, or authenticated once the CLI session is ready.

Splitting the request from check step matches how an agent runs in your terminal. Each command does one piece of work and provides context to the conversation, while Dosu keeps the unfinished login alive in between. During that interaction, the only state the agent persists is your ticket string.

What should your agent do next

The ticket value is inside a larger response, and once the request command can exit, that response has to explain how the workflow resumes. dosu login --request --json returns the following in our CLI.

{
  "ticket": "kJ8f2a...wQ0",
  "url": "https://app.dosu.dev/cli/auth?ticket=kJ8f2a...wQ0",
  "check_command": "dosu login --check kJ8f2a...wQ0 --json",
  "expires_in": 600,
  "agent_next_steps": "Give the URL to the user so they can authorize. Once the user confirms they've signed in, run check_command to retrieve the token."
}

Most of the fields describe where the login stands. The ticket names the pending login, the url belongs in your browser, check_command is the exact command to run later from any new process, and expires_in says how long that command keeps working. An agent whose instructions describe this flow needs nothing more from the response.

agent_next_steps is for an agent with no prior instructions for this flow. This field provides information on who acts first, what you need from the agent, and when the agent should run check_command. Joel Hooks describes contextual next actions as hypermedia controls for CLIs, the way a web page includes the links you can click next.

Our setup --agent command uses the same field at every step. The command prints one JSON object per line, sharing the current step and its status, and when a step can pause, that line includes the command to run when the pause ends. If we renamed check_command next release, every agent reading this response would break, exactly as renaming ticket would break them.

Approval stays in the browser

Open the ticket URL and you get a page titled Authorize Dosu CLI. The page shows your signed-in email when the web app knows one, then waits for you to click Authorize. Nothing happens to the ticket until you click, and a test on that page checks that no bind request goes out before the click. The agent can send you to this page with the URL, but opening the page authorizes nothing.

After the click, the bind endpoint uses your signed-in browser session to identify you, then creates a separate CLI session. The ticket never contains your browser's refresh token. If the CLI reused that token, the identity provider's token rotation would sign both sessions out about an hour after login, so each session refreshes and expires on its own. Binding also keeps the ticket's original expiration, so opening the page late doesn't extend the window.

Each ticket works exactly once. Dosu reads and deletes the ticket record in one step, so a second exchange returns expired and Dosu never returns the same CLI session twice.

The agent never sees your browser credentials, only a ticket that turns into one CLI session after your click.

Stopping to ask is a feature

setup --agent can't click Authorize for you either, so the CLI has to stop and wait. When the CLI has no session for you, the CLI creates a ticket and prints this event instead of blocking the terminal.

{
  "step": "auth",
  "status": "need_user_action",
  "url": "https://app.dosu.dev/cli/auth?ticket=kJ8f2a...wQ0",
  "ticket": "kJ8f2a...wQ0",
  "resume_command": "npx @dosu/cli@latest setup --agent --tool codex --login-ticket kJ8f2a...wQ0",
  "expires_in": 600,
  "agent_next_steps": "Give the URL to the user so they can sign in. Wait for the user to confirm they've signed in, then run resume_command to finish setup."
}

The need_user_action status marks the stop as deliberate and dependent on user action. The CLI exits 0 after printing the line, and the agent's next reply includes the URL and the sign-in request. After you confirm, a new process runs resume_command, setup's version of check_command, which redeems the same ticket and finishes the remaining setup steps.

Why does the status matter? When a CLI reports every stop as a failure, the response gives the agent no signal to choose between a retry, an exit, and a user prompt. Propelcode's agent-first CLI guide makes the same argument for error codes paired with a recommended next step. need_user_action signals that the next step requires user action.

We don't report every stop this way yet. When your account has more than one MCP deployment, the setup stops again, but setup reports that stop as a status: "error" with a multiple_deployments reason, the list of deployments, and an instruction to re-run with --deployment <id>. The agent can recover, since the response includes the choices and the next command, but the CLI shouldn't encode a decision point as an error.

A stop that includes the command to resume works beyond login, too. Picture a deployment CLI that prepares a release and then needs an operator's approval. The same fields apply. A need_user_action line includes the approval URL, a resume_command lets a fresh process finish once the operator signs off, and an expires_in keeps a stale approval from shipping.

Instructions to change agent behavior

Netlify's login --request returned a ticket, a URL, and a check command for agents on a different machine from their user, and an agent that hit authenticate() without a valid token polled for five minutes and timed out. Sean Roberts added agent_next_steps to tell the agent to poll check_command or wait for the user, and a week later he added one more sentence to the front of that instruction.

Give the URL to the user so they can authorize.

The ticket protocol didn't change. One added sentence makes the handoff more explicit, and the two handoff strings in our own flow open with the same instruction.

No unit test would have told Netlify that "Give the URL to the user" was missing. A person reading an ambiguous message can look around the screen and recover. A coding agent acts on whatever text sits in front of the model, so the wording steers which command the agent runs next. Testing has to include the conversation around the command. Does the agent hand you the URL before polling, and does the next process run the command from the response? Unit tests can check that the fields are there. Only a full run with a real agent shows what the agent tells you and which command it runs next.

You can watch a full run of our flow with one command. @dosu/cli runs the whole flow with any coding agent.

npx @dosu/cli@latest setup --agent --tool codex

When your agent has no Dosu session yet, the first line out is the need_user_action event from earlier. The agent's reply includes the URL, resume_command stays in its context, you click Authorize, and a new process redeems the ticket and finishes the setup flow. The agent keeps the ticket from one process to the next, Dosu enforces the ticket's expiry, and your click turns the ticket into a session that keeps you logged in.

Let your agent run the setup

When the setup flow finishes, your coding agent has Dosu connected as an MCP server, so the questions the agent would otherwise stall on, about your codebase, your docs, and the decisions behind them, have an answer waiting. At Dosu, we build the knowledge layer your agents read from, and this login flow is the first step into it.

Start using Dosu today and let your agent run the setup for you. If you want help connecting Dosu to your own CLI or agent workflow, book a demo with our team.

Found this article helpful?

Share it with your network to help others discover valuable insights.

Want more like this? Subscribe via RSS

Related Articles

Ready to transform your workflow?

Join leading organizations using Dosu to automate documentation, streamline support, and empower development teams to focus on building great products.