@openschool_01/skills 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/openschool-skills.js +7 -0
- package/bundled-skills/wechat-assistant/package.json +12 -0
- package/bundled-skills/wechat-assistant/skill/OPENSCHOOL-META.md +19 -0
- package/bundled-skills/wechat-assistant/skill/SKILL.md +109 -0
- package/bundled-skills/wechat-assistant/skill/assets/wechat-article-template.html +26 -0
- package/bundled-skills/wechat-assistant/skill/config/wechat-style-config.json +7 -0
- package/bundled-skills/wechat-assistant/skill/references/title-guidelines.md +48 -0
- package/bundled-skills/wechat-assistant/skill/references/writing-rules.md +40 -0
- package/bundled-skills/wechat-assistant/skill/scripts/audit_article_quality.py +160 -0
- package/bundled-skills/wechat-assistant/skill/scripts/render_wechat_html.py +171 -0
- package/bundled-skills/xiaohongshu-assistant/package.json +12 -0
- package/bundled-skills/xiaohongshu-assistant/skill/OPENSCHOOL-META.md +14 -0
- package/bundled-skills/xiaohongshu-assistant/skill/SKILL.md +100 -0
- package/bundled-skills/xiaohongshu-assistant/skill/references/title-guidelines.md +36 -0
- package/bundled-skills/xiaohongshu-assistant/skill/references/writing-rules.md +33 -0
- package/package.json +2 -1
- package/registry/skills-registry.json +293 -45
package/bin/openschool-skills.js
CHANGED
|
@@ -11,6 +11,7 @@ const tar = require("tar");
|
|
|
11
11
|
const CLI_ROOT = path.resolve(__dirname, "..");
|
|
12
12
|
const REPO_ROOT = path.resolve(CLI_ROOT, "..", "..");
|
|
13
13
|
const BUNDLED_REGISTRY_PATH = path.join(CLI_ROOT, "registry", "skills-registry.json");
|
|
14
|
+
const BUNDLED_SKILLS_ROOT = path.join(CLI_ROOT, "bundled-skills");
|
|
14
15
|
const WORKSPACE_REGISTRY_PATH = path.join(REPO_ROOT, "registry", "skills-registry.json");
|
|
15
16
|
const NPM_EXECUTABLE = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
16
17
|
|
|
@@ -116,6 +117,12 @@ function writeSourceMeta(targetDir, meta) {
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
function getLocalSkillDirectory(entry) {
|
|
120
|
+
const bundledSkillDir = path.join(BUNDLED_SKILLS_ROOT, entry.slug, "skill");
|
|
121
|
+
|
|
122
|
+
if (fs.existsSync(bundledSkillDir)) {
|
|
123
|
+
return bundledSkillDir;
|
|
124
|
+
}
|
|
125
|
+
|
|
119
126
|
if (!entry.localPackageDir) {
|
|
120
127
|
return null;
|
|
121
128
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# 公众号助手
|
|
2
|
+
|
|
3
|
+
适用于把一个选题整理成可发布到微信公众号的完整内容稿。
|
|
4
|
+
|
|
5
|
+
当前版本专注 4 件事:
|
|
6
|
+
|
|
7
|
+
1. 明确选题和读者视角
|
|
8
|
+
2. 生成更像公众号风格的标题方向
|
|
9
|
+
3. 写出 Markdown 正文
|
|
10
|
+
4. 渲染成微信公众号可用的 HTML
|
|
11
|
+
|
|
12
|
+
这不是一个“全自动发布器”。当前版本不负责:
|
|
13
|
+
|
|
14
|
+
- 生图
|
|
15
|
+
- 封面生成
|
|
16
|
+
- 微信草稿箱发布
|
|
17
|
+
|
|
18
|
+
## 什么时候用
|
|
19
|
+
|
|
20
|
+
当用户要做这些事时使用:
|
|
21
|
+
|
|
22
|
+
- 写公众号文章
|
|
23
|
+
- 把直播内容整理成公众号稿
|
|
24
|
+
- 生成公众号标题
|
|
25
|
+
- 输出微信公众号排版版式的 HTML
|
|
26
|
+
- 检查文章是否适合公众号发布
|
|
27
|
+
|
|
28
|
+
## 推荐工作流
|
|
29
|
+
|
|
30
|
+
### 1. 先收集输入
|
|
31
|
+
|
|
32
|
+
至少确认这 4 个信息:
|
|
33
|
+
|
|
34
|
+
- 文章主题
|
|
35
|
+
- 目标读者
|
|
36
|
+
- 读者当前最常见的问题
|
|
37
|
+
- 这篇文章承诺解决什么
|
|
38
|
+
|
|
39
|
+
### 2. 先给标题方向
|
|
40
|
+
|
|
41
|
+
优先输出 3 到 5 个标题,并解释它们分别适合:
|
|
42
|
+
|
|
43
|
+
- 偏方法拆解
|
|
44
|
+
- 偏观点判断
|
|
45
|
+
- 偏案例复盘
|
|
46
|
+
|
|
47
|
+
标题规则见:
|
|
48
|
+
|
|
49
|
+
- `references/title-guidelines.md`
|
|
50
|
+
|
|
51
|
+
### 3. 再写正文
|
|
52
|
+
|
|
53
|
+
正文默认要求:
|
|
54
|
+
|
|
55
|
+
- 使用 4 到 6 个二级标题
|
|
56
|
+
- 先给判断,再展开解释
|
|
57
|
+
- 至少包含 1 个真实场景
|
|
58
|
+
- 至少包含 1 个常见误区
|
|
59
|
+
- 至少包含 1 组可执行动作
|
|
60
|
+
- 结尾自然引向下一篇或下一步动作
|
|
61
|
+
|
|
62
|
+
写作约束见:
|
|
63
|
+
|
|
64
|
+
- `references/writing-rules.md`
|
|
65
|
+
|
|
66
|
+
### 4. 渲染 HTML
|
|
67
|
+
|
|
68
|
+
正文 Markdown 完成后,运行:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python scripts/render_wechat_html.py --input "<article.md>" --output "<article>.html"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 5. 做一次质量检查
|
|
75
|
+
|
|
76
|
+
如果用户在意发布质量,再运行:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
python scripts/audit_article_quality.py --input "<article.md>"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 目录说明
|
|
83
|
+
|
|
84
|
+
- `references/title-guidelines.md`
|
|
85
|
+
标题风格和筛选规则
|
|
86
|
+
- `references/writing-rules.md`
|
|
87
|
+
正文写作规则和结构要求
|
|
88
|
+
- `assets/wechat-article-template.html`
|
|
89
|
+
微信 HTML 模板
|
|
90
|
+
- `config/wechat-style-config.json`
|
|
91
|
+
样式配置
|
|
92
|
+
- `scripts/render_wechat_html.py`
|
|
93
|
+
Markdown 转微信 HTML
|
|
94
|
+
- `scripts/audit_article_quality.py`
|
|
95
|
+
质量检查脚本
|
|
96
|
+
|
|
97
|
+
## 交付要求
|
|
98
|
+
|
|
99
|
+
默认交付:
|
|
100
|
+
|
|
101
|
+
- 3 到 5 个标题备选
|
|
102
|
+
- 1 篇 Markdown 正文
|
|
103
|
+
- 1 个 HTML 文件
|
|
104
|
+
|
|
105
|
+
如果脚本已经执行,还要明确告诉用户:
|
|
106
|
+
|
|
107
|
+
- Markdown 文件路径
|
|
108
|
+
- HTML 文件路径
|
|
109
|
+
- 质量检查结果
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{TITLE}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
{{STYLE}}
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<main class="wx-wrap">
|
|
13
|
+
<article class="wx-article">
|
|
14
|
+
<header class="wx-header">
|
|
15
|
+
<h1 class="wx-title">{{TITLE}}</h1>
|
|
16
|
+
{{SUBTITLE_BLOCK}}
|
|
17
|
+
</header>
|
|
18
|
+
<section class="wx-content">
|
|
19
|
+
{{CONTENT}}
|
|
20
|
+
</section>
|
|
21
|
+
{{CTA_BLOCK}}
|
|
22
|
+
{{FOOTER_BLOCK}}
|
|
23
|
+
</article>
|
|
24
|
+
</main>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 标题规则
|
|
2
|
+
|
|
3
|
+
公众号标题优先追求“清楚、可信、能打开”,不是追求夸张。
|
|
4
|
+
|
|
5
|
+
## 默认输出 3 类标题
|
|
6
|
+
|
|
7
|
+
### 1. 观点判断型
|
|
8
|
+
|
|
9
|
+
适合表达一个明确判断。
|
|
10
|
+
|
|
11
|
+
示例结构:
|
|
12
|
+
|
|
13
|
+
- 为什么我现在更建议先做 X,再做 Y
|
|
14
|
+
- 真正拉开差距的,不是 X,而是 Y
|
|
15
|
+
|
|
16
|
+
### 2. 方法拆解型
|
|
17
|
+
|
|
18
|
+
适合教程、流程、经验总结。
|
|
19
|
+
|
|
20
|
+
示例结构:
|
|
21
|
+
|
|
22
|
+
- 我现在做公众号内容,基本只用这 4 步
|
|
23
|
+
- 从选题到成稿,我现在固定这样写公众号
|
|
24
|
+
|
|
25
|
+
### 3. 复盘案例型
|
|
26
|
+
|
|
27
|
+
适合直播复盘、项目拆解、过程型内容。
|
|
28
|
+
|
|
29
|
+
示例结构:
|
|
30
|
+
|
|
31
|
+
- 这场直播做完后,我把整个流程又重搭了一遍
|
|
32
|
+
- 我复盘了今天的内容链路,发现问题不在写作本身
|
|
33
|
+
|
|
34
|
+
## 标题筛选规则
|
|
35
|
+
|
|
36
|
+
- 不要震惊体
|
|
37
|
+
- 不要故意制造焦虑
|
|
38
|
+
- 不要只有概念没有对象
|
|
39
|
+
- 要让读者一眼知道这篇内容大概讲什么
|
|
40
|
+
- 最好能带一个具体场景、对象或判断
|
|
41
|
+
|
|
42
|
+
## 输出建议
|
|
43
|
+
|
|
44
|
+
默认输出 3 到 5 个标题,并补一句:
|
|
45
|
+
|
|
46
|
+
- 哪个更适合当前主题
|
|
47
|
+
- 哪个更适合偏增长
|
|
48
|
+
- 哪个更适合偏专业感
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 写作规则
|
|
2
|
+
|
|
3
|
+
## 默认结构
|
|
4
|
+
|
|
5
|
+
1. 开头先给判断
|
|
6
|
+
2. 解释为什么今天值得讲这件事
|
|
7
|
+
3. 讲清楚这件事到底是什么
|
|
8
|
+
4. 讲清楚为什么大家容易做错
|
|
9
|
+
5. 给出一套能直接执行的方法
|
|
10
|
+
6. 用边界和下一步收尾
|
|
11
|
+
|
|
12
|
+
## 文字要求
|
|
13
|
+
|
|
14
|
+
- 用连续叙述,不写成培训提纲
|
|
15
|
+
- 默认 1600 到 2800 字
|
|
16
|
+
- 使用 4 到 6 个 `##` 二级标题
|
|
17
|
+
- 每节至少两段自然段
|
|
18
|
+
- 列表尽量少,只在确实需要步骤时使用
|
|
19
|
+
|
|
20
|
+
## 内容要求
|
|
21
|
+
|
|
22
|
+
- 至少 1 个真实场景
|
|
23
|
+
- 至少 1 个常见误区
|
|
24
|
+
- 至少 1 组可执行动作
|
|
25
|
+
- 至少 1 个明确判断
|
|
26
|
+
- 结尾不能只剩 CTA
|
|
27
|
+
|
|
28
|
+
## 风格要求
|
|
29
|
+
|
|
30
|
+
- 专业,但不要端着
|
|
31
|
+
- 有判断,但不要喊口号
|
|
32
|
+
- 可以有第一人称经验
|
|
33
|
+
- 不要低质口癖
|
|
34
|
+
- 不要泛泛而谈“提效、闭环、赋能”
|
|
35
|
+
|
|
36
|
+
## 输出约束
|
|
37
|
+
|
|
38
|
+
- 正文用 Markdown
|
|
39
|
+
- 重要判断句用 `**加粗**`
|
|
40
|
+
- 如果有命令、配置、提示词,放进 fenced code block
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Heuristic quality gate for WeChat article markdown."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
BAD_SLANG = [
|
|
13
|
+
"家人们",
|
|
14
|
+
"绝绝子",
|
|
15
|
+
"冲就完了",
|
|
16
|
+
"yyds",
|
|
17
|
+
"哈哈哈",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
TRANSITIONS = [
|
|
21
|
+
"先说结论",
|
|
22
|
+
"更关键的是",
|
|
23
|
+
"换句话说",
|
|
24
|
+
"问题在于",
|
|
25
|
+
"所以",
|
|
26
|
+
"接下来",
|
|
27
|
+
"最后",
|
|
28
|
+
"这意味着",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
SCENE_MARKERS = [
|
|
32
|
+
"比如",
|
|
33
|
+
"例如",
|
|
34
|
+
"我自己",
|
|
35
|
+
"我实测",
|
|
36
|
+
"这周",
|
|
37
|
+
"上周",
|
|
38
|
+
"项目里",
|
|
39
|
+
"直播间",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
ACTION_MARKERS = [
|
|
43
|
+
"可以先",
|
|
44
|
+
"建议你",
|
|
45
|
+
"下一步",
|
|
46
|
+
"按这三步",
|
|
47
|
+
"立刻",
|
|
48
|
+
"先做",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def normalize(markdown: str) -> str:
|
|
53
|
+
markdown = re.sub(r"```[\s\S]*?```", "", markdown)
|
|
54
|
+
markdown = re.sub(r"!\[[^\]]*]\([^)]+\)", "", markdown)
|
|
55
|
+
return markdown
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def split_paragraphs(markdown: str) -> list[str]:
|
|
59
|
+
chunks = re.split(r"\n\s*\n", markdown)
|
|
60
|
+
paragraphs = []
|
|
61
|
+
for chunk in chunks:
|
|
62
|
+
text = chunk.strip()
|
|
63
|
+
if not text:
|
|
64
|
+
continue
|
|
65
|
+
if text.startswith("#") or text.startswith(">") or text == "---":
|
|
66
|
+
continue
|
|
67
|
+
if re.match(r"^\s*(?:[-*]|\d+\.)\s+", text):
|
|
68
|
+
continue
|
|
69
|
+
paragraphs.append(text)
|
|
70
|
+
return paragraphs
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def count_chinese_chars(text: str) -> int:
|
|
74
|
+
return len(re.findall(r"[\u4e00-\u9fff]", text))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def build_report(markdown: str) -> dict:
|
|
78
|
+
text = normalize(markdown)
|
|
79
|
+
lines = text.splitlines()
|
|
80
|
+
non_empty = [line for line in lines if line.strip()]
|
|
81
|
+
list_lines = [line for line in lines if re.match(r"^\s*(?:[-*]|\d+\.)\s+", line)]
|
|
82
|
+
h2_headings = [line for line in lines if re.match(r"^\s*##\s+", line)]
|
|
83
|
+
paragraphs = split_paragraphs(text)
|
|
84
|
+
|
|
85
|
+
zh_chars = count_chinese_chars(text)
|
|
86
|
+
list_ratio = (len(list_lines) / len(non_empty)) if non_empty else 0
|
|
87
|
+
short_paragraphs = [paragraph for paragraph in paragraphs if count_chinese_chars(re.sub(r"\s+", "", paragraph)) < 60]
|
|
88
|
+
|
|
89
|
+
transition_hits = sum(text.count(item) for item in TRANSITIONS)
|
|
90
|
+
scene_hits = sum(text.count(item) for item in SCENE_MARKERS)
|
|
91
|
+
action_hits = sum(text.count(item) for item in ACTION_MARKERS)
|
|
92
|
+
slang_hits = [item for item in BAD_SLANG if item in text]
|
|
93
|
+
|
|
94
|
+
has_what = ("是什么" in text) or ("这件事是" in text) or ("你可以把它理解成" in text)
|
|
95
|
+
has_why = ("为什么" in text) or ("原因" in text) or ("问题在于" in text)
|
|
96
|
+
has_how = ("怎么做" in text) or ("可以先" in text) or ("建议你" in text) or ("下一步" in text)
|
|
97
|
+
has_next = ("下一篇" in text) or ("下篇" in text) or ("下一步" in text)
|
|
98
|
+
punctuation_burst = bool(re.search(r"[!?!?]{3,}", text))
|
|
99
|
+
|
|
100
|
+
suggestions: list[str] = []
|
|
101
|
+
if zh_chars < 1200:
|
|
102
|
+
suggestions.append("正文偏短,建议补到至少 1200 个中文字符。")
|
|
103
|
+
if list_ratio > 0.2:
|
|
104
|
+
suggestions.append("列表密度偏高,建议改写成更多自然段。")
|
|
105
|
+
if len(h2_headings) < 4:
|
|
106
|
+
suggestions.append("结构层级偏少,建议补足 4 到 6 个二级标题。")
|
|
107
|
+
if transition_hits < 4:
|
|
108
|
+
suggestions.append("章节承接偏弱,可以增加“先说结论、问题在于、换句话说”这类过渡句。")
|
|
109
|
+
if scene_hits < 1:
|
|
110
|
+
suggestions.append("缺少真实场景,建议补一个直播、项目或实测片段。")
|
|
111
|
+
if action_hits < 1:
|
|
112
|
+
suggestions.append("缺少可执行动作,建议补一组能直接照做的步骤。")
|
|
113
|
+
if slang_hits:
|
|
114
|
+
suggestions.append(f"存在口语化过强表达:{', '.join(slang_hits)}。")
|
|
115
|
+
if not has_next:
|
|
116
|
+
suggestions.append("结尾缺少下一步引导,建议加一句自然收束。")
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
"metrics": {
|
|
120
|
+
"zh_chars": zh_chars,
|
|
121
|
+
"h2_count": len(h2_headings),
|
|
122
|
+
"paragraph_count": len(paragraphs),
|
|
123
|
+
"short_paragraph_count": len(short_paragraphs),
|
|
124
|
+
"list_line_count": len(list_lines),
|
|
125
|
+
"list_ratio": round(list_ratio, 3),
|
|
126
|
+
"transition_hits": transition_hits,
|
|
127
|
+
"scene_hits": scene_hits,
|
|
128
|
+
"action_hits": action_hits,
|
|
129
|
+
"slang_hits": slang_hits,
|
|
130
|
+
"punctuation_burst": punctuation_burst
|
|
131
|
+
},
|
|
132
|
+
"gates": {
|
|
133
|
+
"logic": "PASS" if has_what and has_why and has_how and len(h2_headings) >= 4 else "FAIL",
|
|
134
|
+
"humanity": "PASS" if scene_hits >= 1 and action_hits >= 1 else "FAIL",
|
|
135
|
+
"language": "PASS" if not slang_hits and not punctuation_burst else "FAIL",
|
|
136
|
+
"publish_ready": "PASS" if has_next and zh_chars >= 1200 and list_ratio <= 0.2 else "FAIL"
|
|
137
|
+
},
|
|
138
|
+
"suggestions": suggestions
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def main() -> None:
|
|
143
|
+
parser = argparse.ArgumentParser()
|
|
144
|
+
parser.add_argument("--input", required=True)
|
|
145
|
+
parser.add_argument("--output", default="")
|
|
146
|
+
args = parser.parse_args()
|
|
147
|
+
|
|
148
|
+
markdown = Path(args.input).read_text(encoding="utf-8")
|
|
149
|
+
report = build_report(markdown)
|
|
150
|
+
output = json.dumps(report, ensure_ascii=False, indent=2)
|
|
151
|
+
print(output)
|
|
152
|
+
|
|
153
|
+
if args.output:
|
|
154
|
+
output_path = Path(args.output)
|
|
155
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
output_path.write_text(output, encoding="utf-8")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
main()
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render markdown into WeChat-friendly article HTML."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import html
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
DEFAULT_STYLE = """
|
|
14
|
+
body { margin: 0; background: #ffffff; color: #111111; font-family: "PingFang SC", "Microsoft YaHei", sans-serif; }
|
|
15
|
+
.wx-wrap { max-width: 720px; margin: 0 auto; padding: 24px 18px 48px; box-sizing: border-box; }
|
|
16
|
+
.wx-title { margin: 0 0 12px; font-size: 28px; line-height: 1.35; font-weight: 700; color: #111111; }
|
|
17
|
+
.wx-subtitle { margin: 0 0 20px; font-size: 15px; line-height: 1.8; color: #666666; }
|
|
18
|
+
.wx-content p, .wx-content li { margin: 0 0 14px; font-size: 16px; line-height: 1.8; color: #111111; text-align: justify; text-justify: inter-ideograph; word-break: break-word; }
|
|
19
|
+
.wx-content h2, .wx-content h3 { margin: 26px 0 10px; font-size: 20px; line-height: 1.6; font-weight: 700; color: rgb(0,128,255); }
|
|
20
|
+
.wx-content strong { color: rgb(0,128,255); font-weight: 700; }
|
|
21
|
+
.wx-content ul, .wx-content ol { margin: 0 0 14px 1.4em; padding: 0; }
|
|
22
|
+
.wx-content pre { margin: 0 0 18px; padding: 14px 16px; background: #f7f8fa; border-left: 3px solid rgba(0,128,255,.45); overflow-x: auto; border-radius: 6px; }
|
|
23
|
+
.wx-content code { font-size: 14px; line-height: 1.7; color: #333333; font-family: "Consolas", "Menlo", monospace; white-space: pre-wrap; }
|
|
24
|
+
.wx-sep { margin: 12px 0 16px; font-size: 16px; line-height: 1.8; color: #999999; text-align: center; }
|
|
25
|
+
.wx-cta { margin-top: 28px; padding-top: 16px; border-top: 1px solid #ececec; }
|
|
26
|
+
.wx-cta p { margin: 0; font-size: 15px; line-height: 1.8; color: #444444; }
|
|
27
|
+
.wx-footer { margin-top: 44px; padding-top: 18px; border-top: 1px solid #f0f0f0; }
|
|
28
|
+
.wx-footer p { margin: 0; font-size: 13px; line-height: 1.9; color: #a0a0a0; text-align: center; }
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def load_style(config_path: Path | None) -> str:
|
|
33
|
+
if not config_path or not config_path.exists():
|
|
34
|
+
return DEFAULT_STYLE
|
|
35
|
+
|
|
36
|
+
config = json.loads(config_path.read_text(encoding="utf-8"))
|
|
37
|
+
body_font_size = int(config.get("bodyFontSize", 16))
|
|
38
|
+
body_line_height = float(config.get("bodyLineHeight", 1.8))
|
|
39
|
+
content_width = int(config.get("contentWidth", 720))
|
|
40
|
+
accent = str(config.get("accentColor", "rgb(0,128,255)"))
|
|
41
|
+
text = str(config.get("textColor", "#111111"))
|
|
42
|
+
|
|
43
|
+
return DEFAULT_STYLE.replace("720px", f"{content_width}px").replace(
|
|
44
|
+
"rgb(0,128,255)", accent
|
|
45
|
+
).replace("#111111", text).replace("font-size: 16px; line-height: 1.8;", f"font-size: {body_font_size}px; line-height: {body_line_height};")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def convert_inline(text: str) -> str:
|
|
49
|
+
text = re.sub(r"!\[\[[^\]]+\]\]", "", text)
|
|
50
|
+
text = html.escape(text)
|
|
51
|
+
text = re.sub(r"`([^`]+)`", r"<code>\1</code>", text)
|
|
52
|
+
text = re.sub(r"\*\*([^*]+)\*\*", r"<strong>\1</strong>", text)
|
|
53
|
+
text = re.sub(r"\*([^*]+)\*", r"<em>\1</em>", text)
|
|
54
|
+
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<a href="\2">\1</a>', text)
|
|
55
|
+
return text
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def markdown_to_blocks(content: str) -> tuple[str, str, bool]:
|
|
59
|
+
lines = content.splitlines()
|
|
60
|
+
title = ""
|
|
61
|
+
blocks: list[str] = []
|
|
62
|
+
in_code = False
|
|
63
|
+
code_lines: list[str] = []
|
|
64
|
+
paragraph_lines: list[str] = []
|
|
65
|
+
has_cta = False
|
|
66
|
+
|
|
67
|
+
def flush_paragraph() -> None:
|
|
68
|
+
nonlocal paragraph_lines, has_cta
|
|
69
|
+
if not paragraph_lines:
|
|
70
|
+
return
|
|
71
|
+
text = re.sub(r"\s+", " ", " ".join(line.strip() for line in paragraph_lines if line.strip())).strip()
|
|
72
|
+
paragraph_lines = []
|
|
73
|
+
if not text:
|
|
74
|
+
return
|
|
75
|
+
if any(keyword in text for keyword in ["留言", "评论", "下一篇", "下篇", "领取"]):
|
|
76
|
+
has_cta = True
|
|
77
|
+
blocks.append(f"<p>{convert_inline(text)}</p>")
|
|
78
|
+
|
|
79
|
+
for raw_line in lines:
|
|
80
|
+
line = raw_line.rstrip()
|
|
81
|
+
stripped = line.strip()
|
|
82
|
+
|
|
83
|
+
if stripped.startswith("```"):
|
|
84
|
+
flush_paragraph()
|
|
85
|
+
if in_code:
|
|
86
|
+
blocks.append("<pre><code>" + html.escape("\n".join(code_lines)) + "</code></pre>")
|
|
87
|
+
code_lines = []
|
|
88
|
+
in_code = False
|
|
89
|
+
else:
|
|
90
|
+
in_code = True
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
if in_code:
|
|
94
|
+
code_lines.append(line)
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
if not stripped:
|
|
98
|
+
flush_paragraph()
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
if not title and stripped.startswith("# "):
|
|
102
|
+
title = stripped[2:].strip()
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
if stripped.startswith("## "):
|
|
106
|
+
flush_paragraph()
|
|
107
|
+
blocks.append(f"<h2>{convert_inline(stripped[3:])}</h2>")
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
if stripped.startswith("### "):
|
|
111
|
+
flush_paragraph()
|
|
112
|
+
blocks.append(f"<h3>{convert_inline(stripped[4:])}</h3>")
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
if re.match(r"^\d+\.\s+", stripped):
|
|
116
|
+
flush_paragraph()
|
|
117
|
+
blocks.append("<ol><li>" + convert_inline(re.sub(r"^\d+\.\s+", "", stripped)) + "</li></ol>")
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
if re.match(r"^[-*]\s+", stripped):
|
|
121
|
+
flush_paragraph()
|
|
122
|
+
blocks.append("<ul><li>" + convert_inline(re.sub(r"^[-*]\s+", "", stripped)) + "</li></ul>")
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
if stripped == "---":
|
|
126
|
+
flush_paragraph()
|
|
127
|
+
blocks.append('<p class="wx-sep">···</p>')
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
paragraph_lines.append(stripped)
|
|
131
|
+
|
|
132
|
+
flush_paragraph()
|
|
133
|
+
return title, "\n".join(blocks), has_cta
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def render_html(markdown: str, title_override: str, style_config_path: Path | None) -> str:
|
|
137
|
+
title, body, has_cta = markdown_to_blocks(markdown)
|
|
138
|
+
title = title_override or title or "公众号文章"
|
|
139
|
+
template_path = Path(__file__).resolve().parent.parent / "assets" / "wechat-article-template.html"
|
|
140
|
+
template = template_path.read_text(encoding="utf-8")
|
|
141
|
+
cta = "" if has_cta else '<footer class="wx-cta"><p>如果这篇内容对你有帮助,下一步可以继续把同主题素材整理成系列文章。</p></footer>'
|
|
142
|
+
footer = '<footer class="wx-footer"><p>由 OpenSchool 严选技能整理输出</p></footer>'
|
|
143
|
+
subtitle_block = ""
|
|
144
|
+
|
|
145
|
+
output = template.replace("{{TITLE}}", html.escape(title))
|
|
146
|
+
output = output.replace("{{SUBTITLE_BLOCK}}", subtitle_block)
|
|
147
|
+
output = output.replace("{{STYLE}}", load_style(style_config_path).strip())
|
|
148
|
+
output = output.replace("{{CONTENT}}", body)
|
|
149
|
+
output = output.replace("{{CTA_BLOCK}}", cta)
|
|
150
|
+
output = output.replace("{{FOOTER_BLOCK}}", footer)
|
|
151
|
+
return output
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def main() -> None:
|
|
155
|
+
parser = argparse.ArgumentParser()
|
|
156
|
+
parser.add_argument("--input", required=True)
|
|
157
|
+
parser.add_argument("--output", required=True)
|
|
158
|
+
parser.add_argument("--title", default="")
|
|
159
|
+
parser.add_argument("--style-config", default="")
|
|
160
|
+
args = parser.parse_args()
|
|
161
|
+
|
|
162
|
+
style_config_path = Path(args.style_config) if args.style_config else Path(__file__).resolve().parent.parent / "config" / "wechat-style-config.json"
|
|
163
|
+
markdown = Path(args.input).read_text(encoding="utf-8")
|
|
164
|
+
output = render_html(markdown, args.title, style_config_path)
|
|
165
|
+
output_path = Path(args.output)
|
|
166
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
167
|
+
output_path.write_text(output, encoding="utf-8")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
main()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# 小红书助手
|
|
2
|
+
|
|
3
|
+
适用于把一个主题、直播片段、公众号原稿或观点素材,整理成更适合小红书发布的内容。
|
|
4
|
+
|
|
5
|
+
当前版本专注 4 件事:
|
|
6
|
+
|
|
7
|
+
1. 明确小红书切角
|
|
8
|
+
2. 生成更像平台风格的标题方向
|
|
9
|
+
3. 输出可直接发布的正文
|
|
10
|
+
4. 补齐标签、封面文案和评论区引导
|
|
11
|
+
|
|
12
|
+
当前版本不负责:
|
|
13
|
+
|
|
14
|
+
- 生图
|
|
15
|
+
- 封面出图
|
|
16
|
+
- 自动发布
|
|
17
|
+
|
|
18
|
+
## 什么时候用
|
|
19
|
+
|
|
20
|
+
当用户要做这些事时使用:
|
|
21
|
+
|
|
22
|
+
- 把公众号内容改成小红书
|
|
23
|
+
- 写小红书标题
|
|
24
|
+
- 生成小红书正文
|
|
25
|
+
- 做一版更适合平台传播的内容重写
|
|
26
|
+
|
|
27
|
+
## 推荐工作流
|
|
28
|
+
|
|
29
|
+
### 1. 先收集输入
|
|
30
|
+
|
|
31
|
+
至少确认:
|
|
32
|
+
|
|
33
|
+
- 原始主题或素材
|
|
34
|
+
- 目标读者
|
|
35
|
+
- 这次最想强调的结论
|
|
36
|
+
- 希望走哪种内容逻辑
|
|
37
|
+
|
|
38
|
+
### 2. 先定传播逻辑
|
|
39
|
+
|
|
40
|
+
默认优先在这几类里选一个主逻辑:
|
|
41
|
+
|
|
42
|
+
- 误区避坑
|
|
43
|
+
- 保姆级步骤
|
|
44
|
+
- 强观点判断
|
|
45
|
+
- 案例复盘
|
|
46
|
+
- 清单速看
|
|
47
|
+
|
|
48
|
+
一次只保留一个主逻辑,不要把一篇小红书写成什么都想讲的说明文。
|
|
49
|
+
|
|
50
|
+
### 3. 再出标题
|
|
51
|
+
|
|
52
|
+
默认输出 5 个标题备选,并覆盖:
|
|
53
|
+
|
|
54
|
+
- 警告拦截型
|
|
55
|
+
- 数字步骤型
|
|
56
|
+
- 观点判断型
|
|
57
|
+
|
|
58
|
+
标题规则见:
|
|
59
|
+
|
|
60
|
+
- `references/title-guidelines.md`
|
|
61
|
+
|
|
62
|
+
### 4. 再写正文
|
|
63
|
+
|
|
64
|
+
正文默认要求:
|
|
65
|
+
|
|
66
|
+
- 开头前两句就给钩子和结论
|
|
67
|
+
- 用短段落,控制阅读压力
|
|
68
|
+
- 优先写误区、建议、步骤和判断
|
|
69
|
+
- 结尾补评论区互动问题
|
|
70
|
+
|
|
71
|
+
写作规则见:
|
|
72
|
+
|
|
73
|
+
- `references/writing-rules.md`
|
|
74
|
+
|
|
75
|
+
### 5. 最终补齐发布要素
|
|
76
|
+
|
|
77
|
+
默认交付:
|
|
78
|
+
|
|
79
|
+
- 5 个标题备选
|
|
80
|
+
- 1 篇可直接发布的正文
|
|
81
|
+
- 3 条封面文案
|
|
82
|
+
- 8 到 12 个标签
|
|
83
|
+
- 3 条评论区引导
|
|
84
|
+
|
|
85
|
+
## 目录说明
|
|
86
|
+
|
|
87
|
+
- `references/title-guidelines.md`
|
|
88
|
+
标题方向规则
|
|
89
|
+
- `references/writing-rules.md`
|
|
90
|
+
正文改写规则
|
|
91
|
+
|
|
92
|
+
## 交付要求
|
|
93
|
+
|
|
94
|
+
如果没有特别说明,默认用中文直接输出:
|
|
95
|
+
|
|
96
|
+
- 标题备选
|
|
97
|
+
- 正文
|
|
98
|
+
- 封面文案
|
|
99
|
+
- 标签建议
|
|
100
|
+
- 评论区引导
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# 标题规则
|
|
2
|
+
|
|
3
|
+
小红书标题优先追求“停下来、看下去、愿意点开”,不是写成说明书标题。
|
|
4
|
+
|
|
5
|
+
## 默认输出 5 个标题
|
|
6
|
+
|
|
7
|
+
至少覆盖这三类:
|
|
8
|
+
|
|
9
|
+
### 1. 警告拦截型
|
|
10
|
+
|
|
11
|
+
示例:
|
|
12
|
+
|
|
13
|
+
- 别一上来就这样做
|
|
14
|
+
- 新手最容易踩坑的,其实是这一步
|
|
15
|
+
|
|
16
|
+
### 2. 数字步骤型
|
|
17
|
+
|
|
18
|
+
示例:
|
|
19
|
+
|
|
20
|
+
- 我现在只用这 3 步写小红书
|
|
21
|
+
- 1 分钟看懂这件事到底怎么做
|
|
22
|
+
|
|
23
|
+
### 3. 观点判断型
|
|
24
|
+
|
|
25
|
+
示例:
|
|
26
|
+
|
|
27
|
+
- 真正拉开差距的,不是会不会写,而是会不会切角
|
|
28
|
+
- 很多人不是不会做内容,是一开始就写偏了
|
|
29
|
+
|
|
30
|
+
## 标题筛选规则
|
|
31
|
+
|
|
32
|
+
- 优先 14 到 20 字
|
|
33
|
+
- 一眼能看懂在讲什么
|
|
34
|
+
- 不要空泛“干货、分享、教程”
|
|
35
|
+
- 可以有轻度情绪,但不要过度夸张
|
|
36
|
+
- 优先带对象、场景、结果或判断
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 写作规则
|
|
2
|
+
|
|
3
|
+
## 默认结构
|
|
4
|
+
|
|
5
|
+
1. 开头两句完成钩子和结论
|
|
6
|
+
2. 中间用 3 到 5 个短段落展开
|
|
7
|
+
3. 优先写误区、建议、步骤、判断
|
|
8
|
+
4. 结尾用一句总结加一个互动问题收尾
|
|
9
|
+
|
|
10
|
+
## 内容要求
|
|
11
|
+
|
|
12
|
+
- 正文尽量控制在 280 到 520 字
|
|
13
|
+
- 复杂主题也尽量不要超过 1000 字
|
|
14
|
+
- 每段 1 到 3 行
|
|
15
|
+
- 一次只保留一个主逻辑
|
|
16
|
+
- 至少有一个可直接执行动作
|
|
17
|
+
|
|
18
|
+
## 风格要求
|
|
19
|
+
|
|
20
|
+
- 更像真实用户分享
|
|
21
|
+
- 少用说明文腔调
|
|
22
|
+
- 少铺背景,多给结论
|
|
23
|
+
- 少讲大道理,多给具体判断
|
|
24
|
+
|
|
25
|
+
## 输出要求
|
|
26
|
+
|
|
27
|
+
默认输出:
|
|
28
|
+
|
|
29
|
+
- 5 个标题
|
|
30
|
+
- 1 版正文
|
|
31
|
+
- 3 条封面文案
|
|
32
|
+
- 8 到 12 个标签
|
|
33
|
+
- 3 条评论区互动引导
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openschool_01/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "OpenSchool curated skills installer CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
11
|
+
"bundled-skills",
|
|
11
12
|
"registry",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
@@ -10,10 +10,22 @@
|
|
|
10
10
|
"installCommand": "npx @openschool_01/skills install clawdhub",
|
|
11
11
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
12
12
|
"summary": "适合作为新手接触技能生态的第一步,把技能的搜索、安装和更新入口先统一起来。",
|
|
13
|
-
"outcomes": [
|
|
14
|
-
|
|
13
|
+
"outcomes": [
|
|
14
|
+
"搜索技能",
|
|
15
|
+
"安装技能",
|
|
16
|
+
"更新技能",
|
|
17
|
+
"发布技能"
|
|
18
|
+
],
|
|
19
|
+
"fitFor": [
|
|
20
|
+
"第一次装技能",
|
|
21
|
+
"搭建技能环境",
|
|
22
|
+
"技能管理入门"
|
|
23
|
+
],
|
|
15
24
|
"updatedAt": "2026-03-29",
|
|
16
|
-
"prerequisites": [
|
|
25
|
+
"prerequisites": [
|
|
26
|
+
"需联网安装",
|
|
27
|
+
"本地需要可用的 npm 环境"
|
|
28
|
+
],
|
|
17
29
|
"sourceType": "clawhub",
|
|
18
30
|
"clawhubSlug": "clawdhub",
|
|
19
31
|
"clawhubUrl": "https://clawhub.ai/steipete/clawdhub"
|
|
@@ -29,10 +41,22 @@
|
|
|
29
41
|
"installCommand": "npx @openschool_01/skills install skill-vetter",
|
|
30
42
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
31
43
|
"summary": "适合新手在安装技能前先做基础安全检查,降低来源不明和权限过高带来的风险。",
|
|
32
|
-
"outcomes": [
|
|
33
|
-
|
|
44
|
+
"outcomes": [
|
|
45
|
+
"来源检查",
|
|
46
|
+
"权限检查",
|
|
47
|
+
"风险识别",
|
|
48
|
+
"安装前审查"
|
|
49
|
+
],
|
|
50
|
+
"fitFor": [
|
|
51
|
+
"第一次装技能",
|
|
52
|
+
"筛选第三方技能",
|
|
53
|
+
"降低安装风险"
|
|
54
|
+
],
|
|
34
55
|
"updatedAt": "2026-03-29",
|
|
35
|
-
"prerequisites": [
|
|
56
|
+
"prerequisites": [
|
|
57
|
+
"需联网安装",
|
|
58
|
+
"本地需要可用的 npm 环境"
|
|
59
|
+
],
|
|
36
60
|
"sourceType": "clawhub",
|
|
37
61
|
"clawhubSlug": "skill-vetter",
|
|
38
62
|
"clawhubUrl": "https://clawhub.ai/spclaudehome/skill-vetter"
|
|
@@ -48,10 +72,22 @@
|
|
|
48
72
|
"installCommand": "npx @openschool_01/skills install automation-workflows",
|
|
49
73
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
50
74
|
"summary": "适合刚开始梳理自动化场景的人,用更清晰的方式把重复工作变成流程。",
|
|
51
|
-
"outcomes": [
|
|
52
|
-
|
|
75
|
+
"outcomes": [
|
|
76
|
+
"发现重复工作",
|
|
77
|
+
"设计工作流",
|
|
78
|
+
"连接自动化平台",
|
|
79
|
+
"流程搭建建议"
|
|
80
|
+
],
|
|
81
|
+
"fitFor": [
|
|
82
|
+
"运营自动化",
|
|
83
|
+
"个人提效",
|
|
84
|
+
"团队 SOP"
|
|
85
|
+
],
|
|
53
86
|
"updatedAt": "2026-03-29",
|
|
54
|
-
"prerequisites": [
|
|
87
|
+
"prerequisites": [
|
|
88
|
+
"需联网安装",
|
|
89
|
+
"本地需要可用的 npm 环境"
|
|
90
|
+
],
|
|
55
91
|
"sourceType": "clawhub",
|
|
56
92
|
"clawhubSlug": "automation-workflows",
|
|
57
93
|
"clawhubUrl": "https://clawhub.ai/jk-0001/automation-workflows"
|
|
@@ -67,10 +103,22 @@
|
|
|
67
103
|
"installCommand": "npx @openschool_01/skills install openclaw-tavily-search",
|
|
68
104
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
69
105
|
"summary": "适合作为联网搜索的备用能力,在常规检索失效时继续兜底。",
|
|
70
|
-
"outcomes": [
|
|
71
|
-
|
|
106
|
+
"outcomes": [
|
|
107
|
+
"联网兜底",
|
|
108
|
+
"持续检索",
|
|
109
|
+
"搜索补充",
|
|
110
|
+
"可用性提升"
|
|
111
|
+
],
|
|
112
|
+
"fitFor": [
|
|
113
|
+
"搜索任务",
|
|
114
|
+
"联网调研",
|
|
115
|
+
"信息收集"
|
|
116
|
+
],
|
|
72
117
|
"updatedAt": "2026-03-29",
|
|
73
|
-
"prerequisites": [
|
|
118
|
+
"prerequisites": [
|
|
119
|
+
"需联网安装",
|
|
120
|
+
"本地需要可用的 npm 环境"
|
|
121
|
+
],
|
|
74
122
|
"sourceType": "clawhub",
|
|
75
123
|
"clawhubSlug": "openclaw-tavily-search",
|
|
76
124
|
"clawhubUrl": "https://clawhub.ai/jacky1n7/openclaw-tavily-search"
|
|
@@ -86,10 +134,22 @@
|
|
|
86
134
|
"installCommand": "npx @openschool_01/skills install multi-search-engine",
|
|
87
135
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
88
136
|
"summary": "适合需要更稳搜索结果的人,通过多引擎对照减少单一来源偏差。",
|
|
89
|
-
"outcomes": [
|
|
90
|
-
|
|
137
|
+
"outcomes": [
|
|
138
|
+
"多源检索",
|
|
139
|
+
"结果对比",
|
|
140
|
+
"信息校验",
|
|
141
|
+
"可靠性提升"
|
|
142
|
+
],
|
|
143
|
+
"fitFor": [
|
|
144
|
+
"信息查证",
|
|
145
|
+
"资料搜集",
|
|
146
|
+
"多源调研"
|
|
147
|
+
],
|
|
91
148
|
"updatedAt": "2026-03-29",
|
|
92
|
-
"prerequisites": [
|
|
149
|
+
"prerequisites": [
|
|
150
|
+
"需联网安装",
|
|
151
|
+
"本地需要可用的 npm 环境"
|
|
152
|
+
],
|
|
93
153
|
"sourceType": "clawhub",
|
|
94
154
|
"clawhubSlug": "multi-search-engine",
|
|
95
155
|
"clawhubUrl": "https://clawhub.ai/gpyangyoujun/multi-search-engine"
|
|
@@ -105,10 +165,22 @@
|
|
|
105
165
|
"installCommand": "npx @openschool_01/skills install self-improving",
|
|
106
166
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
107
167
|
"summary": "适合作为长期使用中的自我优化工具,把一次次踩坑变成后续少走弯路的经验。",
|
|
108
|
-
"outcomes": [
|
|
109
|
-
|
|
168
|
+
"outcomes": [
|
|
169
|
+
"错误复盘",
|
|
170
|
+
"经验沉淀",
|
|
171
|
+
"持续优化",
|
|
172
|
+
"方法复用"
|
|
173
|
+
],
|
|
174
|
+
"fitFor": [
|
|
175
|
+
"长期迭代",
|
|
176
|
+
"个人方法库",
|
|
177
|
+
"流程改进"
|
|
178
|
+
],
|
|
110
179
|
"updatedAt": "2026-03-29",
|
|
111
|
-
"prerequisites": [
|
|
180
|
+
"prerequisites": [
|
|
181
|
+
"需联网安装",
|
|
182
|
+
"本地需要可用的 npm 环境"
|
|
183
|
+
],
|
|
112
184
|
"sourceType": "clawhub",
|
|
113
185
|
"clawhubSlug": "self-improving",
|
|
114
186
|
"clawhubUrl": "https://clawhub.ai/ivangdavila/self-improving"
|
|
@@ -124,10 +196,22 @@
|
|
|
124
196
|
"installCommand": "npx @openschool_01/skills install obsidian",
|
|
125
197
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
126
198
|
"summary": "适合想把零散知识长期积累下来的人,让知识管理和工作过程更连贯。",
|
|
127
|
-
"outcomes": [
|
|
128
|
-
|
|
199
|
+
"outcomes": [
|
|
200
|
+
"笔记协作",
|
|
201
|
+
"知识沉淀",
|
|
202
|
+
"长期积累",
|
|
203
|
+
"资产管理"
|
|
204
|
+
],
|
|
205
|
+
"fitFor": [
|
|
206
|
+
"知识管理",
|
|
207
|
+
"长期项目",
|
|
208
|
+
"笔记工作流"
|
|
209
|
+
],
|
|
129
210
|
"updatedAt": "2026-03-29",
|
|
130
|
-
"prerequisites": [
|
|
211
|
+
"prerequisites": [
|
|
212
|
+
"需联网安装",
|
|
213
|
+
"本地需要可用的 npm 环境"
|
|
214
|
+
],
|
|
131
215
|
"sourceType": "clawhub",
|
|
132
216
|
"clawhubSlug": "obsidian",
|
|
133
217
|
"clawhubUrl": "https://clawhub.ai/steipete/obsidian"
|
|
@@ -143,10 +227,22 @@
|
|
|
143
227
|
"installCommand": "npx @openschool_01/skills install ontology",
|
|
144
228
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
145
229
|
"summary": "适合希望把信息关系理清楚的人,让知识图谱和任务结构更清晰。",
|
|
146
|
-
"outcomes": [
|
|
147
|
-
|
|
230
|
+
"outcomes": [
|
|
231
|
+
"结构化关系",
|
|
232
|
+
"知识图谱",
|
|
233
|
+
"任务关联",
|
|
234
|
+
"信息整理"
|
|
235
|
+
],
|
|
236
|
+
"fitFor": [
|
|
237
|
+
"复杂项目",
|
|
238
|
+
"知识管理",
|
|
239
|
+
"结构化梳理"
|
|
240
|
+
],
|
|
148
241
|
"updatedAt": "2026-03-29",
|
|
149
|
-
"prerequisites": [
|
|
242
|
+
"prerequisites": [
|
|
243
|
+
"需联网安装",
|
|
244
|
+
"本地需要可用的 npm 环境"
|
|
245
|
+
],
|
|
150
246
|
"sourceType": "clawhub",
|
|
151
247
|
"clawhubSlug": "ontology",
|
|
152
248
|
"clawhubUrl": "https://clawhub.ai/oswalpalash/ontology"
|
|
@@ -162,10 +258,22 @@
|
|
|
162
258
|
"installCommand": "npx @openschool_01/skills install markdown-converter",
|
|
163
259
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
164
260
|
"summary": "适合把各类文件整理成统一 Markdown 工作流的人,方便后续复用和再加工。",
|
|
165
|
-
"outcomes": [
|
|
166
|
-
|
|
261
|
+
"outcomes": [
|
|
262
|
+
"文档转换",
|
|
263
|
+
"统一格式",
|
|
264
|
+
"内容复用",
|
|
265
|
+
"二次加工准备"
|
|
266
|
+
],
|
|
267
|
+
"fitFor": [
|
|
268
|
+
"文档处理",
|
|
269
|
+
"知识整理",
|
|
270
|
+
"内容迁移"
|
|
271
|
+
],
|
|
167
272
|
"updatedAt": "2026-03-29",
|
|
168
|
-
"prerequisites": [
|
|
273
|
+
"prerequisites": [
|
|
274
|
+
"需联网安装",
|
|
275
|
+
"本地需要可用的 npm 环境"
|
|
276
|
+
],
|
|
169
277
|
"sourceType": "clawhub",
|
|
170
278
|
"clawhubSlug": "markdown-converter",
|
|
171
279
|
"clawhubUrl": "https://clawhub.ai/steipete/markdown-converter"
|
|
@@ -181,10 +289,22 @@
|
|
|
181
289
|
"installCommand": "npx @openschool_01/skills install summarize",
|
|
182
290
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
183
291
|
"summary": "适合作为高频总结工具,把信息先快速收拢,再继续整理和输出。",
|
|
184
|
-
"outcomes": [
|
|
185
|
-
|
|
292
|
+
"outcomes": [
|
|
293
|
+
"网页总结",
|
|
294
|
+
"视频摘要",
|
|
295
|
+
"PDF 摘要",
|
|
296
|
+
"二次加工准备"
|
|
297
|
+
],
|
|
298
|
+
"fitFor": [
|
|
299
|
+
"资料阅读",
|
|
300
|
+
"快速理解",
|
|
301
|
+
"内容提炼"
|
|
302
|
+
],
|
|
186
303
|
"updatedAt": "2026-03-29",
|
|
187
|
-
"prerequisites": [
|
|
304
|
+
"prerequisites": [
|
|
305
|
+
"需联网安装",
|
|
306
|
+
"部分场景需要额外 API Key 或本地 CLI"
|
|
307
|
+
],
|
|
188
308
|
"sourceType": "clawhub",
|
|
189
309
|
"clawhubSlug": "summarize",
|
|
190
310
|
"clawhubUrl": "https://clawhub.ai/steipete/summarize"
|
|
@@ -200,10 +320,24 @@
|
|
|
200
320
|
"installCommand": "npx @openschool_01/skills install web-access",
|
|
201
321
|
"packageName": "@openschool/skill-web-access",
|
|
202
322
|
"summary": "这是 OpenSchool 收录的高质量联网技能,适合需要真实浏览器环境、登录态访问、动态页面读取和网页交互的任务。",
|
|
203
|
-
"outcomes": [
|
|
204
|
-
|
|
323
|
+
"outcomes": [
|
|
324
|
+
"网页搜索调度",
|
|
325
|
+
"动态页面读取",
|
|
326
|
+
"浏览器点击交互",
|
|
327
|
+
"媒体与截图提取"
|
|
328
|
+
],
|
|
329
|
+
"fitFor": [
|
|
330
|
+
"网页调研",
|
|
331
|
+
"登录态站点操作",
|
|
332
|
+
"小红书等平台浏览",
|
|
333
|
+
"需要浏览器自动化的任务"
|
|
334
|
+
],
|
|
205
335
|
"updatedAt": "2026-03-28",
|
|
206
|
-
"prerequisites": [
|
|
336
|
+
"prerequisites": [
|
|
337
|
+
"Node.js 22+",
|
|
338
|
+
"Chrome 开启 remote debugging",
|
|
339
|
+
"适合桌面本机环境"
|
|
340
|
+
],
|
|
207
341
|
"sourceType": "curated-github",
|
|
208
342
|
"githubRepo": "eze-is/web-access",
|
|
209
343
|
"githubRef": "main",
|
|
@@ -220,10 +354,23 @@
|
|
|
220
354
|
"installCommand": "npx @openschool_01/skills install word-docx",
|
|
221
355
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
222
356
|
"summary": "适合需要稳定处理 Word 文档的人,能更可靠地生成、检查和修改 DOCX 文件,尤其适合正式文档、方案和交付稿。",
|
|
223
|
-
"outcomes": [
|
|
224
|
-
|
|
357
|
+
"outcomes": [
|
|
358
|
+
"生成 DOCX 文档",
|
|
359
|
+
"编辑段落与样式",
|
|
360
|
+
"处理表格和编号",
|
|
361
|
+
"兼容性检查"
|
|
362
|
+
],
|
|
363
|
+
"fitFor": [
|
|
364
|
+
"方案文档",
|
|
365
|
+
"交付材料",
|
|
366
|
+
"正式报告",
|
|
367
|
+
"需要 Word 结构化输出的工作"
|
|
368
|
+
],
|
|
225
369
|
"updatedAt": "2026-03-30",
|
|
226
|
-
"prerequisites": [
|
|
370
|
+
"prerequisites": [
|
|
371
|
+
"需联网安装",
|
|
372
|
+
"本地需要可用的 npm 环境"
|
|
373
|
+
],
|
|
227
374
|
"sourceType": "clawhub",
|
|
228
375
|
"clawhubSlug": "word-docx",
|
|
229
376
|
"clawhubUrl": "https://clawhub.ai/ivangdavila/word-docx"
|
|
@@ -239,10 +386,23 @@
|
|
|
239
386
|
"installCommand": "npx @openschool_01/skills install powerpoint-pptx",
|
|
240
387
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
241
388
|
"summary": "适合要稳定做演示文稿的人,可以围绕版式、模板、占位符、备注和图表来处理 PPTX,比只生成一堆页面更适合正式办公场景。",
|
|
242
|
-
"outcomes": [
|
|
243
|
-
|
|
389
|
+
"outcomes": [
|
|
390
|
+
"生成 PPTX 演示稿",
|
|
391
|
+
"编辑版式与模板",
|
|
392
|
+
"处理图表与占位符",
|
|
393
|
+
"演示稿质量检查"
|
|
394
|
+
],
|
|
395
|
+
"fitFor": [
|
|
396
|
+
"汇报演示",
|
|
397
|
+
"项目提案",
|
|
398
|
+
"培训材料",
|
|
399
|
+
"客户汇报 PPT"
|
|
400
|
+
],
|
|
244
401
|
"updatedAt": "2026-03-30",
|
|
245
|
-
"prerequisites": [
|
|
402
|
+
"prerequisites": [
|
|
403
|
+
"需联网安装",
|
|
404
|
+
"本地需要可用的 npm 环境"
|
|
405
|
+
],
|
|
246
406
|
"sourceType": "clawhub",
|
|
247
407
|
"clawhubSlug": "powerpoint-pptx",
|
|
248
408
|
"clawhubUrl": "https://clawhub.ai/ivangdavila/powerpoint-pptx"
|
|
@@ -258,10 +418,23 @@
|
|
|
258
418
|
"installCommand": "npx @openschool_01/skills install excel-xlsx",
|
|
259
419
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
260
420
|
"summary": "适合经常处理表格的人,能更稳定地创建和修改 Excel 工作簿,适用于公式、格式、日期类型和模板保留这类办公高频需求。",
|
|
261
|
-
"outcomes": [
|
|
262
|
-
|
|
421
|
+
"outcomes": [
|
|
422
|
+
"生成 XLSX 表格",
|
|
423
|
+
"处理公式与日期",
|
|
424
|
+
"保留模板格式",
|
|
425
|
+
"读取和修改工作簿"
|
|
426
|
+
],
|
|
427
|
+
"fitFor": [
|
|
428
|
+
"数据台账",
|
|
429
|
+
"财务表格",
|
|
430
|
+
"运营报表",
|
|
431
|
+
"需要 Excel 结构化输出的工作"
|
|
432
|
+
],
|
|
263
433
|
"updatedAt": "2026-03-30",
|
|
264
|
-
"prerequisites": [
|
|
434
|
+
"prerequisites": [
|
|
435
|
+
"需联网安装",
|
|
436
|
+
"本地需要可用的 npm 环境"
|
|
437
|
+
],
|
|
265
438
|
"sourceType": "clawhub",
|
|
266
439
|
"clawhubSlug": "excel-xlsx",
|
|
267
440
|
"clawhubUrl": "https://clawhub.ai/ivangdavila/excel-xlsx"
|
|
@@ -277,12 +450,87 @@
|
|
|
277
450
|
"installCommand": "npx @openschool_01/skills install pdf-processing",
|
|
278
451
|
"packageName": "OpenSchool 安装器(内部接入 ClawHub)",
|
|
279
452
|
"summary": "适合日常办公里处理 PDF 的场景,能做提取、表格读取、表单填写和文档合并,比较贴近真实文档流转需求。",
|
|
280
|
-
"outcomes": [
|
|
281
|
-
|
|
453
|
+
"outcomes": [
|
|
454
|
+
"提取 PDF 文本",
|
|
455
|
+
"识别表格内容",
|
|
456
|
+
"填写 PDF 表单",
|
|
457
|
+
"合并 PDF 文档"
|
|
458
|
+
],
|
|
459
|
+
"fitFor": [
|
|
460
|
+
"合同和附件处理",
|
|
461
|
+
"表单回填",
|
|
462
|
+
"资料提取",
|
|
463
|
+
"PDF 日常办公流转"
|
|
464
|
+
],
|
|
282
465
|
"updatedAt": "2026-03-30",
|
|
283
|
-
"prerequisites": [
|
|
466
|
+
"prerequisites": [
|
|
467
|
+
"需联网安装",
|
|
468
|
+
"本地需要可用的 npm 环境"
|
|
469
|
+
],
|
|
284
470
|
"sourceType": "clawhub",
|
|
285
471
|
"clawhubSlug": "pdf-processing",
|
|
286
472
|
"clawhubUrl": "https://clawhub.ai/rainshow/pdf-processing"
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
"slug": "wechat-assistant",
|
|
476
|
+
"name": "公众号助手",
|
|
477
|
+
"tagline": "把选题、标题、正文和微信公众号 HTML 成稿整理成一条稳定链路。",
|
|
478
|
+
"category": "content",
|
|
479
|
+
"badge": "OpenSchool 推荐",
|
|
480
|
+
"status": "已上线",
|
|
481
|
+
"difficulty": "进阶",
|
|
482
|
+
"installCommand": "npx @openschool_01/skills install wechat-assistant",
|
|
483
|
+
"packageName": "@openschool_01/skill-wechat-assistant",
|
|
484
|
+
"summary": "适合直播复盘、观点拆解、教程型文章和系列内容整理,先把公众号内容从想法变成可落地的 Markdown 与 HTML,再继续发布。",
|
|
485
|
+
"outcomes": [
|
|
486
|
+
"标题方向生成",
|
|
487
|
+
"公众号正文写作",
|
|
488
|
+
"微信 HTML 成稿",
|
|
489
|
+
"文章质量检查"
|
|
490
|
+
],
|
|
491
|
+
"fitFor": [
|
|
492
|
+
"公众号内容运营",
|
|
493
|
+
"直播内容转文章",
|
|
494
|
+
"知识型写作",
|
|
495
|
+
"需要微信版式输出的内容团队"
|
|
496
|
+
],
|
|
497
|
+
"updatedAt": "2026-04-10",
|
|
498
|
+
"prerequisites": [
|
|
499
|
+
"本地需要可用的 Python 3 环境",
|
|
500
|
+
"适合先产出文章与 HTML,不含封面生成和自动发布"
|
|
501
|
+
],
|
|
502
|
+
"sourceType": "official",
|
|
503
|
+
"localPackageDir": "packages/skills/wechat-assistant"
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"slug": "xiaohongshu-assistant",
|
|
507
|
+
"name": "小红书助手",
|
|
508
|
+
"tagline": "把主题、原稿和观点素材改写成更适合小红书发布的一版内容。",
|
|
509
|
+
"category": "content",
|
|
510
|
+
"badge": "OpenSchool 推荐",
|
|
511
|
+
"status": "已上线",
|
|
512
|
+
"difficulty": "进阶",
|
|
513
|
+
"installCommand": "npx @openschool_01/skills install xiaohongshu-assistant",
|
|
514
|
+
"packageName": "@openschool_01/skill-xiaohongshu-assistant",
|
|
515
|
+
"summary": "适合把公众号原稿、直播片段、知识观点和碎片想法改写成更像小红书平台原生表达的内容,默认补齐标题、正文、标签和互动引导。",
|
|
516
|
+
"outcomes": [
|
|
517
|
+
"小红书标题生成",
|
|
518
|
+
"正文改写",
|
|
519
|
+
"标签建议",
|
|
520
|
+
"评论区引导"
|
|
521
|
+
],
|
|
522
|
+
"fitFor": [
|
|
523
|
+
"小红书内容运营",
|
|
524
|
+
"公众号转小红书",
|
|
525
|
+
"知识型内容改写",
|
|
526
|
+
"直播内容二次分发"
|
|
527
|
+
],
|
|
528
|
+
"updatedAt": "2026-04-10",
|
|
529
|
+
"prerequisites": [
|
|
530
|
+
"适合先产出纯文案内容",
|
|
531
|
+
"当前版本不含封面生图和自动发布"
|
|
532
|
+
],
|
|
533
|
+
"sourceType": "official",
|
|
534
|
+
"localPackageDir": "packages/skills/xiaohongshu-assistant"
|
|
287
535
|
}
|
|
288
536
|
]
|