Metadata-Version: 2.4
Name: remote-coder
Version: 0.4.2
Summary: Telegram-based remote AI coding automation server
Author: Remote AI Coder contributors
License:                                  Apache License
                                   Version 2.0, January 2004
                                http://www.apache.org/licenses/
        
           TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
        
           1. Definitions.
        
              "License" shall mean the terms and conditions for use, reproduction,
              and distribution as defined by Sections 1 through 9 of this document.
        
              "Licensor" shall mean the copyright owner or entity authorized by
              the copyright owner that is granting the License.
        
              "Legal Entity" shall mean the union of the acting entity and all
              other entities that control, are controlled by, or are under common
              control with that entity. For the purposes of this definition,
              "control" means (i) the power, direct or indirect, to cause the
              direction or management of such entity, whether by contract or
              otherwise, or (ii) ownership of fifty percent (50%) or more of the
              outstanding shares, or (iii) beneficial ownership of such entity.
        
              "You" (or "Your") shall mean an individual or Legal Entity
              exercising permissions granted by this License.
        
              "Source" form shall mean the preferred form for making modifications,
              including but not limited to software source code, documentation
              source, and configuration files.
        
              "Object" form shall mean any form resulting from mechanical
              transformation or translation of a Source form, including but
              not limited to compiled object code, generated documentation,
              and conversions to other media types.
        
              "Work" shall mean the work of authorship, whether in Source or
              Object form, made available under the License, as indicated by a
              copyright notice that is included in or attached to the work
              (an example is provided in the Appendix below).
        
              "Derivative Works" shall mean any work, whether in Source or Object
              form, that is based on (or derived from) the Work and for which the
              editorial revisions, annotations, elaborations, or other modifications
              represent, as a whole, an original work of authorship. For the purposes
              of this License, Derivative Works shall not include works that remain
              separable from, or merely link (or bind by name) to the interfaces of,
              the Work and Derivative Works thereof.
        
              "Contribution" shall mean any work of authorship, including
              the original version of the Work and any modifications or additions
              to that Work or Derivative Works thereof, that is intentionally
              submitted to Licensor for inclusion in the Work by the copyright owner
              or by an individual or Legal Entity authorized to submit on behalf of
              the copyright owner. For the purposes of this definition, "submitted"
              means any form of electronic, verbal, or written communication sent
              to the Licensor or its representatives, including but not limited to
              communication on electronic mailing lists, source code control systems,
              and issue tracking systems that are managed by, or on behalf of, the
              Licensor for the purpose of discussing and improving the Work, but
              excluding communication that is conspicuously marked or otherwise
              designated in writing by the copyright owner as "Not a Contribution."
        
              "Contributor" shall mean Licensor and any individual or Legal Entity
              on behalf of whom a Contribution has been received by Licensor and
              subsequently incorporated within the Work.
        
           2. Grant of Copyright License. Subject to the terms and conditions of
              this License, each Contributor hereby grants to You a perpetual,
              worldwide, non-exclusive, no-charge, royalty-free, irrevocable
              copyright license to reproduce, prepare Derivative Works of,
              publicly display, publicly perform, sublicense, and distribute the
              Work and such Derivative Works in Source or Object form.
        
           3. Grant of Patent License. Subject to the terms and conditions of
              this License, each Contributor hereby grants to You a perpetual,
              worldwide, non-exclusive, no-charge, royalty-free, irrevocable
              (except as stated in this section) patent license to make, have made,
              use, offer to sell, sell, import, and otherwise transfer the Work,
              where such license applies only to those patent claims licensable
              by such Contributor that are necessarily infringed by their
              Contribution(s) alone or by combination of their Contribution(s)
              with the Work to which such Contribution(s) was submitted. If You
              institute patent litigation against any entity (including a
              cross-claim or counterclaim in a lawsuit) alleging that the Work
              or a Contribution incorporated within the Work constitutes direct
              or contributory patent infringement, then any patent licenses
              granted to You under this License for that Work shall terminate
              as of the date such litigation is filed.
        
           4. Redistribution. You may reproduce and distribute copies of the
              Work or Derivative Works thereof in any medium, with or without
              modifications, and in Source or Object form, provided that You
              meet the following conditions:
        
              (a) You must give any other recipients of the Work or
                  Derivative Works a copy of this License; and
        
              (b) You must cause any modified files to carry prominent notices
                  stating that You changed the files; and
        
              (c) You must retain, in the Source form of any Derivative Works
                  that You distribute, all copyright, patent, trademark, and
                  attribution notices from the Source form of the Work,
                  excluding those notices that do not pertain to any part of
                  the Derivative Works; and
        
              (d) If the Work includes a "NOTICE" text file as part of its
                  distribution, then any Derivative Works that You distribute must
                  include a readable copy of the attribution notices contained
                  within such NOTICE file, excluding those notices that do not
                  pertain to any part of the Derivative Works, in at least one
                  of the following places: within a NOTICE text file distributed
                  as part of the Derivative Works; within the Source form or
                  documentation, if provided along with the Derivative Works; or,
                  within a display generated by the Derivative Works, if and
                  wherever such third-party notices normally appear. The contents
                  of the NOTICE file are for informational purposes only and
                  do not modify the License. You may add Your own attribution
                  notices within Derivative Works that You distribute, alongside
                  or as an addendum to the NOTICE text from the Work, provided
                  that such additional attribution notices cannot be construed
                  as modifying the License.
        
              You may add Your own copyright statement to Your modifications and
              may provide additional or different license terms and conditions
              for use, reproduction, or distribution of Your modifications, or
              for any such Derivative Works as a whole, provided Your use,
              reproduction, and distribution of the Work otherwise complies with
              the conditions stated in this License.
        
           5. Submission of Contributions. Unless You explicitly state otherwise,
              any Contribution intentionally submitted for inclusion in the Work
              by You to the Licensor shall be under the terms and conditions of
              this License, without any additional terms or conditions.
              Notwithstanding the above, nothing herein shall supersede or modify
              the terms of any separate license agreement you may have executed
              with Licensor regarding such Contributions.
        
           6. Trademarks. This License does not grant permission to use the trade
              names, trademarks, service marks, or product names of the Licensor,
              except as required for reasonable and customary use in describing the
              origin of the Work and reproducing the content of the NOTICE file.
        
           7. Disclaimer of Warranty. Unless required by applicable law or
              agreed to in writing, Licensor provides the Work (and each
              Contributor provides its Contributions) on an "AS IS" BASIS,
              WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
              implied, including, without limitation, any warranties or conditions
              of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
              PARTICULAR PURPOSE. You are solely responsible for determining the
              appropriateness of using or redistributing the Work and assume any
              risks associated with Your exercise of permissions under this License.
        
           8. Limitation of Liability. In no event and under no legal theory,
              whether in tort (including negligence), contract, or otherwise,
              unless required by applicable law (such as deliberate and grossly
              negligent acts) or agreed to in writing, shall any Contributor be
              liable to You for damages, including any direct, indirect, special,
              incidental, or consequential damages of any character arising as a
              result of this License or out of the use or inability to use the
              Work (including but not limited to damages for loss of goodwill,
              work stoppage, computer failure or malfunction, or any and all
              other commercial damages or losses), even if such Contributor
              has been advised of the possibility of such damages.
        
           9. Accepting Warranty or Additional Liability. While redistributing
              the Work or Derivative Works thereof, You may choose to offer,
              and charge a fee for, acceptance of support, warranty, indemnity,
              or other liability obligations and/or rights consistent with this
              License. However, in accepting such obligations, You may act only
              on Your own behalf and on Your sole responsibility, not on behalf
              of any other Contributor, and only if You agree to indemnify,
              defend, and hold each Contributor harmless for any liability
              incurred by, or claims asserted against, such Contributor by reason
              of your accepting any such warranty or additional liability.
        
           END OF TERMS AND CONDITIONS
        
           APPENDIX: How to apply the Apache License to your work.
        
              To apply the Apache License to your work, attach the following
              boilerplate notice, with the fields enclosed by brackets "[]"
              replaced with your own identifying information. (Don't include
              the brackets!)  The text should be enclosed in the appropriate
              comment syntax for the file format. We also recommend that a
              file or class name and description of purpose be included on the
              same "printed page" as the copyright notice for easier
              identification within third-party archives.
        
           Copyright 2026 maroomir
        
           Licensed under the Apache License, Version 2.0 (the "License");
           you may not use this file except in compliance with the License.
           You may obtain a copy of the License at
        
               http://www.apache.org/licenses/LICENSE-2.0
        
           Unless required by applicable law or agreed to in writing, software
           distributed under the License is distributed on an "AS IS" BASIS,
           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           See the License for the specific language governing permissions and
           limitations under the License.
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi<1.0,>=0.115
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: pydantic<3.0,>=2.7
Requires-Dist: pydantic-settings<3.0,>=2.3
Requires-Dist: python-dotenv<2.0,>=1.0
Requires-Dist: pyyaml<7.0,>=6.0
Requires-Dist: uvicorn[standard]<1.0,>=0.30
Provides-Extra: dev
Requires-Dist: pytest<10.0,>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio<2.0,>=0.23; extra == "dev"
Requires-Dist: respx<1.0,>=0.21; extra == "dev"
Dynamic: license-file

