@clawhub-harrylabsj-35a31b2850
Help the user close the loop after effort by turning a completed task into a loot table, a fair reward plan, a visible record of the win, and a sensible next...
---
name: loot-reward-celebrator
description: Help the user close the loop after effort by turning a completed task into a loot table, a fair reward plan, a visible record of the win, and a sensible next unlock condition. Use when the user finishes a habit streak, project milestone, exam, or demanding task and needs positive reinforcement that does not sabotage health, money, or long-term goals.
---
# Loot Reward Celebrator
Chinese name: 战利品奖励庆祝.
## Overview
Use this skill when the user has already done the work and now needs a healthy, believable reward loop. It highlights what was actually earned, suggests sustainable celebration options, and keeps the reward proportional.
## When to use
Use this skill when the user wants to:
- celebrate a finished task or milestone
- feel the win instead of instantly moving on
- choose a reward that fits budget and values
- record a success in a visible way
- define the next unlock condition
### Example prompts
- "Help me celebrate this milestone without overdoing it"
- "What is a good reward after finishing this project?"
- "Turn this completed habit streak into a loot table"
## Inputs
Useful inputs include:
- completed task or milestone
- effort, cost, or emotional load
- budget and time
- values or constraints such as health, savings, or family context
- preferred style of celebration
## Workflow
1. Name what was completed.
2. Translate the win into visible and invisible loot.
3. Suggest one immediate reward and one bigger option.
4. Add a way to record or display the win.
5. Set a fair next unlock condition.
## Output
Return markdown with:
- loot table
- reward suggestions
- display or record idea
- next unlock condition
- sustainability guardrails
## Limits
- This skill does not handle purchases or payments.
- It should avoid rewards that directly undermine health, money, or recovery needs.
- It is not financial planning or addiction support.
## Acceptance Criteria
- The reward matches the real effort.
- The celebration is sustainable.
- The output helps the user feel seen without creating future damage.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, List, Tuple
SKILL_SLUG = 'loot-reward-celebrator'
PROMPT_TEMPLATE = """Celebrate a completed task like earned loot.
Name the visible and invisible rewards, suggest one immediate and one richer celebration option, include a way to record the win, and keep the reward aligned with budget and long-term goals."""
GROWTH_MAP = [
('study', ['study', 'essay', 'exam', 'read', 'learn'], 'knowledge, consistency, and follow-through'),
('health', ['exercise', 'walk', 'workout', 'sleep', 'meal', 'run'], 'discipline, body trust, and recovery skills'),
('admin', ['application', 'form', 'paperwork', 'email', 'submit', 'tax'], 'follow-through, tolerance for friction, and completion energy'),
('care', ['family', 'child', 'parent', 'home', 'care'], 'care capacity, patience, and reliability'),
]
def _load_skill_meta(skill_name):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as handle:
content = handle.read()
match = re.search(r'^---\s*\n(.*?)\n---\s*', content, re.DOTALL | re.MULTILINE)
meta = {}
if match:
for line in match.group(1).splitlines():
if ':' in line:
key, value = line.split(':', 1)
meta[key.strip()] = value.strip()
meta['skill_name'] = skill_name or meta.get('name', SKILL_SLUG)
return meta
def _load_prompt_template(skill_name):
return PROMPT_TEMPLATE
def _clean_text(value: Any) -> str:
if value is None:
return ''
if isinstance(value, str):
return value.strip()
if isinstance(value, (int, float)):
return str(value)
return json.dumps(value, ensure_ascii=False)
def _parse_text_map(raw: str) -> Dict[str, str]:
data: Dict[str, str] = {}
for line in raw.splitlines():
if re.search(r'[::]', line):
key, value = re.split(r'[::]', line, maxsplit=1)
normalized = re.sub(r'\s+', '_', key.strip().lower())
data[normalized] = value.strip()
return data
def _coerce_input(user_input: Any) -> Tuple[str, Dict[str, Any]]:
if isinstance(user_input, dict):
return json.dumps(user_input, ensure_ascii=False), dict(user_input)
raw = _clean_text(user_input)
if raw.startswith('{') and raw.endswith('}'):
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
return raw, parsed
except json.JSONDecodeError:
pass
return raw, _parse_text_map(raw)
def _pick(data: Dict[str, Any], keys: List[str], default: str = '') -> str:
for key in keys:
if key in data:
text = _clean_text(data.get(key))
if text:
return text
return default
def _budget_level(budget: str) -> str:
lowered = budget.lower()
if not lowered:
return 'low'
match = re.search(r'(\d+)', lowered)
if match:
amount = int(match.group(1))
if amount <= 30:
return 'low'
if amount <= 120:
return 'medium'
return 'high'
if any(word in lowered for word in ['low', 'tight', 'limited', 'small', 'budget']):
return 'low'
if any(word in lowered for word in ['medium', 'moderate']):
return 'medium'
if any(word in lowered for word in ['high', 'generous']):
return 'high'
return 'low'
def _growth_gain(task: str) -> str:
lowered = task.lower()
for _, words, gain in GROWTH_MAP:
if any(word in lowered for word in words):
return gain
return 'follow-through, proof of effort, and a stronger completion loop'
def _reward_options(task: str, budget_level: str, values: str) -> Tuple[str, str, str, str]:
values_lower = values.lower()
health_guard = any(word in values_lower for word in ['health', 'sleep', 'diet', 'fitness'])
money_guard = any(word in values_lower for word in ['save', 'saving', 'budget', 'money']) or budget_level == 'low'
if money_guard or health_guard:
immediate = 'A low-cost reset, such as favorite tea, a quiet walk, stretching, music, or 20 guilt-free minutes off duty.'
chest = 'A modest ritual reward, such as a library visit, a fresh notebook page, a favorite podcast, or a family shout-out.'
keepsake = 'Record the win with a photo, journal note, sticker, or shared message so the reward is visible without being expensive.'
guardrail = 'Keep the reward restorative and low-cost, and avoid celebrations that create money stress or health backlash.'
return immediate, chest, keepsake, guardrail
if budget_level == 'medium':
immediate = 'A short enjoyable reward you can do today, such as favorite coffee, a long shower, or protected leisure time.'
chest = 'A slightly bigger treat, such as a small purchase, a meaningful outing, or a dedicated hobby block.'
keepsake = 'Mark the milestone in your notes, tracker, or calendar so the win does not disappear.'
guardrail = 'Keep the reward proportional so celebration supports momentum instead of becoming the new source of pressure.'
return immediate, chest, keepsake, guardrail
immediate = 'A satisfying immediate reward, such as a favorite meal, a longer rest block, or an intentional splurge within your limit.'
chest = 'A richer milestone reward, such as a day trip, a meaningful item, or a larger shared celebration.'
keepsake = 'Create a visible milestone record so the reward is not only consumed but also remembered.'
guardrail = 'Even high-budget rewards should protect sleep, recovery, and the next day’s stability.'
return immediate, chest, keepsake, guardrail
def _next_unlock(task: str) -> str:
lowered = task.lower()
if any(word in lowered for word in ['day', 'streak', 'habit', 'daily', 'week']):
return 'Unlock the next chest after the next repeatable streak milestone, not after a random hard day.'
if any(word in lowered for word in ['project', 'milestone', 'essay', 'application', 'submit']):
return 'Unlock the next bigger reward when the next milestone is shipped, not merely planned.'
return 'Set the next unlock condition as one visible repeat of this behavior or the next meaningful milestone.'
def _display_idea(task: str, values: str) -> str:
values_lower = values.lower()
if 'family' in values_lower or 'child' in values_lower:
return 'Share the win with family, add it to a shared board, or let someone celebrate it with you.'
if 'private' in values_lower:
return 'Keep it private but visible with a journal entry, habit tracker mark, or screenshot archive.'
return f'Write a one-line victory log for {task} and store it somewhere you will actually revisit.'
def _build_result(raw: str, data: Dict[str, Any], template: str) -> str:
task = _pick(data, ['completed_task', 'task', 'milestone', 'win'], 'a meaningful completed task')
effort = _pick(data, ['effort', 'cost', 'energy'], 'real effort was invested, even if it looked small from the outside')
mood = _pick(data, ['mood', 'emotion', 'state'], 'ready for a fair celebration')
budget = _pick(data, ['budget', 'reward_budget'], 'low')
values = _pick(data, ['values', 'constraints', 'guardrails'], '')
budget_level = _budget_level(budget)
growth_gain = _growth_gain(task)
immediate, chest, keepsake, guardrail = _reward_options(task, budget_level, values)
display_idea = _display_idea(task, values)
next_unlock = _next_unlock(task)
lines: List[str] = []
lines.append('# Loot Reward Celebration')
lines.append('')
lines.append('## Loot Table')
lines.append(f'- Mission cleared: {task}')
lines.append(f'- Visible reward already earned: progress, relief, and a completed checkpoint linked to {task}.')
lines.append(f'- Invisible growth gained: {growth_gain}.')
lines.append(f'- Proof you earned it: {effort}.')
lines.append(f'- Celebration frame: {template.splitlines()[0]}')
lines.append('')
lines.append('## Reward Suggestions')
lines.append(f'- Immediate drop: {immediate}')
lines.append(f'- Treasure chest option: {chest}')
lines.append(f'- Keepsake loot: {keepsake}')
lines.append(f'- Current mood note: {mood}')
lines.append('')
lines.append('## How to Display the Win')
lines.append(f'- {display_idea}')
lines.append('')
lines.append('## Next Unlock Condition')
lines.append(f'- {next_unlock}')
lines.append('')
lines.append('## Sustainability Guardrails')
lines.append(f'- {guardrail}')
return '\n'.join(lines)
def handle(args):
skill_name = args.get('skill_name', SKILL_SLUG) or SKILL_SLUG
user_input = args.get('input', '')
mode = args.get('mode', 'guide')
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
if mode == 'meta':
return {'result': json.dumps(meta, ensure_ascii=False, indent=2)}
if mode == 'prompt':
return {'result': template}
raw, data = _coerce_input(user_input)
return {'result': _build_result(raw, data, template)}
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_low_budget_health_friendly_reward_is_suggested():
result = handle({
'skill_name': 'loot-reward-celebrator',
'input': {
'completed_task': 'Finished 7 days of exercise',
'effort': 'I showed up even when I was tired',
'budget': '20',
'values': 'health, budget-conscious',
},
})['result']
assert 'Loot Table' in result
assert 'low-cost' in result.lower()
assert 'health backlash' in result.lower()
def test_next_unlock_condition_exists():
result = handle({'skill_name': 'loot-reward-celebrator', 'input': 'Completed task: submitted the application\nBudget: medium'})['result']
assert 'Next Unlock Condition' in result
assert 'submitted the application' in result
def test_prompt_mode_returns_template():
result = handle({'skill_name': 'loot-reward-celebrator', 'mode': 'prompt'})['result']
assert 'earned loot' in result.lower()
assert 'budget' in result.lower()
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Rehearse a real-world conversation by modeling likely counterpart styles such as busy, defensive, friendly, or authority-like NPCs. Generate a natural openin...
---
name: npc-dialogue-rehearser
description: Rehearse a real-world conversation by modeling likely counterpart styles such as busy, defensive, friendly, or authority-like NPCs. Generate a natural opening line, a short branching script, safer alternatives to risky phrases, and a calm closing line. Use when the user wants to ask for help, express a boundary, explain a need, or prepare for a mildly stressful conversation without becoming manipulative.
---
# NPC Dialogue Rehearser
Chinese name: NPC 对话排练.
## Overview
Use this skill when the user wants to practice wording before a real conversation. It keeps the language natural, respects the relationship, and prepares a recovery line when the other person responds in an unexpected way.
## When to use
Use this skill when the user wants to:
- ask for help or accommodation
- express a boundary clearly
- explain a need without sounding vague
- prepare for a mildly tense conversation
- avoid freezing or overexplaining
### Example prompts
- "Help me rehearse how to ask for an extension"
- "Give me a natural script for setting a boundary"
- "What should I say if the other person gets defensive?"
## Inputs
Useful inputs include:
- scenario and counterpart
- goal or request
- taboo areas and worries
- preferred tone, such as gentle, firm, or brief
## Workflow
1. Clarify the real goal.
2. Choose the likely NPC styles.
3. Generate an opening line and branching short script.
4. Swap risky phrases for safer ones.
5. End with a calm closing line.
## Output
Return markdown with:
- dialogue objective
- opening line
- three-branch script
- risky phrase replacements
- closing line
- safety note for high-risk situations when relevant
## Limits
- This skill does not guarantee the real outcome.
- It should not be used for coercion, manipulation, or abusive dynamics.
- Serious legal, medical, violent, or power-abuse situations may require real-world support.
## Acceptance Criteria
- The language sounds natural.
- At least one branch handles an off-script response.
- The script protects the user’s goal without turning manipulative.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, List, Tuple
SKILL_SLUG = 'npc-dialogue-rehearser'
PROMPT_TEMPLATE = """Rehearse a real-world conversation.
Clarify the goal, choose likely counterpart styles, generate a natural opening line, three short response branches, risky phrase replacements, and a calm closing line. Keep the script non-manipulative and relationship-aware."""
HIGH_RISK_WORDS = ['abuse', 'violent', 'violence', 'threat', 'unsafe', 'self-harm', 'medical', 'legal', 'harassment']
NPC_STYLE_RULES = {
'authority': ['teacher', 'boss', 'manager', 'doctor', 'coach', 'principal', 'authority', '家长', '老师', '领导'],
'busy': ['busy', 'rushed', 'time', 'deadline', 'quick', '赶时间', '忙'],
'defensive': ['defensive', 'blame', 'angry', 'upset', 'reject', '误会', '生气', '防御'],
'friendly': ['friend', 'partner', 'classmate', 'family', 'supportive', '朋友', '家人', '同学'],
}
BRANCH_LIBRARY = {
'authority': {
'label': 'Authority NPC',
'npc_line': 'What exactly are you requesting, and why?',
'you_line': 'My request is: {ask}. The reason is: {reason}. I can also work with this fallback: {fallback}.',
'repair': 'If the full request is not possible, what smaller option would you allow?'
},
'busy': {
'label': 'Busy NPC',
'npc_line': 'I only have a minute, what do you need?',
'you_line': 'The short version is: {ask}. The one next step I am asking for is {next_step}.',
'repair': 'If now is a bad time, can we set a specific moment to finish this conversation?'
},
'defensive': {
'label': 'Defensive NPC',
'npc_line': 'Why are you bringing this up now? Are you blaming me?',
'you_line': 'I am not trying to blame you. I want to solve this clearly: {ask}. What matters most is {goal_focus}.',
'repair': 'Let me restate it simply. I am making a request, not attacking you.'
},
'friendly': {
'label': 'Friendly NPC',
'npc_line': 'Okay, tell me more. What would help?',
'you_line': 'Thanks for hearing me out. What would help most is {ask}. Even one small next step would make this easier.',
'repair': 'If that is too much right now, what smaller version feels workable?'
},
}
def _load_skill_meta(skill_name):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as handle:
content = handle.read()
match = re.search(r'^---\s*\n(.*?)\n---\s*', content, re.DOTALL | re.MULTILINE)
meta = {}
if match:
for line in match.group(1).splitlines():
if ':' in line:
key, value = line.split(':', 1)
meta[key.strip()] = value.strip()
meta['skill_name'] = skill_name or meta.get('name', SKILL_SLUG)
return meta
def _load_prompt_template(skill_name):
return PROMPT_TEMPLATE
def _clean_text(value: Any) -> str:
if value is None:
return ''
if isinstance(value, str):
return value.strip()
if isinstance(value, (int, float)):
return str(value)
return json.dumps(value, ensure_ascii=False)
def _parse_text_map(raw: str) -> Dict[str, str]:
data: Dict[str, str] = {}
for line in raw.splitlines():
if re.search(r'[::]', line):
key, value = re.split(r'[::]', line, maxsplit=1)
normalized = re.sub(r'\s+', '_', key.strip().lower())
data[normalized] = value.strip()
return data
def _coerce_input(user_input: Any) -> Tuple[str, Dict[str, Any]]:
if isinstance(user_input, dict):
return json.dumps(user_input, ensure_ascii=False), dict(user_input)
raw = _clean_text(user_input)
if raw.startswith('{') and raw.endswith('}'):
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
return raw, parsed
except json.JSONDecodeError:
pass
return raw, _parse_text_map(raw)
def _pick(data: Dict[str, Any], keys: List[str], default: str = '') -> str:
for key in keys:
if key in data:
text = _clean_text(data.get(key))
if text:
return text
return default
def _detect_primary_style(counterpart: str, scenario: str, worry: str) -> str:
combined = f'{counterpart} {scenario} {worry}'.lower()
for style, words in NPC_STYLE_RULES.items():
if any(word in combined for word in words):
return style
return 'busy'
def _branch_order(primary_style: str) -> List[str]:
if primary_style == 'authority':
return ['authority', 'busy', 'defensive']
if primary_style == 'friendly':
return ['friendly', 'busy', 'defensive']
if primary_style == 'defensive':
return ['defensive', 'busy', 'friendly']
return ['busy', 'friendly', 'defensive']
def _opening_line(goal: str, tone: str) -> str:
tone_lower = tone.lower()
if 'firm' in tone_lower or 'direct' in tone_lower:
return f'I want to be clear and respectful. My request is {goal}.'
if 'brief' in tone_lower or 'short' in tone_lower:
return f'Quick version: I am hoping to {goal}.'
return f'I want to say this clearly and calmly. I am hoping to {goal}.'
def _risk_swaps(goal: str) -> List[Tuple[str, str]]:
return [
('"You never listen."', '"I want to make sure my point is landing clearly."'),
('"Fine, whatever."', '"I want to be honest about what would help here."'),
(f'"You have to {goal}."', f'"My request is {goal}, and I want to see what is workable."'),
]
def _safety_note(raw: str) -> str:
lowered = raw.lower()
if any(word in lowered for word in HIGH_RISK_WORDS):
return 'This sounds higher risk than a normal rehearsal. Consider real-world support, documentation, or professional help before relying on phrasing alone.'
return ''
def _build_result(raw: str, data: Dict[str, Any], template: str) -> str:
scenario = _pick(data, ['scenario', 'context'], 'A conversation the user wants to handle better')
counterpart = _pick(data, ['counterpart', 'person', 'other_person'], 'the other person')
goal = _pick(data, ['goal', 'request', 'ask'], 'make a clear request')
taboo = _pick(data, ['taboo', 'avoid', 'sensitive'], 'Avoid blame, pressure, and manipulative framing.')
worry = _pick(data, ['worry', 'fear', 'concern'], 'The other person may react in a way that makes the user freeze or over-explain.')
tone = _pick(data, ['tone', 'style'], 'gentle and direct')
primary_style = _detect_primary_style(counterpart, scenario, worry)
branches = _branch_order(primary_style)
reason = scenario if scenario else 'it would reduce friction and confusion'
fallback = 'one smaller step, a later time, or a partial yes'
next_step = 'one clear yes, no, or alternative'
goal_focus = goal
safety_note = _safety_note(f'{raw} {scenario} {counterpart} {worry}')
lines: List[str] = []
lines.append('# NPC Dialogue Rehearsal')
lines.append('')
lines.append('## Dialogue Objective')
lines.append(f'- Scenario: {scenario}')
lines.append(f'- Counterpart: {counterpart}')
lines.append(f'- Goal: {goal}')
lines.append(f'- Tone: {tone}')
lines.append(f'- Guardrail: {taboo}')
lines.append(f'- Rehearsal frame: {template.splitlines()[0]}')
lines.append('')
lines.append('## Opening Line')
lines.append(f'- {_opening_line(goal, tone)}')
lines.append('')
lines.append('## Three-Branch Script')
for style in branches:
branch = BRANCH_LIBRARY[style]
lines.append(f"### {branch['label']}")
lines.append(f"- Likely reply: {branch['npc_line']}")
lines.append(f"- Your next line: {branch['you_line'].format(ask=goal, reason=reason, fallback=fallback, next_step=next_step, goal_focus=goal_focus)}")
lines.append(f"- Repair line: {branch['repair']}")
lines.append('')
lines.append('## Risky Phrase Swaps')
for risky, safer in _risk_swaps(goal):
lines.append(f'- Risky: {risky}')
lines.append(f' - Better: {safer}')
lines.append('')
lines.append('## Closing Line')
lines.append('- Thanks for hearing me out. Let’s confirm the next step so I know how to proceed.')
if safety_note:
lines.append('')
lines.append('## Safety Note')
lines.append(f'- {safety_note}')
return '\n'.join(lines)
def handle(args):
skill_name = args.get('skill_name', SKILL_SLUG) or SKILL_SLUG
user_input = args.get('input', '')
mode = args.get('mode', 'guide')
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
if mode == 'meta':
return {'result': json.dumps(meta, ensure_ascii=False, indent=2)}
if mode == 'prompt':
return {'result': template}
raw, data = _coerce_input(user_input)
return {'result': _build_result(raw, data, template)}
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_authority_branch_is_selected_for_teacher_case():
result = handle({
'skill_name': 'npc-dialogue-rehearser',
'input': {
'scenario': 'I need to ask for a two-day extension on an essay.',
'counterpart': 'teacher',
'goal': 'request a two-day extension',
'worry': 'They may think I am making excuses.',
'tone': 'respectful and direct',
},
})['result']
assert 'Authority NPC' in result
assert 'Opening Line' in result
assert 'request a two-day extension' in result
def test_safety_note_appears_for_high_risk_language():
result = handle({
'skill_name': 'npc-dialogue-rehearser',
'input': {
'scenario': 'I need wording for an abusive partner who has become threatening.',
'counterpart': 'partner',
'goal': 'state a boundary',
},
})['result']
assert 'Safety Note' in result
def test_prompt_mode_returns_template():
result = handle({'skill_name': 'npc-dialogue-rehearser', 'mode': 'prompt'})['result']
assert 'conversation' in result.lower()
assert 'three short response branches' in result
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Sort clutter, obligations, files, objects, or attention drains through a game inventory lens. Classify what should stay equipped, stay in quick access, move...
---
name: game-inventory-manager
description: Sort clutter, obligations, files, objects, or attention drains through a game inventory lens. Classify what should stay equipped, stay in quick access, move to storage, be sold or delegated, be discarded or archived, or be combined into higher-value batches. Use when the user feels overloaded by too many items, tasks, or commitments and needs a practical first cleanup move.
---
# Game Inventory Manager
Chinese name: 游戏背包整理.
## Overview
Use this skill to reduce complexity instead of creating a prettier pile. It helps the user classify what deserves active attention, what should be stored, and what is only occupying scarce space or energy.
## When to use
Use this skill when the user wants to:
- declutter physical or digital items
- triage too many tasks or obligations
- reduce low-value attention drains
- decide what to keep, park, combine, delegate, or remove
- find one 10 to 20 minute cleanup action
### Example prompts
- "Sort this messy to-do list like a game inventory"
- "Help me decide what stays in active use and what should leave"
- "I feel overloaded by files, errands, and random obligations"
## Inputs
Useful inputs include:
- list of items, tasks, files, or commitments
- what feels heavy, urgent, or emotionally sticky
- value, frequency of use, and replacement cost
- time or energy constraints
## Workflow
1. Review the current inventory.
2. Sort items into equipped, quick access, storage, sell or delegate, discard or archive, and craft or combine.
3. Explain the sorting logic.
4. Pick the highest-value slot to clear first.
5. End with one short cleanup action.
## Output
Return markdown with:
- backpack status overview
- sorted inventory by category
- first slot to clear
- craft suggestions
- one cleanup move under 20 minutes
## Limits
- This skill does not delete files, move objects, or spend money automatically.
- It should not force emotionally loaded items into an immediate discard decision.
- Mixed physical and digital clutter may need separate passes.
## Acceptance Criteria
- The output reduces complexity.
- The classification logic is visible.
- At least one realistic cleanup action fits inside 10 to 20 minutes.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, List, Tuple
SKILL_SLUG = 'game-inventory-manager'
PROMPT_TEMPLATE = """Sort the user's clutter like a game inventory.
Classify items into equipped, quick access, storage, sell or delegate, discard or archive, and craft or combine. Explain the logic and end with one cleanup move that fits inside 10 to 20 minutes."""
CATEGORY_LABELS = [
('equip', 'Equipped Now', 'High current value or direct support for active goals.'),
('quick', 'Quick Access', 'Useful often enough to keep near the surface.'),
('storage', 'Storage', 'Worth keeping, but not worth active attention right now.'),
('sell', 'Sell / Delegate', 'Still has value, but should leave your active backpack.'),
('discard', 'Discard / Archive', 'Expired, duplicate, broken, or no longer worth carrying.'),
('craft', 'Craft / Combine', 'Fragments that become more valuable if batched or merged.'),
]
KEYWORDS = {
'discard': ['expired', 'broken', 'duplicate', 'trash', 'stale', 'useless', 'old', 'delete', 'empty', 'outdated'],
'sell': ['sell', 'donate', 'delegate', 'outsource', 'return', 'swap', 'extra', 'loan'],
'craft': ['draft', 'note', 'idea', 'receipt', 'snippet', 'fragment', 'batch', 'merge', 'combine', 'outline'],
'equip': ['daily', 'urgent', 'must', 'current', 'active', 'important', 'health', 'today', 'key', 'passport', 'charger'],
'quick': ['weekly', 'often', 'favorite', 'routine', 'template', 'reference'],
'storage': ['seasonal', 'backup', 'archive', 'later', 'someday', 'memory', 'keepsake'],
}
def _load_skill_meta(skill_name):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as handle:
content = handle.read()
match = re.search(r'^---\s*\n(.*?)\n---\s*', content, re.DOTALL | re.MULTILINE)
meta = {}
if match:
for line in match.group(1).splitlines():
if ':' in line:
key, value = line.split(':', 1)
meta[key.strip()] = value.strip()
meta['skill_name'] = skill_name or meta.get('name', SKILL_SLUG)
return meta
def _load_prompt_template(skill_name):
return PROMPT_TEMPLATE
def _clean_text(value: Any) -> str:
if value is None:
return ''
if isinstance(value, str):
return value.strip()
if isinstance(value, (int, float)):
return str(value)
return json.dumps(value, ensure_ascii=False)
def _parse_text_map(raw: str) -> Dict[str, str]:
data: Dict[str, str] = {}
for line in raw.splitlines():
if re.search(r'[::]', line):
key, value = re.split(r'[::]', line, maxsplit=1)
normalized = re.sub(r'\s+', '_', key.strip().lower())
data[normalized] = value.strip()
return data
def _coerce_input(user_input: Any) -> Tuple[str, Dict[str, Any]]:
if isinstance(user_input, dict):
return json.dumps(user_input, ensure_ascii=False), dict(user_input)
raw = _clean_text(user_input)
if raw.startswith('{') and raw.endswith('}'):
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
return raw, parsed
except json.JSONDecodeError:
pass
return raw, _parse_text_map(raw)
def _split_items(value: Any) -> List[str]:
if isinstance(value, list):
return [_clean_text(item) for item in value if _clean_text(item)]
text = _clean_text(value)
if not text:
return []
items = [re.sub(r'^[\-•\d.\s]+', '', part).strip() for part in re.split(r'[\n,;,;]+', text)]
return [item for item in items if item]
def _extract_items(raw: str, data: Dict[str, Any]) -> List[str]:
for key in ['items', 'objects', 'tasks', 'scope', 'list']:
if key in data:
items = _split_items(data[key])
if items:
return items
raw_items = _split_items(raw)
if raw_items:
return raw_items
return ['current obligations', 'stored clutter', 'unfinished fragments']
def _classify_item(item: str) -> Tuple[str, str]:
lowered = item.lower()
for category in ['discard', 'sell', 'craft', 'equip', 'quick', 'storage']:
for word in KEYWORDS[category]:
if word in lowered:
if category == 'discard':
return category, 'low value, duplicate, expired, or no longer functional'
if category == 'sell':
return category, 'valuable enough to leave through delegation, donation, or sale'
if category == 'craft':
return category, 'fragmented material that becomes better when merged into one batch'
if category == 'equip':
return category, 'actively supports today or a live priority'
if category == 'quick':
return category, 'used often enough to stay near the surface'
if category == 'storage':
return category, 'worth keeping, but not worth active attention right now'
if len(item.split()) <= 2:
return 'quick', 'small, reusable item that may deserve easy access'
return 'storage', 'not urgent enough for active loadout, but not obviously discardable'
def _bucket_items(items: List[str]) -> Dict[str, List[Tuple[str, str]]]:
buckets: Dict[str, List[Tuple[str, str]]] = {name: [] for name, _, _ in CATEGORY_LABELS}
for item in items:
category, reason = _classify_item(item)
buckets[category].append((item, reason))
return buckets
def _biggest_load(buckets: Dict[str, List[Tuple[str, str]]]) -> str:
ordered = sorted(buckets.items(), key=lambda pair: len(pair[1]), reverse=True)
top_name, top_items = ordered[0]
label = next(label for name, label, _ in CATEGORY_LABELS if name == top_name)
return f'{label} ({len(top_items)} item(s))'
def _first_slot_to_clear(buckets: Dict[str, List[Tuple[str, str]]]) -> str:
priority = ['discard', 'sell', 'craft', 'quick', 'storage', 'equip']
for category in priority:
if buckets[category]:
label = next(label for name, label, _ in CATEGORY_LABELS if name == category)
target = buckets[category][0][0]
return f'Start with **{label}** by processing `{target}` first. That unlocks visible relief fastest.'
return 'Start with the smallest visible category and clear one complete slot.'
def _craft_suggestions(buckets: Dict[str, List[Tuple[str, str]]]) -> List[str]:
suggestions: List[str] = []
if len(buckets['craft']) >= 2:
craft_names = ', '.join(item for item, _ in buckets['craft'][:3])
suggestions.append(f'Combine fragmented materials into one named batch, for example: {craft_names}.')
if len(buckets['sell']) >= 2:
suggestions.append('Create one delegate, donate, or resale batch instead of deciding item by item.')
if len(buckets['quick']) >= 3:
suggestions.append('Move truly recurring items into one quick-access list so they stop resurfacing as random reminders.')
if not suggestions:
suggestions.append('If no obvious combination exists, create one short “process later” batch so your backpack stops carrying loose fragments.')
return suggestions
def _cleanup_move(buckets: Dict[str, List[Tuple[str, str]]]) -> str:
if buckets['discard']:
return 'Set a 15-minute timer and clear the discard or archive pile completely before touching anything else.'
if buckets['sell']:
return 'Spend 15 minutes making one delegate, donate, or resale batch with a single exit plan.'
if buckets['craft']:
return 'Use 15 minutes to merge the top fragments into one named checklist, folder, or note.'
return 'Use 10 minutes to move active items into one visible quick-access slot and park the rest.'
def _build_result(raw: str, data: Dict[str, Any], template: str) -> str:
items = _extract_items(raw, data)
buckets = _bucket_items(items)
friction_candidates = [item for category in ['discard', 'sell', 'craft'] for item, _ in buckets[category]][:3]
lines: List[str] = []
lines.append('# Game Inventory Review')
lines.append('')
lines.append('## Backpack Status')
lines.append(f'- Total items reviewed: {len(items)}')
lines.append(f'- Biggest current load: {_biggest_load(buckets)}')
lines.append(f'- Sorting frame: {template.splitlines()[0]}')
if friction_candidates:
lines.append(f"- Most attention-draining items: {', '.join(friction_candidates)}")
else:
lines.append('- Most attention-draining items: No obvious clutter sink was stated, so start with whichever slot feels heaviest.')
lines.append('')
lines.append('## Sorted Inventory')
for name, label, summary in CATEGORY_LABELS:
lines.append(f'### {label}')
if buckets[name]:
for item, reason in buckets[name]:
lines.append(f'- {item} — {reason}')
else:
lines.append('- No clear items in this slot right now.')
lines.append(f'- Why this slot exists: {summary}')
lines.append('')
lines.append('## First Slot to Clear')
lines.append(f'- {_first_slot_to_clear(buckets)}')
lines.append('')
lines.append('## Craft Suggestions')
for suggestion in _craft_suggestions(buckets):
lines.append(f'- {suggestion}')
lines.append('')
lines.append('## 15-Minute Cleanup Move')
lines.append(f'- {_cleanup_move(buckets)}')
return '\n'.join(lines)
def handle(args):
skill_name = args.get('skill_name', SKILL_SLUG) or SKILL_SLUG
user_input = args.get('input', '')
mode = args.get('mode', 'guide')
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
if mode == 'meta':
return {'result': json.dumps(meta, ensure_ascii=False, indent=2)}
if mode == 'prompt':
return {'result': template}
raw, data = _coerce_input(user_input)
return {'result': _build_result(raw, data, template)}
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_inventory_sections_and_classification():
result = handle({
'skill_name': 'game-inventory-manager',
'input': {
'items': ['daily planner', 'expired coupons', 'duplicate cable', 'draft notes about summer plan']
},
})['result']
assert 'Equipped Now' in result
assert 'Discard / Archive' in result
assert 'expired coupons' in result
assert 'draft notes about summer plan' in result
def test_cleanup_move_exists():
result = handle({'skill_name': 'game-inventory-manager', 'input': 'Items: broken charger, weekly checklist, seasonal coat'})['result']
assert '15-Minute Cleanup Move' in result
assert 'broken charger' in result
def test_prompt_mode_returns_template():
result = handle({'skill_name': 'game-inventory-manager', 'mode': 'prompt'})['result']
assert 'inventory' in result.lower()
assert '10 to 20 minutes' in result
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Break a fuzzy, high-friction goal into a sequenced quest chain with prerequisite nodes, parallel branches, waiting points, Definition of Done, and the first...
---
name: quest-chain-decomposer
description: Break a fuzzy, high-friction goal into a sequenced quest chain with prerequisite nodes, parallel branches, waiting points, Definition of Done, and the first three actions. Use when the user knows the destination but cannot see the first move, keeps stalling mid-project, or needs a practical task map for study, home, admin, or personal projects.
---
# Quest Chain Decomposer
Chinese name: 任务链分解器.
## Overview
Use this skill to turn a large or messy objective into a practical quest chain. It keeps the first step small, shows dependencies, and avoids fake progress such as endless research without a deliverable.
## When to use
Use this skill when the user wants to:
- break a goal into a realistic sequence
- identify prerequisite, parallel, and waiting nodes
- define what “done” means for each step
- start today instead of circling the task
- re-route after blockers appear
### Example prompts
- "Break this project into a quest chain"
- "I know what I want, but I cannot find the first step"
- "Map the dependencies for this admin task"
## Inputs
Useful inputs include:
- goal or deliverable
- deadline or time window
- success definition
- resources and constraints
- blockers, approvals, or handoffs
## Workflow
1. Clarify the finish line.
2. Sort work into main quest, prerequisite nodes, parallel branches, and waiting points.
3. Give each step a clear Definition of Done.
4. Make the first move small enough to start immediately.
5. Add reroute rules for common stalls.
## Output
Return markdown with:
- main quest objective
- quest chain map
- Definition of Done by step
- starter trio
- reroute rules
## Limits
- This skill does not replace full project management software.
- For large cross-team schedules, it should stay at the planning level.
- If the goal itself is unclear, define the mission first before decomposing it.
## Acceptance Criteria
- The first quest is small enough to start now.
- Dependencies are visible.
- Each step has an explicit done line.
- The plan points toward a real deliverable, not just thinking about the work.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, List, Tuple
SKILL_SLUG = 'quest-chain-decomposer'
PROMPT_TEMPLATE = """Turn a fuzzy goal into a quest chain.
Focus on the finish line, prerequisite and waiting nodes, explicit Definition of Done, a starter trio that can begin today, and reroute rules when blockers appear."""
CATEGORY_RULES = {
'learning': ['learn', 'study', 'exam', 'course', 'practice', 'essay', 'homework', '读', '学', '考试', '作业'],
'admin': ['apply', 'application', 'form', 'document', 'visa', 'register', 'appointment', 'tax', '申请', '材料', '报名'],
'organize': ['clean', 'organize', 'declutter', 'sort', 'pack', '整理', '收纳', '家务'],
}
CATEGORY_BLUEPRINTS = {
'learning': [
{
'title': 'Quest 1, define the learning win',
'objective': 'Turn the goal into one visible proof of learning for {goal}.',
'dod': 'A concrete proof exists, such as one solved set, one outline, one mock answer, or one finished page.',
'trap': 'Staying in broad research mode instead of choosing the smallest proof.',
},
{
'title': 'Quest 2, gather only the minimum materials',
'objective': 'Pick the exact sources, examples, or tools needed for the first learning sprint.',
'dod': 'The minimum study pack is listed and opened, with obvious extras parked for later.',
'trap': 'Collecting ten resources when two would unlock the next action.',
},
{
'title': 'Quest 3, produce a first practice artifact',
'objective': 'Create the first draft, problem set, recap note, or rehearsal artifact.',
'dod': 'A rough but real artifact exists and can be reviewed.',
'trap': 'Waiting to feel ready before attempting a first pass.',
},
{
'title': 'Quest 4, review gaps and ship the next version',
'objective': 'Check weak spots, patch the highest-value gap, and submit or store the final version.',
'dod': 'The deliverable is submitted, stored, or scheduled for the next iteration with one clear follow-up.',
'trap': 'Polishing forever instead of closing the loop.',
},
],
'admin': [
{
'title': 'Quest 1, define the exact requirement',
'objective': 'Name what must be submitted, confirmed, or approved for {goal}.',
'dod': 'A one-line requirement summary exists with deadline and owner.',
'trap': 'Starting forms before knowing the finish condition.',
},
{
'title': 'Quest 2, gather required documents and facts',
'objective': 'Collect IDs, receipts, screenshots, contact details, and missing facts.',
'dod': 'All must-have materials are ready or each missing item has an owner and follow-up date.',
'trap': 'Opening the submission flow with missing proof.',
},
{
'title': 'Quest 3, complete the submission path',
'objective': 'Fill the form, send the email, book the slot, or file the request.',
'dod': 'The request is fully submitted and a confirmation artifact exists.',
'trap': 'Leaving the process half-finished without proof.',
},
{
'title': 'Quest 4, confirm and archive the result',
'objective': 'Verify status, record the confirmation, and set the next follow-up if needed.',
'dod': 'Status is checked and the proof is stored in an easy-to-find place.',
'trap': 'Assuming it is done before receiving confirmation.',
},
],
'organize': [
{
'title': 'Quest 1, define the done line',
'objective': 'Choose the exact space, list, or category that counts as complete for {goal}.',
'dod': 'One bounded scope is selected, such as one shelf, one folder, or one drawer.',
'trap': 'Trying to reorganize the whole world at once.',
},
{
'title': 'Quest 2, sort by category and friction',
'objective': 'Group items into keep, move, process, and discard buckets.',
'dod': 'Every item in the current scope has a temporary category.',
'trap': 'Rearranging clutter without deciding what it is.',
},
{
'title': 'Quest 3, clear the highest-friction batch',
'objective': 'Finish the most valuable processing batch first.',
'dod': 'One real area is visibly clearer and a follow-up batch is queued.',
'trap': 'Stopping after sorting without completing a batch.',
},
{
'title': 'Quest 4, reset and maintain',
'objective': 'Label where things go and define a small maintenance rule.',
'dod': 'The space is reset and one rule exists to stop the clutter from respawning.',
'trap': 'Creating a one-day clean space with no maintenance rule.',
},
],
'general': [
{
'title': 'Quest 1, define the mission',
'objective': 'Describe the smallest visible outcome that counts as success for {goal}.',
'dod': 'The mission has one sentence, one owner, and one visible finish line.',
'trap': 'Keeping the goal abstract or motivational instead of concrete.',
},
{
'title': 'Quest 2, unlock prerequisites',
'objective': 'List missing inputs, approvals, tools, and decisions needed before execution.',
'dod': 'Every obvious blocker is either resolved, delegated, or parked as a waiting node.',
'trap': 'Mistaking dependency confusion for lack of motivation.',
},
{
'title': 'Quest 3, build the first shippable piece',
'objective': 'Produce the first usable draft, checklist, or working version.',
'dod': 'A real artifact exists and can be reviewed by you or someone else.',
'trap': 'Thinking about the work longer than it takes to create version one.',
},
{
'title': 'Quest 4, review and close the loop',
'objective': 'Check quality, submit, communicate status, and define the next checkpoint if needed.',
'dod': 'The artifact is shipped or parked with a precise next step and date.',
'trap': 'Letting a near-finished task drift in limbo.',
},
],
}
STARTER_ACTIONS = {
'learning': [
'Write one sentence that defines the exact proof of learning you want today.',
'Open the minimum source set and close unrelated tabs or materials.',
'Create the first rough artifact immediately, even if it is incomplete.',
],
'admin': [
'Write the exact submission or approval you need in one line.',
'List the must-have documents and mark which one is still missing.',
'Start the form, draft email, or booking flow until the next real blocker appears.',
],
'organize': [
'Choose one bounded zone instead of the whole category.',
'Set out four temporary buckets: keep, move, process, discard.',
'Clear one visible batch completely before touching the next area.',
],
'general': [
'Write the finish line in one sentence that someone else could verify.',
'List the top dependency or uncertainty blocking action.',
'Create the first real artifact, even if it is only version 0.1.',
],
}
def _load_skill_meta(skill_name):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as handle:
content = handle.read()
match = re.search(r'^---\s*\n(.*?)\n---\s*', content, re.DOTALL | re.MULTILINE)
meta = {}
if match:
for line in match.group(1).splitlines():
if ':' in line:
key, value = line.split(':', 1)
meta[key.strip()] = value.strip()
meta['skill_name'] = skill_name or meta.get('name', SKILL_SLUG)
return meta
def _load_prompt_template(skill_name):
return PROMPT_TEMPLATE
def _clean_text(value: Any) -> str:
if value is None:
return ''
if isinstance(value, str):
return value.strip()
if isinstance(value, (int, float)):
return str(value)
return json.dumps(value, ensure_ascii=False)
def _parse_text_map(raw: str) -> Dict[str, str]:
data: Dict[str, str] = {}
for line in raw.splitlines():
if re.search(r'[::]', line):
key, value = re.split(r'[::]', line, maxsplit=1)
normalized = re.sub(r'\s+', '_', key.strip().lower())
data[normalized] = value.strip()
return data
def _coerce_input(user_input: Any) -> Tuple[str, Dict[str, Any]]:
if isinstance(user_input, dict):
return json.dumps(user_input, ensure_ascii=False), dict(user_input)
raw = _clean_text(user_input)
if raw.startswith('{') and raw.endswith('}'):
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
return raw, parsed
except json.JSONDecodeError:
pass
return raw, _parse_text_map(raw)
def _pick(data: Dict[str, Any], keys: List[str], default: str = '') -> str:
for key in keys:
if key in data:
text = _clean_text(data.get(key))
if text:
return text
return default
def _first_sentence(raw: str) -> str:
cleaned = raw.strip()
if not cleaned:
return ''
parts = re.split(r'[\n。.!?!?]', cleaned)
for part in parts:
part = part.strip(' -•')
if part:
return part
return ''
def _split_values(value: Any) -> List[str]:
if isinstance(value, list):
return [_clean_text(item) for item in value if _clean_text(item)]
text = _clean_text(value)
if not text:
return []
items = [re.sub(r'^[\-•\d.\s]+', '', part).strip() for part in re.split(r'[\n,;,;]+', text)]
return [item for item in items if item]
def _detect_category(goal: str, raw: str) -> str:
combined = f'{goal} {raw}'.lower()
for category, words in CATEGORY_RULES.items():
if any(word in combined for word in words):
return category
return 'general'
def _waiting_node(blockers: List[str]) -> str:
lowered = ' '.join(blockers).lower()
if any(word in lowered for word in ['wait', 'waiting', 'approval', 'reply', 'feedback', 'teacher', 'review', '审批', '回复', '配合']):
return 'Waiting node: track the external dependency, set a follow-up date, and define parallel work that can continue while you wait.'
if blockers:
return 'Blocker node: turn each blocker into either a resolved task, a delegated task, or a clearly parked item with the next check date.'
return ''
def _build_result(raw: str, data: Dict[str, Any], template: str) -> str:
goal = _pick(data, ['goal', 'objective', 'project', 'task', 'mission'], _first_sentence(raw) or 'Complete the current goal')
deadline = _pick(data, ['deadline', 'due', 'timebox', 'date'], 'No explicit deadline provided')
success_definition = _pick(
data,
['success_definition', 'success', 'definition_of_done', 'done', 'deliverable'],
f'A visible, reviewable deliverable exists for {goal}.',
)
resources = _split_values(data.get('resources', data.get('resource', '')))
blockers = _split_values(data.get('blockers', data.get('blocker', data.get('constraints', ''))))
category = _detect_category(goal, raw)
steps = CATEGORY_BLUEPRINTS[category]
starter_actions = STARTER_ACTIONS[category]
waiting_note = _waiting_node(blockers)
lines: List[str] = []
lines.append('# Quest Chain Map')
lines.append('')
lines.append('## Main Quest Objective')
lines.append(f'- Goal: {goal}')
lines.append(f'- Success definition: {success_definition}')
lines.append(f'- Deadline or timebox: {deadline}')
lines.append(f'- Planning frame: {template.splitlines()[0]}')
if resources:
lines.append(f"- Useful resources: {', '.join(resources)}")
else:
lines.append('- Useful resources: Start with the minimum tools needed for the next artifact.')
if blockers:
lines.append(f"- Visible blockers: {', '.join(blockers)}")
else:
lines.append('- Visible blockers: None stated, so treat hidden ambiguity as the first thing to test.')
lines.append('')
lines.append('## Quest Chain Map')
for index, step in enumerate(steps, 1):
depends = 'None' if index == 1 else f'Quest {index - 1}'
lines.append(f"{index}. **{step['title']}**")
lines.append(f' - Depends on: {depends}')
lines.append(f" - Objective: {step['objective'].format(goal=goal)}")
lines.append(f" - Failure trap: {step['trap']}")
lines.append('- Parallel branch: Prepare support material, checklist, or follow-up messages while the main chain is moving.')
if waiting_note:
lines.append(f'- {waiting_note}')
lines.append('')
lines.append('## Definition of Done by Step')
for step in steps:
lines.append(f"- **{step['title']}**: {step['dod']}")
lines.append('')
lines.append('## Starter Trio')
for index, action in enumerate(starter_actions, 1):
lines.append(f'{index}. {action}')
lines.append('')
lines.append('## Reroute Rules')
lines.append('- If the goal is still fuzzy after Quest 1, pause decomposition and rewrite the mission as one visible deliverable.')
lines.append('- If an external dependency stalls the chain, move it into a waiting node with a date, owner, and parallel fallback task.')
lines.append('- If you keep researching instead of producing, force the next action to create a draft, checklist, or proof artifact in under 25 minutes.')
lines.append('- If energy drops, keep the main quest but shrink the next step instead of inventing a brand-new plan.')
return '\n'.join(lines)
def handle(args):
skill_name = args.get('skill_name', SKILL_SLUG) or SKILL_SLUG
user_input = args.get('input', '')
mode = args.get('mode', 'guide')
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
if mode == 'meta':
return {'result': json.dumps(meta, ensure_ascii=False, indent=2)}
if mode == 'prompt':
return {'result': template}
raw, data = _coerce_input(user_input)
return {'result': _build_result(raw, data, template)}
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_goal_and_waiting_node_are_rendered():
result = handle({
'skill_name': 'quest-chain-decomposer',
'input': {
'goal': 'Submit the school volunteer application',
'deadline': 'Friday',
'success_definition': 'The application is submitted with all supporting documents.',
'blockers': 'Waiting for teacher approval',
},
})['result']
assert 'Submit the school volunteer application' in result
assert 'Starter Trio' in result
assert 'Waiting node' in result
def test_prompt_mode_returns_template():
result = handle({'skill_name': 'quest-chain-decomposer', 'mode': 'prompt'})['result']
assert 'quest chain' in result.lower()
assert 'Definition of Done' in result
def test_string_input_is_supported():
result = handle({'skill_name': 'quest-chain-decomposer', 'input': 'Goal: finish my tax form\nDeadline: this weekend\nBlockers: missing receipt'})['result']
assert '# Quest Chain Map' in result
assert 'finish my tax form' in result.lower()
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Transform upcoming high-stakes events into boss battles with countdown phases, preparation checkpoints, risk warnings, and recovery plans. Use when the user...
---
name: boss-battle-calendar
description: Transform upcoming high-stakes events into boss battles with countdown phases, preparation checkpoints, risk warnings, and recovery plans. Use when the user needs to prepare for exams, trips, deadlines, meetings, or other date-driven events.
---
# Boss Battle Calendar
Chinese name: BOSS 战日历
## Purpose
Reframe important upcoming events as boss battles so the user prepares early, sees risk clearly, and plans recovery after the fight.
This skill is descriptive only. It does not create reminders, buy tickets, or operate external calendar systems.
## Use this skill when
- The user has an exam, trip, deadline, meeting, or other important dated event.
- The user keeps waiting until the deadline feels urgent.
- The user wants a countdown plan with checkpoints instead of vague anxiety.
- The user needs to manage multiple high-pressure events in one period.
## Inputs to collect
- Important event name
- Date or time window
- Stakes or impact level
- Current preparation status
- Key risks or likely failure points
- Recovery needs after the event
## Workflow
1. Collect important events for the next week to three months, including timing, stakes, risk, and preparation level.
2. Classify them as main boss fights, elite enemies, or multi-stage campaigns.
3. Split each event into scouting, preparation, battle, and recovery phases with checkpoints.
4. Build the countdown rhythm, key prep actions, risk warnings, and fallback actions.
5. Add a post-battle recovery and review plan so the user does not crash after the event.
## Output Format
- Boss list with name, date, danger level, and likely HP-loss points.
- Phase timeline with what should happen in each stage.
- Specific risk warnings plus recovery or backup actions.
- Post-battle recovery, review, and loot-processing notes.
## Quality bar
- Always show a timeline, not just abstract advice.
- Cover preparation and recovery, not only the deadline day.
- Risk warnings must be concrete, such as materials, energy, communication, transport, or missing information.
- If several events collide, show priority and resource conflict handling.
## Edge cases and limits
- If the date is uncertain, switch to a battle-window plan instead of pretending precision.
- If multiple boss fights overlap, show prioritization and capacity tradeoffs.
- Do not position this skill as a replacement for real reminders, ticketing, registration, or execution systems.
## Compatibility notes
- Works for exams, family logistics, travel prep, important meetings, and project milestones.
- Can pair conceptually with daily-dungeon-challenger for daily execution.
- Text only, suitable for planning before manual entry into other tools.
FILE:handler.py
import json
import re
from pathlib import Path
SKILL_DIR = Path(__file__).resolve().parent
SKILL_FILE = SKILL_DIR / "SKILL.md"
def _read_skill_file():
return SKILL_FILE.read_text(encoding="utf-8")
def _split_frontmatter(content):
match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", content, re.DOTALL)
if match:
return match.group(1), match.group(2)
return "", content
def _parse_frontmatter(frontmatter):
meta = {}
for line in frontmatter.splitlines():
line = line.strip()
if not line or ":" not in line:
continue
key, value = line.split(":", 1)
meta[key.strip()] = value.strip().strip('"').strip("'")
return meta
def _load_skill_meta(skill_name):
content = _read_skill_file()
frontmatter, _ = _split_frontmatter(content)
parsed = _parse_frontmatter(frontmatter)
return {
"name": parsed.get("name", skill_name or SKILL_DIR.name),
"description": parsed.get("description", ""),
}
def _load_prompt_template(skill_name):
del skill_name
content = _read_skill_file()
_, body = _split_frontmatter(content)
return body.strip()
def _extract_title(body):
match = re.search(r"^#\s+(.+)$", body, re.MULTILINE)
if match:
return match.group(1).strip()
return SKILL_DIR.name.replace("-", " ").title()
def _extract_chinese_name(body):
match = re.search(r"^Chinese name:\s*(.+)$", body, re.MULTILINE | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _parse_sections(body):
sections = {}
pattern = re.compile(r"^##\s+(.+?)\n(.*?)(?=^##\s+|\Z)", re.MULTILINE | re.DOTALL)
for heading, content in pattern.findall(body):
sections[heading.strip().lower()] = content.strip()
return sections
def _normalize_input(user_input):
if user_input is None:
return ""
if isinstance(user_input, str):
return user_input.strip()
return json.dumps(user_input, ensure_ascii=False, indent=2, sort_keys=True)
def _extract_items(text):
items = []
for raw_line in text.splitlines():
line = raw_line.strip()
if not line:
continue
if re.match(r"^[-*]\s+", line):
items.append(re.sub(r"^[-*]\s+", "", line))
elif re.match(r"^\d+\.\s+", line):
items.append(re.sub(r"^\d+\.\s+", "", line))
if items:
return items
collapsed = " ".join(line.strip() for line in text.splitlines() if line.strip())
return [collapsed] if collapsed else []
def _limit_for_mode(mode):
normalized = (mode or "guide").strip().lower()
if normalized in {"brief", "compact", "quick"}:
return 2
if normalized in {"full", "deep", "extended"}:
return None
return 4
def _render_list(title, items, mode, ordered=False):
limit = _limit_for_mode(mode)
chosen = items if limit is None else items[:limit]
if not chosen:
return []
lines = [f"## {title}"]
for index, item in enumerate(chosen, start=1):
prefix = f"{index}." if ordered else "-"
lines.append(f"{prefix} {item}")
lines.append("")
return lines
def handle(args):
if not isinstance(args, dict):
args = {"input": args}
skill_name = args.get("skill_name", "")
user_input = args.get("input", "")
mode = args.get("mode", "guide")
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
sections = _parse_sections(template)
title = _extract_title(template)
chinese_name = _extract_chinese_name(template)
normalized_input = _normalize_input(user_input)
lines = [f"# {title}"]
lines.append(f"- Skill slug: {meta['name']}")
if chinese_name:
lines.append(f"- Chinese name: {chinese_name}")
lines.append(f"- Mode: {mode}")
if meta["description"]:
lines.append(f"- Summary: {meta['description']}")
lines.append("")
if normalized_input:
lines.append("## User context")
lines.append(normalized_input)
lines.append("")
purpose_items = _extract_items(sections.get("purpose", ""))
lines.extend(_render_list("Purpose", purpose_items, mode))
lines.extend(_render_list("Use cases", _extract_items(sections.get("use this skill when", "")), mode))
lines.extend(_render_list("Inputs to collect", _extract_items(sections.get("inputs to collect", "")), mode))
lines.extend(_render_list("Workflow", _extract_items(sections.get("workflow", "")), mode, ordered=True))
lines.extend(_render_list("Output format", _extract_items(sections.get("output format", "")), mode))
lines.extend(_render_list("Quality bar", _extract_items(sections.get("quality bar", "")), mode))
lines.extend(_render_list("Edge cases and limits", _extract_items(sections.get("edge cases and limits", "")), mode))
lines.extend(_render_list("Compatibility notes", _extract_items(sections.get("compatibility notes", "")), mode))
section_count = len([name for name in sections if sections[name]])
lines.append("## Prompt template status")
lines.append(f"- Loaded from SKILL.md with {section_count} structured sections.")
lines.append("- This handler returns a descriptive guidance card and does not perform external actions.")
return {"result": "\n".join(lines).strip()}
if __name__ == "__main__":
print(json.dumps(handle({"skill_name": SKILL_DIR.name, "input": "demo", "mode": "guide"}), ensure_ascii=False, indent=2))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
from pathlib import Path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import _load_prompt_template, _load_skill_meta, handle
SKILL_DIR = Path(__file__).resolve().parents[1]
SKILL_NAME = SKILL_DIR.name
def test_load_skill_meta():
meta = _load_skill_meta(SKILL_NAME)
assert meta['name'] == SKILL_NAME
assert meta['description']
def test_prompt_template_sections_exist():
template = _load_prompt_template(SKILL_NAME)
for heading in ['## Purpose', '## Workflow', '## Output Format', '## Quality bar']:
assert heading in template
def test_handle_returns_descriptive_card():
payload = {
'skill_name': SKILL_NAME,
'input': 'Need help turning this into a playable plan.',
'mode': 'guide',
}
result = handle(payload)
assert isinstance(result, dict)
text = result['result']
assert f'- Skill slug: {SKILL_NAME}' in text
assert '## Workflow' in text
assert '## Output format' in text
assert 'Need help turning this into a playable plan.' in text
assert 'Loaded from SKILL.md' in text
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Organize learning goals into a skill tree with dependencies, current level, upgrade conditions, and the next best path. Use when the user wants a realistic g...
---
name: level-up-skill-tree
description: Organize learning goals into a skill tree with dependencies, current level, upgrade conditions, and the next best path. Use when the user wants a realistic growth roadmap across study, work, or personal interests.
---
# Level Up Skill Tree
Chinese name: 技能树成长
## Purpose
Turn scattered learning goals into a skill tree with dependencies so the user can stop guessing what to learn next.
This skill is descriptive only. It does not run assessments, enroll courses, or call external services.
## Use this skill when
- The user wants to learn many things but needs a realistic order.
- The user feels stuck on a plateau and wants a next upgrade path.
- The user needs to balance core growth with side branches.
- The user wants a role-based learning map instead of a flat task list.
## Inputs to collect
- Target role or identity
- Core capabilities to build
- Existing baseline or prior experience
- Time constraints and resource limits
- Current blockers or plateau symptoms
- Desired outcome horizon
## Workflow
1. Collect the target role, important capabilities, existing foundations, and real-world constraints.
2. Break the capabilities into trunk skills, branch skills, prerequisite nodes, passive buffs, and breakthrough nodes.
3. Define each node with current level, upgrade condition, recommended practice action, and whether a skip is realistic.
4. Draw the current-stage tree and mark already unlocked nodes.
5. Recommend the next best upgrade route with one short-cycle training action and a validation method.
## Output Format
- Role positioning with a main-role and side-role style summary.
- Skill tree overview with branches, dependencies, and already unlocked nodes.
- Next upgrade route with a reasoned priority order.
- This week's smallest upgrade actions and how to verify progress.
## Quality bar
- The tree must reflect dependencies rather than a flat list.
- Recommendations must match the user's real time and baseline.
- Include at least one short-cycle action that can be validated quickly.
- Avoid unrealistic leapfrogging when prerequisites are missing.
## Edge cases and limits
- If the user has too many goals, choose one main tree and park the rest as side branches.
- If skill labels are too abstract, translate them into trainable behavior first.
- Do not present this as professional certification, academic advising, or formal career assessment.
## Compatibility notes
- Works for students, parents, self-learners, career shifters, and long-term builders.
- Can pair conceptually with achievement-unlock-tracker or quest-chain-decomposer.
- Fully dialogue-based, no assessment API required.
FILE:handler.py
import json
import re
from pathlib import Path
SKILL_DIR = Path(__file__).resolve().parent
SKILL_FILE = SKILL_DIR / "SKILL.md"
def _read_skill_file():
return SKILL_FILE.read_text(encoding="utf-8")
def _split_frontmatter(content):
match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", content, re.DOTALL)
if match:
return match.group(1), match.group(2)
return "", content
def _parse_frontmatter(frontmatter):
meta = {}
for line in frontmatter.splitlines():
line = line.strip()
if not line or ":" not in line:
continue
key, value = line.split(":", 1)
meta[key.strip()] = value.strip().strip('"').strip("'")
return meta
def _load_skill_meta(skill_name):
content = _read_skill_file()
frontmatter, _ = _split_frontmatter(content)
parsed = _parse_frontmatter(frontmatter)
return {
"name": parsed.get("name", skill_name or SKILL_DIR.name),
"description": parsed.get("description", ""),
}
def _load_prompt_template(skill_name):
del skill_name
content = _read_skill_file()
_, body = _split_frontmatter(content)
return body.strip()
def _extract_title(body):
match = re.search(r"^#\s+(.+)$", body, re.MULTILINE)
if match:
return match.group(1).strip()
return SKILL_DIR.name.replace("-", " ").title()
def _extract_chinese_name(body):
match = re.search(r"^Chinese name:\s*(.+)$", body, re.MULTILINE | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _parse_sections(body):
sections = {}
pattern = re.compile(r"^##\s+(.+?)\n(.*?)(?=^##\s+|\Z)", re.MULTILINE | re.DOTALL)
for heading, content in pattern.findall(body):
sections[heading.strip().lower()] = content.strip()
return sections
def _normalize_input(user_input):
if user_input is None:
return ""
if isinstance(user_input, str):
return user_input.strip()
return json.dumps(user_input, ensure_ascii=False, indent=2, sort_keys=True)
def _extract_items(text):
items = []
for raw_line in text.splitlines():
line = raw_line.strip()
if not line:
continue
if re.match(r"^[-*]\s+", line):
items.append(re.sub(r"^[-*]\s+", "", line))
elif re.match(r"^\d+\.\s+", line):
items.append(re.sub(r"^\d+\.\s+", "", line))
if items:
return items
collapsed = " ".join(line.strip() for line in text.splitlines() if line.strip())
return [collapsed] if collapsed else []
def _limit_for_mode(mode):
normalized = (mode or "guide").strip().lower()
if normalized in {"brief", "compact", "quick"}:
return 2
if normalized in {"full", "deep", "extended"}:
return None
return 4
def _render_list(title, items, mode, ordered=False):
limit = _limit_for_mode(mode)
chosen = items if limit is None else items[:limit]
if not chosen:
return []
lines = [f"## {title}"]
for index, item in enumerate(chosen, start=1):
prefix = f"{index}." if ordered else "-"
lines.append(f"{prefix} {item}")
lines.append("")
return lines
def handle(args):
if not isinstance(args, dict):
args = {"input": args}
skill_name = args.get("skill_name", "")
user_input = args.get("input", "")
mode = args.get("mode", "guide")
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
sections = _parse_sections(template)
title = _extract_title(template)
chinese_name = _extract_chinese_name(template)
normalized_input = _normalize_input(user_input)
lines = [f"# {title}"]
lines.append(f"- Skill slug: {meta['name']}")
if chinese_name:
lines.append(f"- Chinese name: {chinese_name}")
lines.append(f"- Mode: {mode}")
if meta["description"]:
lines.append(f"- Summary: {meta['description']}")
lines.append("")
if normalized_input:
lines.append("## User context")
lines.append(normalized_input)
lines.append("")
purpose_items = _extract_items(sections.get("purpose", ""))
lines.extend(_render_list("Purpose", purpose_items, mode))
lines.extend(_render_list("Use cases", _extract_items(sections.get("use this skill when", "")), mode))
lines.extend(_render_list("Inputs to collect", _extract_items(sections.get("inputs to collect", "")), mode))
lines.extend(_render_list("Workflow", _extract_items(sections.get("workflow", "")), mode, ordered=True))
lines.extend(_render_list("Output format", _extract_items(sections.get("output format", "")), mode))
lines.extend(_render_list("Quality bar", _extract_items(sections.get("quality bar", "")), mode))
lines.extend(_render_list("Edge cases and limits", _extract_items(sections.get("edge cases and limits", "")), mode))
lines.extend(_render_list("Compatibility notes", _extract_items(sections.get("compatibility notes", "")), mode))
section_count = len([name for name in sections if sections[name]])
lines.append("## Prompt template status")
lines.append(f"- Loaded from SKILL.md with {section_count} structured sections.")
lines.append("- This handler returns a descriptive guidance card and does not perform external actions.")
return {"result": "\n".join(lines).strip()}
if __name__ == "__main__":
print(json.dumps(handle({"skill_name": SKILL_DIR.name, "input": "demo", "mode": "guide"}), ensure_ascii=False, indent=2))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
from pathlib import Path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import _load_prompt_template, _load_skill_meta, handle
SKILL_DIR = Path(__file__).resolve().parents[1]
SKILL_NAME = SKILL_DIR.name
def test_load_skill_meta():
meta = _load_skill_meta(SKILL_NAME)
assert meta['name'] == SKILL_NAME
assert meta['description']
def test_prompt_template_sections_exist():
template = _load_prompt_template(SKILL_NAME)
for heading in ['## Purpose', '## Workflow', '## Output Format', '## Quality bar']:
assert heading in template
def test_handle_returns_descriptive_card():
payload = {
'skill_name': SKILL_NAME,
'input': 'Need help turning this into a playable plan.',
'mode': 'guide',
}
result = handle(payload)
assert isinstance(result, dict)
text = result['result']
assert f'- Skill slug: {SKILL_NAME}' in text
assert '## Workflow' in text
assert '## Output format' in text
assert 'Need help turning this into a playable plan.' in text
assert 'Loaded from SKILL.md' in text
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Convert recent progress into a game-style achievement board with evidence, rarity, near-unlock targets, and hidden growth signals. Use for weekly or monthly...
---
name: achievement-unlock-tracker
description: Convert recent progress into a game-style achievement board with evidence, rarity, near-unlock targets, and hidden growth signals. Use for weekly or monthly reviews, motivation repair, or milestone reflection.
---
# Achievement Unlock Tracker
Chinese name: 成就解锁追踪
## Purpose
Help the user see progress that already exists by translating real effort and outcomes into a grounded achievement system.
This skill is descriptive only. It does not certify performance or connect to external analytics.
## Use this skill when
- The user says, "I did nothing," despite recent effort.
- The user wants a weekly review, monthly review, or milestone settlement.
- The user needs motivation from visible evidence of progress.
- The user wants the next meaningful achievement target.
## Inputs to collect
- Recent completed actions
- Milestones or breakthroughs
- Difficult moments overcome
- Time, effort, or cost invested
- Current self-judgment or doubt level
## Workflow
1. Collect recent actions, milestones, breakthroughs, and meaningful effort.
2. Sort them into unlocked achievements, near-unlock achievements, hidden achievements, and long-arc legendary achievements.
3. Name each achievement, state the unlock condition, and cite the evidence that supports it.
4. Identify the next most valuable achievement to chase and what single step would move it forward.
5. If the user is heavily self-critical, separate feelings from evidence in a correction pass.
## Output Format
- Unlocked achievement list with name, evidence, rarity, and short commentary.
- Near-unlock achievements with the missing requirement and the suggested next move.
- Hidden achievement clues that point to quiet growth.
- A next milestone recommendation with a clear reason.
## Quality bar
- Every achievement must be anchored in user-provided facts.
- The names should feel game-like without becoming unrealistic fluff.
- Include at least one near-term achievement the user can progress soon.
- Keep praise honest and evidence-based.
## Edge cases and limits
- If recent output is small, allow recovery, stabilization, and restart behavior to count as valid small achievements.
- If the user offers feelings but no facts, ask for evidence before assigning achievements.
- Do not use this skill as a substitute for formal performance review or resume validation.
## Compatibility notes
- Works for personal growth, learning, health, family management, and career reflection.
- Can pair conceptually with loot-reward-celebrator or level-up-skill-tree.
- Purely descriptive, no external metrics required.
FILE:handler.py
import json
import re
from pathlib import Path
SKILL_DIR = Path(__file__).resolve().parent
SKILL_FILE = SKILL_DIR / "SKILL.md"
def _read_skill_file():
return SKILL_FILE.read_text(encoding="utf-8")
def _split_frontmatter(content):
match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", content, re.DOTALL)
if match:
return match.group(1), match.group(2)
return "", content
def _parse_frontmatter(frontmatter):
meta = {}
for line in frontmatter.splitlines():
line = line.strip()
if not line or ":" not in line:
continue
key, value = line.split(":", 1)
meta[key.strip()] = value.strip().strip('"').strip("'")
return meta
def _load_skill_meta(skill_name):
content = _read_skill_file()
frontmatter, _ = _split_frontmatter(content)
parsed = _parse_frontmatter(frontmatter)
return {
"name": parsed.get("name", skill_name or SKILL_DIR.name),
"description": parsed.get("description", ""),
}
def _load_prompt_template(skill_name):
del skill_name
content = _read_skill_file()
_, body = _split_frontmatter(content)
return body.strip()
def _extract_title(body):
match = re.search(r"^#\s+(.+)$", body, re.MULTILINE)
if match:
return match.group(1).strip()
return SKILL_DIR.name.replace("-", " ").title()
def _extract_chinese_name(body):
match = re.search(r"^Chinese name:\s*(.+)$", body, re.MULTILINE | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _parse_sections(body):
sections = {}
pattern = re.compile(r"^##\s+(.+?)\n(.*?)(?=^##\s+|\Z)", re.MULTILINE | re.DOTALL)
for heading, content in pattern.findall(body):
sections[heading.strip().lower()] = content.strip()
return sections
def _normalize_input(user_input):
if user_input is None:
return ""
if isinstance(user_input, str):
return user_input.strip()
return json.dumps(user_input, ensure_ascii=False, indent=2, sort_keys=True)
def _extract_items(text):
items = []
for raw_line in text.splitlines():
line = raw_line.strip()
if not line:
continue
if re.match(r"^[-*]\s+", line):
items.append(re.sub(r"^[-*]\s+", "", line))
elif re.match(r"^\d+\.\s+", line):
items.append(re.sub(r"^\d+\.\s+", "", line))
if items:
return items
collapsed = " ".join(line.strip() for line in text.splitlines() if line.strip())
return [collapsed] if collapsed else []
def _limit_for_mode(mode):
normalized = (mode or "guide").strip().lower()
if normalized in {"brief", "compact", "quick"}:
return 2
if normalized in {"full", "deep", "extended"}:
return None
return 4
def _render_list(title, items, mode, ordered=False):
limit = _limit_for_mode(mode)
chosen = items if limit is None else items[:limit]
if not chosen:
return []
lines = [f"## {title}"]
for index, item in enumerate(chosen, start=1):
prefix = f"{index}." if ordered else "-"
lines.append(f"{prefix} {item}")
lines.append("")
return lines
def handle(args):
if not isinstance(args, dict):
args = {"input": args}
skill_name = args.get("skill_name", "")
user_input = args.get("input", "")
mode = args.get("mode", "guide")
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
sections = _parse_sections(template)
title = _extract_title(template)
chinese_name = _extract_chinese_name(template)
normalized_input = _normalize_input(user_input)
lines = [f"# {title}"]
lines.append(f"- Skill slug: {meta['name']}")
if chinese_name:
lines.append(f"- Chinese name: {chinese_name}")
lines.append(f"- Mode: {mode}")
if meta["description"]:
lines.append(f"- Summary: {meta['description']}")
lines.append("")
if normalized_input:
lines.append("## User context")
lines.append(normalized_input)
lines.append("")
purpose_items = _extract_items(sections.get("purpose", ""))
lines.extend(_render_list("Purpose", purpose_items, mode))
lines.extend(_render_list("Use cases", _extract_items(sections.get("use this skill when", "")), mode))
lines.extend(_render_list("Inputs to collect", _extract_items(sections.get("inputs to collect", "")), mode))
lines.extend(_render_list("Workflow", _extract_items(sections.get("workflow", "")), mode, ordered=True))
lines.extend(_render_list("Output format", _extract_items(sections.get("output format", "")), mode))
lines.extend(_render_list("Quality bar", _extract_items(sections.get("quality bar", "")), mode))
lines.extend(_render_list("Edge cases and limits", _extract_items(sections.get("edge cases and limits", "")), mode))
lines.extend(_render_list("Compatibility notes", _extract_items(sections.get("compatibility notes", "")), mode))
section_count = len([name for name in sections if sections[name]])
lines.append("## Prompt template status")
lines.append(f"- Loaded from SKILL.md with {section_count} structured sections.")
lines.append("- This handler returns a descriptive guidance card and does not perform external actions.")
return {"result": "\n".join(lines).strip()}
if __name__ == "__main__":
print(json.dumps(handle({"skill_name": SKILL_DIR.name, "input": "demo", "mode": "guide"}), ensure_ascii=False, indent=2))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
from pathlib import Path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import _load_prompt_template, _load_skill_meta, handle
SKILL_DIR = Path(__file__).resolve().parents[1]
SKILL_NAME = SKILL_DIR.name
def test_load_skill_meta():
meta = _load_skill_meta(SKILL_NAME)
assert meta['name'] == SKILL_NAME
assert meta['description']
def test_prompt_template_sections_exist():
template = _load_prompt_template(SKILL_NAME)
for heading in ['## Purpose', '## Workflow', '## Output Format', '## Quality bar']:
assert heading in template
def test_handle_returns_descriptive_card():
payload = {
'skill_name': SKILL_NAME,
'input': 'Need help turning this into a playable plan.',
'mode': 'guide',
}
result = handle(payload)
assert isinstance(result, dict)
text = result['result']
assert f'- Skill slug: {SKILL_NAME}' in text
assert '## Workflow' in text
assert '## Output format' in text
assert 'Need help turning this into a playable plan.' in text
assert 'Loaded from SKILL.md' in text
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Reframe a day into a main dungeon and side quests with clear win conditions, time budgets, fallback modes, and reward prompts. Use for daily planning, mornin...
---
name: daily-dungeon-challenger
description: Reframe a day into a main dungeon and side quests with clear win conditions, time budgets, fallback modes, and reward prompts. Use for daily planning, morning starts, midday replans, or low-energy recovery.
---
# Daily Dungeon Challenger
Chinese name: 每日副本挑战
## Purpose
Turn an ordinary day into a game-like dungeon run so the user can see what matters, what can wait, and how to keep moving when energy drops.
This skill is descriptive only. It does not create calendar events or call external systems.
## Use this skill when
- The user has too many tasks and needs a clear main challenge for today.
- The user wants a morning launch plan, midday replan, or evening rescue version.
- The user is procrastinating because the day feels shapeless.
- The user wants a realistic plan that still feels motivating.
## Inputs to collect
- Available time today
- Current energy level or focus capacity
- Must-do items
- Nice-to-progress items
- Urgent interruptions or fixed constraints
- Preferred reward or rest style
## Workflow
1. Collect the user's available time, energy bar, required tasks, and optional progress tasks.
2. Map the day into one main dungeon, one to three side quests, a supply phase, and a failure floor.
3. For each dungeon element, define the title, win condition, time budget, reward drop, and retreat rule.
4. If surprise tasks arrive or energy collapses, rewrite the run as a simplified dungeon mode.
5. End with a minimum viable win line so the user still knows how to salvage the day.
## Output Format
- Today's dungeon overview with one main dungeon and one to three side quests.
- Clear completion rules, recommended order, and time budget for each item.
- A fallback mode with minimum success criteria and damage control steps.
- At least one reward, recovery, or reflection prompt after completion.
## Quality bar
- The main dungeon must be concrete enough that success is obvious.
- Total task load must match real energy and available time.
- Always include what to do if the user cannot finish the ideal version.
- Avoid fantasy scheduling and overload.
## Edge cases and limits
- If the user is already overloaded, output a retreat build instead of adding more challenge.
- If urgent items interrupt the plan, allow the dungeon order to change immediately.
- Do not present this as a replacement for formal project management software.
## Compatibility notes
- Works for morning planning, midday replanning, and evening rescue mode.
- Can connect conceptually with boss-battle-calendar or quest-chain-decomposer for larger arcs.
- Text only, no calendar sync required.
FILE:handler.py
import json
import re
from pathlib import Path
SKILL_DIR = Path(__file__).resolve().parent
SKILL_FILE = SKILL_DIR / "SKILL.md"
def _read_skill_file():
return SKILL_FILE.read_text(encoding="utf-8")
def _split_frontmatter(content):
match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", content, re.DOTALL)
if match:
return match.group(1), match.group(2)
return "", content
def _parse_frontmatter(frontmatter):
meta = {}
for line in frontmatter.splitlines():
line = line.strip()
if not line or ":" not in line:
continue
key, value = line.split(":", 1)
meta[key.strip()] = value.strip().strip('"').strip("'")
return meta
def _load_skill_meta(skill_name):
content = _read_skill_file()
frontmatter, _ = _split_frontmatter(content)
parsed = _parse_frontmatter(frontmatter)
return {
"name": parsed.get("name", skill_name or SKILL_DIR.name),
"description": parsed.get("description", ""),
}
def _load_prompt_template(skill_name):
del skill_name
content = _read_skill_file()
_, body = _split_frontmatter(content)
return body.strip()
def _extract_title(body):
match = re.search(r"^#\s+(.+)$", body, re.MULTILINE)
if match:
return match.group(1).strip()
return SKILL_DIR.name.replace("-", " ").title()
def _extract_chinese_name(body):
match = re.search(r"^Chinese name:\s*(.+)$", body, re.MULTILINE | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _parse_sections(body):
sections = {}
pattern = re.compile(r"^##\s+(.+?)\n(.*?)(?=^##\s+|\Z)", re.MULTILINE | re.DOTALL)
for heading, content in pattern.findall(body):
sections[heading.strip().lower()] = content.strip()
return sections
def _normalize_input(user_input):
if user_input is None:
return ""
if isinstance(user_input, str):
return user_input.strip()
return json.dumps(user_input, ensure_ascii=False, indent=2, sort_keys=True)
def _extract_items(text):
items = []
for raw_line in text.splitlines():
line = raw_line.strip()
if not line:
continue
if re.match(r"^[-*]\s+", line):
items.append(re.sub(r"^[-*]\s+", "", line))
elif re.match(r"^\d+\.\s+", line):
items.append(re.sub(r"^\d+\.\s+", "", line))
if items:
return items
collapsed = " ".join(line.strip() for line in text.splitlines() if line.strip())
return [collapsed] if collapsed else []
def _limit_for_mode(mode):
normalized = (mode or "guide").strip().lower()
if normalized in {"brief", "compact", "quick"}:
return 2
if normalized in {"full", "deep", "extended"}:
return None
return 4
def _render_list(title, items, mode, ordered=False):
limit = _limit_for_mode(mode)
chosen = items if limit is None else items[:limit]
if not chosen:
return []
lines = [f"## {title}"]
for index, item in enumerate(chosen, start=1):
prefix = f"{index}." if ordered else "-"
lines.append(f"{prefix} {item}")
lines.append("")
return lines
def handle(args):
if not isinstance(args, dict):
args = {"input": args}
skill_name = args.get("skill_name", "")
user_input = args.get("input", "")
mode = args.get("mode", "guide")
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
sections = _parse_sections(template)
title = _extract_title(template)
chinese_name = _extract_chinese_name(template)
normalized_input = _normalize_input(user_input)
lines = [f"# {title}"]
lines.append(f"- Skill slug: {meta['name']}")
if chinese_name:
lines.append(f"- Chinese name: {chinese_name}")
lines.append(f"- Mode: {mode}")
if meta["description"]:
lines.append(f"- Summary: {meta['description']}")
lines.append("")
if normalized_input:
lines.append("## User context")
lines.append(normalized_input)
lines.append("")
purpose_items = _extract_items(sections.get("purpose", ""))
lines.extend(_render_list("Purpose", purpose_items, mode))
lines.extend(_render_list("Use cases", _extract_items(sections.get("use this skill when", "")), mode))
lines.extend(_render_list("Inputs to collect", _extract_items(sections.get("inputs to collect", "")), mode))
lines.extend(_render_list("Workflow", _extract_items(sections.get("workflow", "")), mode, ordered=True))
lines.extend(_render_list("Output format", _extract_items(sections.get("output format", "")), mode))
lines.extend(_render_list("Quality bar", _extract_items(sections.get("quality bar", "")), mode))
lines.extend(_render_list("Edge cases and limits", _extract_items(sections.get("edge cases and limits", "")), mode))
lines.extend(_render_list("Compatibility notes", _extract_items(sections.get("compatibility notes", "")), mode))
section_count = len([name for name in sections if sections[name]])
lines.append("## Prompt template status")
lines.append(f"- Loaded from SKILL.md with {section_count} structured sections.")
lines.append("- This handler returns a descriptive guidance card and does not perform external actions.")
return {"result": "\n".join(lines).strip()}
if __name__ == "__main__":
print(json.dumps(handle({"skill_name": SKILL_DIR.name, "input": "demo", "mode": "guide"}), ensure_ascii=False, indent=2))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
from pathlib import Path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import _load_prompt_template, _load_skill_meta, handle
SKILL_DIR = Path(__file__).resolve().parents[1]
SKILL_NAME = SKILL_DIR.name
def test_load_skill_meta():
meta = _load_skill_meta(SKILL_NAME)
assert meta['name'] == SKILL_NAME
assert meta['description']
def test_prompt_template_sections_exist():
template = _load_prompt_template(SKILL_NAME)
for heading in ['## Purpose', '## Workflow', '## Output Format', '## Quality bar']:
assert heading in template
def test_handle_returns_descriptive_card():
payload = {
'skill_name': SKILL_NAME,
'input': 'Need help turning this into a playable plan.',
'mode': 'guide',
}
result = handle(payload)
assert isinstance(result, dict)
text = result['result']
assert f'- Skill slug: {SKILL_NAME}' in text
assert '## Workflow' in text
assert '## Output format' in text
assert 'Need help turning this into a playable plan.' in text
assert 'Loaded from SKILL.md' in text
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Turn habit building into a quest log with daily mission tiers, streak feedback, fallback actions, and recovery guidance after missed days. Use when the user...
---
name: habit-quest-logger
description: Turn habit building into a quest log with daily mission tiers, streak feedback, fallback actions, and recovery guidance after missed days. Use when the user wants to build or restart 1 to 5 habits with lower friction and more visible progress.
---
# Habit Quest Logger
Chinese name: 习惯养成任务日志
## Purpose
Turn dull habit tracking into a quest log with clear mission tiers, visible streak feedback, and a shame-free reset path.
This skill is descriptive only. It does not create reminders, sync data, or call external APIs.
## Use this skill when
- The user wants to track 1 to 5 habits in a more motivating way.
- The user keeps quitting after one missed day and needs a recovery loop.
- The user wants a daily mission board instead of vague habit intentions.
- The user needs a low-friction way to restart consistency.
## Inputs to collect
- Habit name
- Frequency or cadence
- Target duration or minimum version
- Main difficulty or friction point
- Current streak or recent consistency
- Today's energy, time, or constraints
## Workflow
1. Collect up to 5 habits and clarify frequency, duration, difficulty, and current streak.
2. Rewrite each habit as a main quest, side quest, and minimum fallback quest.
3. Pick only one main quest for today and keep the rest as support or backup actions.
4. Define how experience points are earned, what counts as success, and what the user should do after a miss.
5. If the user has been using the system for several days, add streak protection, difficulty adjustment, and a return-to-game rule.
## Output Format
- Today's quest log with the exact action and completion line.
- Streak status with current days, current level, and next level condition.
- Failure recovery move with the smallest comeback action.
- End-of-day settlement line with short positive reinforcement.
## Quality bar
- Default to one main quest and only a small number of fallback actions.
- Every habit must have a concrete completion standard.
- Always include a recovery mechanism after misses.
- Keep the plan within the user's real capacity for today.
## Edge cases and limits
- If the user lists too many habits, compress scope and keep only the highest-value ones active.
- If the goal is vague, translate it into observable behavior before building the quest log.
- Do not position this skill as therapy, medical care, or hard accountability enforcement.
## Compatibility notes
- Accepts free-form Chinese or English input, plus semi-structured habit details.
- Works for one-off daily planning or repeated day-by-day iteration.
- Can pair conceptually with habit-tracker or daily-dungeon-challenger, but it does not depend on other tools.
FILE:handler.py
import json
import re
from pathlib import Path
SKILL_DIR = Path(__file__).resolve().parent
SKILL_FILE = SKILL_DIR / "SKILL.md"
def _read_skill_file():
return SKILL_FILE.read_text(encoding="utf-8")
def _split_frontmatter(content):
match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", content, re.DOTALL)
if match:
return match.group(1), match.group(2)
return "", content
def _parse_frontmatter(frontmatter):
meta = {}
for line in frontmatter.splitlines():
line = line.strip()
if not line or ":" not in line:
continue
key, value = line.split(":", 1)
meta[key.strip()] = value.strip().strip('"').strip("'")
return meta
def _load_skill_meta(skill_name):
content = _read_skill_file()
frontmatter, _ = _split_frontmatter(content)
parsed = _parse_frontmatter(frontmatter)
return {
"name": parsed.get("name", skill_name or SKILL_DIR.name),
"description": parsed.get("description", ""),
}
def _load_prompt_template(skill_name):
del skill_name
content = _read_skill_file()
_, body = _split_frontmatter(content)
return body.strip()
def _extract_title(body):
match = re.search(r"^#\s+(.+)$", body, re.MULTILINE)
if match:
return match.group(1).strip()
return SKILL_DIR.name.replace("-", " ").title()
def _extract_chinese_name(body):
match = re.search(r"^Chinese name:\s*(.+)$", body, re.MULTILINE | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _parse_sections(body):
sections = {}
pattern = re.compile(r"^##\s+(.+?)\n(.*?)(?=^##\s+|\Z)", re.MULTILINE | re.DOTALL)
for heading, content in pattern.findall(body):
sections[heading.strip().lower()] = content.strip()
return sections
def _normalize_input(user_input):
if user_input is None:
return ""
if isinstance(user_input, str):
return user_input.strip()
return json.dumps(user_input, ensure_ascii=False, indent=2, sort_keys=True)
def _extract_items(text):
items = []
for raw_line in text.splitlines():
line = raw_line.strip()
if not line:
continue
if re.match(r"^[-*]\s+", line):
items.append(re.sub(r"^[-*]\s+", "", line))
elif re.match(r"^\d+\.\s+", line):
items.append(re.sub(r"^\d+\.\s+", "", line))
if items:
return items
collapsed = " ".join(line.strip() for line in text.splitlines() if line.strip())
return [collapsed] if collapsed else []
def _limit_for_mode(mode):
normalized = (mode or "guide").strip().lower()
if normalized in {"brief", "compact", "quick"}:
return 2
if normalized in {"full", "deep", "extended"}:
return None
return 4
def _render_list(title, items, mode, ordered=False):
limit = _limit_for_mode(mode)
chosen = items if limit is None else items[:limit]
if not chosen:
return []
lines = [f"## {title}"]
for index, item in enumerate(chosen, start=1):
prefix = f"{index}." if ordered else "-"
lines.append(f"{prefix} {item}")
lines.append("")
return lines
def handle(args):
if not isinstance(args, dict):
args = {"input": args}
skill_name = args.get("skill_name", "")
user_input = args.get("input", "")
mode = args.get("mode", "guide")
meta = _load_skill_meta(skill_name)
template = _load_prompt_template(skill_name)
sections = _parse_sections(template)
title = _extract_title(template)
chinese_name = _extract_chinese_name(template)
normalized_input = _normalize_input(user_input)
lines = [f"# {title}"]
lines.append(f"- Skill slug: {meta['name']}")
if chinese_name:
lines.append(f"- Chinese name: {chinese_name}")
lines.append(f"- Mode: {mode}")
if meta["description"]:
lines.append(f"- Summary: {meta['description']}")
lines.append("")
if normalized_input:
lines.append("## User context")
lines.append(normalized_input)
lines.append("")
purpose_items = _extract_items(sections.get("purpose", ""))
lines.extend(_render_list("Purpose", purpose_items, mode))
lines.extend(_render_list("Use cases", _extract_items(sections.get("use this skill when", "")), mode))
lines.extend(_render_list("Inputs to collect", _extract_items(sections.get("inputs to collect", "")), mode))
lines.extend(_render_list("Workflow", _extract_items(sections.get("workflow", "")), mode, ordered=True))
lines.extend(_render_list("Output format", _extract_items(sections.get("output format", "")), mode))
lines.extend(_render_list("Quality bar", _extract_items(sections.get("quality bar", "")), mode))
lines.extend(_render_list("Edge cases and limits", _extract_items(sections.get("edge cases and limits", "")), mode))
lines.extend(_render_list("Compatibility notes", _extract_items(sections.get("compatibility notes", "")), mode))
section_count = len([name for name in sections if sections[name]])
lines.append("## Prompt template status")
lines.append(f"- Loaded from SKILL.md with {section_count} structured sections.")
lines.append("- This handler returns a descriptive guidance card and does not perform external actions.")
return {"result": "\n".join(lines).strip()}
if __name__ == "__main__":
print(json.dumps(handle({"skill_name": SKILL_DIR.name, "input": "demo", "mode": "guide"}), ensure_ascii=False, indent=2))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
from pathlib import Path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import _load_prompt_template, _load_skill_meta, handle
SKILL_DIR = Path(__file__).resolve().parents[1]
SKILL_NAME = SKILL_DIR.name
def test_load_skill_meta():
meta = _load_skill_meta(SKILL_NAME)
assert meta['name'] == SKILL_NAME
assert meta['description']
def test_prompt_template_sections_exist():
template = _load_prompt_template(SKILL_NAME)
for heading in ['## Purpose', '## Workflow', '## Output Format', '## Quality bar']:
assert heading in template
def test_handle_returns_descriptive_card():
payload = {
'skill_name': SKILL_NAME,
'input': 'Need help turning this into a playable plan.',
'mode': 'guide',
}
result = handle(payload)
assert isinstance(result, dict)
text = result['result']
assert f'- Skill slug: {SKILL_NAME}' in text
assert '## Workflow' in text
assert '## Output format' in text
assert 'Need help turning this into a playable plan.' in text
assert 'Loaded from SKILL.md' in text
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Guide users to rehearse difficult conversations by crafting calm, clear openings, anticipating responses, preparing steady replies, and setting firm boundaries.
---
name: difficult-conversation-rehearser
description: Help a user rehearse a necessary hard conversation with a clear opening, likely responses, steady replies, and a boundary line. Use when someone fears conflict, over-explains, or needs calm language. Chinese alias: 艰难对话排练师.
---
# Difficult Conversation Rehearser / 艰难对话排练师
Use this skill when a user needs language for a hard conversation that is honest, calm, and direct.
## What it helps with
- Clarifying the goal of the conversation, such as informing, requesting, repairing, or setting a boundary
- Identifying stakes, relationship dynamics, and the one key message that must be said
- Drafting an opening line that is clear, respectful, and not rambling
- Predicting likely responses from the other person
- Preparing steady replies, bridge phrases, and one boundary line
- Ending with a clean close or next-step sentence
## Workflow
1. Clarify the goal of the conversation.
2. Name the stakes, relationship dynamics, and key message.
3. Draft a clear opening line.
4. Predict likely responses.
5. Prepare steady replies and one boundary sentence.
6. End with a clean close or next step.
## Output format
```markdown
# Conversation Rehearsal Brief
## Purpose
- What I need to say:
- What outcome I want:
## Opening
- ...
## Likely Responses and My Replies
1. If they say:
- I can reply:
2. If they say:
- I can reply:
## Boundaries
- What I will not keep debating:
- My boundary sentence:
## Close
- How I will end the conversation:
```
## Quality bar
- One core objective is visible.
- The opening is direct and emotionally regulated.
- It includes empathy without surrendering the main point.
- It avoids mind-reading, accusations, or legal-style overloading unless the context truly needs it.
## Limits
- Abuse, coercion, or safety threats require stronger support than conversational rehearsal.
- Some contexts need documentation or specialist advice.
- The other person may still react badly even if the language is strong.
- Role-play guidance only, with no live outreach or message sending.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts: List[str] = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item) for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _listify(value: Any) -> List[str]:
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
return []
if isinstance(value, str):
clean = value.replace(';', '\n').replace(',', '\n')
return [chunk.strip() for chunk in clean.splitlines() if chunk.strip()]
if isinstance(value, (list, tuple, set)):
return [str(item).strip() for item in value if str(item).strip()]
return [str(value).strip()]
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _detect_many(text: str, rules: Dict[str, Sequence[str]], default: List[str], limit: int = 4) -> List[str]:
lower = text.lower()
found = [label for label, keywords in rules.items() if any(keyword in lower for keyword in keywords)]
ordered: List[str] = []
for item in found + default:
if item not in ordered:
ordered.append(item)
return ordered[:limit]
def _shorten(text: str, limit: int = 140) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
PURPOSE_RULES = {
'Setting a boundary': ['boundary', 'stop', 'too much', 'late at night', 'need space', 'not okay'],
'Requesting a change': ['ask', 'request', 'need you to', 'can we', 'change'],
'Repairing': ['repair', 'apologize', 'sorry', 'reconnect', 'fix this'],
'Informing clearly': ['tell', 'inform', 'update', 'let you know'],
}
RELATION_RULES = {
'Manager or coworker': ['boss', 'manager', 'coworker', 'colleague', 'team'],
'Partner': ['partner', 'husband', 'wife', 'girlfriend', 'boyfriend', 'spouse'],
'Parent or family member': ['parent', 'mom', 'dad', 'family', 'relative'],
'Friend': ['friend', 'buddy'],
}
SAFETY_KEYWORDS = ['abuse', 'unsafe', 'threat', 'violence', 'coercion']
def _purpose(text: str) -> str:
return _detect_one(text, PURPOSE_RULES, 'Informing clearly')
def _relationship(text: str) -> str:
return _detect_one(text, RELATION_RULES, 'Important relationship')
def _default_outcome(purpose: str) -> str:
mapping = {
'Setting a boundary': 'A clear limit is heard and the next expectation is explicit.',
'Requesting a change': 'The other person understands the request and the next step is concrete.',
'Repairing': 'The conversation moves toward repair without erasing accountability.',
'Informing clearly': 'The message is delivered directly, with less guessing and less over-explaining.',
}
return mapping.get(purpose, 'The message lands clearly and calmly.')
def _opening_line(purpose: str, key_message: str) -> str:
if purpose == 'Setting a boundary':
return f'I want to say this clearly because it matters: {key_message}. Going forward, I need a different pattern.'
if purpose == 'Requesting a change':
return f'I want to talk about one specific change that would help: {key_message}.'
if purpose == 'Repairing':
return f'I want to own my part and say this clearly: {key_message}. I care more about repair than winning the point.'
return f'I want to say this directly so there is less guessing between us: {key_message}.'
def _responses(purpose: str) -> List[Dict[str, str]]:
base = [
{
'if_they_say': 'You are overreacting.',
'reply': 'You may see it differently, and I still need to be clear about the impact on me.',
},
{
'if_they_say': 'I did not mean it that way.',
'reply': 'I hear that, and I am talking about the effect and the change I need now.',
},
{
'if_they_say': 'Let us do this later.',
'reply': 'If now is not workable, let us set a specific time so it does not drift.',
},
]
if purpose == 'Repairing':
base[0] = {
'if_they_say': 'I am still hurt.',
'reply': 'I understand that, and I am not asking you to skip the impact. I want to understand it and repair what I can.',
}
if purpose == 'Requesting a change':
base[1] = {
'if_they_say': 'This is just how I do things.',
'reply': 'I hear that it is familiar, and I am still asking for a different pattern going forward.',
}
return base
def _boundary_focus(purpose: str) -> str:
if purpose == 'Setting a boundary':
return 'Whether the limit is allowed to stay in place.'
if purpose == 'Repairing':
return 'A circular argument about intentions with no ownership or next step.'
return 'Repeating the same point without moving toward a decision or next step.'
def _boundary_sentence(purpose: str) -> str:
if purpose == 'Setting a boundary':
return 'I am willing to discuss next steps, but I am not willing to keep discussing whether this boundary should exist.'
if purpose == 'Repairing':
return 'I am open to repair, and I am not willing to keep turning this into a fight about who is more wrong.'
return 'I am happy to keep this respectful, and I am not going to keep circling the same argument.'
def _close_line(purpose: str) -> str:
if purpose == 'Requesting a change':
return 'Thanks for hearing me. The next thing I want is one concrete agreement about what changes after this conversation.'
if purpose == 'Repairing':
return 'Thank you for staying in this. I would like us to leave with one repair step or one follow-up time.'
return 'Thank you for hearing me. I wanted to be clear, and I want the next step to match what we just discussed.'
def handle(inputs):
meta = _load_skill_meta('difficult-conversation-rehearser')
data = _coerce_dict(inputs)
text = _normalize_inputs(inputs)
key_message = _pick_text(data, ['message', 'what_i_need_to_say', 'issue', 'topic'], _shorten(text, 140) or 'Say the important thing clearly and briefly.')
purpose = _pick_text(data, ['purpose', 'goal'], _purpose(text))
relationship = _pick_text(data, ['relationship', 'person'], _relationship(text))
outcome = _pick_text(data, ['outcome', 'desired_outcome'], _default_outcome(purpose))
responses = _responses(purpose)
note = ''
if any(keyword in text.lower() for keyword in SAFETY_KEYWORDS):
note = 'If safety, threats, or coercion are involved, prioritize support, documentation, and protection over rehearsal alone.'
lines: List[str] = []
lines.append('# Conversation Rehearsal Brief')
lines.append('')
if note:
lines.append(f'> {note}')
lines.append('')
lines.append('## Purpose')
lines.append(f'- What I need to say: {key_message}')
lines.append(f'- What outcome I want: {outcome}')
lines.append(f'- Conversation goal: {purpose}')
lines.append(f'- Relationship context: {relationship}')
lines.append('')
lines.append('## Opening')
lines.append(f'- {_opening_line(purpose, key_message)}')
lines.append('')
lines.append('## Likely Responses and My Replies')
for idx, item in enumerate(responses[:3], 1):
lines.append(f'{idx}. If they say: {item["if_they_say"]}')
lines.append(f' - I can reply: {item["reply"]}')
lines.append('')
lines.append('## Boundaries')
lines.append(f'- What I will not keep debating: {_boundary_focus(purpose)}')
lines.append(f'- My boundary sentence: {_boundary_sentence(purpose)}')
lines.append('')
lines.append('## Close')
lines.append(f'- How I will end the conversation: {_close_line(purpose)}')
return '\n'.join(lines)
if __name__ == "__main__":
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_boundary_scenario_detects_goal():
output = handle('I need to set a boundary with a coworker who keeps messaging me late at night')
assert '# Conversation Rehearsal Brief' in output
assert '- Conversation goal: Setting a boundary' in output
def test_dict_input_preserves_requested_outcome():
output = handle({
'purpose': 'Requesting a change',
'message': 'I need clearer turnaround times on edits',
'outcome': 'A more predictable workflow next week'
})
assert 'I need clearer turnaround times on edits' in output
assert 'A more predictable workflow next week' in output
def test_output_contains_boundary_sentence_and_close():
output = handle('I need to tell a friend I cannot keep cancelling my own plans for them')
assert '- My boundary sentence:' in output
assert '## Close' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Generate tailored creative prompts that diagnose and overcome specific blocks in various domains, enabling immediate and structured creative progress.
---
name: creative-prompts-arsenal
description: Generate domain-aware creative prompts that match the user's blockage, from blank-page paralysis to stale style or too many ideas. Use when someone needs better prompts, not more pressure. Chinese alias: 创意灵感库.
---
# Creative Prompts Arsenal / 创意灵感库
Use this skill when a user feels creatively stuck and needs prompts that unlock movement fast.
## What it helps with
- Identifying the creative domain, such as writing, design, parenting, teaching, or business
- Diagnosing the type of stuckness, such as blank page, too many ideas, weak direction, stale style, or fear of judgment
- Selecting prompt families that fit the blockage
- Generating a compact set of prompts across divergent and convergent modes
- Highlighting one start-now prompt that can be answered immediately
- Offering a simple sequencing order from warm-up to refinement
## Workflow
1. Identify the creative domain.
2. Diagnose the type of stuckness.
3. Select prompt families that fit the blockage.
4. Generate a compact prompt set across expansion, inversion, remix, and refinement.
5. Highlight one start-now prompt that creates immediate motion.
6. Optionally suggest a sequence from warm-up to sharper editing.
## Output format
```markdown
# Creative Prompt Kit
## Context
- Domain:
- What feels stuck:
## Prompt Set
### Expand
1. ...
2. ...
### Invert
1. ...
2. ...
### Remix
1. ...
2. ...
### Refine
1. ...
2. ...
## Best First Prompt
- Start with:
- Why this one first:
```
## Quality bar
- Prompts are adapted to the user's domain and blockage.
- Include both idea generation and idea sharpening.
- At least one prompt creates immediate motion within a few minutes.
- Avoid vague "be creative" language.
## Limits
- If the user lacks domain basics, prompts alone may not solve the problem.
- Too many prompts can become a new avoidance strategy.
- Some creative blocks need reassurance, not only ideation.
- Descriptive support only, with no external search or model chaining.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts: List[str] = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item) for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _listify(value: Any) -> List[str]:
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
return []
if isinstance(value, str):
clean = value.replace(';', '\n').replace(',', '\n')
return [chunk.strip() for chunk in clean.splitlines() if chunk.strip()]
if isinstance(value, (list, tuple, set)):
return [str(item).strip() for item in value if str(item).strip()]
return [str(value).strip()]
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _detect_many(text: str, rules: Dict[str, Sequence[str]], default: List[str], limit: int = 4) -> List[str]:
lower = text.lower()
found = [label for label, keywords in rules.items() if any(keyword in lower for keyword in keywords)]
ordered: List[str] = []
for item in found + default:
if item not in ordered:
ordered.append(item)
return ordered[:limit]
def _shorten(text: str, limit: int = 140) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
DOMAIN_RULES = {
'Writing': ['write', 'essay', 'article', 'story', 'book', 'blog', 'newsletter', 'script'],
'Design': ['design', 'ui', 'ux', 'layout', 'brand', 'logo', 'poster', 'visual'],
'Parenting': ['parent', 'child', 'kid', 'family activity', 'homeschool'],
'Teaching': ['teach', 'lesson', 'class', 'student', 'curriculum', 'workshop'],
'Business': ['business', 'offer', 'marketing', 'campaign', 'product', 'sales', 'service'],
}
STUCKNESS_RULES = {
'Blank page': ['blank page', 'stuck', 'start', 'nothing', 'empty', 'first line'],
'Too many ideas': ['too many ideas', 'too many directions', 'too many options', 'scattered'],
'Weak direction': ['direction', 'unclear', 'not sure where to go', 'weak idea'],
'Stale style': ['stale', 'same', 'boring', 'flat', 'predictable'],
'Fear of judgment': ['judged', 'cringe', 'embarrassed', 'not good enough', 'fear'],
}
DOMAIN_FRAMES = {
'Writing': {'artifact': 'piece', 'audience': 'reader', 'raw_material': 'scene, argument, or paragraph'},
'Design': {'artifact': 'concept', 'audience': 'user', 'raw_material': 'layout, flow, or visual system'},
'Parenting': {'artifact': 'idea', 'audience': 'child or family', 'raw_material': 'routine, activity, or conversation'},
'Teaching': {'artifact': 'lesson', 'audience': 'student', 'raw_material': 'example, explanation, or activity'},
'Business': {'artifact': 'offer', 'audience': 'customer or team', 'raw_material': 'message, test, or customer problem'},
'General': {'artifact': 'project', 'audience': 'intended audience', 'raw_material': 'core material'},
}
def _context_text(data: Dict[str, Any], text: str) -> str:
return _pick_text(data, ['project', 'topic', 'idea', 'brief', 'context'], _shorten(text, 100) or 'this project')
def _prompt_sets(domain: str, stuckness: str, context: str) -> Dict[str, List[str]]:
frame = DOMAIN_FRAMES.get(domain, DOMAIN_FRAMES['General'])
artifact = frame['artifact']
audience = frame['audience']
raw_material = frame['raw_material']
return {
'Expand': [
f'List 10 possible angles for {context} without judging them yet.',
f'What would make this {artifact} 20% more vivid, useful, or surprising for the {audience}?',
],
'Invert': [
f'If the obvious approach is wrong, what opposite move would you try with the {raw_material}?',
f'What would you remove, mute, or simplify so the core of {context} becomes easier to feel?',
],
'Remix': [
f'Borrow one pattern from an unrelated field and apply it to this {artifact}.',
f'Reframe {context} for a completely different audience, age group, or setting, then steal one useful insight back.',
],
'Refine': [
f'Choose one promising direction and state the single effect it should create for the {audience}.',
f'Name the next draft, sketch, or test you can finish in 15 minutes.',
],
}
def _best_first_prompt(stuckness: str, prompts: Dict[str, List[str]]) -> Dict[str, str]:
if stuckness == 'Blank page':
return {
'prompt': prompts['Expand'][0],
'why': 'It removes the pressure to be good and replaces it with immediate motion.',
}
if stuckness == 'Too many ideas':
return {
'prompt': prompts['Refine'][0],
'why': 'It narrows the field so the best direction becomes easier to test.',
}
if stuckness == 'Stale style':
return {
'prompt': prompts['Remix'][0],
'why': 'A fresh borrowed pattern often breaks sameness faster than more polishing.',
}
if stuckness == 'Fear of judgment':
return {
'prompt': prompts['Expand'][0],
'why': 'Quantity lowers the stakes and helps you move before your inner critic takes over.',
}
return {
'prompt': prompts['Invert'][0],
'why': 'A clean perspective shift usually restores direction when the problem feels muddy.',
}
def handle(inputs):
meta = _load_skill_meta('creative-prompts-arsenal')
data = _coerce_dict(inputs)
text = _normalize_inputs(inputs)
domain = _pick_text(data, ['domain'], _detect_one(text, DOMAIN_RULES, 'General'))
stuckness = _pick_text(data, ['stuckness', 'block', 'what_feels_stuck'], _detect_one(text, STUCKNESS_RULES, 'Weak direction'))
context = _context_text(data, text)
prompts = _prompt_sets(domain, stuckness, context)
best = _best_first_prompt(stuckness, prompts)
lines: List[str] = []
lines.append('# Creative Prompt Kit')
lines.append('')
lines.append('## Context')
lines.append(f'- Domain: {domain}')
lines.append(f'- What feels stuck: {stuckness}')
lines.append('')
lines.append('## Prompt Set')
for section in ['Expand', 'Invert', 'Remix', 'Refine']:
lines.append(f'### {section}')
for idx, prompt in enumerate(prompts[section], 1):
lines.append(f'{idx}. {prompt}')
lines.append('')
lines.append('## Best First Prompt')
lines.append(f'- Start with: {best["prompt"]}')
lines.append(f'- Why this one first: {best["why"]}')
lines.append('- Suggested order: Expand -> Invert -> Remix -> Refine')
return '\n'.join(lines)
if __name__ == "__main__":
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_blank_page_writing_context_is_detected():
output = handle('I have a blank page and need to write an article')
assert '# Creative Prompt Kit' in output
assert '- Domain: Writing' in output
assert '- What feels stuck: Blank page' in output
def test_dict_input_supports_business_context():
output = handle({
'domain': 'Business',
'stuckness': 'Too many ideas',
'project': 'a new consulting offer'
})
assert 'a new consulting offer' in output
assert 'Too many ideas' in output
def test_output_contains_best_first_prompt():
output = handle('My design work feels stale and repetitive')
assert '## Best First Prompt' in output
assert '- Start with:' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Helps assess social energy, evaluate upcoming events' impact, and create realistic plans with boundaries and recovery to manage social overload.
---
name: social-battery-monitor
description: Help a user assess current social energy, classify upcoming social load, protect recovery time, and prepare realistic boundary phrases. Use when someone feels socially drained, overcommitted, or unsure how to pace connection. Chinese alias: 社交电量监控师.
---
# Social Battery Monitor / 社交电量监控师
Use this skill when a user is trying to balance connection, obligations, and recovery without crashing socially.
## What it helps with
- Naming what social energy feels like when full, low, or depleted
- Spotting personal drain signals instead of using generic labels
- Reviewing upcoming events as nourishing, neutral, or draining
- Estimating energy cost by intensity, duration, closeness, and performance demand
- Adding pre-event buffers, exit strategies, and post-event recharge blocks
- Offering boundary phrases the user can actually say
## Workflow
1. Ask how social energy usually feels when full, low, and depleted.
2. Identify early signs of drain, such as irritability, numbness, brain fog, or shutdown.
3. Review upcoming social events and label them by likely cost and value.
4. Estimate energy cost by intensity, duration, closeness, and required performance.
5. Add buffers, exit strategies, and recharge blocks.
6. End with a simple protection plan and a recharge menu.
## Output format
```markdown
# Social Battery Plan
## Current Battery
- Current level:
- Signs I am already low:
## Upcoming Social Load
- Event:
- Expected cost:
- Expected value:
- Recovery needed:
## Protection Plan
- Before the event:
- During the event:
- Exit line:
- After the event:
## Recharge Menu
- Quick recharge:
- Deep recharge:
```
## Quality bar
- Use the user's own depletion cues, not generic personality labels.
- Include both recovery planning and boundary planning.
- Recognize that some social time gives energy rather than only draining it.
- Produce a workable plan for the next few days, not just abstract insight.
## Limits
- Unavoidable work or family obligations may limit ideal choices.
- Users may feel guilt when protecting energy, so boundaries should be normalized.
- The goal is pacing, not total avoidance.
- Descriptive support only, with no calendar sync or message sending.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts: List[str] = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item) for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _listify(value: Any) -> List[str]:
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
return []
if isinstance(value, str):
clean = value.replace(';', '\n').replace(',', '\n')
return [chunk.strip() for chunk in clean.splitlines() if chunk.strip()]
if isinstance(value, (list, tuple, set)):
return [str(item).strip() for item in value if str(item).strip()]
return [str(value).strip()]
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _detect_many(text: str, rules: Dict[str, Sequence[str]], default: List[str], limit: int = 4) -> List[str]:
lower = text.lower()
found = [label for label, keywords in rules.items() if any(keyword in lower for keyword in keywords)]
ordered: List[str] = []
for item in found + default:
if item not in ordered:
ordered.append(item)
return ordered[:limit]
def _shorten(text: str, limit: int = 140) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
BATTERY_RULES = {
'Depleted': ['depleted', 'shutdown', 'burned out', 'burnt out', 'exhausted'],
'Low': ['drained', 'tired', 'overwhelmed', 'brain fog', 'low battery', 'irritable'],
'Moderate': ['okay', 'fine', 'steady', 'medium'],
'Full': ['energized', 'excited', 'full battery', 'ready', 'good social energy'],
}
SIGN_RULES = {
'irritability': ['irritable', 'snappy', 'annoyed'],
'brain fog': ['brain fog', 'foggy', 'fried'],
'numbness': ['numb', 'flat'],
'urge to withdraw': ['hide', 'withdraw', 'avoid'],
'shutdown feeling': ['shutdown', 'silent', 'checked out'],
}
EVENT_RULES = {
'Networking event': ['networking', 'conference', 'mixer', 'industry event'],
'Team meeting or workshop': ['meeting', 'workshop', 'offsite', 'standup'],
'Family gathering': ['family', 'relative', 'parent', 'in-law'],
'Friend catch-up': ['friend', 'coffee', 'hangout', 'dinner with friends'],
'Party or celebration': ['party', 'birthday', 'wedding', 'celebration'],
'School or caregiving event': ['school', 'teacher', 'pickup', 'caregiving', 'parent meeting'],
}
def _battery_level(text: str) -> str:
return _detect_one(text, BATTERY_RULES, 'Moderate')
def _default_signs(level: str) -> List[str]:
mapping = {
'Depleted': ['brain fog', 'urge to withdraw', 'shutdown feeling'],
'Low': ['irritability', 'brain fog', 'urge to withdraw'],
'Moderate': ['mild fatigue by the end of the day'],
'Full': ['clear focus and easier small talk'],
}
return mapping.get(level, ['mild fatigue by the end of the day'])
def _event_entries(data: Dict[str, Any], text: str) -> List[str]:
provided = _listify(data.get('events'))
if provided:
return provided[:3]
return _detect_many(text, EVENT_RULES, ['Upcoming social commitments this week'], limit=3)
def _event_plan(label: str, level: str) -> Dict[str, str]:
lower = label.lower()
if 'network' in lower or 'conference' in lower:
cost = 'High' if level in ['Low', 'Depleted'] else 'Medium to high'
value = 'Opportunity, visibility, and useful connection'
recovery = '60 to 90 minutes of quiet time afterward'
elif 'family' in lower:
cost = 'Medium to high' if level in ['Low', 'Depleted'] else 'Medium'
value = 'Relationship maintenance and family presence'
recovery = 'A lighter evening or early exit buffer'
elif 'friend' in lower:
cost = 'Low to medium'
value = 'Potentially nourishing if the relationship feels safe and easy'
recovery = 'A short decompression block afterward'
elif 'party' in lower or 'celebration' in lower:
cost = 'High'
value = 'Joy or connection, but only if duration is capped'
recovery = 'A protected quiet block and no stacked plans after'
elif 'school' in lower or 'care' in lower:
cost = 'Medium'
value = 'Necessary presence and practical support'
recovery = '15 to 30 minutes alone before the next obligation'
elif 'meeting' in lower or 'workshop' in lower:
cost = 'Medium'
value = 'Work coordination and reduced future friction'
recovery = 'A short reset walk or no-meeting buffer afterward'
else:
cost = 'Medium'
value = 'Social maintenance with mixed payoff'
recovery = 'A short quiet reset afterward'
return {'cost': cost, 'value': value, 'recovery': recovery}
def _exit_line(event_label: str) -> str:
lower = event_label.lower()
if 'family' in lower:
return 'I can come for a shorter window, but I will not stay the whole time tonight.'
if 'meeting' in lower or 'workshop' in lower:
return 'I can stay for the key part, then I need to step out for another commitment.'
if 'friend' in lower:
return 'I want to see you, and I only have about an hour today, so I need to keep it short.'
return 'I can be there for the important part, then I need to head out and recharge.'
def _quick_recharge(level: str) -> str:
if level in ['Low', 'Depleted']:
return '10 to 20 minutes alone, no notifications, low light, and zero conversation.'
return 'A short walk, quiet tea, or one low-stimulation break between plans.'
def _deep_recharge(level: str) -> str:
if level in ['Low', 'Depleted']:
return 'A protected half-day or evening with no performance demands and no stacked social plans.'
return 'One longer block of solo time, sleep recovery, and a low-input activity.'
def handle(inputs):
meta = _load_skill_meta('social-battery-monitor')
data = _coerce_dict(inputs)
text = _normalize_inputs(inputs)
level = _pick_text(data, ['current_level', 'battery', 'energy_level'], _battery_level(text))
signs = _listify(data.get('signs')) or _detect_many(text, SIGN_RULES, _default_signs(level), limit=4)
events = _event_entries(data, text)
first_event = events[0] if events else 'Upcoming social commitments this week'
before = 'Protect a small buffer before the event, eat something, and decide the maximum duration in advance.'
if level in ['Low', 'Depleted']:
before = 'Protect 30 to 60 minutes alone before the event, reduce extra errands, and decide the shortest acceptable version.'
during = 'Check body cues halfway through, step outside if needed, and avoid performing harder than the situation requires.'
after = 'Block a recharge window right after the event instead of stacking another draining plan.'
if level in ['Low', 'Depleted']:
after = 'Leave a clean recovery block afterward with no calls, no chores, and no surprise social spillover.'
lines: List[str] = []
lines.append('# Social Battery Plan')
lines.append('')
lines.append('## Current Battery')
lines.append(f'- Current level: {level}')
lines.append(f"- Signs I am already low: {', '.join(signs)}")
lines.append('')
lines.append('## Upcoming Social Load')
for event in events:
plan = _event_plan(event, level)
lines.append(f'- Event: {event}')
lines.append(f' - Expected cost: {plan["cost"]}')
lines.append(f' - Expected value: {plan["value"]}')
lines.append(f' - Recovery needed: {plan["recovery"]}')
lines.append('')
lines.append('## Protection Plan')
lines.append(f'- Before the event: {before}')
lines.append(f'- During the event: {during}')
lines.append(f'- Exit line: {_exit_line(first_event)}')
lines.append(f'- After the event: {after}')
lines.append('')
lines.append('## Recharge Menu')
lines.append(f'- Quick recharge: {_quick_recharge(level)}')
lines.append(f'- Deep recharge: {_deep_recharge(level)}')
return '\n'.join(lines)
if __name__ == "__main__":
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_drained_input_flags_low_battery():
output = handle('I am drained and I still have a networking event tonight')
assert '# Social Battery Plan' in output
assert '- Current level: Low' in output or '- Current level: Depleted' in output
def test_dict_input_renders_events():
output = handle({
'current_level': 'Moderate',
'events': ['Family gathering', 'Friend catch-up']
})
assert 'Family gathering' in output
assert 'Friend catch-up' in output
def test_output_contains_exit_line_and_recharge_menu():
output = handle('Too many meetings and a birthday party this week')
assert '- Exit line:' in output
assert '## Recharge Menu' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Helps break a big goal into clear milestones, next actions, dependencies, and a review checkpoint for actionable progress and reduced overwhelm.
---
name: goal-chunking-helper
description: Break a vague or overwhelming goal into milestones, tiny next actions, visible dependencies, and a review checkpoint. Use when a user has a big goal but needs a concrete plan. Chinese alias: 目标拆解帮手.
---
# Goal Chunking Helper / 目标拆解帮手
Use this skill when a user has a meaningful goal but cannot see the next concrete move.
## What it helps with
- Turning a large outcome into a smaller, usable path
- Breaking the goal into milestones, phases, or sub-results
- Separating planning actions from execution actions
- Surfacing dependencies before they turn into delays
- Ending with the next three executable steps and a review checkpoint
## Workflow
1. Define the goal in outcome language and add a deadline or review horizon.
2. Reverse-engineer the goal into milestones, phases, or sub-results.
3. Identify dependencies, missing resources, and likely blockers.
4. Convert the first milestone into very small action units.
5. Separate planning work from real execution work.
6. End with the next three executable steps and one checkpoint question.
## Output format
```markdown
# Goal Breakdown
## Goal Definition
- Goal:
- Why it matters:
- Time horizon:
## Milestone Ladder
1. ...
2. ...
3. ...
## Next Actions
- Action 1:
- Action 2:
- Action 3:
## Dependencies and Risks
- ...
## Review Checkpoint
- By [date], I will know progress if:
```
## Quality bar
- Every next action starts with a verb and is easy to begin.
- The user can tell what to do today, not just what matters eventually.
- Dependencies are visible before they cause delay.
- The plan reduces overwhelm instead of creating a giant task list.
## Limits
- If the goal itself is unclear, the first task may be clarification rather than execution.
- Some goals need nested decomposition, not just one layer of planning.
- Unrealistic timelines should be challenged directly.
- Descriptive planning only, with no project-management software integration.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts: List[str] = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item) for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _listify(value: Any) -> List[str]:
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
return []
if isinstance(value, str):
clean = value.replace(';', '\n').replace(',', '\n')
return [chunk.strip() for chunk in clean.splitlines() if chunk.strip()]
if isinstance(value, (list, tuple, set)):
return [str(item).strip() for item in value if str(item).strip()]
return [str(value).strip()]
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _detect_many(text: str, rules: Dict[str, Sequence[str]], default: List[str], limit: int = 4) -> List[str]:
lower = text.lower()
found = [label for label, keywords in rules.items() if any(keyword in lower for keyword in keywords)]
ordered: List[str] = []
for item in found + default:
if item not in ordered:
ordered.append(item)
return ordered[:limit]
def _shorten(text: str, limit: int = 140) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
GOAL_TYPE_RULES = {
'Learning or study': ['learn', 'study', 'course', 'exam', 'practice', 'certification', 'class'],
'Writing or shipping': ['write', 'book', 'essay', 'article', 'draft', 'publish', 'portfolio', 'newsletter'],
'Health or fitness': ['exercise', 'fitness', 'run', 'sleep', 'health', 'workout', 'diet', 'training'],
'Business or launch': ['launch', 'business', 'product', 'client', 'sales', 'marketing', 'offer', 'startup'],
'Family or home': ['family', 'home', 'parent', 'kid', 'child', 'house', 'routine'],
}
BLOCKER_RULES = {
'Clarity gap around what done looks like': ['unclear', 'not sure', 'vague', 'confused'],
'Time availability and calendar protection': ['busy', 'time', 'schedule', 'calendar'],
'Missing information, research, or examples': ['research', 'information', 'data', 'example'],
'Skill or confidence gap': ['new', 'learn', 'practice', 'confidence', 'skill'],
'Budget, tools, or resource access': ['money', 'budget', 'tool', 'software', 'equipment'],
'Coordination with other people': ['team', 'manager', 'client', 'partner', 'family'],
}
def _extract_time_horizon(text: str) -> str:
lower = text.lower()
patterns = [
r'by ([a-z0-9\- ]+)',
r'in (\d+\s+(?:day|days|week|weeks|month|months))',
r'within (\d+\s+(?:day|days|week|weeks|month|months))',
]
for pattern in patterns:
match = re.search(pattern, lower)
if match:
return match.group(0)
for token in ['today', 'tomorrow', 'this week', 'next week', 'this month', 'next month', 'this quarter', 'next quarter']:
if token in lower:
return token
return ''
def _goal_milestones(goal_type: str, unclear: bool) -> List[str]:
if unclear:
return [
'Clarify the outcome, success criteria, and review horizon before doing more work.',
'Name the first milestone and the minimum resources needed to start it.',
'Run a short execution cycle, then review scope, pace, and blockers.',
]
mapping = {
'Learning or study': [
'Define the exact skill target, scope, and baseline level.',
'Build a repeatable practice plan and complete the first learning block.',
'Test understanding in a real output, drill, or exam-style checkpoint.',
],
'Writing or shipping': [
'Lock the core message, audience, and outline of the deliverable.',
'Complete a rough draft or first shippable version without over-editing.',
'Revise, polish, and deliver or publish with a feedback checkpoint.',
],
'Health or fitness': [
'Define the target behavior and the smallest repeatable routine.',
'Set up the environment, schedule, and tracking needed for consistency.',
'Review results after the first week and adjust load or frequency.',
],
'Business or launch': [
'Define the offer, audience, and concrete launch outcome.',
'Build the minimum assets, process, or prototype needed to test it.',
'Run a first launch or outreach cycle and review the signal honestly.',
],
'Family or home': [
'Clarify the desired outcome, who is affected, and what must change first.',
'Prepare the first small system, routine, or decision that reduces friction.',
'Review what held up in real life and simplify the next round.',
],
}
return mapping.get(goal_type, [
'Define what success looks like and what the first milestone actually is.',
'Complete the first milestone with a simple, time-bounded plan.',
'Review progress, blockers, and the next stretch of work.',
])
def _goal_actions(goal_type: str, unclear: bool, goal: str) -> List[str]:
if unclear:
return [
'Clarify the outcome by writing one sentence that defines what done looks like.',
'List the first milestone, the time available this week, and the main blocker.',
'Schedule one 25-minute session to start the smallest useful action.',
]
mapping = {
'Learning or study': [
'Define the exact topic, level, and review date for the learning goal.',
'Choose one resource and complete the first focused study block.',
'Practice by solving one exercise, quiz, or mini-project today.',
],
'Writing or shipping': [
'Define the audience, promise, and rough outline of the deliverable.',
'Draft the ugliest usable first version of the opening or core section.',
'Schedule one revision pass with a clear finish line.',
],
'Health or fitness': [
'Define the smallest version of the routine you can repeat this week.',
'Prepare the environment, gear, or trigger that removes startup friction.',
'Start one short session today and log how it felt afterward.',
],
'Business or launch': [
'Define the offer, audience, and success metric for the first test.',
'List the minimum assets or conversations needed before launch.',
'Start one concrete build or outreach task that creates real signal.',
],
'Family or home': [
'Define the one outcome that would make daily life feel easier.',
'List who needs to be involved and what decision must happen first.',
'Start one small setup task that reduces friction today.',
],
}
return mapping.get(goal_type, [
'Define the first milestone in one sentence.',
'List the smallest actions that move the milestone forward.',
'Schedule the first work block on the calendar.',
])
def handle(inputs):
meta = _load_skill_meta('goal-chunking-helper')
data = _coerce_dict(inputs)
text = _normalize_inputs(inputs)
lower = text.lower()
goal = _pick_text(data, ['goal', 'outcome', 'objective', 'project'], _shorten(text, 120) or 'Clarify the real goal before execution')
why = _pick_text(data, ['why', 'reason', 'importance', 'motivation'], 'Reduce overwhelm and create a path that is easier to start.')
horizon = _pick_text(data, ['deadline', 'time_horizon', 'review_horizon', 'review', 'by'], _extract_time_horizon(text)) or 'Set a review point within the next 1 to 4 weeks.'
goal_type = _detect_one(text, GOAL_TYPE_RULES, 'General')
unclear = len(goal.split()) < 4 or any(token in goal.lower() for token in ['something', 'better', 'figure it out', 'improve life'])
milestones = _goal_milestones(goal_type, unclear)
actions = _goal_actions(goal_type, unclear, goal)
dependencies = _listify(data.get('dependencies')) or _detect_many(text, BLOCKER_RULES, [
'Time availability and calendar protection',
'Missing information, research, or examples',
'A realistic review checkpoint',
], limit=4)
if any(token in lower for token in ['today', 'tomorrow', 'this week']) and goal_type in ['Learning or study', 'Writing or shipping', 'Business or launch']:
if 'Timeline compression, which may require a smaller first milestone' not in dependencies:
dependencies.append('Timeline compression, which may require a smaller first milestone')
lines: List[str] = []
lines.append('# Goal Breakdown')
lines.append('')
lines.append('## Goal Definition')
lines.append(f'- Goal: {goal}')
lines.append(f'- Why it matters: {why}')
lines.append(f'- Time horizon: {horizon}')
lines.append('')
lines.append('## Milestone Ladder')
for idx, milestone in enumerate(milestones, 1):
lines.append(f'{idx}. {milestone}')
lines.append('')
lines.append('## Next Actions')
for idx, action in enumerate(actions[:3], 1):
lines.append(f'- Action {idx}: {action}')
lines.append('')
lines.append('## Dependencies and Risks')
for item in dependencies[:5]:
lines.append(f'- {item}')
if 'Set a review point within the next 1 to 4 weeks.' in horizon:
checkpoint = 'the next review date'
else:
checkpoint = horizon
lines.append('')
lines.append('## Review Checkpoint')
lines.append(f'- Progress is real if, by {checkpoint}, milestone 1 is complete, the next actions are scheduled, and the main blocker is named.')
return '\n'.join(lines)
if __name__ == "__main__":
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_string_input_generates_goal_breakdown():
output = handle('I want to launch a small course by next month')
assert '# Goal Breakdown' in output
assert '## Milestone Ladder' in output
assert '## Next Actions' in output
def test_dict_input_preserves_goal_details():
output = handle({
'goal': 'Finish a writing portfolio',
'why': 'Apply for new roles with stronger samples',
'deadline': 'in 6 weeks'
})
assert 'Finish a writing portfolio' in output
assert 'Apply for new roles with stronger samples' in output
assert 'in 6 weeks' in output
def test_output_contains_action_slots_and_dependencies():
output = handle('Learn Python for data analysis this quarter')
assert '- Action 1:' in output
assert '- Action 2:' in output
assert '## Dependencies and Risks' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Turn several days of observation into a simple energy pattern review with likely peak windows, low windows, disruptors, task matching, and a one-week experim...
---
name: energy-peak-finder
description: Turn several days of observation into a simple energy pattern review with likely peak windows, low windows, disruptors, task matching, and a one-week experiment. Use when the user wants to schedule deep work, admin, recovery, and social tasks around real energy patterns instead of guesswork.
---
# Energy Peak Finder
## Overview
Use this skill to spot when the user's energy actually supports focus, creativity, stamina, and recovery. It helps the user observe repeated peaks and troughs, separate sustainable energy from urgency or caffeine spikes, and match task types to the most realistic time windows.
This skill is descriptive only. It does not use wearables, calendars, biometrics, or analytics backends.
## Trigger
Use this skill when the user wants to:
- find their best window for deep work
- understand daily energy slumps
- distinguish true energy from urgency or caffeine buzz
- match task type to time of day
- run a one-week experiment before locking in a schedule
### Example prompts
- "Help me figure out when my energy peaks"
- "I keep doing hard work at the wrong time of day"
- "Map my focus windows from these notes"
- "Turn my energy observations into a weekly experiment"
## Workflow
1. Review several days of energy, alertness, mood, hunger, and interruptions.
2. Divide the day into simple time blocks.
3. Identify repeated peaks, troughs, and false peaks.
4. Note confounders like sleep, meals, workouts, caffeine, and interruptions.
5. Match task types to each energy band.
6. Suggest a one-week experiment to validate the pattern.
## Inputs
The user can provide any mix of:
- notes about mornings, afternoons, and evenings
- focus, creativity, stamina, or crash periods
- sleep quality, meals, caffeine, or workout context
- interruptions from work or family
- task types that feel easier or harder at different times
- notes from several days or one rough week
## Outputs
Return a markdown energy review with:
- observation summary
- task matching by energy band
- hypotheses about what boosts or drains energy
- one seven-day experiment
## Safety
- Use repeated observations when possible, not one unusually good or bad day.
- Separate energy from mood, panic, or deadline urgency.
- Keep recommendations flexible when family, health, or work obligations limit ideal scheduling.
- Do not turn the pattern into a fixed identity label too quickly.
## Acceptance Criteria
- Return markdown text.
- Identify a best window, lowest window, and common disruptors.
- Match peak, medium, and low-energy tasks to usable time bands.
- End with a one-week experiment.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
BLOCK_KEYWORDS = {
'early morning': ['early morning', '6am', '7am', 'wake up', 'first thing'],
'late morning': ['late morning', '9am', '10am', '11am', 'mid-morning'],
'early afternoon': ['early afternoon', 'after lunch', '1pm', '2pm'],
'late afternoon': ['late afternoon', '3pm', '4pm', '5pm'],
'evening': ['evening', 'night', '7pm', '8pm', '9pm'],
}
POSITIVE_WORDS = ['sharp', 'clear', 'focused', 'best', 'good', 'great', 'creative', 'strong', 'steady', 'alert']
NEGATIVE_WORDS = ['crash', 'slump', 'low', 'foggy', 'tired', 'drained', 'flat', 'sleepy', 'hard']
DISRUPTOR_RULES = [
('poor sleep', ['poor sleep', 'slept badly', 'sleep debt', 'up late']),
('caffeine spikes and crashes', ['coffee', 'caffeine', 'espresso']),
('meal timing or after-lunch dip', ['after lunch', 'hungry', 'meal', 'ate late']),
('family or work interruptions', ['interrupt', 'kids', 'child', 'meeting', 'messages']),
('workout timing', ['workout', 'exercise', 'run', 'gym']),
]
class EnergyPeakFinder:
def __init__(self, inputs: Any):
self.raw = _normalize_inputs(inputs)
self.lower = self.raw.lower()
self.snippets = self._snippets()
self.block_scores = self._score_blocks()
self.best_window = self._best_window()
self.lowest_window = self._lowest_window()
self.disruptors = self._disruptors()
self.hypotheses = self._hypotheses()
self.task_matching = self._task_matching()
self.experiment = self._experiment()
def _snippets(self) -> List[str]:
parts = re.split(r'[\n.;!?]+|,\s*but\s+|\s+but\s+', self.raw, flags=re.IGNORECASE)
return [part.strip(' -•') for part in parts if part.strip(' -•')]
def _score_blocks(self) -> Dict[str, int]:
scores = {block: 0 for block in BLOCK_KEYWORDS}
mentioned = False
for snippet in self.snippets:
lower = snippet.lower()
positive = sum(1 for word in POSITIVE_WORDS if word in lower)
negative = sum(1 for word in NEGATIVE_WORDS if word in lower)
delta = positive - negative
for block, keywords in BLOCK_KEYWORDS.items():
if any(keyword in lower for keyword in keywords):
scores[block] += delta if delta != 0 else 1
mentioned = True
if not mentioned:
scores['late morning'] = 2
scores['early afternoon'] = -1
return scores
def _best_window(self) -> str:
return max(self.block_scores, key=self.block_scores.get)
def _lowest_window(self) -> str:
return min(self.block_scores, key=self.block_scores.get)
def _disruptors(self) -> List[str]:
found = [label for label, words in DISRUPTOR_RULES if any(word in self.lower for word in words)]
if 'urgent' in self.lower or 'deadline' in self.lower or 'adrenaline' in self.lower:
found.append('urgency may be disguising itself as usable energy')
if not found:
found.append('not enough repeated disruptor data yet')
unique: List[str] = []
for item in found:
if item not in unique:
unique.append(item)
return unique
def _hypotheses(self) -> Dict[str, str]:
boosts: List[str] = []
drains: List[str] = []
if any(word in self.lower for word in ['slept well', 'good sleep', 'early bed']):
boosts.append('Sleep quality appears to lift your steadier focus windows.')
if any(word in self.lower for word in ['walk', 'exercise', 'workout', 'sunlight']):
boosts.append('Movement or light exposure may be supporting alertness.')
if self.best_window == 'late morning':
boosts.append('Late morning looks like your most reliable cognitive window right now.')
if any(word in self.lower for word in ['coffee', 'caffeine', 'espresso']):
drains.append('Caffeine may be creating a false peak or a sharper later drop.')
if any(word in self.lower for word in ['after lunch', 'hungry', 'meal', 'ate late']):
drains.append('Meal timing may be contributing to an afternoon dip.')
if any(word in self.lower for word in ['interrupt', 'kids', 'meeting', 'messages']):
drains.append('Interruptions are likely reducing usable energy even when alertness exists.')
if not boosts:
boosts.append('Your best energy window needs one more week of observation to confirm.')
if not drains:
drains.append('The main drain is not fully clear yet, so keep tracking sleep, meals, and interruptions.')
return {
'boosts': ' '.join(boosts[:2]),
'drains': ' '.join(drains[:2]),
}
def _task_matching(self) -> Dict[str, str]:
return {
'peak': f'Use {self.best_window} for deep work, writing, strategy, or hard problem-solving.',
'medium': 'Use the middle windows for planning, meetings, lighter study, and admin that still needs attention.',
'low': f'Use {self.lowest_window} for chores, inbox, recovery, or lower-stakes tasks.',
}
def _experiment(self) -> str:
confounders = ', '.join(self.disruptors[:2])
return f'For the next 7 days, protect {self.best_window} for your hardest task, keep {self.lowest_window} lighter, and log sleep, meals, and {confounders} once per day.'
def render(self) -> str:
lines: List[str] = []
lines.append('# Energy Pattern Review')
lines.append('')
lines.append('## Observation Summary')
lines.append(f'- Best energy window: {self.best_window}')
lines.append(f'- Lowest energy window: {self.lowest_window}')
lines.append(f"- Most common disruptors: {', '.join(self.disruptors)}")
lines.append('')
lines.append('## Task Matching')
lines.append(f"- Peak energy tasks: {self.task_matching['peak']}")
lines.append(f"- Medium energy tasks: {self.task_matching['medium']}")
lines.append(f"- Low energy tasks: {self.task_matching['low']}")
lines.append('')
lines.append('## Hypotheses')
lines.append(f"- What seems to boost energy: {self.hypotheses['boosts']}")
lines.append(f"- What seems to drain energy: {self.hypotheses['drains']}")
lines.append('')
lines.append('## Next Experiment')
lines.append(f'- For the next 7 days, I will: {self.experiment}')
return '\n'.join(lines)
def handle(inputs):
_load_skill_meta('energy-peak-finder')
finder = EnergyPeakFinder(inputs)
return finder.render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import EnergyPeakFinder, handle
def test_best_window_detection():
finder = EnergyPeakFinder('Late morning is when I feel sharp and focused, but I crash in the early afternoon after lunch.')
assert finder.best_window == 'late morning'
assert finder.lowest_window == 'early afternoon'
def test_disruptor_detection():
finder = EnergyPeakFinder('I get a good 10am burst, then coffee and after lunch make me foggy.')
joined = ' '.join(finder.disruptors)
assert 'caffeine' in joined or 'lunch' in joined or 'meal' in joined
def test_output_sections():
output = handle('My energy is best in the late morning, but meetings and messages kill the afternoon.')
assert output.startswith('# Energy Pattern Review')
assert '## Observation Summary' in output
assert '## Task Matching' in output
assert '## Next Experiment' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Build a realistic evening wind-down plan with a ramp-down timeline, environment checklist, last stimulating action boundary, fallback version, and next-morni...
---
name: sleep-wind-down-coach
description: Build a realistic evening wind-down plan with a ramp-down timeline, environment checklist, last stimulating action boundary, fallback version, and next-morning feedback prompts. Use when the user stays mentally on until bed and needs a gentler transition from stimulation to sleep.
---
# Sleep Wind-Down Coach
## Overview
Use this skill to create a clear transition from stimulation to rest. It helps the user define a bedtime target, build a 30, 60, or 90 minute wind-down ladder, reduce late-night stimulation, and keep a fallback version for imperfect nights.
This skill is descriptive wellness guidance only. It is not treatment for insomnia or a clinical sleep disorder.
## Trigger
Use this skill when the user wants to:
- stop working or scrolling right up to bedtime
- build a wind-down routine that starts before bed
- reduce mental activation late at night
- create a fallback routine for already-late evenings
- review how evening choices affect next-morning mood
### Example prompts
- "Help me build a 60 minute wind-down routine"
- "I stay mentally on until bed and sleep feels hard"
- "Make me a fallback bedtime routine for late nights"
- "I want to stop checking work email before sleep"
## Workflow
1. Identify the desired bedtime, current pattern, and drift points.
2. Choose a 30, 60, or 90 minute ramp-down ladder.
3. Sequence calming actions and one clear boundary for stimulating inputs.
4. Add environment adjustments for light, screens, temperature, and noise.
5. Create a fallback version for late nights.
6. Review the next morning how the plan affected sleep onset and mood.
## Inputs
The user can provide any mix of:
- desired bedtime
- actual bedtime or drift pattern
- late-night work or screen habits
- evening stimulation triggers
- hygiene, stretching, reading, or breathing preferences
- home constraints like children, noise, or irregular schedules
- how they usually feel on waking
## Outputs
Return a markdown wind-down plan with:
- sleep target and current pattern
- ramp-down timeline
- environment checklist
- fallback version
- morning feedback questions
## Safety
- Start before bedtime, not at bedtime.
- Include at least one behavioral and one environmental adjustment.
- Keep the routine realistic enough that it does not become a perfection ritual.
- Do not present the skill as diagnosis, medication advice, or treatment.
## Acceptance Criteria
- Return markdown text.
- Include a bedtime target, timeline, and boundary for stimulation.
- Include a fallback version for imperfect nights.
- Include next-morning feedback prompts.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
TIME_PATTERN = r'(\d{1,2}(?::\d{2})?\s*(?:am|pm)?)'
class SleepWindDownCoach:
def __init__(self, inputs: Any):
self.raw = _normalize_inputs(inputs)
self.lower = self.raw.lower()
self.desired_bedtime = self._desired_bedtime()
self.current_pattern = self._current_pattern()
self.ladder_minutes = self._ladder_minutes()
self.timeline = self._timeline()
self.environment = self._environment()
self.fallback = self._fallback()
def _desired_bedtime(self) -> str:
patterns = [
rf'(?:bed by|asleep by|bedtime\s*(?:is|:)?|target bedtime\s*(?:is|:)?|want to be asleep by)\s*{TIME_PATTERN}',
rf'(?:go to bed by)\s*{TIME_PATTERN}',
]
for pattern in patterns:
match = re.search(pattern, self.lower)
if match:
return match.group(1).strip()
return '10:30 pm'
def _current_pattern(self) -> str:
patterns = [
rf'(?:usually|currently|actual bedtime is|but usually)\s+(?:around\s+)?{TIME_PATTERN}',
rf'(?:stay up until|scroll until|work until|awake until)\s*{TIME_PATTERN}',
]
for pattern in patterns:
match = re.search(pattern, self.lower)
if match:
return f'Usually closer to {match.group(1).strip()}'
if any(word in self.lower for word in ['late', 'drift', 'too late']):
return 'Bedtime drifts later than intended.'
return 'Current pattern not clearly specified.'
def _ladder_minutes(self) -> int:
explicit = re.search(r'\b(30|60|90)\s*(minutes|minute|min)\b', self.lower)
if explicit:
return int(explicit.group(1))
if any(word in self.lower for word in ['email', 'scroll', 'screens', 'doomscroll', 'mentally on', 'work until']):
return 60
if any(word in self.lower for word in ['very late', 'chaotic', 'kids', 'short version']):
return 30
return 60
def _boundary(self) -> str:
boundary_label = 'T-60' if self.ladder_minutes >= 60 else 'T-30'
if any(word in self.lower for word in ['email', 'work', 'slack', 'message']):
return f'No work email or work chat after {boundary_label}.'
if any(word in self.lower for word in ['scroll', 'social', 'phone', 'screens']):
return f'No scrolling or high-stimulation screens after {boundary_label}.'
return f'No new stimulating input after {boundary_label}.'
def _timeline(self) -> Dict[str, str]:
top_label = 'T-90' if self.ladder_minutes == 90 else ('T-60' if self.ladder_minutes >= 60 else 'T-30')
top_step = 'Dim lights, close work loops, and make tomorrow capture notes short and final.'
if self.ladder_minutes == 30:
top_step = 'End stimulating inputs, dim lights, and start the shortest calming sequence available.'
middle_step = 'Do hygiene, light stretching, or quiet reading instead of productivity tasks.'
final_step = 'Put the phone away, lower sensory input, and use 3 slow breaths or one page of reading as the final bridge into bed.'
return {
top_label: top_step,
'T-30': middle_step,
'T-10': final_step,
}
def _environment(self) -> Dict[str, str]:
light = 'Lights get noticeably dimmer once the wind-down starts.'
screens = self._boundary()
temperature = 'Cool the room slightly or remove one layer of heat if the room runs warm.'
noise = 'Use the quietest setup available, such as a fan, earplugs, or a closed-door buffer.'
if 'noise' in self.lower or 'kids' in self.lower or 'child' in self.lower:
noise = 'Use a realistic noise buffer, such as a fan, earplugs, or the calmest room available.'
return {
'light': light,
'screens': screens,
'temperature': temperature,
'noise': noise,
}
def _fallback(self) -> str:
return 'If the night is already late, stop new inputs now, do the shortest wash-up possible, dim lights, take 3 slow breaths, and get into bed without trying to perfect the routine.'
def render(self) -> str:
top_label = next(key for key in self.timeline.keys() if key in ['T-90', 'T-60', 'T-30'])
lines: List[str] = []
lines.append('# Wind-Down Plan')
lines.append('')
lines.append('## Sleep Target')
lines.append(f'- Desired bedtime: {self.desired_bedtime}')
lines.append(f'- Current pattern: {self.current_pattern}')
lines.append('')
lines.append('## Ramp-Down Timeline')
lines.append(f"- {top_label}: {self.timeline[top_label]}")
lines.append(f"- T-30: {self.timeline['T-30']}")
lines.append(f"- T-10: {self.timeline['T-10']}")
lines.append('')
lines.append('## Environment Checklist')
lines.append(f"- Light: {self.environment['light']}")
lines.append(f"- Screens: {self.environment['screens']}")
lines.append(f"- Temperature: {self.environment['temperature']}")
lines.append(f"- Noise: {self.environment['noise']}")
lines.append('')
lines.append('## Fallback Version')
lines.append(f'- If the night is already late, I will: {self.fallback}')
lines.append('')
lines.append('## Morning Feedback')
lines.append('- How fast did I settle?')
lines.append('- How did I feel on wake-up?')
return '\n'.join(lines)
def handle(inputs):
_load_skill_meta('sleep-wind-down-coach')
coach = SleepWindDownCoach(inputs)
return coach.render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import SleepWindDownCoach, handle
def test_bedtime_detection():
coach = SleepWindDownCoach('I want to be asleep by 10:30 pm, but I usually answer work email until 11:15 pm.')
assert coach.desired_bedtime == '10:30 pm'
assert coach.ladder_minutes == 60
def test_screen_boundary_mentions_work_email():
coach = SleepWindDownCoach('I keep checking work email late at night before bed.')
assert 'work email' in coach.environment['screens'].lower()
def test_output_sections():
output = handle('Build me a wind-down plan because I scroll until midnight and feel wired.')
assert output.startswith('# Wind-Down Plan')
assert '## Ramp-Down Timeline' in output
assert '## Environment Checklist' in output
assert '## Morning Feedback' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Recognize genuine growth with evidence-based milestones, meaningful celebration, and a next step that sustains momentum without empty praise.
---
name: growth-milestone-celebrator
description: Help a user recognize real growth with evidence, proportionate celebration, and a next step that keeps momentum alive. Use when someone downplays progress, struggles to notice process wins, or wants to celebrate effort, courage, consistency, recovery, or learning without empty praise. Chinese alias: 成长里程碑庆祝师.
---
# Growth Milestone Celebrator / 成长里程碑庆祝师
Use this skill when a user needs grounded recognition of progress rather than vague encouragement.
## What it helps with
- Naming a recent milestone in effort, courage, consistency, recovery, or learning
- Collecting evidence so the recognition feels real
- Translating the moment into identity-level meaning
- Choosing a celebration that matches the size of the milestone
- Linking the celebration to the next step so momentum continues
- Valuing process wins, not just polished outcomes
## Workflow
1. Ask what the user recently did that signals real growth.
2. Categorize the milestone.
3. Gather concrete evidence.
4. Choose a small or larger celebration that fits.
5. Extract the identity-level meaning.
6. End with the next step that builds on the win.
## Output format
```markdown
# Growth Milestone Record
## What Happened
- Milestone:
- Category:
- Evidence:
## Why It Matters
- What changed:
- What this says about me:
## Celebration
- Small celebration:
- Bigger celebration, if appropriate:
## Next Step
- To build on this, I will:
```
## Quality bar
- Recognize process wins, not only polished results.
- Use evidence instead of empty praise.
- Match the celebration to the size and meaning of the milestone.
- Leave the user encouraged but still grounded.
## Limits
- Perfectionistic users may resist calling anything a milestone.
- Comparing progress with others can dilute the recognition.
- Avoid inflating tiny actions into false grandiosity.
- Manual reflection support only, with no badges, sharing, or app integration.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
text = str(value).strip()
if text:
return text
return default
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _shorten(text: str, limit: int = 120) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
CATEGORY_RULES = {
'Consistency': ['streak', 'consistent', 'showed up', 'every day', 'kept at it', 'routine'],
'Courage': ['spoke up', 'hard conversation', 'boundary', 'asked for', 'brave', 'fear'],
'Recovery': ['recovered', 'came back', 'restarted', 'after missing', 'bounced back', 'reset'],
'Learning': ['learned', 'practiced', 'studied', 'improved', 'figured out'],
}
def _what_changed(category: str) -> str:
return {
'Consistency': 'You repeated the behavior enough times to make it feel more like a pattern than a random exception.',
'Courage': 'You acted while discomfort was still present instead of waiting to feel fearless.',
'Recovery': 'You returned after disruption instead of letting the setback define the story.',
'Learning': 'You turned effort into usable skill, not just more information.',
'Effort': 'You kept moving in the right direction before the outcome was fully polished.',
}[category]
def _identity_line(category: str) -> str:
return {
'Consistency': 'I am becoming someone who follows through in small, repeatable ways.',
'Courage': 'I am becoming someone who can stay honest and steady under discomfort.',
'Recovery': 'I am becoming someone who can return after setbacks without drama.',
'Learning': 'I am becoming someone who turns practice into capability.',
'Effort': 'I am becoming someone who keeps showing up before applause arrives.',
}[category]
def _next_step(category: str) -> str:
return {
'Consistency': 'Repeat the same action at the next scheduled time so the pattern stays alive.',
'Courage': 'Write down the brave move you want to repeat and the next situation where it matters.',
'Recovery': 'Protect the restart cue that helped you come back and use it again this week.',
'Learning': 'Practice the new skill again within 48 hours so it sticks.',
'Effort': 'Name the next smallest step and put it somewhere visible or scheduled.',
}[category]
def handle(inputs):
meta = _load_skill_meta('skill')
data = _coerce_dict(inputs)
raw = _normalize_inputs(inputs)
milestone = _pick_text(
data,
['milestone', 'what_happened', 'win', 'growth'],
_shorten(raw) if raw else 'You noticed a real sign of movement worth naming.',
)
category = _pick_text(data, ['category'], _detect_one(raw, CATEGORY_RULES, 'Effort'))
evidence = _pick_text(data, ['evidence', 'proof'], milestone)
small_celebration = _pick_text(
data,
['small_celebration'],
'Mark it in writing, take one full breath of recognition, and give yourself a small treat or pause that feels sincere.',
)
bigger_celebration = _pick_text(
data,
['bigger_celebration'],
'If it feels meaningful, share it with one supportive person or pair it with a slightly bigger ritual, meal, or outing.',
)
return f"""# Growth Milestone Record
## What Happened
- Milestone: {milestone}
- Category: {category}
- Evidence: {evidence}
## Why It Matters
- What changed: {_what_changed(category)}
- What this says about me: {_identity_line(category)}
## Celebration
- Small celebration: {small_celebration}
- Bigger celebration, if appropriate: {bigger_celebration}
## Next Step
- To build on this, I will: {_next_step(category)}
"""
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_streak_context_detects_consistency_category():
output = handle('I kept a 10-day walking streak even after a rough week')
assert '# Growth Milestone Record' in output
assert '- Category: Consistency' in output
def test_dict_input_preserves_milestone_and_evidence():
output = handle({
'milestone': 'Restarted my study habit after missing a week',
'category': 'Recovery',
'evidence': 'Studied 4 days this week instead of quitting'
})
assert 'Restarted my study habit after missing a week' in output
assert 'Studied 4 days this week instead of quitting' in output
assert '- Category: Recovery' in output
def test_output_contains_celebration_and_next_step():
output = handle('I finally asked for help on a hard project instead of hiding it')
assert '## Celebration' in output
assert '- To build on this, I will:' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Design intentional alone time with a clear purpose, realistic duration, phone boundaries, nourishing activities, and a gentle transition back.
---
name: alone-time-architect
description: Design nourishing solitude with a clear purpose, realistic duration, phone boundaries, and a gentle re-entry plan. Use when someone wants alone time for rest, clarity, play, recovery, or creative space but tends to waste it, feel guilty, or drift into numbing withdrawal. Chinese alias: 独处时光建筑师.
---
# Alone Time Architect / 独处时光建筑师
Use this skill when a user wants solitude to feel restorative and intentional rather than accidental or guilty.
## What it helps with
- Naming what kind of solitude is needed right now: rest, clarity, play, recovery, or creative space
- Matching alone time to the real duration and energy available
- Choosing an environment, entry ritual, and phone boundary
- Offering activity options that fit the intended need
- Adding a gentle re-entry step
- Checking whether the solitude actually nourished the user or became avoidance
## Workflow
1. Clarify the purpose of the solitude block.
2. Match it to available time and energy.
3. Choose a location and phone boundary.
4. Offer a small activity palette based on the need.
5. Add a gentle re-entry step.
6. Reflect on whether the solitude met the intended need.
## Output format
```markdown
# Alone Time Blueprint
## Purpose
- What I need from this solitude:
- Available time:
## Boundaries
- Location:
- Phone rule:
- Interruptions rule:
## Activity Palette
- Low-energy option:
- Medium-energy option:
- High-presence option:
## Re-entry
- How I will return to the day:
- What to notice after:
```
## Quality bar
- Design solitude with a purpose, not just as free time.
- Match the activities to the user’s real energy.
- Include both boundary protection and re-entry.
- Help distinguish replenishment from avoidance.
## Limits
- Parents and caregivers may only have micro-solitude rather than ideal long blocks.
- Lonely users may need connection planning alongside solitude planning.
- Passive scrolling needs an explicit phone boundary.
- Pure descriptive support only, with no device management or scheduling automation.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, Sequence, Tuple
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
text = str(value).strip()
if text:
return text
return default
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
PURPOSE_RULES = {
'Rest': ['rest', 'tired', 'exhausted', 'overstimulated', 'quiet', 'need a break'],
'Clarity': ['clarity', 'decision', 'think', 'clear my head', 'mental clutter', 'brain fog'],
'Play': ['play', 'fun', 'joy', 'lightness', 'explore'],
'Recovery': ['recover', 'healing', 'burnout', 'after conflict', 'tender'],
'Creative space': ['create', 'write', 'draw', 'sketch', 'idea', 'deep work', 'project'],
}
ENERGY_RULES = {
'Low': ['tired', 'drained', 'overwhelmed', 'burned out', 'burnt out'],
'High': ['energized', 'curious', 'restless', 'ready'],
}
def _extract_duration(data: Dict[str, Any], raw: str) -> str:
explicit = _pick_text(data, ['available_time', 'time', 'duration'])
if explicit:
return explicit
lowered = raw.lower()
match = re.search(r'(\d+)\s*(minutes?|mins?|hours?)', lowered)
if match:
unit = match.group(2)
if unit.startswith('hour'):
return f"{match.group(1)} hour" + ('' if match.group(1) == '1' else 's')
return f"{match.group(1)} minutes"
return '30 minutes'
def _default_location(purpose: str) -> str:
return {
'Rest': 'A quiet room, shaded bench, or another low-stimulation place',
'Clarity': 'A walkable route or a table with a notebook and no chatter',
'Play': 'A light, low-pressure place that feels a little fresh',
'Recovery': 'A protected, gentle space where you do not need to perform',
'Creative space': 'A desk, library table, studio corner, or café with minimal interruption',
}[purpose]
def _activity_palette(purpose: str) -> Tuple[str, str, str]:
palettes = {
'Rest': (
'Tea, stretching, closed-eye rest, or lying down without input',
'A slow walk or a few pages of quiet reading without multitasking',
'Ten minutes of silence and breathing before writing one simple line about what you need',
),
'Clarity': (
'A brain-dump list of everything crowding your mind',
'A solo walk with one question and no podcast',
'Sit with one notebook page and ask what matters most right now',
),
'Play': (
'Flip through an art book, playlist, or playful prompt',
'Sketch, doodle, wander, or make something with no outcome pressure',
'Let yourself explore one small curiosity without turning it into a project',
),
'Recovery': (
'Warm tea, gentle stretching, or sitting somewhere soft and quiet',
'A slow walk, soothing music, or a very light tidy reset',
'A full protected block with no obligation except settling your nervous system',
),
'Creative space': (
'Freewrite or sketch fragments without judging them',
'Read a few pages, collect notes, or outline one idea thread',
'One uninterrupted making block with the phone out of reach',
),
}
return palettes[purpose]
def handle(inputs):
meta = _load_skill_meta('skill')
data = _coerce_dict(inputs)
raw = _normalize_inputs(inputs)
purpose = _pick_text(data, ['purpose', 'need'], _detect_one(raw, PURPOSE_RULES, 'Clarity'))
energy = _pick_text(data, ['energy_level', 'energy'], _detect_one(raw, ENERGY_RULES, 'Medium'))
available_time = _extract_duration(data, raw)
location = _pick_text(data, ['location'], _default_location(purpose))
phone_rule = _pick_text(
data,
['phone_rule'],
'Put the phone on Do Not Disturb, keep it out of reach, and only check it at the planned end of the block.',
)
interruptions_rule = _pick_text(
data,
['interruptions_rule'],
'Only break the block for genuine emergencies; everything else can wait until re-entry.',
)
low, medium, high = _activity_palette(purpose)
if energy == 'Low':
medium = 'Keep the medium option gentle: a slow walk, easy reading, or a notebook check-in with no pressure.'
notice_after = {
'Rest': 'Notice whether your body feels softer, slower, or less defended.',
'Clarity': 'Notice whether the next step feels more obvious than it did before the block.',
'Play': 'Notice whether you feel lighter, more curious, or less heavy.',
'Recovery': 'Notice whether the solitude calmed you rather than numbed you out.',
'Creative space': 'Notice whether one idea thread feels alive enough to continue later.',
}[purpose]
return_to_day = 'Take two slower breaths, name one sentence about what helped, and choose one simple next action before rejoining people or tasks.'
return f"""# Alone Time Blueprint
## Purpose
- What I need from this solitude: {purpose}
- Available time: {available_time}
## Boundaries
- Location: {location}
- Phone rule: {phone_rule}
- Interruptions rule: {interruptions_rule}
## Activity Palette
- Low-energy option: {low}
- Medium-energy option: {medium}
- High-presence option: {high}
## Re-entry
- How I will return to the day: {return_to_day}
- What to notice after: {notice_after}
"""
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_overstimulated_context_detects_rest_need():
output = handle('I feel overstimulated after a busy family day and need 30 minutes alone')
assert '# Alone Time Blueprint' in output
assert '- What I need from this solitude: Rest' in output
assert '- Available time: 30 minutes' in output
def test_dict_input_preserves_location_and_time():
output = handle({
'purpose': 'Creative space',
'available_time': '1 hour',
'location': 'Library table near a window'
})
assert 'Creative space' in output
assert '1 hour' in output
assert 'Library table near a window' in output
def test_output_contains_phone_rule_and_reentry():
output = handle('Need some solitude to clear my head without wasting it on my phone')
assert '- Phone rule:' in output
assert '## Re-entry' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Create a realistic weekly meal plan balancing schedule, preferences, effort, leftovers, backup meals, and grouped shopping for efficient cooking.
---
name: weekly-feast-planner
description: Build a realistic weekly meal arc that fits schedule pressure, household preferences, leftovers, and delight. Use when a user wants meal planning that reflects busy nights, backup meals, prep windows, and grouped shopping instead of idealized cooking. Chinese alias: 一周美食策划师.
---
# Weekly Feast Planner / 一周美食策划师
Use this skill when a user wants meal planning that fits real life, not fantasy energy.
## What it helps with
- Reviewing the coming week for busy nights, budget pressure, dietary notes, and eaters
- Matching meal difficulty to energy instead of treating every day equally
- Building variety across comfort, light, high-protein, soup, leftover remix, and one playful dish
- Grouping ingredients to reduce shopping friction
- Assigning prep tasks to earlier low-pressure moments
- Adding backup meals for chaotic nights
## Workflow
1. Ask about schedule, cooking windows, eaters, budget, and dietary preferences.
2. Mark the hardest nights first.
3. Build a meal arc with variety and realistic effort.
4. Group ingredients into shopping clusters.
5. Assign prep tasks to easier moments.
6. Add leftovers and backup meals on purpose.
## Output format
```markdown
# Weekly Feast Plan
## Week Constraints
- Busy nights:
- Budget notes:
- Dietary notes:
## Meal Board
- Monday:
- Tuesday:
- Wednesday:
- Thursday:
- Friday:
- Weekend:
## Prep and Leftovers
- Prep ahead:
- Leftover plan:
- Backup meal:
## Shopping Clusters
- Produce:
- Protein:
- Pantry:
```
## Quality bar
- Reflect actual schedule and cooking capacity.
- Include variety without excessive complexity.
- Use leftovers intentionally, not accidentally.
- Always include at least one backup solution for the hardest day.
## Limits
- Allergies, tight budgets, or picky eaters may reduce flexibility.
- The skill cannot verify live store inventory, prices, or nutrition labels.
- Exact macros or medical nutrition planning need another layer.
- Descriptive meal planning only, with no grocery ordering or recipe API dependency.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item).strip() for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
DAY_ALIASES = {
'Monday': ['monday', 'mon'],
'Tuesday': ['tuesday', 'tue'],
'Wednesday': ['wednesday', 'wed'],
'Thursday': ['thursday', 'thu'],
'Friday': ['friday', 'fri'],
}
DIET_RULES = {
'Vegetarian': ['vegetarian', 'meatless', 'veggie', 'plant-based'],
'High-protein': ['high protein', 'protein', 'training', 'gym meals'],
}
BUDGET_RULES = {
'Tight': ['tight budget', 'cheap', 'budget', 'save money', 'low cost'],
'Flexible': ['flexible', 'splurge', 'treat', 'special dinner'],
}
ARC_TEMPLATES = {
'Mixed': {
'comfort': 'Sheet-pan chicken, potatoes, and one green vegetable',
'light': 'Quick rice bowls with eggs or tuna, cucumbers, and a bright sauce',
'protein': 'Fast stir-fry with protein, vegetables, and rice',
'soup': 'Big pot noodle soup with extra vegetables for leftovers',
'experimental': 'Fun Friday tacos or wraps with one fresh topping',
'weekend': 'One slower family meal plus a leftover remix lunch',
'simple': '15-minute noodles, eggs on toast, or rotisserie chicken wraps',
'backup': 'Eggs, toast, and fruit or a freezer dumpling night',
'produce': 'Leafy greens, cucumbers, onions, potatoes, carrots, herbs',
'protein_list': 'Chicken, eggs, tofu, tuna, yogurt',
'pantry': 'Rice, noodles, canned beans, broth, tortillas, sauces',
},
'Vegetarian': {
'comfort': 'Roasted vegetable grain bowls with tahini or yogurt sauce',
'light': 'Chopped salad with beans, feta, and toasted seeds',
'protein': 'Tofu or lentil stir-fry with rice and crunchy vegetables',
'soup': 'Miso noodle soup with tofu, mushrooms, and greens',
'experimental': 'Crispy mushroom tacos or paneer wraps with slaw',
'weekend': 'One slower vegetarian feast plus a leftover grain-bowl remix',
'simple': '15-minute tofu noodles, bean quesadillas, or tomato eggs',
'backup': 'Bean quesadillas, soup from the freezer, or peanut noodles',
'produce': 'Greens, mushrooms, tomatoes, cucumbers, carrots, herbs',
'protein_list': 'Tofu, beans, lentils, eggs, yogurt, paneer',
'pantry': 'Rice, noodles, canned beans, lentils, tortillas, broth',
},
'High-protein': {
'comfort': 'Sheet-pan chicken with potatoes and a yogurt-herb sauce',
'light': 'Salmon or chicken rice bowls with crisp vegetables',
'protein': 'Turkey, tofu, or beef stir-fry with rice and greens',
'soup': 'High-protein noodle soup with chicken and extra eggs',
'experimental': 'Skewers, tacos, or lettuce wraps with a bold sauce',
'weekend': 'One protein-forward grill or roast plus smart leftovers',
'simple': 'Greek yogurt bowls, rotisserie chicken wraps, or egg fried rice',
'backup': 'Rotisserie chicken, frozen edamame, and microwaved rice',
'produce': 'Greens, peppers, cucumbers, onions, potatoes, fruit',
'protein_list': 'Chicken, eggs, Greek yogurt, tofu, salmon, turkey',
'pantry': 'Rice, oats, noodles, beans, broth, tortillas, seasonings',
},
}
def _extract_busy_nights(data: Dict[str, Any], raw: str) -> List[str]:
explicit = data.get('busy_nights')
if isinstance(explicit, (list, tuple, set)):
return [str(item).strip().title() for item in explicit if str(item).strip()]
if isinstance(explicit, str) and explicit.strip():
return [chunk.strip().title() for chunk in explicit.split(',') if chunk.strip()]
lower = raw.lower()
found = []
for day, aliases in DAY_ALIASES.items():
if any(re.search(rf'\b{alias}\b', lower) for alias in aliases):
found.append(day)
return found or ['Wednesday', 'Thursday']
def _build_meal_board(diet: str, busy_nights: List[str]) -> Dict[str, str]:
template = ARC_TEMPLATES[diet]
roles = {
'Monday': 'comfort',
'Tuesday': 'light',
'Wednesday': 'protein',
'Thursday': 'soup',
'Friday': 'experimental',
}
board = {}
for day, role in roles.items():
if day in busy_nights:
board[day] = template['simple']
else:
board[day] = template[role]
board['Weekend'] = template['weekend']
return board
def handle(inputs):
meta = _load_skill_meta('skill')
data = _coerce_dict(inputs)
raw = _normalize_inputs(inputs)
diet = _detect_one(f"{_pick_text(data, ['dietary_notes', 'diet', 'dietary_preferences'])} {raw}", DIET_RULES, 'Mixed')
budget = _pick_text(data, ['budget_notes', 'budget'], _detect_one(raw, BUDGET_RULES, 'Moderate'))
busy_nights = _extract_busy_nights(data, raw)
eaters = _pick_text(data, ['eaters', 'household'])
dietary_notes = _pick_text(data, ['dietary_notes', 'diet', 'dietary_preferences'], diet)
if eaters:
dietary_notes = f'{dietary_notes}; eaters: {eaters}'
board = _build_meal_board(diet, busy_nights)
template = ARC_TEMPLATES[diet]
prep_ahead = 'Cook one grain, wash produce, and mix one sauce early in the week so busy nights stay light.'
leftover_plan = 'Make extra on Monday or Thursday and fold it into bowls, wraps, or soup the next day.'
if diet == 'High-protein':
prep_ahead = 'Cook one protein base, one grain, and a yogurt sauce before the hardest night.'
leftover_plan = 'Turn extra protein into wraps, fried rice, or salad bowls instead of starting from zero.'
elif diet == 'Vegetarian':
prep_ahead = 'Wash greens, roast one tray of vegetables, and cook lentils or tofu ahead.'
leftover_plan = 'Use leftover grains, roasted vegetables, and beans for bowls or quesadillas.'
return f"""# Weekly Feast Plan
## Week Constraints
- Busy nights: {', '.join(busy_nights)}
- Budget notes: {budget}
- Dietary notes: {dietary_notes}
## Meal Board
- Monday: {board['Monday']}
- Tuesday: {board['Tuesday']}
- Wednesday: {board['Wednesday']}
- Thursday: {board['Thursday']}
- Friday: {board['Friday']}
- Weekend: {board['Weekend']}
## Prep and Leftovers
- Prep ahead: {prep_ahead}
- Leftover plan: {leftover_plan}
- Backup meal: {template['backup']}
## Shopping Clusters
- Produce: {template['produce']}
- Protein: {template['protein_list']}
- Pantry: {template['pantry']}
"""
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_busy_nights_and_high_protein_context_render_meal_board():
output = handle('We have busy Tuesday and Thursday nights and want high protein dinners this week')
assert '# Weekly Feast Plan' in output
assert '- Busy nights: Tuesday, Thursday' in output
assert 'Greek yogurt' in output or 'Chicken' in output
def test_dict_input_preserves_budget_and_dietary_notes():
output = handle({
'busy_nights': ['Tuesday', 'Thursday'],
'dietary_notes': 'Vegetarian',
'budget_notes': 'Keep it moderate'
})
assert '- Budget notes: Keep it moderate' in output
assert '- Dietary notes: Vegetarian' in output
assert 'Tofu' in output or 'beans' in output.lower()
def test_output_contains_backup_and_shopping_clusters():
output = handle('Need easy dinners for a family with a chaotic Wednesday')
assert '- Backup meal:' in output
assert '## Shopping Clusters' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Guide users through a low-pressure, time- or page-based morning writing practice to help release thoughts without editing or over-analysis.
---
name: morning-pages-facilitator
description: Guide a user through a low-pressure morning pages practice with a simple container, anti-stall prompts, and a clean stopping point. Use when someone wants to free-write in the morning but freezes, self-edits, or turns the practice into polished writing. Chinese alias: 晨间书写引导师.
---
# Morning Pages Facilitator / 晨间书写引导师
Use this skill when a user wants mental clearing through morning writing, not performance writing.
## What it helps with
- Explaining that the goal is release, not quality
- Setting a time-based or page-based writing container
- Offering anti-stall prompts when the page feels frozen
- Encouraging continuous writing without editing
- Ending with one insight tag or one next action, without over-analyzing the whole entry
- Normalizing inconsistency and gentle restarts
## Workflow
1. Reframe the practice as release instead of polished output.
2. Set a light container using time or page count.
3. Offer anti-stall cues if the user freezes.
4. Encourage continuous writing, even when the content feels messy or repetitive.
5. Close with one phrase, one feeling, or one next action.
6. Normalize restarting after missed days.
## Output format
```markdown
# Morning Pages Session
## Container
- Time or page target:
- Writing rule:
## Starter Cues
- Right now my mind is full of...
- If I am honest, I keep thinking about...
- What I do not want to write is...
## After Writing
- One phrase that stood out:
- One feeling I notice:
- One next action, if any:
```
## Quality bar
- Protect the non-performative spirit of the practice.
- Give just enough prompt support without turning it into a questionnaire.
- Encourage completion over elegance.
- Provide a clean stopping point so the session does not bleed into over-analysis.
## Limits
- Free-writing can surface distressing material for some users.
- Very busy users may need a five-minute version.
- If the practice becomes pure rumination, add more structure or closure cues.
- Descriptive writing guidance only, with no document creation or sync.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
text = str(value).strip()
if text:
return text
return default
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
OBSTACLE_RULES = {
'Freeze on page one': ['freeze', 'frozen', 'blank page', 'page one', 'cannot start'],
'Self-editing': ['self-edit', 'editing', 'polish', 'perfect', 'sound good'],
'Over-analysis': ['overthink', 'analyze', 'ruminate', 'spiral'],
'Low time': ['5 minutes', 'five minutes', 'short', 'busy', 'little time'],
}
def _extract_container(data: Dict[str, Any], raw: str) -> str:
page_target = _pick_text(data, ['page_target', 'pages'])
if page_target:
return page_target if 'page' in page_target.lower() else f'{page_target} pages'
time_target = _pick_text(data, ['time', 'minutes', 'duration'])
if time_target:
lower = time_target.lower()
if 'minute' in lower or 'page' in lower:
return time_target
return f'{time_target} minutes'
lowered = raw.lower()
minute_match = re.search(r'(\d+)\s*(minutes?|mins?)', lowered)
if minute_match:
return f"{minute_match.group(1)} minutes"
page_match = re.search(r'(\d+)\s*pages?', lowered)
if page_match:
return f"{page_match.group(1)} pages"
if 'five-minute' in lowered or '5-minute' in lowered:
return '5 minutes'
return '10 minutes or about 2 pages'
def _build_cues(obstacle: str, focus: str) -> List[str]:
cue_one = 'Right now my mind is full of...'
cue_two = 'If I am honest, I keep thinking about...'
cue_three = 'What I do not want to write is...'
if obstacle == 'Self-editing':
cue_two = 'If this never had to sound smart, I would say...'
elif obstacle == 'Over-analysis':
cue_two = 'The loop I keep replaying is...'
elif obstacle == 'Low time':
cue_one = 'In the next five minutes, I need to unload...'
if focus:
cue_one = f'Right now my mind is full of {focus}, and the first honest sentence is...'
return [cue_one, cue_two, cue_three]
def handle(inputs):
meta = _load_skill_meta('skill')
data = _coerce_dict(inputs)
raw = _normalize_inputs(inputs)
focus = _pick_text(data, ['focus', 'theme', 'topic'])
obstacle = _pick_text(data, ['obstacle', 'stuckness'], _detect_one(raw, OBSTACLE_RULES, 'Freeze on page one'))
container = _extract_container(data, raw)
writing_rule = 'Keep the pen or cursor moving, do not edit while writing, and write the next honest sentence even if it feels repetitive.'
if obstacle == 'Self-editing':
writing_rule = 'Write without polishing or crossing out, and let awkward sentences stay awkward until the session ends.'
elif obstacle == 'Over-analysis':
writing_rule = 'Stay with raw observation, not interpretation, until the timer or page target ends.'
cues = _build_cues(obstacle, focus)
gentle_focus = focus if focus else 'Whatever feels loudest in your mind this morning.'
return f"""# Morning Pages Session
## Container
- Time or page target: {container}
- Writing rule: {writing_rule}
- Gentle focus, if helpful: {gentle_focus}
## Starter Cues
- {cues[0]}
- {cues[1]}
- {cues[2]}
## After Writing
- One phrase that stood out: Circle or copy one line that surprised you.
- One feeling I notice: Name the clearest feeling in one or two words.
- One next action, if any: Pull only one gentle next step if it feels obvious.
- Restart note: Missing days does not break the practice; restart with one honest sentence tomorrow.
"""
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_five_minute_freeze_context_sets_short_container():
output = handle('I freeze on page one and only have 5 minutes before the kids wake up')
assert '# Morning Pages Session' in output
assert '- Time or page target: 5 minutes' in output
assert 'What I do not want to write is' in output
def test_dict_input_preserves_page_target_and_focus():
output = handle({
'page_target': '3 pages',
'focus': 'career anxiety'
})
assert '- Time or page target: 3 pages' in output
assert 'career anxiety' in output
def test_output_contains_after_writing_closure():
output = handle('I keep trying to make my morning pages sound polished')
assert '## After Writing' in output
assert '- One next action, if any:' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Helps create a realistic daily hydration plan with personalized intake ranges, simple tracking anchors, and alerts for under- and overhydration risks.
---
name: water-intake-tracker
description: Help a user build a realistic hydration rhythm with anchor points, a simple tracking method, and clear watch-outs for both underhydration and overdoing it. Use when someone wants to drink more water, stop forgetting hydration, or turn water intake into a workable daily routine. Chinese alias: 饮水追踪师.
---
# Water Intake Tracker / 饮水追踪师
Use this skill when a user wants better hydration without turning it into rigid math.
## What it helps with
- Reviewing current intake patterns, activity level, caffeine use, weather, and routine
- Proposing a reasonable hydration range instead of a universal target
- Breaking water intake into wake-up, meal, work-block, workout, and evening anchors
- Choosing a simple tracking method such as cups, bottle refills, or tally marks
- Adding a catch-up rule so missed water early in the day does not become late-night overdrinking
- Noticing thirst cues, urine color, and signs of both underhydration and overhydration
## Workflow
1. Ask about current intake pattern, activity level, caffeine use, weather, and routine.
2. Set a realistic hydration range rather than a rigid universal target.
3. Break the day into anchor points.
4. Choose a simple logging method.
5. Add a catch-up rule for missed water earlier in the day.
6. End with watch-outs for both underhydration and overdoing it.
## Output format
```markdown
# Hydration Plan
## Baseline
- Current pattern:
- Context factors:
## Daily Range
- Suggested range:
- Why this range:
## Drinking Anchors
- Morning:
- Midday:
- Afternoon:
- Evening:
## Tracking Method
- Cup, bottle, or tally system:
## Watch-outs
- Signs I am underhydrated:
- When not to force more water:
```
## Quality bar
- Keep the target realistic and tied to routine anchors.
- Avoid one-size-fits-all intake advice.
- Include both underhydration and overhydration caution.
- Make the plan easy enough to run for at least a week.
## Limits
- Medical conditions, fluid restrictions, or pregnancy may require clinician guidance.
- Users who dislike plain water may need flavor or temperature strategies.
- Heavy exercise and hot weather can change needs quickly.
- Descriptive wellness guidance only, with no smart bottle or medical monitoring integration.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
from typing import Any, Dict, Sequence, Tuple
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item).strip() for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _shorten(text: str, limit: int = 120) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
ACTIVITY_RULES = {
'High': ['workout', 'work out', 'working out', 'exercise', 'run', 'gym', 'training', 'sports', 'hike', 'sweat'],
'Low': ['desk', 'sedentary', 'indoors', 'sitting', 'low activity'],
}
WEATHER_RULES = {
'Hot': ['hot', 'heat', 'summer', 'humid', 'warm'],
'Cool': ['cold', 'winter', 'cool', 'mild'],
}
CAFFEINE_RULES = {
'Higher caffeine': ['energy drink', 'multiple coffees', 'lots of coffee', '3 coffees', '4 coffees', 'caffeine'],
}
TRACKING_RULES = {
'Bottle refill system': ['bottle', 'refill', 'thermos'],
'Cup count': ['cup', 'glass', 'mug'],
'Tally marks': ['tally', 'check mark', 'checkbox'],
}
def _build_range(activity: str, weather: str, caffeine: str) -> Tuple[str, str]:
minimum, maximum = 1.8, 2.3
reasons = ['it starts from a steady baseline instead of a rigid universal rule']
if activity == 'High':
minimum += 0.4
maximum += 0.6
reasons.append('higher activity usually means more sweat loss')
elif activity == 'Low':
minimum -= 0.2
maximum -= 0.2
reasons.append('lighter activity keeps the target a bit lower')
if weather == 'Hot':
minimum += 0.2
maximum += 0.3
reasons.append('hot weather raises hydration demand')
elif weather == 'Cool':
reasons.append('cooler weather keeps the need closer to baseline')
if caffeine == 'Higher caffeine':
minimum += 0.1
maximum += 0.1
reasons.append('more caffeine makes a structured rhythm more useful')
minimum = max(1.5, minimum)
maximum = max(minimum + 0.3, maximum)
return f'{minimum:.1f} to {maximum:.1f} L', '; '.join(reasons)
def _build_anchors(activity: str) -> Dict[str, str]:
afternoon = 'Use one work-block reminder, drink with lunch, and add another glass mid-afternoon.'
if activity == 'High':
afternoon = 'Use one work-block reminder, drink one bottle around workouts or walks, and replace sweat loss before dinner.'
return {
'Morning': 'Drink 300 to 500 mL within 30 minutes of waking and pair another glass with breakfast.',
'Midday': 'Aim to reach about half of the daily range by lunch so the rest of the day stays easy.',
'Afternoon': afternoon,
'Evening': 'Drink moderately with dinner, then taper so catch-up does not spill into late-night waking.',
}
def handle(inputs):
meta = _load_skill_meta('skill')
data = _coerce_dict(inputs)
raw = _normalize_inputs(inputs)
current_pattern = _pick_text(
data,
['current_pattern', 'pattern', 'intake', 'baseline'],
_shorten(raw) if raw else 'Hydration happens inconsistently and mostly depends on memory or thirst cues.',
)
activity = _pick_text(data, ['activity_level', 'activity'], _detect_one(raw, ACTIVITY_RULES, 'Moderate'))
weather = _pick_text(data, ['weather'], _detect_one(raw, WEATHER_RULES, 'Temperate'))
caffeine = _pick_text(data, ['caffeine', 'caffeine_use'], _detect_one(raw, CAFFEINE_RULES, 'Moderate caffeine'))
tracking_method = _pick_text(data, ['tracking_method', 'tracking', 'log_method'], _detect_one(raw, TRACKING_RULES, 'Bottle refill system'))
suggested_range = _pick_text(data, ['suggested_range'])
why_range = _pick_text(data, ['why_range'])
if not suggested_range:
suggested_range, why_range = _build_range(activity, weather, caffeine)
routine = _pick_text(data, ['routine', 'schedule'])
context_bits = [f'activity={activity}', f'weather={weather}', f'caffeine={caffeine}']
if routine:
context_bits.append(f'routine={routine}')
anchors = _build_anchors(activity)
return f"""# Hydration Plan
## Baseline
- Current pattern: {current_pattern}
- Context factors: {'; '.join(context_bits)}
## Daily Range
- Suggested range: {suggested_range}
- Why this range: {why_range}
## Drinking Anchors
- Morning: {anchors['Morning']}
- Midday: {anchors['Midday']}
- Afternoon: {anchors['Afternoon']}
- Evening: {anchors['Evening']}
## Tracking Method
- Cup, bottle, or tally system: {tracking_method}
- Catch-up rule: If you are behind by midday, add one glass with lunch and one in the next work block, but stop aggressive catch-up within 2 hours of bed.
## Watch-outs
- Signs I am underhydrated: dry mouth, darker yellow urine, headache, unusual fatigue, or strong thirst.
- When not to force more water: when urine is already very pale, your stomach feels sloshy, night waking is rising, or a clinician has given you a fluid limit.
"""
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_hot_and_active_context_gets_higher_range():
output = handle('I keep forgetting to drink water when it is hot and I work out most afternoons')
assert '# Hydration Plan' in output
assert '- Suggested range: 2.4 to 3.2 L' in output
def test_dict_input_preserves_pattern_and_tracking_method():
output = handle({
'current_pattern': 'Two cups in the morning and then I forget until dinner',
'tracking_method': 'Cup count',
'weather': 'Hot'
})
assert 'Two cups in the morning and then I forget until dinner' in output
assert '- Cup, bottle, or tally system: Cup count' in output
def test_output_includes_catch_up_rule_and_watchouts():
output = handle('Need a simple hydration routine with a bottle refill method')
assert '- Catch-up rule:' in output
assert '## Watch-outs' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Help users audit and analyze screen habits by device, app, and time to identify triggers and create a realistic one-week reduction plan.
---
name: screen-time-auditor
description: Help a user manually audit screen habits, find trigger loops, identify the worst leakage zones, and run one week of realistic reduction experiments. Use when someone feels trapped by scrolling, checking, or rebound screen behavior. Chinese alias: 屏幕时间审计师.
---
# Screen Time Auditor / 屏幕时间审计师
Use this skill when a user knows screen time feels too high but cannot yet see where the time goes, why it happens, or how to reduce it without a rebound.
## What it helps with
- Estimating screen time by device, app, and time band
- Separating functional use from unconscious drift and emotional escape
- Mapping trigger loops such as boredom, transitions, fatigue, procrastination, or social comparison
- Identifying the worst leakage zones, such as late-night scrolling or fragmented checking
- Recommending friction changes, replacement rituals, and protected phone-free windows
- Converting the audit into one realistic week of experiments
## Workflow
1. Ask the user to estimate or manually review screen time by device, app, and time band.
2. Separate functional screen use from drift and emotional escape.
3. Map the trigger loops behind the behavior.
4. Identify the worst leakage zones.
5. Recommend friction changes, replacement rituals, and phone-free windows.
6. Turn the audit into a one-week experiment.
## Output format
```markdown
# Screen Time Audit
## Current Pattern
- Main devices:
- Main drain apps or behaviors:
- Worst time bands:
## Trigger Map
- Trigger:
- Typical behavior:
- What it gives me:
- Better substitute:
## Reduction Plan
- Friction to add:
- Phone-free zone:
- Replacement action:
- Weekly target:
```
## Quality bar
- Move beyond shame into a specific pattern diagnosis.
- Distinguish useful use from compulsive drift.
- Include at least one friction change and one replacement behavior.
- Target one or two problem zones first instead of demanding perfection.
## Limits
- Some users need screens for work, caregiving, or study, so total reduction is not the right metric.
- Over-restriction can backfire if boredom or emotional need is ignored.
- Shared household devices can reduce data accuracy.
- Manual audit only, with no telemetry or app-blocker integration.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List, Sequence
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
if isinstance(inputs, dict):
parts: List[str] = []
for key, value in inputs.items():
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
continue
if isinstance(value, (list, tuple, set)):
rendered = ', '.join(str(item) for item in value)
else:
rendered = str(value)
parts.append(f"{key}: {rendered}")
return ' | '.join(parts)
if isinstance(inputs, (list, tuple, set)):
return ' | '.join(str(item) for item in inputs)
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
def _coerce_dict(inputs: Any) -> Dict[str, Any]:
return inputs if isinstance(inputs, dict) else {}
def _pick_text(data: Dict[str, Any], keys: Sequence[str], default: str = '') -> str:
for key in keys:
value = data.get(key)
if value is None:
continue
if isinstance(value, (list, tuple, set)):
text = ', '.join(str(item) for item in value if str(item).strip())
else:
text = str(value).strip()
if text:
return text
return default
def _listify(value: Any) -> List[str]:
if value in (None, '', [], {}, ()): # type: ignore[comparison-overlap]
return []
if isinstance(value, str):
clean = value.replace(';', '\n').replace(',', '\n')
return [chunk.strip() for chunk in clean.splitlines() if chunk.strip()]
if isinstance(value, (list, tuple, set)):
return [str(item).strip() for item in value if str(item).strip()]
return [str(value).strip()]
def _detect_one(text: str, rules: Dict[str, Sequence[str]], default: str) -> str:
lower = text.lower()
for label, keywords in rules.items():
if any(keyword in lower for keyword in keywords):
return label
return default
def _detect_many(text: str, rules: Dict[str, Sequence[str]], default: List[str], limit: int = 4) -> List[str]:
lower = text.lower()
found = [label for label, keywords in rules.items() if any(keyword in lower for keyword in keywords)]
ordered: List[str] = []
for item in found + default:
if item not in ordered:
ordered.append(item)
return ordered[:limit]
def _shorten(text: str, limit: int = 140) -> str:
clean = ' '.join(text.split())
if len(clean) <= limit:
return clean
return clean[: limit - 3].rstrip() + '...'
DEVICE_RULES = {
'Phone': ['phone', 'mobile', 'iphone', 'android'],
'Laptop': ['laptop', 'computer', 'desktop', 'work computer'],
'Tablet': ['tablet', 'ipad'],
'TV': ['tv', 'television'],
}
APP_RULES = {
'Short video apps': ['tiktok', 'douyin', 'reels', 'short video'],
'Social feeds': ['instagram', 'xiaohongshu', 'xhs', 'facebook', 'twitter', 'social media'],
'Messaging and checking': ['wechat', 'slack', 'telegram', 'message', 'checking'],
'News or reading drift': ['news', 'reddit', 'headline'],
'Games': ['game', 'gaming'],
'Shopping apps': ['taobao', 'jd', 'amazon', 'shopping'],
}
TIME_RULES = {
'Late night': ['late night', 'late-night', 'before bed', 'midnight', 'night'],
'Morning startup': ['morning', 'after waking', 'wake up'],
'Work transitions': ['between tasks', 'transition', 'between meetings', 'break'],
'Stress moments': ['stress', 'anxious', 'overwhelmed'],
'Study avoidance': ['procrastination', 'avoid', 'deadline'],
}
TRIGGER_RULES = {
'Boredom': ['bored', 'boredom'],
'Fatigue': ['tired', 'fatigue', 'exhausted'],
'Procrastination': ['procrastination', 'avoid', 'put off'],
'Stress relief': ['stress', 'anxious', 'overwhelmed'],
'Social comparison': ['compare', 'comparison', 'jealous'],
}
def _trigger_details(trigger: str) -> Dict[str, str]:
mapping = {
'Boredom': {
'behavior': 'Open the easiest feed or app for stimulation.',
'gain': 'Quick novelty without needing effort.',
'substitute': 'Keep one offline micro-option ready, such as a short walk, paper notes, or stretching.',
},
'Fatigue': {
'behavior': 'Scroll passively instead of transitioning into real rest.',
'gain': 'Numbing relief and low-effort input.',
'substitute': 'Charge the phone away from the body and switch to a short wind-down ritual.',
},
'Procrastination': {
'behavior': 'Check apps in fragments to avoid the harder task.',
'gain': 'Temporary escape from resistance.',
'substitute': 'Use a 10-minute starter task before any check and keep the harder task visible.',
},
'Stress relief': {
'behavior': 'Reach for the screen to downshift emotionally.',
'gain': 'A fast feeling of comfort or distraction.',
'substitute': 'Use a lower-stimulation reset first, like breathing, walking, or making tea.',
},
'Social comparison': {
'behavior': 'Keep checking feeds that spark comparison loops.',
'gain': 'A sense of being plugged in, even when it feels bad later.',
'substitute': 'Replace one comparison loop with direct contact, journaling, or a saved reading list.',
},
}
return mapping.get(trigger, {
'behavior': 'Check the nearest screen automatically.',
'gain': 'A fast shift of attention.',
'substitute': 'Add one offline reset between urge and action.',
})
def _friction_changes(apps: List[str], time_bands: List[str]) -> str:
changes = ['turn off non-critical notifications', 'move high-drift apps off the home screen']
if 'Late night' in time_bands:
changes.append('charge the phone outside the bedroom')
if 'Short video apps' in apps:
changes.append('log out of short-video apps after each session')
return ', '.join(changes[:4])
def _phone_free_zone(time_bands: List[str]) -> str:
if 'Late night' in time_bands:
return 'Bedroom after the evening cutoff and the last 30 minutes before sleep.'
if 'Morning startup' in time_bands:
return 'The first 30 minutes after waking.'
return 'One meal and one focused work block each day.'
def _weekly_target(time_bands: List[str]) -> str:
if 'Late night' in time_bands:
return 'No scrolling in bed for 5 of the next 7 nights.'
if 'Work transitions' in time_bands:
return 'Batch checking into 3 planned windows per day for one week.'
return 'Reduce the main leakage zone with one rule that holds for 5 of the next 7 days.'
def handle(inputs):
meta = _load_skill_meta('screen-time-auditor')
data = _coerce_dict(inputs)
text = _normalize_inputs(inputs)
devices = _listify(data.get('devices')) or _detect_many(text, DEVICE_RULES, ['Phone', 'Laptop'], limit=3)
apps = _listify(data.get('apps')) or _detect_many(text, APP_RULES, ['Social feeds', 'Messaging and checking'], limit=4)
time_bands = _listify(data.get('time_bands')) or _detect_many(text, TIME_RULES, ['Late night', 'Work transitions'], limit=3)
triggers = _listify(data.get('triggers')) or _detect_many(text, TRIGGER_RULES, ['Boredom', 'Fatigue'], limit=2)
replacement = _trigger_details(triggers[0])['substitute']
lines: List[str] = []
lines.append('# Screen Time Audit')
lines.append('')
lines.append('## Current Pattern')
lines.append(f"- Main devices: {', '.join(devices)}")
lines.append(f"- Main drain apps or behaviors: {', '.join(apps)}")
lines.append(f"- Worst time bands: {', '.join(time_bands)}")
lines.append('')
lines.append('## Trigger Map')
for trigger in triggers[:3]:
detail = _trigger_details(trigger)
lines.append(f'- Trigger: {trigger}')
lines.append(f' - Typical behavior: {detail["behavior"]}')
lines.append(f' - What it gives me: {detail["gain"]}')
lines.append(f' - Better substitute: {detail["substitute"]}')
lines.append('')
lines.append('## Reduction Plan')
lines.append(f'- Friction to add: {_friction_changes(apps, time_bands)}')
lines.append(f'- Phone-free zone: {_phone_free_zone(time_bands)}')
lines.append(f'- Replacement action: {replacement}')
lines.append(f'- Weekly target: {_weekly_target(time_bands)}')
return '\n'.join(lines)
if __name__ == "__main__":
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import handle
def test_late_night_phone_pattern_is_detected():
output = handle('I keep doomscrolling on my phone late at night when I am tired')
assert '# Screen Time Audit' in output
assert 'Late night' in output
def test_dict_input_supports_manual_audit_fields():
output = handle({
'devices': ['Phone', 'Laptop'],
'apps': ['Short video apps', 'Messaging and checking'],
'time_bands': ['Work transitions']
})
assert 'Phone, Laptop' in output
assert 'Short video apps' in output
def test_output_contains_friction_and_target():
output = handle('Too much checking between meetings on Slack and social media')
assert '- Friction to add:' in output
assert '- Weekly target:' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Organize reading into Now, Next, Later, and Drop, then track current position, format, purpose, next stopping point, and lightweight session notes. Use when...
---
name: reading-progress-tracker
description: Organize reading into Now, Next, Later, and Drop, then track current position, format, purpose, next stopping point, and lightweight session notes. Use when the user wants a simple manual reading dashboard that supports print, ebook, and audiobook without turning reading into homework.
---
# Reading Progress Tracker
## Overview
Use this skill to keep reading visible, lightweight, and honest. It helps the user track where they are, why they are reading a book, what the next stopping point is, and whether a book should stay active, move later, or be dropped.
This skill is descriptive only. It does not sync Goodreads, Kindle, audiobooks, or external reading apps.
## Trigger
Use this skill when the user wants to:
- organize books into Now, Next, Later, and Drop
- remember where they stopped reading
- keep short session notes that actually get revisited
- give each active book a purpose
- make peace with pausing or dropping books
### Example prompts
- "Build me a reading dashboard"
- "Track my current books and where I left off"
- "Help me stop hoarding books without reading them"
- "Turn these notes into a simple reading log"
## Workflow
1. Sort books into Now, Next, Later, and Drop.
2. Clarify why each current book matters.
3. Track format, progress, pace, and next stopping point.
4. Capture one idea, one quote or example, and one question after each session.
5. Review whether a book should continue, be skimmed, or be abandoned.
6. Keep the system light enough to support real reading momentum.
## Inputs
The user can provide any mix of:
- current books
- queue books
- purpose for reading
- print, ebook, or audiobook format
- pages, chapters, or percentage progress
- quotes, ideas, and questions
- books they want to pause or drop
## Outputs
Return a markdown dashboard with:
- current reads and their status
- one lightweight session note
- queue sections for Next, Later, and Drop or pause
## Safety
- Keep tracking light enough that it does not become homework.
- Allow non-linear reading for reference books.
- Support print, ebook, and audiobook without forcing one metric.
- Make it acceptable to pause or drop a book when the fit is wrong.
## Acceptance Criteria
- Return markdown text.
- Give every active book a purpose and progress status.
- Include a session note with one idea, one quote or example, and one question.
- Include a queue that makes continuing, pausing, or dropping feel usable.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
WHY_RULES = [
('learning', ['learn', 'study', 'skill', 'understand', 'research']),
('enjoyment', ['enjoy', 'fun', 'fiction', 'novel', 'pleasure']),
('reference', ['reference', 'lookup', 'consult', 'manual']),
('parenting', ['parent', 'child', 'kid', 'family']),
]
FORMAT_RULES = [
('audiobook', ['audio', 'audiobook', 'listening']),
('ebook', ['ebook', 'kindle', 'pdf', 'epub']),
('print', ['print', 'paperback', 'hardcover', 'physical book']),
]
class ReadingProgressTracker:
def __init__(self, inputs: Any):
self.raw = _normalize_inputs(inputs)
self.lower = self.raw.lower()
self.snippets = self._snippets()
self.current_reads = self._current_reads()
self.session_note = self._session_note()
self.queue = self._queue()
def _snippets(self) -> List[str]:
parts = re.split(r'[\n;]+', self.raw)
return [part.strip(' -•') for part in parts if part.strip(' -•')]
def _extract_titles(self) -> List[str]:
patterns = [
r'(?:reading|read|working through|currently reading)\s+["“]?(.+?)(?=(?:\s+on\s+|\s+for\s+|,|\.|$))',
r'(?:listening to)\s+["“]?(.+?)(?=(?:\s+on\s+|\s+for\s+|,|\.|$))',
r'now\s*:\s*(.+?)(?=(?:\n|;|$))',
]
titles: List[str] = []
for pattern in patterns:
for match in re.finditer(pattern, self.raw, flags=re.IGNORECASE):
title = match.group(1).strip(' "”')
if title and title not in titles:
titles.append(title)
return titles[:3]
def _purpose(self, snippet: str) -> str:
lower = snippet.lower()
for purpose, words in WHY_RULES:
if any(word in lower for word in words):
return purpose
return 'learning'
def _format(self, snippet: str) -> str:
lower = snippet.lower()
for format_name, words in FORMAT_RULES:
if any(word in lower for word in words):
return format_name
return 'Not specified'
def _progress(self, snippet: str) -> str:
patterns = [
r'(\d{1,4}\s*%)',
r'(page\s+\d+(?:-\d+)?)',
r'(pages\s+\d+(?:-\d+)?)',
r'(chapter\s+\d+)',
]
for pattern in patterns:
match = re.search(pattern, snippet, flags=re.IGNORECASE)
if match:
return match.group(1)
return 'Not specified yet'
def _next_stop(self, progress: str) -> str:
match = re.search(r'page\s+(\d+)', progress, flags=re.IGNORECASE)
if match:
page = int(match.group(1))
return f'Page {page + 15}'
match = re.search(r'pages\s+(\d+)-(\d+)', progress, flags=re.IGNORECASE)
if match:
page = int(match.group(2))
return f'Page {page + 10}'
match = re.search(r'chapter\s+(\d+)', progress, flags=re.IGNORECASE)
if match:
chapter = int(match.group(1))
return f'End of chapter {chapter + 1}'
match = re.search(r'(\d{1,3})\s*%', progress)
if match:
pct = int(match.group(1))
return f'{min(pct + 10, 100)}%'
return 'Choose a small stopping point before the next context switch.'
def _snippet_for_title(self, title: str) -> str:
for snippet in self.snippets:
if title.lower() in snippet.lower():
return snippet
return self.raw
def _current_reads(self) -> List[Dict[str, str]]:
titles = self._extract_titles()
if not titles:
titles = ['Current book not named']
books: List[Dict[str, str]] = []
for title in titles:
snippet = self._snippet_for_title(title)
progress = self._progress(snippet)
books.append({
'title': title,
'why': self._purpose(snippet),
'format': self._format(snippet),
'progress': progress,
'next_stop': self._next_stop(progress),
})
return books
def _find_field(self, label: str, default: str) -> str:
match = re.search(rf'{label}\s*[:\-]\s*(.+?)(?:\n|;|$)', self.raw, flags=re.IGNORECASE)
if match:
return match.group(1).strip()
return default
def _session_note(self) -> Dict[str, str]:
date_match = re.search(r'(20\d{2}-\d{2}-\d{2}|today|yesterday)', self.lower)
date_value = date_match.group(1) if date_match else 'Today'
progress = self.current_reads[0]['progress'] if self.current_reads else 'Not specified'
return {
'date': date_value.capitalize() if isinstance(date_value, str) else 'Today',
'pages_or_chapter': progress,
'idea': self._find_field('idea', 'One idea worth keeping from this session.'),
'quote': self._find_field('quote', 'Capture one line, scene, or example that stayed with you.'),
'question': self._find_field('question', 'What do I want to notice or test in the next reading session?'),
}
def _queue(self) -> Dict[str, str]:
next_item = self._find_field('next', 'Choose one book that feels timely and genuinely readable next.')
later_item = self._find_field('later', 'Everything interesting but not urgent belongs here.')
drop_item = self._find_field('drop', 'Any book that feels like obligation only can be paused or dropped.')
return {
'next': next_item,
'later': later_item,
'drop': drop_item,
}
def render(self) -> str:
lines: List[str] = []
lines.append('# Reading Dashboard')
lines.append('')
lines.append('## Current Reads')
for book in self.current_reads:
lines.append(f"- Title: {book['title']}")
lines.append(f"- Why I am reading it: {book['why']}")
lines.append(f"- Format: {book['format']}")
lines.append(f"- Current progress: {book['progress']}")
lines.append(f"- Next stopping point: {book['next_stop']}")
lines.append('')
lines.append('## Session Note')
lines.append(f"- Date: {self.session_note['date']}")
lines.append(f"- Pages or chapter: {self.session_note['pages_or_chapter']}")
lines.append(f"- One idea: {self.session_note['idea']}")
lines.append(f"- One quote or example: {self.session_note['quote']}")
lines.append(f"- One question: {self.session_note['question']}")
lines.append('')
lines.append('## Queue')
lines.append(f"- Next: {self.queue['next']}")
lines.append(f"- Later: {self.queue['later']}")
lines.append(f"- Drop or pause: {self.queue['drop']}")
return '\n'.join(lines)
def handle(inputs):
_load_skill_meta('reading-progress-tracker')
tracker = ReadingProgressTracker(inputs)
return tracker.render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import ReadingProgressTracker, handle
def test_page_progress_extraction():
tracker = ReadingProgressTracker('I am reading Deep Work on Kindle, page 87, for learning.')
assert tracker.current_reads[0]['progress'].lower() == 'page 87'
assert tracker.current_reads[0]['next_stop'] == 'Page 102'
def test_format_detection():
tracker = ReadingProgressTracker('I am listening to Atomic Habits as an audiobook during walks.')
assert tracker.current_reads[0]['format'] == 'audiobook'
def test_output_sections():
output = handle('I am reading The Anxious Generation in print, chapter 3. Idea: attention is trainable.')
assert output.startswith('# Reading Dashboard')
assert '## Current Reads' in output
assert '## Session Note' in output
assert '## Queue' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Translate a user's felt state into a color-based mood map with body cues, possible emotion words, likely needs, and one gentle next step under 10 minutes. Us...
---
name: mood-color-mapper
description: Translate a user's felt state into a color-based mood map with body cues, possible emotion words, likely needs, and one gentle next step under 10 minutes. Use when the user struggles to name emotions directly and responds better to colors, shades, or visual metaphors than strict feeling labels.
---
# Mood Color Mapper
## Overview
Use this skill when a color-first check-in feels easier than naming emotions directly. It starts with the body, lets the user choose a color or texture, translates that palette into possible feeling words, and ends with one small response that respects the emotion instead of suppressing it.
This skill is descriptive only. It is not diagnosis, therapy, or crisis support.
## Trigger
Use this skill when the user wants to:
- describe an emotional state through color
- find feeling words without forcing one exact label
- separate body sensation from self-judgment
- understand what an emotion might need right now
- choose a gentle regulation step under 10 minutes
### Example prompts
- "I cannot name my mood, but it feels dark blue"
- "Map my emotions with colors instead of labels"
- "Help me understand this mixed restless and heavy feeling"
- "Give me a gentle emotional check-in"
## Workflow
1. Start with body sensation and energy level.
2. Choose a primary color, shade, or texture.
3. Add a secondary color if the feeling is mixed.
4. Translate the palette into possible emotion words.
5. Identify what the emotion might need.
6. Suggest one small next step that respects the state.
## Inputs
The user can provide any mix of:
- body sensations
- energy level
- explicit color words
- emotion words
- mixed or conflicting feelings
- context about stress, sadness, anger, or calm
- a wish for a gentle check-in instead of analysis
## Outputs
Return a markdown mood map with:
- current palette
- body cue summary
- possible emotion words
- likely need
- one gentle next step under 10 minutes
## Safety
- Separate feelings from self-judgment and story loops.
- Offer language options instead of pretending one exact label is always possible.
- During intense distress, bias toward grounding, rest, and human support language.
- Do not present the skill as mental health diagnosis or emergency care.
## Acceptance Criteria
- Return markdown text.
- Include at least one body cue and one need statement.
- Support mixed states with a secondary color when useful.
- End with one small next step, not a giant fix.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
COLOR_ORDER = ['blue', 'gray', 'grey', 'red', 'orange', 'yellow', 'green', 'purple', 'pink', 'black', 'white']
COLOR_EMOTIONS = {
'blue': ['sad', 'tender', 'tired'],
'gray': ['flat', 'numb', 'drained'],
'grey': ['flat', 'numb', 'drained'],
'red': ['angry', 'overloaded', 'activated'],
'orange': ['frustrated', 'driven', 'restless'],
'yellow': ['anxious', 'buzzing', 'alert'],
'green': ['calm', 'steady', 'safe'],
'purple': ['reflective', 'complex', 'sensitive'],
'pink': ['soft', 'connected', 'comfort-seeking'],
'black': ['heavy', 'shut down', 'overwhelmed'],
'white': ['blank', 'spaced out', 'quiet'],
}
COLOR_NEEDS = {
'blue': 'Rest, comfort, and a slower pace.',
'gray': 'Gentle activation plus low-pressure care.',
'grey': 'Gentle activation plus low-pressure care.',
'red': 'Space, release, and a reduction in stimulation.',
'orange': 'Movement, expression, and a clean boundary.',
'yellow': 'Reassurance, grounding, and less mental noise.',
'green': 'Protection of calm and steady routines.',
'purple': 'Reflection, expression, and emotional room.',
'pink': 'Warmth, softness, and relational safety.',
'black': 'Containment, grounding, and support.',
'white': 'Orientation, hydration, and a gentle return to the body.',
}
BODY_CUES = [
('tight chest or shallow breathing', ['chest', 'breath', 'breathing', 'tight']),
('heavy shoulders or body weight', ['heavy', 'slump', 'shoulder', 'tired', 'drained']),
('buzzing or restless energy', ['restless', 'buzzing', 'antsy', 'wired', 'jittery']),
('warmth, tension, or pressure', ['angry', 'heat', 'tense', 'pressure', 'frustrated']),
('softness or emotional tenderness', ['tender', 'tear', 'cry', 'soft', 'sad']),
('steady breathing and lower urgency', ['calm', 'steady', 'grounded', 'peaceful']),
]
INTENSITY_HIGH = ['very', 'intense', 'overwhelmed', 'panic', 'panicky', 'furious', 'extreme']
INTENSITY_LOW = ['slightly', 'a bit', 'soft', 'gentle', 'light', 'pale']
INFERRED_COLOR_RULES = [
('red', ['angry', 'rage', 'furious', 'overloaded']),
('yellow', ['anxious', 'nervous', 'restless', 'wired']),
('blue', ['sad', 'down', 'tearful', 'tired']),
('gray', ['numb', 'flat', 'drained', 'empty']),
('green', ['calm', 'steady', 'grounded', 'safe']),
('purple', ['mixed', 'complex', 'reflective']),
]
class MoodColorMapper:
def __init__(self, inputs: Any):
self.raw = _normalize_inputs(inputs)
self.lower = self.raw.lower()
self.primary_color, self.secondary_color = self._detect_palette()
self.intensity = self._detect_intensity()
self.body_note = self._body_note()
self.emotion_words = self._emotion_words()
self.likely_need = self._likely_need()
self.next_step = self._next_step()
def _explicit_colors(self) -> List[str]:
found: List[str] = []
for color in COLOR_ORDER:
pattern = rf'\b{color}\b'
if re.search(pattern, self.lower):
normalized = 'gray' if color == 'grey' else color
if normalized not in found:
found.append(normalized)
return found
def _detect_palette(self) -> List[str]:
explicit = self._explicit_colors()
if explicit:
if len(explicit) == 1 and any(word in self.lower for word in ['mixed', 'and', 'but']) and explicit[0] not in ['gray', 'purple']:
return [explicit[0], 'gray']
return [explicit[0], explicit[1] if len(explicit) > 1 else '']
inferred: List[str] = []
for color, words in INFERRED_COLOR_RULES:
if any(word in self.lower for word in words):
inferred.append(color)
if not inferred:
inferred = ['blue'] if any(word in self.lower for word in ['low energy', 'quiet']) else ['green']
if len(inferred) == 1 and any(word in self.lower for word in ['and', 'but', 'mixed']) and inferred[0] != 'gray':
inferred.append('gray')
unique: List[str] = []
for color in inferred:
if color not in unique:
unique.append(color)
return [unique[0], unique[1] if len(unique) > 1 else '']
def _detect_intensity(self) -> str:
if any(word in self.lower for word in INTENSITY_HIGH):
return 'High'
if any(word in self.lower for word in INTENSITY_LOW):
return 'Low'
if self.primary_color in ['red', 'black']:
return 'High'
return 'Medium'
def _body_note(self) -> str:
cues = [label for label, words in BODY_CUES if any(word in self.lower for word in words)]
if cues:
return '; '.join(cues[:2])
defaults = {
'blue': 'Energy feels lower and the body may want softness or rest.',
'gray': 'The body may feel muted, low-drive, or hard to read clearly.',
'red': 'The body may be carrying heat, pressure, or urgency.',
'yellow': 'The body may feel buzzy, alert, or hard to settle.',
'green': 'Breathing and body tension may be steadier than the mind expects.',
'purple': 'The body may be holding more than one feeling at once.',
'black': 'The body may need grounding and a sense of safety first.',
}
return defaults.get(self.primary_color, 'The body may be carrying a signal that needs gentle attention.')
def _emotion_words(self) -> str:
words: List[str] = []
for color in [self.primary_color, self.secondary_color]:
if color:
for item in COLOR_EMOTIONS[color]:
if item not in words:
words.append(item)
return ', '.join(words[:5])
def _likely_need(self) -> str:
primary_need = COLOR_NEEDS[self.primary_color]
if self.secondary_color:
secondary_need = COLOR_NEEDS[self.secondary_color]
if secondary_need != primary_need:
return f'{primary_need} Secondary need: {secondary_need}'
return primary_need
def _next_step(self) -> str:
if self.primary_color in ['red', 'yellow', 'black'] or self.intensity == 'High':
return 'Take 3 slow breaths, unclench your shoulders, and step away from extra stimulation for 5 minutes.'
if self.primary_color in ['blue', 'gray']:
return 'Choose one low-effort care move, like water, a blanket, a short walk, or a quiet note to yourself.'
if self.primary_color == 'green':
return 'Protect this steadier state for 5 more minutes before switching contexts.'
return 'Write one sentence or draw one small color note about what this feeling is trying to say.'
def render(self) -> str:
lines: List[str] = []
lines.append('# Mood Color Map')
lines.append('')
lines.append('## Current Palette')
lines.append(f'- Primary color: {self.primary_color}')
lines.append(f"- Secondary color, if any: {self.secondary_color or 'None'}")
lines.append(f'- Intensity level: {self.intensity}')
lines.append('')
lines.append('## What the Body Is Saying')
lines.append(f'- {self.body_note}')
lines.append('')
lines.append('## Possible Emotion Words')
lines.append(f'- {self.emotion_words}')
lines.append('')
lines.append('## Likely Need')
lines.append(f'- {self.likely_need}')
lines.append('')
lines.append('## Gentle Next Step')
lines.append(f'- One action under 10 minutes: {self.next_step}')
return '\n'.join(lines)
def handle(inputs):
_load_skill_meta('mood-color-mapper')
mapper = MoodColorMapper(inputs)
return mapper.render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import MoodColorMapper, handle
def test_explicit_primary_color_detection():
mapper = MoodColorMapper('I feel dark blue today and a little gray underneath it.')
assert mapper.primary_color == 'blue'
assert mapper.secondary_color == 'gray'
def test_need_statement_exists():
mapper = MoodColorMapper('I feel anxious and wired, like bright yellow all over.')
assert 'grounding' in mapper.likely_need.lower() or 'reassurance' in mapper.likely_need.lower()
def test_output_sections():
output = handle('My mood feels red and tense after a hard day.')
assert output.startswith('# Mood Color Map')
assert '## What the Body Is Saying' in output
assert '## Likely Need' in output
assert 'One action under 10 minutes' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Build a focused work session card with the right session mode, a visible finish line, start ritual, distraction capture plan, break rule, and interruption re...
---
name: focus-session-timer
description: Build a focused work session card with the right session mode, a visible finish line, start ritual, distraction capture plan, break rule, and interruption recovery. Use when the user wants help choosing between a quick sprint, standard focus block, or deep work block without relying on an actual timer app.
---
# Focus Session Timer
## Overview
Use this skill to shape one work or study block into a clear, recoverable session instead of a vague attempt to "focus harder." It helps the user match session length to task difficulty, define what done looks like, protect attention during the block, and restart cleanly after interruptions.
This skill is descriptive only. It does not run a stopwatch, trigger notifications, or connect to focus apps.
## Trigger
Use this skill when the user wants to:
- choose the right focus block for a task
- define one visible finish line before starting
- reduce distraction drift during work or study
- recover from interruptions without abandoning the session
- end a session with a clean handoff into the next block
### Example prompts
- "Help me set up a deep work block for writing"
- "I need a focus session card for studying calculus"
- "I keep getting interrupted and losing my work rhythm"
- "Choose a better work interval than random Pomodoros"
## Workflow
1. Identify the task and how cognitively heavy it is.
2. Match the task to a mode: Quick Sprint, Standard Focus, or Deep Work Block.
3. Define a visible finish line for the session.
4. Add a start ritual, distraction capture method, and break rule.
5. Add an interruption recovery protocol.
6. End with a short debrief and next starting point.
## Inputs
The user can provide any mix of:
- the task or study target
- difficulty or cognitive load
- time available
- energy level
- interruption risk
- work, family, or support-role constraints
- what usually derails focus
## Outputs
Return a markdown focus card with:
- session goal and definition of done
- chosen mode
- start ritual
- during-session distraction handling
- break plan
- short end review
## Safety
- Keep one session equal to one clear objective.
- Match the session mode to real energy and interruption risk, not idealized ambition.
- Encourage breaks that restore attention instead of fragmenting it.
- Do not pretend this skill is an actual timer or attention-treatment tool.
## Acceptance Criteria
- Return markdown text.
- Include a clear task, definition of done, and chosen mode.
- Include interruption recovery and a next starting point.
- Keep the session realistic for the stated context.
FILE:handler.py
#!/usr/bin/env python3
import json
import os
import re
import sys
from typing import Any, Dict, List
def _load_skill_meta(slug):
path = os.path.join(os.path.dirname(__file__), 'SKILL.md')
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
meta = re.search(r'^---$(.*?)^---$', content, re.DOTALL | re.MULTILINE)
return meta.group(1).strip() if meta else ''
def _normalize_inputs(inputs: Any) -> str:
if inputs is None:
return ''
if isinstance(inputs, str):
return inputs.strip()
try:
return json.dumps(inputs, ensure_ascii=False, sort_keys=True)
except TypeError:
return str(inputs)
HEAVY_TASK_WORDS = ['write', 'draft', 'strategy', 'code', 'coding', 'research', 'study', 'chapter', 'memo', 'analysis', 'problem set', 'exam']
LIGHT_TASK_WORDS = ['email', 'inbox', 'admin', 'cleanup', 'review', 'reply', 'small task', 'quick']
LOW_ENERGY_WORDS = ['tired', 'low energy', 'foggy', 'drained', 'sleepy', 'exhausted']
INTERRUPTION_WORDS = ['interrupt', 'kids', 'child', 'caregiving', 'meeting', 'on-call', 'support', 'phone', 'messages']
class FocusSessionPlanner:
def __init__(self, inputs: Any):
self.raw = _normalize_inputs(inputs)
self.lower = self.raw.lower()
self.task = self._extract_task()
self.cognitive_load = self._detect_cognitive_load()
self.mode = self._choose_mode()
self.definition_of_done = self._definition_of_done()
self.start_ritual = self._start_ritual()
self.interruption_rule = self._interruption_rule()
self.break_plan = self._break_plan()
self.progress_prompt = self._progress_prompt()
self.next_start = self._next_start()
def _snippets(self) -> List[str]:
pieces = re.split(r'[\n.;!?]+', self.raw)
return [piece.strip(' -•') for piece in pieces if piece.strip(' -•')]
def _extract_task(self) -> str:
patterns = [
r'(?:focus on|work on|study|writing|write|finish|complete)\s+(.+?)(?:\b(?:for|with|before|but|and)\b|$)',
r'task\s*(?:is|:)?\s*(.+?)(?:\b(?:for|with|before|but|and)\b|$)',
]
for pattern in patterns:
match = re.search(pattern, self.lower)
if match:
task = match.group(1).strip(' ,')
if task:
return task[:1].upper() + task[1:]
snippets = self._snippets()
if snippets:
snippet = snippets[0].strip()
return snippet[:1].upper() + snippet[1:]
return 'Move one meaningful task forward'
def _detect_cognitive_load(self) -> str:
if any(word in self.lower for word in HEAVY_TASK_WORDS):
return 'heavy'
if any(word in self.lower for word in LIGHT_TASK_WORDS):
return 'light'
return 'medium'
def _choose_mode(self) -> Dict[str, str]:
interrupted = any(word in self.lower for word in INTERRUPTION_WORDS)
low_energy = any(word in self.lower for word in LOW_ENERGY_WORDS)
explicit_short = bool(re.search(r'\b(10|15)\s*(minutes|minute|min)\b', self.lower))
explicit_long = bool(re.search(r'\b(45|50|60|90)\s*(minutes|minute|min)\b', self.lower)) or 'deep work' in self.lower
if self.cognitive_load == 'light' or explicit_short:
return {'label': 'Quick Sprint', 'length': '15 minutes', 'break': '3 minutes'}
if self.cognitive_load == 'heavy' and not interrupted and not low_energy:
return {'label': 'Deep Work Block', 'length': '50 minutes', 'break': '10 minutes'}
if explicit_long and not interrupted:
return {'label': 'Deep Work Block', 'length': '50 minutes', 'break': '10 minutes'}
return {'label': 'Standard Focus', 'length': '25 minutes', 'break': '5 minutes'}
def _definition_of_done(self) -> str:
if 'write' in self.lower or 'draft' in self.lower:
return 'Finish one rough draft or one clear section.'
if 'study' in self.lower or 'problem' in self.lower or 'exam' in self.lower:
return 'Finish one problem set, section, or review target.'
if 'email' in self.lower or 'inbox' in self.lower:
return 'Clear one defined batch of messages and leave the inbox with a visible stopping point.'
if 'code' in self.lower or 'debug' in self.lower:
return 'Finish one bug fix, one function, or one tested chunk of implementation.'
return f'Finish one visible chunk of: {self.task.lower()}.'
def _start_ritual(self) -> List[str]:
ritual = [
'Put the task name and finish line where you can see it.',
'Close or silence the obvious distraction channels for one block.',
]
if self.mode['label'] == 'Quick Sprint':
ritual.append('Open only the single document, tab, or queue this sprint needs.')
elif self.mode['label'] == 'Deep Work Block':
ritual.append('Set a blank capture line for stray ideas so they do not steal the block.')
else:
ritual.append('Take one breath and name the first concrete action before starting.')
return ritual
def _interruption_rule(self) -> str:
if any(word in self.lower for word in INTERRUPTION_WORDS):
return 'Mark the exact stopping point, write the next tiny action, handle the interruption, then restart with a 5 minute re-entry sprint.'
return 'If interrupted, write one sentence about where you stopped, park the distraction, and restart from the smallest next action.'
def _break_plan(self) -> str:
return f"{self.mode['break']} away from the task. A real break means standing up, moving, water, or a window, not messages or scrolling."
def _progress_prompt(self) -> str:
if self.mode['label'] == 'Deep Work Block':
return 'Name the deepest part of the task that actually moved.'
if self.mode['label'] == 'Quick Sprint':
return 'Name the smallest visible thing you finished.'
return 'Name what moved and what stayed sticky.'
def _next_start(self) -> str:
if 'write' in self.lower or 'draft' in self.lower:
return 'Start the next session by reopening the draft and revising the first unfinished paragraph.'
if 'study' in self.lower or 'problem' in self.lower:
return 'Start with the first unsolved problem or the note you left beside it.'
if 'email' in self.lower or 'inbox' in self.lower:
return 'Start with the next message batch, not the whole inbox.'
return 'Start with the first unfinished step you can do in under two minutes.'
def render(self) -> str:
lines: List[str] = []
lines.append('# Focus Session Card')
lines.append('')
lines.append('## Session Goal')
lines.append(f'- Task: {self.task}')
lines.append(f'- Definition of done: {self.definition_of_done}')
lines.append(f"- Chosen mode: {self.mode['label']} ({self.mode['length']})")
lines.append('')
lines.append('## Start Ritual')
for item in self.start_ritual:
lines.append(f'- {item}')
lines.append('')
lines.append('## During Session')
lines.append('- Distraction capture: Keep one scratch line or note open for unrelated thoughts, errands, or tabs to revisit later.')
lines.append(f'- If interrupted, I will: {self.interruption_rule}')
lines.append('')
lines.append('## Break Plan')
lines.append(f"- Length: {self.mode['break']}")
lines.append(f'- What counts as a real break: {self.break_plan}')
lines.append('')
lines.append('## End Review')
lines.append(f'- Progress made: {self.progress_prompt}')
lines.append(f'- Next starting point: {self.next_start}')
return '\n'.join(lines)
def handle(inputs):
_load_skill_meta('focus-session-timer')
planner = FocusSessionPlanner(inputs)
return planner.render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import FocusSessionPlanner, handle
def test_deep_work_mode_for_heavy_task():
planner = FocusSessionPlanner('I need to write a strategy memo and I can protect a full hour this morning.')
assert planner.mode['label'] == 'Deep Work Block'
def test_quick_sprint_for_light_task():
planner = FocusSessionPlanner('Help me focus on email cleanup for 15 minutes before a meeting.')
assert planner.mode['label'] == 'Quick Sprint'
def test_output_sections():
output = handle('I want a focused study session for calculus, but I get interrupted by my kids.')
assert output.startswith('# Focus Session Card')
assert '## During Session' in output
assert '## Break Plan' in output
assert '## End Review' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')