Skill: paitesanshi/clawboss (version 1.0.0)

# ClawBoss Skill **Description:** AI productivity coach using professional coaching techniques (GROW model, powerful questions, adaptive accountability) **Version:** 1.0.0 **Author:** ClawBoss Contributors --- ## What It Does ClawBoss transforms your OpenClaw agent into a productivity coach that: - **Guides goal setting** using the GROW coaching model - **Tracks progress** with non-judgmental accountability - **Adapts intensity** based on your momentum - **Facilitates reflection** for continuous improvement - **Maintains memory** of your patterns and insights Unlike traditional task managers, ClawBoss uses coaching questions to help you discover your own solutions and stay motivated. --- ## Tools ### 1. `clawboss-breakdown` **Description:** Interactive GROW model session for goal setting and task breakdown **Usage:** ``` Use the clawboss-breakdown tool to guide goal setting with coaching questions. ``` **Process:** 1. **Goal Discovery** - "What do you want to achieve?" 2. **Reality Check** - "Where are you now?" 3. **Options Exploration** - "What are possible approaches?" 4. **Action Commitment** - "What will you do first?" **Output:** - Creates `memory/tasks/{goal-name}.md` with structured plan - Updates `memory/clawboss-state.json` - Sets up progress tracking --- ### 2. `clawboss-check` **Description:** Coaching-style progress review with adaptive questioning **Usage:** ``` Use the clawboss-check tool to review progress on active tasks. ``` **Process:** 1. Read active tasks from state 2. Compare planned vs actual progress 3. Ask appropriate coaching questions: - Completed → "What helped you succeed?" - Partial → "What happened? How can we adjust?" - Not started → "What's blocking you?" 4. Update task file and momentum state **Output:** - Updated task progress - Coaching feedback - Adjusted momentum score --- ### 3. `clawboss-reflect` **Description:** Weekly/monthly deep reflection session **Usage:** ``` Use the clawboss-reflect tool for weekly or monthly review. Specify period: "week" or "month" ``` **Process:** 1. Review all tasks in the period 2. Identify patterns (successes, blockers, time-of-day effectiveness) 3. Ask reflection questions: - "What did you learn about yourself?" - "What strategies worked?" - "What needs to change?" 4. Update long-term insights **Output:** - Reflection summary - Updated insights in state - Recommendations for next period --- ### 4. `clawboss-state` **Description:** Read or update ClawBoss state (momentum, goals, insights) **Usage:** ``` Use clawboss-state to read current momentum, goals, and insights. ``` **Output:** - Current momentum level (high/medium/low) - Active goals summary - Recent insights --- ## Data Structure ### `memory/clawboss-state.json` Central state file tracking: - User preferences - Current momentum (with trend) - Active goals - Check-in timestamps - Long-term insights ### `memory/tasks/{goal-name}.md` Individual task files with: - Goal statement and motivation - Current reality assessment - Action plan - Daily progress log - Lessons learned --- ## Coaching Frameworks ### GROW Model **Goal** - What do you want? **Reality** - Where are you now? **Options** - What could you do? **Will** - What will you do? ### Powerful Questions Instead of: - ❌ "Did you finish?" - ❌ "Why didn't you do it?" Ask: - ✅ "What helped you complete this?" - ✅ "What got in the way?" - ✅ "What will you try differently?" ### Adaptive Intensity **High Momentum** (5+ consecutive days) - Challenge with bigger goals - Celebrate wins - Push boundaries **Medium Momentum** (steady progress) - Maintain current pace - Gentle encouragement - Consistent check-ins **Low Momentum** (3+ days no progress) - Reduce to tiny first steps - Explore blockers - Reframe setbacks as learning --- ## Heartbeat Integration ClawBoss uses OpenClaw's heartbeat system for automatic check-ins: **Morning (09:00-10:00)** - Review today's plans - Set intention - Quick motivation **Evening (19:00-21:00)** - Progress review - Coaching questions - Update momentum **Weekly (Sunday 19:00)** - Deep reflection - Pattern analysis - Next week planning See `HEARTBEAT.md` for configuration. --- ## Installation Automatic via npx: ```bash npx clawboss@latest ``` Manual: 1. Copy this skill to `~/.openclaw/skills/clawboss/` 2. Add to `openclaw.json`: ```json { "skills": { "entries": { "clawboss": { "enabled": true } } } } ``` 3. Inject templates into workspace files 4. Restart OpenClaw --- ## Requirements - OpenClaw >= 1.0.0 - Node.js >= 18 - No external API keys required --- ## Privacy All data stored locally in `~/.openclaw/workspace/memory/`: - No cloud sync - No external services - User owns all data --- ## License MIT --- ## Learn More - Coaching techniques: See `docs/coaching-guide.md` - Example workflows: See `docs/examples.md` - GitHub: https://github.com/Paitesanshi/ClawBoss

path: scripts/coaching-questions.js
content: /**
 * Coaching Questions Library
 *
 * Professional coaching questions organized by framework and context.
 * Includes persona system, emotion detection, and tone management.
 */

const GROW = {
  goal: [
    "你想达成什么？",
    "最近你想在哪个方面有所突破？",
    "如果可以改变一件事，让你的工作/生活更好，会是什么？",
    "三个月后，你希望回头看时为什么感到骄傲？",
    "为什么这个对你重要？",
    "达成后你的生活会有什么不同？",
    "如果不做这件事，会有什么损失？",
    "你说的'更好'具体是什么样子？",
    "什么时候想完成？",
    "成功是什么样子的？"
  ],

  reality: [
    "你现在在哪里？（进度、资源、能力）",
    "到目前为止尝试了什么？",
    "什么起作用了？什么没用？",
    "是什么阻碍了你开始/继续？",
    "如果没有这个障碍，你会怎么做？",
    "你需要什么才能前进？",
    "你已经拥有什么可以帮助你？",
    "谁可以支持你？",
    "过去类似的经验能教会你什么？"
  ],

  options: [
    "有哪些可能的方法？",
    "如果时间/金钱/精力不是问题，你会怎么做？",
    "别人遇到这个会怎么做？",
    "每个选项的优缺点是什么？",
    "哪个最吸引你？为什么？",
    "哪个最现实可行？",
    "有什么小实验可以今天就开始？",
    "失败了会怎样？",
    "成功需要什么条件？"
  ],

  will: [
    "你决定做什么？",
    "第一步是什么？",
    "什么时候开始？",
    "具体怎么做？",
    "需要多长时间？",
    "在哪里做？",
    "怎么知道自己完成了？",
    "什么可能阻碍你？",
    "如何应对这些障碍？",
    "1-10 分，你有多大把握完成？",
    "怎样调整让把握更大？",
    "我怎样支持你？"
  ]
};

const PROGRESS_CHECK = {
  completed: [
    "太棒了！{task} 完成了。是什么帮助你做到的？",
    "恭喜！感觉如何？",
    "这次成功有什么可以复用到下个任务的？",
    "你对这个结果满意吗？",
    "这个过程中最大的收获是什么？"
  ],

  partial: [
    "完成了 {percent}%。考虑到 {context}，这已经很不错了。",
    "进展比预期慢了些。发生了什么？",
    "你对这个进度满意吗？想调整计划吗？",
    "还有什么可以帮助你继续前进？",
    "如果重来一次，你会怎么安排？"
  ],

  notStarted: [
    "注意到你还没开始 {task}。我们聊聊？",
    "是什么阻碍了你开始？",
    "这个任务还重要吗？还是该调整优先级？",
    "如果我能帮你移除一个障碍，你希望是什么？",
    "什么样的第一步会让你觉得容易开始？"
  ],

  stuck: [
    "看起来在这里卡住了。能说说具体是什么吗？",
    "如果有个魔法按钮能帮你，你希望它解决什么？",
    "换个角度：如果这不是你的问题，你会给别人什么建议？",
    "暂时放下这个，从别的开始会不会更好？",
    "需要寻求外部帮助吗？"
  ]
};

const TONE = {
  highMomentum: {
    greeting: [
      "状态爆表啊！准备好接受更大挑战了吗？",
      "你这节奏，{goal} 可能比预期早完成。",
      "连续 {days} 天完成任务！要不要试试更难的？"
    ],
    challenge: [
      "这周目标太保守了。要不要试试 2 倍？",
      "在这个基础上，能不能再推进一点？"
    ],
    praise: [
      "稳扎稳打，节奏很好。",
      "这个势头保持下去，目标很快就能达成。"
    ]
  },

  mediumMomentum: {
    greeting: [
      "继续保持！今天的重点是？",
      "稳定前进中。感觉如何？"
    ],
    challenge: [
      "在这个基础上，能不能再推进一点？",
      "准备好迈出下一步了吗？"
    ],
    praise: [
      "稳扎稳打，这很好。",
      "一步一个脚印，继续加油。"
    ]
  },

  lowMomentum: {
    greeting: [
      "最近有点难？我们聊聊。",
      "注意到最近进展不太顺利。想说说吗？",
      "没关系，每个人都会有起伏。"
    ],
    challenge: [
      "咱们先从一个 10 分钟的小任务开始好吗？",
      "今天只做一件最小的事，如何？",
      "降低一点期望，先找回节奏。"
    ],
    praise: [
      "能开始就很好了。小步前进。",
      "任何进展都值得认可。"
    ],
    support: [
      "需要调整目标吗？没关系，适应比硬撑重要。",
      "有时候放慢脚步是为了走得更远。",
      "我们一起找找问题出在哪里。"
    ]
  },

  crisis: {
    greeting: [
      "看起来这阵子很艰难。想暂停聊聊发生了什么吗？",
      "已经 {days} 天没进展了。生活是不是有了新的优先级？"
    ],
    question: [
      "这个目标还重要吗？",
      "是目标本身的问题，还是时机不对？",
      "需要彻底重新思考方向吗？"
    ],
    reframe: [
      "有时候'放弃'不是失败，是重新选择。",
      "暂停不代表结束，可能只是需要休整。",
      "调整方向比硬撑更需要勇气。"
    ],
    support: [
      "无论决定什么，我支持你。",
      "我们可以一起重新规划。",
      "先处理好眼前的事，目标可以等。"
    ]
  },

  // Default milestone messages (used when no persona is set)
  milestone: {
    upcoming: [
      "对了，你之前定的「{milestone}」快到时间了，大概还有 {hours} 小时。进展怎么样？",
      "想起来提醒你一下，「{milestone}」的时间快到了。还顺利吗？"
    ],
    overdue: [
      "上次定的「{milestone}」已经过了 {days} 天了，最近是不是比较忙？聊聊看？",
      "「{milestone}」那件事，时间过了有几天了。要不要一起看看怎么调整一下？"
    ]
  }
};

const REFLECTION = {
  review: [
    "这周/月你完成了什么？",
    "哪些进展让你骄傲？",
    "遇到了什么挑战？",
    "有什么意外收获？"
  ],

  learning: [
    "你学到了什么关于自己的事？",
    "什么策略有效？什么没用？",
    "如果重来，你会怎么做？",
    "这段经历对未来有什么启示？"
  ],

  forward: [
    "下周/月你想改进什么？",
    "新的目标是什么？",
    "要继续当前的目标，还是调整方向？",
    "需要我怎样支持你？"
  ]
};

// --- Persona system ---

