Cross-CLI

Delegation & permission relay

You can send a prompt from your CLI to a different CLI on the same machine. Glider runs that CLI without a console. If the CLI stops for a permission decision, Glider sends the question to you as a usual reply. Glider does not refuse the permission automatically.

The CLIs you can send a task to

Three CLIs are supported today. Glider finds the CLIs that are on your machine when you push Rescan on the Vendors page of the dashboard. The equivalent request is POST /api/vendors/discover. A CLI that Glider does not find is not a flag that you can use.

FlagCLIProgramDefault templateTemplates
/claude Claude Code claude headless — the answer comes back to you default, resume, interactive
/cursor-agent Cursor Agent cursor-agent headless — the answer comes back to you default, resume, interactive
/agy Antigravity agy interactive — a window opens, and no text comes back default, headless, resume, interactive

Each CLI operates in two directions. It can send a task with a flag, and it can do a task that a different CLI sends to it. Glider does not stop you from sending a task to the CLI that you use. But this gives you no advantage, and it adds the start time of a second process.

The number three is not a limit in the design. Glider keeps the list of CLIs as data in configs/vendor_candidates.yaml. The shared Go code does not contain a condition on a vendor name. Behaviour that is different for one CLI goes into an adapter for that CLI. To add a fourth CLI, you write one YAML entry and one adapter, and you change no shared code. How to add a fourth vendor gives the full list of files.

The flag

fix the failing test in internal/foo /claude
refactor this function /cursor-agent
summarize recent commits /agy

vendors.ParseDelegateCommand looks for a /vendor-name flag at the end of the message. The flag must not be at the start, and it must not be in the middle. DelegateHandler takes the message before the usual routing. On the gateway, the Messages route takes it. Therefore the flag does not change a usual request. Add a :template suffix to select a different command template. For example, fix the auth bug /agy:headless. Each vendor has an interactive template in configs/vendor_candidates.yaml. That template does not run the CLI without a console and does not send the text back. It opens the native session of the vendor in a new console window, with your task as the first message. You then control that session. No text comes back to this chat.

Why the flag must be at the end. A live test showed the cause. The client of Claude Code reads an unknown /word at the start as a local command. It then shows "Unknown command: /agy" and does not send the message. Therefore a flag at the start never comes to Glider from that CLI. The parser cannot correct this. A flag at the end prevents the problem for each CLI, not only for Claude Code. It also keeps the full text before the flag as the prompt. An earlier parser looked for the flag at each position, and it deleted the text before the first flag.

Why Glider cannot stop the run and ask you

The supported vendors do not stop for a permission decision when they operate without a console (-p mode):

VendorWhat the vendor does with a permission
cursor-agentIt does not stop. It records the refusal in the stream-json output, and the model continues.
claude (Claude Code)It does not stop. The refused tool gets an error immediately, and the last event contains a list of the refusals.
agy (Antigravity)It always fails. Its stderr says that this mode cannot ask a question.

Therefore Glider uses a resume loop. Glider runs the CLI without a console, finds the refusal in the recorded output, and asks you. After your answer, Glider continues the session of that CLI with the new permission. The run is complete before Glider asks the question, but the loop is quick.

Sequence

User (in claude, say)     DelegateHandler         vendors.RunWithOptions      agy subprocess
  |                             |                          |                        |
  | "fix the bug /agy"         |                          |                        |
  |---------------------------->| ParseDelegateCommand     |                        |
  |                             |------------------------->| exec agy -p --add-dir  |
  |                             |                          |----------------------->|
  |                             |                          |                        | denies a tool
  |                             |                          |<-----------------------|
  |                             |<-------------------------| DetectDenials          |
  |                             | RegisterPendingResume(token)                      |
  |<----------------------------| reply: "needs permission… abc123 /agy:allow"      |
  |                             |                          |                        |
  | "abc123 /agy:allow"        |                          |                        |
  |---------------------------->| TakePendingResume(token) |                        |
  |                             | GrantResumePermission (vendor-specific)           |
  |                             |------------------------->| exec agy -p --add-dir  |
  |                             |                          |----------------------->|
  |                             |                          |          edits a file  |
  |                             |                          |<-----------------------|
  |                             |<-------------------------| ExtractEditViews       |
  |<----------------------------| reply: result + diff block                        |

How Glider gives the permission

Your approval must change a condition before Glider runs the CLI again. If it does not, the new run stops at the same permission. Each CLI has a different mechanism, and there are two types:

VendorWhat your approval does
claude Glider adds --allowedTools <refused tools> to the resume command. Glider knows the tool name only after the refusal. Therefore the name cannot be in a fixed template, and Glider makes the argument from the refusal.
cursor-agent Glider adds nothing. --resume [chatId] --trust is sufficient. This CLI has no flag for one tool. The only other flag is --force or --yolo, which permits each tool. Glider does not use these flags for you.
agy Glider writes a limited rule into the settings.json of agy. If the workspace has its own config, Glider writes to that file also. Glider then puts the original bytes back into the two files. The changelog of agy shows that this is the correct mechanism for a permission in this mode. It is not an unofficial method.

