Skill: jzw6/interview-coach (version 1.0.0)

--- name: interview-coach description: 面试准备助手。用户提供简历和岗位JD后，支持两种模式：① 分析考点并生成针对性面试题目和参考答案；② 针对JD个性化修改简历，并说明每处改动的原因。适用于"帮我准备面试"、"根据JD出题"、"帮我改简历"、"简历怎么针对这个岗位优化"等场景。简历和JD支持 PDF/图片/Word/纯文字，自动识别格式。 metadata: openclaw: emoji: "🎯" requires: bins: - python - uv optionalEnv: - name: PADDLEOCR_TOKEN description: "PaddleOCR AIStudio Access Token，仅处理图片或扫描版 PDF 时需要。文本 PDF、Word、TXT 文件无需配置，可直接使用。" link: "https://aistudio.baidu.com/paddleocr" install: - id: uv kind: uv label: Install uv bins: - uv --- # Interview Coach ## 快速开始 **大多数情况下无需任何配置，直接使用：** | 文件类型 | 是否需要配置 Token | |---------|-----------------| | 文本版 PDF（可复制文字）| ❌ 直接使用 | | Word 文档（.docx / .doc） | ❌ 直接使用 | | TXT 纯文本 | ❌ 直接使用 | | 直接粘贴文字 | ❌ 直接使用 | | 图片（JPG / PNG / WEBP） | ✅ 需要 Token | | 扫描版 PDF（无法复制文字）| ✅ 需要 Token | --- ## 配置 PaddleOCR Token（仅图片/扫描版 PDF 需要） 1. 访问 https://aistudio.baidu.com/paddleocr 2. 右上角获取 Access Token 3. 在 skill 目录下编辑 `.env` 文件，填入 token： ``` PADDLEOCR_TOKEN=你的token ``` 或设置系统环境变量： ```bash export PADDLEOCR_TOKEN=你的token ``` --- ## 第一步：解析文件 对简历和 JD 文件，分别执行： ```bash python $SKILL_DIR/scripts/parse_input.py "<文件完整路径>" ``` > **说明：** > - `$SKILL_DIR` 为本 skill 的安装目录，由平台自动注入 > - 文件路径使用绝对路径，避免找不到文件 > - 依赖库在首次运行时自动安装，稍等片刻即可 **Windows 用户** 请改用以下命令避免乱码： ```bash set PYTHONIOENCODING=utf-8 && python %SKILL_DIR%\scripts\parse_input.py "<完整路径>" ``` 如果用户直接粘贴了文字内容，跳过此步骤。 解析失败或输出为空时，告知用户具体错误信息，并请他直接粘贴文字内容。 --- ## 第二步：交叉分析并生成面试题 拿到简历文本和 JD 文本后，直接分析并输出题目。 **分析维度：** - **技能匹配**：JD 要求的每项技能 → 简历中是否有对应经历（有→深挖，无→补考） - **项目挖掘**：简历中的重要经历 → 设计 STAR 追问（背景/任务/行动/结果） - **行为面试**：JD 软技能要求 → 结合候选人背景出具体事例题 - **情景假设**：岗位核心职责 → 设计真实工作场景题 - **动机匹配**：离职原因、选择理由、职业规划 **出题要求：** - 共 14–18 道题，优先质量而非数量 - 同一技能点不超过 2 道题 - 语言跟随 JD（中文 JD 出中文题） - 每道题格式： ``` 【题目 N】 分类：技术硬技能 / 项目经历 / 行为面试 / 情景假设 / 动机匹配 难度：易 / 中 / 难 出题依据：（一句话，关联到 JD 或简历的具体点） 题目：…… 答题要点： 1. …… 2. …… 3. …… 参考答案：……（100-200字） ⚠️ 红线提示：……（什么样的回答说明有问题） ``` --- ## 简历优化模式 当用户说"帮我改简历"、"优化简历"、"简历怎么针对这个 JD 修改"等，进入本模式。 需要同时拥有简历和 JD 才能执行（解析方式同第一步）。若只有简历，先输出通用优化建议，再提示提供 JD 可做针对性修改。 ### 分析框架 对简历和 JD 做三层比对： 1. **关键词覆盖**：JD 高频词、硬技能词是否出现在简历中（影响 ATS 系统过滤） 2. **经历相关性**：现有经历哪些与 JD 强相关、哪些可以弱化或删除 3. **表达方式**：描述是否量化、是否突出个人贡献、是否契合岗位语言风格 ### 输出格式 先输出一段总体评估（3–5 句），说明简历与 JD 的整体匹配度和核心差距，再逐条给出修改建议。 每条修改建议格式： ``` 【修改项 N】优先级：高 / 中 / 低 位置：（简历中的具体模块，如"工作经历·XX公司·第二条"） 问题：（一句话说明当前写法的不足） 修改原因：（关联到 JD 的具体要求，说清楚为什么这么改） 原文： …… 建议改为： …… 改动说明： - ……（每处改动对应一条说明，解释改了什么、为什么） ``` ### 修改原则 - **只增强，不捏造**：所有修改必须基于用户已有经历，禁止添加简历中未提及的项目、技能或数字 - **量化优先**：能加数字的地方优先补充，没有数据时引导用户回忆（"这个项目大概影响了多少用户/节省了多少时间？"） - **关键词自然植入**：将 JD 核心词融入描述，但不得堆砌关键词到影响可读性 - **删减也是优化**：与 JD 关联度低的经历建议缩减篇幅，而非一律保留 - **语言风格对齐**：互联网公司 JD 倾向结果导向，咨询/金融 JD 倾向逻辑严密，外资倾向英文术语——修改语言风格应与目标公司匹配 ### 修改项优先级判断 | 优先级 | 条件 | |--------|------| | 高 | JD 明确要求的技能/经历，简历完全未体现 | | 高 | 现有描述无量化结果，且岗位高度结果导向 | | 中 | JD 偏好的关键词未出现，但经历本身相关 | | 中 | 表述方式与目标岗位风格不匹配 | | 低 | 格式、用词细节优化，不影响实质匹配度 | ### 结尾引导 修改建议输出完毕后，主动询问： > "以上是针对这份 JD 的简历修改建议。需要我帮你把修改后的完整简历重新整理成一份吗？或者有哪条修改你想进一步讨论？" 修改技巧详见 `./references/resume-optimization.md` --- ## 边界情况 **面试题模式：** - **只有简历没有 JD**：出通用题，提示"提供 JD 后可出更精准的题目" - **只有 JD 没有简历**：出岗位标准题，提示"提供简历后可针对经历定制" **简历优化模式：** - **只有简历没有 JD**：给出通用优化建议（量化、动词开头、去冗余），提示"提供 JD 后可做针对性修改" - **只有 JD 没有简历**：提示需要简历才能开始，无法凭空生成 **通用：** - **文件解析失败**：告知用户具体错误，提示直接粘贴文字内容 - **用户同时要求出题 + 改简历**：先输出简历优化建议，再输出面试题，中间用分隔线区分 各题型出题技巧详见 `./references/question-types.md` 简历修改技巧详见 `./references/resume-optimization.md`