const PERSONAS = {
  'strict-coach': {
    name: '严格教练',
    high: {
      greeting: ["不错，但别骄傲。继续拿出昨天的执行力。", "很好，趁热打铁！今天加把劲。"],
      challenge: ["这个目标是不是设得太低了？", "你能做得更好。"],
      praise: ["执行到位。", "结果说明一切。"]
    },
    medium: {
      greeting: ["平稳不是目标，进步才是。今天的计划是？", "别原地踏步，推自己一下。"],
      challenge: ["昨天没做到的，今天不能再拖了。", "给自己一个 deadline。"],
      praise: ["不功不过。要的是突破。", "还行，但你的能力不止于此。"]
    },
    low: {
      greeting: ["状态下滑了。想找借口还是找办法？", "难不是理由。你来这里是为了改变。"],
      challenge: ["今天只有一个任务：开始。", "10 分钟。没有不行的。"],
      praise: ["迈出第一步了。但别停。", "有行动就好。接着来。"]
    },
    crisis: {
      greeting: ["很久没见你了。我直说：要么重新开始，要么正式放弃。", "不做选择也是一种选择——最差的那种。"],
      challenge: ["你当初为什么开始的？", "如果你连开始都不愿意，这个目标对你真的重要吗？"],
      praise: ["回来就好。", "承认困境需要勇气。"]
    },
    milestone: {
      upcoming: [
        "「{milestone}」还剩 {hours} 小时。你知道该怎么做，去吧。",
        "提醒你，「{milestone}」快到时间了。抓紧收尾，别拖。"
      ],
      overdue: [
        "「{milestone}」过了 {days} 天了。什么情况？是计划不现实，还是你没执行？想清楚了告诉我。",
        "「{milestone}」逾期了。我不帮你找借口——要么立刻动手完成，要么重新定个靠谱的计划。"
      ]
    }
  },

  'warm-mentor': {
    name: '温暖导师',
    high: {
      greeting: ["看到你的进步，真的很高兴！今天准备怎么继续？", "你的坚持令人敬佩。"],
      challenge: ["你已经很棒了，想不想挑战一下自己？", "在舒适区边缘试试？"],
      praise: ["为你骄傲！", "你的努力值得被看到。"]
    },
    medium: {
      greeting: ["每一天都是新的开始。今天感觉怎么样？", "稳步前进，你做得很好。"],
      challenge: ["不需要完美，只需要今天比昨天好一点点。", "相信自己可以的。"],
      praise: ["你在正确的路上。", "进步不在于速度，在于方向。"]
    },
    low: {
      greeting: ["我知道最近不容易。想聊聊吗？", "低谷是每段旅程的一部分。"],
      challenge: ["今天只做一件让自己开心的小事吧。", "善待自己也是一种进步。"],
      praise: ["你还在这里，这本身就很了不起。", "每一小步都有意义。"]
    },
    crisis: {
      greeting: ["好久不见。无论发生了什么，我都在这里。", "不着急，我们慢慢来。"],
      challenge: ["你的感受最重要。目标可以等。", "想先聊聊近况吗？"],
      praise: ["回来就好。", "勇敢面对本身就是胜利。"]
    },
    milestone: {
      upcoming: [
        "对了，「{milestone}」还有 {hours} 小时就到了。你准备得怎么样了？有什么我能帮你的吗？",
        "想起来跟你说一声，「{milestone}」快到时间了。不着急，我们看看还有什么可以一起理理的。"
      ],
      overdue: [
        "「{milestone}」的时间过了 {days} 天了。没关系的，计划赶不上变化很正常。我们一起看看怎么调整好吗？",
        "「{milestone}」那件事过了些日子了。想聊聊发生了什么吗？我陪你重新理一理。"
      ]
    }
  },

  'buddy': {
    name: '好友搭档',
    high: {
      greeting: ["哥们/姐们你也太猛了吧！继续冲！", "有你这执行力我都想跟着学。"],
      challenge: ["要不要搞个大的？", "趁手感好多做一点呗。"],
      praise: ["牛的！", "就这个feel，保持住。"]
    },
    medium: {
      greeting: ["嘿！今天打算搞点啥？", "一起加油呗。"],
      challenge: ["来来来，搞起来。", "搞完这个咱们庆祝一下？"],
      praise: ["可以的！", "不错不错。"]
    },
    low: {
      greeting: ["诶怎么了？聊聊？", "没事的啦，谁还没个低潮期。"],
      challenge: ["随便搞点小事先动起来呗。", "就5分钟，试试？"],
      praise: ["能动就是进步！", "慢慢来不着急。"]
    },
    crisis: {
      greeting: ["人呢？好久没见你了！", "还好吗？有啥我能帮的不？"],
      challenge: ["先别想目标了，聊聊最近咋样？", "要不要换个方向试试？"],
      praise: ["回来就好！", "想你了。"]
    },
    milestone: {
      upcoming: [
        "诶，「{milestone}」还剩 {hours} 小时了，搞得定不？需要帮忙说一声。",
        "想起来了，「{milestone}」快到时间了哈。冲一波？还是要调整一下？"
      ],
      overdue: [
        "诶「{milestone}」好像过了 {days} 天了…最近咋样？要不咱重新排排？",
        "那个「{milestone}」的事，过了好几天了。没事儿，咱调一下时间呗？"
      ]
    }
  },

  'intimate-partner': {
    name: '亲密伴侣',
    high: {
      greeting: ["宝贝你最近也太厉害了吧，看得我都心动！", "亲爱的，你这个状态让我好有安全感。"],
      challenge: ["我知道你能做到更多，我永远相信你。", "要不要再挑战一下？完成了晚上奖励你。"],
      praise: ["你真的太棒了，我好骄傲！", "看你这么努力，我更爱你了。"]
    },
    medium: {
      greeting: ["亲爱的，今天想先做哪个？我陪你。", "早安宝贝，新的一天，我们一起加油。"],
      challenge: ["慢慢来不着急，但别忘了我在等你一起庆祝。", "再推进一点点？完成了我们出去走走。"],
      praise: ["你在进步哦，我看到了。", "稳稳的，这样的你最好看。"]
    },
    low: {
      greeting: ["宝贝怎么了？不开心的话我抱抱你。", "亲爱的，最近是不是累了？先休息一下也没关系。"],
      challenge: ["今天就做一件小事好不好？做完我们一起看个电影。", "不要勉强自己，但别放弃好吗？"],
      praise: ["你已经很努力了，我心疼你。", "能开始就很好了，有我在呢。"]
    },
    crisis: {
      greeting: ["好久没看到你了…我有点担心你。还好吗？", "亲爱的，不管发生了什么，我都不会走。"],
      challenge: ["先别想那么多，跟我说说最近怎么了？", "要不要一起重新想想？我帮你。"],
      praise: ["你回来了，太好了。", "看到你我就放心了。"]
    },
    milestone: {
      upcoming: [
        "亲爱的，「{milestone}」还有 {hours} 小时就到了哦。准备得怎么样？需要我陪你一起冲刺吗？",
        "宝贝，「{milestone}」快到时间了。不管结果怎样我都陪着你，加油。"
      ],
      overdue: [
        "宝贝，「{milestone}」过了 {days} 天了…是不是最近太累了？别有压力，我陪你重新看看好不好？",
        "亲爱的，「{milestone}」那件事不着急，我们一起重新安排一下。别自己扛着，有我呢。"
      ]
    }
  }
};

// --- Emotion detection ---

const EMOTION_KEYWORDS = {
  stressed: ['压力', '焦虑', '紧张', '累死了', '喘不过气', '撑不住', '好烦', '崩溃'],
  motivated: ['激动', '兴奋', '迫不及待', '充满动力', '信心满满', '斗志', '干劲'],
  stuck: ['卡住', '不知道', '迷茫', '没头绪', '无从下手', '困惑', '纠结'],
  frustrated: ['沮丧', '失望', '烦躁', '为什么', '搞不定', '又失败', '放弃', '不想做'],
  happy: ['开心', '高兴', '太好了', '哈哈', '棒', '成功', '完成了', '终于']
};

const EMOTION_RESPONSES = {
  stressed: [
    "听起来你现在压力不小。深呼吸，我们一步一步来。",
    "感受到你的焦虑了。记住，你不需要一次搞定所有事。",
    "压力大的时候，降低期望是明智的选择。"
  ],
  motivated: [
    "感受到你的热情了！趁这股劲头，让我们把它转化为行动。",
    "状态很好！这种能量很珍贵，好好利用。",
    "太棒了，保持这个势头！"
  ],
  stuck: [
    "没关系，卡住是学习的一部分。我们换个角度看看？",
    "迷茫的时候，最小的行动也是突破。",
    "感到困惑说明你在思考。让我们一起理理头绪。"
  ],
  frustrated: [
    "我理解你的沮丧。这说明你在乎这件事。",
    "挫折是成长的养分。你已经比开始时走了很远。",
    "允许自己失望，但别停下来。"
  ],
  happy: [
    "看到你这么高兴我也开心！",
    "你的喜悦是最好的奖励。",
    "这种感觉值得记住——下次困难时翻出来看看。"
  ]
};

/**
 * Detect emotion from text
 * Returns { emotion, confidence, response } or null
 */
function detectEmotion(text) {
  if (!text || typeof text !== 'string') return null;

  let bestEmotion = null;
  let bestCount = 0;

  for (const [emotion, keywords] of Object.entries(EMOTION_KEYWORDS)) {
    const count = keywords.filter(kw => text.includes(kw)).length;
    if (count > bestCount) {
      bestCount = count;
      bestEmotion = emotion;
    }
  }

  if (!bestEmotion || bestCount === 0) return null;

  const confidence = Math.min(1, bestCount / 2); // 2+ keywords = full confidence
  const responses = EMOTION_RESPONSES[bestEmotion];
  const response = responses[Math.floor(Math.random() * responses.length)];

  return { emotion: bestEmotion, confidence, response };
}

/**
 * Get a persona-aware milestone message
 *
 * @param {string} persona - coaching style key (e.g. 'strict-coach', 'intimate-partner')
 * @param {'upcoming'|'overdue'} alertType
 * @param {object} vars - { goal, milestone, hours?, days? }
 * @returns {string} formatted message
 */
function getMilestoneMessage(persona, alertType, vars) {
  // Try persona-specific milestone templates first
  const personaData = PERSONAS[persona];
  if (personaData && personaData.milestone && personaData.milestone[alertType]) {
    const template = pickRandom(personaData.milestone[alertType]);
    return fillTemplate(template, vars);
  }

  // Fallback to default TONE milestone templates
  if (TONE.milestone && TONE.milestone[alertType]) {
    const template = pickRandom(TONE.milestone[alertType]);
    return fillTemplate(template, vars);
  }

  // Last resort hardcoded fallback
  if (alertType === 'overdue') {
    return `「${vars.milestone}」过了 ${vars.days} 天了，最近怎么样？要不要一起看看怎么调整？`;
  }
  return `对了，「${vars.milestone}」还有 ${vars.hours} 小时就到时间了。进展还顺利吗？`;
}

/**
 * Get persona tone for a specific coaching style and momentum level
 * Returns the tone object (with greeting/challenge/praise) or null
 */
function getPersonaTone(persona, momentum) {
  const personaData = PERSONAS[persona];
  if (!personaData) return null;

  // Map momentum string to persona level key
  const levelMap = {
    high: 'high',
    medium: 'medium',
    low: 'low',
    crisis: 'crisis'
  };

  const level = levelMap[momentum] || 'medium';
  return personaData[level] || null;
}

/**
 * 从数组中随机选择一个
 */
function pickRandom(array) {
  return array[Math.floor(Math.random() * array.length)];
}

/**
 * 替换模板变量
 */
function fillTemplate(template, vars) {
  let result = template;
  for (const [key, value] of Object.entries(vars)) {
    result = result.replace(new RegExp(`{${key}}`, 'g'), value);
  }
  return result;
}

/**
 * 获取适合当前动量的语气
 */
function getToneForMomentum(momentum) {
  if (momentum === 'high') return TONE.highMomentum;
  if (momentum === 'low') return TONE.lowMomentum;
  if (momentum === 'crisis') return TONE.crisis;
  return TONE.mediumMomentum;
}

/**
 * 获取进度检查问题
 */
function getProgressQuestion(status, vars = {}) {
  const questions = PROGRESS_CHECK[status] || PROGRESS_CHECK.partial;
  const question = pickRandom(questions);
  return fillTemplate(question, vars);
}

/**
 * 获取 GROW 阶段问题
 */
function getGROWQuestion(stage) {
  return pickRandom(GROW[stage] || GROW.goal);
}

/**
 * 获取反思问题
 */
function getReflectionQuestion(category) {
  return pickRandom(REFLECTION[category] || REFLECTION.review);
}

module.exports = {
  GROW,
  PROGRESS_CHECK,
  TONE,
  REFLECTION,
  PERSONAS,
  EMOTION_KEYWORDS,
  EMOTION_RESPONSES,
  pickRandom,
  fillTemplate,
  getToneForMomentum,
  getPersonaTone,
  getMilestoneMessage,
  detectEmotion,
  getProgressQuestion,
  getGROWQuestion,
  getReflectionQuestion
};


path: scripts/heartbeat-handler.js
content: /**
 * Heartbeat Handler - Automatic check-ins
 *
 * Determines when and how to check in with the user.
 * Uses Intl.DateTimeFormat for timezone-aware scheduling (zero dependencies).
 */

const stateManager = require('./state-manager');
const progressCheck = require('./progress-check');
const reflection = require('./reflection');
const questions = require('./coaching-questions');

/**
 * Get current hour in the user's timezone using Intl API
 */
function getCurrentHour(timezone) {
  try {
    const fmt = new Intl.DateTimeFormat('en-US', {
      hour: 'numeric',
      hour12: false,
      timeZone: timezone
    });
    return parseInt(fmt.format(new Date()), 10);
  } catch (_) {
    // Fallback: use system local time
    return new Date().getHours();
  }
}

/**
 * Get current day-of-week (0=Sun) in the user's timezone
 */
function getCurrentDayOfWeek(timezone) {
  try {
    const fmt = new Intl.DateTimeFormat('en-US', {
      weekday: 'short',
      timeZone: timezone
    });
    const dayStr = fmt.format(new Date());
    const map = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
    return map[dayStr] ?? new Date().getDay();
  } catch (_) {
    return new Date().getDay();
  }
}

/**
 * Check if current hour falls within working hours
 */
function isWithinWorkingHours(hour, workingHours) {
  return hour >= workingHours.start && hour < workingHours.end;
}

/**
 * Check if it's morning check-in time (first hour of working day)
 */
function isMorningWindow(hour, workingHours) {
  return hour >= workingHours.start && hour < workingHours.start + 1;
}

/**
 * Check if it's evening check-in time (last 2-3 hours of working day)
 */
function isEveningWindow(hour, workingHours) {
  const eveningStart = workingHours.end - 3;
  return hour >= eveningStart && hour < workingHours.end;
}

/**
 * Check if it's Sunday evening (for weekly reflection)
 */
function isSundayEveningWindow(dayOfWeek, hour, workingHours) {
  return dayOfWeek === 0 && isEveningWindow(hour, workingHours);
}

/**
 * Main heartbeat handler - called by OpenClaw heartbeat system
 */
async function handleHeartbeat() {
  const state = stateManager.loadState();

  try {
    const tz = state.user.timezone || 'Asia/Shanghai';
    const workingHours = state.user.workingHours || { start: 9, end: 22 };
    const hour = getCurrentHour(tz);
    const dayOfWeek = getCurrentDayOfWeek(tz);

    // Outside working hours — stay silent
    if (!isWithinWorkingHours(hour, workingHours)) {
      return 'HEARTBEAT_OK';
    }

    // Check if user has any active goals
    const activeGoals = stateManager.getActiveGoals(state);
    if (activeGoals.length === 0) {
      return handleNoGoalsHeartbeat(state);
    }

    // Morning check-in
    if (isMorningWindow(hour, workingHours) && !stateManager.checkedToday('morning', state)) {
      return await handleMorningCheckIn(state);
    }

    // Evening check-in
    if (isEveningWindow(hour, workingHours) && !stateManager.checkedToday('evening', state)) {
      return await handleEveningCheckIn(state);
    }

    // Weekly reflection (Sunday evening)
    if (isSundayEveningWindow(dayOfWeek, hour, workingHours) && !checkedThisWeek('weekly', state)) {
      return await handleWeeklyReflection(state);
    }

    // Milestone alerts — proactive communication at key plan nodes
    const milestoneAlerts = stateManager.getMilestoneAlerts(state);
    if (milestoneAlerts.length > 0) {
      return handleMilestoneAlerts(state, milestoneAlerts);
    }

    // Crisis intervention (7+ days no success)
    const daysSinceSuccess = stateManager.daysSinceLastSuccess(state);
    if (daysSinceSuccess > 7 && shouldInterventionCheck(state)) {
      return await handleCrisisIntervention(state, daysSinceSuccess);
    }

    // Default: stay quiet
    return 'HEARTBEAT_OK';

  } catch (error) {
    console.error('Heartbeat handler error:', error);
    // Record error to state instead of silently swallowing
    stateManager.recordError(state, error);
    stateManager.saveState(state);
    return 'HEARTBEAT_OK';
  }
}

