adspecs 0.1.42 → 0.2.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 (454) hide show
  1. package/.adspecs/scripts/powershell/check-prerequisites.ps1 +3 -3
  2. package/.adspecs/scripts/powershell/setup-tasks.ps1 +2 -2
  3. package/.adspecs/templates/01-/344/270/232/345/212/241/351/234/200/346/261/202/345/210/206/346/236/220/346/250/241/346/235/277.md +3 -3
  4. package/.adspecs/templates/02-/344/272/247/345/223/201/351/234/200/346/261/202/350/257/264/346/230/216/344/271/246/346/250/241/346/235/277.md +1 -1
  5. package/.adspecs/templates/03-/347/263/273/347/273/237/350/256/276/350/256/241/346/250/241/346/235/277.md +1 -1
  6. package/.adspecs/templates/05-/345/220/216/347/253/257/344/273/273/345/212/241/346/270/205/345/215/225/346/250/241/346/235/277.md +613 -613
  7. package/.adspecs/templates/05b-/345/211/215/347/253/257/344/273/273/345/212/241/346/270/205/345/215/225/346/250/241/346/235/277.md +583 -583
  8. package/.adspecs/templates/14-/351/234/200/346/261/202/350/277/275/346/272/257/347/237/251/351/230/265/346/250/241/346/235/277.md +1 -1
  9. package/.claude-plugin/marketplace.json +23 -23
  10. package/.claude-plugin/plugin.json +18 -18
  11. package/.codebuddy-plugin/plugin.json +24 -24
  12. package/.qoder-plugin/plugin.json +30 -30
  13. package/.workbuddy-plugin/plugin.json +24 -24
  14. package/CHANGELOG.md +10 -0
  15. package/CLAUDE.md +45 -45
  16. package/README.md +51 -51
  17. package/hooks/commit-queue.js +245 -245
  18. package/hooks/session-logger.js +346 -346
  19. package/package.json +65 -65
  20. package/skills/{adspecs-adversarial-review → ads-adversarial-review}/SKILL.md +8 -8
  21. package/skills/{adspecs-analyze → ads-analyze}/SKILL.md +564 -564
  22. package/skills/{adspecs-architecture-planning → ads-architecture-planning}/SKILL.md +9 -9
  23. package/skills/{adspecs-biz-blueprint → ads-biz-blueprint}/SKILL.md +18 -18
  24. package/skills/{adspecs-clarify → ads-clarify}/SKILL.md +7 -7
  25. package/skills/{adspecs-constitution → ads-constitution}/SKILL.md +3 -3
  26. package/skills/{adspecs-export-word → ads-export-word}/SKILL.md +9 -9
  27. package/skills/{adspecs-export-word → ads-export-word}/references/md-to-docx.js +862 -862
  28. package/skills/ads-export-word/references/package-lock.json +220 -0
  29. package/skills/{adspecs-export-word → ads-export-word}/references/package.json +10 -10
  30. package/skills/{adspecs-front-prototype → ads-front-prototype}/SKILL.md +8 -8
  31. package/skills/{adspecs-front-spec → ads-front-spec}/SKILL.md +12 -12
  32. package/skills/{adspecs-front-tasks → ads-front-tasks}/SKILL.md +5 -5
  33. package/skills/{adspecs-git-commit → ads-git-commit}/SKILL.md +1 -1
  34. package/skills/{adspecs-git-initialize → ads-git-initialize}/SKILL.md +1 -1
  35. package/skills/{adspecs-new-requirement → ads-new-requirement}/SKILL.md +11 -11
  36. package/skills/{adspecs-plan → ads-plan}/SKILL.md +6 -6
  37. package/skills/{adspecs-prd → ads-prd}/SKILL.md +9 -9
  38. package/skills/{adspecs-prd-to-demo → ads-prd-to-demo}/SKILL.md +19 -19
  39. package/skills/{adspecs-requirement-research → ads-requirement-research}/SKILL.md +15 -3
  40. package/skills/{adspecs-save-session → ads-save-session}/SKILL.md +1 -1
  41. package/skills/{adspecs-tasks → ads-tasks}/SKILL.md +5 -5
  42. package/skills/{adspecs-update-status → ads-update-status}/SKILL.md +7 -7
  43. package/skills/{adspecs-utest → ads-utest}/SKILL.md +7 -7
  44. package/skills/{adspecs-write-planning-report → ads-write-planning-report}/SKILL.md +2 -2
  45. package/skills/{adspecs-write-roadmap → ads-write-roadmap}/SKILL.md +2 -2
  46. package/skills/{adspecs-write-sow → ads-write-sow}/SKILL.md +5 -5
  47. package/skills/adspecs-export-word/references/node_modules/.bin/marked +16 -0
  48. package/skills/adspecs-export-word/references/node_modules/.bin/marked.cmd +17 -0
  49. package/skills/adspecs-export-word/references/node_modules/.bin/marked.ps1 +28 -0
  50. package/skills/adspecs-export-word/references/node_modules/.bin/nanoid +16 -0
  51. package/skills/adspecs-export-word/references/node_modules/.bin/nanoid.cmd +17 -0
  52. package/skills/adspecs-export-word/references/node_modules/.bin/nanoid.ps1 +28 -0
  53. package/skills/adspecs-export-word/references/node_modules/.bin/xml-js +16 -0
  54. package/skills/adspecs-export-word/references/node_modules/.bin/xml-js.cmd +17 -0
  55. package/skills/adspecs-export-word/references/node_modules/.bin/xml-js.ps1 +28 -0
  56. package/skills/adspecs-export-word/references/{package-lock.json → node_modules/.package-lock.json} +0 -8
  57. package/skills/adspecs-export-word/references/node_modules/@types/node/LICENSE +21 -0
  58. package/skills/adspecs-export-word/references/node_modules/@types/node/README.md +15 -0
  59. package/skills/adspecs-export-word/references/node_modules/@types/node/assert/strict.d.ts +59 -0
  60. package/skills/adspecs-export-word/references/node_modules/@types/node/assert.d.ts +950 -0
  61. package/skills/adspecs-export-word/references/node_modules/@types/node/async_hooks.d.ts +711 -0
  62. package/skills/adspecs-export-word/references/node_modules/@types/node/buffer.buffer.d.ts +466 -0
  63. package/skills/adspecs-export-word/references/node_modules/@types/node/buffer.d.ts +1765 -0
  64. package/skills/adspecs-export-word/references/node_modules/@types/node/child_process.d.ts +1366 -0
  65. package/skills/adspecs-export-word/references/node_modules/@types/node/cluster.d.ts +432 -0
  66. package/skills/adspecs-export-word/references/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  67. package/skills/adspecs-export-word/references/node_modules/@types/node/console.d.ts +93 -0
  68. package/skills/adspecs-export-word/references/node_modules/@types/node/constants.d.ts +14 -0
  69. package/skills/adspecs-export-word/references/node_modules/@types/node/crypto.d.ts +4058 -0
  70. package/skills/adspecs-export-word/references/node_modules/@types/node/dgram.d.ts +537 -0
  71. package/skills/adspecs-export-word/references/node_modules/@types/node/diagnostics_channel.d.ts +552 -0
  72. package/skills/adspecs-export-word/references/node_modules/@types/node/dns/promises.d.ts +497 -0
  73. package/skills/adspecs-export-word/references/node_modules/@types/node/dns.d.ts +876 -0
  74. package/skills/adspecs-export-word/references/node_modules/@types/node/domain.d.ts +150 -0
  75. package/skills/adspecs-export-word/references/node_modules/@types/node/events.d.ts +1008 -0
  76. package/skills/adspecs-export-word/references/node_modules/@types/node/fs/promises.d.ts +1468 -0
  77. package/skills/adspecs-export-word/references/node_modules/@types/node/fs.d.ts +4780 -0
  78. package/skills/adspecs-export-word/references/node_modules/@types/node/globals.d.ts +150 -0
  79. package/skills/adspecs-export-word/references/node_modules/@types/node/globals.typedarray.d.ts +101 -0
  80. package/skills/adspecs-export-word/references/node_modules/@types/node/http.d.ts +2147 -0
  81. package/skills/adspecs-export-word/references/node_modules/@types/node/http2.d.ts +2489 -0
  82. package/skills/adspecs-export-word/references/node_modules/@types/node/https.d.ts +400 -0
  83. package/skills/adspecs-export-word/references/node_modules/@types/node/index.d.ts +117 -0
  84. package/skills/adspecs-export-word/references/node_modules/@types/node/inspector/promises.d.ts +35 -0
  85. package/skills/adspecs-export-word/references/node_modules/@types/node/inspector.d.ts +264 -0
  86. package/skills/adspecs-export-word/references/node_modules/@types/node/inspector.generated.d.ts +4406 -0
  87. package/skills/adspecs-export-word/references/node_modules/@types/node/module.d.ts +755 -0
  88. package/skills/adspecs-export-word/references/node_modules/@types/node/net.d.ts +970 -0
  89. package/skills/adspecs-export-word/references/node_modules/@types/node/os.d.ts +498 -0
  90. package/skills/adspecs-export-word/references/node_modules/@types/node/package.json +155 -0
  91. package/skills/adspecs-export-word/references/node_modules/@types/node/path/posix.d.ts +8 -0
  92. package/skills/adspecs-export-word/references/node_modules/@types/node/path/win32.d.ts +8 -0
  93. package/skills/adspecs-export-word/references/node_modules/@types/node/path.d.ts +178 -0
  94. package/skills/adspecs-export-word/references/node_modules/@types/node/perf_hooks.d.ts +612 -0
  95. package/skills/adspecs-export-word/references/node_modules/@types/node/process.d.ts +2206 -0
  96. package/skills/adspecs-export-word/references/node_modules/@types/node/punycode.d.ts +89 -0
  97. package/skills/adspecs-export-word/references/node_modules/@types/node/querystring.d.ts +139 -0
  98. package/skills/adspecs-export-word/references/node_modules/@types/node/quic.d.ts +897 -0
  99. package/skills/adspecs-export-word/references/node_modules/@types/node/readline/promises.d.ts +158 -0
  100. package/skills/adspecs-export-word/references/node_modules/@types/node/readline.d.ts +507 -0
  101. package/skills/adspecs-export-word/references/node_modules/@types/node/repl.d.ts +420 -0
  102. package/skills/adspecs-export-word/references/node_modules/@types/node/sea.d.ts +47 -0
  103. package/skills/adspecs-export-word/references/node_modules/@types/node/sqlite.d.ts +1068 -0
  104. package/skills/adspecs-export-word/references/node_modules/@types/node/stream/consumers.d.ts +114 -0
  105. package/skills/adspecs-export-word/references/node_modules/@types/node/stream/iter.d.ts +301 -0
  106. package/skills/adspecs-export-word/references/node_modules/@types/node/stream/promises.d.ts +211 -0
  107. package/skills/adspecs-export-word/references/node_modules/@types/node/stream/web.d.ts +300 -0
  108. package/skills/adspecs-export-word/references/node_modules/@types/node/stream.d.ts +1774 -0
  109. package/skills/adspecs-export-word/references/node_modules/@types/node/string_decoder.d.ts +27 -0
  110. package/skills/adspecs-export-word/references/node_modules/@types/node/test/reporters.d.ts +59 -0
  111. package/skills/adspecs-export-word/references/node_modules/@types/node/test.d.ts +2279 -0
  112. package/skills/adspecs-export-word/references/node_modules/@types/node/timers/promises.d.ts +93 -0
  113. package/skills/adspecs-export-word/references/node_modules/@types/node/timers.d.ts +149 -0
  114. package/skills/adspecs-export-word/references/node_modules/@types/node/tls.d.ts +1193 -0
  115. package/skills/adspecs-export-word/references/node_modules/@types/node/trace_events.d.ts +103 -0
  116. package/skills/adspecs-export-word/references/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
  117. package/skills/adspecs-export-word/references/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  118. package/skills/adspecs-export-word/references/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  119. package/skills/adspecs-export-word/references/node_modules/@types/node/ts5.6/index.d.ts +119 -0
  120. package/skills/adspecs-export-word/references/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  121. package/skills/adspecs-export-word/references/node_modules/@types/node/ts5.7/index.d.ts +119 -0
  122. package/skills/adspecs-export-word/references/node_modules/@types/node/tty.d.ts +225 -0
  123. package/skills/adspecs-export-word/references/node_modules/@types/node/url.d.ts +556 -0
  124. package/skills/adspecs-export-word/references/node_modules/@types/node/util/types.d.ts +558 -0
  125. package/skills/adspecs-export-word/references/node_modules/@types/node/util.d.ts +1677 -0
  126. package/skills/adspecs-export-word/references/node_modules/@types/node/v8.d.ts +980 -0
  127. package/skills/adspecs-export-word/references/node_modules/@types/node/vm.d.ts +1136 -0
  128. package/skills/adspecs-export-word/references/node_modules/@types/node/wasi.d.ts +131 -0
  129. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
  130. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/blob.d.ts +23 -0
  131. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/console.d.ts +9 -0
  132. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
  133. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  134. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
  135. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/events.d.ts +106 -0
  136. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/fetch.d.ts +69 -0
  137. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
  138. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
  139. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  140. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/performance.d.ts +45 -0
  141. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  142. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/streams.d.ts +115 -0
  143. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/timers.d.ts +44 -0
  144. package/skills/adspecs-export-word/references/node_modules/@types/node/web-globals/url.d.ts +24 -0
  145. package/skills/adspecs-export-word/references/node_modules/@types/node/worker_threads.d.ts +671 -0
  146. package/skills/adspecs-export-word/references/node_modules/@types/node/zlib/iter.d.ts +131 -0
  147. package/skills/adspecs-export-word/references/node_modules/@types/node/zlib.d.ts +589 -0
  148. package/skills/adspecs-export-word/references/node_modules/core-util-is/LICENSE +19 -0
  149. package/skills/adspecs-export-word/references/node_modules/core-util-is/README.md +3 -0
  150. package/skills/adspecs-export-word/references/node_modules/core-util-is/lib/util.js +107 -0
  151. package/skills/adspecs-export-word/references/node_modules/core-util-is/package.json +38 -0
  152. package/skills/adspecs-export-word/references/node_modules/docx/LICENSE +21 -0
  153. package/skills/adspecs-export-word/references/node_modules/docx/README.md +124 -0
  154. package/skills/adspecs-export-word/references/node_modules/docx/dist/index.cjs +33170 -0
  155. package/skills/adspecs-export-word/references/node_modules/docx/dist/index.d.cts +3214 -0
  156. package/skills/adspecs-export-word/references/node_modules/docx/dist/index.d.ts +3214 -0
  157. package/skills/adspecs-export-word/references/node_modules/docx/dist/index.iife.js +33173 -0
  158. package/skills/adspecs-export-word/references/node_modules/docx/dist/index.mjs +32868 -0
  159. package/skills/adspecs-export-word/references/node_modules/docx/dist/index.umd.cjs +33174 -0
  160. package/skills/adspecs-export-word/references/node_modules/docx/package.json +114 -0
  161. package/skills/adspecs-export-word/references/node_modules/hash.js/.eslintrc.js +41 -0
  162. package/skills/adspecs-export-word/references/node_modules/hash.js/.travis.yml +10 -0
  163. package/skills/adspecs-export-word/references/node_modules/hash.js/README.md +48 -0
  164. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/common.js +92 -0
  165. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/hmac.js +47 -0
  166. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/ripemd.js +146 -0
  167. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha/1.js +74 -0
  168. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha/224.js +30 -0
  169. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha/256.js +105 -0
  170. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha/384.js +35 -0
  171. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha/512.js +330 -0
  172. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha/common.js +49 -0
  173. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/sha.js +7 -0
  174. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash/utils.js +278 -0
  175. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash.d.ts +106 -0
  176. package/skills/adspecs-export-word/references/node_modules/hash.js/lib/hash.js +15 -0
  177. package/skills/adspecs-export-word/references/node_modules/hash.js/package.json +35 -0
  178. package/skills/adspecs-export-word/references/node_modules/hash.js/test/hash-test.js +140 -0
  179. package/skills/adspecs-export-word/references/node_modules/hash.js/test/hmac-test.js +62 -0
  180. package/skills/adspecs-export-word/references/node_modules/immediate/LICENSE.txt +20 -0
  181. package/skills/adspecs-export-word/references/node_modules/immediate/README.md +93 -0
  182. package/skills/adspecs-export-word/references/node_modules/immediate/dist/immediate.js +75 -0
  183. package/skills/adspecs-export-word/references/node_modules/immediate/dist/immediate.min.js +1 -0
  184. package/skills/adspecs-export-word/references/node_modules/immediate/lib/browser.js +69 -0
  185. package/skills/adspecs-export-word/references/node_modules/immediate/lib/index.js +73 -0
  186. package/skills/adspecs-export-word/references/node_modules/immediate/package.json +42 -0
  187. package/skills/adspecs-export-word/references/node_modules/inherits/LICENSE +16 -0
  188. package/skills/adspecs-export-word/references/node_modules/inherits/README.md +42 -0
  189. package/skills/adspecs-export-word/references/node_modules/inherits/inherits.js +9 -0
  190. package/skills/adspecs-export-word/references/node_modules/inherits/inherits_browser.js +27 -0
  191. package/skills/adspecs-export-word/references/node_modules/inherits/package.json +29 -0
  192. package/skills/adspecs-export-word/references/node_modules/isarray/.travis.yml +4 -0
  193. package/skills/adspecs-export-word/references/node_modules/isarray/Makefile +6 -0
  194. package/skills/adspecs-export-word/references/node_modules/isarray/README.md +60 -0
  195. package/skills/adspecs-export-word/references/node_modules/isarray/component.json +19 -0
  196. package/skills/adspecs-export-word/references/node_modules/isarray/index.js +5 -0
  197. package/skills/adspecs-export-word/references/node_modules/isarray/package.json +45 -0
  198. package/skills/adspecs-export-word/references/node_modules/isarray/test.js +20 -0
  199. package/skills/adspecs-export-word/references/node_modules/jszip/.codeclimate.yml +16 -0
  200. package/skills/adspecs-export-word/references/node_modules/jszip/.editorconfig +8 -0
  201. package/skills/adspecs-export-word/references/node_modules/jszip/.eslintrc.js +43 -0
  202. package/skills/adspecs-export-word/references/node_modules/jszip/.github/workflows/pr.yaml +58 -0
  203. package/skills/adspecs-export-word/references/node_modules/jszip/.jekyll-metadata +0 -0
  204. package/skills/adspecs-export-word/references/node_modules/jszip/.travis.yml +17 -0
  205. package/skills/adspecs-export-word/references/node_modules/jszip/CHANGES.md +204 -0
  206. package/skills/adspecs-export-word/references/node_modules/jszip/LICENSE.markdown +651 -0
  207. package/skills/adspecs-export-word/references/node_modules/jszip/README.markdown +33 -0
  208. package/skills/adspecs-export-word/references/node_modules/jszip/deps.js +37 -0
  209. package/skills/adspecs-export-word/references/node_modules/jszip/dist/jszip.js +11577 -0
  210. package/skills/adspecs-export-word/references/node_modules/jszip/dist/jszip.min.js +13 -0
  211. package/skills/adspecs-export-word/references/node_modules/jszip/graph.svg +601 -0
  212. package/skills/adspecs-export-word/references/node_modules/jszip/index.d.ts +330 -0
  213. package/skills/adspecs-export-word/references/node_modules/jszip/lib/base64.js +106 -0
  214. package/skills/adspecs-export-word/references/node_modules/jszip/lib/compressedObject.js +74 -0
  215. package/skills/adspecs-export-word/references/node_modules/jszip/lib/compressions.js +14 -0
  216. package/skills/adspecs-export-word/references/node_modules/jszip/lib/crc32.js +77 -0
  217. package/skills/adspecs-export-word/references/node_modules/jszip/lib/defaults.js +11 -0
  218. package/skills/adspecs-export-word/references/node_modules/jszip/lib/external.js +18 -0
  219. package/skills/adspecs-export-word/references/node_modules/jszip/lib/flate.js +85 -0
  220. package/skills/adspecs-export-word/references/node_modules/jszip/lib/generate/ZipFileWorker.js +539 -0
  221. package/skills/adspecs-export-word/references/node_modules/jszip/lib/generate/index.js +57 -0
  222. package/skills/adspecs-export-word/references/node_modules/jszip/lib/index.js +55 -0
  223. package/skills/adspecs-export-word/references/node_modules/jszip/lib/license_header.js +11 -0
  224. package/skills/adspecs-export-word/references/node_modules/jszip/lib/load.js +88 -0
  225. package/skills/adspecs-export-word/references/node_modules/jszip/lib/nodejs/NodejsStreamInputAdapter.js +74 -0
  226. package/skills/adspecs-export-word/references/node_modules/jszip/lib/nodejs/NodejsStreamOutputAdapter.js +42 -0
  227. package/skills/adspecs-export-word/references/node_modules/jszip/lib/nodejsUtils.js +57 -0
  228. package/skills/adspecs-export-word/references/node_modules/jszip/lib/object.js +384 -0
  229. package/skills/adspecs-export-word/references/node_modules/jszip/lib/readable-stream-browser.js +10 -0
  230. package/skills/adspecs-export-word/references/node_modules/jszip/lib/reader/ArrayReader.js +57 -0
  231. package/skills/adspecs-export-word/references/node_modules/jszip/lib/reader/DataReader.js +116 -0
  232. package/skills/adspecs-export-word/references/node_modules/jszip/lib/reader/NodeBufferReader.js +19 -0
  233. package/skills/adspecs-export-word/references/node_modules/jszip/lib/reader/StringReader.js +38 -0
  234. package/skills/adspecs-export-word/references/node_modules/jszip/lib/reader/Uint8ArrayReader.js +22 -0
  235. package/skills/adspecs-export-word/references/node_modules/jszip/lib/reader/readerFor.js +28 -0
  236. package/skills/adspecs-export-word/references/node_modules/jszip/lib/signature.js +7 -0
  237. package/skills/adspecs-export-word/references/node_modules/jszip/lib/stream/ConvertWorker.js +26 -0
  238. package/skills/adspecs-export-word/references/node_modules/jszip/lib/stream/Crc32Probe.js +24 -0
  239. package/skills/adspecs-export-word/references/node_modules/jszip/lib/stream/DataLengthProbe.js +29 -0
  240. package/skills/adspecs-export-word/references/node_modules/jszip/lib/stream/DataWorker.js +116 -0
  241. package/skills/adspecs-export-word/references/node_modules/jszip/lib/stream/GenericWorker.js +263 -0
  242. package/skills/adspecs-export-word/references/node_modules/jszip/lib/stream/StreamHelper.js +214 -0
  243. package/skills/adspecs-export-word/references/node_modules/jszip/lib/support.js +38 -0
  244. package/skills/adspecs-export-word/references/node_modules/jszip/lib/utf8.js +275 -0
  245. package/skills/adspecs-export-word/references/node_modules/jszip/lib/utils.js +501 -0
  246. package/skills/adspecs-export-word/references/node_modules/jszip/lib/zipEntries.js +261 -0
  247. package/skills/adspecs-export-word/references/node_modules/jszip/lib/zipEntry.js +293 -0
  248. package/skills/adspecs-export-word/references/node_modules/jszip/lib/zipObject.js +133 -0
  249. package/skills/adspecs-export-word/references/node_modules/jszip/package.json +67 -0
  250. package/skills/adspecs-export-word/references/node_modules/jszip/sponsors.md +21 -0
  251. package/skills/adspecs-export-word/references/node_modules/jszip/tsconfig.json +101 -0
  252. package/skills/adspecs-export-word/references/node_modules/jszip/vendor/FileSaver.js +247 -0
  253. package/skills/adspecs-export-word/references/node_modules/lie/README.md +62 -0
  254. package/skills/adspecs-export-word/references/node_modules/lie/dist/lie.js +350 -0
  255. package/skills/adspecs-export-word/references/node_modules/lie/dist/lie.min.js +1 -0
  256. package/skills/adspecs-export-word/references/node_modules/lie/dist/lie.polyfill.js +358 -0
  257. package/skills/adspecs-export-word/references/node_modules/lie/dist/lie.polyfill.min.js +1 -0
  258. package/skills/adspecs-export-word/references/node_modules/lie/lib/browser.js +273 -0
  259. package/skills/adspecs-export-word/references/node_modules/lie/lib/index.js +298 -0
  260. package/skills/adspecs-export-word/references/node_modules/lie/license.md +7 -0
  261. package/skills/adspecs-export-word/references/node_modules/lie/lie.d.ts +244 -0
  262. package/skills/adspecs-export-word/references/node_modules/lie/package.json +69 -0
  263. package/skills/adspecs-export-word/references/node_modules/lie/polyfill.js +4 -0
  264. package/skills/adspecs-export-word/references/node_modules/marked/LICENSE.md +44 -0
  265. package/skills/adspecs-export-word/references/node_modules/marked/README.md +106 -0
  266. package/skills/adspecs-export-word/references/node_modules/marked/bin/main.js +282 -0
  267. package/skills/adspecs-export-word/references/node_modules/marked/bin/marked.js +15 -0
  268. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.cjs +2211 -0
  269. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.cjs.map +7 -0
  270. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.d.cts +728 -0
  271. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.d.ts +728 -0
  272. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.esm.js +2189 -0
  273. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.esm.js.map +7 -0
  274. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.umd.js +2213 -0
  275. package/skills/adspecs-export-word/references/node_modules/marked/lib/marked.umd.js.map +7 -0
  276. package/skills/adspecs-export-word/references/node_modules/marked/man/marked.1 +111 -0
  277. package/skills/adspecs-export-word/references/node_modules/marked/man/marked.1.md +92 -0
  278. package/skills/adspecs-export-word/references/node_modules/marked/marked.min.js +69 -0
  279. package/skills/adspecs-export-word/references/node_modules/marked/package.json +111 -0
  280. package/skills/adspecs-export-word/references/node_modules/minimalistic-assert/LICENSE +13 -0
  281. package/skills/adspecs-export-word/references/node_modules/minimalistic-assert/index.js +11 -0
  282. package/skills/adspecs-export-word/references/node_modules/minimalistic-assert/package.json +19 -0
  283. package/skills/adspecs-export-word/references/node_modules/minimalistic-assert/readme.md +4 -0
  284. package/skills/adspecs-export-word/references/node_modules/nanoid/LICENSE +20 -0
  285. package/skills/adspecs-export-word/references/node_modules/nanoid/README.md +34 -0
  286. package/skills/adspecs-export-word/references/node_modules/nanoid/bin/nanoid.js +66 -0
  287. package/skills/adspecs-export-word/references/node_modules/nanoid/index.browser.js +57 -0
  288. package/skills/adspecs-export-word/references/node_modules/nanoid/index.d.ts +106 -0
  289. package/skills/adspecs-export-word/references/node_modules/nanoid/index.js +83 -0
  290. package/skills/adspecs-export-word/references/node_modules/nanoid/nanoid.js +1 -0
  291. package/skills/adspecs-export-word/references/node_modules/nanoid/non-secure/index.d.ts +48 -0
  292. package/skills/adspecs-export-word/references/node_modules/nanoid/non-secure/index.js +23 -0
  293. package/skills/adspecs-export-word/references/node_modules/nanoid/package.json +46 -0
  294. package/skills/adspecs-export-word/references/node_modules/nanoid/url-alphabet/index.js +2 -0
  295. package/skills/adspecs-export-word/references/node_modules/pako/CHANGELOG.md +164 -0
  296. package/skills/adspecs-export-word/references/node_modules/pako/LICENSE +21 -0
  297. package/skills/adspecs-export-word/references/node_modules/pako/README.md +191 -0
  298. package/skills/adspecs-export-word/references/node_modules/pako/dist/pako.js +6818 -0
  299. package/skills/adspecs-export-word/references/node_modules/pako/dist/pako.min.js +1 -0
  300. package/skills/adspecs-export-word/references/node_modules/pako/dist/pako_deflate.js +3997 -0
  301. package/skills/adspecs-export-word/references/node_modules/pako/dist/pako_deflate.min.js +1 -0
  302. package/skills/adspecs-export-word/references/node_modules/pako/dist/pako_inflate.js +3300 -0
  303. package/skills/adspecs-export-word/references/node_modules/pako/dist/pako_inflate.min.js +1 -0
  304. package/skills/adspecs-export-word/references/node_modules/pako/index.js +14 -0
  305. package/skills/adspecs-export-word/references/node_modules/pako/lib/deflate.js +400 -0
  306. package/skills/adspecs-export-word/references/node_modules/pako/lib/inflate.js +423 -0
  307. package/skills/adspecs-export-word/references/node_modules/pako/lib/utils/common.js +105 -0
  308. package/skills/adspecs-export-word/references/node_modules/pako/lib/utils/strings.js +187 -0
  309. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/README +59 -0
  310. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/adler32.js +51 -0
  311. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/constants.js +68 -0
  312. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/crc32.js +59 -0
  313. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/deflate.js +1874 -0
  314. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/gzheader.js +58 -0
  315. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/inffast.js +345 -0
  316. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/inflate.js +1556 -0
  317. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/inftrees.js +343 -0
  318. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/messages.js +32 -0
  319. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/trees.js +1222 -0
  320. package/skills/adspecs-export-word/references/node_modules/pako/lib/zlib/zstream.js +47 -0
  321. package/skills/adspecs-export-word/references/node_modules/pako/package.json +44 -0
  322. package/skills/adspecs-export-word/references/node_modules/process-nextick-args/index.js +45 -0
  323. package/skills/adspecs-export-word/references/node_modules/process-nextick-args/license.md +19 -0
  324. package/skills/adspecs-export-word/references/node_modules/process-nextick-args/package.json +25 -0
  325. package/skills/adspecs-export-word/references/node_modules/process-nextick-args/readme.md +18 -0
  326. package/skills/adspecs-export-word/references/node_modules/readable-stream/.travis.yml +34 -0
  327. package/skills/adspecs-export-word/references/node_modules/readable-stream/CONTRIBUTING.md +38 -0
  328. package/skills/adspecs-export-word/references/node_modules/readable-stream/GOVERNANCE.md +136 -0
  329. package/skills/adspecs-export-word/references/node_modules/readable-stream/LICENSE +47 -0
  330. package/skills/adspecs-export-word/references/node_modules/readable-stream/README.md +58 -0
  331. package/skills/adspecs-export-word/references/node_modules/readable-stream/doc/wg-meetings/2015-01-30.md +60 -0
  332. package/skills/adspecs-export-word/references/node_modules/readable-stream/duplex-browser.js +1 -0
  333. package/skills/adspecs-export-word/references/node_modules/readable-stream/duplex.js +1 -0
  334. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/_stream_duplex.js +131 -0
  335. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/_stream_passthrough.js +47 -0
  336. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/_stream_readable.js +1019 -0
  337. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/_stream_transform.js +214 -0
  338. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/_stream_writable.js +685 -0
  339. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/internal/streams/BufferList.js +78 -0
  340. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/internal/streams/destroy.js +84 -0
  341. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
  342. package/skills/adspecs-export-word/references/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
  343. package/skills/adspecs-export-word/references/node_modules/readable-stream/package.json +52 -0
  344. package/skills/adspecs-export-word/references/node_modules/readable-stream/passthrough.js +1 -0
  345. package/skills/adspecs-export-word/references/node_modules/readable-stream/readable-browser.js +7 -0
  346. package/skills/adspecs-export-word/references/node_modules/readable-stream/readable.js +19 -0
  347. package/skills/adspecs-export-word/references/node_modules/readable-stream/transform.js +1 -0
  348. package/skills/adspecs-export-word/references/node_modules/readable-stream/writable-browser.js +1 -0
  349. package/skills/adspecs-export-word/references/node_modules/readable-stream/writable.js +8 -0
  350. package/skills/adspecs-export-word/references/node_modules/safe-buffer/LICENSE +21 -0
  351. package/skills/adspecs-export-word/references/node_modules/safe-buffer/README.md +584 -0
  352. package/skills/adspecs-export-word/references/node_modules/safe-buffer/index.d.ts +187 -0
  353. package/skills/adspecs-export-word/references/node_modules/safe-buffer/index.js +62 -0
  354. package/skills/adspecs-export-word/references/node_modules/safe-buffer/package.json +37 -0
  355. package/skills/adspecs-export-word/references/node_modules/sax/LICENSE.md +55 -0
  356. package/skills/adspecs-export-word/references/node_modules/sax/README.md +227 -0
  357. package/skills/adspecs-export-word/references/node_modules/sax/lib/sax.js +1848 -0
  358. package/skills/adspecs-export-word/references/node_modules/sax/package.json +34 -0
  359. package/skills/adspecs-export-word/references/node_modules/setimmediate/LICENSE.txt +20 -0
  360. package/skills/adspecs-export-word/references/node_modules/setimmediate/package.json +30 -0
  361. package/skills/adspecs-export-word/references/node_modules/setimmediate/setImmediate.js +186 -0
  362. package/skills/adspecs-export-word/references/node_modules/string_decoder/.travis.yml +50 -0
  363. package/skills/adspecs-export-word/references/node_modules/string_decoder/LICENSE +48 -0
  364. package/skills/adspecs-export-word/references/node_modules/string_decoder/README.md +47 -0
  365. package/skills/adspecs-export-word/references/node_modules/string_decoder/lib/string_decoder.js +296 -0
  366. package/skills/adspecs-export-word/references/node_modules/string_decoder/package.json +31 -0
  367. package/skills/adspecs-export-word/references/node_modules/undici-types/LICENSE +21 -0
  368. package/skills/adspecs-export-word/references/node_modules/undici-types/README.md +6 -0
  369. package/skills/adspecs-export-word/references/node_modules/undici-types/agent.d.ts +32 -0
  370. package/skills/adspecs-export-word/references/node_modules/undici-types/api.d.ts +43 -0
  371. package/skills/adspecs-export-word/references/node_modules/undici-types/balanced-pool.d.ts +30 -0
  372. package/skills/adspecs-export-word/references/node_modules/undici-types/cache-interceptor.d.ts +179 -0
  373. package/skills/adspecs-export-word/references/node_modules/undici-types/cache.d.ts +36 -0
  374. package/skills/adspecs-export-word/references/node_modules/undici-types/client-stats.d.ts +15 -0
  375. package/skills/adspecs-export-word/references/node_modules/undici-types/client.d.ts +123 -0
  376. package/skills/adspecs-export-word/references/node_modules/undici-types/connector.d.ts +36 -0
  377. package/skills/adspecs-export-word/references/node_modules/undici-types/content-type.d.ts +21 -0
  378. package/skills/adspecs-export-word/references/node_modules/undici-types/cookies.d.ts +30 -0
  379. package/skills/adspecs-export-word/references/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  380. package/skills/adspecs-export-word/references/node_modules/undici-types/dispatcher.d.ts +279 -0
  381. package/skills/adspecs-export-word/references/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  382. package/skills/adspecs-export-word/references/node_modules/undici-types/errors.d.ts +177 -0
  383. package/skills/adspecs-export-word/references/node_modules/undici-types/eventsource.d.ts +66 -0
  384. package/skills/adspecs-export-word/references/node_modules/undici-types/fetch.d.ts +211 -0
  385. package/skills/adspecs-export-word/references/node_modules/undici-types/formdata.d.ts +108 -0
  386. package/skills/adspecs-export-word/references/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  387. package/skills/adspecs-export-word/references/node_modules/undici-types/global-origin.d.ts +7 -0
  388. package/skills/adspecs-export-word/references/node_modules/undici-types/h2c-client.d.ts +73 -0
  389. package/skills/adspecs-export-word/references/node_modules/undici-types/handlers.d.ts +15 -0
  390. package/skills/adspecs-export-word/references/node_modules/undici-types/header.d.ts +160 -0
  391. package/skills/adspecs-export-word/references/node_modules/undici-types/index.d.ts +91 -0
  392. package/skills/adspecs-export-word/references/node_modules/undici-types/interceptors.d.ts +80 -0
  393. package/skills/adspecs-export-word/references/node_modules/undici-types/mock-agent.d.ts +68 -0
  394. package/skills/adspecs-export-word/references/node_modules/undici-types/mock-call-history.d.ts +111 -0
  395. package/skills/adspecs-export-word/references/node_modules/undici-types/mock-client.d.ts +27 -0
  396. package/skills/adspecs-export-word/references/node_modules/undici-types/mock-errors.d.ts +12 -0
  397. package/skills/adspecs-export-word/references/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  398. package/skills/adspecs-export-word/references/node_modules/undici-types/mock-pool.d.ts +27 -0
  399. package/skills/adspecs-export-word/references/node_modules/undici-types/package.json +55 -0
  400. package/skills/adspecs-export-word/references/node_modules/undici-types/patch.d.ts +29 -0
  401. package/skills/adspecs-export-word/references/node_modules/undici-types/pool-stats.d.ts +19 -0
  402. package/skills/adspecs-export-word/references/node_modules/undici-types/pool.d.ts +41 -0
  403. package/skills/adspecs-export-word/references/node_modules/undici-types/proxy-agent.d.ts +29 -0
  404. package/skills/adspecs-export-word/references/node_modules/undici-types/readable.d.ts +68 -0
  405. package/skills/adspecs-export-word/references/node_modules/undici-types/retry-agent.d.ts +8 -0
  406. package/skills/adspecs-export-word/references/node_modules/undici-types/retry-handler.d.ts +125 -0
  407. package/skills/adspecs-export-word/references/node_modules/undici-types/round-robin-pool.d.ts +41 -0
  408. package/skills/adspecs-export-word/references/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  409. package/skills/adspecs-export-word/references/node_modules/undici-types/socks5-proxy-agent.d.ts +25 -0
  410. package/skills/adspecs-export-word/references/node_modules/undici-types/util.d.ts +18 -0
  411. package/skills/adspecs-export-word/references/node_modules/undici-types/utility.d.ts +7 -0
  412. package/skills/adspecs-export-word/references/node_modules/undici-types/webidl.d.ts +347 -0
  413. package/skills/adspecs-export-word/references/node_modules/undici-types/websocket.d.ts +188 -0
  414. package/skills/adspecs-export-word/references/node_modules/util-deprecate/History.md +16 -0
  415. package/skills/adspecs-export-word/references/node_modules/util-deprecate/LICENSE +24 -0
  416. package/skills/adspecs-export-word/references/node_modules/util-deprecate/README.md +53 -0
  417. package/skills/adspecs-export-word/references/node_modules/util-deprecate/browser.js +67 -0
  418. package/skills/adspecs-export-word/references/node_modules/util-deprecate/node.js +6 -0
  419. package/skills/adspecs-export-word/references/node_modules/util-deprecate/package.json +27 -0
  420. package/skills/adspecs-export-word/references/node_modules/xml/.travis.yml +8 -0
  421. package/skills/adspecs-export-word/references/node_modules/xml/LICENSE +22 -0
  422. package/skills/adspecs-export-word/references/node_modules/xml/examples/examples.js +81 -0
  423. package/skills/adspecs-export-word/references/node_modules/xml/examples/server.js +24 -0
  424. package/skills/adspecs-export-word/references/node_modules/xml/lib/escapeForXML.js +18 -0
  425. package/skills/adspecs-export-word/references/node_modules/xml/lib/xml.js +282 -0
  426. package/skills/adspecs-export-word/references/node_modules/xml/package.json +38 -0
  427. package/skills/adspecs-export-word/references/node_modules/xml/readme.md +184 -0
  428. package/skills/adspecs-export-word/references/node_modules/xml/test/xml.test.js +150 -0
  429. package/skills/adspecs-export-word/references/node_modules/xml-js/LICENSE +21 -0
  430. package/skills/adspecs-export-word/references/node_modules/xml-js/README.md +390 -0
  431. package/skills/adspecs-export-word/references/node_modules/xml-js/bin/cli-helper.js +47 -0
  432. package/skills/adspecs-export-word/references/node_modules/xml-js/bin/cli.js +83 -0
  433. package/skills/adspecs-export-word/references/node_modules/xml-js/bin/test.json +23 -0
  434. package/skills/adspecs-export-word/references/node_modules/xml-js/bin/test.xml +3 -0
  435. package/skills/adspecs-export-word/references/node_modules/xml-js/dist/xml-js.js +8310 -0
  436. package/skills/adspecs-export-word/references/node_modules/xml-js/dist/xml-js.min.js +8 -0
  437. package/skills/adspecs-export-word/references/node_modules/xml-js/index.js +1 -0
  438. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/array-helper.js +11 -0
  439. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/index.js +13 -0
  440. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/js2xml.js +320 -0
  441. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/json2xml.js +18 -0
  442. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/options-helper.js +43 -0
  443. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/xml2js.js +362 -0
  444. package/skills/adspecs-export-word/references/node_modules/xml-js/lib/xml2json.js +22 -0
  445. package/skills/adspecs-export-word/references/node_modules/xml-js/package.json +111 -0
  446. package/skills/adspecs-export-word/references/node_modules/xml-js/types/index.d.ts +154 -0
  447. package/skills/adspecs-export-word/references/node_modules/xml-js/types/tsconfig.json +14 -0
  448. package/skills/adspecs-export-word/references/node_modules/xml-js/types/typings.json +4 -0
  449. package/skills/adspecs-export-word/references/node_modules/xml-js/types/xml-js-tests.ts +60 -0
  450. package/skills/adspecs-export-word/references/node_modules/xml-js/webpack.config.js +28 -0
  451. package/skills/project-init/SKILL.md +10 -10
  452. package/src/commands/plugin.js +4 -4
  453. package/src/lib/readme-gen.js +1 -1
  454. /package/skills/{adspecs-requirement-research → ads-requirement-research}/02-/351/234/200/346/261/202/346/235/245/346/272/220/344/270/216/351/207/207/351/233/206/350/256/260/345/275/225.md" +0 -0