Because of this difference, the interface has two methods and not one. ExtraResumeArgs gives the command-line arguments. GrantResumePermission makes a change outside the command line. Each vendor uses the method that its CLI supports. The shared code calls the two methods and does not know which one operates.

Two other methods for agy were tested first, and they failed. The --dangerously-skip-permissions flag was more simple, and it was the same as the other vendors. But in a live test the model gave an explanation of the flag and did not obey it. The --continue and --conversation <id> flags also failed, because agy continued a different conversation.

The VendorAdapter interface contains each difference between the vendors. The methods are DetectDenials, ExtractSessionID, GrantResumePermission, ExtraResumeArgs, WrapResumePrompt and ExtractEditViews. The shared code never makes a decision from a vendor name.

How Glider finds the workspace

The protocols that Glider intercepts do not contain a file path. Therefore a delegate call cannot find the project directory of the origin CLI from the request. Glider tried to read the path from the memory of the origin process (the PEB). This method failed: on a usual installation, Windows Defender protects the memory of a process, and it makes the read incomplete.

Glider uses the PID of the origin process instead. It gets the PID from the TCP connection, and it does not read the memory. The PID is the key to a small registry of directories. For a new PID, Glider asks you one time. Reply with <path> /workspace. This uses the same flag position as above. Glider keeps the directory while that process operates. Glider asks each new session, also when you set a default workspace on the Vendors page of the dashboard. That default goes in the question, and you send it back. Glider does not use it as a silent answer, because it cannot see which project a new CLI is in.

The context for the delegate

A delegate CLI received only two items: the prompt text and a work directory. It did not know the full task, and it did not know which CLI sent the task. Now each delegate run gets its own directory at ~/.glider/delegates/<id>. That directory holds one context file. Glider gives the file to the CLI with a flag that the vendor supports. The CLI reads the file when the session starts, and the file does not use the prompt space.

VendorHow the context comes to the CLI
claude--append-system-prompt-file=<file>. Glider gives the full path, and the name of the file is not important.
cursor-agent--add-dir=<dir>. The CLI reads an AGENTS.md file in that directory.
agy--add-dir=<dir>. This is the same method, and it also uses AGENTS.md.

These flags are usual template arguments in configs/vendor_candidates.yaml. They use {{context_file}} and {{context_dir}}. The flag for each vendor is data. The shared code does not make a decision from the vendor name. If a vendor has no context setting, Glider runs it with no context flag. Glider removes the flag.

The context file contains the task, the name of the CLI that sent the task, and the workspace. It also contains the recent instructions from the session that sent the task. The oldest instruction is first, and each one has a mark that shows it is background data.

Glider does not write in your files. Glider never changes the CLAUDE.md file or the AGENTS.md file of your project. An earlier design wrote to these files. That design had to lock them, put the original bytes back, and correct them after a run stopped incorrectly. One directory for each delegate removes these problems. The directory belongs to one run, and Glider deletes it when the run ends.

How Glider shows the result

Glider adds the edits of a delegate to the reply as a diff. This is the same for each vendor. VendorAdapter.ExtractEditViews reads the edit format of the vendor and makes a standard EditViews structure. Claude gives patch hunks, cursor-agent gives a before-and-after string, and agy replaces a range or writes a full file. Then FormatEditSummary shows the standard structure in one format.

Clean or raw

A headless run writes a full transcript to its output. Glider does not give you that transcript by default. The setting is on the Vendors page of the dashboard, and POST /api/vendors/response-detail also writes it.

ValueWhat you receive
cleanThe final answer only. DelegateRenderer removes the frames of the protocol. This is the default, and an unknown value becomes this value.
rawThe recorded output, with no change. Use it when a delegate gives a strange answer and you must see what the CLI truly wrote.

The value stays on disk, in the vendor registry. Therefore it continues after a restart. Refer to NGL for how a renderer finds the answer in the output of a vendor.

What Glider records about a vendor

GET /api/vendors gives the registry. Discovery writes it, and the dashboard changes it.

FieldContent
name · binary · pathThe name that a flag uses, the name of the program, and where discovery found it.
versionWhat probe_args gave back.
printFlagThe flag for a run with no console.
templatesThe command shapes. A :name after the flag selects one.
contextFileThe file that this CLI reads when a session starts: CLAUDE.md for claude, AGENTS.md for the others. Glider writes the pack of the delegate there, and puts the original content back after the run. An empty value stops the sharing of context for that vendor.
enabled · discoveredAtWhether Glider uses it, and when it found it.

The registry also holds defaultWorkspace and responseDetail. Discovery never changes those two, therefore a rescan does not remove your settings.

An interactive template opens a window

A template with mode: interactive does not go through any of the above. LaunchInteractive opens a new, visible console with the native session of that CLI, and your task is its first message. Glider records nothing from that window and relays nothing back. You control it.

POST /api/vendors/{name}/launch-interactive does the same thing from the dashboard.

The known limits

Send the task without a flag

Each example on this page has a flag that you type. Your CLI can also write that flag itself. Put rules in the context file of your project, and the CLI applies them. Automatic rules gives a template and the conditions that operate correctly. This needs no new function in Glider, because the flag is only text at the end of a message.

The full design is in planning/permission_relay_design.md. The adapter interface is in planning/ngl_and_adapters.md.