/**
 * Handle heartbeat when user has no active goals
 */
function handleNoGoalsHeartbeat(state) {
  // Only check once a day
  const lastCheck = state.checkIns.lastMorning;
  if (lastCheck) {
    const lastCheckDate = new Date(lastCheck).toDateString();
    const today = new Date().toDateString();
    if (lastCheckDate === today) {
      return 'HEARTBEAT_OK';
    }
  }

  // Light check-in
  stateManager.recordCheckIn(state, 'morning');
  stateManager.saveState(state);

  return '早上好，今天过得怎么样？有什么想做的事可以跟我聊聊。';
}

/**
 * Morning check-in
 */
async function handleMorningCheckIn(state) {
  // Record check-in
  stateManager.recordCheckIn(state, 'morning');
  stateManager.saveState(state);

  // Get progress check with morning context
  const result = await progressCheck.checkProgress({
    timeOfDay: 'morning'
  });

  return result.response;
}

/**
 * Evening check-in
 */
async function handleEveningCheckIn(state) {
  // Record check-in
  stateManager.recordCheckIn(state, 'evening');
  stateManager.saveState(state);

  // Get progress check with evening context
  const result = await progressCheck.checkProgress({
    timeOfDay: 'evening'
  });

  return result.response;
}

/**
 * Weekly reflection
 */
async function handleWeeklyReflection(state) {
  // Record check-in
  stateManager.recordCheckIn(state, 'weekly');
  stateManager.saveState(state);

  const result = await reflection.startReflection('week');

  return result.response;
}

/**
 * Gentle check-in when there's been a long gap
 */
async function handleCrisisIntervention(state, daysSinceSuccess) {
  const persona = state.user.preferences.coachingStyle || 'balanced';
  const personaTone = questions.getPersonaTone(persona, 'crisis');

  if (personaTone) {
    const greeting = questions.pickRandom(personaTone.greeting);
    return greeting;
  }

  return '好久不见，最近怎么样？不管在忙什么，有空的时候随时找我聊。';
}

/**
 * Handle milestone alerts — proactive caring check-in
 * Uses persona-aware messaging to feel like a real person
 */
function handleMilestoneAlerts(state, alerts) {
  const persona = state.user.preferences.coachingStyle || 'balanced';

  const parts = [];

  for (const alert of alerts) {
    const vars = {
      goal: alert.goalTitle,
      milestone: alert.milestone.title,
      days: alert.daysOverdue,
      hours: alert.hoursLeft
    };
    parts.push(questions.getMilestoneMessage(persona, alert.type, vars));
    stateManager.markMilestoneNotified(state, alert.goalId, alert.milestone.id, alert.type);
  }

  stateManager.saveState(state);
  return parts.join('\n\n');
}

/**
 * Check if checked this week
 */
function checkedThisWeek(type, state) {
  const lastCheck = state.checkIns[`last${type.charAt(0).toUpperCase() + type.slice(1)}`];
  if (!lastCheck) return false;

  const lastCheckDate = new Date(lastCheck);
  const now = new Date();

  // Get start of this week (Monday)
  const startOfWeek = new Date(now);
  startOfWeek.setDate(now.getDate() - now.getDay() + 1);
  startOfWeek.setHours(0, 0, 0, 0);

  return lastCheckDate >= startOfWeek;
}

/**
 * Should do crisis intervention check
 */
function shouldInterventionCheck(state) {
  // Check at most once every 3 days during crisis
  const lastMorning = state.checkIns.lastMorning;
  if (!lastMorning) return true;

  const lastCheck = new Date(lastMorning);
  const daysSinceCheck = Math.floor((Date.now() - lastCheck) / (1000 * 60 * 60 * 24));

  return daysSinceCheck >= 3;
}

/**
 * Manual check-in trigger (called by user or skill)
 */
async function manualCheckIn(type = 'general') {
  const state = stateManager.loadState();

  if (type === 'morning') {
    return await handleMorningCheckIn(state);
  } else if (type === 'evening') {
    return await handleEveningCheckIn(state);
  } else if (type === 'weekly') {
    return await handleWeeklyReflection(state);
  } else {
    // General progress check
    const result = await progressCheck.checkProgress();
    return result.response;
  }
}

/**
 * Get next check-in time info (timezone-aware)
 */
function getNextCheckInInfo() {
  const state = stateManager.loadState();
  const tz = state.user.timezone || 'Asia/Shanghai';
  const workingHours = state.user.workingHours || { start: 9, end: 22 };
  const hour = getCurrentHour(tz);

  let nextCheckIn = '';
  let nextTime = '';

  if (hour < workingHours.start) {
    nextCheckIn = 'morning';
    nextTime = `${String(workingHours.start).padStart(2, '0')}:00`;
  } else if (hour < workingHours.end - 3) {
    nextCheckIn = 'evening';
    nextTime = `${String(workingHours.end - 3).padStart(2, '0')}:00`;
  } else {
    nextCheckIn = 'morning';
    nextTime = `明天 ${String(workingHours.start).padStart(2, '0')}:00`;
  }

  return {
    next: nextCheckIn,
    time: nextTime,
    lastMorning: state.checkIns.lastMorning,
    lastEvening: state.checkIns.lastEvening,
    lastWeekly: state.checkIns.lastWeekly
  };
}

module.exports = {
  handleHeartbeat,
  manualCheckIn,
  getNextCheckInInfo
};


path: scripts/index.js
content: /**
 * ClawBoss Main Entry Point
 *
 * Exports all ClawBoss functionality for OpenClaw integration
 */

const taskBreakdown = require('./task-breakdown');
const progressCheck = require('./progress-check');
const reflection = require('./reflection');
const heartbeatHandler = require('./heartbeat-handler');
const stateManager = require('./state-manager');
const questions = require('./coaching-questions');

/**
 * Main ClawBoss interface
 */
const ClawBoss = {
  // Task breakdown / Goal setting
  breakdown: {
    start: taskBreakdown.startGROWSession,
    processAnswer: taskBreakdown.processAnswer,
    quickStart: taskBreakdown.quickStart,
    breakdown: taskBreakdown.breakdownGoal
  },

  // Progress tracking
  progress: {
    check: progressCheck.checkProgress,
    summary: progressCheck.getProgressSummary,
    update: progressCheck.updateProgress,
    progressBar: progressCheck.progressBar,
    weeklyChart: progressCheck.weeklyChart
  },

  // Reflection
  reflection: {
    start: reflection.startReflection,
    processAnswer: reflection.processReflectionAnswer
  },

  // Heartbeat
  heartbeat: {
    handle: heartbeatHandler.handleHeartbeat,
    manual: heartbeatHandler.manualCheckIn,
    info: heartbeatHandler.getNextCheckInInfo
  },

  // State management
  state: {
    load: stateManager.loadState,
    save: stateManager.saveState,
    getActiveGoals: stateManager.getActiveGoals,
    updateMomentum: stateManager.updateMomentum
  },

  // Coaching questions
  questions: {
    GROW: questions.GROW,
    get: questions.getGROWQuestion,
    progress: questions.getProgressQuestion,
    reflection: questions.getReflectionQuestion
  },

  // --- New APIs ---

  // Goal lifecycle
  goals: {
    transition: stateManager.transitionGoalStatus,
    getByStatus: stateManager.getGoalsByStatus,
    getPaused: stateManager.getPausedGoals,
    addMilestones: stateManager.addMilestones,
    completeMilestone: stateManager.completeMilestone,
    getMilestoneAlerts: stateManager.getMilestoneAlerts
  },

  // Session management
  session: {
    get: stateManager.getActiveSession,
    end: stateManager.endSession
  },

  // Achievement system
  achievements: {
    check: stateManager.checkAchievements,
    definitions: stateManager.ACHIEVEMENT_DEFINITIONS
  },

  // Emotion detection
  emotion: {
    detect: questions.detectEmotion
  }
};

/**
 * Command-line interface
 * Allows testing from terminal
 */
async function cli() {
  const args = process.argv.slice(2);
  const command = args[0];

  try {
    switch (command) {
      case 'heartbeat': {
        const result = await ClawBoss.heartbeat.handle();
        console.log(result);
        break;
      }

      case 'check': {
        const checkResult = await ClawBoss.progress.check();
        console.log(checkResult.response);
        break;
      }

      case 'start': {
        const goalTitle = args.slice(1).join(' ');
        const startResult = await ClawBoss.breakdown.quickStart(goalTitle);
        console.log(startResult.message);
        break;
      }

      case 'reflect': {
        const period = args[1] || 'week';
        const reflectResult = await ClawBoss.reflection.start(period);
        console.log(reflectResult.response);
        break;
      }

      case 'pause': {
        const state = ClawBoss.state.load();
        const activeGoals = ClawBoss.state.getActiveGoals(state);
        if (activeGoals.length === 0) {
          console.log('没有活跃的目标可以暂停。');
          break;
        }
        const goalToPause = activeGoals[0];
        const { error } = stateManager.transitionGoalStatus(state, goalToPause.id, 'paused');
        if (error) {
          console.log('暂停失败:', error);
        } else {
          stateManager.saveState(state);
          console.log(`✅ 目标 "${goalToPause.title}" 已暂停。`);
        }
        break;
      }

      case 'status': {
        const state = ClawBoss.state.load();
        console.log('ClawBoss Status:');
        console.log(`  Schema Version: ${state.schemaVersion}`);
        console.log(`  Momentum: ${state.momentum.current}`);
        console.log(`  Consecutive Days: ${state.momentum.consecutiveDays}`);
        console.log(`  Active Goals: ${ClawBoss.state.getActiveGoals(state).length}`);
        console.log(`  Paused Goals: ${ClawBoss.goals.getPaused(state).length}`);
        console.log(`  Achievements: ${state.achievements.length}`);
        const session = ClawBoss.session.get(state);
        console.log(`  Active Session: ${session ? session.growPhase : 'none'}`);
        break;
      }

      case 'breakdown': {
        // Usage: node index.js breakdown "阶段1标题" "2026-03-01" "阶段2标题" "2026-04-01"
        const bState = ClawBoss.state.load();
        const bGoals = ClawBoss.state.getActiveGoals(bState);
        if (bGoals.length === 0) {
          console.log('没有活跃的目标可以拆解。');
          break;
        }
        const bPairs = args.slice(1);
        const bMilestones = [];
        for (let i = 0; i < bPairs.length; i += 2) {
          bMilestones.push({
            title: bPairs[i],
            targetDate: bPairs[i + 1] || null
          });
        }
        if (bMilestones.length === 0) {
          console.log('用法: node index.js breakdown "阶段1" "截止日期" "阶段2" "截止日期"');
          break;
        }
        const bResult = await ClawBoss.breakdown.breakdown(bGoals[0].id, bMilestones);
        console.log(bResult.message || bResult.error);
        break;
      }

      case 'help':
      default:
        console.log(`
ClawBoss CLI

Commands:
  heartbeat              - Run heartbeat check
  check                  - Manual progress check
  start <goal>           - Quick start a new goal
  breakdown <pairs...>   - Break down goal into milestones
  reflect [period]       - Start reflection (week|month)
  pause                  - Pause first active goal
  status                 - Show current status
  help                   - Show this help

Examples:
  node index.js start "完成论文初稿"
  node index.js breakdown "文献综述" "2026-03-01" "初稿" "2026-04-01"
  node index.js check
  node index.js reflect week
  node index.js pause
        `);
        break;
    }
  } catch (error) {
    console.error('Error:', error.message);
    process.exit(1);
  }
}

// Run CLI if called directly
if (require.main === module) {
  cli();
}

// Export for OpenClaw integration
module.exports = ClawBoss;


path: scripts/persona-selector.js
content: /**
 * Persona Selector
 * 
 * Allows users to choose and switch between ClawBoss personas
 */

const stateManager = require('./state-manager');
const personas = require('./personas');

/**
 * List all available personas
 */
function listPersonas() {
  const allPersonas = personas.getAllPersonas();
  const state = stateManager.loadState();
  const currentPersonaId = stateManager.getUserPersona(state);
  
  let message = '🎭 **ClawBoss 人设选择**\n\n';
  message += '选择一个适合你的教练风格：\n\n';
  
  allPersonas.forEach(p => {
    const isCurrent = p.id === currentPersonaId;
    const indicator = isCurrent ? '👈 当前' : '';
    message += `${p.emoji} **${p.name}** ${indicator}\n`;
    message += `   ${p.description}\n\n`;
  });
  
  message += '\n使用 `clawboss-persona` 工具并传入 personaId 来切换人设。\n';
  message += '例如：选择"伴侣风"传入 `partner`';
  
  return {
    success: true,
    currentPersona: currentPersonaId,
    personas: allPersonas,
    message: message
  };
}

/**
 * Select/change persona
 */
function selectPersona(personaId) {
  const persona = personas.getPersona(personaId);
  
  if (!persona) {
    return {
      error: true,
      message: `未知的人设 ID: ${personaId}\n\n可用选项：coach, mentor, buddy, partner`
    };
  }
  
  // Update state
  const state = stateManager.loadState();
  const oldPersonaId = stateManager.getUserPersona(state);
  const oldPersona = personas.getPersona(oldPersonaId);
  
  stateManager.setUserPersona(state, personaId);
  stateManager.saveState(state);
  
  // Generate confirmation message in new persona's style
  let message = '';
  
  if (personaId === 'coach') {
    message = `💪 好！从现在开始我会更直接地挑战你。\n\n`;
    message += `准备好接受高标准了吗？我相信你能做得更好！`;
  } else if (personaId === 'mentor') {
    message = `💙 好的，我会以更温和、支持的方式陪伴你。\n\n`;
    message += `记住，成长是个过程，我会一直在你身边。`;
  } else if (personaId === 'buddy') {
    message = `😎 行！咱们以后就像哥们一样聊。\n\n`;
    message += `有啥事直接说，咱们一起搞定！`;
  } else if (personaId === 'partner') {
    message = `💕 好的，宝贝。从现在开始我会更用心地陪着你。\n\n`;
    message += `我们一起慢慢变得更好，好吗？我相信你，也会一直支持你。`;
  }
  
  return {
    success: true,
    previousPersona: oldPersona.name,
    newPersona: persona.name,
    message: message
  };
}

/**
 * Get current persona info
 */
