@huicai/cli 2026.814.0 → 2026.1019.0

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 (485) hide show
  1. package/LICENSE +391 -32
  2. package/README.md +137 -8
  3. package/bin/huicai.js +146 -73
  4. package/package.json +27 -8
  5. package/skills/ab-testing/trigger-cases.json +30 -0
  6. package/skills/academic-writing/trigger-cases.json +30 -0
  7. package/skills/accounting-ops/trigger-cases.json +30 -0
  8. package/skills/ai-agent/trigger-cases.json +30 -0
  9. package/skills/ai-aiops/trigger-cases.json +30 -0
  10. package/skills/ai-coding/trigger-cases.json +30 -0
  11. package/skills/ai-compliance/trigger-cases.json +30 -0
  12. package/skills/ai-eval/trigger-cases.json +30 -0
  13. package/skills/ai-llm-app/trigger-cases.json +30 -0
  14. package/skills/ai-mlops/trigger-cases.json +30 -0
  15. package/skills/ai-multimodal/trigger-cases.json +30 -0
  16. package/skills/ai-prompt/trigger-cases.json +30 -0
  17. package/skills/ai-rag/trigger-cases.json +30 -0
  18. package/skills/anomaly-detection/trigger-cases.json +30 -0
  19. package/skills/api-design/trigger-cases.json +31 -0
  20. package/skills/api-integration/trigger-cases.json +31 -0
  21. package/skills/arch-design/trigger-cases.json +12 -0
  22. package/skills/arch-diagram/SKILL.md +118 -0
  23. package/skills/arch-diagram/trigger-cases.json +30 -0
  24. package/skills/arch-distributed/trigger-cases.json +30 -0
  25. package/skills/arch-multitenant/trigger-cases.json +30 -0
  26. package/skills/audio-brief/SKILL.md +65 -0
  27. package/skills/audio-brief/trigger-cases.json +30 -0
  28. package/skills/audit-assist/trigger-cases.json +30 -0
  29. package/skills/audit-chain/trigger-cases.json +31 -0
  30. package/skills/banking-credit/trigger-cases.json +30 -0
  31. package/skills/brand-design/trigger-cases.json +30 -0
  32. package/skills/business-plan/trigger-cases.json +30 -0
  33. package/skills/capital-ops/trigger-cases.json +30 -0
  34. package/skills/cicd-pipeline/trigger-cases.json +31 -0
  35. package/skills/code-archaeology/SKILL.md +55 -0
  36. package/skills/code-archaeology/trigger-cases.json +9 -0
  37. package/skills/code-quality/SKILL.md +138 -0
  38. package/skills/code-quality/references/languages.md +107 -0
  39. package/skills/code-quality/scripts/__pycache__/code_battery.cpython-312.pyc +0 -0
  40. package/skills/code-quality/scripts/__pycache__/code_check.cpython-312.pyc +0 -0
  41. package/skills/code-quality/scripts/__pycache__/code_check.cpython-313.pyc +0 -0
  42. package/skills/code-quality/scripts/__pycache__/code_csharp.cpython-312.pyc +0 -0
  43. package/skills/code-quality/scripts/__pycache__/code_csharp.cpython-313.pyc +0 -0
  44. package/skills/code-quality/scripts/__pycache__/code_fix.cpython-312.pyc +0 -0
  45. package/skills/code-quality/scripts/__pycache__/code_fix.cpython-313.pyc +0 -0
  46. package/skills/code-quality/scripts/__pycache__/code_java.cpython-312.pyc +0 -0
  47. package/skills/code-quality/scripts/__pycache__/code_java.cpython-313.pyc +0 -0
  48. package/skills/code-quality/scripts/__pycache__/code_jsts.cpython-312.pyc +0 -0
  49. package/skills/code-quality/scripts/__pycache__/code_jsts.cpython-313.pyc +0 -0
  50. package/skills/code-quality/scripts/__pycache__/code_lens.cpython-312.pyc +0 -0
  51. package/skills/code-quality/scripts/__pycache__/code_lens.cpython-313.pyc +0 -0
  52. package/skills/code-quality/scripts/__pycache__/code_python.cpython-312.pyc +0 -0
  53. package/skills/code-quality/scripts/__pycache__/code_python.cpython-313.pyc +0 -0
  54. package/skills/code-quality/scripts/__pycache__/code_testplan.cpython-312.pyc +0 -0
  55. package/skills/code-quality/scripts/code_battery.py +1072 -0
  56. package/skills/code-quality/scripts/code_check.py +1269 -0
  57. package/skills/code-quality/scripts/code_csharp.py +371 -0
  58. package/skills/code-quality/scripts/code_fix.py +201 -0
  59. package/skills/code-quality/scripts/code_java.py +695 -0
  60. package/skills/code-quality/scripts/code_jsts.py +313 -0
  61. package/skills/code-quality/scripts/code_lens.py +1079 -0
  62. package/skills/code-quality/scripts/code_python.py +536 -0
  63. package/skills/code-quality/scripts/code_testplan.py +239 -0
  64. package/skills/code-quality/trigger-cases.json +45 -0
  65. package/skills/code-review/SKILL.md +24 -0
  66. package/skills/code-review/trigger-cases.json +35 -0
  67. package/skills/commit-and-pr/trigger-cases.json +31 -0
  68. package/skills/content-creation/trigger-cases.json +30 -0
  69. package/skills/corporate-governance/trigger-cases.json +30 -0
  70. package/skills/crawl-chain/trigger-cases.json +31 -0
  71. package/skills/customer-service/trigger-cases.json +30 -0
  72. package/skills/data-analysis/trigger-cases.json +30 -0
  73. package/skills/data-chain/SKILL.md +1 -1
  74. package/skills/data-chain/trigger-cases.json +31 -0
  75. package/skills/data-governance/trigger-cases.json +30 -0
  76. package/skills/data-report-pro/trigger-cases.json +30 -0
  77. package/skills/data-streaming/trigger-cases.json +30 -0
  78. package/skills/data-wrangling/SKILL.md +1 -1
  79. package/skills/data-wrangling/trigger-cases.json +30 -0
  80. package/skills/db-es/trigger-cases.json +30 -0
  81. package/skills/db-migration/trigger-cases.json +30 -0
  82. package/skills/db-mysql/trigger-cases.json +30 -0
  83. package/skills/db-postgres/trigger-cases.json +30 -0
  84. package/skills/db-redis/trigger-cases.json +30 -0
  85. package/skills/debug-root-cause/trigger-cases.json +30 -0
  86. package/skills/dependency-audit/SKILL.md +56 -0
  87. package/skills/dependency-audit/trigger-cases.json +7 -0
  88. package/skills/dependency-upgrade/trigger-cases.json +31 -0
  89. package/skills/dev-cpp/trigger-cases.json +30 -0
  90. package/skills/dev-cross-platform/trigger-cases.json +30 -0
  91. package/skills/dev-csharp/SKILL.md +23 -0
  92. package/skills/dev-csharp/trigger-cases.json +30 -0
  93. package/skills/dev-frontend/SKILL.md +19 -0
  94. package/skills/dev-frontend/trigger-cases.json +30 -0
  95. package/skills/dev-go/trigger-cases.json +30 -0
  96. package/skills/dev-harmony/trigger-cases.json +30 -0
  97. package/skills/dev-java/SKILL.md +20 -0
  98. package/skills/dev-java/trigger-cases.json +30 -0
  99. package/skills/dev-mobile/trigger-cases.json +30 -0
  100. package/skills/dev-node/trigger-cases.json +30 -0
  101. package/skills/dev-php/trigger-cases.json +30 -0
  102. package/skills/dev-python/SKILL.md +21 -0
  103. package/skills/dev-python/trigger-cases.json +30 -0
  104. package/skills/dev-rust/trigger-cases.json +30 -0
  105. package/skills/dev-toolchain/trigger-cases.json +31 -0
  106. package/skills/doc-chain/trigger-cases.json +31 -0
  107. package/skills/doc-compare/trigger-cases.json +30 -0
  108. package/skills/docx-author/SKILL.md +127 -25
  109. package/skills/docx-author/scripts/__pycache__/docx_battery.cpython-312.pyc +0 -0
  110. package/skills/docx-author/scripts/__pycache__/docx_build.cpython-312.pyc +0 -0
  111. package/skills/docx-author/scripts/__pycache__/docx_check.cpython-312.pyc +0 -0
  112. package/skills/docx-author/scripts/__pycache__/docx_layout.cpython-312.pyc +0 -0
  113. package/skills/docx-author/scripts/__pycache__/zh_prose.cpython-312.pyc +0 -0
  114. package/skills/docx-author/scripts/docx_battery.py +586 -0
  115. package/skills/docx-author/scripts/docx_build.py +995 -0
  116. package/skills/docx-author/scripts/docx_check.py +1306 -0
  117. package/skills/docx-author/scripts/docx_doc.py +14 -154
  118. package/skills/docx-author/scripts/docx_layout.py +490 -0
  119. package/skills/docx-author/scripts/zh_prose.py +639 -0
  120. package/skills/docx-author/trigger-cases.json +58 -0
  121. package/skills/echo-brief/SKILL.md +84 -0
  122. package/skills/echo-brief/references/echo-format.md +68 -0
  123. package/skills/echo-brief/trigger-cases.json +40 -0
  124. package/skills/ecommerce-ops/trigger-cases.json +30 -0
  125. package/skills/education-training/trigger-cases.json +30 -0
  126. package/skills/ehs-safety/trigger-cases.json +30 -0
  127. package/skills/email-writing/trigger-cases.json +30 -0
  128. package/skills/eng-ai/trigger-cases.json +30 -0
  129. package/skills/eng-architecture/trigger-cases.json +31 -0
  130. package/skills/eng-automation/trigger-cases.json +31 -0
  131. package/skills/eng-collaboration/trigger-cases.json +30 -0
  132. package/skills/eng-complex-env/trigger-cases.json +30 -0
  133. package/skills/eng-data-platform/trigger-cases.json +31 -0
  134. package/skills/eng-database/trigger-cases.json +31 -0
  135. package/skills/eng-develop/trigger-cases.json +30 -0
  136. package/skills/eng-docs/SKILL.md +24 -0
  137. package/skills/eng-docs/trigger-cases.json +30 -0
  138. package/skills/eng-efficiency/trigger-cases.json +30 -0
  139. package/skills/eng-infra/trigger-cases.json +30 -0
  140. package/skills/eng-integration/trigger-cases.json +31 -0
  141. package/skills/eng-legacy/trigger-cases.json +31 -0
  142. package/skills/eng-observability/trigger-cases.json +31 -0
  143. package/skills/eng-ops/trigger-cases.json +31 -0
  144. package/skills/eng-performance/trigger-cases.json +31 -0
  145. package/skills/eng-planning/trigger-cases.json +31 -0
  146. package/skills/eng-quality/SKILL.md +8 -0
  147. package/skills/eng-quality/trigger-cases.json +31 -0
  148. package/skills/eng-release/trigger-cases.json +31 -0
  149. package/skills/eng-reliability/trigger-cases.json +31 -0
  150. package/skills/eng-requirements/trigger-cases.json +12 -0
  151. package/skills/eng-research/trigger-cases.json +30 -0
  152. package/skills/eng-security/trigger-cases.json +31 -0
  153. package/skills/eng-ux/trigger-cases.json +30 -0
  154. package/skills/engineering-chain/trigger-cases.json +30 -0
  155. package/skills/env-offline/trigger-cases.json +31 -0
  156. package/skills/env-private-deploy/trigger-cases.json +31 -0
  157. package/skills/env-xinchuang/trigger-cases.json +31 -0
  158. package/skills/equipment-tpm/trigger-cases.json +30 -0
  159. package/skills/esg-report/trigger-cases.json +30 -0
  160. package/skills/event-management/trigger-cases.json +30 -0
  161. package/skills/excel-analysis-chain/SKILL.md +17 -4
  162. package/skills/excel-analysis-chain/trigger-cases.json +31 -0
  163. package/skills/explain-codebase/trigger-cases.json +11 -0
  164. package/skills/file-ingest/trigger-cases.json +30 -0
  165. package/skills/finance-analysis/trigger-cases.json +31 -0
  166. package/skills/finance-chain/trigger-cases.json +31 -0
  167. package/skills/financial-modeling/trigger-cases.json +31 -0
  168. package/skills/find-simplifications/SKILL.md +43 -0
  169. package/skills/find-simplifications/trigger-cases.json +23 -0
  170. package/skills/foreign-trade/trigger-cases.json +30 -0
  171. package/skills/funnel-cohort/trigger-cases.json +30 -0
  172. package/skills/gov-document/trigger-cases.json +30 -0
  173. package/skills/gui-change-evidence/SKILL.md +47 -0
  174. package/skills/gui-change-evidence/trigger-cases.json +23 -0
  175. package/skills/hr-chain/trigger-cases.json +31 -0
  176. package/skills/hr-operations/trigger-cases.json +30 -0
  177. package/skills/info-disclosure/trigger-cases.json +31 -0
  178. package/skills/infra-gateway/trigger-cases.json +31 -0
  179. package/skills/infra-k8s/trigger-cases.json +31 -0
  180. package/skills/infra-mq/trigger-cases.json +30 -0
  181. package/skills/insurance-advisory/trigger-cases.json +30 -0
  182. package/skills/internal-audit/trigger-cases.json +30 -0
  183. package/skills/ip-management/trigger-cases.json +31 -0
  184. package/skills/ipo-listing/trigger-cases.json +31 -0
  185. package/skills/it-audit/trigger-cases.json +31 -0
  186. package/skills/itsm-ops/trigger-cases.json +31 -0
  187. package/skills/knowledge-sop/trigger-cases.json +31 -0
  188. package/skills/legal-chain/trigger-cases.json +31 -0
  189. package/skills/legal-contract/trigger-cases.json +30 -0
  190. package/skills/litigation-support/trigger-cases.json +31 -0
  191. package/skills/logistics-warehouse/trigger-cases.json +30 -0
  192. package/skills/long-doc-analysis/trigger-cases.json +31 -0
  193. package/skills/management-accounting/trigger-cases.json +30 -0
  194. package/skills/manufacturing-chain/trigger-cases.json +31 -0
  195. package/skills/manufacturing-quality/trigger-cases.json +30 -0
  196. package/skills/marketing-analytics/trigger-cases.json +31 -0
  197. package/skills/marketing-chain/trigger-cases.json +31 -0
  198. package/skills/meeting-minutes/trigger-cases.json +30 -0
  199. package/skills/metrics-system/trigger-cases.json +31 -0
  200. package/skills/negotiation-prep/trigger-cases.json +30 -0
  201. package/skills/obs-prometheus/trigger-cases.json +31 -0
  202. package/skills/office-automation/trigger-cases.json +31 -0
  203. package/skills/okr-review/trigger-cases.json +30 -0
  204. package/skills/ops-fleet/trigger-cases.json +31 -0
  205. package/skills/ops-linux/trigger-cases.json +31 -0
  206. package/skills/optimization-solver/trigger-cases.json +30 -0
  207. package/skills/patent-disclosure/SKILL.md +105 -0
  208. package/skills/patent-disclosure/references/figures.md +110 -0
  209. package/skills/patent-disclosure/references/outline.md +162 -0
  210. package/skills/patent-disclosure/scripts/patent_docx.py +230 -0
  211. package/skills/patent-disclosure/trigger-cases.json +31 -0
  212. package/skills/patent-drafting/SKILL.md +108 -0
  213. package/skills/patent-drafting/trigger-cases.json +31 -0
  214. package/skills/patent-portfolio/SKILL.md +81 -0
  215. package/skills/patent-portfolio/trigger-cases.json +31 -0
  216. package/skills/pdf-toolkit/trigger-cases.json +31 -0
  217. package/skills/perf-optimize/trigger-cases.json +31 -0
  218. package/skills/pivot-crosstab/trigger-cases.json +30 -0
  219. package/skills/policy-analysis/trigger-cases.json +31 -0
  220. package/skills/portfolio-analysis/trigger-cases.json +31 -0
  221. package/skills/ppt-chain/SKILL.md +14 -6
  222. package/skills/ppt-chain/trigger-cases.json +31 -0
  223. package/skills/pptx-author/SKILL.md +86 -33
  224. package/skills/pptx-author/scripts/__pycache__/pptx_check.cpython-312.pyc +0 -0
  225. package/skills/pptx-author/scripts/__pycache__/pptx_deck.cpython-312.pyc +0 -0
  226. package/skills/pptx-author/scripts/__pycache__/pptx_layout.cpython-312.pyc +0 -0
  227. package/skills/pptx-author/scripts/pptx_battery.py +178 -0
  228. package/skills/pptx-author/scripts/pptx_build.py +119 -0
  229. package/skills/pptx-author/scripts/pptx_check.py +1182 -0
  230. package/skills/pptx-author/scripts/pptx_deck.py +736 -145
  231. package/skills/pptx-author/scripts/pptx_layout.py +384 -0
  232. package/skills/pptx-author/trigger-cases.json +15 -0
  233. package/skills/pr-comms/trigger-cases.json +30 -0
  234. package/skills/procurement-supply/trigger-cases.json +31 -0
  235. package/skills/product-design/trigger-cases.json +31 -0
  236. package/skills/production-planning/trigger-cases.json +31 -0
  237. package/skills/project-management/trigger-cases.json +31 -0
  238. package/skills/project-proposal/trigger-cases.json +31 -0
  239. package/skills/prose-standard/SKILL.md +53 -0
  240. package/skills/prose-standard/trigger-cases.json +23 -0
  241. package/skills/qa-api-automation/trigger-cases.json +31 -0
  242. package/skills/qa-mobile-automation/trigger-cases.json +31 -0
  243. package/skills/qa-ui-automation/trigger-cases.json +31 -0
  244. package/skills/quant-analysis/trigger-cases.json +31 -0
  245. package/skills/rag-triage/SKILL.md +64 -0
  246. package/skills/rag-triage/evals.json +57 -0
  247. package/skills/rag-triage/references/triage-playbook.md +75 -0
  248. package/skills/rag-triage/trigger-cases.json +31 -0
  249. package/skills/real-estate-property/trigger-cases.json +30 -0
  250. package/skills/refactor-safely/SKILL.md +1 -1
  251. package/skills/refactor-safely/trigger-cases.json +30 -0
  252. package/skills/regression-forecast/trigger-cases.json +31 -0
  253. package/skills/release-checklist/trigger-cases.json +31 -0
  254. package/skills/report-craft/SKILL.md +76 -0
  255. package/skills/report-craft/references/feasibility.md +118 -0
  256. package/skills/report-craft/references/review-checklist.md +51 -0
  257. package/skills/report-craft/references/visuals.md +114 -0
  258. package/skills/report-craft/references/writing.md +80 -0
  259. package/skills/report-craft/scripts/__pycache__/chart_render.cpython-312.pyc +0 -0
  260. package/skills/report-craft/scripts/__pycache__/fin_model.cpython-312.pyc +0 -0
  261. package/skills/report-craft/scripts/__pycache__/project_facts.cpython-312.pyc +0 -0
  262. package/skills/report-craft/scripts/chart_render.py +1222 -0
  263. package/skills/report-craft/scripts/fin_model.py +602 -0
  264. package/skills/report-craft/scripts/project_facts.py +580 -0
  265. package/skills/report-craft/scripts/report_battery.py +349 -0
  266. package/skills/report-craft/trigger-cases.json +15 -0
  267. package/skills/retail-store-ops/trigger-cases.json +30 -0
  268. package/skills/risk-compliance/trigger-cases.json +31 -0
  269. package/skills/scope-creep/SKILL.md +45 -0
  270. package/skills/scope-creep/trigger-cases.json +7 -0
  271. package/skills/sec-auth/trigger-cases.json +31 -0
  272. package/skills/sec-compliance/trigger-cases.json +31 -0
  273. package/skills/securities-chain/trigger-cases.json +31 -0
  274. package/skills/securities-research/trigger-cases.json +31 -0
  275. package/skills/security-audit/trigger-cases.json +31 -0
  276. package/skills/slides-html/SKILL.md +25 -10
  277. package/skills/slides-html/trigger-cases.json +11 -0
  278. package/skills/sql-analysis/SKILL.md +110 -0
  279. package/skills/sql-analysis/trigger-cases.json +34 -0
  280. package/skills/sql-tuning/trigger-cases.json +31 -0
  281. package/skills/statistical-testing/trigger-cases.json +31 -0
  282. package/skills/strategy-planning/trigger-cases.json +30 -0
  283. package/skills/supply-chain/trigger-cases.json +31 -0
  284. package/skills/tax-compliance/trigger-cases.json +30 -0
  285. package/skills/tech-doc/SKILL.md +262 -0
  286. package/skills/tech-doc/scripts/__pycache__/archive_battery.cpython-312.pyc +0 -0
  287. package/skills/tech-doc/scripts/__pycache__/archive_build.cpython-312.pyc +0 -0
  288. package/skills/tech-doc/scripts/__pycache__/archive_check.cpython-312.pyc +0 -0
  289. package/skills/tech-doc/scripts/__pycache__/archive_spec.cpython-312.pyc +0 -0
  290. package/skills/tech-doc/scripts/__pycache__/doc_templates.cpython-312.pyc +0 -0
  291. package/skills/tech-doc/scripts/archive_battery.py +860 -0
  292. package/skills/tech-doc/scripts/archive_build.py +599 -0
  293. package/skills/tech-doc/scripts/archive_check.py +1307 -0
  294. package/skills/tech-doc/scripts/archive_spec.py +499 -0
  295. package/skills/tech-doc/scripts/doc_templates.py +624 -0
  296. package/skills/tech-doc/trigger-cases.json +57 -0
  297. package/skills/tech-research/trigger-cases.json +31 -0
  298. package/skills/tender-bidding/trigger-cases.json +31 -0
  299. package/skills/translation-pro/trigger-cases.json +30 -0
  300. package/skills/treasury-mgmt/trigger-cases.json +31 -0
  301. package/skills/trim-cot-leakage/SKILL.md +58 -0
  302. package/skills/trim-cot-leakage/trigger-cases.json +23 -0
  303. package/skills/web-craft/SKILL.md +103 -0
  304. package/skills/web-craft/references/craft-checklist.md +75 -0
  305. package/skills/web-craft/references/design-direction.md +122 -0
  306. package/skills/web-craft/references/h5-mobile.md +100 -0
  307. package/skills/web-craft/references/motion.md +136 -0
  308. package/skills/web-craft/references/recipes.md +74 -0
  309. package/skills/web-craft/templates/wedding-invitation.html +1180 -0
  310. package/skills/web-craft/trigger-cases.json +14 -0
  311. package/skills/web-scraping/trigger-cases.json +34 -0
  312. package/skills/web-search/SKILL.md +11 -10
  313. package/skills/web-search/trigger-cases.json +31 -0
  314. package/skills/weekly-report/SKILL.md +134 -0
  315. package/skills/weekly-report/trigger-cases.json +11 -0
  316. package/skills/write-tests/trigger-cases.json +31 -0
  317. package/skills/xlsx-analysis/SKILL.md +143 -77
  318. package/skills/xlsx-analysis/scripts/__pycache__/data_profile.cpython-312.pyc +0 -0
  319. package/skills/xlsx-analysis/scripts/__pycache__/xlsx_build.cpython-312.pyc +0 -0
  320. package/skills/xlsx-analysis/scripts/__pycache__/xlsx_check.cpython-312.pyc +0 -0
  321. package/skills/xlsx-analysis/scripts/__pycache__/xlsx_layout.cpython-312.pyc +0 -0
  322. package/skills/xlsx-analysis/scripts/__pycache__/xlsx_toolkit.cpython-312.pyc +0 -0
  323. package/skills/xlsx-analysis/scripts/data_profile.py +391 -0
  324. package/skills/xlsx-analysis/scripts/xlsx_battery.py +389 -0
  325. package/skills/xlsx-analysis/scripts/xlsx_build.py +522 -0
  326. package/skills/xlsx-analysis/scripts/xlsx_check.py +924 -0
  327. package/skills/xlsx-analysis/scripts/xlsx_layout.py +235 -0
  328. package/skills/xlsx-analysis/scripts/xlsx_style.py +28 -66
  329. package/skills/xlsx-analysis/scripts/xlsx_toolkit.py +61 -5
  330. package/skills/xlsx-analysis/trigger-cases.json +59 -0
  331. package/squads/hc-defense.md +47 -0
  332. package/squads/hc-rdarchive.md +110 -0
  333. package/squads/hc-redteam.md +50 -0
  334. package/squads/hc-weekly.md +125 -0
  335. package/src/account.js +1 -1
  336. package/src/acp.js +1 -0
  337. package/src/advisor.js +1 -1
  338. package/src/agent.js +1 -1
  339. package/src/api.js +1 -1
  340. package/src/archaeology.js +1 -0
  341. package/src/ask-ui.js +1 -0
  342. package/src/assets-import.js +1 -1
  343. package/src/autofix.js +1 -1
  344. package/src/automation-tools.js +1 -1
  345. package/src/automation.js +1 -1
  346. package/src/autorun.js +1 -1
  347. package/src/banner.js +1 -1
  348. package/src/capability-probe.js +1 -0
  349. package/src/checkpoints.js +1 -1
  350. package/src/code-tools.js +1 -0
  351. package/src/codeindex.js +1 -1
  352. package/src/codemode.js +1 -0
  353. package/src/command-bridge.js +1 -0
  354. package/src/command-ui.js +1 -0
  355. package/src/commands.js +1 -1
  356. package/src/compaction.js +1 -1
  357. package/src/computer.js +1 -1
  358. package/src/config.js +1 -1
  359. package/src/consult.js +1 -1
  360. package/src/context-report.js +1 -1
  361. package/src/context-window.js +1 -0
  362. package/src/context.js +1 -1
  363. package/src/crew.js +1 -0
  364. package/src/crush.js +1 -0
  365. package/src/daemon.js +1 -1
  366. package/src/decisions.js +1 -1
  367. package/src/depaudit.js +1 -0
  368. package/src/device.js +1 -1
  369. package/src/diagram-draw.js +1 -0
  370. package/src/diagram-layouts.js +1 -0
  371. package/src/diagram-raster.js +1 -0
  372. package/src/diagram.js +1 -0
  373. package/src/dictation.js +1 -0
  374. package/src/diffview.js +1 -1
  375. package/src/doctor.js +1 -1
  376. package/src/docx-tools.js +1 -0
  377. package/src/errkind.js +1 -1
  378. package/src/evidence.js +1 -0
  379. package/src/executors.js +1 -1
  380. package/src/expert-catalog.js +1 -1
  381. package/src/experts-store.js +1 -1
  382. package/src/experts.js +1 -1
  383. package/src/external-agents.js +1 -0
  384. package/src/forks.js +1 -1
  385. package/src/fs-observe.js +1 -0
  386. package/src/gitflow.js +1 -1
  387. package/src/goalround.js +1 -0
  388. package/src/hooks.js +1 -1
  389. package/src/input.js +1 -1
  390. package/src/insights.js +1 -1
  391. package/src/jobs.js +1 -0
  392. package/src/kbsync.js +1 -1
  393. package/src/lsp.js +1 -0
  394. package/src/mcp-apps.js +1 -0
  395. package/src/mcp.js +1 -1
  396. package/src/memory.js +1 -1
  397. package/src/notify.js +1 -1
  398. package/src/permissions.js +1 -1
  399. package/src/plan-limits.js +1 -0
  400. package/src/policy.js +1 -0
  401. package/src/pptx-tools.js +1 -0
  402. package/src/presence.js +1 -1
  403. package/src/privatedir.js +1 -0
  404. package/src/pyruntime.js +1 -1
  405. package/src/radar.js +1 -0
  406. package/src/render.js +1 -1
  407. package/src/repl.js +1 -1
  408. package/src/report-tools.js +1 -0
  409. package/src/routing.js +1 -0
  410. package/src/sandbox-probe.js +1 -0
  411. package/src/sandbox-win-launcher.mjs +156 -0
  412. package/src/sandbox.js +1 -0
  413. package/src/scan.js +1 -1
  414. package/src/schedule.js +1 -1
  415. package/src/scopecreep.js +1 -0
  416. package/src/sdk.js +1 -0
  417. package/src/selection.js +1 -0
  418. package/src/session-events.js +1 -0
  419. package/src/session.js +1 -1
  420. package/src/sessionindex.js +1 -0
  421. package/src/sessionlog.js +1 -0
  422. package/src/skill-catalog.js +1 -0
  423. package/src/skill-lint.js +1 -0
  424. package/src/skill-optimize.js +1 -0
  425. package/src/skill-scan.js +1 -0
  426. package/src/skills.js +1 -1
  427. package/src/spill.js +1 -0
  428. package/src/squad.js +1 -1
  429. package/src/subagent.js +1 -1
  430. package/src/surface-state.js +1 -0
  431. package/src/tapd.js +1 -0
  432. package/src/terminal-select.js +1 -0
  433. package/src/terminal.js +1 -0
  434. package/src/test-runner.js +1 -0
  435. package/src/todo.js +1 -1
  436. package/src/toolpipe.js +1 -0
  437. package/src/tools.js +1 -1
  438. package/src/toon.js +1 -0
  439. package/src/transcript.js +1 -1
  440. package/src/tui/app.js +1 -1
  441. package/src/tui/command-menu.js +1 -0
  442. package/src/tui/progress.js +1 -0
  443. package/src/tui/scroll.js +1 -0
  444. package/src/tui/welcome.js +1 -0
  445. package/src/tui/workbench.js +1 -0
  446. package/src/ui-spec.js +1 -0
  447. package/src/uitree.js +1 -0
  448. package/src/uninstall.js +1 -1
  449. package/src/visual.js +1 -1
  450. package/src/voice-form.js +1 -0
  451. package/src/voice.js +1 -0
  452. package/src/watch.js +1 -1
  453. package/src/web-audit.js +1 -0
  454. package/src/web-cdp.js +1 -0
  455. package/src/web-inspect.js +1 -0
  456. package/src/websearch.js +1 -0
  457. package/src/weekly.js +1 -0
  458. package/src/workflow.js +1 -0
  459. package/src/worklog.js +1 -0
  460. package/src/xlsx-tools.js +1 -0
  461. package/ui/command-picker.js +79 -0
  462. package/skills/ab-testing/scripts/__pycache__/ab_test.cpython-312.pyc +0 -0
  463. package/skills/accounting-ops/scripts/__pycache__/acct_tools.cpython-312.pyc +0 -0
  464. package/skills/anomaly-detection/scripts/__pycache__/anomaly.cpython-312.pyc +0 -0
  465. package/skills/audit-assist/scripts/__pycache__/audit_sampling.cpython-312.pyc +0 -0
  466. package/skills/banking-credit/scripts/__pycache__/credit_metrics.cpython-312.pyc +0 -0
  467. package/skills/data-report-pro/scripts/__pycache__/bigdata.cpython-312.pyc +0 -0
  468. package/skills/data-wrangling/scripts/__pycache__/join_tools.cpython-312.pyc +0 -0
  469. package/skills/ecommerce-ops/scripts/__pycache__/gmv_decompose.cpython-312.pyc +0 -0
  470. package/skills/funnel-cohort/scripts/__pycache__/funnel.cpython-312.pyc +0 -0
  471. package/skills/management-accounting/scripts/__pycache__/costing.cpython-312.pyc +0 -0
  472. package/skills/manufacturing-quality/scripts/__pycache__/spc.cpython-312.pyc +0 -0
  473. package/skills/marketing-analytics/scripts/__pycache__/rfm.cpython-312.pyc +0 -0
  474. package/skills/optimization-solver/scripts/__pycache__/solver.cpython-312.pyc +0 -0
  475. package/skills/pivot-crosstab/scripts/__pycache__/pivot_tools.cpython-312.pyc +0 -0
  476. package/skills/portfolio-analysis/scripts/__pycache__/portfolio.cpython-312.pyc +0 -0
  477. package/skills/procurement-supply/scripts/__pycache__/supply.cpython-312.pyc +0 -0
  478. package/skills/production-planning/scripts/__pycache__/mrp_capacity.cpython-312.pyc +0 -0
  479. package/skills/project-management/scripts/__pycache__/cpm.cpython-312.pyc +0 -0
  480. package/skills/quant-analysis/scripts/__pycache__/quant.cpython-312.pyc +0 -0
  481. package/skills/real-estate-property/scripts/__pycache__/property_calc.cpython-312.pyc +0 -0
  482. package/skills/regression-forecast/scripts/__pycache__/regress.cpython-312.pyc +0 -0
  483. package/skills/securities-research/scripts/__pycache__/valuation.cpython-312.pyc +0 -0
  484. package/skills/statistical-testing/scripts/__pycache__/stat_tests.cpython-312.pyc +0 -0
  485. package/skills/treasury-mgmt/scripts/__pycache__/cashflow_forecast.cpython-312.pyc +0 -0
