Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,28 @@ Notable changes to `@leemour/tg-cli`. One section per version, newest first; ver

### What's new

- **The bundled agent instructions explain how to find agreements, prepare meetings and recommend contacts.**
Agents check archive coverage, compare group and personal chats, distinguish people with the same name,
and retain a draft when sending is refused.

- **`tg messages link` and read-only MCP `tg_messages_link` return a message permalink and locator.**
Channels and supergroups preserve thread context; private links require access and grant no
membership. Dialogs, basic groups and Saved Messages return a locator. Offline validates the
stored message without connecting; locators for another account are refused.
Singular `link` differs from conversation-graph `links`.

### Changed — may break scripts

- **`tg commands [path...] --json` can describe one command or group.** For example,
`tg commands messages search --json` includes global options and exit codes alongside that command.
Without a path the full tree is unchanged; scoped responses add `scope` and `inheritedOptions`.
Inspect different command paths in separate calls.

### Fixed

- Commands opening the local archive together wait briefly for initialization instead of failing
immediately when another process holds its write lock. Persistent locks still fail normally.

- Voice transcription downloads use the history connection and close it before local recognition,
avoiding a second connection for `messages list --transcribe`, `inbox` and `review`.

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ tg bot list --check # every bot on this computer
- **Messages and chats.** Send, edit, delete and pin, to a chat by id or by title, or to a person
as `user:<id>`; `--md`, `--html`, a file or a photo. `bot store fetch` imports older channel and supergroup messages;
`messages list` reads what this bot sent, received or imported on this computer.
History import uses a separate MTProto session; private chats and basic groups are unsupported.
- **Admins, members, buttons, the menu, webhooks.** `bot chats admins`, `bot chats members remove`,
`bot callbacks answer`, `bot commands`, `bot webhooks` — the same commands `max bot` has.
- **`bot watch`** prints what happens in the bot's chats as it arrives, and keeps it: that is the
Expand Down
8 changes: 6 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1907,12 +1907,16 @@ tg doctor report create [options]

## `tg commands`

every command, option and exit code as JSON — what an agent reads instead of --help
commands, options and exit codes as JSON — inspect one command path per call

```sh
tg commands
tg commands [path]
```

| Argument | | What it is |
|---|---|---|
| `path` | optional | one command path, for example: messages search; inspect other groups in separate calls. |

## `tg complete`

shell completion: `tg complete zsh` prints the script to source
Expand Down
5 changes: 3 additions & 2 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,14 @@ no fields, only the one button. The client's own window shows the arguments as t
| `tg_messages_photo` | `tg messages download` | a message's photo as an image to look at, up to 512 KB; anything else is refused with the `tg messages download` command that saves it |
| `tg_messages_transcribe` | `tg messages transcribe` | a voice message as text — by Telegram (Premium or the weekly trial), else by a speech model on this machine; `local: true` skips Telegram; `pending: true` means Telegram was not finished within a minute; a missing model is refused with `tg models audio download`, never downloaded |
| `tg_messages_search` | `tg messages search` | search what this machine has kept; never asks Telegram |
| `tg_messages_send` | `tg messages send`, `--reply-to`, `--topic` | send, by `messages.send`; `reply_to` answers a message; `send_id` repeats a send whose outcome was unknown; `silent`, `no_preview` and `md` as `--silent`, `--no-preview` and `--md`; `topic` picks a forum topic; `at_time` sends it later — never retried, the confirmation form shows the clock time; `file` or `photo` attaches a path from this machine, the text as the caption (`as_file` keeps a video a file), `voice` sends an Ogg Opus file as a voice message — hidden files, `~/.ssh`, tg's own folders and the message store are refused, with no way around it over MCP |
| `tg_messages_link` | `tg messages link` | a permalink where supported and an account-scoped locator; read-only; a link grants no chat membership |
| `tg_messages_send` | `tg messages send`, `--reply-to`, `--topic` | send, by `messages.send`; `reply_to` answers a message and must belong to the chosen topic; `send_id` repeats a send whose outcome was unknown in the same chat and topic; `silent`, `no_preview` and `md` as `--silent`, `--no-preview` and `--md`; `topic` picks a forum topic; `at_time` sends it later — never retried, the confirmation form shows the clock time; `file` or `photo` attaches a path from this machine, the text as the caption (`as_file` keeps a video a file), `voice` sends an Ogg Opus file as a voice message — hidden files, `~/.ssh`, tg's own folders and the message store are refused, with no way around it over MCP |
| `tg_messages_edit` | `tg messages edit` | the new text of the owner's own message, by `messages.edit`; `md` as `--md`; repeating it changes nothing |
| `tg_chats_mark_read` | `tg chats mark-read` | mark a chat read, to its newest message or `until` one, by `chats.mark-read` — the other side sees it |
| `tg_messages_delete` | `tg messages delete` | up to 10 messages from the owner's view, by `messages.delete` — a form first by default; never for everyone; each counts toward the hourly limit |
| `tg_reactions_add`, `tg_reactions_remove` | `tg reactions add`, `remove` | the owner's reaction on one message, by `reactions`; the confirmation form shows the emoji |
| `tg_polls_show` | `tg polls show` | a poll and its answer ids; a read tool |
| `tg_polls_vote`, `tg_polls_close`, `tg_polls_create` | `tg polls vote`, `close`, `create` | vote by answer id (`polls.vote`), close the owner's own poll (`polls.close`), create one (`polls.create`, with `send_id` for a retry and `revote` to let people change their vote, and `topic` to choose a forum topic) |
| `tg_polls_vote`, `tg_polls_close`, `tg_polls_create` | `tg polls vote`, `close`, `create` | vote by answer id (`polls.vote`), close the owner's own poll (`polls.close`), create one (`polls.create`, with `send_id` for a retry in the same chat and topic, `revote` to let people change their vote, and `topic` to choose an open forum topic) |
| `tg_messages_forward` | `tg messages forward` | one message into another chat (`to`), by `messages.forward`; `send_id` repeats a forward whose outcome was unknown |
| `tg_messages_pin`, `tg_messages_unpin` | `tg messages pin`, `unpin` | pin one message, quietly unless `notify`, by `messages.pin` and `messages.unpin` |
| `tg_chats_create`, `tg_chats_join`, `tg_chats_leave` | `tg chats create`, `join`, `leave` | make a group or channel with these people, join one by its link, leave one — the others see each |
Expand Down
9 changes: 7 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ before `tg store fetch <chat> --last 100`. An agent can read `tg skill show` wit
use `tg setup --agent codex` to select its skill explicitly. `tg setup --help` explains the flags.
Nothing more is needed to read.

