@openschool_01/skills 0.1.2 → 0.1.4

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.
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@openschool_01/skill-human-writing",
3
+ "version": "0.1.0",
4
+ "description": "OpenSchool curated skill: human writing.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "files": [
10
+ "skill"
11
+ ]
12
+ }
@@ -0,0 +1,7 @@
1
+ # OpenSchool 收录说明
2
+
3
+ - 收录名称:Human Writing
4
+ - 收录方式:OpenSchool 严选整合
5
+ - 当前版本:润色版
6
+
7
+ 当前版本重点解决“AI 写得太像 AI”这个问题,适合和公众号助手、小红书助手串起来用。
@@ -0,0 +1,57 @@
1
+ # Human Writing
2
+
3
+ 适用于把 AI 生成的生硬文案,改成更像真人写的版本。
4
+
5
+ 当前版本专注 4 件事:
6
+
7
+ 1. 减少模板味和说明书腔
8
+ 2. 增加真实判断和自然转场
9
+ 3. 保留原信息,但让表达更顺
10
+ 4. 让文案更适合内容平台直接使用
11
+
12
+ ## 什么时候用
13
+
14
+ 当用户要做这些事时使用:
15
+
16
+ - 让 AI 文案更像真人写的
17
+ - 给公众号文章去掉 AI 味
18
+ - 给小红书文案去掉模板感
19
+ - 把结构保留但把语气写活
20
+
21
+ ## 推荐工作流
22
+
23
+ ### 1. 先判断目标平台
24
+
25
+ 优先确认:
26
+
27
+ - 是公众号
28
+ - 小红书
29
+ - 视频口播
30
+ - 普通说明文
31
+
32
+ ### 2. 再判断要保留什么
33
+
34
+ 优先保留:
35
+
36
+ - 核心结论
37
+ - 关键信息
38
+ - 必要步骤
39
+
40
+ 优先改掉:
41
+
42
+ - 套话
43
+ - 重复解释
44
+ - 机械转场
45
+ - 空泛词
46
+
47
+ ### 3. 输出时默认给两版
48
+
49
+ - 精修版
50
+ - 更口语一点的版本
51
+
52
+ ## 输出要求
53
+
54
+ 如果用户没有特别说明,默认补一段简短说明:
55
+
56
+ - 这次主要改掉了什么
57
+ - 哪一版更适合当前平台
@@ -0,0 +1,22 @@
1
+ # 改写规则
2
+
3
+ ## 优先改掉
4
+
5
+ - 本文将
6
+ - 首先、其次、最后
7
+ - 综上所述
8
+ - 赋能、闭环、全方位打造
9
+ - 大量重复的解释句
10
+
11
+ ## 优先保留
12
+
13
+ - 核心观点
14
+ - 真实场景
15
+ - 能直接执行的动作
16
+ - 必须保留的事实信息
17
+
18
+ ## 风格目标
19
+
20
+ - 更像真实人写的
21
+ - 更像平台内容,不像汇报材料
22
+ - 更顺口,但不油腻
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@openschool_01/skill-nano-banana-pro",
3
+ "version": "0.1.0",
4
+ "description": "OpenSchool curated skill: nano banana pro.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "files": [
10
+ "skill"
11
+ ]
12
+ }
@@ -0,0 +1,7 @@
1
+ # OpenSchool 收录说明
2
+
3
+ - 收录名称:Nano Banana Pro
4
+ - 收录方式:OpenSchool 严选整合
5
+ - 当前版本:提示词版
6
+
7
+ 这个版本先解决“图怎么描述才更容易出好”,不先解决“去哪里出图”和“怎么自动下载”。
@@ -0,0 +1,62 @@
1
+ # Nano Banana Pro
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
+ ### 3. 默认输出三部分
50
+
51
+ - 主提示词
52
+ - 2 到 3 条可选变体
53
+ - 负面提示词
54
+
55
+ ## 输出要求
56
+
57
+ 如果用户没指定,默认一起给:
58
+
59
+ - 中文主提示词
60
+ - 英文高质量版提示词
61
+ - 负面提示词
62
+ - 一句使用建议
@@ -0,0 +1,22 @@
1
+ # 提示词规则
2
+
3
+ ## 默认结构
4
+
5
+ 1. 先写画面主体
6
+ 2. 再写风格和材质
7
+ 3. 再写构图和比例
8
+ 4. 最后补背景、氛围和限制
9
+
10
+ ## 适用场景
11
+
12
+ - 公众号封面
13
+ - 小红书封面
14
+ - 宣传海报
15
+ - 产品配图
16
+
17
+ ## 输出原则
18
+
19
+ - 不要只给几个关键词
20
+ - 要能直接复制去生图
21
+ - 主提示词尽量完整
22
+ - 负面提示词尽量简洁
@@ -2,18 +2,19 @@
2
2
 
