{
  "markdown": "# NexusAgent\n\nA macOS menu bar app and Telegram bot that bridges your messages to locally-installed AI coding CLI tools, giving you remote access to full coding agent capabilities (file editing, terminal commands, MCP tools, multi-provider support) from any device with Telegram or directly from your Mac's Quick Prompt.\n\n---\n\n## System Architecture\n\n### Overview\n\nThe bot acts as a thin bridge between the Telegram Bot API and a locally-running AI CLI process. Every message you send on Telegram is forwarded as a headless prompt to your configured CLI tool (e.g. `gemini -p`, or a custom provider), and the CLI's JSON response is parsed, formatted, and sent back as a Telegram reply.\n\n```mermaid\nflowchart LR\n    A[Telegram API] <-->|long-poll| B[Bot Server\\nNode.js]\n    B -->|child process| C[AI CLI\\nGemini/Custom]\n    B <--> D[(Session Store\\nin-mem)]\n    C <--> E[(Local File System)]\n    C <--> F[Terminal, MCP, Git]\n\n    subgraph \"Your Machine\"\n        B\n        C\n        D\n        E\n        F\n    end\n```\n\n### Key Design Decisions\n\n| Decision | Rationale |\n|----------|-----------|\n| **Long polling** (not webhooks) | No public URL or TLS certificate required — runs entirely on your local machine |\n| **Child process per prompt** | The CLI is stateless per invocation; session continuity is handled via CLI flags (`--resume`) |\n| **In-memory session store** | Simple `Map<chatId, sessionId>` — no database needed for single-user use |\n| **JSON output format** | Structured parsing of CLI responses instead of fragile text scraping |\n| **`yolo` approval mode** | Auto-approves all tool actions for unattended operation (configurable) |\n\n---\n\n## Software Architecture\n\n### Module Dependency Graph\n\n```mermaid\ngraph TD\n    A[src/bot.js<br>Entry point, orchestration] --> B[src/gemini.js<br>CLI Process & Provider Logic]\n    A --> C[src/formatter.js<br>Telegram Message Formatting]\n    A --> D[src/sessions.js<br>Session Management]\n```\n\n### Module Details\n\n#### `src/bot.js` — Bot Server & Orchestration\n\nThe main entry point. Initializes the Telegraf bot, registers middleware, commands, and message handlers.\n\n**Responsibilities:**\n- Load configuration from environment variables via `dotenv`\n- Initialize Telegraf with the bot token\n- Apply authentication middleware (user ID whitelist)\n- Register command handlers (`/start`, `/new`, `/session`, `/help`)\n- Forward incoming text messages to the Gemini module\n- Format and send responses back, splitting if needed\n- Maintain typing indicator during long-running prompts\n- Graceful shutdown on `SIGINT`/`SIGTERM`\n\n**Middleware pipeline:**\n\n```mermaid\nflowchart TD\n    A[Incoming Update] --> B{Auth Middleware}\n    B -->|Reject| C[\"Not authorized message\"]\n    B -->|Pass| D[Command or Text Handler]\n```\n\n#### `src/gemini.js` — AI CLI Interface & Pluggable Provider Management\n\nManages spawning of AI CLI child processes and tracking sessions per chat.\n\n**Responsibilities:**\n- Spawn `gemini -p \"<prompt>\"` or custom provider using `CLI_COMMAND_TEMPLATE`\n- Tokenize and inject `{prompt}` and `{model}` into custom provider arguments\n- Set working directory to `GEMINI_WORKING_DIR`\n- If a session exists for the chat, pass it to context continuity (e.g., via `--resume`)\n- Collect stdout/stderr buffers and parse on process exit\n- Stream support (`executePromptStreaming`) and multi-strategy JSON parsing\n- Track active running processes to allow prompt cancellation\n\n**Exported API:**\n\n| Function | Description |\n|----------|-------------|\n| `executePrompt(prompt, options)` | Run a prompt and wait, return `{ text, sessionId }` |\n| `executePromptStreaming(prompt, options)` | Run prompt with callback chunks |\n| `cancelPrompt(chatId)` | Terminate a running process for a chat |\n| `clearSession(chatId)` | Forget session for a chat |\n\n**CLI invocation example:**\n```bash\ngemini -p \"explain this function\" \\\n  --output-format json \\\n  --approval-mode yolo \\\n  --resume 910c55f0-f6a2-450e-9129-215a4e07abe2\n```\n\n#### `src/formatter.js` — Response Formatting\n\nHandles Telegram's message constraints and format conversion.\n\n**Responsibilities:**\n- Split responses exceeding Telegram's 4096-character limit into multiple messages\n- Intelligent splitting at paragraph boundaries → newlines → spaces → hard break\n- MarkdownV2 escaping utility (for future use)\n- Format selection (currently sends as plain text for maximum compatibility)\n\n---\n\n## Request Lifecycle\n\nA full request-response cycle for a text message:\n\n```mermaid\nsequenceDiagram\n    participant U as User (Telegram)\n    participant T as Telegram API\n    participant B as Bot Server\n    participant C as AI CLI Process\n\n    U->>T: Send message\n    T->>B: Deliver update via long-poll\n    B->>B: Auth middleware check\n    B->>T: Send \"typing\" action\n    B->>C: Spawn process (e.g. gemini -p \"message\")\n    Note over C: Executes shell, reads files, runs MCP\n    C-->>B: Return JSON to stdout\n    B->>B: Parse JSON & extract session ID\n    B->>B: Split response via formatter if > 4096 chars\n    B->>T: Send reply message(s)\n```\n\n---\n\n## Session Management\n\nSessions provide conversation continuity so follow-up messages have context.\n\n```mermaid\nflowchart LR\n    C1[Chat 1] --> S1[sessions.get] --> U1[session-uuid-abc] --> G1[CLI --resume session-uuid-abc]\n    C2[Chat 2] --> S2[sessions.get] --> U2[session-uuid-xyz] --> G2[CLI --resume session-uuid-xyz]\n```\n\n- **First message** in a chat: no resume flag is passed. The CLI starts a new session and returns a `sessionId` in its JSON output.\n- **Subsequent messages**: the stored `sessionId` is passed (via `--resume`), giving the CLI full conversation history.\n- **`/new` command**: deletes the stored session ID, so the next message starts fresh.\n- **Storage**: persisted across restarts using `.bot-sessions.json`.\n\n---\n\n## Security Model\n\n```mermaid\nblock-beta\n    columns 1\n    A(\"1. Telegram Bot Token (only you know it)\"):1\n    B(\"2. User ID Whitelist (ALLOWED_USER_IDS)\"):1\n    C(\"3. Local-only execution (no public server)\"):1\n    D(\"4. Process-level sandboxing (optional -s)\"):1\n```\n\n| Layer | Protection |\n|-------|------------|\n| **Bot token** | Only someone with the token can receive updates. Keep it secret. |\n| **User ID whitelist** | Even if someone finds your bot, they can't interact unless their Telegram user ID is in `ALLOWED_USER_IDS`. Unauthorized attempts are logged. |\n| **Local execution** | The bot uses long-polling, not webhooks — no ports are exposed to the internet. |\n| **Sandbox mode** | Pass `GEMINI_APPROVAL_MODE=default` or use Gemini CLI's `--sandbox` flag for restricted execution in a Docker/Podman container. |\n\n> ⚠️ **Warning**: `GEMINI_APPROVAL_MODE=yolo` auto-approves all tool actions (file writes, command execution). Only use this when you trust all messages will come from you.\n\n---\n\n## Quick Start\n\n### 1. Create a Telegram Bot\n\n1. Message [@BotFather](https://t.me/BotFather) on Telegram\n2. Send `/newbot` and follow the prompts\n3. Copy the bot token\n\n### 2. Get Your Telegram User ID\n\nMessage [@userinfobot](https://t.me/userinfobot) on Telegram — it will reply with your user ID.\n\n### 3. Configure\n\n```bash\ncp .env.example .env\n```\n\nEdit `.env`:\n```\nTELEGRAM_BOT_TOKEN=your_bot_token_here\nALLOWED_USER_IDS=your_user_id_here\nGEMINI_WORKING_DIR=/path/to/your/project\n```\n\n### 4. Install Dependencies\n\n```bash\nnpm install\n```\n\n### 5. Run\n\nThere are four ways to run the bot:\n\n#### Option A: Direct (foreground)\n\n```bash\nnpm start\n```\n\nRuns in the foreground — you'll see logs in your terminal. Press `Ctrl+C` to stop.\n\n#### Option B: Daemon via `bot.sh`\n\n```bash\n./bot.sh start     # Start in background\n./bot.sh stop      # Graceful shutdown\n./bot.sh restart   # Stop + start\n./bot.sh status    # Check if running\n./bot.sh logs      # Tail the log file\n```\n\nRuns in the background with PID tracking and orphan process cleanup. Logs are written to `bot.log`.\n\n#### Option C: macOS Menu Bar App\n\nDownload the latest DMG from the [Releases page](https://github.com/VitruvianSoftware/nexus-agent/releases). Open it and drag the app to your Applications folder.\n\nA native SwiftUI app that lives in the menu bar (no dock icon). Provides a GUI to start/stop the bot, view logs, configure settings, and handle Quick Prompts. See [macOS App Setup](#macos-menu-bar-app) below for Gatekeeper instructions.\n\n#### Option D: Gemini CLI Extension\n\n```bash\ngemini extensions install https://github.com/<your-repo>/nexus-agent\n# or link locally:\ngemini extensions link /path/to/nexus-agent\n```\n\nInstalls the bot as a Gemini CLI extension. Ask Gemini *\"help me set up the Telegram bot\"* and it will walk you through configuration using the bundled playbook.\n\n---\n\n## macOS Menu Bar App\n\nA native SwiftUI companion app that manages the bot daemon from the menu bar.\n\n### Features\n\n| Feature | Description |\n|---------|-------------|\n| **Status icon** | ✈️ filled = running, outline = stopped |\n| **Controls** | Start / Stop / Restart from the dropdown |\n| **Logs** | Recent log lines inline + open full log |\n| **Settings** | GUI for bot token, user IDs, working dir, model, approval mode |\n| **Auto-start** | Optionally start the bot when the app launches |\n| **No dock icon** | `LSUIElement=true` — menu bar only |\n\n### Installation\n\nThe application is distributed as a universal DMG. Because it is currently **unsigned**, macOS Gatekeeper will block the first launch. Follow these steps to install and open it:\n\n1. **Download** the latest `NexusAgent-x.x.x-universal.dmg` from the [GitHub Releases page](https://github.com/VitruvianSoftware/nexus-agent/releases).\n2. **Mount the DMG** by double-clicking it.\n3. **Install** by dragging the `NexusAgent` app into the `Applications` folder shortcut.\n4. **First Launch (Important):**\n   - Open your `Applications` folder in Finder.\n   - You **cannot** double-click the app directly (macOS will warn you about an unidentified developer).\n   - *For macOS 14 Sonoma and older:* **Right-click (or Control-click)** the app and select **Open**.\n   - *For macOS 15 Sequoia and newer:* Apple has removed the Right-click bypass. You have two options:\n     - **Option A (System Settings):** Double click the app and click **Done** on the warning. Open **System Settings > Privacy & Security**, scroll down to the Security section, and click **Open Anyway**.\n     - **Option B (Terminal):** Open your **Terminal** and run the following command to clear the browser download quarantine flag, then open the app normally:\n       ```bash\n       find /Applications/NexusAgent.app -print0 | xargs -0 xattr -c\n       ```\n\n*You only need to do this exact process once. For subsequent launches, or when the app auto-updates, it will open normally.*\n\n### Auto-Update\n\nThe app includes a built-in auto-updater. It will periodically check the GitHub Releases page for new versions. When an update is available:\n1. An \"Update available\" banner will appear in the menu bar dropdown.\n2. Click the **Update** button.\n3. The app will download the new version, replace itself in the `Applications` folder, and automatically relaunch.\n\n---\n\n## Bot Commands\n\n### Core\n\n| Command | Description |\n|---------|-------------|\n| `/start` | Welcome message and info |\n| `/help` | Show all available commands |\n\n### Session Management\n\n| Command | Description |\n|---------|-------------|\n| `/new` | Clear session and start fresh |\n| `/session` | Show current session info |\n| `/sessions` | List all available Gemini CLI sessions |\n| `/resume <n>` | Resume a session by index (e.g. `/resume 5` or `/resume latest`) |\n| `/delete_session <n>` | Delete a session by index |\n\n### CLI Management\n\n| Command | Description |\n|---------|-------------|\n| `/extensions` | List installed Gemini CLI extensions |\n| `/skills` | List available agent skills |\n| `/mcp` | List configured MCP servers |\n\n### Settings (per-chat)\n\n| Command | Description |\n|---------|-------------|\n| `/model <name>` | Set the Gemini model (e.g. `/model gemini-2.5-flash`) |\n| `/mode <mode>` | Set approval mode (`default`, `auto_edit`, `yolo`) |\n| `/sandbox` | Toggle sandbox mode (Docker/Podman) |\n| `/workdir <path>` | Set working directory for Gemini CLI |\n| `/settings` | Show all current settings |\n\n\n## Configuration Reference\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `TELEGRAM_BOT_TOKEN` | Bot token from BotFather | *required* |\n| `ALLOWED_USER_IDS` | Comma-separated Telegram user IDs | *empty = all allowed* |\n| `GEMINI_WORKING_DIR` | Working directory for AI CLI | Current directory |\n| `GEMINI_TIMEOUT_MS` | Max execution time per prompt (ms) | `300000` (5 min) |\n| `GEMINI_APPROVAL_MODE` | Tool approval mode (`default`, `auto_edit`, `yolo`) | `yolo` |\n| `GEMINI_MODEL` | Default model to use | CLI default |\n| `GEMINI_BIN` | Path to the `gemini` binary | `/opt/homebrew/bin/gemini` |\n| `CLI_PROVIDER` | Provider selection (`gemini`, `custom`) | `gemini` |\n| `CLI_COMMAND_TEMPLATE` | Custom CLI template (e.g. `ollama run {model} \"{prompt}\"`) | *empty* |\n| `GEMINI_THINKING` | Employs extended timeouts to support thinking models | *false* |\n\n## Requirements\n\n- Node.js 18+\n- [Gemini CLI](https://github.com/google-gemini/gemini-cli) installed and authenticated (`npm i -g @google/gemini-cli`)\n- A Telegram bot token from [@BotFather](https://t.me/BotFather)\n",
  "bytes": 13279,
  "sha": "c71d8ab73f004f4c119b42d49470c1035f5da408af976854759e816640047fae",
  "repo_slug": "vitruviansoftware/nexus-agent",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_vitruviansoftware_nexus_agent_67a0eef5/readme"
}