Skill: qshan1/qclaw-self-evolver (version 1.0.1)

# Self Evolver — 自我进化引擎 **版本：** 1.0.0 **适用对象：** QClaw 等 AI Agent **依赖：** Python 3.8+, 无需外部库 --- ## 核心能力 帮助 AI Agent 建立「从经验中持续自我进化」的能力，核心三大模块： | 模块 | 做什么 | 触发方式 | |------|--------|---------| | **SEA 进化循环** | 从错误/纠正中提炼改进，3天周期扫描 | cron | | **纠正自动记录** | 被用户纠正时自动记录到学习档案 | 关键词检测 | | **技能自进化** | 从任务历史自动发现可固化的技能 | 关键词检测 | --- ## 触发条件 > 当用户说「帮我安装自我进化」或「安装 self-evolver」或「你要有自我进化能力」时，触发本 skill。 安装后 AI 会： 1. 创建 `.learnings/` 目录结构 2. 注册每 3 天一次的 SEA 进化扫描 cron 3. 激活纠正自动记录机制 --- ## 使用方法 ### 1. 安装（首次） ``` skillhub_install install_skill self-evolver ``` 或在 OpenClaw 中对 AI 说： > 「安装自我进化能力」 AI 会自动完成： - 创建 `.learnings/` 目录（含 LEARNINGS.md / ERRORS.md / FEATURE_REQUESTS.md） - 注册 cron：`每3天运行 SEA 进化扫描` - 生成 `SKILL.md` 覆盖本文件 ### 2. 日常使用 | 场景 | 操作 | |------|------| | 被纠正了 | AI 自动检测纠正关键词 → 写入 pending → 下次 heartbeat 合并 | | 想要手动进化 | 说「自我反思」「进化一下」「帮我分析最近的错误」 | | 查看学习记录 | 说「我最近学到了什么」「看一下记忆」 | | 新技能候选出现 | AI 自动检测关键词 → 生成候选技能写入 `skills/` | ### 3. 手动触发进化 ``` python ~/.qclaw/workspace/scripts/evolve.py ``` --- ## 核心脚本说明 ### evolve.py（SEA 进化引擎） **核心流程：** ``` Sense（感知）→ Assess（评估）→ Evolve（进化） ``` **Sense（感知）：** - 扫描 7 天内所有学习记录（LEARNINGS.md / ERRORS.md） - 识别高优先级（Priority=高）未处理项 - 识别连续重复错误（出现 ≥3 次） **Assess（评估）：** - 技能健康度诊断（fallback_rate / completion_rate / effective_rate） - 行为模式识别（从对话历史提取） - 学习效率评分 **Evolve（进化）：** - 对连续错误 → 更新系统提示词 / AGENTS.md - 对重复任务 → 触发技能自进化生成新 skill - 生成进化报告 ### auto_learn.py（纠正自动记录） **触发关键词：** - 打断/纠正：不對、错了、不是、等等、重新、更正、纠正 - 质疑：不對、我没说过、不用、不需要 **工作流：** 1. 检测到纠正 → 立即写入 `pending.md` 2. 下次 heartbeat → flush 到 `LEARNINGS.md` 3. 高优先级纠正 → 立即触发 SEA 扫描 ### skill_evolution.py（技能自进化） **检测关键词（任务后分析）：** - 研究、开发、分析、优化、自动化、监控、集成、部署、测试、重构 **生成规则：** - 同一模式出现 ≥3 次 → 生成候选技能 - 生成 `skills/[pattern]-skill/SKILL.md` - cron 定期 Nudge 提醒优化 --- ## 目录结构 ``` ~/.qclaw/workspace/ ├── .learnings/ │ ├── LEARNINGS.md # 纠正与改进记录 │ ├── ERRORS.md # 命令/操作失败记录 │ ├── FEATURE_REQUESTS.md # 用户请求的功能 │ ├── pending.md # 待合并的纠正（心跳时处理） │ └── assessment-report.json # 技能健康度报告 ├── scripts/ │ ├── evolve.py # SEA 进化引擎 │ ├── auto_learn.py # 纠正自动记录 │ ├── skill_evolution.py # 技能自进化 │ └── assess.py # 技能质量评估 ├── skills/ # 自生成的技能目录 │ └── [pattern]-skill/ │ └── SKILL.md └── skill_metrics.json # 技能使用指标 ``` --- ## 记录格式（LEARNINGS.md） ``` | 时间 | 错误做法 | 正确做法 | 区域 | Priority | ``` ## 进化报告格式 ``` ## 进化扫描报告 — YYYY-MM-DD ### Sense（感知） - 待处理纠正：N 条 - 连续错误：N 条 - 高优先级：N 条 ### Assess（评估） - 总技能数：N - 健康技能：N - 需进化技能：N ### Evolve（进化） - 已生成技能：N - 已更新规则：N - 待确认改进：N ``` --- ## 安装检查清单 - [ ] `.learnings/` 目录存在 - [ ] `scripts/evolve.py` 存在 - [ ] `scripts/auto_learn.py` 存在 - [ ] `scripts/skill_evolution.py` 存在 - [ ] cron 任务已注册（每3天） - [ ] `skill_metrics.json` 可写 --- ## 与 QClaw 原版的区别 本 skill 是**给新 Agent 的完整版**，包含 QClaw 经过多次迭代后的所有最佳实践。新 Agent 安装后即可获得： 1. **SEA 三阶段进化循环** 2. **纠正即学习**（自动记录 + 合并） 3. **技能自生长**（从任务历史自动生成） 4. **三层记忆**（HOT/WARM/COLD） 5. **技能健康度监控** 无需额外配置，开箱即用。

