From 6cd433f7fe38be8fd9777a9878832400796c1572 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 07:57:20 +0000 Subject: [PATCH 1/7] docs: add v2 roadmap Plan the 2.0 release in docs/ROADMAP-v2.md: SDK 0.2 migration, interactive question/plan/permission UX over inline keyboards, file-checkpoint undo, per-conversation concurrency, a session browser, token-based usage reporting, classic-mode removal, and container/PyPI distribution. Work already covered by open pull requests is listed as out of scope so contributors do not duplicate it. Link it from the docs index and changelog. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- CHANGELOG.md | 3 + docs/README.md | 1 + docs/ROADMAP-v2.md | 376 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+) create mode 100644 docs/ROADMAP-v2.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 642d83537..8e9aa4b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Documentation +- **v2 roadmap**: `docs/ROADMAP-v2.md` plans the 2.0 release (SDK 0.2, interactive permission and question UX, per-conversation concurrency, session browser, classic-mode removal, container distribution), scoped to work not already covered by open pull requests + ## [1.6.1] - 2026-09-11 ### Security diff --git a/docs/README.md b/docs/README.md index 2c473f890..8a5551c10 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ Quick links to every doc in this project. Start with the [README](../README.md) |----------|-------------| | [Project Overview](project-overview.md) | Architecture, core objectives, request flow diagrams, and design decisions | | [SDK Duplication Review](SDK_DUPLICATION_REVIEW.md) | Audit of `src/claude/` against the Claude Agent SDK — what to simplify or remove | +| [v2 Roadmap](ROADMAP-v2.md) | Plan for the 2.0 release: milestones, sizing, and what is deliberately left to open PRs | ## Repository-Level diff --git a/docs/ROADMAP-v2.md b/docs/ROADMAP-v2.md new file mode 100644 index 000000000..cdb5f2d3d --- /dev/null +++ b/docs/ROADMAP-v2.md @@ -0,0 +1,376 @@ +# v2 Roadmap + +**Status:** proposal, September 2026 +**Baseline:** v1.6.1, `claude-agent-sdk ^0.1.39`, 539 tests, 56% coverage + +This document plans the 2.0 release. It deliberately excludes work that already +exists as an open pull request (see [Out of scope](#out-of-scope-covered-by-open-prs)); +those are handled by the PR backlog triage, not by this roadmap. + +## What v2 is + +v1 proved the idea: Claude Code, reachable from a phone. v2 makes it the +*right* way to run Claude Code from a phone: + +1. **Interactive, not fire-and-forget.** Claude can ask questions, propose a + plan, and request permission, and the user can answer from Telegram. + Today `AskUserQuestion` is allowed but unanswerable, and there is no plan + or undo flow. +2. **Many conversations at once.** One global lock currently serialises every + Claude request across every user and every project topic. v2 runs one + request per conversation, with a global concurrency cap. +3. **Sessions you can see.** List, switch, fork, and pick up sessions that + scheduled jobs or webhooks started. +4. **Current SDK.** Move from 0.1.x to 0.2.x and use the SDK for permissions, + hooks, checkpointing and session management instead of home-grown code. +5. **One mode.** Agentic mode becomes the only mode. Classic mode's useful + pieces (file handling, voice, git helpers) already live in shared modules. +6. **Installable in one command.** Container image and a published package. + +Everything else (multi-backend support, more chat platforms) is out of scope +for 2.0. The bet is to be the best Claude-specific bridge, leaning on what +only the Agent SDK provides. + +## Out of scope (covered by open PRs) + +Do not duplicate these in v2 work. Merge, revise, or close them during triage. + +| Area | PR(s) | Related issue | +|------|-------|---------------| +| Per-tool Allow/Deny approval prompt | #217 | #216 | +| Polling stops after pool timeout | #214 | #213 | +| Startup hang on `ConfigurationError` | #212 | #211 | +| Alternative model providers (MiniMax, base URL) | #210, #143 | #171, #208 | +| Voice replies (TTS) | #167 | | +| `None` allowed/disallowed tools crash | #206 | | +| Sending agent-mentioned images / arbitrary files | #204, #191 | | +| Document, image and PDF uploads | #199, #193 | | +| Daily session reset by timezone | #198 | | +| Skill discovery and command-name normalisation | #197 | | +| `/model` command | #160 | #138 | +| Webhook mode startup fix | #196 | | +| Reply/quote context in prompt | #194 | | +| Token auth end-to-end | #190 | #58 | +| Photo album buffering, chunked paste concat | #188, #187 | #186 | +| Custom commands in the bot menu | #179 | #173, #176 | +| Scheduler misfire grace and background jobs | #178, #177 | #175, #174 | +| Per-chat routing, group trigger prefix, history buffer | #165 | | +| `/schedule` command | #151 | #150 | +| Streaming drafts, rich HTML, follow-up interrupts | #152 | #126 | + +Two of these interact with v2 work and should be merged first so v2 builds on +them rather than around them: #217 (approval prompt, extended in M1) and +#165 (moves session state from `user_data` to `chat_data`, extended in M2). + +## Milestones + +Sizes: **S** under a day, **M** two to four days, **L** a week or more. +Each item lists the files most likely to change so work can be split. + +### M0. Foundations (ship as 1.7, non-breaking) + +Preparation that every later milestone depends on. Nothing here changes +behaviour for a default install. + +| # | Item | Size | +|---|------|------| +| 0.1 | Bump `claude-agent-sdk` to `^0.2` | M | +| 0.2 | Parse `ResultMessage.subtype` | S | +| 0.3 | Raise `CLAUDE_MAX_TURNS` default | S | +| 0.4 | Container image and package publishing | M | +| 0.5 | Repository hygiene | S | +| 0.6 | CI matrix and coverage gate | S | + +**0.1 SDK bump.** The 0.1 to 0.2 jump has three known breaking changes and +the bot is already clear of two: the options class is `ClaudeAgentOptions`, +and the bot passes its own `system_prompt` so the removed default prompt does +not matter. The third is strict skill-name validation (0.2.129): names with +spaces, commas or wildcards raise at connect time. Audit +`CLAUDE_ALLOWED_TOOLS` parsing to strip whitespace (`.env.example` currently +contains `Skill,AskUserQuestion,EnterPlanMode, ExitPlanMode` with a stray +space). Files: `pyproject.toml`, `src/config/settings.py`, +`src/claude/sdk_integration.py`. Done when the suite passes on 0.2.x and a +live smoke test resumes an existing session. + +**0.2 Result subtype.** `execute_command` reads `total_cost_usd` from the +`ResultMessage` but ignores `subtype`. A run that ends with +`error_max_turns` or `error_max_budget_usd` is reported as a normal +completion, which is the likely cause of the false "Task completed" reports +in #172. Surface the subtype in `ClaudeResponse` and render a distinct +footer ("Stopped: turn limit reached. Send a message to continue."). Files: +`src/claude/sdk_integration.py`, `src/bot/orchestrator.py`. + +**0.3 Turn limit.** `DEFAULT_CLAUDE_MAX_TURNS = 10` is a chat-era default. +Agentic tasks routinely need 30 to 50 tool round-trips. Raise the default to +50 and rely on `max_budget_usd` as the real safety cap. File: +`src/utils/constants.py`, docs. + +**0.4 Distribution.** Add a `Dockerfile` (python:3.12-slim, Node for the +Claude Code CLI, non-root user, `data/` volume) and a `docker-compose.yml` +with the three required variables. Publish to GHCR from `release.yml` on +tag. Publish the wheel to PyPI from the same workflow so +`pipx install claude-code-telegram` works. Fix the project URLs in +`pyproject.toml`, which point at `richardatkinson/...` instead of +`RichardAtCT/...`. Done when a fresh machine goes from zero to a responding +bot with `docker compose up` and a three-line `.env`. + +**0.5 Hygiene.** Add `.github/ISSUE_TEMPLATE/` (bug, feature, question), +`CODEOWNERS`, and a label set (`bug`, `enhancement`, `sdk`, `security`, +`good first issue`, `needs-triage`). Restore an automated first-pass review +workflow on pull requests. Update `docs/tools.md` (still describes the +removed `ToolMonitor`) and `CONTRIBUTING.md` (still lists "TODO-7, Next" as +project status). + +**0.6 CI.** Test on 3.11, 3.12 and 3.13. Add mypy to the lint job (the +Makefile runs it, CI does not). Fail the test job below the current coverage +so it can only go up. + +### M1. Interactive agent UX + +The core of v2. Every item here turns something Claude Code does in the +terminal into something the user can do from a Telegram keyboard. + +| # | Item | Size | +|---|------|------| +| 1.1 | Answer `AskUserQuestion` from Telegram | M | +| 1.2 | Plan mode with plan approval | M | +| 1.3 | Permission modes per conversation | M | +| 1.4 | "Allow for this session" on approval prompts | S | +| 1.5 | Undo last change via file checkpointing | M | +| 1.6 | Surface tool failures and subagent activity | S | +| 1.7 | `/effort` command | S | + +**1.1 AskUserQuestion.** The tool is in the default allowlist, so Claude +asks questions that nobody can answer; the run stalls or Claude guesses. The +SDK routes the call through `can_use_tool` with the questions in the tool +input, but only for tools that are *not* pre-approved in `allowed_tools` +(#217 established this against a live bot). So first strip +`AskUserQuestion` from the allowlist handed to the SDK, exactly as #217 does +for its gated tools, then intercept it in the callback: render each question as an inline keyboard (one +row per option, plus "Other…" which switches the conversation to +free-text reply mode), await the answer with a timeout, and return +`PermissionResultAllow(updated_input=...)` where the input is the original +`questions` list plus an `answers` map of question text to the chosen label +(or list of labels for multi-select). This is the documented SDK mechanism +for `AskUserQuestion`. Reuse the +pending-prompt registry and priority callback prefix that #217 introduces for +Allow/Deny so button presses bypass the sequential lock. Multi-select +questions toggle checkmarks and finish with a "Done" button. Files: +`src/claude/sdk_integration.py` (callback plumbing), +`src/bot/orchestrator.py` (keyboard rendering, callback handler), +`src/bot/update_processor.py` (priority prefix). Done when Claude can ask a +two-question clarification and receive both answers without the user typing. + +**1.2 Plan mode.** `/plan ` runs with `permission_mode="plan"`. Claude +explores read-only and calls `ExitPlanMode` with the plan text. Intercept +that in `can_use_tool` (strip `ExitPlanMode` from the SDK allowlist as in +1.1 so the callback fires; a `PreToolUse` hook matcher is the fallback if it +does not), post the plan as a message with "Approve", +"Revise" and "Cancel" buttons. Approve returns allow and switches the live +client to `acceptEdits` for the remainder of the run via the client's +permission-mode setter (documented for the TypeScript client as +`setPermissionMode`; confirm the Python name during 0.1). Revise prompts for +a text reply that is sent as the next turn while staying in plan mode; +Cancel interrupts. Files as 1.1 plus `src/claude/facade.py` to carry the +mode. +Done when a user can review and approve a multi-file change before any file +is written. + +**1.3 Permission modes.** `/mode` shows and sets the conversation's mode: +`default` (approval prompt from #217 for gated tools), `plan`, +`acceptEdits` (edits auto-approved, Bash still prompts), `auto` (the SDK's +classifier decides; requires the CLI version the SDK bundles), and +`bypass` (only if `DISABLE_TOOL_VALIDATION=true`). Store per chat and +thread. Show the current mode in the "Working…" progress message so the +user always knows what will and will not prompt. Files: +`src/bot/orchestrator.py`, `src/config/settings.py` (default mode), +`src/storage/` (persist per conversation). + +**1.4 Allow for session.** Extend the #217 keyboard with "Allow for this +session". Implement by returning `PermissionResultAllow` with a +`PermissionUpdate` that adds an allow rule for the tool (and for Bash, the +command prefix) scoped to the session. Persist the choice alongside the +session record so it survives a bot restart. Depends on #217 merging. + +**1.5 Undo.** Enable `enable_file_checkpointing` together with +`extra_args={"replay-user-messages": None}` so the stream carries +`UserMessage.uuid` for each turn; store that UUID in the messages table. Add +an "Undo" button to the final response; pressing it calls +`client.rewind_files(uuid)` and replies with the list of files restored. +Offer a confirmation step when the rewind spans more than one turn. +Limitation to state in the UI: checkpoints cover `Write`, `Edit` and +`NotebookEdit` only. Changes made through `Bash` (or by subagents) are not +tracked, so the button should say what it will and will not revert. Files: +`src/claude/sdk_integration.py`, `src/storage/repositories.py` (new +column), `src/bot/orchestrator.py`. Done when a bad edit can be reverted +from the phone without touching git. + +**1.6 Failures and subagents.** Register `PostToolUseFailure`, +`SubagentStart` and `SubagentStop` hooks. Failures render as a single line in +the progress message ("Bash failed: exit 1") instead of the current generic +"(No content to display)" fallback. Subagent lines show the agent name and +elapsed time at verbose level 1 and above. Files: +`src/claude/sdk_integration.py`, `src/bot/orchestrator.py`. + +**1.7 Effort.** `/effort low|medium|high|xhigh|max` sets the SDK `effort` +option per conversation, shown in `/status`. Pairs with `/model` from #160. + +### M2. Sessions + +Addresses #130, #149 and #209. Depends on #165 (state keyed by chat, not +user) or an equivalent change. + +| # | Item | Size | +|---|------|------| +| 2.1 | Key session state by conversation | M | +| 2.2 | `/sessions` list, switch, fork, rename | M | +| 2.3 | Continue a scheduled or webhook run | M | +| 2.4 | Token-based usage reporting | S | + +**2.1 Conversation key.** Today the active session lives in +`context.user_data["claude_session_id"]`, so one user has one live session +regardless of chat or topic. Introduce a `ConversationKey(chat_id, +thread_id)` and a `conversations` table (key, working directory, active +session ID, permission mode, effort, verbose level). Project-threads mode +already derives a `chat:thread` state key; generalise it. Files: +`src/bot/orchestrator.py`, `src/storage/database.py` (migration), +`src/storage/repositories.py`, `src/projects/thread_manager.py`. + +**2.2 Session browser.** `/sessions` lists the last ten sessions for the +current working directory from the local sessions table joined with the +SDK's `list_sessions` and `get_session_info` helpers (importable from +`claude_agent_sdk`; they read the CLI's local session store, so they also +see sessions started from a terminal on the same machine, which is the +cross-device half of #130). Show title, last used, turns and cost. Each row is an inline +button; pressing it sets the conversation's active session. Long-press +alternatives via a second row: "Fork" (resume with `fork_session=True` so +the original is untouched), "Rename", "Delete". `/new` gains an optional +title. Files: `src/bot/orchestrator.py`, `src/storage/repositories.py`, +`src/claude/session.py`. Done when a user can return to yesterday's +conversation in two taps. + +**2.3 Continue automated runs.** Scheduled jobs and webhook handlers +already call `run_command` and get a session ID back, then drop it. Store +it on the notification and add a "Continue in chat" button to the delivered +message. Pressing it binds the current conversation to that session and +working directory. Files: `src/events/handlers.py`, +`src/notifications/`, `src/bot/orchestrator.py`. Closes #149. + +**2.4 Usage.** Read `ResultMessage.model_usage` and store input, output and +cache tokens per model alongside cost. `/status` shows tokens always and +dollars only when an API key is configured, since the SDK's cost figure is +meaningless under subscription auth (#209). Files: +`src/claude/sdk_integration.py`, `src/storage/repositories.py` +(`cost_tracking` gains token columns), `src/bot/orchestrator.py`. + +### M3. Concurrency + +| # | Item | Size | +|---|------|------| +| 3.1 | Per-conversation locking with a global cap | M | +| 3.2 | Steer a running request without killing it | M | +| 3.3 | Automated runs share the cap | S | + +**3.1 Locking.** `StopAwareUpdateProcessor` holds one `asyncio.Lock` for +every non-priority update, so two users, or two topics of one user, cannot +run Claude at the same time. Replace it with a lock per `ConversationKey` +and an `asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)` (default 3) around the +Claude call itself. Messages for a busy conversation queue behind its lock +as they do now; messages for an idle conversation run immediately. +`ClaudeSDKManager` holds no per-request state, so concurrent +`execute_command` calls are safe. Key `_active_requests` by conversation +rather than user so Stop buttons target the right run. Files: +`src/bot/update_processor.py`, `src/bot/orchestrator.py`, +`src/config/settings.py`. Done when two topics each show a live progress +message at once. Closes #102. + +**3.2 Steering.** #152 interrupts the running request when a follow-up +arrives. The SDK does not accept a new user message while a turn is still +streaming, so true mid-step steering is not available; what is available is +keeping the `ClaudeSDKClient` open after the turn and sending the next +`query()` on it immediately, with no session reload. v2 offers the choice: a +message during a run gets two buttons, "Queue" (hold it and send it as the +next turn the moment the current one finishes, keeping all work) and "Stop +and replace" (interrupt now, the #152 behaviour). Default to "Queue" after +a short timeout. Requires keeping the client object reachable from +`ActiveRequest` and holding it open for the queued turn. Files: +`src/claude/sdk_integration.py`, `src/bot/orchestrator.py`. + +**3.3 Automated runs.** Scheduler and webhook handlers bypass PTB and +therefore the lock today; under 3.1 they take the same semaphore so a burst +of webhooks cannot starve chat. + +### M4. 2.0 cleanup (breaking) + +| # | Item | Size | +|---|------|------| +| 4.1 | Remove classic mode | L | +| 4.2 | Remove dead code and stale settings | S | +| 4.3 | Test coverage to 75% | L | +| 4.4 | Upgrade guide | S | + +**4.1 Classic mode.** `src/bot/handlers/` and the classic-only parts of +`src/bot/features/` are roughly 6,500 lines that duplicate agentic mode +with a different UI. Deprecate in 1.7 (log a warning when +`AGENTIC_MODE=false`), remove in 2.0. Keep the three classic commands that +have no agentic equivalent as agentic commands: `/cd` (alias of `/repo`), +`/export` (session export already lives in `src/bot/features/`), and +`/git` (read-only status and log). Drop `/actions`, `/ls`, `/pwd`, +`/projects`, `/continue`, `/end`; Claude does all of these better from a +plain message. + +**4.2 Dead code.** `src/claude/monitor.py` survives only for two helper +functions; move them into `src/security/validators.py`. Remove `USE_SDK` +(the CLI backend was deleted in 1.4). Remove `ENABLE_QUICK_ACTIONS`, +`QUICK_ACTIONS_TIMEOUT` and `ENABLE_CONVERSATION_MODE` with classic mode. +Fold `docs/SDK_DUPLICATION_REVIEW.md` findings that are complete into the +changelog and delete the document. + +**4.3 Tests.** Current gaps: `src/main.py` 3%, `src/storage/session_storage.py` +6%, `src/scheduler/scheduler.py` 7%, `src/events/middleware.py` 7%, +`src/mcp/telegram_server.py` 47%. Add a fake SDK transport fixture that +yields scripted `AssistantMessage`, `ToolUseBlock` and `ResultMessage` +sequences so orchestrator flows (approval, question, plan, undo, steer) are +tested end to end without a CLI. Raise the coverage gate from 0.6 as each +milestone lands. + +**4.4 Upgrade guide.** `docs/upgrading-to-v2.md`: removed settings and +their replacements, the `conversations` migration, what happens to existing +`user_data` session state (migrated to the user's private chat on first +message), and the new permission-mode default. + +### Stretch (after 2.0) + +- **Structured webhook output.** Use `output_format` with a JSON schema for + GitHub PR events so the webhook handler can post a review back through + `gh` instead of only summarising to Telegram. +- **Per-topic subagents.** Define `agents` per project in + `config/projects.yaml` (a reviewer, a test-runner) and expose them as + `/agent ` in that project's topic. +- **Telegram Mini App** for the session browser and diff viewer, if inline + keyboards prove too cramped for 2.2. + +## Release mechanics + +1. **1.7.0**: M0 complete. Deprecation warning for classic mode. Announce + the v2 plan in the release notes with a link to this document. +2. **2.0.0-beta.1**: M1 and M2 complete on `main`, published as a + pre-release tag and a `beta` Docker tag. Two to three weeks of feedback. +3. **2.0.0-beta.2**: M3 complete. Concurrency defaults tuned from beta + feedback. +4. **2.0.0**: M4 complete. Upgrade guide published. Classic mode removed. + +Each milestone is a GitHub milestone with one issue per numbered item so +contributors can claim work. Items marked S are candidates for +`good first issue`. + +## Suggested order + +M0 first, in full, because the SDK bump changes the surface every later item +builds on. Then M1.1 and M1.2 (the two features users notice most), then +M3.1 (the fix that makes project-threads mode viable), then the rest of M1 +and M2 in parallel, then M3.2, then M4. + +Rough total: M0 about two weeks, M1 three, M2 two, M3 two, M4 three. Around +twelve weeks of focused work, less with two or three regular contributors +taking S and M items. From 992ae6d6a7a4efdb373dd149680cfe88fb133090 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 08:16:41 +0000 Subject: [PATCH 2/7] chore: add community health files and first-pass review automation Add the files a contributor-driven project needs that were missing: issue forms (bug, feature, question) that ask for version, mode and logs up front; a pull request template with a hand-testing section; CODEOWNERS; MAINTAINERS.md with roles, a one-week response promise, the label set, and the path to maintainership; and a Contributor Covenant 2.1 code of conduct. Rewrite CONTRIBUTING.md, which still described the project as mid-way through "TODO-7", to cover PR scope rules, the AI-assisted contribution policy, the current source layout, and the review flow. Add Dependabot (weekly for the Claude SDK and Telegram library, monthly grouped otherwise) and restore a Claude Code Review workflow as a read-only first-pass reviewer that also covers fork PRs. Link the new files from the README, docs index and changelog. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- .github/CODEOWNERS | 12 + .github/ISSUE_TEMPLATE/bug_report.yml | 75 +++ .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 51 ++ .github/ISSUE_TEMPLATE/question.yml | 24 + .github/dependabot.yml | 41 ++ .github/pull_request_template.md | 30 ++ .github/workflows/claude-code-review.yml | 83 ++++ CHANGELOG.md | 5 + CODE_OF_CONDUCT.md | 133 +++++ CONTRIBUTING.md | 535 ++++++--------------- MAINTAINERS.md | 88 ++++ README.md | 6 +- docs/README.md | 2 + 14 files changed, 697 insertions(+), 396 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/question.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/claude-code-review.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 MAINTAINERS.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..90f3a3e79 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# Reviewers automatically requested for pull requests. +# Add co-maintainers per area as they join; see MAINTAINERS.md. + +* @RichardAtCT + +# Security-sensitive paths: always include the maintainer even when +# an area owner is added later. +/src/security/ @RichardAtCT +/src/claude/ @RichardAtCT +/src/api/ @RichardAtCT +/.github/workflows/ @RichardAtCT +/SECURITY.md @RichardAtCT diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..726288106 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,75 @@ +name: Bug report +description: Something is broken or behaves unexpectedly +labels: ["bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for the report. The fields below are what a maintainer needs to reproduce the problem without a round trip. Please fill in as many as you can. + - type: textarea + id: what + attributes: + label: What happened + description: What you did, what you expected, and what happened instead. + placeholder: Sent a photo with a caption; the bot replied "Working..." and never finished. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: | + 1. Start the bot with AGENTIC_MODE=true + 2. Send ... + 3. Observe ... + validations: + required: true + - type: input + id: version + attributes: + label: Bot version + description: Output of `/status`, or the tag you installed. Say `main` and the commit if you run from source. + placeholder: v1.6.1 + validations: + required: true + - type: input + id: sdk + attributes: + label: claude-agent-sdk and Claude Code CLI versions + description: "`poetry run pip show claude-agent-sdk | grep Version` and `claude --version`" + placeholder: 0.1.39 / 2.1.x + - type: dropdown + id: mode + attributes: + label: Mode + options: + - Agentic (default) + - Classic (AGENTIC_MODE=false) + - Project threads (ENABLE_PROJECT_THREADS=true) + validations: + required: true + - type: dropdown + id: auth + attributes: + label: Claude authentication + options: + - Claude CLI login (subscription) + - ANTHROPIC_API_KEY + - Other provider or custom base URL + - type: input + id: platform + attributes: + label: OS and Python version + placeholder: Ubuntu 24.04, Python 3.12.3 + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Run with `make run-debug` and paste the lines around the failure. Remove tokens, chat IDs and file paths you do not want public. + render: shell + - type: textarea + id: config + attributes: + label: Non-default settings + description: Any `.env` values you changed from `.env.example`, with secrets removed. + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..2a3b82500 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/RichardAtCT/claude-code-telegram/security/advisories/new + about: Report a vulnerability privately. Please do not open a public issue for security problems. + - name: v2 roadmap + url: https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/ROADMAP-v2.md + about: Check whether your idea is already planned before opening a feature request. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..fa00adce1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,51 @@ +name: Feature request +description: Propose a change or an addition +labels: ["enhancement", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Before filing, check the [v2 roadmap](https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/ROADMAP-v2.md) and the open pull requests. If it is already planned, comment on that item instead so effort is not duplicated. + - type: textarea + id: problem + attributes: + label: Problem + description: What you are trying to do from Telegram and what gets in the way today. + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed behaviour + description: What the bot should do. If it involves a command or button, sketch the interaction. + placeholder: | + You: /sessions + Bot: [Yesterday: fix auth bug] [Mon: add tests] ... + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Workarounds you tried, or other ways this could be solved. + - type: dropdown + id: scope + attributes: + label: Which part of the bot does this touch? + multiple: true + options: + - Agentic chat (orchestrator) + - Claude SDK integration + - Security / permissions + - Sessions and storage + - Webhooks, scheduler, notifications + - Project threads + - Voice, images, files + - Deployment and configuration + - Documentation + - type: checkboxes + id: help + attributes: + label: Contribution + options: + - label: I am willing to open a pull request for this diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 000000000..c5176a46b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,24 @@ +name: Question or support +description: Setup help, configuration questions, "is this possible?" +labels: ["question", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Check [docs/setup.md](https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/setup.md) and [docs/configuration.md](https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/configuration.md) first. If GitHub Discussions is enabled for this repository, questions are better asked there so other users can answer too. + - type: textarea + id: question + attributes: + label: Question + validations: + required: true + - type: textarea + id: tried + attributes: + label: What you have tried + description: Commands run, settings changed, docs read. + - type: input + id: version + attributes: + label: Bot version + placeholder: v1.6.1 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..6f009a24f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,41 @@ +version: 2 +updates: + # The Claude Agent SDK moves fast (0.1.39 -> 0.2.152 in six months). + # Surface it on its own so the bump is never buried in a grouped PR. + - package-ecosystem: pip + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: ["dependencies", "sdk"] + allow: + - dependency-name: claude-agent-sdk + - dependency-name: python-telegram-bot + - dependency-name: anthropic + commit-message: + prefix: "deps" + + - package-ecosystem: pip + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 5 + labels: ["dependencies"] + ignore: + - dependency-name: claude-agent-sdk + - dependency-name: python-telegram-bot + - dependency-name: anthropic + groups: + python-minor-and-patch: + update-types: ["minor", "patch"] + commit-message: + prefix: "deps" + + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + labels: ["dependencies", "ci"] + commit-message: + prefix: "ci" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..73da9ae54 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,30 @@ +## Description + + + +## Related issue + +Closes # + +## Type of change + +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change (documented in CHANGELOG under "Changed" or "Removed") +- [ ] Documentation or tooling only + +## How it was tested + + + +- [ ] Tests added or updated +- [ ] `make test` and `make lint` pass locally +- [ ] Tested by hand against a running bot: + +## Checklist + +- [ ] One concern per PR; unrelated changes are split out +- [ ] `CHANGELOG.md` has an entry under `[Unreleased]` +- [ ] Documentation updated (`README.md`, `docs/`, `.env.example`, `CLAUDE.md`) where settings or commands changed +- [ ] New settings default to current behaviour +- [ ] If AI tools helped write this change, I reviewed every line and the hand-testing above is mine diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 000000000..1f3818cb1 --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,83 @@ +name: Claude Code Review + +# First-pass review on every pull request. It comments; it never approves, +# merges, or pushes. A human maintainer still reviews and merges. +# +# Requires one repository secret: CLAUDE_CODE_OAUTH_TOKEN (from +# `claude setup-token`) or ANTHROPIC_API_KEY. Swap the `with:` key below +# to match. See https://github.com/anthropics/claude-code-action +# +# `pull_request_target` is used so PRs from forks are reviewed (plain +# `pull_request` has no access to secrets on fork PRs). Because the +# workflow then runs with base-repo secrets against untrusted code, the +# tool allowlist is read-only plus `gh pr comment`; no arbitrary Bash. + +on: + pull_request_target: + types: [opened, synchronize, ready_for_review, reopened] + paths-ignore: + - "**.md" + - "docs/**" + +concurrency: + group: claude-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: read + id-token: write + steps: + - name: Checkout PR merge ref + uses: actions/checkout@v6 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + fetch-depth: 0 + + - name: Review + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + # anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + use_sticky_comment: true + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + + You are the first-pass reviewer for a Telegram bot that gives + remote access to Claude Code. Read CLAUDE.md for the + architecture and the five-layer security model, then review + the diff of this pull request. + + Report only findings you are confident about, most severe + first. Focus on: + 1. Security regressions: anything that widens what a Telegram + user can make Claude do on the host. Path checks against + APPROVED_DIRECTORY, the can_use_tool callback, the + SecurityValidator patterns, webhook signature checks, + secrets in logs, new settings that relax defaults. + 2. Correctness: async misuse (blocking calls, missing awaits, + shared state across concurrent updates), datetime handling + (must be timezone-aware UTC), SQLite migrations, Telegram + API limits (message length, rate limits, HTML escaping). + 3. Scope: does the PR do one thing? Does CHANGELOG.md have an + entry? Are new settings documented in .env.example and + docs/configuration.md and defaulted to current behaviour? + 4. Tests: are behaviour changes covered? Do tests assert the + new behaviour rather than mock it away? + + Post one review comment using `gh pr comment` with a short + summary, then the findings as a list with file:line + references. If there is nothing worth raising, say so in one + line. Do not approve, request changes, merge, or push. + claude_args: >- + --allowed-tools "Read,Grep,Glob,Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*),Bash(git diff:*),Bash(git log:*)" + --disallowed-tools "Write,Edit,MultiEdit,NotebookEdit,WebFetch,WebSearch" + --max-turns 40 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e9aa4b6e..d2867c252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Documentation - **v2 roadmap**: `docs/ROADMAP-v2.md` plans the 2.0 release (SDK 0.2, interactive permission and question UX, per-conversation concurrency, session browser, classic-mode removal, container distribution), scoped to work not already covered by open pull requests +- **Community files**: issue forms (bug, feature, question), a pull request template with a hand-testing section, `CODEOWNERS`, `MAINTAINERS.md` (roles, one-week response promise, label set, path to maintainership), a Contributor Covenant 2.1 `CODE_OF_CONDUCT.md`, and a rewritten `CONTRIBUTING.md` (PR scope rules, AI-assisted contribution policy, current project layout) + +### Added +- **Dependabot**: weekly PRs for `claude-agent-sdk`, `python-telegram-bot` and `anthropic`; monthly grouped PRs for other Python dependencies and GitHub Actions +- **Claude Code Review workflow**: read-only first-pass review comment on every non-draft pull request, including fork PRs. Requires the `CLAUDE_CODE_OAUTH_TOKEN` (or `ANTHROPIC_API_KEY`) repository secret ## [1.6.1] - 2026-09-11 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..189704159 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the maintainers listed in [MAINTAINERS.md](MAINTAINERS.md), by +email to richardatk01@gmail.com, or through a private message to a maintainer +on GitHub. All complaints will be reviewed and investigated promptly and +fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ca866c76..3d189471e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,426 +1,173 @@ # Contributing to Claude Code Telegram Bot -Thank you for your interest in contributing! This document provides guidelines for contributing to the project. - -## Development Status - -This project is currently under active development with the following status: - -- ✅ **Project Structure & Configuration** (Complete) -- ✅ **Authentication & Security** (Complete) -- ✅ **Bot Core & Integration** (TODO-4, TODO-5, Complete) -- ✅ **Storage Layer** (TODO-6, Complete) -- 🚧 **Advanced Features** (TODO-7, Next) - -## Getting Started - -### Prerequisites - -- Python 3.11 or higher -- Poetry for dependency management -- Git for version control - -### Setting Up Development Environment - -1. **Fork and clone the repository**: - ```bash - git clone https://github.com/your-username/claude-code-telegram.git - cd claude-code-telegram - ``` - -2. **Install dependencies**: - ```bash - make dev - ``` - This also installs **pre-commit hooks** that automatically format your code (black, isort) on each commit, so you never need to worry about formatting manually. - - > **Linux users**: If `make dev` shows a `DBusErrorResponse` / `ItemNotFoundException` - > error for `aiolimiter`, this is a known Poetry keyring issue on Linux. To prevent - > it, disable the keyring backend before running `make dev`: - > ```bash - > PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring make dev - > ``` - > Or configure Poetry once to disable keyring globally: - > ```bash - > poetry config keyring.enabled false - > make dev - > ``` - > If you already ran `make dev` and see 2 test failures, install `aiolimiter` - > manually and re-run tests: - > ```bash - > poetry run pip install aiolimiter - > make test - > ``` - -3. **Set up configuration**: - ```bash - cp .env.example .env - # Edit .env with your development settings - ``` - -4. **Verify setup**: - ```bash - make test - make lint - ``` - -## Development Workflow - -### Before Starting Work - -1. **Check existing issues** for similar work -2. **Create an issue** if none exists -3. **Comment on the issue** to indicate you're working on it -4. **Create a feature branch** from main - -### Making Changes - -1. **Follow the project structure**: - ``` - src/ - ├── config/ # Configuration (✅ Complete) - ├── security/ # Authentication & Security (✅ Complete) - ├── bot/ # Telegram bot (✅ Complete - TODO-4) - ├── claude/ # Claude integration (✅ Complete - TODO-5) - └── storage/ # Database (✅ Complete - TODO-6) - ``` - -2. **Write tests** for new functionality: - ```bash - # Add tests in tests/unit/ or tests/integration/ - make test - ``` - -3. **Follow code standards**: - ```bash - make format # Auto-format code - make lint # Check code quality - ``` - -4. **Update documentation** as needed - -### Code Standards - -#### Type Hints - -All code must include comprehensive type hints: - -```python -from typing import Optional, List, Dict, Any -from pathlib import Path - -async def process_data( - items: List[Dict[str, Any]], - config: Optional[Path] = None -) -> bool: - """Process data with optional config.""" - # Implementation - return True +Thanks for helping. This guide covers how to get set up, what a good pull +request looks like here, and what to expect from maintainers. For who the +maintainers are and how to become one, see [MAINTAINERS.md](MAINTAINERS.md). +For what is planned, see [docs/ROADMAP-v2.md](docs/ROADMAP-v2.md). + +## What to expect from us + +- A reply to every issue and pull request within **seven days**. It may be + "not now" or "please add X", but it will not be silence. +- New issues and PRs are labelled `needs-triage` until a maintainer has + looked at them; that label is cleared weekly. +- A first-pass review comment from the Claude Code Review workflow on every + non-draft PR, followed by a human review before merge. + +## Before you start + +1. **Check the roadmap and open PRs.** A lot of common requests already have + a pull request waiting for review. Comment on that PR or roadmap item + rather than opening a second one. +2. **Open an issue for anything non-trivial** and say you are working on it. + For small fixes (typos, a one-function bug) go straight to a PR. +3. **Discuss larger changes first.** Anything that touches the security + model, adds a setting, changes the database schema, or exceeds about 400 + lines of diff should be agreed in an issue before you write it. This is + what keeps big PRs from stalling. + +## Setting up + +Requirements: Python 3.11 or newer, [Poetry](https://python-poetry.org/), Git. + +```bash +git clone https://github.com//claude-code-telegram.git +cd claude-code-telegram +make dev # installs all deps and pre-commit hooks (black, isort on commit) +cp .env.example .env +make test +make lint ``` -#### Error Handling +> **Linux users**: if `make dev` fails with a `DBusErrorResponse` or +> `ItemNotFoundException` for `aiolimiter`, it is a Poetry keyring issue. +> Run `poetry config keyring.enabled false` once, then `make dev` again. -Use the custom exception hierarchy: +Useful targets: -```python -from src.exceptions import ConfigurationError, SecurityError - -try: - # Some operation - pass -except ValueError as e: - raise ConfigurationError(f"Invalid configuration: {e}") from e +```bash +make run-debug # run the bot with debug logging +make run-watch # auto-restart on file changes +make format # black + isort +poetry run pytest tests/unit/test_config.py -k test_name -v # one test +poetry run mypy src # types only ``` -#### Logging - -Use structured logging: - -```python -import structlog - -logger = structlog.get_logger() +## Project layout -def some_function(): - logger.info("Operation started", operation="example", user_id=123) - # Implementation ``` - -#### Testing - -Write comprehensive tests: - -```python -import pytest -from src.config import create_test_config - -@pytest.mark.asyncio -async def test_feature(): - """Test feature functionality.""" - config = create_test_config(debug=True) - # Test implementation - assert config.debug is True +src/ +├── bot/ Telegram layer: orchestrator (agentic mode), classic handlers, +│ middleware (auth, rate limit, security), shared features +├── claude/ Claude Agent SDK integration, facade, session tracking +├── security/ Auth providers, input validators, rate limiter, audit log +├── storage/ SQLite via aiosqlite, repositories, models, migrations +├── config/ Pydantic settings, feature flags, YAML project loader +├── projects/ Multi-project registry and Telegram topic routing +├── events/ Async event bus and agent handler (webhooks, scheduler) +├── api/ FastAPI webhook server +├── scheduler/ APScheduler jobs persisted in SQLite +├── notifications/ Rate-limited Telegram delivery +└── mcp/ The bot's own MCP server (send image/file to user) +tests/unit/ pytest, asyncio_mode=auto +docs/ Setup, configuration, tools, development, roadmap ``` -## Contribution Types - -### High Priority (Current TODOs) - -#### TODO-7: Advanced Features (Next Priority) -- File upload handling with security validation -- Git integration for repository operations -- Quick actions system for common workflows -- Session export features (Markdown, JSON, HTML) -- Image/screenshot support and processing - -**Files to create/modify**: -- `src/bot/handlers/file.py` -- `src/git/integration.py` -- `src/features/quick_actions.py` -- `src/features/export.py` -- `tests/unit/test_features.py` - -### Recently Completed ✅ - -#### TODO-4: Telegram Bot Core -- ✅ Bot connection and handler registration -- ✅ Command routing system -- ✅ Message parsing and formatting -- ✅ Inline keyboard support -- ✅ Error handling middleware - -#### TODO-5: Claude Code Integration -- ✅ Subprocess management for Claude CLI -- ✅ Response streaming and parsing -- ✅ Session state persistence -- ✅ Timeout handling -- ✅ Tool usage monitoring - -#### TODO-6: Storage Layer -- ✅ SQLite database schema -- ✅ Repository pattern implementation -- ✅ Migration system -- ✅ Analytics and reporting - -### Documentation Improvements +[CLAUDE.md](CLAUDE.md) has the architecture summary, request flow, and the +five-layer security model. Read it before touching `src/security/` or +`src/claude/`. -- API documentation -- User guides -- Deployment guides -- Architecture documentation +## Pull requests -### Testing Improvements +### Scope -- Integration tests -- End-to-end tests -- Performance tests -- Security tests +- **One concern per PR.** A feature, a fix, or a refactor. Not all three. + Bundled PRs are the ones that sit unreviewed for months. +- **Keep it under about 400 lines of diff** unless agreed in an issue first. + Split larger work into a sequence of PRs that each leave `main` working. +- **New settings default to today's behaviour.** Nothing changes for an + existing install until the operator opts in. +- **Never relax the security model silently.** Anything that widens what a + Telegram user can make Claude do on the host needs the `security` label + and lead-maintainer review. -## Submitting Changes +### Requirements -### Pull Request Process +- Tests for behaviour changes. `make test` and `make lint` pass. CI runs + black, isort, flake8 and the test suite on every PR. +- A line under `[Unreleased]` in `CHANGELOG.md`, in the Keep a Changelog + style already used there. +- Docs updated where a setting or command changed: `README.md`, + `.env.example`, `docs/configuration.md`, and `CLAUDE.md` if it affects how + Claude Code itself should work in this repo. +- The pull request template filled in, including what you tested by hand. + The suite cannot drive Telegram or the SDK end to end, so a real bot run + is part of the evidence for anything in the message path. -1. **Ensure tests pass**: - ```bash - make test - make lint - ``` +### AI-assisted contributions -2. **Update documentation** if needed +Using Claude Code (or any assistant) to write contributions is welcome; it +is what this project is for. Two rules: -3. **Create pull request** with: - - Clear title and description - - Reference to related issue - - List of changes made - - Screenshots if UI-related +1. You have read and understood every line you are submitting, and you + answer review comments yourself. +2. The hand-testing section of the PR template describes what *you* ran + against a real bot. An assistant's claim that it tested something does + not count. -4. **Respond to review feedback** promptly +PRs that fail either rule will be closed with a pointer here. -### Commit Message Format +### Commit messages -Use conventional commits: +Conventional-commit prefixes, imperative mood, and the issue number in the +body or PR: ``` -feat: add rate limiting functionality -fix: resolve configuration validation issue -docs: update development guide -test: add tests for authentication system -refactor: reorganize bot handlers -``` - -### Pull Request Template - -```markdown -## Description -Brief description of changes made. - -## Related Issue -Fixes #123 - -## Type of Change -- [ ] Bug fix -- [ ] New feature -- [ ] Breaking change -- [ ] Documentation update - -## Testing -- [ ] Tests added/updated -- [ ] All tests pass -- [ ] Manual testing completed - -## Checklist -- [ ] `make format` has been run (or pre-commit hooks are installed via `make dev`) -- [ ] Code follows project style guidelines -- [ ] Self-review completed -- [ ] Documentation updated -- [ ] No breaking changes (or clearly documented) -``` - -## Code Review Guidelines - -### For Contributors - -- **Self-review** your code before submitting -- **Write clear commit messages** and PR descriptions -- **Respond promptly** to review feedback -- **Keep PRs focused** on a single change -- **Add tests** for new functionality - -### For Reviewers - -- **Be constructive** and helpful in feedback -- **Test functionality** when possible -- **Check for security implications** -- **Verify documentation updates** -- **Ensure tests are comprehensive** - -## Issue Guidelines - -### Bug Reports - -```markdown -**Describe the bug** -A clear description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior. - -**Expected behavior** -What you expected to happen. - -**Environment** -- OS: [e.g. macOS, Linux] -- Python version: [e.g. 3.9] -- Poetry version: [e.g. 1.7.1] - -**Additional context** -Any other context about the problem. +feat: add /sessions command for switching between sessions +fix: parse ResultMessage.subtype so turn-limit stops are reported +docs: correct tools reference after ToolMonitor removal +test: cover scheduler job persistence +refactor: key active requests by conversation ``` -### Feature Requests - -```markdown -**Is your feature request related to a problem?** -A clear description of what the problem is. - -**Describe the solution you'd like** -A clear description of what you want to happen. - -**Describe alternatives you've considered** -Alternative solutions or features you've considered. - -**Additional context** -Any other context about the feature request. -``` +### Review and merge + +1. The review workflow comments first; address anything real it finds. +2. A maintainer reviews. Expect questions about tests and security before + style. Style is handled by the hooks. +3. Merge needs green CI and one approving review; security-sensitive paths + also need the lead maintainer. See [MAINTAINERS.md](MAINTAINERS.md). +4. Squash-merging is preferred so `main` stays close to one commit per PR; + a merge commit is fine for a PR whose history is worth keeping. + +## Code standards + +- Black (88 columns), isort (black profile), flake8, mypy strict. Type hints + on every function, including tests. +- `structlog` for logging, with key=value context rather than f-strings: + `logger.info("Session resumed", user_id=user_id, session_id=session_id)`. +- Timezone-aware UTC everywhere: `datetime.now(UTC)`, never + `datetime.utcnow()`. Model `from_row()` methods guard `fromisoformat()` + with `isinstance(val, str)` because SQLite returns `datetime` for + declared TIMESTAMP columns. +- Raise from the project exception hierarchy (`src/exceptions.py`) and chain + the original: `raise ConfigurationError(...) from e`. +- Tests use `create_test_config()` from `src.config` and mock the Telegram + and SDK boundaries, not the code under test. + +## Issues + +Use the issue forms; they ask for the version, mode, and log lines a +maintainer needs. Questions are welcome as issues too, or in GitHub +Discussions where enabled. ## Security -### Reporting Security Issues - -**Do not** create public issues for security vulnerabilities. - -Instead: -1. Email security concerns to [maintainer email] -2. Include detailed description of the vulnerability -3. Wait for acknowledgment before public disclosure - -### Security Guidelines - -- **Never commit secrets** or credentials -- **Validate all inputs** thoroughly -- **Use parameterized queries** for database operations -- **Follow principle of least privilege** -- **Log security-relevant events** - -## Development Environment - -### Required Tools - -- **Poetry**: Dependency management -- **Black**: Code formatting -- **isort**: Import sorting -- **flake8**: Linting -- **mypy**: Type checking -- **pytest**: Testing - -### Recommended IDE Setup - -#### VS Code -```json -{ - "python.defaultInterpreterPath": ".venv/bin/python", - "python.formatting.provider": "black", - "python.linting.enabled": true, - "python.linting.flake8Enabled": true, - "python.linting.mypyEnabled": true -} -``` - -#### PyCharm -- Configure Poetry interpreter -- Enable Black formatting -- Enable flake8 and mypy inspections - -## Community Guidelines - -### Code of Conduct - -- **Be respectful** and inclusive -- **Welcome newcomers** and help them get started -- **Give constructive feedback** -- **Focus on the code**, not the person -- **Assume good intentions** - -### Communication - -- **Use clear, concise language** -- **Provide context** in issues and PRs -- **Ask questions** when unsure -- **Share knowledge** and help others - -## Getting Help - -### Documentation -- Check `docs/` directory for guides -- Review existing code for patterns -- Read the configuration guide - -### Asking Questions -- Search existing issues first -- Provide context and examples -- Include relevant environment details -- Be specific about what you've tried - -### Debugging -- Use `make run-debug` for detailed logging -- Check test output with `make test` -- Run type checking with `poetry run mypy src` - -## Recognition +Do not open a public issue for a vulnerability. Use +[GitHub Security Advisories](https://github.com/RichardAtCT/claude-code-telegram/security/advisories/new) +as described in [SECURITY.md](SECURITY.md). -Contributors will be recognized in: -- `CHANGELOG.md` for their contributions -- Project documentation -- Release notes +## Community -Thank you for contributing to Claude Code Telegram Bot! 🚀 +This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). Be kind, +assume good intent, and focus feedback on the change rather than the person. diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 000000000..9a7d068c8 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,88 @@ +# Maintainers + +This file says who maintains the project, what that means, and how to join. +It is deliberately short. The project is small enough that a page of shared +expectations does more good than a governance document. + +## Current maintainers + +| Name | GitHub | Role | Areas | +|------|--------|------|-------| +| Richard Atkinson | [@RichardAtCT](https://github.com/RichardAtCT) | Lead maintainer, releases | Everything; final say on security-sensitive changes | + +Co-maintainers are listed here as they join. `CODEOWNERS` mirrors this table +so reviewers are requested automatically. + +## What maintainers do + +- **Respond within a week.** Every new issue and pull request gets a reply + within seven days, even if the reply is "not now" or "needs more + information". Silence is the one thing this project promises not to do. +- **Triage.** Apply labels, close duplicates, ask for missing details, and + point contributors at the roadmap item or open PR that already covers + their idea. +- **Review and merge.** Any maintainer may merge a pull request that has + green CI, a first-pass bot review with no unresolved findings, and one + approving human review. Changes under `src/security/`, `src/claude/`, + `src/api/` and `.github/workflows/` need the lead maintainer's approval + as well. +- **Release.** The lead maintainer cuts releases with `make bump-*`. Any + maintainer may prepare the changelog and propose a release. + +## Becoming a maintainer + +The path is: contributor, then triager, then maintainer. + +1. **Contributor.** Anyone with a merged pull request. +2. **Triager.** A contributor with three or more merged PRs, or sustained + helpful activity on issues, can be given the GitHub *Triage* role on + request or by invitation. Triagers label, close, and respond to issues + and PRs but do not merge. +3. **Maintainer.** A triager who has been active for about three months and + whose reviews the existing maintainers trust is invited to take *Write* + access and is added to the table above and to `CODEOWNERS` for the areas + they know. + +Maintainers who have been inactive for six months are moved to an +"emeritus" line below, with thanks, and their access is reduced. They can +return at any time by asking. + +## Decisions + +Most decisions happen in the pull request or issue where they arise. When +maintainers disagree, the person who will do the work proposes, the others +comment within a week, and the lead maintainer decides if there is still no +agreement. Decisions that change the project's direction (a new major +version, dropping a feature area, adding a platform) are written down in an +issue labelled `decision` before work starts, so the reasoning survives. + +## Labels + +Maintainers keep this set current in the repository settings. Issue +templates apply the first two automatically. + +| Label | Meaning | +|-------|---------| +| `needs-triage` | New, not yet looked at by a maintainer. Cleared weekly. | +| `bug`, `enhancement`, `question` | Type, set by the issue template. | +| `security` | Touches the security model. Lead maintainer review required. | +| `sdk` | Concerns the Claude Agent SDK integration or a version bump. | +| `good first issue` | Small, self-contained, and described well enough to start without asking. | +| `help wanted` | A maintainer wants this but will not get to it soon. | +| `blocked` | Waiting on an upstream change or another PR. Say which in a comment. | +| `decision` | A direction-setting discussion, see above. | +| `dependencies`, `ci` | Set by Dependabot. | + +## Releases and support + +- Minor releases roughly monthly while there is merged work to ship; patch + releases whenever a fix warrants one. +- Semantic versioning. Behaviour is removed only in a major version, and + only after a deprecation warning has shipped in at least one minor + release before it. +- The previous major receives security fixes for six months after the next + major ships. + +## Emeritus + +None yet. diff --git a/README.md b/README.md index e30bb05be..fbb090d7f 100644 --- a/README.md +++ b/README.md @@ -357,9 +357,11 @@ Each command commits, tags, and pushes automatically, triggering CI tests and a 1. Fork the repository 2. Create a feature branch: `git checkout -b feature/amazing-feature` 3. Make changes with tests: `make test && make lint` -4. Submit a Pull Request +4. Submit a Pull Request (one feature or fix per PR, with tests) -**Code standards:** Python 3.11+, Black formatting (88 chars), type hints required, pytest with >85% coverage. +**Code standards:** Python 3.11+, Black formatting (88 chars), type hints required, pytest. + +See [CONTRIBUTING.md](CONTRIBUTING.md) for the full process, [MAINTAINERS.md](MAINTAINERS.md) for who reviews and how to become a maintainer, [docs/ROADMAP-v2.md](docs/ROADMAP-v2.md) for what is planned, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community expectations. ## License diff --git a/docs/README.md b/docs/README.md index 8a5551c10..6385e5f81 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,5 +25,7 @@ Quick links to every doc in this project. Start with the [README](../README.md) | Document | Description | |----------|-------------| | [Contributing](../CONTRIBUTING.md) | How to contribute: branching, code standards, PR process | +| [Maintainers](../MAINTAINERS.md) | Who maintains the project, response-time promise, labels, how to become a maintainer | +| [Code of Conduct](../CODE_OF_CONDUCT.md) | Community expectations and how to report a problem | | [Security Policy](../SECURITY.md) | Supported versions, vulnerability reporting, and security architecture | | [Changelog](../CHANGELOG.md) | Release history following Keep a Changelog format | From 230c8329f12cd4de0842e43abf5c6753c924e724 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 09:01:06 +0000 Subject: [PATCH 3/7] fix: point project URLs at the RichardAtCT repository The Homepage, Repository and Documentation entries in pyproject.toml referenced github.com/richardatkinson/claude-code-telegram, a handle that is not the project's. These URLs surface in `pip show` and would become the links on a PyPI listing. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- CHANGELOG.md | 3 +++ pyproject.toml | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2867c252..f6793cbbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **v2 roadmap**: `docs/ROADMAP-v2.md` plans the 2.0 release (SDK 0.2, interactive permission and question UX, per-conversation concurrency, session browser, classic-mode removal, container distribution), scoped to work not already covered by open pull requests - **Community files**: issue forms (bug, feature, question), a pull request template with a hand-testing section, `CODEOWNERS`, `MAINTAINERS.md` (roles, one-week response promise, label set, path to maintainership), a Contributor Covenant 2.1 `CODE_OF_CONDUCT.md`, and a rewritten `CONTRIBUTING.md` (PR scope rules, AI-assisted contribution policy, current project layout) +### Fixed +- **Project URLs**: the Homepage, Repository and Documentation links in `pyproject.toml` pointed at `github.com/richardatkinson/...`; they now point at `RichardAtCT`, so `pip show` and any future PyPI listing link to this repository + ### Added - **Dependabot**: weekly PRs for `claude-agent-sdk`, `python-telegram-bot` and `anthropic`; monthly grouped PRs for other Python dependencies and GitHub Actions - **Claude Code Review workflow**: read-only first-pass review comment on every non-draft pull request, including fork PRs. Requires the `CLAUDE_CODE_OAUTH_TOKEN` (or `ANTHROPIC_API_KEY`) repository secret diff --git a/pyproject.toml b/pyproject.toml index c57add8e8..2f377e657 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,9 +23,9 @@ requires-python = ">=3.11" dynamic = ["dependencies"] [project.urls] -Homepage = "https://github.com/richardatkinson/claude-code-telegram" -Repository = "https://github.com/richardatkinson/claude-code-telegram" -Documentation = "https://github.com/richardatkinson/claude-code-telegram/blob/main/docs/" +Homepage = "https://github.com/RichardAtCT/claude-code-telegram" +Repository = "https://github.com/RichardAtCT/claude-code-telegram" +Documentation = "https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/" [project.scripts] claude-telegram-bot = "src.main:run" From d1e20f2f5247885649d6d499833d88330c24d734 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 09:04:06 +0000 Subject: [PATCH 4/7] docs: rebase v2 roadmap on the 1.6.2 baseline Drop the out-of-scope rows for PRs merged in 1.6.2 (#214, #212, #196, #177, #178, #206), note #220, point the AskUserQuestion and ExitPlanMode items at the GUARDED_TOOLS set that 1.6.2 introduced, and remove the docs/tools.md item that 1.6.2 already fixed. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- docs/ROADMAP-v2.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/ROADMAP-v2.md b/docs/ROADMAP-v2.md index cdb5f2d3d..9e4389740 100644 --- a/docs/ROADMAP-v2.md +++ b/docs/ROADMAP-v2.md @@ -1,7 +1,7 @@ # v2 Roadmap **Status:** proposal, September 2026 -**Baseline:** v1.6.1, `claude-agent-sdk ^0.1.39`, 539 tests, 56% coverage +**Baseline:** v1.6.2, `claude-agent-sdk ^0.1.39`, 559 tests, 56% coverage This document plans the 2.0 release. It deliberately excludes work that already exists as an open pull request (see [Out of scope](#out-of-scope-covered-by-open-prs)); @@ -38,29 +38,28 @@ Do not duplicate these in v2 work. Merge, revise, or close them during triage. | Area | PR(s) | Related issue | |------|-------|---------------| | Per-tool Allow/Deny approval prompt | #217 | #216 | -| Polling stops after pool timeout | #214 | #213 | -| Startup hang on `ConfigurationError` | #212 | #211 | | Alternative model providers (MiniMax, base URL) | #210, #143 | #171, #208 | | Voice replies (TTS) | #167 | | -| `None` allowed/disallowed tools crash | #206 | | | Sending agent-mentioned images / arbitrary files | #204, #191 | | | Document, image and PDF uploads | #199, #193 | | | Daily session reset by timezone | #198 | | | Skill discovery and command-name normalisation | #197 | | | `/model` command | #160 | #138 | -| Webhook mode startup fix | #196 | | | Reply/quote context in prompt | #194 | | | Token auth end-to-end | #190 | #58 | | Photo album buffering, chunked paste concat | #188, #187 | #186 | | Custom commands in the bot menu | #179 | #173, #176 | -| Scheduler misfire grace and background jobs | #178, #177 | #175, #174 | | Per-chat routing, group trigger prefix, history buffer | #165 | | | `/schedule` command | #151 | #150 | | Streaming drafts, rich HTML, follow-up interrupts | #152 | #126 | -Two of these interact with v2 work and should be merged first so v2 builds on -them rather than around them: #217 (approval prompt, extended in M1) and -#165 (moves session state from `user_data` to `chat_data`, extended in M2). +Merged in 1.6.2 and therefore no longer listed: #214, #212, #196, #177, #178, +#206, and #220 (guarded tools now routed through `can_use_tool`). + +Two of the remaining PRs interact with v2 work and should be merged first so +v2 builds on them rather than around them: #217 (approval prompt, extended in +M1) and #165 (moves session state from `user_data` to `chat_data`, extended +in M2). ## Milestones @@ -117,9 +116,8 @@ bot with `docker compose up` and a three-line `.env`. **0.5 Hygiene.** Add `.github/ISSUE_TEMPLATE/` (bug, feature, question), `CODEOWNERS`, and a label set (`bug`, `enhancement`, `sdk`, `security`, `good first issue`, `needs-triage`). Restore an automated first-pass review -workflow on pull requests. Update `docs/tools.md` (still describes the -removed `ToolMonitor`) and `CONTRIBUTING.md` (still lists "TODO-7, Next" as -project status). +workflow on pull requests. (`docs/tools.md` was corrected in 1.6.2 and +`CONTRIBUTING.md` is rewritten alongside this roadmap.) **0.6 CI.** Test on 3.11, 3.12 and 3.13. Add mypy to the lint job (the Makefile runs it, CI does not). Fail the test job below the current coverage @@ -144,9 +142,10 @@ terminal into something the user can do from a Telegram keyboard. asks questions that nobody can answer; the run stalls or Claude guesses. The SDK routes the call through `can_use_tool` with the questions in the tool input, but only for tools that are *not* pre-approved in `allowed_tools` -(#217 established this against a live bot). So first strip -`AskUserQuestion` from the allowlist handed to the SDK, exactly as #217 does -for its gated tools, then intercept it in the callback: render each question as an inline keyboard (one +(#219/#220 established this against a live bot; 1.6.2 strips the +`GUARDED_TOOLS` set from the allowlist handed to the SDK for exactly this +reason). So first add `AskUserQuestion` to that set, then intercept it in the +callback: render each question as an inline keyboard (one row per option, plus "Other…" which switches the conversation to free-text reply mode), await the answer with a timeout, and return `PermissionResultAllow(updated_input=...)` where the input is the original @@ -163,9 +162,9 @@ two-question clarification and receive both answers without the user typing. **1.2 Plan mode.** `/plan ` runs with `permission_mode="plan"`. Claude explores read-only and calls `ExitPlanMode` with the plan text. Intercept -that in `can_use_tool` (strip `ExitPlanMode` from the SDK allowlist as in -1.1 so the callback fires; a `PreToolUse` hook matcher is the fallback if it -does not), post the plan as a message with "Approve", +that in `can_use_tool` (add `ExitPlanMode` to `GUARDED_TOOLS` as in 1.1 so +the callback fires; a `PreToolUse` hook matcher is the fallback if it does +not), post the plan as a message with "Approve", "Revise" and "Cancel" buttons. Approve returns allow and switches the live client to `acceptEdits` for the remainder of the run via the client's permission-mode setter (documented for the TypeScript client as From 44e13c1f501adfb5ba1fc4559c1b081d3fa5d99e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:22:38 +0000 Subject: [PATCH 5/7] chore: repoint URLs at the overwirehq organisation The repository moved from RichardAtCT to the overwirehq organisation. Update every github.com//claude-code-telegram URL across the README, docs, issue templates, SECURITY.md, packaging metadata and the star-history badge. GitHub redirects the old URLs, so this is about having one canonical location rather than about anything breaking. References to @RichardAtCT as a user are left alone: the CODEOWNERS entries and the maintainers table point at a personal account that has not moved. Name the container image path in roadmap item 0.4 now that the organisation exists, so the first published image lands at ghcr.io/overwirehq/claude-code-telegram with no legacy path to keep. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- .github/ISSUE_TEMPLATE/config.yml | 4 ++-- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- .github/ISSUE_TEMPLATE/question.yml | 2 +- CHANGELOG.md | 16 +++++++++++++++- CONTRIBUTING.md | 2 +- README.md | 12 ++++++------ SECURITY.md | 6 +++--- docs/ROADMAP-v2.md | 12 ++++++------ docs/SDK_DUPLICATION_REVIEW.md | 4 ++-- docs/development.md | 2 +- docs/setup.md | 12 ++++++------ pyproject.toml | 6 +++--- tests/unit/test_bot/test_middleware.py | 2 +- 13 files changed, 48 insertions(+), 34 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2a3b82500..7d924a890 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - name: Security vulnerability - url: https://github.com/RichardAtCT/claude-code-telegram/security/advisories/new + url: https://github.com/overwirehq/claude-code-telegram/security/advisories/new about: Report a vulnerability privately. Please do not open a public issue for security problems. - name: v2 roadmap - url: https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/ROADMAP-v2.md + url: https://github.com/overwirehq/claude-code-telegram/blob/main/docs/ROADMAP-v2.md about: Check whether your idea is already planned before opening a feature request. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index fa00adce1..e97a6d64e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - Before filing, check the [v2 roadmap](https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/ROADMAP-v2.md) and the open pull requests. If it is already planned, comment on that item instead so effort is not duplicated. + Before filing, check the [v2 roadmap](https://github.com/overwirehq/claude-code-telegram/blob/main/docs/ROADMAP-v2.md) and the open pull requests. If it is already planned, comment on that item instead so effort is not duplicated. - type: textarea id: problem attributes: diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml index c5176a46b..9f6b0d1b1 100644 --- a/.github/ISSUE_TEMPLATE/question.yml +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - Check [docs/setup.md](https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/setup.md) and [docs/configuration.md](https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/configuration.md) first. If GitHub Discussions is enabled for this repository, questions are better asked there so other users can answer too. + Check [docs/setup.md](https://github.com/overwirehq/claude-code-telegram/blob/main/docs/setup.md) and [docs/configuration.md](https://github.com/overwirehq/claude-code-telegram/blob/main/docs/configuration.md) first. If GitHub Discussions is enabled for this repository, questions are better asked there so other users can answer too. - type: textarea id: question attributes: diff --git a/CHANGELOG.md b/CHANGELOG.md index 75a7bdf6e..04ba2ac36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Documentation +- **v2 roadmap**: `docs/ROADMAP-v2.md` plans the 2.0 release (SDK 0.2, interactive permission and question UX, per-conversation concurrency, session browser, classic-mode removal, container distribution), scoped to work not already covered by open pull requests +- **Community files**: issue forms (bug, feature, question), a pull request template with a hand-testing section, `CODEOWNERS`, `MAINTAINERS.md` (roles, one-week response promise, label set, path to maintainership), a Contributor Covenant 2.1 `CODE_OF_CONDUCT.md`, and a rewritten `CONTRIBUTING.md` (PR scope rules, AI-assisted contribution policy, current project layout) + +### Changed +- **Repository moved to the `overwirehq` organisation**: the canonical location is now `github.com/overwirehq/claude-code-telegram`. GitHub redirects the old URLs, but every link in the README, docs, issue templates and packaging metadata has been updated. Existing clones keep working; `git remote set-url origin https://github.com/overwirehq/claude-code-telegram.git` points one at the new location directly + +### Fixed +- **Project URLs**: the Homepage, Repository and Documentation links in `pyproject.toml` pointed at `github.com/richardatkinson/...`, an owner unrelated to this project, so `pip show` and any future PyPI listing linked to the wrong place + +### Added +- **Dependabot**: weekly PRs for `claude-agent-sdk`, `python-telegram-bot` and `anthropic`; monthly grouped PRs for other Python dependencies and GitHub Actions +- **Claude Code Review workflow**: read-only first-pass review comment on every non-draft pull request, including fork PRs. Requires the `CLAUDE_CODE_OAUTH_TOKEN` (or `ANTHROPIC_API_KEY`) repository secret + ## [1.7.0] - 2026-09-11 Released as a minor rather than a patch: the security fix below changes runtime @@ -17,7 +31,7 @@ behaviour for every deployment. See the upgrade note under **Changed**. ### Changed - **Upgrade note for #220**: tool calls targeting paths outside `APPROVED_DIRECTORY` are now denied where they previously succeeded. This restores the behaviour the documentation always described, but it is a real change for any deployment that relied on the gap. Routing each guarded call through the callback also adds one control-request round trip per call. `DISABLE_TOOL_VALIDATION=true` restores the previous permissive behaviour for trusted environments. -- **Known limitation**: `CLAUDE_ALLOWED_TOOLS` does not block tools left off the list — unlisted tools reach the callback, which allows anything passing its boundary checks. `CLAUDE_DISALLOWED_TOOLS` is the only setting that denies a tool. Tracked in [#221](https://github.com/RichardAtCT/claude-code-telegram/issues/221); `SECURITY.md` and `docs/tools.md` now describe the actual behaviour. +- **Known limitation**: `CLAUDE_ALLOWED_TOOLS` does not block tools left off the list — unlisted tools reach the callback, which allows anything passing its boundary checks. `CLAUDE_DISALLOWED_TOOLS` is the only setting that denies a tool. Tracked in [#221](https://github.com/overwirehq/claude-code-telegram/issues/221); `SECURITY.md` and `docs/tools.md` now describe the actual behaviour. ### Fixed - **Polling no longer dies permanently**: a `getUpdates` request torn down mid-flight (unstable network, proxy or tunnel drop) left its connection checked out of a pool holding exactly one, so every later poll failed with "Pool timeout" and never recovered, even after the network came back (#214, closes #213) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d189471e..fb079b1ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,7 +164,7 @@ Discussions where enabled. ## Security Do not open a public issue for a vulnerability. Use -[GitHub Security Advisories](https://github.com/RichardAtCT/claude-code-telegram/security/advisories/new) +[GitHub Security Advisories](https://github.com/overwirehq/claude-code-telegram/security/advisories/new) as described in [SECURITY.md](SECURITY.md). ## Community diff --git a/README.md b/README.md index fbb090d7f..3b335a032 100644 --- a/README.md +++ b/README.md @@ -45,24 +45,24 @@ Choose your preferred method: ```bash # Using uv (recommended — installs in an isolated environment) -uv tool install git+https://github.com/RichardAtCT/claude-code-telegram@v1.3.0 +uv tool install git+https://github.com/overwirehq/claude-code-telegram@v1.3.0 # Or using pip -pip install git+https://github.com/RichardAtCT/claude-code-telegram@v1.3.0 +pip install git+https://github.com/overwirehq/claude-code-telegram@v1.3.0 # Track the latest stable release -pip install git+https://github.com/RichardAtCT/claude-code-telegram@latest +pip install git+https://github.com/overwirehq/claude-code-telegram@latest ``` #### Option B: From source (for development) ```bash -git clone https://github.com/RichardAtCT/claude-code-telegram.git +git clone https://github.com/overwirehq/claude-code-telegram.git cd claude-code-telegram make dev # requires Poetry ``` -> **Note:** Always install from a tagged release (not `main`) for stability. See [Releases](https://github.com/RichardAtCT/claude-code-telegram/releases) for available versions. +> **Note:** Always install from a tagged release (not `main`) for stability. See [Releases](https://github.com/overwirehq/claude-code-telegram/releases) for available versions. ### 3. Configure @@ -369,7 +369,7 @@ MIT License -- see [LICENSE](LICENSE). ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=RichardAtCT/claude-code-telegram&type=Date)](https://star-history.com/#RichardAtCT/claude-code-telegram&Date) +[![Star History Chart](https://api.star-history.com/svg?repos=overwirehq/claude-code-telegram&type=Date)](https://star-history.com/#overwirehq/claude-code-telegram&Date) ## Acknowledgments diff --git a/SECURITY.md b/SECURITY.md index 347f7c8a6..5172869b6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -79,7 +79,7 @@ the guarded tools from the `allowed_tools` it hands the SDK and disables `autoAllowBashIfSandboxed` (a second bypass, which auto-approves sandboxed Bash without a control request). Before this was fixed the checks were wired up but never consulted on a default configuration -([#219](https://github.com/RichardAtCT/claude-code-telegram/issues/219)). +([#219](https://github.com/overwirehq/claude-code-telegram/issues/219)). Routing a tool call through the callback costs one local stdio round trip to the CLI subprocess plus ~30-90 microseconds of validation -- negligible against the @@ -106,7 +106,7 @@ to the callback, which allows anything that passes the boundary checks. Use exists, but `src/main.py` still backs it with `InMemoryTokenStorage`, so issued tokens are lost on restart and there is no supported flow for issuing one. **Use `ALLOWED_USERS` as the access control for any real deployment.** Tracked in -[#58](https://github.com/RichardAtCT/claude-code-telegram/issues/58). +[#58](https://github.com/overwirehq/claude-code-telegram/issues/58). ## Security Configuration @@ -230,7 +230,7 @@ ENVIRONMENT=production # Enables strict security defaults Report it privately through GitHub Security Advisories: -**https://github.com/RichardAtCT/claude-code-telegram/security/advisories/new** +**https://github.com/overwirehq/claude-code-telegram/security/advisories/new** That form is private to you and the maintainers, supports attachments and follow-up discussion, and lets us credit you on the published advisory. You can diff --git a/docs/ROADMAP-v2.md b/docs/ROADMAP-v2.md index 9e4389740..e28b0776b 100644 --- a/docs/ROADMAP-v2.md +++ b/docs/ROADMAP-v2.md @@ -106,12 +106,12 @@ Agentic tasks routinely need 30 to 50 tool round-trips. Raise the default to **0.4 Distribution.** Add a `Dockerfile` (python:3.12-slim, Node for the Claude Code CLI, non-root user, `data/` volume) and a `docker-compose.yml` -with the three required variables. Publish to GHCR from `release.yml` on -tag. Publish the wheel to PyPI from the same workflow so -`pipx install claude-code-telegram` works. Fix the project URLs in -`pyproject.toml`, which point at `richardatkinson/...` instead of -`RichardAtCT/...`. Done when a fresh machine goes from zero to a responding -bot with `docker compose up` and a three-line `.env`. +with the three required variables. Publish to +`ghcr.io/overwirehq/claude-code-telegram` from `release.yml` on tag. Publish +the wheel to PyPI from the same workflow so +`pipx install claude-code-telegram` works. Done when a fresh machine goes +from zero to a responding bot with `docker compose up` and a three-line +`.env`. **0.5 Hygiene.** Add `.github/ISSUE_TEMPLATE/` (bug, feature, question), `CODEOWNERS`, and a label set (`bug`, `enhancement`, `sdk`, `security`, diff --git a/docs/SDK_DUPLICATION_REVIEW.md b/docs/SDK_DUPLICATION_REVIEW.md index 73811f959..58f28211b 100644 --- a/docs/SDK_DUPLICATION_REVIEW.md +++ b/docs/SDK_DUPLICATION_REVIEW.md @@ -569,8 +569,8 @@ Before any refactor: | Date | PR | Findings Addressed | Summary | |------|:---:|:---:|---------| -| 2026-02-20 | [#56](https://github.com/RichardAtCT/claude-code-telegram/pull/56) | F1 (partial), F8, F9 | Migrated `query()` → `ClaudeSDKClient`, eliminated `temp_*` IDs and session swapping, uses `ResultMessage.result`, removed dead `active_sessions` state | -| 2026-02-20 | [#59](https://github.com/RichardAtCT/claude-code-telegram/pull/59) | F3 (complete), F5 (complete) | Deleted CLI subprocess backend (`integration.py`, `parser.py`), removed `use_sdk` flag, passed `disallowed_tools` to SDK, ~1,060 lines removed | +| 2026-02-20 | [#56](https://github.com/overwirehq/claude-code-telegram/pull/56) | F1 (partial), F8, F9 | Migrated `query()` → `ClaudeSDKClient`, eliminated `temp_*` IDs and session swapping, uses `ResultMessage.result`, removed dead `active_sessions` state | +| 2026-02-20 | [#59](https://github.com/overwirehq/claude-code-telegram/pull/59) | F3 (complete), F5 (complete) | Deleted CLI subprocess backend (`integration.py`, `parser.py`), removed `use_sdk` flag, passed `disallowed_tools` to SDK, ~1,060 lines removed | | 2026-02-20 | Phase 3 branch | F2 (complete), F6 (complete) | Replaced `ToolMonitor` with SDK's `can_use_tool` callback, removed bash pattern blocklist, removed facade interception + admin message helpers, removed `ClaudeToolValidationError`, ~350 lines removed | ### Next Steps diff --git a/docs/development.md b/docs/development.md index 6260bf408..8f40ca261 100644 --- a/docs/development.md +++ b/docs/development.md @@ -17,7 +17,7 @@ This document provides detailed information for developers working on the Claude 1. **Clone the repository**: ```bash - git clone https://github.com/RichardAtCT/claude-code-telegram.git + git clone https://github.com/overwirehq/claude-code-telegram.git cd claude-code-telegram ``` diff --git a/docs/setup.md b/docs/setup.md index acb7f906d..b7433481f 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -47,13 +47,13 @@ Choose your preferred installation method: ```bash # Using uv (recommended — installs in an isolated environment) -uv tool install git+https://github.com/RichardAtCT/claude-code-telegram@v1.3.0 +uv tool install git+https://github.com/overwirehq/claude-code-telegram@v1.3.0 # Or using pip -pip install git+https://github.com/RichardAtCT/claude-code-telegram@v1.3.0 +pip install git+https://github.com/overwirehq/claude-code-telegram@v1.3.0 # Track the latest stable release -pip install git+https://github.com/RichardAtCT/claude-code-telegram@latest +pip install git+https://github.com/overwirehq/claude-code-telegram@latest ``` > **Don't have uv?** Install it with `curl -LsSf https://astral.sh/uv/install.sh | sh`. @@ -61,12 +61,12 @@ pip install git+https://github.com/RichardAtCT/claude-code-telegram@latest #### Option B: From source (for development) ```bash -git clone https://github.com/RichardAtCT/claude-code-telegram.git +git clone https://github.com/overwirehq/claude-code-telegram.git cd claude-code-telegram make dev ``` -> **Important:** Always install from a [tagged release](https://github.com/RichardAtCT/claude-code-telegram/releases), not `main`, for stability. +> **Important:** Always install from a [tagged release](https://github.com/overwirehq/claude-code-telegram/releases), not `main`, for stability. ### 4. Configure Environment @@ -362,4 +362,4 @@ ENABLE_TELEMETRY=true - **Documentation**: Check the main [README.md](../README.md) - **Configuration**: See [configuration.md](configuration.md) for all options - **Security**: See [SECURITY.md](../SECURITY.md) for security concerns -- **Issues**: [Open an issue](https://github.com/RichardAtCT/claude-code-telegram/issues) +- **Issues**: [Open an issue](https://github.com/overwirehq/claude-code-telegram/issues) diff --git a/pyproject.toml b/pyproject.toml index 9cb8b99af..231445b5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,9 +23,9 @@ requires-python = ">=3.11" dynamic = ["dependencies"] [project.urls] -Homepage = "https://github.com/RichardAtCT/claude-code-telegram" -Repository = "https://github.com/RichardAtCT/claude-code-telegram" -Documentation = "https://github.com/RichardAtCT/claude-code-telegram/blob/main/docs/" +Homepage = "https://github.com/overwirehq/claude-code-telegram" +Repository = "https://github.com/overwirehq/claude-code-telegram" +Documentation = "https://github.com/overwirehq/claude-code-telegram/blob/main/docs/" [project.scripts] claude-telegram-bot = "src.main:run" diff --git a/tests/unit/test_bot/test_middleware.py b/tests/unit/test_bot/test_middleware.py index 4ff583651..0e4fb6453 100644 --- a/tests/unit/test_bot/test_middleware.py +++ b/tests/unit/test_bot/test_middleware.py @@ -4,7 +4,7 @@ violation, rate limit exceeded), ApplicationHandlerStop is raised to prevent subsequent handler groups from processing the update. -Regression tests for: https://github.com/RichardAtCT/claude-code-telegram/issues/44 +Regression tests for: https://github.com/overwirehq/claude-code-telegram/issues/44 """ from unittest.mock import AsyncMock, MagicMock From 9d6f121700e04a00f18943d8784c9711fe2dd1f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:33:20 +0000 Subject: [PATCH 6/7] docs: rebase the v2 roadmap on the 1.7.0 release The security fix for #219 shipped as 1.7.0 rather than 1.6.2, and #217 merged into main. Move M0 from 1.7 to 1.8 so it does not claim a version that is already out, drop #217 from the out-of-scope table and from the prerequisites, and correct the 1.6.2 references left behind by the version rename. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- docs/ROADMAP-v2.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/ROADMAP-v2.md b/docs/ROADMAP-v2.md index e28b0776b..ff028ad77 100644 --- a/docs/ROADMAP-v2.md +++ b/docs/ROADMAP-v2.md @@ -1,7 +1,7 @@ # v2 Roadmap **Status:** proposal, September 2026 -**Baseline:** v1.6.2, `claude-agent-sdk ^0.1.39`, 559 tests, 56% coverage +**Baseline:** v1.7.0, `claude-agent-sdk ^0.1.39`, 559 tests, 56% coverage This document plans the 2.0 release. It deliberately excludes work that already exists as an open pull request (see [Out of scope](#out-of-scope-covered-by-open-prs)); @@ -37,7 +37,6 @@ Do not duplicate these in v2 work. Merge, revise, or close them during triage. | Area | PR(s) | Related issue | |------|-------|---------------| -| Per-tool Allow/Deny approval prompt | #217 | #216 | | Alternative model providers (MiniMax, base URL) | #210, #143 | #171, #208 | | Voice replies (TTS) | #167 | | | Sending agent-mentioned images / arbitrary files | #204, #191 | | @@ -53,20 +52,20 @@ Do not duplicate these in v2 work. Merge, revise, or close them during triage. | `/schedule` command | #151 | #150 | | Streaming drafts, rich HTML, follow-up interrupts | #152 | #126 | -Merged in 1.6.2 and therefore no longer listed: #214, #212, #196, #177, #178, -#206, and #220 (guarded tools now routed through `can_use_tool`). +Merged in 1.7.0 and therefore no longer listed: #214, #212, #196, #177, #178, +#206, #220 (guarded tools now routed through `can_use_tool`) and #217 +(per-tool Allow/Deny approval prompt, closing #216). -Two of the remaining PRs interact with v2 work and should be merged first so -v2 builds on them rather than around them: #217 (approval prompt, extended in -M1) and #165 (moves session state from `user_data` to `chat_data`, extended -in M2). +One remaining PR interacts with v2 work and should be merged first so v2 +builds on it rather than around it: #165, which moves session state from +`user_data` to `chat_data` and is extended in M2. ## Milestones Sizes: **S** under a day, **M** two to four days, **L** a week or more. Each item lists the files most likely to change so work can be split. -### M0. Foundations (ship as 1.7, non-breaking) +### M0. Foundations (ship as 1.8, non-breaking) Preparation that every later milestone depends on. Nothing here changes behaviour for a default install. @@ -116,7 +115,7 @@ from zero to a responding bot with `docker compose up` and a three-line **0.5 Hygiene.** Add `.github/ISSUE_TEMPLATE/` (bug, feature, question), `CODEOWNERS`, and a label set (`bug`, `enhancement`, `sdk`, `security`, `good first issue`, `needs-triage`). Restore an automated first-pass review -workflow on pull requests. (`docs/tools.md` was corrected in 1.6.2 and +workflow on pull requests. (`docs/tools.md` was corrected in 1.7.0 and `CONTRIBUTING.md` is rewritten alongside this roadmap.) **0.6 CI.** Test on 3.11, 3.12 and 3.13. Add mypy to the lint job (the @@ -142,7 +141,7 @@ terminal into something the user can do from a Telegram keyboard. asks questions that nobody can answer; the run stalls or Claude guesses. The SDK routes the call through `can_use_tool` with the questions in the tool input, but only for tools that are *not* pre-approved in `allowed_tools` -(#219/#220 established this against a live bot; 1.6.2 strips the +(#219/#220 established this against a live bot; 1.7.0 strips the `GUARDED_TOOLS` set from the allowlist handed to the SDK for exactly this reason). So first add `AskUserQuestion` to that set, then intercept it in the callback: render each question as an inline keyboard (one @@ -189,7 +188,8 @@ user always knows what will and will not prompt. Files: session". Implement by returning `PermissionResultAllow` with a `PermissionUpdate` that adds an allow rule for the tool (and for Bash, the command prefix) scoped to the session. Persist the choice alongside the -session record so it survives a bot restart. Depends on #217 merging. +session record so it survives a bot restart. #217 shipped the Allow/Deny +keyboard in 1.7.0; this adds the third button to it. **1.5 Undo.** Enable `enable_file_checkpointing` together with `extra_args={"replay-user-messages": None}` so the stream carries @@ -310,7 +310,7 @@ of webhooks cannot starve chat. **4.1 Classic mode.** `src/bot/handlers/` and the classic-only parts of `src/bot/features/` are roughly 6,500 lines that duplicate agentic mode -with a different UI. Deprecate in 1.7 (log a warning when +with a different UI. Deprecate in 1.8 (log a warning when `AGENTIC_MODE=false`), remove in 2.0. Keep the three classic commands that have no agentic equivalent as agentic commands: `/cd` (alias of `/repo`), `/export` (session export already lives in `src/bot/features/`), and @@ -351,7 +351,7 @@ message), and the new permission-mode default. ## Release mechanics -1. **1.7.0**: M0 complete. Deprecation warning for classic mode. Announce +1. **1.8.0**: M0 complete. Deprecation warning for classic mode. Announce the v2 plan in the release notes with a link to this document. 2. **2.0.0-beta.1**: M1 and M2 complete on `main`, published as a pre-release tag and a `beta` Docker tag. Two to three weeks of feedback. From c8d0a8fc2258816f9a851a657b57ad69ced3514c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:43:28 +0000 Subject: [PATCH 7/7] fix: correct the allowed_tools expectation left stale by #206 main is red. test_allowed_tools_none_unaffected_by_approval_filter asserts that allowed_tools is None when DISABLE_TOOL_VALIDATION is set, but 94d764d (#206) changed that value to [] so it matches the list[str] that ClaudeAgentOptions declares. #217 added the test against a base that did not yet carry #206, so neither pull request was red on its own and the failure only appeared once both were merged. The test's intent is sound: the interactive-approval filter must not disturb the tool list when validation is disabled. Only the expected value was stale, so assert [] instead. Rename the test and reword its docstring, since "none" in the name is what made the mismatch easy to miss. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019LaezV767Pguhpat9zVyEs --- CHANGELOG.md | 1 + tests/unit/test_claude/test_sdk_integration.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04ba2ac36..11f36a44a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Repository moved to the `overwirehq` organisation**: the canonical location is now `github.com/overwirehq/claude-code-telegram`. GitHub redirects the old URLs, but every link in the README, docs, issue templates and packaging metadata has been updated. Existing clones keep working; `git remote set-url origin https://github.com/overwirehq/claude-code-telegram.git` points one at the new location directly ### Fixed +- **Green test suite**: `test_allowed_tools_none_unaffected_by_approval_filter` asserted that `allowed_tools` is `None` under `DISABLE_TOOL_VALIDATION`, but #206 had already changed that value to `[]` for type correctness. #217 was written against a base without #206, so the collision only surfaced once both were on `main`, leaving the default branch red. The expectation is now `[]`, and the test is renamed to say so - **Project URLs**: the Homepage, Repository and Documentation links in `pyproject.toml` pointed at `github.com/richardatkinson/...`, an owner unrelated to this project, so `pip show` and any future PyPI listing linked to the wrong place ### Added diff --git a/tests/unit/test_claude/test_sdk_integration.py b/tests/unit/test_claude/test_sdk_integration.py index 84f6e066b..eb2b412a6 100644 --- a/tests/unit/test_claude/test_sdk_integration.py +++ b/tests/unit/test_claude/test_sdk_integration.py @@ -658,8 +658,8 @@ async def test_allowed_tools_untouched_when_interactive_approval_disabled( assert captured_options[0].allowed_tools == config.claude_allowed_tools - async def test_allowed_tools_none_unaffected_by_approval_filter(self, tmp_path): - """When allowed_tools is None (DISABLE_TOOL_VALIDATION), filtering is a no-op.""" + async def test_empty_allowed_tools_unaffected_by_approval_filter(self, tmp_path): + """When DISABLE_TOOL_VALIDATION empties allowed_tools, filtering is a no-op.""" config = Settings( telegram_bot_token="test:token", telegram_bot_username="testbot", @@ -686,7 +686,7 @@ async def test_allowed_tools_none_unaffected_by_approval_filter(self, tmp_path): working_directory=tmp_path, ) - assert captured_options[0].allowed_tools is None + assert captured_options[0].allowed_tools == [] async def test_system_prompt_set_with_working_directory( self, sdk_manager, tmp_path