强化市场校验与索引元数据

合并市场索引、分类、i18n 校验、skill-creator 工具和已修改 skill 版本号更新。
This commit is contained in:
2026-07-07 21:14:31 +08:00
committed by GitHub
parent b273a1008a
commit a4718379d9
34 changed files with 1129 additions and 218 deletions

View File

@@ -336,6 +336,22 @@ jobs:
issue_number: context.issue.number,
labels: ['i18n-translation-failed'],
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
'🌐 **i18n translation check failed**',
'',
'Please inspect the workflow logs for the exact failing skill/locale.',
'',
'Common causes:',
'- A machine-translated locale could not be regenerated.',
'- A `translated_by: human` locale has source hash drift and must be manually synchronized.',
'',
'After manually reviewing a human-locked translation, update its `source_hash` to match the current source content.',
].join('\n'),
});
- name: Skip notice
if: steps.changes.outputs.relevant != 'true'

View File

@@ -65,7 +65,6 @@ jobs:
- name: Check for stale translations
if: steps.changes.outputs.relevant == 'true'
run: uv run --quiet scripts/i18n/translate.py --check
continue-on-error: true
- name: Skip notice
if: steps.changes.outputs.relevant != 'true'

235
README.md
View File

@@ -1,153 +1,152 @@
# DesireCore Market
DesireCore 官方市场仓库,存放经过官方验证的 AgentSkill 定义。
DesireCore 官方市场仓库,存放官方维护的 Agent/Skill 定义,以及经过整理的第三方 Skill 入口
## 目录结构
## Repository Shape
```
.
├── manifest.json # 仓库元数据
├── categories.json # 分类配置
├── README.md # 本文件
├── agents/ # Agent 定义目录
── task-master/
│ │ └── agent.json
│ ├── code-reviewer/
│ │ └── agent.json
│ ├── business-analyst/
│ │ └── agent.json
│ ├── writing-coach/
│ │ └── agent.json
│ ├── translator/
│ │ └── agent.json
│ └── data-analyst/
├── manifest.json # Market metadata, supported locales, aggregate stats
├── categories.json # Category registry and localized labels
├── builtin-skills.json # Built-in local SKILL.md skills
├── agents/
── desirecore/
│ └── agent.json
└── skills/ # Skill 定义目录
├── web-search/
── skill.json
├── file-manager/
│ └── skill.json
├── calendar-sync/
│ └── skill.json
├── doc-parser/
│ └── skill.json
└── email-client/
└── skill.json
└── skills/
├── <local-skill>/
── SKILL.md
│ └── SKILL.<locale>.md
└── <external-entry>/
└── entry.json
```
## Agent 清单
The market currently contains:
| ID | 名称 | 分类 | 定位 |
|----|------|------|------|
| task-master | 任务管家 | 效率 | 智能任务规划与追踪专家 |
| code-reviewer | 代码审查官 | 开发 | 资深代码审查与质量把控 |
| business-analyst | 商业洞察师 | 商业 | 数据驱动的商业分析顾问 |
| writing-coach | 写作教练 | 创意 | 从构思到润色的写作伙伴 |
| translator | 译界通 | 沟通 | 跨文化沟通与专业翻译 |
| data-analyst | 数据洞察师 | 数据 | 数据科学与业务洞察专家 |
- `1` Agent: `desirecore`
- `30` local built-in skills with `SKILL.md`
- `15` external skill entries with `entry.json`
- `45` publishable skills in total (`SKILL.md` + `entry.json`)
## Skill 清单
## Skill Sources
| ID | 名称 | 分类 | 风险等级 | 功能概述 |
|----|------|------|----------|----------|
| web-search | 网络搜索 | 效率 | 中 | 智能搜索与信息聚合 |
| file-manager | 文件管家 | 开发 | 高 | 本地文件系统管理 |
| calendar-sync | 日历同步 | 商业 | 中 | 日历服务连接与调度 |
| doc-parser | 文档解析器 | 数据 | 低 | 50+ 格式文档解析 |
| email-client | 邮件助理 | 沟通 | 高 | 智能邮件管理与起草 |
Local built-in skills are installable from this repository and must be listed in `builtin-skills.json`:
## 数据格式
```text
configuring-compute, create-agent, dashscope-image-gen, delete-agent,
dev-environment-setup, discover-agent, docx, frontend-design, guizang-ppt,
image-to-image, mail-operations, manage-skills, manage-teams, markdown,
minimax-music-gen, minimax-video-gen, nodejs-runtime, pdf, pptx,
python-runtime, registering-services, s3-storage-operations, skill-creator,
tech-diagram, update-agent, using-services, web-access, workflow,
xiaomi-tts, xlsx
```
### Agent 定义 (agents/{id}/agent.json)
External entries are marketplace pointers to Git/Web/ZIP sources:
```text
ai-news-radar, amap-jsapi-skill, dingtalk-api, flyai-skill,
follow-builders, larksuite-cli, luckin-my-coffee, mattpocock-skills,
minimax-image-gen, minimax-tts, mt-paotui-for-client, netease-skills,
taste-skill, wechatpay-skills, wecom-cli
```
## Data Formats
### Local Skill (`skills/<id>/SKILL.md`)
Local skills use YAML frontmatter plus Markdown body. The top-level `name` must equal the directory slug. Display strings live in `metadata.i18n`.
```yaml
---
name: web-access
description: >-
Use this skill when ...
version: 2.0.1
type: procedural
risk_level: low
status: enabled
metadata:
author: desirecore
updated_at: '2026-05-05'
i18n:
default_locale: en-US
source_locale: zh-CN
locales: [zh-CN, en-US]
zh-CN:
name: 联网访问
short_desc: 联网搜索、网页抓取、登录态浏览器访问
body: ./SKILL.zh-CN.md
translated_by: human
en-US:
name: Web Access
short_desc: Web search, page fetching, logged-in browser access
body: ./SKILL.md
source_hash: sha256:...
translated_by: human
market:
category: research
channel: latest
maintainer:
name: DesireCore Official
verified: true
---
```
### External Entry (`skills/<id>/entry.json`)
External entries point to upstream packages or repositories. They are counted in `manifest.stats.totalSkills` but are not included in `builtin-skills.json`.
```json
{
"id": "string",
"name": "string",
"avatar": { "t": "string", "bg": "string" },
"shortDesc": "string",
"fullDesc": "string",
"category": "productivity|development|business|creative|media|communication|data|management",
"tags": ["string"],
"version": "semver",
"latestVersion": "semver",
"updatedAt": "YYYY-MM-DD",
"maintainer": { "name": "string", "verified": boolean },
"downloads": number,
"rating": number,
"ratingCount": number,
"installStatus": "not_installed",
"persona": {
"role": "string",
"traits": ["string"],
"tools": ["string"]
"id": "example-skill",
"name": "Example Skill",
"category": "development",
"tags": ["example"],
"maintainer": {
"name": "Example",
"verified": false,
"account": "example",
"url": "https://github.com/example/example-skill"
},
"stewardship": "community",
"license": "MIT",
"redistribution": "allowed",
"source": {
"kind": "git",
"repoUrl": "https://github.com/example/example-skill.git",
"repoBranch": "main"
}
}
```
### Skill 定义 (skills/{id}/skill.json)
## Categories
```json
{
"id": "string",
"name": "string",
"icon": "lucide-icon-name",
"shortDesc": "string",
"fullDesc": "string",
"category": "productivity|development|business|creative|media|communication|data|management",
"tags": ["string"],
"version": "semver",
"latestVersion": "semver",
"updatedAt": "YYYY-MM-DD",
"maintainer": { "name": "string", "verified": boolean },
"downloads": number,
"rating": number,
"ratingCount": number,
"installStatus": "not_installed",
"riskLevel": "low|medium|high",
"requires": {
"tools": ["string"],
"connections": ["string"]
},
"compatibleAgents": ["string"]
}
Valid category slugs are declared in `categories.json`:
```text
productivity, development, business, creative, design, media,
communication, research, data, management
```
## 设计令牌
## Validation
头像背景色使用 DesireCore 设计系统:
- **Green (#34C759)**: 效率、商业类
- **Blue (#007AFF)**: 开发、沟通类
- **Purple (#AF52DE)**: 创意、数据类
## 多语言i18n
仓库支持多语言展示与多语言 SKILL.md 正文。当前覆盖 `zh-CN`(简体中文,源语言)与 `en-US`(英文,默认 fallback`manifest.json#supportedLocales` 声明。
- **作者只交一种语言**CI 自动翻译为其余语言。CI 默认走 [GitHub Models](https://docs.github.com/en/github-models)`openai/gpt-5-mini`,使用 repo 内置 `GITHUB_TOKEN` + `permissions: models: read`);可切到 Anthropic Claude设置 `vars.TRANSLATE_BACKEND=anthropic``secrets.ANTHROPIC_API_KEY`)。
- **顶层 `name` 字段为 ASCII slug**== 目录名),中文显示名安放在 `metadata.i18n.zh-CN.name`,符合 [agentskills.io 规范](https://agentskills.io/specification)。
- **正文文件**`SKILL.md`(默认语言)+ `SKILL.<locale>.md`(其他语言)兄弟文件。
工具:
Run these checks before submitting changes:
```bash
# 校验 i18n 完整性
# Full market + i18n validation
uv run scripts/i18n/validate-i18n.py
# 检查哪些 locale 缺翻译(不调 API
# Translation freshness check
uv run scripts/i18n/translate.py --check
# 本地翻译:默认 GitHub Models需要 fine-grained PAT 含 Models: Read
GITHUB_TOKEN=ghp_... uv run scripts/i18n/translate.py
# 切到 Anthropic Claude
TRANSLATE_BACKEND=anthropic ANTHROPIC_API_KEY=sk-ant-... \
uv run scripts/i18n/translate.py
# Optional network check for entry.json source URLs
uv run scripts/i18n/validate-i18n.py --online
```
详细规范见 [docs/I18N.md](docs/I18N.md)。
The validator checks market stats, category references, `builtin-skills.json`, `entry.json` structure, i18n completeness, and translation freshness. Human-locked translations (`translated_by: human`) must keep `source_hash` aligned after manual review.
## 许可证
Detailed i18n guidance is in [docs/I18N.md](docs/I18N.md).
MIT License - 详见 [LICENSE](LICENSE)
## License
MIT License. See [LICENSE](LICENSE).

View File

@@ -8,6 +8,7 @@
"discover-agent",
"docx",
"frontend-design",
"guizang-ppt",
"image-to-image",
"mail-operations",
"manage-skills",

View File

@@ -59,6 +59,21 @@
}
}
},
"design": {
"color": "#FF2D55",
"icon": "pen-tool",
"i18n": {
"default_locale": "en-US",
"en-US": {
"label": "Design",
"description": "Interface, visual, and product design skills for creating polished user experiences, layouts, and design systems."
},
"zh-CN": {
"label": "设计",
"description": "界面、视觉与产品设计技能,用于创建高质量用户体验、布局与设计系统"
}
}
},
"media": {
"color": "#AF52DE",
"icon": "image",
@@ -89,6 +104,21 @@
}
}
},
"research": {
"color": "#5AC8FA",
"icon": "search",
"i18n": {
"default_locale": "en-US",
"en-US": {
"label": "Research",
"description": "Information access, web research, source gathering, and synthesis skills for current or external knowledge."
},
"zh-CN": {
"label": "研究",
"description": "信息访问、联网调研、资料收集与综合分析技能,用于获取当前或外部知识"
}
}
},
"data": {
"color": "#AF52DE",
"icon": "database",

View File

@@ -76,11 +76,12 @@ market:
### 新建 skill首次贡献
1.`skills/<your-slug>/` 下新建目录与 `SKILL.md`
2. 编写中文 frontmatter + 中文 body
2. 编写中文 frontmatter + 中文 body,并给默认英文 locale 留可替换占位
- `name: <your-slug>`ASCII
- `metadata.i18n.{default_locale: en-US, source_locale: zh-CN, locales: [zh-CN]}`
- `metadata.i18n.{default_locale: en-US, source_locale: zh-CN, locales: [zh-CN, en-US]}`
- `metadata.i18n.zh-CN.{name, short_desc, description}`
- `metadata.i18n.zh-CN.body: ./SKILL.zh-CN.md`
- `metadata.i18n.en-US.{name, short_desc, description, body: ./SKILL.md, translated_by: ai:pending}`
- 中文正文写在 `SKILL.zh-CN.md`,首行 `<!-- locale: zh-CN -->`
-`SKILL.md` body 留作翻译占位符CI 会替换)
3. 本地校验:`uv run scripts/i18n/validate-i18n.py skills/<your-slug>`
@@ -97,14 +98,14 @@ CI 用 [GitHub Models inference API](https://docs.github.com/en/github-models)
- **切换模型**repository → Settings → Variables → `TRANSLATE_MODEL`,取值参考 [GitHub Models marketplace](https://github.com/marketplace?type=models) 的 `publisher/model-name` 格式。
- **切到 Anthropic Claude**:在 repo 加 secret `ANTHROPIC_API_KEY`,再加 variable `TRANSLATE_BACKEND=anthropic``TRANSLATE_MODEL=claude-sonnet-4-6`
提交回仓库的方式:`desirecore-bot` 在同 PR 追加 commit `chore(i18n): auto-translate skills [skip ci]`,并在 PR 留 review comment 提示人工核对。
提交回仓库的方式:`desirecore-bot` 在同 PR 追加 commit `chore(i18n): auto-translate skills`,并在 PR 留 comment 提示人工核对。该 commit 不带 `[skip ci]`,因为必需检查需要在翻译结果上重新运行。
### 修改已有 skill
1. 修改源语言 body`SKILL.zh-CN.md`)或 `metadata.i18n.zh-CN.{name,short_desc,description}`
2. 不要手动改 `source_hash`
3. 开 PR。CI 检测 hash 变化,重新翻译目标 locale 并追加 commit。
4. 若某个 locale 的 `translated_by: human`CI 会跳过该 locale 并在 PR 留 review 提示「目标语已脱钩,请确认」,由你手工同步
4. 若某个 locale 的 `translated_by: human``source_hash` 与源语言不一致CI 会失败并提示该人工翻译已过期。请手工同步目标语言正文,确认无误后更新 `source_hash`
---
@@ -114,6 +115,7 @@ CI 用 [GitHub Models inference API](https://docs.github.com/en/github-models)
# 校验 i18n 状态
uv run scripts/i18n/validate-i18n.py
uv run scripts/i18n/validate-i18n.py skills/web-access
uv run scripts/i18n/validate-i18n.py --online # 可选:检查 entry.json 外部 URL
# 一次性把旧格式 SKILL.md 迁移到 i18n 格式(旧 skill 已迁完,新 skill 一般不需要)
uv run scripts/i18n/migrate.py --apply skills/<your-slug>

View File

@@ -1,6 +1,6 @@
{
"name": "DesireCore Official Market",
"version": "1.2.7",
"version": "1.2.8",
"schemaVersion": "1.1.0",
"supportedLocales": ["zh-CN", "en-US"],
"defaultLocale": "en-US",
@@ -27,11 +27,11 @@
},
"stats": {
"totalAgents": 1,
"totalSkills": 26,
"totalSkills": 45,
"lastUpdated": "2026-07-07"
},
"features": [
"verified-only",
"curated-index",
"auto-sync",
"multi-language",
"ai-translation"

View File

@@ -79,7 +79,21 @@
"additionalProperties": true,
"properties": {
"icon": { "type": "string" },
"category": { "type": "string" },
"category": {
"type": "string",
"enum": [
"productivity",
"development",
"business",
"creative",
"design",
"media",
"communication",
"research",
"data",
"management"
]
},
"channel": {
"type": "string",
"enum": ["latest", "stable", "canary", "beta"]

View File

@@ -429,9 +429,10 @@ def translate_skill(
if target_block.get("translated_by") == "human":
if target_block.get("source_hash") != current_hash:
plan["actions"].append(
f"WARN: human-translated locale {target_locale} is stale "
f"(source_hash drift). Skipping; please update manually."
f"ERROR: human-translated locale {target_locale} is stale "
f"(source_hash drift). Skipping automatic translation; update it manually."
)
plan["human_stale"] = True
else:
plan["actions"].append(f"locale {target_locale} is human-locked, skipping")
return plan
@@ -604,6 +605,7 @@ def main(argv: list[str]) -> int:
})
needs = [p for p in plans if p.get("needs_translation")]
human_stale = [p for p in plans if p.get("human_stale")]
errs = [p for p in plans if p.get("errors")]
if args.check:
for p in plans:
@@ -612,7 +614,7 @@ def main(argv: list[str]) -> int:
for p in errs:
for e in p["errors"]:
print(f" ERROR [{p['skill']}/{p['target']}]: {e}")
return 1 if (needs or errs) else 0
return 1 if (needs or human_stale or errs) else 0
print(f"Backend: {backend} Model: {model} Endpoint: {endpoint}\n")
for p in plans:
@@ -621,7 +623,7 @@ def main(argv: list[str]) -> int:
print(f" - {a}")
for e in p.get("errors", []):
print(f" ✗ ERROR: {e}")
return 1 if errs else 0
return 1 if (human_stale or errs) else 0
if __name__ == "__main__":

View File

@@ -15,6 +15,9 @@ Checks:
6. Frontmatter parses cleanly; heading count of locale body matches source body (+/- 0).
7. categories.json's per-category i18n covers all locales declared in manifest.json.
8. Top-level description is 1-1024 chars (spec); top-level name is 1-64 chars (spec).
9. Skill/Agent counts and builtin skill index match the repository contents.
10. Skill, Agent, and entry.json category references exist in categories.json.
11. entry.json pointers have the required marketplace fields and safe source URLs.
Exit codes:
0 = pass
@@ -24,6 +27,7 @@ Exit codes:
Usage:
python3 scripts/i18n/validate-i18n.py # validate everything under repo root
python3 scripts/i18n/validate-i18n.py skills/web-access # validate single skill
python3 scripts/i18n/validate-i18n.py --online # also check entry.json source URLs
python3 scripts/i18n/validate-i18n.py --json # machine-readable output
"""
from __future__ import annotations
@@ -31,10 +35,13 @@ from __future__ import annotations
import argparse
import json
import re
import ssl
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Iterable
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
try:
import yaml
@@ -50,6 +57,7 @@ LOCALE_PATTERN = re.compile(r"^[a-z]{2,3}(?:-[A-Z]{2})?$")
LOCALE_HEADER_PATTERN = re.compile(r"^<!--\s*locale:\s*([a-zA-Z-]+)\s*-->")
HEADING_PATTERN = re.compile(r"^(#{1,6})\s+\S", re.MULTILINE)
FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n(.*)$", re.DOTALL)
SAFE_URL_PATTERN = re.compile(r"^https://")
@dataclass
@@ -100,7 +108,12 @@ def heading_count(text: str) -> int:
return len(HEADING_PATTERN.findall(text or ""))
def validate_skill(skill_dir: Path, report: Report, declared_locales: set[str] | None = None) -> None:
def validate_skill(
skill_dir: Path,
report: Report,
declared_locales: set[str] | None = None,
category_ids: set[str] | None = None,
) -> None:
"""Validate one skill directory (must contain SKILL.md)."""
rel_dir = skill_dir.relative_to(REPO_ROOT).as_posix()
skill_md = skill_dir / "SKILL.md"
@@ -117,6 +130,10 @@ def validate_skill(skill_dir: Path, report: Report, declared_locales: set[str] |
name = fm.get("name", "")
description = fm.get("description", "")
market = fm.get("market") or {}
category = market.get("category") if isinstance(market, dict) else None
if category is None:
category = fm.get("category")
# Rule 1: name spec-compliance + matches dir
if not isinstance(name, str) or not NAME_PATTERN.match(name) or len(name) > 64 or name in RESERVED_NAMES:
@@ -137,6 +154,15 @@ def validate_skill(skill_dir: Path, report: Report, declared_locales: set[str] |
f"description must be 1-1024 chars (got {len(description) if isinstance(description, str) else 'non-string'})"
))
if category_ids is not None:
if not isinstance(category, str) or not category.strip():
report.add(Issue(f"{rel_dir}/SKILL.md", "market-category", "market.category is missing"))
elif category not in category_ids:
report.add(Issue(
f"{rel_dir}/SKILL.md", "market-category",
f"category '{category}' is not declared in categories.json"
))
# Rule 2/3/4: i18n block
metadata = fm.get("metadata") or {}
i18n = metadata.get("i18n") if isinstance(metadata, dict) else None
@@ -245,18 +271,31 @@ def validate_skill(skill_dir: Path, report: Report, declared_locales: set[str] |
))
def validate_market_root(report: Report) -> set[str]:
"""Validate manifest.json + categories.json. Returns the declared locale set or empty."""
def load_json(path: Path, report: Report, rule: str) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
report.add(Issue(path.relative_to(REPO_ROOT).as_posix(), rule, "file not found"))
return {}
except json.JSONDecodeError as e:
report.add(Issue(path.relative_to(REPO_ROOT).as_posix(), rule, f"JSON parse error: {e}"))
return {}
if not isinstance(value, dict):
report.add(Issue(path.relative_to(REPO_ROOT).as_posix(), rule, "JSON root must be an object"))
return {}
return value
def validate_market_root(report: Report) -> tuple[set[str], set[str], dict[str, Any]]:
"""Validate manifest.json + categories.json. Returns locales, categories, manifest."""
manifest_path = REPO_ROOT / "manifest.json"
categories_path = REPO_ROOT / "categories.json"
declared: set[str] = set()
category_ids: set[str] = set()
manifest: dict[str, Any] = {}
if manifest_path.is_file():
try:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except json.JSONDecodeError as e:
report.add(Issue("manifest.json", "rule-7", f"JSON parse error: {e}"))
manifest = {}
manifest = load_json(manifest_path, report, "rule-7")
supported = manifest.get("supportedLocales") or []
if not isinstance(supported, list) or not all(isinstance(x, str) and LOCALE_PATTERN.match(x) for x in supported):
report.add(Issue("manifest.json", "rule-7", "supportedLocales must be a list of BCP-47 tags"))
@@ -267,11 +306,8 @@ def validate_market_root(report: Report) -> set[str]:
report.add(Issue("manifest.json", "rule-7", f"defaultLocale '{default}' not in supportedLocales"))
if categories_path.is_file() and declared:
try:
categories = json.loads(categories_path.read_text(encoding="utf-8"))
except json.JSONDecodeError as e:
report.add(Issue("categories.json", "rule-7", f"JSON parse error: {e}"))
return declared
categories = load_json(categories_path, report, "rule-7")
category_ids = set(categories)
for cat_id, cat in categories.items():
i18n = cat.get("i18n") if isinstance(cat, dict) else None
if not isinstance(i18n, dict):
@@ -284,7 +320,155 @@ def validate_market_root(report: Report) -> set[str]:
"categories.json", "rule-7",
f"category '{cat_id}' missing i18n.{locale}.label"
))
return declared
return declared, category_ids, manifest
def count_publishable_skills() -> tuple[list[str], list[str]]:
skill_md_names = sorted(p.parent.name for p in (REPO_ROOT / "skills").glob("*/SKILL.md"))
entry_names = sorted(p.parent.name for p in (REPO_ROOT / "skills").glob("*/entry.json"))
return skill_md_names, entry_names
def validate_builtin_skills(report: Report, skill_md_names: list[str]) -> None:
builtin_path = REPO_ROOT / "builtin-skills.json"
builtin = load_json(builtin_path, report, "builtin-skills")
skills = builtin.get("skills")
if not isinstance(skills, list) or not all(isinstance(x, str) for x in skills):
report.add(Issue("builtin-skills.json", "builtin-skills", "skills must be a list of strings"))
return
expected = sorted(skill_md_names)
actual = list(skills)
if actual != expected:
missing = sorted(set(expected) - set(actual))
extra = sorted(set(actual) - set(expected))
order_issue = not missing and not extra
parts = []
if missing:
parts.append(f"missing local SKILL.md skills {missing}")
if extra:
parts.append(f"contains non-local skills {extra}")
if order_issue:
parts.append("skills list is not sorted")
report.add(Issue("builtin-skills.json", "builtin-skills", "; ".join(parts)))
def validate_agent_json(report: Report, agent_file: Path, category_ids: set[str]) -> None:
rel = agent_file.relative_to(REPO_ROOT).as_posix()
agent = load_json(agent_file, report, "agent-json")
if not agent:
return
if agent.get("id") != agent_file.parent.name:
report.add(Issue(rel, "agent-json", f"id must equal parent directory '{agent_file.parent.name}'"))
category = agent.get("category")
if not isinstance(category, str) or category not in category_ids:
report.add(Issue(rel, "agent-json", f"category '{category}' is not declared in categories.json"))
def validate_entry_json(report: Report, entry_file: Path, category_ids: set[str], online: bool) -> None:
rel = entry_file.relative_to(REPO_ROOT).as_posix()
entry = load_json(entry_file, report, "entry-json")
if not entry:
return
required = ("id", "name", "category", "maintainer", "stewardship", "license", "redistribution", "source")
for key in required:
if key not in entry:
report.add(Issue(rel, "entry-json", f"missing required field '{key}'"))
if entry.get("id") != entry_file.parent.name:
report.add(Issue(rel, "entry-json", f"id must equal parent directory '{entry_file.parent.name}'"))
category = entry.get("category")
if not isinstance(category, str) or category not in category_ids:
report.add(Issue(rel, "entry-json", f"category '{category}' is not declared in categories.json"))
maintainer = entry.get("maintainer")
if not isinstance(maintainer, dict) or not isinstance(maintainer.get("name"), str):
report.add(Issue(rel, "entry-json", "maintainer.name is required"))
tags = entry.get("tags")
if tags is not None:
if not isinstance(tags, list) or not all(isinstance(x, str) for x in tags):
report.add(Issue(rel, "entry-json", "tags must be a list of strings"))
elif len(tags) != len(set(tags)):
report.add(Issue(rel, "entry-json", "tags must be unique"))
source = entry.get("source")
if not isinstance(source, dict):
report.add(Issue(rel, "entry-json", "source must be an object"))
return
kind = source.get("kind")
if kind not in {"git", "web", "zip"}:
report.add(Issue(rel, "entry-json", f"source.kind '{kind}' must be one of git/web/zip"))
repo_url = source.get("repoUrl")
if not isinstance(repo_url, str) or not repo_url.strip():
report.add(Issue(rel, "entry-json", "source.repoUrl is required"))
elif not SAFE_URL_PATTERN.match(repo_url):
report.add(Issue(rel, "entry-json", "source.repoUrl must use https://"))
elif online:
validate_url(report, rel, repo_url)
def validate_url(report: Report, path: str, url: str) -> None:
context = ssl.create_default_context()
for method in ("HEAD", "GET"):
try:
req = Request(url, method=method, headers={"User-Agent": "desirecore-market-validator/1.0"})
with urlopen(req, timeout=12, context=context) as resp:
if 200 <= resp.status < 400:
return
report.add(Issue(path, "entry-online", f"{url} returned HTTP {resp.status}"))
return
except HTTPError as e:
if method == "HEAD" and e.code in {403, 405}:
continue
report.add(Issue(path, "entry-online", f"{url} returned HTTP {e.code}"))
return
except (URLError, TimeoutError, OSError) as e:
report.add(Issue(path, "entry-online", f"{url} is not reachable: {e}"))
return
def validate_market_catalog(report: Report, manifest: dict[str, Any], category_ids: set[str], online: bool) -> None:
agent_files = sorted((REPO_ROOT / "agents").glob("*/agent.json"))
skill_md_names, entry_names = count_publishable_skills()
stats = manifest.get("stats")
if not isinstance(stats, dict):
report.add(Issue("manifest.json", "market-stats", "stats must be an object"))
else:
expected_agents = len(agent_files)
expected_skills = len(skill_md_names) + len(entry_names)
if stats.get("totalAgents") != expected_agents:
report.add(Issue(
"manifest.json", "market-stats",
f"stats.totalAgents is {stats.get('totalAgents')}, expected {expected_agents}"
))
if stats.get("totalSkills") != expected_skills:
report.add(Issue(
"manifest.json", "market-stats",
f"stats.totalSkills is {stats.get('totalSkills')}, expected {expected_skills}"
))
features = manifest.get("features") or []
if isinstance(features, list) and "verified-only" in features:
for entry_file in sorted((REPO_ROOT / "skills").glob("*/entry.json")):
entry = load_json(entry_file, report, "entry-json")
maintainer = entry.get("maintainer") if isinstance(entry, dict) else None
verified = maintainer.get("verified") if isinstance(maintainer, dict) else None
if entry.get("stewardship") != "official" or verified is not True:
report.add(Issue(
"manifest.json", "market-features",
"features includes 'verified-only' but the market contains non-official or unverified entry.json pointers"
))
break
validate_builtin_skills(report, skill_md_names)
for agent_file in agent_files:
validate_agent_json(report, agent_file, category_ids)
for entry_file in sorted((REPO_ROOT / "skills").glob("*/entry.json")):
validate_entry_json(report, entry_file, category_ids, online)
def iter_skill_dirs(targets: Iterable[Path]) -> Iterable[Path]:
@@ -302,11 +486,13 @@ def iter_skill_dirs(targets: Iterable[Path]) -> Iterable[Path]:
def main(argv: list[str]) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("paths", nargs="*", help="Skills or directories to validate (default: repo root)")
parser.add_argument("--online", action="store_true", help="Check entry.json source URLs with HEAD/GET requests")
parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON")
args = parser.parse_args(argv)
report = Report()
declared_locales = validate_market_root(report)
declared_locales, category_ids, manifest = validate_market_root(report)
validate_market_catalog(report, manifest, category_ids, online=args.online)
if args.paths:
targets = [Path(p).resolve() for p in args.paths]
@@ -314,7 +500,12 @@ def main(argv: list[str]) -> int:
targets = [REPO_ROOT / "skills"]
for skill_dir in iter_skill_dirs(targets):
validate_skill(skill_dir, report, declared_locales=declared_locales or None)
validate_skill(
skill_dir,
report,
declared_locales=declared_locales or None,
category_ids=category_ids or None,
)
if args.json:
json.dump([i.to_dict() for i in report.issues], sys.stdout, indent=2, ensure_ascii=False)

View File

@@ -6,7 +6,7 @@ description: >-
(write-only), verify them, and reload the configuration. Use when the user
asks to add or configure a model provider, set an API key, or fix compute
configuration. 用户要求配置算力、添加模型供应商或设置 API Key 时使用。
version: 1.0.0
version: 1.0.1
type: meta
risk_level: medium
status: enabled
@@ -44,6 +44,7 @@ metadata:
asks to add or configure a model provider, set an API key, or fix compute configuration.
body: ./SKILL.md
translated_by: human
source_hash: sha256:15e9247a2c27edf7
market:
icon: >-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#007AFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/></svg>

View File

@@ -8,7 +8,7 @@ description: >-
Use when the user mentions: generate image, draw, text-to-image, create image,
AI painting, illustration, design picture.
license: Complete terms in LICENSE.txt
version: 1.4.0
version: 1.4.1
type: procedural
risk_level: low
status: enabled
@@ -48,7 +48,7 @@ metadata:
short_desc: AI text-to-image generation (GPT Image / Wan models)
description: "Use this skill when the user wants to generate images. Supports multiple models: gpt-image-2 (default via DesireCore Cloud), wan2.7-image-pro / wan2.7-image (when user has DashScope configured). Uses the OpenAI-compatible /images/generations API. Trigger keywords: generate image, draw, text-to-image, create image, AI painting, illustration, design picture."
body: ./SKILL.md
source_hash: sha256:b3d5f7a91c2e4068
source_hash: sha256:8398726504b70586
translated_by: human
market:
icon: >-

View File

@@ -7,7 +7,7 @@ description: >-
decks (web PPT) in two visual systems — editorial "magazine × e-ink" or
"Swiss International". 当用户需要制作分享 / 演讲 / 发布会风格的网页 PPT或提到 杂志风 PPT、瑞士风
PPT、Swiss Style、horizontal swipe deck、网页 PPT 时使用。
version: 1.0.0
version: 1.0.1
type: procedural
risk_level: low
status: enabled
@@ -21,7 +21,7 @@ metadata:
author: 歸藏 (op7418)
updated_at: '2026-05-31'
i18n:
default_locale: zh-CN
default_locale: en-US
source_locale: zh-CN
locales:
- zh-CN
@@ -32,7 +32,7 @@ metadata:
description: >-
生成横向翻页的单文件 HTML 网页 PPT提供“电子杂志 × 电子墨水”与“瑞士国际主义”两套视觉系统,含 WebGL
背景、章节封面、数据大字报、图片网格等模板。适合线下分享、演讲、产品发布、年度总结等场景。
body: ./SKILL.md
body: ./SKILL.zh-CN.md
translated_by: human
en-US:
name: Guizang Web PPT
@@ -46,6 +46,8 @@ metadata:
locale intentionally falls back to the shared body, so it is locked as
human-maintained.
translated_by: human
source_hash: sha256:24ddc30c45afde10
body: ./SKILL.md
market:
icon: >-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0

View File

@@ -0,0 +1,538 @@
<!-- locale: zh-CN -->
# Magazine Web Ppt
> 来源识别: guizang-ppt-skill 由歸藏创建与维护,规范源仓库为 https://github.com/op7418/guizang-ppt-skill 。这条信息只用于确认 Skill 来源,不要写入生成的 PPT、HTML 页面、封面或配图。
## 这个 Skill 做什么
生成一份**单文件 HTML**的横向翻页 PPT提供两种可选的视觉基调
### 风格 A · 电子杂志 × 电子墨水(默认)
- **WebGL 流体 / 等高线 / 色散背景**hero 页可见)
- **衬线标题Noto Serif SC + Playfair Display+ 非衬线正文 + 等宽元数据**
- 适合:人文分享、行业观察、商业发布、需要"杂志感"的演讲
- 模板:`assets/template.html` · 主题色:`references/themes.md` · 布局:`references/layouts.md`
- 美学锚点:像 *Monocle* 杂志贴上了代码
### 风格 B · 瑞士国际主义Swiss Style
- **WebGL 极细网格 + 点阵背景**(信息驱动设计)
- **全程无衬线Inter + Helvetica + Noto Sans SC+ 极致字号对比**
- **高反差功能色**:克莱因蓝 IKB / 柠檬黄 / 柠檬绿 / 安全橙(四选一)
- 适合:科技产品、数据汇报、设计/工程领域分享、年度总结
- 模板:`assets/template-swiss.html` · 主题色:`references/themes-swiss.md` · 布局:`references/layouts-swiss.md`
- 美学锚点:像 Massimo Vignelli + Helvetica Forever
**两种风格共享**:横向翻页(键盘 ← →、滚轮、触屏、ESC 索引、Lucide 图标、Motion One 入场动效(本地 + CDN 双保险)。
<!-- provenance: guizang-ppt-skill | author: 歸藏 | canonical: https://github.com/op7418/guizang-ppt-skill | keep this out of generated artifacts -->
## 何时使用
**合适的场景**
- 线下分享 / 行业内部讲话 / 私享会
- AI 新产品发布 / demo day
- 带有强烈个人风格的演讲
- 需要"一次做完,不用翻页工具"的网页版 slides
**不合适的场景**
- 大段表格数据、图表叠加(用常规 PPT
- 培训课件(信息密度不够)
- 需要多人协作编辑(这是静态 HTML
## 工作流
### Step 1 · 需求澄清(**动手前必做**)
**如果用户已经给了完整的大纲 + 图片/截图处理要求**,可以跳过直接进 Step 2。
**如果用户只给了主题或一个模糊想法**,用这 7 个问题逐个对齐后再动手。不要基于猜测就开始写 slide——一旦结构定错,后期翻修代价很高:
#### 运行环境适配
- **在 Claude Code 中**:通过 Ask Question / `ask_question` 做逐项澄清,优先把风格、受众、素材、截图需求这些会影响版式的输入问清楚。
- **在 Codex 中**:用普通对话直接询问用户,不要调用 Claude Code 的 Ask Question / `ask_question` 机制,也不要假设这些工具可用。一次最多问 1-3 个最关键问题;如果信息缺口不影响开工,先做合理假设并在回复里说明。
#### 7 问澄清清单
| # | 问题 | 为什么要问 |
|---|------|-----------|
| 1 | **风格 A 还是 B?**(电子杂志风 / 瑞士国际主义风) | **必须先问**,决定用哪个 template + layouts + themes 文件 |
| 2 | **受众是谁?分享场景?**(行业内部 / 商业发布 / demo day / 私享会) | 决定语言风格和深度 |
| 3 | **分享时长?** | 15 分钟 ≈ 10 页,30 分钟 ≈ 20 页,45 分钟 ≈ 25-30 页 |
| 4 | **有没有原始素材?**(文档 / 数据 / 旧 PPT / 文章链接) | 有素材就基于素材,没有就帮他搭 |
| 5 | **有没有图片或截图?希望怎么处理?** | 决定图文版式、图片槽位、截图是否需要 CleanShot X 式适配或 GPT-M 2.0 重构 |
| 6 | **想要哪套主题色?** | 杂志风 5 套(`themes.md`) / 瑞士风 4 套(`themes-swiss.md`),挑一 |
| 7 | **有没有硬约束?**(必须包含 XX 数据 / 不能出现 YY) | 避免返工 |
#### 风格选择参考(问题 1)
| 如果用户说... | 推荐风格 |
|---|---|
| "杂志感" / "人文" / "Monocle 风" / 不指定 | **A · 电子杂志风** |
| "瑞士风" / "Swiss Style" / "Helvetica" / "极简" / "网格" / "信息图" / "数据驱动" | **B · 瑞士国际主义风** |
| 内容是 AI 产品 / 技术 / 工程 / 数据汇报 | B 更合适 |
| 内容是行业观察 / 人文 / 故事 / 文化 | A 更合适 |
| 用户给了大量 KPI 数字 / 路线图 / 流程 | B 更合适(`Data Hero` 布局是瑞士风专长) |
| 用户给了大量纪实照片 / 人文图片 | A 更合适(图片网格、左文右图是杂志风专长) |
| 用户需要 GPT-M 2.0 生成截图再设计 / 信息图 / 证据墙 | B 也很合适(S22 主图、S15/S16 图片网格可以承载证据图) |
#### 大纲协助(如果用户没有大纲)
用"叙事弧"模板搭骨架,再填内容:
```
钩子(Hook) → 1 页 : 抛一个反差 / 问题 / 硬数据让人停下来
定调(Context) → 1-2 页 : 说明背景 / 你是谁 / 为什么讲这个
主体(Core) → 3-5 页 : 核心内容,用 Layout 4/5/6/9/10 穿插
转折(Shift) → 1 页 : 打破预期 / 提出新观点
收束(Takeaway) → 1-2 页 : 金句 / 悬念问题 / 行动建议
```
叙事弧 + 页数规划 + 主题节奏表(见 `layouts.md`),**三张表对齐后**再进 Step 2。
大纲建议保存为 `项目记录.md``大纲-v1.md`,便于后续迭代。
#### 图片约定(告知用户)
在动手前向用户说清:
- **文件夹位置**:`项目/XXX/ppt/images/` 下(和 `index.html` 同级)
- **命名规范**:`{页号}-{语义}.{ext}`,例如 `01-cover.jpg` / `03-figma.jpg` / `05-dashboard.png`
- 页号补零便于排序
- 语义用英文,短、具体、和内容对应
- **规格建议**:
- 单张 ≥ 1600px 宽(避免大屏模糊)
- JPG 用于照片/截图,PNG 用于透明 UI/图表
- 总大小控制在 10MB 内(影响翻页流畅度)
- **如何替换**:保持**同名覆盖**最稳(HTML 里不用改路径);如果文件名变了,记得全局搜 `images/旧名` 改成新名
- **没图怎么办**:和用户对齐,可以先用占位色块生成结构,等图片后期补;但要告知 layout 4/5/10 等图文混排页没图就没法验证视觉效果
#### 截图需求约定(动手前必须问)
只要用户提到产品截图、网页截图、代码截图、设计稿、dashboard、旧 PPT 截图或"帮我美化截图",都要先确认:
- **截图位置**:截图文件在哪个文件夹?是否已经命名好?
- **使用目的**:保真展示 / 截图美化 / 截图再设计 / UI 情景图?
- **落位比例**:最终放进哪个版式槽位?常用 `21:9` / `16:10` / `16:9` / `4:3` / `1:1`
- **内容要求**:是否必须保留全部文字、品牌、数据?是否有敏感信息要遮挡?
- **视觉处理**:是否需要主题背景、留边、居中/角落对齐、拆成长截图面板?
默认策略:先让内容适配模板,再处理图片比例。截图需要保真时,先读 `references/screenshot-framing.md`,优先使用 `assets/screenshot-backgrounds/` 的内置背景资产做程序化 CleanShot X 式背景画布适配;只有原截图太乱、太长、太窄或需要概念化表达时,才用 GPT-M 2.0 做截图再设计。
#### Codex 配图生成(可选)
如果当前运行环境是 **Codex**,完成 deck 初稿后,主动问用户是否需要用 GPT-M 2.0 生成配图并插入 PPT。不要默认生成。
推荐询问方式:
> 要不要为这份 PPT 生成几张配图?可以做成人文纪实照片、杂志风信息图、流程/对比/系统关系图,或把截图再设计成统一的杂志风视觉。
如果用户确认生成,再问他想要哪种图片类型或风格;如果用户没有偏好,根据页面内容自行推荐 1-3 张最值得生成的配图。
如果用户提供的是截图,先判断是**截图美化**还是**截图再设计**:
- 截图美化:读 `references/screenshot-framing.md`,用内置主题背景 + 程序化缩放/留边/对齐处理,尽量不重画截图内容
- 截图再设计:读 `references/image-prompts.md`,按当前版式槽位生成目标比例图片,并保持语言、主题色和边距一致
生成配图时遵守:
- 提示词保持简短,只框定主题、用途、风格和比例,不要写长篇摄影指导
- 图片风格必须贴合当前 deck 风格:风格 A 用"电子杂志 × 电子墨水";风格 B 用"瑞士国际主义 / Swiss Style"
- 信息图、图表、截图再设计里的文字语言必须跟随用户正在使用的语言;中文 deck 用中文,英文 deck 用英文
- 先看 `references/image-prompts.md` 选择图片类型和基础提示词
- 如果处理用户原始截图,先看 `references/screenshot-framing.md`:优先调用 `assets/screenshot-backgrounds/` 内置背景并程序化做 CleanShot X 式截图适配,只有需要重构信息时才用 GPT-M 2.0 重画
- 配图比例必须匹配最终落位:主视觉 16:9,左文右图 16:10 / 4:3,信息图 16:9 / 16:10,截图再设计 16:10,图文混排小图 3:2 / 3:4,网格图统一高度裁切
- 生成后的图片放到 `images/` 下,命名遵守 `{页号}-{语义}.{ext}`
### Step 2 · 拷贝模板
**根据 Step 1 选定的风格,拷贝对应的模板**到目标位置(通常是 `项目/XXX/ppt/index.html`),同时在同级建一个 `images/` 文件夹准备接图片。
```bash
mkdir -p "项目/XXX/ppt/images"
# 风格 A · 电子杂志风
cp "<SKILL_ROOT>/assets/template.html" "项目/XXX/ppt/index.html"
# 或 风格 B · 瑞士国际主义风
cp "<SKILL_ROOT>/assets/template-swiss.html" "项目/XXX/ppt/index.html"
```
两个 `template*.html` 都是**完整可运行**的文件——CSS、WebGL shader、翻页 JS、字体/图标 CDN 全已预设好,只有 `<!-- SLIDES_HERE -->` 占位符等待你填充 slide 内容。
**注意**:风格 A 和 B **不能混用**。layouts.md 里的类(如 `.h-hero` 衬线大标题、`.display-zh` 等)只在 template.html 有定义layouts-swiss.md 里的类(如 `.kpi-hero``.accent-block``.span-N``.dots` 等)只在 template-swiss.html 有定义。一份 deck 只能选一套。
#### 2.1 · 必改占位符(**容易漏**
拷贝后立刻改掉以下占位符,否则浏览器 Tab 会显示"[必填] 替换为 PPT 标题"这种尴尬文字:
| 位置 | 原始 | 需改为 |
|------|------|--------|
| `<title>` | `[必填] 替换为 PPT 标题 · Deck Title` | 实际 deck 标题(如 `一种新的工作方式 · Luke Wroblewski`) |
每次拷贝完 template.html 第一件事:grep 一下"[必填]" 确认全部替换完。
#### 2.2 · 选定主题色(5 套预设 · 不允许自定义)
本 skill **只允许从 5 套精心调配的预设里选一套**,不接受用户自定义 hex 值——颜色搭配错了画面瞬间变丑,保护美学比给自由更重要。
| # | 主题 | 适合 |
|---|------|------|
| 1 | 🖋 墨水经典 | 通用 / 商业发布 / 不知道选啥的默认 |
| 2 | 🌊 靛蓝瓷 | 科技 / 研究 / 数据 / 技术发布会 |
| 3 | 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 |
| 4 | 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 |
| 5 | 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 |
**操作**:
1. 基于内容主题推荐一套,或直接问用户选哪一套
2. 打开 `references/themes.md`,找到对应主题的 `:root`
3. **整体替换** `assets/template.html`(已拷贝版本)开头 `:root{` 块里标有"主题色"注释的那几行(`--ink` / `--ink-rgb` / `--paper` / `--paper-rgb` / `--paper-tint` / `--ink-tint`)
4. 其他 CSS 都走 `var(--...)`,无需任何其他改动
**硬规则**:
- 一份 deck 只用一套主题,不要中途换色
- 不要接受用户给的任意 hex 值——委婉拒绝并展示 5 套让选
- 不要混搭(例如 ink 取墨水经典、paper 取沙丘)——会彻底违和
### Step 3 · 填充内容
#### 3.0 · 预检:类名必须在模板的 `<style>` 里有定义(**最重要**
**这是所有生成问题的源头**。layouts 骨架使用了很多类名,如果模板的 `<style>` 里没有对应定义,浏览器会 fallback 到默认样式——大标题字体错、卡片挤成一团、pipeline 糊成一行、图片堆到页面底部。
**两种风格类名互不通用**(再次强调):
- 风格 A 模板里有 `h-hero`(衬线)、`stat-card``grid-2-7-5``frame`
- 风格 B 模板里有 `h-hero`(无衬线)、`kpi-hero``accent-block``span-N``dots``grid-12`
- 同名 class 在两个模板里**视觉表现完全不同**(例:风格 A 的 `h-hero` 是 Noto Serif SC 衬线,风格 B 的 `h-hero` 是 Inter 无衬线)
**在写任何 slide 代码之前:**
1. **先 Read 当前用的模板**(至少读到 `<style>` 块末尾):
- 风格 A → `assets/template.html`
- 风格 B → `assets/template-swiss.html`
2. **对照对应 layouts 文件的 Pre-flight 列表**,确认你要用的每个类都在 `<style>` 里存在
3. 如果某个类缺失:**在模板的 `<style>` 里补上**,不要在每个 slide 里 inline 重写
4. **模板是唯一的类名来源**——不要发明新类名,如需自定义用 `style="..."` inline
**风格 A 常见容易遗漏的类**:
`h-hero` / `h-xl` / `h-sub` / `h-md` / `lead` / `kicker` / `meta-row` / `stat-card` / `stat-label` / `stat-nb` / `stat-unit` / `stat-note` / `pipeline-section` / `pipeline-label` / `pipeline` / `step` / `step-nb` / `step-title` / `step-desc` / `grid-2-7-5` / `grid-2-6-6` / `grid-2-8-4` / `grid-3-3` / `grid-6` / `grid-3` / `grid-4` / `frame` / `frame-img` / `img-cap` / `callout` / `callout-src` / `chrome` / `foot`
**风格 B 常见容易遗漏的类**(2026-05 重构后):
- 画布:`canvas-card` / `chrome-min`
- 排版:`h-hero`(无衬线 7.4vw weight 200) / `h-statement`(9.6vw) / `h-xl` / `h-md` / `t-cat`(SemiBold 600 小标) / `t-meta`(mono uppercase) / `lead` / `num-mega` / `mono`
- 卡片(四类互斥):`card-ink` / `card-accent` / `card-fill` / `card-outlined`
- 网格:`grid-12` / `grid-2-9` / `grid-2-9-5` / `span-N`
- 时间线:`timeline-v` + `tl-node` + `tl-axis` + `dot` / `timeline-h` + `tl-h-node` + `tl-h-axis`
- 图表:`kpi-tower-row` + `bar-tower` / `h-bar-chart` + `bar-row` + `bar-fill` / `spec-bars` + `bar-vert`
- 装饰:`dot-mat`(SVG mask 实心点)/ `ring-mat`(描边圆)/ `cross-mat`(× 网格)/ `hr-hairline`
- 版式专属:`cover-split` / `closing-split` / `duo-compare` + `vrule` / `manifesto-top` + `ink-banner-full` / `three-forces` / `loop-diagram` / `matrix-fill` + `matrix-cell` / `brief-grid` + `brief-card` / `system-diagram` / `why-now-grid` / `four-cards` / `stacked-ledger` + `ledger-row` / `tech-spec` / `image-hero` + `hero-img-wrap` + `hero-overlay-block` + `hero-stats`
- 图片混排:`frame-img` / `fit-contain` / `r-21x9` / `r-16x9` / `r-16x10` / `h-22` / `h-26` / `swiss-img-split` / `swiss-img-grid` / `swiss-img-caption` / `swiss-keyline` / `swiss-lined`
- spacing token:`--sp-3`...`--sp-13`(8/12/16/24/32/40/48/64/80/96/160 px)
#### 3.0.5 · 规划主题节奏(**和类预检同等重要**)
**在挑布局之前**,必须先列出每一页的主题 class(`hero dark` / `hero light` / `light` / `dark`)并写到文档或草稿里对齐。详细规则看 `references/layouts.md` 开头的"主题节奏规划"一节。
**强制规则**:
- 每页 section 必须带 `light` / `dark` / `hero light` / `hero dark` 之一,不要只写 `hero`
- 连续 3 页以上同主题 = 视觉疲劳,不允许
- 8 页以上必须有 ≥1 个 `hero dark` + ≥1 个 `hero light`
- 整个 deck 不能只有 `light` 正文页,必须有 `dark` 正文页制造呼吸
- 每 3-4 页插入 1 个 hero 页(封面/幕封/问题/大引用)
**生成后自检**:`grep 'class="slide' index.html` 列出所有主题,人工确认节奏合理再交付。
#### 3.1 · 挑布局
**不要从零写 slide**。打开对应的 layouts 文件,里面有 10 种现成布局骨架,每种都是完整可粘贴的 `<section>` 代码块。
**风格 A**`references/layouts.md`:
| Layout | 用途 |
|---|---|
| 1. 开场封面 | 第 1 页 |
| 2. 章节幕封 | 每幕开场 |
| 3. 数据大字报 | 抛硬数据 |
| 4. 左文右图(Quote + Image) | 身份反差 / 故事 |
| 5. 图片网格 | 多图对比 / 截图实证 |
| 6. 两列流水线(Pipeline) | 工作流程 |
| 7. 悬念收束 / 问题页 | 幕末 / 收尾 |
| 8. 大引用页(Big Quote) | 衬线金句 / takeaway |
| 9. 并列对比(Before / After) | 旧模式 vs 新模式 |
| 10. 图文混排(Lead Image + Side Text) | 信息密集的图文页 |
**风格 B** → 先读 `references/swiss-layout-lock.md`,再读 `references/layouts-swiss.md`
瑞士主题默认进入 **Swiss locked mode**:
- 正文页只能使用原始参考 PPT 登记的 22 个版式 `S01-S22`;新增首页/尾页只能使用 Skill 明确提供的 `SWISS-COVER-ASCII` / `SWISS-CLOSING-ASCII`
- 每个 `<section class="slide">` 必须写 `data-layout="Sxx"`。没有 `data-layout` 就视为未登记版式。
- 不允许临时发明 `P23/P24``Swiss Image Split``Evidence Grid` 这类原始 22P 之外的正文结构,除非用户明确要求实验版式。
- 顶部中文标题默认左对齐、处在左上内容轴。不要把小标题放左列、大标题放右列,造成视觉居中;只有原始 statement/split 版式允许强中心叙事。
- SVG 只负责几何图形。不要在 SVG 里写文字标签,所有标签改用 HTML 网格/卡片/caption。
- 地理/历史/城市路线/地点关系页使用 `S08 + Swiss Map Component`:先读 `references/swiss-map-component.md`,仍保留 `data-layout="S08"`
原始 22 个正文版式如下:
| Layout | 用途 |
|---|---|
| S01 Index Cover | 原始索引封面 |
| S02 Vertical Timeline + KPI | 演化对比 / 年代变迁 |
| S03 Split Statement | 核心论点 / 左右分屏 |
| S04 Six Cells | 6 项概念定义 |
| S05 Three Layers | 三层架构 |
| S06 KPI Tower | 4 项数据视觉化高度差 |
| S07 H-Bar Chart | 5-10 项排名比较 |
| S08 Duo Compare | Before/After 对照 |
| S09 Dot Matrix Statement | 大引述 / statement |
| S10 Split Closing | 收束页 |
| S11 Horizontal Timeline | 4-7 步流程 |
| S12 Manifesto + Ink Banner | 阶段性结论 |
| S13 Three Forces | 3 个对等概念深化 |
| S14 Loop Form | 自学闭环 / 自动化 |
| S15 Matrix + Hero Stat | 8-12 项矩阵 + 总数据 |
| S16 Multi-card Brief | 6 项快讯小卡 |
| S17 System Diagram | 三层架构 / 生态地图 |
| S18 Why Now | 三论点 + 数据支撑 |
| S19 Four Cards | 4 项等权特性 |
| S20 Stacked KPI Ledger | 纵向账单数据 |
| S21 Tech Spec Sheet | 产品规格 / benchmark |
| S22 Image Hero | 21:9 顶图 + 标题块 + 三列 KPI |
**登记扩展**:`S08 + Swiss Map Component` 用于地点、人物住所、路线、城市关系。它不是新 layout,而是 S08 右侧插槽的 MapLibre 地图组件;必须按 `references/swiss-map-component.md` 的点位、连线、卡片和右上角缩放/拖动控制实现。
选对应 layout,粘过去,改文案和图片路径即可。**务必先完成 3.0 预检**。
**风格 B 版式多样性硬规则**:
- 7-8 页 deck 至少使用 **6 个不同 S 编号版式**;10 页以上至少使用 8 个不同版式。
- 如果用户说"测试模板 / 看看效果 / 多一点版式",必须覆盖:一个封面、一个收尾、至少 1 个对比或时间线(S08/S11/S02)、至少 1 个结构图(S14/S17/S15)、至少 1 个图片版式(S22 或 S15/S16 图片格改造)。
- 不允许连续 3 页使用同一种主体结构,例如连续三页 `head + grid + card`
- 图片页不能偷懒发明新结构。2-3 张图时,用 S15/S16 的原始网格骨架改造成图片格;单张大图用 S22。
- 开写 HTML 前先列一张 `页码 → data-layout → 选用理由 → 图片槽位` 草稿;交付前运行 `node <SKILL_ROOT>/scripts/validate-swiss-deck.mjs index.html`
#### 3.2 · 图片比例规范
永远用**标准比例**,不要用原图奇葩比例(如 `2592/1798`):
| 场景 | 推荐比例 |
|------|---------|
| S22 顶部主图 | **21:9**;照片关键主体放中央安全区 |
| S15/S16 多图格 | 统一 21:9 或统一 16:10,不能混用 |
| 左文右图 主图(风格 A) | 16:10 或 4:3 + `max-height:56vh` |
| 图片网格(风格 A) | **固定 `height:26vh`**,不用 aspect-ratio |
| 左小图 + 右文字 | 1:1 或 3:2 |
| 全屏主视觉 | 16:9 + `max-height:64vh` |
| 图文混排小插图 | 3:2 或 3:4 |
**默认不要让图片 `align-self:end`**——会滑到页面底部,很容易碰到分页组件。用 grid 容器 + `align-items:start`(template 已预设)让图片贴顶即可;如果确实需要图文底对齐,必须先控制图片高度,再使用模板已有安全区类 `.nav-safe-bottom` / `.nav-safe-bottom-tight`,不要让最低处碰到分页组件。
**风格 B 瑞士风额外规则**:
- 单张大图用 S22;多图测试用 S15/S16 的原始卡片网格改造,不要用未登记的 P23/P24
- 生成图片前先写 `data-image-slot`:例如 `s22-hero-21x9` / `s15-grid-21x9` / `s16-brief-21x9`
- S22 配图默认生成 21:9,提示词必须包含 `subject centered in the safe middle area`;照片容器用 `object-position:center 35%`,不要用 `top center`
- 图片容器必须直角、无阴影、无圆角;默认背景用白色 `var(--paper)`,不要用灰底包白底信息图
- 白底 GPT 信息图/流程图/UI 图默认不要加外框描边,不要随手套 `.swiss-keyline`;需要强调时只用 `.swiss-lined` 的顶部 accent 线
- UI/信息图如果是用户原始截图或文字密集图,才用 `.fit-contain`;如果已按 S15/S16 槽位重生成,必须用 `.frame-img.r-21x9` / `.frame-img.r-16x10` 铺满容器,不要固定 `height:18vh` 后把图缩小
- 多图同组必须统一图片槽位、比例和高度,不能混用
- GPT-M 2.0 生成图使用 `image-prompts.md` 的"风格 B:瑞士国际主义配图规则"
- 任何图片、caption、timeline label、footnote 的最低处都不能进入底部分页区域;需要贴底时用 `.nav-safe-bottom` / `.nav-safe-bottom-tight`,不要手写 `bottom:2vh`
#### 3.2.1 · 中文大标题字号分档(风格 B 必做)
中文方块字视觉面积大,不能直接套英文 hero 的 6.8-7vw。写中文大标题前先分档:
| 标题形态 | 推荐字号 |
|---|---|
| 1 行,≤ 8 个中文字符 | `min(6.4vw,11.2vh)` |
| 2 行,每行≤ 8 个中文字符 | `min(5.8vw,10.2vh)` |
| 2 行,任一行 9-12 个中文字符 | `min(5.2vw,9.2vh)` |
| 3 行或更长 | 优先改写标题;不得已用 `min(4.6vw,8.2vh)` |
如果标题挤占了图片或正文区域,先压缩标题文案,再降字号;不要靠把下方内容推到底来硬塞。
#### 3.2.2 · 瑞士风演示最小字号与字重阶梯(风格 B 必做)
瑞士风用于投屏演示时,小字不能按网页注释的 10-12px 写。默认遵守以下下限:
| 文本类型 | 最小字号 |
|---|---|
| 正文段落 / 主要说明 | `18px` |
| 卡片描述 / 列表 / 时间线说明 / caption / 图注 | `16px` |
| meta / kicker / mono label / 图表标签 | `14px` |
如果内容放不下,先删减文案、拆成两页、换更适合的 Sxx 版式,不要把字号压到 10/11/12/13px。尤其是中文 deck,不要为了塞三行解释把 `body-sm`、caption、timeline label 改小。
**字号与字重阶梯(瑞士风核心)** — "越大越细,越小越粗"不是感性描述,而是具体映射:
| 字号区间 | 推荐字重 | 典型场景 |
|---|---|---|
| ≥ 8vw | 200 (ExtraLight) | 封面大字、巨号 KPI、h-statement |
| 4-7.9vw | 200-300 | 章节标题(h-xl/h-xl-zh)、大编号 |
| 1.8-3.9vw | 300-400 | 中型标题、takeaway 标题(≈1.8vw)、中号数字 |
| 1-1.7vw / 16-20px | 400-500 | 正文段落、卡片描述、说明文字 |
| 13-15px(小字) | 500-600 | meta、kicker、角标、图表标签、caption 强调 |
**硬规则:**
- 同一页内,字号越小的元素字重必须 ≥ 字号越大的元素(不允许 16px 正文用 300 而 1.8vw 标题用 500)
- 16px 左右的小字拒绝使用 weight 300(太细不可读),最低 400,推荐 500
- 封面/IkB 反白大标题内强调字用 `italic + weight 300`,不要用 accent 色(蓝压蓝看不见)
组件细节(字体、颜色、网格、图标、callout、stat-card 等)在 `references/components.md`
### Step 4 · 对照检查清单自检
生成完一定要打开 `references/checklist.md`,逐项对照。里面总结了**真实迭代过程中踩过的所有坑**P0 级别的问题emoji、图片撑破、标题换行、字体分工必须全部通过。
#### 4.0 · 不只看代码:必须打开网页做视觉核对
代码只能证明类名和结构存在,不能证明版式舒服。生成后必须打开网页逐页看:
1. 同时打开原始参考 PPT、当前模板或生成页、测试 PPT;原始参考是 `/Users/guohao/Documents/op7418的仓库/项目/Thin-Harness-Fat-Skills/ppt/index.html`
2. 截图前等入场动效稳定(约 1-2 秒),不要把动画中间态当成版式问题。
3. 先看视觉:大标题字重、标题与内容间距、图片是否与正文对齐、图片/说明是否碰到底部分页组件。
4. 再看代码:确认该页选用的版式与内容形状匹配,没有把数据专用版式拿来讲概念,也没有把可选组件堆成装饰。
5. 对照原始参考模板时,以实际页面用法为准,不要只看 CSS helper 定义;原始页面的大字实际多为 200/300,不要被 raw CSS 里的 700/800/900 带偏。
6. 如果页面别扭,先判断是版式选错、必选组件缺失、可选组件滥用,还是间距/安全区问题;不要直接靠加 margin 硬救。
#### 风格 A · 电子杂志风必查
1. **大标题必须是衬线字体**——如果显示成非衬线,99% 是 Step 3.0 预检没做,`h-hero` 类在 template.html 里缺失
2. **图片网格里只用 `height:Nvh`,不用 `aspect-ratio`**(会撑破)
3. **图片不能堆到页面底部**——不要用 `align-self:end`,用 grid + `align-items:start`(见 Step 3.2)
4. **图片只能用标准比例**(16:10 / 4:3 / 3:2 / 1:1 / 16:9),不要复制原图的奇葩比例
5. **中文大标题 ≤ 5 字且 `nowrap`**(避免 1 字 1 行)
6. **用 Lucide,不用 emoji**
7. **标题用衬线,正文用非衬线,元数据用等宽**
#### 风格 B · 瑞士国际主义必查
1. **全程无衬线**——任何衬线字体出现都是错的(检查 `font-family` 没用 `--serif` 类变量)
2. **只有一个 accent 色**——一份 deck 不能同时出现 IKB 蓝 + 柠檬黄 + 安全橙等多个高亮色
3. **不允许渐变 / 阴影 / 圆角**——所有色块直角纯色,任何 `box-shadow` / `linear-gradient` / `border-radius` > 0 都要砍掉(rule 横线除外)
4. **极致字号对比**——主标题与正文比例 ≥ 8:1
5. **大字号必须双约束限高**——`font-size:min(Xvw, Yvh)`,只用 vw 在标准 16:9 屏会溢出(吸取 P15/P20/P22 教训)
6. **大字字重 200**(ExtraLight)——字号越大越细,瑞士风灵魂;**禁止** 600/700/800 大字
7. **卡片填充类型互斥**——`card-ink` / `card-accent` / `card-fill` / `card-outlined` 四类**不能混用**(禁止"蓝底+蓝描边"、"灰底+描边"等)
8. **多卡并列时统一样式**——3-12 张卡用同一类(优先 `card-fill` 灰底);只突出一项时单独换 `card-accent`,且**只允许一张**
9. **直角到底**——任何 `border-radius` 都不允许;装饰用 8×8 直角小方块,**不要** 9px 圆形点
10. **图标用 lucide,不自己画 SVG**——`<i data-lucide="name"></i>` + `lucide.createIcons()`,选棱角风格(避免圆胖)
11. **时间线对齐**——axis 列固定 12px + dot 绝对定位,**不要**用 grid `justify-self`(会与虚线错位)
12. **章节级标题与内容间距 ≥ 9vh**——避免拥挤(吸取 P15/P16 教训)
13. **每页一个语义化动效 recipe**——不是统一 fade-up,数字 scale 弹入、bar scaleY 拉起、SVG stroke 描线、节点序列点亮等;**禁止**所有页用同一个 generic 配方
14. **playSlide 入口 reveal 容器**——`[data-anim]` 容器先强制 opacity:1,recipe 内再用 motion `{opacity:[0,1]}` 覆盖,否则有些页会"看不见"
15. **ESC 索引页可见性**——cloned slide 必须有 CSS override 让 `[data-anim]` 在缩略图里 opacity:1
16. **Helvetica/Inter 兜底中文字体**——Windows 用户没有"苹方",必须 fallback 到 `"Microsoft YaHei UI", "Noto Sans SC"`
17. **字体粗细体例**:大字 200 / 正文 300 / `t-cat` SemiBold 600 / `t-meta` mono uppercase
18. **保留低功耗快捷键**——右下角必须提示 `B 静态`;按 `B` 切换 `body.low-power`,停止 WebGL/ASCII canvas RAF 和 Motion 入场动画
19. **装饰元素严格在 grid 内**——bars 矩阵、点阵、ring-mat 不能贴边或溢出页面
20. **底部内容预留 nav 空间**——nav 在 ~97vh,内容收尾不要过 93vh(吸取 P22 KPI 大字溢底教训)
21. **图片容器直角无阴影**——`.frame-img` 不加 `border-radius` / `box-shadow`;边界只用 hairline
22. **S15/S16/S22 图片同组一致**——同一组图片统一比例、高度、边距、线条粗细;信息图/UI 图加 `.fit-contain`
23. **组件角色要正确**——S15/S16 图片格需要 caption 信息锚点;S22 的 KPI/说明是必选;数据专用版式必须有真实数据,不能靠文案硬填
24. **通用/非通用版式要分清**——S03/S08/S11/S19 较通用;S06/S07/S20/S21/S22 是数据/案例专用;S14/S15/S17 是结构专用
### Step 5 · 本地预览
直接在浏览器打开 `index.html` 就行。macOS 下:
```bash
open "项目/XXX/ppt/index.html"
```
不需要本地服务器。图片走相对路径 `images/xxx.png`
### Step 6 · 迭代
根据用户反馈修改——模板的 CSS 已经高度参数化90% 的调整都是改 inline style字号 `font-size:Xvw` / 高度 `height:Yvh` / 间距 `gap:Zvh`)。
---
## 资源文件导览
```
guizang-ppt-skill/
├── SKILL.md ← 你正在读
├── assets/
│ ├── template.html ← 风格 A · 电子杂志风模板(种子文件)
│ ├── template-swiss.html ← 风格 B · 瑞士国际主义风模板(种子文件)
│ ├── screenshot-backgrounds/ ← 截图美化内置背景(WebP):style-a 5 套 / style-b 4 套
│ └── motion.min.js ← Motion One 本地副本(离线兜底,约 64KB,共用)
├── scripts/
│ └── validate-swiss-deck.mjs ← 风格 B 静态校验:登记版式、图片槽位、SVG 文本、标题对齐
└── references/
├── components.md ← 组件手册字体、色、网格、图标、callout、stat、pipeline、动效... 风格 A 适用)
├── layouts.md ← 风格 A · 10 种页面布局骨架(可直接粘贴,含动效标记)
├── swiss-layout-lock.md ← 风格 B · 原始 22P 版式锁,正文页必须按这里登记
├── layouts-swiss.md ← 风格 B · 原始 22P 骨架说明 + 少量明确标注的实验区
├── swiss-map-component.md ← 风格 B · S08 地图扩展组件(MapLibre 点位/连线/卡片/控制)
├── themes.md ← 风格 A · 5 套主题色预设(只能选不能自定义)
├── themes-swiss.md ← 风格 B · 4 套瑞士风主题色预设IKB / 柠檬黄 / 柠檬绿 / 安全橙)
├── image-prompts.md ← GPT-M 2.0 配图类型、比例和基础提示词
├── screenshot-framing.md ← CleanShot X 式截图适配语义 + 内置背景资产映射
└── checklist.md ← 质量检查清单P0/P1/P2/P3 分级)
```
**加载顺序建议**
1. 先读完 `SKILL.md`(这个文件)了解整体
2. Step 1 需求澄清**第一问**先确定风格 A 还是 B,然后:
- 风格 A:读 `themes.md` 帮用户选一套主题色
- 风格 B:读 `themes-swiss.md` 帮用户选一套主题色
3. **动手前 Read 对应模板的 `<style>` 块**——这是类名的唯一来源,缺类会导致整页样式崩
- 风格 A → `assets/template.html`
- 风格 B → `assets/template-swiss.html`
4. 读对应的 layouts 文件挑布局:
- 风格 A → `layouts.md`(顶部有 Pre-flight 类名清单、主题节奏规划、动效 recipe 决策树)
- 风格 B → **先读 `swiss-layout-lock.md`**,再读 `layouts-swiss.md`;正文页必须从 S01-S22 选择,每页写 `data-layout`
5. 如果风格 B 需要地点、路线、人物住所或城市关系地图,读 `swiss-map-component.md`
6. 如果在 Codex 中生成配图,读 `image-prompts.md` 挑图片类型、比例和基础提示词;如果是用户原始截图,先读 `screenshot-framing.md`,优先使用 `assets/screenshot-backgrounds/` 的内置背景资产
7. 细节调整时读 `components.md` 查组件(含 Motion 动效系统章节,主要服务风格 A;风格 B 的组件细节在 `layouts-swiss.md` 附录)
8. 生成后先运行 `node scripts/validate-swiss-deck.mjs path/to/index.html`,再读 `checklist.md` 自检
**动效相关**:模板已把 Motion One 的加载和 recipe 逻辑内嵌到底部 module script。你不需要改 JS,只需要按 `layouts.md` / `layouts-swiss.md` 的骨架在 HTML 里加 `data-anim` / `data-animate` 即可。离线演示靠 `assets/motion.min.js`,断网时自动降级为"无动画但内容可读"。风格 B 模板必须保留 `B` 键低功耗模式:切换后停止 WebGL/ASCII canvas RAF,取消正在运行的 Web Animations,并把当前页内容直接 reveal 到静态最终态。
## 核心设计原则(哲学)
### 风格 A · 电子杂志风5 轮迭代总结)
> 违反其中任何一条,杂志感都会垮。
1. **克制优于炫技** — WebGL 背景只在 hero 页透出,普通页几乎看不见
2. **结构优于装饰** — 不用阴影、不用浮动卡片、不用 padding box一切信息靠**大字号 + 字体对比 + 网格留白**
3. **内容层级由字号和字体共同定义** — 最大衬线 = 主标题,中衬线 = 副标,大非衬线 = lead小非衬线 = body等宽 = 元数据
4. **图片是第一公民** — 图片只裁底部,保证顶部和左右完整;网格用 `height:Nvh` 固定,不要用 `aspect-ratio`
5. **节奏靠 hero 页** — hero 和 non-hero 交替,才不累眼睛
6. **术语统一** — Skills 就是 Skills不要中英混合翻译
### 风格 B · 瑞士国际主义风
> 违反其中任何一条,画面瞬间从瑞士掉到 PowerPoint。
1. **单一锚点色** — 一份 deck 只用一个 accent不允许多色高亮拼贴
2. **极致字号对比** — 主标题与正文比例 ≥ 8:1,KPI 必须是"Data Hero"(屏幕宽度的 18-22%)
3. **无衬线只此一家** — Inter / Helvetica / Noto Sans SC,任何衬线都是错的
4. **直角纯色** — 不允许渐变 / 阴影 / 圆角(rule 横线除外)
5. **网格至上** — 所有元素吸附到 12-col grid,左对齐 + 大幅留白做非对称美学
6. **Hairline 是手术刀** — 1px 的极细分割线就够,不要加粗、不要加阴影
7. **点阵装饰只在 hero 页透出** — 正文页保持纯净底色
## 参考作品
本 skill 的两种风格分别参考了:
**风格 A · 电子杂志风**:
- 歸藏 "一人公司:被 AI 折叠的组织" 分享2026-04-2227 页)
- *Monocle* 杂志的版式
- YC 总裁 Garry Tan "Thin Harness, Fat Skills" 那篇博客的 demo
**风格 B · 瑞士国际主义风**:
- Massimo Vignelli 的 NYC Subway / Unimark 系统
- *Helvetica Forever* 的字体设计语言
- Josef Müller-Brockmann 的网格系统经典著作
- 当代设计:Acne Studios / Off-White / IKEA / Beck Design
可以把它们当做风格锚点。

View File

@@ -8,7 +8,7 @@ description: >-
换背景、换风格、图片编辑、以图生图、参考图、基于这张图、
把这张图改成、在这张图上、image edit、img2img。
license: Complete terms in LICENSE.txt
version: 1.0.0
version: 1.0.1
type: procedural
risk_level: low
status: enabled
@@ -44,7 +44,7 @@ metadata:
short_desc: AI image editing and transformation based on reference images
description: "Use this skill when the user wants to edit, transform, or modify an existing image using AI. Supports image-to-image via gpt-image-2 model through the OpenAI-compatible images/generations API. Trigger keywords: image edit, img2img, modify image, transform image, change background, change style, based on this image, edit this image."
body: ./SKILL.md
source_hash: sha256:b3d5e7f01a2c4e8f
source_hash: sha256:12ffa59804b9fc9a
translated_by: human
market:
icon: >-

View File

@@ -9,7 +9,7 @@ description: >-
Use when 用户提到 邮件、邮箱、收件箱、发邮件、回复邮件、查邮件、Gmail、
Outlook、QQ邮箱、163邮箱、附件、标签、草稿、自动回复、邮件规则、
转发、抄送、未读邮件、收信、发信、邮件同步、邮件搜索。
version: 1.0.4
version: 1.0.5
type: procedural
risk_level: medium
status: enabled
@@ -44,7 +44,7 @@ metadata:
description: >-
Use this skill whenever the user wants to interact with email. This includes reading inbox, sending emails, replying, searching messages, managing labels and categories, downloading attachments, setting up auto-reply rules, or triggering agents to handle incoming emails. Supports Gmail, Outlook, and IMAP/SMTP (QQ Mail, 163, Yahoo, etc.) through DesireCore's local REST API. Use when the user mentions email, mailbox, inbox, sending email, replying, checking email, Gmail, Outlook, QQ Mail, 163 Mail, attachments, labels, drafts, auto-reply, email rules, forwarding, CC, unread email, receiving, sending, email sync, or email search.
body: ./SKILL.md
source_hash: sha256:24bffbade0dc09a7
source_hash: sha256:0ee04ebff49d92ea
translated_by: human
translated_at: '2026-05-03'
market:

View File

@@ -4,7 +4,7 @@ description: >-
管理 Agent 的技能生命周期:通过 HTTP API 导入、安装、更新、删除技能,
或通过 AgentFS 文件系统直接编写符合规范的 SKILL.md。Use when 用户要求
安装技能、从 URL/Git 导入技能、编写新技能、或管理已有技能。
version: 1.0.4
version: 1.0.5
type: meta
risk_level: low
status: enabled
@@ -38,7 +38,7 @@ metadata:
description: >-
Manage the Skill lifecycle of an Agent: import, install, update, and delete Skills via HTTP API, or directly author standards-compliant SKILL.md files via the AgentFS filesystem. Use when the user requests to install Skills, import Skills from URL/Git, author new Skills, or manage existing Skills.
body: ./SKILL.md
source_hash: sha256:e67016840ba430ae
source_hash: sha256:eb098cb8c9bdf482
translated_by: human
market:
icon: >-

View File

@@ -7,7 +7,7 @@ description: >-
AI 作曲、创作歌曲、写一首歌、音乐生成、AI 音乐、MiniMax 音乐、
作词作曲、纯音乐、伴奏、翻唱、cover。
license: Complete terms in LICENSE.txt
version: 1.1.4
version: 1.1.5
type: procedural
risk_level: low
status: enabled
@@ -45,7 +45,7 @@ metadata:
description: >-
Use this skill when the user wants to generate music using MiniMax's Music Generation API. Supports text-to-music with lyrics, instrumental generation, and music cover. Use when the user mentions generating music, text-to-music, AI composing, creating songs, writing a song, music generation, AI music, MiniMax music, songwriting, instrumental music, accompaniment, cover, or remake.
body: ./SKILL.md
source_hash: sha256:f3785e1da2fc5a11
source_hash: sha256:dfd9408d2a567bac
translated_by: human
market:
icon: >-

View File

@@ -7,7 +7,7 @@ description: >-
Use when 用户提到 生成视频、文生视频、AI 视频、创建视频、视频生成、
动画生成、MiniMax 视频、海螺、Hailuo、图片变视频、图生视频。
license: Complete terms in LICENSE.txt
version: 1.3.0
version: 1.3.1
type: procedural
risk_level: low
status: enabled
@@ -45,7 +45,7 @@ metadata:
description: >-
Use this skill when the user wants to generate videos using MiniMax's Hailuo model. Supports text-to-video, image-to-video, and subject reference. The API is asynchronous — submit a task, poll for status, then download. Use when the user mentions generating videos, text-to-video, AI video, creating videos, video generation, animation generation, MiniMax video, Hailuo, image-to-video.
body: ./SKILL.md
source_hash: sha256:3b2855b9ff2d0ef1
source_hash: sha256:47463799495b3fdf
translated_by: human
market:
icon: >-

View File

@@ -13,7 +13,7 @@ description: >-
"yarn", "volta", "nvm", "fnm", "nodejs version", "package-lock", or any
Node.js / npm runtime error. 使用场景:用户需要 安装 Node.js、安装 npm、
pnpm、yarn、配置全局包、解决 EACCES、PATH 问题、镜像/代理配置。
version: 1.0.2
version: 1.0.3
type: procedural
risk_level: low
status: enabled
@@ -50,7 +50,7 @@ metadata:
description: >-
Use this skill when the user needs to install, upgrade, or troubleshoot Node.js, npm, pnpm, yarn, and JavaScript/TypeScript runtime environments. Covers four-tier fallback strategy: (1) DesireCore HTTP API for in-app installation, (2) DesireCore built-in Volta CLI for Node.js + package manager version management, (3) system package managers (brew/apt/dnf/winget/NodeSource), (4) community nvm/fnm as last resort. Also covers global package management, npm registry/proxy configuration, EACCES permission errors, and PATH troubleshooting. Triggers include: "install node", "node not found", "npm not found", "npm EACCES", "pnpm", "yarn", "volta", "nvm", "fnm", "nodejs version", "package-lock", or any Node.js / npm runtime error. Use cases: the user needs to install Node.js, install npm, pnpm, yarn, configure global packages, resolve EACCES, PATH issues, registry mirror / proxy configuration.
body: ./SKILL.md
source_hash: sha256:2b8a00816c65d71c
source_hash: sha256:cb8150156dd47cbc
translated_by: human
translated_at: '2026-05-03'
market:

View File

@@ -12,7 +12,7 @@ description: >-
content afterward. If a .pptx file needs to be opened, created, or touched,
use this skill. Use when 用户提到 PPT、演示文稿、幻灯片、演讲稿、汇报材料、
pptx、创建演示、编辑幻灯片。
version: 1.0.3
version: 1.0.4
type: procedural
risk_level: low
status: enabled
@@ -46,7 +46,7 @@ metadata:
description: >-
Use this skill any time a .pptx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates, layouts, speaker notes, or comments. Trigger whenever the user mentions "deck," "slides," "presentation," or references a .pptx filename, regardless of what they plan to do with the content afterward. If a .pptx file needs to be opened, created, or touched, use this skill. Use when the user mentions PPT, presentation, slides, speaker notes, briefing materials, pptx, creating presentations, or editing slides.
body: ./SKILL.md
source_hash: sha256:a952bf5622ede8e0
source_hash: sha256:9d8c46a62ef8bfb8
translated_by: human
market:
icon: >-

View File

@@ -13,7 +13,7 @@ description: >-
version", "pip command not found", or any Python-related runtime error.
使用场景:用户需要 安装 Python、安装 pip、配置虚拟环境、管理多版本、
解决 PEP 668、import 失败、PATH 问题、SSL 证书错误等。
version: 1.0.2
version: 1.0.3
type: procedural
risk_level: low
status: enabled
@@ -51,7 +51,7 @@ metadata:
description: >-
Use this skill when the user needs to install, upgrade, or troubleshoot Python and pip environments. Covers four-tier fallback strategy: (1) DesireCore HTTP API for in-app installation, (2) DesireCore built-in Hatch CLI for Python version management, (3) system package managers (brew/apt/dnf/winget), (4) community pyenv as last resort. Also covers virtual environments (venv/pipx/conda), PEP 668 externally-managed errors, and import / PATH troubleshooting. Triggers include: "install python", "pip not found", "python not found", "PEP 668", "externally-managed", "venv", "virtualenv", "pipx", "conda", "miniconda", "pyenv", "hatch", "python version", "pip command not found", or any Python-related runtime error. Use when the user needs to install Python, install pip, configure virtual environments, manage multiple versions, resolve PEP 668, import failures, PATH issues, SSL certificate errors, etc.
body: ./SKILL.md
source_hash: sha256:ea796e0282dc77af
source_hash: sha256:585a14843750b051
translated_by: human
translated_at: '2026-05-03'
market:

View File

@@ -5,7 +5,7 @@ description: >-
Catalog so that other Agents (and the human user) can discover and call it.
Use when the user mentions adding a new service, registering an API, or
publishing a backend you control to the team's catalog. 新增服务、注册 API、把后端发布到团队目录时使用。
version: 1.0.1
version: 1.0.2
type: meta
risk_level: medium
status: enabled
@@ -39,6 +39,7 @@ metadata:
Register an external HTTP/MCP service to the global Application & Service Catalog so that other Agents (and the human user) can discover and call it. Use when the user mentions adding a new service, registering an API, or publishing a backend you control to the team's catalog.
body: ./SKILL.md
translated_by: human
source_hash: sha256:dcd8732ac76f009f
market:
icon: >-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#34C759" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="12" height="6" rx="1.5"/><rect x="3" y="14" width="12" height="6" rx="1.5"/><circle cx="6.5" cy="7" r="0.5" fill="#34C759"/><circle cx="6.5" cy="17" r="0.5" fill="#34C759"/><line x1="19.5" y1="5" x2="19.5" y2="11"/><line x1="16.5" y1="8" x2="22.5" y2="8"/></svg>

View File

@@ -1,7 +1,7 @@
---
name: s3-storage-operations
description: 操作 S3 兼容对象存储(上传、下载、列举、删除),通过 DesireCore HTTP API 调用。Use when 用户要求上传/下载/分享文件、需要生成下载链接、或工作流产出文件需要持久化存储与分发。
version: 2.0.3
version: 2.0.4
type: procedural
risk_level: medium
status: enabled
@@ -36,7 +36,7 @@ metadata:
description: >-
Operate S3-compatible object storage (upload, download, list, delete) via the DesireCore HTTP API. Use when the user requests file upload/download/sharing, needs a download link, or when workflow outputs need persistent storage and distribution.
body: ./SKILL.md
source_hash: sha256:6ea8e1375f12de72
source_hash: sha256:0182951808f5f831
translated_by: human
translated_at: '2026-05-03'
market:

View File

@@ -5,7 +5,7 @@ description: >-
frontmatter 元数据 + L0/L1/L2 分层内容 + 脚本/参考/资产)和 Claude Code
基础格式。Use when 用户要求创建新技能、更新已有技能、或将经验封装为可复用
的技能包。
version: 1.0.3
version: 1.0.4
type: meta
risk_level: low
status: enabled
@@ -39,7 +39,7 @@ metadata:
description: >-
Guides users to create and edit standards-compliant SKILL.md skill packages. Supports the DesireCore full format (frontmatter metadata + L0/L1/L2 layered content + scripts/references/assets) and the Claude Code basic format. Use when the user requests to create a new Skill, update an existing Skill, or package experience into a reusable Skill bundle.
body: ./SKILL.md
source_hash: sha256:2e8b886dc0b77dd1
source_hash: sha256:e14a6879bb800455
translated_by: human
market:
icon: >-

View File

@@ -68,7 +68,7 @@ market:
|------|------|------|
| `market.icon` | string | 内联 SVG 图标 |
| `market.short_desc` | string | 一句话简介 |
| `market.category` | string | 分类 slug`productivity``knowledge``development` |
| `market.category` | string | 分类 slug`productivity``design``research``development` |
| `market.maintainer.name` | string | 维护者名称 |
| `market.maintainer.verified` | boolean | 是否官方认证 |
| `market.compatible_agents` | string[] | 兼容的 Agent ID |

View File

@@ -23,21 +23,59 @@ DESIRECORE_TEMPLATE = """\
---
name: {skill_name}
description: >-
[TODO: 完整描述技能用途。必须包含 "Use when" 触发提示,
帮助 AI 判断何时使用该技能。]
[TODO: Complete and informative explanation of what the skill does and when to
use it. Include "Use when" trigger hints and Chinese trigger keywords.]
version: 1.0.0
type: procedural
risk_level: low
status: enabled
tags:
- [TODO: 添加标签]
- {first_tag}
metadata:
author: user
updated_at: '{today}'
i18n:
default_locale: en-US
source_locale: zh-CN
locales:
- zh-CN
- en-US
zh-CN:
name: {skill_title}
short_desc: '[TODO: 中文一句话简介]'
description: >-
[TODO: 中文较长描述。]
body: ./SKILL.zh-CN.md
translated_by: human
en-US:
name: {skill_title}
short_desc: '[TODO: English one-line summary]'
description: >-
[TODO: Longer English description. CI can replace this when source_hash
is missing or stale.]
body: ./SKILL.md
translated_by: ai:pending
market:
icon: ''
category: productivity
channel: latest
maintainer:
name: user
verified: false
---
# {skill_title}
_Translation pending. The source body is in `SKILL.zh-CN.md`; run
`uv run scripts/i18n/translate.py {skill_path}` from the market repository to
generate this default-language body._
"""
DESIRECORE_SOURCE_BODY = """\
<!-- locale: zh-CN -->
# {skill_title}
## L0一句话摘要
[TODO: 用一句话描述这个技能做什么]
@@ -182,6 +220,8 @@ def init_skill(skill_name, path, fmt='desirecore'):
skill_content = template.format(
skill_name=skill_name,
skill_title=skill_title,
first_tag=skill_name.split('-')[0],
skill_path=f"skills/{skill_name}",
today=date.today().isoformat(),
)
@@ -193,6 +233,15 @@ def init_skill(skill_name, path, fmt='desirecore'):
print(f"❌ Error creating SKILL.md: {e}")
return None
if fmt == 'desirecore':
source_body = DESIRECORE_SOURCE_BODY.format(skill_title=skill_title)
try:
(skill_dir / 'SKILL.zh-CN.md').write_text(source_body)
print("✅ Created SKILL.zh-CN.md (source locale)")
except Exception as e:
print(f"❌ Error creating SKILL.zh-CN.md: {e}")
return None
# Create resource directories with example files
try:
scripts_dir = skill_dir / 'scripts'

View File

@@ -8,6 +8,7 @@ Also accepts Claude Code basic format (name + description only).
import sys
import re
import json
from pathlib import Path
try:
@@ -17,30 +18,36 @@ except ImportError:
sys.exit(1)
# DesireCore 已知的顶层字段集合
# 来源lib/schemas/agent/skill-frontmatter.ts 的 properties 定义
# Schema 设置了 additionalProperties: true所以未知字段只警告不报错
KNOWN_PROPERTIES = {
# 核心字段
'name', 'description', 'version', 'type', 'requires',
'risk_level', 'status', 'tags', 'metadata',
# 功能控制
'disable-model-invocation', 'disable_model_invocation',
'allowed-tools', 'user-invocable', 'argument-hint',
'model', 'context', 'agent',
# 高级字段
'error_message', 'skill_package', 'input_schema', 'output_schema',
'market', 'x_desirecore', 'json_output',
# Claude Code 兼容字段
'license', 'compatibility',
}
REPO_ROOT = Path(__file__).resolve().parents[3]
SCHEMA_PATH = REPO_ROOT / 'scripts' / 'i18n' / 'schema' / 'skill-frontmatter.schema.json'
CATEGORIES_PATH = REPO_ROOT / 'categories.json'
VALID_TYPES = {'procedural', 'conversational', 'meta'}
VALID_RISK_LEVELS = {'low', 'medium', 'high'}
VALID_STATUSES = {'enabled', 'disabled'}
def load_market_schema():
if not SCHEMA_PATH.is_file():
return {}
return json.loads(SCHEMA_PATH.read_text(encoding='utf-8'))
SCHEMA = load_market_schema()
SCHEMA_PROPERTIES = SCHEMA.get('properties', {}) if isinstance(SCHEMA, dict) else {}
KNOWN_PROPERTIES = set(SCHEMA_PROPERTIES)
REQUIRED_PROPERTIES = set(SCHEMA.get('required', [])) if isinstance(SCHEMA, dict) else set()
VALID_TYPES = set(SCHEMA_PROPERTIES.get('type', {}).get('enum', []))
VALID_RISK_LEVELS = set(SCHEMA_PROPERTIES.get('risk_level', {}).get('enum', []))
VALID_STATUSES = set(SCHEMA_PROPERTIES.get('status', {}).get('enum', []))
VALID_CONTEXTS = {'default', 'fork'}
SEMVER_RE = re.compile(r'^\d+\.\d+\.\d+$')
KEBAB_RE = re.compile(r'^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$')
SEMVER_RE = re.compile(SCHEMA_PROPERTIES.get('version', {}).get('pattern', r'^\d+\.\d+\.\d+$'))
NAME_RE = re.compile(SCHEMA_PROPERTIES.get('name', {}).get('pattern', r'^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$'))
TAG_RE = re.compile(SCHEMA_PROPERTIES.get('tags', {}).get('items', {}).get('pattern', r'^[a-z0-9][a-z0-9-]*$'))
LOCALE_RE = re.compile(r'^[a-z]{2,3}(?:-[A-Z]{2})?$')
def load_category_ids():
if not CATEGORIES_PATH.is_file():
return set()
data = json.loads(CATEGORIES_PATH.read_text(encoding='utf-8'))
return set(data) if isinstance(data, dict) else set()
def validate_skill(skill_path):
@@ -76,8 +83,11 @@ def validate_skill(skill_path):
return False, [f"Invalid YAML: {e}"], []
# === 必填字段 ===
if 'description' not in frontmatter:
errors.append("Missing required field: 'description'")
is_basic_claude_skill = set(frontmatter).issubset({'name', 'description', 'license', 'compatibility'})
required = {'name', 'description'} if is_basic_claude_skill else REQUIRED_PROPERTIES
for field in sorted(required):
if field not in frontmatter:
errors.append(f"Missing required field: '{field}'")
# === description 质量检查 ===
description = frontmatter.get('description', '')
@@ -96,15 +106,15 @@ def validate_skill(skill_path):
n = name.strip()
if len(n) > 64:
errors.append(f"Name too long ({len(n)} chars, max 64)")
# kebab-case 检查仅当 name 是英文时
if re.match(r'^[a-z0-9-]+$', n):
if not KEBAB_RE.match(n):
warnings.append(f"Name '{n}' starts/ends with hyphen or has consecutive hyphens")
if not NAME_RE.match(n):
errors.append("Name must be lowercase ASCII kebab-case, cannot start/end with hyphen, and cannot contain consecutive hyphens")
if n != skill_path.name:
errors.append(f"Name '{n}' must equal parent directory '{skill_path.name}'")
# === version 格式检查 ===
version = frontmatter.get('version')
if version is not None and not SEMVER_RE.match(str(version)):
warnings.append(f"Version '{version}' is not valid semver (expected x.y.z)")
errors.append(f"Version '{version}' is not valid semver")
# === 枚举字段检查 ===
skill_type = frontmatter.get('type')
@@ -123,6 +133,61 @@ def validate_skill(skill_path):
if context is not None and context not in VALID_CONTEXTS:
errors.append(f"Invalid context: '{context}'. Must be one of: {', '.join(sorted(VALID_CONTEXTS))}")
tags = frontmatter.get('tags')
if tags is not None:
if not isinstance(tags, list) or not all(isinstance(x, str) for x in tags):
errors.append("tags must be a list of strings")
else:
duplicate_tags = sorted({x for x in tags if tags.count(x) > 1})
if duplicate_tags:
errors.append(f"tags must be unique; duplicates: {', '.join(duplicate_tags)}")
invalid_tags = [x for x in tags if not TAG_RE.match(x)]
if invalid_tags:
errors.append(f"Invalid tag(s): {', '.join(invalid_tags)}")
if not is_basic_claude_skill:
metadata = frontmatter.get('metadata')
if not isinstance(metadata, dict):
errors.append("metadata must be an object")
else:
i18n = metadata.get('i18n')
if not isinstance(i18n, dict):
errors.append("metadata.i18n block missing")
else:
locales = i18n.get('locales')
if not isinstance(locales, list) or not all(isinstance(x, str) and LOCALE_RE.match(x) for x in locales):
errors.append("metadata.i18n.locales must be a list of BCP-47 locale strings")
locale_set = set()
else:
locale_set = set(locales)
for key in ('default_locale', 'source_locale'):
value = i18n.get(key)
if not isinstance(value, str) or not LOCALE_RE.match(value):
errors.append(f"metadata.i18n.{key} must be a BCP-47 locale string")
elif value not in locale_set:
errors.append(f"metadata.i18n.{key} must be present in metadata.i18n.locales")
for locale in sorted(locale_set):
payload = i18n.get(locale)
if not isinstance(payload, dict):
errors.append(f"metadata.i18n.{locale} block missing")
continue
for field in ('name', 'short_desc'):
if not isinstance(payload.get(field), str) or not payload.get(field).strip():
errors.append(f"metadata.i18n.{locale}.{field} is required")
body = payload.get('body')
if body is not None:
if not isinstance(body, str) or not body.startswith('./') or not body.endswith('.md'):
errors.append(f"metadata.i18n.{locale}.body must be a relative Markdown path starting with './'")
elif not (skill_path / body.removeprefix('./')).is_file():
errors.append(f"metadata.i18n.{locale}.body points to missing file: {body}")
market = frontmatter.get('market')
if isinstance(market, dict):
category = market.get('category')
category_ids = load_category_ids()
if category_ids and category not in category_ids:
errors.append(f"market.category '{category}' is not declared in categories.json")
# === 未知字段警告(不阻断) ===
unknown = set(frontmatter.keys()) - KNOWN_PROPERTIES
if unknown:

View File

@@ -3,7 +3,7 @@ name: update-agent
description: >-
安全更新现有智能体的配置、人格、原则、技能与记忆,输出可审阅 diff 并在确认后应用与提交。Use when 用户要求修改 Agent
行为、安装/卸载技能、调整配置、回滚变更或修订规则。
version: 3.0.3
version: 3.0.4
type: meta
risk_level: low
status: enabled
@@ -35,7 +35,7 @@ metadata:
description: >-
Safely update an existing Agent's config, persona, principles, skills, and memory, producing reviewable diffs that are applied and committed only after confirmation. Use when the user asks to modify Agent behavior, install/uninstall skills, adjust config, roll back changes, or revise rules.
body: ./SKILL.md
source_hash: sha256:a0fecd84f92204bd
source_hash: sha256:a58c60b086945340
translated_by: human
translated_at: '2026-05-03'
market:

View File

@@ -5,7 +5,7 @@ description: >-
catalog. Use when the user asks the Agent to call an API, search for an
existing service, or query a backend that was registered via the
registering-services skill. 调用某个 API、查找已有服务、访问已注册的后端服务时使用。
version: 1.0.1
version: 1.0.2
type: meta
risk_level: low
status: enabled
@@ -39,6 +39,7 @@ metadata:
Discover and invoke HTTP/MCP services already registered in the global catalog. Use when the user asks the Agent to call an API, search for an existing service, or query a backend that was registered via the registering-services skill.
body: ./SKILL.md
translated_by: human
source_hash: sha256:8a20b2835e5e54d1
market:
icon: >-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#34C759" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="9" width="6" height="6" rx="1.5"/><rect x="15" y="3" width="6" height="6" rx="1.5"/><rect x="15" y="15" width="6" height="6" rx="1.5"/><path d="M9 11.5h3.5V6H15"/><path d="M9 12.5h3.5V18H15"/></svg>

View File

@@ -14,7 +14,7 @@ description: >-
新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、
推特、X、知乎、公众号、已登录、登录状态。
license: Complete terms in LICENSE.txt
version: 2.0.1
version: 2.0.2
type: procedural
risk_level: low
status: enabled
@@ -61,7 +61,7 @@ metadata:
short_desc: Web search, page fetching, logged-in browser access via CDP, research workflows
description: A three-layer web-access toolkit — search public pages, fetch heavy pages via Jina Reader, and reach logged-in sites via Chrome CDP.
body: ./SKILL.md
source_hash: sha256:1d044824f5ab31bc
source_hash: sha256:4cda9ca594fbd974
translated_by: human
market:
icon: >-

View File

@@ -4,7 +4,7 @@ description: >-
引导 Agent 设计、编辑、测试和执行 Workflow 工作流。Use when
用户要求创建工作流、编排多步骤自动化流程、设计审批流水线、
或将重复性多节点任务编排成可复用的 DSL。
version: 1.0.6
version: 1.0.7
type: procedural
risk_level: low
status: enabled
@@ -37,7 +37,7 @@ metadata:
description: >-
Guides the Agent to design, edit, test, and execute Workflow workflows. Use when the user asks to create a workflow, orchestrate multi-step automation, design an approval pipeline, or turn repetitive multi-node tasks into a reusable DSL.
body: ./SKILL.md
source_hash: sha256:aa197c62ae8a33d7
source_hash: sha256:ef8c32dbcc87bc32
translated_by: human
translated_at: '2026-05-04'
market:

View File

@@ -8,7 +8,7 @@ description: >-
Use when 用户提到 语音合成、文字转语音、TTS、朗读、读出来、生成语音、
生成音频、文本转音频、配音、念出来、小米语音、MiMo 语音、小米 TTS。
license: Complete terms in LICENSE.txt
version: 1.0.1
version: 1.0.2
type: procedural
risk_level: low
status: enabled
@@ -46,7 +46,7 @@ metadata:
short_desc: Text-to-speech synthesis using Xiaomi MiMo models
description: "Use this skill when the user wants to convert text to speech using Xiaomi MiMo's TTS models (mimo-v2.5-tts). Built on the OpenAI-compatible chat/completions API with audio response, supporting multiple preset voices and custom voice design. Trigger keywords: text-to-speech, TTS, read aloud, narrate, generate audio, voice synthesis, MiMo voice, Xiaomi TTS."
body: ./SKILL.md
source_hash: sha256:afa1138c9b2cbd20
source_hash: sha256:aadc404fd75e8c1a
translated_by: human
market:
icon: >-

View File

@@ -15,7 +15,7 @@ description: >-
report, standalone Python script, database pipeline, or Google Sheets API
integration, even if tabular data is involved. Use when 用户提到 Excel、
电子表格、xlsx、表格处理、公式计算、数据清洗、图表、CSV导入导出。
version: 1.0.2
version: 1.0.3
type: procedural
risk_level: low
status: enabled
@@ -48,7 +48,7 @@ metadata:
description: >-
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. Use when the user mentions Excel, spreadsheets, xlsx, table processing, formula computation, data cleaning, charts, or CSV import/export.
body: ./SKILL.md
source_hash: sha256:17c76a78ed03d451
source_hash: sha256:e399420dae6ea7b4
translated_by: human
market:
icon: >-