feat(discord): make the typed first-message hint a separate opt-in, off by default

Claude keeps the silent system-prompt hint (startupHint, on). Other CLIs are sent the typed message only with typedHint, which costs a model turn per tab.
This commit is contained in:
Devvyn committed 2026-10-30 01:38:37 +08:00
1 parent e95d38a944
commit 6d4357502b
10 files changed
+77 -27

No files matched your search

+1 -1
View File
@@ -1493,7 +1493,7 @@ One thread per tab in a private Discord channel, alerts out and replies typed ba
| Method | Path | Body | Notes |
| ------ | ------ | ------ | ------ |
| `GET` | `/api/discord` | none | Config without the token, plus `hasToken`, `tokenMasked`, `gateway` (`off` \| `connecting` \| `connected` \| `error`), `gatewayError` and `lastResult`. |
| `PUT` | `/api/discord` | `{ enabled?, token?, guildId?, parentChannelId?, allowedUserIds?, threadCreation?, onSessionDelete?, pingMode?, scope?, startupHint? }` (strict) | `threadCreation`: `lazy` \| `onSessionStart`. `onSessionDelete`: `keep` \| `archive` \| `delete`. `pingMode`: `off` \| `critical` \| `all`. `scope`: `attention` \| `all`. An absent `token` keeps the saved one; `""` clears it. `400` for a non-numeric id or enabling without a token, guild, channel and at least one allowed user. |
| `PUT` | `/api/discord` | `{ enabled?, token?, guildId?, parentChannelId?, allowedUserIds?, threadCreation?, onSessionDelete?, pingMode?, scope?, startupHint?, typedHint? }` (strict) | `threadCreation`: `lazy` \| `onSessionStart`. `onSessionDelete`: `keep` \| `archive` \| `delete`. `pingMode`: `off` \| `critical` \| `all`. `scope`: `attention` \| `all`. An absent `token` keeps the saved one; `""` clears it. `400` for a non-numeric id or enabling without a token, guild, channel and at least one allowed user. |
| `POST` | `/api/discord/test` | none | Posts one message to the parent channel with the saved config, even while disabled. `200` with `data.ok`; `400` if no token or channel is saved. |
| `POST` | `/api/sessions/:id/discord-notify` | `{ message, urgent?, file? }` (strict, message 1 to 1500 characters) | A CLI asks for the user: posts into that tab's thread, pinging when `urgent`. `file` is an absolute path inside the tab's working folder (real path, regular file, at most 8 MB, local sessions only) uploaded with the post; `400` otherwise. `409` while the bridge is disabled. The `codeman notify "message" [--urgent] [--file path]` command wraps this route. |
+11 -10
View File
@@ -88,20 +88,21 @@ In the Discord mobile app set the server to **All Messages**. Discord's own push
## Options
| Option | Values | Default | Effect |
| ----------------- | --------------------------- | ----------- | ---------------------------------------------------------------------------------------- |
| `threadCreation` | `lazy`, `onSessionStart` | `lazy` | `lazy` makes a thread on the first notification. `onSessionStart` makes one per new tab |
| `onSessionDelete` | `keep`, `archive`, `delete` | `archive` | What happens to the thread when its tab is deleted. `delete` needs Manage Threads |
| `pingMode` | `off`, `critical`, `all` | `critical` | `critical` pings you for permission prompts, questions, errors and urgent posts |
| `scope` | `attention`, `all` | `attention` | `attention` skips the quiet "response complete" event. `all` sends it too |
| `startupHint` | on, off | on | Tell each CLI about `codeman notify` (Claude at launch, others once on their first idle) |
| Option | Values | Default | Effect |
| ----------------- | --------------------------- | ----------- | --------------------------------------------------------------------------------------- |
| `threadCreation` | `lazy`, `onSessionStart` | `lazy` | `lazy` makes a thread on the first notification. `onSessionStart` makes one per new tab |
| `onSessionDelete` | `keep`, `archive`, `delete` | `archive` | What happens to the thread when its tab is deleted. `delete` needs Manage Threads |
| `pingMode` | `off`, `critical`, `all` | `critical` | `critical` pings you for permission prompts, questions, errors and urgent posts |
| `scope` | `attention`, `all` | `attention` | `attention` skips the quiet "response complete" event. `all` sends it too |
| `startupHint` | on, off | on | Tell Claude about `codeman notify` in its system prompt at launch |
| `typedHint` | on, off | off | Also send the note once, as a first message, to brand-new non-Claude CLI tabs |
## Telling the CLIs
A CLI only posts deliberately if it knows it can. While the bridge and `startupHint` are on:
A CLI only posts deliberately if it knows it can. While the bridge is on:
- **Claude** is launched with `--append-system-prompt`, so nothing shows in the conversation.
- **Every other CLI** is sent the same text once, as a message, the first time a brand-new tab becomes idle. Tabs that are restored when Codeman starts, resumed tabs, shell tabs, and remote (ssh) and Docker tabs (which have no `codeman` command) are never sent it.
- **Claude** (`startupHint`, on by default) is launched with `--append-system-prompt`, so nothing shows in the conversation.
- **Every other CLI** is sent nothing unless you turn on `typedHint` (off by default: it costs one model turn per tab, and for a spawned worker or a scheduled run it arrives together with the first real prompt). With it on, the same text is sent once, as a message, the first time a brand-new tab becomes idle. Tabs that are restored when Codeman starts, resumed tabs, shell tabs, and remote (ssh) and Docker tabs (which have no `codeman` command) are never sent it.
The text explains `codeman notify "message" --urgent`. `codeman` is already on the PATH of every session and reads `CODEMAN_SESSION_ID` and `CODEMAN_API_URL`, so no other setup is needed. Without a bridge it prints that the bridge is off and exits cleanly. Tabs started before the bridge was enabled are not told; use the skill or `CLAUDE.md` for those.
+11 -10
View File
@@ -38,23 +38,24 @@ only), never in the shared settings, and never shown again once saved.
## Options
| Option | Choices | Default |
| ------------------------------ | ---------------------------------------- | --------------- |
| Create threads | On the first alert, or for every new tab | First alert |
| When a tab is deleted | Keep, archive or delete the thread | Archive |
| @ ping | Never, critical events only, every post | Critical only |
| Which events | Needs attention, or everything | Needs attention |
| Tell each CLI how to reach you | On or off | On |
| Option | Choices | Default |
| ---------------------------------- | ---------------------------------------- | --------------- |
| Create threads | On the first alert, or for every new tab | First alert |
| When a tab is deleted | Keep, archive or delete the thread | Archive |
| @ ping | Never, critical events only, every post | Critical only |
| Which events | Needs attention, or everything | Needs attention |
| Tell Claude how to reach you | On or off | On |
| Also tell other CLIs, as a message | On or off | Off |
Deleting a thread needs the **Manage Threads** permission on the channel. "Critical" means a
permission prompt, a question, an error, or a post an agent marked urgent.
## Telling the agents
An agent only posts deliberately if it knows it can. With **Tell each CLI how to reach you** on:
An agent only posts deliberately if it knows it can:
- **Claude** is launched with the instruction as part of its system prompt, so nothing shows in the chat.
- **Every other CLI** is sent the same text once, as a message, the first time a brand-new tab goes idle.
- **Claude** is launched with the instruction as part of its system prompt (**Tell Claude how to reach you**, on by default), so nothing shows in the chat.
- **Every other CLI** is told only if you turn on **Also tell other CLIs, as a message** (off by default: it costs one model turn per tab, and for a spawned worker or a scheduled run it arrives with the first real prompt). Then the same text is sent once, as a message, the first time a brand-new tab goes idle.
Restored tabs, resumed tabs, shell tabs, and remote (ssh) and Docker tabs (which have no `codeman` command) are never sent it.
Tabs that were already open when you turned the bridge on are not told. The alerts still reach