Skill: jiafei0007/tarot-reading-new (version 1.0.0)

--- name: tarot-reading description: "塔罗牌解读技能。当用户提到以下意图时使用此技能：抽塔罗牌、算塔罗、塔罗占卜、塔罗解读；问塔罗阵型、塔罗单牌、三牌阵、塞尔特十字；问是/否问题（能否、可不可以、会不会）；找东西、丢了什么在哪里；希望得到建议或提醒。此技能使用 Python secrets 模块实现密码学安全的随机抽牌（真随机，非 AI 概率采样）。" --- # 塔罗牌解读技能 ## 功能概述 本技能提供韦特塔罗 78 张牌的完整解读，支持 4 种问题类型和 7 种牌阵，使用 `secrets` 模块实现密码学级别的随机抽牌。 ## 核心原则 1. **随机性**：所有抽牌使用 `secrets` 模块（系统级加密随机），而非 AI 概率采样 2. **牌库**：标准韦特塔罗（Rider-Waite-Smith）78 张牌，大阿尔卡纳 22 张 + 小阿尔卡纳 56 张 3. **尊重用户**：用户提问时心中默念问题，不追问具体内容 4. **牌意解读**：基于牌面符号象征和传统韦特塔罗牌意解读 5. **娱乐定位**：塔罗解读仅供娱乐参考，不构成任何实际决策建议 ## 问题类型与默认阵型 | 问题类型 | 默认推荐阵型 | 说明 | |---------|------------|------| | **是否** | 单牌（可升级为三牌阵） | 快速二选一答案 | | **建议** | 过去-现在-未来 | 寻求行动指引 | | **提醒** | 过去-现在-未来 | 需要注意什么 | | **找物** | 单牌（可升级为寻物阵） | 寻找丢失物品 | ## 支持的牌阵 ### 1. 单牌（1张） - **适合**：是否问题、找物 - **解读**：直接给出答案或物品线索 ### 2. 过去-现在-未来（3张） - **适合**：建议、提醒、是否 - **解读**：揭示时间线上的变化 ### 3. 状况-建议-结果（3张） - **适合**：建议、提醒 - **解读**：客观分析 → 行动指引 → 预期结果 ### 4. 自己-对方-关系（3张） - **适合**：建议、提醒（人际关系专项） - **解读**：分析关系中双方立场 ### 5. 四要素（4张） - **适合**：建议、提醒 - **解读**：从火/水/风/土四元素分析问题 ### 6. 马蹄铁（7张） - **适合**：建议、提醒 - **解读**：U字形阵，分析阻碍及如何移除 ### 7. 塞尔特十字（10张） - **适合**：建议、提醒（最全面） - **解读**：深入分析复杂问题，包含核心、阻碍、根源等10个位置 ### 8. 寻物（3张） - **适合**：找物 - **解读**：物品状态 → 障碍 → 寻找指引 ### 9. 寻物扩展（5张） - **适合**：找物 - **解读**：详细位置、状态、障碍、时间、行动指引 ## 使用流程 ### Step 1：理解用户问题 判断用户问题属于哪种类型： - **是否**：问"能不能/会不会/是不是" → 归为**是否** - **建议**：问"该怎么办/做什么/怎么选" → 归为**建议** - **提醒**：问"需要注意什么/有什么要提醒" → 归为**提醒** - **找物**：问"东西在哪/找不到XX了" → 归为**找物** ### Step 2：确定阵型 - 如果用户指定了阵型 → 使用指定阵型 - 如果用户没有指定 → 使用该问题类型的默认阵型 - 可以询问用户是否要升级（如"是否"想升级为三牌阵吗？） ### Step 3：执行抽牌脚本 调用 `scripts/draw_tarot.py` 执行抽牌： ``` python <skill_dir>/scripts/draw_tarot.py [问题类型] [阵型名称] ``` 参数说明： - 问题类型：是否 / 建议 / 提醒 / 找物 - 阵型名称：单牌 / 过去-现在-未来 / 状况-建议-结果 / 自己-对方-关系 / 四要素 / 马蹄铁 / 塞尔特十字 / 寻物 / 寻物扩展 ### Step 4：解读结果 脚本会输出两部分内容： 1. **JSON 结构化数据**：包含时间、牌阵、问题类型、各张牌的详细信息 2. **可读解读文本**：格式化后的解读内容 **解读风格要求**： - 简洁自然，不说废话 - 结合用户问题的实际场景解读 - 说明为什么抽到这张牌与问题相关 - 不做过度联想，忠于牌意 - 结尾提示"仅供娱乐参考" ### Step 5：互动延伸（如适用） 解读结束后，可以： - 问用户是否要换一种阵型深入看 - 问用户是否要针对某张牌单独解读 - 如果是"是否"问题，可问是否升级为三牌阵看更多细节 ## 是/否判断逻辑 单牌阵判断是/否时，根据以下规则： | 类别 | 正位 | 逆位 | |------|------|------| | 大阿尔卡纳（阳性牌） | ✅ 是 | ❌ 否 | | 大阿尔卡纳（阴性牌） | ❌ 否 | ✅ 是 | | 大阿尔卡纳（中性牌） | ✅ 是 | ❌ 否 | | 权杖/圣杯/星币（小阿尔卡纳） | ✅ 是 | ❌ 否 | | 宝剑（小阿尔卡纳） | ❌ 否 | ✅ 是 | **阳性大阿尔卡纳**（倾向于肯定）：愚人0、魔术师1、皇后3、恋人6、力量8、隐士9、命运之轮10、正义11、节制14、星星17、太阳19、审判20、世界21 **阴性大阿尔卡纳**（倾向于否定）：教皇5、战车7、吊人12、死神13、恶魔15、塔16、月亮18 ## 输出格式模板 ``` 🎴 【塔罗解读 · <牌阵名称>】 <每张牌的解读> --- <根据问题类型的总结> --- ⚠️ 塔罗解读仅供娱乐参考，不构成任何实际决策建议。 ``` ## 常见问题处理 **Q: 用户没有明确指定阵型怎么办？** A: 使用该问题类型的默认阵型，并告知用户选择的理由。 **Q: 用户想连续抽多张同一位置可以吗？** A: 可以，但不建议超过3次。多次抽同一位置的牌通常反映的是用户内心的不确定性，而非牌面本身的信息。 **Q: 抽到相同的牌怎么办？** A: 如果连续两次抽到完全相同的牌（牌号和方位都相同），告知用户这是极低概率事件（1/6084），可能有某种深层含义，可以深入解读。 **Q: 用户问的是很私人的问题不想说具体内容？** A: 完全理解，尊重用户隐私。只需用户心中默念问题即可，不需要说出来。

