Imported from SMARK2022/opencode-telegram-bot (
AGENTS.md). Install upstream withnpx skills add SMARK2022/opencode-telegram-bot. Copyright stays with the author.
AGENTS.md
Instructions for AI agents working on this project.
About the project
opencode-telegram-bot is a Telegram bot that acts as a mobile client for OpenCode. It lets a user run and monitor coding tasks on a local machine through Telegram.
Functional requirements, features, and development status are in PRODUCT.md.
Technology stack
- Language: TypeScript 5.x
- Runtime: Node.js 20+
- Package manager: npm
- Configuration: environment variables (
.env) - Logging: custom logger with levels (
debug,info,warn,error)
Core dependencies
grammy- Telegram Bot API framework (https://grammy.dev/)@grammyjs/menu- inline keyboards and menus@opencode-ai/sdk- official OpenCode Server SDKdotenv- environment variable loading
Test dependencies
- Vitest
- Mocks/stubs via
vi.mock()
Code quality
- ESLint + Prettier
- TypeScript strict mode
Architecture
Main components
- Bot Layer - grammY setup, middleware, commands, callback handlers
- OpenCode Client Layer - SDK wrapper and SSE event subscription
- State Managers - session/project/settings/question/permission/model/agent/variant/keyboard/pinned
- Summary Pipeline - event aggregation and Telegram-friendly formatting
- Process Manager - local OpenCode server process start, stop, and status
- Runtime/CLI Layer - runtime mode, config bootstrap, CLI commands
- I18n Layer - localized bot and CLI strings to multiple languages
Data flow
Telegram User
-> Telegram Bot (grammY)
-> Managers + OpenCodeClient
-> OpenCode Server
OpenCode Server
-> SSE Events
-> Event Listener
-> Summary Aggregator / Tool Managers
-> Telegram Bot
-> Telegram User
State management
- Persistent state is stored in
settings.json. - Active runtime state is kept in dedicated in-memory managers.
- Session/project/model/agent context is synchronized through OpenCode API calls.
- The app is currently single-user by design.
AI agent behavior rules
Communication
- Response language: Reply in the same language the user uses in their questions.
- Clarifications: If plan confirmation is needed, use the
questiontool. Do not make major decisions (architecture changes, mass deletion, risky changes) without explicit confirmation.
Think Before Coding
Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
Simplicity First
Minimum code that solves the problem. Nothing speculative.
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
Surgical Changes
Touch only what you must. Clean up only your own mess.
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
Goal-Driven Execution
Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
Git
- Commits: Never create commits automatically. Commit only when the user explicitly asks.
Windows / PowerShell
- Keep in mind the runtime environment is Windows.
- Avoid fragile one-liners that can break in PowerShell.
- Use absolute paths when working with file tools (
read,write,edit).
Coding rules
Language
- Code, identifiers, comments, and in-code documentation must be in English.
- User-facing Telegram messages should be localized through i18n.
Code style
- Use TypeScript strict mode.
- Use ESLint + Prettier.
- Prefer
constoverlet. - Use clear names and avoid unnecessary abbreviations.
- Keep functions small and focused.
- Prefer
async/awaitover chained.then().
Error handling
- Use
try/catcharound async operations. - Log errors with context (session ID, operation type, etc.).
- Send understandable error messages to users.
- Never expose stack traces to users.
Bot commands
The command list is centralized in src/bot/commands/definitions.ts.
const COMMAND_DEFINITIONS: BotCommandI18nDefinition[] = [
{ command: "status", descriptionKey: "cmd.description.status" },
{ command: "new", descriptionKey: "cmd.description.new" },
{ command: "abort", descriptionKey: "cmd.description.stop" },
{ command: "sessions", descriptionKey: "cmd.description.sessions" },
{ command: "projects", descriptionKey: "cmd.description.projects" },
{ command: "rename", descriptionKey: "cmd.description.rename" },
{ command: "opencode_start", descriptionKey: "cmd.description.opencode_start" },
{ command: "opencode_stop", descriptionKey: "cmd.description.opencode_stop" },
{ command: "help", descriptionKey: "cmd.description.help" },
];
Important:
- When adding a command, update
definitions.tsonly. - The same source is used for Telegram
setMyCommandsand help/docs. - Do not duplicate command lists elsewhere.
Logging
The project uses src/utils/logger.ts with level-based logging.
Log files:
- In source mode logs are stored
<project root>/logsby default. - Each source-mode bot run writes to a separate file named
bot-YYYY-MM-DD_HH-MM-SS_<pid>.log. - The
logs/directory is gitignored, so search inside it directly: usepath: "logs"withpattern: "*.log". - Installed mode writes under the installed app home
logsdirectory and uses daily files namedbot-YYYY-MM-DD.log.
Levels:
- DEBUG - detailed diagnostics (callbacks, keyboard build, SSE internals, polling flow)
- INFO - key lifecycle events (session/task start/finish, status changes)
- WARN - recoverable issues (timeouts, retries, unauthorized attempts)
- ERROR - critical failures requiring attention
Use:
import { logger } from "../utils/logger.js";
logger.debug("[Component] Detailed operation", details);
logger.info("[Component] Important event occurred");
logger.warn("[Component] Recoverable problem", error);
logger.error("[Component] Critical failure", error);
Important:
- Do not use raw
console.log/console.errordirectly in feature code; uselogger. - Put internal diagnostics under
debug. - Keep important operational events under
info. - Default level is
info.
Testing
What to test
- Unit tests for business logic, formatters, managers, runtime helpers
- Integration-style tests around OpenCode SDK interaction using mocks
- Focus on critical paths; avoid over-testing trivial code
Test structure
- Tests live in
tests/(organized by module) - Use descriptive test names
- Follow Arrange-Act-Assert
- Use
vi.mock()for external dependencies
OpenCode SDK quick reference
import { createOpencodeClient } from "@opencode-ai/sdk";
const client = createOpencodeClient({ baseUrl: "http://localhost:4096" });
await client.global.health();
await client.project.list();
await client.project.current();
await client.session.list();
await client.session.create({ body: { title: "My session" } });
await client.session.prompt({
path: { id: "session-id" },
body: { parts: [{ type: "text", text: "Implement feature X" }] },
});
await client.session.abort({ path: { id: "session-id" } });
const events = await client.event.subscribe();
for await (const event of events.stream) {
// handle SSE event
}
Full docs: https://opencode.ai/docs/sdk
Workflow
- Read PRODUCT.md to understand scope and status.
- Inspect existing code before adding or changing components.
- Align major architecture changes (including new dependencies) with the user first.
- Add or update tests for new functionality.
- After code changes, run quality checks:
npm run build,npm run lint, andnpm test. - Update checkboxes in
PRODUCT.mdwhen relevant tasks are completed. - Keep code clean, consistent, and maintainable.