@huicai/cli 2026.804.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 (486) hide show
  1. package/LICENSE +391 -32
  2. package/README.md +176 -10
  3. package/bin/huicai.js +166 -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 -0
  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 -0
  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 -0
  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 -0
  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 -0
  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 -0
  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/vision.js +1 -0
  450. package/src/visual.js +1 -0
  451. package/src/voice-form.js +1 -0
  452. package/src/voice.js +1 -0
  453. package/src/watch.js +1 -1
  454. package/src/web-audit.js +1 -0
  455. package/src/web-cdp.js +1 -0
  456. package/src/web-inspect.js +1 -0
  457. package/src/websearch.js +1 -0
  458. package/src/weekly.js +1 -0
  459. package/src/workflow.js +1 -0
  460. package/src/worklog.js +1 -0
  461. package/src/xlsx-tools.js +1 -0
  462. package/ui/command-picker.js +79 -0
  463. package/skills/ab-testing/scripts/__pycache__/ab_test.cpython-312.pyc +0 -0
  464. package/skills/accounting-ops/scripts/__pycache__/acct_tools.cpython-312.pyc +0 -0
  465. package/skills/anomaly-detection/scripts/__pycache__/anomaly.cpython-312.pyc +0 -0
  466. package/skills/audit-assist/scripts/__pycache__/audit_sampling.cpython-312.pyc +0 -0
  467. package/skills/banking-credit/scripts/__pycache__/credit_metrics.cpython-312.pyc +0 -0
  468. package/skills/data-report-pro/scripts/__pycache__/bigdata.cpython-312.pyc +0 -0
  469. package/skills/data-wrangling/scripts/__pycache__/join_tools.cpython-312.pyc +0 -0
  470. package/skills/ecommerce-ops/scripts/__pycache__/gmv_decompose.cpython-312.pyc +0 -0
  471. package/skills/funnel-cohort/scripts/__pycache__/funnel.cpython-312.pyc +0 -0
  472. package/skills/management-accounting/scripts/__pycache__/costing.cpython-312.pyc +0 -0
  473. package/skills/manufacturing-quality/scripts/__pycache__/spc.cpython-312.pyc +0 -0
  474. package/skills/marketing-analytics/scripts/__pycache__/rfm.cpython-312.pyc +0 -0
  475. package/skills/optimization-solver/scripts/__pycache__/solver.cpython-312.pyc +0 -0
  476. package/skills/pivot-crosstab/scripts/__pycache__/pivot_tools.cpython-312.pyc +0 -0
  477. package/skills/portfolio-analysis/scripts/__pycache__/portfolio.cpython-312.pyc +0 -0
  478. package/skills/procurement-supply/scripts/__pycache__/supply.cpython-312.pyc +0 -0
  479. package/skills/production-planning/scripts/__pycache__/mrp_capacity.cpython-312.pyc +0 -0
  480. package/skills/project-management/scripts/__pycache__/cpm.cpython-312.pyc +0 -0
  481. package/skills/quant-analysis/scripts/__pycache__/quant.cpython-312.pyc +0 -0
  482. package/skills/real-estate-property/scripts/__pycache__/property_calc.cpython-312.pyc +0 -0
  483. package/skills/regression-forecast/scripts/__pycache__/regress.cpython-312.pyc +0 -0
  484. package/skills/securities-research/scripts/__pycache__/valuation.cpython-312.pyc +0 -0
  485. package/skills/statistical-testing/scripts/__pycache__/stat_tests.cpython-312.pyc +0 -0
  486. package/skills/treasury-mgmt/scripts/__pycache__/cashflow_forecast.cpython-312.pyc +0 -0