@@ -17,12 +17,15 @@ version: 1.0.0
17
17
 
18
18
  ## 引擎决策表(双引擎,按需选)
19
19
 
20
- | 维度 | pptx-author(python-pptx) | slides-html(渲染管线,桌面端) |
20
+ | 维度 | pptx-author(`create_deck`,四端都有) | slides-html(渲染管线,仅桌面端) |
21
21
  |---|---|---|
22
22
  | 可编辑性 | ✅ 原生可改 | ❌ 贴图(如实告知) |
23
- | 设计表现力 | 常规版式 | ✅ 渐变/精细排版/发布会级 |
23
+ | 设计表现力 | 十三种版式,规整 | ✅ 渐变/精细排版/发布会级 |
24
24
  | 适用 | 日常汇报/需二次编辑/模板合规 | 对外发布/路演/交付 PDF |
25
- | 自检 | verify 页数标题 | ✅ 截图注入逐页自检闭环 |
25
+ | 版式自检 | ✅ `pptx_inspect` 几何+度量机检,可自动修 | ✅ DOM 机检 + 截图注入看图 |
26
+
27
+ 两条路都有**机检**:必修项为 0 才算做完。区别只在"机器看得见什么"——
28
+ PPTX 侧看的是形状坐标与文字度量,HTML 侧看的是真实渲染后的 DOM。
26
29
 