3
3
  - 收录名称:公众号助手
4
4
  - 收录方式:OpenSchool 严选整合
5
- - 当前版本:纯内容版
6
-
7
- 当前版本整合了公众号写作流程里最常用的三块能力:
5
+ - 当前版本:内容与草稿箱双模版
8
6
 
7
+ 本版本重点能力:
9
8
  1. 标题方向整理
10
- 2. 正文写作约束
11
- 3. 微信 HTML 渲染与质量检查
12
-
13
- 本版本刻意不包含:
9
+ 2. 正文写作与质量检查
10
+ 3. 公众号预览 HTML 渲染(默认无尾注)
11
+ 4. 可选上传草稿箱(用户显式提供 appid/appsecret 时执行)
14
12
 
13
+ 默认不包含:
15
14
  - 生图
16
- - 封面生成
17
- - 微信 API 发布
15
+ - 封面自动生成
16
+ - 自动正式发布
18
17
 
19
- 这样可以先保证“写得出、改得动、落得下、能预览”这条链路稳定。
18
+ 上线约束:
19
+ - 不内置任何个人信息尾注
20
+ - 不在仓库存储真实公众号凭据
@@ -2,108 +2,112 @@
2
2
 
3
3
  适用于把一个选题整理成可发布到微信公众号的完整内容稿。
4
4
 
5
- 当前版本专注 4 件事:
6
-
7
- 1. 明确选题和读者视角
8
- 2. 生成更像公众号风格的标题方向
9
- 3. 写出 Markdown 正文
10
- 4. 渲染成微信公众号可用的 HTML
11
-
12
- 这不是一个“全自动发布器”。当前版本不负责:
13
-
5
+ 当前版本重点做 5 件事:
6
+ 1. 选题和标题方向整理
7
+ 2. 正文 Markdown 写作
8
+ 3. 微信预览 HTML 渲染
9
+ 4. 文章质量检查
10
+ 5. 可选上传到公众号草稿箱(需要用户主动提供凭据)
11
+
12
+ 默认不做:
14
13
  - 生图
15
- - 封面生成
16
- - 微信草稿箱发布
14
+ - 封面图自动生成
15
+ - 自动正式发布
17
16
 
18
- ## 什么时候用
17
+ 默认不追加任何个人尾注信息。
19
18
 
20
- 当用户要做这些事时使用:
19
+ ## 什么时候用
21
20
 
22
21
  - 写公众号文章
23
- - 把直播内容整理成公众号稿
24
- - 生成公众号标题
25
- - 输出微信公众号排版版式的 HTML
26
- - 检查文章是否适合公众号发布
22
+ - 把直播内容整理成公众号稿件
23
+ - 生成公众号标题候选
24
+ - 输出可预览、可复制到后台的 HTML
25
+ - 需要通过 API 上传到草稿箱
27
26
 
28
- ## 推荐工作流
27
+ ## 推荐流程
29
28
 
30
- ### 1. 先收集输入
29
+ ### 1. 收集输入
31
30
 
32
31
  至少确认这 4 个信息:
33
-
34
32
  - 文章主题
35
33
  - 目标读者
36
- - 读者当前最常见的问题
34
+ - 读者当前最常见问题
37
35
  - 这篇文章承诺解决什么
38
36
 
39
- ### 2. 先给标题方向
40
-
41
- 优先输出 3 到 5 个标题,并解释它们分别适合:
42
-
43
- - 偏方法拆解
44
- - 偏观点判断
45
- - 偏案例复盘
37
+ ### 2. 先出标题方向
46
38
 