path: SKILL_en.md
content: ---
name: tarot-reading
description: "Tarot reading skill. This skill should be used when the user wants to: draw tarot cards, do a tarot reading, ask about tarot spreads or card meanings; ask yes/no questions (can I, will it, should I); find lost items; seek advice or reminders. This skill uses Python's secrets module for cryptographically secure random card draws (TRNG-level, not AI probability sampling). Supports both Chinese and English. Supports multiple languages: call the script with 'en' as the third argument for English output, or use English question types directly (Yes/No, Find, Advice, Reminder)."
---

# Tarot Reading Skill

## Overview

This skill provides complete readings for the 78-card Rider-Waite-Smith tarot deck, supporting 4 question types and 7 spreads, with cryptographically secure random card draws using Python's `secrets` module.

**Dual Language Support**: Works with both Chinese and English users. Language is auto-detected from question type keywords or the `lang` parameter.

## Core Principles

1. **Randomness**: All card draws use Python's `secrets` module (system-level cryptographic entropy) — not AI probability sampling
2. **Deck**: Standard Rider-Waite-Smith 78-card deck — 22 Major Arcana + 56 Minor Arcana
3. **Respect Privacy**: User holds the question in their mind; never ask for specifics
4. **Card Meanings**: Based on traditional Rider-Waite-Smith symbolism and interpretation
5. **Entertainment**: Tarot readings are for entertainment only and do not constitute actual decision-making advice

## Question Types

| Type | Description | Trigger Phrases |
|------|-------------|-----------------|
| **Yes/No** | Quick binary answer | can I, will it, should I, is it, does, will |
| **Find** | Locate lost items | where is, lost, missing, can't find |
| **Advice** | Seek guidance on what to do | what should I do, how to, advice, guidance |
| **Reminder** | Alerts and things to watch for | watch out, remind me, be careful, notice |

## Supported Spreads

### 1. Single Card (1 card)
- **Best for**: Yes/No, Find
- **Interpretation**: Direct answer or item location clue

### 2. Past-Present-Future (3 cards)
- **Best for**: Advice, Reminder, Yes/No upgrade
- **Interpretation**: Reveals the timeline of events

### 3. Situation-Advice-Outcome (3 cards)
- **Best for**: Advice, Reminder
- **Interpretation**: Objective analysis → action guidance → expected outcome

### 4. Self-Others-Relationship (3 cards)
- **Best for**: Advice, Reminder (interpersonal focus)
- **Interpretation**: Analyzes both parties' positions in a relationship

### 5. Four Elements (4 cards)
- **Best for**: Advice, Reminder
- **Interpretation**: Analyzes from Fire/Water/Air/Earth perspectives

### 6. Horseshoe (7 cards)
- **Best for**: Advice, Reminder
- **Interpretation**: U-shaped spread analyzing obstacles and how to overcome them

### 7. Celtic Cross (10 cards)
- **Best for**: Advice, Reminder (most comprehensive)
- **Interpretation**: In-depth analysis with 10 positions including core, obstacle, foundation, past, possible future, above, self, surroundings, hope/fear, and outcome

### 8. Finding (3 cards)
- **Best for**: Find
- **Interpretation**: Item status → obstacle → search guidance

### 9. Finding Extended (5 cards)
- **Best for**: Find
- **Interpretation**: Detailed location, condition, barrier, time, and action guidance

## Usage Flow

### Step 1: Understand the User's Question

Map the user's question to one of the 4 types:
- **Yes/No**: Asks "can/will/is" → **Yes/No**
- **Advice**: Asks "what to do/how to/which to choose" → **Advice**
- **Reminder**: Asks "what to watch/be careful about" → **Reminder**
- **Find**: Asks "where is/lost/missing" → **Find**

### Step 2: Determine the Spread

- User specifies a spread → use specified spread
- User doesn't specify → use the default for that question type
- Ask if they want to upgrade (e.g., "Would you like to upgrade this Yes/No to a 3-card Past-Present-Future?")

### Step 3: Execute the Draw Script

Call `scripts/draw_tarot.py` to perform the draw:

```
python <skill_dir>/scripts/draw_tarot.py [question_type] [spread_name] [lang]
```

**Parameters**:
- `question_type`: `Yes/No`, `Find`, `Advice`, `Reminder` (or Chinese equivalents)
- `spread_name`: `Single Card`, `Past-Present-Future`, `Situation-Advice-Outcome`, `Self-Others-Relationship`, `Four Elements`, `Horseshoe`, `Celtic Cross`, `Finding`, `Finding Extended`
- `lang`: `cn` (default) or `en` — auto-detected if English question type is used

### Step 4: Interpret the Result

The script outputs two parts:
1. **JSON structured data**: timestamp, spread, question type, card details
2. **Formatted readable text**: interpreted reading content

**Interpretation Style Requirements**:
- Concise and natural, no fluff
- Connect card meanings to the user's actual question context
- Explain why each card is relevant to the question
- Stay faithful to card meanings, no over-interpretation
- End with "Tarot readings are for entertainment only"

### Step 5: Follow-up Options (if applicable)

After the reading:
- Ask if user wants to try a different spread for more depth
- Offer to interpret a specific card in more detail
- For Yes/No questions, ask if they'd like to upgrade to a 3-card spread

## Yes/No Judgment Logic

Single card Yes/No answers are determined by:

| Category | Upright | Reversed |
|----------|---------|----------|
| Major Arcana (positive cards) | YES | NO |
| Major Arcana (negative cards) | NO | YES |
| Major Arcana (neutral cards) | YES | NO |
| Wands/Cups/Pentacles (Minor) | YES | NO |
| Swords (Minor) | NO | YES |

