Operate

Agent-Friendly CLI

Agent-Friendly CLI

ClickClack should be agent-friendly without embedding an LLM runtime. The Go binary owns a stable command surface that humans, scripts, CI jobs, and external agents can drive. Model providers and agent loops live outside ClickClack.

The same binary has two roles:

  • clickclack serve hosts a ClickClack server.
  • clickclack ... without serve acts as a local or remote chat client.

This page is the target contract for that client CLI. The implemented subset is tracked in cli.md.

#Design Goals

  • Pleasant for humans in a terminal.
  • Predictable for agents and shell scripts.
  • Works against localhost and hosted servers with the same commands.
  • Uses the public HTTP/WebSocket API; no private server backdoors.
  • Supports durable cursor recovery for long-running watchers.
  • Keeps all LLM/provider behavior outside the ClickClack binary.

#Command Shape

Implemented now:

  • login --magic-token, logout, whoami, status.
  • workspaces list.
  • channels list.
  • send, messages send, messages list.
  • threads open, threads reply, and reply.
  • --server, --token, --user / --user-id, --workspace, --channel,
  • --json, --plain, --no-input, and --verbose.

Still target-only:

  • workspaces use, channels use, channels create.
  • messages tail, messages watch, events tail.
  • reactions from the CLI.
  • Stable non-generic exit-code mapping.
clickclack [global flags] <command> [args]

Server:
  serve                         run the HTTP/WebSocket server
  migrate                       apply embedded SQL migrations
  backup                        write a SQLite backup file
  export                        write a JSON dump

Client:
  login                         authenticate with a server
  logout                        remove stored credentials
  whoami                        print the current caller
  status                        print server, auth, workspace, and channel

Workspaces:
  workspaces list               list visible workspaces
  workspaces use <workspace>    set the default workspace

Channels:
  channels list                 list channels in the current workspace
  channels use <channel>        set the default channel
  channels create <name>        create a channel

Messages:
  send <body>                   send to the default channel
  messages send <body>          send to a channel
  messages list                 list recent channel messages
  messages tail                 stream new channel messages
  messages watch                stream durable events

Threads:
  threads open <message-id>     list a message thread
  threads reply <message-id>    reply in a thread

Reactions:
  reactions add <message-id> <emoji>
  reactions remove <message-id> <emoji>

Events:
  events tail                   stream workspace events

Short aliases are allowed for the high-frequency path:

clickclack send "deploy started"
clickclack tail --channel ops
clickclack reply msg_... --stdin <summary.md

#Global Flags

FlagMeaning
--server <url>Server URL. Defaults to config, then http://localhost:8080.
--workspace <id-or-slug>Workspace override.
--channel <id-or-name>Channel override.
--token <token>Authentication token. Prefer env or stored credentials for regular use.
--user <user-id>Local/dev user override. Skips stored bearer tokens unless --token is explicit.
--jsonEmit JSON instead of human text.
--plainEmit stable line-oriented text where useful.
--no-inputDisable prompts. Required for non-interactive automation.
--quietSuppress non-essential diagnostics.
--verbosePrint request IDs, selected config, and reconnect diagnostics to stderr.
--no-colorDisable ANSI color. Also respect NO_COLOR and TERM=dumb.

-h and --help always show help and ignore other flags. --version prints the binary version to stdout.

#Config

Current client resolution order:

  1. CLI flags.
  2. Environment variables.
  3. User config.
  4. Defaults.

Environment variables:

CLICKCLACK_SERVER=https://clickclack.chat
CLICKCLACK_TOKEN=ses_...
CLICKCLACK_USER_ID=usr_dev
CLICKCLACK_WORKSPACE=wsp_...
CLICKCLACK_CHANNEL=chn_...

User config:

{
  "server": "https:
   "ses_...",
   "main",
   "general"
}

The implemented client stores this at ~/.config/clickclack/config.json. Stored bearer tokens, workspace defaults, and channel defaults are scoped to their saved server URL. If a command points at another server, the saved token is not sent unless --token or CLICKCLACK_TOKEN explicitly provides one; workspace/channel defaults from the saved server are ignored.

--user / CLICKCLACK_USER_ID is only for local/dev impersonation. When it is set without an explicit token, the CLI skips any stored bearer token so the server sees X-ClickClack-User. For hosted agents, use a real session token.

#Input

Message body precedence:

  1. Positional body argument.
  2. --body <text>.
  3. --file <path>.
  4. --stdin.

Examples:

clickclack send "hello from a script"
clickclack messages send --channel ops --file incident.md
printf 'build passed\n' | clickclack send --stdin

Non-interactive commands must never prompt unless stdin is a TTY and --no-input is not set.

#Output

Primary data goes to stdout. Diagnostics, progress, reconnect notices, and warnings go to stderr.

Human output should be compact:

sent msg_01kr... to #ops

--json emits one JSON object for finite commands:

{"message":{"id":"msg_01kr...","channel_id":"chn_...","body":"hello"}}

Streaming commands use newline-delimited JSON:

{"type":"message.created","cursor":"evt_...","message":{"id":"msg_..."}}
{"type":"thread.reply_created","cursor":"evt_...","message":{"id":"msg_..."}}

Agents should use --json for commands that parse output and --plain only for stable single-field output such as IDs or tokens.

#Realtime Behavior

messages tail, messages watch, and events tail use the same recovery model as the web client:

  1. Load the saved cursor for the selected server/workspace/channel.
  2. Fetch missed durable events with /api/realtime/events?after_cursor=....
  3. Open /api/realtime/ws.
  4. Persist the last delivered cursor after each event.
  5. If the server returns resync_required, refetch state and replace the saved
  6. cursor.

Default cursor state belongs under:

~/.local/state/clickclack/cursors/

#Exit Codes

CodeMeaning
0Success.
1Generic failure.
2Invalid usage or validation failure.
3Authentication required or invalid credentials.
4Requested workspace, channel, message, or thread was not found.
5Authenticated but not allowed.
10Network unavailable or server unreachable.
11Server returned an unexpected response.

Scripts should branch on exit codes, not error text.

#Agent Examples

Post a message to a hosted server:

CLICKCLACK_SERVER=https://clickclack.chat \
CLICKCLACK_TOKEN=ses_... \
clickclack send --channel ops "release started"

Watch channel messages as JSON:

clickclack messages tail --channel ops --json --no-input

Reply to a thread with generated text from another process:

agent-summarize incident.log | clickclack threads reply msg_01kr... --stdin

Bridge CI output into a channel:

if pnpm test; then
  clickclack send --channel builds "tests passed"
else
  clickclack send --channel builds "tests failed"
fi

Fetch recent messages for context:

clickclack messages list --channel support --limit 20 --json

#Implementation Order

  1. login, logout, whoami, and credential storage.
  2. workspaces list/use and channels list/use.
  3. send and messages list.
  4. messages tail --json with durable cursor recovery.
  5. threads open/reply.
  6. Shell completions and stricter exit-code mapping.