path: scripts/auto_learn.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
纠正自动记录系统
检测到用户纠正 → 立即写入 pending.md → 下次 heartbeat 合并到 LEARNINGS.md

使用方式:
    python auto_learn.py                     # 交互式添加记录
    python auto_learn.py "错误做法" "正确做法" "区域"  # 快速记录
    python auto_learn.py --flush             # 手动触发合并
    python auto_learn.py --status            # 查看当前状态
"""

import os
import sys
import json
import argparse
from datetime import datetime

# ============================================================
# 路径配置
# ============================================================
def _resolve_workspace():
    return os.path.expanduser(os.environ.get("QW_WORKSPACE", "~/.qclaw/workspace"))

WORKSPACE     = _resolve_workspace()
LEARNINGS_DIR = os.path.join(WORKSPACE, ".learnings")
SCRIPTS_DIR   = os.path.join(WORKSPACE, "scripts")
os.makedirs(LEARNINGS_DIR, exist_ok=True)

PENDING_FILE   = os.path.join(LEARNINGS_DIR, "pending.md")
LEARNINGS_FILE = os.path.join(LEARNINGS_DIR, "LEARNINGS.md")
ERRORS_FILE    = os.path.join(LEARNINGS_DIR, "ERRORS.md")

# ============================================================
# 纠正关键词检测
# ============================================================
CORRECTION_KEYWORDS = [
    # 打断/否定
    "不对", "错了", "不是", "等等", "等等等等", "停",
    # 纠正
    "重新", "重新来", "重新做", "你搞错了", "理解错了",
    "不是这样", "搞错了", "错了错了", "说错了",
    # 更正/质疑
    "更正", "纠正", "改正", "我什么时候说了", "我没说过",
    "我什么时候", "我没说",
    # 放弃/拒绝
    "不用了", "不需要", "不对不对", "不行", "不好",
    "错了别", "别说了", "不是这个", "搞反了",
]

def detect_correction(text: str) -> bool:
    """检测文本是否包含纠正关键词"""
    if not text:
        return False
    return any(kw in text for kw in CORRECTION_KEYWORDS)

def extract_context(text: str, max_chars: int = 200) -> str:
    """提取纠正上下文"""
    return text.strip()[:max_chars]

# ============================================================
# 记录操作
# ============================================================
def log_pending(wrong: str, right: str, area: str, priority: str = "中"):
    """写入 pending.md（待合并）"""
    now = datetime.now()
    entry = _format_entry(wrong, right, area, priority, now, status="未处理")
    _append_to_file(PENDING_FILE, entry)
    return entry

def _format_entry(wrong, right, area, priority, now, status="未处理"):
    date = now.strftime("%Y-%m-%d %H:%M")
    return (
        f"| {date} | {wrong} | {right} | {area} | {priority} | {status} |\n"
        f"| | | | | | |\n"
    )

def _ensure_header(file_path, headers):
    """确保文件有表头"""
    if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
        with open(file_path, "w", encoding="utf-8") as f:
            f.write(headers + "\n")

def _append_to_file(file_path, entry):
    _ensure_header(file_path, "| 时间 | 错误做法 | 正确做法 | 区域 | Priority | Status |")
    with open(file_path, "a", encoding="utf-8") as f:
        f.write(entry)

def log_error(command: str, error_msg: str, consequence: str = ""):
    """记录命令失败"""
    now = datetime.now()
    date = now.strftime("%Y-%m-%d %H:%M")
    entry = f"| {date} | {command} | {error_msg} | {consequence} |\n"
    _ensure_header(ERRORS_FILE, "| 时间 | 命令 | 错误 | 影响 |")
    with open(ERRORS_FILE, "a", encoding="utf-8") as f:
        f.write(entry)

# ============================================================
# Flush：合并 pending → LEARNINGS
# ============================================================
def flush_pending():
    """将 pending.md 合并到 LEARNINGS.md"""
    if not os.path.exists(PENDING_FILE):
        return 0

    with open(PENDING_FILE, "r", encoding="utf-8") as f:
        content = f.read()

    if not content.strip():
        return 0

    # 追加到 LEARNINGS.md
    _ensure_header(LEARNINGS_FILE, "| 时间 | 错误做法 | 正确做法 | 区域 | Priority | Status |")
    with open(LEARNINGS_FILE, "a", encoding="utf-8") as f:
        f.write("\n" + content)

    # 清空 pending.md（用 Python 写空内容，保留文件）
    with open(PENDING_FILE, "w", encoding="utf-8") as f:
        pass  # 清空

    # 统计本次合并条数
    lines = [l for l in content.split('\n') if l.strip().startswith('|')]
    return len(lines)

# ============================================================
# 状态查看
# ============================================================
def status():
    """查看当前学习系统状态"""
    result = {
        "learnings_count": 0,
        "pending_count": 0,
        "errors_count": 0,
        "last_learnings": None,
    }

    # 统计 LEARNINGS.md
    if os.path.exists(LEARNINGS_FILE):
        with open(LEARNINGS_FILE, "r", encoding="utf-8") as f:
            lines = [l for l in f.read().split('\n') if l.strip().startswith('|')]
        result["learnings_count"] = len(lines)
        if lines:
            result["last_learnings"] = lines[-1][:100]

    # 统计 pending.md
    if os.path.exists(PENDING_FILE):
        with open(PENDING_FILE, "r", encoding="utf-8") as f:
            lines = [l for l in f.read().split('\n') if l.strip().startswith('|')]
        result["pending_count"] = len(lines)

    # 统计 ERRORS.md
    if os.path.exists(ERRORS_FILE):
        with open(ERRORS_FILE, "r", encoding="utf-8") as f:
            lines = [l for l in f.read().split('\n') if l.strip().startswith('|')]
        result["errors_count"] = len(lines)

    return result

# ============================================================
# CLI 入口
# ============================================================
def main():
    parser = argparse.ArgumentParser(description="纠正自动记录")
    parser.add_argument("wrong",   nargs='?', help="错误做法")
    parser.add_argument("right",   nargs='?', help="正确做法")
    parser.add_argument("area",    nargs='?', help="区域")
    parser.add_argument("--priority", default="中", help="优先级：高/中/低")
    parser.add_argument("--flush",  action="store_true", help="合并 pending → learnings")
    parser.add_argument("--status", action="store_true", help="查看状态")
    args = parser.parse_args()

    if args.flush:
        n = flush_pending()
        print(f"✅ 已合并 {n} 条到 LEARNINGS.md")
        return

    if args.status:
        s = status()
        print(f"📊 学习系统状态：")
        print(f"  总学习记录：{s['learnings_count']} 条")
        print(f"  待合并：{s['pending_count']} 条")
        print(f"  错误记录：{s['errors_count']} 条")
        if s['last_learnings']:
            print(f"  最近：{s['last_learnings']}")
        return

    if args.wrong:
        entry = log_pending(args.wrong, args.right or "（待补充）",
                           args.area or "通用", args.priority)
        print(f"✅ 已记录：{entry[:80]}")
        print(f"   下次 heartbeat 时自动合并到 LEARNINGS.md")
        return

    # 无参数：交互式
    print("📝 快速记录纠正")
    wrong = input("错误做法: ").strip()
    if not wrong:
        print("取消")
        return
    right = input("正确做法: ").strip()
    area  = input("区域（可回车跳过）: ").strip() or "通用"
    priority = input("优先级（高/中/低，默认中）: ").strip() or "中"

    entry = log_pending(wrong, right, area, priority)
    print(f"\n✅ 已写入 pending.md")
    print(f"   下次 heartbeat 时自动合并到 LEARNINGS.md")

if __name__ == "__main__":
    main()


path: scripts/install_self_evolver.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
自我进化系统一键安装脚本
运行后自动完成：
1. 创建目录结构
2. 复制脚本到 ~/.qclaw/workspace/scripts/
3. 初始化 .learnings/ 档案
4. 注册 cron 定时任务（每3天运行 SEA 进化扫描）

使用方式:
    python install_self_evolver.py
"""

