@topmindspace/topmind-writing-skills 0.7.1

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.
Files changed (115) hide show
  1. package/CHANGELOG.md +931 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +124 -0
  4. package/README.md +124 -0
  5. package/bin/topmind-writing-skills.js +388 -0
  6. package/package.json +56 -0
  7. package/topmind-briefs/CHANGELOG.md +22 -0
  8. package/topmind-briefs/README.en.md +41 -0
  9. package/topmind-briefs/README.md +37 -0
  10. package/topmind-briefs/SKILL.md +101 -0
  11. package/topmind-briefs/dist/topmind-briefs.manifest.json +21 -0
  12. package/topmind-briefs/dist/topmind-briefs.zip +0 -0
  13. package/topmind-briefs/package-lock.json +13 -0
  14. package/topmind-briefs/package.json +1 -0
  15. package/topmind-briefs/references/examples.md +131 -0
  16. package/topmind-briefs/references/platform-specs.md +63 -0
  17. package/topmind-briefs/references/style-guide.md +60 -0
  18. package/topmind-briefs/scripts/audit_css.py +39 -0
  19. package/topmind-briefs/scripts/audit_docs.py +80 -0
  20. package/topmind-briefs/scripts/audit_skill.py +60 -0
  21. package/topmind-briefs/scripts/audit_styles.py +54 -0
  22. package/topmind-briefs/scripts/negative_tests.py +88 -0
  23. package/topmind-briefs/scripts/package_skill.py +188 -0
  24. package/topmind-cover/README.en.md +54 -0
  25. package/topmind-cover/README.md +54 -0
  26. package/topmind-cover/SKILL.md +134 -0
  27. package/topmind-cover/assets/examples/README.md +34 -0
  28. package/topmind-cover/assets/examples/bg-blur-alt.png +0 -0
  29. package/topmind-cover/assets/examples/bg-blur-wechat.png +0 -0
  30. package/topmind-cover/assets/examples/bg-blur.png +0 -0
  31. package/topmind-cover/assets/examples/big-type-wechat.png +0 -0
  32. package/topmind-cover/assets/examples/big-type.png +0 -0
  33. package/topmind-cover/assets/examples/brand-launch-wechat.png +0 -0
  34. package/topmind-cover/assets/examples/brand-launch.png +0 -0
  35. package/topmind-cover/assets/examples/gan-huo-wechat.png +0 -0
  36. package/topmind-cover/assets/examples/gan-huo.png +0 -0
  37. package/topmind-cover/assets/examples/ip-fun-wechat.png +0 -0
  38. package/topmind-cover/assets/examples/ip-fun.png +0 -0
  39. package/topmind-cover/assets/examples/magazine-wechat.png +0 -0
  40. package/topmind-cover/assets/examples/magazine.png +0 -0
  41. package/topmind-cover/assets/examples/minimal-wechat.png +0 -0
  42. package/topmind-cover/assets/examples/minimal.png +0 -0
  43. package/topmind-cover/assets/examples/news-flash-wechat.png +0 -0
  44. package/topmind-cover/assets/examples/news-flash.png +0 -0
  45. package/topmind-cover/assets/examples/overview.png +0 -0
  46. package/topmind-cover/assets/examples/paper-collage-alt.png +0 -0
  47. package/topmind-cover/assets/examples/paper-collage-wechat.png +0 -0
  48. package/topmind-cover/assets/examples/paper-collage.png +0 -0
  49. package/topmind-cover/assets/examples/tutorial-steps-wechat.png +0 -0
  50. package/topmind-cover/assets/examples/tutorial-steps.png +0 -0
  51. package/topmind-cover/assets/examples/white-clean-alt-wechat.png +0 -0
  52. package/topmind-cover/assets/examples/white-clean-alt.png +0 -0
  53. package/topmind-cover/assets/examples/white-clean-wechat.png +0 -0
  54. package/topmind-cover/assets/examples/white-clean.png +0 -0
  55. package/topmind-cover/package-lock.json +16 -0
  56. package/topmind-cover/package.json +16 -0
  57. package/topmind-cover/references/cover-prompts.md +436 -0
  58. package/topmind-cover/references/cover-styles.md +496 -0
  59. package/topmind-cover/references/impact-language.md +94 -0
  60. package/topmind-cover/references/ref-white-clean-1.png +0 -0
  61. package/topmind-cover/references/ref-white-clean-2.png +0 -0
  62. package/topmind-cover/scripts/audit_css.py +39 -0
  63. package/topmind-cover/scripts/audit_docs.py +80 -0
  64. package/topmind-cover/scripts/audit_skill.py +60 -0
  65. package/topmind-cover/scripts/audit_styles.py +54 -0
  66. package/topmind-cover/scripts/crop-cover.py +74 -0
  67. package/topmind-cover/scripts/negative_tests.py +71 -0
  68. package/topmind-cover/scripts/package_skill.py +188 -0
  69. package/topmind-wechat-post/README.en.md +111 -0
  70. package/topmind-wechat-post/README.md +111 -0
  71. package/topmind-wechat-post/SKILL.md +251 -0
  72. package/topmind-wechat-post/assets/themes/amber-review.json +35 -0
  73. package/topmind-wechat-post/assets/themes/graphite.json +35 -0
  74. package/topmind-wechat-post/assets/themes/minimal-ink.json +35 -0
  75. package/topmind-wechat-post/assets/themes/newsprint.json +35 -0
  76. package/topmind-wechat-post/assets/themes/tech-blue.json +35 -0
  77. package/topmind-wechat-post/package-lock.json +16 -0
  78. package/topmind-wechat-post/package.json +16 -0
  79. package/topmind-wechat-post/references/design.md +50 -0
  80. package/topmind-wechat-post/references/element-spec.md +163 -0
  81. package/topmind-wechat-post/references/known-pits.md +85 -0
  82. package/topmind-wechat-post/references/theme-map.md +139 -0
  83. package/topmind-wechat-post/references/typography-rules.md +62 -0
  84. package/topmind-wechat-post/references/wechat-constraints.md +111 -0
  85. package/topmind-wechat-post/references/workflow.md +102 -0
  86. package/topmind-wechat-post/references/writing-quality.md +105 -0
  87. package/topmind-wechat-post/scripts/audit_css.py +39 -0
  88. package/topmind-wechat-post/scripts/audit_docs.py +80 -0
  89. package/topmind-wechat-post/scripts/audit_skill.py +60 -0
  90. package/topmind-wechat-post/scripts/audit_styles.py +54 -0
  91. package/topmind-wechat-post/scripts/lint-wechat.py +446 -0
  92. package/topmind-wechat-post/scripts/md2wechat.py +1792 -0
  93. package/topmind-wechat-post/scripts/negative_tests.py +171 -0
  94. package/topmind-wechat-post/scripts/new-article.py +183 -0
  95. package/topmind-wechat-post/scripts/package_skill.py +188 -0
  96. package/topmind-wechat-post/scripts/push-to-topstream.py +226 -0
  97. package/topmind-wechat-post/scripts/scan_ai_flavor.py +464 -0
  98. package/topmind-wechat-post/scripts/sync-mapping.py +220 -0
  99. package/topmind-wechat-post/scripts/sync-status.py +187 -0
  100. package/topmind-x-article/README.en.md +109 -0
  101. package/topmind-x-article/README.md +104 -0
  102. package/topmind-x-article/SKILL.md +156 -0
  103. package/topmind-x-article/package-lock.json +16 -0
  104. package/topmind-x-article/package.json +16 -0
  105. package/topmind-x-article/references/publish-checklist.md +31 -0
  106. package/topmind-x-article/references/x-format.md +27 -0
  107. package/topmind-x-article/references/x-html-format.md +85 -0
  108. package/topmind-x-article/scripts/audit_css.py +39 -0
  109. package/topmind-x-article/scripts/audit_docs.py +80 -0
  110. package/topmind-x-article/scripts/audit_skill.py +60 -0
  111. package/topmind-x-article/scripts/audit_styles.py +54 -0
  112. package/topmind-x-article/scripts/md2x-html.py +522 -0
  113. package/topmind-x-article/scripts/md2x.py +323 -0
  114. package/topmind-x-article/scripts/negative_tests.py +91 -0
  115. package/topmind-x-article/scripts/package_skill.py +188 -0
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """技能入口审计:SKILL.md 结构完整性(任一不过 → 退出码 1)。"""
4
+ from __future__ import annotations
5
+
6
+ import re
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ try:
11
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
12
+ except Exception:
13
+ pass
14
+
15
+ ROOT = Path(__file__).resolve().parent.parent
16
+ NAME = ROOT.name
17
+ errors: list[str] = []
18
+
19
+
20
+ def check(cond: bool, msg: str) -> None:
21
+ print((" ✓ " if cond else " ✗ ") + msg)
22
+ if not cond:
23
+ errors.append(msg)
24
+
25
+
26
+ def main() -> None:
27
+ print(f"[audit_skill] {NAME}")
28
+ text = (ROOT / "SKILL.md").read_text(encoding="utf-8")
29
+
30
+ m = re.search(r"^triggers:\s*\n((?: - .+\n?)+)", text, re.M)
31
+ triggers = re.findall(r"^ - (.+)$", m.group(1), re.M) if m else []
32
+ check(len(triggers) >= 1, f"triggers ≥1(实 {len(triggers)})")
33
+
34
+ check("When NOT to use" in text or "何时不用" in text, "含 When NOT to use / 何时不用")
35
+
36
+ has_workflow = bool(re.search(r"工作流|Workflow|用法|Usage", text))
37
+ check(has_workflow, "含工作流/用法说明")
38
+
39
+ fm = re.match(r"^---\r?\n([\s\S]*?)\r?\n---", text)
40
+ desc = ""
41
+ if fm:
42
+ dm = re.search(r'^description:\s*"([^"]*)"\s*$', fm.group(1), re.M)
43
+ if dm:
44
+ desc = dm.group(1).strip()
45
+ else:
46
+ dm = re.search(r"^description:\s*>\-\s*\n((?:[ \t]+.*\n?)+)", fm.group(1), re.M)
47
+ if dm:
48
+ desc = " ".join(l.strip() for l in dm.group(1).splitlines())
49
+ check("Use when" in desc or "用" in desc, "description 含使用场景")
50
+ check("Do NOT use" in desc or "不用" in desc, "description 含不适用场景")
51
+ check(len(desc) <= 1024, f"description ≤1024(实 {len(desc)})")
52
+
53
+ if errors:
54
+ print(f"\n[audit_skill] {NAME}: {len(errors)} 项失败")
55
+ sys.exit(1)
56
+ print(f"\n[audit_skill] {NAME}: 全部通过")
57
+
58
+
59
+ if __name__ == "__main__":
60
+ main()
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """样式资产审计:主题/风格资源有效性(任一不过 → 退出码 1)。
4
+
5
+ 本组技能无 CSS 样式表:若存在 assets/themes/*.json 则校验 JSON 可解析;
6
+ references 风格文档非空(已由 audit_docs 覆盖,此处做存在性断言)。
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ try:
15
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
16
+ except Exception:
17
+ pass
18
+
19
+ ROOT = Path(__file__).resolve().parent.parent
20
+ NAME = ROOT.name
21
+ errors: list[str] = []
22
+
23
+
24
+ def check(cond: bool, msg: str) -> None:
25
+ print((" ✓ " if cond else " ✗ ") + msg)
26
+ if not cond:
27
+ errors.append(msg)
28
+
29
+
30
+ def main() -> None:
31
+ print(f"[audit_styles] {NAME}")
32
+ themes = sorted((ROOT / "assets" / "themes").glob("*.json")) if (ROOT / "assets" / "themes").is_dir() else []
33
+ if themes:
34
+ for t in themes:
35
+ try:
36
+ json.loads(t.read_text(encoding="utf-8"))
37
+ check(True, f"assets/themes/{t.name} JSON 可解析")
38
+ except (OSError, json.JSONDecodeError) as e:
39
+ check(False, f"assets/themes/{t.name} 解析失败: {e}")
40
+ else:
41
+ check(True, "无主题 JSON 资产(本技能不依赖主题文件)")
42
+
43
+ refdir = ROOT / "references"
44
+ has_style_doc = refdir.is_dir() and any(refdir.glob("*.md"))
45
+ check(has_style_doc, "references 风格/格式文档存在")
46
+
47
+ if errors:
48
+ print(f"\n[audit_styles] {NAME}: {len(errors)} 项失败")
49
+ sys.exit(1)
50
+ print(f"\n[audit_styles] {NAME}: 全部通过")
51
+
52
+
53
+ if __name__ == "__main__":
54
+ main()
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """topmind-briefs 异常输入测试:技能工具链在坏输入下干净失败、不崩溃(任一失败 → 退出码 1)。
4
+
5
+ 覆盖:
6
+ 1. 工作流引用的跨技能脚本存在(md2x-html.py / md2wechat.py 缺失即失败,不静默)
7
+ 2. audit_skill.py 在 frontmatter 损坏的 SKILL.md 上退出码 1(不抛未捕获异常)
8
+ 3. package_skill.py --check 在缺 README.md 的技能目录上退出码 1(不抛未捕获异常)
9
+ 4. scripts/*.py 全部可编译
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import shutil
14
+ import subprocess
15
+ import sys
16
+ import tempfile
17
+ from pathlib import Path
18
+
19
+ try:
20
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
21
+ except Exception:
22
+ pass
23
+
24
+ ROOT = Path(__file__).resolve().parent.parent
25
+ REPO = ROOT.parent
26
+ PY = sys.executable
27
+ fails: list[str] = []
28
+
29
+
30
+ def check(name: str, cond: bool, detail: str = "") -> None:
31
+ print((" ✓ " if cond else " ✗ ") + name + (f" — {detail}" if detail and not cond else ""))
32
+ if not cond:
33
+ fails.append(name)
34
+
35
+
36
+ def run_quiet(args: list[str], cwd: Path) -> subprocess.CompletedProcess:
37
+ return subprocess.run([PY, *args], capture_output=True, text=True, timeout=60, cwd=str(cwd))
38
+
39
+
40
+ def main() -> None:
41
+ print("[negative_tests] topmind-briefs")
42
+
43
+ # 1. 跨技能脚本依赖存在
44
+ for rel in ("topmind-x-article/scripts/md2x-html.py",
45
+ "topmind-wechat-post/scripts/md2wechat.py"):
46
+ p = REPO / rel
47
+ check(f"跨技能依赖存在: {rel}", p.is_file())
48
+
49
+ # 2. frontmatter 损坏 → audit_skill 退出 1 且不崩溃
50
+ with tempfile.TemporaryDirectory() as td:
51
+ t = Path(td)
52
+ shutil.copytree(ROOT, t / "skill", ignore=shutil.ignore_patterns("dist", "__pycache__"))
53
+ sk = t / "skill" / "SKILL.md"
54
+ sk.write_text("no frontmatter here\n# broken\n", encoding="utf-8")
55
+ r = run_quiet(["scripts/audit_skill.py"], t / "skill")
56
+ check("audit_skill 在坏 frontmatter 上退出码=1",
57
+ r.returncode == 1, f"rc={r.returncode}")
58
+ check("audit_skill 在坏 frontmatter 上无未捕获 Traceback",
59
+ "Traceback" not in (r.stderr or ""), (r.stderr or "")[-200:])
60
+
61
+ # 3. 缺 README.md → package_skill --check 退出 1 且不崩溃
62
+ with tempfile.TemporaryDirectory() as td:
63
+ t = Path(td)
64
+ shutil.copytree(ROOT, t / "skill", ignore=shutil.ignore_patterns("dist", "__pycache__"))
65
+ (t / "skill" / "README.md").unlink()
66
+ (t / "skill" / "README.en.md").unlink(missing_ok=True)
67
+ r = run_quiet(["scripts/package_skill.py", "--check"], t / "skill")
68
+ check("package_skill --check 在缺 README.md 时退出码=1",
69
+ r.returncode == 1, f"rc={r.returncode}")
70
+ check("package_skill --check 在缺 README.md 时无未捕获 Traceback",
71
+ "Traceback" not in (r.stderr or ""), (r.stderr or "")[-200:])
72
+
73
+ # 4. scripts/*.py 可编译(用内置 compile 只验语法,不写 .pyc,避免污染工作区)
74
+ for py in sorted((ROOT / "scripts").glob("*.py")):
75
+ try:
76
+ compile(py.read_text(encoding="utf-8"), str(py), "exec")
77
+ check(f"可编译: scripts/{py.name}", True)
78
+ except SyntaxError as e:
79
+ check(f"可编译: scripts/{py.name}", False, str(e))
80
+
81
+ if fails:
82
+ print(f"\n[negative_tests] topmind-briefs: {len(fails)} 项失败")
83
+ sys.exit(1)
84
+ print("\n[negative_tests] topmind-briefs: 全部通过")
85
+
86
+
87
+ if __name__ == "__main__":
88
+ main()
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """技能分发包打包器(发布前完整性校验 + 发布清单)。
4
+
5
+ 用法:
6
+ python scripts/package_skill.py # 校验 → 打包 → 写发布清单
7
+ python scripts/package_skill.py --check # 只校验不打包(CI / 提交前自检)
8
+
9
+ 产物:
10
+ dist/<skill>.zip 分发包(解压目录名 = 技能名)
11
+ dist/<skill>.manifest.json 发布清单(版本 / 条目 / 大小 / SHA-256)
12
+
13
+ 校验(任一不过 → 退出码 1):
14
+ · SKILL.md frontmatter 含 name / description;name 与目录名一致;
15
+ description ≤ 1024 字符(平台截断阈值)
16
+ · README.md / package.json 存在;package.json name 与技能名一致
17
+ · scripts/*.py 全部可编译;references/*.md 非空
18
+ · SKILL.md 引用的 references/<file> 全部存在
19
+ · 无 scripts/_* / __pycache__ / .env 等不应进包的文件
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import hashlib
24
+ import json
25
+ import re
26
+ import sys
27
+ import zipfile
28
+ from datetime import datetime, timezone
29
+ from pathlib import Path
30
+
31
+ try:
32
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
33
+ except Exception:
34
+ pass
35
+
36
+ ROOT = Path(__file__).resolve().parent.parent
37
+ NAME = ROOT.name
38
+ OUT = ROOT / "dist" / f"{NAME}.zip"
39
+ MANIFEST = ROOT / "dist" / f"{NAME}.manifest.json"
40
+ DESC_LIMIT = 1024
41
+
42
+ INCLUDE = ["SKILL.md", "README.md", "package.json", "assets/", "references/", "scripts/", "evals/", "agents/"]
43
+ EXCLUDE_RES = [
44
+ re.compile(r"(^|/)scripts/_"),
45
+ re.compile(r"(^|/)\.env($|\.)"),
46
+ re.compile(r"(^|/)__pycache__(/|$)"),
47
+ re.compile(r"(^|/)node_modules(/|$)"),
48
+ re.compile(r"(^|/)dist(/|$)"),
49
+ ]
50
+
51
+ errors: list[str] = []
52
+
53
+
54
+ def fail(msg: str) -> None:
55
+ errors.append(msg)
56
+ print(f" ✗ {msg}")
57
+
58
+
59
+ def ok(msg: str) -> None:
60
+ print(f" ✓ {msg}")
61
+
62
+
63
+ def read_frontmatter() -> dict:
64
+ text = (ROOT / "SKILL.md").read_text(encoding="utf-8")
65
+ m = re.match(r"^---\r?\n([\s\S]*?)\r?\n---", text)
66
+ if not m:
67
+ fail("SKILL.md 缺 frontmatter")
68
+ return {}
69
+ fm = m.group(1)
70
+ name = re.search(r"^name:\s*(\S+)\s*$", fm, re.M)
71
+ desc = ""
72
+ dm = re.search(r'^description:\s*"([^"]*)"\s*$', fm, re.M)
73
+ if dm:
74
+ desc = dm.group(1).strip()
75
+ else:
76
+ dm = re.search(r"^description:\s*>\-\s*\n((?:[ \t]+.*\n?)+)", fm, re.M)
77
+ if dm:
78
+ desc = " ".join(line.strip() for line in dm.group(1).splitlines() if line.strip())
79
+ return {"name": name.group(1) if name else "", "description": desc}
80
+
81
+
82
+ def check() -> bool:
83
+ print(f"[check] {NAME}")
84
+ fm = read_frontmatter()
85
+ if fm.get("name") != NAME:
86
+ fail(f"frontmatter name={fm.get('name')!r} 与目录名 {NAME!r} 不一致")
87
+ else:
88
+ ok("name 与目录名一致")
89
+ desc = fm.get("description", "")
90
+ if not desc:
91
+ fail("description 为空")
92
+ elif len(desc) > DESC_LIMIT:
93
+ fail(f"description {len(desc)} 字符,超过 {DESC_LIMIT}")
94
+ else:
95
+ ok(f"description {len(desc)} 字符")
96
+
97
+ for f in ("README.md", "package.json"):
98
+ if not (ROOT / f).is_file():
99
+ fail(f"缺 {f}")
100
+ else:
101
+ ok(f"有 {f}")
102
+ try:
103
+ pkg = json.loads((ROOT / "package.json").read_text(encoding="utf-8"))
104
+ if pkg.get("name") != NAME:
105
+ fail(f"package.json name={pkg.get('name')!r} 与技能名不一致")
106
+ if not re.match(r"^\d+\.\d+\.\d+", str(pkg.get("version", ""))):
107
+ fail("package.json version 非 semver")
108
+ except (OSError, json.JSONDecodeError) as e:
109
+ fail(f"package.json 解析失败: {e}")
110
+
111
+ for p in ROOT.rglob("*"):
112
+ if not p.is_file() or ".git" in p.parts:
113
+ continue
114
+ rel = p.relative_to(ROOT).as_posix()
115
+ for rx in EXCLUDE_RES:
116
+ if rx.search(rel):
117
+ fail(f"不应进包的文件: {rel}")
118
+ break
119
+ ok("无禁用文件")
120
+
121
+ for py in sorted((ROOT / "scripts").glob("*.py")):
122
+ try:
123
+ compile(py.read_text(encoding="utf-8"), str(py), "exec")
124
+ except SyntaxError as e:
125
+ fail(f"{py.name} 编译失败: {e}")
126
+ ok("scripts/*.py 编译通过")
127
+
128
+ refdir = ROOT / "references"
129
+ if refdir.is_dir():
130
+ for md in sorted(refdir.glob("*.md")):
131
+ if md.stat().st_size == 0:
132
+ fail(f"references/{md.name} 为空")
133
+ skill_text = (ROOT / "SKILL.md").read_text(encoding="utf-8")
134
+ for ref in sorted(set(re.findall(r"references/([\w\-.]+\.md)", skill_text))):
135
+ if not (refdir / ref).is_file():
136
+ fail(f"SKILL.md 引用的 references/{ref} 不存在")
137
+ ok("references 完整性通过")
138
+
139
+ if errors:
140
+ print(f"\n[check] {NAME}: {len(errors)} 项失败")
141
+ return False
142
+ print(f"\n[check] {NAME}: 全部通过")
143
+ return True
144
+
145
+
146
+ def package() -> None:
147
+ if not check():
148
+ sys.exit(1)
149
+ (ROOT / "dist").mkdir(exist_ok=True)
150
+ entries: list[str] = []
151
+ with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED) as z:
152
+ for p in sorted(ROOT.rglob("*")):
153
+ if not p.is_file() or ".git" in p.parts:
154
+ continue
155
+ rel = p.relative_to(ROOT)
156
+ rel_posix = rel.as_posix()
157
+ if any(rx.search(rel_posix) for rx in EXCLUDE_RES):
158
+ continue
159
+ if not any(
160
+ rel_posix == inc.rstrip("/") or rel_posix.startswith(inc)
161
+ for inc in INCLUDE
162
+ if inc.endswith("/")
163
+ ) and rel_posix not in INCLUDE:
164
+ continue
165
+ z.write(p, arcname=f"{NAME}/{rel_posix}")
166
+ entries.append(rel_posix)
167
+ sha = hashlib.sha256(OUT.read_bytes()).hexdigest()
168
+ pkg = json.loads((ROOT / "package.json").read_text(encoding="utf-8"))
169
+ manifest = {
170
+ "name": NAME,
171
+ "version": pkg.get("version"),
172
+ "built_at": datetime.now(timezone.utc).isoformat(),
173
+ "files": len(entries),
174
+ "sha256": sha,
175
+ "entries": entries,
176
+ }
177
+ MANIFEST.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
178
+ print(f"\n[package] {OUT} ({len(entries)} 文件, sha256={sha[:12]}…)")
179
+
180
+
181
+ def main() -> None:
182
+ if "--check" in sys.argv[1:]:
183
+ sys.exit(0 if check() else 1)
184
+ package()
185
+
186
+
187
+ if __name__ == "__main__":
188
+ main()
@@ -0,0 +1,54 @@
1
+ # topmind-cover · Cover Art Generation
2
+
3
+ **[中文](./README.md)** | English
4
+
5
+ A reusable skill for generating cover art for X long-form posts and WeChat articles. Goal: **striking, eye-catching, theme-focused**.
6
+
7
+ - Version: **v0.3.0** (same tag as `@topmindspace/topmind-writing-skills@0.7.1`)
8
+
9
+ ## Style gallery
10
+
11
+ <p align="center">
12
+ <img src="https://github.com/topmindspace/topmind-writing-skills/raw/main/topmind-cover/assets/examples/overview.png" alt="topmind-cover · 11 cover-style overview" width="960" />
13
+ </p>
14
+
15
+ 11 styles (clean white / background blur / paper collage / news flash / giant-type manifesto / tutorial steps / minimalist / editorial magazine / viral dry-goods / brand launch / fun IP, light-first ordering): use cases, palette stories, abstract design principles, and Chinese+English prompt recipes in [`references/cover-styles.md`](./references/cover-styles.md); full-size per-style shots in [`assets/examples/`](./assets/examples/). Prefer writing your own prompts and sending them straight to an image model (bypassing the skill)? See [`references/cover-prompts.md`](./references/cover-prompts.md) — a copy-paste prompt compendium plus cross-platform viral-cover research.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npx @topmindspace/topmind-writing-skills install topmind-cover
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ 0. **Pick a style**: choose 1 of 11 styles from `references/cover-styles.md` by topic (light-first) → check the example image to confirm the visual language.
26
+ 1. **Refine the title**: lock the title copy first (usually ≤ 10 chars) using that style's title rules; rewrite until it lands — no drawing yet.
27
+ 2. **Composition brief**: one-sentence brief (theme / audience / mood / visual metaphor / palette direction) before composing the prompt.
28
+ 3. Input: article title + 3 theme keywords + platform (x / wechat / both, default both).
29
+ 4. Generate with the agent's image-generation capability (landscape 5:2, 8% margin on all sides).
30
+ 5. **Impact self-check** (required): pass every item in the [impact self-check list](./SKILL.md#冲击力自检清单); on failure, tweak the prompt and regenerate, max 3 attempts.
31
+ 6. Crop and save:
32
+
33
+ ```bash
34
+ python3 scripts/crop-cover.py <main-image> --out-dir <package>/images/
35
+ # produces 00-封面.png (1500x600) + 00-封面-公众号.png (900x383, center crop)
36
+ ```
37
+
38
+ ## Sizes
39
+
40
+ | Platform | Size | Aspect |
41
+ |----------|------|--------|
42
+ | X Article cover | 1500x600 | 5:2 (master image) |
43
+ | WeChat cover large image | 900×383 | 2.35:1 (center crop) |
44
+
45
+ ## Design rules
46
+
47
+ One image, one theme; subject fills 40%+ of the frame; big title ≤ 10 chars, high contrast; avoid clutter, tiny dense text, and competing subjects. **Safe-zone rule**: title text and key subject must stay inside the central vertical 60% safe zone.
48
+
49
+ ## Development
50
+
51
+ ```bash
52
+ python3 scripts/package_skill.py --check
53
+ python3 scripts/negative_tests.py
54
+ ```
@@ -0,0 +1,54 @@
1
+ # topmind-cover · 封面配图生成
2
+
3
+ [English](./README.en.md) | 中文
4
+
5
+ 为 X 长文和公众号文章生成封面图的可复用技能。目标:**震撼、醒目、主题突出**。
6
+
7
+ - 版本:**v0.3.0**(与 `@topmindspace/topmind-writing-skills@0.7.1` 同 tag)
8
+
9
+ ## 风格样张
10
+
11
+ <p align="center">
12
+ <img src="https://github.com/topmindspace/topmind-writing-skills/raw/main/topmind-cover/assets/examples/overview.png" alt="topmind-cover · 11 封面风格总览" width="960" />
13
+ </p>
14
+
15
+ 11 种风格(白色清新 / 背景虚化 / 纸感拼贴 / 资讯快报 / 巨字宣言 / 教程步骤 / 极简留白 / 杂志编辑 / 爆款干货 / 品牌发布 / IP 趣味,浅色优先排序):适用场景、配色故事、抽象设计原则、中英 prompt 配方见 [`references/cover-styles.md`](./references/cover-styles.md);单风格大图在 [`assets/examples/`](./assets/examples/)。想自己写提示词直接给 AI 生图(不走技能流程),看 [`references/cover-prompts.md`](./references/cover-prompts.md)(可直接拷贝的提示词大全 + 全平台爆款流派调研)。
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npx @topmindspace/topmind-writing-skills install topmind-cover
21
+ ```
22
+
23
+ ## 用法
24
+
25
+ 0. **选风格**:按题材从 `references/cover-styles.md` 选 1 种(11 选 1,浅色优先)→ 看示例图确认视觉语言。
26
+ 1. **标题提炼**:先定标题文案(一般 ≤10 字),用该风格章节的标题写法规范;不满意重写,不先画图。
27
+ 2. **构图简报**:一句话 brief(主题/受众/情绪/视觉隐喻/配色方向),组 prompt 前先写。
28
+ 3. 输入文章标题 + 3 个主题关键词 + 平台(x / wechat / both,默认 both)。
29
+ 4. 用 agent 的图片生成能力出图(横构图 5:2,四周留白 8%)。
30
+ 5. **冲击力自检**(必做):按 [SKILL.md 冲击力自检清单](./SKILL.md#冲击力自检清单) 逐项过;不通过→调 prompt 重生成,最多 3 次。
31
+ 6. 裁剪落盘:
32
+
33
+ ```bash
34
+ python3 scripts/crop-cover.py <主图> --out-dir <包>/images/
35
+ # 产出 00-封面.png (1500×600) + 00-封面-公众号.png (900×383,中央裁剪)
36
+ ```
37
+
38
+ ## 尺寸
39
+
40
+ | 平台 | 尺寸 | 比例 |
41
+ |------|------|------|
42
+ | X Article 封面 | 1500×600 | 5:2(主图) |
43
+ | 公众号封面大图 | 900×383 | 2.35:1(中央裁) |
44
+
45
+ ## 设计铁律
46
+
47
+ 一图一主题;主体占画面 40%+;大标题 ≤10 字高对比;忌元素堆砌、小字密排、多主体打架。**安全区铁律**:标题字与关键主体必须落在画面中央垂直 60% 安全区内。
48
+
49
+ ## 开发
50
+
51
+ ```bash
52
+ python3 scripts/package_skill.py --check
53
+ python3 scripts/negative_tests.py
54
+ ```
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: topmind-cover
3
+ version: 0.3.0
4
+ description: "文章封面配图生成:X 长文与公众号共用。震撼、醒目、主题突出;平台尺寸规范、11 种风格模板、命名落盘、成图检查全流程覆盖。Use when 文章封面、封面图、头图、题图、cover。Do NOT use for 正文插图、PPT/报告封面。"
5
+ action_category: write
6
+ triggers:
7
+ - 封面
8
+ - 封面图
9
+ - 头图
10
+ - 题图
11
+ - 首图
12
+ - 配图
13
+ - 缩略图
14
+ - banner
15
+ - cover
16
+ - thumbnail
17
+ triggers_cn:
18
+ - 公众号封面
19
+ - 公众号题图
20
+ - X 封面
21
+ - X 首图
22
+ - 文章封面
23
+ author: TopMindspace
24
+ license: MIT
25
+ homepage: https://github.com/topmindspace/topmind-writing-skills#readme
26
+ updated: 2026-09-29
27
+ ---
28
+
29
+ # topmind-cover · 封面配图生成
30
+
31
+ 为 X 长文和公众号文章生成封面图。目标:**震撼、醒目、主题突出**。
32
+
33
+ ```
34
+ 标题提炼 → 构图简报 → 风格模板 → 生成 → 冲击力自检 → 裁剪双尺寸 → 落盘命名
35
+ ```
36
+
37
+ ## 风格索引(浅色优先:1~8 浅色,9~11 深色/高饱和)
38
+
39
+ 风格总览图见 `assets/examples/overview.png`(11 宫格:3 行 × 4 列,离线可看)。
40
+
41
+ | 风格 | 首选题材 |
42
+ |------|----------|
43
+ | 白色清新 `white-clean` | 干货清单、实测盘点、效率/副业/职场 |
44
+ | 背景虚化 `bg-blur` | 生活方式、职场日常、运动健康 |
45
+ | 纸感拼贴 `paper-collage` | 手账、整理术、旧物改造 |
46
+ | 资讯快报 `news-flash` | 资讯快讯、热点解读、揭秘类选题 |
47
+ | 巨字宣言 `big-type` | 观点评论、深度长文、发布宣言 |
48
+ | 教程步骤 `tutorial-steps` | 教程、上手指南、分步实操 |
49
+ | 极简留白 `minimal` | 随笔、书评、轻观点 |
50
+ | 杂志编辑 `magazine` | 深度访谈、人物特写、商业分析 |
51
+ | 爆款干货 `gan-huo` | 干货清单、评测、盘点、实测筛选 |
52
+ | 品牌发布 `brand-launch` | 产品发布、版本更新、官方白皮书 |
53
+ | IP 趣味 `ip-fun` | 实战案例、数据战报、复盘、连载 |
54
+
55
+ 适用场景细则、配色故事、prompt 配方见 `references/cover-styles.md`;单风格大图 `assets/examples/<style>.png`。
56
+
57
+ > 示例图为原创演示:只学设计原则,不临摹第三方版式(详见 `references/cover-styles.md` 顶部"原创铁律")。
58
+
59
+ ## 意图判断 → 直出 / 讨论(生成前必做)
60
+
61
+ 默认按意图直出;意图模糊/多解/缺关键信息时先与用户确认(三问以内)。
62
+
63
+ | # | 检查项 | 通过标准 | 不通过→追问(给选项,不给问答题) |
64
+ |---|--------|----------|----------------------------------|
65
+ | 1 | 标题确定 | 有明确标题文案(≤10 字),或用户给了原文可直接提炼 | 给 2 个备选标题让用户挑:"用 A 还是 B?" |
66
+ | 2 | 主题明确 | 能说清文章讲什么(3 个主题关键词) | 追问:"这篇的核心主题是?"附 2~3 个关键词备选 |
67
+ | 3 | 风格明确 | 题材对上某风格首选题材,或用户点名了风格 | 给 2~3 个候选风格 + 各一句话差异,让用户选 |
68
+ | 4 | 关键信息有出处 | 数字/品牌/人物/署名等真实可查 | 缺→问;问不到→删掉该元素,**绝不编造** |
69
+
70
+ - **四项全过 → 直接生成**,不问。
71
+ - **任一项模糊/多解 → 先确认再生成**,问题总数 ≤3(风格、标题、内容各至多一问)。
72
+ - 讨论时每个问题附 2~3 个具体备选,用户只做选择题。
73
+
74
+ ## 尺寸表
75
+
76
+ | 平台 | 尺寸 | 比例 | 备注 |
77
+ |------|------|------|------|
78
+ | X Article 封面 | 1500×600 | 5:2 | 主尺寸,先按这个生成 |
79
+ | 公众号封面大图 | 900×383 | 2.35:1 | 从 5:2 主图中央裁剪(左右各裁约 3%) |
80
+
81
+ 生成 prompt 里写明"横构图 5:2";公众号版用 `scripts/crop-cover.py` 从主图中央裁出,不重新生成(保系列一致)。
82
+
83
+ ## 工作流
84
+
85
+ 0. **意图判断**(必做):先过上方"意图判断 → 直出 / 讨论"决策流;意图明确→直接生成,模糊→三问以内确认。
86
+ **选风格(三步,必做)**:① 按题材从 `references/cover-styles.md` 选 1 种(11 选 1,浅色优先)→ ② 看 `assets/examples/<style>.png` 确认视觉语言 → ③ 看该风格章节的标题写法规范,心里有标题雏形。
87
+ 1. **标题提炼**(先定文案,不画图):用所选风格在 `cover-styles.md` 对应章节的标题写法规范提炼标题(一般 ≤10 字);不满意就重写,满意再进下一步。
88
+ - 坏:"关于 Manus 2.0 的一些思考与体验"(长、没钩子)→ 好:"Manus 2.0 实测"(短、有断言)
89
+ 2. **构图简报**(一句话 brief,组 prompt 前先写):主题 + 受众 + 情绪 + 视觉隐喻 + 配色方向。
90
+ - 例:"给 AI 从业者看的 Manus 2.0 实测封面,情绪'震撼但可信',视觉隐喻是破壳而出的机械手,深空黑底 + 电光蓝强调。"
91
+ 3. **输入与组 prompt**:用步骤 1 定下的标题 + 3 个主题关键词 + 平台(`x` / `wechat` / `both`,默认 both;平台只影响 prompt 侧重,裁剪落盘永远产出双尺寸)。按风格配方组装 prompt,标题逐字写明(防 AI 自造错别字)。
92
+ 4. **生成**:`media.generate_image`,`name` 用 `<slug>-cover`。
93
+ 5. **冲击力自检**(必做):过下方自检清单 9 项;不通过→调整 prompt 重生成,最多 3 次;3 次仍不过→换一种风格,或回步骤 1 重写标题。
94
+ 6. **裁剪落盘**:
95
+ ```bash
96
+ python3 scripts/crop-cover.py <主图> --out-dir <包>/images/
97
+ ```
98
+ 产出 `00-封面.png`(1500×600,X/公众号共用主文件)与 `00-封面-公众号.png`(900×383)。
99
+ 公众号包规约:正文引用 `images/00-封面.png`,`图片上传清单.md` 登记两版。
100
+ 7. **系列感**:同一系列固定色系 + 版式语言,prompt 复用同一风格模板。
101
+
102
+ ## 冲击力自检清单
103
+
104
+ **生成后必过**。
105
+
106
+ | # | 检查项 | 不通过怎么改 |
107
+ |---|--------|--------------|
108
+ | 1 | 3 秒测试:缩到 120px 宽,3 秒内读出标题并说清"这是什么" | 放大标题,删小字 |
109
+ | 2 | 标题字高 ≥ 画面高度 1/4,空间允许时冲击 1/3 | 放大标题,其他元素让路 |
110
+ | 3 | 对比果断:字色与底色明度差拉满(拒绝灰底白字式低对比) | 底色走极端(极深或极浅) |
111
+ | 4 | 记忆点:3 秒后能向人描述出一个具体的东西 | 加一个形象/数字/符号/质感记忆点 |
112
+ | 5 | 质感:背景有纹理、光影或描边,拒绝纯色死平 | 背景层加一层纸纹/噪点/光影 |
113
+ | 6 | 标题字逐字正确:无 AI 自造错字/乱码/笔画粘连(含装饰导致) | 有错字/粘连→减装饰、缩短标题或修正标题写法后重生成 |
114
+ | 7 | 标题与关键主体落在中央垂直 60% 安全区(900×383 裁剪不切字) | 把元素收拢进安全区 |
115
+ | 8 | `references/impact-language.md` 20 条原则快速勾选 | 按"保守陷阱清单"逐项纠正 |
116
+ | 9 | 装饰手法 ≤ 该风格指定数量(多了即乱;minimal 只许 1 种) | 超量→删掉最弱的一种装饰再生成 |
117
+
118
+ ## 设计铁律
119
+
120
+ - 一图一主题;主体占画面 40% 以上,居中或三分法
121
+ - 大标题 ≤10 字;对比与字高见冲击力自检 #2/#3
122
+ - 四周 8% 安全边距,标题不贴边(中央 60% 安全区见自检 #7)
123
+ - 忌:元素堆砌、小字密排、多主体打架
124
+
125
+ ## 外部依赖 / 规约来源
126
+
127
+ "公众号包规约"(正文引用 `images/00-封面.png`、`图片上传清单.md` 登记两版)来自用户侧
128
+ workbuddy 环境,仓库内无此文件;缺失时仍按固定命名落盘,不阻塞核心流程。
129
+
130
+ ## When NOT to use
131
+
132
+ - 正文插图:主体是文字说明,按正文配图流程走
133
+ - 报告/PPT 封面:走 `topmind-presentation`(仓库 `topmind-presentation`,已从本仓独立出去)
134
+ - 只想找现成图:走 `image_search` 技能
@@ -0,0 +1,34 @@
1
+ # 封面风格示例图
2
+
3
+ 26 张示例(随 npm 包发布,npx 安装即得):11 种风格 × 2 种尺寸 = 22 张 + 3 张 alt 样张
4
+ + 1 张 alt 微信尺寸版(`white-clean-alt-wechat.png`),
5
+ 另附 `overview.png`(11 宫格总览:3 行 × 4 列缩略图 + 风格名标注,选风格时先看它)。
6
+
7
+ - `<style>.png`(1200×675,X 长文封面 / 公众号共用主尺寸)
8
+ > 注:现行主尺寸已改为 1500×600(5:2),见 `../SKILL.md` 尺寸表。
9
+ > 本目录样张为 16:9 旧版,仅作风格参考,不重制。
10
+ - `<style>-wechat.png`(900×383,公众号封面大图,中央裁剪版)
11
+ - `<style>-alt.png`(1200×675,同风格第二张演示样张;白色清新 / 背景虚化 / 纸感拼贴
12
+ 三种新风格各有 2 张:主样张与 alt 样张用了不同的优选组合)
13
+ - `<style>-alt-wechat.png`(900×383,alt 样张的公众号尺寸版;目前只有 white-clean 有,即
14
+ `white-clean-alt-wechat.png`,`crop-cover.py` 流程产出双尺寸,新增 alt 样张时一并产出)
15
+ - `overview.png`(11 宫格总览:3 行 × 4 列缩略图 + 风格名标注,选风格时先看它)
16
+
17
+ 11 种风格(白色清新 `white-clean` / 背景虚化 `bg-blur` / 纸感拼贴 `paper-collage` /
18
+ 资讯快报 `news-flash` / 巨字宣言 `big-type` / 教程步骤 `tutorial-steps` /
19
+ 极简留白 `minimal` / 杂志编辑 `magazine` / 爆款干货 `gan-huo` / 品牌发布 `brand-launch` /
20
+ IP 趣味 `ip-fun`,浅色优先排序):适用场景索引与配方见 `../references/cover-styles.md`。
21
+
22
+ ## 原创与虚构声明(重要)
23
+
24
+ 本目录 26 张示例图均为**原创设计**(只演示抽象设计原则,不临摹任何第三方封面的
25
+ 版式与配色;复用本库设计时亦不得与第三方封面构成实质相似——只学原则,不学版式)。
26
+ 图中标题文案、数字、品牌名、署名均为**虚构演示内容**(如"破晓 6.0"、
27
+ "@老周带单")——直接复用前请换成自己的真实标题;凡封面上的数字必须在正文中有
28
+ 出处,严禁编造数据。
29
+
30
+ ## 复用声明
31
+
32
+ **示例图可直接拿去用/改,作封面底图或风格参考。**拿去用时按 `scripts/crop-cover.py`
33
+ 的流程重新裁剪双尺寸并按包规约命名落盘;改图时保留原风格的配色与版式语言,
34
+ 只换主体与标题字,以维持系列感。