**Positive Major Arcana** (favor YES): Fool 0, Magician 1, Empress 3, Lovers 6, Strength 8, Hermit 9, Wheel of Fortune 10, Justice 11, Temperance 14, Star 17, Sun 19, Judgement 20, World 21

**Negative Major Arcana** (favor NO): Hierophant 5, Chariot 7, Hanged Man 12, Death 13, Devil 15, Tower 16, Moon 18

## Output Format Template

```
[*] [Tarot Reading · <Spread Name>]

<Interpretation of each card>

---
<Summary based on question type>
---

[!] Tarot readings are for entertainment only and do not constitute actual decision-making advice.
```

## Common Issues

**Q: User didn't specify a spread?**
A: Use the default spread for that question type and explain your choice.

**Q: Same card drawn twice?**
A: If the exact same card (same number and orientation) appears twice, note that the probability is ~1/6084. This may carry deeper meaning — interpret it accordingly.

**Q: User's question is very personal and they don't want to share details?**
A: That's perfectly fine. Respect their privacy — they only need to hold the question in their mind.


path: references/cards.py
content: # 塔罗牌完整数据库
# 韦特塔罗 Rider-Waite-Smith 78张
# 大阿尔卡纳 22张 + 小阿尔卡纳 56张

major_arcana = {
    # 编号(0-21)：(中文名, 英文名, 正位含义, 逆位含义)
    0: {
        "cn": "愚人", "en": "The Fool",
        "upright": "纯真、冒险、自由、新开始、天真、 spontaneity",
        "reversed": "冲动、轻率、鲁莽、延误、缺乏计划、天真幼稚"
    },
    1: {
        "cn": "魔术师", "en": "The Magician",
        "upright": "意志力、行动、创造力、沟通、skill、manifestation",
        "reversed": "欺骗、伎俩、缺乏方向、浪费天赋、拖延、光说不做"
    },
    2: {
        "cn": "女祭司", "en": "The High Priestess",
        "upright": "直觉、神秘、智慧、內在知识、信仰、潜意识",
        "reversed": "隐秘、秘密、缺乏直觉、表面化、欺骗、隐瞒"
    },
    3: {
        "cn": "皇后", "en": "The Empress",
        "upright": "丰盛、滋养、自然、母亲般关怀、创造力、感官",
        "reversed": "依赖、过度保护、停滞、创造力受阻、母亲问题"
    },
    4: {
        "cn": "皇帝", "en": "The Emperor",
        "upright": "权威、结构、父亲般领导、稳定性、control、决断",
        "reversed": "暴政、控制欲过强、缺乏纪律、rigidity、冲动"
    },
    5: {
        "cn": "教皇", "en": "The Hierophant",
        "upright": "传统、精神指引、导师、信仰、价值观、社会规范",
        "reversed": "反叛、打破常规、个人信仰、误导导师、不合群"
    },
    6: {
        "cn": "恋人", "en": "The Lovers",
        "upright": "爱情、选择、伙伴关系、价值观、吸引、结合",
        "reversed": "失衡、不和、价值观冲突、欺骗、犹豫不决"
    },
    7: {
        "cn": "战车", "en": "The Chariot",
        "upright": "意志力、胜利、决心、掌控、掌控、行动力",
        "reversed": "失控、缺乏方向、冲动、失去控制、固执"
    },
    8: {
        "cn": "力量", "en": "Strength",
        "upright": "勇气、耐心、内在力量、同情心、韧性、温柔的力量",
        "reversed": "自我怀疑、脆弱、失控、内在勇气不足、恐惧"
    },
    9: {
        "cn": "隐士", "en": "The Hermit",
        "upright": "內省、孤独、內在指引、寻找真理、休息、洞察",
        "reversed": "孤立、孤独感、过度內省、退缩、拒绝帮助"
    },
    10: {
        "cn": "命运之轮", "en": "Wheel of Fortune",
        "upright": "命运转变、 cycle、机会、好运、 inevitability",
        "reversed": "厄运、cycle中断、抗拒改变、错失机会、停滞"
    },
    11: {
        "cn": "正义", "en": "Justice",
        "upright": "公正、真相、法律、因果报应、平衡、诚实",
        "reversed": "不公正、不诚实、法律问题、缺乏责任感、偏见"
    },
    12: {
        "cn": "吊人", "en": "The Hanged Man",
        "upright": "暂停、牺牲、新的视角、臣服、等待、转变观点",
        "reversed": "拖延、牺牲过度、拒绝妥协、坐以待毙、牺牲白费"
    },
    13: {
        "cn": "死神", "en": "Death",
        "upright": "结束、彻底转变、重生、释放过去、蜕变",
        "reversed": "抗拒改变、停滞、渐进的结束、恐惧结束、腐烂"
    },
    14: {
        "cn": "节制", "en": "Temperance",
        "upright": "平衡、耐心、目的、调和、整合、适中",
        "reversed": "失衡、过度、极端、缺乏目标、浪费"
    },
    15: {
        "cn": "恶魔", "en": "The Devil",
        "upright": "束缚、物质主义、欲望、阴影面、沉迷、成瘾",
        "reversed": "打破束缚、释放、面对阴影、改变模式、摆脱控制"
    },
    16: {
        "cn": "塔", "en": "The Tower",
        "upright": "突然改变、 disruption、启示、觉醒、打破旧结构",
        "reversed": "害怕改变、内在动荡、延迟灾难、被困在旧结构中"
    },
    17: {
        "cn": "星星", "en": "The Star",
        "upright": "希望、灵感、宁静、疗愈、 faith、Guidance",
        "reversed": "绝望、失去信心、混乱、缺乏希望、误导"
    },
    18: {
        "cn": "月亮", "en": "The Moon",
        "upright": "幻象、恐惧、潜意识、不安、 illusion、夜晚",
        "reversed": "恐惧暴露、压抑情绪、寻找真相、幻觉消退"
    },
    19: {
        "cn": "太阳", "en": "The Sun",
        "upright": "喜悦、成功、活力、生命力、乐观、温暖、成功",
        "reversed": "过度乐观、过度兴奋、成功路上有障碍、暂时的阴霾"
    },
    20: {
        "cn": "审判", "en": "Judgement",
        "upright": "重生、內省、评判、觉醒、因果、宽恕",
        "reversed": "自我怀疑、后悔、忽视內在呼声、不反省、自我否定"
    },
    21: {
        "cn": "世界", "en": "The World",
        "upright": "完成、成就、圆满、整合、旅程终点、成功",
        "reversed": "功亏一篑、缺乏成就感、停滞、延迟完成、原地踏步"
    }
}