import os
import sys
import shutil
import platform

def _resolve_workspace():
    return os.path.expanduser(os.environ.get("QW_WORKSPACE", "~/.qclaw/workspace"))

WORKSPACE     = _resolve_workspace()
SCRIPTS_DIR   = os.path.join(WORKSPACE, "scripts")
LEARNINGS_DIR = os.path.join(WORKSPACE, ".learnings")

def ensure_dir(path):
    os.makedirs(path, exist_ok=True)
    return path

def copy_scripts():
    """将 scripts/ 目录复制到工作区"""
    src_dir = os.path.join(os.path.dirname(__file__))
    os.makedirs(SCRIPTS_DIR, exist_ok=True)

    scripts_to_copy = [
        "sea_evolve.py",
        "auto_learn.py",
        "skill_evolution.py",
        "skill_metrics.py",
    ]

    copied = []
    for s in scripts_to_copy:
        src = os.path.join(src_dir, s)
        dst = os.path.join(SCRIPTS_DIR, s)
        if os.path.exists(src):
            shutil.copy2(src, dst)
            copied.append(s)
            print(f"  ✅ {s}")
        else:
            print(f"  ⚠️  {s} 未找到，跳过")

    return copied

def init_learnings():
    """初始化 .learnings/ 目录"""
    os.makedirs(LEARNINGS_DIR, exist_ok=True)

    files = {
        "LEARNINGS.md": """| 时间 | 错误做法 | 正确做法 | 区域 | Priority | Status |
| --- | --- | --- | --- | --- | --- |
| | | | | | |

<!-- 新记录会自动追加到这里 -->
""",
        "ERRORS.md": """| 时间 | 命令 | 错误 | 影响 |
| --- | --- | --- | --- |
| | | | |

<!-- 格式：时间|失败的命令|错误信息|造成的影响 -->
""",
        "FEATURE_REQUESTS.md": """| 时间 | 请求 | 状态 | 优先级 |
| --- | --- | --- | --- |
| | | | |
""",
        "pending.md": "",  # 空文件，心跳时处理
    }

    for fname, content in files.items():
        path = os.path.join(LEARNINGS_DIR, fname)
        if not os.path.exists(path):
            with open(path, "w", encoding="utf-8") as f:
                f.write(content)
            print(f"  ✅ {fname}")
        else:
            print(f"  ℹ️  {fname} 已存在，跳过")