path: .env
content: # Interview Coach 配置文件
# 复制此文件为 .env 并填入你的配置
#
# 大多数情况下无需配置，以下情况才需要填写 Token：
#   - 上传图片（JPG / PNG / WEBP）
#   - 上传扫描版 PDF（无法复制文字的 PDF）
#
# 文本 PDF、Word 文档、TXT 文件无需任何配置，可直接使用。

# 获取 Token：https://aistudio.baidu.com/paddleocr → 右上角 → Access Token
PADDLEOCR_TOKEN=

# 可选：自定义 PaddleOCR 服务地址（默认使用官方地址，一般不需要修改）
# PADDLEOCR_SERVER_URL=https://g4hcg732gdjfofu1.aistudio-app.com


path: scripts/parse_input.py
content: #!/usr/bin/env python3
"""
解析简历或 JD 文件，输出纯文本到 stdout。

支持格式：PDF、DOCX、DOC、JPG/PNG/JPEG/WEBP、TXT
OCR 使用 PaddleOCR AIStudio 云端 API（仅图片/扫描版 PDF 需要 token）

用法：
  python parse_input.py <文件路径>

环境变量（可在脚本同目录的 .env 文件中配置，也可直接设置系统环境变量）：
  PADDLEOCR_TOKEN       AIStudio Access Token（图片/扫描PDF时必填）
  PADDLEOCR_SERVER_URL  PaddleOCR 服务地址（可选，默认使用官方地址）

获取 Token：https://aistudio.baidu.com/paddleocr → 右上角 → Access Token
"""

