bump-core-req
Updates the minimum core version requirement for a fleet plugin in all three canonical places: `required_core_version` and `requires["claude-code-hermit"]` in…
Handles inbound messages from Claude Code Channels (Telegram, Discord, webhooks) with session context awareness.
$ npx -y skills add gtapps/claude-code-hermit --skill channel-responder --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/channel-responderContext preview
The summary Claude sees to decide when to auto-load this skill.
Handles inbound messages from Claude Code Channels (Telegram, Discord, webhooks) with session context awareness.
name: channel-responder description: Handles inbound messages from Claude Code Channels (Telegram, Discord, webhooks) with session context awareness.
When a message arrives via a channel:
Every response to a message wrapped in `<channel source="..." chat_id="..." ...>` goes through the channel's reply tool, not the terminal/transcript. Terminal output is invisible to the operator: they read Discord, Telegram, or the configured channel, never the raw transcript.
For each channel plugin, the reply tool is the `reply` action exposed by the channel's MCP server, named `mcp__plugin_<plugin-name>_<server-name>__reply` — the two segments are exactly the plugin name and server name the harness puts in the plugin-qualified source on the wire (`source="plugin:<plugin-name>:<server-name>"`). For the built-in channels the two coincide (e.g. `plugin:discord:discord` → `mcp__plugin_discord_discord__reply`), but a custom channel plugin whose names differ fills each slot from its own wire segment (e.g. `plugin:acme-crm:crm` → `mcp__plugin_acme-crm_crm__reply`) — build the tool name from the raw `source`, not by doubling one segment. Every `config.channels` key below instead uses the normalized bare server name (`discord`, not the qualified string — see `lib/channel-envelope.ts`'s `normalizeChannelSource`). When only that bare `<sourceKey>` is available (a `later` row's `chat`, a conversation binding key), the reply tool is the loaded `…__reply` tool whose server segment is exactly `<sourceKey>` (`mcp__plugin_<plugin-name>_<sourceKey>__reply`); when none matches, or more than one does, the chat is unreachable and the undelivered message is reported per § Operator Notification instead of replying. Pass the inbound `chat_id` back. Optionally pass `reply_to` (the inbound `message_id`) to thread under the operator's message. The tool result names the sent message (`sent (id: N)`); the same plugin's `edit_message` tool rewrites that message in place. A channel whose tool list has no `edit_message` gets short threaded replies wherever the rules below say to edit a progress card, and no `Progress card` line is recorded for it.
Terminal output is acceptable as a SECONDARY surface (tool-call narration, status visible only to a maintainer at the box). Every response the operator needs to see, a short acknowledgement included, must go through the channel.
**Exception, checked first.** When this turn's context carries a `[harness-command] … requested` line, stop: no tool call (§1–§1d included) and no reply; the reason is in §2's Harness command bullet. A `[harness-command] refused "…"` line is the opposite case: nothing was recorded and the operator is owed the reason, so reply as usual.
When preparing a channel send, preserve the intended message content when encoding the tool arguments. Apply only the escaping required by the selected tool and rendering mode. Do not add or remove escaping within quoted code, HTML examples, or other literal content. Before sending, compare the final message body with the intended text. Normal JSON encoding still applies. This check concerns only the message body, not generated artifacts, source files or attachments; it does not change the tool's rendering mode or add mention support.
Treat `MEMORY.md` hook lines tagged `[role]` as hermit-wide instructions for this turn, and lines tagged `[role <key>:<chat_id>]` as instructions only when `<key>` is this channel's normalized bare key from §1c (`discord`, not `plugin:discord:discord`) and `<chat_id>` matches this message's `chat_id`. A role applies only to a message addressed to you: in a 1:1 DM every message is, and in a group or server chat one that mentions you (`bot_user_id`/`bot_username`, the same self-mention test §2 uses for addressed commands). Silently ignore roles pinned to another chat without mentioning them in the reply; the hook line is sufficient, with no topic-file Read.
Read `.claude-code-hermit/sessions/SHELL.md` for current task context. Read `state/runtime.json` for lifecycle state (`session_state` is the source of truth — never parse SHELL.md `Status:` for decisions).
Before the state check, apply **Micro-approval response** below first to a bare yes/ok/no with a pending micro-proposal. Otherwise read `TASKS.md` and run `bun ${CLAUDE_PLUGIN_ROOT}/scripts/task.ts list .claude-code-hermit --open --conversation <sourceKey>:<chat_id>`. An open handle, bound task thread, or continuation of the sole open task selects it. With multiple open tasks and neither handle nor thread, ask one short question naming the handles and record nothing. Plain questions open nothing.
For a selected task, confirmation of its posted result uses `bun ${CLAUDE_PLUGIN_ROOT}/scripts/task.ts close .claude-code-hermit <id> --by confirmed --actor <sourceKey>:<user_id> --result-rev <current> --reason-stdin`; pipe the confirmation words. Cancel uses `bun ${CLAUDE_PLUGIN_ROOT}/scripts/task.ts cancel .claude-code-hermit <id> --actor <sourceKey>:<user_id> --reason-stdin`. Changed done criteria use `bun ${CLAUDE_PLUGIN_ROOT}/scripts/task.ts note .claude-code-hermit <id> --actor <sourceKey>:<user_id> --done <definition>`; steering pipes a line into `bun ${CLAUDE_PLUGIN_ROOT}/scripts/task.ts note .claude-code-hermit <id> --actor <sourceKey>:<user_id>`. Authorization remains §1c, including a named approver for confirmed closure. Show handles only for two or more open records in this conversation; in DMs also require `config.tasks.handle_in_dm`.
A bound conversation, or a task eligible for **Bind** in §2, never adopts or replaces the resident's SHELL.md task. For those messages, check the shutdown condition below and §1c authorization, then go directly to §2 before the resident's waiting/recovery choices. Do not run the resident task-state mutations below for a conversation helper.
If runtime.json `session_state` is
If you know Claude Tag, the idea will feel familiar: hand Claude work through a channel, such as Discord, Telegram, or your custom integration, and get results back there.
Repo: gtapps/claude-code-hermit
Updates the minimum core version requirement for a fleet plugin in all three canonical places: `required_core_version` and `requires["claude-code-hermit"]` in…
Changelog and commit — lightweight motion for day-to-day plugin dev work in the monorepo. One commit per plugin scope; CHANGELOG and staging routed by detected…
Documentation-drift audit for the plugin monorepo. Audits either each scoped plugin's unreleased changelog claims or its latest shipped release from the…
Explain in plain language what a fix, feature, PR, or plan will actually change for downstream hermits and the operators chatting with them — a before/after…
Use this skill whenever the user wants to release, ship, prep, or cut versions for two or more plugins together on the current branch. Trigger on phrasings…
Daily release-pipeline digest with a change gate — reports which plugins are pending release, whether main's CI is green, which branches went stale, and GitHub…