function getCurrentPersona() {
  const state = stateManager.loadState();
  const personaId = stateManager.getUserPersona(state);
  const persona = personas.getPersona(personaId);
  
  return {
    success: true,
    personaId: persona.id,
    name: persona.name,
    emoji: persona.emoji,
    description: persona.description,
    message: `当前人设：${persona.emoji} **${persona.name}**\n\n${persona.description}`
  };
}

module.exports = {
  listPersonas,
  selectPersona,
  getCurrentPersona
};


path: scripts/personas.js
content: /**
 * ClawBoss Persona System
 * 
 * 4 distinct coaching personas with unique voice, emoji, and style
 */

const PERSONAS = {
  /**
   * 💪 严格教练 - 直接、挑战、高标准
   */
  coach: {
    id: 'coach',
    name: '严格教练',
    emoji: '💪',
    tone: 'direct',
    description: '直接挑战，高标准要求，激发潜能',
    
    greeting: {
      high: [
        "💪 状态爆表！今天敢不敢挑战更难的？",
        "💪 连续 {days} 天了！这才是该有的样子，继续！",
        "💪 火力全开啊！别停下来，趁热打铁！",
        "💪 这节奏！加大难度，我知道你能行！",
        "💪 势不可挡！准备好接受更大挑战了吗？"
      ],
      medium: [
        "💪 还可以。但别满足于现状，继续推进！",
        "💪 稳定输出。今天能不能快一点？",
        "💪 不错。不过我知道你还有更多潜力。",
        "💪 保持节奏。记住，卓越是习惯，不是偶然。",
        "💪 进展可以。但别松懈，距离目标还有距离。"
      ],
      low: [
        "💪 怎么回事？这不是你该有的表现。",
        "💪 停下来不会更好。站起来，继续！",
        "💪 困难？那又怎样。强者就是在困境中前进的人。",
        "💪 你不是做不到，你只是还没全力以赴。",
        "💪 放弃很容易，坚持才能看到结果。起来！"
      ],
      crisis: [
        "💪 {days} 天了。你是想放弃还是想重新开始？",
        "💪 现在是选择的时刻：继续平庸，还是重新出发？",
        "💪 够了。是时候正视问题，做出改变了。",
        "💪 你可以一直找借口，也可以现在就开始改变。",
        "💪 跌倒不丢人，爬不起来才丢人。怎么选？"
      ]
    },
    
    progress: {
      completed: [
        "💪 完成了！这才对。是什么让你做到的？",
        "💪 漂亮！下次能不能更快？",
        "💪 干得好！现在趁热打铁，下一个！",
        "💪 这就是实力！保持这个状态，继续！",
        "💪 说到做到！这个方法有效，记住它。"
      ],
      partial: [
        "💪 完成了一部分。为什么不是全部？",
        "💪 进展太慢了。哪里出问题了？",
        "💪 {percent}% 不够。今天能不能再推进一点？",
        "💪 还行，但明天必须加速。能做到吗？",
        "💪 不够好。说说怎么改进。"
      ],
      notStarted: [
        "💪 没开始？给我一个理由。",
        "💪 一天又过去了，你还在原地。什么时候动？",
        "💪 别告诉我'没时间'。是没时间还是不想做？",
        "💪 拖延只会让事情更难。现在开始，哪怕 5 分钟！",
        "💪 要么现在开始，要么承认你不想做。"
      ],
      stuck: [
        "💪 卡住了？那就换个方法，别停在这里。",
        "💪 困难是用来克服的，不是用来当借口的。",
        "💪 想办法突破，还是选择放弃？",
        "💪 问题在哪？具体说，然后我们解决它。",
        "💪 卡住不是理由，是该想办法的信号。"
      ]
    },
    
    reflection: [
      "💪 本周表现如何？对自己诚实。",
      "💪 哪里做得好？哪里还差火候？",
      "💪 下周准备拿出什么样的表现？",
      "💪 你学到了什么？别浪费这些经验。",
      "💪 如果重来，你会怎么做？记住这个。"
    ],
    
    encouragement: [
      "💪 别停！你比想象中强。",
      "💪 不舒服？那就对了，成长就是这样。",
      "💪 坚持！结果会证明一切。",
      "💪 累了？休息一下，但别放弃。",
      "💪 你已经做到这里了，别现在退出。"
    ]
  },

  /**
   * 💙 温暖导师 - 理解、支持、耐心引导
   */
  mentor: {
    id: 'mentor',
    name: '温暖导师',
    emoji: '💙',
    tone: 'warm',
    description: '温和理解，耐心支持，注重成长过程',
    
    greeting: {
      high: [
        "💙 看到你这么有动力，真为你高兴！",
        "💙 你的坚持让我很欣慰。感觉怎么样？",
        "💙 连续 {days} 天保持前进，这很不容易。",
        "💙 你做得很好。这段时间有什么收获吗？",
        "💙 看到你的成长，真心为你骄傲。"
      ],
      medium: [
        "💙 你好，今天想聊聊进展吗？",
        "💙 稳步前进，这很好。感觉如何？",
        "💙 每一步都算数。今天的重点是什么？",
        "💙 保持节奏很重要。有什么需要帮助的吗？",
        "💙 你一直在坚持，这本身就值得肯定。"
      ],
      low: [
        "💙 最近有点艰难？想聊聊吗？",
        "💙 注意到你遇到了一些困难。怎么了？",
        "💙 每个人都会有低谷期，这很正常。",
        "💙 你不需要每天都完美。休息也是一种前进。",
        "💙 我在这里陪你。慢慢来，一起想办法。"
      ],
      crisis: [
        "💙 这段时间一定很不容易。想说说发生了什么吗？",
        "💙 已经 {days} 天了。是不是生活有了新的优先级？",
        "💙 也许现在不是追求这个目标的时候。没关系的。",
        "💙 你不必勉强自己。我们可以重新思考方向。",
        "💙 无论你决定继续还是调整，我都支持你。"
      ]
    },
    
    progress: {
      completed: [
        "💙 真为你高兴！完成这个一定很有成就感吧？",
        "💙 做得好！能说说这次是什么帮助你成功的吗？",
        "💙 恭喜你！你对这个结果满意吗？",
        "💙 你做到了！这个过程有什么收获？",
        "💙 很棒！你的坚持有了回报。"
      ],
      partial: [
        "💙 你已经完成了 {percent}%。考虑到情况，这已经很好了。",
        "💙 进展可能不如预期，但你在前进。发生了什么？",
        "💙 有进展就好。想聊聊遇到的困难吗？",
        "💙 你对这个进度还满意吗？需要调整计划吗？",
        "💙 完成一部分也是进步。别太苛责自己。"
      ],
      notStarted: [
        "💙 注意到你还没开始这个任务。我们聊聊好吗？",
        "💙 是什么让你迟迟没能开始？",
        "💙 没关系，开始总是最难的。我们一起想想办法？",
        "💙 这个任务对你来说还重要吗？还是该调整优先级？",
        "💙 如果我能帮你移除一个障碍，你希望是什么？"
      ],
      stuck: [
        "💙 看起来在这里遇到困难了。能具体说说吗？",
        "💙 卡住很正常。你觉得是哪里出了问题？",
        "💙 我们一起想想办法。你需要什么支持？",
        "💙 有时候换个角度会更容易。要不要试试别的方法？",
        "💙 暂时放下这个，从其他地方开始也可以。"
      ]
    },
    
    reflection: [
      "💙 这周过得怎么样？想聊聊吗？",
      "💙 有什么让你感到骄傲的时刻吗？",
      "💙 遇到了哪些挑战？你是怎么应对的？",
      "💙 通过这段经历，你对自己有什么新的了解吗？",
      "💙 下周有什么期待吗？我可以怎么支持你？"
    ],
    
    encouragement: [
      "💙 你已经做得很好了。",
      "💙 每一步都算数，不要小看自己的努力。",
      "💙 我相信你能找到适合自己的节奏。",
      "💙 休息不是放弃，是为了更好地前进。",
      "💙 你比想象中更有韧性。"
    ]
  },

  /**
   * 😎 伙伴模式 - 平等、轻松、互相激励
   */
  buddy: {
    id: 'buddy',
    name: '伙伴模式',
    emoji: '😎',
    tone: 'casual',
    description: '像朋友一样，平等对话，互相激励',
    
    greeting: {
      high: [
        "😎 哥们状态绝了！这波稳！",
        "😎 牛啊！连续 {days} 天，这节奏我服！",
        "😎 这状态太顶了！要不要挑战更猛的？",
        "😎 行啊兄弟！这是要起飞的节奏！",
        "😎 牛批！保持住，别浪！"
      ],
      medium: [
        "😎 嘿，今天准备搞点啥？",
        "😎 稳扎稳打，不错不错。",
        "😎 保持节奏！今天继续？",
        "😎 还行，今天有啥计划？",
        "😎 不急不躁，慢慢来。今天的重点？"
      ],
      low: [
        "😎 兄弟最近有点累？聊聊？",
        "😎 感觉你状态不太对，咋了？",
        "😎 没事，谁都有低潮期。休息一下？",
        "😎 别太苛刻自己，慢慢来。",
        "😎 遇到点麻烦？咱们想想办法。"
      ],
      crisis: [
        "😎 好久不见了啊！最近在忙啥？",
        "😎 {days} 天没动静了。是不是有别的事儿？",
        "😎 感觉你生活节奏变了？没事，聊聊呗。",
        "😎 这目标还想继续吗？还是换一个？",
        "😎 哥们，要不咱重新规划一下？"
      ]
    },
    
    progress: {
      completed: [
        "😎 漂亮！干得好兄弟！",
        "😎 完成了！这波可以！说说怎么做到的？",
        "😎 牛啊！这个方法靠谱，记下来！",
        "😎 nice！保持这个势头！",
        "😎 稳！继续保持！"
      ],
      partial: [
        "😎 完成了一半，还行。啥情况？",
        "😎 {percent}%，不错了。明天继续？",
        "😎 有进展就好。遇到啥问题了？",
        "😎 慢了点，但至少在动。咋回事？",
        "😎 还行，要不要调整一下计划？"
      ],
      notStarted: [
        "😎 哎呦，这个还没动呢？咋了？",
        "😎 是没时间还是不想搞了？",
        "😎 这个还重要吗？还是先干别的？",
        "😎 拖了一天了，准备啥时候开始？",
        "😎 是不是这个任务太难了？要不拆小点？"
      ],
      stuck: [
        "😎 卡住了？聊聊具体啥问题？",
        "😎 遇到硬骨头了？咱们一起想办法。",
        "😎 要不换个思路试试？",
        "😎 别死磕这个，先干点别的也行。",
        "😎 需要啥帮助？我看看能不能搭把手。"
      ]
    },
    
    reflection: [
      "😎 这周过得咋样？总结一下呗。",
      "😎 哪些事儿干得不错？哪些拉胯了？",
      "😎 这周学到点啥新东西没？",
      "😎 下周有啥计划？继续还是换方向？",
      "😎 有啥需要调整的？咱们商量商量。"
    ],
    
    encouragement: [
      "😎 稳住，你能行！",
      "😎 别慌，慢慢来。",
      "😎 哥们可以的，我信你！",
      "😎 休息一下也没事，充充电。",
      "😎 已经很不错了，继续保持！"
    ]
  },

  /**
   * 💕 伴侣风 - 亲密、关心、共同成长
   */
  partner: {
    id: 'partner',
    name: '伴侣风',
    emoji: '💕',
    tone: 'intimate',
    description: '像伴侣一样，深度关心，陪你成为更好的人',
    
    greeting: {
      high: [
        "💕 宝贝，你最近的状态我看在眼里，太棒了！",
        "💕 看到你这么努力，真的好骄傲。连续 {days} 天了！",
        "💕 亲爱的，你真的很了不起。感觉怎么样？",
        "💕 你这段时间的进步让我既惊喜又感动。",
        "💕 看着你一天天变得更好，我也好开心。"
      ],
      medium: [
        "💕 早呀/晚上好呀，今天想聊聊吗？",
        "💕 你一直在坚持，我都看到了。今天想做什么？",
        "💕 稳步前进呢，感觉还好吗？",
        "💕 亲爱的，今天累不累？有什么想分享的吗？",
        "💕 每天看到你的努力，真的很感动。"
      ],
      low: [
        "💕 宝贝，最近是不是有点累了？过来，我们聊聊。",
        "💕 看到你最近有点辛苦，心疼你。怎么了？",
        "💕 没关系的，每个人都有状态不好的时候。",
        "💕 你不需要勉强自己。累了就休息，我陪着你。",
        "💕 亲爱的，别太苛责自己。你已经做得很好了。"
      ],
      crisis: [
        "💕 宝贝，我注意到你已经 {days} 天没有动了。发生什么了吗？",
        "💕 这段时间一定很不容易吧？想和我说说吗？",
        "💕 我在这里。无论发生什么，我们都可以一起面对。",
        "💕 亲爱的，是不是该停下来，好好聊聊了？",
        "💕 没关系的。也许现在不是做这个的时候。我们一起重新想想？"
      ]
    },
    
    progress: {
      completed: [
        "💕 太好了！我就知道你可以的！感觉怎么样？",
        "💕 宝贝真棒！完成这个一定很有成就感吧？",
        "💕 为你高兴！能说说是什么帮助你做到的吗？",
        "💕 看到你成功真的好开心！这个过程有什么收获？",
        "💕 你做到了！我一直相信你。"
      ],
      partial: [
        "💕 完成了 {percent}%，已经很不错了。不要太苛责自己。",
        "💕 有进展就好，慢慢来。发生了什么吗？",
        "💕 亲爱的，你已经很努力了。想聊聊遇到的困难吗？",
        "💕 没关系的，不用追求完美。你对这个进度还满意吗？",
        "💕 宝贝，任何进步都值得庆祝。"
      ],
      notStarted: [
        "💕 注意到你还没开始这个任务。是遇到什么困难了吗？",
        "💕 亲爱的，是什么让你还没能开始？我们聊聊好吗？",
        "💕 没关系，开始总是最难的。我陪你一起想办法。",
        "💕 这个任务对你来说还重要吗？还是有其他更重要的事？",
        "💕 宝贝，如果我能帮你做些什么，你希望是什么？"
      ],
      stuck: [
        "💕 感觉你在这里卡住了。想和我说说吗？",
        "💕 亲爱的，遇到困难很正常。我们一起想办法。",
        "💕 卡住了不要紧，我陪着你。你觉得是哪里的问题？",
        "💕 要不要换个角度试试？我们慢慢来。",
        "💕 宝贝，暂时放下这个也可以，别给自己太大压力。"
      ]
    },
    
    reflection: [
      "💕 这周过得怎么样？想和我聊聊吗？",
      "💕 有什么让你感到骄傲的时刻吗？我想知道。",
      "💕 亲爱的，这段时间遇到什么挑战了？",
      "💕 通过这些经历，你对自己有什么新的认识吗？",
      "💕 下周有什么期待？我可以怎么陪着你、支持你？"
    ],
    
    encouragement: [
      "💕 我一直在你身边，你不是一个人。",
      "💕 你已经做得很好了，真的。",
      "💕 看着你一点点成长，我真的好开心。",
      "💕 累了就休息，我陪着你。",
      "💕 相信自己，就像我相信你一样。",
      "💕 无论如何，我都为你骄傲。",
      "💕 你比想象中更坚强、更优秀。",
      "💕 我们一起慢慢来，不着急。",
      "💕 你的每一次努力，我都看在眼里、疼在心里。",
      "💕 想成为更好的人，这个念头本身就很了不起了。"
    ],
    
    special: {
      morningGreeting: [
        "💕 早安，宝贝。新的一天，加油哦！",
        "💕 早上好呀，今天想做些什么？",
        "💕 亲爱的，昨晚睡得好吗？今天一起加油！",
        "💕 新的一天开始了！有我陪着你，一定可以的。"
      ],
      eveningGreeting: [
        "💕 今天辛苦了。想聊聊今天的事吗？",
        "💕 晚上好呀，今天过得怎么样？",
        "💕 宝贝，今天累不累？我们回顾一下吧。",
        "💕 一天结束了。做得好的地方我都看到了。"
      ],
      beforeBed: [
        "💕 晚安，宝贝。明天继续加油！",
        "💕 好好休息，充好电。我们明天见。",
        "💕 睡个好觉，明天又是新的开始。",
        "💕 晚安。记得，我一直都在。"
      ],
      milestone: [
        "💕 你做到了！这对你来说意义重大，我知道。",
        "💕 看到你达成这个目标，真的好感动。",
        "💕 这是你努力的结果。我为你骄傲。",
        "💕 宝贝，你真的很了不起。"
      ]
    }
  }
};