# Remote AI Coder

An MVP that runs AI coding tasks on your local development machine through Telegram messages and reports Git branch/commit results back as notifications.

> [!WARNING]
> This project runs AI CLI and Git operations on your local machine via Telegram messages. Do not expose the server or admin UI directly to the public internet. Always configure the Telegram allowlist (and optionally a webhook secret) and use it only in a private, trusted environment.

## Multi-bot model (summary)

- **Each registered project gets its own Telegram bot.** There is no `/project` command to switch the target repository from chat.
- Each bot has a distinct webhook path: `POST /telegram/webhook/{first 16 hex chars of SHA-256(bot token)}` (the token itself is never placed in the URL).
- Bot token, allowed Chat/User IDs, and an optional webhook secret are stored in the **project registry** (`projects.json`, etc.). **Tokens are stored in plain text**, so keep strict file permissions and a careful backup policy.
- See [`docs/multi-bot-setup.md`](docs/multi-bot-setup.md) for the full procedure.
- When a project is **disabled or deleted**, the server stops routing updates arriving with that token hash prefix. Even if an old URL remains registered on Telegram, this app ignores it. To clear or repoint a bot's webhook, call the Bot API `deleteWebhook` or re-run [`scripts/set_webhook.py`](scripts/set_webhook.py) against the current registry.

