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
Composes and delivers the daily fitness brief — a forward-looking morning read (readiness + today's plan) or a backward-looking evening read (today's training, or an earned-rest note, + tomorrow's setup) — in the operator's configured voice. Invoke with /claude-code-fitness-hermit:fitness-brief --morning|--evening|--slot <name>. Becomes the plugin's two daily beats — the morning Strava connectivity check and the evening activity sync, RPE binding, and Run deep-dive.
development
Renew the hermit's long-lived Claude login token over the channel, before it expires. Relays a one-time sign-in link to the operator, takes the code back, installs the new token, and restarts. Activates on messages like 'relogin', 'renew my login', 'reauth', 'the login is expiring', or when doctor's credential-expiry check flags setup-token.
development
Synthesizes the past 7 days of archived briefs into a weekly digest — top stories, emerging vs faded themes, category activity, and per-source performance built from archive frontmatter. Delivers to the operator's configured channel and archives a weekly note. Designed as a weekly routine. Invoke with /feed-hermit:weekly-digest.
development
Manage developing story arcs tracked across briefs — add, resolve, and list active arcs in compiled/story-arcs-*.md. Arc Watch keywords drive the feed-brief arc-tagging enrichment. Invoke with /feed-hermit:story-arcs add|resolve|list.