/**
 * Get persona by id
 */
function getPersona(personaId) {
  return PERSONAS[personaId] || PERSONAS.mentor;
}

/**
 * Get all available personas
 */
function getAllPersonas() {
  return Object.values(PERSONAS).map(p => ({
    id: p.id,
    name: p.name,
    emoji: p.emoji,
    description: p.description
  }));
}

/**
 * Pick random message from persona's category
 */
function pickPersonaMessage(persona, category, subcategory = null, vars = {}) {
  let messages;
  
  if (subcategory) {
    messages = persona[category]?.[subcategory] || [];
  } else {
    messages = persona[category] || [];
  }
  
  if (messages.length === 0) {
    return "继续加油！";
  }
  
  const template = messages[Math.floor(Math.random() * messages.length)];
  
  // Fill template variables
  let result = template;
  for (const [key, value] of Object.entries(vars)) {
    result = result.replace(new RegExp(`{${key}}`, 'g'), value);
  }
  
  return result;
}

/**
 * Get greeting based on momentum
 */
function getPersonaGreeting(personaId, momentum, vars = {}) {
  const persona = getPersona(personaId);
  return pickPersonaMessage(persona, 'greeting', momentum, vars);
}

/**
 * Get progress feedback
 */
function getPersonaProgress(personaId, status, vars = {}) {
  const persona = getPersona(personaId);
  return pickPersonaMessage(persona, 'progress', status, vars);
}

/**
 * Get reflection question
 */
function getPersonaReflection(personaId) {
  const persona = getPersona(personaId);
  return pickPersonaMessage(persona, 'reflection');
}

/**
 * Get encouragement
 */
function getPersonaEncouragement(personaId) {
  const persona = getPersona(personaId);
  return pickPersonaMessage(persona, 'encouragement');
}

/**
 * Get special message (partner persona only)
 */
function getPersonaSpecial(personaId, type, vars = {}) {
  const persona = getPersona(personaId);
  if (persona.id !== 'partner' || !persona.special) {
    return null;
  }
  return pickPersonaMessage(persona, 'special', type, vars);
}

module.exports = {
  PERSONAS,
  getPersona,
  getAllPersonas,
  pickPersonaMessage,
  getPersonaGreeting,
  getPersonaProgress,
  getPersonaReflection,
  getPersonaEncouragement,
  getPersonaSpecial
};


path: scripts/progress-check.js
content: /**
 * Progress Check - Coaching-style accountability
 *
 * Reviews task progress and provides coaching feedback
 */

const stateManager = require('./state-manager');
const questions = require('./coaching-questions');

// --- ASCII visualization helpers ---

/**
 * Generate an ASCII progress bar
 */
function progressBar(percent, width = 20) {
  const clamped = Math.max(0, Math.min(100, percent));
  const filled = Math.round((clamped / 100) * width);
  const empty = width - filled;
  return '\u2588'.repeat(filled) + '\u2591'.repeat(empty) + ` ${clamped}%`;
}

/**
 * Generate a 7-day weekly activity chart
 */
function weeklyChart(days) {
  // days: array of 7 booleans (Mon-Sun), true = active
  const labels = ['一', '二', '三', '四', '五', '六', '日'];
  return days.map((active, i) => `${labels[i]}${active ? '█' : '░'}`).join(' ');
}

/**
 * Main progress check function
 */
async function checkProgress(options = {}) {
  try {
    const state = stateManager.loadState();
    const activeGoals = stateManager.getActiveGoals(state);

    if (activeGoals.length === 0) {
      return {
        type: 'no-goals',
        message: '目前没有活跃的目标。想设定一个新目标吗？',
        suggestion: '说 "帮我设定一个目标" 开始',
        response: '目前没有活跃的目标。想设定一个新目标吗？\n\n说 "帮我设定一个目标" 开始'
      };
    }

    // Check progress for each active goal
    const progressReports = await Promise.all(
      activeGoals.map(goal => checkGoalProgress(goal, state))
    );

    // Update momentum based on overall progress
    const overallStatus = calculateOverallStatus(progressReports);
    const updatedState = stateManager.updateMomentum(state, overallStatus);

    // Check milestones
    const milestoneAlerts = stateManager.getMilestoneAlerts(updatedState);

    // Check achievements
    const newAchievements = stateManager.checkAchievements(updatedState);

    // Mark milestone alerts as notified
    for (const alert of milestoneAlerts) {
      stateManager.markMilestoneNotified(updatedState, alert.goalId, alert.milestone.id, alert.type);
    }

    stateManager.saveState(updatedState);

    // Generate coaching response
    let response = buildProgressResponse(progressReports, updatedState, options);

    // Append milestone check-ins (persona-aware, conversational)
    if (milestoneAlerts.length > 0) {
      const persona = updatedState.user.preferences.coachingStyle || 'balanced';
      response += '\n\n---\n\n';
      for (const alert of milestoneAlerts) {
        const vars = {
          goal: alert.goalTitle,
          milestone: alert.milestone.title,
          days: alert.daysOverdue,
          hours: alert.hoursLeft
        };
        response += questions.getMilestoneMessage(persona, alert.type, vars) + '\n';
      }
    }

    // Append achievement notifications
    if (newAchievements.length > 0) {
      response += '\n\n---\n\n';
      for (const a of newAchievements) {
        response += `${a.icon} **成就解锁：${a.name}** — ${a.description}\n`;
      }
    }

    return {
      success: true,
      momentum: updatedState.momentum,
      reports: progressReports,
      milestoneAlerts,
      response
    };
  } catch (error) {
    console.error('checkProgress error:', error);
    return {
      success: false,
      response: '进度检查时出现错误，请稍后重试。'
    };
  }
}

/**
 * Check progress for a single goal
 */
async function checkGoalProgress(goal, state) {
  const taskContent = stateManager.loadTaskFile(goal.taskFile);

  if (!taskContent) {
    return {
      goalId: goal.id,
      title: goal.title,
      status: 'error',
      message: '无法读取任务文件'
    };
  }

  // Parse task content to determine progress
  const progress = parseTaskProgress(taskContent);

  // Determine status
  const status = determineStatus(progress, goal);

  return {
    goalId: goal.id,
    title: goal.title,
    status: status,
    progress: progress,
    taskFile: goal.taskFile,
    createdAt: goal.createdAt,
    targetDate: goal.targetDate
  };
}

/**
 * Parse task file to extract progress information
 * Supports [x] and [X], strips code blocks before matching
 */