# 小阿尔卡纳：四要素 x 14张 = 56张
# 编号 22-77

minor_arcana = {
    # 权杖 suit = wands, element = fire
    "wands": {
        "name_cn": "权杖", "element": "火", "nature": "行动、热情、创造、野心",
        "court": ["侍从", "骑士", "王后", "国王"],
        "court_meanings": {
            "侍从": {"upright": "机会、好奇、探索、热忱", "reversed": "延迟、缺乏热情、浪费机会"},
            "骑士": {"upright": "行动、热情、旅行、冲动", "reversed": "延迟、挫折、冲动行事"},
            "王后": {"upright": "富足、自信、创业、社交", "reversed": "过度自信、炫耀、自私"},
            "国王": {"upright": "领导力、权威、创业、荣耀", "reversed": "专制、傲慢、缺乏远见"}
        },
        "ace": {"upright": "创造、灵感、新开始、潜力", "reversed": "延误计划、缺乏热情、方向不明"},
        "numbered": {
            2: {"upright": "创意计划、选择、远见、 partnership", "reversed": "犹豫不决、缺乏计划、冲突"},
            3: {"upright": "扩张、进步、沟通、进展", "reversed": "延误、挫折、停滞"},
            4: {"upright": "庆祝、 harmony、home、和平", "reversed": "冲突、打破和平、干扰"},
            5: {"upright": "竞争、冲突、变化、选择", "reversed": "避免冲突、赢得竞争、和平解决"},
            6: {"upright": "胜利、认可、荣誉、成就感", "reversed": "失去认可、失败、骄傲导致失败"},
            7: {"upright": "防御、坚持、挑战、面对竞争", "reversed": "放弃、失去优势、被击败"},
            8: {"upright": "快速行动、旅行、移动、消息", "reversed": "延误、挫折、被阻止"},
            9: {"upright": "坚韧、忍耐、hope、perseverance", "reversed": "精疲力尽、过度劳累、倦怠"},
            10: {"upright": "过度负担、责任、压力、辛劳", "reversed": "压力减轻、分享负担、卸下重担"}
        }
    },
    # 圣杯 suit = cups, element = water
    "cups": {
        "name_cn": "圣杯", "element": "水", "nature": "情感、爱、关系、情绪、直觉",
        "court": ["侍从", "骑士", "王后", "国王"],
        "court_meanings": {
            "侍从": {"upright": "创意、浪漫、好奇心、message、机会", "reversed": "忽略情绪、过度浪漫、消息延误"},
            "骑士": {"upright": "浪漫、想象、魅力、追求", "reversed": "欺骗、失望、逃避、浪漫过度"},
            "王后": {"upright": "情感成熟、滋养、直觉、慈悲", "reversed": "情绪化、依赖、缺乏边界"},
            "国王": {"upright": "情感掌控、慷慨、情绪平衡、爱", "reversed": "情绪操纵、喜怒无常、占有欲"}
        },
        "ace": {"upright": "爱、情感新开始、同情心、丰富", "reversed": "情绪压抑、空虚、缺乏情感"},
        "numbered": {
            2: {"upright": "爱、伙伴关系、吸引、浪漫", "reversed": "不平衡、欺骗、关系冲突"},
            3: {"upright": " friendship、 celebration、creative、 collaboration", "reversed": "过度、过度沉溺、孤独的庆祝"},
            4: {"upright": " apathetic、boredom、contemplation、dissatisfaction", "reversed": " new enthusiasm、awakening、hope"},
            5: {"upright": " loss、disappointment、regret、perspective", "reversed": " recovery、reconciliation、moving on"},
            6: {"upright": " memories、nostalgia、 innocence、reunion", "reversed": " stuck in past、unresolved issues、naivety"},
            7: {"upright": " choices、illusion、wishful thinking、opportunities", "reversed": " alignment、clarity、realistic view"},
            8: {"upright": " leaving、walking away、disillusionment、 search for meaning", "reversed": " fear of change、status quo、nostalgia"},
            9: {"upright": " wishes、satisfaction、contentment、gratitude", "reversed": " dissatisfaction、greed、unfulfilled wishes"},
            10: {"upright": " happiness、family、home、harmony、emotional fulfillment", "reversed": " family conflict、miscommunication、broken home"}
        }
    },
    # 宝剑 suit = swords, element = air
    "swords": {
        "name_cn": "宝剑", "element": "风", "nature": "心智、思维、沟通、冲突、真相、决定",
        "court": ["侍从", "骑士", "王后", "国王"],
        "court_meanings": {
            "侍从": {"upright": " curiosity、new ideas、thirst for knowledge", "reversed": " deception、gossip、manipulation"},
            "骑士": {"upright": "action、breakthrough、fast-moving、mental clarity", "reversed": "而立、冲突、冲动、鲁莽"},
            "王后": {"upright": " clear thinking、independence、direct communication", "reversed": " coldness、cruelty、bitterness、overthinking"},
            "国王": {"upright": " intellectual power、authority、truth、clear thinking", "reversed": " abuse of power、 manipulation、tyranny"}
        },
        "ace": {"upright": " clarity、breakthrough、new ideas、mental clarity", "reversed": " confusion、misinformation、chaos"},
        "numbered": {
            2: {"upright": " difficult choice、stalemate、contemplation、balance", "reversed": " indecision、stalemate、confusion"},
            3: {"upright": " heartbreak、painful ending、 grief、loss", "reversed": " recovery from loss、moving on、healing"},
            4: {"upright": " rest、recovery、contemplation、meditation", "reversed": " restlessness、burnout、lack of progress"},
            5: {"upright": " conflict、defeat、losing、dispute", "reversed": " reconciliation、making peace、overcoming conflict"},
            6: {"upright": " transition、moving on、leaving、passage", "reversed": " resisting change、stuck、transition problems"},
            7: {"upright": " deception、strategy、trickery、stealth", "reversed": " coming clean、confession、getting caught"},
            8: {"upright": " restriction、 imprisonment、 stuck、helplessness", "reversed": " freedom、release、new perspective"},
            9: {"upright": " anxiety、fear、worst-case scenario、nightmares", "reversed": " hope、recovery、overcoming fears"},
            10: {"upright": " ending、painful ending、crisis、loss", "reversed": " recovery、healing、end of problems"}
        }
    },
    # 星币 suit = pentacles, element = earth
    "pentacles": {
        "name_cn": "星币", "element": "土", "nature": "物质、财富、工作、健康、现实",
        "court": ["侍从", "骑士", "王后", "国王"],
        "court_meanings": {
            "侍从": {"upright": "manifestation、financial opportunity、skill development", "reversed": " missed opportunity、procrastination、lack of progress"},
            "骑士": {"upright": " efficiency、routine、hard work、productivity", "reversed": " laziness、overwork、boredom"},
            "王后": {"upright": " abundance、luxury、security、comfort", "reversed": " financial insecurity、overindulgence、stinginess"},
            "国王": {"upright": " wealth、business、leadership、security", "reversed": " greed、materialism、poor financial decisions"}
        },
        "ace": {"upright": " new financial opportunity、manifestation、abundance", "reversed": " missed opportunity、poor financial planning"},
        "numbered": {
            2: {"upright": " balance、adaptability、time management、prioritizing", "reversed": " imbalance、over-committed、poor prioritization"},
            3: {"upright": " teamwork、collaboration、learning、implementation", "reversed": " lack of teamwork、poor quality、disharmony"},
            4: {"upright": " security、conservation、control、avings", "reversed": " insecurity、greed、over-spending"},
            5: {"upright": " financial loss、isolation、hardship、homelessness", "reversed": " recovery、improvement、spiritual growth through hardship"},
            6: {"upright": " generosity、charity、giving and receiving、sharing", "reversed": " strings attached、inequality、egoism"},
            7: {"upright": " patience、long-term vision、investing、delayed gratification", "reversed": " impatience、poor returns、lack of long-term vision"},
            8: {"upright": " skill、mastery、dedication、quality craftsmanship", "reversed": " lack of quality、incomplete work、mediocrity"},
            9: {"upright": " abundance、luxury、self-sufficiency、independence", "reversed": " over-reliance、superficiality、greed"},
            10: {"upright": " wealth、inheritance、family、legacy、abundance", "reversed": " financial failure、family conflict、poverty"}
        }
    }
}


