# User Guide ## Purpose Use `docs/user-guide/` for operator-facing instructions on how to run, inspect, and troubleshoot `clisbot`. This folder should explain: - how to start and inspect the service - how to interact with the dedicated tmux server - where runtime state lives - where channel-specific operating notes live - how config reload works in practice - how stale tmux cleanup and sensitive commands work in practice - how app ownership, roles, and privileged actions are expected to work for operators Related pages: - [Channel Operations](channels.md) - [API Channel](api-channel.md) - [Surface Access Model](surface-access-model.md) - [Bots And Credentials](bots-and-credentials.md) - [Release Notes](../releases/README.md) - [Codex CLI Guide](codex-cli.md) - [Claude CLI Guide](claude-cli.md) - [Gemini CLI Guide](gemini-cli.md) - [Telegram Bot Setup](telegram-setup.md) - [Zalo Bot Setup](zalo-bot-setup.md) - [Zalo Personal](zalo-personal.md) - [Slack App Setup](slack-setup.md) - [Slash Commands](slash-commands.md) - [Native CLI Commands](native-cli-commands.md) - [CLI Commands](cli-commands.md) - [Agent Progress Replies](agent-progress-replies.md) - [Authorization And Roles](auth-and-roles.md) - [Runtime Operations](runtime-operations.md) - [Generic README Backup](generic-readme.md) If setup is unclear, clone this repo, open it in Codex or Claude Code, and ask it to help set up `clisbot`. The docs here are kept current enough for guided setup and troubleshooting. ## CLI-Specific Notes If you are choosing a default coding CLI or debugging routed behavior, start here: - [Codex CLI Guide](codex-cli.md) - [Claude CLI Guide](claude-cli.md) - [Gemini CLI Guide](gemini-cli.md) ## Platform Support - Linux and macOS are the supported host environments today. - Native Windows is not supported yet because `clisbot` currently depends on `tmux`, a dedicated tmux socket, and Bash-based runtime helpers. - If you use Windows, run `clisbot` inside WSL2 and follow the normal Linux-style setup from that WSL2 shell. ## Service Default config path: `~/.clisbot/clisbot.json` Bootstrap the default config once: ```bash bun run init ``` Fastest path: ```bash bun run start --cli codex --bot-type personal --telegram-bot-token ``` Focused help exists for first-run bootstrap too: ```bash clisbot start --help clisbot init --help ``` For durable one-shot follow-up work, use `clisbot queues --help`. Queue creation requires explicit routed addressing plus `--sender`; there is no ambient `--current` mode, and queue commands use `--channel/--target` addressing. After your first successful `clisbot start`: 1. Get your principal from a surface the bot can already see: Telegram groups or topics can use `/whoami` even before routing, while DMs with `pairing` policy need pairing first. `/whoami` and routed `/start` show: - `principal` Routed `/whoami` also shows `sessionId` plus whether that value is already persisted for the conversation. Typical values look like `telegram:1276408333` or `slack:U123ABC456`. 2. Grant the first app owner with `clisbot auth add-user app --role owner --user `, for example `clisbot auth add-user app --role owner --user telegram:1276408333`. 3. Inspect and tune role permissions with `clisbot auth --help`. 4. Continue with route setup in [Channel Operations](channels.md) and auth details in [Authorization And Roles](auth-and-roles.md). Persist the token for later plain `clisbot start` runs: ```bash clisbot start --cli codex --bot-type personal --telegram-bot-token --persist ``` If you use the packaged CLI: ```bash clisbot start --cli codex --bot-type personal --telegram-bot-token --persist ``` Packaged CLI runtime expects Node 20+. Native Windows shells such as PowerShell or Command Prompt are not supported for runtime operation. Use WSL2 instead. Start the service in the foreground: ```bash bun run start ``` Start the packaged CLI in the background: ```bash npx clisbot start ``` Restart the packaged CLI: ```bash clisbot restart ``` Stop the packaged CLI: ```bash clisbot stop ``` Stop the packaged CLI and clean up all tmux sessions on the clisbot socket: ```bash clisbot stop --hard ``` Start the service in dev mode: ```bash bun run dev ``` Print the resolved default config path: ```bash clisbot status ``` Important distinction: - `clisbot start` seeds `~/.clisbot/clisbot.json` automatically if it does not exist - `clisbot start` requires Slack, Telegram, or Zalo Bot token references before it bootstraps anything - when no agents exist yet, `start` requires both `--cli` and `--bot-type` to create the first `default` agent - `--slack-app-token`, `--slack-bot-token`, `--telegram-bot-token`, and `--zalo-bot-token` accept either bare env names like `CUSTOM_SLACK_APP_TOKEN` or placeholder form like `${CUSTOM_SLACK_APP_TOKEN}` - `clisbot start` prints which token refs or credential sources it is using for the channels you requested - existing enabled channel token refs are validated before the detached runtime is spawned - fresh bootstrap enables only the channels and bots you named explicitly with flags - `--persist` writes canonical credential files for any literal channel tokens from that invocation, so later plain `clisbot start` can reuse them - Gemini-first setups also need Gemini itself to already be authenticated for routed use, either by a prior direct `gemini` login or by headless auth such as `GEMINI_API_KEY` or Vertex AI credentials - the generated default config does not preseed Slack channel routes, Slack groups, Telegram groups, or Telegram topics - you must add channel routes manually in `~/.clisbot/clisbot.json` - `clisbot start` prints a brief agents and channels summary after launch - `clisbot start` and `clisbot status` print the primary agent workspace before the config path - that workspace is the default working directory for the agent and contains runtime state, sessions, personality files, and setup guidance - when no agents exist yet, `clisbot start` prints first-run guidance for direct `start --cli ... --bot-type ...` usage and bootstrap completion - `clisbot start` runs as a background service and writes runtime pid and log files under `~/.clisbot/state` - `bun run dev` watches source files in this repo - `control.configReload.watch` watches the runtime config file - these are separate mechanisms ## Agents CLI Use `clisbot agents ...` to manage configured agents, bootstrap files, and agent-local mode overrides. Focused help: ```bash clisbot agents --help ``` Most-used commands: - `clisbot agents list` - `clisbot agents list --json` - `clisbot agents add --cli ` - `clisbot agents bootstrap --bot-type ` - `clisbot agents response-mode status --agent ` - `clisbot agents response-mode set --agent ` - `clisbot agents response-mode clear --agent ` - `clisbot agents additional-message-mode status --agent ` - `clisbot agents additional-message-mode set --agent ` - `clisbot agents additional-message-mode clear --agent ` Important rules: - `agents add` requires `--cli` - supported tools are `codex`, `claude`, and `gemini` - `agents bootstrap` requires `--bot-type` - first-run public choices stay `--bot-type personal|team` - bot fallback routing now belongs to `clisbot bots ...` - route-level routing now belongs to `clisbot routes ...` Bootstrap behavior: - bootstrap is optional; agents can run without it when you already have a workspace and do not need clisbot to seed guidance files - `personal-assistant` and `team-assistant` copy the matching bootstrap templates when you pass `--bot-type` - `AGENTS.md` is the one canonical workspace instruction file across supported CLIs - codex bootstrap requires `AGENTS.md` and `IDENTITY.md` - claude bootstrap requires `AGENTS.md`, `IDENTITY.md`, and a `CLAUDE.md -> AGENTS.md` symlink for Claude discovery - gemini bootstrap requires `AGENTS.md`, `IDENTITY.md`, and a `GEMINI.md -> AGENTS.md` symlink for Gemini discovery - bootstrap state is `missing`, `not-bootstrapped`, or `bootstrapped` - Gemini still needs its own direct auth or headless auth path before routed prompts can succeed Operational note: - the default generated bot config still points at the `default` agent - if your first useful agent uses another id, update the fallback with `clisbot bots set-agent ...` or override it on a specific route with `clisbot routes set-agent ...` ## Bots CLI Use `clisbot bots ...` to manage provider bot identities, credentials, default bot selection, and bot-level fallback policy. Focused help: ```bash clisbot bots --help ``` Most-used commands: - `clisbot bots list` - `clisbot bots add --channel telegram --bot default --bot-token TELEGRAM_BOT_TOKEN --persist` - `clisbot bots add --channel zalo-bot --bot default --bot-token ZALO_BOT_TOKEN --persist` - `clisbot bots add --channel slack --bot default --app-token SLACK_APP_TOKEN --bot-token SLACK_BOT_TOKEN --persist` - `clisbot bots set-agent --channel telegram --bot default --agent support` - `clisbot bots set-default --channel slack --bot ops` - `clisbot bots get-credentials-source --channel slack --bot default` - `clisbot bots set-dm-policy --channel telegram --bot default --policy pairing` ## Routes CLI Use `clisbot routes ...` to admit specific Slack, Telegram, or Zalo Bot surfaces under a bot. Focused help: ```bash clisbot routes --help clisbot routes --help --channel telegram ``` Most-used commands: - `clisbot routes list` - `clisbot routes add --channel slack group:C1234567890 --bot default` - `clisbot routes add --channel slack group:G1234567890 --bot default` - `clisbot routes add --channel telegram group:-1001234567890 --bot default` - `clisbot routes add --channel telegram topic:-1001234567890:42 --bot default` - `clisbot routes add --channel telegram dm:* --bot default` - `clisbot routes add --channel zalo-bot dm: --bot default` - `clisbot routes set-agent --channel slack group:C1234567890 --bot default --agent support` - `clisbot routes set-require-mention --channel telegram group:-1001234567890 --bot default --value false` - `clisbot routes set-response-mode --channel slack group:C1234567890 --bot default --mode message-tool` - `clisbot routes set-additional-message-mode --channel telegram topic:-1001234567890:42 --bot default --mode queue` Important behavior: - preferred route ids are channel-scoped: Slack/Telegram shared surfaces use `group:` or `group:*`, Telegram topics use `topic::`, and DM-only providers such as Zalo Bot use `dm:` - stored config uses raw ids plus `*` inside `directMessages` and `groups` - legacy Slack `channel:` input is still accepted for compatibility - `group:*` is the default multi-user sender policy node of a bot - `disabled` means truly silent, including owner/admin and pairing guidance - owner/admin do not bypass `groupPolicy`/`channelPolicy` admission; after a group is admitted and enabled, they bypass sender allowlists, while `blockUsers` still wins - `add` is create-only - if the route already exists, use the matching `set-` command - route-local auth visibility still depends on `verbose` and resolved auth roles - `/loop 3 /codereview` - `/loop 3` - `/loop status` - `/loop cancel ` - every `/loop` command must include an interval, count, or wall-clock schedule - if no prompt is supplied after that interval, count, or schedule, clisbot uses the workspace `LOOP.md` file - bare positive integers in `/loop` mean times mode, compact durations such as `5m` mean interval mode, and `every ... at HH:MM` means wall-clock schedule mode - interval loops must be at least `1m` - interval loops below `5m` require `--force` - with leading interval syntax, place `--force` immediately after the interval token - with `every ...` syntax, place `--force` immediately after the interval clause - advanced recurring loop creation also accepts `--loop-start ` to override the default scheduled start notification behavior for that one loop; omit it to keep the route default - wall-clock schedules must use `HH:MM` in 24-hour format and wait until the next matching local time instead of firing immediately - wall-clock schedules resolve timezone from the effective timezone resolver: one-off loop override, route/topic, agent, bot, `app.timezone`, legacy fallbacks, then host fallback only when config is missing - chat `/loop` wall-clock creation persists immediately and returns the resolved timezone, local next run, UTC next run, and exact cancel command so a wrong timezone can be fixed quickly - managed loops are created with ids, bounded by `control.loop.maxRunsPerLoop`, and capped globally by `control.loop.maxActiveLoops` - managed loops use `skip-if-busy`, so a tick is dropped instead of stacking more queued work when the session is already busy - managed loops persist in session state and are restored after restart - queued prompts persist under the session entry, can be inspected with `clisbot queues list`, and are capped per session by `control.queue.maxPendingItemsPerSession` (default `20`) - `/stop` interrupts the current run only; use `/loop cancel` to cancel loops - `/nudge` sends one extra `Enter` to the current tmux session without resending the prompt body; use it only as a manual recovery tool when a session seems stuck after input delivery Timezone config examples: ```json { "app": { "timezone": "Asia/Ho_Chi_Minh" } } ``` ```json { "bots": { "telegram": { "default": { "groups": { "-1001234567890": { "timezone": "Asia/Ho_Chi_Minh", "topics": { "4": { "timezone": "America/Los_Angeles" } } } } } } } } ``` - agent `timezone` overrides `app.timezone` for that assistant/workspace - route `timezone` overrides app/agent timezone for that surface - use `clisbot timezone set ` for the app default, `clisbot agents set-timezone --agent ` for one assistant/workspace, and `clisbot routes set-timezone ... ` for one surface - once a wall-clock loop is created, its effective timezone is persisted on that loop record - if the service is already running, restart it after changing channel enablement - `clisbot routes` and `clisbot routes --help` print setup guidance for Slack ids, Telegram group or topic ids, allowlists, and routed auth docs ## Loops CLI Use `clisbot loops ...` to create, inspect, or cancel loop work from the operator CLI. Current subcommands: - `clisbot loops list` - `clisbot loops status` - `clisbot loops status --channel slack --target group:C123 --thread-id 1712345678.123456` - `clisbot loops create --channel slack --target group:C123 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI` - `clisbot loops --channel telegram --target -1001234567890 --topic-id 42 --sender telegram:1276408333 5m check CI` - `clisbot loops --channel slack --target group:C123 --thread-id 1712345678.123456 --sender slack:U1234567890 3 review backlog` - `clisbot loops cancel ` - `clisbot loops cancel --channel slack --target group:C123 --thread-id 1712345678.123456 --all` - `clisbot loops cancel --all` Targeting rules: - `--target` chooses the routed surface - on Slack, use `group:`, `dm:`, or raw `C...` / `G...` / `D...` ids - on Telegram, use `group:` or `topic::`; raw numeric chat ids are still accepted for compatibility - `--thread-id` means an existing Slack thread ts - `--topic-id` means a Telegram topic id - omitting the sub-surface flag targets the parent Slack channel/group/DM or the parent Telegram chat - use `clisbot loops --help --channel ` or `clisbot loops create --help --channel ` for channel-specific loop extensions - `--sender ` is required for loop creation and records the human creator as `slack:`, `telegram:`, or `zalo-bot:` - `--sender-name ` and `--sender-handle ` optionally store readable creator context for scheduled prompts - in Telegram forum groups, omitting `--topic-id` targets the parent chat surface; sends then follow Telegram's normal no-`message_thread_id` behavior, which is the General topic when that forum has one Important behavior: - bare `list` and `status` are app-wide inventory - scoped `list` or `status` with `--channel ... --target ...` matches one routed session - recurring CLI-created loops reuse the same parser family as `/loop` and land in the same persisted session store - CLI loop creation fails without `--sender` so delayed work keeps a real creator instead of rendering sender as unavailable - the CLI accepts the same expression families as `/loop`: interval, forced interval, times/count, and wall-clock schedules - advanced recurring `clisbot loops create` also accepts `--loop-start ` to override the default scheduled start notification behavior for that one loop - omitting the prompt body loads `LOOP.md` from the target workspace, matching maintenance-loop behavior from chat - every row includes `agentId` and `sessionKey` because the operator CLI is app-wide rather than route-scoped - `cancel --all` is app-wide when no routed target is given - scoped `cancel --all` clears one routed session and scoped `cancel --all --app` clears the whole app - if runtime is already running, it reconciles new recurring loops from persistence; if runtime is stopped, those loops activate on the next start - count/times loops reserve all iterations immediately as durable queue items for the routed session; recurring loops are persisted for the runtime scheduler - loop state is read from `session.storePath`, which defaults to `~/.clisbot/state/sessions.json` - if `CLISBOT_HOME` is set, the default session store becomes `/state/sessions.json` - the runtime scheduler re-checks persisted loop state before each scheduled tick, so cancelling through the CLI suppresses future runs without adding a separate control socket - cancelling through the CLI does not interrupt an iteration that is already running ## Start And Status Output `clisbot start` now prints an operator summary after startup. What it includes: - configured agents with tool, bootstrap state, routed usage, and last activity - configured Slack and Telegram channel summaries with connection state and last activity - first-run guidance when no agents are configured - bootstrap follow-up guidance when an agent workspace is `missing` or `not-bootstrapped` - direct next steps for `clisbot --help` and the user guide when no agents are configured - channel-specific token checks such as `Slack channel: found token...` or `Telegram channel: token not found...` - the same operator help even when the service is already running ## Custom Token References If your shell uses different environment variable names, pass them directly on first run: ```bash clisbot start \ --cli codex \ --bot-type personal \ --slack-app-token CUSTOM_SLACK_APP_TOKEN \ --slack-bot-token CUSTOM_SLACK_BOT_TOKEN ``` Or for Telegram: ```bash clisbot start \ --cli claude \ --bot-type team \ --telegram-bot-token CUSTOM_TELEGRAM_BOT_TOKEN ``` Important behavior: - these values are written into `~/.clisbot/clisbot.json` as `${ENV_NAME}` placeholders - bare env names are normalized into `${ENV_NAME}` placeholders in config - `clisbot` does not resolve or print the secret value during config bootstrap - this is meant for env variable names you chose, not raw secret literals - if you still prefer manual setup, `clisbot init` accepts the same `--cli`, `--bot-type`, and token-reference flags as `clisbot start`, but it does not start the runtime `clisbot status` now prints: - process status, pid, config path, log path, and tmux socket - aggregate stats for agents, bootstrapped agents, pending bootstrap agents, and running tmux sessions - per-agent last activity - per-channel last activity and effective connection state - active runtime channel identity when available, such as account id, bot label, app id, and a short token fingerprint Connection state meaning: - `active`: channel is enabled and the runtime process is running - `stopped`: channel is enabled in config but the runtime process is not running - `disabled`: channel is turned off in config ## Runtime Operations For runtime-level tuning and troubleshooting, use the dedicated guide: - [Runtime Operations](runtime-operations.md) That page now holds: - turn execution timeout semantics - long-running session commands such as `/attach`, `/detach`, and `/watch` - runner debug commands such as `clisbot runner list|inspect|watch` and the `clisbot inspect` / `clisbot watch` shorthand - dedicated tmux socket usage and raw tmux fallback commands - runtime state file locations and Codex trust troubleshooting - stale tmux cleanup behavior - config reload rules and follow-up-state notes ## Notes - the default workspace for agent `default` is `~/.clisbot/workspaces/default` - tmux session names now derive from session keys, so one agent can have multiple tmux sessions at once - the default session name template is `agents.defaults.session.name = "{sessionKey}"` - tmux session names use a readable tmux-safe prefix plus a stable short hash from the logical `sessionKey` - prefer `clisbot runner list`, `clisbot inspect --latest`, and `clisbot watch --latest|--next` before dropping to raw tmux - if the Codex trust screen appears stale after attaching, press `Ctrl-L` inside the tmux session to redraw the pane