QuickRouter · 人工操作教材 · 非目标产品 UI / 非模型会话日志

Gemini CLI Plan 模式 · 配置与操作材料

plan-request.md

Use /plan first. Plan an offline converter, max side 1600, no upscaling, alpha preserved, separate output, deterministic collision suffixes and dry-run. Do not implement until I approve the plan.

convert.py

import argparse,hashlib,json
from pathlib import Path
from PIL import Image
def convert(source,dest,dry=False):
 source=Path(source).resolve();dest=Path(dest).resolve()
 if source==dest or dest.is_relative_to(source):raise ValueError('output must be separate from input')
 rows=[];reserved=set()
 for p in sorted(source.iterdir()):
  if p.suffix.lower() not in {'.png','.jpg','.jpeg'}:continue
  target=dest/(p.stem+'.webp');n=1
  while target.name in reserved or target.exists():target=dest/(p.stem+'-'+str(n)+'.webp');n+=1
  reserved.add(target.name)
  with Image.open(p) as im:
   original=im.size;im=im.convert('RGBA' if 'A' in im.getbands() else 'RGB');im.thumbnail((1600,1600))
   if not dry:dest.mkdir(parents=True,exist_ok=True);im.save(target,'WEBP',quality=85)
   rows.append({'source':p.name,'output':target.name,'before':original,'after':im.size,'alpha':'A' in im.getbands(),'sourceSha':hashlib.sha256(p.read_bytes()).hexdigest()})
 return rows
if __name__=='__main__':
 p=argparse.ArgumentParser();p.add_argument('--input',default='input');p.add_argument('--output',default='output');p.add_argument('--dry-run',action='store_true');a=p.parse_args();print(json.dumps(convert(a.input,a.output,a.dry_run),ensure_ascii=False,indent=2))