27
30
  品牌规范(色/字/logo)从 brand-design 的 VI 取;两引擎主题色系同源。
28
31
 
@@ -33,8 +36,11 @@ version: 1.0.0
33
36
  (中文字体!);一页一图;
34
37
  3. **素材**:图片版权可用性;图标用 Emoji/内联 SVG(slides-html 禁外链);
35
38
  4. **生成**:按引擎决策表;结构页型(封面/目录/章节/要点/图文/KPI/引用/结尾)套用;
36
- 5. **自检**:验收清单(无溢出/无缺字/主题一致/页码/数字与来源核对——**讲错一个数,
37
- 整场信用清零**,同述职纪律);
39
+ 5. **自检(两层,缺一不可)**:
40
+ ①**版式机检**——`pptx_inspect` 或 `slides_render` 的体检报告,必修项清零,
41
+ 溢出/重叠/变形/越界这类"打开就看得见的事故"不许带到交付;
42
+ ②**内容核对**——数字与来源逐个对,**讲错一个数,整场信用清零**(同述职纪律);
43
+ 主题一致、页码、无缺字。机检管得了前者,管不了后者。
38
44
  6. **导出与演讲**:PDF(通用)/PPTX/放映页;讲稿按 260 字/分钟配时(okr-review 口径),
39
45
  QA 预演(pr-comms 攻防表)。