## Publishing / security notes

- Never commit `TELEGRAM_BOT_TOKEN` (optional seed), the registry `bot_token`, Chat/User IDs, webhook secrets, AI API keys, or personal paths to code or docs.
- `.env`, `.remote-coder/` (especially `projects.json`), worktrees, logs, and the SQLite conversation-memory file are local-only data. This repository's `.gitignore` excludes them by default.
- The admin UI (`/`, `/projects`, `/advanced`, `/logs`, `/database`) is designed for localhost only. Do not expose it externally via reverse proxy, ngrok, port forwarding, etc.
- Options such as Claude `--dangerously-skip-permissions`, Gemini `--approval-mode yolo`, and Codex `danger-full-access` can modify local files. Use them only after restricting the allowed projects and trusted users.
- The conversation-memory SQLite may store users' Telegram requests and Job summaries. Do not paste sensitive code into messages, and clean up with `/clear memory` or the admin UI advanced settings when needed.

See [`SECURITY.md`](SECURITY.md) for vulnerability reporting and pre-publication review steps.

## Prerequisites

- Python 3.11+ or Conda
- A Telegram Bot Token (BotFather) per project, plus allowed Chat IDs (required) and User IDs (optional)
- An HTTPS tunnel tool (e.g. ngrok for development)
- At least one of: Claude Code CLI, Codex CLI, Gemini CLI
- A target Git project and a local directory for worktrees