path: references/spreads.py
content: # 塔罗阵型定义
spreads = {
    "单牌": {
        "cards": 1,
        "suitable": ["是否", "找物"],
        "description": "抽一张牌，快速解答简单问题。适合问答案为是/否的二选一问题，或寻找某个物品。",
        "positions": {
            1: "答案 / 物品位置或状态"
        }
    },

    "过去-现在-未来": {
        "cards": 3,
        "suitable": ["建议", "提醒", "是否"],
        "description": "经典三牌阵，揭示问题的过去背景、当前状态和未来可能走向。适合寻求建议或需要全面了解情况时使用。",
        "positions": {
            1: "过去（问题的根源或背景）",
            2: "现在（当前的实际状态）",
            3: "未来（如果保持现状的走向）"
        }
    },

    "状况-建议-结果": {
        "cards": 3,
        "suitable": ["建议", "提醒"],
        "description": "聚焦于客观分析并给出行动指引。适合需要明确方向的问题。",
        "positions": {
            1: "状况（客观分析当前处境）",
            2: "建议（应该采取的行动或心态）",
            3: "结果（如果听从建议会怎样）"
        }
    },

    "自己-对方-关系": {
        "cards": 3,
        "suitable": ["建议", "提醒"],
        "description": "专门用于人际关系问题，分析双方立场及关系本质。",
        "positions": {
            1: "问卜者的立场和心态",
            2: "对方的立场和心态",
            3: "两人关系的本质和走向"
        }
    },

    "四要素": {
        "cards": 4,
        "suitable": ["建议", "提醒"],
        "description": "从四个维度分析问题，类似西方四元素分析法，全面评估局势。",
        "positions": {
            1: "火 - 行动力、热情、你想做什么",
            2: "水 - 情感、感受、你的内心状态",
            3: "风 - 思维、沟通、你的想法",
            4: "土 - 物质、现实、实际行动"
        }
    },

    "马蹄铁": {
        "cards": 7,
        "suitable": ["建议", "提醒"],
        "description": "马蹄铁阵呈U字形，专门分析阻碍和如何移除阻碍。适合需要找出障碍并寻求突破方向的情况。",
        "positions": {
            1: "阻碍（当前最大的障碍是什么）",
            2: "基础（问题的根基或支撑）",
            3: "过去（最近发生的影响事件）",
            4: "现在（目前的真实状况）",
            5: "周围（影响局面的因素）",
            6: "可能未来（不采取行动的走向）",
            7: "建议（如何移除阻碍）"
        }
    },

    "塞尔特十字": {
        "cards": 10,
        "suitable": ["建议", "提醒"],
        "description": "最全面、最经典的塔罗阵型。适合需要深入分析复杂问题，尤其是涉及他人或外部因素的情况。",
        "positions": {
            1: "核心（当前问题的核心所在）",
            2: "阻碍（横在面前的障碍）",
            3: "基础（问题的深层根源）",
            4: "过去（最近发生的相关事件）",
            5: "可能未来（如果不行动的结果）",
            6: "头顶（即将面对的影响或建议）",
            7: "自己（问卜者的心态和立场）",
            8: "环境（周围环境如何看待此事）",
            9: "希望/恐惧（内心深处真正的感受）",
            10: "结果（最终结局）"
        }
    },

    "寻物": {
        "cards": 3,
        "suitable": ["找物"],
        "description": "专门用于寻找丢失物品的问题。",
        "positions": {
            1: "物品状态（物品现在在哪里/什么状态）",
            2: "隐藏因素（为什么找不到/障碍是什么）",
            3: "指引（如何找到它的线索）"
        }
    },

    "寻物扩展": {
        "cards": 5,
        "suitable": ["找物"],
        "description": "详细寻物阵型，提供更多线索。",
        "positions": {
            1: "物品当前大概位置（哪个区域）",
            2: "物品具体状态（完好/损坏/被藏）",
            3: "为什么找不到（心理或物理障碍）",
            4: "时间线索（什么时候可能找到）",
            5: "行动指引（应该怎么找）"
        }
    }
}