40
46
 
@@ -57,5 +63,7 @@ slides-html 四主题选型+定制封面 → 逐页截图自检至无溢出 →
57
63
 
58
64
  ## 使用纪律
59
65
 
60
- 大纲未确认不生成;数字未溯源不上页;PPTX 贴图版的不可编辑性**必须**提前告知;
66
+ 大纲未确认不生成;数字未溯源不上页;**版式机检必修项未清零不交付**;
67
+ PPTX 贴图版的不可编辑性**必须**提前告知;
68
+ 坐标不自己算——用 `create_deck` 的结构化版式,手写 python-pptx 摆坐标是全部排版事故的来源;
61
69
  模板资产沉淀(好版式回写团队模板,knowledge-sop 管理)。
@@ -0,0 +1,31 @@
1
+ {
2
+ "skill": "ppt-chain",
3
+ "cases": [
4
+ {
5
+ "id": "pos-1",
6
+ "prompt": "要做个 PPT 但场景和素材还没定",
7
+ "shouldTrigger": true
8
+ },
9
+ {
10
+ "id": "pos-2",
11
+ "prompt": "从数据到汇报,整条 PPT 链怎么走",
12
+ "shouldTrigger": true
13
+ },
14
+ {
15
+ "id": "pos-3",
16
+ "prompt": "经营月报的 PPT 全流程怎么排",
17
+ "shouldTrigger": true
18
+ },
19
+ {
20
+ "id": "neg-1",
21
+ "prompt": "直接生成一个 pptx 演示文稿",
22
+ "shouldTrigger": false,
23
+ "lexical": false
24
+ },
25
+ {
26
+ "id": "neg-2",
27
+ "prompt": "写一份 Word 报告",
28
+ "shouldTrigger": false
29
+ }
30
+ ]
31
+ }
@@ -1,48 +1,101 @@
1
1
  ---