import sys
import os
import base64

# ── 配置 ──────────────────────────────────────────────────────────────────────

def _load_env() -> None:
    """
    从脚本同目录的 .env 文件加载配置。
    已存在的环境变量不会被覆盖，安全地合并配置。
    """
    # 同时支持 scripts/.env 和上一级目录 .env（兼容不同部署结构）
    script_dir = os.path.dirname(os.path.abspath(__file__))
    candidates = [
        os.path.join(script_dir, ".env"),
        os.path.join(script_dir, "..", ".env"),
    ]
    for env_path in candidates:
        env_path = os.path.normpath(env_path)
        if not os.path.exists(env_path):
            continue
        with open(env_path, encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith("#") or "=" not in line:
                    continue
                key, _, val = line.partition("=")
                key = key.strip()
                val = val.strip().strip('"').strip("'")
                if key and val and key not in os.environ:
                    os.environ[key] = val
        break  # 找到第一个 .env 就停止

_load_env()

[REDACTED_SECRET]"PADDLEOCR_TOKEN", "").strip()
SERVER_URL = os.getenv(
    "PADDLEOCR_SERVER_URL",
    "https://g4hcg732gdjfofu1.aistudio-app.com"
).rstrip("/")

STRUCT_ENDPOINT = f"{SERVER_URL}/layout-parsing"

IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".webp"}
PDF_EXT    = ".pdf"
DOCX_EXTS  = {".docx", ".doc"}
TXT_EXT    = ".txt"

# ── 依赖安装 ──────────────────────────────────────────────────────────────────

def _install(pkg: str) -> None:
    """尝试用 uv 安装，不存在时回退到 pip。"""
    import subprocess
    print(f"[安装依赖] {pkg} ...", file=sys.stderr)
    try:
        subprocess.check_call(
            ["uv", "pip", "install", pkg, "--system", "-q"],
            stderr=subprocess.DEVNULL,
        )
    except FileNotFoundError:
        print("[安装依赖] uv 未找到，改用 pip ...", file=sys.stderr)
        try:
            subprocess.check_call(
                [sys.executable, "-m", "pip", "install", pkg, "-q"],
                stderr=subprocess.DEVNULL,
            )
        except subprocess.CalledProcessError:
            raise RuntimeError(
                f"自动安装 {pkg} 失败，请手动执行：pip install {pkg}"
            )

# ── OCR：图片 ─────────────────────────────────────────────────────────────────

def ocr_image(file_path: str) -> str:
    """调用 PaddleOCR AIStudio API 识别图片文字。"""
    _require_token("图片 OCR")

    try:
        import requests
    except ImportError:
        _install("requests")
        import requests

    with open(file_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("utf-8")

    payload = {"file": b64, "fileType": 1}
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"token {TOKEN}",
    }

    resp = _post_with_error_handling(requests, payload, headers, timeout=60)
    return _extract_text_from_response(resp)


# ── OCR：扫描版 PDF ───────────────────────────────────────────────────────────

def _parse_pdf_via_api(file_path: str) -> str:
    """调用 PaddleOCR PP-StructureV3 API 解析扫描版 PDF。"""
    _require_token("扫描版 PDF 解析")

    try:
        import requests
    except ImportError:
        _install("requests")
        import requests

    with open(file_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("utf-8")

    payload = {"file": b64, "fileType": 0}
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"token {TOKEN}",
    }

    resp = _post_with_error_handling(requests, payload, headers, timeout=120)
    return _extract_text_from_response(resp)


def _parse_pdf_local(file_path: str) -> str:
    """用 pdfplumber 提取原生文本 PDF（无需 token，速度快）。"""
    try:
        import pdfplumber
    except ImportError:
        _install("pdfplumber")
        import pdfplumber

    with pdfplumber.open(file_path) as pdf:
        pages = [p.extract_text() or "" for p in pdf.pages]
    return "\n\n".join(p.strip() for p in pages if p.strip())