question_spread_map = {
    "是否": ["单牌", "过去-现在-未来"],
    "找物": ["单牌", "寻物", "寻物扩展"],
    "建议": ["过去-现在-未来", "状况-建议-结果", "塞尔特十字"],
    "提醒": ["过去-现在-未来", "状况-建议-结果", "塞尔特十字"]
}


path: references/spreads_en.py
content: # Tarot Spreads - English Version
spreads = {
    "Single Card": {
        "cards": 1,
        "suitable": ["Yes/No", "Find"],
        "description": "Draw one card for a quick answer. Best for yes/no questions or locating an item.",
        "positions": {
            1: "Answer / Item Location or Status"
        }
    },

    "Past-Present-Future": {
        "cards": 3,
        "suitable": ["Advice", "Reminder", "Yes/No"],
        "description": "The classic three-card spread revealing the past context, present state, and possible future path.",
        "positions": {
            1: "Past (Root cause or background of the issue)",
            2: "Present (Current actual situation)",
            3: "Future (Where things are heading if nothing changes)"
        }
    },

    "Situation-Advice-Outcome": {
        "cards": 3,
        "suitable": ["Advice", "Reminder"],
        "description": "Focuses on objective analysis with clear action guidance.",
        "positions": {
            1: "Situation (Objective analysis of the current circumstances)",
            2: "Advice (Action or mindset you should take)",
            3: "Outcome (What happens if you follow the advice)"
        }
    },

    "Self-Others-Relationship": {
        "cards": 3,
        "suitable": ["Advice", "Reminder"],
        "description": "Designed for interpersonal questions, analyzing both parties' positions and the nature of the relationship.",
        "positions": {
            1: "Your position and mindset",
            2: "The other person's position and mindset",
            3: "The nature and direction of the relationship"
        }
    },

    "Four Elements": {
        "cards": 4,
        "suitable": ["Advice", "Reminder"],
        "description": "Analyzes the situation from four dimensions using the classical four elements.",
        "positions": {
            1: "Fire - Action, passion, what you want to do",
            2: "Water - Emotion, feeling, your inner state",
            3: "Air - Mind, communication, your thoughts",
            4: "Earth - Material, reality, practical action"
        }
    },

    "Horseshoe": {
        "cards": 7,
        "suitable": ["Advice", "Reminder"],
        "description": "The Horseshoe spread forms a U-shape, specifically designed to analyze obstacles and how to remove them.",
        "positions": {
            1: "Obstacle (The biggest obstacle right now)",
            2: "Foundation (The root or support of the issue)",
            3: "Past (Recent events that have impacted the situation)",
            4: "Present (The actual current situation)",
            5: "Surrounding (Factors influencing the situation)",
            6: "Possible Future (Where things go if no action is taken)",
            7: "Advice (How to remove the obstacle)"
        }
    },

    "Celtic Cross": {
        "cards": 10,
        "suitable": ["Advice", "Reminder"],
        "description": "The most comprehensive and classic tarot spread. Best for in-depth analysis of complex issues, especially those involving others or external factors.",
        "positions": {
            1: "Core (The heart of the current issue)",
            2: "Obstacle (The obstacle in your way)",
            3: "Foundation (The deep root of the issue)",
            4: "Past (Recent relevant events)",
            5: "Possible Future (Outcome if you take no action)",
            6: "Above (Upcoming influence or advice)",
            7: "Self (Your mindset and position)",
            8: "Surroundings (How your environment sees this)",
            9: "Hope/Fear (Your deepest feelings)",
            10: "Outcome (Final conclusion)"
        }
    },

    "Finding": {
        "cards": 3,
        "suitable": ["Find"],
        "description": "Specifically for locating lost items.",
        "positions": {
            1: "Item Status (Where the item is / what condition it's in)",
            2: "Hidden Factor (Why you can't find it / what the obstacle is)",
            3: "Guidance (Clues on how to find it)"
        }
    },

    "Finding Extended": {
        "cards": 5,
        "suitable": ["Find"],
        "description": "Detailed spread for finding items with more clues.",
        "positions": {
            1: "General Area (Which area the item is likely in)",
            2: "Item Condition (Intact / Damaged / Hidden)",
            3: "Why Unfound (Psychological or physical barrier)",
            4: "Time Clue (When you might find it)",
            5: "Action Guidance (How you should search)"
        }
    }
}

question_spread_map = {
    "Yes/No": ["Single Card", "Past-Present-Future"],
    "Find": ["Single Card", "Finding", "Finding Extended"],
    "Advice": ["Past-Present-Future", "Situation-Advice-Outcome", "Celtic Cross"],
    "Reminder": ["Past-Present-Future", "Situation-Advice-Outcome", "Celtic Cross"]
}


path: scripts/draw_tarot.py
content: """
Tarot Card Drawing Script
Uses secrets module for cryptographically secure random card draws
Supports Rider-Waite-Smith 78-card deck, all standard spreads
Supports multiple languages: Chinese (cn) and English (en)
"""
import secrets
import datetime
import json
import sys
import os
import random as _random

# 解决 Windows 终端 emoji 输出问题
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')

# 添加 references 路径以便导入牌数据库
script_dir = os.path.dirname(os.path.abspath(__file__))
ref_dir = os.path.join(os.path.dirname(script_dir), 'references')
sys.path.insert(0, ref_dir)

from cards import major_arcana, minor_arcana
from spreads import spreads as spreads_cn
try:
    from spreads_en import spreads as spreads_en
except Exception:
    spreads_en = None


