Skill: wjz-p/gemini-skill (version 0.1.0)

--- name: gemini-skill description: 通过 Gemini 官网（gemini.google.com）执行问答与生图操作。用户提到“问问Gemini/让Gemini回答/去Gemini问”，或出现“生图/画图/绘图/nano banana/nanobanana/生成图片”等关键词时触发。默认使用可用模型中最强档（优先 Gemini 3.1 Pro），按任务切换文本问答或图片生成流程，并把结果回传给用户。 --- # Gemini Web Ops ## 核心规则 1. 使用 OpenClaw 内置浏览器，`profile="openclaw"`。 2. 涉及生图关键词（如：生图、绘图、画一张、nano banana）时，优先用无头浏览器流程执行。 3. 文本问答任务（如“问问Gemini xxx”）走 Gemini 文本提问链路。 4. 默认模型：可用列表中最强模型，优先 `Gemini 3.1 Pro`。 5. 执行生图后先向用户回报“正在绘图中”，完成后回传图片。 ## 任务分流 - **文本问答**触发词：`问问Gemini`、`让Gemini回答`、`去Gemini问`。 - **生图任务**触发词：`生图`、`画`、`绘图`、`海报`、`nano banana`、`nanobanana`、`image generation`。 - 若请求含糊，先确认：是文本回答还是要出图。 ## 标准执行流程 ### A. 文本问答 1. 打开 `https://gemini.google.com`。 2. 校验登录态（头像/输入框可见）。 3. 选择最强可用模型（优先 Gemini 3.1 Pro）。 4. 将用户问题原样输入并发送。 5. 等待完整输出，提炼后回传（必要时附原文要点）。 ### B. 生图流程 1. 打开 Gemini 页面并确认登录。 2. 选择最强可用模型（优先 Gemini 3.1 Pro）。 3. 将用户提示词原样输入。 4. 开启/勾选图片生成能力（若 UI 有“生成图片/图片”开关）。 5. 发送后立即通知用户：正在绘图中。 6. 结果出现后： - 优先用“下载原图”按钮获取原图。 - 若无下载按钮或失败，可对图片右键另存（通常是标清图）。 7. 把图片返回用户；若有多张，按顺序全部回传。 ## 失败回退 1. 元素定位失败：刷新页面后重试一次。 2. 模型不可用：降级到次优 Gemini 模型并告知。 3. 生成超时：回报“仍在生成中”，继续等待一次；再次超时则请用户换短提示词。 ## 低 token 优先策略 - 优先使用 `scripts/gemini_ui_shortcuts.js` 的快捷选择器。 - 先 evaluate 批量动作，再 snapshot 精准兜底。 - 避免高频全量快照。 ## 参考 - 详细执行与回退：`references/gemini-flow.md` - 关键词与路由：`references/intent-routing.md`

path: references/gemini-flow.md
content: # Gemini Flow

## 1) 登录校验

最小校验项：
- 页面存在可输入提问的输入框
- 右上角有用户头像或账户入口

若未登录：提示用户先在 openclaw profile 浏览器中登录。

## 2) 模型策略

优先级：
1. Gemini 3.1 Pro
2. 当前页面可见的次优 Pro/Advanced 模型

若切换失败，保留默认并告知用户。

## 3) 生图结果获取

优先顺序：
1. 图片右上角“下载原图”
2. 右键另存为（标清）

下载到本地后再通过渠道回传。

## 4) 用户提示文案（建议）

- 开始生图：
  - `已收到，正在用 Gemini 给你绘图中 🎨`
- 生成中超时：
  - `还在渲染中，我继续盯着，马上回你。`
- 完成：
  - `画好了，给你发图啦～`


path: references/intent-routing.md
content: # Intent Routing

## 文本问答触发

- 问问Gemini xxx
- 去Gemini帮我问 xxx
- 让Gemini总结 xxx

动作：走文本问答链路。

## 生图触发

- 给我画一只小猫
- 生图：未来城市海报
- nano banana 风格来一张

动作：走生图链路，并先反馈“正在绘图中”。

## 混合请求

例如：
- 先问Gemini这个产品定位，再出一张封面图

动作：拆成两步执行，先问答后生图。


path: scripts/gemini_ui_shortcuts.js
content: (function initGeminiOps(){
  const S = {
    promptInput: [
      'textarea[aria-label*="Gemini"]',
      'textarea[placeholder*="Gemini"]',
      'div[contenteditable="true"]'
    ],
    sendBtn: [
      'button[aria-label*="发送"]',
      'button[aria-label*="Send"]',
      'button:has-text("发送")'
    ],
    imageToggle: [
      'button:has-text("图片")',
      'button:has-text("生成图片")',
      '[role="button"]:has-text("Image")'
    ],
    modelBtn: [
      'button:has-text("Gemini")',
      '[role="button"][aria-haspopup="menu"]'
    ]
  };

  function visible(el){
    if(!el) return false;
    const r=el.getBoundingClientRect();
    const st=getComputedStyle(el);
    return r.width>0 && r.height>0 && st.display!=='none' && st.visibility!=='hidden';
  }

  function q(sel){
    try{
      if(sel.includes(':has-text(')){
        const m=sel.match(/^(.*):has-text\("(.*)"\)$/);
        if(!m) return null;
        const nodes=[...document.querySelectorAll(m[1]||'*')];
        return nodes.find(n=>visible(n)&&n.textContent?.includes(m[2]))||null;
      }
      return [...document.querySelectorAll(sel)].find(visible)||null;
    }catch{return null;}
  }

  function find(key){
    for(const s of (S[key]||[])){
      const el=q(s);
      if(el) return el;
    }
    return null;
  }

  function click(key){
    const el=find(key);
    if(!el) return {ok:false,key,error:'not_found'};
    el.click();
    return {ok:true,key};
  }

  function fillPrompt(text){
    const el=find('promptInput');
    if(!el) return {ok:false,error:'prompt_not_found'};
    el.focus();
    if(el.tagName==='TEXTAREA'){
      el.value=text;
      el.dispatchEvent(new Event('input',{bubbles:true}));
    }else{
      document.execCommand('selectAll',false,null);
      document.execCommand('insertText',false,text);
      el.dispatchEvent(new Event('input',{bubbles:true}));
    }
    return {ok:true};
  }

  function probe(){
    return {
      promptInput: !!find('promptInput'),
      sendBtn: !!find('sendBtn'),
      imageToggle: !!find('imageToggle'),
      modelBtn: !!find('modelBtn')
    };
  }

  window.GeminiOps = {probe, click, fillPrompt, selectors:S, version:'0.1.0'};
})();


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