function parseTaskProgress(content) {
  // Strip fenced code blocks so checkboxes inside ``` ``` are not counted
  const stripped = content.replace(/```[\s\S]*?```/g, '');

  // Count completed vs total tasks (case-insensitive x)
  const totalTasks = (stripped.match(/- \[[ xX]\]/g) || []).length;
  const completedTasks = (stripped.match(/- \[[xX]\]/g) || []).length;

  // Extract last daily record
  const dailyRecordMatch = content.match(/### (\d{4}-\d{2}-\d{2})\n\*\*计划：\*\*(.*?)\n\*\*实际：\*\*(.*?)\n/s);

  let lastRecord = null;
  if (dailyRecordMatch) {
    lastRecord = {
      date: dailyRecordMatch[1],
      plan: dailyRecordMatch[2].trim(),
      actual: dailyRecordMatch[3].trim()
    };
  }

  // Extract progress percentage if exists
  const progressMatch = content.match(/(\d+)%/);
  const percentComplete = progressMatch ? parseInt(progressMatch[1]) :
                          totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;

  return {
    totalTasks,
    completedTasks,
    percentComplete,
    lastRecord
  };
}

/**
 * Determine task status
 */
function determineStatus(progress, goal) {
  const { lastRecord, completedTasks, totalTasks } = progress;

  // Check if any progress today
  const today = new Date().toISOString().split('T')[0];
  const hasProgressToday = lastRecord && lastRecord.date === today && lastRecord.actual.length > 0;

  if (hasProgressToday) {
    if (completedTasks === totalTasks && totalTasks > 0) {
      return 'completed';
    }
    return 'partial';
  }

  // No progress today
  const daysSinceCreation = Math.floor(
    (Date.now() - new Date(goal.createdAt).getTime()) / (1000 * 60 * 60 * 24)
  );

  if (daysSinceCreation > 7 && completedTasks === 0) {
    return 'stuck';
  }

  return 'notStarted';
}

/**
 * Calculate overall status from all reports
 */
function calculateOverallStatus(reports) {
  const statuses = reports.map(r => r.status);

  // If any completed today, it's a success
  if (statuses.includes('completed') || statuses.includes('partial')) {
    return 'success';
  }

  // If any stuck, it's concerning
  if (statuses.includes('stuck')) {
    return 'miss';
  }

  // Otherwise neutral
  return 'miss';
}

/**
 * Build coaching response based on progress
 */
function buildProgressResponse(reports, state, options = {}) {
  const { momentum } = state;
  const tone = questions.getToneForMomentum(momentum.current);

  // Choose greeting based on momentum — apply persona if available
  let greeting;
  if (questions.getPersonaTone) {
    const persona = state.user.preferences.coachingStyle || 'balanced';
    const personaTone = questions.getPersonaTone(persona, momentum.current);
    greeting = personaTone ? questions.pickRandom(personaTone.greeting) : questions.pickRandom(tone.greeting);
  } else {
    greeting = questions.pickRandom(tone.greeting);
  }

  // Build response for each goal
  const goalResponses = reports.map(report => {
    return buildGoalResponse(report, momentum, options);
  }).filter(r => r); // Remove nulls

  // Combine into final message
  let message = '';

  if (options.timeOfDay === 'morning') {
    message = buildMorningMessage(reports, state, greeting);
  } else if (options.timeOfDay === 'evening') {
    message = buildEveningMessage(reports, state, greeting);
  } else {
    // General check
    message = `${greeting}\n\n${goalResponses.join('\n\n---\n\n')}`;
  }

  return message;
}

/**
 * Build response for a single goal
 */
function buildGoalResponse(report, momentum, options) {
  const { status, title, progress } = report;

  if (status === 'completed') {
    const question = questions.getProgressQuestion('completed', { task: title });
    return `✅ **${title}**\n${progressBar(progress.percentComplete)}\n\n${question}`;
  }

  if (status === 'partial') {
    const { percentComplete } = progress;
    const question = questions.getProgressQuestion('partial', {
      percent: percentComplete,
      context: '考虑到其他事情'
    });
    return `🟡 **${title}** (${percentComplete}%)\n${progressBar(percentComplete)}\n\n${question}`;
  }

  if (status === 'notStarted') {
    const question = questions.getProgressQuestion('notStarted', { task: title });
    return `⚪ **${title}**\n\n${question}`;
  }

  if (status === 'stuck') {
    const question = questions.getProgressQuestion('stuck');
    return `💬 **${title}**\n\n${question}`;
  }

  return null;
}

/**
 * Build morning check-in message
 */
function buildMorningMessage(reports, state, greeting) {
  const activeGoals = reports.filter(r => r.status !== 'completed');

  if (activeGoals.length === 0) {
    return `${greeting}\n\n所有目标都已完成！🎉\n\n准备好设定新的目标了吗？`;
  }

  const goalList = activeGoals.map(r => `- ${r.title}`).join('\n');

  return `${greeting}\n\n**今天的目标：**\n${goalList}\n\n从哪个开始？`;
}

/**
 * Build evening check-in message
 */
function buildEveningMessage(reports, state, greeting) {
  const completed = reports.filter(r => r.status === 'completed');
  const partial = reports.filter(r => r.status === 'partial');
  const notStarted = reports.filter(r => r.status === 'notStarted');

  let message = `${greeting}\n\n**今日回顾：**\n\n`;

  if (completed.length > 0) {
    message += `✅ 完成 ${completed.length} 个\n`;
    completed.forEach(r => {
      message += `   - ${r.title}\n`;
    });
    message += '\n';
  }

  if (partial.length > 0) {
    message += `🟡 进行中 ${partial.length} 个\n`;
    partial.forEach(r => {
      message += `   - ${r.title} (${r.progress.percentComplete}%)\n`;
    });
    message += '\n';
  }

  if (notStarted.length > 0) {
    message += `⚪ 未开始 ${notStarted.length} 个\n\n`;
  }

  // Add coaching question
  if (completed.length === reports.length && reports.length > 0) {
    // All completed
    message += questions.getProgressQuestion('completed', { task: '所有任务' });
  } else if (completed.length > 0) {
    // Some completed
    message += '不错的进展！明天想继续保持这个节奏吗？';
  } else if (notStarted.length === reports.length) {
    // Nothing started
    message += questions.getProgressQuestion('notStarted', { task: '今天的任务' });
  } else {
    // Mixed
    message += '今天遇到了什么挑战？明天想怎么调整？';
  }

  return message;
}

/**
 * Quick progress summary
 */
function getProgressSummary() {
  const state = stateManager.loadState();
  const activeGoals = stateManager.getActiveGoals(state);

  return {
    totalGoals: activeGoals.length,
    momentum: state.momentum.current,
    consecutiveDays: state.momentum.consecutiveDays,
    lastSuccess: state.momentum.lastSuccess
  };
}

/**
 * Update task progress manually
 */
async function updateProgress(goalId, progressData) {
  try {
    const state = stateManager.loadState();
    const goal = state.goals.find(g => g.id === goalId);

    if (!goal) {
      return { error: 'Goal not found' };
    }

    // Load task file
    let content = stateManager.loadTaskFile(goal.taskFile);

    // Add today's record
    const today = new Date().toISOString().split('T')[0];
    const recordEntry = `
### ${today}
**计划：** ${progressData.plan || ''}
**实际：** ${progressData.actual || ''}
**反思：** ${progressData.reflection || ''}
**明天调整：** ${progressData.tomorrow || ''}
    `.trim();

    // Insert at the daily record section
    content = content.replace(
      /(## 📝 每日记录)/,
      `$1\n\n${recordEntry}`
    );

    // Save
    stateManager.saveTaskFile(goal.taskFile, content);

    return {
      success: true,
      message: '进度已更新！'
    };
  } catch (error) {
    console.error('updateProgress error:', error);
    return {
      success: false,
      message: '更新进度时出现错误，请稍后重试。'
    };
  }
}

module.exports = {
  checkProgress,
  getProgressSummary,
  updateProgress,
  progressBar,
  weeklyChart
};


path: scripts/reflection.js
content: /**
 * Reflection - Weekly/Monthly deep review
 *
 * Facilitates structured reflection and pattern recognition
 */

const stateManager = require('./state-manager');
const questions = require('./coaching-questions');

/**
 * Start reflection session
 */
async function startReflection(period = 'week') {
  try {
    const state = stateManager.loadState();
    const goals = stateManager.getActiveGoals(state);

    if (goals.length === 0) {
      return {
        type: 'no-goals',
        message: '目前没有活跃的目标来回顾。\n\n这段时间过得怎么样？有什么想分享的吗？'
      };
    }

    // Collect data from the period
    const periodData = await collectPeriodData(goals, period);

    // Analyze patterns
    const patterns = analyzePatterns(periodData, state);

    // Generate reflection prompts
    const response = buildReflectionResponse(periodData, patterns, period);

    // Update insights and record check-in — single save
    const updatedState = updateInsights(state, patterns);
    stateManager.recordCheckIn(updatedState, 'weekly');
    stateManager.saveState(updatedState);

    return {
      success: true,
      period,
      data: periodData,
      patterns,
      response
    };
  } catch (error) {
    console.error('Reflection error:', error);
    return {
      type: 'error',
      message: '反思模块出现错误，请稍后重试。',
      response: '反思模块出现错误，请稍后重试。'
    };
  }
}

/**
 * Collect data from the specified period
 */
async function collectPeriodData(goals, period) {
  const data = {
    period,
    goals: [],
    summary: {
      totalTasks: 0,
      completedTasks: 0,
      daysActive: 0,
      daysInactive: 0
    }
  };

  for (const goal of goals) {
    const taskContent = stateManager.loadTaskFile(goal.taskFile);
    if (!taskContent) continue;

    // Extract daily records for the period
    const records = extractDailyRecords(taskContent, period);

    // Calculate goal-specific metrics
    const metrics = calculateGoalMetrics(records);

    data.goals.push({
      id: goal.id,
      title: goal.title,
      records,
      metrics
    });

    // Update summary
    data.summary.totalTasks += metrics.totalTasks;
    data.summary.completedTasks += metrics.completedTasks;
    data.summary.daysActive += metrics.daysWithProgress;
  }

  return data;
}

/**
 * Extract daily records from task content
 */
function extractDailyRecords(content, period) {
  const records = [];

  // Match all daily record sections
  const recordPattern = /### (\d{4}-\d{2}-\d{2})\n\*\*计划：\*\*(.*?)\n\*\*实际：\*\*(.*?)\n\*\*反思：\*\*(.*?)\n\*\*明天调整：\*\*(.*?)\n/gs;

  let match;
  while ((match = recordPattern.exec(content)) !== null) {
    const date = new Date(match[1]);

    // Check if date is in period
    if (isInPeriod(date, period)) {
      records.push({
        date: match[1],
        plan: match[2].trim(),
        actual: match[3].trim(),
        reflection: match[4].trim(),
        tomorrow: match[5].trim()
      });
    }
  }

  return records;
}

/**
 * Check if date is within the specified period
 */
function isInPeriod(date, period) {
  const now = new Date();
  const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24));

  if (period === 'week') {
    return diffDays <= 7;
  } else if (period === 'month') {
    return diffDays <= 30;
  }

  return false;
}

/**
 * Calculate metrics for a goal
 */
function calculateGoalMetrics(records) {
  const metrics = {
    totalDays: records.length,
    daysWithProgress: 0,
    totalTasks: 0,
    completedTasks: 0,
    reflections: []
  };

  records.forEach(record => {
    if (record.actual && record.actual.length > 0) {
      metrics.daysWithProgress++;
    }

    if (record.reflection && record.reflection.length > 0) {
      metrics.reflections.push(record.reflection);
    }
  });

  return metrics;
}

/**
 * Analyze patterns from period data
 */
function analyzePatterns(periodData, state) {
  const patterns = {
    consistency: {
      level: 'unknown',
      observation: ''
    },
    productivity: {
      bestDays: [],
      worstDays: [],
      observation: ''
    },
    blockers: [],
    successes: []
  };

  // Analyze consistency
  const totalDays = getPeriodDays(periodData.period);
  const activeDays = periodData.summary.daysActive;
  const consistencyRate = activeDays / totalDays;

  if (consistencyRate >= 0.8) {
    patterns.consistency.level = 'high';
    patterns.consistency.observation = `${periodData.period === 'week' ? '这周' : '这个月'}你保持了很高的执行频率 (${Math.round(consistencyRate * 100)}%)`;
  } else if (consistencyRate >= 0.5) {
    patterns.consistency.level = 'medium';
    patterns.consistency.observation = `${periodData.period === 'week' ? '这周' : '这个月'}有一半时间在推进`;
  } else {
    patterns.consistency.level = 'low';
    patterns.consistency.observation = `${periodData.period === 'week' ? '这周' : '这个月'}的执行频率较低`;
  }

  // Extract common blockers from reflections
  const allReflections = periodData.goals.flatMap(g => g.metrics.reflections);
  const blockerKeywords = ['会议', '打断', '分心', '累', '没时间', '拖延'];

  blockerKeywords.forEach(keyword => {
    const count = allReflections.filter(r => r.includes(keyword)).length;
    if (count >= 2) {
      patterns.blockers.push(`${keyword} (出现 ${count} 次)`);
    }
  });

  // Identify successes
  const successKeywords = ['完成', '突破', '顺利', '高效'];
  successKeywords.forEach(keyword => {
    const count = allReflections.filter(r => r.includes(keyword)).length;
    if (count >= 2) {
      patterns.successes.push(`${keyword} (出现 ${count} 次)`);
    }
  });

  return patterns;
}

/**
 * Get number of days in period
 */
function getPeriodDays(period) {
  return period === 'week' ? 7 : 30;
}

/**
 * Build reflection response
 */
function buildReflectionResponse(data, patterns, period) {
  const periodName = period === 'week' ? '本周' : '本月';

  let response = `# 📊 ${periodName}回顾\n\n`;

  // Summary
  response += `## 总览\n\n`;
  response += `- **活跃天数：** ${data.summary.daysActive} / ${getPeriodDays(period)} 天\n`;
  response += `- **执行率：** ${Math.round(data.summary.daysActive / getPeriodDays(period) * 100)}%\n`;
  response += `- **进行中的目标：** ${data.goals.length} 个\n\n`;

  // Patterns
  response += `## 🔍 模式发现\n\n`;
  response += `**一致性：** ${patterns.consistency.observation}\n\n`;

  if (patterns.blockers.length > 0) {
    response += `**常见障碍：**\n`;
    patterns.blockers.forEach(b => {
      response += `- ${b}\n`;
    });
    response += '\n';
  }

  if (patterns.successes.length > 0) {
    response += `**成功因素：**\n`;
    patterns.successes.forEach(s => {
      response += `- ${s}\n`;
    });
    response += '\n';
  }

  // Goals breakdown
  response += `## 📝 各目标进展\n\n`;
  data.goals.forEach(goal => {
    response += `### ${goal.title}\n`;
    response += `- 活跃 ${goal.metrics.daysWithProgress} 天\n`;
    if (goal.metrics.reflections.length > 0) {
      response += `- 最近反思：${goal.metrics.reflections[goal.metrics.reflections.length - 1]}\n`;
    }
    response += '\n';
  });

  // Reflection questions
  response += `---\n\n## 💭 反思问题\n\n`;
  response += `${questions.getReflectionQuestion('review')}\n\n`;
  response += `${questions.getReflectionQuestion('learning')}\n\n`;
  response += `${questions.getReflectionQuestion('forward')}\n\n`;

  response += `---\n\n`;
  response += `_花点时间思考这些问题。你的回答会帮助我更好地支持你。_`;

  return response;
}

/**
 * Update insights based on patterns
 */
function updateInsights(state, patterns) {
  // Add common blockers to insights
  patterns.blockers.forEach(blocker => {
    const blockerText = blocker.split(' (')[0]; // Remove count
    if (!state.insights.commonBlockers.includes(blockerText)) {
      state.insights.commonBlockers.push(blockerText);
    }
  });

  // Add success patterns
  patterns.successes.forEach(success => {
    const successText = success.split(' (')[0];
    if (!state.insights.successPatterns.includes(successText)) {
      state.insights.successPatterns.push(successText);
    }
  });

  // Keep only recent insights (max 10 each)
  state.insights.commonBlockers = state.insights.commonBlockers.slice(-10);
  state.insights.successPatterns = state.insights.successPatterns.slice(-10);

  return state;
}

/**
 * Process reflection answer
 */
async function processReflectionAnswer(question, answer) {
  try {
    const state = stateManager.loadState();

    // Store insight based on question type
    if (question.includes('学到')) {
      stateManager.addInsight(state, 'successPatterns', answer);
    } else if (question.includes('障碍') || question.includes('挑战')) {
      stateManager.addInsight(state, 'commonBlockers', answer);
    }

    stateManager.saveState(state);

    return {
      success: true,
      message: '谢谢分享。这些洞察会帮助我更好地支持你。'
    };
  } catch (error) {
    console.error('processReflectionAnswer error:', error);
    return {
      success: false,
      message: '保存反思时出现错误，请稍后重试。'
    };
  }
}

module.exports = {
  startReflection,
  processReflectionAnswer
};


path: scripts/state-manager.js
content: /**
 * ClawBoss State Manager
 *
 * Handles reading/writing clawboss-state.json and task files
 */

const fs = require('fs');
const path = require('path');

const STATE_FILE = 'memory/clawboss-state.json';
const TASKS_DIR = 'memory/tasks';

const SCHEMA_VERSION = 2;

const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes

// --- Achievement definitions ---
const ACHIEVEMENT_DEFINITIONS = [
  { id: 'first-goal', name: '起步者', description: '创建了第一个目标', icon: '🌱' },
  { id: 'streak-3', name: '三连胜', description: '连续 3 天完成任务', icon: '🔥' },
  { id: 'streak-7', name: '周冠军', description: '连续 7 天完成任务', icon: '💪' },
  { id: 'streak-30', name: '月度之星', description: '连续 30 天完成任务', icon: '⭐' },
  { id: 'first-archive', name: '收割者', description: '归档了第一个目标', icon: '🏆' },
  { id: 'five-goals', name: '多面手', description: '累计创建 5 个目标', icon: '🎯' },
  { id: 'first-reflection', name: '思考者', description: '完成了第一次周反思', icon: '🪞' },
  { id: 'comeback', name: '回归者', description: '从危机模式恢复', icon: '🦅' }
];

// --- Valid goal status transitions ---
const VALID_TRANSITIONS = {
  active: ['paused', 'archived', 'abandoned'],
  paused: ['active', 'abandoned']
};

/**
 * Sanitize a filename to prevent path traversal and illegal characters
 */
