opencode-skills-collection 3.1.23 → 4.0.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 (241) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +24 -1
  2. package/bundled-skills/007/SKILL.md +2 -2
  3. package/bundled-skills/10-andruia-skill-smith/SKILL.md +1 -1
  4. package/bundled-skills/agent-squad/SKILL.md +5 -0
  5. package/bundled-skills/agent-squad/alex/SKILL.md +3 -0
  6. package/bundled-skills/agent-squad/aria/SKILL.md +3 -0
  7. package/bundled-skills/agent-squad/dep/SKILL.md +3 -0
  8. package/bundled-skills/agent-squad/luna/SKILL.md +4 -1
  9. package/bundled-skills/agent-squad/mason/SKILL.md +3 -0
  10. package/bundled-skills/agent-squad/max/SKILL.md +3 -0
  11. package/bundled-skills/agent-squad/quinn/SKILL.md +3 -0
  12. package/bundled-skills/agent-squad/rex/SKILL.md +3 -0
  13. package/bundled-skills/angular/metadata.json +1 -1
  14. package/bundled-skills/angular-best-practices/metadata.json +1 -1
  15. package/bundled-skills/angular-state-management/metadata.json +1 -1
  16. package/bundled-skills/angular-ui-patterns/metadata.json +1 -1
  17. package/bundled-skills/antigravity-skill-orchestrator/SKILL.md +2 -2
  18. package/bundled-skills/apify-actor-development/SKILL.md +2 -2
  19. package/bundled-skills/apple-container/SKILL.md +165 -0
  20. package/bundled-skills/apple-container/references/commands.md +1593 -0
  21. package/bundled-skills/apple-container/references/concepts.md +294 -0
  22. package/bundled-skills/apple-container/references/configuration.md +499 -0
  23. package/bundled-skills/apple-container/references/workflows.md +498 -0
  24. package/bundled-skills/ask-copilot/SKILL.md +112 -0
  25. package/bundled-skills/auto-research/SKILL.md +126 -0
  26. package/bundled-skills/brooks-lint/SKILL.md +1 -1
  27. package/bundled-skills/browser-harness/references/install.md +3 -1
  28. package/bundled-skills/chrome-extension-developer/SKILL.md +1 -1
  29. package/bundled-skills/clean-code-guard/SKILL.md +184 -0
  30. package/bundled-skills/clean-code-guard/references/ai-failure-modes.md +345 -0
  31. package/bundled-skills/clean-code-guard/references/comments-and-formatting.md +115 -0
  32. package/bundled-skills/clean-code-guard/references/dry-kiss-yagni.md +133 -0
  33. package/bundled-skills/clean-code-guard/references/naming-and-functions.md +187 -0
  34. package/bundled-skills/clean-code-guard/references/review-checklist.md +150 -0
  35. package/bundled-skills/clean-code-guard/references/solid.md +280 -0
  36. package/bundled-skills/clean-code-guard/references/sources.md +47 -0
  37. package/bundled-skills/cloudflare-security-audit/SKILL.md +137 -0
  38. package/bundled-skills/cloudflare-security-audit/references/AI-AND-LLM.md +67 -0
  39. package/bundled-skills/cloudflare-security-audit/references/ATTACK-CLASSES.md +116 -0
  40. package/bundled-skills/cloudflare-security-audit/references/CLIENT-SIDE.md +67 -0
  41. package/bundled-skills/cloudflare-security-audit/references/HUNTING.md +110 -0
  42. package/bundled-skills/cloudflare-security-audit/references/MEMORY-SAFETY-AND-BINARY.md +58 -0
  43. package/bundled-skills/cloudflare-security-audit/references/RECONNAISSANCE.md +46 -0
  44. package/bundled-skills/cloudflare-security-audit/references/VALIDATION-AND-REPORTING.md +109 -0
  45. package/bundled-skills/cloudflare-security-audit/references/WEB-PROTOCOL-AND-AUTH.md +85 -0
  46. package/bundled-skills/cloudflare-security-audit/resources/report-schema.json +210 -0
  47. package/bundled-skills/comfyui-gateway/references/troubleshooting.md +1 -1
  48. package/bundled-skills/commit/SKILL.md +2 -1
  49. package/bundled-skills/deepapi/SKILL.md +6 -6
  50. package/bundled-skills/docs/README.md +1 -1
  51. package/bundled-skills/docs/contributors/quality-bar.md +1 -1
  52. package/bundled-skills/docs/contributors/security-guardrails.md +1 -1
  53. package/bundled-skills/docs/integrations/jetski-cortex.md +5 -5
  54. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +3 -3
  55. package/bundled-skills/docs/maintainers/ci-drift-fix.md +7 -5
  56. package/bundled-skills/docs/maintainers/merge-batch.md +24 -7
  57. package/bundled-skills/docs/maintainers/pr-autonomy.md +89 -0
  58. package/bundled-skills/docs/maintainers/release-notes-7.2.0.md +1 -1
  59. package/bundled-skills/docs/maintainers/release-process.md +9 -9
  60. package/bundled-skills/docs/maintainers/repo-growth-seo.md +5 -5
  61. package/bundled-skills/docs/maintainers/skills-date-tracking.md +1 -1
  62. package/bundled-skills/docs/maintainers/skills-update-guide.md +2 -2
  63. package/bundled-skills/docs/users/{antigravity-awesome-skills-vs-awesome-claude-skills.md → agentic-awesome-skills-vs-awesome-claude-skills.md} +5 -5
  64. package/bundled-skills/docs/users/ai-agent-skills.md +4 -4
  65. package/bundled-skills/docs/users/best-claude-code-skills-github.md +3 -3
  66. package/bundled-skills/docs/users/best-cursor-skills-github.md +3 -3
  67. package/bundled-skills/docs/users/bundles.md +56 -57
  68. package/bundled-skills/docs/users/claude-code-skills.md +6 -6
  69. package/bundled-skills/docs/users/codex-cli-skills.md +5 -5
  70. package/bundled-skills/docs/users/cursor-skills.md +3 -3
  71. package/bundled-skills/docs/users/faq.md +27 -15
  72. package/bundled-skills/docs/users/gemini-cli-skills.md +4 -4
  73. package/bundled-skills/docs/users/getting-started.md +22 -12
  74. package/bundled-skills/docs/users/kiro-integration.md +7 -7
  75. package/bundled-skills/docs/users/plugins.md +8 -9
  76. package/bundled-skills/docs/users/security-skills.md +1 -1
  77. package/bundled-skills/docs/users/skills-vs-mcp-tools.md +1 -1
  78. package/bundled-skills/docs/users/specialized-plugin-roadmap.md +3 -4
  79. package/bundled-skills/docs/users/usage.md +9 -9
  80. package/bundled-skills/docs/users/visual-guide.md +7 -7
  81. package/bundled-skills/docs/users/windows-truncation-recovery.md +1 -1
  82. package/bundled-skills/docs/users/workflows.md +1 -1
  83. package/bundled-skills/docs/vietnamese/BUNDLES.vi.md +1 -1
  84. package/bundled-skills/docs/vietnamese/FAQ.vi.md +15 -8
  85. package/bundled-skills/docs/vietnamese/GETTING_STARTED.vi.md +12 -5
  86. package/bundled-skills/docs/vietnamese/QUALITY_BAR.vi.md +1 -1
  87. package/bundled-skills/docs/vietnamese/README.vi.md +21 -17
  88. package/bundled-skills/docs/vietnamese/SECURITY_GUARDRAILS.vi.md +1 -1
  89. package/bundled-skills/docs/vietnamese/SKILLS_README.vi.md +2 -2
  90. package/bundled-skills/docs/vietnamese/TRANSLATION_PLAN.vi.md +1 -1
  91. package/bundled-skills/docs/vietnamese/VISUAL_GUIDE.vi.md +8 -8
  92. package/bundled-skills/docs-guard/SKILL.md +109 -0
  93. package/bundled-skills/docs-guard/references/code-samples.md +46 -0
  94. package/bundled-skills/docs-guard/references/docstrings.md +60 -0
  95. package/bundled-skills/docs-guard/references/review-checklist.md +45 -0
  96. package/bundled-skills/docs-guard/references/sources.md +22 -0
  97. package/bundled-skills/docs-guard/references/verification.md +51 -0
  98. package/bundled-skills/efficient-web-research/SKILL.md +4 -0
  99. package/bundled-skills/electron-development/SKILL.md +1 -1
  100. package/bundled-skills/event-staffing-compliance/SKILL.md +3 -0
  101. package/bundled-skills/event-staffing-ordering/SKILL.md +3 -0
  102. package/bundled-skills/feature-tracking/SKILL.md +222 -0
  103. package/bundled-skills/flutter-expert/SKILL.md +6 -1
  104. package/bundled-skills/flutter-expert/resources/implementation-playbook.md +214 -0
  105. package/bundled-skills/gemini-deep-research/README.md +246 -0
  106. package/bundled-skills/gemini-deep-research/SKILL.md +136 -0
  107. package/bundled-skills/gemini-deep-research/requirements.txt +2 -0
  108. package/bundled-skills/gemini-deep-research/scripts/research.py +692 -0
  109. package/bundled-skills/git-pushing/SKILL.md +21 -4
  110. package/bundled-skills/git-pushing/scripts/smart_commit.sh +206 -11
  111. package/bundled-skills/go-in-depth/scripts/workflow-script.js +4 -1
  112. package/bundled-skills/grok-build/SKILL.md +167 -0
  113. package/bundled-skills/grok-build/references/cli.md +99 -0
  114. package/bundled-skills/huggingface-tool-builder/references/find_models_by_paper.sh +1 -1
  115. package/bundled-skills/idea-autopsy/SKILL.md +147 -0
  116. package/bundled-skills/it-manager-hospital/README.md +1 -1
  117. package/bundled-skills/it-manager-pro/README.md +1 -1
  118. package/bundled-skills/itil-expert/README.md +1 -1
  119. package/bundled-skills/langgraph/SKILL.md +1 -1
  120. package/bundled-skills/linkedin-content-generator/SKILL.md +1 -1
  121. package/bundled-skills/lint-and-validate/scripts/lint_runner.py +23 -23
  122. package/bundled-skills/lint-and-validate/scripts/type_coverage.py +30 -30
  123. package/bundled-skills/logic-lens/SKILL.md +1 -1
  124. package/bundled-skills/lore/LICENSE +21 -0
  125. package/bundled-skills/lore/README.md +386 -0
  126. package/bundled-skills/lore/README.zh-CN.md +386 -0
  127. package/bundled-skills/lore/SKILL.md +453 -0
  128. package/bundled-skills/lore/WORKFLOWS.md +216 -0
  129. package/bundled-skills/lore/WORKFLOWS.zh-CN.md +216 -0
  130. package/bundled-skills/lore/references/audit-template.md +60 -0
  131. package/bundled-skills/lore/references/compatibility.md +228 -0
  132. package/bundled-skills/lore/references/config.md +125 -0
  133. package/bundled-skills/lore/references/entry-format.md +70 -0
  134. package/bundled-skills/lore/references/history-command.md +106 -0
  135. package/bundled-skills/lore/references/monorepo-detection.md +78 -0
  136. package/bundled-skills/lore/references/platform-mirrors.md +377 -0
  137. package/bundled-skills/lore/references/stale-new-markers.md +62 -0
  138. package/bundled-skills/lore/references/summary-template.md +98 -0
  139. package/bundled-skills/lore/scripts/README.md +54 -0
  140. package/bundled-skills/lore/scripts/README.zh-CN.md +54 -0
  141. package/bundled-skills/lore/scripts/find_duplicates.py +211 -0
  142. package/bundled-skills/lore/scripts/find_stale.py +116 -0
  143. package/bundled-skills/lore/scripts/history.py +527 -0
  144. package/bundled-skills/lore/scripts/id_hash.py +32 -0
  145. package/bundled-skills/lore/scripts/list_entries.py +183 -0
  146. package/bundled-skills/lovable-cleanup/SKILL.md +3 -3
  147. package/bundled-skills/makepad-splash/SKILL.md +1 -1
  148. package/bundled-skills/mmx-cli/SKILL.md +14 -1
  149. package/bundled-skills/monopoly/SKILL.md +5 -0
  150. package/bundled-skills/monopoly/patterns/SKILL.md +4 -0
  151. package/bundled-skills/monopoly/scale-benchmarks/SKILL.md +4 -0
  152. package/bundled-skills/monopoly/security-checklist/SKILL.md +4 -0
  153. package/bundled-skills/monopoly/tech-matrix/SKILL.md +4 -0
  154. package/bundled-skills/mtls-configuration/SKILL.md +1 -1
  155. package/bundled-skills/network-101/SKILL.md +4 -5
  156. package/bundled-skills/nika/SKILL.md +270 -0
  157. package/bundled-skills/not-a-vibe-coder/SKILL.md +4 -0
  158. package/bundled-skills/ontoly-software-graph/SKILL.md +160 -0
  159. package/bundled-skills/openclaw-github-repo-commander/SKILL.md +1 -1
  160. package/bundled-skills/pilot-protocol/SKILL.md +6 -3
  161. package/bundled-skills/postgres-readonly-queries/README.md +77 -0
  162. package/bundled-skills/postgres-readonly-queries/SKILL.md +152 -0
  163. package/bundled-skills/postgres-readonly-queries/connections.example.json +34 -0
  164. package/bundled-skills/postgres-readonly-queries/requirements.txt +1 -0
  165. package/bundled-skills/postgres-readonly-queries/scripts/query.py +262 -0
  166. package/bundled-skills/pptx-deck-creation/SKILL.md +275 -0
  167. package/bundled-skills/pptx-deck-creation/references/audit-checklist.md +156 -0
  168. package/bundled-skills/pptx-deck-creation/references/design-profiles.md +96 -0
  169. package/bundled-skills/pptx-deck-creation/references/python-snippets.md +150 -0
  170. package/bundled-skills/pptx-deck-creation/references/reference-deck-analysis.md +91 -0
  171. package/bundled-skills/pptx-deck-creation/references/visual-asset-adapters.md +158 -0
  172. package/bundled-skills/privilege-escalation-methods/SKILL.md +1 -1
  173. package/bundled-skills/product-decision-agent/SKILL.md +94 -0
  174. package/bundled-skills/product-decision-agent/agents/openai.yaml +7 -0
  175. package/bundled-skills/product-decision-agent/references/methodology-basis.md +232 -0
  176. package/bundled-skills/product-decision-agent/references/product-playbooks.md +813 -0
  177. package/bundled-skills/product-decision-agent/references/reasoning-engine.md +289 -0
  178. package/bundled-skills/product-decision-agent/references/response-examples.md +279 -0
  179. package/bundled-skills/product-decision-agent/scripts/quality_gate.py +221 -0
  180. package/bundled-skills/product-decision-agent/scripts/test_quality_gate.py +94 -0
  181. package/bundled-skills/pytest-skill/reference/playbook.md +358 -0
  182. package/bundled-skills/quit-sponsor/SKILL.md +96 -0
  183. package/bundled-skills/rclone-cli/references/providers/s3.md +1 -1
  184. package/bundled-skills/security-and-hardening/SKILL.md +1 -1
  185. package/bundled-skills/semgrep-rule-creator/SKILL.md +6 -6
  186. package/bundled-skills/skill-creator/README.md +3 -3
  187. package/bundled-skills/skill-creator/SKILL.md +8 -4
  188. package/bundled-skills/skill-creator/scripts/quick_validate.py +1 -1
  189. package/bundled-skills/sql-injection-testing/SKILL.md +4 -4
  190. package/bundled-skills/subagent-orchestrator/README.md +1 -1
  191. package/bundled-skills/super-code/bash/SKILL.md +3 -0
  192. package/bundled-skills/super-code/c/SKILL.md +3 -0
  193. package/bundled-skills/super-code/cpp/SKILL.md +3 -0
  194. package/bundled-skills/super-code/csharp/SKILL.md +3 -0
  195. package/bundled-skills/super-code/dart/SKILL.md +3 -0
  196. package/bundled-skills/super-code/elixir/SKILL.md +3 -0
  197. package/bundled-skills/super-code/go/SKILL.md +3 -0
  198. package/bundled-skills/super-code/java/SKILL.md +3 -0
  199. package/bundled-skills/super-code/kotlin/SKILL.md +3 -0
  200. package/bundled-skills/super-code/php/SKILL.md +3 -0
  201. package/bundled-skills/super-code/python/SKILL.md +3 -0
  202. package/bundled-skills/super-code/ruby/SKILL.md +3 -0
  203. package/bundled-skills/super-code/rust/SKILL.md +3 -0
  204. package/bundled-skills/super-code/scala/SKILL.md +3 -0
  205. package/bundled-skills/super-code/swift/SKILL.md +3 -0
  206. package/bundled-skills/super-code/typescript/SKILL.md +3 -0
  207. package/bundled-skills/telegram-bot-messaging/README.md +185 -0
  208. package/bundled-skills/telegram-bot-messaging/SKILL.md +101 -0
  209. package/bundled-skills/telegram-bot-messaging/scripts/telegram.sh +420 -0
  210. package/bundled-skills/test-guard/SKILL.md +132 -0
  211. package/bundled-skills/test-guard/references/jest.md +50 -0
  212. package/bundled-skills/test-guard/references/llm-app-testing.md +40 -0
  213. package/bundled-skills/test-guard/references/phpunit.md +56 -0
  214. package/bundled-skills/test-guard/references/pytest.md +65 -0
  215. package/bundled-skills/trading-ledger/SKILL.md +11 -3
  216. package/bundled-skills/uizze-ui-research/SKILL.md +93 -0
  217. package/bundled-skills/vibe-code-auditor/SKILL.md +1 -1
  218. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +1 -1
  219. package/bundled-skills/videodb/scripts/ws_listener.py +29 -15
  220. package/bundled-skills/voice-ai-engine-development/README.md +1 -1
  221. package/bundled-skills/vulnerability-scanner/SKILL.md +1 -1
  222. package/bundled-skills/warehouse/SKILL.md +154 -0
  223. package/bundled-skills/woo-guard/SKILL.md +113 -0
  224. package/bundled-skills/woo-guard/references/checkout-and-money.md +64 -0
  225. package/bundled-skills/woo-guard/references/hpos-and-crud.md +87 -0
  226. package/bundled-skills/woo-guard/references/review-checklist.md +44 -0
  227. package/bundled-skills/woo-guard/references/sources.md +17 -0
  228. package/bundled-skills/wp-guard/SKILL.md +119 -0
  229. package/bundled-skills/wp-guard/references/i18n.md +88 -0
  230. package/bundled-skills/wp-guard/references/performance.md +70 -0
  231. package/bundled-skills/wp-guard/references/review-checklist.md +57 -0
  232. package/bundled-skills/wp-guard/references/security.md +100 -0
  233. package/bundled-skills/wp-guard/references/sources.md +27 -0
  234. package/bundled-skills/writing-skills/gotchas.md +1 -1
  235. package/bundled-skills/writing-skills/references/standards/metadata-standard.md +1 -1
  236. package/bundled-skills/x-twitter-scraper/SKILL.md +14 -32
  237. package/bundled-skills/xss-html-injection/SKILL.md +3 -3
  238. package/bundled-skills/youtube-seo-optimizer/SKILL.md +1 -1
  239. package/bundled-skills/youtube-summarizer/CHANGELOG.md +2 -2
  240. package/package.json +1 -1
  241. package/skills_index.json +273 -0
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env python3
2
+ """Quality checks for Maoxuan Product Agent sample outputs.
3
+
4
+ Usage:
5
+ quality_gate.py output1.md output2.md ...
6
+
7
+ The script catches common regressions:
8
+ - source/theory leakage
9
+ - English-dominant answers in Chinese work scenes
10
+ - vague advice without concrete next actions
11
+ - excessive question dumping in the "需要确认" section
12
+ - missing judgment, risk, or decision signal
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import re
18
+ import sys
19
+ from pathlib import Path
20
+
21
+
22
+ HARD_BANNED = [
23
+ r"毛泽东",
24
+ r"毛选",
25
+ r"矛盾论",
26
+ r"实践论",
27
+ r"教员",
28
+ r"毛主席",
29
+ r"同志",
30
+ r"阶级",
31
+ r"革命",
32
+ r"斗争",
33
+ r"主席指出",
34
+ r"《[^》]+》认为",
35
+ ]
36
+
37
+ SOFT_BANNED = [
38
+ r"辩证",
39
+ r"唯物",
40
+ r"经典",
41
+ r"原文",
42
+ ]
43
+
44
+ VAGUE_PATTERNS = [
45
+ r"提升用户体验",
46
+ r"加强沟通",
47
+ r"多看数据",
48
+ r"深入了解用户",
49
+ r"持续优化",
50
+ r"形成闭环",
51
+ r"赋能",
52
+ r"抓手",
53
+ r"打透认知",
54
+ ]
55
+
56
+ JUDGMENT_HINTS = [
57
+ "问题判断",
58
+ "核心是",
59
+ "关键是",
60
+ "不是",
61
+ "当前最",
62
+ "先不要",
63
+ ]
64
+
65
+ ACTION_HINTS = [
66
+ "行动建议",
67
+ "下一步",
68
+ "今天",
69
+ "本周",
70
+ "24 小时",
71
+ "48 小时",
72
+ "2 天",
73
+ "一周",
74
+ "两周",
75
+ "负责人",
76
+ "指标",
77
+ "验证",
78
+ "实验",
79
+ "灰度",
80
+ "拆",
81
+ "砍",
82
+ "暂停",
83
+ "停止",
84
+ ]
85
+
86
+ DECISION_HINTS = [
87
+ "成功指标",
88
+ "失败指标",
89
+ "主指标",
90
+ "影响指标",
91
+ "护栏",
92
+ "全量",
93
+ "停止",
94
+ "暂停",
95
+ "复盘",
96
+ "监控",
97
+ "达标",
98
+ "不达标",
99
+ "决策",
100
+ "上线",
101
+ "回滚",
102
+ "留存",
103
+ "转化",
104
+ "回复率",
105
+ "复访",
106
+ "D7",
107
+ "7 日",
108
+ "7日",
109
+ ]
110
+
111
+ RISK_HINTS = [
112
+ "风险提醒",
113
+ "不要",
114
+ "先不要",
115
+ "暂时不要",
116
+ "不建议",
117
+ ]
118
+
119
+
120
+ def count_questions(text: str) -> int:
121
+ """Count questions in the "需要确认" section only."""
122
+ match = re.search(
123
+ r"(?:^|\n)(?:#{1,3}\s*)?(?:\*\*)?需要确认(?:\*\*)?[^\n]*\n"
124
+ r"(?P<section>.*?)(?=\n(?:#{1,3}\s+|\*\*[^*\n]+?\*\*)|\Z)",
125
+ text,
126
+ re.DOTALL,
127
+ )
128
+ if not match:
129
+ return 0
130
+
131
+ section = match.group("section")
132
+ questions = 0
133
+ for line in section.splitlines():
134
+ stripped = line.strip()
135
+ if not stripped:
136
+ continue
137
+ # Count sentence-ending question marks. This ignores URL query strings
138
+ # such as https://example.com?a=1 while still catching two questions
139
+ # written on the same line.
140
+ questions += len(
141
+ re.findall(r"?|\?(?=[\s\u4e00-\u9fff]|$)", stripped)
142
+ )
143
+ return questions
144
+
145
+
146
+ def chinese_ratio(text: str) -> float:
147
+ zh = len(re.findall(r"[\u4e00-\u9fff]", text))
148
+ en = len(re.findall(r"[A-Za-z]", text))
149
+ if zh + en == 0:
150
+ return 0.0
151
+ return zh / (zh + en)
152
+
153
+
154
+ def check_file(path: Path) -> tuple[list[str], list[str]]:
155
+ text = path.read_text(encoding="utf-8")
156
+ errors: list[str] = []
157
+ warnings: list[str] = []
158
+
159
+ for pattern in HARD_BANNED:
160
+ if re.search(pattern, text, re.IGNORECASE):
161
+ errors.append(f"exposes hard-banned source/style term: {pattern}")
162
+
163
+ for pattern in SOFT_BANNED:
164
+ if re.search(pattern, text, re.IGNORECASE):
165
+ warnings.append(f"contains review-needed term: {pattern}")
166
+
167
+ vague_hits = [p for p in VAGUE_PATTERNS if re.search(p, text)]
168
+ if vague_hits:
169
+ errors.append(f"contains vague phrase(s): {', '.join(vague_hits)}")
170
+
171
+ if chinese_ratio(text) < 0.72:
172
+ errors.append("answer is not Chinese-dominant enough")
173
+
174
+ if not any(hint in text for hint in JUDGMENT_HINTS):
175
+ errors.append("missing problem judgment")
176
+
177
+ if not any(hint in text for hint in ACTION_HINTS):
178
+ errors.append("missing concrete action hints")
179
+
180
+ if not any(hint in text for hint in DECISION_HINTS):
181
+ errors.append("missing metric/decision/review signal")
182
+
183
+ if not any(hint in text for hint in RISK_HINTS):
184
+ errors.append("missing risk or stop-doing guidance")
185
+
186
+ if count_questions(text) > 3:
187
+ errors.append("asks too many questions")
188
+
189
+ stripped_len = len(text.strip())
190
+ if stripped_len < 120:
191
+ errors.append("output is likely too thin")
192
+ if stripped_len > 2200:
193
+ errors.append("output is likely too verbose")
194
+
195
+ return errors, warnings
196
+
197
+
198
+ def main(argv: list[str]) -> int:
199
+ if not argv:
200
+ print("Usage: quality_gate.py output1.md output2.md ...", file=sys.stderr)
201
+ return 2
202
+
203
+ failed = False
204
+ for item in argv:
205
+ path = Path(item)
206
+ errors, warnings = check_file(path)
207
+ if errors:
208
+ failed = True
209
+ print(f"FAIL {path}")
210
+ for err in errors:
211
+ print(f" - {err}")
212
+ else:
213
+ print(f"PASS {path}")
214
+ for warning in warnings:
215
+ print(f" WARN {warning}")
216
+
217
+ return 1 if failed else 0
218
+
219
+
220
+ if __name__ == "__main__":
221
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ """Regression tests for the sample-output quality gate."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import importlib.util
7
+ import tempfile
8
+ import unittest
9
+ from pathlib import Path
10
+
11
+
12
+ MODULE_PATH = Path(__file__).with_name("quality_gate.py")
13
+ SPEC = importlib.util.spec_from_file_location("quality_gate", MODULE_PATH)
14
+ if SPEC is None or SPEC.loader is None:
15
+ raise RuntimeError(f"Cannot load {MODULE_PATH}")
16
+ quality_gate = importlib.util.module_from_spec(SPEC)
17
+ SPEC.loader.exec_module(quality_gate)
18
+
19
+
20
+ VALID_OUTPUT = """# 测试回答
21
+
22
+ **问题判断**
23
+ 当前最关键的是先拆清楚留存下降来自哪个用户群体。
24
+
25
+ **原因分析**
26
+ - 总量变化可能掩盖渠道和版本差异。
27
+
28
+ **行动建议**
29
+ 1. 今天按渠道和版本拆 D7 留存,并用结果决定是否回滚。
30
+
31
+ **风险提醒**
32
+ 不要在口径不清时直接全量回滚。
33
+ """
34
+
35
+
36
+ class QuestionCountingTests(unittest.TestCase):
37
+ def test_counts_only_confirmation_section(self) -> None:
38
+ text = VALID_OUTPUT + "\n前文的问题是什么?\n"
39
+ self.assertEqual(quality_gate.count_questions(text), 0)
40
+
41
+ def test_counts_multiple_questions_on_one_line(self) -> None:
42
+ text = VALID_OUTPUT + """
43
+
44
+ **需要确认**
45
+ 1. 掉的是新用户吗?集中在哪个版本?
46
+ 2. 埋点改过吗?是否有发布事故?
47
+ """
48
+ self.assertEqual(quality_gate.count_questions(text), 4)
49
+
50
+ def test_ignores_url_query_string(self) -> None:
51
+ text = VALID_OUTPUT + """
52
+
53
+ **需要确认**
54
+ 1. 请确认看板 https://example.com/report?cohort=new 是否采用同一口径。
55
+ """
56
+ self.assertEqual(quality_gate.count_questions(text), 0)
57
+
58
+
59
+ class FileCheckingTests(unittest.TestCase):
60
+ def check(self, text: str) -> tuple[list[str], list[str]]:
61
+ with tempfile.TemporaryDirectory() as temp_dir:
62
+ path = Path(temp_dir) / "sample.md"
63
+ path.write_text(text, encoding="utf-8")
64
+ return quality_gate.check_file(path)
65
+
66
+ def test_valid_output_passes(self) -> None:
67
+ errors, warnings = self.check(VALID_OUTPUT)
68
+ self.assertEqual(errors, [])
69
+ self.assertEqual(warnings, [])
70
+
71
+ def test_hard_source_leak_fails(self) -> None:
72
+ errors, _ = self.check(VALID_OUTPUT + "\n毛泽东认为应该这样做。\n")
73
+ self.assertTrue(any("hard-banned" in error for error in errors))
74
+
75
+ def test_soft_term_warns_without_failing(self) -> None:
76
+ errors, warnings = self.check(VALID_OUTPUT + "\n这是一个经典案例。\n")
77
+ self.assertEqual(errors, [])
78
+ self.assertTrue(any("review-needed" in warning for warning in warnings))
79
+
80
+ def test_more_than_three_confirmation_questions_fails(self) -> None:
81
+ text = VALID_OUTPUT + """
82
+
83
+ **需要确认**
84
+ 1. 指标是什么?
85
+ 2. 持续多久?
86
+ 3. 哪类用户?
87
+ 4. 试过什么?
88
+ """
89
+ errors, _ = self.check(text)
90
+ self.assertIn("asks too many questions", errors)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ unittest.main()
@@ -0,0 +1,358 @@
1
+ # pytest — Advanced Implementation Playbook
2
+
3
+ ## §1 — Production Configuration
4
+
5
+ ```ini
6
+ # pytest.ini
7
+ [pytest]
8
+ testpaths = tests
9
+ python_files = test_*.py
10
+ python_classes = Test*
11
+ python_functions = test_*
12
+ addopts = -v --strict-markers --tb=short -q
13
+ markers =
14
+ slow: marks tests as slow (deselect with '-m "not slow"')
15
+ integration: marks integration tests
16
+ smoke: marks smoke tests
17
+ api: marks API tests
18
+ filterwarnings =
19
+ error
20
+ ignore::DeprecationWarning
21
+ ```
22
+
23
+ ```toml
24
+ # pyproject.toml (alternative)
25
+ [tool.pytest.ini_options]
26
+ testpaths = ["tests"]
27
+ addopts = "-v --strict-markers --tb=short"
28
+ markers = [
29
+ "slow: marks tests as slow",
30
+ "integration: integration tests",
31
+ "smoke: smoke tests",
32
+ ]
33
+
34
+ [tool.coverage.run]
35
+ source = ["src"]
36
+ omit = ["tests/*", "*/migrations/*"]
37
+
38
+ [tool.coverage.report]
39
+ fail_under = 80
40
+ show_missing = true
41
+ ```
42
+
43
+ ## §2 — Fixtures (Scoping, Factories, Teardown)
44
+
45
+ ```python
46
+ # conftest.py — shared fixtures
47
+ import pytest
48
+ from sqlalchemy import create_engine
49
+ from sqlalchemy.orm import Session
50
+
51
+ # Session-scoped: created once per test session
52
+ @pytest.fixture(scope="session")
53
+ def engine():
54
+ engine = create_engine("sqlite:///test.db")
55
+ Base.metadata.create_all(engine)
56
+ yield engine
57
+ Base.metadata.drop_all(engine)
58
+
59
+ # Function-scoped: created per test (default), auto-cleanup
60
+ @pytest.fixture
61
+ def db_session(engine):
62
+ connection = engine.connect()
63
+ transaction = connection.begin()
64
+ session = Session(bind=connection)
65
+ yield session
66
+ session.close()
67
+ transaction.rollback()
68
+ connection.close()
69
+
70
+ # Factory fixture — create multiple instances
71
+ @pytest.fixture
72
+ def user_factory(db_session):
73
+ created = []
74
+ def _create_user(name="Test User", email=None, role="viewer"):
75
+ email = email or f"{name.lower().replace(' ', '.')}@test.com"
76
+ user = User(name=name, email=email, role=role)
77
+ db_session.add(user)
78
+ db_session.commit()
79
+ created.append(user)
80
+ return user
81
+ yield _create_user
82
+ for user in created:
83
+ db_session.delete(user)
84
+ db_session.commit()
85
+
86
+ # Autouse fixture — runs for every test in module
87
+ @pytest.fixture(autouse=True)
88
+ def reset_cache():
89
+ cache.clear()
90
+ yield
91
+ cache.clear()
92
+
93
+ # tmp_path for file operations (built-in)
94
+ def test_writes_output(tmp_path):
95
+ output_file = tmp_path / "result.json"
96
+ generate_report(output_file)
97
+ assert output_file.exists()
98
+ data = json.loads(output_file.read_text())
99
+ assert data["status"] == "complete"
100
+ ```
101
+
102
+ ## §3 — Parameterized Tests
103
+
104
+ ```python
105
+ import pytest
106
+
107
+ # Basic parametrize
108
+ @pytest.mark.parametrize("input,expected", [
109
+ ("hello", "HELLO"),
110
+ ("world", "WORLD"),
111
+ ("", ""),
112
+ ("123", "123"),
113
+ ])
114
+ def test_uppercase(input, expected):
115
+ assert input.upper() == expected
116
+
117
+ # Multiple parameters with IDs
118
+ @pytest.mark.parametrize("email,valid", [
119
+ ("user@test.com", True),
120
+ ("invalid", False),
121
+ ("", False),
122
+ ("user@.com", False),
123
+ ], ids=["valid_email", "no_at_sign", "empty", "missing_domain"])
124
+ def test_validate_email(email, valid):
125
+ assert validate_email(email) == valid
126
+
127
+ # Combine parametrize (cartesian product)
128
+ @pytest.mark.parametrize("browser", ["chrome", "firefox", "edge"])
129
+ @pytest.mark.parametrize("resolution", ["1920x1080", "1366x768", "375x667"])
130
+ def test_responsive_layout(browser, resolution):
131
+ assert render_page(browser, resolution).is_valid()
132
+
133
+ # Indirect parametrize (pass to fixture)
134
+ @pytest.fixture
135
+ def user(request):
136
+ return create_user(role=request.param)
137
+
138
+ @pytest.mark.parametrize("user", ["admin", "editor", "viewer"], indirect=True)
139
+ def test_permissions(user):
140
+ assert user.can_view()
141
+ ```
142
+
143
+ ## §4 — Mocking with pytest-mock
144
+
145
+ ```python
146
+ # pip install pytest-mock
147
+
148
+ def test_send_email(mocker):
149
+ mock_smtp = mocker.patch("myapp.email.smtplib.SMTP")
150
+ send_email("test@example.com", "Hello", "Body")
151
+ mock_smtp.return_value.sendmail.assert_called_once()
152
+
153
+ def test_api_call(mocker):
154
+ mock_get = mocker.patch("myapp.api.requests.get")
155
+ mock_get.return_value.json.return_value = {"id": 1, "name": "Alice"}
156
+ mock_get.return_value.status_code = 200
157
+ user = get_user(1)
158
+ assert user["name"] == "Alice"
159
+ mock_get.assert_called_once_with("https://api.example.com/users/1")
160
+
161
+ def test_database_error(mocker):
162
+ mocker.patch("myapp.db.session.commit", side_effect=IntegrityError("duplicate"))
163
+ with pytest.raises(DuplicateError):
164
+ create_user("Alice", "alice@test.com")
165
+
166
+ # Spy — track calls without replacing
167
+ def test_logging(mocker):
168
+ spy = mocker.spy(logger, "info")
169
+ process_order(order)
170
+ spy.assert_called_with("Order processed: %s", order.id)
171
+
172
+ # Mock environment variables
173
+ def test_config(monkeypatch):
174
+ monkeypatch.setenv("API_KEY", "test-key-123")
175
+ monkeypatch.setenv("DEBUG", "true")
176
+ config = load_config()
177
+ assert config.api_key == "test-key-123"
178
+ assert config.debug is True
179
+ ```
180
+
181
+ ## §5 — Async Testing
182
+
183
+ ```python
184
+ # pip install pytest-asyncio
185
+
186
+ import pytest
187
+
188
+ @pytest.mark.asyncio
189
+ async def test_async_fetch():
190
+ result = await fetch_data("https://api.example.com/data")
191
+ assert result["status"] == "ok"
192
+
193
+ @pytest.mark.asyncio
194
+ async def test_async_exception():
195
+ with pytest.raises(ConnectionError):
196
+ await fetch_data("https://invalid.example.com")
197
+
198
+ # Async fixtures
199
+ @pytest.fixture
200
+ async def async_client():
201
+ async with AsyncClient(app=app, base_url="http://test") as client:
202
+ yield client
203
+
204
+ @pytest.mark.asyncio
205
+ async def test_api_endpoint(async_client):
206
+ response = await async_client.get("/api/users")
207
+ assert response.status_code == 200
208
+ assert len(response.json()) > 0
209
+ ```
210
+
211
+ ## §6 — Testing Exceptions & Warnings
212
+
213
+ ```python
214
+ # Exception testing
215
+ def test_division_by_zero():
216
+ with pytest.raises(ZeroDivisionError):
217
+ 1 / 0
218
+
219
+ def test_error_message():
220
+ with pytest.raises(ValueError, match=r".*invalid email.*"):
221
+ validate_email("not-an-email")
222
+
223
+ def test_raises_with_info():
224
+ with pytest.raises(PermissionError) as exc_info:
225
+ delete_file("/protected/file.txt")
226
+ assert "permission denied" in str(exc_info.value).lower()
227
+ assert exc_info.value.errno == 13
228
+
229
+ # Warning testing
230
+ def test_deprecation_warning():
231
+ with pytest.warns(DeprecationWarning, match="use new_func"):
232
+ old_func()
233
+ ```
234
+
235
+ ## §7 — Markers & Custom Plugins
236
+
237
+ ```python
238
+ # Custom marker usage
239
+ @pytest.mark.slow
240
+ def test_full_data_processing():
241
+ result = process_large_dataset()
242
+ assert result.row_count > 1_000_000
243
+
244
+ @pytest.mark.integration
245
+ def test_database_connection():
246
+ assert db.is_connected()
247
+
248
+ # Run by marker: pytest -m "not slow"
249
+ # Run by marker: pytest -m "smoke and not integration"
250
+
251
+ # Custom plugin — conftest.py
252
+ def pytest_collection_modifyitems(config, items):
253
+ """Auto-mark tests in integration/ directory"""
254
+ for item in items:
255
+ if "integration" in str(item.fspath):
256
+ item.add_marker(pytest.mark.integration)
257
+
258
+ # Custom report header
259
+ def pytest_report_header(config):
260
+ return f"Environment: {os.getenv('ENV', 'local')}"
261
+ ```
262
+
263
+ ## §8 — Class-Based Test Organization
264
+
265
+ ```python
266
+ class TestUserService:
267
+ @pytest.fixture(autouse=True)
268
+ def setup(self, db_session, user_factory):
269
+ self.db = db_session
270
+ self.create_user = user_factory
271
+ self.service = UserService(db_session)
272
+
273
+ def test_create_user(self):
274
+ user = self.service.create("Alice", "alice@test.com")
275
+ assert user.id is not None
276
+ assert user.name == "Alice"
277
+
278
+ def test_find_by_email(self):
279
+ self.create_user(name="Bob", email="bob@test.com")
280
+ user = self.service.find_by_email("bob@test.com")
281
+ assert user.name == "Bob"
282
+
283
+ def test_delete_nonexistent(self):
284
+ with pytest.raises(NotFoundError):
285
+ self.service.delete(999)
286
+
287
+ class TestPermissions:
288
+ """Nested class for permission-related tests"""
289
+ def test_admin_can_delete(self, user_factory):
290
+ admin = user_factory(role="admin")
291
+ assert admin.can_delete()
292
+
293
+ def test_viewer_cannot_delete(self, user_factory):
294
+ viewer = user_factory(role="viewer")
295
+ assert not viewer.can_delete()
296
+ ```
297
+
298
+ ## §9 — CI/CD Integration
299
+
300
+ ```yaml
301
+ # GitHub Actions
302
+ name: Python Tests
303
+ on: [push, pull_request]
304
+ jobs:
305
+ test:
306
+ runs-on: ubuntu-latest
307
+ strategy:
308
+ matrix:
309
+ python-version: ['3.10', '3.11', '3.12']
310
+ steps:
311
+ - uses: actions/checkout@v4
312
+ - uses: actions/setup-python@v5
313
+ with: { python-version: '${{ matrix.python-version }}' }
314
+ - name: Install deps
315
+ run: pip install -r requirements-test.txt
316
+ - name: Run tests
317
+ run: pytest --cov=src --cov-report=xml --junitxml=results.xml -v
318
+ - name: Upload coverage
319
+ uses: codecov/codecov-action@v4
320
+ with: { files: coverage.xml }
321
+ - name: Upload results
322
+ uses: actions/upload-artifact@v4
323
+ if: always()
324
+ with: { name: test-results-${{ matrix.python-version }}, path: results.xml }
325
+ ```
326
+
327
+ ## §10 — Debugging Quick-Reference
328
+
329
+ | Problem | Cause | Fix |
330
+ |---------|-------|-----|
331
+ | Fixture not found | Wrong scope or missing conftest.py | Check conftest.py location, fixture name |
332
+ | `ScopeMismatch` | Function fixture depends on session fixture | Match scope: session → module → function |
333
+ | Tests interfere | Shared mutable state | Use function-scoped fixtures, `autouse` cleanup |
334
+ | Parametrize fails | Wrong number of params | Ensure tuple count matches parameter names |
335
+ | Slow collection | Too many test paths | Set `testpaths` in pytest.ini |
336
+ | Async test hangs | Missing `@pytest.mark.asyncio` | Add marker or set `asyncio_mode = "auto"` |
337
+ | Coverage wrong | Source path mismatch | Set `source` in `[tool.coverage.run]` |
338
+ | Import errors | Missing `__init__.py` or bad path | Add `__init__.py` or use `src` layout with `--import-mode=importlib` |
339
+ | Monkeypatch not reverting | Using at module scope | Only use in function-scoped fixtures |
340
+ | Marker warnings | Marker not registered | Add to `markers` in pytest.ini |
341
+
342
+ ## §11 — Best Practices Checklist
343
+
344
+ - ✅ Use fixtures over setup/teardown methods
345
+ - ✅ Use `conftest.py` for shared fixtures (auto-discovered)
346
+ - ✅ Use `tmp_path` for file operations (built-in, auto-cleanup)
347
+ - ✅ Use `monkeypatch` for env vars and attribute patching
348
+ - ✅ Use `pytest-mock` (mocker fixture) over `unittest.mock`
349
+ - ✅ Use `@pytest.mark.parametrize` for data-driven tests
350
+ - ✅ Register all custom markers in `pytest.ini`
351
+ - ✅ Use `--strict-markers` to catch typos in marker names
352
+ - ✅ Use `pytest-cov` for coverage with `--cov-fail-under=80`
353
+ - ✅ Use `pytest-xdist` for parallel execution: `pytest -n auto`
354
+ - ✅ Use `--tb=short` for concise tracebacks in CI
355
+ - ✅ Structure: `tests/unit/`, `tests/integration/`, `conftest.py`
356
+ - ✅ Name files `test_*.py` and functions `test_*`
357
+ - ✅ Use factory fixtures for creating test objects
358
+ - ✅ Use `pytest.raises(match=...)` for precise error checking