@@ -0,0 +1,599 @@
1
+ # 从一张「项目档案卡」生成整套研发归档骨架(约 45 份文档 + 表格)。
2
+ #
3
+ # 为什么值得做:一个项目的归档件里,**真正需要人想**的只有那几件事——项目名称与编号、
4
+ # 负责人与联系人、起止时间、考核指标、人员分工、里程碑、预算。其余四十多份文档的封面、
5
+ # 目录编号、章节骨架、签批栏、12 期周期性报告、5 个阶段评审文件,全是这几件事的机械展开。
6
+ # 人手抄这四十多遍,抄出来的就是真实归档里那些毛病:项目名称三种写法、编号两个值、
7
+ # 联系人栏填成电话、12 期月度验收报告一份没交。
8
+ #
9
+ # **所有文档的封面字段都取自同一张卡**——跨文档一致性因此天然为真,
10
+ # 而那恰恰是真实归档里最高频、也最难靠人工消灭的一类缺陷。
11
+ #
12
+ # 用法:python archive_build.py 档案卡.json 输出目录 [--only 研发任务书,立项申请书]
13
+
14
+ import calendar
15
+ import datetime
16
+ import json
17
+ import os
18
+ import re
19
+ import sys
20
+
21
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
22
+ import archive_spec as S # noqa: E402
23
+
24
+
25
+ def safe_name(name, max_bytes=180, fallback='未命名'):
26
+ """清洗成安全的文件/目录名:去非法字符 + **按字节截断**。
27
+
28
+ 只去非法字符不够:多数文件系统的单段上限是 255 字节,一个 200 字的中文项目名
29
+ 就是 600 字节,落盘直接 OSError。截断要按字节走且不能把多字节字符切一半。"""
30
+ s = re.sub(r'[\\/:*?"<>|\r\n\t]', '_', str(name or '')).strip().strip('.') or fallback
31
+ if len(s.encode('utf-8')) <= max_bytes:
32
+ return s
33
+ out, n = [], 0
34
+ for ch in s:
35
+ w = len(ch.encode('utf-8'))
36
+ if n + w > max_bytes - 3:
37
+ break
38
+ out.append(ch)
39
+ n += w
40
+ return ''.join(out) + '…'
41
+
42
+
43
+ def _sibling(skill, mod):
44
+ """取同源技能的引擎(docx-author / xlsx-analysis)。缺席时给出可读原因而不是裸崩。"""
45
+ here = os.path.dirname(os.path.abspath(__file__))
46
+ d = os.path.join(os.path.dirname(os.path.dirname(here)), skill, 'scripts')
47
+ if d not in sys.path:
48
+ sys.path.insert(0, d)
49
+ return __import__(mod)
50
+
51
+
52
+ # ── 档案卡 ─────────────────────────────────────────────────────────────────
53
+ CARD_REQUIRED = ['项目名称', '项目编号', '项目负责人', '建设单位', '部门', '起止时间']
54
+ CARD_DEFAULTS = {
55
+ '项目联系人': None, # 缺省跟随项目负责人
56
+ '联系电话': '',
57
+ '编制人': None, # 缺省跟随项目负责人
58
+ '年度': None, # 缺省从起止时间取
59
+ '项目级别': 'B',
60
+ '项目类型': '新产品',
61
+ '预算': 0,
62
+ '考核指标': [], # [[指标, 要求值, 测试方法/判定依据], …]
63
+ '人员': [], # [[姓名, 单位, 职称/职务, 分工], …]
64
+ '里程碑': [], # [[阶段, 任务, 处理人, 工时, 开始, 结束, 交付物], …]
65
+ '预算科目': [], # [[科目, 全年金额], …];缺省按研发费用归集口径给零表
66
+ '章节内容': {}, # {文档类型: {章节名: 正文}} —— 有多少填多少,其余出骨架
67
+ }
68
+ YEAR_RE = re.compile(r'(20\d{2})')
69
+
70
+
71
+ def normalize_card(card):
72
+ c = dict(CARD_DEFAULTS)
73
+ c.update({k: v for k, v in (card or {}).items() if v is not None})
74
+ miss = [k for k in CARD_REQUIRED if not str(c.get(k) or '').strip()]
75
+ if miss:
76
+ raise ValueError('档案卡缺必填项:' + '、'.join(miss)
77
+ + '(这几项决定全档一致性,没有它们生成出来的骨架必然对不上)')
78
+ c['项目联系人'] = c['项目联系人'] or c['项目负责人']
79
+ c['编制人'] = c['编制人'] or c['项目负责人']
80
+ if not c['年度']:
81
+ m = YEAR_RE.search(str(c['起止时间']))
82
+ c['年度'] = int(m.group(1)) if m else datetime.date.today().year
83
+ c['年度'] = int(c['年度'])
84
+ c['单位落款'] = f"{c['建设单位']}技术中心{c['部门']}"
85
+ return c
86
+
87
+
88
+ def cover_fields(card, kind):
89
+ """统一封面信息栏。**唯一来源就是这张卡**,所以不可能出现填串或截断。"""
90
+ base = [['项目名称', card['项目名称']], ['项目编号', card['项目编号']],
91
+ ['项目负责人', card['项目负责人']], ['项目联系人', card['项目联系人']],
92
+ ['联系电话', card['联系电话']], ['编制人', card['编制人']]]
93
+ if kind in ('工作总结报告', '技术总结报告', '项目验收报告', '研发评价总结纪要'):
94
+ base = [['项目名称', card['项目名称']], ['项目编号', card['项目编号']],
95
+ ['建设单位', card['建设单位']], ['项目负责人', card['项目负责人']],
96
+ ['联系电话', card['联系电话']], ['编制人', card['编制人']]]
97
+ return base
98
+
99
+
100
+ # ── 单份 docx ──────────────────────────────────────────────────────────────
101
+ def _doc_blocks(kind, card, spec, inserts=None, tail=None, warns=None):
102
+ """章节骨架 + 表单字段表 + 签批栏。
103
+
104
+ inserts = {章节名: [块…]} —— 把表格**插进它该在的那一节里**,不是统统堆到文末。
105
+ 位置错了不只是难看:档案体检的「立项考核指标 → 验收实测值」勾稽是按节取文的,
106
+ 指标表落在节外,那条最值钱的检查就永远判成「这一节没有量化指标」。
107
+ """
108
+ blocks = []
109
+ inserts = inserts or {}
110
+ body = (card.get('章节内容') or {}).get(kind) or {}
111
+ form = spec['form_fields'].get(kind)
112
+ if form:
113
+ pairs = []
114
+ for f in form:
115
+ k = S.field_key(f)
116
+ pairs.append([f, str(card.get(k, '')) if k else str(body.get(f, ''))])
117
+ blocks.append({'type': 'kv', 'pairs': pairs})
118
+ for title, subs, _level, _al in spec['sections'].get(kind, []):
119
+ blocks.append({'type': 'h1', 'text': title})
120
+ if body.get(title):
121
+ blocks.append({'type': 'p', 'text': str(body[title])})
122
+ blocks.extend(inserts.get(title) or [])
123
+ for sname in subs:
124
+ blocks.append({'type': 'h2', 'text': sname})
125
+ if body.get(sname):
126
+ blocks.append({'type': 'p', 'text': str(body[sname])})
127
+ blocks.extend(inserts.get(sname) or [])
128
+ # 注入到「不存在的章节名」的内容**绝不能静默丢弃**:用户写了字,字却凭空消失,
129
+ # 而且没有任何提示——这是最坏的一种失败。原样排到文末并如实报出来。
130
+ used = {t for t, subs, _l, _a in spec['sections'].get(kind, []) for t in [t] + list(subs)}
131
+ extra_keys = [k for k in body if k not in used]
132
+ if extra_keys:
133
+ if warns is not None:
134
+ # 把**该类文档的规范章节列全**。只说「写错了」不给对的名字,
135
+ # 调用方(人或模型)没法自纠,只能一次次猜——真跑里模型自造的章节名
136
+ # 几乎没一个对得上,正文全被排到了文末。
137
+ warns.append(f'【{kind}】{extra_keys} 不是该类文档的规范章节,'
138
+ f'已作为附加章节排在文末;该类文档的规范章节是:'
139
+ f'{[t for t, _s, _l, _a in spec["sections"].get(kind, [])]}')
140
+ for k in extra_keys:
141
+ blocks.append({'type': 'h1', 'text': str(k)})
142
+ blocks.append({'type': 'p', 'text': str(body[k])})
143
+ blocks.extend(tail or [])
144
+ ap = spec['approvals'].get(kind)
145
+ if ap:
146
+ # 有的文档把审批本身就列为正式章节(立项申请书的「审批意见」、实施计划的「审批记录」)。
147
+ # 那种情况下只补签批表,不再另起一节——否则会生成两个同名章节,
148
+ # 而重复标题恰恰是本项目的单文档体检会报的问题。
149
+ titles = [t for t, _s, _l, _a in spec['sections'].get(kind, [])]
150
+ own = next((t for t in titles if t in ('审批意见', '审批记录')), None)
151
+ if not own:
152
+ blocks.append({'type': 'h1', 'text': '审批记录'})
153
+ blocks.append({'type': 'kv', 'pairs': [[a, ''] for a in ap]})
154
+ return blocks
155
+
156
+
157
+ def _write_doc(kind, card, spec, path, title=None, inserts=None, tail=None,
158
+ numbering='cn', warns=None):
159
+ B = _sibling('docx-author', 'docx_build')
160
+ spec_doc = {
161
+ 'profile': 'tech', 'numbering': numbering,
162
+ 'title': title or f"{card['项目名称']}\n{kind}",
163
+ 'subtitle': kind if not title else '',
164
+ 'org': card['单位落款'], 'author': card['编制人'],
165
+ 'doc_no': card['项目编号'],
166
+ # 落款日期必须跟着卡走:周期性文档各自落在当期期末,不是「生成当天」
167
+ 'date': card.get('date'),
168
+ 'cover_fields': cover_fields(card, kind),
169
+ # 封面信息栏必须是表格:只有表格形态才能被档案体检按「键→值」读出来核对
170
+ 'cover_table': True,
171
+ 'toc': bool(spec['sections'].get(kind)),
172
+ 'header': f"{card['项目名称']} · {kind}",
173
+ 'footer': card['单位落款'],
174
+ 'blocks': _doc_blocks(kind, card, spec, inserts, tail, warns),
175
+ 'inspect': False,
176
+ }
177
+ os.makedirs(os.path.dirname(path), exist_ok=True)
178
+ B.build(spec_doc, path)
179
+ return path
180
+
181
+
182
+ # ── 表格件 ─────────────────────────────────────────────────────────────────
183
+ PLAN_COLS = [('seq', '序列'), ('stage', '阶段名称'), ('task', '任务名称'), ('owner', '处理人'),
184
+ ('est', '预估工时'), ('start', '计划开始'), ('end', '计划结束'),
185
+ ('pct', '进度'), ('memo', '备注')]
186
+ ACT_EXTRA = [('act_h', '实际工时'), ('act_s', '实际开始'), ('act_e', '实际结束')]
187
+
188
+ BUDGET_ROWS = [
189
+ '一、内部研究开发投入额', '其中:人员工资', '福利费', '办公及通讯费', '差旅及交通费',
190
+ '服务费', '测试调试费', '无形资产摊销', '业务招待费', '其它', '折旧费用与长期费用摊销',
191
+ '二、委托外部研究开发投入额', '其中:境内的投入额', '境外的投入额',
192
+ ]
193
+
194
+
195
+ def _write_plan(card, path, actual_quarter=None):
196
+ X = _sibling('xlsx-analysis', 'xlsx_build')
197
+ cols = list(PLAN_COLS)
198
+ if actual_quarter:
199
+ cols = PLAN_COLS[:7] + ACT_EXTRA + PLAN_COLS[7:]
200
+ rows = []
201
+ for i, m in enumerate(card['里程碑'], 1):
202
+ stage, task, owner, est, start, end = (list(m) + [''] * 6)[:6]
203
+ r = [i, stage, task, owner, est, start, end]
204
+ # 季度实际表是**累计**的:第 3 季度那张要含前两季度已完成任务的实际数据,
205
+ # 不是只填本季度发生的。真实归档正是这样(各季表的内容逐季变长)。
206
+ # 只填本季度会造出「进度标 1、实际日期却空着」的自相矛盾——
207
+ # 那恰恰是本项目的进度表体检会报的必修项。
208
+ done_by_now = bool(actual_quarter and _quarter_of(end)
209
+ and _quarter_of(end) <= actual_quarter)
210
+ if actual_quarter:
211
+ r += [est if done_by_now else '', start if done_by_now else '',
212
+ end if done_by_now else '']
213
+ r += [1 if done_by_now else 0, '']
214
+ rows.append(r)
215
+ title = f"{card['项目名称']}项目研发进度{'计划' if not actual_quarter else f'表-实际-第{actual_quarter}季度'}表"
216
+ X.build({'profile': 'plain', 'sheets': [{
217
+ 'name': '任务计划', 'title': title,
218
+ 'columns': [{'key': k, 'header': h,
219
+ 'format': 'int' if k in ('est', 'act_h', 'seq') else
220
+ ('percent' if k == 'pct' else
221
+ ('date' if k in ('start', 'end', 'act_s', 'act_e') else 'general'))}
222
+ for k, h in cols],
223
+ 'rows': rows, 'total': {'label': '合计', 'cols': ['est'] + (['act_h'] if actual_quarter else [])},
224
+ }], 'inspect': False}, path)
225
+ return path
226
+
227
+
228
+ def _quarter_of(d):
229
+ m = re.search(r'(?:^|\D)(\d{1,2})\s*[-/月]', str(d or ''))
230
+ if not m:
231
+ return None
232
+ mm = int(m.group(1))
233
+ return (mm - 1) // 3 + 1 if 1 <= mm <= 12 else None
234
+
235
+
236
+ def budget_unmatched(card):
237
+ """档案卡里对不上固定科目表的预算科目。
238
+
239
+ 预算表的科目是**研发费用归集口径的固定一套**(模板原样),卡里写「人员费/设备费」
240
+ 这类自造名字一个都对不上,`plan.get(name, 0)` 会让它们**静默变零**——
241
+ 于是卡上预算 280 万、科目加总也 280 万(内部勾稽还是通过的),
242
+ 生成出来的预算表却整张全零,没有任何提示。这是最难查的一类失败。
243
+ 不做同义词猜测:「设备费」在这套口径里没有对应行(它属于折旧费用),
244
+ 猜错的科目归集比留空更糟——只如实点名,把正确科目表给出来。
245
+ """
246
+ return [str(k) for k, _v in (card.get('预算科目') or []) if str(k) not in BUDGET_ROWS]
247
+
248
+
249
+ def _write_budget(card, path):
250
+ X = _sibling('xlsx-analysis', 'xlsx_build')
251
+ plan = {str(k): float(v) for k, v in (card.get('预算科目') or [])}
252
+ rows = []
253
+ for name in BUDGET_ROWS:
254
+ total = round(plan.get(name, 0.0), 2)
255
+ # 12 个月按四舍五入平摊后**加起来往往不等于全年**(2600000/12×12 = 2600000.04)。
256
+ # 差额必须由末月吸收——预算表最忌讳的就是横向加总对不上全年数,
257
+ # 财务复核第一件事就是拿计算器横着加一遍。
258
+ per = round(total / 12, 2) if total else 0.0
259
+ months = [per] * 11 + [round(total - per * 11, 2)] if total else [0.0] * 12
260
+ rows.append([name] + months + [total])
261
+ cols = ([{'key': 'sub', 'header': '科目'}]
262
+ + [{'key': f'm{i}', 'header': f'{i}月', 'format': 'amount'} for i in range(1, 13)]
263
+ + [{'key': 'sum', 'header': '合计(元)', 'format': 'amount'}])
264
+ X.build({'profile': 'finance', 'sheets': [{
265
+ 'name': '研发支出预算表', 'title': f"{card['部门']}{card['年度']}年度项目研发支出预算表",
266
+ 'note': f"项目:{card['项目名称']}({card['项目编号']}) 口径:研发费用归集口径,单位:元",
267
+ 'columns': cols, 'rows': rows, 'total': {'label': '小计'},
268
+ 'print': {'landscape': True},
269
+ }], 'inspect': False}, path)
270
+ return path
271
+
272
+
273
+ REVIEW_ITEMS = ['立项申请书', '项目建议书', '可行性分析报告', '产品研发任务书',
274
+ '研发实施计划', '总体预算', '总体研发进度表']
275
+
276
+
277
+ def _write_review(card, stage, path):
278
+ X = _sibling('xlsx-analysis', 'xlsx_build')
279
+ X.build({'profile': 'plain', 'sheets': [
280
+ {'name': '评审说明', 'title': f'{stage}评审报告',
281
+ 'columns': [{'key': 'k', 'header': '项目'}, {'key': 'v', 'header': '内容'}],
282
+ 'rows': [['项目名称', card['项目名称']], ['项目编号', card['项目编号']],
283
+ ['项目经理', card['项目负责人']], ['评审方式', '中心正式评审'],
284
+ ['评审内容', ';'.join(REVIEW_ITEMS)], ['评审日期', ''], ['评审地点', ''],
285
+ ['评审准备总工作量', ''], ['评审会议时间长度', ''], ['问题跟踪工作量', '']]},
286
+ {'name': '评审小组',
287
+ 'columns': [{'key': 'role', 'header': '角色'}, {'key': 'name', 'header': '姓名(电话)'},
288
+ {'key': 'prep', 'header': '评审准备工作量(小时)', 'format': 'int'},
289
+ {'key': 'meet', 'header': '评审会议工作量(小时)', 'format': 'int'}],
290
+ 'rows': [[r, '', 0, 0] for r in ('主持人,评审人', '作者', '记录员,评审人',
291
+ '跟踪人,评审人', '评审人', '评审人')],
292
+ 'total': {'label': '合计'}},
293
+ {'name': '评审记录',
294
+ 'columns': [{'key': c, 'header': h} for c, h in
295
+ [('id', 'ID'), ('desc', '问题描述'), ('by', '提出人'), ('fix', '解决建议'),
296
+ ('way', '解决方式'), ('sev', '严重程度'), ('due', '预计解决时间'),
297
+ ('who', '修改人'), ('track', '跟踪人'), ('sign', '跟踪意见、签字')]],
298
+ 'rows': [[i] + [''] * 9 for i in range(1, 6)]},
299
+ {'name': '评审结论',
300
+ 'columns': [{'key': 'k', 'header': '评审内容'}, {'key': 'v', 'header': '评审结论'}],
301
+ 'rows': [[it, ''] for it in REVIEW_ITEMS] + [['是否需要重新评审', ''], ['缺陷跟踪计划', '']]},
302
+ ], 'inspect': False}, path)
303
+ return path
304
+
305
+
306
+ # ── 周期性文档:内容从里程碑推导(这一层才是「自动化」的实质)──────────────
307
+ def _month_progress(card, month):
308
+ """按里程碑推出这一期的进度叙述——真实进度报告里的「项目进度情况」正是这么写的。
309
+ 没有里程碑数据就返回空,让人去写,**不编内容**。"""
310
+ ms = card.get('里程碑') or []
311
+ if not ms:
312
+ return '', ''
313
+ y = card['年度']
314
+ done, doing = [], []
315
+ for m in ms:
316
+ stage, task, owner, _est, start, end = (list(m) + [''] * 6)[:6]
317
+ em, sm = _month_of(end), _month_of(start)
318
+ if em and em <= month:
319
+ done.append(f'{stage}·{task}')
320
+ elif sm and sm <= month <= (em or 12):
321
+ doing.append(f'{stage}·{task}({owner})')
322
+ pct = round(len(done) / max(len(ms), 1) * 100)
323
+ last = calendar.monthrange(y, month)[1]
324
+ txt = (f'截至 {y} 年 {month} 月 {last} 日,项目累计完成 {len(done)}/{len(ms)} 项任务,'
325
+ f'整体进度约 {pct}%。')
326
+ if done:
327
+ txt += '本期已完成:' + ';'.join(done[-6:]) + '。'
328
+ if doing:
329
+ txt += '正在进行:' + ';'.join(doing[:6]) + '。'
330
+ return txt, f'{y}年{month:02d}月31日' if last == 31 else f'{y}年{month:02d}月{last}日'
331
+
332
+
333
+ def _month_of(d):
334
+ m = re.search(r'(?:^|\D)(\d{1,2})\s*[-/月]', str(d or ''))
335
+ if not m:
336
+ return None
337
+ v = int(m.group(1))
338
+ return v if 1 <= v <= 12 else None
339
+
340
+
341
+ def _period_end(year, n, count):
342
+ """这一期的落款日期:月度取当月最后一天,季度取季末最后一天。
343
+
344
+ 十二期月报全盖「生成当天」是不对的——真实月报各自落在当期月末,
345
+ 档案体检会(正确地)报「落款月份与期号对不上」。日期本来就算出来了,别丢掉。"""
346
+ if count == 12:
347
+ return year, n, calendar.monthrange(year, n)[1]
348
+ if count == 4:
349
+ m = n * 3
350
+ return year, m, calendar.monthrange(year, m)[1]
351
+ return None
352
+
353
+
354
+ def _write_periodic(card, spec, root, kind, dirname, pattern, count):
355
+ made = []
356
+ for n in range(1, count + 1):
357
+ txt, _asof = _month_progress(card, n) if count == 12 else ('', '')
358
+ body = {'编写目的': f"通过本报告了解 {card['项目名称']} 在 {card['年度']} 年第 {n} "
359
+ f"{'月' if count == 12 else '期'}的研发进度与经费执行情况。",
360
+ '项目进度情况': txt}
361
+ c2 = dict(card)
362
+ c2['章节内容'] = dict(card.get('章节内容') or {})
363
+ c2['章节内容'][kind] = {**(c2['章节内容'].get(kind) or {}), **{k: v for k, v in body.items() if v}}
364
+ pe = _period_end(card['年度'], n, count)
365
+ if pe:
366
+ c2['date'] = f'{pe[0]} 年 {pe[1]} 月 {pe[2]} 日'
367
+ p = os.path.join(root, dirname, pattern.format(n=n))
368
+ _write_doc(kind, c2, spec, p,
369
+ title=f"{card['项目名称']}\n{kind}({card['年度']}年{n:02d}月)")
370
+ made.append(p)
371
+ return made
372
+
373
+
374
+ # ── 主流程 ─────────────────────────────────────────────────────────────────
375
+ def build(card, out_root, spec_name='hengtong', only=None):
376
+ spec = S.load(spec_name)
377
+ card = normalize_card(card)
378
+ root = os.path.join(out_root, safe_name(card['项目名称']))
379
+ os.makedirs(root, exist_ok=True)
380
+ made, warns = [], []
381
+ want = set(only) if only else None
382
+
383
+ def need(kind):
384
+ return want is None or kind in want
385
+
386
+ bad_subs = budget_unmatched(card)
387
+ if bad_subs:
388
+ warns.append(f'档案卡的「预算科目」有 {len(bad_subs)} 项对不上研发费用归集口径 {bad_subs},'
389
+ f'这些金额不会进预算表(该行会是 0);'
390
+ f'可用科目:{BUDGET_ROWS}')
391
+ subs = sum(float(v) for _k, v in (card.get('预算科目') or []))
392
+ total = float(card.get('预算') or 0)
393
+ if subs and total and abs(subs - total) > max(1.0, total * 1e-6):
394
+ warns.append(f'预算科目加总 {subs:,.2f} 与档案卡的预算总额 {total:,.2f} 不符'
395
+ f'(差 {subs - total:,.2f})——这类数字在立项书、预算表、验收决算里都要出现,'
396
+ f'先在卡上对平再生成')
397
+
398
+ known = set(spec['sections']) | {'研发进度报告', '月度验收报告', '评审签到表'}
399
+ unknown_kinds = [k for k in (card.get('章节内容') or {}) if k not in known]
400
+ if unknown_kinds:
401
+ # 可用类型**列全,不截断**。真跑里模型写了「项目工作总结报告」(正确是「工作总结报告」),
402
+ # 提示里的类型表被 `…` 截掉,它拿不到对的名字,只能反复猜——
403
+ # 报错说了「你写错了」却不说对的是什么,等于没报。
404
+ warns.append(f'档案卡的「章节内容」里有未收录的文档类型 {unknown_kinds},这部分内容不会被使用;'
405
+ f'可用类型:{sorted(known)}')
406
+
407
+ ind_rows = [['序号', '考核指标', '要求值', '测试方法/判定依据']] + \
408
+ [[i] + list(x)[:3] for i, x in enumerate(card['考核指标'], 1)]
409
+ ppl_rows = [['姓名', '单位', '职称/职务', '分工']] + [list(x)[:4] for x in card['人员']]
410
+
411
+ ind_tbl = {'type': 'table', 'rows': ind_rows, 'caption': '项目考核指标'}
412
+ ppl_tbl = {'type': 'table', 'rows': ppl_rows, 'caption': '项目主要参加人员'}
413
+ # 交付物清单必须落进计划类文档:它是「任务书 → 实施计划 → 验收报告」那条勾稽链的起点,
414
+ # 只躺在档案卡里等于没有——验收时按什么逐条核对?
415
+ dlv_rows = [['序号', '阶段', '任务', '交付物', '计划完成']] + [
416
+ [i, (list(m) + [''] * 7)[0], (list(m) + [''] * 7)[1],
417
+ (list(m) + [''] * 7)[6], (list(m) + [''] * 7)[5]]
418
+ for i, m in enumerate(card['里程碑'], 1)]
419
+ dlv_tbl = {'type': 'table', 'rows': dlv_rows, 'caption': '交付物与里程碑'}
420
+ plan = {
421
+ '项目建议书': ('1-立项文档/1-项目建议书.docx',
422
+ {'项目团队': [ppl_tbl]}, []),
423
+ '可行性研究报告': ('1-立项文档/2-可行性研究报告.docx',
424
+ {'项目实施主要内容、技术关键与创新点、预期目标': [ind_tbl],
425
+ '承担单位和主要研究人员简况': [ppl_tbl]}, []),
426
+ '立项申请书': ('1-立项文档/3-立项申请书.docx',
427
+ {'项目考核指标': [ind_tbl], '项目主要参加人员': [ppl_tbl]}, []),
428
+ '项目立项的决议': ('1-立项文档/4-项目立项的决议.docx', {}, [
429
+ {'type': 'p', 'text': f"经公司管理层会议决定,同意 {card['项目名称']}"
430
+ f"(项目编号 {card['项目编号']})立项,研究开发起止时间为"
431
+ f"{card['起止时间']},由{card['部门']}承担实施;"
432
+ f"财务部门设置研究开发项目费用核算账目,实行专账管理。"}]),
433
+ '产品开发需求申请': ('产品开发需求申请.docx', {}, []),
434
+ '项目开发需求申请': ('项目开发需求申请.docx', {}, []),
435
+ '研发任务书': ('5-研发任务书.docx',
436
+ {'目标及要求': [{**ind_tbl, 'caption': '技术指标与判定依据'}],
437
+ '项目人员': [{**ppl_tbl, 'caption': '项目人员'}],
438
+ '项目开发计划': [dlv_tbl]}, []),
439
+ '项目研发实施计划': ('6-项目研发实施计划.docx',
440
+ {'主要参与人员': [{**ppl_tbl, 'caption': '主要参与人员'}],
441
+ '产品及成果': [{**dlv_tbl, 'caption': '交付物清单'}]}, []),
442
+ '项目验收申请表': ('9-10验收/项目验收申请表.docx', {}, []),
443
+ '项目验收报告': ('9-10验收/项目验收报告.docx',
444
+ {'考核技术指标': [{'type': 'table', 'caption': '考核指标验收对照',
445
+ 'rows': [['立项书规定技术指标', '实际完成情况', '专家审查意见']]
446
+ + [[f'{x[0]} {x[1] if len(x) > 1 else ""}', '', ''] for x in card['考核指标']]}],
447
+ '项目主要参加人员名单': [{**ppl_tbl, 'caption': '项目主要参加人员名单'}]}, []),
448
+ '工作总结报告': ('9-10验收/工作总结报告.docx',
449
+ {'项目考核指标完成情况': [{'type': 'table', 'caption': '考核指标完成情况',
450
+ 'rows': [['序号', '合同规定', '实际完成内容', '完成评价']]
451
+ + [[i, f'{x[0]} {x[1] if len(x) > 1 else ""}', '', ''] for i, x in enumerate(card['考核指标'], 1)]}]}, []),
452
+ '技术总结报告': ('9-10验收/技术总结报告.docx', {}, []),
453
+ '研发评价总结纪要': ('9-10验收/研发评价总结纪要.docx', {}, []),
454
+ }
455
+ for kind, (rel, ins, tail) in plan.items():
456
+ if not need(kind):
457
+ continue
458
+ try:
459
+ made.append(_write_doc(kind, card, spec, os.path.join(root, rel),
460
+ inserts=ins, tail=tail, warns=warns))
461
+ except Exception as e:
462
+ warns.append(f'{kind} 生成失败:{type(e).__name__}: {e}')
463
+
464
+ if need('项目研发进度表-计划'):
465
+ try:
466
+ made.append(_write_plan(card, os.path.join(root, '7-进度表', '1-项目研发进度表-计划.xlsx')))
467
+ for q in range(1, 5):
468
+ made.append(_write_plan(card, os.path.join(
469
+ root, '7-进度表', f'1-项目研发进度表-实际-第{q}季度.xlsx'), actual_quarter=q))
470
+ except Exception as e:
471
+ warns.append(f'进度表生成失败:{type(e).__name__}: {e}')
472
+ if need('年度研发支出预算表'):
473
+ try:
474
+ made.append(_write_budget(card, os.path.join(
475
+ root, safe_name(f"8-{card['部门']}{card['年度']}年度项目研发支出预算表", 120)
476
+ + '.xlsx')))
477
+ except Exception as e:
478
+ warns.append(f'预算表生成失败:{type(e).__name__}: {e}')
479
+ for i, r in enumerate(spec['reviews'], 1):
480
+ if not need('评审文件'):
481
+ break
482
+ try:
483
+ made.append(_write_review(card, r['stage'], os.path.join(
484
+ root, '评审文件', f"{i}-{r['stage']}评审报告.xlsx")))
485
+ made.append(_write_doc('评审签到表', card, spec, os.path.join(
486
+ root, '评审文件', f"{i}-{r['stage']}评审会议签到表.docx"),
487
+ title=f"{card['项目名称']}\n{r['stage']}评审会议签到表",
488
+ tail=[{'type': 'table', 'caption': '与会人员签到',
489
+ 'rows': [['序号', '姓名', '部门', '职务', '签到']] + [[i2, '', '', '', ''] for i2 in range(1, 11)]}]))
490
+ except Exception as e:
491
+ warns.append(f"{r['stage']}评审文件生成失败:{type(e).__name__}: {e}")
492
+ for p in spec['periodic']:
493
+ if p['kind'].startswith('项目研发进度表'):
494
+ continue # 进度表在上面按季度单独出
495
+ # **选做的周期性文档不默认生成**:规范目录里列了周报(52 期)与月度开发报告,
496
+ # 但两个真实项目都没做。默认全生成会凭空多出六十多份没人要的文件。
497
+ # 想要就在 only 里点名。
498
+ if not p.get('required', True) and (want is None or p['kind'] not in want):
499
+ continue
500
+ if not need(p['kind']):
501
+ continue
502
+ if not str(p['pattern']).lower().endswith('.docx'):
503
+ # 这一层只会用 docx 建版器。把 docx 内容写进 .xlsx 文件名,
504
+ # 落盘时不报错,打开时才发现「不是有效的工作簿」——必须在这里挡住。
505
+ warns.append(f"{p['kind']} 的规范产物是 {os.path.splitext(p['pattern'])[1]} 而非 .docx,"
506
+ f"本工具暂不生成,请按模板手工补")
507
+ continue
508
+ try:
509
+ made += _write_periodic(card, spec, root, p['kind'], p['dir'],
510
+ p['pattern'], p['count'])
511
+ except Exception as e:
512
+ warns.append(f"{p['kind']} 生成失败:{type(e).__name__}: {e}")
513
+
514
+ if want is None:
515
+ try:
516
+ made.append(_write_manifest(card, root, made))
517
+ except Exception as e:
518
+ warns.append(f'档案清册生成失败:{type(e).__name__}: {e}')
519
+ return root, made, warns
520
+
521
+
522
+ def _write_manifest(card, root, made):
523
+ X = _sibling('xlsx-analysis', 'xlsx_build')
524
+ rows = []
525
+ for i, p in enumerate(sorted(set(made)), 1):
526
+ rel = os.path.relpath(p, root)
527
+ rows.append([i, os.path.dirname(rel) or '(根目录)', os.path.basename(rel),
528
+ S.kind_of(os.path.basename(rel)) or '—',
529
+ round(os.path.getsize(p) / 1024, 1) if os.path.exists(p) else 0, ''])
530
+ path = os.path.join(root, '档案清册.xlsx')
531
+ X.build({'profile': 'report', 'sheets': [{
532
+ 'name': '档案清册', 'title': f"{card['项目名称']} 档案清册",
533
+ 'note': f"项目编号 {card['项目编号']} 起止 {card['起止时间']} 编制 {card['编制人']}",
534
+ 'columns': [{'key': 'no', 'header': '序号', 'format': 'int'},
535
+ {'key': 'dir', 'header': '所在目录'}, {'key': 'f', 'header': '文件名'},
536
+ {'key': 'kind', 'header': '文档类型'},
537
+ {'key': 'kb', 'header': '大小(KB)', 'format': 'amount'},
538
+ {'key': 'memo', 'header': '备注'}],
539
+ 'rows': rows, 'total': {'label': '合计', 'cols': ['kb']},
540
+ }], 'inspect': False}, path)
541
+ return path
542
+
543
+
544
+ def main(argv):
545
+ args = [a for a in argv if not a.startswith('--')]
546
+ if len(args) < 2:
547
+ print('用法:python archive_build.py 档案卡.json 输出目录 [--only 类型1,类型2] [--demo]')
548
+ return 2
549
+ only = None
550
+ for a in argv:
551
+ if a.startswith('--only'):
552
+ only = (a.split('=', 1)[1] if '=' in a else args[2] if len(args) > 2 else '').split(',')
553
+ only = [x.strip() for x in only if x.strip()] or None
554
+ card = json.load(open(args[0], encoding='utf-8'))
555
+ try:
556
+ root, made, warns = build(card, args[1], only=only)
557
+ except ValueError as e:
558
+ # 档案卡填得不对是**用户输入问题**,报「缺了哪几项」就够了。
559
+ # 前面缀个 ValueError: 只会让人以为是工具坏了——本项目的报错口径是「结论 + 下一步」。
560
+ print(json.dumps({'error': str(e)}, ensure_ascii=False))
561
+ return 1
562
+ except Exception as e:
563
+ print(json.dumps({'error': f'{type(e).__name__}: {e}'}, ensure_ascii=False))
564
+ return 1
565
+ print(json.dumps({'root': root, 'files': len(made), 'warnings': warns,
566
+ 'made': [os.path.relpath(p, root) for p in made]},
567
+ ensure_ascii=False))
568
+ return 0
569
+
570
+
571
+ DEMO_CARD = {
572
+ '项目名称': '基于知识图谱的智能客服系统研发', '项目编号': 'ZY202601001',
573
+ '项目负责人': '张工', '联系电话': '13800000000',
574
+ '建设单位': '示例科技股份有限公司', '部门': '研发一部',
575
+ '起止时间': '2026年1月1日至2026年12月31日', '预算': 3800000,
576
+ '考核指标': [['意图识别准确率', '≥ 92%', '标注集 5000 条离线评测'],
577
+ ['平均响应时延', '≤ 300ms', '800 并发压测 P95'],
578
+ ['人工转接率', '≤ 15%', '生产环境连续 30 天统计']],
579
+ '人员': [['张工', '研发一部', '高级工程师', '项目负责人'],
580
+ ['李工', '研发一部', '工程师', '算法开发'],
581
+ ['王工', '测试部', '工程师', '测试验证']],
582
+ '里程碑': [['需求分析', '需求调研与规格说明', '张工', 80, '2026-01-01', '2026-01-31', '需求规格说明书'],
583
+ ['设计', '总体与详细设计', '张工', 120, '2026-02-01', '2026-03-31', '设计说明书'],
584
+ ['开发', '知识图谱构建与对话引擎', '李工', 480, '2026-04-01', '2026-08-31', '可运行系统'],
585
+ ['测试', '功能与性能测试', '王工', 160, '2026-09-01', '2026-10-31', '测试报告'],
586
+ ['验收', '试运行与验收', '张工', 80, '2026-11-01', '2026-12-31', '验收报告']],
587
+ '预算科目': [['其中:人员工资', 2600000], ['服务费', 400000], ['测试调试费', 300000],
588
+ ['办公及通讯费', 200000], ['差旅及交通费', 300000]],
589
+ }
590
+
591
+ if __name__ == '__main__':
592
+ if '--demo' in sys.argv:
593
+ out = [a for a in sys.argv[1:] if not a.startswith('--')] or ['.']
594
+ r, m, w = build(DEMO_CARD, out[0])
595
+ print(f'demo → {r}({len(m)} 份)')
596
+ if w:
597
+ print('提示:' + ';'.join(w))
598
+ sys.exit(0)
599
+ sys.exit(main(sys.argv[1:]))