def parse_pdf(file_path: str) -> str:
    """
    PDF 解析策略：
    1. 先用 pdfplumber 提取原生文本（快、无需 token）
    2. 文本过少（可能是扫描件）→ 提示用户需要 token，改用 API OCR
    """
    text = _parse_pdf_local(file_path)
    if len(text.strip()) >= 300:
        return text

    # 文本不足，判断是否为扫描件
    if not TOKEN:
        raise EnvironmentError(
            "此 PDF 可能是扫描件（提取到的文字过少），需要使用 OCR 解析。\n"
            "请配置 PADDLEOCR_TOKEN 后重试：\n"
            "  1. 访问 https://aistudio.baidu.com/paddleocr 获取 Access Token\n"
            "  2. 在 .env 文件中添加：PADDLEOCR_TOKEN=你的token\n"
            "  或者：直接粘贴简历/JD 文字内容，无需 token。"
        )
    print("[解析] PDF 原生文本不足，改用 PaddleOCR API 解析...", file=sys.stderr)
    return _parse_pdf_via_api(file_path)


# ── DOCX ──────────────────────────────────────────────────────────────────────

def parse_docx(file_path: str) -> str:
    try:
        from docx import Document
    except ImportError:
        _install("python-docx")
        from docx import Document

    doc = Document(file_path)
    paragraphs = [p.text for p in doc.paragraphs if p.text.strip()]

    # 同时提取表格中的文字（简历常用表格排版）
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                if cell.text.strip():
                    paragraphs.append(cell.text.strip())

    return "\n".join(paragraphs)


# ── TXT ───────────────────────────────────────────────────────────────────────

def parse_txt(file_path: str) -> str:
    # 自动检测编码，兼容 GBK/UTF-8
    encodings = ["utf-8", "gbk", "utf-16"]
    for enc in encodings:
        try:
            with open(file_path, "r", encoding=enc) as f:
                return f.read()
        except (UnicodeDecodeError, LookupError):
            continue
    # 全部失败则忽略非法字节
    with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
        return f.read()


# ── 工具函数 ──────────────────────────────────────────────────────────────────

def _require_token(feature: str) -> None:
    """检查 token 是否已配置，未配置时给出清晰的操作指引。"""
    if not TOKEN:
        raise EnvironmentError(
            f"{feature} 需要 PaddleOCR Token，但当前未配置。\n"
            "配置方法：\n"
            "  1. 访问 https://aistudio.baidu.com/paddleocr 获取 Access Token\n"
            "  2. 在 skill 目录的 .env 文件中添加：PADDLEOCR_TOKEN=你的token\n"
            "     或设置系统环境变量：export PADDLEOCR_TOKEN=你的token\n"
            "\n"
            "如果只有文本 PDF / Word / TXT，无需 token，直接使用即可。"
        )


def _post_with_error_handling(requests_mod, payload, headers, timeout: int):
    """统一处理 API 请求错误，给出友好提示。"""
    try:
        resp = requests_mod.post(
            STRUCT_ENDPOINT, json=payload, headers=headers, timeout=timeout
        )
        resp.raise_for_status()
        return resp
    except requests_mod.exceptions.Timeout:
        raise RuntimeError(
            f"OCR 请求超时（>{timeout}s），请检查网络连接后重试。\n"
            "也可以直接粘贴文字内容，跳过文件解析。"
        )
    except requests_mod.exceptions.HTTPError as e:
        status = e.response.status_code
        if status == 401:
            raise RuntimeError(
                "Token 无效或已过期（HTTP 401）。\n"
                "请到 https://aistudio.baidu.com/paddleocr 重新获取 Token。"
            )
        raise RuntimeError(
            f"OCR API 返回错误（HTTP {status}）。\n"
            "请检查 PADDLEOCR_TOKEN 是否正确，或稍后重试。"
        )
    except requests_mod.exceptions.ConnectionError:
        raise RuntimeError(
            "无法连接到 OCR 服务，请检查网络连接。\n"
            "也可以直接粘贴文字内容，跳过文件解析。"
        )