## Quick start (recommended)

Install with a single `pip` command — no separate Conda environment required. This installs the `remote-coder` CLI; check prerequisites (ngrok, AI CLIs) with `remote-coder doctor`.

```bash
pip install remote-coder
```

> Before the first PyPI release, install straight from the git source (this is still `pip`):
>
> ```bash
> pip install git+https://github.com/maroomir/remote-coder.git
> ```

After installing, start the server and finish setup in the browser:

```bash
remote-coder up     # ngrok tunnel + Telegram webhook registration + server, all at once (stop: Ctrl+C)
```

- On the first run (no projects yet), open the local admin UI at **http://127.0.0.1:8000/** and use the **First-time setup** card to add your first project (bot token, allowed Chat IDs, target repo path). The bot goes live as soon as the project is saved.
- The project registry (`projects.json` under `REMOTE_CODER_HOME`, default `~/.remote-coder`) is the source of truth; the admin UI writes to it. A global `REMOTE_CODER_HOME/.env` is optional and used only to seed the first project.
- Prerequisites: `ngrok` (after installing, run `ngrok config add-authtoken <token>`) and at least one AI CLI (`claude`/`codex`/`gemini`). Check them with `remote-coder doctor` or in the setup card.
- To run the server only, without a tunnel: `remote-coder up --no-tunnel`.

### Other installation methods