47
- 标题规则见:
39
+ 输出 3 到 5 个标题,并说明适用场景。
48
40
 
49
- - `references/title-guidelines.md`
41
+ ### 3. 再写正文 Markdown
50
42
 
51
- ### 3. 再写正文
43
+ 正文建议:
44
+ - 4 到 6 个二级标题
45
+ - 先给判断,再解释
46
+ - 至少 1 个真实场景
47
+ - 至少 1 个常见误区
48
+ - 至少 1 组可执行动作
52
49
 
53
- 正文默认要求:
50
+ ### 4. 渲染公众号 HTML(无尾注)
54
51
 
55
- - 使用 4 到 6 个二级标题
56
- - 先给判断,再展开解释
57
- - 至少包含 1 个真实场景
58
- - 至少包含 1 个常见误区
59
- - 至少包含 1 组可执行动作
60
- - 结尾自然引向下一篇或下一步动作
52
+ ```bash
53
+ python scripts/render_wechat_html.py --input "<article.md>" --output "<article>.html"
54
+ ```
61
55
 
62
- 写作约束见:
56
+ ### 5. 质量检查(可选)
63
57
 
64
- - `references/writing-rules.md`
58
+ ```bash
59
+ python scripts/audit_article_quality.py --input "<article.md>"
60
+ ```
65
61
 
66
- ### 4. 渲染 HTML
62
+ ### 6. 上传草稿箱(可选)
67
63
 
68
- 正文 Markdown 完成后,运行:
64
+ 当用户明确需要上传草稿箱时,使用:
69
65
 
70
66
  ```bash
71
- python scripts/render_wechat_html.py --input "<article.md>" --output "<article>.html"
67
+ python scripts/wechat_html_api_publish.py \
68
+ --markdown "<article.md>" \
69
+ --config "config/wechat-publish-api.example.json" \
70
+ --dry-run
72
71
  ```
73
72
 
74
- ### 5. 做一次质量检查
73
+ 确认 dry-run 输出没问题后,再去掉 `--dry-run`。
75
74
 
76
- 如果用户在意发布质量,再运行:
75
+ 也可以直接传入账号参数:
77
76
 
78
77
  ```bash
79
- python scripts/audit_article_quality.py --input "<article.md>"
78
+ python scripts/wechat_html_api_publish.py \
79
+ --markdown "<article.md>" \
80
+ --appid "<appid>" \
81
+ --appsecret "<appsecret>" \
82
+ --author "<author>"
80
83
  ```
81
84
 
85
+ ## 安全约束
86
+
87
+ - 不要把用户的 appid/appsecret 写死进脚本。
88
+ - 不要把真实凭据提交到仓库。
89
+ - 默认使用示例配置文件,用户自行填写本地私密配置。
90
+
82
91
  ## 目录说明
83
92
 
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
- 质量检查脚本
93
+ - `references/title-guidelines.md`:标题参考
94
+ - `references/writing-rules.md`:正文写作规则
95
+ - `assets/wechat-article-template.html`:公众号预览模板
96
+ - `config/wechat-style-config.json`:样式配置
97
+ - `config/wechat-publish-api.example.json`:草稿箱上传配置示例
98
+ - `scripts/render_wechat_html.py`:Markdown 转公众号预览 HTML(无尾注)
99
+ - `scripts/render_wechat_api_content.py`:Markdown 转 API 友好内容片段
100
+ - `scripts/wechat_html_api_publish.py`:调用公众号接口创建草稿
101
+ - `scripts/audit_article_quality.py`:质量检查
96
102
 
97
103
  ## 交付要求
98
104
 
99
105
  默认交付:
100
-
101
- - 3 到 5 个标题备选
106
+ - 3 到 5 个标题候选
102
107
  - 1 篇 Markdown 正文
103
- - 1 个 HTML 文件
104
-
105
- 如果脚本已经执行,还要明确告诉用户:
108
+ - 1 个 HTML 预览文件
106
109
 