2
2
  name: pptx-author
3
- description: 生成 PPT 演示文稿。当用户说"做个 PPT/幻灯片/汇报材料/演示文稿/pptx"时使用。
4
- version: 1.0.0
3
+ description: 生成 PPT/幻灯片演示文稿,并对成品做版式体检(文字溢出、元素重叠、图片变形、超出页面)。当用户说"做个 PPT/做几页幻灯片/汇报材料/演示文稿/pptx",或说"PPT 排版有问题/幻灯片版式检查/修复溢出重叠"时使用。
4
+ version: 2.0.0
5
5
  ---
6
6
 
7
- # PPT 生成(经 run_python 执行,python-pptx 已随托管运行时就绪)
7
+ # PPT 生成与版式体检
8
8
 
9
- ## 流程
9
+ **PPT 的排版事故不是审美问题,是算术问题**:文字塞进一个拍脑袋定的框、图片同时给了宽和高、
10
+ 两块内容坐标算重了——这三件事在保存那一刻都不报错,等人打开 PPT 才看见。
11
+ 所以本技能的做法是:坐标不由你算,出片后再机检一遍。
10
12
 
11
- 1. **大纲先行**:先在回复里给出逐页大纲(每页=标题 + ≤5 条要点 + 可选备注),页数建议 8–15;重要汇报让用户确认再生成。
12
- 2. **选主题**:`read_skill_file` 读本技能 `scripts/pptx_deck.py`,内置四套配色主题——`navy`(深蓝商务,默认)/ `tech`(科技紫)/ `mono`(极简灰) / `warm`(活力橙)。用户有品牌色时改 `THEMES` 传自定义。
13
- 3. **逐页构建**(16:9,helper 已处理版式/配色/字体):
14
- ```python
15
- d = Deck(theme='navy')
16
- d.add_cover('2026 上半年经营分析', '数据一部 · 2026-07')
17
- d.add_agenda(['经营概览', '分区域表现', '问题与对策', '下半年计划'])
18
- d.add_section('一、经营概览')
19
- d.add_bullets('核心指标', ['营收 3.6 亿,同比 +18%', '毛利率 32.5%,环比 +1.2pct'], notes='强调同比口径')
20
- d.add_image('分区域销售', '销售图.png', caption='华东领先,西南承压')
21
- d.add_table('明细数据', [['地区','销售额','同比'], ['华东','1.2亿','+21%']])
22
- d.add_end('谢谢,欢迎讨论')
23
- d.save('经营分析.pptx')
24
- ```
25
- 4. **图表页**:先按 data-analysis 技能出 png(务必先设中文字体),再 `add_image` 插入——比 pptx 原生图表稳定且好看。
26
- 5. **验收(必做)**:`verify('产物.pptx')` 重开打印页数与每页标题,对照大纲无缺页。
13
+ ## 一、生成:用 `create_deck`,不要自己摆坐标
27
14
 
28
- ## 设计约束(避免"惨不忍睹"的 PPT)
15
+ `create_deck(path, spec)` 是首选路径。你只给「每页讲什么」,引擎负责量与画:
16
+ 一页装不下先降字号(22→16pt),降到下限自动拆页并把标题改成「原标题(续)」;
17
+ 图片一律按原比例等比缩放居中(**不存在把图拉变形的写法**);表格列宽按内容算、行多了自动分页;
18
+ 每次落盘后**自动跑一遍版式体检**,报告随结果返回。
29
19
 
30
- - 一页一个观点;要点每条 ≤20 字;超了就拆页或进备注。
31
- - 数字是主角:关键数字用 `add_kpi`(大数字卡片页)而非藏在句子里。
32
- - 不堆图:一页一图;表格 ≤7 行,更多的放 xlsx 附件并在页脚注明。
33
- - 中文字体 helper 已设(微软雅黑/苹方由播放机器渲染,不嵌字体)。
20
+ ```jsonc
21
+ { "theme": "navy", "footer": "数据一部 · 2026-07", "slides": [
22
+ { "layout": "cover", "title": "2026 上半年经营分析", "subtitle": "数据一部", "meta": "2026-07-15" },
23
+ { "layout": "agenda", "items": ["经营概览", "分区域表现", "问题与对策", "下半年计划"] },
24
+ { "layout": "section", "title": "经营概览", "no": "PART 01" },
25
+ { "layout": "kpi", "title": "核心指标",
26
+ "kpis": [["3.6亿", "营业收入", "同比 +18%"], ["32.5%", "毛利率", "环比 +1.2pct"]] },
27
+ { "layout": "bullets", "title": "增长来自哪里", "lead": "结论:靠华东与订阅制",
28
+ "bullets": ["华东新签 12 家大客户", { "text": "产品侧", "sub": ["智能报表续费率 92%"] }],
29
+ "notes": "这里展开讲渠道的账" },
30
+ { "layout": "table", "title": "分区域明细",
31
+ "rows": [["地区","销售额(万)","同比"], ["华东","12000","+21%"]], "note": "口径:含税" },
32
+ { "layout": "end", "text": "谢谢,欢迎讨论" }
33
+ ]}
34
+ ```
34
35
 