def _extract_text_from_response(resp) -> str:
    """从 PaddleOCR API 响应中提取文字内容。"""
    data = resp.json()
    parts = []
    for res in data.get("result", {}).get("layoutParsingResults", []):
        text = res.get("markdown", {}).get("text", "").strip()
        if text:
            parts.append(text)
    return "\n\n".join(parts)


# ── 入口 ──────────────────────────────────────────────────────────────────────

PARSERS = {
    **{ext: ocr_image  for ext in IMAGE_EXTS},
    PDF_EXT: parse_pdf,
    **{ext: parse_docx for ext in DOCX_EXTS},
    TXT_EXT: parse_txt,
}

SUPPORTED_EXTS = ", ".join(sorted(PARSERS.keys()))


def main() -> None:
    if len(sys.argv) < 2:
        print(f"用法：python3 parse_input.py <文件路径>", file=sys.stderr)
        print(f"支持格式：{SUPPORTED_EXTS}", file=sys.stderr)
        sys.exit(1)

    file_path = sys.argv[1]

    if not os.path.exists(file_path):
        print(f"错误：文件不存在 → {file_path}", file=sys.stderr)
        sys.exit(1)

    if not os.path.isfile(file_path):
        print(f"错误：路径不是文件 → {file_path}", file=sys.stderr)
        sys.exit(1)

    ext = os.path.splitext(file_path)[1].lower()
    parser = PARSERS.get(ext)

    if not parser:
        print(
            f"错误：不支持的文件格式 {ext}\n"
            f"支持的格式：{SUPPORTED_EXTS}",
            file=sys.stderr,
        )
        sys.exit(1)

    try:
        text = parser(file_path)
    except EnvironmentError as e:
        # Token 未配置等配置问题，给出明确指引
        print(f"\n⚠️  配置问题：\n{e}", file=sys.stderr)
        sys.exit(2)  # 用不同退出码区分"配置问题"和"解析失败"
    except RuntimeError as e:
        # API 调用失败等运行时问题
        print(f"\n❌  解析失败：\n{e}", file=sys.stderr)
        sys.exit(3)
    except Exception as e:
        print(f"\n❌  未预期的错误：{e}", file=sys.stderr)
        sys.exit(1)

    if not text.strip():
        print(
            "警告：未提取到任何文字内容。\n"
            "请尝试：直接粘贴文字内容，或换用其他格式的文件。",
            file=sys.stderr,
        )
        sys.exit(1)

    # 强制 UTF-8 输出，避免 Windows GBK 乱码（兼容 Python 3.6+）
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8")
    else:
        import io
        sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")

    print(text)


if __name__ == "__main__":
    main()


path: references/question-types.md
content: # 题型出题技巧参考

## 技术/专业硬技能题
从"会用"到"深理解"分层追问，结合候选人简历中的具体项目。
- ✅ "你用Spark处理TB级数据时，分区策略怎么设计的？遇到数据倾斜怎么解决？"
- ❌ "请介绍机器学习的常见算法"（太宽泛，无诊断价值）

## 项目经历追问（STAR）
聚焦个人贡献而非团队成果，追问量化数字。
S（背景）→ T（你的任务）→ A（具体行动）→ R（量化结果）

## 行为面试题（BEI）
固定开头"请描述一次你…"，要求具体事例，不接受假设性回答。
常见维度：领导力、跨部门协作、抗压、快速学习、结果导向

## 情景假设题
设计有真实冲突的场景，看思考过程而非唯一答案。
- ✅ "入职第一个月发现核心指标连续下滑但无人知道原因，你怎么做？"

## 动机与匹配度
必问：离职原因、选择理由、职业规划、对行业认知。

## 红线信号速查

| 信号 | 可能说明 |
|------|---------|
| 项目描述全是"我们"，无法说清个人贡献 | 可能夸大经历 |
| 无法量化任何结果 | 业务影响认知薄弱 |
| 离职原因反复指向他人 | 可能有协作困难 |
| 情景题只谈理论，无具体行动 | 实操经验不足 |
| 核心技能完全无法展开 | 简历可能注水 |


path: references/resume-optimization.md
content: # 简历修改技巧参考