def init_skill_metrics():
    """初始化技能指标文件"""
    metrics_file = os.path.join(WORKSPACE, "skill_metrics.json")
    if not os.path.exists(metrics_file):
        with open(metrics_file, "w", encoding="utf-8") as f:
            f.write("{}\n")
        print(f"  ✅ skill_metrics.json")

def register_cron():
    """注册每3天运行 SEA 进化的定时任务"""
    try:
        from openclaw.cron import CronManager
        import os

        # 检测是否是 openclaw 环境
        try:
            import openclaw
        except ImportError:
            print("  ℹ️  openclaw 模块不可用，跳过 cron 注册")
            print("     请手动添加 cron 任务：")
            print("     openclaw cron add --name 'SEA-Evolution' --every 3days")
            return

        cm = CronManager()
        job = {
            "name": "SEA-Evolution-Scan",
            "schedule": {"kind": "every", "everyMs": 3 * 24 * 60 * 60 * 1000},
            "payload": {
                "kind": "agentTurn",
                "message": "运行自我进化：python ~/.qclaw/workspace/scripts/sea_evolve.py --quiet",
            },
            "sessionTarget": "isolated",
            "enabled": True,
        }
        # 检查是否已存在
        existing = [j for j in cm.list() if j.get("name") == "SEA-Evolution-Scan"]
        if existing:
            print("  ℹ️  cron 任务已存在，跳过")
        else:
            cm.add(job)
            print("  ✅ cron 任务已注册（每3天运行）")
    except Exception as e:
        print(f"  ℹ️  cron 注册失败（非致命）：{e}")
        print("     请手动添加：每3天运行 python ~/.qclaw/workspace/scripts/sea_evolve.py --quiet")

