@litfamily/litopencode 1.0.7 → 1.0.10

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 (726) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README-Ko-KR.md +348 -31
  3. package/README.md +347 -29
  4. package/dist/activation-primary-prompts.d.ts +1 -0
  5. package/dist/activation-primary-prompts.js +16 -0
  6. package/dist/activation-probe.js +4 -1
  7. package/dist/activation-prompt-utils.d.ts +1 -1
  8. package/dist/activation-routing.d.ts +6 -1
  9. package/dist/activation-routing.js +174 -16
  10. package/dist/activation-workflow-prompts.d.ts +1 -1
  11. package/dist/activation-workflow-prompts.js +18 -17
  12. package/dist/activation.d.ts +15 -8
  13. package/dist/activation.js +61 -7
  14. package/dist/agents/defaults.js +14 -6
  15. package/dist/cli/command-aliases.js +24 -22
  16. package/dist/cli/doctor.js +5 -0
  17. package/dist/cli/install-report.d.ts +1 -0
  18. package/dist/cli/install-report.js +4 -0
  19. package/dist/cli/install.js +7 -4
  20. package/dist/cli/managed-skill-assets.d.ts +1 -1
  21. package/dist/cli/managed-skill-assets.js +15 -5
  22. package/dist/cli/motion-runtime.d.ts +6 -0
  23. package/dist/cli/motion-runtime.js +46 -0
  24. package/dist/cli/native-skill-install.js +32 -9
  25. package/dist/cli/native-skill-integrity.js +1 -0
  26. package/dist/cli/native-skills.d.ts +2 -0
  27. package/dist/cli/native-skills.js +2 -1
  28. package/dist/cli/office-dependencies.d.ts +14 -0
  29. package/dist/cli/office-dependencies.js +24 -0
  30. package/dist/cli.js +13 -3
  31. package/dist/commands.d.ts +2 -11
  32. package/dist/commands.js +9 -17
  33. package/dist/deliverable-hedge-guard.d.ts +20 -21
  34. package/dist/deliverable-hedge-guard.js +234 -275
  35. package/dist/features.d.ts +81 -2
  36. package/dist/features.js +80 -13
  37. package/dist/hooks.d.ts +3 -2
  38. package/dist/hooks.js +4 -4
  39. package/dist/ignition.d.ts +1 -1
  40. package/dist/ignition.js +15 -4
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/interface-handoff.d.ts +1 -0
  44. package/dist/interface-handoff.js +1 -0
  45. package/dist/jev-skill-hint.d.ts +48 -0
  46. package/dist/jev-skill-hint.js +291 -0
  47. package/dist/motion-image-reads.d.ts +6 -0
  48. package/dist/motion-image-reads.js +46 -0
  49. package/dist/server.js +38 -20
  50. package/dist/skill-renames.d.ts +4 -2
  51. package/dist/skill-renames.js +12 -4
  52. package/dist/skills.d.ts +31 -3
  53. package/dist/skills.js +53 -9
  54. package/dist/tools.js +4 -4
  55. package/docs/ab/S11/baseline-desktop.webp +0 -0
  56. package/docs/ab/S11/baseline-phone.webp +0 -0
  57. package/docs/ab/S11/lit-desktop.webp +0 -0
  58. package/docs/ab/S11/lit-phone.webp +0 -0
  59. package/docs/ab/S3/baseline-desktop.webp +0 -0
  60. package/docs/ab/S3/baseline-phone.webp +0 -0
  61. package/docs/ab/S3/lit-desktop.webp +0 -0
  62. package/docs/ab/S3/lit-phone.webp +0 -0
  63. package/docs/ab/S4/baseline-desktop.webp +0 -0
  64. package/docs/ab/S4/baseline-phone.webp +0 -0
  65. package/docs/ab/S4/lit-desktop.webp +0 -0
  66. package/docs/ab/S4/lit-phone.webp +0 -0
  67. package/docs/ab/S5/lit-slides.webp +0 -0
  68. package/docs/ab/S7/lit-diagram.webp +0 -0
  69. package/docs/ab/S8/baseline-slides.webp +0 -0
  70. package/docs/ab/S8/lit-slides.webp +0 -0
  71. package/docs/ab/S9/lit-pages.webp +0 -0
  72. package/docs/assets/cover-motion-still.webp +0 -0
  73. package/docs/assets/cover-motion.webp +0 -0
  74. package/docs/assets/readme/badge-version.svg +1 -1
  75. package/docs/assets/skills/autoconference.webp +0 -0
  76. package/docs/assets/skills/automatic-guards.webp +0 -0
  77. package/docs/assets/skills/autoresearch.webp +0 -0
  78. package/docs/assets/skills/browser-drive.webp +0 -0
  79. package/docs/assets/skills/debugging.webp +0 -0
  80. package/docs/assets/skills/deep-interview.webp +0 -0
  81. package/docs/assets/skills/doctor-installer.webp +0 -0
  82. package/docs/assets/skills/durable-litgoal.webp +0 -0
  83. package/docs/assets/skills/frontend-ui-ux.webp +0 -0
  84. package/docs/assets/skills/lit-burnoff-file.webp +0 -0
  85. package/docs/assets/skills/lit-burnoff.webp +0 -0
  86. package/docs/assets/skills/lit-code.webp +0 -0
  87. package/docs/assets/skills/lit-commit.webp +0 -0
  88. package/docs/assets/skills/lit-comprehend.webp +0 -0
  89. package/docs/assets/skills/lit-crucible.webp +0 -0
  90. package/docs/assets/skills/lit-diagram-drawer.webp +0 -0
  91. package/docs/assets/skills/lit-docx.webp +0 -0
  92. package/docs/assets/skills/lit-fetch.webp +0 -0
  93. package/docs/assets/skills/lit-handoff.webp +0 -0
  94. package/docs/assets/skills/lit-humanizer.webp +0 -0
  95. package/docs/assets/skills/lit-init.webp +0 -0
  96. package/docs/assets/skills/lit-plan.webp +0 -0
  97. package/docs/assets/skills/lit-pptx.webp +0 -0
  98. package/docs/assets/skills/lit-recap.webp +0 -0
  99. package/docs/assets/skills/lit-scientific-visualization.webp +0 -0
  100. package/docs/assets/skills/lit-typographic-motion.webp +0 -0
  101. package/docs/assets/skills/litresearch.webp +0 -0
  102. package/docs/assets/skills/litwork.webp +0 -0
  103. package/docs/assets/skills/lsp-setup.webp +0 -0
  104. package/docs/assets/skills/lsp.webp +0 -0
  105. package/docs/assets/skills/readme-studio.webp +0 -0
  106. package/docs/assets/skills/refactor.webp +0 -0
  107. package/docs/assets/skills/review-work.webp +0 -0
  108. package/docs/assets/skills/rules.webp +0 -0
  109. package/docs/assets/skills/start-work.webp +0 -0
  110. package/docs/assets/skills/structural-search.webp +0 -0
  111. package/docs/assets/skills/visual-qa.webp +0 -0
  112. package/docs/assets/skills/wikify.webp +0 -0
  113. package/docs/privacy.md +12 -0
  114. package/docs/reference-Ko-KR.md +44 -1
  115. package/docs/reference.md +49 -2
  116. package/package.json +1 -1
  117. package/rules/bundled-rules/lit-humanizer.md +12 -0
  118. package/skills/autoconference/SKILL.md +1 -1
  119. package/skills/autoconference/assets/report_template.md +1 -1
  120. package/skills/autoresearch/references/type-checklists.md +1 -1
  121. package/skills/browser-drive/SKILL.md +19 -5
  122. package/skills/browser-drive/scripts/capability-probe.mjs +26 -5
  123. package/skills/frontend-ui-ux/SKILL.md +10 -15
  124. package/skills/frontend-ui-ux/data/THIRD-PARTY-NOTICE.txt +10 -0
  125. package/skills/frontend-ui-ux/references/adaptive-layout.md +3 -11
  126. package/skills/frontend-ui-ux/references/brand-and-imagery.md +6 -11
  127. package/skills/frontend-ui-ux/references/complete-contract.md +10 -3
  128. package/skills/frontend-ui-ux/references/composition.md +1 -7
  129. package/skills/frontend-ui-ux/references/craft-floor.md +94 -0
  130. package/skills/frontend-ui-ux/references/creative-directions.md +1 -7
  131. package/skills/frontend-ui-ux/references/evidence-review.md +2 -12
  132. package/skills/frontend-ui-ux/references/implementation-platforms.md +1 -4
  133. package/skills/frontend-ui-ux/references/inclusive-interface.md +1 -3
  134. package/skills/frontend-ui-ux/references/interaction-motion.md +1 -7
  135. package/skills/frontend-ui-ux/references/motion-guide.md +125 -0
  136. package/skills/frontend-ui-ux/references/operating-lanes.md +2 -8
  137. package/skills/frontend-ui-ux/references/product-direction.md +1 -6
  138. package/skills/frontend-ui-ux/references/production.md +12 -5
  139. package/skills/frontend-ui-ux/references/redesign-playbook.md +1 -7
  140. package/skills/frontend-ui-ux/references/slop-register.md +103 -0
  141. package/skills/frontend-ui-ux/references/system-foundations.md +1 -8
  142. package/skills/frontend-ui-ux/references/taste-direction.md +4 -11
  143. package/skills/frontend-ui-ux/references/visual-language.md +1 -6
  144. package/skills/frontend-ui-ux/references/visual-reconstruction.md +2 -7
  145. package/skills/frontend-ui-ux/scripts/probe-page.mjs +326 -0
  146. package/skills/frontend-ui-ux/scripts/probe-server.mjs +24 -0
  147. package/skills/frontend-ui-ux/scripts/probe-static.mjs +42 -0
  148. package/skills/frontend-ui-ux/scripts/probe-thresholds.json +86 -0
  149. package/skills/frontend-ui-ux/scripts/probe.mjs +183 -0
  150. package/skills/lit-comprehend/SKILL.md +10 -11
  151. package/skills/lit-comprehend/references/artifact-template.md +2 -18
  152. package/skills/lit-comprehend/scripts/verify-explainer.ts +0 -14
  153. package/skills/lit-diagram-drawer/NOTICE.md +33 -0
  154. package/skills/lit-diagram-drawer/SKILL.md +101 -0
  155. package/skills/lit-diagram-drawer/assets/examples/type-architecture-dark.html +1 -0
  156. package/skills/lit-diagram-drawer/assets/examples/type-architecture-full.html +1 -0
  157. package/skills/lit-diagram-drawer/assets/examples/type-architecture-light.html +1 -0
  158. package/skills/lit-diagram-drawer/assets/examples/type-bar-dark.html +1 -0
  159. package/skills/lit-diagram-drawer/assets/examples/type-bar-full.html +1 -0
  160. package/skills/lit-diagram-drawer/assets/examples/type-bar-light.html +1 -0
  161. package/skills/lit-diagram-drawer/assets/examples/type-beeswarm-dark.html +1 -0
  162. package/skills/lit-diagram-drawer/assets/examples/type-beeswarm-full.html +1 -0
  163. package/skills/lit-diagram-drawer/assets/examples/type-beeswarm-light.html +1 -0
  164. package/skills/lit-diagram-drawer/assets/examples/type-bubble-dark.html +1 -0
  165. package/skills/lit-diagram-drawer/assets/examples/type-bubble-full.html +1 -0
  166. package/skills/lit-diagram-drawer/assets/examples/type-bubble-light.html +1 -0
  167. package/skills/lit-diagram-drawer/assets/examples/type-bump-dark.html +1 -0
  168. package/skills/lit-diagram-drawer/assets/examples/type-bump-full.html +1 -0
  169. package/skills/lit-diagram-drawer/assets/examples/type-bump-light.html +1 -0
  170. package/skills/lit-diagram-drawer/assets/examples/type-data-flow-dark.html +1 -0
  171. package/skills/lit-diagram-drawer/assets/examples/type-data-flow-full.html +1 -0
  172. package/skills/lit-diagram-drawer/assets/examples/type-data-flow-light.html +1 -0
  173. package/skills/lit-diagram-drawer/assets/examples/type-datalake-dark.html +1 -0
  174. package/skills/lit-diagram-drawer/assets/examples/type-datalake-full.html +1 -0
  175. package/skills/lit-diagram-drawer/assets/examples/type-datalake-light.html +1 -0
  176. package/skills/lit-diagram-drawer/assets/examples/type-db-schema-dark.html +1 -0
  177. package/skills/lit-diagram-drawer/assets/examples/type-db-schema-full.html +1 -0
  178. package/skills/lit-diagram-drawer/assets/examples/type-db-schema-light.html +1 -0
  179. package/skills/lit-diagram-drawer/assets/examples/type-dependency-dark.html +1 -0
  180. package/skills/lit-diagram-drawer/assets/examples/type-dependency-full.html +1 -0
  181. package/skills/lit-diagram-drawer/assets/examples/type-dependency-light.html +1 -0
  182. package/skills/lit-diagram-drawer/assets/examples/type-deployment-dark.html +1 -0
  183. package/skills/lit-diagram-drawer/assets/examples/type-deployment-full.html +1 -0
  184. package/skills/lit-diagram-drawer/assets/examples/type-deployment-light.html +1 -0
  185. package/skills/lit-diagram-drawer/assets/examples/type-dp-integration-dark.html +1 -0
  186. package/skills/lit-diagram-drawer/assets/examples/type-dp-integration-full.html +1 -0
  187. package/skills/lit-diagram-drawer/assets/examples/type-dp-integration-light.html +1 -0
  188. package/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-dark.html +1 -0
  189. package/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-full.html +1 -0
  190. package/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-light.html +1 -0
  191. package/skills/lit-diagram-drawer/assets/examples/type-er-dark.html +1 -0
  192. package/skills/lit-diagram-drawer/assets/examples/type-er-full.html +1 -0
  193. package/skills/lit-diagram-drawer/assets/examples/type-er-light.html +1 -0
  194. package/skills/lit-diagram-drawer/assets/examples/type-fishbone-dark.html +1 -0
  195. package/skills/lit-diagram-drawer/assets/examples/type-fishbone-full.html +1 -0
  196. package/skills/lit-diagram-drawer/assets/examples/type-fishbone-light.html +1 -0
  197. package/skills/lit-diagram-drawer/assets/examples/type-flowchart-dark.html +1 -0
  198. package/skills/lit-diagram-drawer/assets/examples/type-flowchart-full.html +1 -0
  199. package/skills/lit-diagram-drawer/assets/examples/type-flowchart-light.html +1 -0
  200. package/skills/lit-diagram-drawer/assets/examples/type-gantt-dark.html +1 -0
  201. package/skills/lit-diagram-drawer/assets/examples/type-gantt-full.html +1 -0
  202. package/skills/lit-diagram-drawer/assets/examples/type-gantt-light.html +1 -0
  203. package/skills/lit-diagram-drawer/assets/examples/type-heatmap-dark.html +1 -0
  204. package/skills/lit-diagram-drawer/assets/examples/type-heatmap-full.html +1 -0
  205. package/skills/lit-diagram-drawer/assets/examples/type-heatmap-light.html +1 -0
  206. package/skills/lit-diagram-drawer/assets/examples/type-high-level-dark.html +1 -0
  207. package/skills/lit-diagram-drawer/assets/examples/type-high-level-full.html +1 -0
  208. package/skills/lit-diagram-drawer/assets/examples/type-high-level-light.html +1 -0
  209. package/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-dark.html +1 -0
  210. package/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-full.html +1 -0
  211. package/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-light.html +1 -0
  212. package/skills/lit-diagram-drawer/assets/examples/type-import-drawio-dark.html +1 -0
  213. package/skills/lit-diagram-drawer/assets/examples/type-import-drawio-full.html +1 -0
  214. package/skills/lit-diagram-drawer/assets/examples/type-import-drawio-light.html +1 -0
  215. package/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-dark.html +1 -0
  216. package/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-full.html +1 -0
  217. package/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-light.html +1 -0
  218. package/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-dark.html +1 -0
  219. package/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-full.html +1 -0
  220. package/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-light.html +1 -0
  221. package/skills/lit-diagram-drawer/assets/examples/type-it-state-dark.html +1 -0
  222. package/skills/lit-diagram-drawer/assets/examples/type-it-state-full.html +1 -0
  223. package/skills/lit-diagram-drawer/assets/examples/type-it-state-light.html +1 -0
  224. package/skills/lit-diagram-drawer/assets/examples/type-journey-dark.html +1 -0
  225. package/skills/lit-diagram-drawer/assets/examples/type-journey-full.html +1 -0
  226. package/skills/lit-diagram-drawer/assets/examples/type-journey-light.html +1 -0
  227. package/skills/lit-diagram-drawer/assets/examples/type-kanban-dark.html +1 -0
  228. package/skills/lit-diagram-drawer/assets/examples/type-kanban-full.html +1 -0
  229. package/skills/lit-diagram-drawer/assets/examples/type-kanban-light.html +1 -0
  230. package/skills/lit-diagram-drawer/assets/examples/type-layers-dark.html +1 -0
  231. package/skills/lit-diagram-drawer/assets/examples/type-layers-full.html +1 -0
  232. package/skills/lit-diagram-drawer/assets/examples/type-layers-light.html +1 -0
  233. package/skills/lit-diagram-drawer/assets/examples/type-line-dark.html +1 -0
  234. package/skills/lit-diagram-drawer/assets/examples/type-line-full.html +1 -0
  235. package/skills/lit-diagram-drawer/assets/examples/type-line-light.html +1 -0
  236. package/skills/lit-diagram-drawer/assets/examples/type-loop-dark.html +1 -0
  237. package/skills/lit-diagram-drawer/assets/examples/type-loop-full.html +1 -0
  238. package/skills/lit-diagram-drawer/assets/examples/type-loop-light.html +1 -0
  239. package/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-dark.html +1 -0
  240. package/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-full.html +1 -0
  241. package/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-light.html +1 -0
  242. package/skills/lit-diagram-drawer/assets/examples/type-marimekko-dark.html +1 -0
  243. package/skills/lit-diagram-drawer/assets/examples/type-marimekko-full.html +1 -0
  244. package/skills/lit-diagram-drawer/assets/examples/type-marimekko-light.html +1 -0
  245. package/skills/lit-diagram-drawer/assets/examples/type-medallion-dark.html +1 -0
  246. package/skills/lit-diagram-drawer/assets/examples/type-medallion-full.html +1 -0
  247. package/skills/lit-diagram-drawer/assets/examples/type-medallion-light.html +1 -0
  248. package/skills/lit-diagram-drawer/assets/examples/type-nested-dark.html +1 -0
  249. package/skills/lit-diagram-drawer/assets/examples/type-nested-full.html +1 -0
  250. package/skills/lit-diagram-drawer/assets/examples/type-nested-light.html +1 -0
  251. package/skills/lit-diagram-drawer/assets/examples/type-org-chart-dark.html +1 -0
  252. package/skills/lit-diagram-drawer/assets/examples/type-org-chart-full.html +1 -0
  253. package/skills/lit-diagram-drawer/assets/examples/type-org-chart-light.html +1 -0
  254. package/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-dark.html +1 -0
  255. package/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-full.html +1 -0
  256. package/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-light.html +1 -0
  257. package/skills/lit-diagram-drawer/assets/examples/type-polar-dark.html +1 -0
  258. package/skills/lit-diagram-drawer/assets/examples/type-polar-full.html +1 -0
  259. package/skills/lit-diagram-drawer/assets/examples/type-polar-light.html +1 -0
  260. package/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-dark.html +1 -0
  261. package/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-full.html +1 -0
  262. package/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-light.html +1 -0
  263. package/skills/lit-diagram-drawer/assets/examples/type-process-dark.html +1 -0
  264. package/skills/lit-diagram-drawer/assets/examples/type-process-full.html +1 -0
  265. package/skills/lit-diagram-drawer/assets/examples/type-process-light.html +1 -0
  266. package/skills/lit-diagram-drawer/assets/examples/type-pyramid-dark.html +1 -0
  267. package/skills/lit-diagram-drawer/assets/examples/type-pyramid-full.html +1 -0
  268. package/skills/lit-diagram-drawer/assets/examples/type-pyramid-light.html +1 -0
  269. package/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-dark.html +1 -0
  270. package/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-full.html +1 -0
  271. package/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-light.html +1 -0
  272. package/skills/lit-diagram-drawer/assets/examples/type-quadrant-dark.html +1 -0
  273. package/skills/lit-diagram-drawer/assets/examples/type-quadrant-full.html +1 -0
  274. package/skills/lit-diagram-drawer/assets/examples/type-quadrant-light.html +1 -0
  275. package/skills/lit-diagram-drawer/assets/examples/type-queue-animated-dark.html +1 -0
  276. package/skills/lit-diagram-drawer/assets/examples/type-queue-animated-full.html +1 -0
  277. package/skills/lit-diagram-drawer/assets/examples/type-queue-animated-light.html +1 -0
  278. package/skills/lit-diagram-drawer/assets/examples/type-radar-dark.html +1 -0
  279. package/skills/lit-diagram-drawer/assets/examples/type-radar-full.html +1 -0
  280. package/skills/lit-diagram-drawer/assets/examples/type-radar-light.html +1 -0
  281. package/skills/lit-diagram-drawer/assets/examples/type-ridgeline-dark.html +1 -0
  282. package/skills/lit-diagram-drawer/assets/examples/type-ridgeline-full.html +1 -0
  283. package/skills/lit-diagram-drawer/assets/examples/type-ridgeline-light.html +1 -0
  284. package/skills/lit-diagram-drawer/assets/examples/type-sankey-dark.html +1 -0
  285. package/skills/lit-diagram-drawer/assets/examples/type-sankey-full.html +1 -0
  286. package/skills/lit-diagram-drawer/assets/examples/type-sankey-light.html +1 -0
  287. package/skills/lit-diagram-drawer/assets/examples/type-scatter-dark.html +1 -0
  288. package/skills/lit-diagram-drawer/assets/examples/type-scatter-full.html +1 -0
  289. package/skills/lit-diagram-drawer/assets/examples/type-scatter-light.html +1 -0
  290. package/skills/lit-diagram-drawer/assets/examples/type-sequence-dark.html +1 -0
  291. package/skills/lit-diagram-drawer/assets/examples/type-sequence-full.html +1 -0
  292. package/skills/lit-diagram-drawer/assets/examples/type-sequence-light.html +1 -0
  293. package/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-dark.html +1 -0
  294. package/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-full.html +1 -0
  295. package/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-light.html +1 -0
  296. package/skills/lit-diagram-drawer/assets/examples/type-slopegraph-dark.html +1 -0
  297. package/skills/lit-diagram-drawer/assets/examples/type-slopegraph-full.html +1 -0
  298. package/skills/lit-diagram-drawer/assets/examples/type-slopegraph-light.html +1 -0
  299. package/skills/lit-diagram-drawer/assets/examples/type-state-dark.html +1 -0
  300. package/skills/lit-diagram-drawer/assets/examples/type-state-full.html +1 -0
  301. package/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-dark.html +1 -0
  302. package/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-full.html +1 -0
  303. package/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-light.html +1 -0
  304. package/skills/lit-diagram-drawer/assets/examples/type-state-light.html +1 -0
  305. package/skills/lit-diagram-drawer/assets/examples/type-story-map-dark.html +1 -0
  306. package/skills/lit-diagram-drawer/assets/examples/type-story-map-full.html +1 -0
  307. package/skills/lit-diagram-drawer/assets/examples/type-story-map-light.html +1 -0
  308. package/skills/lit-diagram-drawer/assets/examples/type-streamgraph-dark.html +1 -0
  309. package/skills/lit-diagram-drawer/assets/examples/type-streamgraph-full.html +1 -0
  310. package/skills/lit-diagram-drawer/assets/examples/type-streamgraph-light.html +1 -0
  311. package/skills/lit-diagram-drawer/assets/examples/type-swimlane-dark.html +1 -0
  312. package/skills/lit-diagram-drawer/assets/examples/type-swimlane-full.html +1 -0
  313. package/skills/lit-diagram-drawer/assets/examples/type-swimlane-light.html +1 -0
  314. package/skills/lit-diagram-drawer/assets/examples/type-timeline-dark.html +1 -0
  315. package/skills/lit-diagram-drawer/assets/examples/type-timeline-full.html +1 -0
  316. package/skills/lit-diagram-drawer/assets/examples/type-timeline-light.html +1 -0
  317. package/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-dark.html +1 -0
  318. package/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-full.html +1 -0
  319. package/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-light.html +1 -0
  320. package/skills/lit-diagram-drawer/assets/examples/type-tree-dark.html +1 -0
  321. package/skills/lit-diagram-drawer/assets/examples/type-tree-full.html +1 -0
  322. package/skills/lit-diagram-drawer/assets/examples/type-tree-light.html +1 -0
  323. package/skills/lit-diagram-drawer/assets/examples/type-treemap-dark.html +1 -0
  324. package/skills/lit-diagram-drawer/assets/examples/type-treemap-full.html +1 -0
  325. package/skills/lit-diagram-drawer/assets/examples/type-treemap-light.html +1 -0
  326. package/skills/lit-diagram-drawer/assets/examples/type-uml-class-dark.html +1 -0
  327. package/skills/lit-diagram-drawer/assets/examples/type-uml-class-full.html +1 -0
  328. package/skills/lit-diagram-drawer/assets/examples/type-uml-class-light.html +1 -0
  329. package/skills/lit-diagram-drawer/assets/examples/type-venn-dark.html +1 -0
  330. package/skills/lit-diagram-drawer/assets/examples/type-venn-full.html +1 -0
  331. package/skills/lit-diagram-drawer/assets/examples/type-venn-light.html +1 -0
  332. package/skills/lit-diagram-drawer/assets/examples/type-wardley-dark.html +1 -0
  333. package/skills/lit-diagram-drawer/assets/examples/type-wardley-full.html +1 -0
  334. package/skills/lit-diagram-drawer/assets/examples/type-wardley-light.html +1 -0
  335. package/skills/lit-diagram-drawer/assets/examples/type-waterfall-dark.html +1 -0
  336. package/skills/lit-diagram-drawer/assets/examples/type-waterfall-full.html +1 -0
  337. package/skills/lit-diagram-drawer/assets/examples/type-waterfall-light.html +1 -0
  338. package/skills/lit-diagram-drawer/assets/fonts/OFL.txt +94 -0
  339. package/skills/lit-diagram-drawer/assets/fonts/PretendardVariable.woff2 +0 -0
  340. package/skills/lit-diagram-drawer/assets/fonts/provenance.json +23 -0
  341. package/skills/lit-diagram-drawer/assets/icons.html +231 -0
  342. package/skills/lit-diagram-drawer/assets/licenses/devicon-MIT.txt +20 -0
  343. package/skills/lit-diagram-drawer/assets/licenses/diagram-design-MIT.txt +21 -0
  344. package/skills/lit-diagram-drawer/assets/licenses/log-z-MIT.txt +21 -0
  345. package/skills/lit-diagram-drawer/assets/licenses/simple-icons-CC0.md +30 -0
  346. package/skills/lit-diagram-drawer/assets/licenses/tabler-MIT.txt +21 -0
  347. package/skills/lit-diagram-drawer/examples/01-service-architecture/after.html +1 -0
  348. package/skills/lit-diagram-drawer/examples/01-service-architecture/brief.md +33 -0
  349. package/skills/lit-diagram-drawer/examples/02-release-decision/after.html +1 -0
  350. package/skills/lit-diagram-drawer/examples/02-release-decision/brief.md +18 -0
  351. package/skills/lit-diagram-drawer/examples/03-data-retention/after.html +1 -0
  352. package/skills/lit-diagram-drawer/examples/03-data-retention/brief.md +19 -0
  353. package/skills/lit-diagram-drawer/examples/04-oauth-sequence/after.html +1 -0
  354. package/skills/lit-diagram-drawer/examples/04-oauth-sequence/brief.md +17 -0
  355. package/skills/lit-diagram-drawer/examples/05-review-lanes/after.html +1 -0
  356. package/skills/lit-diagram-drawer/examples/05-review-lanes/brief.md +17 -0
  357. package/skills/lit-diagram-drawer/examples/06-rollout-gates/after.html +1 -0
  358. package/skills/lit-diagram-drawer/examples/06-rollout-gates/brief.md +17 -0
  359. package/skills/lit-diagram-drawer/examples/07-deployment-boundary/after.html +1 -0
  360. package/skills/lit-diagram-drawer/examples/07-deployment-boundary/brief.md +19 -0
  361. package/skills/lit-diagram-drawer/examples/08-policy-traces/after.html +1 -0
  362. package/skills/lit-diagram-drawer/examples/08-policy-traces/brief.md +20 -0
  363. package/skills/lit-diagram-drawer/references/accessibility.md +37 -0
  364. package/skills/lit-diagram-drawer/references/export.md +17 -0
  365. package/skills/lit-diagram-drawer/references/import-drawio.md +13 -0
  366. package/skills/lit-diagram-drawer/references/import-excalidraw.md +11 -0
  367. package/skills/lit-diagram-drawer/references/import-mermaid.md +12 -0
  368. package/skills/lit-diagram-drawer/references/import-schema.md +28 -0
  369. package/skills/lit-diagram-drawer/references/korean-typography.md +34 -0
  370. package/skills/lit-diagram-drawer/references/motion.md +29 -0
  371. package/skills/lit-diagram-drawer/references/office-pptx-docx.md +28 -0
  372. package/skills/lit-diagram-drawer/references/output-spec.md +48 -0
  373. package/skills/lit-diagram-drawer/references/primitive-annotation.md +12 -0
  374. package/skills/lit-diagram-drawer/references/primitive-icons.md +21 -0
  375. package/skills/lit-diagram-drawer/references/primitive-sketchy.md +11 -0
  376. package/skills/lit-diagram-drawer/references/primitive-terminal.md +11 -0
  377. package/skills/lit-diagram-drawer/references/profiles.md +24 -0
  378. package/skills/lit-diagram-drawer/references/semantic-patterns.md +27 -0
  379. package/skills/lit-diagram-drawer/references/style-guide.md +67 -0
  380. package/skills/lit-diagram-drawer/references/type-architecture.md +51 -0
  381. package/skills/lit-diagram-drawer/references/type-bar.md +51 -0
  382. package/skills/lit-diagram-drawer/references/type-beeswarm.md +49 -0
  383. package/skills/lit-diagram-drawer/references/type-bubble.md +49 -0
  384. package/skills/lit-diagram-drawer/references/type-bump.md +49 -0
  385. package/skills/lit-diagram-drawer/references/type-catalog.json +1433 -0
  386. package/skills/lit-diagram-drawer/references/type-data-flow.md +51 -0
  387. package/skills/lit-diagram-drawer/references/type-datalake.md +49 -0
  388. package/skills/lit-diagram-drawer/references/type-db-schema.md +49 -0
  389. package/skills/lit-diagram-drawer/references/type-dependency.md +49 -0
  390. package/skills/lit-diagram-drawer/references/type-deployment.md +49 -0
  391. package/skills/lit-diagram-drawer/references/type-dp-integration.md +49 -0
  392. package/skills/lit-diagram-drawer/references/type-dp-security-matrix.md +49 -0
  393. package/skills/lit-diagram-drawer/references/type-er.md +49 -0
  394. package/skills/lit-diagram-drawer/references/type-fishbone.md +49 -0
  395. package/skills/lit-diagram-drawer/references/type-flowchart.md +49 -0
  396. package/skills/lit-diagram-drawer/references/type-gantt.md +49 -0
  397. package/skills/lit-diagram-drawer/references/type-heatmap.md +53 -0
  398. package/skills/lit-diagram-drawer/references/type-high-level-vertical.md +53 -0
  399. package/skills/lit-diagram-drawer/references/type-high-level.md +53 -0
  400. package/skills/lit-diagram-drawer/references/type-import-drawio.md +53 -0
  401. package/skills/lit-diagram-drawer/references/type-import-excalidraw.md +53 -0
  402. package/skills/lit-diagram-drawer/references/type-import-mermaid.md +53 -0
  403. package/skills/lit-diagram-drawer/references/type-it-state.md +54 -0
  404. package/skills/lit-diagram-drawer/references/type-journey.md +54 -0
  405. package/skills/lit-diagram-drawer/references/type-kanban.md +54 -0
  406. package/skills/lit-diagram-drawer/references/type-layers.md +54 -0
  407. package/skills/lit-diagram-drawer/references/type-line.md +54 -0
  408. package/skills/lit-diagram-drawer/references/type-loop-terminal.md +53 -0
  409. package/skills/lit-diagram-drawer/references/type-loop.md +54 -0
  410. package/skills/lit-diagram-drawer/references/type-marimekko.md +54 -0
  411. package/skills/lit-diagram-drawer/references/type-medallion.md +54 -0
  412. package/skills/lit-diagram-drawer/references/type-nested.md +59 -0
  413. package/skills/lit-diagram-drawer/references/type-org-chart.md +58 -0
  414. package/skills/lit-diagram-drawer/references/type-paved-road-animated.md +72 -0
  415. package/skills/lit-diagram-drawer/references/type-polar.md +67 -0
  416. package/skills/lit-diagram-drawer/references/type-policy-trace-animated.md +69 -0
  417. package/skills/lit-diagram-drawer/references/type-process.md +58 -0
  418. package/skills/lit-diagram-drawer/references/type-pyramid.md +59 -0
  419. package/skills/lit-diagram-drawer/references/type-quadrant-consultant.md +60 -0
  420. package/skills/lit-diagram-drawer/references/type-quadrant.md +57 -0
  421. package/skills/lit-diagram-drawer/references/type-queue-animated.md +63 -0
  422. package/skills/lit-diagram-drawer/references/type-radar.md +57 -0
  423. package/skills/lit-diagram-drawer/references/type-ridgeline.md +63 -0
  424. package/skills/lit-diagram-drawer/references/type-sankey.md +68 -0
  425. package/skills/lit-diagram-drawer/references/type-scatter.md +60 -0
  426. package/skills/lit-diagram-drawer/references/type-sequence-oauth.md +66 -0
  427. package/skills/lit-diagram-drawer/references/type-sequence.md +63 -0
  428. package/skills/lit-diagram-drawer/references/type-slopegraph.md +65 -0
  429. package/skills/lit-diagram-drawer/references/type-state-lifecycle.md +53 -0
  430. package/skills/lit-diagram-drawer/references/type-state.md +59 -0
  431. package/skills/lit-diagram-drawer/references/type-story-map.md +52 -0
  432. package/skills/lit-diagram-drawer/references/type-streamgraph.md +53 -0
  433. package/skills/lit-diagram-drawer/references/type-swimlane.md +52 -0
  434. package/skills/lit-diagram-drawer/references/type-timeline.md +52 -0
  435. package/skills/lit-diagram-drawer/references/type-tree-block-decomposition.md +57 -0
  436. package/skills/lit-diagram-drawer/references/type-tree.md +54 -0
  437. package/skills/lit-diagram-drawer/references/type-treemap.md +54 -0
  438. package/skills/lit-diagram-drawer/references/type-uml-class.md +52 -0
  439. package/skills/lit-diagram-drawer/references/type-venn.md +51 -0
  440. package/skills/lit-diagram-drawer/references/type-wardley.md +52 -0
  441. package/skills/lit-diagram-drawer/references/type-waterfall.md +54 -0
  442. package/skills/lit-diagram-drawer/references/verifier-guide.md +59 -0
  443. package/skills/lit-diagram-drawer/scripts/block-registry.mjs +43 -0
  444. package/skills/lit-diagram-drawer/scripts/brief-contract.mjs +94 -0
  445. package/skills/lit-diagram-drawer/scripts/capability-probe.mjs +132 -0
  446. package/skills/lit-diagram-drawer/scripts/check-visible-text.mjs +15 -0
  447. package/skills/lit-diagram-drawer/scripts/craft-thresholds.json +15 -0
  448. package/skills/lit-diagram-drawer/scripts/diagram-metrics.mjs +216 -0
  449. package/skills/lit-diagram-drawer/scripts/doctor.mjs +8 -0
  450. package/skills/lit-diagram-drawer/scripts/drawio-extract.mjs +214 -0
  451. package/skills/lit-diagram-drawer/scripts/excalidraw-extract.mjs +151 -0
  452. package/skills/lit-diagram-drawer/scripts/export.mjs +133 -0
  453. package/skills/lit-diagram-drawer/scripts/import-shared.mjs +130 -0
  454. package/skills/lit-diagram-drawer/scripts/mermaid-extract.mjs +173 -0
  455. package/skills/lit-diagram-drawer/scripts/office-safety.mjs +120 -0
  456. package/skills/lit-diagram-drawer/scripts/renderer-safety.mjs +46 -0
  457. package/skills/lit-diagram-drawer/scripts/test-block-registry.mjs +20 -0
  458. package/skills/lit-diagram-drawer/scripts/test-brief-contract.mjs +27 -0
  459. package/skills/lit-diagram-drawer/scripts/test-importers.mjs +119 -0
  460. package/skills/lit-diagram-drawer/scripts/test-office-safety.mjs +67 -0
  461. package/skills/lit-diagram-drawer/scripts/test-sequence-verifier.mjs +28 -0
  462. package/skills/lit-diagram-drawer/scripts/test-visual-quality.mjs +68 -0
  463. package/skills/lit-diagram-drawer/scripts/verify-all.mjs +74 -0
  464. package/skills/lit-diagram-drawer/scripts/verify-brief.mjs +47 -0
  465. package/skills/lit-diagram-drawer/scripts/verify-coverage.mjs +56 -0
  466. package/skills/lit-diagram-drawer/scripts/verify-diagram.mjs +26 -0
  467. package/skills/lit-diagram-drawer/scripts/verify-motion.mjs +12 -0
  468. package/skills/lit-diagram-drawer/scripts/verify-type.mjs +48 -0
  469. package/skills/lit-diagram-drawer/scripts/visual-quality.mjs +363 -0
  470. package/skills/lit-docx/LICENSE-MIT.txt +21 -0
  471. package/skills/lit-docx/SKILL.md +78 -0
  472. package/skills/lit-docx/references/frontmatter_schema.md +42 -0
  473. package/skills/lit-docx/references/journal_style_spec.md +17 -0
  474. package/skills/lit-docx/references/markdown_quality_checklist.md +37 -0
  475. package/skills/lit-docx/references/slop_phrase_list.yaml +58 -0
  476. package/skills/lit-docx/references/slop_rules.md +35 -0
  477. package/skills/lit-docx/requirements.in +5 -0
  478. package/skills/lit-docx/requirements.lock +289 -0
  479. package/skills/lit-docx/run.py +64 -0
  480. package/skills/lit-docx/scripts/clean_markdown.py +95 -0
  481. package/skills/lit-docx/scripts/convert_md_to_docx.py +1047 -0
  482. package/skills/lit-docx/scripts/convert_md_to_pdf.py +281 -0
  483. package/skills/lit-docx/scripts/convert_pdf.py +130 -0
  484. package/skills/lit-docx/scripts/craft-thresholds.json +11 -0
  485. package/skills/lit-docx/scripts/craft_docx.py +83 -0
  486. package/skills/lit-docx/scripts/edit_docx.py +297 -0
  487. package/skills/lit-docx/scripts/embed_images.py +78 -0
  488. package/skills/lit-docx/scripts/generate_docx_templates.py +491 -0
  489. package/skills/lit-docx/scripts/slop_lint.py +485 -0
  490. package/skills/lit-docx/scripts/visual_audit.py +41 -0
  491. package/skills/lit-docx/templates/docx/acs.docx +0 -0
  492. package/skills/lit-docx/templates/docx/elsevier.docx +0 -0
  493. package/skills/lit-docx/templates/docx/ieee.docx +0 -0
  494. package/skills/lit-docx/templates/docx/korean-generic.docx +0 -0
  495. package/skills/lit-docx/templates/docx/nature.docx +0 -0
  496. package/skills/lit-docx/templates/latex/acs/README.md +5 -0
  497. package/skills/lit-docx/templates/latex/acs/template.tex +37 -0
  498. package/skills/lit-docx/templates/latex/elsevier/README.md +5 -0
  499. package/skills/lit-docx/templates/latex/elsevier/template.tex +38 -0
  500. package/skills/lit-docx/templates/latex/ieee/README.md +5 -0
  501. package/skills/lit-docx/templates/latex/ieee/template.tex +37 -0
  502. package/skills/lit-docx/templates/latex/korean-generic/README.md +5 -0
  503. package/skills/lit-docx/templates/latex/korean-generic/template.tex +37 -0
  504. package/skills/lit-docx/templates/latex/nature/README.md +5 -0
  505. package/skills/lit-docx/templates/latex/nature/template.tex +37 -0
  506. package/skills/lit-docx/templates/registry.yaml +1366 -0
  507. package/skills/lit-humanizer/NOTICE +160 -0
  508. package/skills/lit-humanizer/SKILL.md +122 -0
  509. package/skills/lit-humanizer/assets/always-on-rule.md +9 -0
  510. package/skills/lit-humanizer/assets/report-skeleton.md +25 -0
  511. package/skills/lit-humanizer/assets/slide-text-skeleton.md +28 -0
  512. package/skills/lit-humanizer/examples/accessibility-note/after.md +7 -0
  513. package/skills/lit-humanizer/examples/accessibility-note/before.md +7 -0
  514. package/skills/lit-humanizer/examples/api-reference/after.md +9 -0
  515. package/skills/lit-humanizer/examples/api-reference/before.md +15 -0
  516. package/skills/lit-humanizer/examples/changelog/after.md +5 -0
  517. package/skills/lit-humanizer/examples/changelog/before.md +12 -0
  518. package/skills/lit-humanizer/examples/code-comments/after.md +10 -0
  519. package/skills/lit-humanizer/examples/code-comments/before.md +19 -0
  520. package/skills/lit-humanizer/examples/commit/after.md +5 -0
  521. package/skills/lit-humanizer/examples/commit/before.md +7 -0
  522. package/skills/lit-humanizer/examples/data-release/after.md +7 -0
  523. package/skills/lit-humanizer/examples/data-release/before.md +9 -0
  524. package/skills/lit-humanizer/examples/english-essay/after.md +5 -0
  525. package/skills/lit-humanizer/examples/english-essay/before.md +5 -0
  526. package/skills/lit-humanizer/examples/incident-update/after.md +7 -0
  527. package/skills/lit-humanizer/examples/incident-update/before.md +7 -0
  528. package/skills/lit-humanizer/examples/ko-essay/after.md +7 -0
  529. package/skills/lit-humanizer/examples/ko-essay/before.md +9 -0
  530. package/skills/lit-humanizer/examples/ko-report/after.md +9 -0
  531. package/skills/lit-humanizer/examples/ko-report/before.md +13 -0
  532. package/skills/lit-humanizer/examples/ko-slide/after.md +19 -0
  533. package/skills/lit-humanizer/examples/ko-slide/before.md +21 -0
  534. package/skills/lit-humanizer/examples/meeting-brief/after.md +9 -0
  535. package/skills/lit-humanizer/examples/meeting-brief/before.md +11 -0
  536. package/skills/lit-humanizer/examples/policy-faq/after.md +9 -0
  537. package/skills/lit-humanizer/examples/policy-faq/before.md +9 -0
  538. package/skills/lit-humanizer/examples/pr/after.md +15 -0
  539. package/skills/lit-humanizer/examples/pr/before.md +17 -0
  540. package/skills/lit-humanizer/examples/project-update/after.md +7 -0
  541. package/skills/lit-humanizer/examples/project-update/before.md +9 -0
  542. package/skills/lit-humanizer/examples/readme/after.md +12 -0
  543. package/skills/lit-humanizer/examples/readme/before.md +14 -0
  544. package/skills/lit-humanizer/examples/report/after.md +13 -0
  545. package/skills/lit-humanizer/examples/report/before.md +11 -0
  546. package/skills/lit-humanizer/examples/research-abstract/after.md +5 -0
  547. package/skills/lit-humanizer/examples/research-abstract/before.md +5 -0
  548. package/skills/lit-humanizer/examples/slides/after.md +19 -0
  549. package/skills/lit-humanizer/examples/slides/before.md +19 -0
  550. package/skills/lit-humanizer/examples/troubleshooting/after.md +11 -0
  551. package/skills/lit-humanizer/examples/troubleshooting/before.md +11 -0
  552. package/skills/lit-humanizer/fixtures/ko-metrics-golden.json +49 -0
  553. package/skills/lit-humanizer/fixtures/office/minimal.docx +0 -0
  554. package/skills/lit-humanizer/fixtures/office/minimal.pptx +0 -0
  555. package/skills/lit-humanizer/fixtures/pos-real.txt +26 -0
  556. package/skills/lit-humanizer/fixtures/rule-cases.json +306 -0
  557. package/skills/lit-humanizer/references/code-patterns.md +468 -0
  558. package/skills/lit-humanizer/references/deliverable-channels.md +111 -0
  559. package/skills/lit-humanizer/references/en-patterns-checklist.md +43 -0
  560. package/skills/lit-humanizer/references/en-patterns-content.md +243 -0
  561. package/skills/lit-humanizer/references/en-patterns-structure.md +375 -0
  562. package/skills/lit-humanizer/references/en-patterns.md +57 -0
  563. package/skills/lit-humanizer/references/ko-metrics.md +33 -0
  564. package/skills/lit-humanizer/references/ko-patterns-a-d.md +659 -0
  565. package/skills/lit-humanizer/references/ko-patterns-e-j.md +387 -0
  566. package/skills/lit-humanizer/references/ko-patterns.md +31 -0
  567. package/skills/lit-humanizer/references/rewrite-playbook.md +54 -0
  568. package/skills/lit-humanizer/references/taxonomy.md +153 -0
  569. package/skills/lit-humanizer/rules.json +884 -0
  570. package/skills/lit-humanizer/scripts/core.mjs +208 -0
  571. package/skills/lit-humanizer/scripts/detect.mjs +91 -0
  572. package/skills/lit-humanizer/scripts/extract_office_text.py +89 -0
  573. package/skills/lit-humanizer/scripts/guard-scan.mjs +36 -0
  574. package/skills/lit-humanizer/scripts/ko-metrics.mjs +124 -0
  575. package/skills/lit-humanizer/scripts/test-fixtures.mjs +54 -0
  576. package/skills/lit-humanizer/scripts/test-ko-metrics.mjs +27 -0
  577. package/skills/lit-pptx/FORBIDDEN_TERMS.json +55 -0
  578. package/skills/lit-pptx/NOTICE.md +3 -0
  579. package/skills/lit-pptx/SKILL.md +76 -0
  580. package/skills/lit-pptx/agents/analyst.md +49 -0
  581. package/skills/lit-pptx/agents/antithesis.md +59 -0
  582. package/skills/lit-pptx/agents/architect.md +44 -0
  583. package/skills/lit-pptx/agents/critic.md +42 -0
  584. package/skills/lit-pptx/agents/synthesizer.md +47 -0
  585. package/skills/lit-pptx/agents/thesis.md +33 -0
  586. package/skills/lit-pptx/assets/azure/circle-lg.png +0 -0
  587. package/skills/lit-pptx/assets/azure/circle-md.png +0 -0
  588. package/skills/lit-pptx/assets/azure/circle-sm.png +0 -0
  589. package/skills/lit-pptx/fonts/a2z-font/A2Z-Black.otf +0 -0
  590. package/skills/lit-pptx/fonts/a2z-font/A2Z-Bold.otf +0 -0
  591. package/skills/lit-pptx/fonts/a2z-font/A2Z-Light.otf +0 -0
  592. package/skills/lit-pptx/fonts/a2z-font/A2Z-Medium.otf +0 -0
  593. package/skills/lit-pptx/fonts/a2z-font/A2Z-Regular.otf +0 -0
  594. package/skills/lit-pptx/fonts/a2z-font/OFL.txt +94 -0
  595. package/skills/lit-pptx/fonts/a2z-font/SOURCE.md +29 -0
  596. package/skills/lit-pptx/package-lock.json +747 -0
  597. package/skills/lit-pptx/package.json +13 -0
  598. package/skills/lit-pptx/pretendard-font/LICENSE.txt +94 -0
  599. package/skills/lit-pptx/pretendard-font/public/static/LitOpenCodeSans-Bold.otf +0 -0
  600. package/skills/lit-pptx/pretendard-font/public/static/LitOpenCodeSans-Regular.otf +0 -0
  601. package/skills/lit-pptx/requirements.in +2 -0
  602. package/skills/lit-pptx/requirements.lock +285 -0
  603. package/skills/lit-pptx/run.mjs +71 -0
  604. package/skills/lit-pptx/scripts/check_ooxml.py +47 -0
  605. package/skills/lit-pptx/scripts/compile-deck.js +447 -0
  606. package/skills/lit-pptx/scripts/craft-thresholds.json +26 -0
  607. package/skills/lit-pptx/scripts/craft_extras.py +224 -0
  608. package/skills/lit-pptx/scripts/embed_fonts.py +204 -0
  609. package/skills/lit-pptx/scripts/layout_inventory.py +70 -0
  610. package/skills/lit-pptx/scripts/learn_template.py +433 -0
  611. package/skills/lit-pptx/scripts/lib/derive-palette.js +176 -0
  612. package/skills/lit-pptx/scripts/lib/directive-parser.js +244 -0
  613. package/skills/lit-pptx/scripts/lib/font-map.js +79 -0
  614. package/skills/lit-pptx/scripts/lib/gen-bg.js +85 -0
  615. package/skills/lit-pptx/scripts/lib/gen-gradient-assets.js +36 -0
  616. package/skills/lit-pptx/scripts/lib/layout-resolver.js +737 -0
  617. package/skills/lit-pptx/scripts/lib/markdown-loader.js +136 -0
  618. package/skills/lit-pptx/scripts/lib/render-adapter-html.js +364 -0
  619. package/skills/lit-pptx/scripts/lib/render-adapter-pptx.js +1848 -0
  620. package/skills/lit-pptx/scripts/lib/slide-ast.js +534 -0
  621. package/skills/lit-pptx/scripts/lib/spec-validator.js +94 -0
  622. package/skills/lit-pptx/scripts/lib/template-registry.js +355 -0
  623. package/skills/lit-pptx/scripts/qa_deck.py +773 -0
  624. package/skills/lit-pptx/scripts/validate_pptx.py +206 -0
  625. package/skills/lit-pptx/specs/markdown-slide-spec-v1.md +251 -0
  626. package/skills/lit-pptx/specs/markdown-slide-spec-v2.md +223 -0
  627. package/skills/lit-pptx/specs/slide-ast-v1.schema.json +229 -0
  628. package/skills/lit-pptx/specs/slide-ast-v2.schema.json +144 -0
  629. package/skills/lit-pptx/specs/template-enrollment-contract.md +379 -0
  630. package/skills/lit-pptx/templates/bauhaus_light.css +4 -0
  631. package/skills/lit-pptx/templates/boilerplate_professional.css +508 -0
  632. package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/capabilities.yaml +146 -0
  633. package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/layout-mapping.yaml +454 -0
  634. package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/template.yaml +37 -0
  635. package/skills/lit-pptx/templates/enrolled/AZURE-PRO/capabilities.yaml +154 -0
  636. package/skills/lit-pptx/templates/enrolled/AZURE-PRO/layout-mapping.yaml +483 -0
  637. package/skills/lit-pptx/templates/enrolled/AZURE-PRO/template.yaml +37 -0
  638. package/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/capabilities.yaml +179 -0
  639. package/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/layout-mapping.yaml +299 -0
  640. package/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/template.yaml +45 -0
  641. package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/capabilities.yaml +181 -0
  642. package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/layout-mapping.yaml +299 -0
  643. package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/template.yaml +44 -0
  644. package/skills/lit-scientific-visualization/SKILL.md +2 -0
  645. package/skills/lit-typographic-motion/NOTICE +23 -0
  646. package/skills/lit-typographic-motion/SKILL.md +77 -0
  647. package/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +25 -0
  648. package/skills/lit-typographic-motion/audio-analysis.py +52 -0
  649. package/skills/lit-typographic-motion/engine/browser.mjs +83 -0
  650. package/skills/lit-typographic-motion/engine/constants.mjs +177 -0
  651. package/skills/lit-typographic-motion/engine/contrast.mjs +101 -0
  652. package/skills/lit-typographic-motion/engine/encode.mjs +46 -0
  653. package/skills/lit-typographic-motion/engine/flash.mjs +216 -0
  654. package/skills/lit-typographic-motion/engine/look.mjs +166 -0
  655. package/skills/lit-typographic-motion/engine/page/common.js +105 -0
  656. package/skills/lit-typographic-motion/engine/page/engine.js +451 -0
  657. package/skills/lit-typographic-motion/engine/page/gl.js +213 -0
  658. package/skills/lit-typographic-motion/engine/page/lines.js +88 -0
  659. package/skills/lit-typographic-motion/engine/page/passes.js +144 -0
  660. package/skills/lit-typographic-motion/engine/page/post.js +119 -0
  661. package/skills/lit-typographic-motion/engine/page/scenes.js +187 -0
  662. package/skills/lit-typographic-motion/engine/page/type.js +321 -0
  663. package/skills/lit-typographic-motion/engine/png.mjs +139 -0
  664. package/skills/lit-typographic-motion/engine/sound.mjs +291 -0
  665. package/skills/lit-typographic-motion/engine/stage/capture.mjs +133 -0
  666. package/skills/lit-typographic-motion/engine/stage/clock.js +171 -0
  667. package/skills/lit-typographic-motion/engine/stage/frame-worker.mjs +47 -0
  668. package/skills/lit-typographic-motion/engine/stage/gate.mjs +119 -0
  669. package/skills/lit-typographic-motion/engine/stage/qa-page.js +158 -0
  670. package/skills/lit-typographic-motion/engine/stage/qa.mjs +208 -0
  671. package/skills/lit-typographic-motion/engine/stage/render.mjs +418 -0
  672. package/skills/lit-typographic-motion/engine/stage/scan.mjs +162 -0
  673. package/skills/lit-typographic-motion/engine/stage/stage-kit.js +310 -0
  674. package/skills/lit-typographic-motion/engine/stills.mjs +60 -0
  675. package/skills/lit-typographic-motion/engine/text.mjs +126 -0
  676. package/skills/lit-typographic-motion/engine/timing.mjs +289 -0
  677. package/skills/lit-typographic-motion/engine/treatment.mjs +254 -0
  678. package/skills/lit-typographic-motion/fonts/Archivo-100-400.ttf +0 -0
  679. package/skills/lit-typographic-motion/fonts/Archivo-100-700.ttf +0 -0
  680. package/skills/lit-typographic-motion/fonts/Archivo-100-900.ttf +0 -0
  681. package/skills/lit-typographic-motion/fonts/Archivo-125-400.ttf +0 -0
  682. package/skills/lit-typographic-motion/fonts/Archivo-125-700.ttf +0 -0
  683. package/skills/lit-typographic-motion/fonts/Archivo-125-900.ttf +0 -0
  684. package/skills/lit-typographic-motion/fonts/Archivo-75-400.ttf +0 -0
  685. package/skills/lit-typographic-motion/fonts/Archivo-75-700.ttf +0 -0
  686. package/skills/lit-typographic-motion/fonts/Archivo-75-900.ttf +0 -0
  687. package/skills/lit-typographic-motion/fonts/Archivo-OFL.txt +93 -0
  688. package/skills/lit-typographic-motion/fonts/Silkscreen-Bold.ttf +0 -0
  689. package/skills/lit-typographic-motion/fonts/Silkscreen-OFL.txt +93 -0
  690. package/skills/lit-typographic-motion/fonts/Silkscreen-Regular.ttf +0 -0
  691. package/skills/lit-typographic-motion/fonts/VT323-OFL.txt +93 -0
  692. package/skills/lit-typographic-motion/fonts/VT323-Regular.ttf +0 -0
  693. package/skills/lit-typographic-motion/fonts/pins.json +44 -0
  694. package/skills/lit-typographic-motion/fonts/stroke/CREDITS +52 -0
  695. package/skills/lit-typographic-motion/fonts/stroke/EMSAllure.svg +216 -0
  696. package/skills/lit-typographic-motion/fonts/stroke/EMSFelix.svg +216 -0
  697. package/skills/lit-typographic-motion/fonts/stroke/EMSOsmotron.svg +216 -0
  698. package/skills/lit-typographic-motion/fonts/stroke/EMSReadability.svg +216 -0
  699. package/skills/lit-typographic-motion/fonts/stroke/EMSTech.svg +217 -0
  700. package/skills/lit-typographic-motion/fonts/stroke/OFL.txt +85 -0
  701. package/skills/lit-typographic-motion/gate.mjs +593 -0
  702. package/skills/lit-typographic-motion/package-lock.json +78 -0
  703. package/skills/lit-typographic-motion/package.json +11 -0
  704. package/skills/lit-typographic-motion/probe.mjs +74 -0
  705. package/skills/lit-typographic-motion/references/authoring.md +110 -0
  706. package/skills/lit-typographic-motion/references/craft-loop.md +110 -0
  707. package/skills/lit-typographic-motion/references/runtime.md +88 -0
  708. package/skills/lit-typographic-motion/references/stage.md +164 -0
  709. package/skills/lit-typographic-motion/references/style-bibles.md +88 -0
  710. package/skills/lit-typographic-motion/references/treatment.md +143 -0
  711. package/skills/lit-typographic-motion/references/type-craft.md +55 -0
  712. package/skills/lit-typographic-motion/render.mjs +654 -0
  713. package/skills/lit-typographic-motion/requirements-audio.in +1 -0
  714. package/skills/lit-typographic-motion/requirements-audio.txt +790 -0
  715. package/skills/lit-typographic-motion/runtime.mjs +259 -0
  716. package/skills/lit-typographic-motion/word-timing-models.json +25 -0
  717. package/skills/litresearch/SKILL.md +41 -12
  718. package/skills/managed-skill-manifest.json +2367 -63
  719. package/skills/skill-rename-aliases.json +6 -1
  720. package/tools/check-pack-payload.mjs +22 -1
  721. package/tools/check-payload-substance.mjs +2 -2
  722. package/tools/payload-reference-exemptions.json +4 -0
  723. package/tools/payload-substance-allowlist.json +2 -3
  724. package/tools/payload-substance-parity.json +7 -7
  725. package/tools/version-manifests.json +2 -8
  726. package/skills/lit-korean/SKILL.md +0 -231