If you prefer isolated installs, use [pipx](https://pipx.pypa.io/) or [uv](https://docs.astral.sh/uv/). (Before the PyPI release, use `git+https://github.com/maroomir/remote-coder.git` instead of the package name.)

```bash
pipx install remote-coder
uv tool install remote-coder
```

There is also an install script that handles prerequisite checks in one go (isolated install via uv):

```bash
curl -fsSL https://raw.githubusercontent.com/maroomir/remote-coder/main/scripts/install.sh | bash
```

### Development install

Editable install from a source checkout:

```bash
python -m pip install -e ".[dev]"
remote-coder up --no-tunnel --reload
```

`remote-coder up --no-tunnel` runs the server only, without tunnel/webhook registration — equivalent to `uvicorn app.main:app`.

### Building distribution packages

```bash
python -m pip install build
python -m build
```

Outputs are `dist/remote_coder-<version>.tar.gz` and `dist/remote_coder-<version>-py3-none-any.whl`. Pushing a tag (`vX.Y.Z`) makes GitHub Actions build, publish to PyPI (Trusted Publishing), and create a GitHub Release automatically.

### Homebrew distribution

Since this is a CLI/server package, a Formula (`brew install remote-coder`) is a better fit than a macOS app-bundle Cask. A draft Formula is at [`packaging/homebrew/remote-coder.rb`](packaging/homebrew/remote-coder.rb).

After a release you still need to:

- Replace `homepage` with the actual repository URL
- Replace `url` with the `remote_coder-<version>.tar.gz` URL from PyPI or the GitHub Release
- Replace `sha256` with the value from `shasum -a 256 dist/remote_coder-<version>.tar.gz`
- Generate the Python dependency `resource` blocks with a tool like `brew pypi-poet remote-coder` and add them to the Formula

> Sections "1) – 3)" below are for **developers/contributors** who work directly from the repository or handle configuration manually instead of using `remote-coder up` plus the admin UI. Skip them if the quick start is enough.

## 1) Environment setup (Conda, for developers/contributors)

```bash
conda env create -f environment.yml
conda activate remote-coder
```

## 2) Configuration

Copy and edit this `.env` only when configuring manually instead of using the admin UI. The `.env` is optional and seeds the first project when the registry is empty. (When running globally, `REMOTE_CODER_HOME/.env` takes precedence; when developing inside the repo, the current directory's `.env` takes precedence.)

```bash
cp .env.example .env
```

Fill in the following values in `.env`:

- Optional (initial seed): `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALLOWED_CHAT_IDS`, `TELEGRAM_ALLOWED_USER_IDS`, `TELEGRAM_WEBHOOK_SECRET` — used only to create the first project when the registry is empty. Production settings prefer the **per-project** fields in the admin UI or `projects.json`.
- Optional: `GIT_REMOTE_NAME` (default `origin`) — used for push after commit and for `/rebase`, `/pr`, `/clear`
- Optional: `PROJECTS_CONFIG_PATH` — path to the registry file (JSON or `.yaml`) for multiple Git projects
- Optional: `CONVERSATION_DB_PATH` — SQLite path for per-project + per-chat conversation memory (defaults to `PROJECT_ROOT/.remote-coder/conversations.sqlite3`)
- Optional: `CONVERSATION_RECENT_LIMIT` — number of recent records appended to the runner for ambiguous follow-ups (default `10`)
- Optional: `CODEX_SANDBOX` — the Codex `codex exec --sandbox` value (`read-only`, `workspace-write`, `danger-full-access`). Default `workspace-write` (files can be edited in the Job worktree)
- Optional: to use Gemini, install the Gemini CLI with `npm install -g @google/gemini-cli` and make sure `gemini` is on your PATH
- Initial seed (one-time): `DEFAULT_PROJECT`, `PROJECT_ROOT`, `WORKTREE_BASE_DIR`

If you were using a single `.env` before → move each project's `bot_token`/allowlist into the admin UI, or clean sensitive values from `.env` after the seed is created. See the migration section in [`docs/multi-bot-setup.md`](docs/multi-bot-setup.md).

## 2.5) Local admin UI (project registration)

After starting the server, open it in a browser **on the same machine**.

- Admin hub: `http://127.0.0.1:8000/` (summary, navigation to other pages)
- Project registration: `http://127.0.0.1:8000/projects` (list, add/edit/delete, fallback defaults, **bot token / allowlist / webhook secret**, per-bot webhook path. While `remote-coder up` is running, the Telegram webhook and `/` command menu for active projects you add/edit are refreshed immediately)
- Advanced settings: `http://127.0.0.1:8000/advanced`
- Server logs: `http://127.0.0.1:8000/logs` (in-memory ring buffer for the `app` logger; auto-refresh, category and `chat_id`/`job_id` filters)
- Data browser: `http://127.0.0.1:8000/database` (browse the conversation-memory SQLite tables, export CSV)
- The natural-language task target is **bound to its bot**. The `project:` token is not supported.
- If `PROJECTS_CONFIG_PATH` is unset, the default path `PROJECT_ROOT/.remote-coder/projects.json` is used.
- If the registry file is missing, it is created automatically from the `.env` seed values (`DEFAULT_PROJECT`, `PROJECT_ROOT`, `WORKTREE_BASE_DIR`).

### Server log (event) logger naming convention

Entries shown in the admin UI `/logs` and the API `GET /api/logs` are recorded by `app` package loggers. The main logger names and their purposes:

| Logger name | Purpose |
|-------------|---------|
| `app.telegram.inbound` | Webhook receipt, empty-message skips |
| `app.telegram.outbound` | `sendMessage` success/failure, Job intake/result notifications |
| `app.telegram.command` | Slash-command handling, natural-language Job intake, state changes like `/init`/`/clear` confirmations |
| `app.security.auth` | Webhook secret mismatch, allowlist rejection |
| `app.jobs.lifecycle` | Job submission, stages (`git_worktree`/`runner`/…), success, failure |
| `app.git.service` | Git adapter: worktree creation, commit, push, cleanup, rebase integration |
| `app.ai.claude` / `app.ai.codex` / `app.ai.gemini` | Runner start/end/timeout |

Structured fields (`category`, `chat_id`, `user_id`, `project`, `job_id`) can be filtered/badged in the UI. In code, use `app.monitoring.events.EventLogger` and the `app.monitoring.log_buffer.LOG_RECORD_CONTEXT_KEYS` whitelist.

### Advanced settings (dangerous options)

On the admin UI **Advanced settings** page (`http://127.0.0.1:8000/advanced`) you can read and save the global settings file `PROJECT_ROOT/.remote-coder/advanced_settings.json`. **Interface language** (`ui_language`): the default is **English**, and it governs not only the Telegram bot's messages and button labels but the **entire admin UI** (`/`, `/projects`, `/advanced`, `/logs`, `/database`). Switching to **Korean**, saving, and refreshing renders both the admin UI and Telegram responses in Korean. (The admin UI renders English by default and overlays Korean on the client.)

Defaults differ per option (e.g. server-lifecycle Telegram notifications are on by default; `git pull` on server start is off by default); an option left off behaves as if that feature is disabled. Keys used only by older versions are ignored on load (e.g. the removed `auto_pull_on_project_switch`).

> [!WARNING]
> The "immediately apply the request result to main/master and push" option auto-merges AI changes into the integration branch. Unless this is a personal experimental repository, keep the default (off) and verify remote branch protection and your backup policy before enabling it.

- **Immediately apply the request result to main/master and push**: When a Job succeeds through commit and branch push, similar to `/rebase`, it fast-forward-merges that branch into the integration branch (`main` or `master`) and pushes to the remote. If integration fails (conflict, non-ff, etc.), the Job is recorded as failed.
- **SQLite conversation-memory size limit**: When enabled, it targets the whole `conversation_entries` table and deletes oldest rows first. At least one of **max row count** and **max DB size (bytes)** must be a positive number; if both are set, it first meets the row-count limit, then repeats delete/`VACUUM` to meet the size limit. `message_branch_links` orphan links are cleaned up.

## 3) Run it all at once

With the installed CLI, `remote-coder up` handles ngrok startup, webhook registration, and server start in one command. It passes the public ngrok URL to the server as `TELEGRAM_WEBHOOK_PUBLIC_BASE_URL`, so even without restarting the server, the Telegram webhook and `/` command menu for active projects you add/edit in the admin UI are refreshed immediately.

```bash
remote-coder up
```

For multi-bot setups, you can also pass just the public HTTPS Base URL to register the webhook and command menu for each active project with `python scripts/set_webhook.py <URL>`.

On Windows PowerShell you can use the following script:

```powershell
.\run.ps1
```

Or use the batch wrapper that auto-bypasses the PowerShell execution policy:

```bat
run.bat
```

On Windows, `ngrok.exe` must be installed and runnable from PATH before running. Verify:

```powershell
ngrok version
```

- After running the script, just message the bot on Telegram and it works.
- Press `Ctrl+C` to stop the server, which also terminates ngrok.

## 4) Supported commands (MVP)

Refreshing the Telegram registration with `remote-coder up` or `python scripts/set_webhook.py <URL>` registers the same `/` command menu on each project bot that you would configure in BotFather.

- `/start` : Inline menu hub (shortcut buttons for model, monitor, clear, admin items)
- `/help` : Command help (inline buttons for model, monitor, clear items)
- `/model` : Show the default model (select via inline buttons)
- `/model claude` : Change this chat's default model to claude
- `/model codex` : Change this chat's default model to codex
- `/model gemini` : Change this chat's default model to gemini
- `/status` : Select from the recent Job list via inline buttons
- `/status <job_id>` : Query job status
- `/init` : Reset this chat's default-model override, `/clear`, and natural-language Job confirmation-pending state (the bot-bound project is unchanged; SQLite and Git are untouched)
- `/reports` : SQL-aggregate the SQLite conversation memory for the current chat + current working project into a summary report
- `/branch` : Show the currently checked-out branch of this chat's **bound project** repository
- `/branch <name>` : `git switch` only when a local branch exists in the bound project (errors if missing; does not auto-create branches that exist only on the remote)
- `/pull` : Fetch all branch info from the remote and pull the current branch. Also attempts fast-forward updates for other local branches (including main) that are not checked out.
- `/rebase` : Select a branch that exists both locally and on the remote (excluding main/master) via inline buttons, then rebase onto `main` (or `master`) → fast-forward merge → push to remote
- `/rebase <branch>` : Rebase a directly specified branch
- `/pr` : Select a local branch via inline buttons and create a GitHub Pull Request. The PR body includes the requests and AI results exchanged while working on that branch. Requires the GitHub CLI (`gh`) (`gh auth login`).
- `/pr <branch>` : Create a PR for a directly specified branch
- `/clear branch` : Clean up `remote-*` local/remote branches and their linked worktrees, **only in this bot's bound project**
- `/clear worktrees` : Clean up the managed worktrees of **this bot's project** + prune stale entries
- `/clear memory` : Delete only the conversation memory (SQLite) of **this bot's project + the current chat**
- `/stop` : Select an in-progress Job from a list via inline buttons to cancel it
- `/stop <job_id>` : Force-stop the specified Job (only for queued/running states)
- `/fix` : Rework a previous Job's commit message (`commit`) or source (`source`). Overwrites the existing commit with `git commit --amend` and reflects it to the remote with `git push --force-with-lease`. The commit trailer `committed by remote-coder: <id>` keeps the **original Job ID**.
- `/fix commit <job_id>` : Regenerate only the AI commit message and preview it → amend on `y`/`Y` confirmation
- `/fix source <job_id>` : Take a follow-up message as fix instructions and re-run the AI in the same branch worktree → amend + push on `y`/`Y` confirmation
- Replying to a previous bot message with `fix: <instruction>` (or `수정: <instruction>`) immediately shows a source-mode fix confirmation for that Job. The target Job must be in `SUCCEEDED + branch + commit` state.
- `/monitor model` : Based on the current chat's default model, a CLI probe (Claude `claude auth status` / Codex `codex --version` / Gemini `gemini --version`) plus a usage summary observed from local CLI logs. If a Codex session log has `rate_limits`, it shows the 5-hour/weekly remaining rates and reset times; Claude/Gemini show per-model token/request details from local transcript/chat logs.
- `/monitor memory` : SQLite conversation-memory row counts, rows per role, and DB file size for this chat + current **bound project**
- `/monitor branch` : Branch summary of the bound project repository (local/remote counts and lists)
- `/monitor worktrees` : Linked worktree list, detached count, and Remote Coder managed candidate summary
- `/monitor code` : Estimated code file/line counts based on the bound project root (extension whitelist; excludes `.git`, `node_modules`, etc.)
- `/monitor project` : Summary of the project record **bound to this bot** (name, enabled state, path, default model, worktree directory)
- Natural-language message: After agent/plan/ask parsing succeeds, it shows the current project, working branch, model in use, and mode, then creates an AI task (Job) after receiving `y`/`Y` (or an inline confirmation button when advanced settings enable it). Messages starting with the `plan:`/`ask:`/`계획:`/`질문:` prefix (colon `:` or `：`) or `/plan`/`/ask` run in **read-only** plan/ask mode and do not commit/push when the Job runs.

  e.g. `plan: just outline how to refactor the login flow`, `/plan model: codex risks only`, `ask: what's the test command in this repo?`, `/ask the role of JobManager`

Notes:

- A per-chat default model overridden with `/model` is in-memory. On server restart it reverts to the project's `default_model` in the registry.
- The **bound project is always the name bound to this bot instance**. **`/branch`, `/rebase`, `/monitor memory|branch|worktrees|code|project`, etc. operate against that repository.**
- `/init` reverts the per-chat model override and confirmation-pending state (without touching the conversation-memory SQLite or Git repository).
- An AI Job creates a detached worktree from the **current `HEAD` commit** of the **project repository used for the request**, then runs. It creates a working branch and commits **only when the working tree has changes**. If there is a commit, it pushes to `GIT_REMOTE_NAME` (default `origin`). To change the repository branch, switch the local branch first with `/branch <name>`.
- Auto-generated commit messages use the following format:

  ```text
  type: title
  - contents1
  - contents2

  committed by remote-coder: job-id
  ```

  `title` summarizes the functional change in one line, and the first body item describes what the AI agent changed — not the user's raw request or a list of recently modified files. The list of changed files is shown separately in the Job result notification.

- In natural-language messages you can also use the tokens `model: codex`, `model: gemini`, `branch: my-branch`, `no commit`. (The `branch:` value is validated by the same rules as `/branch`. In `plan:`/`ask:` mode, `branch:` and `no commit` are ignored.)
- Natural-language requests do not run immediately after parsing. The confirmation message shows the current project, working branch, and model/mode, and a Job is created only after `y` or `Y` (or an inline confirmation). If a new parseable natural-language message arrives while a confirmation is pending, it silently replaces the previous pending one; if an unparseable input arrives, the pending one is canceled.
- **Conversation memory (SQLite)**: User messages and Job intake/result summaries accumulate in SQLite per the same Telegram chat + same working project. It persists across server restarts. If you previously sent a specific instruction and then send a short follow-up like "start the task", "go ahead", "do that", or "begin", it merges recent records into an AI instruction. If there is no context, the bot sends a guidance message.
- **Reply chains**: For each natural-language request sent as a reply to a previous message, the bodies of ancestor messages remaining in SQLite and the Job-result summaries linked to each message are prepended to the Codex/Claude instruction as a `[Reply chain context]` block. (Restored only if the bot received and stored those messages.)
- You can pass a recent display count, like `/reports 7`. The allowed range is `1–10`, default `5`.
- Writability is checked right after worktree creation. If the AI output contains expressions like read-only/cannot-modify and there is no Git change, it is treated as a **failure**, not a success.
- Task completion/failure messages include a summary of the AI execution result (`stdout`/`stderr`).
- The full raw output is available in the worktree log file (`WORKTREE_BASE_DIR/_logs/<job_id>.log`).

## 5) Per-model usage guides

- Multi-bot / webhook / migration: [`docs/multi-bot-setup.md`](docs/multi-bot-setup.md)
- Claude users: [`docs/claude-guide.md`](docs/claude-guide.md)
- Codex users: [`docs/codex-guide.md`](docs/codex-guide.md)
- Gemini users: [`docs/gemini-guide.md`](docs/gemini-guide.md)
- When a worktree fails as read-only: [`docs/read-only-workspace.md`](docs/read-only-workspace.md)

**Runner operation notes:** The Gemini CLI is wired primarily for non-interactive execution, so expectations may differ from an interactive TUI. The Codex CLI may restrict network or some tool calls depending on its sandbox/approval policy.

## 6) Tests

Multi-bot routing, notification isolation, and project-scoped state are covered by `tests/test_webhook_multibot.py`, `tests/test_bot_instance_manager.py`, `tests/test_project_scoped_state.py`, etc.

```bash
conda activate remote-coder
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest -q -p pytest_asyncio.plugin -p respx.fixtures
```

## 7) Public repository management

- License: [Apache License 2.0](LICENSE)
- How to contribute: [CONTRIBUTING.md](CONTRIBUTING.md)
- Security policy: [SECURITY.md](SECURITY.md)
- Before opening a Pull Request, run `PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 conda run -n remote-coder pytest -q -p pytest_asyncio.plugin -p respx.fixtures`.