def draw_cards(count=1, exclude_indices=None):
    """从78张牌中抽取指定数量，不重复"""
    if exclude_indices is None:
        exclude_indices = set()
    else:
        exclude_indices = set(exclude_indices)

    drawn = []
    available = [i for i in range(78) if i not in exclude_indices]

    for _ in range(count):
        if not available:
            break
        idx = secrets.randbelow(len(available))
        card_idx = available[idx]
        orientation = 'Upright' if secrets.randbelow(2) == 0 else 'Reversed'
        drawn.append((card_idx, orientation))
        available.pop(idx)

    return drawn


def get_card_info(card_idx, orientation):
    """获取单张牌的信息"""
    if card_idx <= 21:
        card = major_arcana[card_idx]
        card_type_cn = "Major Arcana"
        card_type_en = "Major Arcana"
        cn = card["cn"]
        en = card["en"]
        element = "Spirit"
        meaning_upright = card["upright"]
        meaning_reversed = card["reversed"]
    else:
        adjusted = card_idx - 22
        suit_idx = adjusted // 14
        value_idx = adjusted % 14
        suit_keys = list(minor_arcana.keys())
        suit_key = suit_keys[suit_idx]
        suit_data = minor_arcana[suit_key]

        element = suit_data["element"]
        suit_cn = suit_data["name_cn"]
        suit_en_map = {"权杖": "Wands", "圣杯": "Cups", "宝剑": "Swords", "星币": "Pentacles"}
        suit_en = suit_en_map.get(suit_cn, suit_cn)
        card_type_cn = "Minor Arcana · " + suit_cn
        card_type_en = "Minor Arcana · " + suit_en

        if value_idx == 0:
            cn = suit_cn + " Ace"
            en = "Ace of " + suit_key.capitalize()
            meaning_upright = suit_data["ace"]["upright"]
            meaning_reversed = suit_data["ace"]["reversed"]
        elif 1 <= value_idx <= 9:
            card_num = value_idx + 1
            cn = suit_cn + " " + str(card_num)
            en = str(card_num) + " of " + suit_key.capitalize()
            meaning_upright = suit_data["numbered"][card_num]["upright"]
            meaning_reversed = suit_data["numbered"][card_num]["reversed"]
        else:
            court_idx = value_idx - 10
            court_cn = suit_data["court"][court_idx]
            court_en_map = {"Page": "Page", "Knight": "Knight", "Queen": "Queen", "King": "King"}
            court_en = court_en_map.get(court_cn, court_cn)
            cn = suit_cn + " " + court_cn
            en = court_en + " of " + suit_key.capitalize()
            cm = suit_data["court_meanings"][court_cn]
            meaning_upright = cm["upright"]
            meaning_reversed = cm["reversed"]

    meaning = meaning_upright if orientation == "Upright" else meaning_reversed

    return {
        "index": card_idx,
        "type_cn": card_type_cn,
        "type_en": card_type_en,
        "cn": cn,
        "en": en,
        "orientation": orientation,
        "element": element,
        "meaning_upright": meaning_upright,
        "meaning_reversed": meaning_reversed,
        "meaning": meaning
    }


def judge_yes_no(cards_info):
    """判断是否问题的答案"""
    card_idx = cards_info[0]["index"]
    orientation = cards_info[0]["orientation"]

    if card_idx <= 21:
        positive_major = [0, 1, 3, 6, 8, 9, 10, 11, 14, 17, 19, 20, 21]
        negative_major = [5, 7, 12, 13, 15, 16, 18]
        if card_idx in positive_major:
            return "YES"
        elif card_idx in negative_major:
            return "NO"
        else:
            return "YES" if orientation == "Upright" else "NO"
    else:
        adjusted = card_idx - 22
        suit_idx = adjusted // 14
        if suit_idx in [0, 1, 3]:
            return "YES" if orientation == "Upright" else "NO"
        else:
            return "NO" if orientation == "Upright" else "YES"


def judge_whereabouts(cards_info, lang='cn'):
    """判断物品在哪里（找物功能）"""
    orientation = cards_info[0]["orientation"]
    seed = secrets.randbelow(1000000)
    rng = _random.Random(seed)

    hints_cn_upright = [
        "物品可能在经常放东西的地方（桌面、包包、口袋），仔细找找",
        "就在视线容易忽略的地方，被其他东西遮住了",
        "近期有移动，和记忆中位置不一致",
        "物品就在附近，耐心找一下就能找到"
    ]
    hints_cn_reversed = [
        "可能在不常用的位置（抽屉深处、衣柜角落）",
        "可能被借走了或放在别人那里",
        "掉进了某个缝隙或容器里",
        "建议回想最后一次使用它的场景"
    ]
    hints_en_upright = [
        "The item is probably in a place where you often put things - check your desk, bag, or pockets",
        "It's in a spot your eyes easily miss, maybe hidden under something",
        "It was moved recently and isn't where you remember leaving it",
        "The item is nearby - just be patient and you'll find it"
    ]
    hints_en_reversed = [
        "It might be in an unused area - deep in a drawer or corner of a closet",
        "It may have been borrowed or placed with someone else",
        "It could have fallen into a gap or container",
        "Try to recall the last time you actually used it"
    ]

    if lang == 'en':
        hints = hints_en_upright if orientation == "Upright" else hints_en_reversed
    else:
        hints = hints_cn_upright if orientation == "Upright" else hints_cn_reversed

    return rng.choice(hints)


def get_texts(lang='cn'):
    """获取多语言文本"""
    if lang == 'en':
        return {
            "header": "[Draw Result]",
            "spread_label": "Spread:",
            "time_label": "Time:",
            "card_label": "Card",
            "position_label": "Position",
            "meaning_label": "Meaning:",
            "answer_label": "[Answer]:",
            "finding_header": "[Item Clues]",
            "insight_header": "[Insight]",
            "reminder_header": "[Reminder]",
            "tip_label": "Hint:",
            "detail_label": "Detail:",
            "guidance_label": "Guidance:",
            "positions_detail": "Position-by-position breakdown:",
            "drawn_cards": "Cards drawn:",
            "yes": "YES",
            "no": "NO",
            "upright": "Upright",
            "reversed": "Reversed",
        }
    else:
        return {
            "header": "[Draw Result]",
            "spread_label": "Card Spread:",
            "time_label": "Time:",
            "card_label": "Card",
            "position_label": "Position",
            "meaning_label": "Meaning:",
            "answer_label": "[Answer]:",
            "finding_header": "[Item Clues]",
            "insight_header": "[Insight]",
            "reminder_header": "[Reminder]",
            "tip_label": "Hint:",
            "detail_label": "Detail:",
            "guidance_label": "Guidance:",
            "positions_detail": "Position-by-position breakdown:",
            "drawn_cards": "Cards drawn:",
            "yes": "Yes",
            "no": "No",
            "upright": "Upright",
            "reversed": "Reversed",
        }