107
- - Markdown 文件路径
108
- - HTML 文件路径
109
- - 质量检查结果
110
+ 如果执行了草稿箱上传,还要明确返回:
111
+ - 是否 dry-run
112
+ - 草稿接口返回结果
113
+ - 产物文件路径
@@ -0,0 +1,6 @@
1
+ {
2
+ "appid": "YOUR_WECHAT_APPID",
3
+ "appsecret": "YOUR_WECHAT_APPSECRET",
4
+ "author": "OpenSchool",
5
+ "content_source_url": ""
6
+ }
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """Render markdown into WeChat Official Account API-friendly HTML fragment."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import html
8
+ import re
9
+ from pathlib import Path
10
+
11
+ WECHAT_INLINE = {
12
+ "p": "margin:0 0 18px;line-height:1.85em;font-size:16px;color:#222;text-align:justify;word-break:break-word;",
13
+ "h2": "margin:30px 0 14px;line-height:1.75em;font-size:17px;font-weight:700;color:#0f6fff;",
14
+ "h3": "margin:22px 0 12px;line-height:1.75em;font-size:15px;font-weight:700;color:#0f6fff;",
15
+ "li": "margin:0 0 10px;line-height:1.85em;font-size:16px;color:#222;",
16
+ "blockquote": "margin:0 0 18px;padding:10px 14px;background:#f7faff;border-left:3px solid #0f6fff;color:#355070;line-height:1.85em;font-size:15px;",
17
+ "pre": "margin:0 0 18px;padding:12px 14px;background:#f7f8fa;border:0;border-left:3px solid rgba(15,111,255,.45);overflow-x:auto;",
18
+ "code": "font-size:14px;line-height:1.75em;color:#333;font-family:Consolas,Menlo,monospace;white-space:pre-wrap;",
19
+ "sep": "margin:22px 0 20px;text-align:center;color:#bbb;letter-spacing:1px;",
20
+ "cta": "margin-top:28px;padding-top:14px;border-top:1px solid #e6e6e6;",
21
+ }
22
+
23
+
24
+ def convert_inline(text: str) -> str:
25
+ text = html.escape(text)
26
+ text = re.sub(r"`([^`]+)`", r"<code style=\"%s\">\1</code>" % WECHAT_INLINE["code"], text)
27
+ text = re.sub(r"\*\*([^*]+)\*\*", r"<strong style=\"color:#0f6fff;font-weight:700;\">\1</strong>", text)
28
+ text = re.sub(r"\*([^*]+)\*", r"<em>\1</em>", text)
29
+ text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<a href="\2">\1</a>', text)
30
+ return text
31
+
32
+
33
+ def markdown_to_api_blocks(content: str):
34
+ lines = content.splitlines()
35
+ title = ""
36
+ blocks = []
37
+ in_code = False
38
+ code = []
39
+ para = []
40
+ quote = []
41
+ list_items: list[str] = []
42
+ list_kind = ""
43
+ has_cta = False
44
+
45
+ def flush_para():
46
+ nonlocal para, has_cta
47
+ if not para:
48
+ return
49
+ txt = re.sub(r"\s+", " ", " ".join(p.strip() for p in para if p.strip())).strip()
50
+ para = []
51
+ if not txt:
52
+ return
53
+ if any(k in txt for k in ["留言", "评论", "关键词", "下一篇", "下篇", "领取"]):
54
+ has_cta = True
55
+ blocks.append(f'<p style="{WECHAT_INLINE["p"]}">{convert_inline(txt)}</p>')
56
+
57
+ def flush_quote():
58
+ nonlocal quote
59
+ if not quote:
60
+ return
61
+ txt = re.sub(r"\s+", " ", " ".join(q.strip() for q in quote if q.strip())).strip()
62
+ quote = []
63
+ if not txt:
64
+ return
65
+ blocks.append(f'<blockquote style="{WECHAT_INLINE["blockquote"]}">{convert_inline(txt)}</blockquote>')
66
+
67
+ def flush_list():
68
+ nonlocal list_items, list_kind
69
+ if not list_items:
70
+ return
71
+ tag = "ol" if list_kind == "ol" else "ul"
72
+ items = "".join(f'<li style="{WECHAT_INLINE["li"]}">{convert_inline(item)}</li>' for item in list_items)
73
+ blocks.append(f"<{tag}>{items}</{tag}>")
74
+ list_items = []
75
+ list_kind = ""
76
+
77
+ for raw in lines:
78
+ s = raw.rstrip()
79
+ st = s.strip()
80
+ if st.startswith("```"):
81
+ flush_para()
82
+ flush_quote()
83
+ flush_list()
84
+ if in_code:
85
+ code_html = html.escape("\n".join(code))
86
+ blocks.append(f'<pre style="{WECHAT_INLINE["pre"]}"><code style="{WECHAT_INLINE["code"]}">{code_html}</code></pre>')
87
+ code = []
88
+ in_code = False
89
+ else:
90
+ in_code = True
91
+ continue
92
+
93
+ if in_code:
94
+ code.append(s)
95
+ continue
96
+
97
+ if not st:
98
+ flush_para()
99
+ flush_quote()
100
+ flush_list()
101
+ continue
102
+
103
+ if not title and st.startswith("# "):
104
+ flush_para()
105
+ flush_quote()
106
+ flush_list()
107
+ title = st[2:].strip()
108
+ continue
109
+
110
+ if st.startswith("## "):
111
+ flush_para()
112
+ flush_quote()
113
+ flush_list()
114
+ blocks.append(f'<h2 style="{WECHAT_INLINE["h2"]}">{convert_inline(st[3:])}</h2>')
115
+ continue
116
+
117
+ if st.startswith("### "):
118
+ flush_para()
119
+ flush_quote()
120
+ flush_list()
121
+ blocks.append(f'<h3 style="{WECHAT_INLINE["h3"]}">{convert_inline(st[4:])}</h3>')
122
+ continue
123
+
124
+ if st.startswith("> "):
125
+ flush_para()
126
+ flush_list()
127
+ quote.append(st[2:])
128
+ continue
129
+
130
+ if re.match(r"^\d+\.\s+", st):
131
+ flush_para()
132
+ flush_quote()
133
+ kind = "ol"
134
+ if list_kind and list_kind != kind:
135
+ flush_list()
136
+ list_kind = kind
137
+ list_items.append(re.sub(r"^\d+\.\s+", "", st))
138
+ continue
139
+
140
+ if re.match(r"^[-*]\s+", st):
141
+ flush_para()
142
+ flush_quote()
143
+ kind = "ul"
144
+ if list_kind and list_kind != kind:
145
+ flush_list()
146
+ list_kind = kind
147
+ list_items.append(re.sub(r"^[-*]\s+", "", st))
148
+ continue
149
+
150
+ if st == "---":
151
+ flush_para()
152
+ flush_quote()
153
+ flush_list()
154
+ blocks.append(f'<p style="{WECHAT_INLINE["sep"]}">———</p>')
155
+ continue
156
+
157
+ para.append(st)
158
+
159
+ flush_para()
160
+ flush_quote()
161
+ flush_list()
162
+ return title, "\n".join(blocks), has_cta
163
+
164
+
165
+ def render_api_content(markdown: str, title_override: str = "") -> tuple[str, str, bool]:
166
+ title, body, has_cta = markdown_to_api_blocks(markdown)
167
+ title = title_override or title or "公众号文章"
168
+ cta = (
169
+ ""
170
+ if has_cta
171
+ else f'<section style="{WECHAT_INLINE["cta"]}"><p style="{WECHAT_INLINE["p"]}color:#355070;">如果这篇内容对你有帮助,欢迎留言关键词,我会继续补充相关资料。</p></section>'
172
+ )
173
+ return title, body + cta, has_cta
174
+
175
+
176
+ def main() -> None:
177
+ parser = argparse.ArgumentParser()
178
+ parser.add_argument("--input", required=True)
179
+ parser.add_argument("--output", required=True)
180
+ parser.add_argument("--title", default="")
181
+ args = parser.parse_args()
182
+
183
+ markdown = Path(args.input).read_text(encoding="utf-8")
184
+ _, html_doc, _ = render_api_content(markdown, args.title)
185
+ out = Path(args.output)
186
+ out.parent.mkdir(parents=True, exist_ok=True)
187
+ out.write_text(html_doc, encoding="utf-8")
188
+
189
+
190
+ if __name__ == "__main__":
191
+ main()