To discover the arguments for a task, use `tg commands messages search --json` for one command
or `tg commands messages --json` for a group. Both include global options and exit codes.
Inspect each command path in a separate call; `tg commands --json` returns the whole tree.

## Log in

`tg setup` is the first-run command. It defaults to automatic app registration and QR login;
Expand Down Expand Up @@ -211,8 +215,9 @@ tg models audio download parakeet-v3 # once, checked against the sha256 this v
| `gigaam-v3-ctc` | Russian — a little faster, rougher with capital letters | 225 MB |

`--model` picks another model for one command, beside `--transcribe` or in `messages transcribe`; `transcribeWith` and `speechModel` in the settings
choose the defaults ([configuration.md](configuration.md)). A transcript is kept in the local store,
so asking again answers at once. `--transcribe` can take minutes.
choose the defaults ([configuration.md](configuration.md)). A transcript is kept in the local store
and reused by message lists, inboxes and reviews. Calling `messages transcribe` again can request
a new transcript or run recognition again. `--transcribe` can take minutes.

### Files

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
},
"dependencies": {
"@leemour/cli-core": "0.17.0",
"@leemour/cli-messaging": "0.139.0",
"@leemour/cli-messaging": "0.140.0",
"@mtcute/node": "0.32.3",
"commander": "15.0.0",
"valibot": "1.5.0"
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ allowBuilds:
lefthook: true
minimumReleaseAgeExclude:
- '@leemour/cli-core@0.7.0 || 0.9.0 || 0.12.0 || 0.14.0 || 0.15.0 || 0.16.0 || 0.17.0'
- '@leemour/cli-messaging@0.1.0 || 0.2.0 || 0.3.0 || 0.4.0 || 0.5.0 || 0.6.0 || 0.7.0 || 0.8.0 || 0.9.0 || 0.11.0 || 0.12.0 || 0.14.0 || 0.15.0 || 0.16.0 || 0.17.0 || 0.18.0 || 0.19.0 || 0.20.0 || 0.21.0 || 0.22.0 || 0.23.0 || 0.24.0 || 0.25.0 || 0.27.0 || 0.29.0 || 0.30.0 || 0.31.0 || 0.34.0 || 0.35.0 || 0.37.0 || 0.38.0 || 0.39.0 || 0.40.0 || 0.41.0 || 0.42.0 || 0.43.0 || 0.44.0 || 0.45.0 || 0.46.0 || 0.47.0 || 0.48.0 || 0.49.0 || 0.50.0 || 0.51.0 || 0.52.0 || 0.53.0 || 0.54.0 || 0.57.0 || 0.61.0 || 0.67.0 || 0.70.0 || 0.73.0 || 0.74.0 || 0.75.0 || 0.76.0 || 0.77.0 || 0.78.0 || 0.80.0 || 0.81.0 || 0.82.0 || 0.83.0 || 0.84.0 || 0.85.0 || 0.87.0 || 0.88.0 || 0.89.0 || 0.90.0 || 0.91.0 || 0.92.0 || 0.93.0 || 0.94.0 || 0.95.0 || 0.96.0 || 0.97.0 || 0.98.0 || 0.99.0 || 0.103.0 || 0.104.0 || 0.106.0 || 0.108.0 || 0.109.0 || 0.111.0 || 0.112.0 || 0.113.0 || 0.119.0 || 0.120.0 || 0.121.0 || 0.123.0 || 0.125.0 || 0.126.0 || 0.127.0 || 0.129.0 || 0.132.0 || 0.134.0 || 0.135.0 || 0.136.0 || 0.137.0 || 0.139.0'
- '@leemour/cli-messaging@0.1.0 || 0.2.0 || 0.3.0 || 0.4.0 || 0.5.0 || 0.6.0 || 0.7.0 || 0.8.0 || 0.9.0 || 0.11.0 || 0.12.0 || 0.14.0 || 0.15.0 || 0.16.0 || 0.17.0 || 0.18.0 || 0.19.0 || 0.20.0 || 0.21.0 || 0.22.0 || 0.23.0 || 0.24.0 || 0.25.0 || 0.27.0 || 0.29.0 || 0.30.0 || 0.31.0 || 0.34.0 || 0.35.0 || 0.37.0 || 0.38.0 || 0.39.0 || 0.40.0 || 0.41.0 || 0.42.0 || 0.43.0 || 0.44.0 || 0.45.0 || 0.46.0 || 0.47.0 || 0.48.0 || 0.49.0 || 0.50.0 || 0.51.0 || 0.52.0 || 0.53.0 || 0.54.0 || 0.57.0 || 0.61.0 || 0.67.0 || 0.70.0 || 0.73.0 || 0.74.0 || 0.75.0 || 0.76.0 || 0.77.0 || 0.78.0 || 0.80.0 || 0.81.0 || 0.82.0 || 0.83.0 || 0.84.0 || 0.85.0 || 0.87.0 || 0.88.0 || 0.89.0 || 0.90.0 || 0.91.0 || 0.92.0 || 0.93.0 || 0.94.0 || 0.95.0 || 0.96.0 || 0.97.0 || 0.98.0 || 0.99.0 || 0.103.0 || 0.104.0 || 0.106.0 || 0.108.0 || 0.109.0 || 0.111.0 || 0.112.0 || 0.113.0 || 0.119.0 || 0.120.0 || 0.121.0 || 0.123.0 || 0.125.0 || 0.126.0 || 0.127.0 || 0.129.0 || 0.132.0 || 0.134.0 || 0.135.0 || 0.136.0 || 0.137.0 || 0.139.0 || 0.140.0'
- '@leemour/cli-messaging-sqlite@1.0.0'
- '@leemour/cli-messaging-onnx@1.0.0'
30 changes: 26 additions & 4 deletions skills/tg-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ description: Set up Telegram and read or send messages in the owner's personal a
`tg` works with the owner's **real personal account**. A mistake here does not fail a test; it
writes to a living person. One call, one action: connect, do it, print, exit.

The full list of commands and flags is **`tg commands --json`**: the whole tree in one answer —
arguments, flags (whether each takes a value, whether it is required), exit codes, and `mutates:
true` on the commands that change something in Telegram. `tg --help` is the same for a person. This
file holds what the help cannot say: the traps and the boundaries.
Read this skill, then discover only the commands relevant to the task. **`tg commands messages
search --json`** describes one command; **`tg commands messages --json`** describes that group.
Both include arguments, options, global options and exit codes. Use `tg <command> --help` for a
shorter explanation. Command words form one path, not a list of groups: inspect different groups
in separate calls. **`tg commands --json`** returns the entire tree when you need an overview;
do not read it in full before every task. `mutates: true` identifies writes; `local: true` limits
those writes to this machine. This file holds the traps and boundaries.

## Installation readiness

Expand Down Expand Up @@ -161,6 +164,25 @@ summary; news digests remain separate future work. Permission: `messages.evidenc

## The usual path

Choose a path from the user's task rather than reading recent messages by default:

- **Find an agreement or document:** find the relevant chat IDs, then search the local archive.
Inspect search coverage and `store status`; `messages list` alone is only a recent window.
Follow promising hits with `messages context` or `messages show` and cite their locators.
Check related chats for a later correction. Empty hits, an empty chat and `hasMore: false`
never establish complete Telegram history. If missing history matters, explain the gap.
Fetch only an authorized chat and bounded period/amount; inspect `store fetch --estimate`
before a download. Offline cannot fetch missing history.
- **Prepare for a meeting:** find project groups and the participants' personal chats. A DM's
title need not contain the project name. Compare dated group messages with relevant DMs;
a later confirmation can close an old blocker. Use `messages evidence` for a brief, inspect
coverage and follow its cursor. Distinguish decisions, open questions and inferred dates.
- **Recommend a person:** compare actual evidence of relevant experience across chats. Match
message sender IDs to `contacts show` and relevant personal chats; two identical display
names are not one person. Past availability is not current availability. Prepare a draft
unless the owner explicitly asks to send it to the identified recipient. If sending is
refused by permissions, stop and keep the draft; do not change settings or switch profiles.

The ids below are made up — use the real ones from the previous answer.

```sh
Expand Down
Loading