skills/channel-responder/SKILL.md
Handles inbound messages from Claude Code Channels (Telegram, Discord, webhooks) with session context awareness.
npx skillsauth add gtapps/claude-code-hermit channel-responderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When a message arrives via a channel:
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).
If runtime.json session_state is idle (no active task):
If runtime.json session_state is waiting (alive but blocked on input):
Read waiting_reason from runtime.json to understand why:
"unclean_shutdown" or "dead_process" → operator reply is an archive/resume choice: (1) = archive as partial and start fresh, (2) = resume as-is. Handle accordingly via claude-code-hermit:session-mgr — session-mgr owns the full state transition including clearing waiting_reason.
"operator_input", "conservative_pickup", or null → treat as normal task resumption.
Status request → respond with current context, stay waiting
New instruction or answer to a question → update runtime.json session_state to in_progress, clear waiting_reason to null, update SHELL.md Status to in_progress (cosmetic), resume work
Anything else → respond, stay waiting
Read config.json → channels.<channel>.allowed_users for the inbound channel:
allowed_users list: ignore the message silently — do not respond, do not log. Applies to ALL message types including status requests.allowed_users is absent for this channel: accept all messages (backwards compatible)allowed_users is an empty array []: accept from no one (explicit lockdown)The allowlist is per-channel inside the channels object in config.json:
{
"channels": {
"discord": { "enabled": true, "allowed_users": ["user-id-1"] },
"telegram": { "enabled": true, "allowed_users": ["user-id-1"] }
}
}
After authorization passes, store the inbound chat_id to config.json → channels.<channel>.dm_channel_id (e.g. channels.discord.dm_channel_id) if it differs from the currently stored value or hasn't been stored yet.
This is how the agent learns the DM channel ID for proactive outbound notifications. In Discord, the DM channel ID differs from the user ID and is only discoverable from an inbound message. Write back to config.json only when the value has changed to avoid unnecessary writes.
Slash command (message starts with /, e.g. /simplify, /plugin:command)
Status request ("what are you working on?", "status", "progress")
idle: respond with session summary — tasks completed, cumulative cost, "ready for what's next"in_progress: respond with a concise summary of SHELL.md: task, current step, blockersTask assignment (only when Status is idle: "work on X", "next task: Z", "start Y", or any message describing work to be done)
/claude-code-hermit:session-start to begin the new task (idle → in_progress)Micro-approval response ("yes", "no", "MP-… yes/no", or similar while any pending micro-proposal exists)
state/micro-proposals.json → pending. Filter to status: "pending" entries.MP-YYYYMMDD-N yes): match that entry by id.yes/no and exactly one pending entry: apply to that entry.yes/no and multiple pending entries: reply listing the pending IDs and ask the operator to specify (e.g. "MP-20260422-0 yes"). Do not resolve yet.question from the entry before modifying.status: "approved". Append micro-resolved event via append-metrics.js: {"ts":"<now ISO>","type":"micro-resolved","micro_id":"<id>","action":"approved","question":"<question>"}/claude-code-hermit:proposal-create, queue for next idle, set status: "approved". Append micro-resolved event with "action":"approved".status: "rejected". Append micro-resolved event with "action":"rejected","question":"<question>".pending. Write the file.Proposal approval ("accept PROP-", "go ahead with PROP-", "approve PROP-", or referencing proposal numbers)
/claude-code-hermit:proposal-act accept PROP-NNN for each referenced proposal/claude-code-hermit:proposal-list to resolve to PROP-NNN IDs. If no match, tell the operator.New instruction ("work on X", "switch to Y", "prioritize Z")
idle: treat as Task assignment (above)Question ("why did you...", "what about...", "how does X work?")
Emergency ("stop", "abort", "revert", "rollback")
blocked and reasonThis skill is a stub for the Channels research preview (Claude Code v2.1.98+). As Channels matures, extend this skill with:
tools
Presence history & tracker-health report — current home/away state, reliability, recent arrival/departure transitions, and activity patterns for person/device_tracker entities. Use when the operator asks about presence history or when a presence-dependent automation (locks, alarm, vacuum, climate) misbehaves.
development
Evening house brief — end-of-day security check, device status, and energy snapshot. Runs as a daily routine at 22:30 or on demand.
tools
Browse and explain the hermit's Home Assistant automations — list by topic, filter by keyword with plain-language YAML explanations, or sort by last-fired. Read-only. Use when the operator asks "what automations do I have / what does this one do / which haven't fired."
tools
On-demand HA-voice brainstorm — reads entity inventory, automation/script listings, and operator intent to surface at most 2 capability-gap ideas, each gated by proposal-triage before becoming a PROP. Invoke when the operator asks "what automations am I missing?", "any coverage gaps?", or "brainstorm improvements". Never runs autonomously.