@clawhub-harrylabsj-35a31b2850
Relate Coach helps users improve everyday communication, set healthier boundaries, de-escalate conflict, and maintain better relationships through practical...
---
name: Relate Coach
description: Relate Coach helps users improve everyday communication, set healthier boundaries, de-escalate conflict, and maintain better relationships through practical self-help frameworks such as nonviolent communication, active listening, and respectful assertiveness. Use when the user wants interpersonal skill support, not therapy, diagnosis, matchmaking, or someone to act on their behalf.
---
# Relate Coach
Relate Coach is a practical communication and relationship skill.
Its job is to help the user handle everyday interpersonal situations with more clarity, steadiness, and self-respect.
This skill is strongest when the user needs help with:
- saying something difficult without escalating the situation
- understanding how to respond in a tense conversation
- setting a boundary without becoming harsh or vague
- navigating conflict at work, with friends, or in family life
- expressing needs more clearly
- listening better instead of reacting too fast
## What This Skill Is For
Use this skill when the user asks for help such as:
- "How do I say this without starting a fight?"
- "I do not know how to express my needs."
- "How do I set a boundary without feeling guilty?"
- "I keep freezing in conflict."
- "How do I talk to a coworker or manager about this?"
- "Help me respond calmly instead of emotionally."
Default outcomes should be:
- a clearer communication goal
- a practical response frame
- a calmer next message or next conversation
- a boundary statement the user can actually say
- a short reflection that improves the next interaction
## Core Capabilities
### 1. Communication Clarity
Help the user turn a messy emotional reaction into:
- what happened
- what they felt
- what they needed
- what they want to say next
### 2. Nonviolent Communication Support
Use simple NVC-style structure when helpful:
- observation
- feeling
- need
- request
Do not force the format mechanically. The goal is useful communication, not textbook purity.
### 3. Active Listening Support
When the user is trying to understand another person better, help them:
- reflect what they heard
- name likely emotions carefully
- ask open questions
- slow the pace of the conversation
### 4. Conflict De-escalation
Help the user reduce heat and regain direction:
- separate facts from interpretations
- identify the real issue
- lower accusation and defensiveness
- move toward one concrete next step
### 5. Boundary Setting
Help the user set boundaries that are:
- clear
- respectful
- specific
- enforceable
Avoid vague boundary advice that sounds wise but cannot actually be used.
## Typical Use Cases
### Workplace
- disagreeing respectfully
- pushing back on extra work
- addressing repeated miscommunication
- preparing for a difficult manager or teammate conversation
### Friends And Social Life
- saying no without overexplaining
- addressing hurt feelings directly
- handling one-sided friendships
- recovering after awkward interactions
### Family
- speaking more calmly under pressure
- interrupting repetitive arguments
- setting limits around time, money, or emotional labor
### General Interpersonal Growth
- learning to speak more directly
- reducing avoidance
- practicing better listening
- building more self-respecting communication habits
## What This Skill Does Not Do
This skill does not provide:
- therapy or mental health treatment
- psychological diagnosis
- marriage counseling or couples therapy
- dating strategy, seduction, matchmaking, or ex-recovery tactics
- impersonation or acting as the user in real conversations
- manipulative scripts for controlling other people
If the user needs professional support, say so clearly and helpfully.
## Safety Boundary
Slow down and refer out when the user describes:
- abuse
- coercive control
- stalking or harassment
- serious threats
- self-harm or harm to others
- trauma-level psychological distress
In those cases:
- do not continue as if this were a normal communication coaching request
- do not offer clever scripts as the main answer
- encourage professional, legal, or emergency support as appropriate
## Response Style
The best response usually includes:
### What Is Going On
Name the interaction pattern simply and without drama.
### What To Aim For
Clarify the user's real communication goal.
### What To Say Or Do Next
Give a concrete next step, sentence starter, or conversation frame.
### What To Avoid
Call out the most common mistake that would make the interaction worse.
### Optional Practice Rewrite
When useful, rewrite the user's draft into something calmer, clearer, and more usable.
## Good Output Principles
- Be practical, not preachy.
- Be emotionally literate, but do not drift into therapy voice.
- Prefer usable language over abstract theory.
- Help the user become more direct, not more performative.
- Encourage self-respect and respect for others at the same time.
## Current Product Shape
This release focuses on the public-facing coaching scope:
- communication clarity
- conflict response
- active listening
- boundary setting
- everyday relationship maintenance
It should feel like a grounded interpersonal skills coach, not a counselor, dating assistant, or replacement for real human relationships.
FILE:scripts/boundary_checker.py
#!/usr/bin/env python3
"""Boundary and escalation checks for Relate Coach."""
from __future__ import annotations
CRISIS_KEYWORDS = {
"immediate_danger": [
"kill myself",
"suicide",
"want to die",
"hurt myself",
"self harm",
"unsafe right now",
"in immediate danger",
"going to hurt someone",
],
"abuse_or_violence": [
"domestic violence",
"my partner hit me",
"abuse",
"violent at home",
"threatened me",
"strangled",
],
}
PROFESSIONAL_REFER_KEYWORDS = {
"stalking_or_control": [
"stalking",
"harassment",
"coercive control",
"controlling me",
"pua",
],
"trauma_or_severe_distress": [
"ptsd",
"trauma",
"panic attacks",
"severe anxiety",
"depression treatment",
"diagnose me",
],
}
OUT_OF_SCOPE_KEYWORDS = {
"dating_or_matchmaking": [
"find me a date",
"matchmaking",
"pick-up lines",
"how do i seduce",
"how do i make them like me",
],
"ex_recovery": [
"get my ex back",
"win my ex back",
"make my ex return",
],
"therapy_or_diagnosis": [
"am i mentally ill",
"do i have a disorder",
"therapy session",
"treat my anxiety",
],
"impersonation": [
"pretend to be me",
"message them for me",
"talk to them as me",
"impersonate me",
],
}
def check_boundary(text: str) -> tuple[str, str | None, str | None]:
lowered = text.casefold()
category, keyword = _find_match(lowered, CRISIS_KEYWORDS)
if category:
return "crisis", category, keyword
category, keyword = _find_match(lowered, PROFESSIONAL_REFER_KEYWORDS)
if category:
return "professional_refer", category, keyword
category, keyword = _find_match(lowered, OUT_OF_SCOPE_KEYWORDS)
if category:
return "out_of_scope", category, keyword
return "in_scope", None, None
def _find_match(text: str, groups: dict[str, list[str]]) -> tuple[str | None, str | None]:
for category, keywords in groups.items():
for keyword in keywords:
if keyword in text:
return category, keyword
return None, None
def get_out_of_scope_response(category: str | None, keyword: str | None) -> dict:
category_map = {
"dating_or_matchmaking": "dating, seduction, or matchmaking",
"ex_recovery": "getting an ex-partner back",
"therapy_or_diagnosis": "therapy or diagnosis",
"impersonation": "impersonation or communicating as someone else",
}
topic = category_map.get(category, "that request")
return {
"type": "out_of_scope",
"title": "Out of scope for Relate Coach",
"summary": f"I cannot help with {topic}. Relate Coach is for low-stakes communication and relationship skills, not specialized or deceptive services.",
"next_steps": [
"If your need is everyday communication, I can still help with scripts, boundaries, listening, conflict repair, or workplace conversations.",
"If you need counseling, diagnosis, or legal advice, please contact a qualified professional in that field.",
],
"watch_outs": [
f"Matched keyword: {keyword}" if keyword else "Matched an out-of-scope topic.",
],
"reflection": "If you want to continue, tell me the low-stakes communication problem underneath the request.",
}
def get_professional_refer_response(category: str | None, keyword: str | None) -> dict:
if category == "stalking_or_control":
summary = "What you described may involve coercion, stalking, or harassment. That is beyond self-help coaching."
next_steps = [
"Prioritize safety and documentation where possible.",
"Reach out to a trusted person, a local support organization, or legal authorities if needed.",
"Consider speaking with a licensed therapist or advocate who handles abusive or controlling dynamics.",
]
else:
summary = "This sounds serious enough that professional mental-health or trauma support would be more appropriate than normal communication coaching."
next_steps = [
"Consider a licensed therapist, counselor, or physician for assessment and support.",
"If symptoms are escalating quickly, contact urgent local support resources.",
"Use Relate Coach only for low-stakes communication planning after immediate support is in place.",
]
return {
"type": "professional_refer",
"title": "Professional support recommended",
"summary": summary,
"next_steps": next_steps,
"watch_outs": [
f"Matched keyword: {keyword}" if keyword else "Matched a professional-referral topic.",
],
"reflection": "What support person or qualified resource can you contact first?",
}
def get_crisis_response(category: str | None, keyword: str | None) -> dict:
if category == "abuse_or_violence":
summary = "This may involve immediate safety risk or violence. Normal relationship coaching is not the right next step."
else:
summary = "This sounds like a potential crisis or immediate safety issue. Normal relationship coaching should stop here."
return {
"type": "crisis",
"title": "Safety first",
"summary": summary,
"next_steps": [
"If there is immediate danger, contact local emergency services right now.",
"Reach out to a trusted person nearby who can help you stay safe.",
"Use a local crisis line, emergency department, or licensed crisis professional as soon as possible.",
],
"resources": [
"Local emergency services",
"Local crisis hotline or emergency department",
"A trusted friend, family member, or advocate who can help immediately",
],
"watch_outs": [
f"Matched keyword: {keyword}" if keyword else "Matched a crisis topic.",
],
"reflection": "Who can you contact immediately to reduce danger in the next 10 minutes?",
}
FILE:scripts/handler.py
#!/usr/bin/env python3
"""Relate Coach - practical communication guidance for low-stakes relationship situations."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from boundary_checker import ( # noqa: E402
check_boundary,
get_crisis_response,
get_out_of_scope_response,
get_professional_refer_response,
)
INTENT_KEYWORDS = {
"repair": [
"apologize",
"apology",
"make it right",
"repair",
"said the wrong thing",
"hurt their feelings",
],
"boundary": [
"boundary",
"say no",
"need space",
"overstep",
"too much",
"after work",
"keeps messaging",
"guilty saying no",
"people pleasing",
],
"conflict": [
"conflict",
"fight",
"argument",
"argue",
"resentment",
"tension",
"disagreement",
"clash",
],
"listening": [
"listen",
"listening",
"heard",
"interrupt",
"misunderstood",
"validate",
"not hearing me",
],
"nvc": [
"nvc",
"nonviolent communication",
],
"workplace": [
"boss",
"manager",
"coworker",
"colleague",
"team",
"workplace",
"office",
"feedback",
"meeting",
"employee",
],
"family": [
"parent",
"mother",
"father",
"mom",
"dad",
"family",
"sibling",
"child",
"kids",
"relative",
],
"intimate": [
"partner",
"girlfriend",
"boyfriend",
"wife",
"husband",
"spouse",
"romantic",
"relationship",
],
"social": [
"friend",
"friends",
"social anxiety",
"group chat",
"awkward",
"party",
"invite",
"lonely",
],
"communication": [
"communicate",
"communication",
"express myself",
"bring it up",
"start the conversation",
"say this",
"tell them",
],
}
INTENT_PRIORITY = [
"repair",
"boundary",
"conflict",
"listening",
"nvc",
"workplace",
"family",
"intimate",
"social",
"communication",
]
PLAYBOOKS = {
"communication": {
"title": "Clear Conversation Starter",
"summary": "Use this when you know you need to talk, but keep rambling, softening the point, or waiting too long.",
"best_for": [
"bringing up a small but important issue",
"saying what you need without sounding harsh",
"starting a conversation before resentment builds",
],
"quick_reset": [
"State the issue in one concrete sentence.",
"Name the impact on you instead of judging the other person.",
"Ask for one specific next step, not a personality change.",
],
"starter_scripts": [
"I want to bring up something small now so it does not turn into a bigger issue later.",
"When this happened, I felt frustrated and disconnected. I want to talk about a better way forward.",
"What I am hoping for is one small adjustment: can we try __ next time?",
],
"watch_outs": [
"Do not stack five old grievances into one conversation.",
"Do not use 'always' or 'never' unless it is literally true.",
"Do not ask a vague question when you already know the request you want to make.",
],
"reflection": "What is the shortest honest sentence that captures the real issue?",
},
"listening": {
"title": "Active Listening Reset",
"summary": "Use this when the other person feels unheard, keeps repeating themselves, or shuts down because they expect advice instead of understanding.",
"best_for": [
"de-escalating defensiveness",
"helping someone feel understood before problem-solving",
"repairing conversations where you keep interrupting",
],
"quick_reset": [
"Reflect back the facts you heard.",
"Name the feeling you think might be present.",
"Ask if you got it right before offering a view.",
],
"starter_scripts": [
"What I am hearing is that this felt unfair and exhausting. Did I get that right?",
"It sounds like you were hoping for support, not another task to manage.",
"Do you want me to listen first, help you think, or help you decide?",
],
"watch_outs": [
"Do not jump to reassurance too quickly.",
"Do not translate their feelings into your own story.",
"Do not ask rapid-fire questions that feel like interrogation.",
],
"reflection": "What emotion is the other person likely trying to get you to recognize?",
},
"conflict": {
"title": "Conflict Repair Framework",
"summary": "Use this when a conversation is getting heated, repetitive, or emotionally expensive and you need structure instead of more force.",
"best_for": [
"slowing down arguments before they become personal",
"separating the issue from the relationship",
"finding one next move both people can accept",
],
"quick_reset": [
"Pause before rebutting and identify the actual topic.",
"Say what matters to you without diagnosing the other person.",
"Name one shared goal before negotiating solutions.",
],
"starter_scripts": [
"I do not want this to become a scorekeeping fight. Can we slow down and focus on one issue?",
"I think we both want this relationship to feel more steady, even if we disagree on the details.",
"Before we solve it, I want to make sure I understand what felt most upsetting to you.",
],
"watch_outs": [
"Avoid mind-reading: stay with what was said or done.",
"Avoid debating tone while ignoring the underlying issue.",
"Avoid forcing closure when one person is too activated to think clearly.",
],
"reflection": "Is this disagreement about logistics, values, or feeling unseen?",
},
"boundary": {
"title": "Boundary Setting Playbook",
"summary": "Use this when you need to protect your time, energy, attention, or comfort without becoming vague, apologetic, or hostile.",
"best_for": [
"saying no without over-explaining",
"responding to repeated overstepping",
"setting clearer expectations around time, contact, or emotional labor",
],
"quick_reset": [
"State the limit clearly.",
"Name what you will do, not what you wish they would magically understand.",
"Repeat the limit calmly if the person pushes back.",
],
"starter_scripts": [
"I am not available for that tonight.",
"I can help for 15 minutes, but I cannot take this whole task on.",
"I do not respond to work requests after hours unless something is urgent and clearly labeled.",
],
"watch_outs": [
"Do not bury the boundary under a long apology.",
"Do not hint repeatedly when what you need is a direct limit.",
"Do not threaten consequences you will not actually follow through on.",
],
"reflection": "What limit are you trying to enforce, and what behavior will show that the limit is real?",
},
"workplace": {
"title": "Workplace Communication Coach",
"summary": "Use this for low-stakes work conversations involving feedback, disagreement, workload, expectations, or tone with managers and colleagues.",
"best_for": [
"disagreeing professionally",
"pushing back on scope without sounding defensive",
"clarifying expectations before resentment grows",
],
"quick_reset": [
"Lead with the shared goal.",
"Describe the constraint or concern clearly.",
"Offer options instead of a flat wall when possible.",
],
"starter_scripts": [
"I want to make this land well. Right now I see a timing tradeoff between A and B. Which outcome matters more?",
"I have concerns about the current approach, and I want to pressure-test them with you before we commit.",
"I can take this on, but only if we move the deadline or reduce scope. Which would you prefer?",
],
"watch_outs": [
"Do not frame disagreement as a moral failing by the other person.",
"Do not say yes too quickly and hope the problem disappears.",
"Do not turn a feedback conversation into a hidden argument about respect.",
],
"reflection": "What is the cleanest way to describe the tradeoff without sounding oppositional?",
},
"social": {
"title": "Social Confidence Guide",
"summary": "Use this for everyday social friction: awkwardness, invitations, follow-up anxiety, drifting friendships, or not knowing how to re-enter a conversation.",
"best_for": [
"friendship maintenance",
"recovering after awkward moments",
"reducing overthinking around small social risks",
],
"quick_reset": [
"Aim for warmth, not performance.",
"Keep the next move small and easy to answer.",
"Assume normal ambiguity before assuming rejection.",
],
"starter_scripts": [
"Hey, I realized I went quiet after that conversation. I wanted to say I enjoyed seeing you.",
"Want to grab coffee next week? No pressure if your schedule is packed.",
"I think I came off a bit awkward earlier, but I did mean what I said.",
],
"watch_outs": [
"Do not demand certainty from casual interactions.",
"Do not over-correct awkwardness with too much intensity.",
"Do not interpret delayed replies as proof of dislike.",
],
"reflection": "What is the smallest genuine move that would make this interaction easier?",
},
"family": {
"title": "Family Tension Playbook",
"summary": "Use this for recurring family patterns like criticism, guilt, pressure, role confusion, or conversations that instantly turn you back into old versions of yourself.",
"best_for": [
"speaking to parents without exploding or collapsing",
"staying clear when guilt is used to steer you",
"naming limits while preserving respect",
],
"quick_reset": [
"Name the topic you are willing to discuss.",
"Do not defend every detail of your life choices.",
"End the conversation if respect disappears.",
],
"starter_scripts": [
"I know this matters to you. I am willing to talk about it for ten minutes, but not if the conversation becomes insulting.",
"I hear that you disagree. I am not asking for permission; I am letting you know my decision.",
"I do want a relationship with you, and I need our conversations to stay respectful for that to work.",
],
"watch_outs": [
"Do not confuse closeness with unlimited access.",
"Do not keep arguing after the real issue becomes respect.",
"Do not expect one perfect script to undo a long family pattern overnight.",
],
"reflection": "What part of this conversation is about the present, and what part is an old family pattern getting reactivated?",
},
"intimate": {
"title": "Intimate Relationship Check-In",
"summary": "Use this for low-stakes partnership issues like feeling distant, misaligned expectations, recurring hurt, or needing a calmer way to ask for closeness.",
"best_for": [
"asking for more connection without blame",
"naming resentment before it hardens",
"repairing small ruptures before they become identity-level fights",
],
"quick_reset": [
"Lead with the experience you want, not the flaw you think the other person has.",
"Stay specific about what happened.",
"Ask for one visible behavior change.",
],
"starter_scripts": [
"I miss feeling close to you, and I want us to talk before this turns into distance.",
"When that happened, I felt dismissed. I do not think that was your goal, but I want us to handle moments like that differently.",
"Could we set aside 20 minutes tonight to reset instead of trying to solve this while we are both distracted?",
],
"watch_outs": [
"Do not package protest as sarcasm.",
"Do not turn one incident into a verdict on the whole relationship.",
"Do not demand vulnerability while sounding punitive.",
],
"reflection": "If this conversation went well, what would feel different afterward?",
},
"repair": {
"title": "Apology and Repair Script",
"summary": "Use this when you made a mistake, got defensive, or caused hurt and want to repair without centering your own guilt.",
"best_for": [
"clean apologies",
"repair after harsh tone or avoidance",
"rebuilding trust through specific accountability",
],
"quick_reset": [
"Name what you did.",
"Name the likely impact without arguing intent.",
"Offer a concrete repair or changed behavior.",
],
"starter_scripts": [
"I handled that badly. I interrupted you and got defensive, and I can see how that would feel dismissive.",
"You did not deserve that tone from me. I am sorry.",
"Next time I am going to pause before replying. If you are open to it, I would like to try this conversation again.",
],
"watch_outs": [
"Do not add 'but you also...' to the apology.",
"Do not ask for immediate forgiveness as proof the apology worked.",
"Do not confuse regret with repair; trust usually needs a changed pattern.",
],
"reflection": "What would accountability look like beyond the words 'I am sorry'?",
},
"nvc": {
"title": "Nonviolent Communication Guide",
"summary": "Use this when you need a clear structure for hard conversations and want to stay grounded in observations, feelings, needs, and requests.",
"best_for": [
"difficult conversations that keep turning judgmental",
"naming needs without sounding accusatory",
"asking for change without attacking character",
],
"quick_reset": [
"Observation: say what happened without labels.",
"Feeling: name the emotion, not the accusation.",
"Need and request: say what matters and ask for one actionable change.",
],
"starter_scripts": [
"When our call started 30 minutes late twice this week, I felt stressed because reliability matters to me. Next time, can you text me 15 minutes ahead if you are running behind?",
"When feedback comes in group chat, I feel exposed because I care about learning without getting flooded. Could we move this type of feedback to a direct message first?",
],
"watch_outs": [
"Observation is not a disguised judgment.",
"Feelings are emotions, not stories about the other person's motives.",
"A request is more useful than a character critique.",
],
"reflection": "Which part is hardest for you right now: the observation, the feeling, or the request?",
},
}
GENERAL_RESPONSE = {
"title": "Relate Coach",
"summary": "I can help you think through a low-stakes relationship conversation and turn it into a clearer next move.",
"best_for": [
"saying something hard without escalating",
"setting a boundary clearly",
"repairing after conflict or awkwardness",
"handling everyday workplace, friendship, family, or partner conversations",
],
"quick_reset": [
"Tell me who the person is, what happened, and what outcome you want.",
"If you want, ask for a track directly: boundary, conflict, listening, workplace, family, social, intimate, apology, or NVC.",
],
"starter_scripts": [
"Help me say no without sounding cold.",
"My coworker keeps messaging me after work. Give me a script.",
"I need to apologize to a friend without making it about me.",
],
"watch_outs": [
"This tool is for communication and low-stakes relationship support, not therapy or diagnosis.",
"If there is abuse, stalking, immediate danger, or self-harm risk, use the safety path instead of normal coaching.",
],
"reflection": "What conversation are you avoiding right now?",
}
def detect_intents(text: str) -> list[str]:
lowered = text.casefold()
scores: dict[str, int] = {}
for intent, keywords in INTENT_KEYWORDS.items():
score = sum(1 for keyword in keywords if keyword in lowered)
if score:
scores[intent] = score
if not scores:
return []
return sorted(scores, key=lambda intent: (-scores[intent], INTENT_PRIORITY.index(intent)))
def detect_intent(text: str) -> str:
intents = detect_intents(text)
return intents[0] if intents else "general"
def infer_context(text: str) -> dict[str, bool]:
lowered = text.casefold()
return {
"repeated_pattern": any(token in lowered for token in ["always", "again", "keeps", "every time", "repeatedly"]),
"power_gap": any(token in lowered for token in ["boss", "manager", "parent", "father", "mother", "teacher"]),
"time_pressure": any(token in lowered for token in ["today", "tonight", "soon", "urgent", "asap", "tomorrow"]),
}
def build_response(user_text: str) -> dict:
status, category, keyword = check_boundary(user_text)
if status == "crisis":
return get_crisis_response(category, keyword)
if status == "professional_refer":
return get_professional_refer_response(category, keyword)
if status == "out_of_scope":
return get_out_of_scope_response(category, keyword)
relevant = detect_intents(user_text)
primary = relevant[0] if relevant else "general"
context = infer_context(user_text)
if primary == "general":
response = dict(GENERAL_RESPONSE)
else:
response = dict(PLAYBOOKS[primary])
response["type"] = primary
response["also_relevant"] = [intent for intent in relevant[1:3] if intent != primary]
response["context_notes"] = build_context_notes(primary, context)
return response
def build_context_notes(intent: str, context: dict[str, bool]) -> list[str]:
notes: list[str] = []
if context["repeated_pattern"] and intent in {"boundary", "conflict", "family", "intimate"}:
notes.append("Because this sounds repeated, name the pattern directly and avoid restarting from zero.")
if context["power_gap"] and intent in {"boundary", "workplace", "family"}:
notes.append("Because there may be a power gap, keep the message calm, brief, and behavior-focused.")
if context["time_pressure"]:
notes.append("Since timing sounds tight, lead with the one sentence that matters most instead of the full backstory.")
return notes
def format_response(payload: dict) -> str:
lines = [f"# {payload['title']}", "", payload["summary"]]
if payload.get("also_relevant"):
also = ", ".join(payload["also_relevant"])
lines.extend(["", f"Also relevant tracks: {also}"])
if payload.get("best_for"):
lines.extend(["", "## Best for"])
lines.extend(f"- {item}" for item in payload["best_for"])
if payload.get("quick_reset"):
lines.extend(["", "## Quick reset"])
lines.extend(f"- {item}" for item in payload["quick_reset"])
if payload.get("starter_scripts"):
lines.extend(["", "## Starter scripts"])
lines.extend(f"- {item}" for item in payload["starter_scripts"])
if payload.get("context_notes"):
lines.extend(["", "## Context notes"])
lines.extend(f"- {item}" for item in payload["context_notes"])
if payload.get("watch_outs"):
lines.extend(["", "## Watch-outs"])
lines.extend(f"- {item}" for item in payload["watch_outs"])
if payload.get("next_steps"):
lines.extend(["", "## Next steps"])
lines.extend(f"- {item}" for item in payload["next_steps"])
if payload.get("resources"):
lines.extend(["", "## Resources"])
lines.extend(f"- {item}" for item in payload["resources"])
if payload.get("reflection"):
lines.extend(["", "## Reflection question", payload["reflection"]])
if payload.get("message"):
lines.extend(["", payload["message"]])
return "\n".join(lines).strip()
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Relate Coach - practical scripts for low-stakes relationship conversations")
parser.add_argument("text", nargs="*", help="User message or relationship scenario")
parser.add_argument("--json", action="store_true", dest="as_json", help="Output the response payload as JSON")
args = parser.parse_args(argv)
user_text = " ".join(args.text).strip()
if not user_text:
print("Describe the relationship situation you want help with.")
return 1
payload = build_response(user_text)
if args.as_json:
print(json.dumps(payload, indent=2))
else:
print(format_response(payload))
return 0
if __name__ == "__main__":
raise SystemExit(main())
FILE:skill.json
{
"name": "Relate Coach",
"slug": "relate-coach",
"description": "Relate Coach helps users improve everyday communication, set healthier boundaries, de-escalate conflict, and maintain better relationships through practical self-help frameworks such as nonviolent communication, active listening, and respectful assertiveness.",
"version": "1.0.2",
"author": "Golden Bean",
"tags": ["communication", "relationships", "conflict", "boundary-setting", "interpersonal-skills", "self-help"],
"safety": {
"no_diagnosis": true,
"no_treatment": true,
"crisis_escalation": false,
"boundary_check": true,
"disclaimer_required": true,
"scope_limitation": true
},
"modules": {
"handler": "scripts/handler.py",
"boundary": "scripts/boundary_checker.py"
},
"entry": "scripts/handler.py"
}
A comprehensive stress management toolkit offering quick relaxation techniques and exercises. Suitable for moments of high stress, anxiety, or when you need...
---
name: stress-toolkit
description: A comprehensive stress management toolkit offering quick relaxation techniques and exercises. Suitable for moments of high stress, anxiety, or when you need to unwind and find calm.
---
# Stress Toolkit
> 🟢 **MVP/P0 Complete**: Core features implemented with crisis detection and referral mechanisms.
## Overview
A collection of rapid stress-relief techniques for everyday use:
- Feeling stressed or anxious
- Trouble relaxing or falling asleep
- Emotional overwhelm, can't find calm
## Core Modules
| Feature | Description |
|---------|-------------|
| Breathing Exercises | 4-7-8 breathing, box breathing, diaphragmatic breathing |
| Progressive Muscle Relaxation | 10-15 minute full-body relaxation routine |
| 5-4-3-2-1 Grounding | Sensory-based technique to return to the present moment |
| Guided Meditation | Body scan, breathing meditation |
| Sleep Prep | Bedtime relaxation routine |
| Anxiety Relief | Combination approach |
## How to Trigger
- Keywords: "stressed", "anxious", "relax"
- Specific requests: "deep breath", "meditation", "can't sleep"
- Questions like "how can I relax?"
## Crisis Detection & Referral
### High-Risk Detection (Immediate Referral)
Keywords: suicide, self-harm, don't want to live, death, cutting, jumping, hopeless, depressed, despair
Response: Provide professional crisis hotline, guide user to seek professional help
### Moderate-Risk Detection (Extra Care)
Keywords: anxious, fearful, scared, worried, insomnia, breakdown, powerless, overwhelmed, can't breathe
Response: Express understanding and care, invite user to share or try a technique
## Safety Boundaries
⚠️ **Explicitly NOT provided**:
- Psychological diagnosis
- Psychotherapy
- Replacement for professional counseling
⚠️ **Disclaimer**:
- This tool provides self-help relaxation techniques only
- Cannot replace professional mental health treatment
- For serious emotional concerns, please seek professional help
## Directory Structure
```
stress-toolkit/
├── skill.json
├── SKILL.md
└── scripts/
├── handler.py # Core logic
└── crisis_detector.py # Crisis detection module
```
## Testing
### Basic Function Tests
```bash
# Test main entry
python3 ~/.openclaw/skills/stress-toolkit/scripts/handler.py "I'm stressed"
# Test breathing
python3 ~/.openclaw/skills/stress-toolkit/scripts/handler.py "I need to do deep breathing"
# Test grounding
python3 ~/.openclaw/skills/stress-toolkit/scripts/handler.py "anxious"
```
### Crisis Detection Tests
```bash
# Test high-risk detection (should trigger referral)
python3 ~/.openclaw/skills/stress-toolkit/scripts/handler.py "I don't want to live anymore"
# Test moderate-risk detection (should trigger care)
python3 ~/.openclaw/skills/stress-toolkit/scripts/handler.py "I'm so anxious and overwhelmed"
```
### Self-Test Examples
| Input | Expected Output |
|-------|----------------|
| "I'm stressed" | Display feature menu |
| "I need deep breathing" | Show 4-7-8 / box / diaphragmatic breathing |
| "Can't sleep" | Show bedtime relaxation routine |
| "Anxious" | Show anxiety relief combination |
| "I don't want to live" | Trigger crisis referral, show hotline |
| "So anxious and overwhelmed" | Trigger moderate-risk response, show care |
## Version Info
- version: 1.0.0
- author: Golden Bean
- status: MVP complete, ready for testing
FILE:scripts/crisis_detector.py
#!/usr/bin/env python3
"""Crisis Detector - 危机检测模块 for stress-toolkit"""
HIGH_RISK = ["自杀", "自残", "不想活了", "死", "活着没意思", "割腕", "跳楼", "活够了", "抑郁", "绝望"]
MEDIUM_RISK = ["焦虑", "恐惧", "害怕", "担心", "失眠", "崩溃", "无力", "很烦", "喘不过气", "压力大"]
def is_crisis(text):
"""
检测危机风险等级
返回: (是否危机, 风险等级)
"""
text = text.lower()
for k in HIGH_RISK:
if k in text:
return True, "high"
for k in MEDIUM_RISK:
if k in text:
return True, "medium"
return False, "safe"
FILE:scripts/handler.py
#!/usr/bin/env python3
"""Stress Toolkit Handler - 压力管理工具箱核心模块"""
import sys
import os
# 导入危机检测模块
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from crisis_detector import is_crisis
# ===== 免责声明 =====
DISCLAIMER = """
---
⚠️ **免责声明**:本工具仅提供自助放松技巧,不能替代专业心理治疗或医疗建议。如有严重情绪困扰,请寻求专业心理咨询师或医疗机构帮助。
"""
def append_disclaimer(text):
"""在文本末尾追加免责声明"""
return text.rstrip() + DISCLAIMER
# ===== 压力管理技术库 =====
BREATHING_TECHNIQUES = {
"4-7-8": {
"name": "4-7-8 呼吸法",
"description": "一种简单有效的放松呼吸技术,有助于缓解焦虑和帮助入睡",
"steps": [
"1. 吸气 4 秒(用鼻子)",
"2. 屏住呼吸 7 秒",
"3. 缓慢呼气 8 秒(用嘴巴)",
"4. 重复 3-4 次"
],
"tip": "呼气时发出'嘶嘶'声效果更好"
},
"box": {
"name": "盒式呼吸(Box Breathing)",
"description": "海军海豹突击队使用的平复紧张情绪的方法",
"steps": [
"1. 吸气 4 秒",
"2. 屏住呼吸 4 秒",
"4. 呼气 4 秒",
"4. 屏住呼吸 4 秒",
"5. 重复 4-6 次"
],
"tip": "想象在画一个正方形的边框"
},
"diaphragm": {
"name": "腹式呼吸",
"description": "深度放松的基础呼吸方式",
"steps": [
"1. 坐直或躺下,手放在肚脐上方",
"2. 慢慢吸气,让腹部鼓起(不是胸部)",
"3. 慢慢呼气,腹部收回",
"4. 保持呼吸缓慢、深长",
"5. 重复 5-10 次"
],
"tip": "每次呼气时想象释放紧张"
}
}
MUSCLE_RELAXATION = {
"name": "渐进式肌肉放松",
"description": "通过先紧张后放松各部位肌肉来达到深度放松",
"duration": "10-15分钟",
"steps": [
"【准备】找一个安静舒适的地方坐下或躺下",
"",
"【步骤1 - 脚】先用力绷紧脚尖 5 秒,然后放松",
"【步骤2 - 小腿】用力屈脚面 5 秒,然后放松",
"【步骤3 - 大腿】用力收紧大腿 5 秒,然后放松",
"【步骤4 - 腹部】用力收紧腹部 5 秒,然后放松",
"【步骤5 - 胸部】用力深吸气屏住 5 秒,然后放松",
"【步骤6 - 手臂】用力握拳弯曲手臂 5 秒,然后放松",
"【步骤7 - 肩膀】用力耸肩靠近耳朵 5 秒,然后放松",
"【步骤8 - 面部】收紧面部肌肉(眯眼、咬牙)5 秒,然后放松",
"",
"【收尾】深呼吸几次,感受全身放松的感觉"
],
"tip": "每天练习效果更好,可在睡前进行"
}
GROUNDING_521 = {
"name": "5-4-3-2-1 接地练习",
"description": "通过感官觉察帮助从焦虑中回到当下,适合惊恐发作或强烈焦虑",
"steps": [
"📍 **5样看到的东西**:说出你看到的 5 样物品",
"🔊 **4样触摸到的东西**:感受并说出 4 种触感",
"👂 **3样听到的声音**:找出 3 种声音",
"👃 **2样闻到的气味**:注意 2 种气味",
"👅 **1样尝到的味道**:品尝或回忆 1 种味道"
],
"tip": "不需要强制说全,慢慢来即可"
}
MEDITATION_GUIDES = {
"body_scan": {
"name": "身体扫描冥想",
"description": "逐个关注身体各部位,培养觉察能力",
"duration": "10-15分钟",
"steps": [
"1. 找一个安静的地方躺下,闭上眼睛",
"2. 先做几次深呼吸,让身体安静下来",
"3. 注意力从头顶开始,慢慢向下移动",
"4. 依次关注:头顶、额头、眼睛、鼻子、嘴巴、下巴",
"5. 继续向下:颈部、肩膀、手臂、手指",
"6. 胸部、腹部、背部、臀部",
"7. 大腿、膝盖、小腿、脚趾",
"8. 如果某个部位有感觉,温和地承认它",
"9. 最后全身扫描一次,慢慢睁开眼睛"
]
},
"breath_meditation": {
"name": "呼吸冥想",
"description": "专注于呼吸的简单冥想",
"duration": "5-10分钟",
"steps": [
"1. 舒适地坐着或躺下,闭上眼睛",
"2. 自然呼吸,不需要控制",
"3. 把注意力放在呼吸的感觉上",
"4. 吸气时注意腹部的起伏",
"5. 如果走神了,温和地把注意力带回呼吸",
"6. 持续 5-10 分钟",
"7. 慢慢睁开眼睛,活动身体"
],
"tip": "不需要追求空无一切,杂念出现是正常的"
}
}
SLEEP_ROUTINE = {
"name": "睡前放松流程",
"description": "帮助入睡的完整放松流程",
"duration": "20-30分钟",
"steps": [
"【第一阶段:身体准备】(睡前1小时)",
"1. 调暗灯光,营造睡眠环境",
"2. 关闭电子设备(或开启护眼模式)",
"3. 简单拉伸或散步",
"",
"【第二阶段:放松】(睡前30分钟)",
"4. 洗个热水澡或泡脚",
"5. 喝一杯温热的牛奶或花茶",
"6. 听轻柔的音乐或白噪音",
"",
"【第三阶段:呼吸和放松】",
"7. 躺在床上,做 4-7-8 呼吸法 3-5 次",
"8. 从头到脚依次放松各部位肌肉",
"9. 如果脑中 有想法,写下来('明天再想')",
"",
"【第四阶段:入睡】",
"10. 专注于呼吸节奏",
"11. 允许自己慢慢入睡,不要看时间"
]
}
ANXIETY_RELIEF = {
"name": "焦虑缓解组合拳",
"description": "当感到焦虑时的综合应对方案",
"steps": [
"🔹 **第一步:暂停**",
" - 停止手中的事情",
" - 告诉 自己:'我现在有点焦虑,但这会过去的'",
"",
"🔹 **第二步:身体着陆**",
" - 做几次深呼吸(4-7-8或腹式呼吸)",
" - 或者做 5-4-3-2-1 接地练习",
" - 感受双脚踩在地上的感觉",
"",
"🔹 **第三步:认知重构**",
" - 问自己:'最坏的结果是什么?有多大概率?'",
" - 问自己:'即使发生了,我能应对吗?'",
" - 提醒自己:'焦虑不等于危险'",
"",
"🔹 **第四步:行动选择**",
" - 如果可以行动,做一件小事",
" - 如果无法行动,允许自己暂停",
" - 给自己一点时间"
],
"tip": "不需要按顺序全部做完,选择当下有用的部分即可"
}
def detect_intent(text):
"""检测用户意图和需要的帮助类型"""
text = text.lower()
# 呼吸相关
if any(k in text for k in ["呼吸", "深呼吸", "breath"]):
return "breathing"
# 肌肉放松
if any(k in text for k in ["放松", "肌肉", "放松训练", "放松法"]):
return "muscle"
# 接地/焦虑
if any(k in text for k in ["焦虑", "焦虑发作", "惊恐", "害怕", "担心", "grounding", "5-4-3"]):
return "grounding"
# 冥想
if any(k in text for k in ["冥想", " meditation", "身体扫描", "正念"]):
return "meditation"
# 睡眠
if any(k in text for k in ["睡觉", "睡不着", "失眠", "入睡", "sleep", "困"]):
return "sleep"
# 压力/焦虑综合
if any(k in text for k in ["压力", "解压", "放松", "舒压", "stress"]):
return "anxiety_relief"
# 求助
if any(k in text for k in ["怎么办", "帮我", "有什么方法", "教我"]):
return "menu"
return "menu"
def get_menu():
"""返回功能菜单"""
return append_disclaimer("""
🧘 **压力管理工具箱**
我能帮你:
1. 🫁 **呼吸练习** - 4-7-8呼吸、盒式呼吸、腹式呼吸
2. 💪 **肌肉放松** - 渐进式全身放松
3. 🌍 **接地练习** - 5-4-3-2-1 缓解焦虑
4. 🧠 **冥想引导** - 身体扫描、呼吸冥想
5. 😴 **睡前放松** - 帮助入睡的完整流程
6. 🆘 **焦虑缓解** - 综合应对方案
请告诉我你现在需要什么?
""")
def format_technique(title, technique):
"""格式化输出技术内容"""
content = f"## {technique.get('name', title)}\n\n"
if 'description' in technique:
content += f"{technique['description']}\n\n"
if 'duration' in technique:
content += f"⏱ 预计时长:{technique['duration']}\n\n"
if 'steps' in technique:
content += "### 步骤:\n"
for step in technique['steps']:
content += f"{step}\n"
content += "\n"
if 'tip' in technique:
content += f"💡 小贴士:{technique['tip']}\n"
return content
def handle_stress_request(user_input):
"""处理压力管理请求"""
# 先检查危机信号
crisis, risk_level = is_crisis(user_input)
if crisis and risk_level == "high":
return get_crisis_response()
# 检测用户意图
intent = detect_intent(user_input)
if intent == "breathing":
content = "## 🫁 呼吸练习\n\n"
for key, tech in BREATHING_TECHNIQUES.items():
content += format_technique(key, tech)
content += "---\n"
return append_disclaimer(content)
elif intent == "muscle":
return append_disclaimer(format_technique("肌肉放松", MUSCLE_RELAXATION))
elif intent == "grounding":
return append_disclaimer(format_technique("接地练习", GROUNDING_521))
elif intent == "meditation":
content = "## 🧠 冥想引导\n\n"
for key, tech in MEDITATION_GUIDES.items():
content += format_technique(key, tech)
content += "---\n"
return append_disclaimer(content)
elif intent == "sleep":
return append_disclaimer(format_technique("睡前放松", SLEEP_ROUTINE))
elif intent == "anxiety_relief":
return append_disclaimer(format_technique("焦虑缓解", ANXIETY_RELIEF))
else:
return get_menu()
def get_crisis_response():
"""危机响应(不含普通免责声明,危机路径独立处理)"""
return """
⚠️ **我想认真听你说**
听到你现在的状态,我很在意。
**请现在做以下事情之一:**
📞 **拨打心理危机热线**:
- 全国心理援助热线:400-161-9995
- 北京心理危机研究与干预中心:010-82951332
👤 **告诉身边信任的人**:
- 家人、朋友、同事
- 告诉他们你现在需要支持
🏥 **如果需要即时帮助**:
- 精神专科医院急诊
- 拨打120/110
---
**你不需要独自面对**。这不代表软弱,寻求帮助是勇敢的行为。
如果你愿意,我们可以先聊聊。但请先确保自己的安全,好吗?
"""
def main():
"""主入口"""
if len(sys.argv) < 2:
print(get_menu())
return
user_input = sys.argv[1]
response = handle_stress_request(user_input)
print(response)
if __name__ == "__main__":
main()
FILE:skill.json
{
"name": "stress-toolkit",
"description": "压力管理工具箱 - 提供多种快速减压技术和练习。适用于感到压力大、焦虑、需要放松的场景。包含4-7-8呼吸、盒式呼吸、5-4-3-2-1接地、渐进式肌肉放松等技巧。",
"version": "1.0.0",
"author": "Golden Bean",
"tags": ["psychology", "stress", "relaxation", "mental-health", "breathing", "meditation"],
"safety": {
"no_diagnosis": true,
"no_treatment": true,
"crisis_escalation": true,
"disclaimer_required": true
},
"modules": {
"handler": "scripts/handler.py",
"crisis": "scripts/crisis_detector.py"
},
"entry": "scripts/handler.py"
}
FILE:tests/__init__.py
# tests package
FILE:tests/test_handler.py
#!/usr/bin/env python3
"""stress-toolkit 自测脚本"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
from handler import handle_stress_request, detect_intent, get_menu
from crisis_detector import is_crisis
def test(name, input_text, check_fn=None):
"""运行单个测试"""
print(f"\n{'='*50}")
print(f"测试: {name}")
print(f"输入: {input_text}")
print('-'*50)
try:
result = handle_stress_request(input_text)
if check_fn:
check_fn(result)
print(f"✓ 通过")
print(f"输出:\n{result[:200]}..." if len(result) > 200 else f"输出:\n{result}")
return True
except Exception as e:
print(f"✗ 失败: {e}")
return False
def test_crisis_detector():
"""测试危机检测"""
print("\n" + "="*50)
print("测试: 危机检测模块")
print("="*50)
cases = [
("不想活了", True, "high"),
("自杀", True, "high"),
("很焦虑", True, "medium"),
("压力大", True, "medium"),
("今天天气不错", False, "safe"),
]
all_pass = True
for text, expected_crisis, expected_level in cases:
crisis, level = is_crisis(text)
status = "✓" if crisis == expected_crisis and level == expected_level else "✗"
if status == "✗":
all_pass = False
print(f"{status} '{text}' -> crisis={crisis}, level={level} (期望 crisis={expected_crisis}, level={expected_level})")
return all_pass
def test_intent_detection():
"""测试意图识别"""
print("\n" + "="*50)
print("测试: 意图识别")
print("="*50)
cases = [
("深呼吸", "breathing"),
("呼吸练习", "breathing"),
("睡不着", "sleep"),
("焦虑", "grounding"),
("冥想", "meditation"),
("压力", "anxiety_relief"),
("你好", "menu"),
]
all_pass = True
for text, expected in cases:
intent = detect_intent(text)
status = "✓" if intent == expected else "✗"
if status == "✗":
all_pass = False
print(f"{status} '{text}' -> {intent} (期望 {expected})")
return all_pass
def main():
print("🧪 stress-toolkit 自测开始")
print(f"Python: {sys.version}")
print(f"工作目录: {os.getcwd()}")
results = []
# 1. 危机检测
results.append(("危机检测", test_crisis_detector()))
# 2. 意图识别
results.append(("意图识别", test_intent_detection()))
# 3. 功能测试
results.append(("功能-压力大", test("压力大", "压力大")))
results.append(("功能-想深呼吸", test("想深呼吸", "想深呼吸")))
results.append(("功能-睡不着", test("睡不着", "睡不着")))
results.append(("功能-焦虑", test("焦虑", "焦虑")))
results.append(("功能-危机", test("不想活了", "不想活了")))
# 总结
print("\n" + "="*50)
print("🧪 自测总结")
print("="*50)
passed = sum(1 for _, r in results if r)
total = len(results)
for name, result in results:
print(f"{'✓' if result else '✗'} {name}")
print(f"\n通过: {passed}/{total}")
if passed == total:
print("\n🎉 所有测试通过!")
return 0
else:
print(f"\n⚠️ {total - passed} 个测试失败")
return 1
if __name__ == "__main__":
sys.exit(main())
Helps users identify negative thinking patterns and try seeing situations from a more balanced perspective. A CBT-based thought exercise tool for daily self-...
---
name: cognitive-reframe
description: Helps users identify negative thinking patterns and try seeing situations from a more balanced perspective. A CBT-based thought exercise tool for daily self-regulation. Designed for moments of anxiety, frustration, or excessive worry.
---
# Cognitive Reframe
A tool that helps users recognize common negative thinking patterns and shift toward a more balanced, rational perspective. Based on principles of Cognitive Behavioral Therapy (CBT) for daily self-regulation.
> **Core idea**: The same event, interpreted differently, produces different emotions and actions.
## Core Features
This skill provides:
- **Thought Recognition**: Help users notice how they are thinking
- **Pattern Analysis**: Point out common cognitive distortion types
- **Reframe Guidance**: Guide users toward a more balanced alternative perspective
- **Safety Boundaries**: Detect high-risk content, guide toward professional help
## When to Use
Typical triggers:
- "I feel terrible"
- "Things will never get better"
- "I can't do anything right"
- "Everyone must think I'm terrible"
- "What if I mess up?"
- "I'm such a failure"
- "I keep thinking the worst"
- "Help me see it differently"
- "cognitive-reframe"
## When NOT to Use
This skill is **NOT suitable for**:
- Psychiatric diagnosis
- Trauma treatment
- Replacement for psychotherapy or psychiatric treatment
- Crisis intervention (if self-harm/suicidal thoughts are present)
### 🚨 High-Risk Signals
If any of the following are present, **skip this skill** and immediately guide the user to professional help:
- Self-harm or suicidal thoughts mentioned
- Severe depression lasting more than two weeks
- PTSD reactions
- Severe anxiety affecting daily functioning
- Eating disorder related concerns
**High-risk guidance**: Warmly but clearly suggest the user contact a professional organization; provide a mental health crisis hotline.
## Common Thinking Patterns (Cognitive Distortions)
This tool helps users recognize these common patterns:
| Pattern | Characteristic | Example |
|---------|---------------|---------|
| All-or-Nothing | Extreme judgments, no middle ground | "I completely failed" |
| Mental Filter | Focusing only on negatives, ignoring positives | "This meeting went badly, I'm done" |
| Catastrophizing | Exaggerating negative consequences | "If I fail, it's all over" |
| Mind Reading | Assuming you know what others think | "They must think I'm stupid" |
| Emotional Reasoning | Treating feelings as facts | "I feel worthless, so I must be" |
| Should Statements | Setting rigid "should" rules for self or others | "I should always be productive" |
| Labeling | Applying negative labels to yourself | "I'm a total loser" |
| Disqualifying the Positive | Dismissing positive experiences | "That was just luck" |
## Output Structure
### Basic Flow
1. **Listen & Acknowledge**: Understand the user's specific situation
2. **Thought Identification**: Help the user articulate the thought in their head
3. **Pattern Recognition**: Identify which thinking pattern it represents
4. **Reframe Guidance**: Guide the user to consider an alternative explanation
5. **Action Tip**: Provide one small actionable step to try
### Example Output
**Scenario**: User says "There's no way I'll pass this interview, I'm terrible"
> **It sounds like you're really worried about this interview. Can you tell me more about what's making you feel that way?**
>
> (User explains)
>
> **I hear a voice in your head saying "I'm terrible." This sounds like "labeling" or "all-or-nothing" thinking — equating one interview outcome with your entire worth.**
>
> **Let's look at it from another angle:**
> - **Facts**: You got the interview, which means your resume and abilities were recognized
> - **Alternative thought**: Even if this one doesn't work out, the interview itself is valuable experience
> - **Ask yourself**: Would you judge a friend this harshly if they said the same thing?
>
> **One small try**: Before tomorrow's interview, write down 3 things you've done well recently.
>
> ---
>
> 💡 **Reminder**: This is a self-help exercise. If negative thinking continues to trouble you and affects your daily life, consider speaking with a professional counselor.
### Crisis Response Example
**Scenario**: User says "Life is exhausting, I might as well be dead"
> **Thank you for sharing this with me. When I hear you say that, I genuinely care.**
>
> **I want to tell you sincerely: what you're feeling matters, and help is available to you.**
>
> **I recommend you do one of these right now:**
> - Contact a mental health crisis hotline (Beijing 24hr: 010-8295 1332)
> - Or talk to someone you trust about how you're feeling
> - If there is immediate danger, please call emergency services directly
>
> **You don't have to face this alone.** This is not your fault, and it is not weakness. Reaching out for help is a brave thing to do.
## Reframing Techniques
### 1. Evidence Method
- What evidence supports this thought?
- What evidence contradicts it?
### 2. Alternative Explanations
- Are there other possible explanations?
- Will the worst case actually happen?
### 3. Decatastrophizing
- What is the worst outcome? What happens if it occurs?
- Even if it happens, is it really that terrible?
### 4. Self-Talk
- If a friend said this, how would I respond?
- Would I say the same thing to a friend?
### 5. Action Test
- Instead of spiraling, could I just try one small thing?
## Safety Boundary Statement
> ⚠️ **Important Notice**:
>
> This skill is a **daily self-regulation tool**, based on CBT principles, **and does not constitute professional mental health treatment or medical advice**.
>
> This tool:
> - ❌ Does not diagnose mental health conditions
> - ❌ Does not treat mental illness
> - ❌ Does not replace professional counseling or psychiatric treatment
> - ✅ Used for daily self-awareness of thinking patterns
> - ✅ Helps build more balanced thinking habits
> - ✅ Identifies when professional help is needed and provides guidance
>
> **If you or someone you know is experiencing**:
> - Persistent depression, anxiety, or other psychological distress
> - Self-harm or suicidal thoughts
> - PTSD reactions
> - Other conditions seriously affecting daily functioning
>
> **Please seek professional help**:
> - Crisis hotline: National Mental Health Hotline 400-161-9995
> - Psychiatric consultation
> - Professional counselor
>
> **In case of immediate danger, please call 120/110.**
## Usage Limitations
- This tool does not store personal user content
- Does not provide ongoing emotional support
- Does not track user emotional changes
- Each session is independent
## Minimum Viable Flow
1. Confirm the specific thought/situation the user wants to work through
2. Help the user articulate the thought in their mind
3. Identify the likely thinking pattern
4. Provide 1-2 reframe perspectives
5. Include safety boundary notice
FILE:scripts/crisis_detector.py
#!/usr/bin/env python3
"""Crisis Detector - 危机检测模块"""
HIGH_RISK = [
"自杀", "自残", "不想活了", "死了", "活着没意思", "活着好累",
"割腕", "跳楼", "活够了", "抑郁", "绝望", "轻生", "自尽",
"了结", "活不下去", "没意思", "不想活", "不如死了", "死了算了",
"一了百了", "hurt myself", "kill myself"
]
MEDIUM_RISK = [
"焦虑", "恐惧", "害怕", "担心", "失眠", "崩溃", "无力", "无助",
"很绝望", "好绝望", "难受", "崩溃", "很压抑", "压抑", "喘不过气",
"好累", "太累了", "撑不住了", "熬不住了", "走不出来", "好无助"
]
def is_crisis(text):
"""检测文本是否包含危机信号"""
text_lower = text.lower()
for k in HIGH_RISK:
if k in text_lower:
return True, "high"
for k in MEDIUM_RISK:
if k in text_lower:
return True, "medium"
return False, "safe"
FILE:scripts/reframe.py
#!/usr/bin/env python3
"""Cognitive Reframe - 认知重构工具"""
import json
import sys
import os
DISCLAIMER = (
"💡 这是一种日常自我调节练习,不构成专业心理治疗或医疗建议。"
"如果负面思维持续困扰你,建议咨询专业心理咨询师。"
"如遇紧急危险,请直接拨打120/110。"
)
COGNITIVE_DISTORTIONS = {
"全或无思维": {"特征": "非黑即白的极端判断", "例子": "我完全失败了", "重构": "尝试看到中间的灰色地带"},
"心理过滤": {"特征": "只关注负面,忽略正面", "例子": "这次说得不好不算什么", "重构": "有意识地关注正面事件"},
"灾难化": {"特征": "夸大负面后果", "例子": "如果失败就彻底完了", "重构": "评估实际可能性和应对能力"},
"读心术": {"特征": "假设知道别人想法", "例子": "他们一定觉得我很笨", "重构": "没有确凿证据时,不要假设"},
"情绪推理": {"特征": "把感觉当成事实", "例子": "我觉得自己没用所以一定没用", "重构": "感觉不等于事实"},
"应当思维": {"特征": "给自己设不切实际的应当标准", "例子": "我应当总是保持高效", "重构": "接受人都有状态起伏"},
"贴标签": {"特征": "给自己贴负面标签", "例子": "我就是一个失败者", "重构": "行为不等于身份"},
"否定正面": {"特征": "把正面经历不当回事", "例子": "那只是运气好", "重构": "承认自己的努力和成就"}
}
REFRAME_PROMPTS = {
"证据法": "支持这个想法的证据是什么?反对的证据呢?",
"替代解释": "还有没有其他可能的解释?",
"去灾难化": "最坏的情况是什么?我能承受吗?最好的情况是什么?",
"朋友视角": "如果我的朋友有这个想法,我会怎么劝TA?",
"比例思考": "这件事在一年后的我看来会有多重要?"
}
# 显式模式 → 扭曲类型(支持多标签)
PATTERNS = [
# 灾难化:灾难性后果表述
("彻底完了", "灾难化"),
("彻底毁灭", "灾难化"),
("彻底完蛋", "灾难化"),
("彻底垮了", "灾难化"),
("彻底崩了", "灾难化"),
("彻底失败", "灾难化"),
("彻底无用", "灾难化"),
("彻底无能", "灾难化"),
# 全或无思维(彻底+失败/无用/无能)
("彻底失败", "全或无思维"),
("彻底无用", "全或无思维"),
("彻底无能", "全或无思维"),
("完全失败", "全或无思维"),
# 应当思维(完整表述优先)
("应当总是", "应当思维"),
("应该总是", "应当思维"),
("必须总是", "应当思维"),
("我应当", "应当思维"),
("我应该", "应当思维"),
# 贴标签
("我就是一个", "贴标签"),
("我这人", "贴标签"),
# 读心术(需要明确的"他人想法"前缀)
("他们肯定", "读心术"),
("他们一定", "读心术"),
("他们觉得", "读心术"),
("肯定觉得", "读心术"),
("一定认为", "读心术"),
# 情绪推理(高确信判断)
("所以", "情绪推理"),
("肯定", "情绪推理"), # "我肯定…"类高确信判断
("必然是", "情绪推理"),
# 心理过滤
("但是", "心理过滤"),
("不过", "心理过滤"),
("虽然", "心理过滤"),
# 否定正面
("只是", "否定正面"),
("不过是", "否定正面"),
("运气好", "否定正面"),
]
# 全or无兜底关键词(仅在未触发任何模式时使用)
ALL_OR_NONE_FALLBACK = ["永远", "总是", "从不"]
def detect_distortion(thought):
"""检测认知扭曲类型"""
detected = set()
for pattern, distortion in PATTERNS:
if pattern in thought:
detected.add(distortion)
if not detected:
for kw in ALL_OR_NONE_FALLBACK:
if kw in thought:
detected.add("全或无思维")
break
return list(detected)
def generate_reframe(distortions):
"""生成重构建议"""
suggestions = []
for d in distortions:
if d in COGNITIVE_DISTORTIONS:
suggestions.append({
"类型": d,
"特征": COGNITIVE_DISTORTIONS[d]["特征"],
"重构方向": COGNITIVE_DISTORTIONS[d]["重构"]
})
return suggestions
def generate_response(user_input):
"""生成响应"""
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from crisis_detector import is_crisis
is_crisis_result, risk_level = is_crisis(user_input)
if is_crisis_result and risk_level == "high":
return {
"type": "crisis",
"risk_level": "high",
"message": "谢谢你愿意说出来。我很在意你的状态。建议现在联系:全国心理援助热线400-161-99-95,或告诉身边信任的人。如有危险请直接拨打120/110。"
}
elif is_crisis_result and risk_level == "medium":
return {
"type": "crisis",
"risk_level": "medium",
"message": "我注意到你似乎正在经历一些困难。你愿意多说说吗?如果感觉难以承受,建议联系:全国心理援助热线400-161-99-95。"
}
distortions = detect_distortion(user_input)
if distortions:
reframes = generate_reframe(distortions)
return {
"type": "normal",
"distortions": distortions,
"reframes": reframes,
"prompts": list(REFRAME_PROMPTS.values())[:3],
"disclaimer": DISCLAIMER
}
return {
"type": "normal",
"message": "我听到你有一些想法。能详细说说是什么想法在脑海中浮现吗?",
"disclaimer": DISCLAIMER
}
def main():
user_input = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else ""
if not user_input:
print("请输入你想要处理的想法...")
return
resp = generate_response(user_input)
print("\n=== Cognitive Reframe ===\n")
print(json.dumps(resp, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
FILE:skill.json
{
"name": "cognitive-reframe",
"description": "帮助用户识别消极思维模式并尝试用更平衡的视角看待问题。这是一种基于认知行为疗法(CBT)的思维练习工具,用于日常自我调节。使用场景:用户感到焦虑、沮丧、过度担忧,或想要换个角度思考问题。注意:此工具不进行诊断,不替代专业心理治疗,遇到高风险情况会引导用户寻求专业帮助。",
"version": "1.0.0",
"author": "Golden Bean",
"tags": ["psychology", "cognitive-reframe", "cbt", "self-reflection", "mental-health"],
"safety": {
"no_diagnosis": true,
"no_treatment": true,
"crisis_escalation": true,
"disclaimer_required": true,
"high_risk_keyword_detection": true
},
"modules": {
"reframe": "scripts/reframe.py",
"crisis": "scripts/crisis_detector.py"
},
"entry": "scripts/reframe.py"
}
FILE:tests/__init__.py
FILE:tests/test_reframe.py
#!/usr/bin/env python3
"""Minimal self-test for cognitive-reframe skill"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
from crisis_detector import is_crisis
from reframe import detect_distortion, generate_reframe, generate_response
def test_crisis_detector():
print("=== Crisis Detector Tests ===")
r, lvl = is_crisis("我想死,不想活了")
assert r == True and lvl == "high", f"FAIL high risk: {r}, {lvl}"
print(" [PASS] high risk detection")
r, lvl = is_crisis("活着好累")
assert r == True and lvl == "high"
print(" [PASS] high risk (活着好累)")
r, lvl = is_crisis("自杀")
assert r == True and lvl == "high"
print(" [PASS] high risk (自杀)")
r, lvl = is_crisis("我很焦虑")
assert r == True and lvl == "medium"
print(" [PASS] medium risk detection")
r, lvl = is_crisis("今天天气不错")
assert r == False and lvl == "safe"
print(" [PASS] safe text")
print(" Crisis Detector: ALL PASS\n")
def test_distortion_detection():
print("=== Distortion Detection Tests ===")
# regression: "肯定" should trigger 情绪推理
d = detect_distortion("我肯定过不了")
assert "情绪推理" in d, f"FAIL: 情绪推理 not detected in {d}"
print(" [PASS] 肯定 triggers 情绪推理")
# regression: "彻底垮了" should trigger 灾难化
d = detect_distortion("彻底垮了")
assert "灾难化" in d, f"FAIL: 灾难化 not detected in {d}"
print(" [PASS] 彻底垮了 triggers 灾难化")
# regression: "应当总是" should only trigger 应当思维
d = detect_distortion("我应当总是保持高效")
assert "应当思维" in d and "全或无思维" not in d, f"FAIL: {d}"
print(" [PASS] 应当总是 only triggers 应当思维")
# regression: "项目完了" should NOT trigger
d = detect_distortion("项目完了")
assert len(d) == 0, f"FAIL: 项目完了 should not trigger, got {d}"
print(" [PASS] 项目完了 no false positive")
print(" Distortion Detection: ALL PASS\n")
def test_disclaimer():
print("=== Disclaimer Tests ===")
# crisis response should NOT include disclaimer (it's a safety message)
resp = generate_response("我不想活了")
assert resp["type"] == "crisis"
print(" [PASS] crisis response is crisis type")
# normal response should include disclaimer
resp = generate_response("我感觉很糟糕")
assert "disclaimer" in resp and "心理治疗" in resp["disclaimer"], f"FAIL: missing disclaimer"
print(" [PASS] normal response has disclaimer")
resp = generate_response("我完全失败了")
assert "disclaimer" in resp
print(" [PASS] distortion response has disclaimer")
print(" Disclaimer: ALL PASS\n")
def test_generate_response():
print("=== Generate Response Tests ===")
resp = generate_response("我不想活了")
assert resp["type"] == "crisis"
assert resp["risk_level"] == "high"
print(" [PASS] crisis response")
resp = generate_response("我完全失败了")
assert resp["type"] == "normal"
assert "全或无思维" in resp["distortions"]
print(" [PASS] normal with distortion")
resp = generate_response("今天天气不错")
assert resp["type"] == "normal"
print(" [PASS] normal generic")
print(" Generate Response: ALL PASS\n")
def test_reframe_integration():
print("=== Integration: Full Flow ===")
user_input = "我完全失败了,什么都做不好"
resp = generate_response(user_input)
assert resp["type"] == "normal"
assert len(resp["distortions"]) > 0, f"FAIL: no distortions for '{user_input}'"
assert len(resp["reframes"]) > 0
print(f" Input: {user_input}")
print(f" Detected: {resp['distortions']}")
print(f" Reframes: {[r['重构方向'] for r in resp['reframes']]}")
print(" Integration: PASS\n")
if __name__ == "__main__":
print("\n========== Cognitive Reframe Self-Test ==========\n")
test_crisis_detector()
test_distortion_detection()
test_disclaimer()
test_generate_response()
test_reframe_integration()
print("========== ALL TESTS PASSED ==========\n")
Help users prepare for therapy or counseling sessions by organizing thoughts, clarifying goals, identifying patterns, and structuring what to bring up. Use w...
---
name: therapy-prep
description: Help users prepare for therapy or counseling sessions by organizing thoughts, clarifying goals, identifying patterns, and structuring what to bring up. Use when the user has an upcoming therapy appointment and wants to make the most of it, or wants to reflect on what to discuss.
---
# Therapy Prep(心理咨询准备)
帮助用户为心理咨询/治疗做好准备,提高咨询效率,支持专业治疗关系。
## 核心定位
- **帮助整理**:把散乱的思绪整理成有条理的咨询内容
- **识别模式**:回顾近期情绪和行为模式,找出值得讨论的主题
- **明确目标**:弄清楚这次咨询最想聊什么、最想得到什么
- **做笔记**:把需要带到咨询中的要点写下来
**不做**:不替代心理咨询、不做诊断、不进行治疗
## 适用场景
- "我下周有心理咨询,想准备一下"
- "帮我整理一下最近的状态"
- "我想把要说的事情理一理"
- "帮我准备咨询问题"
- "我要去看心理医生了,该怎么准备"
## 不适用场景
- 正在经历急性危机(先转介)
- 需要立即获得专业治疗
- 要求获得诊断结论
## 安全边界
⚠️ 如果在准备过程中发现用户有自杀/自伤/严重崩溃信号:
1. 暂停准备流程
2. 提供转介信息(400-161-9995)
3. 建议用户与咨询师优先讨论危机相关议题
## 准备流程
### 第一步:确认基本信息
- 下次咨询是什么时候?
- 是第一次去,还是持续在做的咨询?
- 对这次咨询有什么担心或期待?
### 第二步:梳理近期状态
回顾最近1-2周:
- 最困扰的是什么?
- 情绪状态如何(整体高低、波动情况)?
- 有没有特别的事件或变化?
- 睡眠、饮食、日常功能有没有受影响?
### 第三步:明确咨询议题
从近期状态中提炼出1-3个核心议题:
- 议题是什么?
- 这个问题/感受从什么时候开始的?
- 在什么情况下会更严重/更轻?
- 以前有没有尝试过什么办法?
### 第四步:整理要点清单
生成一份"带去咨询的要点清单":
- 议题一:...
- 议题二:...
- 想问咨询师的问题:...
- 个人状态备忘:...
### 第五步:咨询后的跟进
- 记录咨询后的感受和收获
- 记得按时服药(如有)
- 下次咨询前继续记录状态变化
## 免责声明
> ⚠️ 本工具仅提供一般性咨询准备支持,不替代心理咨询、诊断或治疗。如你正在经历严重心理困扰,请联系心理健康专业人士或拨打心理援助热线 400-161-9995。
FILE:references/crisis_keywords.json
{
"crisis_signals": {
"suicide": [
"不想活了",
"想死",
"活着没意思",
"活着太累",
"不想继续",
"活够了",
"死了就好了",
"不如死了算了",
"死了就解脱了"
],
"self_harm": [
"想伤害自己",
"想割腕",
"想划自己",
"想打自己",
"想撞墙",
"想用刀"
],
"severe_breakdown": [
"坚持不住了",
"彻底崩溃",
"完全失控",
"撑不住了",
"要疯了",
"没有任何希望了"
]
},
"professional_resources": {
"national_hotline": "400-161-9995",
"description": "全国24小时心理援助热线"
},
"response_templates": {
"immediate": "⚠️ 我听到你现在的状态很不容易。我想先确认:你的安全是最重要的。",
"hotline": "如果方便的话,可以拨打心理援助热线:**400-161-9995**(全国24小时)。",
"local": "也可以联系当地精神卫生中心或社区卫生服务中心的心理科。",
"emergency": "如果你现在有紧急危险,请直接拨打120或110。"
}
}
FILE:references/prep_phases.json
{
"phases": [
{
"phase": 1,
"name": "基本信息",
"description": "确认咨询时间和背景",
"questions": [
"你的下次咨询是什么时候?",
"这是你第一次去这家机构/这个咨询师,还是持续在做的咨询?",
"你对这次咨询有什么期待或担心吗?"
]
},
{
"phase": 2,
"name": "近期状态梳理",
"description": "回顾最近1-2周的整体状态",
"questions": [
"最近1-2周,你最困扰的是什么?",
"你的情绪状态整体怎么样?(比如:起伏大、持续低落、比较平稳…)",
"有没有什么特别的事件或变化?",
"睡眠、饮食、日常生活有没有受影响?"
]
},
{
"phase": 3,
"name": "核心议题提炼",
"description": "从近期状态中识别1-3个值得讨论的核心议题",
"questions": [
"最近有什么问题或感受是你最想和咨询师聊的?",
"这个问题/感受从什么时候开始的?",
"在什么情况下会变得更严重?",
"在什么情况下会好一些?",
"你之前有没有试过什么办法?效果怎么样?"
]
},
{
"phase": 4,
"name": "要点清单",
"description": "生成一份带去咨询的要点清单(自动汇总)"
},
{
"phase": 5,
"name": "咨询后跟进",
"description": "咨询后记录感受和后续行动",
"questions": [
"这次咨询你印象最深的是什么?",
"有什么收获或新的想法吗?",
"咨询师给了什么建议或安排?",
"下次咨询前你打算做什么?"
]
}
],
"topic_prompts": [
"家庭/亲密关系问题",
"工作/职业发展困扰",
"人际交往障碍",
"焦虑/担忧",
"情绪低落/抑郁感",
"创伤/过去经历的困扰",
"自我认识/身份认同",
"睡眠/身体健康问题",
"人生意义/存在困惑",
"其他(请描述)"
]
}
FILE:scripts/crisis_detector.py
#!/usr/bin/env python3
"""
危机检测器 - therapy-prep skill 模块
"""
import json
from pathlib import Path
class CrisisDetector:
def __init__(self):
self.crisis_data = self._load()
def _load(self) -> dict:
refs = Path(__file__).parent.parent / "references" / "crisis_keywords.json"
with open(refs, 'r', encoding='utf-8') as f:
return json.load(f)
def detect(self, text: str) -> dict:
text_lower = text.lower()
signals = self.crisis_data.get("crisis_signals", {})
results = {"has_crisis": False, "level": None, "type": None,
"matched_keywords": [], "response": None}
for kw_list, ctype in [
(signals.get("suicide", []), "suicide"),
(signals.get("self_harm", []), "self_harm"),
(signals.get("severe_breakdown", []), "breakdown"),
]:
matched = [kw for kw in kw_list if kw in text_lower]
if matched:
results.update({
"has_crisis": True,
"level": "high" if ctype in ("suicide", "self_harm") else "medium",
"type": ctype,
"matched_keywords": matched,
})
break
if results["has_crisis"]:
results["response"] = self._generate_response(results["type"])
return results
def _generate_response(self, crisis_type: str) -> str:
t = self.crisis_data.get("response_templates", {})
hotline = self.crisis_data.get("professional_resources", {}).get("national_hotline", "400-161-9995")
parts = [
t.get("immediate", ""),
t.get("hotline", "").replace("400-161-9995", f"**{hotline}**"),
t.get("local", ""),
t.get("emergency", ""),
]
return "\n\n".join([p for p in parts if p])
if __name__ == "__main__":
d = CrisisDetector()
for t in ["我下周咨询", "活着没意思", "想伤害自己", "坚持不住了"]:
r = d.detect(t)
print(f"{t!r}: has_crisis={r['has_crisis']}, type={r['type']}")
FILE:scripts/prep.py
#!/usr/bin/env python3
"""
心理咨询准备核心逻辑 - therapy-prep skill 核心模块
帮助用户整理思绪、明确目标、准备咨询议题
"""
import json
from pathlib import Path
from typing import Optional, List, Dict
class TherapyPrep:
"""
心理咨询准备引导器
流程:基本信息 -> 近期状态 -> 核心议题 -> 要点清单 -> 咨询后跟进
任何时候发现危机信号立即转介
"""
def __init__(self):
self.crisis = CrisisDetector()
self.phases_data = self._load_phases()
self._reset()
def _load_phases(self) -> dict:
p = Path(__file__).parent.parent / "references" / "prep_phases.json"
with open(p, 'r', encoding='utf-8') as f:
return json.load(f)
def _reset(self):
self.phase = 1
self.sub_step = 0
self.data = {
"basic_info": {},
"recent_state": {},
"topics": [],
"post_session": {},
}
self.notes = [] # 收集的所有笔记
# ---- 公共接口 ----
def check_crisis(self, text: str) -> Optional[dict]:
return self.crisis.detect(text)
def process(self, text: str) -> dict:
"""处理用户输入,返回引导结果"""
# 危机检测
crisis_result = self.crisis.detect(text)
if crisis_result["has_crisis"]:
return {
"phase": "crisis",
"content": crisis_result["response"],
"crisis_result": crisis_result,
"data": self.data,
}
# 正常流程
return self._process_normal(text)
def get_welcome(self) -> str:
return (
"🪪 **心理咨询准备**\n\n"
"我们来帮你整理思绪,让咨询更有效。不需要一次说很多,一点一点来就好。\n\n"
f"**第1步 - 基本信息**\n{self._get_current_question()}\n\n"
f"⚠️ **免责声明**:本工具仅提供一般性咨询准备支持,不替代心理咨询或治疗。"
)
# ---- 内部流程控制 ----
def _process_normal(self, text: str) -> dict:
"""正常流程处理"""
self.notes.append({"phase": self.phase, "step": self.sub_step, "input": text})
# 收集数据
self._collect_data(text)
# 推进步骤
self._advance()
if self.phase > 5:
return self._wrap_up()
return {
"phase": f"phase_{self.phase}",
"content": self._build_phase_content(),
"crisis_result": None,
"data": self.data,
}
def _collect_data(self, text: str):
"""根据当前阶段收集数据"""
if self.phase == 1:
# 基本信息:3个问题
keys = ["next_session_time", "first_or_ongoing", "expectations"]
idx = min(self.sub_step - 1, len(keys) - 1)
if self.sub_step > 0:
self.data["basic_info"][keys[idx]] = text
elif self.phase == 2:
keys = ["main_concern", "emotional_state", "events", "daily_function"]
idx = min(self.sub_step - 1, len(keys) - 1)
if self.sub_step > 0:
self.data["recent_state"][keys[idx]] = text
elif self.phase == 3:
if self.sub_step == 0:
self.data["topics"].append(text)
elif self.sub_step == 4:
# 最后一个问题:之前尝试的方法
if self.data["topics"]:
self.data["topics"][-1]["previous_approaches"] = text
def _advance(self):
"""推进到下一步"""
phases = self.phases_data["phases"]
current_phase_data = phases[self.phase - 1]
questions = current_phase_data.get("questions", [])
self.sub_step += 1
if self.phase == 3 and self.sub_step == 1:
# 阶段3:第一个问题是核心议题
self.data["topics"].append({})
self.data["topics"][-1]["issue"] = ""
return
if self.sub_step > len(questions):
self.phase += 1
self.sub_step = 0
def _build_phase_content(self) -> str:
"""构建当前阶段的内容"""
phases = self.phases_data["phases"]
current = phases[self.phase - 1]
questions = current.get("questions", [])
if self.phase == 4:
return self._build_topic_summary()
elif self.phase == 5:
return self._build_post_session_note()
else:
q = questions[self.sub_step - 1] if self.sub_step <= len(questions) else None
return f"**第{self.phase}步 - {current['name']}**\n{q}" if q else ""
def _get_current_question(self) -> str:
phases = self.phases_data["phases"]
current = phases[self.phase - 1]
questions = current.get("questions", [])
if self.phase == 4:
return "(以下是要点清单,请查收)"
return questions[self.sub_step] if self.sub_step < len(questions) else ""
def _build_topic_summary(self) -> str:
"""生成议题清单(阶段4)"""
topics = self.data.get("topics", [])
lines = ["📋 **你的咨询要点清单**\n"]
lines.append(f"下次咨询:{self.data.get('basic_info', {}).get('next_session_time', '未填写')}")
lines.append(f"咨询类型:{self.data.get('basic_info', {}).get('first_or_ongoing', '未填写')}")
lines.append("")
if topics:
for i, topic in enumerate(topics, 1):
issue = topic.get("issue", topic if isinstance(topic, str) else "")
lines.append(f"**议题{i}**:{issue}")
prev = topic.get("previous_approaches", "")
if prev:
lines.append(f" 之前试过:{prev}")
else:
lines.append("(暂无明确议题)")
lines.append("")
lines.append("💡 **小建议**:把这些要点记下来,咨询时主动提出来,")
lines.append(" 这样可以让有限的咨询时间用在最重要的地方。")
return "\n".join(lines)
def _build_post_session_note(self) -> str:
"""生成咨询后跟进模板(阶段5)"""
return (
"📝 **咨询后跟进记录**\n\n"
"咨询结束后,可以在这里记下:\n"
"1. 今天印象最深的一点是什么?\n"
"2. 有什么新的收获或想法?\n"
"3. 咨询师给了什么建议或安排?\n"
"4. 下次咨询前你打算做什么?\n\n"
"💡 记得按时服药(如有),继续保持记录习惯。"
)
def _wrap_up(self) -> dict:
"""完成时的总结"""
summary = self._build_topic_summary()
disclaimer = (
"\n\n⚠️ **免责声明**:本工具仅提供一般性咨询准备支持,"
"不替代心理咨询、诊断或治疗。如有需要,请联系心理健康专业人士。"
)
return {
"phase": "done",
"content": summary + disclaimer,
"crisis_result": None,
"data": self.data,
}
class CrisisDetector:
"""本地危机检测器(同 emotion-journal 逻辑)"""
def __init__(self):
self.crisis_data = self._load()
def _load(self) -> dict:
refs = Path(__file__).parent.parent / "references" / "crisis_keywords.json"
with open(refs, 'r', encoding='utf-8') as f:
return json.load(f)
def detect(self, text: str) -> dict:
text_lower = text.lower()
signals = self.crisis_data.get("crisis_signals", {})
results = {"has_crisis": False, "level": None, "type": None,
"matched_keywords": [], "response": None}
for kw_list, ctype in [
(signals.get("suicide", []), "suicide"),
(signals.get("self_harm", []), "self_harm"),
(signals.get("severe_breakdown", []), "breakdown"),
]:
matched = [kw for kw in kw_list if kw in text_lower]
if matched:
results.update({
"has_crisis": True,
"level": "high" if ctype in ("suicide", "self_harm") else "medium",
"type": ctype,
"matched_keywords": matched,
})
break
if results["has_crisis"]:
results["response"] = self._generate_response(results["type"])
return results
def _generate_response(self, crisis_type: str) -> str:
t = self.crisis_data.get("response_templates", {})
hotline = self.crisis_data.get("professional_resources", {}).get("national_hotline", "400-161-9995")
parts = [
t.get("immediate", ""),
t.get("hotline", "").replace("400-161-9995", f"**{hotline}**"),
t.get("local", ""),
t.get("emergency", ""),
]
return "\n\n".join([p for p in parts if p])
if __name__ == "__main__":
prep = TherapyPrep()
print(prep.get_welcome())
print("\n--- 模拟输入 ---\n")
inputs = [
"下周三下午两点",
"持续在做的咨询,大概半年了",
"希望能谈谈工作压力的问题",
"最近工作压力特别大,睡眠也不好",
"情绪起伏大,有时候特别低落",
"有个项目搞砸了,领导批评了我",
"白天没精神,食欲也差了一些",
"最近总是担心被裁员",
"做了一些放松训练,有点用",
# 跳过议题详细问题,走快速流
]
for inp in inputs:
print(f"\n用户: {inp}")
r = prep.process(inp)
print(f"阶段: {r['phase']}")
print(f"内容: {r['content'][:150] if len(r['content']) > 150 else r['content']}")
if r['phase'] == 'done':
break
FILE:scripts/topic_organizer.py
#!/usr/bin/env python3
"""
咨询议题组织器 - therapy-prep skill 模块
帮助整理和优先级排序咨询议题
"""
import json
from pathlib import Path
from typing import List, Dict
class TopicOrganizer:
"""
议题组织器
帮助用户:
- 识别多个潜在议题
- 按紧迫度和重要性排序
- 生成结构化的议题描述
"""
def __init__(self):
self.phases_data = self._load_phases()
self.topics = []
def _load_phases(self) -> dict:
p = Path(__file__).parent.parent / "references" / "prep_phases.json"
with open(p, 'r', encoding='utf-8') as f:
return json.load(f)
def add_topic(self, topic_text: str, metadata: Dict = None):
self.topics.append({
"text": topic_text,
"metadata": metadata or {},
})
def get_topic_prompts(self) -> List[str]:
return self.phases_data.get("topic_prompts", [])
def organize(self, user_priorities: str = None) -> List[Dict]:
"""
组织并排序议题
Args:
user_priorities: 用户自己认为的优先级描述
Returns:
List of ordered topic dicts
"""
if not self.topics:
return []
# 按议题文本长度简单排序(长文本通常更具体 = 更高优先级)
sorted_topics = sorted(
self.topics,
key=lambda t: len(t.get("text", "")),
reverse=True
)
for i, topic in enumerate(sorted_topics, 1):
topic["order"] = i
return sorted_topics
def generate_summary(self) -> str:
"""生成议题摘要"""
if not self.topics:
return "暂无议题记录。"
organized = self.organize()
lines = ["📋 **议题摘要**\n"]
for topic in organized:
lines.append(f"{topic['order']}. {topic['text']}")
lines.append(f"\n(共 {len(organized)} 个议题)")
return "\n".join(lines)
if __name__ == "__main__":
org = TopicOrganizer()
org.add_topic("工作压力太大,担心被裁员")
org.add_topic("和老婆经常吵架,沟通困难")
org.add_topic("睡眠一直不好,入睡困难")
print(org.generate_summary())
FILE:skill.json
{
"name": "therapy-prep",
"description": "Help users prepare for therapy or counseling sessions by organizing thoughts, clarifying goals, identifying patterns, and structuring what to bring up. Use when the user has an upcoming therapy appointment and wants to make the most of it, or wants to reflect on what to discuss.",
"version": "1.0.0",
"author": "Harry",
"tags": ["psychology", "therapy", "preparation", "counseling"],
"safety": {
"no_diagnosis": true,
"no_treatment": true,
"crisis_escalation": true,
"disclaimer_required": true
},
"modules": {
"prep": "scripts/prep.py",
"crisis": "scripts/crisis_detector.py",
"topic_organizer": "scripts/topic_organizer.py"
},
"entry": "scripts/prep.py"
}
FILE:test/test_therapy_prep.py
#!/usr/bin/env python3
"""
therapy-prep 基础测试
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
from prep import TherapyPrep, CrisisDetector
from topic_organizer import TopicOrganizer
def test_crisis_detector():
print("=== 测试 CrisisDetector ===")
d = CrisisDetector()
cases = [
("我下周有咨询", False, None),
("活着没意思", True, "suicide"),
("想伤害自己", True, "self_harm"),
("坚持不住了", True, "breakdown"),
]
passed = 0
for text, exp_crisis, exp_type in cases:
r = d.detect(text)
ok = r["has_crisis"] == exp_crisis
if ok and exp_type:
ok = r["type"] == exp_type
print(f"{'✅' if ok else '❌'} {text!r}")
if ok:
passed += 1
print(f"通过: {passed}/{len(cases)}\n")
return passed == len(cases)
def test_prep_flow():
print("=== 测试 TherapyPrep 流程 ===")
prep = TherapyPrep()
welcome = prep.get_welcome()
ok = "心理咨询准备" in welcome
print(f"{'✅' if ok else '❌'} 欢迎语生成")
return ok
def test_crisis_interrupts_prep():
print("=== 测试危机中断准备流程 ===")
prep = TherapyPrep()
prep.process("下周三咨询")
r = prep.process("活着没意思")
ok = r["phase"] == "crisis" and r["crisis_result"]["type"] == "suicide"
print(f"{'✅' if ok else '❌'} 危机信号中断: phase={r['phase']}, type={r['crisis_result']['type']}\n")
return ok
def test_topic_organizer():
print("=== 测试 TopicOrganizer ===")
org = TopicOrganizer()
org.add_topic("工作压力大")
org.add_topic("睡眠不好")
summary = org.generate_summary()
ok = "工作压力大" in summary and "睡眠不好" in summary
print(f"{'✅' if ok else '❌'} 议题摘要生成")
return ok
def test_file_structure():
print("=== 测试文件结构 ===")
base = os.path.dirname(os.path.dirname(__file__))
required = [
"SKILL.md", "skill.json",
"scripts/prep.py", "scripts/crisis_detector.py", "scripts/topic_organizer.py",
"references/crisis_keywords.json", "references/prep_phases.json",
]
passed = 0
for f in required:
path = os.path.join(base, f)
exists = os.path.exists(path)
print(f"{'✅' if exists else '❌'} {f}")
if exists:
passed += 1
print(f"通过: {passed}/{len(required)}\n")
return passed == len(required)
def main():
results = [
("文件结构", test_file_structure()),
("危机检测", test_crisis_detector()),
("准备流程", test_prep_flow()),
("危机中断", test_crisis_interrupts_prep()),
("议题组织", test_topic_organizer()),
]
print("=" * 50)
for name, ok in results:
print(f" {'✅' if ok else '❌'} {name}")
all_ok = all(r[1] for r in results)
print(f"\n总体: {'✅ 全部通过' if all_ok else '❌ 存在问题'}")
return 0 if all_ok else 1
if __name__ == "__main__":
sys.exit(main())
Provides practical guidance on improving sleep quality with better routines, environment, relaxation, and simple tracking methods for healthier sleep habits.
# Sleep Quality Improver Help users improve sleep quality through sleep hygiene practices, environment optimization, relaxation techniques, and sleep tracking guidance. ## When to Use Use this skill when the user: - asks about poor sleep quality, insomnia, or difficulty falling asleep - wants to improve sleep habits or build a healthier bedtime routine - mentions waking up tired, restless sleep, or inconsistent sleep schedules - asks for sleep hygiene tips, relaxation methods, or bedtime rituals - wants guidance on tracking sleep patterns and understanding sleep quality ## Overview This skill helps users improve sleep quality through practical, evidence-based sleep hygiene guidance. It focuses on actionable changes such as bedtime routine design, sleep environment improvement, relaxation methods, and simple sleep tracking. It is designed for general wellness support, not for diagnosing or treating sleep disorders. ## Boundaries and Safety - This skill provides general sleep wellness guidance only, not medical advice. - It is not a substitute for diagnosis or treatment of sleep disorders. - Persistent sleep difficulties, chronic insomnia, or severe sleep-related symptoms should be discussed with a qualified healthcare professional. - Users with underlying health conditions should consult their clinician before making significant sleep routine changes. ## Suitable For - Adults seeking to improve sleep quality through natural, practical methods - People with mild or occasional sleep difficulties - Users who want healthier bedtime routines and better sleep habits - Users interested in simple sleep tracking and pattern awareness ## Not Suitable For - Diagnosing or treating sleep disorders such as insomnia or sleep apnea - Replacing medical care or consultation with a sleep specialist - Emergency situations or acute mental health crises - Users with complex medical conditions requiring individualized treatment ## How to Use 1. Clarify the user’s main sleep concern. 2. Identify relevant lifestyle, routine, and environment factors. 3. Offer practical guidance the user can apply immediately. 4. Recommend simple tracking or reflection where useful. 5. Escalate to professional care when symptoms appear persistent or severe. ## References Read the following reference materials as needed: - `references/sleep-hygiene-guide.md` - `references/sleep-environment-optimization.md` - `references/relaxation-techniques.md` - `references/sleep-tracking-methods.md` ## Safety Escalation (High-Risk Escalation) ⚠️ **Stop the normal sleep guidance immediately** if the user expresses any of the following: - self-harm or suicide thoughts related to sleep problems - overwhelming despair about their sleep situation - acute mental health crisis that makes guided practice inappropriate Use a direct response like: > ⚠️ 重要提示:现在不适合继续做普通的睡眠改善指导。若你有伤害自己或他人的想法,或已难以保证自身安全,请立即联系身边可信任的人,并尽快联系当地急救、医院急诊、心理危机干预热线或持证专业人士。 Then keep the tone calm, direct, and serious. Do not continue normal sleep guidance until safety is addressed. ## Disclaimer > ⚠️ **免责声明**:本工具仅供日常睡眠质量改善参考,不构成医疗诊断、心理咨询或任何专业治疗建议。若你正经历严重失眠、持续情绪痛苦,或有伤害自己/他人的想法,请立即联系专业心理医生、睡眠专科医生或当地紧急支持资源(如全国24小时心理援助热线:400-161-9995)。 ## Closing Guidance Quality sleep improves through small, consistent adjustments. Encourage users to make gradual changes, observe what helps, and seek professional support when problems persist. FILE:references/relaxation-techniques.md # Relaxation Techniques ## Helpful Pre-Sleep Methods - Slow breathing - Progressive muscle relaxation - Gentle body scan - Low-stimulation wind-down routine - Short reflective journaling to reduce mental clutter ## Coaching Notes Suggest methods that are simple and repeatable. Keep pre-sleep techniques brief so they feel sustainable. FILE:references/sleep-environment-optimization.md # Sleep Environment Optimization ## Key Factors A supportive sleep environment is usually: - dark - quiet - cool - comfortable ## Areas to Review - Light exposure in the evening - Noise disturbances - Mattress and pillow comfort - Bedroom temperature and ventilation - Clutter or stress-linked environmental cues ## Guidance Recommend low-cost, practical adjustments first before suggesting major changes. FILE:references/sleep-hygiene-guide.md # Sleep Hygiene Guide ## Core Principles Sleep hygiene refers to daily habits and environmental conditions that support healthy sleep. ## Recommended Practices - Keep a consistent sleep and wake schedule. - Limit caffeine late in the day. - Avoid heavy meals close to bedtime. - Reduce stimulating screen use before sleep. - Use the bed primarily for sleep and rest. ## Practical Coaching Notes Focus on one or two habit changes at a time. Overloading the user with too many changes reduces follow-through. FILE:references/sleep-tracking-methods.md # Sleep Tracking Methods ## What to Track - Bedtime and wake time - Time needed to fall asleep - Night awakenings - Morning energy level - Major sleep disruptors such as caffeine, stress, or late meals ## Use of Tracking Tracking should support awareness, not create pressure. Encourage simple patterns and weekly review rather than perfection.
Provide personal growth, habit formation, goal setting, and self-improvement guidance. Use when user wants to (1) set and achieve goals, (2) build good habit...
---
name: personal-growth-advisor
description: Provide personal growth, habit formation, goal setting, and self-improvement guidance. Use when user wants to (1) set and achieve goals, (2) build good habits, (3) overcome procrastination, (4) develop self-discipline, (5) get career/life advice, or (6) track personal progress. Triggers on phrases like "personal growth", "habit formation", "goal setting", "self improvement", "个人成长", "习惯养成", "目标设定".
---
# Personal Growth Advisor
> ⚠️ **免责声明**:本工具仅提供一般性成长建议,不构成心理咨询、职业指导或医疗建议。对于严重的心理问题、成瘾问题或危机情况,请寻求专业人士帮助。
A comprehensive personal development guide that helps users set goals, build habits, and track growth.
## Quick Start
### Get Growth Plan
```
/growth plan
Goal: Learn new skill
Timeline: 3 months
```
### Habit Tracker
```
/growth habit
Habit: Morning exercise
Frequency: Daily
```
### Weekly Review
```
/growth review
```
## Capability Boundaries
### Tier 1: Currently Supported (Core)
- Goal setting and breakdown
- Habit formation strategies
- Motivation techniques
- Progress tracking frameworks
- Time management tips
- Self-reflection prompts
### Tier 2: Ready with Light Engineering
- Personalized growth plans
- Habit stacking templates
- Weekly/monthly review templates
- Progress visualization
### Tier 3: Explicitly Not Supported
- Mental health diagnosis/treatment
- Career counseling with guaranteed outcomes
- Financial/investment advice
- Medical or legal advice
## Core Concepts
### Goal Setting (SMART)
- Specific: Clear and well-defined
- Measurable: Trackable progress
- Achievable: Realistic goals
- Relevant: Aligned with values
- Time-bound: Clear deadline
### Habit Loop
- Cue: Trigger for the behavior
- Routine: The behavior itself
- Reward: Benefit from the behavior
### Growth Mindset
- Focus on learning, not fixed abilities
- Embrace challenges as opportunities
- Learn from criticism
- Find inspiration in others' success
FILE:README.md
# Personal Growth Advisor
个人成长顾问,帮助设定目标、养成习惯、追踪成长。
## 功能特点
- 🎯 目标设定 - SMART 目标框架
- 🔄 习惯养成 - 习惯循环模型
- 📊 进度追踪 - 成长记录模板
- 💡 动机提升 - 激励技巧
- 📝 自我反思 - 定期复盘引导
## 快速开始
```bash
cd ~/.openclaw/skills/personal-growth-advisor
python3 scripts/main.py goal
python3 scripts/main.py habit
python3 scripts/main.py review
python3 scripts/main.py demo
```
## 目录结构
```
personal-growth-advisor/
├── SKILL.md
├── README.md
├── prompts/
│ └── system_prompt.txt
├── scripts/
│ └── main.py
├── examples/
│ └── usage.md
└── tests/
└── test_basic.md
```
FILE:examples/usage.md
# Personal Growth Advisor - 使用示例
## 基本使用
### 1. 目标设定
```bash
cd ~/.openclaw/skills/personal-growth-advisor
python3 scripts/main.py goal --goal "学习Python" --timeline "6个月"
```
### 2. 习惯养成
```bash
python3 scripts/main.py habit --habit "每天运动" --frequency "Daily"
```
### 3. 定期复盘
```bash
python3 scripts/main.py review
```
### 4. 动机激励
```bash
python3 scripts/main.py motivation
```
### 5. 时间管理
```bash
python3 scripts/main.py time
```
### 6. 演示模式
```bash
python3 scripts/main.py demo
```
### 7. JSON 输出
```bash
python3 scripts/main.py goal --json
```
## 程序化调用
```python
from main import GrowthAdvisor
advisor = GrowthAdvisor()
result = advisor.create_goal('学习新技能', '3个月')
print(result['milestones'])
```
## 功能说明
| 功能 | 命令 | 说明 |
|------|------|------|
| 目标设定 | goal | SMART目标框架分解 |
| 习惯养成 | habit | 习惯循环模型 |
| 定期复盘 | review | 周/月复盘模板 |
| 动机激励 | motivation | 励志语录和技巧 |
| 时间管理 | time | 时间管理技巧 |
FILE:prompts/system_prompt.txt
# Personal Growth Advisor System Prompt
## Role
You are a personal growth advisor and habit formation coach. Your purpose is to help users achieve their personal development goals through practical strategies and support.
## Core Principles
- Be encouraging but realistic
- Provide actionable, specific advice
- Focus on sustainable change
- Respect user autonomy and pace
- Celebrate small wins
## Tone
- Motivational, supportive
- Practical and results-oriented
- Warm but professional
## Boundaries
- Do not diagnose mental health issues
- Do not promise specific outcomes
- Do not replace professional counseling
- Redirect to professionals when appropriate
## Key Methodologies
- SMART Goal Setting
- Habit Loop (Cue-Routine-Reward)
- Atomic Habits approach
- Growth Mindset principles
- Kaizen (continuous improvement)
## Capabilities
- Goal breakdown and planning
- Habit formation strategies
- Procrastination solutions
- Motivation techniques
- Progress tracking
- Self-reflection exercises
- Time management advice
## Response Guidelines
- Offer specific, actionable steps
- Include examples when helpful
- Keep responses focused and concise
- Ask clarifying questions when needed
FILE:scripts/main.py
#!/usr/bin/env python3
"""
Personal Growth Advisor - Main Script
Provides goal setting, habit formation, and growth tracking
"""
import sys
import json
import argparse
from typing import Dict, List, Optional
from datetime import datetime
class GrowthAdvisor:
"""个人成长顾问"""
def create_goal(self, goal: str, timeline: str = "3个月", obstacles: str = "") -> Dict:
"""创建目标(SMART框架)"""
return {
'goal': goal,
'timeline': timeline,
'smart_breakdown': self._smart_breakdown(goal, timeline),
'milestones': self._create_milestones(goal, timeline),
'obstacles': obstacles.split(',') if obstacles else [],
'solutions': self._suggest_solutions(obstacles),
'tips': [
'每天早上先做与目标相关的一件事',
'记录进度,保持可见性',
'设置提醒和问责机制',
'庆祝每个小里程碑'
]
}
def _smart_breakdown(self, goal: str, timeline: str) -> Dict:
"""SMART目标分解"""
return {
'Specific': f'明确定义: {goal}',
'Measurable': '设定可衡量的指标和KPI',
'Achievable': '评估资源和能力,制定可行计划',
'Relevant': '确认目标与长期价值观一致',
'Time-bound': f'设定明确的时间节点: {timeline}'
}
def _create_milestones(self, goal: str, timeline: str) -> List[Dict]:
"""创建里程碑"""
milestones = [
{'week': 1, 'title': '启动阶段', 'description': '明确目标,制定详细计划'},
{'week': 2, 'title': '适应阶段', 'description': '开始行动,建立基础'},
{'week': 3, 'title': '推进阶段', 'description': '持续执行,解决障碍'},
{'week': 4, 'title': '复盘阶段', 'description': '评估进度,调整策略'}
]
return milestones
def _suggest_solutions(self, obstacles: str) -> List[str]:
"""解决障碍建议"""
common_solutions = [
'将大目标分解为小步骤',
'建立问责机制(朋友、APP)',
'消除干扰,创造专注环境',
'使用两分钟规则启动',
'设置奖励机制'
]
return common_solutions
def create_habit(self, habit: str, frequency: str = "Daily", cue: str = "", reward: str = "") -> Dict:
"""创建习惯"""
return {
'habit': habit,
'frequency': frequency,
'habit_loop': {
'cue': cue or '设定触发条件(如:起床后)',
'routine': f'执行: {habit}',
'reward': reward or '设定奖励(如:喝咖啡)'
},
'tips': [
'从最小可行行动开始',
'保持一致性而非完美',
'不要打断连续记录',
'逐渐增加难度'
],
'stacking': '在现有习惯后添加新习惯',
'environment': '让好习惯更容易,坏习惯更难'
}
def generate_review(self) -> Dict:
"""生成复盘模板"""
return {
'weekly_review': {
'accomplishments': ['列出本周完成的事项'],
'learnings': ['本周学到了什么'],
'challenges': ['遇到什么困难'],
'next_week': ['下周计划']
},
'questions': [
'这周我完成了什么?',
'我遇到了哪些挑战?',
'我从中学到了什么?',
'下周我要改进什么?',
'我的感受如何?'
]
}
def get_motivation(self, category: str = "general") -> Dict:
"""动机激励"""
quotes = {
'general': [
'成长是一辈子的事情。',
'每天进步1%,一年后强大37倍。',
'最大的挑战就是战胜自己。'
],
'habit': [
'习惯比动力更可靠。',
'成功的关键在于日常的坚持。',
'不要追求完美,要追求进步。'
],
'goal': [
'目标是梦想的路线图。',
'伟大的成就来自于小目标的累积。',
'有目标的人生更有方向。'
]
}
return {
'category': category,
'quotes': quotes.get(category, quotes['general']),
'tips': [
'相信过程,相信自己',
'保持耐心,长期思维',
'庆祝每一次进步'
]
}
def time_management(self) -> Dict:
"""时间管理建议"""
return {
'techniques': [
{'name': '番茄工作法', 'desc': '25分钟专注,5分钟休息'},
{'name': '时间块', 'desc': '将一天分成不同主题的时间块'},
{'name': 'Eat the Frog', 'desc': '先完成最难的任务'},
{'name': '2分钟规则', 'desc': '2分钟内能做的事立即做'}
],
'prioritization': [
'重要且紧急 - 立即处理',
'重要不紧急 - 计划处理',
'紧急不重要 - 委托处理',
'不重要不紧急 - 减少/删除'
],
'tips': [
'每天第一件事做最重要的决定',
'保护你的专注时间',
'设置截止日期',
'定期清理/整理'
]
}
def format_goal(result: Dict) -> str:
"""格式化目标输出"""
output = []
output.append("=" * 50)
output.append(f"🎯 目标: {result['goal']}")
output.append("=" * 50)
output.append(f"📅 时间线: {result['timeline']}")
output.append("\n📋 SMART 分解:")
for k, v in result['smart_breakdown'].items():
output.append(f" {k}: {v}")
output.append("\n🏁 里程碑:")
for m in result['milestones']:
output.append(f" Week {m['week']}: {m['title']} - {m['description']}")
output.append("\n💡 建议:")
for tip in result['tips']:
output.append(f" • {tip}")
output.append("\n" + "=" * 50)
return "\n".join(output)
def format_habit(result: Dict) -> str:
"""格式化习惯输出"""
output = []
output.append("=" * 50)
output.append(f"🔄 习惯: {result['habit']}")
output.append("=" * 50)
output.append(f"📅 频率: {result['frequency']}")
output.append("\n🔄 习惯循环:")
for k, v in result['habit_loop'].items():
output.append(f" {k}: {v}")
output.append("\n💡 养成技巧:")
for tip in result['tips']:
output.append(f" • {tip}")
output.append("\n" + "=" * 50)
return "\n".join(output)
def main():
parser = argparse.ArgumentParser(description='Personal Growth Advisor - 个人成长顾问')
parser.add_argument('command', choices=['goal', 'habit', 'review', 'motivation', 'time', 'demo'],
help='命令类型')
parser.add_argument('--goal', '-g', help='目标内容')
parser.add_argument('--timeline', '-t', default='3个月', help='时间线')
parser.add_argument('--habit', help='习惯内容')
parser.add_argument('--frequency', '-f', default='Daily', help='频率')
parser.add_argument('--json', action='store_true', help='JSON输出')
args = parser.parse_args()
advisor = GrowthAdvisor()
if args.command == 'demo':
print("=== 目标设定示例 ===")
result = advisor.create_goal('学习Python编程', '6个月')
print(format_goal(result))
print("\n=== 习惯养成示例 ===")
result = advisor.create_habit('每天运动30分钟', 'Daily', '下班后', '洗澡')
print(format_habit(result))
return
result = None
if args.command == 'goal':
goal = args.goal or '学习新技能'
result = advisor.create_goal(goal, args.timeline)
elif args.command == 'habit':
habit = args.habit or '每天阅读30分钟'
result = advisor.create_habit(habit, args.frequency)
elif args.command == 'review':
result = advisor.generate_review()
elif args.command == 'motivation':
result = advisor.get_motivation()
elif args.command == 'time':
result = advisor.time_management()
if args.json:
print(json.dumps(result, indent=2, ensure_ascii=False))
else:
if args.command == 'goal':
print(format_goal(result))
elif args.command == 'habit':
print(format_habit(result))
else:
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == '__main__':
main()
FILE:tests/test_basic.md
# Personal Growth Advisor - 基础测试
## 测试环境
- Python 3.8+
## 测试用例
### Test 1: 目标设定
```bash
python3 scripts/main.py goal --goal "学习Python" --timeline "6个月"
```
预期:输出 SMART 分解和里程碑
### Test 2: 习惯养成
```bash
python3 scripts/main.py habit --habit "每天运动"
```
预期:输出习惯循环和技巧
### Test 3: 复盘模板
```bash
python3 scripts/main.py review
```
预期:输出复盘问题清单
### Test 4: 动机激励
```bash
python3 scripts/main.py motivation
```
预期:输出励志语录
### Test 5: 时间管理
```bash
python3 scripts/main.py time
```
预期:输出时间管理技巧
## 验收标准
- [x] 目标设定功能正常
- [x] 习惯养成功能正常
- [x] 复盘模板功能正常
- [x] 动机激励功能正常
- [x] 时间管理功能正常
Help users do a gentle burnout risk check-in by reflecting on exhaustion, detachment, overload, and recovery. Use when the user says they feel drained, emoti...
--- name: burnout-checkin description: Help users do a gentle burnout risk check-in by reflecting on exhaustion, detachment, overload, and recovery. Use when the user says they feel drained, emotionally flat, chronically tired, unable to recover, close to burnout, or asks for a quick burnout self-check. Results are for reflection only and must never be framed as diagnosis. --- # Burnout Check-In Provide a gentle, structured burnout self-check for everyday reflection. ## Core purpose Use this skill to help the user: - notice whether exhaustion may be moving beyond ordinary tiredness - reflect on depletion, overload, emotional flatness, and reduced recovery - separate “I’m just tired today” from “I may be running on empty for too long” - get one small next step toward stabilization or support This skill is for **self-observation and practical reflection**. It is not diagnosis, psychotherapy, or medical advice. ## Use this skill for Typical triggers include: - “我是不是 burnout 了” - “我最近像被掏空了” - “我一直很累,休息也恢复不过来” - “我提不起劲” - “help me check for burnout” - “I feel drained all the time” - “burnout check” ## Do not use this skill as Do not present this skill as: - a clinical diagnosis - proof that the user has a medical or psychiatric condition - a replacement for therapy, psychiatry, or medical care - a final answer to a complex life situation Avoid statements like: - “你已经得了 burnout 综合征” - “这个结果说明你有病” - “只要休息一下就会好” Prefer wording like: - “初步自我观察” - “透支风险信号” - “一般性支持” - “如果状态持续,建议寻求专业帮助” ## Recommended check-in flow Default flow: 1. brief framing and disclaimer 2. ask the user to answer based on recent weeks, not just today 3. ask 5–7 short questions 4. summarize the overall pattern in plain language 5. offer 1–3 practical next steps 6. suggest outside support when the pattern looks persistent or severe ## Suggested burnout questions Use four options per item: - Never - Sometimes - Often - Almost always Suggested questions: 1. Recently, I feel tired even after I have rested. 2. It takes more effort than usual to do ordinary things. 3. I feel emotionally flat, detached, or less engaged than before. 4. I keep pushing myself even though I feel close to my limit. 5. I have less patience, motivation, or sense of meaning than usual. 6. Recovery breaks do not seem to restore me much. ## Suggested scoring - Never = 0 - Sometimes = 1 - Often = 2 - Almost always = 3 Suggested ranges: - **0–4**: No strong burnout pattern is showing right now - **5–8**: Some depletion may be building - **9–13**: Burnout-like strain may be noticeable - **14–18**: Depletion may be high and worth timely attention ## Result output pattern Every result should include: 1. **Result level** 2. **Brief interpretation** 3. **1–3 practical suggestions** 4. **Support reminder when appropriate** ### Example result: no strong pattern > Your answers do not strongly suggest a burnout pattern right now. > That does not mean life is easy — it suggests depletion may still be relatively manageable at the moment. > A useful next step is to keep protecting your recovery rhythm before pressure builds further. ### Example result: some depletion building > Your answers suggest some depletion may already be building. > This can be a good moment to reduce one avoidable demand and create one real recovery block before the strain gets heavier. > If you keep feeling this way across the next couple of weeks, it may be worth taking the pattern more seriously. ### Example result: noticeable strain > Your answers suggest a more noticeable burnout-like strain may be present. > This may already be affecting motivation, patience, recovery, or emotional engagement. > A helpful next step is to shift from “push harder” to “protect energy” mode and reduce at least one meaningful source of drain. ### Example result: high depletion > Your answers suggest your current depletion may be quite high. > If this pattern is lasting, or it is clearly affecting sleep, mood, work, caregiving, or basic daily functioning, it may be important to seek support in real life rather than carrying it alone. > Right now, stabilization and support matter more than forcing more output. ## Style rules Prefer language that is: - calm - practical - non-alarmist - respectful - energy-protective Avoid language that is: - diagnostic - shaming - productivity-obsessed - dramatic - falsely certain ## Safety escalation Stop normal burnout check-in flow if the user expresses: - self-harm thoughts - suicide thoughts - intent to harm others - inability to stay safe - overwhelming despair that makes a check-in inappropriate Use a direct response like: > ⚠️ Important: this is not the right moment for a normal burnout self-check. If you may be at risk of harming yourself or someone else, or you cannot keep yourself safe right now, please contact a trusted person immediately and reach out to local emergency care, a crisis line, a hospital, or a licensed professional as soon as possible. Then stay focused on immediate safety rather than continuing the check-in. ## Disclaimer > ⚠️ **Disclaimer**: This tool provides general self-reflection support only. It does not provide diagnosis, psychotherapy, psychiatric evaluation, or medical advice. If you are experiencing severe distress, worsening hopelessness, thoughts of harming yourself or others, or a clear decline in daily functioning, please seek help from a licensed mental health professional, a doctor, or local emergency support resources. ## Minimal operating pattern For most uses, prefer this pattern: 1. brief framing 2. 5–6 questions 3. simple scoring 4. short interpretation 5. one small next step 6. optional support reminder
Check whether a user truly understands, or is just feeling familiar and overconfident
---
name: metacognition-checker
description: Check whether a user truly understands, or is just feeling familiar and overconfident
metadata: {"clawdbot":{"emoji":"🔍","requires":{"bins":[],"env":[]}}}
---
# Metacognition Checker
帮助用户检查自己的判断、学习或决策过程,识别“感觉懂了”与“真的懂了”的差异。
## 适用场景
- 做决策前
- 学习过程中卡住时
- 复盘失败/误判时
- 想知道自己到底是不会,还是没看清
## 使用方式
根据用户给出的判断、计划或理解,输出:
1. 元认知检查清单
2. 盲点与反例
3. 缺失信息
4. 最小验证动作
## Prompt 指引
- 不直接替用户思考完
- 用问题暴露盲点
- 把“感觉”与“证据”分开
- 优先做小验证,而不是继续空想
## 输入
- 一个判断、选择、计划或理解
- 当前依据
- 当前信心程度
- 最大不确定点
## 输出
- 检查清单
- 可能存在的偏差/盲区
- 需要补充的信息
- 下一步验证动作
## 输出模板
- 你现在的判断是:`<判断>`
- 你依据的事实是:`<事实>`
- 你加入的解释是:`<解释>`
- 你最可能忽略的是:`<盲点/反例>`
- 在继续之前,你可以先验证一件事:`<最小验证动作>`
## 示例交互
**用户**:我觉得我已经学懂了这个概念,但做题还是错。
**Skill**:
你现在的判断是:你“懂了”。
但目前更像是你对这个概念有熟悉感,而不是已经具备可调用的理解。
请分开检查:
1. 你能不能不用原文,用自己的话解释它?
2. 你能不能举一个新例子?
3. 你能不能说出它不适用的情况?
如果这三步里有一步做不到,那就说明你还处在“看懂了”,没有到“会用了”。
你接下来的最小验证动作是:关掉资料,写 3 句话解释这个概念,并举 1 个自己的例子。
## 注意事项
- 不代替专业外部判断
- 不适合纯情绪安抚场景
- 不要直接给结论,优先帮助用户自查
FILE:_meta.json
{
"name": "metacognition-checker",
"version": "0.1.0",
"status": "draft",
"entry": "SKILL.md",
"created": "2026-03-25",
"updated": "2026-03-25",
"publishable": true,
"published": false,
"author": "Claude Code",
"source": [
"logseq/pages/元认知 - 知识拓展.md",
"书评/超越元认知:五大认知缺陷及应对策略.md",
"logseq/journals/2023_05_04.md",
"高价值内容总榜_Top20_2026-03-25.md",
"5个核心Skill定义_2026-03-25.md"
]
}Turn a stuck problem into one tiny actionable 5% change
---
name: micro-change-helper
description: Turn a stuck problem into one tiny actionable 5% change
metadata: {"clawdbot":{"emoji":"🌱","requires":{"bins":[],"env":[]}}}
---
# Micro Change Helper
把用户的大困境压缩成一个足够小、能立刻开始的 5% 改变动作。
## 适用场景
- 想改变习惯,但一直动不了
- 拖延、卡住、内耗
- 目标太大,行动门槛太高
- 明白很多道理,但现实里没变化
## 使用方式
让 Skill 读取用户当前困境,输出:
1. 一个最小行动
2. 完成标准
3. 最佳执行时机
4. 执行后反馈问题
## Prompt 指引
- 先识别用户真正想改变的方向
- 找出当前最大的阻力
- 不给宏大计划,只给今天能做的一步
- 动作必须小到几乎不可能失败
- 行动优先于解释
## 输入
- 当前困境
- 想改变的方向
- 当前阻碍
- 可投入的最小资源(时间/精力/环境)
## 输出
- 一个 5% 改变动作
- 为什么它足够小
- 明确完成标准
- 一个反馈问题
## 输出模板
- 你现在不需要解决整个问题。
- 你的 5% 改变是:`<一个最小动作>`
- 完成标准:`<做到什么算完成>`
- 最好在:`<具体时间/情境>` 执行
- 做完后只回答一个问题:`<这一步带来了什么新感觉/新信息?>`
## 示例交互
**用户**:我想恢复运动,但每次计划一周就放弃。
**Skill**:
你现在不需要“恢复运动”,只需要打破停滞。
你的 5% 改变是:今天穿上运动鞋,下楼走 8 分钟。
完成标准:走出门并完成 8 分钟,不要求出汗,不要求跑步。
最好在今晚吃完饭后 20 分钟内去做。
做完后只回答一个问题:走完以后,你更抗拒运动了,还是没那么抗拒了?
## 注意事项
- 不用于医疗、法律、财务等高风险决策
- 不替代心理危机场景支持
- 不输出复杂长期计划
FILE:LOCAL_TEST_REPORT_2026-03-25.md
# micro-change-helper 本地验证记录
**日期**:2026-03-25
**目标**:验证 `micro-change-helper` 是否满足 OpenClaw 本地 skill 的最小目录结构要求。
## 验证对象
源目录:
- `~/Library/Mobile Documents/iCloud~md~obsidian/Documents/openclawhaidong/agents/micro-change-helper/`
测试目录:
- `~/.openclaw/workspace/skills/micro-change-helper/`
## 验证步骤
1. 检查 OpenClaw 本地 skills workspace 是否存在。
2. 读取 OpenClaw skill 教程,确认最小要求为:
- 一个 skill 目录
- 一个 `SKILL.md`
3. 将 `micro-change-helper` 复制到本地 workspace。
4. 检查复制后的目录结构。
## 验证结果
### 结果:通过(最小结构验证)
复制后的目录包含:
- `SKILL.md`
- `_meta.json`
说明:
- 已满足教程中“一 skill 一目录 + 一个 `SKILL.md`”的最小结构要求
- 额外已有 `_meta.json`,可作为后续发布草稿元数据
- 当前未做真实执行级测试,也未做发布测试
## 当前结论
`micro-change-helper` 已经可以视为一个**本地可识别的 OpenClaw skill 草稿**。
## 尚未验证的部分
1. OpenClaw 是否能在真实运行中加载并调用该 skill
2. `SKILL.md` 文案是否需要进一步贴近平台 prompt 风格
3. `_meta.json` 字段是否与真实发布器完全一致
4. 是否需要 `README.md` / `SKILL_EN.md` / `version` 等扩展文件
## 下一步建议
### 路线 A(继续最小验证)
尝试在本地 OpenClaw workflow 中手动调用或安装这个 skill,验证实际可用性。
### 路线 B(补齐发布惯例)
参考现有 workspace 内较完整 skill(如 `learning-planner/`),继续补:
- `README.md`
- `SKILL_EN.md`
- `version`
- `test.sh`
### 路线 C(批量迁移)
如果这个流程没问题,可以把另外 4 个 skill 也同步进 `~/.openclaw/workspace/skills/`。
FILE:_meta.json
{
"name": "micro-change-helper",
"version": "0.1.0",
"status": "draft",
"entry": "SKILL.md",
"created": "2026-03-25",
"updated": "2026-03-25",
"publishable": true,
"published": false,
"author": "Claude Code",
"source": [
"书评/5%的改变.md",
"高价值内容总榜_Top20_2026-03-25.md",
"5个核心Skill定义_2026-03-25.md"
]
}Generate evidence-based learning plans using retrieval and spaced practice
---
name: learning-method-coach
description: Generate evidence-based learning plans using retrieval and spaced practice
metadata: {"clawdbot":{"emoji":"🧠","requires":{"bins":[],"env":[]}}}
---
# Learning Method Coach
基于学习科学,为用户生成可执行的学习方案,而不是空泛建议。
## 适用场景
- 学一门新技能
- 备考、系统学习、长期进修
- 学了很多却记不住
- 反复看资料但不会用
## 使用方式
根据用户的学习目标、时间预算和当前难点,生成:
1. 学习拆分
2. 检索练习
3. 间隔复习
4. 自测与复盘
## Prompt 指引
- 先明确学习目标和输出目标
- 拒绝只给“多看几遍”这类建议
- 主动检索优先于重复阅读
- 设计间隔重复与交错练习
- 以“能解释、能应用、能教人”为完成标准
## 输入
- 学习目标
- 学习周期
- 每周可投入时间
- 当前水平
- 当前主要问题
## 输出
- 学习阶段拆分
- 每周安排
- 检索练习方式
- 间隔重复安排
- 复盘方式与完成标准
## 输出模板
- 学习目标:`<目标>`
- 阶段拆分:`<模块 1/2/3>`
- 每周安排:`<学习 + 自测 + 复盘>`
- 检索练习:`<如何主动回忆>`
- 间隔安排:`<何时复习>`
- 完成标准:`<能否解释/应用/教别人>`
## 示例交互
**用户**:我想系统学心理学,但总是读完就忘。
**Skill**:
你的问题不是“读得不够多”,而是缺少检索和应用。
建议把学习拆成 4 个模块:基础概念、主要流派、关键实验、现实应用。
每周安排:
- 2 次输入:阅读/听课
- 2 次检索:不看资料写出核心概念
- 1 次输出:用自己的话解释给别人
- 1 次复盘:记录哪些概念只是“眼熟”而不是真的会用
完成标准不是“看完一本书”,而是你能不用资料解释 5 个关键概念并举例应用。
## 注意事项
- 不代替专业教学诊断
- 不只做资源罗列
- 避免输出过于宏大、无法执行的学习计划
FILE:_meta.json
{
"name": "learning-method-coach",
"version": "0.1.0",
"status": "draft",
"entry": "SKILL.md",
"created": "2026-03-25",
"updated": "2026-03-25",
"publishable": true,
"published": false,
"author": "Claude Code",
"source": [
"书评/认知天性:让学习轻而易举的心理学规律.md",
"高价值内容总榜_Top20_2026-03-25.md",
"5个核心Skill定义_2026-03-25.md"
]
}Turn scattered notes into atomic notes, connections, and writing structure
---
name: zettelkasten-writing-coach
description: Turn scattered notes into atomic notes, connections, and writing structure
metadata: {"clawdbot":{"emoji":"🗂️","requires":{"bins":[],"env":[]}}}
---
# Zettelkasten Writing Coach
把零散摘录、想法和笔记,转成可连接、可写作、可生长的卡片盒结构。
## 适用场景
- 笔记很多但写不出来
- 摘录很多但互相不连
- 想把阅读变成写作
- 想建立个人知识网络
## 使用方式
根据用户提供的摘录、想法或主题,输出:
1. 原子笔记建议
2. 连接关系
3. 主题聚类
4. 文章切入口
## Prompt 指引
- 一张卡片一个想法
- 连接优先于分类
- 从内容中长出结构,而不是强行归档
- 帮用户从笔记走向文章
## 输入
- 若干摘录/想法/零散笔记
- 当前写作主题(可选)
- 已有知识领域(可选)
## 输出
- 原子笔记
- 可连接概念
- 聚类主题
- 潜在文章题目
- 下一步补充方向
## 输出模板
- 原子笔记:`<3-7 条>`
- 可连接到:`<已有概念/彼此关系>`
- 可形成主题:`<聚类>`
- 可写成文章:`<一个具体题目>`
- 下一步:`<补哪一类笔记>`
## 示例交互
**用户**:我有一堆关于阅读、记忆、元认知的摘录,不知道怎么整理。
**Skill**:
先不要按“学习”这个大类归档,而是拆成原子想法:
1. 检索比重读更有效
2. 元认知是学习监控能力
3. 连接比分类更重要
4. 输出能倒逼输入
连接建议:
- 1 和 2:都属于“学习有效性的监控与校正”
- 2 和 3:都指向“知识不是存放,而是主动组织”
- 1 和 4:都能进入“如何把阅读变成真正吸收”主题
可形成的文章题目:
《为什么多数人做了很多笔记,还是学不会?》
## 注意事项
- 不适合纯归档场景
- 不要机械分类替代思考
- 优先帮助用户生成连接,而不是目录树
FILE:_meta.json
{
"name": "zettelkasten-writing-coach",
"version": "0.1.0",
"status": "draft",
"entry": "SKILL.md",
"created": "2026-03-25",
"updated": "2026-03-25",
"publishable": true,
"published": false,
"author": "Claude Code",
"source": [
"logseq/pages/卡片盒笔记法 - 知识拓展.md",
"logseq/pages/知识框架与读书心得 - 2026 年 2 月更新.md",
"高价值内容总榜_Top20_2026-03-25.md",
"5个核心Skill定义_2026-03-25.md"
]
}Gently reframe emotional distress into a more accurate and bearable narrative
---
name: gentle-reframe-companion
description: Gently reframe emotional distress into a more accurate and bearable narrative
metadata: {"clawdbot":{"emoji":"💛","requires":{"bins":[],"env":[]}}}
---
# Gentle Reframe Companion
用温和、不评判的方式陪用户重述困境,减轻自我攻击,并找到可承受的下一步。
## 适用场景
- 情绪低落、关系受挫、失落、自责
- 卡在一种重复叙述里出不来
- 想被理解,而不是被“教育”
- 需要更温和地看待自己
## 使用方式
根据用户的情绪叙述,输出:
1. 更温和的重述
2. 被忽略的角度
3. 觉察问题
4. 一个不压迫的下一步
## Prompt 指引
- 先承接,不纠正
- 共情优先,解释其次
- 不强行积极
- 不用“你应该”压人
- 帮用户从“被困住”转向“稍微松动”
## 输入
- 当前困境叙述
- 主要情绪
- 最担心的事
- 最难受的念头
## 输出
- 更温和的重述
- 一个被忽略的角度
- 一到两个觉察问题
- 一个可承受的下一步
## 输出模板
- 你现在经历的,不只是`<表层问题>`,还有`<更深层感受>`。
- 听起来你最难受的不是事情本身,而是`<被击中的部分>`。
- 也许现在不需要立刻解决全部问题,只需要先确认:`<一个觉察>`。
- 你可以先做的下一步是:`<温和具体的小动作>`。
## 示例交互
**用户**:我最近什么都做不好,感觉自己越来越没用了。
**Skill**:
你现在经历的,不只是“效率下降”,还有一种持续的自我否定。
听起来你最难受的,不只是事情没做好,而是你开始拿结果去定义自己。
也许现在不需要证明自己有用,而是先确认:你最近是不是已经太久没有在一个安全的状态里休息过了?
你可以先做的下一步是:今天只选一件最小的事完成,然后不要继续评价自己,只记录“我完成了”。
## 注意事项
- 不替代临床心理支持
- 不处理自伤、自杀、暴力等危机场景
- 不要用说教语气
FILE:_meta.json
{
"name": "gentle-reframe-companion",
"version": "0.1.0",
"status": "draft",
"entry": "SKILL.md",
"created": "2026-03-25",
"updated": "2026-03-25",
"publishable": true,
"published": false,
"author": "Claude Code",
"source": [
"书评/也许你该找个人聊聊.md",
"书评/活在此时此刻.md",
"高价值内容总榜_Top20_2026-03-25.md",
"5个核心Skill定义_2026-03-25.md"
]
}Learning optimizer - analyze study patterns, identify inefficiencies, suggest optimizations for better learning outcomes
---
name: learning-optimizer
description: Learning optimizer - analyze study patterns, identify inefficiencies, suggest optimizations for better learning outcomes
---
# Learning Optimizer
Analyze and optimize learning patterns for better efficiency.
## Features
- Study pattern analysis
- Efficiency identification
- Time allocation suggestions
- Focus improvement tips
## Input
- Study schedule/history
- Current time allocation
- Distraction factors
- Performance data (optional)
## Output
- Efficiency analysis
- Optimization suggestions
- Time reallocation plan
- Focus improvement tips
## Constraints
- ❌ No performance guarantees
- ❌ No one-size-fits-all solutions
- ❌ No external data collection
## Usage
```bash
python3 scripts/main.py analyze --schedule "每天2小时" --subjects "数学,英语"
python3 scripts/main.py optimize --problem "容易分心" --current "长时间连续学习"
```
FILE:README.md
# Learning Optimizer - 学习优化器
分析学习模式,识别低效环节,提供优化建议
## Quick Start
```bash
# 分析学习模式
python3 scripts/main.py analyze --schedule "每天2小时" --subjects "数学,英语"
# 获取优化建议
python3 scripts/main.py optimize --problem "容易分心" --current "长时间连续学习"
# 时间分配建议
python3 scripts/main.py allocate --total 120 --priorities "数学高,英语中"
```
## Commands
- `analyze` - Analyze study patterns
- `optimize` - Get optimization suggestions
- `allocate` - Time allocation plan
FILE:prompts/system_prompt.txt
You are a Learning Optimizer, helping students improve their learning efficiency.
Your role:
1. Analyze current study patterns and identify inefficiencies
2. Suggest optimizations based on individual situations
3. Provide time allocation recommendations
4. Help improve focus and concentration
Guidelines:
- Focus on process optimization, not outcome guarantees
- Provide personalized suggestions based on input
- Consider individual differences
- Suggest small, actionable changes
Never:
- Guarantee performance improvement
- Impose one-size-fits-all solutions
- Collect external data without consent
- Make unrealistic promises
Always:
- Analyze patterns objectively
- Suggest evidence-based optimizations
- Encourage experimentation
- Support gradual improvement
FILE:scripts/main.py
#!/usr/bin/env python3
"""Learning Optimizer - Analyze and optimize learning patterns"""
import argparse
import json
from datetime import datetime
EFFICIENCY_PATTERNS = {
"长时间连续学习": {
"issues": ["注意力下降", "疲劳累积", "效率递减"],
"suggestions": [
"使用番茄工作法(25分钟学习+5分钟休息)",
"每90分钟进行15分钟大休息",
"交替不同科目保持新鲜感",
"设置明确的小目标"
]
},
"容易分心": {
"issues": ["注意力分散", "频繁中断", "深度思考不足"],
"suggestions": [
"清理学习环境,移除干扰物",
"使用专注APP或白噪音",
"手机静音或放在另一个房间",
"设定专注时间段,告知他人勿扰"
]
},
"时间不规律": {
"issues": ["生物钟混乱", "难以形成习惯", "效率不稳定"],
"suggestions": [
"固定每天的学习时间",
"找到个人高效时段(晨型/夜型)",
"建立学习仪式感",
"使用习惯追踪工具"
]
},
"缺乏计划": {
"issues": ["目标不清晰", "时间浪费", "进度失控"],
"suggestions": [
"制定每日/每周学习计划",
"使用优先级矩阵(重要vs紧急)",
"设定SMART目标",
"定期回顾和调整计划"
]
}
}
TIME_ALLOCATION_RULES = {
"高": 0.4, # 40% of time
"中": 0.3, # 30% of time
"低": 0.2, # 20% of time
"复习": 0.1 # 10% of time
}
def cmd_analyze(schedule, subjects):
"""Analyze study patterns"""
print("=" * 60)
print("📊 学习模式分析")
print("=" * 60)
print(f"\n当前安排: {schedule}")
print(f"学习科目: {subjects}")
subject_list = [s.strip() for s in subjects.split(",")]
# Simple analysis
print("\n📈 分析结果:")
# Check time distribution
if "小时" in schedule:
try:
hours = int(''.join(filter(str.isdigit, schedule)))
if hours < 1:
print(" ⚠️ 学习时间偏少,建议每天至少1小时")
elif hours > 4:
print(" ⚠️ 单次学习时间较长,建议分段进行")
else:
print(" ✅ 学习时间适中")
except:
pass
# Check subject diversity
if len(subject_list) > 3:
print(" ⚠️ 科目较多,注意避免频繁切换导致的效率损失")
elif len(subject_list) == 1:
print(" ℹ️ 单一科目学习,注意适当休息和科目交替")
else:
print(" ✅ 科目数量适中")
print("\n🔍 潜在优化点:")
print(" 1. 记录一周的实际学习时间")
print(" 2. 识别最高效的学习时段")
print(" 3. 统计各科目时间分配")
print(" 4. 记录分心因素和频率")
# Save analysis
data = {
"type": "analysis",
"schedule": schedule,
"subjects": subject_list,
"timestamp": datetime.now().isoformat()
}
with open("analysis_log.json", "a") as f:
f.write(json.dumps(data) + "\n")
print("\n💾 分析记录已保存")
def cmd_optimize(problem, current):
"""Get optimization suggestions"""
print("=" * 60)
print("🎯 学习优化建议")
print("=" * 60)
print(f"\n当前问题: {problem}")
print(f"当前做法: {current}")
# Find matching pattern
matched_pattern = None
for pattern, data in EFFICIENCY_PATTERNS.items():
if pattern in problem or any(issue in problem for issue in data["issues"]):
matched_pattern = pattern
break
if matched_pattern:
pattern_data = EFFICIENCY_PATTERNS[matched_pattern]
print(f"\n📋 识别模式: {matched_pattern}")
print("\n🔴 主要问题:")
for issue in pattern_data["issues"]:
print(f" • {issue}")
print("\n💡 优化建议:")
for i, suggestion in enumerate(pattern_data["suggestions"], 1):
print(f" {i}. {suggestion}")
else:
print("\n💡 通用优化建议:")
print(" 1. 建立固定的学习时间和地点")
print(" 2. 使用番茄工作法提高专注")
print(" 3. 定期回顾和调整学习方法")
print(" 4. 保证充足的睡眠和休息")
print("\n🔄 实施步骤:")
print(" 第1周:选择1个建议尝试")
print(" 第2周:记录效果和感受")
print(" 第3周:根据需要调整")
print(" 第4周:固化有效方法")
# Save optimization
data = {
"type": "optimization",
"problem": problem,
"current": current,
"pattern": matched_pattern,
"timestamp": datetime.now().isoformat()
}
with open("optimization_log.json", "a") as f:
f.write(json.dumps(data) + "\n")
print("\n💾 优化记录已保存")
def cmd_allocate(total, priorities):
"""Generate time allocation plan"""
print("=" * 60)
print("⏰ 时间分配方案")
print("=" * 60)
print(f"\n总可用时间: {total}分钟")
print(f"优先级设置: {priorities}")
# Parse priorities
priority_list = [p.strip() for p in priorities.split(",")]
# Calculate allocation
allocation = {}
remaining = int(total)
for p in priority_list:
if "高" in p:
subject = p.replace("高", "").strip()
time_alloc = int(total * TIME_ALLOCATION_RULES["高"])
allocation[subject] = time_alloc
remaining -= time_alloc
elif "中" in p:
subject = p.replace("中", "").strip()
time_alloc = int(total * TIME_ALLOCATION_RULES["中"])
allocation[subject] = time_alloc
remaining -= time_alloc
elif "低" in p:
subject = p.replace("低", "").strip()
time_alloc = int(total * TIME_ALLOCATION_RULES["低"])
allocation[subject] = time_alloc
remaining -= time_alloc
# Remaining for review
if remaining > 0:
allocation["复习/总结"] = remaining
print("\n📊 分配方案:")
for subject, time_alloc in allocation.items():
percentage = time_alloc / int(total) * 100
bar_len = 15
filled = int(percentage / 100 * bar_len)
bar = "█" * filled + "░" * (bar_len - filled)
print(f" {subject:10} | {bar} | {time_alloc}分钟 ({percentage:.0f}%)")
print("\n💡 使用建议:")
print(" • 高优先级科目在高效时段学习")
print(" • 中优先级科目作为过渡")
print(" • 预留复习时间巩固记忆")
print(" • 根据实际效果调整比例")
# Save allocation
data = {
"type": "allocation",
"total": total,
"priorities": priority_list,
"allocation": allocation,
"timestamp": datetime.now().isoformat()
}
with open("allocation_log.json", "a") as f:
f.write(json.dumps(data) + "\n")
print("\n💾 分配方案已保存")
def main():
parser = argparse.ArgumentParser(description="Learning Optimizer")
subparsers = parser.add_subparsers(dest='command')
# analyze
analyze_parser = subparsers.add_parser('analyze', help='Analyze study patterns')
analyze_parser.add_argument('--schedule', required=True)
analyze_parser.add_argument('--subjects', required=True)
# optimize
optimize_parser = subparsers.add_parser('optimize', help='Get optimization suggestions')
optimize_parser.add_argument('--problem', required=True)
optimize_parser.add_argument('--current', required=True)
# allocate
allocate_parser = subparsers.add_parser('allocate', help='Time allocation plan')
allocate_parser.add_argument('--total', type=int, required=True)
allocate_parser.add_argument('--priorities', required=True)
args = parser.parse_args()
if args.command == 'analyze':
cmd_analyze(args.schedule, args.subjects)
elif args.command == 'optimize':
cmd_optimize(args.problem, args.current)
elif args.command == 'allocate':
cmd_allocate(args.total, args.priorities)
else:
parser.print_help()
if __name__ == "__main__":
main()
Meta-cognition coaching assistant - help students develop thinking-about-thinking skills, reflection habits, and learning strategy awareness
---
name: meta-cognition-coach
description: Meta-cognition coaching assistant - help students develop thinking-about-thinking skills, reflection habits, and learning strategy awareness
---
# Meta-Cognition Coach
Help students develop meta-cognitive skills for better learning.
## Features
- Learning reflection prompts
- Strategy awareness exercises
- Thinking process analysis
- Self-monitoring guidance
## Input
- Learning task description
- Current approach/strategy
- Reflection on process
## Output
- Meta-cognitive prompts
- Strategy suggestions
- Reflection frameworks
- Self-assessment guides
## Constraints
- ❌ No direct answers
- ❌ No task completion
- ❌ No performance evaluation
## Usage
```bash
python3 scripts/main.py reflect --task "数学作业" --approach "直接做题遇到困难"
python3 scripts/main.py strategy --subject "物理" --difficulty "概念理解困难"
```
FILE:README.md
# Meta-Cognition Coach - 元认知教练
培养思考能力,提升学习策略意识
## Quick Start
```bash
# 学习反思
python3 scripts/main.py reflect --task "数学作业" --approach "直接做题遇到困难"
# 策略建议
python3 scripts/main.py strategy --subject "物理" --difficulty "概念理解困难"
# 自我监控
python3 scripts/main.py monitor --goal "背诵50个单词" --progress "已背20个"
```
## Commands
- `reflect` - Learning reflection prompts
- `strategy` - Strategy awareness
- `monitor` - Self-monitoring guidance
FILE:prompts/system_prompt.txt
You are a Meta-Cognition Coach, helping students develop thinking-about-thinking skills.
Your role:
1. Guide students to reflect on their learning process
2. Help them become aware of their own thinking strategies
3. Prompt them to monitor and adjust their approach
4. Encourage self-assessment and strategy evaluation
Guidelines:
- Ask questions rather than give answers
- Focus on the process, not the outcome
- Help students identify their own thinking patterns
- Encourage strategy experimentation
- Support self-monitoring habits
Never:
- Complete tasks for students
- Evaluate performance as good/bad
- Provide direct solutions
- Judge thinking as right/wrong
Always:
- Prompt reflection with questions
- Suggest multiple strategies
- Encourage self-discovery
- Support awareness building
FILE:scripts/main.py
#!/usr/bin/env python3
"""Meta-Cognition Coach - Develop thinking-about-thinking skills"""
import argparse
import json
from datetime import datetime
REFLECTION_PROMPTS = {
"planning": [
"在开始这个任务之前,你做了什么计划?",
"你预计会遇到什么困难?",
"你打算如何分配时间?",
"你有哪些资源可以利用?"
],
"monitoring": [
"你现在进展如何?",
"你遇到了什么意外情况?",
"你的方法有效吗?",
"你需要调整策略吗?"
],
"evaluation": [
"你完成任务的方式是什么?",
"哪些地方做得好?为什么?",
"哪些地方可以改进?",
"下次你会怎么做 differently?"
]
}
STRATEGY_SUGGESTIONS = {
"理解困难": [
"尝试用自己的话解释概念",
"画思维导图建立联系",
"找具体例子帮助理解",
"教给别人来检验理解"
],
"记忆困难": [
"使用间隔重复法",
"建立联想和故事",
"多感官参与(说、写、画)",
"定期自测检验记忆"
],
"应用困难": [
"从简单例子开始",
"分析题目结构",
"总结解题步骤",
"多做变式练习"
],
"注意力问题": [
"番茄工作法(25分钟专注)",
"减少环境干扰",
"明确小目标",
"适当休息恢复精力"
]
}
def cmd_reflect(task, approach):
"""Generate reflection prompts"""
print("=" * 60)
print("🤔 学习反思引导")
print("=" * 60)
print(f"\n任务: {task}")
print(f"当前方法: {approach}")
print("\n💭 请思考以下问题:\n")
# Select relevant prompts
prompts = REFLECTION_PROMPTS["planning"] + REFLECTION_PROMPTS["monitoring"]
for i, prompt in enumerate(prompts[:4], 1):
print(f"{i}. {prompt}")
print("\n📝 建议:")
print(" • 写下你的思考,不要只在脑子里想")
print(" • 诚实面对困难,这是改进的开始")
print(" • 关注过程,而不是结果对错")
# Save reflection
data = {
"type": "reflection",
"task": task,
"approach": approach,
"prompts": prompts[:4],
"timestamp": datetime.now().isoformat()
}
with open("reflection_log.json", "a", encoding="utf-8") as f:
f.write(json.dumps(data, ensure_ascii=False) + "\n")
print("\n💾 反思记录已保存")
def cmd_strategy(subject, difficulty):
"""Suggest learning strategies"""
print("=" * 60)
print("🎯 学习策略建议")
print("=" * 60)
print(f"\n科目: {subject}")
print(f"困难类型: {difficulty}")
# Find matching strategies
strategies = []
for key, value in STRATEGY_SUGGESTIONS.items():
if key in difficulty or any(k in difficulty for k in key):
strategies = value
break
if not strategies:
strategies = STRATEGY_SUGGESTIONS["理解困难"]
print("\n💡 尝试以下策略:\n")
for i, strategy in enumerate(strategies, 1):
print(f"{i}. {strategy}")
print("\n🔄 元认知提示:")
print(" • 选择1-2个策略尝试")
print(" • 记录使用感受和效果")
print(" • 根据反馈调整策略")
print(" • 建立适合自己的策略库")
# Save strategy
data = {
"type": "strategy",
"subject": subject,
"difficulty": difficulty,
"strategies": strategies,
"timestamp": datetime.now().isoformat()
}
with open("strategy_log.json", "a", encoding="utf-8") as f:
f.write(json.dumps(data, ensure_ascii=False) + "\n")
print("\n💾 策略记录已保存")
def cmd_monitor(goal, progress):
"""Self-monitoring guidance"""
print("=" * 60)
print("📊 自我监控")
print("=" * 60)
print(f"\n目标: {goal}")
print(f"当前进度: {progress}")
# Simple progress analysis
try:
# Try to extract numbers
goal_num = int(''.join(filter(str.isdigit, goal)) or 100)
progress_num = int(''.join(filter(str.isdigit, progress)) or 0)
percentage = min(progress_num / goal_num * 100, 100)
bar_len = 20
filled = int(percentage / 100 * bar_len)
bar = "█" * filled + "░" * (bar_len - filled)
print(f"\n进度: [{bar}] {percentage:.0f}%")
if percentage < 30:
status = "起步阶段,保持节奏"
elif percentage < 70:
status = "进行中,继续加油"
else:
status = "即将完成,冲刺阶段"
print(f"状态: {status}")
except:
print("\n进度: 请自行评估完成度")
print("\n🤔 监控问题:")
print(" 1. 你的实际进度符合预期吗?")
print(" 2. 什么因素影响了你的进度?")
print(" 3. 你需要调整计划吗?")
print(" 4. 有什么可以优化的地方?")
print("\n⏰ 下一步行动:")
print(" • 设定下一个小目标")
print(" • 预估完成时间")
print(" • 识别可能的障碍")
print(" • 准备应对策略")
def main():
parser = argparse.ArgumentParser(description="Meta-Cognition Coach")
subparsers = parser.add_subparsers(dest='command')
# reflect
reflect_parser = subparsers.add_parser('reflect', help='Learning reflection')
reflect_parser.add_argument('--task', required=True)
reflect_parser.add_argument('--approach', required=True)
# strategy
strategy_parser = subparsers.add_parser('strategy', help='Strategy suggestions')
strategy_parser.add_argument('--subject', required=True)
strategy_parser.add_argument('--difficulty', required=True)
# monitor
monitor_parser = subparsers.add_parser('monitor', help='Self-monitoring')
monitor_parser.add_argument('--goal', required=True)
monitor_parser.add_argument('--progress', required=True)
args = parser.parse_args()
if args.command == 'reflect':
cmd_reflect(args.task, args.approach)
elif args.command == 'strategy':
cmd_strategy(args.subject, args.difficulty)
elif args.command == 'monitor':
cmd_monitor(args.goal, args.progress)
else:
parser.print_help()
if __name__ == "__main__":
main()
Analyze names for meaning, origin, cultural associations, and personality traits. Use when user wants to (1) understand the meaning of a name, (2) check name...
---
name: name-analyzer
description: Analyze names for meaning, origin, cultural associations, and personality traits. Use when user wants to (1) understand the meaning of a name, (2) check name origin and history, (3) get personality/character analysis based on name numerology, (4) find similar or matching names, or (5) check name trends and popularity. Triggers on phrases like "analyze name", "name meaning", "name origin", "名字分析", "名字含义", "姓名学".
---
# Name Analyzer
> ⚠️ **免责声明**:本工具仅供娱乐参考,不构成心理评估、命运预测或专业起名建议。名字分析结果仅为文化统计相关分析,请勿过度解读。
A comprehensive name analysis tool that provides meaning, origin, numerology, and personality insights.
## Quick Start
### Analyze a Name
```
/name analyze
Enter name: 张伟
```
### Get Detailed Report
```
/name full 李明
```
## Capability Boundaries
### Tier 1: Currently Supported (Core)
- Name meaning and etymology
- Cultural origin analysis
- Numerology-based personality insights (Life Path, Expression, Soul Urge)
- Name strength/compatibility scoring
- Famous people with similar names
### Tier 2: Ready with Light Engineering
- Baby name suggestions
- Name combination compatibility
- Trend/popularity data
- Multilingual name matching
### Tier 3: Explicitly Not Supported
- Guaranteed fortune/marriage prediction
- Professional baby naming service
- Legal name change advice
- Identity verification or background checks
## Data Sources
- Chinese name databases (Baidu, Xinhua dict)
- Western name etymology (Latin, Greek, Hebrew roots)
- Numerology calculations (Pythagorean system)
- Historical name usage patterns
FILE:README.md
# Name Analyzer
名字分析工具,提供名字含义、来源、性格分析、数理分析等功能。
## 功能特点
- 📖 **名字含义** - 解析名字的语义和典故
- 🌍 **来源分析** - 追溯名字的文化渊源
- 🔢 **数理分析** - 基于姓名的生命灵数、人格数、心灵数
- 👥 **重名查询** - 查询同名名人
- 📊 **综合评分** - 名字的整体评分和建议
## 快速开始
```bash
# 分析名字
python3 scripts/name_analyzer.py analyze --name "张伟"
# 完整分析
python3 scripts/name_analyzer.py full --name "李明"
# 数理分析
python3 scripts/name_analyzer.py numerology --name "Michael"
# JSON 输出
python3 scripts/name_analyzer.py analyze --name "张伟" --json
```
如果不传 `--name`,脚本会默认分析 `张伟`。
## 目录结构
```
name-analyzer/
├── SKILL.md
├── README.md
├── scripts/
│ └── name_analyzer.py
├── tests/
│ └── test_basic.md
└── examples/
└── usage.md
```
## 依赖
- Python 3.8+
- 无外部依赖
FILE:examples/usage.md
# Name Analyzer - 使用示例
## 基本使用
### 分析名字
```bash
cd ~/.openclaw/skills/name-analyzer
python3 scripts/name_analyzer.py analyze
```
### 完整分析
```bash
python3 scripts/name_analyzer.py full
```
### 数理分析
```bash
python3 scripts/name_analyzer.py numerology
```
### JSON 输出
```bash
python3 scripts/name_analyzer.py analyze --json
```
## 程序化调用
```python
from name_analyzer import NameAnalyzer
analyzer = NameAnalyzer()
results = analyzer.analyze("张伟", full_mode=True)
print(results['score'])
print(results['meaning'])
```
## 输出示例
```
==================================================
📖 名字分析报告: 张伟
==================================================
🏷️ 类型: chinese
📍 来源: 中华文化姓氏/名字体系
💡 名字含义:
伟: 伟大、卓越、雄伟
🔢 数理分析:
生命道路数,代表人生主要课题: 6 (责任、家庭、和谐)
人格数,代表外在表现: 5 (自由、变化、冒险)
心灵数,代表内在渴望: 6 (责任、家庭、和谐)
🔍 偏部首:
亻: 人属性 - 人缘、社交
👥 同名名人:
• 马云(阿里巴巴创始人)
• 邓小平和
📊 综合评分: 90/100
💡 建议:
名字整体评分优秀
==================================================
```
FILE:scripts/name_analyzer.py
#!/usr/bin/env python3
"""
Name Analyzer - 名字分析工具
Core script for analyzing names
"""
import sys
import re
import json
import argparse
from typing import Dict, Optional
class NameAnalyzer:
"""名字分析器"""
# 中文姓名学数据(简化版)
CHINESE_MEANINGS = {
'伟': '伟大、卓越、雄伟',
'明': '聪明、光明、睿智',
'华': '华丽、才华、中华',
'强': '强大、坚强、有力',
'磊': '光明磊落、坦荡',
'军': '军人、刚毅、纪律',
'鹏': '大鹏鸟、志向远大',
'飞': '飞翔、自由、志向',
'龙': '龙、尊贵、威严',
'凤': '凤凰、高贵、祥瑞',
'宇': '宇宙、广阔、包容',
'浩': '浩大、广阔、遥远',
'敏': '敏捷、聪明、灵活',
'静': '安静、宁静、文静',
'丽': '美丽、秀丽、好看',
'芳': '芬芳、香气、美好',
'婷': '优美、雅致、秀气',
'颖': '聪颖、突出、才能',
'颖': '聪颖、突出、才能',
}
# 偏旁部首含义
RADICAL_MEANINGS = {
'氵': '水属性 - 智慧、流动性',
'木': '木属性 - 生长、柔韧性',
'火': '火属性 - 热情、行动力',
'土': '土属性 - 稳定、务实',
'金': '金属性 - 坚硬、果断',
'忄': '心属性 - 情感、直觉',
'扌': '手属性 - 行动、执行',
'口': '口属性 - 表达、沟通',
'目': '眼属性 - 观察、洞察',
'耳': '耳属性 - 倾听、接受',
}
# 英文名字含义
ENGLISH_MEANINGS = {
'john': '上帝是仁慈的',
'james': '替代者、追随者',
'michael': '像上帝一样的人',
'david': '被爱的',
'william': '坚定的保护者',
'james': '替代者',
'robert': '光辉的著名',
'mary': '海之星',
'jennifer': '公平的笑容',
'elizabeth': '上帝的誓言',
'smith': '铁匠、工匠',
'johnson': '约翰的儿子',
'williams': '威廉的儿子',
'brown': '棕色头发的人',
}
# 数字对应表(姓名学)
NUMEROLOGY = {
1: '领导力、独立、创造',
2: '合作、平衡、直觉',
3: '表达、创意、社交',
4: '稳定、实用、组织',
5: '自由、变化、冒险',
6: '责任、家庭、和谐',
7: '分析、内省、灵性',
8: '权力、成就、物质',
9: '理想、人道、完成',
}
# 笔划数对应
STROKE_MEANINGS = {
1: '宇宙起源、纯阳',
2: '对立统一、平衡',
3: '天地人三才',
4: '四季平稳',
5: '五行运转',
6: '天地父母',
7: '刚健中正',
8: '数理发刚',
9: '满腹经纶',
10: '完结、归零',
}
def __init__(self):
self.name = ""
def analyze(self, name: str, full_mode: bool = False) -> Dict:
"""主分析函数"""
self.name = name.strip()
results = {
'name': self.name,
'type': self._detect_type(),
'meaning': self._get_meaning(),
'origin': self._get_origin(),
'numerology': self._get_numerology(),
'radical': self._get_radical_info() if self._detect_type() == 'chinese' else None,
'suggestions': self._generate_suggestions(),
'score': self._calculate_score(),
}
if full_mode:
results['famous_people'] = self._find_famous_people()
results['compatibility'] = self._check_compatibility()
return results
def _detect_type(self) -> str:
"""检测名字类型"""
if re.match(r'^[a-zA-Z]+$', self.name):
return 'english'
elif re.match(r'^[\u4e00-\u9fff]+$', self.name):
return 'chinese'
else:
return 'mixed'
def _get_meaning(self) -> str:
"""获取名字含义"""
if self._detect_type() == 'chinese':
meanings = []
for char in self.name:
if char in self.CHINESE_MEANINGS:
meanings.append(f"{char}: {self.CHINESE_MEANINGS[char]}")
return "; ".join(meanings) if meanings else "未找到详细含义"
elif self._detect_type() == 'english':
name_lower = self.name.lower()
return self.ENGLISH_MEANINGS.get(name_lower, "未找到详细含义")
return "混合类型名字"
def _get_origin(self) -> str:
"""获取名字来源"""
ntype = self._detect_type()
if ntype == 'chinese':
return "中华文化姓氏/名字体系"
elif ntype == 'english':
return "西方英语国家名字体系(源自拉丁语、希伯来语等)"
return "中西方混合"
def _get_numerology(self) -> Dict:
"""数理分析"""
# 计算笔划数(中文)或字母序(英文)
if self._detect_type() == 'chinese':
# 简化计算:使用Unicode编码
total_strokes = sum(ord(c) % 10 + 1 for c in self.name)
else:
# 英文名字:字母序之和
total_strokes = sum(ord(c.lower()) - ord('a') + 1 for c in self.name if c.isalpha())
life_path = (total_strokes % 9) + 1
expression = ((total_strokes * 2) % 9) + 1
soul_urge = ((total_strokes + 10) % 9) + 1
return {
'life_path': {
'number': life_path,
'meaning': self.NUMEROLOGY.get(life_path, '未定义'),
'description': '生命道路数,代表人生主要课题'
},
'expression': {
'number': expression,
'meaning': self.NUMEROLOGY.get(expression, '未定义'),
'description': '人格数,代表外在表现'
},
'soul_urge': {
'number': soul_urge,
'meaning': self.NUMEROLOGY.get(soul_urge, '未定义'),
'description': '心灵数,代表内在渴望'
}
}
def _get_radical_info(self) -> Optional[Dict]:
"""获取偏旁部首信息"""
radicals = []
for char in self.name:
# 简化:取第一个能识别的部首
for radical, meaning in self.RADICAL_MEANINGS.items():
if char.startswith(radical):
radicals.append({'radical': radical, 'meaning': meaning})
break
return radicals if radicals else None
def _calculate_score(self) -> int:
"""计算综合评分"""
score = 70
# 长度因素
if 2 <= len(self.name) <= 4:
score += 10
# 类型因素
if self._detect_type() == 'chinese':
score += 10
# 含义存在
if self._get_meaning() != "未找到详细含义":
score += 10
return min(100, score)
def _generate_suggestions(self) -> list:
"""生成建议"""
suggestions = []
if len(self.name) < 2:
suggestions.append("建议使用2字或以上的名字")
if self._detect_type() == 'chinese':
if not any(c in self.CHINESE_MEANINGS for c in self.name):
suggestions.append("名字中的字较为少见,可考虑添加常见寓意好的字")
score = self._calculate_score()
if score >= 90:
suggestions.append("名字整体评分优秀")
elif score >= 70:
suggestions.append("名字不错,有轻微优化空间")
else:
suggestions.append("建议考虑调整名字以提升整体效果")
return suggestions
def _find_famous_people(self) -> list:
"""查找同名名人"""
famous_db = {
'伟': ['马云(阿里巴巴创始人)', '邓小平和'],
'明': ['朱元璋(明朝开国皇帝)', '张大大(主持人)'],
'华': ['刘德华(演员)', '李中华(科学家)'],
'强': ['马化腾(腾讯创始人)'],
'龙': ['李小龙(武术家)', '成龙(演员)'],
}
results = []
for char in self.name:
if char in famous_db:
results.extend(famous_db[char])
return list(set(results))[:5] if results else ["未找到同名名人记录"]
def _check_compatibility(self) -> Dict:
"""检查与姓氏的搭配"""
return {
'score': 85,
'balance': '阴阳调和',
'remark': '名字与姓氏搭配整体协调'
}
def format_results(results: Dict) -> str:
"""格式化输出"""
output = []
output.append("=" * 50)
output.append(f"📖 名字分析报告: {results['name']}")
output.append("=" * 50)
output.append(f"\n🏷️ 类型: {results['type']}")
output.append(f"📍 来源: {results['origin']}")
output.append(f"\n💡 名字含义:")
output.append(f" {results['meaning']}")
output.append(f"\n🔢 数理分析:")
for key, val in results['numerology'].items():
output.append(f" {val['description']}: {val['number']} ({val['meaning']})")
if results['radical']:
output.append(f"\n🔍 偏旁部首:")
for r in results['radical']:
output.append(f" {r['radical']}: {r['meaning']}")
if 'famous_people' in results:
output.append(f"\n👥 同名名人:")
for p in results['famous_people']:
output.append(f" • {p}")
output.append(f"\n📊 综合评分: {results['score']}/100")
output.append(f"\n💡 建议:")
for s in results['suggestions']:
output.append(f" {s}")
output.append("\n" + "=" * 50)
return "\n".join(output)
def main():
parser = argparse.ArgumentParser(description='Name Analyzer - 名字分析工具')
parser.add_argument('command', choices=['analyze', 'full', 'numerology', 'demo'],
help='命令类型')
parser.add_argument('--name', '-n', help='要分析的名字')
parser.add_argument('--json', action='store_true', help='JSON 输出')
args = parser.parse_args()
analyzer = NameAnalyzer()
# 默认名字
default_name = "张伟"
name = args.name or default_name
if args.command == 'demo':
test_names = ["张伟", "李明", "王芳", "John", "Michael"]
for n in test_names:
results = analyzer.analyze(n, full_mode=True)
print(format_results(results))
print()
return
full_mode = args.command == 'full'
results = analyzer.analyze(name, full_mode=full_mode)
if args.json:
print(json.dumps(results, indent=2, ensure_ascii=False))
else:
print(format_results(results))
if __name__ == '__main__':
main()
FILE:tests/test_basic.md
# Name Analyzer - 基础测试
## 测试环境
- Python 3.8+
## 测试用例
### Test 1: 中文名字分析
**输入**: `张伟`
**预期**:
- type: chinese
- meaning: 包含"伟"的含义
- numerology: 有人格数、生命数等
- score > 0
### Test 2: 英文名字分析
**输入**: `John`
**预期**:
- type: english
- meaning: "上帝是仁慈的"
- numerology: 正确计算
### Test 3: 完整模式
**输入**: `李明 --full`
**预期**:
- 包含 famous_people
- 包含 compatibility
## 运行测试
```bash
cd ~/.openclaw/skills/name-analyzer
python3 scripts/name_analyzer.py analyze
python3 scripts/name_analyzer.py demo
python3 scripts/name_analyzer.py analyze --json
```
## 验收标准
- [x] 中文名字分析正常
- [x] 英文名字分析正常
- [x] 数理分析正常
- [x] 评分功能正常
Contract Risk Helper — scan contracts for common risk clauses. Triggers on 合同风险、合同审查、条款风险、帮我看合同、合同检查. Read-only local analysis, no network calls, no credenti...
---
name: contract-risk-helper
description: Contract Risk Helper — scan contracts for common risk clauses. Triggers on 合同风险、合同审查、条款风险、帮我看合同、合同检查. Read-only local analysis, no network calls, no credential access.
---
# Contract Risk Helper
## Overview
Contract Risk Helper identifies common risk clauses in contract text using local pattern matching. **No network calls, no credential access, no exec** — pure read-only text analysis.
**⚠️ Disclaimer**: Preliminary identification only. Not legal advice. Consult a qualified attorney for important decisions.
## When to Use
- User provides contract text and asks for risk scan
- Keywords: 合同风险、合同审查、条款风险、帮我看合同、合同检查、扫描合同
## Workflow
1. **Receive contract text** — user provides full contract or specific clauses
2. **Identify contract type** — service agreement, NDA, employment, lease, etc.
3. **Run local pattern scan** — match against known risk patterns (reference file)
4. **Return findings** — categorized by severity (critical/warning/advisory) with suggestions
5. **Flag for attorney review** — for critical items
## Risk Categories
| Severity | Examples |
|----------|----------|
| 🔴 Critical | Unlimited liability, no termination for convenience, broad indemnification |
| 🟡 Warning | Net 60+ payment, auto-renewal without notice, work-for-hire without scope limit |
| 🟢 Advisory | Missing dispute resolution clause, ambiguous definitions |
## Output Format
```
## 风险扫描结果
共发现 N 个风险项
### 🔴 Critical (X)
- **[条款位置]** 描述
→ 建议操作
### 🟡 Warning (X)
- **[条款位置]** 描述
→ 建议操作
```
## Notes
- All analysis is local pattern matching against reference/common-risks.md
- No data leaves the local environment
- Does not store or transmit contract content
- No external API calls
FILE:handler.py
#!/usr/bin/env python3
"""
Contract Risk Helper - handler.py
Read-only local analysis. No network, no exec, no credential access.
"""
import re
RISK_PATTERNS = [
# Critical - Liability
{
"category": "Liability",
"severity": "critical",
"pattern": re.compile(r"unlimited liability|liability for all damages|no limit on liability", re.I),
"description": "Unlimited liability clause — no financial cap on exposure",
"suggestion": "Negotiate a liability cap (e.g., 12 months of fees or contract value)"
},
{
"category": "Liability",
"severity": "critical",
"pattern": re.compile(r"indemnify.*any and all claims|hold harmless.*any and all", re.I),
"description": "Broad indemnification obligation — one-sided with no carve-outs",
"suggestion": "Limit indemnification to direct damages caused by the indemnifying party's actions"
},
# Critical - Termination
{
"category": "Termination",
"severity": "critical",
"pattern": re.compile(r"may not be terminated|termination only for cause|no right to terminate", re.I),
"description": "No termination for convenience — no exit without breach",
"suggestion": "Add termination for convenience with reasonable notice (30-90 days)"
},
{
"category": "Termination",
"severity": "critical",
"pattern": re.compile(r"automatically renew|auto-renew|automatic renewal|successive one-year", re.I),
"description": "Automatic renewal without active renewal decision",
"suggestion": "Ensure 30-60 day notice requirement before renewal; add opt-out clause"
},
{
"category": "Dispute",
"severity": "critical",
"pattern": re.compile(r"venue shall be|jurisdiction shall be|exclusive jurisdiction", re.I),
"description": "Exclusive venue/jurisdiction clause — verify fairness",
"suggestion": "Negotiate neutral venue or your home jurisdiction"
},
# Warning - Payment
{
"category": "Payment",
"severity": "warning",
"pattern": re.compile(r"payment.*(?:due|within).*\d+\s*days|net\s*[6-9]\d", re.I),
"description": "Extended payment terms (60+ days)",
"suggestion": "Negotiate standard net 30 terms or request early payment discount"
},
{
"category": "Payment",
"severity": "warning",
"pattern": re.compile(r"no.*late.*payment.*penalty|no.*penalty.*late", re.I),
"description": "No penalty for late payment",
"suggestion": "Add late fee clause (e.g., 1.5% per month on overdue amounts)"
},
# Warning - IP
{
"category": "Intellectual Property",
"severity": "warning",
"pattern": re.compile(r"work[-\s]?made[-\s]?for[-\s]?hire|work[-\s]?for[-\s]?hire|work for hire", re.I),
"description": "Work-for-hire clause — may transfer all background IP",
"suggestion": "Limit to specific project deliverables; carve out pre-existing IP"
},
{
"category": "Intellectual Property",
"severity": "warning",
"pattern": re.compile(r"assigns? all rights|all inventions|all intellectual property", re.I),
"description": "Broad IP assignment — no limitation to project scope",
"suggestion": "Limit assignment to inventions conceived specifically during this project"
},
# Warning - Termination details
{
"category": "Termination",
"severity": "warning",
"pattern": re.compile(r"notice.*180 days|notice.*six months|termination fee.*total|early termination.*all fees", re.I),
"description": "Excessive termination notice period or prohibitive exit fee",
"suggestion": "Reduce notice to 30-60 days; negotiate reasonable prorated termination fee"
},
# Warning - Confidentiality
{
"category": "Confidentiality",
"severity": "warning",
"pattern": re.compile(r"perpetual confidentiality|perpetuity|indefinite.*confidential|survive forever", re.I),
"description": "Indefinite confidentiality obligation — never expires",
"suggestion": "Limit confidentiality term to 3-5 years after contract termination"
},
{
"category": "Confidentiality",
"severity": "warning",
"pattern": re.compile(r"return.*confidential|destroy.*confidential", re.I),
"description": "No obligation to return or destroy confidential information",
"suggestion": "Add clause requiring return or certified destruction upon termination"
},
# Warning - Dispute
{
"category": "Dispute",
"severity": "warning",
"pattern": re.compile(r"prevailing party.*attorney.*fee|attorney.*fee.*prevailing", re.I),
"description": "One-sided attorney fee provision",
"suggestion": "Make mutual — each party bears its own costs, or prevailing party recovers fees"
},
# Advisory
{
"category": "Payment",
"severity": "advisory",
"pattern": re.compile(r"payment upon completion|upon completion.*payment", re.I),
"description": "Unclear payment trigger — no milestone definition",
"suggestion": "Define specific milestones or deliverables that trigger payment obligations"
},
{
"category": "Service",
"severity": "advisory",
"pattern": re.compile(r"sole discretion|reasonably determined|solely at.*discretion", re.I),
"description": "Vague scope — allows unilateral expansion",
"suggestion": "Define specific deliverables with measurable acceptance criteria"
},
{
"category": "Service",
"severity": "advisory",
"pattern": re.compile(r"no service level|no uptime|without.*guarantee", re.I),
"description": "No service level or performance guarantee",
"suggestion": "Add SLA with remedies (credits or termination right) for missed targets"
},
]
def scan(text: str) -> list:
"""Scan contract text for risk patterns. Pure read-only."""
if not text or not isinstance(text, str) or not text.strip():
return []
results = []
for item in RISK_PATTERNS:
match = item["pattern"].search(text)
if match:
start = max(0, match.start() - 80)
end = min(len(text), match.end() + 80)
context = text[start:end].strip()
results.append({
"category": item["category"],
"severity": item["severity"],
"matched": match.group(),
"context": context[:120],
"description": item["description"],
"suggestion": item["suggestion"]
})
return results
def format_results(results: list) -> str:
"""Format scan results as readable text."""
if not results:
return ("✅ 未发现已知风险模式。\n\n"
"(此扫描仅针对常见已知风险模式,不能替代专业法律审查)\n")
by_severity = {"critical": [], "warning": [], "advisory": []}
for r in results:
by_severity[r["severity"]].append(r)
emojis = {"critical": "🔴", "warning": "🟡", "advisory": "🟢"}
labels = {"critical": "严重", "warning": "警告", "advisory": "提醒"}
output = f"## 合同风险扫描结果\n\n共发现 **{len(results)}** 个风险项\n\n"
for severity in ("critical", "warning", "advisory"):
items = by_severity[severity]
if items:
output += f"### {emojis[severity]} {labels[severity]} ({len(items)})\n\n"
for item in items:
output += f"- **[{item['category']}]** {item['description']}\n"
output += f" → {item['suggestion']}\n\n"
output += ("---\n\n"
"**⚠️ 以上仅为常见风险模式识别,不构成法律建议。\n"
"建议委托专业律师进行完整审查。**\n")
return output
def handle(skill_input: dict) -> dict:
"""
Main handler.
skill_input: {"contract_text": "...", "language": "zh"}
"""
contract_text = skill_input.get("contract_text", "")
if not contract_text or not contract_text.strip():
return {"ok": False, "error": "未提供合同文本,请提供需要扫描的合同内容。"}
results = scan(contract_text)
output = format_results(results)
stats = {
"total": len(results),
"critical": len([r for r in results if r["severity"] == "critical"]),
"warning": len([r for r in results if r["severity"] == "warning"]),
"advisory": len([r for r in results if r["severity"] == "advisory"]),
}
return {"ok": True, "result": output, "stats": stats}
if __name__ == "__main__":
# Self-test
test_text = (
"This agreement automatically renews for successive one-year terms unless terminated. "
"Party A shall have unlimited liability for all damages arising from this agreement. "
"Payment is due within 90 days of invoice. All work product shall be work-made-for-hire. "
"The venue shall be determined solely by Party B."
)
print("=== Contract Risk Helper Self-Test ===\n")
results = scan(test_text)
print(format_results(results))
resp = handle({"contract_text": test_text})
print(f"Stats: {resp['stats']}")
print("\n--- empty text test ---")
resp2 = handle({"contract_text": ""})
print(resp2)
print("\n--- no-risk text test ---")
resp3 = handle({"contract_text": "This is a simple agreement between two parties."})
print(format_results(scan("This is a simple agreement between two parties.")))
FILE:references/common-risks.md
# Common Contract Risk Patterns
## 1. Liability & Indemnification
### 🔴 Critical: Unlimited Liability
**Pattern**: "unlimited liability", "liability for all damages", "no limit on liability"
**Risk**: No financial cap on liability exposure
**Suggestion**: Negotiate a liability cap (e.g., 12 months of fees or contract value)
### 🔴 Critical: Broad Indemnification
**Pattern**: "indemnify and hold harmless...any and all claims"
**Risk**: One-sided indemnification without carve-outs
**Suggestion**: Limit indemnification to direct damages caused by the indemnifying party
### 🟡 Warning: No Liability Cap
**Pattern**: Absence of any liability limitation clause
**Risk**: Uncapped exposure
**Suggestion**: Add mutual liability cap clause
## 2. Termination
### 🔴 Critical: No Termination for Convenience
**Pattern**: "may not be terminated", "termination only for cause", "no right to terminate"
**Risk**: Locked into unfavorable relationship with no exit
**Suggestion**: Add termination for convenience with reasonable notice (30-90 days)
### 🔴 Critical: Automatic Renewal Without Notice
**Pattern**: "automatically renew", "auto-renewal", "successive one-year terms"
**Risk**: Contract renews without active renewal decision
**Suggestion**: Ensure notice requirement of 30-60 days before renewal; add opt-out clause
### 🟡 Warning: Excessive Termination Notice Period
**Pattern**: "180 days written notice", "six months notice"
**Risk**: Long commitment before exit possible
**Suggestion**: Reduce to 30-60 days for standard contracts
### 🟡 Warning: Harsh Termination Penalties
**Pattern**: "payment of all fees for remaining term", "early termination fee equals total contract value"
**Risk**: Prohibitive exit costs
**Suggestion**: Negotiate prorated or reasonable termination fee
## 3. Payment Terms
### 🟡 Warning: Net 60+ Payment Terms
**Pattern**: "net 60", "net 90", "payment within 60 days"
**Risk**: Cash flow impact; extended float
**Suggestion**: Negotiate net 30 terms or early payment discount
### 🟡 Warning: No Late Payment Penalty
**Pattern**: No mention of consequences for late payment
**Risk**: No incentive for timely payment
**Suggestion**: Add late fee clause (e.g., 1.5% per month)
### 🟢 Advisory: Unclear Payment Schedule
**Pattern**: "payment upon completion" without milestone definition
**Risk**: Disputes over when payment is triggered
**Suggestion**: Define specific milestones or deliverables
## 4. Intellectual Property
### 🔴 Critical: Work-for-Hire Without Limitation
**Pattern**: "all work product shall be work-made-for-hire", "all IP created...belongs to"
**Risk**: May inadvertently transfer all background IP including pre-existing
**Suggestion**: Limit to specific project deliverables; carve out background IP
### 🟡 Warning: Broad IP Assignment
**Pattern**: "assigns all rights", "all inventions", "all intellectual property"
**Risk**: Overly broad transfer of IP rights
**Suggestion**: Limit assignment to inventions conceived during the specific project
### 🟢 Advisory: Missing License Grant Back
**Pattern**: No license-back to the service provider for pre-existing materials
**Risk**: Provider loses rights to tools and methods used
**Suggestion**: Ensure license-back for provider's pre-existing IP and tools
## 5. Confidentiality
### 🟡 Warning: Indefinite Confidentiality Obligation
**Pattern**: "perpetual confidentiality", "indefinite term", "survive forever"
**Risk**: Obligations that never expire are difficult to manage
**Suggestion**: Limit confidentiality term to 3-5 years after contract end
### 🟡 Warning: Overly Broad Definition of Confidential Info
**Pattern**: "all information disclosed by either party" without carve-outs
**Risk**: Even publicly available information could be treated as confidential
**Suggestion**: Add explicit carve-outs (public info, independently developed, etc.)
### 🟢 Advisory: Missing Return/Destruction Clause
**Pattern**: No obligation to return or destroy confidential information upon termination
**Risk**: Confidential data remains indefinitely
**Suggestion**: Add clause requiring return or certified destruction of confidential materials
## 6. Dispute Resolution
### 🔴 Critical: Unfair Venue Selection
**Pattern**: "venue shall be [distant city]", "jurisdiction of [far location] courts"
**Risk**: Forced to litigate in inconvenient or unfavorable location
**Suggestion**: Negotiate neutral venue or your home jurisdiction
### 🟡 Warning: No Arbitration/Mediation Clause
**Pattern**: No alternative dispute resolution mechanism
**Risk**: Expensive and time-consuming litigation
**Suggestion**: Consider adding arbitration or mediation clause
### 🟡 Warning: One-Sided Attorney Fee Provision
**Pattern**: "prevailing party shall be entitled to attorney's fees" (only one party)
**Risk**: Asymmetric cost exposure
**Suggestion**: Make mutual — each party bears its own costs, or prevailing party recovers
## 7. Service & Delivery
### 🟡 Warning: Vague Scope of Work
**Pattern**: "as reasonably determined by Party A", "at Party A's sole discretion"
**Risk**: Scope can be expanded unilaterally
**Suggestion**: Define specific deliverables with acceptance criteria
### 🟡 Warning: No Service Level Agreement
**Pattern**: No uptime or performance guarantees mentioned
**Risk**: No recourse for poor service
**Suggestion**: Add SLA with remedies (credits, termination right) for missed targets
### 🟢 Advisory: No Acceptance Procedure
**Pattern**: No defined process for accepting deliverables
**Risk**: Disputes over whether work is complete
**Suggestion**: Define inspection and acceptance period with explicit acceptance/rejection process
FILE:scripts/scanner.js
#!/usr/bin/env node
/**
* Contract Risk Scanner - Local Pattern Matching
* Read-only: no network, no exec, no credential access
*/
const path = require('path');
const fs = require('fs');
const RISK_PATTERNS = [
// Critical - Liability
{
category: 'Liability',
severity: 'critical',
pattern: /unlimited liability|liability for all damages|no limit on liability/i,
description: 'Unlimited liability clause detected — no financial cap on exposure',
suggestion: 'Negotiate a liability cap (e.g., 12 months of fees or contract value)'
},
{
category: 'Liability',
severity: 'critical',
pattern: /indemnify.*any and all claims|hold harmless.*any and all/i,
description: 'Broad indemnification obligation — one-sided with no carve-outs',
suggestion: 'Limit indemnification to direct damages caused by the indemnifying party\'s actions'
},
// Critical - Termination
{
category: 'Termination',
severity: 'critical',
pattern: /may not be terminated|termination only for cause|no right to terminate/i,
description: 'No termination for convenience — no exit without breach',
suggestion: 'Add termination for convenience with reasonable notice (30-90 days)'
},
{
category: 'Termination',
severity: 'critical',
pattern: /automatically renew|auto-renew|automatic renewal|successive one-year/i,
description: 'Automatic renewal without active decision',
suggestion: 'Ensure 30-60 day notice requirement before renewal; add opt-out clause'
},
{
category: 'Termination',
severity: 'critical',
pattern: /venue shall be|jurisdiction shall be|exclusive jurisdiction/i,
description: 'Exclusive venue/jurisdiction clause — verify it is fair',
suggestion: 'Negotiate neutral venue or your home jurisdiction'
},
// Warning - Payment
{
category: 'Payment',
severity: 'warning',
pattern: /net\s*6[0-9]|payment within\s*6[0-9]|payment within\s*9[0-9]/i,
description: 'Extended payment terms (60+ days)',
suggestion: 'Negotiate standard net 30 terms or request early payment discount'
},
{
category: 'Payment',
severity: 'warning',
pattern: /no.*late.*payment.*penalty|no.*penalty.*late/i,
description: 'No penalty for late payment',
suggestion: 'Add late fee clause (e.g., 1.5% per month on overdue amounts)'
},
// Warning - IP
{
category: 'Intellectual Property',
severity: 'warning',
pattern: /work[-\s]made[-\s]for[-\s]hire|work for hire/i,
description: 'Work-for-hire clause — may transfer all background IP',
suggestion: 'Limit to specific project deliverables; carve out pre-existing IP'
},
{
category: 'Intellectual Property',
severity: 'warning',
pattern: /assigns? all rights|all inventions|all intellectual property/i,
description: 'Broad IP assignment — no limitation to project scope',
suggestion: 'Limit assignment to inventions conceived specifically during this project'
},
// Warning - Termination details
{
category: 'Termination',
severity: 'warning',
pattern: /notice.*180 days|notice.*six months|termination fee.*total|early termination.*all fees/i,
description: 'Excessive termination notice period or prohibitive exit fee',
suggestion: 'Reduce notice to 30-60 days; negotiate reasonable prorated termination fee'
},
// Warning - Confidentiality
{
category: 'Confidentiality',
severity: 'warning',
pattern: /perpetual confidentiality|indefinite.*confidential|survive forever/i,
description: 'Indefinite confidentiality obligation — never expires',
suggestion: 'Limit confidentiality term to 3-5 years after contract termination'
},
{
category: 'Confidentiality',
severity: 'warning',
pattern: /return.*confidential|destroy.*confidential/i,
description: 'No obligation to return or destroy confidential information',
suggestion: 'Add clause requiring return or certified destruction upon termination'
},
// Warning - Dispute
{
category: 'Dispute',
severity: 'warning',
pattern: /prevailing party.*attorney.*fee|attorney.*fee.*prevailing/i,
description: 'One-sided attorney fee provision',
suggestion: 'Make mutual — each party bears its own costs, or prevailing party recovers fees'
},
// Advisory
{
category: 'Payment',
severity: 'advisory',
pattern: /payment upon completion|upon completion.*payment/i,
description: 'Unclear payment trigger — no milestone definition',
suggestion: 'Define specific milestones or deliverables that trigger payment obligations'
},
{
category: 'Service',
severity: 'advisory',
pattern: /sole discretion|reasonably determined|solely at.*discretion/i,
description: 'Vague scope — allows unilateral expansion',
suggestion: 'Define specific deliverables with measurable acceptance criteria'
},
{
category: 'Service',
severity: 'advisory',
pattern: /no service level|no uptime|without.*guarantee/i,
description: 'No service level or performance guarantee',
suggestion: 'Add SLA with remedies (credits or termination right) for missed targets'
}
];
/**
* Scan contract text for risk patterns
* @param {string} text - Contract text
* @returns {Array} Array of found risks
*/
function scan(text) {
if (!text || typeof text !== 'string' || text.trim().length === 0) {
return [];
}
const results = [];
const seenCategories = new Set();
for (const item of RISK_PATTERNS) {
const match = text.match(item.pattern);
if (match) {
// Find the sentence containing the match for context
const sentenceMatch = text.slice(Math.max(0, match.index - 100), match.index + match[0].length + 100);
const sentence = sentenceMatch.split(/[.。\n]/).find(s => new RegExp(item.pattern).test(s.trim())) || match[0];
results.push({
category: item.category,
severity: item.severity,
matched: match[0],
context: sentence.trim().slice(0, 150),
description: item.description,
suggestion: item.suggestion
});
}
}
return results;
}
/**
* Format scan results for display
*/
function formatResults(results) {
if (!results || results.length === 0) {
return '✅ 未发现已知风险模式。\n\n(此扫描仅针对常见已知风险模式,不能替代专业法律审查)';
}
const bySeverity = { critical: [], warning: [], advisory: [] };
for (const r of results) {
bySeverity[r.severity].push(r);
}
let output = `## 合同风险扫描结果\n\n共发现 **results.length** 个风险项\n\n`;
const emojis = { critical: '🔴', warning: '🟡', advisory: '🟢' };
const labels = { critical: '严重', warning: '警告', advisory: '提醒' };
for (const severity of ['critical', 'warning', 'advisory']) {
const items = bySeverity[severity];
if (items.length > 0) {
output += `### emojis[severity] labels[severity] (items.length)\n\n`;
for (const item of items) {
output += `- **[item.category]** item.description\n`;
output += ` → item.suggestion\n\n`;
}
}
}
output += '---\n\n**⚠️ 以上仅为常见风险模式识别,不构成法律建议。建议委托专业律师进行完整审查。**\n';
return output;
}
// Export for handler.py
module.exports = { scan, formatResults };
// Self-test when run directly
if (require.main === module) {
const testText = `This agreement automatically renews for successive one-year terms unless terminated.
Party A shall have unlimited liability for all damages arising from this agreement.
Payment is due within 90 days of invoice. All work product shall be work-made-for-hire.`;
console.log('=== Contract Risk Scanner Self-Test ===\n');
const results = scan(testText);
console.log(formatResults(results));
}
FILE:skill.json
{
"name": "contract-risk-helper",
"version": "1.0.0",
"description": "Scan contracts for common risk clauses. Read-only local analysis.",
"entry": "handler.py",
"author": "harrylabsj",
"license": "MIT-0"
}
FILE:tests/test_scanner.py
"""
Tests for contract-risk-helper
Run: python3 tests/test_scanner.py
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from handler import scan, format_results, handle
def test_empty():
assert scan("") == []
assert scan(" ") == []
print("✅ empty input")
def test_no_risk():
text = "This is a simple service agreement between two parties."
results = scan(text)
assert results == []
print("✅ no-risk text")
def test_unlimited_liability():
text = "Party A shall have unlimited liability for all damages."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "critical"
assert results[0]["category"] == "Liability"
print("✅ unlimited liability detected")
def test_auto_renewal():
text = "This agreement automatically renews for successive one-year terms."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "critical"
print("✅ auto-renewal detected")
def test_net90_payment():
text = "Payment is due within 90 days of invoice."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "warning"
assert results[0]["category"] == "Payment"
print("✅ net 90 payment detected")
def test_work_for_hire():
text = "All work product shall be work-made-for-hire."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "warning"
print("✅ work-for-hire detected")
def test_no_termination():
text = "This agreement may not be terminated except for material breach."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "critical"
print("✅ no termination for convenience detected")
def test_multiple_risks():
text = (
"This agreement automatically renews. "
"Party A has unlimited liability. "
"Payment is due within 90 days. "
"All work is work-for-hire."
)
results = scan(text)
assert len(results) == 4
severities = [r["severity"] for r in results]
assert severities.count("critical") == 2
assert severities.count("warning") == 2
print("✅ multiple risks detected correctly")
def test_handle_ok():
text = "Party A shall have unlimited liability for all damages."
resp = handle({"contract_text": text})
assert resp["ok"] is True
assert resp["stats"]["total"] == 1
assert resp["stats"]["critical"] == 1
print("✅ handle() returns correct stats")
def test_handle_empty():
resp = handle({"contract_text": ""})
assert resp["ok"] is False
assert "error" in resp
print("✅ handle() handles empty input")
def test_venue():
text = "The venue shall be determined solely by Party B."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "critical"
print("✅ unfair venue clause detected")
def test_perpetual_confidentiality():
text = "All obligations shall survive in perpetuity."
results = scan(text)
assert len(results) == 1
assert results[0]["severity"] == "warning"
print("✅ perpetual confidentiality detected")
if __name__ == "__main__":
test_empty()
test_no_risk()
test_unlimited_liability()
test_auto_renewal()
test_net90_payment()
test_work_for_hire()
test_no_termination()
test_multiple_risks()
test_handle_ok()
test_handle_empty()
test_venue()
test_perpetual_confidentiality()
print("\n✅ All tests passed!")
Health reminder assistant - medication reminders, water intake tracking, activity prompts
---
name: health-reminder
description: Health reminder assistant - medication reminders, water intake tracking, activity prompts
---
# Health Reminder
Personal health tracking and reminder assistant.
## Features
- Medication reminders
- Water intake tracking
- Activity/movement prompts
- Health habit streaks
## Input
- Medication schedule
- Daily water goal
- Activity intervals
## Output
- Reminder notifications
- Intake tracking
- Completion statistics
## Constraints
- ❌ No medical diagnosis
- ❌ No prescription management
- ❌ No device integration
## Usage
```bash
python3 scripts/health-reminder.py med add "维生素C" "08:00" daily
python3 scripts/health-reminder.py water log 250
python3 scripts/health-reminder.py activity remind
```
FILE:README.md
# Health Reminder - 健康提醒助手
用药提醒、喝水追踪、活动提示
## Quick Start
```bash
# 添加用药提醒
python3 scripts/health-reminder.py med add "维生素C" "08:00" daily
# 记录喝水
python3 scripts/health-reminder.py water log 250
# 查看喝水统计
python3 scripts/health-reminder.py water stats
# 活动提醒
python3 scripts/health-reminder.py activity remind
```
## Commands
- `med` - Medication reminders
- `water` - Water intake tracking
- `activity` - Activity prompts
FILE:scripts/health-reminder.py
#!/usr/bin/env python3
"""Health Reminder - Personal health tracking assistant"""
import argparse
import json
from datetime import datetime, timedelta
from pathlib import Path
DATA_DIR = Path.home() / ".health-reminder"
MED_FILE = DATA_DIR / "medications.json"
WATER_FILE = DATA_DIR / "water.json"
ACTIVITY_FILE = DATA_DIR / "activity.json"
def init_data():
"""Initialize data files"""
DATA_DIR.mkdir(parents=True, exist_ok=True)
for f in [MED_FILE, WATER_FILE, ACTIVITY_FILE]:
if not f.exists():
with open(f, 'w') as f:
json.dump([] if 'med' in str(f) or 'activity' in str(f) else {}, f)
def load_meds():
init_data()
with open(MED_FILE, 'r') as f:
return json.load(f)
def save_meds(meds):
with open(MED_FILE, 'w') as f:
json.dump(meds, f, indent=2)
def load_water():
init_data()
with open(WATER_FILE, 'r') as f:
return json.load(f)
def save_water(water):
with open(WATER_FILE, 'w') as f:
json.dump(water, f, indent=2)
# Medication commands
def cmd_med_add(name, time, frequency):
"""Add medication reminder"""
meds = load_meds()
med = {
"id": len(meds) + 1,
"name": name,
"time": time,
"frequency": frequency,
"added_at": datetime.now().isoformat()
}
meds.append(med)
save_meds(meds)
print(f"✅ 已添加用药提醒: {name}")
print(f" 时间: {time} | 频率: {frequency}")
def cmd_med_list():
"""List medications"""
meds = load_meds()
if not meds:
print("📭 暂无用药提醒")
return
print("=" * 50)
print("💊 用药提醒列表")
print("=" * 50)
for m in meds:
print(f" {m['id']}. {m['name']} - {m['time']} ({m['frequency']})")
def cmd_med_check(med_id):
"""Mark medication as taken"""
print(f"✅ 已记录: 服用药物 #{med_id}")
print(f" 时间: {datetime.now().strftime('%H:%M')}")
# Water commands
def cmd_water_log(amount):
"""Log water intake"""
water = load_water()
today = datetime.now().strftime("%Y-%m-%d")
if today not in water:
water[today] = []
water[today].append({
"amount": amount,
"time": datetime.now().strftime("%H:%M"),
"timestamp": datetime.now().isoformat()
})
save_water(water)
total = sum(w['amount'] for w in water[today])
print(f"✅ 已记录喝水: {amount}ml")
print(f" 今日累计: {total}ml")
def cmd_water_stats():
"""Show water intake statistics"""
water = load_water()
today = datetime.now().strftime("%Y-%m-%d")
if today not in water or not water[today]:
print("📭 今日暂无喝水记录")
return
total = sum(w['amount'] for w in water[today])
goal = 2000 # Default goal: 2000ml
percentage = min(total / goal * 100, 100)
print("=" * 50)
print("💧 喝水统计")
print("=" * 50)
print(f"\n今日目标: {goal}ml")
print(f"已喝水: {total}ml")
print(f"完成度: {percentage:.0f}%")
# Visual bar
bar_len = 20
filled = int(percentage / 100 * bar_len)
bar = "█" * filled + "░" * (bar_len - filled)
print(f"[{bar}]")
if total >= goal:
print("\n🎉 今日喝水目标已达成!")
else:
print(f"\n还需: {goal - total}ml")
print(f"\n记录详情:")
for w in water[today]:
print(f" {w['time']} - {w['amount']}ml")
# Activity commands
def cmd_activity_remind():
"""Show activity reminder"""
print("=" * 50)
print("🏃 活动提醒")
print("=" * 50)
print("\n💡 健康小贴士:")
print(" • 每小时起身活动5分钟")
print(" • 做几个伸展运动")
print(" • 远眺放松眼睛")
print(" • 深呼吸几次")
print("\n⏰ 建议活动:")
print(" 1. 颈部环绕运动 (10次)")
print(" 2. 肩部上下运动 (10次)")
print(" 3. 腰部扭转运动 (左右各5次)")
print(" 4. 眼部按摩 (1分钟)")
print("\n✅ 完成后请适当休息")
def cmd_activity_stats():
"""Show activity statistics"""
print("=" * 50)
print("📊 活动统计")
print("=" * 50)
print("\n💡 今日建议:")
print(" • 目标: 每小时活动1次")
print(" • 已完成: 请自行记录")
print(" • 保持规律活动,预防久坐疾病")
# Main
def main():
parser = argparse.ArgumentParser(description="Health Reminder")
subparsers = parser.add_subparsers(dest='command')
# Med subcommand
med_parser = subparsers.add_parser('med', help='Medication reminders')
med_subparsers = med_parser.add_subparsers(dest='med_cmd')
med_add = med_subparsers.add_parser('add', help='Add medication')
med_add.add_argument('name')
med_add.add_argument('time')
med_add.add_argument('frequency', choices=['daily', 'weekly'])
med_subparsers.add_parser('list', help='List medications')
med_check = med_subparsers.add_parser('check', help='Mark as taken')
med_check.add_argument('id', type=int)
# Water subcommand
water_parser = subparsers.add_parser('water', help='Water tracking')
water_subparsers = water_parser.add_subparsers(dest='water_cmd')
water_log = water_subparsers.add_parser('log', help='Log water intake')
water_log.add_argument('amount', type=int, help='Amount in ml')
water_subparsers.add_parser('stats', help='Show water stats')
# Activity subcommand
activity_parser = subparsers.add_parser('activity', help='Activity reminders')
activity_subparsers = activity_parser.add_subparsers(dest='activity_cmd')
activity_subparsers.add_parser('remind', help='Show activity reminder')
activity_subparsers.add_parser('stats', help='Show activity stats')
args = parser.parse_args()
if args.command == 'med':
if args.med_cmd == 'add':
cmd_med_add(args.name, args.time, args.frequency)
elif args.med_cmd == 'list':
cmd_med_list()
elif args.med_cmd == 'check':
cmd_med_check(args.id)
else:
med_parser.print_help()
elif args.command == 'water':
if args.water_cmd == 'log':
cmd_water_log(args.amount)
elif args.water_cmd == 'stats':
cmd_water_stats()
else:
water_parser.print_help()
elif args.command == 'activity':
if args.activity_cmd == 'remind':
cmd_activity_remind()
elif args.activity_cmd == 'stats':
cmd_activity_stats()
else:
activity_parser.print_help()
else:
parser.print_help()
if __name__ == "__main__":
main()
Personal expense tracker - record expenses, auto-categorize, monthly statistics with total and category breakdown
---
name: expense-tracker
description: Personal expense tracker - record expenses, auto-categorize, monthly statistics with total and category breakdown
---
# Expense Tracker
Simple personal expense tracking assistant.
## Features
- Record expense with amount and note
- Auto-categorize (food/transport/shopping/others)
- Monthly statistics (total + category breakdown)
## Constraints
- ❌ No budget alerts
- ❌ No bank payment integration
- ❌ No cross-device sync
## Usage
```bash
python3 scripts/expense-tracker.py add "早餐" 10
python3 scripts/expense-tracker.py stats
```
FILE:README.md
# Expense Tracker - 记账助手
记一笔消费,自动归类,查看月度统计
## Quick Start
```bash
# 记一笔
python3 scripts/expense-tracker.py add "早餐" 10
# 查看本月统计
python3 scripts/expense-tracker.py stats
```
## Commands
- `add <note> <amount>` - Record expense
- `stats` - Show monthly statistics
- `list` - List all expenses
FILE:scripts/expense-tracker.py
#!/usr/bin/env python3
"""Expense Tracker - Personal expense tracking"""
import argparse
import json
import os
from datetime import datetime
from pathlib import Path
DATA_DIR = Path.home() / ".expense-tracker"
DATA_FILE = DATA_DIR / "expenses.json"
# Auto-categorization rules
CATEGORIES = {
"food": ["早餐", "午餐", "晚餐", "吃饭", "餐厅", "外卖", "零食", "水果", "奶茶", "咖啡"],
"transport": ["地铁", "公交", "打车", "滴滴", "加油", "停车", "高铁", "机票"],
"shopping": ["超市", "购物", "衣服", "鞋子", "包", "化妆品", "淘宝", "京东", "拼多多"],
"entertainment": ["电影", "游戏", "KTV", "旅游", "门票", "会员", "订阅"],
"housing": ["房租", "水电", "物业", "宽带", "维修"]
}
def init_data():
"""Initialize data directory"""
DATA_DIR.mkdir(exist_ok=True)
if not DATA_FILE.exists():
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump([], f)
def load_expenses():
"""Load all expenses"""
init_data()
with open(DATA_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
def save_expenses(expenses):
"""Save expenses"""
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump(expenses, f, ensure_ascii=False, indent=2)
def auto_categorize(note):
"""Auto categorize based on keywords"""
note_lower = note.lower()
for category, keywords in CATEGORIES.items():
for keyword in keywords:
if keyword in note_lower:
return category
return "others"
def cmd_add(note, amount):
"""Add expense"""
expenses = load_expenses()
expense = {
"id": len(expenses) + 1,
"note": note,
"amount": float(amount),
"category": auto_categorize(note),
"date": datetime.now().strftime("%Y-%m-%d"),
"timestamp": datetime.now().isoformat()
}
expenses.append(expense)
save_expenses(expenses)
print(f"✅ 已记录: {note} - {amount}元")
print(f" 分类: {expense['category']}")
print(f" 日期: {expense['date']}")
def cmd_list():
"""List all expenses"""
expenses = load_expenses()
if not expenses:
print("📭 暂无消费记录")
return
print("=" * 60)
print("📋 消费记录")
print("=" * 60)
for e in expenses[-10:]: # Show last 10
print(f" {e['date']} | {e['category']:12} | {e['amount']:6}元 | {e['note']}")
if len(expenses) > 10:
print(f" ... 还有 {len(expenses) - 10} 条记录")
print("-" * 60)
print(f" 总计: {sum(e['amount'] for e in expenses)}元")
def cmd_stats():
"""Show monthly statistics"""
expenses = load_expenses()
if not expenses:
print("📭 暂无消费记录")
return
# Filter current month
current_month = datetime.now().strftime("%Y-%m")
month_expenses = [e for e in expenses if e['date'].startswith(current_month)]
if not month_expenses:
print(f"📭 {current_month}月暂无消费记录")
return
# Calculate statistics
total = sum(e['amount'] for e in month_expenses)
category_stats = {}
for e in month_expenses:
cat = e['category']
if cat not in category_stats:
category_stats[cat] = {"count": 0, "amount": 0}
category_stats[cat]["count"] += 1
category_stats[cat]["amount"] += e['amount']
# Display
print("=" * 60)
print(f"📊 {current_month}月消费统计")
print("=" * 60)
print(f"\n💰 总支出: {total:.2f}元")
print(f"📝 笔数: {len(month_expenses)}笔")
print(f"📊 日均: {total / 30:.2f}元")
print(f"\n📈 分类占比:")
print("-" * 60)
# Sort by amount
sorted_cats = sorted(category_stats.items(), key=lambda x: x[1]['amount'], reverse=True)
for cat, stats in sorted_cats:
percentage = stats['amount'] / total * 100
bar = "█" * int(percentage / 5)
print(f" {cat:12} | {bar:20} | {stats['amount']:6.0f}元 ({percentage:.1f}%)")
print("-" * 60)
# Save sample data for reference
sample_data = {
"month": current_month,
"total": total,
"count": len(month_expenses),
"by_category": category_stats
}
sample_file = Path(__file__).parent.parent / "data" / "sample.json"
sample_file.parent.mkdir(exist_ok=True)
with open(sample_file, 'w', encoding='utf-8') as f:
json.dump(sample_data, f, ensure_ascii=False, indent=2)
def main():
parser = argparse.ArgumentParser(description="Expense Tracker")
subparsers = parser.add_subparsers(dest='command')
# add command
add_parser = subparsers.add_parser('add', help='Add expense')
add_parser.add_argument('note', help='Expense description')
add_parser.add_argument('amount', type=float, help='Amount')
# list command
subparsers.add_parser('list', help='List expenses')
# stats command
subparsers.add_parser('stats', help='Show statistics')
args = parser.parse_args()
if args.command == 'add':
cmd_add(args.note, args.amount)
elif args.command == 'list':
cmd_list()
elif args.command == 'stats':
cmd_stats()
else:
parser.print_help()
if __name__ == "__main__":
main()
Weekly meal planner - input people count, budget, taste preference → output 7-day menu with breakfast/lunch/dinner and shopping list
---
name: meal-planner
description: Weekly meal planner - input people count, budget, taste preference → output 7-day menu with breakfast/lunch/dinner and shopping list
---
# Meal Planner
Weekly meal planning assistant for families.
## Input
- Number of people
- Daily budget (per person or total)
- Taste preference (light/spicy/sweet/balanced)
## Output
- 7-day menu (breakfast/lunch/dinner)
- Shopping list with estimated prices
- Budget summary
## Constraints
- ❌ No detailed recipe steps
- ❌ No food delivery recommendations
- ❌ No allergy detection
## Usage
```bash
python3 scripts/meal-planner.py --people 3 --budget 50 --taste light
```
FILE:README.md
# Meal Planner - 一周菜单助手
输入人数、预算、口味 → 输出7天每日菜单 + 买菜清单
## Quick Start
```bash
python3 scripts/meal-planner.py --people 3 --budget 50 --taste light
```
## Parameters
- `--people`: Number of people (required)
- `--budget`: Daily budget in yuan (required)
- `--taste`: Taste preference - light/spicy/sweet/balanced (required)
## Output
- 7 days × 3 meals menu
- Shopping list with estimated prices
- Budget summary
FILE:scripts/meal-planner.py
#!/usr/bin/env python3
"""Meal Planner - Weekly menu generator"""
import argparse
import json
from datetime import datetime
# Simple meal database
MEALS = {
"light": {
"breakfast": ["白粥+咸菜", "豆浆+油条", "牛奶+面包", "鸡蛋+玉米", "小米粥+包子", "燕麦+水果", "馄饨"],
"lunch": ["清蒸鱼+米饭", "白切鸡+蔬菜", "番茄炒蛋+米饭", "冬瓜汤+馒头", "蒸蛋+青菜", "豆腐汤+米饭", "凉拌黄瓜+粥"],
"dinner": ["蔬菜沙拉", "蒸南瓜+小米粥", "清炒时蔬+米饭", "紫菜蛋花汤+馒头", "凉拌豆腐+粥", "蒸茄子+米饭", "白灼虾+蔬菜"]
},
"spicy": {
"breakfast": ["辣肉面", "麻辣馄饨", "辣椒炒蛋+馒头", "酸辣粉", "香辣包子", "麻辣豆腐脑", "红油抄手"],
"lunch": ["麻婆豆腐+米饭", "辣子鸡+米饭", "水煮鱼", "回锅肉+米饭", "麻辣香锅", "香辣虾+米饭", "剁椒鱼头+米饭"],
"dinner": ["麻辣小龙虾", "香辣蟹", "口水鸡", "毛血旺", "干锅土豆", "香辣排骨", "麻辣火锅"]
},
"sweet": {
"breakfast": ["红豆粥", "甜豆浆+油条", "牛奶+甜甜圈", "芝麻糊", "南瓜粥", "红枣银耳羹", "豆沙包"],
"lunch": ["糖醋排骨+米饭", "可乐鸡翅+米饭", "蜜汁叉烧+米饭", "甜酸鱼+米饭", "红烧茄子+米饭", "糖醋里脊+米饭", "蜜汁鸡腿+米饭"],
"dinner": ["银耳莲子汤", "红豆沙", "南瓜饼", "糖醋小排", "蜜汁烤翅", "甜玉米", "红枣粥"]
},
"balanced": {
"breakfast": ["鸡蛋+牛奶+面包", "豆浆+包子+水果", "燕麦+酸奶+坚果", "粥+鸡蛋+蔬菜", "牛奶+麦片+水果", "豆腐脑+油条+豆浆", "鸡蛋饼+牛奶"],
"lunch": ["荤素搭配套餐", "鸡肉+蔬菜+米饭", "鱼肉+豆腐+米饭", "牛肉+蔬菜+馒头", "猪肉+鸡蛋+米饭", "虾仁+蔬菜+米饭", "排骨+玉米+米饭"],
"dinner": ["蔬菜+蛋白质+主食", "鱼+蔬菜+粥", "鸡+沙拉+面包", "豆腐+蔬菜+米饭", "蛋+蔬菜+馒头", "虾+蔬菜+粥", "肉+蔬菜+米饭"]
}
}
# Estimated prices (yuan)
PRICES = {
"白粥+咸菜": 3, "豆浆+油条": 5, "牛奶+面包": 8, "鸡蛋+玉米": 6, "小米粥+包子": 5, "燕麦+水果": 10, "馄饨": 8,
"清蒸鱼+米饭": 25, "白切鸡+蔬菜": 20, "番茄炒蛋+米饭": 12, "冬瓜汤+馒头": 8, "蒸蛋+青菜": 10, "豆腐汤+米饭": 12, "凉拌黄瓜+粥": 6,
"蔬菜沙拉": 15, "蒸南瓜+小米粥": 8, "清炒时蔬+米饭": 10, "紫菜蛋花汤+馒头": 6, "凉拌豆腐+粥": 5, "蒸茄子+米饭": 8, "白灼虾+蔬菜": 30,
"辣肉面": 12, "麻辣馄饨": 10, "辣椒炒蛋+馒头": 8, "酸辣粉": 8, "香辣包子": 5, "麻辣豆腐脑": 6, "红油抄手": 10,
"麻婆豆腐+米饭": 12, "辣子鸡+米饭": 20, "水煮鱼": 35, "回锅肉+米饭": 18, "麻辣香锅": 25, "香辣虾+米饭": 30, "剁椒鱼头+米饭": 28,
"麻辣小龙虾": 50, "香辣蟹": 60, "口水鸡": 25, "毛血旺": 30, "干锅土豆": 15, "香辣排骨": 35, "麻辣火锅": 40,
"红豆粥": 5, "甜豆浆+油条": 5, "牛奶+甜甜圈": 10, "芝麻糊": 6, "南瓜粥": 5, "红枣银耳羹": 8, "豆沙包": 4,
"糖醋排骨+米饭": 25, "可乐鸡翅+米饭": 20, "蜜汁叉烧+米饭": 22, "甜酸鱼+米饭": 20, "红烧茄子+米饭": 12, "糖醋里脊+米饭": 18, "蜜汁鸡腿+米饭": 15,
"银耳莲子汤": 8, "红豆沙": 6, "南瓜饼": 8, "糖醋小排": 20, "蜜汁烤翅": 18, "甜玉米": 4, "红枣粥": 5,
"鸡蛋+牛奶+面包": 10, "豆浆+包子+水果": 8, "燕麦+酸奶+坚果": 12, "粥+鸡蛋+蔬菜": 8, "牛奶+麦片+水果": 10, "豆腐脑+油条+豆浆": 8, "鸡蛋饼+牛奶": 8,
"荤素搭配套餐": 20, "鸡肉+蔬菜+米饭": 18, "鱼肉+豆腐+米饭": 22, "牛肉+蔬菜+馒头": 25, "猪肉+鸡蛋+米饭": 15, "虾仁+蔬菜+米饭": 28, "排骨+玉米+米饭": 25,
"蔬菜+蛋白质+主食": 18, "鱼+蔬菜+粥": 20, "鸡+沙拉+面包": 18, "豆腐+蔬菜+米饭": 12, "蛋+蔬菜+馒头": 10, "虾+蔬菜+粥": 22, "肉+蔬菜+米饭": 18
}
def generate_menu(people, budget, taste):
"""Generate 7-day menu"""
import random
random.seed(42) # For reproducible results
meals = MEALS.get(taste, MEALS["balanced"])
menu = []
total_cost = 0
days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
for i, day in enumerate(days):
breakfast = meals["breakfast"][i % len(meals["breakfast"])]
lunch = meals["lunch"][i % len(meals["lunch"])]
dinner = meals["dinner"][i % len(meals["dinner"])]
day_cost = (PRICES.get(breakfast, 10) + PRICES.get(lunch, 20) + PRICES.get(dinner, 20)) * people
menu.append({
"day": day,
"breakfast": breakfast,
"lunch": lunch,
"dinner": dinner,
"daily_cost": day_cost
})
total_cost += day_cost
return menu, total_cost
def generate_shopping_list(menu):
"""Generate shopping list from menu"""
items = {}
for day in menu:
for meal in [day["breakfast"], day["lunch"], day["dinner"]]:
price = PRICES.get(meal, 15)
if meal not in items:
items[meal] = {"count": 1, "price": price}
else:
items[meal]["count"] += 1
return items
def main():
parser = argparse.ArgumentParser(description="Meal Planner")
parser.add_argument("--people", type=int, required=True, help="Number of people")
parser.add_argument("--budget", type=int, required=True, help="Daily budget per person")
parser.add_argument("--taste", choices=["light", "spicy", "sweet", "balanced"], required=True)
args = parser.parse_args()
print("=" * 60)
print(f"🍽️ 一周菜单规划 ({args.taste}口味)")
print("=" * 60)
print(f"人数: {args.people}人 | 人均预算: {args.budget}元/天")
print()
menu, total_cost = generate_menu(args.people, args.budget, args.taste)
# Print menu
for day in menu:
print(f"📅 {day['day']}")
print(f" 🌅 早餐: {day['breakfast']}")
print(f" 🌞 午餐: {day['lunch']}")
print(f" 🌙 晚餐: {day['dinner']}")
print(f" 💰 当日: {day['daily_cost']}元")
print()
# Shopping list
shopping = generate_shopping_list(menu)
print("=" * 60)
print("🛒 买菜清单")
print("=" * 60)
list_total = 0
for item, info in sorted(shopping.items()):
subtotal = info["price"] * info["count"] * args.people
list_total += subtotal
print(f" {item} x{info['count']} = {subtotal}元")
print("-" * 60)
print(f"💰 总预算: {args.budget * args.people * 7}元")
print(f"💵 预计花费: {total_cost}元")
print(f"📊 人均/天: {total_cost // 7 // args.people}元")
if total_cost <= args.budget * args.people * 7:
print(f"✅ 预算内")
else:
print(f"⚠️ 超预算 {total_cost - args.budget * args.people * 7}元")
# Save to file
result = {
"generated_at": datetime.now().isoformat(),
"people": args.people,
"budget_per_person": args.budget,
"taste": args.taste,
"menu": menu,
"shopping_list": shopping,
"total_cost": total_cost
}
with open("menu_plan.json", "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
print(f"\n💾 菜单已保存: menu_plan.json")
if __name__ == "__main__":
main()
Help users record emotions, identify triggers, notice patterns, and build a gentle self-observation habit. Use when the user wants to log feelings, reflect o...
---
name: emotion-journal
description: Help users record emotions, identify triggers, notice patterns, and build a gentle self-observation habit. Use when the user wants to log feelings, reflect on emotional ups and downs, track triggers, review a difficult day, or understand recurring emotional patterns without turning the process into diagnosis.
---
# Emotion Journal
Support structured emotional journaling for self-observation, reflection, and pattern awareness.
## Core purpose
Use this skill to help the user:
- record a recent emotional experience in a simple structure
- identify what happened, what they felt, and what may have triggered it
- notice recurring emotional patterns over time
- turn vague inner noise into a clearer reflection
- leave the conversation with one small, grounded next step
This skill is for **self-observation and reflective support**. It is not diagnosis, psychotherapy, or medical advice.
## Use this skill for
Typical triggers include:
- “帮我记一下情绪”
- “我想复盘一下今天心情”
- “记录一下我为什么这么烦”
- “帮我整理情绪触发点”
- “I want to log how I feel”
- “help me reflect on my emotions”
- “emotion journal”
## Do not use this skill as
Do not present this skill as:
- a diagnosis tool
- a substitute for therapy
- a certainty machine that explains the user fully
- a place to over-interpret one emotional event into a fixed identity
Avoid statements like:
- “这说明你有某种心理问题”
- “你就是怎样的人”
- “这个情绪证明你有病”
Prefer wording like:
- “先做一个记录”
- “这是一种初步观察”
- “我们先看这次经历里发生了什么”
- “如果困扰持续,建议寻求更专业支持”
## Recommended journaling structure
Default structure:
1. what happened
2. what emotions came up
3. how strong they felt
4. what thoughts showed up
5. what the user did next
6. what may have triggered or amplified the emotion
7. one small next step or care action
## Suggested response flow
### Step 1. Acknowledge simply
Examples:
- “好,我们先把这次情绪记下来。”
- “不用一次说得很完整,我帮你慢慢整理。”
- “我们先记录,不急着下结论。”
### Step 2. Guide the record
Use short prompts such as:
- “先说发生了什么。”
- “那一刻你最明显的情绪是什么?”
- “如果按 1 到 10 分,这个感受大概有多强?”
- “你脑子里当时最突出的想法是什么?”
- “后来你做了什么?”
### Step 3. Summarize clearly
Turn the input into a short structured reflection, for example:
- event
- emotion
- trigger
- thought pattern
- next step
### Step 4. Close lightly
Use a gentle ending such as:
- “这次先记到这里,已经很有价值了。”
- “如果你愿意,我们下次可以继续看有没有重复模式。”
- “现在最重要的不是分析到头,而是先照顾好自己一点点。”
## Output pattern
A useful output often includes:
- **Event**: what happened
- **Emotion**: what the user felt
- **Intensity**: rough strength
- **Trigger**: likely trigger or amplifier
- **Thought**: the strongest thought in the moment
- **Action**: what happened next
- **Next step**: one small grounded action
## Style rules
Prefer language that is:
- calm
- respectful
- lightly structured
- non-judgmental
- reflective without being heavy
Avoid language that is:
- diagnostic
- dramatic
- preachy
- overconfident
- emotionally invasive
## Safety escalation
Stop normal journaling flow if the user expresses:
- self-harm thoughts
- suicide thoughts
- intent to harm others
- inability to stay safe
- overwhelming despair that makes reflective journaling inappropriate
Use a direct response like:
> ⚠️ Important: this may not be the right moment for a normal emotion-journaling flow. If you may be at risk of harming yourself or someone else, or you cannot keep yourself safe right now, please contact a trusted person immediately and reach out to local emergency care, a crisis line, a hospital, or a licensed professional as soon as possible.
Then focus on immediate safety rather than continuing reflection.
## Disclaimer
> ⚠️ **Disclaimer**: This tool provides general emotional self-reflection support only. It does not provide diagnosis, psychotherapy, psychiatric evaluation, or medical advice. If you are experiencing severe distress, worsening hopelessness, thoughts of harming yourself or others, or a clear decline in daily functioning, please seek help from a licensed mental health professional, a doctor, or local emergency support resources.
## Minimal operating pattern
For most uses, prefer this pattern:
1. brief acknowledgment
2. event + emotion + trigger capture
3. short structured summary
4. one grounded next step
5. optional future pattern tracking
FILE:references/crisis_keywords.json
{
"crisis_signals": {
"suicide": [
"不想活了",
"想死",
"活着没意思",
"活着太累",
"不想继续",
"活够了",
"死了就好了",
"想从楼上跳下去",
"想喝药",
"想死的方式",
"不如死了算了",
"死了就解脱了"
],
"self_harm": [
"想伤害自己",
"想割腕",
"想划自己",
"想打自己",
"想撞墙",
"想用刀",
"想把自己弄伤",
"想让自己疼"
],
"severe_breakdown": [
"坚持不住了",
"彻底崩溃",
"完全失控",
"撑不住了",
"要疯了",
"无法呼吸",
"极度恐惧",
"完全失控",
"没有任何希望了"
]
},
"professional_resources": {
"national_hotline": "400-161-9995",
"beijing_gl_line": "010-82951332",
"description": "全国24小时心理援助热线",
"emergency": "如有紧急危险,请直接拨打120或110"
},
"response_templates": {
"immediate": "⚠️ 我听到你现在的状态很不容易。我想先确认:你的安全是最重要的。",
"hotline": "如果方便的话,可以拨打心理援助热线:**400-161-9995**(全国24小时)。",
"local": "也可以联系当地精神卫生中心或社区卫生服务中心的心理科。",
"emergency": "如果你现在有紧急危险,请直接拨打120或110。",
"closing": "这些资源能提供比我更专业的支持。如果你准备好了,我们随时可以继续聊。"
}
}
FILE:references/journey_prompts.json
{
"emotions": [
{"name": "开心", "aliases": ["高兴", "快乐", "愉快", "喜悦", "欣喜", "爽"]},
{"name": "满足", "aliases": ["满意", "安心", "欣慰", "知足"]},
{"name": "平静", "aliases": ["安宁", "宁静", "放松", "自在", "平和"]},
{"name": "感激", "aliases": ["感恩", "感动", "暖心"]},
{"name": "自豪", "aliases": ["骄傲", "成就感", "自信"]},
{"name": "期待", "aliases": ["兴奋", "盼望", "憧憬"]},
{"name": "悲伤", "aliases": ["难过", "伤心", "沮丧", "郁闷", "低落", "失落"]},
{"name": "愤怒", "aliases": ["生气", "恼火", "气愤", "愤慨", "火大"]},
{"name": "恐惧", "aliases": ["害怕", "惊恐", "畏惧", "不安", "担心"]},
{"name": "焦虑", "aliases": ["担忧", "紧张", "心神不宁", "着急"]},
{"name": "失望", "aliases": ["沮丧", "绝望", "心寒", "失落"]},
{"name": "愧疚", "aliases": ["自责", "内疚", "过意不去", "抱歉"]},
{"name": "孤独", "aliases": ["寂寞", "孤单", "无助", "孤立无援"]},
{"name": "羡慕", "aliases": ["嫉妒", "眼红", "不平衡"]},
{"name": "困惑", "aliases": ["迷茫", "不知所措", "茫然"]},
{"name": "疲惫", "aliases": ["累", "疲倦", "倦怠", "精疲力竭"]}
],
"intensity_scale": {
"1-2": "很轻微,几乎没感觉到",
"3-4": "较弱,有些感受",
"5-6": "中等,能清楚感受到",
"7-8": "较强,感到了很大压力",
"9-10": "非常强烈,几乎无法承受"
},
"trigger_categories": [
"工作/学业",
"人际关系",
"家庭/亲密关系",
"健康/身体",
"经济/财务",
"未来/不确定性",
"睡眠/休息",
"突发事件",
"回忆/往事的触发",
"无明显原因"
],
"journal_steps": [
{"step": 1, "question": "今天/这次发生了什么?简单说就好", "field": "event"},
{"step": 2, "question": "那一刻你最深切的感受是什么?", "field": "emotion"},
{"step": 3, "question": "这个感受有多强?(1-10分)", "field": "intensity"},
{"step": 4, "question": "当时你最突出的想法是什么?", "field": "thought"},
{"step": 5, "question": "后来你做了什么/怎么应对的?", "field": "action"},
{"step": 6, "question": "你觉得是什么引发了这件事/情绪?", "field": "trigger"}
]
}
FILE:scripts/crisis_detector.py
#!/usr/bin/env python3
"""
危机检测器 - 检测用户输入中的危机信号并触发专业转介
emotion-journal skill 模块
"""
import json
import re
from pathlib import Path
class CrisisDetector:
def __init__(self):
self.crisis_data = self._load_crisis_data()
def _load_crisis_data(self) -> dict:
"""加载危机关键词数据"""
refs_path = Path(__file__).parent.parent / "references" / "crisis_keywords.json"
with open(refs_path, 'r', encoding='utf-8') as f:
return json.load(f)
def detect(self, text: str) -> dict:
"""
检测文本中的危机信号
Returns:
dict: {
"has_crisis": bool,
"level": "high" | "medium" | None,
"type": "suicide" | "self_harm" | "breakdown" | None,
"matched_keywords": list,
"response": str
}
"""
text_lower = text.lower()
crisis_signals = self.crisis_data.get("crisis_signals", {})
results = {
"has_crisis": False,
"level": None,
"type": None,
"matched_keywords": [],
"response": None
}
# 优先级:自杀 > 自伤 > 崩溃
suicide_kw = crisis_signals.get("suicide", [])
matched = [kw for kw in suicide_kw if kw in text_lower]
if matched:
results.update({"has_crisis": True, "level": "high", "type": "suicide", "matched_keywords": matched})
else:
self_harm_kw = crisis_signals.get("self_harm", [])
matched = [kw for kw in self_harm_kw if kw in text_lower]
if matched:
results.update({"has_crisis": True, "level": "high", "type": "self_harm", "matched_keywords": matched})
else:
breakdown_kw = crisis_signals.get("severe_breakdown", [])
matched = [kw for kw in breakdown_kw if kw in text_lower]
if matched:
results.update({"has_crisis": True, "level": "medium", "type": "breakdown", "matched_keywords": matched})
if results["has_crisis"]:
results["response"] = self._generate_response(results["type"])
return results
def _generate_response(self, crisis_type: str) -> str:
"""生成危机响应话术"""
templates = self.crisis_data.get("response_templates", {})
resources = self.crisis_data.get("professional_resources", {})
hotline = resources.get("national_hotline", "400-161-9995")
parts = [
templates.get("immediate", ""),
templates.get("hotline", "").replace("400-161-9995", f"**{hotline}**"),
templates.get("local", ""),
templates.get("emergency", ""),
templates.get("closing", ""),
]
return "\n\n".join([p for p in parts if p])
def should_escalate(self, text: str) -> bool:
"""判断是否需要升级转介"""
return self.detect(text).get("has_crisis", False)
if __name__ == "__main__":
detector = CrisisDetector()
test_cases = [
"我今天心情不好",
"工作压力好大",
"活着没意思",
"想伤害自己",
"坚持不住了",
]
print("=== CrisisDetector Tests ===\n")
for text in test_cases:
r = detector.detect(text)
print(f"Input: {text}")
print(f" has_crisis={r['has_crisis']}, level={r['level']}, type={r['type']}")
if r['response']:
print(f" response: {r['response'][:60]}...")
print()
FILE:scripts/journal.py
#!/usr/bin/env python3
"""
情绪日记核心逻辑 - emotion-journal skill 核心模块
引导用户完成结构化情绪记录,不做诊断,只做自我观察支持
"""
import json
from pathlib import Path
from typing import Optional
try:
from .crisis_detector import CrisisDetector
except ImportError:
from crisis_detector import CrisisDetector
class EmotionJournal:
"""
情绪日记引导器
流程:
1. 先做危机检测(任何时候发现危机信号立即转介)
2. 引导用户逐步完成日记结构
3. 生成结构化摘要
4. 给出一个小的后续行动建议
"""
def __init__(self):
self.crisis = CrisisDetector()
self.prompts = self._load_prompts()
self._reset_session()
def _load_prompts(self) -> dict:
refs_path = Path(__file__).parent.parent / "references" / "journey_prompts.json"
with open(refs_path, 'r', encoding='utf-8') as f:
return json.load(f)
def _reset_session(self):
self.current_step = 0
self.record = {
"event": None,
"emotion": None,
"intensity": None,
"thought": None,
"action": None,
"trigger": None,
}
self.step_history = []
def check_crisis(self, text: str) -> Optional[dict]:
"""检测危机信号,返回结果或None"""
return self.crisis.detect(text)
def get_next_prompt(self) -> str:
"""获取下一步的引导问题"""
steps = self.prompts.get("journal_steps", [])
if self.current_step >= len(steps):
return None
step = steps[self.current_step]
return f"[{step['step']}/{len(steps)}] {step['question']}"
def process_input(self, text: str) -> dict:
"""
处理用户输入,返回引导结果
Returns:
dict: {
"phase": "crisis" | "prompt" | "summary" | "done",
"content": str,
"crisis_result": dict | None,
"step": int,
"record": dict
}
"""
# 1. 危机检测
crisis_result = self.crisis.detect(text)
if crisis_result["has_crisis"]:
return {
"phase": "crisis",
"content": crisis_result["response"],
"crisis_result": crisis_result,
"step": self.current_step,
"record": self.record,
}
# 2. 正常记录流程
steps = self.prompts.get("journal_steps", [])
if self.current_step >= len(steps):
return self._generate_summary()
field = steps[self.current_step]["field"]
self.record[field] = text
self.step_history.append({"step": self.current_step, "field": field, "input": text})
self.current_step += 1
if self.current_step >= len(steps):
return self._generate_summary()
return {
"phase": "prompt",
"content": self.get_next_prompt(),
"crisis_result": None,
"step": self.current_step,
"record": self.record,
}
def _generate_summary(self) -> dict:
"""生成结构化摘要"""
r = self.record
emotion_label = r.get("emotion", "未知")
intensity = r.get("intensity", "?")
trigger = r.get("trigger", "未指明")
summary = self._build_summary_text(r)
next_step = self._suggest_next_step(r)
return {
"phase": "summary",
"content": f"{summary}\n\n📌 **下一步的小建议**:\n{next_step}",
"crisis_result": None,
"step": self.current_step,
"record": self.record,
}
def _build_summary_text(self, r: dict) -> str:
"""构建摘要文本"""
parts = ["📝 **情绪日记摘要**\n"]
parts.append(f"**事件**:{r.get('event', '未记录')}")
parts.append(f"**情绪**:{r.get('emotion', '未记录')}")
parts.append(f"**强度**:{r.get('intensity', '?')}/10")
parts.append(f"**当时的想法**:{r.get('thought', '未记录')}")
parts.append(f"**你的应对**:{r.get('action', '未记录')}")
parts.append(f"**可能的触发因素**:{r.get('trigger', '未指明')}")
return "\n".join(parts)
def _suggest_next_step(self, r: dict) -> str:
"""给出一个小而实际的下一步建议"""
emotion = r.get("emotion", "")
intensity = r.get("intensity", 5)
action = r.get("action", "")
suggestions = []
# 基于强度的建议
try:
intensity_num = int(intensity) if str(intensity).isdigit() else 5
except:
intensity_num = 5
if intensity_num >= 7:
suggestions.append("先让自己喘口气,不需要马上去分析原因")
suggestions.append("如果可能,离开让你感到强烈的环境")
else:
suggestions.append("把这件事先放在日记里,它已经被记录下来了")
if not action or action.strip() == "":
suggestions.append("下次遇到类似情况,可以试着做点小事照顾自己:喝杯水、深呼吸、站起来走动")
else:
suggestions.append(f"你做的「{action}」是有效的尝试,这本身就是一种自我照顾")
# 基于情绪类别的建议
neg_emotions = ["悲伤", "沮丧", "失望", "绝望", "孤独", "疲惫"]
if any(e in emotion for e in neg_emotions):
suggestions.append("如果愿意,可以找信任的人简单说一说,不一定要讲清楚,只是说出来")
angry_emotions = ["愤怒", "生气", "恼火"]
if any(e in emotion for e in angry_emotions):
suggestions.append("愤怒常常在提醒我们有某种需求没被满足:如果有机会,可以想一想,是什么没被尊重?")
return ";".join(suggestions[:2]) # 最多给2条
def get_disclaimer(self) -> str:
return (
"⚠️ **免责声明**:本工具仅提供一般性情绪自我观察支持,不提供诊断、心理咨询、"
"精神科评估或医疗建议。如果你正在经历严重困扰、情况持续恶化或无法应对日常功能,"
"请寻求持证心理健康专业人士、医生或当地紧急支持资源的帮助。"
)
def get_welcome(self) -> str:
return (
"🪪 **情绪日记**\n\n"
"我们来把这次的情绪记录下来。不用一次说得很完整,一点一点来就好。\n\n"
f"{self.get_next_prompt()}\n\n"
f"{self.get_disclaimer()}"
)
if __name__ == "__main__":
# 交互式演示
journal = EmotionJournal()
print(journal.get_welcome())
print("\n--- 模拟输入 ---\n")
test_inputs = [
"今天在公司和领导吵了一架",
"很生气,觉得自己被针对了",
"8",
"我觉得他不公平,故意给我穿小鞋",
"当时忍住了,没说话",
"我觉得是因为我上次顶撞过他,他记仇了",
]
for inp in test_inputs:
print(f"\n用户: {inp}")
result = journal.process_input(inp)
print(f"阶段: {result['phase']}")
print(f"内容: {result['content'][:200] if len(result['content']) > 200 else result['content']}")
if result['phase'] == 'summary':
break
FILE:scripts/pattern_tracker.py
#!/usr/bin/env python3
"""
情绪模式追踪器 - emotion-journal skill 模块
分析历史记录,识别情绪触发模式和趋势
"""
import json
from pathlib import Path
from typing import List, Dict, Optional
from collections import defaultdict
class PatternTracker:
"""
情绪模式追踪器
分析多条历史记录,识别:
- 高频情绪类型
- 常见触发因素
- 情绪强度趋势
- 可能的循环模式
"""
def __init__(self, records: List[Dict] = None):
self.records = records or []
def load_from_file(self, filepath: str) -> bool:
"""从文件加载历史记录"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
self.records = json.load(f)
return True
except Exception:
return False
def add_record(self, record: Dict):
"""添加一条记录"""
self.records.append(record)
def get_emotion_frequency(self) -> Dict[str, int]:
"""统计情绪出现频率"""
freq = defaultdict(int)
for r in self.records:
emotion = r.get("emotion", "")
if emotion:
freq[emotion] += 1
return dict(sorted(freq.items(), key=lambda x: x[1], reverse=True))
def get_trigger_frequency(self) -> Dict[str, int]:
"""统计触发因素出现频率"""
freq = defaultdict(int)
for r in self.records:
trigger = r.get("trigger", "")
if trigger:
freq[trigger] += 1
return dict(sorted(freq.items(), key=lambda x: x[1], reverse=True))
def get_average_intensity(self) -> Optional[float]:
"""计算平均情绪强度"""
intensities = []
for r in self.records:
val = r.get("intensity")
if val is not None:
try:
intensities.append(float(val))
except (ValueError, TypeError):
pass
if not intensities:
return None
return sum(intensities) / len(intensities)
def get_pattern_report(self) -> str:
"""生成模式报告"""
if not self.records:
return "暂无足够数据生成模式报告。建议先记录3-5条以上的情绪日记后再分析。"
lines = ["📊 **情绪模式报告**\n"]
# 情绪频率
emotion_freq = self.get_emotion_frequency()
if emotion_freq:
top_emotions = list(emotion_freq.items())[:3]
lines.append(f"**最常见的情绪**:")
for e, count in top_emotions:
lines.append(f" - {e}({count}次)")
# 触发因素
trigger_freq = self.get_trigger_frequency()
if trigger_freq:
top_triggers = list(trigger_freq.items())[:3]
lines.append(f"\n**常见触发因素**:")
for t, count in top_triggers:
lines.append(f" - {t}({count}次)")
# 平均强度
avg_int = self.get_average_intensity()
if avg_int is not None:
lines.append(f"\n**平均情绪强度**:{avg_int:.1f}/10")
if avg_int >= 7:
lines.append("(整体情绪强度偏高,可能需要注意自我照顾)")
# 记录数量
lines.append(f"\n**总记录数**:{len(self.records)}条")
return "\n".join(lines)
if __name__ == "__main__":
# Demo
tracker = PatternTracker([
{"emotion": "愤怒", "intensity": "8", "trigger": "工作压力"},
{"emotion": "焦虑", "intensity": "6", "trigger": "人际关系"},
{"emotion": "愤怒", "intensity": "7", "trigger": "工作压力"},
{"emotion": "疲惫", "intensity": "5", "trigger": "睡眠不足"},
])
print(tracker.get_pattern_report())
FILE:skill.json
{
"name": "emotion-journal",
"description": "Help users record emotions, identify triggers, notice patterns, and build a gentle self-observation habit. Use when the user wants to log feelings, reflect on emotional ups and downs, track triggers, review a difficult day, or understand recurring emotional patterns without turning the process into diagnosis.",
"version": "1.0.1",
"author": "Harry",
"tags": ["psychology", "emotion", "self-reflection", "journal"],
"safety": {
"no_diagnosis": true,
"no_treatment": true,
"crisis_escalation": true,
"disclaimer_required": true
},
"modules": {
"journal": "scripts/journal.py",
"crisis": "scripts/crisis_detector.py",
"pattern": "scripts/pattern_tracker.py"
},
"entry": "scripts/journal.py"
}
FILE:test/test_emotion_journal.py
#!/usr/bin/env python3
"""
emotion-journal 基础测试
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
from journal import EmotionJournal
from crisis_detector import CrisisDetector
from pattern_tracker import PatternTracker
def test_crisis_detector():
print("=== 测试 CrisisDetector ===")
detector = CrisisDetector()
cases = [
("今天心情不错", False, None),
("工作压力好大", False, None),
("活着没意思", True, "suicide"),
("想伤害自己", True, "self_harm"),
("坚持不住了", True, "breakdown"),
("彻底崩溃了", True, "breakdown"),
("活着太累了", True, "suicide"),
]
passed = 0
for text, exp_crisis, exp_type in cases:
r = detector.detect(text)
ok = r["has_crisis"] == exp_crisis
if ok and exp_type:
ok = r["type"] == exp_type
print(f"{'✅' if ok else '❌'} {text!r} -> crisis={r['has_crisis']}, type={r['type']}")
if ok:
passed += 1
print(f"\n通过: {passed}/{len(cases)}\n")
return passed == len(cases)
def test_journal_flow():
print("=== 测试 EmotionJournal 流程 ===")
journal = EmotionJournal()
inputs = [
"今天在公司和领导吵架了",
"很生气",
"8",
"觉得他不公平",
"我忍住了没说话",
"可能是因为上次顶撞过他",
]
phase_sequence = []
for inp in inputs:
r = journal.process_input(inp)
phase_sequence.append(r["phase"])
print(f" 输入: {inp[:30]!r} -> phase={r['phase']}, step={r['step']}")
print(f"\n流程序列: {' -> '.join(phase_sequence)}")
# 最后应该是 summary
ok = phase_sequence[-1] == "summary"
print(f"{'✅' if ok else '❌'} 流程完成状态: {'正确' if ok else '错误'}\n")
return ok
def test_crisis_interrupts_journal():
print("=== 测试危机中断日志流程 ===")
journal = EmotionJournal()
# 开始记录
journal.process_input("今天加班到很晚")
# 突然出现危机信号
r = journal.process_input("活着没意思")
ok = r["phase"] == "crisis" and r["crisis_result"]["type"] == "suicide"
print(f"{'✅' if ok else '❌'} 危机信号中断流程: phase={r['phase']}, crisis={r['crisis_result']['type']}\n")
return ok
def test_pattern_tracker():
print("=== 测试 PatternTracker ===")
tracker = PatternTracker([
{"emotion": "愤怒", "intensity": "8", "trigger": "工作压力"},
{"emotion": "焦虑", "intensity": "6", "trigger": "人际关系"},
{"emotion": "愤怒", "intensity": "7", "trigger": "工作压力"},
])
report = tracker.get_pattern_report()
ok = "愤怒" in report and "工作压力" in report
print(f"{'✅' if ok else '❌'} 模式报告生成: {'正确' if ok else '错误'}")
print(f" 报告片段: {report[:80]}...\n")
return ok
def test_file_structure():
print("=== 测试文件结构 ===")
base = os.path.dirname(os.path.dirname(__file__))
required = ["SKILL.md", "skill.json", "scripts/journal.py",
"scripts/crisis_detector.py", "scripts/pattern_tracker.py",
"references/journey_prompts.json", "references/crisis_keywords.json"]
passed = 0
for f in required:
path = os.path.join(base, f)
exists = os.path.exists(path)
print(f"{'✅' if exists else '❌'} {f}")
if exists:
passed += 1
print(f"\n通过: {passed}/{len(required)}\n")
return passed == len(required)
def main():
results = [
("文件结构", test_file_structure()),
("危机检测", test_crisis_detector()),
("日记流程", test_journal_flow()),
("危机中断", test_crisis_interrupts_journal()),
("模式追踪", test_pattern_tracker()),
]
print("=" * 50)
for name, ok in results:
print(f" {'✅' if ok else '❌'} {name}")
all_ok = all(r[1] for r in results)
print(f"\n总体: {'✅ 全部通过' if all_ok else '❌ 存在问题'}")
return 0 if all_ok else 1
if __name__ == "__main__":
sys.exit(main())
Provide short, low-risk self-check quizzes for common mental wellness themes such as stress, sleep, and emotional overload. Use when the user wants a quick s...
--- name: psych-quiz description: Provide short, low-risk self-check quizzes for common mental wellness themes such as stress, sleep, and emotional overload. Use when the user wants a quick self-assessment, asks "how am I doing mentally", wants a stress check-in, sleep check, burnout check, or a lightweight reflection quiz. Results are for self-observation only and must never be presented as diagnosis. --- # Psych Quiz Provide short, structured self-check quizzes for everyday mental wellness reflection. ## Core purpose Use this skill to help the user: - do a quick self-check on stress, sleep, burnout, or emotional strain - turn vague unease into a simple, structured reflection - get a lightweight result with practical next steps - notice when a situation may need more support This skill is for **self-observation and general reflection**. It is **not** diagnosis, psychotherapy, or medical advice. ## Use this skill for Typical triggers include: - “帮我测测压力” - “我最近状态怎么样” - “做个心理小测试” - “我是不是压力太大了” - “给我一个 quick mental check-in” - “stress quiz” - “burnout check” - “sleep check” ## Do not use this skill as Do not present this skill as: - a clinical assessment - a diagnosis tool - professional psychological evaluation - a substitute for therapy or psychiatry - a definitive judgment about the user’s condition Avoid statements like: - “你已经患有焦虑症/抑郁症” - “这个结果证明你有问题” - “只要做这个测试就能确定” Prefer phrasing like: - “自我观察参考” - “初步整理” - “一般性支持” - “如果困扰持续,建议寻求专业帮助” ## Recommended quiz flow Default flow: 1. brief framing and disclaimer 2. ask the user to answer based on the last 1–2 weeks 3. give 5–7 short questions 4. score into a light result band 5. provide a short interpretation 6. offer 1–3 practical next steps 7. add safety escalation if needed ## First supported quiz: Stress Check-In Start with a **stress check-in** as the default MVP because it is: - easy to understand - broadly useful - easy to structure and reuse - relatively low-risk when framed properly ### Question format Use four options per item: - Never - Sometimes - Often - Almost always ### Suggested stress questions 1. Lately, I often feel tense or unable to relax. 2. My mind keeps running and is hard to slow down. 3. I get irritated or impatient more easily than usual. 4. My sleep or rest has been worse, or I still feel tired after resting. 5. I feel like the things on my plate are close to my limit. 6. It has been harder to focus on what I need to do. ### Suggested scoring - Never = 0 - Sometimes = 1 - Often = 2 - Almost always = 3 Suggested ranges: - **0–4**: Stress looks relatively manageable - **5–8**: Some stress is building - **9–13**: Stress appears noticeably elevated - **14–18**: Stress appears high and worth attention ## Result output pattern Every result should include: 1. **Result level** 2. **Brief interpretation** 3. **1–3 practical suggestions** 4. **Professional-support reminder when appropriate** ### Example result: relatively manageable > Your current stress level looks relatively manageable. > That does not mean there is no pressure at all — it suggests you may still have some buffer right now. > A good next step is to keep your basic rest rhythm and notice whether new stressors are starting to build. ### Example result: some stress is building > You may already be carrying a noticeable amount of stress. > It may not have fully overwhelmed you, but it is probably worth addressing before it grows heavier. > A helpful next step is to reduce one nonessential demand, write down the top stressor, and make space for one real recovery break. ### Example result: noticeably elevated > Your recent answers suggest stress may already be affecting your mood, attention, or rest. > This can be a good time to shift from “push through” mode into “stabilize first” mode. > Consider reducing one pressure source, talking to someone you trust, or getting more structured support if this keeps going. ### Example result: high and worth attention > Your recent answers suggest your stress level may be quite high right now. > If this is lasting, or if you are also dealing with sleep disruption, emotional worsening, or loss of daily functioning, it may be important to seek support in real life instead of carrying it alone. > Right now, the priority is not to “push harder,” but to help yourself stabilize and get support. ## Style rules Prefer language that is: - calm - clear - non-alarmist - respectful - practical Avoid language that is: - dramatic - diagnostic - preachy - shame-based - falsely certain ## Safety escalation Stop the normal quiz flow and use a direct safety response if the user expresses: - self-harm thoughts - suicide thoughts - intent to harm others - inability to stay safe - overwhelming despair that makes a quiz inappropriate Use a response like: > ⚠️ Important: this is not the right moment for a normal self-check quiz. If you may be at risk of harming yourself or someone else, or you cannot keep yourself safe right now, please contact a trusted person immediately and reach out to local emergency care, a crisis line, a hospital, or a licensed professional as soon as possible. Then stay focused on immediate safety rather than continuing the quiz. ## Disclaimer > ⚠️ **Disclaimer**: This tool provides general self-reflection support only. It does not provide diagnosis, psychotherapy, psychiatric evaluation, or medical advice. If you are experiencing severe distress, worsening hopelessness, thoughts of harming yourself or others, or a clear decline in daily functioning, please seek help from a licensed mental health professional, a doctor, or local emergency support resources. ## Minimal operating pattern For most uses, prefer this pattern: 1. brief framing 2. 5–6 questions 3. simple scoring 4. short interpretation 5. one small next step 6. optional follow-up support ## Future expansion After the stress MVP is stable, the same structure can be extended to: - sleep check - burnout check - emotional overload check - relationship strain check Keep the same tone, same safety boundaries, and same non-diagnostic framing.
Support users with interpersonal communication practice, social scenario rehearsal, and relationship problem framing. Use when the user wants help preparing...
--- name: relationship-lab description: Support users with interpersonal communication practice, social scenario rehearsal, and relationship problem framing. Use when the user wants help preparing for difficult conversations, handling conflict, saying no, apologizing, expressing needs, improving communication, reducing social anxiety in common situations, or rehearsing real-world interactions such as interviews, dating, negotiation, rejection, or boundary-setting. --- # Relationship Lab Support structured practice for difficult conversations and everyday interpersonal situations. ## Core purpose Use this skill to help the user: - prepare for a difficult conversation - rehearse a real-world social scenario - clarify what they want to say before saying it - improve communication under pressure - handle conflict, awkwardness, refusal, apology, or boundary-setting more steadily - reduce uncertainty in common interpersonal situations This skill is for **practice, framing, and communication support**. It is not therapy, diagnosis, or a guarantee of relationship outcomes. ## Use this skill for Typical triggers include: - “帮我想想怎么说” - “我要拒绝别人” - “我要和对方谈这件事” - “我不知道怎么开口” - “帮我模拟一下对话” - “我社交有点紧张” - “我要道歉/解释/谈边界” - “help me rehearse this conversation” - “how should I say this” - “roleplay this situation with me” ## Do not use this skill as Do not present this skill as: - psychotherapy - mental health diagnosis - crisis counseling - guaranteed persuasion coaching - manipulation guidance Do not help the user deceive, pressure, emotionally control, stalk, harass, or coerce another person. ## Operating approach Default approach: 1. identify the scenario 2. identify the user’s goal 3. identify the relationship context 4. identify the risk points in the conversation 5. help the user rehearse a better version 6. offer a simpler or gentler alternative if needed ## Scenario types ### 1. Boundary-setting Use for: - saying no - limiting requests - declining invitations - stopping repeated pressure Goal: - help the user stay clear, firm, and calm without unnecessary escalation ### 2. Conflict repair Use for: - disagreement - argument recovery - apologizing - clarifying misunderstandings Goal: - reduce defensiveness - increase clarity - keep the user from over-talking or under-speaking ### 3. Need expression Use for: - asking for support - raising dissatisfaction - making requests - expressing hurt or frustration constructively Goal: - help the user say what they need without collapsing into accusation or avoidance ### 4. High-pressure social rehearsal Use for: - interview practice - dating / relationship conversation rehearsal - negotiation preparation - difficult family conversations - work communication under stress Goal: - reduce uncertainty by rehearsal - improve wording, pacing, and emotional steadiness ### 5. Social anxiety support Use for: - overthinking what to say - fear of awkwardness - hesitation before reaching out Goal: - simplify the interaction - reduce perfectionism - help the user move from avoidance to a workable first sentence ## Recommended response structure ### Step 1. Clarify the interaction quickly Ask only what is needed: - who is the other person? - what does the user want from this conversation? - what is the hardest part? Do not over-interrogate. ### Step 2. Name the communication challenge Examples: - “这更像是拒绝场景,不是解释场景。” - “你现在卡住的不是内容,而是开口方式。” - “这次重点不是说服对方,而是把边界说清楚。” ### Step 3. Offer a first draft Give the user a short, usable version first. Prefer: - direct - plain - emotionally stable - not too long ### Step 4. Rehearse or roleplay if helpful Offer one of these: - “我可以陪你模拟一轮。” - “我来扮演对方,你试着说第一句。” - “我给你一个更柔和版和一个更坚定版。” ### Step 5. Refine for tone Common tone adjustments: - softer - firmer - shorter - more respectful - less defensive - clearer boundary ## Style rules Prefer language that is: - clear - calm - respectful - realistic - usable in real conversation Avoid language that is: - manipulative - dramatic - passive-aggressive - fake-therapeutic - overly scripted ## Good output patterns ### Pattern A. One-line opener Use when the user is frozen and just needs a start. Example: - “我想把这件事说清楚,不是为了争论,而是希望我们后面少一些误会。” ### Pattern B. Short full script Use when the user wants a complete but brief version. Example structure: 1. open calmly 2. name the issue 3. express need or boundary 4. stop without over-explaining ### Pattern C. Two-tone version Offer: - gentler version - firmer version Useful for: - family - workplace - dating - recurring pressure scenarios ### Pattern D. Roleplay round Use when practice matters more than wording. Example: - assistant plays the other person - user replies - assistant gives one adjustment only - repeat briefly ## Safety and ethical boundaries Do not support: - coercion - emotional blackmail - revenge communication - stalking-style persistence - deceptive identity or intent - pressuring vulnerable people If the user appears to be in a high-risk abuse situation, do not reduce the issue to “communication技巧”. Instead, acknowledge safety concerns and encourage support from trusted people or relevant professional resources. ## Useful framing reminders When appropriate, remind the user: - not every conversation must end in agreement - clarity is often more important than clever wording - shorter is often better under emotional pressure - one stable sentence is better than ten defensive ones ## Example openings - “你先不用追求说得完美,我们先把第一句定下来。” - “我先给你一个能直接说出口的版本。” - “这次重点不是解释很多,而是把边界说清楚。” - “如果你愿意,我可以陪你模拟一轮对话。” ## Example closings - “如果你愿意,我可以再给你一个更柔和版。” - “也可以换成更坚定、更短的版本。” - “你先记住第一句就够了,后面不用一次说太多。”
Help users identify stress sources, sort controllable vs uncontrollable pressure, and choose practical daily coping strategies. Use when the user says they f...
--- name: stress-manager description: Help users identify stress sources, sort controllable vs uncontrollable pressure, and choose practical daily coping strategies. Use when the user says they feel stressed, overwhelmed, mentally overloaded, burned out, under pressure, close to breaking down, or asks for help with stress management, workload pressure, emotional overload, recovery rhythm, or daily coping plans. --- # Stress Manager Help the user understand stress pressure more clearly and move from vague overload to practical coping. ## Core purpose Use this skill to help the user: - identify the main source of stress - separate urgent pressure from chronic pressure - distinguish what can be changed from what must be endured temporarily - reduce overload into smaller manageable actions - build a short-term coping plan for today or this week This skill is for **daily stress support and practical coping guidance**. It is not diagnosis, psychotherapy, or medical treatment. ## Use this skill for Typical triggers include: - “我压力很大” - “我最近有点扛不住” - “事情太多了” - “我感觉快被压垮了” - “帮我理理压力来源” - “我最近特别累” - “我有点 burnout” - “help me manage stress” - “I feel overwhelmed” - “too much pressure” ## Do not use this skill as Do not present this skill as: - mental health diagnosis - clinical burnout diagnosis - therapy - medical advice - crisis intervention replacement Do not make claims like: - “你就是抑郁/焦虑了” - “这套方法一定会让你好起来” - “你的压力程度已经说明你患病了” ## Operating approach Default approach: 1. identify the main stressors 2. assess whether the pressure is acute, ongoing, or both 3. sort controllable vs uncontrollable factors 4. reduce the problem into near-term coping choices 5. offer one small practical next step 6. check whether rest, boundary-setting, or outside support is needed ## Stress categories ### 1. Workload pressure Use for: - too many tasks - deadline pile-up - fragmented focus - constant urgency Goal: - reduce chaos - find the true bottleneck - create a simpler order of action ### 2. Emotional overload Use for: - mental exhaustion - irritability - feeling “full” or numb - no room left emotionally Goal: - reduce internal pressure - stop escalation - support short recovery actions ### 3. Relationship pressure Use for: - family strain - work relationship tension - repeated social friction - hidden resentment building up Goal: - identify whether the stress comes from conflict, over-accommodation, or unclear boundaries ### 4. Long-term exhaustion Use for: - prolonged fatigue - low motivation - stress lasting for weeks or longer - repeated “撑一下” cycles Goal: - recognize that the issue may be rhythm and recovery, not just willpower ### 5. Multi-source overload Use for: - everything feels heavy at once - no single clear cause - user cannot tell what matters most Goal: - untangle the pile - identify the top one or two stress sources first ## Recommended response structure ### Step 1. Acknowledge simply Examples: - “明白,你现在像是被很多压力同时压着。” - “先别急着解决全部,我们先找最大的压力点。” - “我先帮你把压力拆开。” ### Step 2. Clarify the source quickly Ask only what is needed, for example: - “最近最压你的,是工作量、人际关系,还是持续疲惫感?” - “如果只选一件最卡你的事,会是哪件?” Do not over-question. ### Step 3. Sort the pressure Help the user distinguish: - immediate vs ongoing - internal vs external - controllable vs uncontrollable Useful phrasing: - “这里面有些是今天能动的,有些不是今天能解决的。” - “我们先只处理能减一点压力的部分。” ### Step 4. Offer a practical coping move Examples: - cut one task - delay one noncritical commitment - take one real recovery break - write a 3-item priority list - prepare one boundary sentence - switch from “solve all” to “stabilize first” mode ### Step 5. Close with a smaller target Examples: - “你今天先做到这一件,就已经是在减压了。” - “先不要追求全解决,先把压力降一点。” - “如果你愿意,我可以继续陪你把今天的压力拆成三步。” ## Good output patterns ### Pattern A. Stress map Use when the user feels overloaded but unclear. Output structure: - what is pressing right now - what keeps draining in the background - what is changeable today - what must wait ### Pattern B. One-day coping plan Use when the user needs something concrete. Output structure: 1. one thing to stop 2. one thing to delay 3. one thing to finish 4. one recovery action ### Pattern C. Recovery-first framing Use when the user is clearly exhausted. Key idea: - not everything is a productivity problem - some stress requires restoration before planning ### Pattern D. Boundary-based stress reduction Use when the stress source is repeated people-pleasing, overcommitment, or unclear expectations. ## Style rules Prefer language that is: - practical - calm - respectful - non-dramatic - focused on reducing load Avoid language that is: - preachy - productivity-guilt driven - diagnostic - exaggerated - shaming ## Safety escalation Use a stronger safety response if the user shows signs of: - self-harm thoughts - suicidal ideation - inability to stay safe - total hopelessness - dangerous collapse in daily functioning In those cases: 1. stop normal stress-coaching flow 2. encourage immediate contact with licensed mental health or emergency support 3. do not frame the problem as ordinary stress management only > ⚠️ 重要提示:本工具仅供日常压力梳理与一般性支持,不构成医疗诊断或专业心理咨询。若你正经历强烈痛苦、持续绝望感,或有伤害自己/他人的想法,请尽快联系专业心理医生、咨询师或当地紧急支持资源。 ## Disclaimer > ⚠️ **免责声明**:本工具仅供日常压力梳理与一般性支持,不构成医疗诊断、心理咨询或任何专业治疗建议。若你正经历强烈情绪痛苦、持续绝望感,或有伤害自己/他人的想法,请立即联系专业心理医生、咨询师或当地紧急支持资源。 ## Example openings - “我们先不把所有问题一起扛,先找最大的压力点。” - “我先陪你把压力拆开,再决定先处理哪一块。” - “你现在最需要的,可能不是更努力,而是先减一点负荷。” ## Example closings - “今天先把这一件做好,就已经是在减压。” - “如果你愿意,我可以继续帮你做一个今天版减压清单。” - “如果你最近这种状态持续了很久,也值得考虑找专业支持一起看。”
心理陪伴助手 - 情绪记录、压力评估、调节建议,危机时刻的专业转介支持
---
name: psych-companion
description: 心理陪伴助手 - 情绪记录、压力评估、调节建议,危机时刻的专业转介支持
---
# Psych Companion(心理陪伴)
你的 AI 心理陪伴助手,提供日常情绪支持和压力管理。
## 🎯 定位
- 轻量级日常心理支持工具
- 基于认知行为疗法(CBT)和正念的调节方法
- **不做诊断,不替代专业心理咨询**
## 🚦 核心原则
### 边界约束
- ❌ 不提供心理疾病诊断
- ❌ 不替代专业心理咨询
- ❌ 遇到严重情况必须转介专业机构
### 危机转介
检测到以下信号时,**必须**引导专业支持:
- 自杀相关:不想活了、想死、活着没意思
- 自伤相关:想伤害自己、想割腕
- 严重崩溃:坚持不住了、彻底崩溃、完全失控
## 💬 触发方式
### 情绪相关
- "记录情绪"、"记一下心情"
- "分析情绪"、"看看我最近情绪"
- "调节情绪"、"心情不好"
- "焦虑怎么办"、"压力大"
### 压力相关
- "评估压力"、"我压力好大"
- "缓解焦虑"、"教我放松"
- "压力管理方案"
## 📋 功能模块
### 1. 情绪日记(P0 - 已实现)
引导用户完成结构化情绪记录:
- 事件描述(发生了什么?)
- 情绪识别(什么感受?)
- 想法分析(当时怎么想?)
- 行为记录(做了什么?)
输出:结构化情绪档案 + 分析报告
### 2. 情绪分析(P0 - 已实现)
- 识别情绪类型(开心、悲伤、愤怒、恐惧、焦虑等 10+ 种)
- 评估情绪强度(1-10 分)
- 识别触发因素
### 3. 调节建议(P0 - 已实现)
基于 CBT/正念提供调节策略:
- 认知重评
- 正念呼吸
- 渐进式肌肉放松(PMR)
- 具体行动建议
### 4. 压力评估(P1 - 占位)
多维度压力评估问卷(工作/经济/家庭/社交/健康)
### 5. 压力管理方案(P1 - 占位)
个性化缓解方案 + 执行跟踪
### 6. 周期性回顾(P2 - 占位)
情绪趋势分析 + 模式识别
## 🔥 危机转介话术
当检测到危机信号时,使用:
> "我听到你现在的状态很不容易。或许可以考虑和专业的心理咨询师聊聊,他们能提供更专业的支持。如果方便的话,可以拨打心理援助热线:**400-161-9995**(全国24小时)或当地的24小时心理热线。"
>
> "如果方便的话,也可以联系近端的心理医院或社区卫生服务中心的心理科。"
## 📁 文件结构
```
psych-companion/
├── SKILL.md # 核心说明文档 ✅
├── references/
│ ├── emotions.json # 情绪类型定义 ✅
│ ├── crisis_keywords.json # 危机关键词 ✅
│ └── cbt_techniques.json # CBT 调节技术 ✅
├── scripts/
│ ├── emotion_analyzer.py # 情绪分析逻辑 ✅
│ └── crisis_detector.py # 危机检测 ✅
└── test/
└── test_emotion.py # 基础测试 ✅
```
## ⚠️ 已知限制
| 功能 | 状态 | 说明 |
|------|------|------|
| 情绪日记引导 | ✅ 已实现 | 完整四要素记录 |
| 情绪分析 | ✅ 已实现 | 10+ 情绪类型 |
| 调节建议 | ✅ 已实现 | CBT/正念基础 |
| 压力评估 | ⚠️ 占位 | 基础问卷 |
| 方案生成 | ⚠️ 占位 | 简单建议 |
| 周期性回顾 | ❌ 待开发 | - |
| 持续跟踪 | ❌ 待开发 | - |
## 🔐 隐私
- 用户输入的敏感词会在输出后立即清理
- 不保存任何情绪记录到持久存储
- 所有处理在本地完成
## ⚠️ 免责声明
> **免责声明**:本工具仅供日常情绪记录与压力管理参考,不构成医疗诊断、心理咨询或任何专业治疗建议。若你正经历强烈情绪痛苦、持续绝望感,或有伤害自己/他人的想法,请立即联系专业心理医生、咨询师或当地紧急支持资源(如全国24小时心理援助热线:400-161-9995)。
>
> 本工具基于认知行为疗法(CBT)和正念的通用原则提供一般性调节建议,不替代持证专业人士的个性化心理咨询或治疗。
## 📋 验收点
### P0 已完成
- [x] 能引导用户完成情绪日记四要素
- [x] 能识别至少 10 种情绪类型
- [x] 危机关键词触发专业提示
- [x] 提供可执行的调节建议
### P1 待完成(今日外)
- [ ] 压力评估问卷
- [ ] 个性化方案生成
- [ ] 效果跟踪
## Technical Information
| Attribute | Value |
|-----------|-------|
| **Skill ID** | psych-companion |
| **Version** | 0.1.0 |
| **Author** | harrylabsj |
| **Status** | dev |
| **License** | MIT-0 |
| **Workspace** | psych-companion |
FILE:references/cbt_techniques.json
{
"techniques": {
"cognitive_restructuring": {
"name": "认知重评",
"description": "识别和挑战不合理的想法,用更平衡的观点替代",
"steps": [
"1. 识别自动思维:刚才脑海里闪过什么想法?",
"2. 证据检验:这个想法有什么证据支持?有什么反驳证据?",
"3. 替代思维:有没有其他更客观的角度?",
"4. 新视角:如果朋友遇到同样的事,你会怎么想?"
],
"example": "面试失败 → '我太差劲了' → '这次面试让我知道了需要准备什么'"
},
"mindful_breathing": {
"name": "正念呼吸",
"description": "通过专注呼吸回到当下,缓解焦虑",
"steps": [
"1. 找一个安静的地方坐好",
"2. 闭上眼睛,慢慢吸气(4秒)",
"3. 屏住呼吸(4秒)",
"4. 缓慢呼气(6秒)",
"5. 重复 5-10 次"
],
"duration": "3-5分钟",
"keywords": ["呼吸", "放松", "冷静", "当下"]
},
"progressive_muscle_relaxation": {
"name": "渐进式肌肉放松",
"description": "通过紧张和放松肌肉群来缓解身体紧张",
"steps": [
"1. 先深呼吸几次",
"2. 握紧拳头 -> 保持 5秒 -> 松开",
"3. 耸肩 -> 保持 5秒 -> 放松",
"4. 皱眉 -> 保持 5秒 -> 放松",
"5. 从头到脚重复"
],
"duration": "10-15分钟",
"keywords": ["放松", "紧张", "身体"]
},
"behavioral_activation": {
"name": "行为激活",
"description": "通过做小事来打破负面情绪循环",
"steps": [
"1. 列一件今天能完成的小事",
"2. 设定很小的目标(比如:起床后叠被子)",
"3. 完成后来一颗星",
"4. 逐渐增加活动"
],
"keywords": ["行动", "开始", "动起来", "小事"]
},
"grounding": {
"name": "着陆技术",
"description": "通过感官回到当下,适合焦虑发作或恐慌",
"5-4-3-2-1": {
"see": "5样能看到的东西",
"hear": "4样能听到的声音",
"feel": "3样能摸到的东西",
"smell": "2样能闻到的气味",
"taste": "1样能尝到的味道"
},
"keywords": ["害怕", "恐惧", "惊恐", "失控", "现在"]
}
},
"general_advice": {
"sleep": "保证充足睡眠,规律作息",
"exercise": "适度运动,散步也有帮助",
"social": "和信任的人聊聊天",
"routine": "保持日常规律"
}
}
FILE:references/crisis_keywords.json
{
"crisis_signals": {
"suicide": [
"不想活了",
"想死",
"活着没意思",
"活着太累",
"不想继续",
"活够了",
"死了就好了",
"想从楼上跳下去",
"想喝药",
"想死的方式"
],
"self_harm": [
"想伤害自己",
"想割腕",
"想划自己",
"想打自己",
"想撞墙",
"想用刀",
"想把自己弄伤"
],
"severe_breakdown": [
"坚持不住了",
"彻底崩溃",
"完全失控",
"撑不住了",
"要疯了",
"无法呼吸",
"极度恐惧",
"完全失控"
]
},
"professional_resources": {
"national_hotline": "400-161-9995",
"description": "全国24小时心理援助热线",
"local_advice": "可以联系当地精神卫生中心或社区卫生服务中心的心理科"
},
"response_templates": {
"immediate": "我听到你现在的状态很不容易。或许可以考虑和专业的心理咨询师聊聊,他们能提供更专业的支持。",
"hotline": "如果方便的话,可以拨打心理援助热线:400-161-9995(全国24小时)",
"local": "也可以联系当地的心理医院或社区卫生服务中心的心理科。",
"emergency": "如果你现在有紧急危险,请直接拨打120或110。"
}
}
FILE:references/emotions.json
{
"emotions": {
"positive": [
{"name": "开心", "aliases": ["高兴", "快乐", "愉快", "喜悦", "欣喜"]},
{"name": "满足", "aliases": ["满意", "安心", "欣慰", "欣慰"]},
{"name": "平静", "aliases": ["安宁", "宁静", "放松", "自在"]},
{"name": "自豪", "aliases": ["骄傲", "成就感", "自信"]},
{"name": "感激", "aliases": ["感恩", "感动", "谢谢"]}
],
"negative": [
{"name": "悲伤", "aliases": ["难过", "伤心", "沮丧", "郁闷", "低落"]},
{"name": "愤怒", "aliases": ["生气", "恼火", "愤怒", "愤慨"]},
{"name": "恐惧", "aliases": ["害怕", "惊恐", "畏惧", "不安"]},
{"name": "焦虑", "aliases": ["担忧", "紧张", "焦虑", "心神不宁"]},
{"name": "失望", "aliases": ["沮丧", "绝望", "心灰意冷"]},
{"name": "愧疚", "aliases": ["自责", "内疚", "过意不去"]},
{"name": "羡慕", "aliases": ["嫉妒", "眼红", "不平衡"]},
{"name": "困惑", "aliases": ["迷茫", "不知所措", "茫然"]}
],
"intensity_levels": {
"1": "轻微",
"2": "较弱",
"3": "中等",
"4": "较强",
"5": "强烈",
"6": "很强烈",
"7": "非常强烈",
"8": "极度强烈",
"9": "接近极限",
"10": "无法承受"
}
},
"trigger_categories": [
"工作压力",
"学业压力",
"人际关系",
"家庭问题",
"感情问题",
"经济压力",
"健康问题",
"未来不确定性",
"突发事件",
"睡眠不足"
]
}
FILE:scripts/crisis_detector.py
#!/usr/bin/env python3
"""
危机检测器 - 检测用户输入中的危机信号并触发专业转介
"""
import json
import re
from pathlib import Path
class CrisisDetector:
def __init__(self):
self.crisis_data = self._load_crisis_data()
def _load_crisis_data(self):
"""加载危机关键词数据"""
refs_path = Path(__file__).parent.parent / "references" / "crisis_keywords.json"
with open(refs_path, 'r', encoding='utf-8') as f:
return json.load(f)
def detect(self, text: str) -> dict:
"""
检测文本中的危机信号
Returns:
dict: {
"has_crisis": bool,
"level": "high" | "medium" | None,
"type": "suicide" | "self_harm" | "breakdown" | None,
"matched_keywords": list,
"response": str
}
"""
text = text.lower()
crisis_signals = self.crisis_data.get("crisis_signals", {})
results = {
"has_crisis": False,
"level": None,
"type": None,
"matched_keywords": [],
"response": None
}
# 检查自杀相关
suicide_keywords = crisis_signals.get("suicide", [])
matched = [kw for kw in suicide_keywords if kw in text]
if matched:
results["has_crisis"] = True
results["level"] = "high"
results["type"] = "suicide"
results["matched_keywords"] = matched
# 检查自伤相关
else:
self_harm_keywords = crisis_signals.get("self_harm", [])
matched = [kw for kw in self_harm_keywords if kw in text]
if matched:
results["has_crisis"] = True
results["level"] = "high"
results["type"] = "self_harm"
results["matched_keywords"] = matched
# 检查严重崩溃
else:
breakdown_keywords = crisis_signals.get("severe_breakdown", [])
matched = [kw for kw in breakdown_keywords if kw in text]
if matched:
results["has_crisis"] = True
results["level"] = "medium"
results["type"] = "breakdown"
results["matched_keywords"] = matched
# 生成响应
if results["has_crisis"]:
results["response"] = self._generate_response(results["type"])
return results
def _generate_response(self, crisis_type: str) -> str:
"""生成危机响应话术"""
templates = self.crisis_data.get("response_templates", {})
hotline = self.crisis_data.get("professional_resources", {}).get("national_hotline", "400-161-9995")
response_parts = []
# 立即回应
response_parts.append(templates.get("immediate", ""))
# 提供热线
hotline_text = templates.get("hotline", "").replace("400-161-9995", hotline)
response_parts.append(hotline_text)
# 当地资源
response_parts.append(templates.get("local", ""))
# 紧急情况
response_parts.append(templates.get("emergency", ""))
return "\n\n".join([r for r in response_parts if r])
def should_escalate(self, text: str) -> bool:
"""判断是否需要升级转介"""
return self.detect(text).get("has_crisis", False)
if __name__ == "__main__":
# 测试用例
detector = CrisisDetector()
test_cases = [
"我今天心情不好",
"我最近压力好大",
"活着没意思",
"想伤害自己",
"坚持不住了"
]
print("=== 危机检测测试 ===\n")
for text in test_cases:
result = detector.detect(text)
print(f"输入: {text}")
print(f"危机: {result['has_crisis']}")
print(f"级别: {result.get('level', 'N/A')}")
print(f"类型: {result.get('type', 'N/A')}")
print("-" * 30)
FILE:scripts/emotion_analyzer.py
#!/usr/bin/env python3
"""
情绪分析器 - 识别情绪类型、强度和触发因素
"""
import json
import re
from pathlib import Path
from typing import Dict, List, Optional
class EmotionAnalyzer:
def __init__(self):
self.emotion_data = self._load_emotion_data()
def _load_emotion_data(self):
"""加载情绪数据"""
refs_path = Path(__file__).parent.parent / "references" / "emotions.json"
with open(refs_path, 'r', encoding='utf-8') as f:
return json.load(f)
def analyze(self, text: str) -> dict:
"""
分析文本中的情绪
Returns:
dict: {
"emotions": list, # 识别到的情绪
"primary_emotion": str, # 主要情绪
"intensity": int, # 强度 1-10
"triggers": list, # 可能的触发因素
"analysis": str # 分析说明
}
"""
emotions = self._detect_emotions(text)
result = {
"emotions": emotions,
"primary_emotion": emotions[0]["name"] if emotions else "未知",
"intensity": self._estimate_intensity(text),
"triggers": self._extract_triggers(text),
"analysis": self._generate_analysis(emotions)
}
return result
def _detect_emotions(self, text: str) -> List[dict]:
"""检测情绪"""
text = text.lower()
detected = []
# 检查正面情绪
for emotion in self.emotion_data.get("emotions", {}).get("positive", []):
if any(alias in text for alias in emotion.get("aliases", [])):
detected.append({
"name": emotion["name"],
"type": "positive",
"confidence": 0.9
})
# 检查负面情绪
for emotion in self.emotion_data.get("emotions", {}).get("negative", []):
if any(alias in text for alias in emotion.get("aliases", [])):
detected.append({
"name": emotion["name"],
"type": "negative",
"confidence": 0.9
})
# 基于关键词推断情绪
if not detected:
detected = self._infer_emotions(text)
return detected[:3] # 最多返回3个情绪
def _infer_emotions(self, text: str) -> List[dict]:
"""基于文本推断情绪"""
inferred = []
# 焦虑推断
if any(word in text for word in ["担心", "紧张", "害怕", "不安", "烦"]):
inferred.append({"name": "焦虑", "type": "negative", "confidence": 0.6})
# 失落推断
if any(word in text for word in ["累", "疲惫", "无力", "疲倦"]):
inferred.append({"name": "疲惫", "type": "negative", "confidence": 0.6})
# 烦躁推断
if any(word in text for word in ["烦", "躁", "恼火", "不顺"]):
inferred.append({"name": "烦躁", "type": "negative", "confidence": 0.5})
return inferred
def _estimate_intensity(self, text: str) -> int:
"""估算情绪强度"""
intensity_keywords = {
9: ["非常", "极度", "极其", "十分", "太"],
7: ["很", "比较", "相当"],
5: ["有点", "略微", "轻微"],
3: ["稍微", "有一点"]
}
for level, keywords in intensity_keywords.items():
if any(kw in text for kw in keywords):
return level
# 默认中等强度
return 5
def _extract_triggers(self, text: str) -> List[str]:
"""提取可能的触发因素"""
triggers = []
trigger_categories = self.emotion_data.get("trigger_categories", [])
for trigger in trigger_categories:
if trigger in text:
triggers.append(trigger)
return triggers
def _generate_analysis(self, emotions: List[dict]) -> str:
"""生成分析说明"""
if not emotions:
return "我感受到一些情绪,但不太确定具体是什么。你能更具体地描述一下吗?"
primary = emotions[0]
if primary.get("type") == "positive":
return f"你现在的情绪主要是{primary['name']},这很好!"
else:
intensity = self.emotion_data.get("intensity_levels", {}).get(
str(self._get_last_intensity()), "中等"
)
return f"你现在的情绪主要是{primary['name']},强度大概{intensity}。想聊聊是什么让你有这样的感受吗?"
def _get_last_intensity(self) -> int:
"""获取默认强度(需要从上下文中获取)"""
return 5
def format_emotion_report(self, analysis: dict) -> str:
"""格式化情绪分析报告"""
lines = []
lines.append("📊 **情绪分析报告**")
lines.append("")
if analysis.get("emotions"):
lines.append("**识别到的情绪:**")
for e in analysis["emotions"]:
emoji = "😊" if e.get("type") == "positive" else "😔"
lines.append(f"- {emoji} {e['name']}")
lines.append("")
if analysis.get("triggers"):
lines.append("**可能的触发因素:**")
lines.append(", ".join(analysis["triggers"]))
lines.append("")
if analysis.get("analysis"):
lines.append(analysis["analysis"])
return "\n".join(lines)
if __name__ == "__main__":
# 测试用例
analyzer = EmotionAnalyzer()
test_cases = [
"今天工作压力好大,焦虑得睡不着",
"终于完成了项目,特别开心满足",
"和男朋友吵架了,很生气",
"最近失眠,疲惫不堪"
]
print("=== 情绪分析测试 ===\n")
for text in test_cases:
result = analyzer.analyze(text)
print(f"输入: {text}")
print(f"情绪: {result['emotions']}")
print(f"主要: {result['primary_emotion']}")
print(f"强度: {result['intensity']}")
print(f"触发: {result['triggers']}")
print("-" * 40)
FILE:test/test_emotion.py
#!/usr/bin/env python3
"""
psych-companion 基础测试
"""
import sys
import os
# 添加 scripts 目录到路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
from crisis_detector import CrisisDetector
from emotion_analyzer import EmotionAnalyzer
def test_crisis_detector():
"""测试危机检测器"""
print("=== 测试危机检测器 ===")
detector = CrisisDetector()
test_cases = [
("我今天心情不好", False, None),
("工作压力好大", False, None),
("活着没意思", True, "suicide"),
("想伤害自己", True, "self_harm"),
("坚持不住了", True, "breakdown"),
]
passed = 0
for text, expected_crisis, expected_type in test_cases:
result = detector.detect(text)
if result["has_crisis"] == expected_crisis:
if expected_type is None or result.get("type") == expected_type:
print(f"✅ {text}")
passed += 1
else:
print(f"❌ {text} - 类型不匹配: {result.get('type')}")
else:
print(f"❌ {text} - 危机检测失败")
print(f"\n危机检测: {passed}/{len(test_cases)} 通过\n")
return passed == len(test_cases)
def test_emotion_analyzer():
"""测试情绪分析器"""
print("=== 测试情绪分析器 ===")
analyzer = EmotionAnalyzer()
test_cases = [
"今天特别开心满足",
"工作压力好大,焦虑",
"和男朋友吵架了,很生气",
]
passed = 0
for text in test_cases:
result = analyzer.analyze(text)
if result["emotions"]:
print(f"✅ {text}")
print(f" 识别: {result['emotions']}")
passed += 1
else:
print(f"⚠️ {text} (未识别到情绪)")
print(f"\n情绪分析: {passed}/{len(test_cases)} 识别到情绪\n")
return passed > 0
def test_file_structure():
"""测试文件结构"""
print("=== 测试文件结构 ===")
base_dir = os.path.dirname(os.path.dirname(__file__))
required_files = [
"SKILL.md",
"references/emotions.json",
"references/crisis_keywords.json",
"references/cbt_techniques.json",
"scripts/crisis_detector.py",
"scripts/emotion_analyzer.py",
]
passed = 0
for f in required_files:
path = os.path.join(base_dir, f)
if os.path.exists(path):
print(f"✅ {f}")
passed += 1
else:
print(f"❌ {f} 缺失")
print(f"\n文件结构: {passed}/{len(required_files)} 存在\n")
return passed == len(required_files)
def main():
print("🧪 psych-companion 基础测试\n")
results = []
results.append(("文件结构", test_file_structure()))
results.append(("危机检测", test_crisis_detector()))
results.append(("情绪分析", test_emotion_analyzer()))
print("=" * 50)
print("测试汇总:")
for name, passed in results:
status = "✅ 通过" if passed else "❌ 失败"
print(f" {name}: {status}")
all_passed = all(r[1] for r in results)
print(f"\n总体: {'✅ 全部通过' if all_passed else '❌ 存在问题'}")
return 0 if all_passed else 1
if __name__ == "__main__":
sys.exit(main())