35
- ## 桌面端「炫酷版」提示
36
+ **十三种版式**:cover 封面 / agenda 目录 / section 章节 / bullets 要点 / two_col 两栏 /
37
+ kpi 大数字 / image 整幅图 / image_text 图文 / table 表格 / quote 引言 / compare 左右对照 /
38
+ timeline 时间轴 / end 结尾。**六套主题**:navy(默认)/ tech / mono / warm / green / dark。
36
39
 
37
- python-pptx 走的是标准版式,追求设计感/动效的发布会级 PPT 属 M5-⑤ HTML 幻灯片管线(桌面端渲染导出)。用户明确要"炫酷/设计感"时:先用本技能出**结构定稿**,并说明设计版能力的边界。
40
+ 配图先出 png 再引用:工程图用 `render_diagram`,数据图表用 `run_python` + matplotlib(**先设中文字体**)。
38
41
 
39
- ## 验收自查
42
+ ## 二、验收:`pptx_inspect` 必修项清零才算做完
40
43
 
41
- - [ ] verify 输出的页数与标题和大纲一致
42
- - [ ] 每页要点 ≤5 条、每条 ≤20 字(超限已拆页/进备注)
43
- - [ ] 插图的 png 中文显示正常(画图前设了字体)
44
+ `create_deck` 已自动体检;**自己写 python-pptx 生成的更要手动跑一遍** `pptx_inspect(path=...)`。
45
+ 用户拿来一份别人做的 PPT 问"排版有没有问题",也是这个工具。
46
+
47
+ 体检的十几类问题分三档:
48
+
49
+ | 必修(打开就能看见的事故) | 建议(能读,但不专业) | 提示 |
50
+ |---|---|---|
51
+ | 文字溢出被切 / 框自动长高顶出页底 | 要点过多(>6)、单条过长(>25 字) | 标题跨页重复 |
52
+ | 元素重叠(按文字**实际占位**比,不是按框比) | 整页文字过密(>220 字) | 配色过杂 |
53
+ | 图片变形(宽高比≠原图)| 图片发虚(<96dpi) | 页面比例非 16:9 |
54
+ | 超出页面边界、表格顶出页底 | 贴边过近(<0.22")、标题跨页错位 | |
55
+ | 字号 <10pt、对比度 <4.5:1 | 中文未设东亚字体 | |
56
+
57
+ `pptx_inspect(path, fix=true)` 自动修**几何类**问题(图片恢复原比例、元素移回页内、
58
+ 文字缩字或加高文本框、表格缩排回写行高),**另存 `-已修复.pptx` 不覆盖原件**,并回报复检结果。
59
+ 改内容才能解决的(要点太多、表格太长、文案太啰嗦)它会如实说"需要拆页/精简"——**别假装修好了**。
60
+
61
+ ## 三、需要引擎没有的版式时
62
+
63
+ 才用 `run_python` 直接调建版内核(**不要复制粘贴源码**,import 即可):
64
+
65
+ ```python
66
+ import os, sys; sys.path.insert(0, os.path.join(os.environ['HUICAI_SKILLS_DIR'], 'pptx-author', 'scripts'))
67
+ from pptx_deck import Deck
68
+ d = Deck('navy', footer='数据一部')
69
+ d.add_cover('标题', '副标题')
70
+ d.add_bullets('要点', ['第一条', '第二条']) # 装不下会自己降字号/拆页
71
+ d.save('产物.pptx') # save 自动体检并打印报告
72
+ ```
73
+
74
+ 环境变量取不到时(老版本宿主)才退回 `read_skill_file` 读源码。
75
+ **任何情况下都不要自己算 `Inches(...)` 坐标去摆版式**——那正是所有事故的来源。
76
+
77
+ ## 四、内容纪律(比排版更决定成败)
78
+
79
+ - 大纲先行:每页 = 结论式标题 + ≤5 条要点,8–15 页;重要汇报**先让用户确认大纲再生成**。
80
+ - 一页一个观点;一条要点 ≤20 字;展开的话进 `notes`(演讲备注),不进正文。
81
+ - 数字是主角:关键数字用 `kpi` 版式做成大数字卡片,别藏在句子里;数字必须可溯源。
82
+ - 表格 ≤7 行;更多的放 xlsx 附件并在 `note` 里注明。
83
+ - 追求发布会级设计感 → 改用 `slides-html`(桌面端 HTML 渲染管线),本技能出结构定稿。
84
+
85
+ ## 五、交付自查
86
+
87
+ - [ ] `pptx_inspect` 必修 = 0(做不到就如实告诉用户哪几页需要他决定删什么)
88
+ - [ ] 页数与标题和确认过的大纲一致
89
+ - [ ] 插图中文显示正常、清晰不糊
44
90
  - [ ] 文件名是中文描述性名称,已 print 产物清单
45
91
 
46
- ## 附件
92
+ ## 附件(`read_skill_file` 按需读;日常用 `create_deck` 工具即可,不必读源码)
47
93
 
48
- - `scripts/pptx_deck.py` — Deck 构建器:四主题配色、cover/agenda/section/bullets/kpi/image/table/end 八种版式、`verify()` 重读校验;可直接执行自测
94
+ - `scripts/pptx_layout.py` — 版式度量内核:中英文字宽表(对雅黑/苹方实测校准,
95
+ `python pptx_layout.py --calibrate` 可重新校准)、折行、几何、对比度。建版与体检**共用同一把尺**。
96
+ - `scripts/pptx_deck.py` — Deck 建版器:十三版式、先量后画、自动拆页、`save()` 自带体检。直接执行 = 自测。
97
+ - `scripts/pptx_check.py` — 体检与自动修复:`inspect()` / `report()` / `fix()`;也可 CLI 跑
98
+ `python pptx_check.py 汇报.pptx --fix`。
99
+ - `scripts/pptx_build.py` — spec(JSON) → PPTX 的执行端(`create_deck` 工具走的就是它)。
100
+ - `scripts/pptx_battery.py` — 压力测试:随机刁难内容 × 六主题 × 两画幅跑几百份,
101
+ 断言零版式事故;并反向造缺陷验证体检不是瞎子。改了引擎必须重跑:`python pptx_battery.py 300`。
@@ -0,0 +1,178 @@
1
+ # 版式引擎压力测试:拿刻意刁难的内容灌进去,断言**永远不产生版式事故**。
2
+ #
3
+ # 为什么要这么测:排版缺陷不像功能 bug 那样"必现",它取决于这一页恰好有多少字、
4
+ # 标题恰好多长、表格恰好多少列。人工试几页什么都试不出来,只有把
5
+ # 「超长标题 × 超多要点 × 中英数混排 × 六套主题 × 两种画幅」交叉铺开跑几百份,
6
+ # 才谈得上"确保不出现变形、重叠"。
7
+ #
8
+ # 运行:python pptx_battery.py [份数,默认 240]
9
+ # 判据:每份产物的体检结果里,**几何类问题一条都不许有**(内容类告警允许——
10
+ # "要点太多"是在说内容该精简,不是版式坏了)。
11
+
12
+ import os
13
+ import random
14
+ import sys
15
+ import tempfile
16
+
17
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
18
+
19
+ import pptx_check # noqa: E402
20
+ from pptx_deck import Deck, _demo_png # noqa: E402
21
+ from pptx_layout import THEMES # noqa: E402
22
+
23
+ # 几何/可读性类:引擎自己生成的产物里出现任何一条,都是引擎的缺陷
24
+ FORBIDDEN = {'overflow_v', 'overflow_h', 'overlap', 'out_of_slide', 'image_distort',
25
+ 'tiny_shape', 'low_contrast', 'font_too_small', 'no_ea_font', 'off_grid',
26
+ 'page_scroll', 'autofit_shrunk'}
27
+
28
+ CJK = '经营分析渠道毛利率现金流客户续约交付实施顾问标准化复用行业模板签单回款'
29
+ LAT = ['pipeline', 'ROI', 'NPS', 'OKR', 'SaaS', 'A/B', 'CAC', 'GMV', 'QoQ', 'YoY']
30
+
31
+
32
+ def rand_text(rng, lo, hi):
33
+ n = rng.randint(lo, hi)
34
+ out = []
35
+ while sum(len(x) for x in out) < n:
36
+ r = rng.random()
37
+ if r < 0.62:
38
+ out.append(''.join(rng.choice(CJK) for _ in range(rng.randint(2, 8))))
39
+ elif r < 0.82:
40
+ out.append(rng.choice(LAT))
41
+ elif r < 0.95:
42
+ out.append(f'{rng.randint(1, 9999)}{rng.choice(["", "万", "%", "亿", ".5pct", "天"])}')
43
+ else:
44
+ out.append(rng.choice(',。:、()+-—'))
45
+ return ''.join(out)[:n + 6]
46
+
47
+
48
+ def make_deck(rng, png_big, png_small, i):
49
+ theme = rng.choice(list(THEMES))
50
+ d = Deck(theme, size=rng.choice(['16:9'] * 5 + ['4:3']),
51
+ footer=rand_text(rng, 0, 24) if rng.random() < 0.7 else '',
52
+ page_numbers=rng.random() < 0.85)
53
+ d.add_cover(rand_text(rng, 4, 40), rand_text(rng, 0, 30), rand_text(rng, 0, 20))
54
+ d.add_agenda([rand_text(rng, 2, 26) for _ in range(rng.randint(2, 9))])
55
+ d.add_section(rand_text(rng, 2, 34), no=rng.choice([None, 'PART 01', '一']),
56
+ subtitle=rand_text(rng, 0, 30))
57
+ d.add_kpi(rand_text(rng, 2, 20),
58
+ [(rand_text(rng, 1, 9), rand_text(rng, 2, 18), rand_text(rng, 0, 12))
59
+ for _ in range(rng.randint(1, 6))],
60
+ caption=rand_text(rng, 0, 40) if rng.random() < 0.4 else '')
61
+ d.add_bullets(rand_text(rng, 2, 30),
62
+ [rand_text(rng, 4, 60) for _ in range(rng.randint(1, 16))],
63
+ lead=rand_text(rng, 0, 34) if rng.random() < 0.5 else '')
64
+ d.add_two_col(rand_text(rng, 2, 22),
65
+ [rand_text(rng, 3, 34) for _ in range(rng.randint(1, 7))],
66
+ [rand_text(rng, 3, 34) for _ in range(rng.randint(1, 7))],
67
+ rand_text(rng, 0, 12), rand_text(rng, 0, 12))
68
+ d.add_compare(rand_text(rng, 2, 22), rand_text(rng, 2, 10),
69
+ [rand_text(rng, 3, 30) for _ in range(rng.randint(1, 6))],
70
+ rand_text(rng, 2, 10),
71
+ [rand_text(rng, 3, 30) for _ in range(rng.randint(1, 6))])
72
+ d.add_image(rand_text(rng, 2, 24), rng.choice([png_big, png_small]),
73
+ caption=rand_text(rng, 0, 44))
74
+ d.add_image_text(rand_text(rng, 2, 24), png_big,
75
+ [rand_text(rng, 3, 30) for _ in range(rng.randint(1, 6))],
76
+ side=rng.choice(['left', 'right']), caption=rand_text(rng, 0, 30))
77
+ ncol = rng.randint(2, 7)
78
+ rows = [[rand_text(rng, 2, 10) for _ in range(ncol)]]
79
+ rows += [[rand_text(rng, 1, 16) for _ in range(ncol)] for _ in range(rng.randint(1, 24))]
80
+ d.add_table(rand_text(rng, 2, 22), rows, note=rand_text(rng, 0, 40))
81
+ d.add_timeline(rand_text(rng, 2, 22),
82
+ [(rand_text(rng, 2, 8), rand_text(rng, 3, 26)) for _ in range(rng.randint(2, 6))])
83
+ d.add_quote(rand_text(rng, 6, 70), rand_text(rng, 0, 14))
84
+ d.add_end(rand_text(rng, 2, 20), rand_text(rng, 0, 26))
85
+ return d, theme
86
+
87
+
88
+ def main(n=240):
89
+ rng = random.Random(20260831)
90
+ tmp = tempfile.mkdtemp(prefix='pptx-battery-')
91
+ png_big = _demo_png(os.path.join(tmp, 'big.png'), 1600, 900)
92
+ png_small = _demo_png(os.path.join(tmp, 'small.png'), 420, 640)
93
+ bad = []
94
+ pages = 0
95
+ warn_codes = {}
96
+ for i in range(n):
97
+ path = os.path.join(tmp, f'deck{i}.pptx')
98
+ try:
99
+ d, theme = make_deck(rng, png_big, png_small, i)
100
+ res = d.save(path, verbose=False)
101
+ except Exception as e:
102
+ bad.append((i, 'EXCEPTION', f'{type(e).__name__}: {e}'))
103
+ continue
104
+ pages += res['slides']
105
+ for f in res['findings']:
106
+ if f['code'] in FORBIDDEN or (f['code'] == 'table_overflow' and f['level'] == 'error'):
107
+ bad.append((i, f'{theme} p{f["page"]} {f["code"]}', f['detail']))
108
+ else:
109
+ warn_codes[f['code']] = warn_codes.get(f['code'], 0) + 1
110
+ for w in d.warnings:
111
+ if '内部版式冲突' in w:
112
+ bad.append((i, 'CLAIM', w))
113
+ os.remove(path)
114
+ print(f'跑了 {n} 份 · {pages} 页 · 六主题两画幅随机内容')
115
+ print('内容类告警(允许,属于"内容该精简"而非版式坏了):'
116
+ + ('、'.join(f'{k}×{v}' for k, v in sorted(warn_codes.items())) or '无'))
117
+ if bad:
118
+ print(f'\n❌ 版式事故 {len(bad)} 条:')
119
+ for i, where, detail in bad[:25]:
120
+ print(f' #{i} {where} {detail[:110]}')
121
+ return 1
122
+ print('\n✅ 零版式事故:无溢出、无重叠、无变形、无越界、无小字、无低对比度')
123
+ return 0
124
+
125
+
126
+ def detector_check():
127
+ """反向验证:故意造缺陷,体检必须抓得住(不然上面的"零事故"可能只是瞎子发的通行证)。"""
128
+ from pptx import Presentation
129
+ from pptx.util import Inches, Pt
130
+ tmp = tempfile.mkdtemp(prefix='pptx-detect-')
131
+ png = _demo_png(os.path.join(tmp, 'w.png'), 1600, 900)
132
+ prs = Presentation()
133
+ prs.slide_width, prs.slide_height = Inches(13.333), Inches(7.5)
134
+ b = prs.slide_layouts[6]
135
+ s = prs.slides.add_slide(b)
136
+ s.shapes.add_picture(png, Inches(1), Inches(1), width=Inches(4), height=Inches(4)) # 变形
137
+ s = prs.slides.add_slide(b)
138
+ for x in (1.0, 2.0): # 重叠
139
+ tb = s.shapes.add_textbox(Inches(x), Inches(2), Inches(4), Inches(1))
140
+ r = tb.text_frame.paragraphs[0].add_run()
141
+ r.text = '压在一起的两块文字'
142
+ r.font.size = Pt(24)
143
+ s = prs.slides.add_slide(b)
144
+ tb = s.shapes.add_textbox(Inches(11), Inches(7), Inches(4), Inches(1)) # 越界
145
+ r = tb.text_frame.paragraphs[0].add_run()
146
+ r.text = '跑出页面'
147
+ r.font.size = Pt(18)
148
+ tb = s.shapes.add_textbox(Inches(1), Inches(1.5), Inches(5), Inches(1)) # 溢出
149
+ tf = tb.text_frame
150
+ tf.word_wrap = True
151
+ for i in range(8):
152
+ p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
153
+ r = p.add_run()
154
+ r.text = '这一段文字足够长,用来把文本框撑爆 %d' % i
155
+ r.font.size = Pt(20)
156
+ path = os.path.join(tmp, 'defects.pptx')
157
+ prs.save(path)
158
+ res = pptx_check.inspect(path)
159
+ got = {f['code'] for f in res['findings']}
160
+ want = {'image_distort', 'overlap', 'out_of_slide', 'overflow_v'}
161
+ miss = want - got
162
+ print('\n【反向验证】故意造 4 类缺陷 → 抓到:' + '、'.join(sorted(want & got)))
163
+ if miss:
164
+ print('❌ 漏检:' + '、'.join(sorted(miss)))
165
+ return 1
166
+ fixed = pptx_check.fix(path)
167
+ after = {f['code'] for f in pptx_check.inspect(fixed['saved'])['findings']}
168
+ if 'image_distort' in after or 'out_of_slide' in after:
169
+ print('❌ fix 后仍有几何类问题:' + '、'.join(sorted(after)))
170
+ return 1
171
+ print(f' fix 修掉 {len(fixed["changed"])} 处,复检无变形/越界 ✓')
172
+ return 0
173
+
174
+
175
+ if __name__ == '__main__':
176
+ n = int(sys.argv[1]) if len(sys.argv) > 1 and sys.argv[1].isdigit() else 240
177
+ code = main(n) | detector_check()
178
+ sys.exit(code)
@@ -0,0 +1,119 @@
1
+ # 结构化 spec → PPTX:模型只给"每页讲什么",版式/字号/坐标/配色全由引擎算。
2
+ #
3
+ # 这是 create_deck 工具的执行端,也可以单独用:
4
+ # python pptx_build.py 大纲.json 产物.pptx
5
+ #
6
+ # 为什么不让模型直接写 python-pptx:写坐标就一定会有算错的坐标,
7
+ # 那正是"文字出框、两块压在一起、图被拉长"的全部来源。
8
+ # 模型擅长决定"这页讲什么",不擅长也不该负责"这行字在 3.72 英寸处会不会撞到图"。
9
+
10
+ import json
11
+ import os
12
+ import sys
13
+
14
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
15
+
16
+ from pptx_deck import Deck # noqa: E402
17
+
18
+ LAYOUTS = {
19
+ 'cover': ('title',), 'agenda': ('items',), 'section': ('title',), 'bullets': ('title', 'bullets'),
20
+ 'two_col': ('title', 'left', 'right'), 'kpi': ('title', 'kpis'), 'image': ('title', 'image'),
21
+ 'image_text': ('title', 'image', 'bullets'), 'table': ('title', 'rows'), 'quote': ('text',),
22
+ 'compare': ('title', 'left', 'right'), 'timeline': ('title', 'items'), 'end': (),
23
+ }
24
+ ALIAS = { # 模型爱用的同义写法,都接住,别为了一个词名让整份 deck 失败
25
+ '封面': 'cover', '目录': 'agenda', 'toc': 'agenda', 'contents': 'agenda', '章节': 'section',
26
+ 'divider': 'section', '要点': 'bullets', 'bullet': 'bullets', 'list': 'bullets', 'points': 'bullets',
27
+ '两栏': 'two_col', 'twocol': 'two_col', 'two-column': 'two_col', 'columns': 'two_col',
28
+ '指标': 'kpi', 'metrics': 'kpi', 'stats': 'kpi', '图': 'image', 'picture': 'image', 'chart': 'image',
29
+ '图文': 'image_text', 'imagetext': 'image_text', '表': 'table', '表格': 'table',
30
+ '引言': 'quote', '金句': 'quote', '对照': 'compare', '对比': 'compare', 'comparison': 'compare',
31
+ '时间轴': 'timeline', 'roadmap': 'timeline', '结尾': 'end', 'thanks': 'end', 'closing': 'end',
32
+ }
33
+
34
+
35
+ def norm_layout(v):
36
+ k = str(v or '').strip().lower()
37
+ return k if k in LAYOUTS else ALIAS.get(k) or ALIAS.get(str(v or '').strip())
38
+
39
+
40
+ def build(spec, out_path, check=True):
41
+ """按 spec 建一份 PPTX。返回 {path, slides, outline, warnings, check}。"""
42
+ if not isinstance(spec, dict):
43
+ raise ValueError('spec 必须是对象')
44
+ slides = spec.get('slides') or spec.get('pages') or []
45
+ if not isinstance(slides, list) or not slides:
46
+ raise ValueError('spec.slides 不能为空:至少给一页(每页含 layout 与内容字段)')
47
+ d = Deck(theme=spec.get('theme', 'navy'), size=spec.get('size', '16:9'),
48
+ footer=spec.get('footer', ''), page_numbers=spec.get('page_numbers', True))
49
+ for i, sl in enumerate(slides, 1):
50
+ if not isinstance(sl, dict):
51
+ raise ValueError(f'第 {i} 页不是对象')
52
+ lay = norm_layout(sl.get('layout') or sl.get('type'))
53
+ if not lay:
54
+ raise ValueError(f'第 {i} 页 layout={sl.get("layout") or sl.get("type")!r} 不认识;'
55
+ f'可选:{" / ".join(sorted(LAYOUTS))}')
56
+ miss = [k for k in LAYOUTS[lay] if not sl.get(k)]
57
+ if miss:
58
+ raise ValueError(f'第 {i} 页({lay})缺字段:{"、".join(miss)}')
59
+ try:
60
+ _emit(d, lay, sl)
61
+ except Exception as e:
62
+ raise ValueError(f'第 {i} 页({lay})建版失败:{e}') from e
63
+ res = d.save(out_path, check=check, verbose=False)
64
+ return dict(path=os.path.abspath(out_path), slides=len(d.prs.slides),
65
+ outline=[dict(page=n, layout=k, title=t) for n, k, t in d.outline()],
66
+ warnings=d.warnings, check=res)
67
+
68
+
69
+ def _emit(d, lay, s):
70
+ g = s.get
71
+ if lay == 'cover':
72
+ d.add_cover(g('title'), g('subtitle', ''), g('meta', ''), g('notes'))
73
+ elif lay == 'agenda':
74
+ d.add_agenda(list(g('items')), g('title', '目录'), g('notes'))
75
+ elif lay == 'section':
76
+ d.add_section(g('title'), g('no'), g('subtitle', ''), g('notes'))
77
+ elif lay == 'bullets':
78
+ d.add_bullets(g('title'), g('bullets'), g('notes'), g('lead', ''))
79
+ elif lay == 'two_col':
80
+ d.add_two_col(g('title'), g('left'), g('right'), g('left_title', ''), g('right_title', ''), g('notes'))
81
+ elif lay == 'kpi':
82
+ d.add_kpi(g('title'), [tuple(k) if isinstance(k, (list, tuple)) else (str(k), '') for k in g('kpis')],
83
+ g('notes'), g('caption', ''))
84
+ elif lay == 'image':
85
+ d.add_image(g('title'), g('image'), g('caption', ''), g('notes'))
86
+ elif lay == 'image_text':
87
+ d.add_image_text(g('title'), g('image'), g('bullets'), g('side', 'left'), g('caption', ''), g('notes'))
88
+ elif lay == 'table':
89
+ d.add_table(g('title'), g('rows'), g('notes'), g('col_widths'), g('note', ''))
90
+ elif lay == 'quote':
91
+ d.add_quote(g('text'), g('author', ''), g('notes'))
92
+ elif lay == 'compare':
93
+ d.add_compare(g('title'), g('left_title', '现状'), g('left'), g('right_title', '目标'), g('right'), g('notes'))
94
+ elif lay == 'timeline':
95
+ d.add_timeline(g('title'), g('items'), g('notes'))
96
+ elif lay == 'end':
97
+ d.add_end(g('text', '谢谢'), g('subtitle', ''), g('notes'))
98
+
99
+
100
+ def main(argv):
101
+ if len(argv) < 2:
102
+ print('用法:python pptx_build.py <spec.json> <产物.pptx>')
103
+ return 2
104
+ with open(argv[0], encoding='utf-8') as f:
105
+ spec = json.load(f)
106
+ try:
107
+ r = build(spec, argv[1])
108
+ except Exception as e:
109
+ print(json.dumps(dict(error=str(e)), ensure_ascii=False))
110
+ return 1
111
+ import pptx_check
112
+ r['report'] = pptx_check.report(r['check']) if r.get('check') else ''
113
+ r['check'] = {k: r['check'][k] for k in ('ok', 'counts', 'slides', 'ratio')} if r.get('check') else None
114
+ print(json.dumps(r, ensure_ascii=False))
115
+ return 0
116
+
117
+
118
+ if __name__ == '__main__':
119
+ sys.exit(main(sys.argv[1:]))