@@ -1,862 +1,862 @@
1
- #!/usr/bin/env node
2
- /**
3
- * md-to-docx.js — Markdown to Word (.docx) converter
4
- *
5
- * Features:
6
- * - Auto-generated cover page (company logo, project info, document metadata)
7
- * - Table of contents (Heading 1-3)
8
- * - Full Markdown element conversion (headings, tables, code blocks, lists, images, blockquotes)
9
- *
10
- * Usage:
11
- * node md-to-docx.js --input <file> --output <file> --meta '<json>'
12
- * node md-to-docx.js --dir <dir> --output <dir> --meta '<json>' [--recursive]
13
- * [--no-cover] [--no-toc]
14
- */
15
-
16
- const fs = require("fs");
17
- const path = require("path");
18
- const {
19
- Document,
20
- Packer,
21
- Paragraph,
22
- TextRun,
23
- HeadingLevel,
24
- AlignmentType,
25
- TableOfContents,
26
- StyleLevel,
27
- Table,
28
- TableRow,
29
- TableCell,
30
- WidthType,
31
- BorderStyle,
32
- ImageRun,
33
- PageBreak,
34
- ShadingType,
35
- } = require("docx");
36
- const { marked } = require("marked");
37
-
38
- // ── Constants ──────────────────────────────────────────────────────────
39
-
40
- const DARK_BLUE = "1F4E79";
41
- const GRAY = "808080";
42
- const LIGHT_GRAY = "F2F2F2";
43
-
44
- const FONT_BODY = "DengXian";
45
- const FONT_HEADING = "Microsoft YaHei";
46
- const FONT_CODE = "Consolas";
47
-
48
- const HEADING_STYLES = {
49
- 1: { heading: HeadingLevel.HEADING_1, size: 44, font: FONT_HEADING },
50
- 2: { heading: HeadingLevel.HEADING_2, size: 32, font: FONT_HEADING },
51
- 3: { heading: HeadingLevel.HEADING_3, size: 28, font: FONT_HEADING },
52
- 4: { heading: HeadingLevel.HEADING_4, size: 24, font: FONT_HEADING },
53
- 5: { heading: HeadingLevel.HEADING_5, size: 22, font: FONT_HEADING },
54
- 6: { heading: HeadingLevel.HEADING_6, size: 20, font: FONT_HEADING },
55
- };
56
-
57
- // ── CLI Arguments ──────────────────────────────────────────────────────
58
-
59
- function parseArgs() {
60
- const args = process.argv.slice(2);
61
- const result = {
62
- input: null,
63
- dir: null,
64
- output: null,
65
- meta: "{}",
66
- noCover: false,
67
- noToc: false,
68
- recursive: false,
69
- };
70
- for (let i = 0; i < args.length; i++) {
71
- switch (args[i]) {
72
- case "--input":
73
- result.input = args[++i];
74
- break;
75
- case "--dir":
76
- result.dir = args[++i];
77
- break;
78
- case "--output":
79
- result.output = args[++i];
80
- break;
81
- case "--meta":
82
- result.meta = args[++i];
83
- break;
84
- case "--no-cover":
85
- result.noCover = true;
86
- break;
87
- case "--no-toc":
88
- result.noToc = true;
89
- break;
90
- case "--recursive":
91
- result.recursive = true;
92
- break;
93
- }
94
- }
95
- return result;
96
- }
97
-
98
- // ── Markdown Reader ────────────────────────────────────────────────────
99
-
100
- function readMarkdown(filePath) {
101
- const content = fs.readFileSync(filePath, "utf-8");
102
-
103
- // Try YAML frontmatter
104
- const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
105
- let frontmatter = {};
106
- let body = content;
107
-
108
- if (fmMatch) {
109
- for (const line of fmMatch[1].split("\n")) {
110
- const m = line.match(/^(\S+):\s*(.*)$/);
111
- if (m) frontmatter[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
112
- }
113
- body = fmMatch[2];
114
- } else {
115
- // Try blockquote metadata: > **版本**: v1.0
116
- const metaLines = [];
117
- const restLines = [];
118
- let inMeta = true;
119
- for (const line of content.split("\n")) {
120
- if (inMeta && line.startsWith(">")) {
121
- metaLines.push(line);
122
- } else {
123
- inMeta = false;
124
- restLines.push(line);
125
- }
126
- }
127
- body = restLines.join("\n");
128
- for (const line of metaLines) {
129
- const m = line.match(/\*\*(.+?)\*\*[::]\s*(.+)/);
130
- if (m) frontmatter[m[1].trim()] = m[2].trim();
131
- }
132
- }
133
-
134
- return { body, frontmatter };
135
- }
136
-
137
- // ── Inline Token Converter ─────────────────────────────────────────────
138
-
139
- function convertInline(tokens, overrides = {}) {
140
- if (!tokens || !Array.isArray(tokens)) return [];
141
- const runs = [];
142
- const baseFont = overrides.font || FONT_BODY;
143
- const baseSize = overrides.size || 24;
144
-
145
- for (const token of tokens) {
146
- switch (token.type) {
147
- case "text": {
148
- const children = token.tokens ? convertInline(token.tokens, overrides) : [];
149
- if (children.length > 0) {
150
- runs.push(...children);
151
- } else {
152
- runs.push(new TextRun({ text: token.text || "", font: baseFont, size: baseSize, ...overrides }));
153
- }
154
- break;
155
- }
156
- case "strong":
157
- runs.push(...convertInline(token.tokens, { ...overrides, bold: true }));
158
- break;
159
- case "em":
160
- runs.push(...convertInline(token.tokens, { ...overrides, italics: true }));
161
- break;
162
- case "del":
163
- runs.push(...convertInline(token.tokens, { ...overrides, strike: true }));
164
- break;
165
- case "codespan":
166
- runs.push(
167
- new TextRun({ text: token.text || "", font: FONT_CODE, size: Math.min(baseSize, 20), ...overrides })
168
- );
169
- break;
170
- case "link":
171
- runs.push(
172
- new TextRun({
173
- text: token.text || (token.tokens ? token.tokens.map(t => t.text || t.raw || "").join("") : ""),
174
- font: baseFont,
175
- size: baseSize,
176
- color: "0563C1",
177
- underline: { type: "single" },
178
- ...overrides,
179
- })
180
- );
181
- break;
182
- case "image":
183
- try {
184
- const imgPath = token.href;
185
- if (imgPath && fs.existsSync(imgPath)) {
186
- const buf = fs.readFileSync(imgPath);
187
- runs.push(
188
- new ImageRun({
189
- data: buf,
190
- transformation: { width: 200, height: 150 },
191
- altText: { title: token.text || "Image", description: token.text || "Image", name: "image" },
192
- })
193
- );
194
- } else {
195
- runs.push(new TextRun({ text: `[Image: ${token.text || token.href}]`, font: baseFont, size: baseSize, italics: true, color: GRAY }));
196
- }
197
- } catch {
198
- runs.push(new TextRun({ text: `[Image: ${token.text || token.href}]`, font: baseFont, size: baseSize, italics: true, color: GRAY }));
199
- }
200
- break;
201
- case "br":
202
- runs.push(new TextRun({ text: "", break: 1, font: baseFont, size: baseSize }));
203
- break;
204
- default:
205
- if (token.text || token.raw) {
206
- runs.push(new TextRun({ text: token.text || token.raw || "", font: baseFont, size: baseSize, ...overrides }));
207
- }
208
- }
209
- }
210
-
211
- return runs;
212
- }
213
-
214
- // ── Cover Page Builder ─────────────────────────────────────────────────
215
-
216
- function buildCoverPage(meta) {
217
- const blocks = [];
218
- const spacer = (n) => {
219
- for (let i = 0; i < n; i++) blocks.push(new Paragraph({ children: [new TextRun("")] }));
220
- };
221
-
222
- // 1. Top spacing
223
- spacer(3);
224
-
225
- // 2. Company logo
226
- if (meta.company_logo && fs.existsSync(meta.company_logo)) {
227
- try {
228
- const logoBuf = fs.readFileSync(meta.company_logo);
229
- blocks.push(
230
- new Paragraph({
231
- alignment: AlignmentType.CENTER,
232
- children: [
233
- new ImageRun({
234
- data: logoBuf,
235
- transformation: { width: 200, height: 200 },
236
- altText: { title: "Company Logo", description: "Company Logo", name: "logo" },
237
- }),
238
- ],
239
- })
240
- );
241
- spacer(2);
242
- } catch {
243
- // Skip logo on error
244
- }
245
- }
246
-
247
- // 3. Company name
248
- if (meta.company_name) {
249
- blocks.push(
250
- new Paragraph({
251
- alignment: AlignmentType.CENTER,
252
- children: [new TextRun({ text: meta.company_name, bold: true, font: FONT_HEADING, size: 32, color: DARK_BLUE })],
253
- })
254
- );
255
- }
256
-
257
- spacer(1);
258
-
259
- // 4. Project name
260
- if (meta.project_name) {
261
- blocks.push(
262
- new Paragraph({
263
- alignment: AlignmentType.CENTER,
264
- children: [new TextRun({ text: meta.project_name, bold: true, font: FONT_HEADING, size: 36, color: DARK_BLUE })],
265
- })
266
- );
267
- }
268
-
269
- spacer(2);
270
-
271
- // 5. Document title
272
- if (meta.title) {
273
- blocks.push(
274
- new Paragraph({
275
- alignment: AlignmentType.CENTER,
276
- children: [new TextRun({ text: meta.title, bold: true, font: FONT_HEADING, size: 56, color: DARK_BLUE })],
277
- })
278
- );
279
- }
280
-
281
- // 6. Subtitle
282
- if (meta.subtitle) {
283
- blocks.push(
284
- new Paragraph({
285
- alignment: AlignmentType.CENTER,
286
- spacing: { before: 200 },
287
- children: [new TextRun({ text: meta.subtitle, font: FONT_HEADING, size: 28, color: GRAY })],
288
- })
289
- );
290
- }
291
-
292
- spacer(3);
293
-
294
- // 7. Info table
295
- const fields = [
296
- ["文档编号", meta.document_number],
297
- ["版本号", meta.version],
298
- ["创建日期", meta.date],
299
- ["作者", meta.author],
300
- ["审核人", meta.reviewer],
301
- ["批准人", meta.approver],
302
- ["保密等级", meta.confidentiality],
303
- ].filter(([, v]) => v);
304
-
305
- if (fields.length > 0) {
306
- const borderStyle = { style: BorderStyle.SINGLE, size: 1, color: "BBBBBB" };
307
- const borders = { top: borderStyle, bottom: borderStyle, left: borderStyle, right: borderStyle };
308
-
309
- const rows = fields.map(
310
- ([label, value]) =>
311
- new TableRow({
312
- children: [
313
- new TableCell({
314
- borders,
315
- width: { size: 3000, type: WidthType.DXA },
316
- children: [
317
- new Paragraph({
318
- alignment: AlignmentType.CENTER,
319
- children: [new TextRun({ text: label, bold: true, font: FONT_HEADING, size: 22 })],
320
- }),
321
- ],
322
- }),
323
- new TableCell({
324
- borders,
325
- width: { size: 5000, type: WidthType.DXA },
326
- children: [
327
- new Paragraph({
328
- children: [new TextRun({ text: value, font: FONT_BODY, size: 22 })],
329
- }),
330
- ],
331
- }),
332
- ],
333
- })
334
- );
335
-
336
- blocks.push(
337
- new Table({
338
- rows,
339
- width: { size: 8000, type: WidthType.DXA },
340
- })
341
- );
342
- }
343
-
344
- // 8. Page break
345
- blocks.push(
346
- new Paragraph({
347
- children: [new PageBreak()],
348
- })
349
- );
350
-
351
- return blocks;
352
- }
353
-
354
- // ── TOC Builder ────────────────────────────────────────────────────────
355
-
356
- function buildTOC() {
357
- return [
358
- new Paragraph({
359
- heading: HeadingLevel.HEADING_1,
360
- children: [new TextRun({ text: "目录", font: FONT_HEADING, size: 44, bold: true, color: DARK_BLUE })],
361
- }),
362
- new TableOfContents("目录", {
363
- hyperlink: true,
364
- headingStyleRange: "1-3",
365
- stylesWithLevels: [
366
- new StyleLevel("Heading1", 1),
367
- new StyleLevel("Heading2", 2),
368
- new StyleLevel("Heading3", 3),
369
- ],
370
- }),
371
- new Paragraph({
372
- children: [new PageBreak()],
373
- }),
374
- ];
375
- }
376
-
377
- // ── Markdown → docx Converter ──────────────────────────────────────────
378
-
379
- function convertTokens(tokens) {
380
- const blocks = [];
381
-
382
- for (const token of tokens) {
383
- switch (token.type) {
384
- case "heading": {
385
- const style = HEADING_STYLES[token.depth] || HEADING_STYLES[6];
386
- const runs = token.tokens
387
- ? convertInline(token.tokens, { font: style.font, size: style.size, bold: true, color: DARK_BLUE })
388
- : [new TextRun({ text: token.text || "", font: style.font, size: style.size, bold: true, color: DARK_BLUE })];
389
- blocks.push(
390
- new Paragraph({
391
- heading: style.heading,
392
- spacing: { before: 300, after: 100 },
393
- children: runs,
394
- })
395
- );
396
- break;
397
- }
398
-
399
- case "paragraph": {
400
- const runs = convertInline(token.tokens);
401
- blocks.push(
402
- new Paragraph({
403
- spacing: { after: 120 },
404
- children: runs,
405
- })
406
- );
407
- break;
408
- }
409
-
410
- case "code": {
411
- const lines = (token.text || "").split("\n");
412
- for (const line of lines) {
413
- blocks.push(
414
- new Paragraph({
415
- spacing: { after: 0 },
416
- indent: { left: 400 },
417
- shading: { type: ShadingType.CLEAR, color: "auto", fill: LIGHT_GRAY },
418
- children: [new TextRun({ text: line || " ", font: FONT_CODE, size: 20 })],
419
- })
420
- );
421
- }
422
- // Add spacing after code block
423
- blocks.push(new Paragraph({ spacing: { after: 120 }, children: [new TextRun("")] }));
424
- break;
425
- }
426
-
427
- case "table": {
428
- const borderStyle = { style: BorderStyle.SINGLE, size: 1, color: "999999" };
429
- const borders = { top: borderStyle, bottom: borderStyle, left: borderStyle, right: borderStyle };
430
-
431
- // Header row
432
- const headerRow = new TableRow({
433
- tableHeader: true,
434
- children: (token.header || []).map(
435
- (h) =>
436
- new TableCell({
437
- borders,
438
- shading: { type: ShadingType.CLEAR, color: "auto", fill: "D9E2F3" },
439
- children: [
440
- new Paragraph({
441
- children: [
442
- new TextRun({
443
- text: h.text || "",
444
- bold: true,
445
- font: FONT_HEADING,
446
- size: 22,
447
- }),
448
- ],
449
- }),
450
- ],
451
- })
452
- ),
453
- });
454
-
455
- // Data rows
456
- const dataRows = (token.rows || []).map(
457
- (row) =>
458
- new TableRow({
459
- children: row.map(
460
- (cell) =>
461
- new TableCell({
462
- borders,
463
- children: [
464
- new Paragraph({
465
- children: cell.tokens
466
- ? convertInline(cell.tokens)
467
- : [new TextRun({ text: cell.text || "", font: FONT_BODY, size: 22 })],
468
- }),
469
- ],
470
- })
471
- ),
472
- })
473
- );
474
-
475
- blocks.push(new Table({ rows: [headerRow, ...dataRows] }));
476
- // Spacing after table
477
- blocks.push(new Paragraph({ spacing: { after: 120 }, children: [new TextRun("")] }));
478
- break;
479
- }
480
-
481
- case "list": {
482
- blocks.push(...convertList(token, 0));
483
- break;
484
- }
485
-
486
- case "blockquote": {
487
- const innerTokens = token.tokens || [];
488
- for (const inner of innerTokens) {
489
- if (inner.type === "paragraph") {
490
- const runs = convertInline(inner.tokens, { italics: true, color: "555555" });
491
- blocks.push(
492
- new Paragraph({
493
- indent: { left: 720 },
494
- spacing: { after: 120 },
495
- border: {
496
- left: { style: BorderStyle.SINGLE, size: 6, color: DARK_BLUE, space: 10 },
497
- },
498
- shading: { type: ShadingType.CLEAR, color: "auto", fill: LIGHT_GRAY },
499
- children: runs,
500
- })
501
- );
502
- } else {
503
- // Recursively handle other block types inside blockquote
504
- const nested = convertTokens([inner]);
505
- blocks.push(...nested);
506
- }
507
- }
508
- break;
509
- }
510
-
511
- case "hr":
512
- blocks.push(
513
- new Paragraph({
514
- spacing: { before: 200, after: 200 },
515
- border: { bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } },
516
- children: [new TextRun("")],
517
- })
518
- );
519
- break;
520
-
521
- case "html":
522
- // Skip HTML blocks or render as plain text
523
- blocks.push(
524
- new Paragraph({
525
- spacing: { after: 120 },
526
- children: [new TextRun({ text: token.text || "", font: FONT_BODY, size: 24, color: GRAY })],
527
- })
528
- );
529
- break;
530
-
531
- case "space":
532
- break;
533
-
534
- default:
535
- if (token.raw && token.text) {
536
- blocks.push(
537
- new Paragraph({
538
- spacing: { after: 120 },
539
- children: [new TextRun({ text: token.text || token.raw || "", font: FONT_BODY, size: 24 })],
540
- })
541
- );
542
- }
543
- }
544
- }
545
-
546
- return blocks;
547
- }
548
-
549
- // ── List Converter (handles nesting) ───────────────────────────────────
550
-
551
- function convertList(token, level) {
552
- const blocks = [];
553
- const isOrdered = token.ordered;
554
- const items = token.items || [];
555
-
556
- let numIndex = 1;
557
-
558
- for (const item of items) {
559
- const numbering = isOrdered
560
- ? { reference: "numbered-list", level }
561
- : { reference: "bullet-list", level };
562
-
563
- // Process item children (paragraphs, nested lists, code blocks)
564
- const itemChildren = item.tokens || [];
565
-
566
- for (const child of itemChildren) {
567
- if (child.type === "list") {
568
- // Nested list
569
- blocks.push(...convertList(child, level + 1));
570
- } else if (child.type === "text" && !child.tokens) {
571
- // Plain text item
572
- const prefix = item.task ? (item.checked ? "☑ " : "☐ ") : "";
573
- blocks.push(
574
- new Paragraph({
575
- numbering,
576
- spacing: { after: 60 },
577
- children: [new TextRun({ text: prefix + (child.text || child.raw || ""), font: FONT_BODY, size: 24 })],
578
- })
579
- );
580
- } else if (child.type === "text" && child.tokens) {
581
- // Text with inline formatting
582
- const runs = convertInline(child.tokens);
583
- if (item.task) {
584
- const prefix = item.checked ? "☑ " : "☐ ";
585
- runs.unshift(new TextRun({ text: prefix, font: FONT_BODY, size: 24 }));
586
- }
587
- blocks.push(
588
- new Paragraph({
589
- numbering,
590
- spacing: { after: 60 },
591
- children: runs,
592
- })
593
- );
594
- } else if (child.type === "paragraph") {
595
- const runs = convertInline(child.tokens);
596
- if (item.task) {
597
- const prefix = item.checked ? "☑ " : "☐ ";
598
- runs.unshift(new TextRun({ text: prefix, font: FONT_BODY, size: 24 }));
599
- }
600
- blocks.push(
601
- new Paragraph({
602
- numbering,
603
- spacing: { after: 60 },
604
- children: runs,
605
- })
606
- );
607
- } else if (child.type === "code") {
608
- // Code block inside list item
609
- const lines = (child.text || "").split("\n");
610
- for (const line of lines) {
611
- blocks.push(
612
- new Paragraph({
613
- spacing: { after: 0 },
614
- indent: { left: 400 + level * 720 },
615
- shading: { type: ShadingType.CLEAR, color: "auto", fill: LIGHT_GRAY },
616
- children: [new TextRun({ text: line || " ", font: FONT_CODE, size: 20 })],
617
- })
618
- );
619
- }
620
- }
621
- }
622
-
623
- // If no children processed, add a fallback
624
- if (itemChildren.length === 0) {
625
- const prefix = item.task ? (item.checked ? "☑ " : "☐ ") : "";
626
- blocks.push(
627
- new Paragraph({
628
- numbering,
629
- spacing: { after: 60 },
630
- children: [new TextRun({ text: prefix + (item.text || item.raw || ""), font: FONT_BODY, size: 24 })],
631
- })
632
- );
633
- }
634
-
635
- numIndex++;
636
- }
637
-
638
- return blocks;
639
- }
640
-
641
- // ── Document Assembler ─────────────────────────────────────────────────
642
-
643
- function assembleDocument(coverBlocks, tocBlocks, contentBlocks, meta) {
644
- const sections = [];
645
-
646
- // Cover page section (no header/footer)
647
- if (coverBlocks.length > 0) {
648
- sections.push({
649
- properties: {
650
- page: {
651
- size: { width: 11906, height: 16838 },
652
- margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
653
- },
654
- },
655
- children: coverBlocks,
656
- });
657
- }
658
-
659
- // TOC + Content in main section
660
- const mainChildren = [...tocBlocks, ...contentBlocks];
661
-
662
- sections.push({
663
- properties: {
664
- page: {
665
- size: { width: 11906, height: 16838 },
666
- margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
667
- },
668
- },
669
- children: mainChildren,
670
- });
671
-
672
- return new Document({
673
- features: { updateFields: true },
674
- styles: {
675
- default: {
676
- document: {
677
- run: { font: FONT_BODY, size: 24 },
678
- paragraph: { spacing: { line: 360 } },
679
- },
680
- heading1: {
681
- run: { font: FONT_HEADING, size: 44, bold: true, color: DARK_BLUE },
682
- paragraph: { spacing: { before: 400, after: 200 } },
683
- },
684
- heading2: {
685
- run: { font: FONT_HEADING, size: 32, bold: true, color: DARK_BLUE },
686
- paragraph: { spacing: { before: 300, after: 150 } },
687
- },
688
- heading3: {
689
- run: { font: FONT_HEADING, size: 28, bold: true, color: DARK_BLUE },
690
- paragraph: { spacing: { before: 240, after: 120 } },
691
- },
692
- heading4: {
693
- run: { font: FONT_HEADING, size: 24, bold: true, color: DARK_BLUE },
694
- paragraph: { spacing: { before: 200, after: 100 } },
695
- },
696
- },
697
- },
698
- numbering: {
699
- config: [
700
- {
701
- reference: "bullet-list",
702
- levels: [
703
- { level: 0, format: "bullet", text: "•", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } },
704
- { level: 1, format: "bullet", text: "◦", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 1440, hanging: 360 } } } },
705
- { level: 2, format: "bullet", text: "▪", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2160, hanging: 360 } } } },
706
- { level: 3, format: "bullet", text: "•", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2880, hanging: 360 } } } },
707
- ],
708
- },
709
- {
710
- reference: "numbered-list",
711
- levels: [
712
- { level: 0, format: "decimal", text: "%1.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } },
713
- { level: 1, format: "lowerLetter", text: "%2)", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 1440, hanging: 360 } } } },
714
- { level: 2, format: "lowerRoman", text: "%3.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2160, hanging: 360 } } } },
715
- { level: 3, format: "decimal", text: "%4.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2880, hanging: 360 } } } },
716
- ],
717
- },
718
- ],
719
- },
720
- sections,
721
- });
722
- }
723
-
724
- // ── Directory Scanner ──────────────────────────────────────────────────
725
-
726
- function scanDirectory(dir, recursive) {
727
- const files = [];
728
- const entries = fs.readdirSync(dir, { withFileTypes: true });
729
-
730
- for (const entry of entries) {
731
- const fullPath = path.join(dir, entry.name);
732
-
733
- if (entry.isDirectory()) {
734
- // Skip hidden directories
735
- if (entry.name.startsWith(".")) continue;
736
- if (recursive) {
737
- files.push(...scanDirectory(fullPath, true));
738
- }
739
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
740
- // Skip README.md
741
- if (entry.name.toLowerCase() === "readme.md") continue;
742
- files.push(fullPath);
743
- }
744
- }
745
-
746
- return files;
747
- }
748
-
749
- // ── Single File Processor ──────────────────────────────────────────────
750
-
751
- async function processFile(inputFile, outputFile, meta, options) {
752
- const { body, frontmatter } = readMarkdown(inputFile);
753
-
754
- // Merge frontmatter into meta (meta takes priority)
755
- const mergedMeta = { ...frontmatter, ...meta };
756
- if (!mergedMeta.title) {
757
- // Use first H1 or filename
758
- const h1Match = body.match(/^#\s+(.+)$/m);
759
- mergedMeta.title = h1Match ? h1Match[1] : path.basename(inputFile, ".md");
760
- }
761
-
762
- // Parse markdown
763
- const tokens = marked.lexer(body);
764
-
765
- // Build sections
766
- const coverBlocks = options.noCover ? [] : buildCoverPage(mergedMeta);
767
- const tocBlocks = options.noToc ? [] : buildTOC();
768
- const contentBlocks = convertTokens(tokens);
769
-
770
- // Assemble document
771
- const doc = assembleDocument(coverBlocks, tocBlocks, contentBlocks, mergedMeta);
772
-
773
- // Ensure output directory exists
774
- const outDir = path.dirname(outputFile);
775
- if (!fs.existsSync(outDir)) {
776
- fs.mkdirSync(outDir, { recursive: true });
777
- }
778
-
779
- // Write file
780
- const buffer = await Packer.toBuffer(doc);
781
- fs.writeFileSync(outputFile, buffer);
782
- }
783
-
784
- // ── Main ───────────────────────────────────────────────────────────────
785
-
786
- async function main() {
787
- const args = parseArgs();
788
- const meta = JSON.parse(args.meta || "{}");
789
- const options = { noCover: args.noCover, noToc: args.noToc };
790
- const results = [];
791
-
792
- try {
793
- if (args.dir) {
794
- // Directory mode
795
- const dirPath = path.resolve(args.dir);
796
- if (!fs.existsSync(dirPath)) {
797
- throw new Error(`目录不存在: ${dirPath}`);
798
- }
799
-
800
- const files = scanDirectory(dirPath, args.recursive);
801
- if (files.length === 0) {
802
- throw new Error(`目录中未找到 .md 文件: ${dirPath}`);
803
- }
804
-
805
- const outDir = args.output ? path.resolve(args.output) : dirPath;
806
-
807
- for (const file of files) {
808
- const baseName = path.basename(file, ".md");
809
- const outputFile = path.join(outDir, `${baseName}.docx`);
810
-
811
- try {
812
- await processFile(file, outputFile, { ...meta }, options);
813
- results.push({ input: file, output: outputFile, status: "ok" });
814
- } catch (err) {
815
- results.push({ input: file, output: outputFile, status: "error", error: err.message });
816
- }
817
- }
818
- } else if (args.input) {
819
- // Single file mode
820
- const inputFile = path.resolve(args.input);
821
- if (!fs.existsSync(inputFile)) {
822
- throw new Error(`文件不存在: ${inputFile}`);
823
- }
824
-
825
- const baseName = path.basename(inputFile, ".md");
826
- const outputFile = args.output
827
- ? path.resolve(args.output)
828
- : path.join(path.dirname(inputFile), `${baseName}.docx`);
829
-
830
- await processFile(inputFile, outputFile, meta, options);
831
- results.push({ input: inputFile, output: outputFile, status: "ok" });
832
- } else {
833
- throw new Error("必须指定 --input 或 --dir 参数");
834
- }
835
-
836
- const failed = results.filter((r) => r.status === "error");
837
- const status = failed.length === 0 ? "success" : failed.length === results.length ? "error" : "partial";
838
-
839
- console.log(
840
- JSON.stringify({
841
- status,
842
- files: results,
843
- cover: !args.noCover,
844
- toc: !args.noToc,
845
- total: results.length,
846
- success: results.length - failed.length,
847
- failed: failed.length,
848
- })
849
- );
850
- } catch (err) {
851
- console.error(
852
- JSON.stringify({
853
- status: "error",
854
- message: err.message,
855
- files: results,
856
- })
857
- );
858
- process.exit(1);
859
- }
860
- }
861
-
862
- main();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * md-to-docx.js — Markdown to Word (.docx) converter
4
+ *
5
+ * Features:
6
+ * - Auto-generated cover page (company logo, project info, document metadata)
7
+ * - Table of contents (Heading 1-3)
8
+ * - Full Markdown element conversion (headings, tables, code blocks, lists, images, blockquotes)
9
+ *
10
+ * Usage:
11
+ * node md-to-docx.js --input <file> --output <file> --meta '<json>'
12
+ * node md-to-docx.js --dir <dir> --output <dir> --meta '<json>' [--recursive]
13
+ * [--no-cover] [--no-toc]
14
+ */
15
+
16
+ const fs = require("fs");
17
+ const path = require("path");
18
+ const {
19
+ Document,
20
+ Packer,
21
+ Paragraph,
22
+ TextRun,
23
+ HeadingLevel,
24
+ AlignmentType,
25
+ TableOfContents,
26
+ StyleLevel,
27
+ Table,
28
+ TableRow,
29
+ TableCell,
30
+ WidthType,
31
+ BorderStyle,
32
+ ImageRun,
33
+ PageBreak,
34
+ ShadingType,
35
+ } = require("docx");
36
+ const { marked } = require("marked");
37
+
38
+ // ── Constants ──────────────────────────────────────────────────────────
39
+
40
+ const DARK_BLUE = "1F4E79";
41
+ const GRAY = "808080";
42
+ const LIGHT_GRAY = "F2F2F2";
43
+
44
+ const FONT_BODY = "DengXian";
45
+ const FONT_HEADING = "Microsoft YaHei";
46
+ const FONT_CODE = "Consolas";
47
+
48
+ const HEADING_STYLES = {
49
+ 1: { heading: HeadingLevel.HEADING_1, size: 44, font: FONT_HEADING },
50
+ 2: { heading: HeadingLevel.HEADING_2, size: 32, font: FONT_HEADING },
51
+ 3: { heading: HeadingLevel.HEADING_3, size: 28, font: FONT_HEADING },
52
+ 4: { heading: HeadingLevel.HEADING_4, size: 24, font: FONT_HEADING },
53
+ 5: { heading: HeadingLevel.HEADING_5, size: 22, font: FONT_HEADING },
54
+ 6: { heading: HeadingLevel.HEADING_6, size: 20, font: FONT_HEADING },
55
+ };
56
+
57
+ // ── CLI Arguments ──────────────────────────────────────────────────────
58
+
59
+ function parseArgs() {
60
+ const args = process.argv.slice(2);
61
+ const result = {
62
+ input: null,
63
+ dir: null,
64
+ output: null,
65
+ meta: "{}",
66
+ noCover: false,
67
+ noToc: false,
68
+ recursive: false,
69
+ };
70
+ for (let i = 0; i < args.length; i++) {
71
+ switch (args[i]) {
72
+ case "--input":
73
+ result.input = args[++i];
74
+ break;
75
+ case "--dir":
76
+ result.dir = args[++i];
77
+ break;
78
+ case "--output":
79
+ result.output = args[++i];
80
+ break;
81
+ case "--meta":
82
+ result.meta = args[++i];
83
+ break;
84
+ case "--no-cover":
85
+ result.noCover = true;
86
+ break;
87
+ case "--no-toc":
88
+ result.noToc = true;
89
+ break;
90
+ case "--recursive":
91
+ result.recursive = true;
92
+ break;
93
+ }
94
+ }
95
+ return result;
96
+ }
97
+
98
+ // ── Markdown Reader ────────────────────────────────────────────────────
99
+
100
+ function readMarkdown(filePath) {
101
+ const content = fs.readFileSync(filePath, "utf-8");
102
+
103
+ // Try YAML frontmatter
104
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
105
+ let frontmatter = {};
106
+ let body = content;
107
+
108
+ if (fmMatch) {
109
+ for (const line of fmMatch[1].split("\n")) {
110
+ const m = line.match(/^(\S+):\s*(.*)$/);
111
+ if (m) frontmatter[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
112
+ }
113
+ body = fmMatch[2];
114
+ } else {
115
+ // Try blockquote metadata: > **版本**: v1.0
116
+ const metaLines = [];
117
+ const restLines = [];
118
+ let inMeta = true;
119
+ for (const line of content.split("\n")) {
120
+ if (inMeta && line.startsWith(">")) {
121
+ metaLines.push(line);
122
+ } else {
123
+ inMeta = false;
124
+ restLines.push(line);
125
+ }
126
+ }
127
+ body = restLines.join("\n");
128
+ for (const line of metaLines) {
129
+ const m = line.match(/\*\*(.+?)\*\*[::]\s*(.+)/);
130
+ if (m) frontmatter[m[1].trim()] = m[2].trim();
131
+ }
132
+ }
133
+
134
+ return { body, frontmatter };
135
+ }
136
+
137
+ // ── Inline Token Converter ─────────────────────────────────────────────
138
+
139
+ function convertInline(tokens, overrides = {}) {
140
+ if (!tokens || !Array.isArray(tokens)) return [];
141
+ const runs = [];
142
+ const baseFont = overrides.font || FONT_BODY;
143
+ const baseSize = overrides.size || 24;
144
+
145
+ for (const token of tokens) {
146
+ switch (token.type) {
147
+ case "text": {
148
+ const children = token.tokens ? convertInline(token.tokens, overrides) : [];
149
+ if (children.length > 0) {
150
+ runs.push(...children);
151
+ } else {
152
+ runs.push(new TextRun({ text: token.text || "", font: baseFont, size: baseSize, ...overrides }));
153
+ }
154
+ break;
155
+ }
156
+ case "strong":
157
+ runs.push(...convertInline(token.tokens, { ...overrides, bold: true }));
158
+ break;
159
+ case "em":
160
+ runs.push(...convertInline(token.tokens, { ...overrides, italics: true }));
161
+ break;
162
+ case "del":
163
+ runs.push(...convertInline(token.tokens, { ...overrides, strike: true }));
164
+ break;
165
+ case "codespan":
166
+ runs.push(
167
+ new TextRun({ text: token.text || "", font: FONT_CODE, size: Math.min(baseSize, 20), ...overrides })
168
+ );
169
+ break;
170
+ case "link":
171
+ runs.push(
172
+ new TextRun({
173
+ text: token.text || (token.tokens ? token.tokens.map(t => t.text || t.raw || "").join("") : ""),
174
+ font: baseFont,
175
+ size: baseSize,
176
+ color: "0563C1",
177
+ underline: { type: "single" },
178
+ ...overrides,
179
+ })
180
+ );
181
+ break;
182
+ case "image":
183
+ try {
184
+ const imgPath = token.href;
185
+ if (imgPath && fs.existsSync(imgPath)) {
186
+ const buf = fs.readFileSync(imgPath);
187
+ runs.push(
188
+ new ImageRun({
189
+ data: buf,
190
+ transformation: { width: 200, height: 150 },
191
+ altText: { title: token.text || "Image", description: token.text || "Image", name: "image" },
192
+ })
193
+ );
194
+ } else {
195
+ runs.push(new TextRun({ text: `[Image: ${token.text || token.href}]`, font: baseFont, size: baseSize, italics: true, color: GRAY }));
196
+ }
197
+ } catch {
198
+ runs.push(new TextRun({ text: `[Image: ${token.text || token.href}]`, font: baseFont, size: baseSize, italics: true, color: GRAY }));
199
+ }
200
+ break;
201
+ case "br":
202
+ runs.push(new TextRun({ text: "", break: 1, font: baseFont, size: baseSize }));
203
+ break;
204
+ default:
205
+ if (token.text || token.raw) {
206
+ runs.push(new TextRun({ text: token.text || token.raw || "", font: baseFont, size: baseSize, ...overrides }));
207
+ }
208
+ }
209
+ }
210
+
211
+ return runs;
212
+ }
213
+
214
+ // ── Cover Page Builder ─────────────────────────────────────────────────
215
+
216
+ function buildCoverPage(meta) {
217
+ const blocks = [];
218
+ const spacer = (n) => {
219
+ for (let i = 0; i < n; i++) blocks.push(new Paragraph({ children: [new TextRun("")] }));
220
+ };
221
+
222
+ // 1. Top spacing
223
+ spacer(3);
224
+
225
+ // 2. Company logo
226
+ if (meta.company_logo && fs.existsSync(meta.company_logo)) {
227
+ try {
228
+ const logoBuf = fs.readFileSync(meta.company_logo);
229
+ blocks.push(
230
+ new Paragraph({
231
+ alignment: AlignmentType.CENTER,
232
+ children: [
233
+ new ImageRun({
234
+ data: logoBuf,
235
+ transformation: { width: 200, height: 200 },
236
+ altText: { title: "Company Logo", description: "Company Logo", name: "logo" },
237
+ }),
238
+ ],
239
+ })
240
+ );
241
+ spacer(2);
242
+ } catch {
243
+ // Skip logo on error
244
+ }
245
+ }
246
+
247
+ // 3. Company name
248
+ if (meta.company_name) {
249
+ blocks.push(
250
+ new Paragraph({
251
+ alignment: AlignmentType.CENTER,
252
+ children: [new TextRun({ text: meta.company_name, bold: true, font: FONT_HEADING, size: 32, color: DARK_BLUE })],
253
+ })
254
+ );
255
+ }
256
+
257
+ spacer(1);
258
+
259
+ // 4. Project name
260
+ if (meta.project_name) {
261
+ blocks.push(
262
+ new Paragraph({
263
+ alignment: AlignmentType.CENTER,
264
+ children: [new TextRun({ text: meta.project_name, bold: true, font: FONT_HEADING, size: 36, color: DARK_BLUE })],
265
+ })
266
+ );
267
+ }
268
+
269
+ spacer(2);
270
+
271
+ // 5. Document title
272
+ if (meta.title) {
273
+ blocks.push(
274
+ new Paragraph({
275
+ alignment: AlignmentType.CENTER,
276
+ children: [new TextRun({ text: meta.title, bold: true, font: FONT_HEADING, size: 56, color: DARK_BLUE })],
277
+ })
278
+ );
279
+ }
280
+
281
+ // 6. Subtitle
282
+ if (meta.subtitle) {
283
+ blocks.push(
284
+ new Paragraph({
285
+ alignment: AlignmentType.CENTER,
286
+ spacing: { before: 200 },
287
+ children: [new TextRun({ text: meta.subtitle, font: FONT_HEADING, size: 28, color: GRAY })],
288
+ })
289
+ );
290
+ }
291
+
292
+ spacer(3);
293
+
294
+ // 7. Info table
295
+ const fields = [
296
+ ["文档编号", meta.document_number],
297
+ ["版本号", meta.version],
298
+ ["创建日期", meta.date],
299
+ ["作者", meta.author],
300
+ ["审核人", meta.reviewer],
301
+ ["批准人", meta.approver],
302
+ ["保密等级", meta.confidentiality],
303
+ ].filter(([, v]) => v);
304
+
305
+ if (fields.length > 0) {
306
+ const borderStyle = { style: BorderStyle.SINGLE, size: 1, color: "BBBBBB" };
307
+ const borders = { top: borderStyle, bottom: borderStyle, left: borderStyle, right: borderStyle };
308
+
309
+ const rows = fields.map(
310
+ ([label, value]) =>
311
+ new TableRow({
312
+ children: [
313
+ new TableCell({
314
+ borders,
315
+ width: { size: 3000, type: WidthType.DXA },
316
+ children: [
317
+ new Paragraph({
318
+ alignment: AlignmentType.CENTER,
319
+ children: [new TextRun({ text: label, bold: true, font: FONT_HEADING, size: 22 })],
320
+ }),
321
+ ],
322
+ }),
323
+ new TableCell({
324
+ borders,
325
+ width: { size: 5000, type: WidthType.DXA },
326
+ children: [
327
+ new Paragraph({
328
+ children: [new TextRun({ text: value, font: FONT_BODY, size: 22 })],
329
+ }),
330
+ ],
331
+ }),
332
+ ],
333
+ })
334
+ );
335
+
336
+ blocks.push(
337
+ new Table({
338
+ rows,
339
+ width: { size: 8000, type: WidthType.DXA },
340
+ })
341
+ );
342
+ }
343
+
344
+ // 8. Page break
345
+ blocks.push(
346
+ new Paragraph({
347
+ children: [new PageBreak()],
348
+ })
349
+ );
350
+
351
+ return blocks;
352
+ }
353
+
354
+ // ── TOC Builder ────────────────────────────────────────────────────────
355
+
356
+ function buildTOC() {
357
+ return [
358
+ new Paragraph({
359
+ heading: HeadingLevel.HEADING_1,
360
+ children: [new TextRun({ text: "目录", font: FONT_HEADING, size: 44, bold: true, color: DARK_BLUE })],
361
+ }),
362
+ new TableOfContents("目录", {
363
+ hyperlink: true,
364
+ headingStyleRange: "1-3",
365
+ stylesWithLevels: [
366
+ new StyleLevel("Heading1", 1),
367
+ new StyleLevel("Heading2", 2),
368
+ new StyleLevel("Heading3", 3),
369
+ ],
370
+ }),
371
+ new Paragraph({
372
+ children: [new PageBreak()],
373
+ }),
374
+ ];
375
+ }
376
+
377
+ // ── Markdown → docx Converter ──────────────────────────────────────────
378
+
379
+ function convertTokens(tokens) {
380
+ const blocks = [];
381
+
382
+ for (const token of tokens) {
383
+ switch (token.type) {
384
+ case "heading": {
385
+ const style = HEADING_STYLES[token.depth] || HEADING_STYLES[6];
386
+ const runs = token.tokens
387
+ ? convertInline(token.tokens, { font: style.font, size: style.size, bold: true, color: DARK_BLUE })
388
+ : [new TextRun({ text: token.text || "", font: style.font, size: style.size, bold: true, color: DARK_BLUE })];
389
+ blocks.push(
390
+ new Paragraph({
391
+ heading: style.heading,
392
+ spacing: { before: 300, after: 100 },
393
+ children: runs,
394
+ })
395
+ );
396
+ break;
397
+ }
398
+
399
+ case "paragraph": {
400
+ const runs = convertInline(token.tokens);
401
+ blocks.push(
402
+ new Paragraph({
403
+ spacing: { after: 120 },
404
+ children: runs,
405
+ })
406
+ );
407
+ break;
408
+ }
409
+
410
+ case "code": {
411
+ const lines = (token.text || "").split("\n");
412
+ for (const line of lines) {
413
+ blocks.push(
414
+ new Paragraph({
415
+ spacing: { after: 0 },
416
+ indent: { left: 400 },
417
+ shading: { type: ShadingType.CLEAR, color: "auto", fill: LIGHT_GRAY },
418
+ children: [new TextRun({ text: line || " ", font: FONT_CODE, size: 20 })],
419
+ })
420
+ );
421
+ }
422
+ // Add spacing after code block
423
+ blocks.push(new Paragraph({ spacing: { after: 120 }, children: [new TextRun("")] }));
424
+ break;
425
+ }
426
+
427
+ case "table": {
428
+ const borderStyle = { style: BorderStyle.SINGLE, size: 1, color: "999999" };
429
+ const borders = { top: borderStyle, bottom: borderStyle, left: borderStyle, right: borderStyle };
430
+
431
+ // Header row
432
+ const headerRow = new TableRow({
433
+ tableHeader: true,
434
+ children: (token.header || []).map(
435
+ (h) =>
436
+ new TableCell({
437
+ borders,
438
+ shading: { type: ShadingType.CLEAR, color: "auto", fill: "D9E2F3" },
439
+ children: [
440
+ new Paragraph({
441
+ children: [
442
+ new TextRun({
443
+ text: h.text || "",
444
+ bold: true,
445
+ font: FONT_HEADING,
446
+ size: 22,
447
+ }),
448
+ ],
449
+ }),
450
+ ],
451
+ })
452
+ ),
453
+ });
454
+
455
+ // Data rows
456
+ const dataRows = (token.rows || []).map(
457
+ (row) =>
458
+ new TableRow({
459
+ children: row.map(
460
+ (cell) =>
461
+ new TableCell({
462
+ borders,
463
+ children: [
464
+ new Paragraph({
465
+ children: cell.tokens
466
+ ? convertInline(cell.tokens)
467
+ : [new TextRun({ text: cell.text || "", font: FONT_BODY, size: 22 })],
468
+ }),
469
+ ],
470
+ })
471
+ ),
472
+ })
473
+ );
474
+
475
+ blocks.push(new Table({ rows: [headerRow, ...dataRows] }));
476
+ // Spacing after table
477
+ blocks.push(new Paragraph({ spacing: { after: 120 }, children: [new TextRun("")] }));
478
+ break;
479
+ }
480
+
481
+ case "list": {
482
+ blocks.push(...convertList(token, 0));
483
+ break;
484
+ }
485
+
486
+ case "blockquote": {
487
+ const innerTokens = token.tokens || [];
488
+ for (const inner of innerTokens) {
489
+ if (inner.type === "paragraph") {
490
+ const runs = convertInline(inner.tokens, { italics: true, color: "555555" });
491
+ blocks.push(
492
+ new Paragraph({
493
+ indent: { left: 720 },
494
+ spacing: { after: 120 },
495
+ border: {
496
+ left: { style: BorderStyle.SINGLE, size: 6, color: DARK_BLUE, space: 10 },
497
+ },
498
+ shading: { type: ShadingType.CLEAR, color: "auto", fill: LIGHT_GRAY },
499
+ children: runs,
500
+ })
501
+ );
502
+ } else {
503
+ // Recursively handle other block types inside blockquote
504
+ const nested = convertTokens([inner]);
505
+ blocks.push(...nested);
506
+ }
507
+ }
508
+ break;
509
+ }
510
+
511
+ case "hr":
512
+ blocks.push(
513
+ new Paragraph({
514
+ spacing: { before: 200, after: 200 },
515
+ border: { bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } },
516
+ children: [new TextRun("")],
517
+ })
518
+ );
519
+ break;
520
+
521
+ case "html":
522
+ // Skip HTML blocks or render as plain text
523
+ blocks.push(
524
+ new Paragraph({
525
+ spacing: { after: 120 },
526
+ children: [new TextRun({ text: token.text || "", font: FONT_BODY, size: 24, color: GRAY })],
527
+ })
528
+ );
529
+ break;
530
+
531
+ case "space":
532
+ break;
533
+
534
+ default:
535
+ if (token.raw && token.text) {
536
+ blocks.push(
537
+ new Paragraph({
538
+ spacing: { after: 120 },
539
+ children: [new TextRun({ text: token.text || token.raw || "", font: FONT_BODY, size: 24 })],
540
+ })
541
+ );
542
+ }
543
+ }
544
+ }
545
+
546
+ return blocks;
547
+ }
548
+
549
+ // ── List Converter (handles nesting) ───────────────────────────────────
550
+
551
+ function convertList(token, level) {
552
+ const blocks = [];
553
+ const isOrdered = token.ordered;
554
+ const items = token.items || [];
555
+
556
+ let numIndex = 1;
557
+
558
+ for (const item of items) {
559
+ const numbering = isOrdered
560
+ ? { reference: "numbered-list", level }
561
+ : { reference: "bullet-list", level };
562
+
563
+ // Process item children (paragraphs, nested lists, code blocks)
564
+ const itemChildren = item.tokens || [];
565
+
566
+ for (const child of itemChildren) {
567
+ if (child.type === "list") {
568
+ // Nested list
569
+ blocks.push(...convertList(child, level + 1));
570
+ } else if (child.type === "text" && !child.tokens) {
571
+ // Plain text item
572
+ const prefix = item.task ? (item.checked ? "☑ " : "☐ ") : "";
573
+ blocks.push(
574
+ new Paragraph({
575
+ numbering,
576
+ spacing: { after: 60 },
577
+ children: [new TextRun({ text: prefix + (child.text || child.raw || ""), font: FONT_BODY, size: 24 })],
578
+ })
579
+ );
580
+ } else if (child.type === "text" && child.tokens) {
581
+ // Text with inline formatting
582
+ const runs = convertInline(child.tokens);
583
+ if (item.task) {
584
+ const prefix = item.checked ? "☑ " : "☐ ";
585
+ runs.unshift(new TextRun({ text: prefix, font: FONT_BODY, size: 24 }));
586
+ }
587
+ blocks.push(
588
+ new Paragraph({
589
+ numbering,
590
+ spacing: { after: 60 },
591
+ children: runs,
592
+ })
593
+ );
594
+ } else if (child.type === "paragraph") {
595
+ const runs = convertInline(child.tokens);
596
+ if (item.task) {
597
+ const prefix = item.checked ? "☑ " : "☐ ";
598
+ runs.unshift(new TextRun({ text: prefix, font: FONT_BODY, size: 24 }));
599
+ }
600
+ blocks.push(
601
+ new Paragraph({
602
+ numbering,
603
+ spacing: { after: 60 },
604
+ children: runs,
605
+ })
606
+ );
607
+ } else if (child.type === "code") {
608
+ // Code block inside list item
609
+ const lines = (child.text || "").split("\n");
610
+ for (const line of lines) {
611
+ blocks.push(
612
+ new Paragraph({
613
+ spacing: { after: 0 },
614
+ indent: { left: 400 + level * 720 },
615
+ shading: { type: ShadingType.CLEAR, color: "auto", fill: LIGHT_GRAY },
616
+ children: [new TextRun({ text: line || " ", font: FONT_CODE, size: 20 })],
617
+ })
618
+ );
619
+ }
620
+ }
621
+ }
622
+
623
+ // If no children processed, add a fallback
624
+ if (itemChildren.length === 0) {
625
+ const prefix = item.task ? (item.checked ? "☑ " : "☐ ") : "";
626
+ blocks.push(
627
+ new Paragraph({
628
+ numbering,
629
+ spacing: { after: 60 },
630
+ children: [new TextRun({ text: prefix + (item.text || item.raw || ""), font: FONT_BODY, size: 24 })],
631
+ })
632
+ );
633
+ }
634
+
635
+ numIndex++;
636
+ }
637
+
638
+ return blocks;
639
+ }
640
+
641
+ // ── Document Assembler ─────────────────────────────────────────────────
642
+
643
+ function assembleDocument(coverBlocks, tocBlocks, contentBlocks, meta) {
644
+ const sections = [];
645
+
646
+ // Cover page section (no header/footer)
647
+ if (coverBlocks.length > 0) {
648
+ sections.push({
649
+ properties: {
650
+ page: {
651
+ size: { width: 11906, height: 16838 },
652
+ margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
653
+ },
654
+ },
655
+ children: coverBlocks,
656
+ });
657
+ }
658
+
659
+ // TOC + Content in main section
660
+ const mainChildren = [...tocBlocks, ...contentBlocks];
661
+
662
+ sections.push({
663
+ properties: {
664
+ page: {
665
+ size: { width: 11906, height: 16838 },
666
+ margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
667
+ },
668
+ },
669
+ children: mainChildren,
670
+ });
671
+
672
+ return new Document({
673
+ features: { updateFields: true },
674
+ styles: {
675
+ default: {
676
+ document: {
677
+ run: { font: FONT_BODY, size: 24 },
678
+ paragraph: { spacing: { line: 360 } },
679
+ },
680
+ heading1: {
681
+ run: { font: FONT_HEADING, size: 44, bold: true, color: DARK_BLUE },
682
+ paragraph: { spacing: { before: 400, after: 200 } },
683
+ },
684
+ heading2: {
685
+ run: { font: FONT_HEADING, size: 32, bold: true, color: DARK_BLUE },
686
+ paragraph: { spacing: { before: 300, after: 150 } },
687
+ },
688
+ heading3: {
689
+ run: { font: FONT_HEADING, size: 28, bold: true, color: DARK_BLUE },
690
+ paragraph: { spacing: { before: 240, after: 120 } },
691
+ },
692
+ heading4: {
693
+ run: { font: FONT_HEADING, size: 24, bold: true, color: DARK_BLUE },
694
+ paragraph: { spacing: { before: 200, after: 100 } },
695
+ },
696
+ },
697
+ },
698
+ numbering: {
699
+ config: [
700
+ {
701
+ reference: "bullet-list",
702
+ levels: [
703
+ { level: 0, format: "bullet", text: "•", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } },
704
+ { level: 1, format: "bullet", text: "◦", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 1440, hanging: 360 } } } },
705
+ { level: 2, format: "bullet", text: "▪", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2160, hanging: 360 } } } },
706
+ { level: 3, format: "bullet", text: "•", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2880, hanging: 360 } } } },
707
+ ],
708
+ },
709
+ {
710
+ reference: "numbered-list",
711
+ levels: [
712
+ { level: 0, format: "decimal", text: "%1.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } },
713
+ { level: 1, format: "lowerLetter", text: "%2)", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 1440, hanging: 360 } } } },
714
+ { level: 2, format: "lowerRoman", text: "%3.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2160, hanging: 360 } } } },
715
+ { level: 3, format: "decimal", text: "%4.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 2880, hanging: 360 } } } },
716
+ ],
717
+ },
718
+ ],
719
+ },
720
+ sections,
721
+ });
722
+ }
723
+
724
+ // ── Directory Scanner ──────────────────────────────────────────────────
725
+
726
+ function scanDirectory(dir, recursive) {
727
+ const files = [];
728
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
729
+
730
+ for (const entry of entries) {
731
+ const fullPath = path.join(dir, entry.name);
732
+
733
+ if (entry.isDirectory()) {
734
+ // Skip hidden directories
735
+ if (entry.name.startsWith(".")) continue;
736
+ if (recursive) {
737
+ files.push(...scanDirectory(fullPath, true));
738
+ }
739
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
740
+ // Skip README.md
741
+ if (entry.name.toLowerCase() === "readme.md") continue;
742
+ files.push(fullPath);
743
+ }
744
+ }
745
+
746
+ return files;
747
+ }
748
+
749
+ // ── Single File Processor ──────────────────────────────────────────────
750
+
751
+ async function processFile(inputFile, outputFile, meta, options) {
752
+ const { body, frontmatter } = readMarkdown(inputFile);
753
+
754
+ // Merge frontmatter into meta (meta takes priority)
755
+ const mergedMeta = { ...frontmatter, ...meta };
756
+ if (!mergedMeta.title) {
757
+ // Use first H1 or filename
758
+ const h1Match = body.match(/^#\s+(.+)$/m);
759
+ mergedMeta.title = h1Match ? h1Match[1] : path.basename(inputFile, ".md");
760
+ }
761
+
762
+ // Parse markdown
763
+ const tokens = marked.lexer(body);
764
+
765
+ // Build sections
766
+ const coverBlocks = options.noCover ? [] : buildCoverPage(mergedMeta);
767
+ const tocBlocks = options.noToc ? [] : buildTOC();
768
+ const contentBlocks = convertTokens(tokens);
769
+
770
+ // Assemble document
771
+ const doc = assembleDocument(coverBlocks, tocBlocks, contentBlocks, mergedMeta);
772
+
773
+ // Ensure output directory exists
774
+ const outDir = path.dirname(outputFile);
775
+ if (!fs.existsSync(outDir)) {
776
+ fs.mkdirSync(outDir, { recursive: true });
777
+ }
778
+
779
+ // Write file
780
+ const buffer = await Packer.toBuffer(doc);
781
+ fs.writeFileSync(outputFile, buffer);
782
+ }
783
+
784
+ // ── Main ───────────────────────────────────────────────────────────────
785
+
786
+ async function main() {
787
+ const args = parseArgs();
788
+ const meta = JSON.parse(args.meta || "{}");
789
+ const options = { noCover: args.noCover, noToc: args.noToc };
790
+ const results = [];
791
+
792
+ try {
793
+ if (args.dir) {
794
+ // Directory mode
795
+ const dirPath = path.resolve(args.dir);
796
+ if (!fs.existsSync(dirPath)) {
797
+ throw new Error(`目录不存在: ${dirPath}`);
798
+ }
799
+
800
+ const files = scanDirectory(dirPath, args.recursive);
801
+ if (files.length === 0) {
802
+ throw new Error(`目录中未找到 .md 文件: ${dirPath}`);
803
+ }
804
+
805
+ const outDir = args.output ? path.resolve(args.output) : dirPath;
806
+
807
+ for (const file of files) {
808
+ const baseName = path.basename(file, ".md");
809
+ const outputFile = path.join(outDir, `${baseName}.docx`);
810
+
811
+ try {
812
+ await processFile(file, outputFile, { ...meta }, options);
813
+ results.push({ input: file, output: outputFile, status: "ok" });
814
+ } catch (err) {
815
+ results.push({ input: file, output: outputFile, status: "error", error: err.message });
816
+ }
817
+ }
818
+ } else if (args.input) {
819
+ // Single file mode
820
+ const inputFile = path.resolve(args.input);
821
+ if (!fs.existsSync(inputFile)) {
822
+ throw new Error(`文件不存在: ${inputFile}`);
823
+ }
824
+
825
+ const baseName = path.basename(inputFile, ".md");
826
+ const outputFile = args.output
827
+ ? path.resolve(args.output)
828
+ : path.join(path.dirname(inputFile), `${baseName}.docx`);
829
+
830
+ await processFile(inputFile, outputFile, meta, options);
831
+ results.push({ input: inputFile, output: outputFile, status: "ok" });
832
+ } else {
833
+ throw new Error("必须指定 --input 或 --dir 参数");
834
+ }
835
+
836
+ const failed = results.filter((r) => r.status === "error");
837
+ const status = failed.length === 0 ? "success" : failed.length === results.length ? "error" : "partial";
838
+
839
+ console.log(
840
+ JSON.stringify({
841
+ status,
842
+ files: results,
843
+ cover: !args.noCover,
844
+ toc: !args.noToc,
845
+ total: results.length,
846
+ success: results.length - failed.length,
847
+ failed: failed.length,
848
+ })
849
+ );
850
+ } catch (err) {
851
+ console.error(
852
+ JSON.stringify({
853
+ status: "error",
854
+ message: err.message,
855
+ files: results,
856
+ })
857
+ );
858
+ process.exit(1);
859
+ }
860
+ }
861
+
862
+ main();