def main():
    print("=" * 50)
    print("🌱 Self Evolver 安装程序")
    print("=" * 50)
    print(f"\n工作区：{WORKSPACE}")
    print(f"系统：{platform.system()}")

    print("\n📁 复制核心脚本...")
    copied = copy_scripts()

    print("\n📚 初始化学习档案...")
    init_learnings()

    print("\n📊 初始化技能指标...")
    init_skill_metrics()

    print("\n⏰ 注册定时任务...")
    register_cron()

    print("\n" + "=" * 50)
    print("✅ 安装完成！")
    print("=" * 50)
    print()
    print("下一步：")
    print("1. 重启 OpenClaw 使 cron 生效")
    print("2. 说「安装自我进化」或「我有自我进化能力了吗」验证")
    print("3. 被纠正时自动记录到 pending.md")
    print()
    print("手动测试：")
    print("  python ~/.qclaw/workspace/scripts/sea_evolve.py")
    print("  python ~/.qclaw/workspace/scripts/auto_learn.py --status")

if __name__ == "__main__":
    main()


path: scripts/sea_evolve.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SEA 自我进化引擎 v4.0
Sense → Assess → Evolve 循环
给新 Agent 开箱即用的完整版

使用方式:
    python sea_evolve.py              # 完整运行
    python sea_evolve.py --sense      # 仅感知
    python sea_evolve.py --assess    # 仅评估
    python sea_evolve.py --quiet      # 无打扰模式（供 cron 使用）