## 核心原则：改的是表达，不是事实

所有修改必须基于用户已有经历。遇到"没有数据"的情况，应引导用户回忆，而非替他捏造数字。

> "这个项目上线后，大概影响了多少用户？或者节省了多少人工时间？"

---

## 动词开头 × 结果收尾

简历每条经历应遵循：**动词 + 做了什么 + 产生了什么结果**

| 原写法（弱） | 改后（强） |
|------------|----------|
| 负责数据分析工作 | 主导构建用户行为分析看板，支撑产品周迭代决策，覆盖日活 50 万用户数据 |
| 参与了系统重构 | 主导核心模块重构，接口响应时间从 800ms 降至 120ms |
| 协助团队完成项目 | 独立负责前端交互层开发，按时交付并通过 UAT 测试 |

**常用强动词**（按职能分类）：

- 技术类：构建、重构、设计、优化、部署、迁移、封装、集成
- 分析类：分析、建模、挖掘、量化、追踪、评估、预测
- 管理类：主导、统筹、推动、协调、培训、制定、落地
- 增长类：提升、缩短、降低、扩展、节省、覆盖

---

## 量化优先级

并非所有内容都需要数字，按以下优先级处理：

1. **直接数字**：用户量、收入、时间节省、错误率——优先补充
2. **比例/幅度**：提升 X%、降低 X%——次选
3. **规模描述**：TB 级数据、千万量级请求、10 人团队——无法量化时用规模代替
4. **定性但具体**："支撑了公司核心业务线上线"比"参与了重要项目"强

---

## 关键词植入策略

ATS（简历筛选系统）会匹配 JD 关键词，关键词缺失可能在人工审阅前就被过滤。

**做法：**
- 提取 JD 中出现 2 次以上的技术词、职能词
- 检查这些词是否以相同或近义形式出现在简历中
- 自然融入已有描述，不要单独列一行堆砌

**示例：**
> JD 要求"数据驱动决策"，简历写的是"根据数据做判断"
> → 改为"基于数据驱动的分析框架，支撑产品方向决策"

**禁止：**
- 在技能栏堆砌 20+ 个关键词（人工筛选时一眼看出注水）
- 把没有实际使用过的技术加进技能栏

---

## 按岗位风格调整语言

不同类型的公司和岗位有各自的简历语言偏好：

| 目标类型 | 语言风格 | 示例 |
|---------|---------|------|
| 互联网产品/技术 | 结果导向、数据说话 | "DAU 提升 15%"、"P99 延迟 < 50ms" |
| 管理咨询 | 逻辑严密、影响层级高 | "推动跨部门对齐，影响 C-level 决策" |
| 外资/跨国 | 英文术语、国际视野 | "Led cross-functional collaboration across 3 APAC markets" |
| 金融/投行 | 精准、专业术语 | "管理 AUM 5 亿元，年化收益超基准 200bp" |
| 初创公司 | 强调自驱、全栈能力 | "0 到 1 搭建数据基础设施，独立交付" |

---

## 删减也是优化

与 JD 核心职责关联度低的经历，应建议压缩而非保留：

- **缩减**：与岗位无关的项目，压缩到一行，或移至"其他经历"
- **删除**：5 年前且不相关的工作，校招阶段可保留实习，社招阶段可删除
- **重新排序**：把最相关的经历放在最前，不要按时间倒序死板排列

---

## 常见简历问题速查

| 问题 | 诊断信号 | 修改方向 |
|------|---------|---------|
| 描述太空洞 | "负责、参与、协助"开头，无具体内容 | 换强动词 + 补充具体做了什么 |
| 缺少结果 | 只写了做法，没有影响/结果 | 补充量化结果或规模描述 |
| 贡献不清晰 | "我们团队做了…" | 改为"我负责…，最终团队达成…" |
| 关键词缺失 | JD 核心词在简历中找不到 | 自然植入 JD 关键词 |
| 篇幅失衡 | 旧经历写了很多，近期经历反而简短 | 重点经历详写，非核心经历压缩 |
| 技能栏注水 | 列了很多"了解"级别的技术 | 删除未实际使用过的，保留能展开谈的 |


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