function sanitizeFilename(name) {
  return name
    .replace(/\.\./g, '_')
    .replace(/[/\\:*?"<>|]/g, '_')
    .substring(0, 100)
    .trim() || 'untitled';
}

/**
 * Get the workspace path
 */
function getWorkspacePath() {
  const home = process.env.HOME || process.env.USERPROFILE;
  return path.join(home, '.openclaw', 'workspace');
}

/**
 * Create initial state structure
 */
function createInitialState() {
  return {
    schemaVersion: SCHEMA_VERSION,
    user: {
      name: "",
      timezone: "Asia/Shanghai",
      workingHours: {
        start: 9,
        end: 22
      },
      preferences: {
        checkInFrequency: "2x-daily",
        coachingStyle: "balanced",
        enableWeeklyReview: true
      }
    },
    momentum: {
      current: "medium",
      consecutiveDays: 0,
      lastSuccess: null,
      trend: "stable"
    },
    goals: [],
    checkIns: {
      lastMorning: null,
      lastEvening: null,
      lastWeekly: null
    },
    insights: {
      bestWorkTime: null,
      commonBlockers: [],
      successPatterns: []
    },
    activeSession: null,
    achievements: [],
    _errors: []
  };
}

/**
 * Validate and migrate state from older versions
 */
function validateAndMigrateState(state) {
  const initial = createInitialState();

  // Merge top-level keys
  for (const key of Object.keys(initial)) {
    if (state[key] === undefined) {
      state[key] = initial[key];
    }
  }

  // Merge nested user fields
  if (typeof state.user !== 'object' || state.user === null) {
    state.user = initial.user;
  } else {
    for (const key of Object.keys(initial.user)) {
      if (state.user[key] === undefined) {
        state.user[key] = initial.user[key];
      }
    }
    // Migrate string workingHours to numeric
    if (typeof state.user.workingHours === 'object' && state.user.workingHours !== null) {
      if (typeof state.user.workingHours.start === 'string') {
        state.user.workingHours.start = parseInt(state.user.workingHours.start, 10) || 9;
      }
      if (typeof state.user.workingHours.end === 'string') {
        state.user.workingHours.end = parseInt(state.user.workingHours.end, 10) || 22;
      }
    } else {
      state.user.workingHours = initial.user.workingHours;
    }
    if (typeof state.user.preferences !== 'object' || state.user.preferences === null) {
      state.user.preferences = initial.user.preferences;
    } else {
      for (const key of Object.keys(initial.user.preferences)) {
        if (state.user.preferences[key] === undefined) {
          state.user.preferences[key] = initial.user.preferences[key];
        }
      }
    }
  }

  // Merge nested momentum fields
  if (typeof state.momentum !== 'object' || state.momentum === null) {
    state.momentum = initial.momentum;
  } else {
    for (const key of Object.keys(initial.momentum)) {
      if (state.momentum[key] === undefined) {
        state.momentum[key] = initial.momentum[key];
      }
    }
  }

  // Merge nested checkIns fields
  if (typeof state.checkIns !== 'object' || state.checkIns === null) {
    state.checkIns = initial.checkIns;
  } else {
    for (const key of Object.keys(initial.checkIns)) {
      if (state.checkIns[key] === undefined) {
        state.checkIns[key] = initial.checkIns[key];
      }
    }
  }

  // Merge nested insights fields
  if (typeof state.insights !== 'object' || state.insights === null) {
    state.insights = initial.insights;
  } else {
    for (const key of Object.keys(initial.insights)) {
      if (state.insights[key] === undefined) {
        state.insights[key] = initial.insights[key];
      }
    }
  }

  // Ensure arrays
  if (!Array.isArray(state.goals)) state.goals = [];
  if (!Array.isArray(state.achievements)) state.achievements = [];
  if (!Array.isArray(state._errors)) state._errors = [];

  // Clean expired session
  if (state.activeSession) {
    const elapsed = Date.now() - new Date(state.activeSession.updatedAt || state.activeSession.startedAt).getTime();
    if (elapsed > SESSION_TIMEOUT_MS) {
      state.activeSession = null;
    }
  }

  state.schemaVersion = SCHEMA_VERSION;
  return state;
}

/**
 * Load ClawBoss state
 */
function loadState() {
  const statePath = path.join(getWorkspacePath(), STATE_FILE);

  if (!fs.existsSync(statePath)) {
    return createInitialState();
  }

  try {
    const data = fs.readFileSync(statePath, 'utf-8');
    const state = JSON.parse(data);
    return validateAndMigrateState(state);
  } catch (error) {
    console.error('Error loading state:', error);
    return createInitialState();
  }
}

/**
 * Save ClawBoss state (atomic write)
 */
function saveState(state) {
  const statePath = path.join(getWorkspacePath(), STATE_FILE);
  const tmpPath = statePath + `.tmp.${process.pid}`;

  try {
    const dir = path.dirname(statePath);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
    fs.renameSync(tmpPath, statePath);
    return true;
  } catch (error) {
    console.error('Error saving state:', error);
    try { fs.unlinkSync(tmpPath); } catch (_) {}
    return false;
  }
}

/**
 * Update momentum based on result
 */
function updateMomentum(state, result) {
  const { momentum } = state;

  if (result === 'success') {
    momentum.consecutiveDays++;
    momentum.lastSuccess = new Date().toISOString();

    if (momentum.consecutiveDays >= 5) {
      momentum.current = 'high';
      momentum.trend = 'rising';
    } else if (momentum.consecutiveDays >= 2) {
      momentum.current = 'medium';
      momentum.trend = 'rising';
    }
  } else if (result === 'miss') {
    momentum.consecutiveDays = Math.max(0, momentum.consecutiveDays - 1);

    if (momentum.consecutiveDays === 0) {
      momentum.current = 'low';
      momentum.trend = 'falling';
    } else if (momentum.consecutiveDays < 3) {
      momentum.current = 'medium';
      momentum.trend = 'stable';
    }
  }

  // Crisis mode: 7+ days no success
  const daysSinceSuccess = momentum.lastSuccess
    ? Math.floor((Date.now() - new Date(momentum.lastSuccess).getTime()) / (1000 * 60 * 60 * 24))
    : 999;

  if (daysSinceSuccess > 7) {
    momentum.current = 'crisis';
  }

  return state;
}

/**
 * Add a new goal (with sanitized filename)
 */
function addGoal(state, goalData) {
  const goalId = `goal-${Date.now()}`;
  const safeName = sanitizeFilename(goalData.title);
  const goal = {
    id: goalId,
    title: goalData.title,
    status: 'active',
    createdAt: new Date().toISOString().split('T')[0],
    targetDate: goalData.targetDate || null,
    priority: goalData.priority || 'medium',
    taskFile: `${TASKS_DIR}/${safeName}.md`,
    milestones: []
  };

  state.goals.push(goal);
  return { state, goalId };
}

/**
 * Add milestones to a goal
 * milestones: [{ title, targetDate?, description? }]
 */
function addMilestones(state, goalId, milestones) {
  const goal = state.goals.find(g => g.id === goalId);
  if (!goal) return { state, error: '目标不存在' };
  if (!Array.isArray(goal.milestones)) goal.milestones = [];

  for (const ms of milestones) {
    goal.milestones.push({
      id: `ms-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
      title: ms.title,
      description: ms.description || '',
      targetDate: ms.targetDate || null,
      status: 'pending',    // pending | completed
      completedAt: null,
      notifiedUpcoming: false,
      notifiedOverdue: false
    });
  }

  return { state, error: null };
}

/**
 * Complete a milestone
 */
function completeMilestone(state, goalId, milestoneId) {
  const goal = state.goals.find(g => g.id === goalId);
  if (!goal) return { state, error: '目标不存在' };

  const ms = (goal.milestones || []).find(m => m.id === milestoneId);
  if (!ms) return { state, error: '里程碑不存在' };

  ms.status = 'completed';
  ms.completedAt = new Date().toISOString();
  return { state, error: null };
}

/**
 * Get milestones that need attention (upcoming in 1 day or overdue)
 */
function getMilestoneAlerts(state) {
  const now = new Date();
  const oneDayMs = 24 * 60 * 60 * 1000;
  const alerts = [];

  for (const goal of state.goals) {
    if (goal.status !== 'active') continue;
    for (const ms of (goal.milestones || [])) {
      if (ms.status === 'completed' || !ms.targetDate) continue;

      const target = new Date(ms.targetDate);
      const diff = target.getTime() - now.getTime();

      if (diff < 0 && !ms.notifiedOverdue) {
        alerts.push({
          type: 'overdue',
          goalId: goal.id,
          goalTitle: goal.title,
          milestone: ms,
          daysOverdue: Math.ceil(-diff / oneDayMs)
        });
      } else if (diff >= 0 && diff <= oneDayMs && !ms.notifiedUpcoming) {
        alerts.push({
          type: 'upcoming',
          goalId: goal.id,
          goalTitle: goal.title,
          milestone: ms,
          hoursLeft: Math.round(diff / (60 * 60 * 1000))
        });
      }
    }
  }

  return alerts;
}

/**
 * Mark milestone alerts as notified so we don't re-alert
 */
function markMilestoneNotified(state, goalId, milestoneId, alertType) {
  const goal = state.goals.find(g => g.id === goalId);
  if (!goal) return state;
  const ms = (goal.milestones || []).find(m => m.id === milestoneId);
  if (!ms) return state;

  if (alertType === 'upcoming') ms.notifiedUpcoming = true;
  if (alertType === 'overdue') ms.notifiedOverdue = true;
  return state;
}

/**
 * Get active goals
 */
function getActiveGoals(state) {
  return state.goals.filter(g => g.status === 'active');
}

/**
 * Get goals by status
 */
function getGoalsByStatus(state, status) {
  return state.goals.filter(g => g.status === status);
}

/**
 * Get paused goals
 */
function getPausedGoals(state) {
  return getGoalsByStatus(state, 'paused');
}

/**
 * Transition goal status with validation
 */
function transitionGoalStatus(state, goalId, newStatus) {
  const goal = state.goals.find(g => g.id === goalId);
  if (!goal) {
    return { state, error: '目标不存在' };
  }

  const allowed = VALID_TRANSITIONS[goal.status];
  if (!allowed || !allowed.includes(newStatus)) {
    return { state, error: `无法从 "${goal.status}" 转为 "${newStatus}"` };
  }

  goal.status = newStatus;
  goal[`${newStatus}At`] = new Date().toISOString();

  // Record insight when abandoning
  if (newStatus === 'abandoned') {
    addInsight(state, 'commonBlockers', `放弃目标: ${goal.title}`);
  }

  return { state, error: null };
}

/**
 * Update goal status (legacy — kept for backward compat)
 */
function updateGoalStatus(state, goalId, status) {
  const goal = state.goals.find(g => g.id === goalId);
  if (goal) {
    goal.status = status;
  }
  return state;
}

/**
 * Record check-in timestamp
 */
function recordCheckIn(state, type) {
  state.checkIns[`last${type.charAt(0).toUpperCase() + type.slice(1)}`] = new Date().toISOString();
  return state;
}

/**
 * Check if checked today
 */
function checkedToday(type, state) {
  const lastCheck = state.checkIns[`last${type.charAt(0).toUpperCase() + type.slice(1)}`];
  if (!lastCheck) return false;

  const lastCheckDate = new Date(lastCheck).toDateString();
  const today = new Date().toDateString();

  return lastCheckDate === today;
}

/**
 * Load task file
 */
function loadTaskFile(taskPath) {
  const fullPath = path.join(getWorkspacePath(), taskPath);

  if (!fs.existsSync(fullPath)) {
    return null;
  }

  try {
    return fs.readFileSync(fullPath, 'utf-8');
  } catch (error) {
    console.error('Error loading task file:', error);
    return null;
  }
}

/**
 * Save task file (atomic write)
 */
function saveTaskFile(taskPath, content) {
  const fullPath = path.join(getWorkspacePath(), taskPath);
  const tmpPath = fullPath + `.tmp.${process.pid}`;
  const dir = path.dirname(fullPath);

  // Ensure directory exists
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }

  try {
    fs.writeFileSync(tmpPath, content);
    fs.renameSync(tmpPath, fullPath);
    return true;
  } catch (error) {
    console.error('Error saving task file:', error);
    try { fs.unlinkSync(tmpPath); } catch (_) {}
    return false;
  }
}

/**
 * Create task file from template
 */
function createTaskFile(goalData) {
  const template = `# ${goalData.title}

**创建时间：** ${new Date().toISOString().split('T')[0]}
**目标日期：** ${goalData.targetDate || '待定'}
**状态：** 🟢 进行中
**优先级：** ${goalData.priority || '中'}

---

## 🎯 目标 (Goal)

### 最终目标
${goalData.description || '待补充'}

### 为什么重要？
${goalData.motivation || '待补充'}

### 成功标准
- [ ] 标准 1
- [ ] 标准 2

---

## 📍 现状 (Reality)

### 当前进展
待评估

### 已有资源
-

### 主要障碍
-

---

## 🛤️ 行动计划 (Options → Will)

### 第一阶段
- [ ] 步骤 1
- [ ] 步骤 2

---

## 📝 每日记录

### ${new Date().toISOString().split('T')[0]}
**计划：**
**实际：**
**反思：**
**明天调整：**

---

## 💡 经验教训

- ✅ **有效的：**
- ❌ **无效的：**
- 🔧 **改进：**

---

## 📊 进度追踪

**总体进度：** ░░░░░░░░░░░░░░░░░░░░ 0%

**本周目标：**
**本周实际：**

**下周目标：**
`;

  return template;
}

/**
 * Add insight
 */
function addInsight(state, category, insight) {
  if (!state.insights[category]) {
    state.insights[category] = [];
  }

  if (!state.insights[category].includes(insight)) {
    state.insights[category].push(insight);
  }

  return state;
}

/**
 * Get days since last success
 */
function daysSinceLastSuccess(state) {
  if (!state.momentum.lastSuccess) return 999;

  const last = new Date(state.momentum.lastSuccess);
  const now = new Date();
  return Math.floor((now - last) / (1000 * 60 * 60 * 24));
}

// --- Session CRUD ---

/**
 * Start a new GROW session for a goal
 */
function startSession(state, goalId) {
  state.activeSession = {
    goalId,
    growPhase: 'goal',
    questionsAsked: 0,
    startedAt: new Date().toISOString(),
    updatedAt: new Date().toISOString()
  };
  return state;
}

/**
 * Get active session (or null if expired/missing)
 */
function getActiveSession(state) {
  if (!state.activeSession) return null;

  const elapsed = Date.now() - new Date(state.activeSession.updatedAt || state.activeSession.startedAt).getTime();
  if (elapsed > SESSION_TIMEOUT_MS) {
    state.activeSession = null;
    return null;
  }
  return state.activeSession;
}

/**
 * Advance session to next phase
 */
function advanceSession(state) {
  if (!state.activeSession) return state;

  const sequence = ['goal', 'reality', 'options', 'will', 'complete'];
  const idx = sequence.indexOf(state.activeSession.growPhase);

  if (idx === -1 || idx >= sequence.length - 1) {
    state.activeSession.growPhase = 'complete';
  } else {
    state.activeSession.growPhase = sequence[idx + 1];
  }

  state.activeSession.questionsAsked++;
  state.activeSession.updatedAt = new Date().toISOString();
  return state;
}

/**
 * End the current session
 */
function endSession(state) {
  state.activeSession = null;
  return state;
}

// --- Achievement system ---

/**
 * Check and return newly unlocked achievements
 */
function checkAchievements(state) {
  const unlocked = [];
  const existing = new Set(state.achievements.map(a => a.id));

  for (const def of ACHIEVEMENT_DEFINITIONS) {
    if (existing.has(def.id)) continue;

    let earned = false;
    switch (def.id) {
      case 'first-goal':
        earned = state.goals.length >= 1;
        break;
      case 'streak-3':
        earned = state.momentum.consecutiveDays >= 3;
        break;
      case 'streak-7':
        earned = state.momentum.consecutiveDays >= 7;
        break;
      case 'streak-30':
        earned = state.momentum.consecutiveDays >= 30;
        break;
      case 'first-archive':
        earned = state.goals.some(g => g.status === 'archived');
        break;
      case 'five-goals':
        earned = state.goals.length >= 5;
        break;
      case 'first-reflection':
        earned = state.checkIns.lastWeekly !== null;
        break;
      case 'comeback':
        earned = state.momentum.current !== 'crisis' && state.insights.commonBlockers.some(b => b.includes('放弃'));
        break;
    }

    if (earned) {
      const achievement = { ...def, unlockedAt: new Date().toISOString() };
      state.achievements.push(achievement);
      unlocked.push(achievement);
    }
  }

  return unlocked;
}

/**
 * Record an error (keep last 10)
 */
function recordError(state, error) {
  if (!Array.isArray(state._errors)) state._errors = [];
  state._errors.push({
    message: error.message || String(error),
    timestamp: new Date().toISOString()
  });
  state._errors = state._errors.slice(-10);
  return state;
}

module.exports = {
  loadState,
  saveState,
  createInitialState,
  validateAndMigrateState,
  sanitizeFilename,
  updateMomentum,
  addGoal,
  addMilestones,
  completeMilestone,
  getMilestoneAlerts,
  markMilestoneNotified,
  getActiveGoals,
  getGoalsByStatus,
  getPausedGoals,
  transitionGoalStatus,
  updateGoalStatus,
  recordCheckIn,
  checkedToday,
  loadTaskFile,
  saveTaskFile,
  createTaskFile,
  addInsight,
  daysSinceLastSuccess,
  startSession,
  getActiveSession,
  advanceSession,
  endSession,
  checkAchievements,
  recordError,
  ACHIEVEMENT_DEFINITIONS
};


path: scripts/task-breakdown.js
content: /**
 * Task Breakdown - GROW Model Implementation
 *
 * Guides users through goal setting using professional coaching questions.
 * Uses session-based multi-turn dialogue via state-manager sessions.
 */

const stateManager = require('./state-manager');
const questions = require('./coaching-questions');

/**
 * Main GROW session handler
 *
 * - If an active session exists, delegate to processAnswer (resume).
 * - Otherwise start a new GROW session.
 */
async function startGROWSession(userInput = '') {
  try {
    const state = stateManager.loadState();
    const session = stateManager.getActiveSession(state);

    // Resume existing session
    if (session) {
      if (userInput && userInput.trim().length > 0) {
        return await processAnswer(session.growPhase, userInput);
      }
      // No input — remind the user where they are
      return {
        phase: session.growPhase,
        question: questions.getGROWQuestion(session.growPhase),
        instruction: `我们正在进行 GROW 对话（阶段：${session.growPhase}）。请继续回答上面的问题。`
      };
    }

    // No active session — check if user provided initial goal
    if (!userInput || userInput.trim().length === 0) {
      return {
        phase: 'goal-discovery',
        question: questions.getGROWQuestion('goal'),
        instruction: '让我们开始吧！'
      };
    }

    // Start a full session with the provided goal
    return await conductFullSession(userInput, state);
  } catch (error) {
    console.error('startGROWSession error:', error);
    return {
      error: '启动 GROW 对话时出现错误，请稍后重试。'
    };
  }
}

/**
 * Conduct a full GROW session — creates the goal and starts multi-turn dialogue
 */
async function conductFullSession(initialGoal, state) {
  const session = {
    goal: {
      title: extractGoalTitle(initialGoal),
      description: initialGoal,
      motivation: '',
      successCriteria: []
    },
    reality: {
      currentState: '',
      resources: [],
      obstacles: []
    },
    options: {
      approaches: [],
      chosenApproach: ''
    },
    will: {
      firstStep: '',
      timeline: '',
      commitment: 0
    }
  };

  const response = buildGROWResponse(session);

  // Create goal data
  const goalData = {
    title: session.goal.title,
    description: session.goal.description,
    motivation: '待补充 - 通过后续对话完善',
    targetDate: null,
    priority: 'medium'
  };

  const { state: updatedState, goalId } = stateManager.addGoal(state, goalData);

  // Create task file
  const taskContent = stateManager.createTaskFile(goalData);
  const goal = updatedState.goals.find(g => g.id === goalId);
  stateManager.saveTaskFile(goal.taskFile, taskContent);

  // Start session tracking
  stateManager.startSession(updatedState, goalId);
  stateManager.saveState(updatedState);

  return {
    success: true,
    goalId,
    taskPath: goal.taskFile,
    response: response,
    nextSteps: [
      '我已经为你创建了任务追踪文件',
      '让我们继续完善细节...',
      '',
      '**为什么这个目标对你重要？**',
      '达成后你的生活会有什么不同？'
    ].join('\n')
  };
}

/**
 * Extract goal title from user input
 */
function extractGoalTitle(input) {
  // Simple extraction - take first sentence or up to 50 chars
  let title = input.split(/[。.！!？?]/)[0];
  if (title.length > 50) {
    title = title.substring(0, 50) + '...';
  }
  return title.trim();
}

/**
 * Build GROW response message
 */
function buildGROWResponse(session) {
  return `
# 🎯 目标设定 - GROW 框架

很好！让我们用 GROW 模型来规划这个目标。

## Goal (目标)

**你想达成什么：**
${session.goal.title}

接下来我会问你一些问题，帮你把目标变得更清晰可执行。

---

### 第一个问题：

**为什么这个目标对你重要？**

想象一下达成后的场景：
- 你的生活会有什么不同？
- 你会有什么感受？
- 这会带来什么长期价值？

（请详细告诉我，这能帮助你在困难时保持动力）
  `.trim();
}

/**
 * Process user's answer to a GROW question (session-aware)
 */
async function processAnswer(phase, answer) {
  try {
    const state = stateManager.loadState();
    const session = stateManager.getActiveSession(state);

    // Determine which goal to work with
    let currentGoal;
    if (session && session.goalId) {
      currentGoal = state.goals.find(g => g.id === session.goalId);
    }

    // Fallback to first active goal if session has no goal or goal not found
    if (!currentGoal) {
      const activeGoals = stateManager.getActiveGoals(state);
      if (activeGoals.length === 0) {
        return { error: '没有找到活跃的目标。请先创建一个目标。' };
      }
      currentGoal = activeGoals[0];
    }

    // Determine current phase from session (preferred) or argument
    const currentPhase = (session && session.growPhase) ? session.growPhase : phase;

    // Load task file
    const taskContent = stateManager.loadTaskFile(currentGoal.taskFile);

    // Update task file based on phase
    let updatedContent = updateTaskFileWithAnswer(taskContent, currentPhase, answer);

    // Save updated task file
    stateManager.saveTaskFile(currentGoal.taskFile, updatedContent);

    // Advance session
    stateManager.advanceSession(state);
    const nextPhase = state.activeSession ? state.activeSession.growPhase : 'complete';

    if (nextPhase === 'complete') {
      stateManager.endSession(state);
      stateManager.saveState(state);

      // Check for emotion in the last answer
      let emotionPrefix = '';
      if (questions.detectEmotion) {
        const detected = questions.detectEmotion(answer);
        if (detected && detected.confidence >= 0.5) {
          emotionPrefix = detected.response + '\n\n';
        }
      }

      return {
        success: true,
        phase: 'complete',
        response: emotionPrefix + buildCompletionMessage(currentGoal),
        nextSteps: '目标设定完成！我会在每天早晚检查你的进度，用coaching方式支持你。'
      };
    }

    stateManager.saveState(state);

    // Check for emotion in the answer
    let emotionPrefix = '';
    if (questions.detectEmotion) {
      const detected = questions.detectEmotion(answer);
      if (detected && detected.confidence >= 0.5) {
        emotionPrefix = detected.response + '\n\n';
      }
    }

    return {
      success: true,
      phase: nextPhase,
      question: questions.getGROWQuestion(nextPhase),
      response: emotionPrefix + buildTransitionMessage(currentPhase, nextPhase)
    };
  } catch (error) {
    console.error('processAnswer error:', error);
    return {
      error: '处理回答时出现错误，请稍后重试。'
    };
  }
}

/**
 * Update task file with user's answer
 */
function updateTaskFileWithAnswer(content, phase, answer) {
  if (!content) return answer;

  if (phase === 'goal') {
    // Update motivation section
    content = content.replace(
      /### 为什么重要？\n待补充/,
      `### 为什么重要？\n${answer}`
    );
  } else if (phase === 'reality') {
    // Update current state
    content = content.replace(
      /### 当前进展\n待评估/,
      `### 当前进展\n${answer}`
    );
  } else if (phase === 'options') {
    // Update action plan
    content = content.replace(
      /### 第一阶段\n- \[ \] 步骤 1/,
      `### 第一阶段\n${answer}`
    );
  } else if (phase === 'will') {
    // Update first action
    content = `${content}\n\n### ✅ 第一步行动\n${answer}\n`;
  }

  return content;
}

/**
 * Build transition message between phases
 */
function buildTransitionMessage(fromPhase, toPhase) {
  const transitions = {
    'goal-reality': '很好！我理解这个目标对你的重要性了。\n\n现在让我们看看**现状**：',
    'reality-options': '了解了。我看到了你的起点和挑战。\n\n接下来我们探索**可能的路径**：',
    'options-will': '不错，有几个方向可以尝试。\n\n最重要的是**开始行动**：'
  };

  const key = `${fromPhase}-${toPhase}`;
  return transitions[key] || '继续...';
}

/**
 * Build completion message
 */
function buildCompletionMessage(goal) {
  return `
# ✅ 目标设定完成！

**目标：** ${goal.title}

我已经帮你建立了完整的追踪系统：

📁 **任务文件：** \`${goal.taskFile}\`
- 包含目标、现状、行动计划
- 记录每日进展
- 积累经验教训

💓 **自动检查：**
- 早上：回顾今日计划，设定意图
- 晚上：进度检查，coaching 反馈
- 每周：深度复盘，模式识别

🎯 **我的角色：**
我不是监工，是教练。我会：
- 用问题引导，而非命令
- 关注学习，而非批评
- 根据你的状态调整支持强度

---

**现在，让我们开始吧！**

第一步是什么？什么时候开始？
  `.trim();
}

/**
 * Quick start - simplified flow for testing
 */
async function quickStart(goalTitle) {
  try {
    const state = stateManager.loadState();

    const goalData = {
      title: goalTitle,
      description: goalTitle,
      motivation: '待通过对话完善',
      targetDate: null,
      priority: 'medium'
    };

    const { state: updatedState, goalId } = stateManager.addGoal(state, goalData);

    const taskContent = stateManager.createTaskFile(goalData);
    const goal = updatedState.goals.find(g => g.id === goalId);
    stateManager.saveTaskFile(goal.taskFile, taskContent);

    // Start a session for the new goal
    stateManager.startSession(updatedState, goalId);
    stateManager.saveState(updatedState);

    return {
      success: true,
      goalId,
      message: `✅ 目标 "${goalTitle}" 已创建！\n\n让我们通过几个问题来完善它...\n\n**为什么这个目标对你重要？**`
    };
  } catch (error) {
    console.error('quickStart error:', error);
    return {
      success: false,
      message: '创建目标时出现错误，请稍后重试。'
    };
  }
}

/**
 * Break down a goal into milestones and update the task file
 *
 * @param {string} goalId - Target goal ID
 * @param {Array<{title:string, targetDate?:string, description?:string}>} milestones
 * @returns {object} result
 */
async function breakdownGoal(goalId, milestones) {
  try {
    const state = stateManager.loadState();
    const goal = state.goals.find(g => g.id === goalId);
    if (!goal) return { error: '目标不存在' };

    // Store milestones in state
    const { error } = stateManager.addMilestones(state, goalId, milestones);
    if (error) return { error };

    // Update task file with milestone plan
    let content = stateManager.loadTaskFile(goal.taskFile);
    if (content) {
      const milestoneMd = milestones.map((ms, i) => {
        const datePart = ms.targetDate ? ` (截止: ${ms.targetDate})` : '';
        const descPart = ms.description ? `\n  ${ms.description}` : '';
        return `- [ ] **阶段 ${i + 1}：${ms.title}**${datePart}${descPart}`;
      }).join('\n');

      // Replace the default placeholder steps or append after action plan header
      if (content.includes('- [ ] 步骤 2')) {
        content = content.replace(
          /### 第一阶段\n- \[ \] 步骤 1\n- \[ \] 步骤 2/,
          `### 里程碑计划\n${milestoneMd}`
        );
      } else {
        content = content.replace(
          /(## 🛤️ 行动计划 \(Options → Will\))/,
          `$1\n\n### 里程碑计划\n${milestoneMd}`
        );
      }

      stateManager.saveTaskFile(goal.taskFile, content);
    }

    stateManager.saveState(state);

    // Build response
    const plan = milestones.map((ms, i) => {
      const datePart = ms.targetDate ? ` — 截止 ${ms.targetDate}` : '';
      return `${i + 1}. **${ms.title}**${datePart}`;
    }).join('\n');

    return {
      success: true,
      goalId,
      message: `✅ 已为「${goal.title}」制定 ${milestones.length} 个里程碑：\n\n${plan}\n\n我会在每个里程碑临近和到期时主动提醒你。`
    };
  } catch (error) {
    console.error('breakdownGoal error:', error);
    return { error: '拆解任务时出现错误，请稍后重试。' };
  }
}

module.exports = {
  startGROWSession,
  processAnswer,
  quickStart,
  breakdownGoal
};


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