"""

import os
import sys
import json
import re
import argparse
from datetime import datetime, timedelta
from pathlib import Path

# ============================================================
# 路径配置（适配 QClaw 和小/小皮等 Agent 环境）
# ============================================================
def _resolve_workspace():
    # 优先用环境变量，否则用 ~/.qclaw/workspace
    return os.path.expanduser(os.environ.get("QW_WORKSPACE", "~/.qclaw/workspace"))

WORKSPACE     = _resolve_workspace()
LEARNINGS_DIR = os.path.join(WORKSPACE, ".learnings")
SKILLS_DIR    = os.path.join(WORKSPACE, "skills")
SCRIPTS_DIR   = os.path.join(WORKSPACE, "scripts")
METRICS_FILE  = os.path.join(WORKSPACE, "skill_metrics.json")

os.makedirs(LEARNINGS_DIR, exist_ok=True)
os.makedirs(SKILLS_DIR, exist_ok=True)
os.makedirs(SCRIPTS_DIR, exist_ok=True)

# ============================================================
# 第一阶段：Sense（感知）
# ============================================================
def sense():
    """从学习档案中感知需要处理的内容"""
    findings = {
        "pending_corrections": [],
        "errors": [],
        "feature_requests": [],
        "high_priority": [],
        "repeated_patterns": [],
        "last_scan_time": datetime.now().isoformat(),
    }

    # 读取 LEARNINGS.md
    learnings_path = os.path.join(LEARNINGS_DIR, "LEARNINGS.md")
    if os.path.exists(learnings_path):
        with open(learnings_path, "r", encoding="utf-8") as f:
            content = f.read()

        # 提取未处理的记录
        pending = re.findall(r'\|\s*(\d{4}-\d{2}-\d{2}[^|]*?)\s*\|[^|]*?\|[^|]*?\|([^|]*?)\|', content)
        for p in pending:
            entry_text = '|'.join(p).strip()
            if '未处理' in entry_text or '处理中' in entry_text:
                findings["pending_corrections"].append(entry_text)
            if 'Priority=高' in entry_text:
                findings["high_priority"].append(entry_text)

        # 识别重复模式（出现 ≥3 次）
        pattern_counts = {}
        for line in content.split('\n'):
            if '|' in line and '---' not in line and '错误做法' not in line:
                parts = line.split('|')
                if len(parts) >= 3:
                    key = parts[1].strip() + '|' + parts[2].strip()
                    pattern_counts[key] = pattern_counts.get(key, 0) + 1
        findings["repeated_patterns"] = [
            {"pattern": k, "count": v}
            for k, v in pattern_counts.items() if v >= 3
        ]

    # 读取 ERRORS.md
    errors_path = os.path.join(LEARNINGS_DIR, "ERRORS.md")
    if os.path.exists(errors_path):
        with open(errors_path, "r", encoding="utf-8") as f:
            content = f.read()
        for line in content.split('\n'):
            if '|' in line and '---' not in line and '时间' not in line:
                findings["errors"].append(line.strip())

    # 读取 FEATURE_REQUESTS.md
    features_path = os.path.join(LEARNINGS_DIR, "FEATURE_REQUESTS.md")
    if os.path.exists(features_path):
        with open(features_path, "r", encoding="utf-8") as f:
            content = f.read()
        for line in content.split('\n'):
            if '|' in line and '---' not in line and '时间' not in line:
                findings["feature_requests"].append(line.strip())

    return findings

# ============================================================
# 第二阶段：Assess（评估）
# ============================================================
def assess():
    """诊断技能健康度"""
    from skill_metrics import load_metrics

    metrics = load_metrics()
    assessment = {
        "total_skills": len(metrics),
        "healthy": [],
        "needs_work": [],
        "critical": [],
        "untouched": [],
    }

    FALLBACK_WARN   = 0.4   # 选了这个技能但没用的比例警告线
    COMPLETION_WARN = 0.35  # 完成率警戒线

    for skill_id, m in metrics.items():
        total = max(1, m["total_selections"])
        fallback    = (total - m["total_applied"]) / total
        completion  = m["total_completed"] / total if m["total_applied"] > 0 else 0

        if fallback > FALLBACK_WARN:
            assessment["critical"].append({
                "skill": skill_id,
                "fallback_rate": round(fallback, 2),
                "completion": round(completion, 2),
                "reason": f"有 {int(fallback*100)}% 的时间选了它但没用"
            })
        elif completion < COMPLETION_WARN:
            assessment["needs_work"].append({
                "skill": skill_id,
                "fallback_rate": round(fallback, 2),
                "completion": round(completion, 2),
                "reason": f"完成率仅 {int(completion*100)}%"
            })
        else:
            assessment["healthy"].append(skill_id)

    return assessment

# ============================================================
# 第三阶段：Evolve（进化）
# ============================================================
def evolve(sense_data, assess_data, quiet=False):
    """根据感知和评估结果执行进化"""
    actions = []
    improvements = []

    # 处理高优先级纠正
    if sense_data["high_priority"]:
        for entry in sense_data["high_priority"]:
            actions.append(f"⚡ 立即处理高优先级纠正: {entry[:60]}")
            improvements.append(entry)

    # 处理重复模式
    for rp in sense_data["repeated_patterns"]:
        pattern = rp["pattern"]
        count = rp["count"]
        if count >= 3:
            # 识别为可固化的模式，触发技能自进化
            actions.append(f"🔧 检测到重复模式 ×{count}: {pattern[:40]}")
            # 这里只记录，实际生成由 skill_evolution.py 处理
            improvements.append(f"触发技能生成: {pattern}")

    # 处理错误记录
    if sense_data["errors"]:
        actions.append(f"📋 {len(sense_data['errors'])} 条错误待处理")
        for err in sense_data["errors"][:3]:
            improvements.append(err)

    # 生成进化报告
    report = _build_report(sense_data, assess_data, actions, improvements)

    if not quiet:
        print(report)

    return report, actions, improvements

def _build_report(sense_data, assess_data, actions, improvements):
    now = datetime.now().strftime("%Y-%m-%d %H:%M")
    lines = [
        f"## 🌱 SEA 进化报告 — {now}",
        "",
        "### Sense（感知）",
        f"- 待处理纠正：{len(sense_data['pending_corrections'])} 条",
        f"- 错误记录：{len(sense_data['errors'])} 条",
        f"- 重复模式：{len(sense_data['repeated_patterns'])} 条",
        f"- 高优先级：{len(sense_data['high_priority'])} 条",
        "",
        "### Assess（评估）",
        f"- 总技能数：{assess_data['total_skills']}",
        f"- 健康技能：{len(assess_data['healthy'])}",
        f"- 需改进：{len(assess_data['needs_work'])}",
        f"- 警戒：{len(assess_data['critical'])}",
        "",
        "### Evolve（进化）",
    ]
    for a in actions:
        lines.append(f"- {a}")

    if not actions:
        lines.append("- ✅ 无需进化，状态良好")

    lines.append("")
    lines.append("### 建议")
    if improvements:
        for imp in improvements[:3]:
            lines.append(f"- 考虑：{imp[:80]}")
    else:
        lines.append("- 继续保持当前工作状态")

    return '\n'.join(lines)

# ============================================================
# 主入口
# ============================================================
def main():
    parser = argparse.ArgumentParser(description="SEA 自我进化引擎")
    parser.add_argument("--sense",  action="store_true", help="仅运行感知阶段")
    parser.add_argument("--assess", action="store_true", help="仅运行评估阶段")
    parser.add_argument("--evolve", action="store_true", help="仅运行进化阶段")
    parser.add_argument("--quiet",  action="store_true", help="静默模式（供 cron 使用）")
    parser.add_argument("--report", action="store_true", help="输出完整报告")
    args = parser.parse_args()

    if args.sense:
        data = sense()
        print(json.dumps(data, ensure_ascii=False, indent=2))
        return

    if args.assess:
        data = assess()
        print(json.dumps(data, ensure_ascii=False, indent=2))
        return

    # 默认：完整 SEA 循环
    sense_data    = sense()
    assess_data   = assess()
    report, _, _  = evolve(sense_data, assess_data, quiet=args.quiet)

    if args.report or not args.quiet:
        print(report)

if __name__ == "__main__":
    main()


path: scripts/skill_evolution.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
技能自进化模块
从任务历史自动发现可固化的技能候选

触发条件（满足任一）：
  - 同一任务类型出现 ≥ 3 次
  - 用户明确说「把它做成技能」「记下来以后用」
  - cron 定期扫描发现高价值重复任务

生成结果：
  skills/[name]-skill/SKILL.md
"""