def format_reading(question_type, spread_name, cards_info, lang='cn'):
    """格式化输出解读"""
    texts = get_texts(lang)

    # 选择对应语言的牌阵
    if lang == 'en' and spreads_en:
        spread_data = spreads_en.get(spread_name) or spreads_cn.get(spread_name)
    else:
        spread_data = spreads_cn.get(spread_name)

    positions = spread_data.get("positions", {})

    # 中文用中文牌阵名，英文用英文牌阵名
    if lang == 'en' and spreads_en:
        spread_display = spread_name
    else:
        spread_display = spread_name

    emoji_map_cn = {
        "Major Arcana": "[*]",
        "Minor Arcana": "[*]",
    }
    emoji_map_en = {
        "Major Arcana": "[*]",
        "Minor Arcana": "[*]",
    }
    emoji_map = emoji_map_en if lang == 'en' else emoji_map_cn

    output = []
    output.append(texts["header"])
    output.append(texts["spread_label"] + " " + spread_display)
    output.append(texts["time_label"] + " " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    output.append("")

    for i, card in enumerate(cards_info):
        pos_name = positions.get(i + 1, (texts["card_label"] + " " + str(i + 1)))
        emoji = emoji_map.get(card["type_en" if lang == 'en' else "type_cn"], "[*]")
        card_type = card["en"] if lang == 'en' else card["cn"]
        output.append(texts["card_label"] + " " + str(i + 1) + " [" + pos_name + "]")
        output.append("  " + emoji + " " + card["cn"] + " / " + card["en"] + " (" + card["orientation"] + ")")
        output.append("  " + texts["meaning_label"] + " " + card["meaning"])
        output.append("")

    if question_type in ["Yes/No", "Yes/No"]:
        answer = judge_yes_no(cards_info)
        answer_display = texts["yes"] if answer == "YES" else texts["no"]
        emoji_ans = "[+] " + answer_display if lang == 'en' else "[+] " + answer_display
        output.append(texts["answer_label"] + " " + emoji_ans)
        output.append("")
        c0 = cards_info[0]
        output.append(texts["insight_header"].replace("[Insight]", "Interpretation:") + " " + c0["cn"] + " " + c0["orientation"] + " -- " + c0["meaning"])

    elif question_type == "Find":
        where_hint = judge_whereabouts(cards_info, lang)
        output.append(texts["finding_header"])
        output.append(texts["tip_label"] + " " + where_hint)
        if len(cards_info) >= 2:
            c1 = cards_info[1]
            output.append("")
            output.append(texts["detail_label"] + " " + c1["cn"] + " " + c1["orientation"] + " -- " + c1["meaning"])
        if len(cards_info) >= 3:
            c2 = cards_info[2]
            output.append(texts["guidance_label"] + " " + c2["cn"] + " " + c2["orientation"] + " -- " + c2["meaning"])

    elif question_type in ["Advice", "Reminder"]:
        header = texts["reminder_header"] if question_type == "Reminder" else texts["insight_header"]
        output.append(header)
        cards_str = " + ".join([c["cn"] + "(" + c["orientation"] + ")" for c in cards_info])
        output.append(texts["drawn_cards"] + " " + cards_str)
        output.append("")
        output.append(texts["positions_detail"])
        for i, card in enumerate(cards_info):
            pos_name = positions.get(i + 1, texts["card_label"] + " " + str(i + 1))
            output.append("  · " + pos_name + ": " + card["cn"] + " " + card["orientation"] + " -- " + card["meaning"])

    return "\n".join(output)


def main():
    args = sys.argv[1:]

    if len(args) < 2:
        print(json.dumps({"error": "Usage: draw_tarot.py <question_type> <spread_name> [lang]"}))
        sys.exit(1)

    question_type = args[0]
    spread_name = args[1]
    lang = args[2] if len(args) >= 3 else 'cn'

    # 标准化问题类型
    type_map_cn = {"是否": "Yes/No", "找物": "Find", "建议": "Advice", "提醒": "Reminder"}
    type_map_en = {"Yes/No": "Yes/No", "Find": "Find", "Advice": "Advice", "Reminder": "Reminder"}
    # 反向映射
    en_to_cn = {"Yes/No": "是否", "Find": "找物", "Advice": "建议", "Reminder": "提醒"}
    cn_to_en = {"是否": "Yes/No", "找物": "Find", "建议": "Advice", "提醒": "Reminder"}

    # 如果用英文问题类型，自动切换语言
    if question_type in type_map_en:
        lang = 'en'
        question_type_internal = question_type
    elif question_type in type_map_cn:
        question_type_internal = type_map_cn[question_type]
    else:
        question_type_internal = question_type

    # 获取牌阵数据
    spread_data = spreads_cn.get(spread_name)
    if spreads_en:
        spread_data_en = spreads_en.get(spread_name)
        if spread_data_en:
            spread_data = spread_data_en
    if not spread_data:
        available = list(set(list(spreads_cn.keys()) + (list(spreads_en.keys()) if spreads_en else [])))
        print(json.dumps({"error": "Unknown spread. Available: " + str(available)}))
        sys.exit(1)

    card_count = spread_data["cards"]
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    drawn = draw_cards(card_count)
    cards_info = [get_card_info(idx, ori) for idx, ori in drawn]

    reading = format_reading(question_type_internal, spread_name, cards_info, lang)

    result = {
        "timestamp": timestamp,
        "spread": spread_name,
        "question_type": question_type,
        "lang": lang,
        "cards": cards_info
    }

    print("===TAROT_JSON_START===")
    print(json.dumps(result, ensure_ascii=False, indent=2))
    print("===TAROT_JSON_END===")
    print("")
    print(reading)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)