music-suggestion
Skill535 starsChanged 7 months agoApache-2.0View on GitHub
Proactive music suggestion. Routed in by user-emotion-detection/SKILL.md (the router) on emotion.detected (camera) and speech_emotion.detected (voice) events when the synthesized mood is suggestion-worthy (sad/stressed/tired/excited/happy/bored) AND audio is idle AND cooldown is clear. Reuses injected context and emits log HW markers in the same reply as mood. Does NOT fire on motion.activity / [activity] events — those route to wellbeing/SKILL.md only. NOT for user-initiated music requests (those use the music skill).
- Sends data out
We split this skill into the rules it gives an agent
13 rules. About 0 are common, 7 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 24
Only one trigger: Mood — after logging a mood decision that is suggestion-worthy (sad, stressed, tired, excited, happy, bored).
- Distinctive: line 46
If the message has no context block (pre-fetch failed), fall back to the concurrent GET batch:
- Distinctive: line 68
If any condition fails → router took another path (action / checkin / silent).
- Very specific: line 74
If music_pattern_for_hour is non-null → use its preferred_genre.
- Distinctive: line 86
If audio history shows a clear preference (e.g.
- Distinctive: line 90
NEVER auto-play — only suggest.
- Distinctive: line 110
When the user responds in a later turn (accept / reject), POST status via curl as before — that's a regular agent action, not a …
- Very specific: line 131
Use audio_recent from the injected context to personalize.
- Distinctive: line 134
No history → fall back to genre table above.
- Very specific: line 146
After user confirms (known user) → [HW:/audio/play:{"query":"Bill Evans Waltz for …
- Very specific: line 147
After user confirms (unknown) → [HW:/audio/play:{"query":"Bill Evans Waltz for Debby"}][HW:/emotion:{"emotion":"happy","intensity":0.8}] …
- Very specific: line 151
All computation stays in thinking — reply is only the suggestion sentence (with HW markers) or NO_REPLY.
- Distinctive: line 152
Never mention "cooldown", "interval", "threshold", or timestamps in the reply.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Music Suggestion (Proactive)
unknownusers count. Always run suggestion checks whencurrent_useris"unknown"— speak only, no DM. Never skip because the user is unknown/unconfirmed.
Spoken output
For a routed suggestion, emit all required mood/suggestion/emotion markers and one short invitation to play one song, normally at most 20 words. Do not add a separate mood checkin or a second confirmation. Keep genre selection, cooldown checks and marker construction in the provider's native thinking channel; do not summarize them in text before/after tools or in the final answer. If native thinking is unavailable, omit analysis. Use the supplied context and finish once a suitable suggestion is ready, without rereading skills to polish it. This changes wording only: preserve routing, logging, user consent before play, and the existing known-user DM behavior.
Triggers
Only one trigger: Mood — after logging a mood decision that is suggestion-worthy (sad, stressed, tired, excited, happy, bored). Activity events ([activity] Activity detected: ..., whether sedentary, drink/break, or celebrate) route to wellbeing/SKILL.md and never to this skill.
User attribution
{name} MUST come from [context: current_user=X] tag. If missing, use "unknown". NEVER infer from memory or chat history.
What to read (pre-fetched in [emotion_context: ...])
The backend injects everything you need on emotion.detected (face) or speech_emotion.detected (voice) — same block, same fields:
audio_playing(bool) — replacesGET /audio/status.last_suggestion_age_min(int,-1if none today) — replacesmusic-suggestion-history?last=1.prior_decision+is_decision_stale— replacesmood-history?kind=decision&last=1. The freshly synthesized decision from THIS turn still lives in yourthinking.audio_recent({track,duration_s,stopped}) — replacesaudio/history?last=1.music_pattern_for_hour({preferred_genre,strength,peak_hour}ornull) — replacescat patterns.jsonmatching by current hour ±1.suggestion_worthy(bool) — pre-applied bucket gate (true forsad/stressed/tired/excited/happy/bored).mapped_mood— convenient mirror ofuser-emotion-detection's mapping; useful when no fresh decision exists yet.
Do NOT fire any read tool calls when this block is present.
Fallback (only if [emotion_context: ...] is missing)
If the message has no context block (pre-fetch failed), fall back to the concurrent GET batch:
curl -s http://127.0.0.1:5001/audio/status &
curl -s "http://127.0.0.1:5000/api/openclaw/music-suggestion-history?user={name}&last=1" &
curl -s "http://127.0.0.1:5000/api/openclaw/mood-history?user={name}&kind=decision&last=1" &
curl -s "http://127.0.0.1:5001/audio/history?person={name}&last=1" &
cat /root/local/users/{name}/habit/patterns.json 2>/dev/null &
waitRouting precedence
user-emotion-detection/SKILL.md is the router for emotion responses. It picks one of music / checkin / action / silent per turn from the same [emotion_context: ...] block.
This skill produces output only when the router picks music — i.e. all of:
suggestion_worthy == true(mapped_mood ∈sad/stressed/tired/excited/happy/bored)audio_playing == falselast_suggestion_age_min ∉ [0, 7)(cooldown not active — shared with checkin) (production: change to 30 min before ship)is_decision_stale == falseOR a fresh mood decision was synthesized this turn
If any condition fails → router took another path (action / checkin / silent). Skip silently — do NOT emit a music-suggestion marker, do NOT speak. The router (or downstream skill) handles the output. Use audio_recent to personalize genre when proceeding.
Pick genre
Use music_pattern_for_hour from the context block (already matched by current hour ± 1; do NOT re-cat patterns.json).
If music_pattern_for_hour is non-null → use its preferred_genre. Otherwise fall back to the default table below. The pattern is bootstrapped lazily by wellbeing on its first threshold nudge; absent = no habit data yet, fall back without invoking habit Flow A here.
Otherwise, fall back to default genre table:
| User state | Default genre |
| Tired / fatigued | Calm piano, gentle acoustic, nature sounds |
| Stressed / tense | Soft jazz, classical, meditation |
| Happy / energetic | Upbeat pop, jazz, feel-good classics |
| Bored / restless | Fun pop, disco, upbeat indie |
| Sedentary (no mood) | Lo-fi, ambient, study beats |
If audio history shows a clear preference (e.g. K-pop, classical) → override both habit and table.
Suggest (speak only)
- NEVER auto-play — only suggest. Play after user confirms.
- ONE sentence, conversational: "How about some Norah Jones?"
- Suggest 1 song at a time.
- Known users — speak + DM via Telegram:
[HW:/emotion:{"emotion":"caring","intensity":0.5}][HW:/dm:{"telegram_id":"<id>"}] Your suggestion text.telegram_idis in the injected[user_info: ...]block — never fetch. - Unknown users — speak only (no DM):
[HW:/emotion:{"emotion":"caring","intensity":0.5}] Your suggestion text. Log withuser:"unknown".
What to write (HW marker — fires async, no tool turn)
Embed at the start of your spoken reply, alongside the mood signal/decision markers and the emotion / dm markers:
[HW:/music-suggestion/log:{"user":"{name}","trigger":"mood:tired","message":"Want some calm piano?"}]The runtime parses, strips, fires the POST in a goroutine. Skip the marker entirely when you skipped the suggestion (NO_REPLY path).
Do NOT use curl exec for this log — same reason as the mood logs: a tool turn for a side-effect with nothing to wait on.
Regex caveat: the body must not contain }. The message field is usually a short caring sentence, but if it would contain } (rare — emoji, formula text) fall back to curl.
When the user responds in a later turn (accept / reject), POST status via curl as before — that's a regular agent action, not a fire-and-forget side effect:
curl -s -X POST http://127.0.0.1:5000/api/music-suggestion/status \
-H 'Content-Type: application/json' \
-d '{"user":"{name}","day":"<day>","seq":<seq>,"status":"accepted"}'- Accepts →
"status":"accepted" - Rejects →
"status":"rejected" - Ignores → no update
Fallback (only if HW marker is rejected by the runtime)
curl -s -X POST http://127.0.0.1:5000/api/music-suggestion/log \
-H 'Content-Type: application/json' \
-d '{"user":"{name}","trigger":"mood:tired","message":"Want some calm piano?"}'Learning from history
Use audio_recent from the injected context to personalize; only use GET /audio/history in the missing-context fallback above:
- Song ended naturally + listened > 3 min → user enjoyed it → suggest similar artist/genre.
- User stopped manually + listened < 30s → didn't like it → try different direction.
- No history → fall back to genre table above.
Examples
- Mood: tired (known user) →
[HW:/emotion:{"emotion":"caring","intensity":0.5}][HW:/dm:{"telegram_id":"158406741"}] You seem tired — want some calm piano? - Mood: tired (unknown) →
[HW:/emotion:{"emotion":"caring","intensity":0.5}] You seem tired — want some calm piano? - Mood: stressed (known user) →
[HW:/emotion:{"emotion":"caring","intensity":0.6}][HW:/dm:{"telegram_id":"158406741"}] You look a bit tense — want some soft piano to ease into? - Mood: stressed (unknown) →
[HW:/emotion:{"emotion":"caring","intensity":0.6}] You look a bit tense — want some soft piano? - Mood: sad (unknown) →
[HW:/emotion:{"emotion":"caring","intensity":0.6}] Rough moment? Some gentle acoustic might help. - Mood: bored (unknown) →
[HW:/emotion:{"emotion":"caring","intensity":0.5}] Need a lift? How about some upbeat indie? - Mood: excited (unknown) →
[HW:/emotion:{"emotion":"happy","intensity":0.7}] Riding the energy — feel-good pop? - Mood: happy, music already playing →
NO_REPLY - After user confirms (known user) →
[HW:/audio/play:{"query":"Bill Evans Waltz for Debby","person":"{name}"}][HW:/emotion:{"emotion":"happy","intensity":0.8}] Great choice! - After user confirms (unknown) →
[HW:/audio/play:{"query":"Bill Evans Waltz for Debby"}][HW:/emotion:{"emotion":"happy","intensity":0.8}] Great choice!
Rules
- All computation stays in
thinking— reply is only the suggestion sentence (with HW markers) orNO_REPLY. - Never mention "cooldown", "interval", "threshold", or timestamps in the reply.
personfield in/audio/playis{name}— the speaker identified in the injected context, lowercase. Omit the field when the context has no identified user (the "(unknown)" cases above). Never copy a name from an example or guess one; unmatched names land in the sharedunknownbucket anyway.- Never open with a greeting. This is an emotion-driven mood event, NOT a presence/arrival event. Forbidden openers:
hello,hi,hey,welcome back,oh, you're back, anything containingagainor referencing the user re-arriving. Greetings belong only topresence.enterinsensing/SKILL.md. - Tone must match the mood. For
Fear→stressedandSad→saddecisions, use thecaringemotion marker and a gentle acknowledging sentence — never cheerful or playful phrasing. If you can't produce a tone-appropriate one-liner, outputNO_REPLY. - Don't reference the camera or detection. No "I noticed you look…", "I can see…", "your face shows…" — speak as if you simply care, not as if you're describing a sensor reading.
Other versions2 projects use a version of this.
Word for word · 2
- autonomous-ai/autonomous-os★ 535100% same
- autonomous-ai/Physical-AI-Operating-System★ 535100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository34 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .cursor/rules/architecture.mdcCursor rule
- .cursor/rules/docs-on-code-change.mdcCursor rule
- .cursor/rules/multi-ide.mdcCursor rule
- .cursor/rules/testing.mdcCursor rule
- agent-managementSkill
- audioSkill
- cameraSkill
- claude-buddySkill
- computer-useSkill
- connectorsSkill
- displaySkill
- emotionSkill
- environmentSkill
- face-enrollSkill
- faqSkill
- guardSkill
- habitSkill
- harness-useSkill
- input-branchingSkill
- led-controlSkill
- moodSkill
- musicSkill
- sceneSkill
- sensingSkill
- sensing-trackSkill
- servo-controlSkill
- servo-trackingSkill
- skill-creatorSkill
- speaker-recognizerSkill
- user-emotion-detectionSkill
- voiceSkill
- wellbeingSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