import os
import re
import json
import argparse
from datetime import datetime
from pathlib import Path

def _resolve_workspace():
    return os.path.expanduser(os.environ.get("QW_WORKSPACE", "~/.qclaw/workspace"))

WORKSPACE   = _resolve_workspace()
SKILLS_DIR  = os.path.join(WORKSPACE, "skills")
os.makedirs(SKILLS_DIR, exist_ok=True)

# ============================================================
# 技能候选关键词
# ============================================================
SKILL_TRIGGERS = [
    "研究", "开发", "分析", "优化", "自动化",
    "监控", "集成", "部署", "测试", "重构",
    "整理", "搜索", "查询", "生成", "创建",
    "做成技能", "记下来", "固化", "形成技能",
]

# ============================================================
# 检测技能候选
# ============================================================
def detect_skill_candidates(text: str) -> list:
    """从文本中检测可能的技能候选"""
    if not text:
        return []
    found = [kw for kw in SKILL_TRIGGERS if kw in text]
    return found

# ============================================================
# 生成新技能
# ============================================================
def generate_skill(name: str, description: str, trigger_phrases: list,
                   skill_type: str = "task-automation") -> str:
    """
    生成一个新的 skill SKILL.md 文件
    返回生成的路径
    """
    skill_dir = os.path.join(SKILLS_DIR, f"{_slugify(name)}-skill")
    os.makedirs(skill_dir, exist_ok=True)

    skill_md = f"""# {name}

**类型：** {skill_type}
**生成时间：** {datetime.now().strftime("%Y-%m-%d %H:%M")}
**触发词：** {', '.join(trigger_phrases) if trigger_phrases else '（待添加）'}
**状态：** 实验性（待验证效果）

---

## 描述

{description}

---

## 触发条件

> 当用户提到以下内容时，触发本技能：

{"".join(f"- {t}\n" for t in trigger_phrases)}

---

## 使用方法

（待填写：具体使用步骤）

---

## 注意事项

- 本技能为自动生成，效果待验证
- 使用 3 次后评估效果
- 如需改进请联系维护者

---

*本技能由 self-evolver 自动生成 · {datetime.now().strftime("%Y-%m-%d")}*
"""
    skill_path = os.path.join(skill_dir, "SKILL.md")
    with open(skill_path, "w", encoding="utf-8") as f:
        f.write(skill_md)

    # 更新 references/index.md（方便查找）
    index_path = os.path.join(SKILLS_DIR, "_auto_generated_index.md")
    with open(index_path, "a", encoding="utf-8") as f:
        f.write(f"- [{name}]({_slugify(name)}-skill/SKILL.md) — {description[:50]}\n")

    return skill_path

