Skip to content

CLI and socket API

A plugin talks to the tt-git it runs under in one of two ways:

  • Run the CLI. $TTGIT_BIN_PATH ui notify "done". This is the simplest way, and works from any language that can start a process.
  • Use the socket. Connect to $TTGIT_SOCKET_PATH and exchange JSON lines. This suits a long-running program that makes many calls or subscribes to events.

Both reach the same API. Every CLI command that needs a running tt-git is a thin wrapper over one socket method.

Plugins get TTGIT_BIN_PATH (the running executable) and TTGIT_SOCKET_PATH, so these commands find the right tt-git without any flags:

Terminal window
"$TTGIT_BIN_PATH" ui notify "Deployed main" # a message in the help bar
"$TTGIT_BIN_PATH" ui error "Deploy failed" # an error popup
"$TTGIT_BIN_PATH" ui refresh # reload everything
"$TTGIT_BIN_PATH" ui focus branches # status, branches, graph or details
"$TTGIT_BIN_PATH" branch decorate feature/login=!1423 # this plugin's column in the branch list
"$TTGIT_BIN_PATH" plugin pane open --plugin "$TTGIT_PLUGIN_ID" --entrypoint list --env PR_BRANCH=main
"$TTGIT_BIN_PATH" plugin action invoke acme.github.open-commit
"$TTGIT_BIN_PATH" events --event push.completed # stream events, one JSON object per line
"$TTGIT_BIN_PATH" api repo.info # any socket method, printed as JSON

Two commands read the repository directly instead, and don’t need a running tt-git. They use TTGIT_REPO_PATH, or --repo:

Terminal window
"$TTGIT_BIN_PATH" status --json
"$TTGIT_BIN_PATH" branch list --json

Command line lists every command and option.

The socket is a Unix domain socket, or a named pipe on Windows. Its address is in TTGIT_SOCKET_PATH, and it’s unique to each running tt-git. A tt-git started with --no-plugins doesn’t open one.

Messages are newline-delimited JSON. Send a request with an id, a method and optional params; the reply carries the same id and either a result or an error:

→ {"id":"req_1","method":"ui.notify","params":{"message":"hi"}}
← {"id":"req_1","result":{}}
→ {"id":"req_2","method":"ui.notify","params":{}}
← {"id":"req_2","error":{"code":"invalid_params","message":"message is required"}}
import net from "node:net";
const sock = net.createConnection(process.env.TTGIT_SOCKET_PATH);
sock.write(JSON.stringify({ id: "1", method: "repo.info" }) + "\n");
sock.on("data", (buf) => {
for (const line of buf.toString().split("\n").filter(Boolean)) {
console.log(JSON.parse(line).result);
}
sock.end();
});
MethodParamsResult
ping{pong, version}
repo.info{path, name, head, branch, upstream, ahead, behind}
status.getBranch headers, file entries and any merge, rebase or cherry-pick in progress.
branch.listThe unified branch list, each row with its state and decorations ({plugin_id: text}).
branch.decorations.set{plugin_id, decorations: {branch: text}}{count}. Replaces the plugin’s branch column; {} removes it.
graph.selection{scope, branch, index, commit}. branch is set while the graph shows one branch from the Merged or Unmerged tab.
ui.notify{message}{}. Shows the message in the help bar.
ui.error{message}{}. Shows an error popup.
ui.refresh{}. Reloads everything.
ui.focus{pane}: status, branches, graph or details{}
plugin.listInstalled plugins.
plugin.link{path, enabled?}
plugin.unlink, plugin.enable, plugin.disable{plugin_id}
plugin.action.list{plugin_id?}Actions.
plugin.action.invoke{action_id, plugin_id?}{log_id}
plugin.log.list{plugin_id?, limit?}Command logs, newest first; limit defaults to 50.
plugin.pane.open{plugin_id, entrypoint, placement?, width?, height?, env?, focus?}{pane_id}
plugin.pane.focus{pane_id}{}
plugin.pane.close{pane_id}{}
plugin.pane.screen{pane_id}{text}: what the pane shows right now.
plugin.pane.listOpen panes.
popup.close{closed}
events.subscribe{events?: [names]}{subscribed}, then one event envelope per line. See Events.

A plugin can show a few characters next to each branch in the branch list, such as a pull request number. Call branch.decorations.set with your plugin’s id and a map from branch names to text, or run tt-git branch decorate:

Terminal window
"$TTGIT_BIN_PATH" branch decorate feature/login=!1423 fix/typo=!1431
"$TTGIT_BIN_PATH" branch decorate --clear
  • Each call replaces everything the plugin set before, so send the whole set every time. An empty map, or --clear, removes the column.
  • Each plugin gets its own column, between the branch name and the status column, in plugin id order. It’s as wide as its widest value among the branches listed, up to 10 cells, so the values line up.
  • Names are matched against the rows of the full list (name in branch.list), which are local branch names or remote branch names without the remote. Text for a branch that isn’t listed is kept, and shows if the branch appears later.
  • Control characters are removed and text is cut to 10 cells.
  • The plugin must be installed and enabled. When it’s disabled or removed, its column goes away. Columns aren’t saved, so set them again when tt-git starts (on app.started).

The Azure DevOps plugin uses this to show pull request numbers.

plugin.action.invoke accepts a local action id when you also pass plugin_id; otherwise give the qualified id. plugin.pane.open fails with ui_busy if a popup is already open, since only one can be open at a time.

Errors have a code and a message. Codes you may see:

CodeMeans
invalid_requestThe line isn’t a valid request.
unknown_methodNo such method.
invalid_paramsA required param is missing or has the wrong type.
plugins_disabledtt-git runs without plugins.
plugin_action_failedThe action couldn’t start: for example, no such action, or 32 plugin commands are already running. The message says which.
plugin_pane_failed, ui_busyThe pane couldn’t open, or a popup is already open.
plugin_registry_errorLinking, unlinking, enabling or disabling failed.
plugin_not_foundbranch.decorations.set named a plugin that isn’t installed and enabled.
server_shutting_downtt-git is quitting.