@@ -0,0 +1,1047 @@
1
+ #!/usr/bin/env python3
2
+ """Convert Markdown to DOCX with proper styling.
3
+
4
+ Usage:
5
+ python convert_md_to_docx.py input.md output.docx [--template template.docx]
6
+ python convert_md_to_docx.py input.md output.docx --publisher elsevier
7
+
8
+ Features:
9
+ - Headings (H1-H6 → Word heading styles)
10
+ - Paragraphs with bold/italic/code formatting
11
+ - Bullet and numbered lists
12
+ - Tables
13
+ - Code blocks (monospace font)
14
+ - Images (if local)
15
+ - Links (as hyperlinks)
16
+ - [NEW in M1] --publisher NAME loads templates/registry.yaml and applies
17
+ journal design discipline: title block from YAML frontmatter, curly
18
+ quotes, en/em dashes, nbsp before units, ellipsis, heading auto-numbering,
19
+ booktabs tables, banned-font/style stripping, CJK font pairing.
20
+ """
21
+
22
+ from __future__ import annotations
23
+ import argparse
24
+ import re
25
+ import sys
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ try:
30
+ import markdown
31
+ from markdown.extensions.tables import TableExtension
32
+ from markdown.extensions.fenced_code import FencedCodeExtension
33
+ except ImportError:
34
+ print("Error: 'markdown' library not found. Install with: pip install markdown")
35
+ sys.exit(1)
36
+
37
+ try:
38
+ from docx import Document
39
+ from docx.shared import Pt, Inches, Cm, RGBColor
40
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
41
+ from docx.enum.style import WD_STYLE_TYPE
42
+ from docx.oxml.ns import qn
43
+ from docx.oxml import OxmlElement
44
+ except ImportError:
45
+ print("Error: 'python-docx' library not found. Install with: pip install python-docx")
46
+ sys.exit(1)
47
+
48
+ try:
49
+ from bs4 import BeautifulSoup, NavigableString
50
+ except ImportError:
51
+ print("Error: 'beautifulsoup4' library not found. Install with: pip install beautifulsoup4")
52
+ sys.exit(1)
53
+
54
+
55
+ # ===========================================================================
56
+ # Legacy HTML→DOCX helpers (unchanged from pre-M1 — preserved byte-for-byte)
57
+ # ===========================================================================
58
+
59
+
60
+ def create_hyperlink(paragraph, url: str, text: str):
61
+ """Add a hyperlink to a paragraph."""
62
+ part = paragraph.part
63
+ r_id = part.relate_to(url, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", is_external=True)
64
+
65
+ hyperlink = OxmlElement('w:hyperlink')
66
+ hyperlink.set(qn('r:id'), r_id)
67
+
68
+ new_run = OxmlElement('w:r')
69
+ rPr = OxmlElement('w:rPr')
70
+
71
+ # Blue color and underline for hyperlink
72
+ color = OxmlElement('w:color')
73
+ color.set(qn('w:val'), '0000FF')
74
+ rPr.append(color)
75
+
76
+ underline = OxmlElement('w:u')
77
+ underline.set(qn('w:val'), 'single')
78
+ rPr.append(underline)
79
+
80
+ new_run.append(rPr)
81
+
82
+ text_elem = OxmlElement('w:t')
83
+ text_elem.text = text
84
+ new_run.append(text_elem)
85
+
86
+ hyperlink.append(new_run)
87
+ paragraph._p.append(hyperlink)
88
+
89
+
90
+ def process_inline_elements(paragraph, element, md_path: Path):
91
+ """Process inline elements like bold, italic, code, links within a paragraph."""
92
+ if isinstance(element, NavigableString):
93
+ text = str(element)
94
+ if text.strip():
95
+ paragraph.add_run(text)
96
+ elif text:
97
+ paragraph.add_run(text)
98
+ return
99
+
100
+ if element.name == 'strong' or element.name == 'b':
101
+ run = paragraph.add_run(element.get_text())
102
+ run.bold = True
103
+ elif element.name == 'em' or element.name == 'i':
104
+ run = paragraph.add_run(element.get_text())
105
+ run.italic = True
106
+ elif element.name == 'code':
107
+ run = paragraph.add_run(element.get_text())
108
+ run.font.name = 'Courier New'
109
+ run.font.size = Pt(10)
110
+ elif element.name == 'a':
111
+ href = element.get('href', '')
112
+ text = element.get_text()
113
+ if href:
114
+ create_hyperlink(paragraph, href, text)
115
+ else:
116
+ paragraph.add_run(text)
117
+ elif element.name == 'img':
118
+ src = element.get('src', '')
119
+ if src and not src.startswith(('http://', 'https://', 'data:')):
120
+ img_path = (md_path.parent / src).resolve()
121
+ if img_path.exists():
122
+ try:
123
+ paragraph.add_run().add_picture(str(img_path), width=Inches(4))
124
+ except Exception:
125
+ paragraph.add_run(f"[Image: {src}]")
126
+ else:
127
+ paragraph.add_run(f"[Image not found: {src}]")
128
+ else:
129
+ paragraph.add_run(f"[Image: {src}]")
130
+ elif element.name == 'br':
131
+ paragraph.add_run('\n')
132
+ else:
133
+ # Recursively process children
134
+ for child in element.children:
135
+ process_inline_elements(paragraph, child, md_path)
136
+
137
+
138
+ def add_paragraph_with_formatting(doc: Document, element, md_path: Path, style: str = None):
139
+ """Add a paragraph with inline formatting."""
140
+ para = doc.add_paragraph(style=style)
141
+ for child in element.children:
142
+ process_inline_elements(para, child, md_path)
143
+ return para
144
+
145
+
146
+ def process_list(doc: Document, element, md_path: Path, ordered: bool = False, level: int = 0):
147
+ """Process ordered or unordered lists."""
148
+ for i, li in enumerate(element.find_all('li', recursive=False)):
149
+ # Create paragraph with list style
150
+ para = doc.add_paragraph(style='List Number' if ordered else 'List Bullet')
151
+
152
+ # Set indentation for nested lists
153
+ if level > 0:
154
+ para.paragraph_format.left_indent = Inches(0.5 * level)
155
+
156
+ # Process direct text content
157
+ for child in li.children:
158
+ if isinstance(child, NavigableString):
159
+ text = str(child).strip()
160
+ if text:
161
+ para.add_run(text)
162
+ elif child.name in ('ul', 'ol'):
163
+ # Nested list
164
+ process_list(doc, child, md_path, ordered=(child.name == 'ol'), level=level + 1)
165
+ elif child.name == 'p':
166
+ # Process paragraph content inline
167
+ for subchild in child.children:
168
+ process_inline_elements(para, subchild, md_path)
169
+ else:
170
+ process_inline_elements(para, child, md_path)
171
+
172
+
173
+ def process_table(doc: Document, element, md_path: Path):
174
+ """Process HTML table and create DOCX table."""
175
+ rows = element.find_all('tr')
176
+ if not rows:
177
+ return
178
+
179
+ # Count columns from first row
180
+ first_row = rows[0]
181
+ cols = len(first_row.find_all(['th', 'td']))
182
+
183
+ if cols == 0:
184
+ return
185
+
186
+ table = doc.add_table(rows=len(rows), cols=cols)
187
+ table.style = 'Table Grid'
188
+ table.autofit = False
189
+ section = doc.sections[-1]
190
+ usable_width = section.page_width - section.left_margin - section.right_margin
191
+ # Weight each column by its longest visible cell, with a floor so short
192
+ # numeric columns remain readable and a cap so prose cannot crush peers.
193
+ weights = []
194
+ for idx in range(cols):
195
+ lengths = [len(cells[idx].get_text(" ", strip=True)) for tr in rows
196
+ if len(cells := tr.find_all(['th', 'td'])) > idx]
197
+ weights.append(max(8, min(48, max(lengths, default=8))))
198
+ headers = first_row.find_all(['th', 'td'])
199
+ floors = [int(Inches(min(1.5, max(0.75, sum(2 if ord(c) > 127 else 1 for c in h.get_text(strip=True)) * 0.12))))
200
+ for h in headers]
201
+ if sum(floors) > usable_width:
202
+ floors = [int(usable_width * value / sum(floors)) for value in floors]
203
+ spare = usable_width - sum(floors)
204
+ total = sum(weights)
205
+ widths = [floor + int(spare * weight / total) for floor, weight in zip(floors, weights)]
206
+ widths[-1] += usable_width - sum(widths)
207
+ for idx, width in enumerate(widths):
208
+ table.columns[idx].width = width
209
+
210
+ for row_idx, tr in enumerate(rows):
211
+ row = table.rows[row_idx]
212
+ tr_pr = row._tr.get_or_add_trPr()
213
+ tr_pr.append(OxmlElement('w:cantSplit'))
214
+ if row_idx == 0:
215
+ tr_pr.append(OxmlElement('w:tblHeader'))
216
+ cells = tr.find_all(['th', 'td'])
217
+ for col_idx, cell in enumerate(cells):
218
+ if col_idx < cols:
219
+ table_cell = row.cells[col_idx]
220
+ table_cell.width = widths[col_idx]
221
+ # Clear default paragraph
222
+ table_cell.text = ''
223
+ para = table_cell.paragraphs[0]
224
+ for child in cell.children:
225
+ process_inline_elements(para, child, md_path)
226
+ if re.fullmatch(r'[-+]?\d[\d,.]*(?:%|원|억|만)?', cell.get_text(strip=True)):
227
+ para.alignment = WD_ALIGN_PARAGRAPH.RIGHT
228
+
229
+ # Bold for header cells
230
+ if cell.name == 'th':
231
+ for run in para.runs:
232
+ run.bold = True
233
+
234
+
235
+ def process_code_block(doc: Document, element):
236
+ """Process code block with monospace formatting."""
237
+ code = element.find('code')
238
+ text = code.get_text() if code else element.get_text()
239
+
240
+ para = doc.add_paragraph()
241
+ run = para.add_run(text)
242
+ run.font.name = 'Courier New'
243
+ run.font.size = Pt(9)
244
+
245
+ # Light gray background (via shading)
246
+ shading = OxmlElement('w:shd')
247
+ shading.set(qn('w:fill'), 'F0F0F0')
248
+ para._p.get_or_add_pPr().append(shading)
249
+
250
+
251
+ def html_to_docx(html: str, doc: Document, md_path: Path):
252
+ """Convert HTML to DOCX elements."""
253
+ soup = BeautifulSoup(html, 'html.parser')
254
+
255
+ for element in soup.children:
256
+ if isinstance(element, NavigableString):
257
+ text = str(element).strip()
258
+ if text:
259
+ doc.add_paragraph(text)
260
+ continue
261
+
262
+ if element.name in ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'):
263
+ level = int(element.name[1])
264
+ heading = doc.add_heading(level=level)
265
+ for child in element.children:
266
+ process_inline_elements(heading, child, md_path)
267
+
268
+ elif element.name == 'p':
269
+ add_paragraph_with_formatting(doc, element, md_path)
270
+
271
+ elif element.name == 'ul':
272
+ process_list(doc, element, md_path, ordered=False)
273
+
274
+ elif element.name == 'ol':
275
+ process_list(doc, element, md_path, ordered=True)
276
+
277
+ elif element.name == 'table':
278
+ process_table(doc, element, md_path)
279
+
280
+ elif element.name == 'pre':
281
+ process_code_block(doc, element)
282
+
283
+ elif element.name == 'blockquote':
284
+ para = doc.add_paragraph(style='Quote')
285
+ for child in element.children:
286
+ if child.name == 'p':
287
+ for subchild in child.children:
288
+ process_inline_elements(para, subchild, md_path)
289
+ else:
290
+ process_inline_elements(para, child, md_path)
291
+
292
+ elif element.name == 'hr':
293
+ # Add a horizontal line
294
+ para = doc.add_paragraph()
295
+ para.add_run('─' * 50)
296
+ para.alignment = WD_ALIGN_PARAGRAPH.CENTER
297
+
298
+ elif element.name in ('div', 'section', 'article'):
299
+ # Recursively process container elements
300
+ html_to_docx(str(element), doc, md_path)
301
+
302
+
303
+ # ===========================================================================
304
+ # M1: Journal workflow — frontmatter, filters, design enforcement
305
+ # ===========================================================================
306
+
307
+
308
+ def _load_yaml():
309
+ try:
310
+ import yaml
311
+ return yaml
312
+ except ImportError:
313
+ print("Error: 'pyyaml' library required for --publisher. pip install pyyaml")
314
+ sys.exit(1)
315
+
316
+
317
+ def parse_frontmatter(md_text: str) -> tuple[dict, str]:
318
+ """Extract leading YAML frontmatter. Returns (frontmatter_dict, body)."""
319
+ if not md_text.startswith("---"):
320
+ return {}, md_text
321
+ # Find closing fence
322
+ lines = md_text.splitlines(keepends=True)
323
+ if len(lines) < 2:
324
+ return {}, md_text
325
+ end_idx = None
326
+ for i in range(1, len(lines)):
327
+ if lines[i].rstrip() == "---":
328
+ end_idx = i
329
+ break
330
+ if end_idx is None:
331
+ return {}, md_text
332
+ yaml_src = "".join(lines[1:end_idx])
333
+ body = "".join(lines[end_idx + 1:])
334
+ yaml = _load_yaml()
335
+ try:
336
+ data = yaml.safe_load(yaml_src) or {}
337
+ except Exception as exc:
338
+ print(f"Warning: could not parse YAML frontmatter: {exc}", file=sys.stderr)
339
+ return {}, md_text
340
+ if not isinstance(data, dict):
341
+ return {}, md_text
342
+ return data, body
343
+
344
+
345
+ def load_publisher(registry_path: Path, name: str) -> dict:
346
+ yaml = _load_yaml()
347
+ if not registry_path.exists():
348
+ raise SystemExit(f"Error: registry not found: {registry_path}")
349
+ with registry_path.open("r", encoding="utf-8") as fh:
350
+ data = yaml.safe_load(fh) or {}
351
+ pubs = data.get("publishers") or {}
352
+ if name not in pubs:
353
+ raise SystemExit(
354
+ f"Error: publisher {name!r} not in registry "
355
+ f"(known: {', '.join(sorted(pubs)) or 'none'})"
356
+ )
357
+ return pubs[name]
358
+
359
+
360
+ # ---------------------------------------------------------------------------
361
+ # Code-preserving filter helpers
362
+ # ---------------------------------------------------------------------------
363
+
364
+
365
+ _FENCE_RE = re.compile(r"(^```.*?^```)", re.MULTILINE | re.DOTALL)
366
+ _INLINE_CODE_RE = re.compile(r"(`[^`\n]+`)")
367
+
368
+
369
+ def _split_code_preserving(text: str) -> list[tuple[str, bool]]:
370
+ """Split text into (segment, is_code) tuples.
371
+
372
+ Fenced code blocks and inline backtick code are returned as is_code=True
373
+ and must never be touched by the micro-typography filters.
374
+ """
375
+ # First split on fenced code blocks
376
+ out: list[tuple[str, bool]] = []
377
+ pos = 0
378
+ for m in _FENCE_RE.finditer(text):
379
+ if m.start() > pos:
380
+ out.append((text[pos:m.start()], False))
381
+ out.append((m.group(0), True))
382
+ pos = m.end()
383
+ if pos < len(text):
384
+ out.append((text[pos:], False))
385
+ # Then split non-code segments on inline backtick code
386
+ refined: list[tuple[str, bool]] = []
387
+ for seg, is_code in out:
388
+ if is_code:
389
+ refined.append((seg, True))
390
+ continue
391
+ last = 0
392
+ for m in _INLINE_CODE_RE.finditer(seg):
393
+ if m.start() > last:
394
+ refined.append((seg[last:m.start()], False))
395
+ refined.append((m.group(0), True))
396
+ last = m.end()
397
+ if last < len(seg):
398
+ refined.append((seg[last:], False))
399
+ return refined
400
+
401
+
402
+ def _apply_to_noncode(text: str, fn) -> str:
403
+ parts = _split_code_preserving(text)
404
+ out = []
405
+ for seg, is_code in parts:
406
+ out.append(seg if is_code else fn(seg))
407
+ return "".join(out)
408
+
409
+
410
+ # ---------------------------------------------------------------------------
411
+ # Pre-MD micro-typography filters
412
+ # ---------------------------------------------------------------------------
413
+
414
+
415
+ _NUMRANGE_RE = re.compile(r"(\d)-(\d)")
416
+ _PAREN_DASH_RE = re.compile(r"(\w) - (\w)")
417
+
418
+
419
+ def fix_dashes(text: str, rules: dict | None = None) -> str:
420
+ """Numeric ranges → en-dash; parenthetical 'word - word' → em-dash.
421
+
422
+ rules is the design.micro_typography.dashes block (currently informational;
423
+ M1 hardcodes en-dash for numeric range and em-dash for parenthetical).
424
+ """
425
+ def _fn(seg: str) -> str:
426
+ seg = _NUMRANGE_RE.sub(lambda m: f"{m.group(1)}\u2013{m.group(2)}", seg)
427
+ seg = _PAREN_DASH_RE.sub(lambda m: f"{m.group(1)} \u2014 {m.group(2)}", seg)
428
+ return seg
429
+ return _apply_to_noncode(text, _fn)
430
+
431
+
432
+ def fix_quotes(text: str) -> str:
433
+ """ASCII "..." → "..."; '...' → '...' via left/right state machine."""
434
+ def _fn(seg: str) -> str:
435
+ out_chars = []
436
+ open_dq = True
437
+ open_sq = True
438
+ for ch in seg:
439
+ if ch == '"':
440
+ out_chars.append("\u201C" if open_dq else "\u201D")
441
+ open_dq = not open_dq
442
+ elif ch == "'":
443
+ # Keep apostrophe if surrounded by letters (e.g. "don't")
444
+ prev = out_chars[-1] if out_chars else ""
445
+ if prev.isalpha():
446
+ out_chars.append("\u2019")
447
+ else:
448
+ out_chars.append("\u2018" if open_sq else "\u2019")
449
+ open_sq = not open_sq
450
+ else:
451
+ out_chars.append(ch)
452
+ return "".join(out_chars)
453
+ return _apply_to_noncode(text, _fn)
454
+
455
+
456
+ _UNIT_GROUP = (
457
+ r"mg|g|kg|mL|L|\u03BCL|uL|nm|\u03BCm|um|mm|cm|m|km|K|\u00B0C|%|min|h|s|ms"
458
+ )
459
+ _UNIT_RE = re.compile(rf"(\d)(\s+)({_UNIT_GROUP})(?=\b|\W|$)")
460
+ _FIG_RE = re.compile(r"(Fig\.)\s(\d)")
461
+ _TABLE_RE = re.compile(r"(Table)\s(\d)")
462
+ _EQ_RE = re.compile(r"(Eq\.)\s(\d)")
463
+
464
+
465
+ def fix_unit_spacing(text: str) -> str:
466
+ """Insert U+00A0 between number and SI unit, and after Fig./Table/Eq."""
467
+ def _fn(seg: str) -> str:
468
+ seg = _UNIT_RE.sub(lambda m: f"{m.group(1)}\u00A0{m.group(3)}", seg)
469
+ seg = _FIG_RE.sub(lambda m: f"{m.group(1)}\u00A0{m.group(2)}", seg)
470
+ seg = _TABLE_RE.sub(lambda m: f"{m.group(1)}\u00A0{m.group(2)}", seg)
471
+ seg = _EQ_RE.sub(lambda m: f"{m.group(1)}\u00A0{m.group(2)}", seg)
472
+ return seg
473
+ return _apply_to_noncode(text, _fn)
474
+
475
+
476
+ def fix_ellipsis(text: str) -> str:
477
+ def _fn(seg: str) -> str:
478
+ return seg.replace("...", "\u2026")
479
+ return _apply_to_noncode(text, _fn)
480
+
481
+
482
+ def normalize_double_spaces(text: str) -> str:
483
+ def _fn(seg: str) -> str:
484
+ # Preserve leading indentation; collapse interior double spaces
485
+ lines = seg.split("\n")
486
+ cleaned = []
487
+ for line in lines:
488
+ stripped_left = line.lstrip(" ")
489
+ lead = line[: len(line) - len(stripped_left)]
490
+ cleaned.append(lead + re.sub(r" {2,}", " ", stripped_left))
491
+ return "\n".join(cleaned)
492
+ return _apply_to_noncode(text, _fn)
493
+
494
+
495
+ # ---------------------------------------------------------------------------
496
+ # Post-HTML (doc object) filters
497
+ # ---------------------------------------------------------------------------
498
+
499
+
500
+ def apply_heading_numbering(doc, design: dict) -> None:
501
+ def to_roman(value: int) -> str:
502
+ numerals = [
503
+ (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
504
+ (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
505
+ (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"),
506
+ ]
507
+ out: list[str] = []
508
+ remaining = value
509
+ for arabic, roman in numerals:
510
+ while remaining >= arabic:
511
+ out.append(roman)
512
+ remaining -= arabic
513
+ return "".join(out)
514
+
515
+ def prefix_for(level: int, counters: list[int]) -> str | None:
516
+ key = f"h{level}"
517
+ cfg = (design.get("heading_hierarchy") or {}).get(key) or {}
518
+ numbering = cfg.get("numbering")
519
+ if numbering is None:
520
+ return None
521
+ numbering = str(numbering).strip()
522
+ if level == 1 and numbering.upper() == "I.":
523
+ return f"{to_roman(counters[0])}. "
524
+ if level == 1:
525
+ return f"{counters[0]}. "
526
+ if level == 2:
527
+ return f"{counters[0]}.{counters[1]}. "
528
+ if level == 3:
529
+ return f"{counters[0]}.{counters[1]}.{counters[2]}. "
530
+ return None
531
+
532
+ counters = [0, 0, 0]
533
+ for para in doc.paragraphs:
534
+ style_name = para.style.name if para.style else ""
535
+ level = 0
536
+ if style_name == "Heading 1":
537
+ level = 1
538
+ counters[0] += 1
539
+ counters[1] = 0
540
+ counters[2] = 0
541
+ elif style_name == "Heading 2":
542
+ level = 2
543
+ counters[1] += 1
544
+ counters[2] = 0
545
+ elif style_name == "Heading 3":
546
+ level = 3
547
+ counters[2] += 1
548
+ else:
549
+ continue
550
+ prefix = prefix_for(level, counters)
551
+ if prefix is None:
552
+ continue
553
+ current = para.text or ""
554
+ # Skip if already numbered (e.g. "1 Introduction" from the source)
555
+ if re.match(r"^(?:\d+(?:\.\d+)*\.?|[IVXLCDM]+\.?)\s", current):
556
+ continue
557
+ if para.runs:
558
+ para.runs[0].text = prefix + para.runs[0].text
559
+ else:
560
+ para.add_run(prefix)
561
+
562
+
563
+ def _set_cell_borders_nil(tcPr):
564
+ tcBorders = tcPr.find(qn("w:tcBorders"))
565
+ if tcBorders is None:
566
+ tcBorders = OxmlElement("w:tcBorders")
567
+ tcPr.append(tcBorders)
568
+ for side in ("top", "left", "bottom", "right", "insideH", "insideV"):
569
+ el = tcBorders.find(qn(f"w:{side}"))
570
+ if el is None:
571
+ el = OxmlElement(f"w:{side}")
572
+ tcBorders.append(el)
573
+ el.set(qn("w:val"), "nil")
574
+ el.set(qn("w:sz"), "0")
575
+
576
+
577
+ def apply_table_style(doc, design: dict) -> None:
578
+ """Override python-docx 'Table Grid' with booktabs: only horizontal rules.
579
+
580
+ Rules: top rule (table top), header-bottom rule, bottom rule (table bottom).
581
+ All vertical rules (left/right/insideV) set to nil.
582
+ """
583
+ for table in doc.tables:
584
+ table.style = None # detach Table Grid
585
+ tbl = table._tbl
586
+ tblPr = tbl.find(qn("w:tblPr"))
587
+ if tblPr is None:
588
+ tblPr = OxmlElement("w:tblPr")
589
+ tbl.insert(0, tblPr)
590
+ # Remove any existing tblBorders and add our booktabs set
591
+ existing = tblPr.find(qn("w:tblBorders"))
592
+ if existing is not None:
593
+ tblPr.remove(existing)
594
+ tblBorders = OxmlElement("w:tblBorders")
595
+
596
+ def _add_border(name: str, val: str, sz: str):
597
+ el = OxmlElement(f"w:{name}")
598
+ el.set(qn("w:val"), val)
599
+ el.set(qn("w:sz"), sz)
600
+ el.set(qn("w:space"), "0")
601
+ el.set(qn("w:color"), "000000")
602
+ tblBorders.append(el)
603
+
604
+ _add_border("top", "single", "12") # ~1.5pt
605
+ _add_border("bottom", "single", "12") # ~1.5pt
606
+ _add_border("left", "nil", "0")
607
+ _add_border("right", "nil", "0")
608
+ _add_border("insideH", "single", "6") # ~0.75pt (header-bottom)
609
+ _add_border("insideV", "nil", "0") # explicit no verticals
610
+ tblPr.append(tblBorders)
611
+
612
+ # Bold header row + add bottom border only to header cells so the
613
+ # thicker rule sits under the header (and horizontal rules between
614
+ # body rows are suppressed)
615
+ if table.rows:
616
+ header_row = table.rows[0]
617
+ for cell in header_row.cells:
618
+ for p in cell.paragraphs:
619
+ for r in p.runs:
620
+ r.bold = True
621
+ # Remove insideH to prevent inner body rules; header-bottom comes
622
+ # from a per-cell bottom border on the first row.
623
+ insideH = tblBorders.find(qn("w:insideH"))
624
+ if insideH is not None:
625
+ insideH.set(qn("w:val"), "nil")
626
+ for cell in header_row.cells:
627
+ tcPr = cell._tc.get_or_add_tcPr()
628
+ tcBorders = tcPr.find(qn("w:tcBorders"))
629
+ if tcBorders is None:
630
+ tcBorders = OxmlElement("w:tcBorders")
631
+ tcPr.append(tcBorders)
632
+ existing_bottom = tcBorders.find(qn("w:bottom"))
633
+ if existing_bottom is None:
634
+ existing_bottom = OxmlElement("w:bottom")
635
+ tcBorders.append(existing_bottom)
636
+ existing_bottom.set(qn("w:val"), "single")
637
+ existing_bottom.set(qn("w:sz"), "6")
638
+ existing_bottom.set(qn("w:space"), "0")
639
+ existing_bottom.set(qn("w:color"), "000000")
640
+
641
+
642
+ def strip_banned(doc, design: dict) -> None:
643
+ """Remove any banned font/style from runs and paragraphs."""
644
+ banned = design.get("banned") or {}
645
+ banned_fonts = set(banned.get("fonts") or [])
646
+ banned_styles = set(banned.get("styles") or [])
647
+ body_font = None
648
+ # body font is enforced on Normal style already, but we override raw runs too
649
+ for para in doc.paragraphs:
650
+ if para.style and para.style.name in banned_styles:
651
+ try:
652
+ para.style = doc.styles["Normal"]
653
+ except KeyError:
654
+ pass
655
+ for run in para.runs:
656
+ if run.font.name and run.font.name in banned_fonts:
657
+ run.font.name = None # inherit from style
658
+ # Also walk table cells
659
+ for table in doc.tables:
660
+ for row in table.rows:
661
+ for cell in row.cells:
662
+ for para in cell.paragraphs:
663
+ if para.style and para.style.name in banned_styles:
664
+ try:
665
+ para.style = doc.styles["Normal"]
666
+ except KeyError:
667
+ pass
668
+ for run in para.runs:
669
+ if run.font.name and run.font.name in banned_fonts:
670
+ run.font.name = None
671
+
672
+
673
+ def _set_run_fonts(run, latin: str, cjk: str | None) -> None:
674
+ rpr = run._element.get_or_add_rPr()
675
+ rfonts = rpr.find(qn("w:rFonts"))
676
+ if rfonts is None:
677
+ rfonts = OxmlElement("w:rFonts")
678
+ rpr.append(rfonts)
679
+ rfonts.set(qn("w:ascii"), latin)
680
+ rfonts.set(qn("w:hAnsi"), latin)
681
+ rfonts.set(qn("w:cs"), latin)
682
+ if cjk:
683
+ rfonts.set(qn("w:eastAsia"), cjk)
684
+
685
+
686
+ def apply_cjk_font_pairing(doc, font_cfg: dict) -> None:
687
+ body = font_cfg.get("body") or {}
688
+ body_cjk = font_cfg.get("body_cjk") or {}
689
+ latin = body.get("family")
690
+ cjk = body_cjk.get("family")
691
+ if not latin:
692
+ return
693
+
694
+ def _walk(paragraphs):
695
+ for para in paragraphs:
696
+ for run in para.runs:
697
+ # Only fix runs that already have a font set to something
698
+ # other than the registry-banned default; leaving run.font.name
699
+ # as None lets the style do its job. We still force eastAsia.
700
+ current_name = run.font.name
701
+ if current_name and current_name != "Courier New":
702
+ _set_run_fonts(run, latin, cjk)
703
+ elif not current_name:
704
+ # set only eastAsia pair to avoid losing style inheritance
705
+ rpr = run._element.get_or_add_rPr()
706
+ rfonts = rpr.find(qn("w:rFonts"))
707
+ if rfonts is None:
708
+ rfonts = OxmlElement("w:rFonts")
709
+ rpr.append(rfonts)
710
+ if cjk:
711
+ rfonts.set(qn("w:eastAsia"), cjk)
712
+
713
+ _walk(doc.paragraphs)
714
+ for table in doc.tables:
715
+ for row in table.rows:
716
+ for cell in row.cells:
717
+ _walk(cell.paragraphs)
718
+
719
+
720
+ # ---------------------------------------------------------------------------
721
+ # Title block builder
722
+ # ---------------------------------------------------------------------------
723
+
724
+
725
+ def _insert_paragraph_before(doc, before_p, style=None):
726
+ """Insert a new paragraph before the given paragraph element."""
727
+ new_p = OxmlElement("w:p")
728
+ before_p._p.addprevious(new_p)
729
+ from docx.text.paragraph import Paragraph
730
+ para = Paragraph(new_p, before_p._parent)
731
+ if style is not None:
732
+ try:
733
+ para.style = doc.styles[style]
734
+ except KeyError:
735
+ pass
736
+ return para
737
+
738
+
739
+ def inject_title_block(doc, frontmatter: dict, design: dict) -> None:
740
+ """Prepend title/authors/affiliations/abstract/keywords to the doc.
741
+
742
+ Builds the block at the top of the document body via the underlying
743
+ XML so it appears before any html_to_docx output. Called BEFORE
744
+ html_to_docx in M1's control flow so paragraphs just append normally.
745
+ """
746
+ tb = design.get("title_block") or {}
747
+ title = frontmatter.get("title")
748
+ if not title:
749
+ return
750
+
751
+ def _p_center(size: int, bold=False, italic=False, space_after=4):
752
+ p = doc.add_paragraph()
753
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
754
+ p.paragraph_format.first_line_indent = Cm(0)
755
+ p.paragraph_format.space_after = Pt(space_after)
756
+ return p
757
+
758
+ def _add_run(p, text: str, size: int, bold=False, italic=False, sup=False):
759
+ run = p.add_run(text)
760
+ run.font.size = Pt(size)
761
+ run.bold = bold
762
+ run.italic = italic
763
+ run.font.superscript = sup
764
+ return run
765
+
766
+ # Title
767
+ tp = _p_center(
768
+ tb.get("title", {}).get("size_pt", 18),
769
+ bold=True,
770
+ space_after=tb.get("title", {}).get("space_after_pt", 12),
771
+ )
772
+ _add_run(tp, str(title), tb.get("title", {}).get("size_pt", 18), bold=True)
773
+
774
+ # Authors
775
+ authors = frontmatter.get("authors") or []
776
+ if authors:
777
+ ap = _p_center(
778
+ tb.get("authors", {}).get("size_pt", 12),
779
+ space_after=tb.get("authors", {}).get("space_after_pt", 4),
780
+ )
781
+ sep = tb.get("author_separator", ", ")
782
+ corr_mark = tb.get("corresponding_marker", "*")
783
+ size = tb.get("authors", {}).get("size_pt", 12)
784
+ for i, author in enumerate(authors):
785
+ if not isinstance(author, dict):
786
+ continue
787
+ if i > 0:
788
+ _add_run(ap, sep, size)
789
+ name = str(author.get("name", "Author"))
790
+ _add_run(ap, name, size)
791
+ aff = author.get("affiliation")
792
+ if aff is not None:
793
+ if isinstance(aff, list):
794
+ aff_text = ",".join(str(a) for a in aff)
795
+ else:
796
+ aff_text = str(aff)
797
+ _add_run(ap, aff_text, size, sup=True)
798
+ if author.get("corresponding"):
799
+ _add_run(ap, corr_mark, size, sup=True)
800
+
801
+ # Affiliations
802
+ affs = frontmatter.get("affiliations")
803
+ if isinstance(affs, dict):
804
+ for key in sorted(affs.keys(), key=lambda k: str(k)):
805
+ fp = _p_center(
806
+ tb.get("affiliations", {}).get("size_pt", 10),
807
+ italic=True,
808
+ space_after=2,
809
+ )
810
+ _add_run(
811
+ fp,
812
+ f"{key} {affs[key]}",
813
+ tb.get("affiliations", {}).get("size_pt", 10),
814
+ italic=True,
815
+ )
816
+
817
+ # Corresponding email (if present, small italic line)
818
+ corr_email = frontmatter.get("corresponding_email")
819
+ if corr_email:
820
+ cp = _p_center(10, italic=True, space_after=8)
821
+ _add_run(cp, f"* Corresponding author: {corr_email}", 10, italic=True)
822
+
823
+ # Abstract
824
+ abstract = frontmatter.get("abstract")
825
+ if abstract:
826
+ lp = doc.add_paragraph()
827
+ lp.paragraph_format.first_line_indent = Cm(0)
828
+ lp.paragraph_format.space_after = Pt(4)
829
+ _add_run(
830
+ lp,
831
+ tb.get("abstract_label", "Abstract"),
832
+ tb.get("abstract_font_size_pt", 10),
833
+ bold=True,
834
+ )
835
+ body = doc.add_paragraph()
836
+ body.paragraph_format.first_line_indent = Cm(0)
837
+ body.paragraph_format.space_after = Pt(6)
838
+ _add_run(
839
+ body,
840
+ str(abstract),
841
+ tb.get("abstract_font_size_pt", 10),
842
+ )
843
+
844
+ # Keywords
845
+ keywords = frontmatter.get("keywords")
846
+ if keywords:
847
+ kp = doc.add_paragraph()
848
+ kp.paragraph_format.first_line_indent = Cm(0)
849
+ kp.paragraph_format.space_after = Pt(12)
850
+ _add_run(
851
+ kp,
852
+ tb.get("keywords_label", "Keywords") + ": ",
853
+ tb.get("abstract_font_size_pt", 10),
854
+ bold=True,
855
+ )
856
+ if isinstance(keywords, list):
857
+ sep = tb.get("keywords_separator", "; ")
858
+ text = sep.join(str(k) for k in keywords)
859
+ else:
860
+ text = str(keywords)
861
+ _add_run(kp, text, tb.get("abstract_font_size_pt", 10))
862
+
863
+
864
+ # ---------------------------------------------------------------------------
865
+ # Locale detection
866
+ # ---------------------------------------------------------------------------
867
+
868
+
869
+ _HANGUL_RE = re.compile(r"[\uac00-\ud7a3]")
870
+
871
+
872
+ def detect_locale(text: str) -> str:
873
+ total = len(text) or 1
874
+ hangul_count = len(_HANGUL_RE.findall(text))
875
+ ratio = hangul_count / total
876
+ if ratio > 0.05:
877
+ return "ko"
878
+ if ratio > 0.01:
879
+ return "mixed"
880
+ return "en"
881
+
882
+
883
+ # ===========================================================================
884
+ # Conversion entry point
885
+ # ===========================================================================
886
+
887
+
888
+ def convert_md_to_docx(
889
+ md_path: Path,
890
+ docx_path: Path,
891
+ template_path: Path = None,
892
+ publisher_name: str | None = None,
893
+ registry_path: Path | None = None,
894
+ locale: str = "auto",
895
+ ):
896
+ """Convert Markdown file to DOCX.
897
+
898
+ When publisher_name is None, behavior is byte-compatible with the pre-M1
899
+ script: read md, convert to HTML, html_to_docx, save. Regression-guarded.
900
+
901
+ When publisher_name is set, apply the full journal pipeline: registry
902
+ load, frontmatter parse, pre-MD filters, title-block injection, post-HTML
903
+ heading numbering, booktabs tables, banned stripping, CJK font pairing.
904
+ """
905
+ if publisher_name is None:
906
+ # --- Legacy code path (regression-critical: do not mutate) ---
907
+ md_text = md_path.read_text(encoding='utf-8')
908
+ md_converter = markdown.Markdown(extensions=[
909
+ 'tables',
910
+ 'fenced_code',
911
+ 'nl2br',
912
+ 'sane_lists',
913
+ ])
914
+ html = md_converter.convert(md_text)
915
+ if template_path and template_path.exists():
916
+ doc = Document(str(template_path))
917
+ else:
918
+ doc = Document()
919
+ html_to_docx(html, doc, md_path)
920
+ doc.save(str(docx_path))
921
+ print(f"Successfully converted {md_path} to {docx_path}")
922
+ return
923
+
924
+ # --- Journal workflow ---
925
+ script_dir = Path(__file__).resolve().parent
926
+ registry_path = registry_path or (script_dir.parent / "templates" / "registry.yaml")
927
+ publisher = load_publisher(Path(registry_path), publisher_name)
928
+ design = publisher["design"]
929
+ docx_font = publisher["docx"]["font"]
930
+
931
+ md_text = md_path.read_text(encoding="utf-8")
932
+ frontmatter, body = parse_frontmatter(md_text)
933
+
934
+ # Locale detection (informational in M1; Korean rules land in M6)
935
+ detected_locale = detect_locale(body) if locale == "auto" else locale
936
+
937
+ # Pre-MD micro-typography filters
938
+ dash_rules = design.get("micro_typography", {}).get("dashes")
939
+ body = fix_dashes(body, dash_rules)
940
+ body = fix_quotes(body)
941
+ body = fix_unit_spacing(body)
942
+ body = fix_ellipsis(body)
943
+ body = normalize_double_spaces(body)
944
+
945
+ # Load template if available
946
+ template_docx = script_dir.parent / "templates" / "docx" / f"{publisher_name}.docx"
947
+ if template_docx.exists():
948
+ doc = Document(str(template_docx))
949
+ # The template includes a self-describing sample title block and
950
+ # sample heading/body paragraph. Strip all existing body paragraphs
951
+ # and tables so the conversion output is the user's content only.
952
+ body_el = doc.element.body
953
+ sect_pr = body_el.find(qn("w:sectPr"))
954
+ for child in list(body_el):
955
+ if child is sect_pr:
956
+ continue
957
+ body_el.remove(child)
958
+ if sect_pr is None:
959
+ pass # keep original body even if no sectPr detected
960
+ elif template_path and template_path.exists():
961
+ doc = Document(str(template_path))
962
+ else:
963
+ doc = Document()
964
+
965
+ # Title block (built from frontmatter) before main content
966
+ if frontmatter.get("title"):
967
+ inject_title_block(doc, frontmatter, design)
968
+
969
+ # Main content
970
+ md_converter = markdown.Markdown(extensions=[
971
+ 'tables',
972
+ 'fenced_code',
973
+ 'nl2br',
974
+ 'sane_lists',
975
+ ])
976
+ html = md_converter.convert(body)
977
+ html_to_docx(html, doc, md_path)
978
+
979
+ # Post-HTML design enforcement
980
+ apply_heading_numbering(doc, design)
981
+ apply_table_style(doc, design)
982
+ strip_banned(doc, design)
983
+ apply_cjk_font_pairing(doc, docx_font)
984
+
985
+ doc.save(str(docx_path))
986
+ print(
987
+ f"Successfully converted {md_path} to {docx_path} "
988
+ f"[publisher={publisher_name}, locale={detected_locale}]"
989
+ )
990
+
991
+
992
+ def parse_args() -> argparse.Namespace:
993
+ parser = argparse.ArgumentParser(
994
+ description="Convert Markdown to DOCX with proper styling"
995
+ )
996
+ parser.add_argument("input", help="Input Markdown file")
997
+ parser.add_argument("output", help="Output DOCX file")
998
+ parser.add_argument(
999
+ "--template",
1000
+ help="Optional DOCX template to use as base",
1001
+ default=None,
1002
+ )
1003
+ parser.add_argument(
1004
+ "--publisher",
1005
+ help="Journal publisher profile from templates/registry.yaml (e.g. elsevier)",
1006
+ default=None,
1007
+ )
1008
+ parser.add_argument(
1009
+ "--registry",
1010
+ help="Path to templates/registry.yaml (defaults to the one in this skill)",
1011
+ default=None,
1012
+ )
1013
+ parser.add_argument(
1014
+ "--locale",
1015
+ choices=("auto", "en", "ko", "mixed"),
1016
+ default="auto",
1017
+ help="Force locale; 'auto' detects via Hangul codepoint ratio",
1018
+ )
1019
+ args = parser.parse_args()
1020
+ if args.publisher and args.template:
1021
+ parser.error("--publisher and --template are mutually exclusive")
1022
+ return args
1023
+
1024
+
1025
+ def main() -> int:
1026
+ args = parse_args()
1027
+
1028
+ md_path = Path(args.input).expanduser().resolve()
1029
+ docx_path = Path(args.output).expanduser().resolve()
1030
+ template_path = Path(args.template).expanduser().resolve() if args.template else None
1031
+ registry_path = Path(args.registry).expanduser().resolve() if args.registry else None
1032
+
1033
+ if not md_path.exists():
1034
+ print(f"Error: Input file not found: {md_path}")
1035
+ return 1
1036
+
1037
+ convert_md_to_docx(
1038
+ md_path, docx_path, template_path,
1039
+ publisher_name=args.publisher,
1040
+ registry_path=registry_path,
1041
+ locale=args.locale,
1042
+ )
1043
+ return 0
1044
+
1045
+
1046
+ if __name__ == "__main__":
1047
+ raise SystemExit(main())