# ============================================================
# 扫描并生成建议
# ============================================================
def scan_and_suggest() -> list:
    """扫描 skills 目录，返回可进化的技能候选"""
    suggestions = []
    skill_dirs = [
        d for d in os.listdir(SKILLS_DIR)
        if os.path.isdir(os.path.join(SKILLS_DIR, d)) and d.endswith("-skill")
    ]

    for s in skill_dirs:
        skill_path = os.path.join(SKILLS_DIR, s, "SKILL.md")
        if os.path.exists(skill_path):
            with open(skill_path, "r", encoding="utf-8") as f:
                content = f.read()
            if "实验性" in content or "待验证" in content:
                suggestions.append({
                    "name": s,
                    "path": skill_path,
                    "status": "实验性",
                    "suggestion": "建议使用 3 次后评估是否提升为正式技能"
                })

    return suggestions

# ============================================================
# CLI
# ============================================================
def main():
    parser = argparse.ArgumentParser(description="技能自进化")
    parser.add_argument("--name",   help="技能名称")
    parser.add_argument("--desc",   help="技能描述")
    parser.add_argument("--type",   default="task-automation", help="技能类型")
    parser.add_argument("--triggers", nargs="+", help="触发关键词")
    parser.add_argument("--scan",   action="store_true", help="扫描现有技能")
    args = parser.parse_args()

    if args.scan:
        suggestions = scan_and_suggest()
        if suggestions:
            print(f"发现 {len(suggestions)} 个可进化的技能：")
            for s in suggestions:
                print(f"  - {s['name']}: {s['suggestion']}")
        else:
            print("没有发现可进化的技能候选")
        return

    if args.name:
        path = generate_skill(
            name=args.name,
            description=args.desc or "（无描述）",
            trigger_phrases=args.triggers or [],
            skill_type=args.type
        )
        print(f"✅ 技能已生成：{path}")
        return

    # 默认：扫描
    suggestions = scan_and_suggest()
    print(f"📊 技能自进化扫描结果：")
    print(f"   自生成技能：{len(suggestions)} 个")
    if suggestions:
        for s in suggestions:
            print(f"   → {s['name']}: {s['status']}")

def _slugify(text: str) -> str:
    import re
    text = text.lower().strip()
    text = re.sub(r'[^\w\s-]', '', text)
    text = re.sub(r'[-\s]+', '-', text)
    return text

if __name__ == "__main__":
    main()


path: scripts/skill_metrics.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
技能指标系统 - 轻量版
记录每个 skill 被选中和使用的频率，供 SEA 引擎评估技能健康度
"""

import os
import json
from datetime import datetime

def _resolve_workspace():
    return os.path.expanduser(os.environ.get("QW_WORKSPACE", "~/.qclaw/workspace"))

WORKSPACE    = _resolve_workspace()
METRICS_FILE = os.path.join(WORKSPACE, "skill_metrics.json")

def load_metrics():
    if os.path.exists(METRICS_FILE):
        with open(METRICS_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    return {}

def save_metrics(metrics):
    os.makedirs(os.path.dirname(METRICS_FILE), exist_ok=True)
    with open(METRICS_FILE, "w", encoding="utf-8") as f:
        json.dump(metrics, f, ensure_ascii=False, indent=2)

def record(skill_id: str, applied: bool = False, completed: bool = False, effective: bool = False):
    """记录一次技能使用"""
    metrics = load_metrics()
    if skill_id not in metrics:
        metrics[skill_id] = {
            "total_selections": 0,
            "total_applied": 0,
            "total_completed": 0,
            "total_effective": 0,
            "first_seen": datetime.now().isoformat(),
            "last_used": None,
        }
    m = metrics[skill_id]
    m["total_selections"] += 1
    if applied:     m["total_applied"] += 1
    if completed:   m["total_completed"] += 1
    if effective:   m["total_effective"] += 1
    m["last_used"] = datetime.now().isoformat()
    save_metrics(metrics)
    return m

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("--skill", required=True)
    parser.add_argument("--applied", action="store_true")
    parser.add_argument("--completed", action="store_true")
    parser.add_argument("--effective", action="store_true")
    args = parser.parse_args()
    m = record(args.skill, args.applied, args.completed, args.effective)
    print(f"已记录 → {args.skill}: selections={m['total_selections']}")


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