kordoc 4.22.16 → 4.22.18

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 (126) hide show
  1. package/README.md +3 -1
  2. package/dist/{-3XSSQOP6.js → -OTAPIBUW.js} +25 -11
  3. package/dist/{chunk-HXV4V6IH.js → chunk-3AVHW7UT.js} +2 -2
  4. package/dist/{chunk-WMJPOOBJ.js → chunk-3GVPNEIG.js} +2 -2
  5. package/dist/{chunk-NDO4XZMT.js → chunk-6LTGCEGP.js} +2 -2
  6. package/dist/{chunk-RMNJTPSR.js → chunk-7QLKNWMN.js} +4 -4
  7. package/dist/{chunk-UDG4L37H.cjs → chunk-B3Z2PY4R.cjs} +2 -2
  8. package/dist/{chunk-UDG4L37H.cjs.map → chunk-B3Z2PY4R.cjs.map} +1 -1
  9. package/dist/{chunk-FFQLQLRE.cjs → chunk-BXZW4Q5G.cjs} +6 -6
  10. package/dist/{chunk-FFQLQLRE.cjs.map → chunk-BXZW4Q5G.cjs.map} +1 -1
  11. package/dist/{chunk-IUVFOBQM.cjs → chunk-DKZZVFTK.cjs} +3 -3
  12. package/dist/{chunk-IUVFOBQM.cjs.map → chunk-DKZZVFTK.cjs.map} +1 -1
  13. package/dist/{chunk-L5HFJV5O.js → chunk-DQBY4PQ4.js} +17 -4
  14. package/dist/chunk-DQBY4PQ4.js.map +1 -0
  15. package/dist/{chunk-TI534EYY.js → chunk-G4PPT5CR.js} +8 -6
  16. package/dist/{chunk-TI534EYY.js.map → chunk-G4PPT5CR.js.map} +1 -1
  17. package/dist/{chunk-2ZGVOGHM.js → chunk-HX24KLQN.js} +2 -2
  18. package/dist/{chunk-SDIKLPMY.js → chunk-JO5OR4F5.js} +2 -2
  19. package/dist/{chunk-Z7URWVQH.js → chunk-M7SCUHXE.js} +4 -4
  20. package/dist/{chunk-VTVPHZJ6.js → chunk-MI2YNEQJ.js} +494 -15
  21. package/dist/chunk-MI2YNEQJ.js.map +1 -0
  22. package/dist/{chunk-VVOURQFY.js → chunk-MKEBORZM.js} +2 -2
  23. package/dist/{chunk-VVOURQFY.js.map → chunk-MKEBORZM.js.map} +1 -1
  24. package/dist/{chunk-3BQYVNOW.js → chunk-MW42GCQ4.js} +2 -2
  25. package/dist/{chunk-JTRP3OO2.js → chunk-N5QRS6VT.js} +2 -2
  26. package/dist/{chunk-JZCBDX34.cjs → chunk-NUQG356J.cjs} +3 -3
  27. package/dist/{chunk-JZCBDX34.cjs.map → chunk-NUQG356J.cjs.map} +1 -1
  28. package/dist/{chunk-W5SRR7W6.js → chunk-NV3WEN4C.js} +3 -3
  29. package/dist/{chunk-VJ6UNTC7.js → chunk-OWO3RVJY.js} +2 -2
  30. package/dist/{chunk-75YWX55A.js → chunk-RTFUKVOX.js} +3 -3
  31. package/dist/{chunk-2WAKSO2Z.js → chunk-S2WH5IZ3.js} +2 -2
  32. package/dist/{chunk-GDTSPYWP.js → chunk-SJYHFKKB.js} +2 -2
  33. package/dist/{chunk-3NYFGUOJ.cjs → chunk-TLGOPNMC.cjs} +24 -24
  34. package/dist/{chunk-3NYFGUOJ.cjs.map → chunk-TLGOPNMC.cjs.map} +1 -1
  35. package/dist/{chunk-RVZVCHUE.js → chunk-TLU4OKIO.js} +2 -2
  36. package/dist/{chunk-XD4B7B55.js → chunk-VCGGP7SY.js} +2 -2
  37. package/dist/{chunk-XD4B7B55.js.map → chunk-VCGGP7SY.js.map} +1 -1
  38. package/dist/{chunk-5EISWK2J.js → chunk-VROCP4X4.js} +2 -2
  39. package/dist/{chunk-C3XICONM.js → chunk-WITGYMV5.js} +4 -4
  40. package/dist/{chunk-JL3IR45L.cjs → chunk-XIDMSNU2.cjs} +23 -23
  41. package/dist/{chunk-JL3IR45L.cjs.map → chunk-XIDMSNU2.cjs.map} +1 -1
  42. package/dist/{chunk-RWLGFHZO.js → chunk-XOX5L7WN.js} +2 -2
  43. package/dist/chunks-GMADIKNS.js +10 -0
  44. package/dist/cli.js +96 -39
  45. package/dist/cli.js.map +1 -1
  46. package/dist/{formula-XSJKSTPU.cjs → formula-2ISHDHXF.cjs} +9 -9
  47. package/dist/{formula-XSJKSTPU.cjs.map → formula-2ISHDHXF.cjs.map} +1 -1
  48. package/dist/{formula-YWCFQN7M.js → formula-E5G3A3NZ.js} +3 -3
  49. package/dist/{formula-JMFME265.js → formula-WHYRNHRE.js} +4 -4
  50. package/dist/{image-ocr-W52R3U55.js → image-ocr-5WJQITNQ.js} +6 -6
  51. package/dist/{image-ocr-HH4WLRMA.js → image-ocr-6J7B5UVA.js} +9 -9
  52. package/dist/{image-ocr-2OWPVTWD.cjs → image-ocr-MRTNIUZQ.cjs} +17 -17
  53. package/dist/{image-ocr-2OWPVTWD.cjs.map → image-ocr-MRTNIUZQ.cjs.map} +1 -1
  54. package/dist/index.cjs +1301 -809
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.d.cts +92 -1
  57. package/dist/index.d.ts +92 -1
  58. package/dist/index.js +520 -28
  59. package/dist/index.js.map +1 -1
  60. package/dist/mcp.js +47 -28
  61. package/dist/mcp.js.map +1 -1
  62. package/dist/{model-bundle-LAMGDBR5.js → model-bundle-CNNDBCIQ.js} +5 -5
  63. package/dist/{models-UW6K53RI.js → models-PQYMCKFR.js} +5 -5
  64. package/dist/{parse-worker-v2-TCYTBP2P.js → parse-worker-v2-Z3W53OUZ.js} +12 -12
  65. package/dist/{parser-JXAIWIOE.cjs → parser-2YUGVORR.cjs} +157 -157
  66. package/dist/{parser-JXAIWIOE.cjs.map → parser-2YUGVORR.cjs.map} +1 -1
  67. package/dist/{parser-J37C64DT.js → parser-UYBCCGY4.js} +12 -12
  68. package/dist/{parser-Y2OKEGM4.js → parser-YHUWM4KF.js} +9 -9
  69. package/dist/{pdf-ocr-NUQHXJJR.js → pdf-ocr-6JSBUWUO.js} +9 -9
  70. package/dist/pdf-ocr-6Q34QQ7A.cjs +31 -0
  71. package/dist/{pdf-ocr-HPC5BVB7.cjs.map → pdf-ocr-6Q34QQ7A.cjs.map} +1 -1
  72. package/dist/{pdf-ocr-BDNBONES.js → pdf-ocr-DE7PYWN7.js} +6 -6
  73. package/dist/{profile-io-SET4RRP2.js → profile-io-DCZ4DINU.js} +3 -3
  74. package/dist/{rasterize-O7ZM2CRL.js → rasterize-6XD2LLEN.js} +3 -3
  75. package/dist/{redact-doc-Z3ISHGTJ.js → redact-doc-2BLB3IOP.js} +11 -11
  76. package/dist/{render-MLKOC2PN.js → render-YPZMJSZL.js} +7 -7
  77. package/dist/seal-32KIG7OS.js +10 -0
  78. package/dist/{setup-CUB6RCSD.js → setup-GUE2OUWB.js} +3 -3
  79. package/dist/visual-Y32AKOM4.js +21 -0
  80. package/dist/{watch-YPDHDT4N.js → watch-EILA6XGX.js} +12 -12
  81. package/package.json +2 -2
  82. package/dist/chunk-L5HFJV5O.js.map +0 -1
  83. package/dist/chunk-VTVPHZJ6.js.map +0 -1
  84. package/dist/chunks-XXPD5OV5.js +0 -10
  85. package/dist/pdf-ocr-HPC5BVB7.cjs +0 -31
  86. package/dist/seal-7QUWLHB6.js +0 -10
  87. package/dist/visual-QLWVIUQS.js +0 -21
  88. /package/dist/{-3XSSQOP6.js.map → -OTAPIBUW.js.map} +0 -0
  89. /package/dist/{chunk-HXV4V6IH.js.map → chunk-3AVHW7UT.js.map} +0 -0
  90. /package/dist/{chunk-WMJPOOBJ.js.map → chunk-3GVPNEIG.js.map} +0 -0
  91. /package/dist/{chunk-NDO4XZMT.js.map → chunk-6LTGCEGP.js.map} +0 -0
  92. /package/dist/{chunk-RMNJTPSR.js.map → chunk-7QLKNWMN.js.map} +0 -0
  93. /package/dist/{chunk-2ZGVOGHM.js.map → chunk-HX24KLQN.js.map} +0 -0
  94. /package/dist/{chunk-SDIKLPMY.js.map → chunk-JO5OR4F5.js.map} +0 -0
  95. /package/dist/{chunk-Z7URWVQH.js.map → chunk-M7SCUHXE.js.map} +0 -0
  96. /package/dist/{chunk-3BQYVNOW.js.map → chunk-MW42GCQ4.js.map} +0 -0
  97. /package/dist/{chunk-JTRP3OO2.js.map → chunk-N5QRS6VT.js.map} +0 -0
  98. /package/dist/{chunk-W5SRR7W6.js.map → chunk-NV3WEN4C.js.map} +0 -0
  99. /package/dist/{chunk-VJ6UNTC7.js.map → chunk-OWO3RVJY.js.map} +0 -0
  100. /package/dist/{chunk-75YWX55A.js.map → chunk-RTFUKVOX.js.map} +0 -0
  101. /package/dist/{chunk-2WAKSO2Z.js.map → chunk-S2WH5IZ3.js.map} +0 -0
  102. /package/dist/{chunk-GDTSPYWP.js.map → chunk-SJYHFKKB.js.map} +0 -0
  103. /package/dist/{chunk-RVZVCHUE.js.map → chunk-TLU4OKIO.js.map} +0 -0
  104. /package/dist/{chunk-5EISWK2J.js.map → chunk-VROCP4X4.js.map} +0 -0
  105. /package/dist/{chunk-C3XICONM.js.map → chunk-WITGYMV5.js.map} +0 -0
  106. /package/dist/{chunk-RWLGFHZO.js.map → chunk-XOX5L7WN.js.map} +0 -0
  107. /package/dist/{chunks-XXPD5OV5.js.map → chunks-GMADIKNS.js.map} +0 -0
  108. /package/dist/{formula-YWCFQN7M.js.map → formula-E5G3A3NZ.js.map} +0 -0
  109. /package/dist/{formula-JMFME265.js.map → formula-WHYRNHRE.js.map} +0 -0
  110. /package/dist/{image-ocr-W52R3U55.js.map → image-ocr-5WJQITNQ.js.map} +0 -0
  111. /package/dist/{image-ocr-HH4WLRMA.js.map → image-ocr-6J7B5UVA.js.map} +0 -0
  112. /package/dist/{model-bundle-LAMGDBR5.js.map → model-bundle-CNNDBCIQ.js.map} +0 -0
  113. /package/dist/{models-UW6K53RI.js.map → models-PQYMCKFR.js.map} +0 -0
  114. /package/dist/{parse-worker-v2-TCYTBP2P.js.map → parse-worker-v2-Z3W53OUZ.js.map} +0 -0
  115. /package/dist/{parser-J37C64DT.js.map → parser-UYBCCGY4.js.map} +0 -0
  116. /package/dist/{parser-Y2OKEGM4.js.map → parser-YHUWM4KF.js.map} +0 -0
  117. /package/dist/{pdf-ocr-BDNBONES.js.map → pdf-ocr-6JSBUWUO.js.map} +0 -0
  118. /package/dist/{pdf-ocr-NUQHXJJR.js.map → pdf-ocr-DE7PYWN7.js.map} +0 -0
  119. /package/dist/{profile-io-SET4RRP2.js.map → profile-io-DCZ4DINU.js.map} +0 -0
  120. /package/dist/{rasterize-O7ZM2CRL.js.map → rasterize-6XD2LLEN.js.map} +0 -0
  121. /package/dist/{redact-doc-Z3ISHGTJ.js.map → redact-doc-2BLB3IOP.js.map} +0 -0
  122. /package/dist/{render-MLKOC2PN.js.map → render-YPZMJSZL.js.map} +0 -0
  123. /package/dist/{seal-7QUWLHB6.js.map → seal-32KIG7OS.js.map} +0 -0
  124. /package/dist/{setup-CUB6RCSD.js.map → setup-GUE2OUWB.js.map} +0 -0
  125. /package/dist/{visual-QLWVIUQS.js.map → visual-Y32AKOM4.js.map} +0 -0
  126. /package/dist/{watch-YPDHDT4N.js.map → watch-EILA6XGX.js.map} +0 -0
@@ -5,6 +5,7 @@ import {
5
5
  FLAG_DISTRIBUTION,
6
6
  FLAG_DRM,
7
7
  FLAG_ENCRYPTED,
8
+ FLAG_TRACK_CHANGES,
8
9
  INLINE_TABLE_MARK,
9
10
  MIDDLE_CONVERT_MAP,
10
11
  SPACE_EM_FIXED,
@@ -50,10 +51,10 @@ import {
50
51
  resolveParaHeading,
51
52
  simulateWrap,
52
53
  takeTocLeaders
53
- } from "./chunk-TI534EYY.js";
54
+ } from "./chunk-G4PPT5CR.js";
54
55
  import {
55
56
  blocksToPages
56
- } from "./chunk-RVZVCHUE.js";
57
+ } from "./chunk-TLU4OKIO.js";
57
58
  import {
58
59
  bmpToPng,
59
60
  inlineImagesIntoMarkdown
@@ -75,7 +76,7 @@ import {
75
76
  tidyScriptTags,
76
77
  unframeLayoutTables,
77
78
  wrapScript
78
- } from "./chunk-VVOURQFY.js";
79
+ } from "./chunk-MKEBORZM.js";
79
80
  import {
80
81
  MAX_COLS,
81
82
  MAX_ROWS,
@@ -97,7 +98,7 @@ import {
97
98
  noteTableHtml,
98
99
  sanitizeText,
99
100
  visibleText
100
- } from "./chunk-HXV4V6IH.js";
101
+ } from "./chunk-3AVHW7UT.js";
101
102
  import {
102
103
  ANNOTATION_BLANK_RE,
103
104
  ValueCursor,
@@ -130,7 +131,7 @@ import {
130
131
  resolveUnmatched,
131
132
  scanInlineSegments,
132
133
  scanSectionXml
133
- } from "./chunk-L5HFJV5O.js";
134
+ } from "./chunk-DQBY4PQ4.js";
134
135
  import {
135
136
  MAX_DECOMPRESS_SIZE,
136
137
  MAX_XML_DEPTH,
@@ -147,7 +148,7 @@ import {
147
148
  localName,
148
149
  rawTextContent,
149
150
  readZipEntry
150
- } from "./chunk-VJ6UNTC7.js";
151
+ } from "./chunk-OWO3RVJY.js";
151
152
  import {
152
153
  KordocError,
153
154
  classifyError,
@@ -163,7 +164,7 @@ import {
163
164
  toArrayBuffer,
164
165
  unescapeHtml,
165
166
  unzipLimitBytes
166
- } from "./chunk-2WAKSO2Z.js";
167
+ } from "./chunk-S2WH5IZ3.js";
167
168
 
168
169
  // src/table/visual.ts
169
170
  import { readFile as readFile2 } from "fs/promises";
@@ -19327,7 +19328,7 @@ var MIN_SIDE = 24;
19327
19328
  async function ocrEmbeddedImages(blocks, options, warnings) {
19328
19329
  const ocr = options?.ocr;
19329
19330
  if (ocr !== true && ocr !== "force" && typeof ocr !== "function") return false;
19330
- const { parseImageDocument } = await import("./image-ocr-HH4WLRMA.js");
19331
+ const { parseImageDocument } = await import("./image-ocr-6J7B5UVA.js");
19331
19332
  let inserted = false, unreadable = 0;
19332
19333
  let failed = null;
19333
19334
  const walk = async (list, depth) => {
@@ -27372,6 +27373,468 @@ function muncheLintWarnings(text, limit = 10) {
27372
27373
  return shown;
27373
27374
  }
27374
27375
 
27376
+ // src/hwpx/slop-lint.ts
27377
+ var RULES2 = [
27378
+ // 결산 피벗 — 개조식 공문은 장 이름(향후계획·조치계획)이 마무리를 맡는다. 사람 글 838건 중 0건
27379
+ {
27380
+ code: "SLOP_PIVOT",
27381
+ severity: "error",
27382
+ pattern: /결론적으로|요약하면|정리하면|요컨대|종합해 ?보면/g,
27383
+ message: "\uACB0\uC0B0 \uC811\uC18D\uC5B4 \u2014 \uACF5\uBB38\uC740 \uC7A5 \uC774\uB984\uC774 \uB9C8\uBB34\uB9AC\uB97C \uB9E1\uC74C(\uC0DD\uC131\uD615 AI \uBB38\uCCB4)",
27384
+ suggest: "\uC9C0\uC6B0\uACE0 \uD310\uB2E8\xB7\uC870\uCE58\uB97C \uBC14\uB85C \uC801\uC74C"
27385
+ },
27386
+ // 평론 상투구 — 사람 글 0건
27387
+ {
27388
+ code: "SLOP_SIGNATURE",
27389
+ severity: "error",
27390
+ pattern: /시사하는 바가|시사점이 크|주목할 만하|본질적으로|핵심적으로|게임 ?체인저|새로운 지평|새로운 장을 열|의미 있는 첫걸음/g,
27391
+ message: "\uD3C9\uB860\xB7\uD64D\uBCF4 \uC0C1\uD22C\uAD6C(\uC0DD\uC131\uD615 AI \uBB38\uCCB4)",
27392
+ suggest: "\uBB34\uC5C7\uC774 \uB2EC\uB77C\uC9C0\uB294\uC9C0 \uC218\uCE58\xB7\uC0AC\uC2E4\uB85C"
27393
+ },
27394
+ // 사람도 쓰지만 AI 초안에 몰리는 홍보 상투구
27395
+ {
27396
+ code: "SLOP_HYPE",
27397
+ severity: "warning",
27398
+ pattern: /패러다임(?:의)? 전환|한 단계 도약|지금이야말로|초석이 될|발판이 될|획기적|압도적|파격적|비약적|전례 ?없는|극대화/g,
27399
+ message: "\uACFC\uC7A5\xB7\uD64D\uBCF4 \uC0C1\uD22C\uAD6C",
27400
+ suggest: "\uBAA9\uD45C\uCE58\xB7\uB300\uC0C1\xB7\uAE30\uAC04\uC73C\uB85C \uBC14\uAFD4 \uC500"
27401
+ },
27402
+ // 시대 상투구 — 배경을 사실 대신 분위기로 여는 서두. 사람 글 문서 적중 1.5%(급변하는 26회가 대부분)
27403
+ {
27404
+ code: "SLOP_ERA_CLICHE",
27405
+ severity: "warning",
27406
+ pattern: /중요성이 (?:날로|점차|더욱|갈수록)|(?:관심|요구|필요성)이 (?:날로|점차|더욱|갈수록) (?:높아|커지|증대|증가)|급변하는|시대적 (?:요구|흐름|과제)|요구되는 시점|4차 산업혁명 시대|대전환 시대|뉴노멀/g,
27407
+ message: "\uC2DC\uB300\xB7\uBD84\uC704\uAE30\uB85C \uC5EC\uB294 \uC0C1\uD22C \uC11C\uB450 \u2014 \uBC30\uACBD\uC740 \uC0AC\uC2E4\uB85C \uC5F0\uB2E4",
27408
+ suggest: "\uC608) \u300C\u25CB\u25CB\uBC95\u300D \uAC1C\uC815(2026. 3. 1. \uC2DC\uD589), \uBBFC\uC6D0 412\uAC74(\uC804\uB144 \uB300\uBE44 38%\u2191)"
27409
+ },
27410
+ // 체감·만족도 상투구 — AI 초안 29%, 사람 글 2% 이하
27411
+ {
27412
+ code: "SLOP_FEEL_GOOD",
27413
+ severity: "warning",
27414
+ pattern: /체감(?:도|형)? ?(?:높은|향상|제고)|만족도 (?:향상|제고|증진)|삶의 질 (?:향상|제고)|행복 ?(?:지수 )?(?:증진|향상)/g,
27415
+ message: "\uCCB4\uAC10\xB7\uB9CC\uC871\uB3C4 \uC0C1\uD22C\uAD6C \u2014 \uBB34\uC5C7\uC774 \uC5BC\uB9C8\uB098 \uB098\uC544\uC9C0\uB294\uC9C0 \uC5C6\uC74C",
27416
+ suggest: "\uC608) \uB300\uAE30\uC2DC\uAC04 25\uBD84\u219210\uBD84, \uBBFC\uC6D0 30% \uAC10\uC18C"
27417
+ },
27418
+ // 번역투 — 보도자료에도 흔해 문서 안 3회 이상만
27419
+ {
27420
+ code: "SLOP_TRANSLATIONESE",
27421
+ severity: "warning",
27422
+ minCount: 3,
27423
+ pattern: /에 있어서?(?![가-힣])|함에 있어|되어지|되어진|[을를] 가지고 있|에 대해서/g,
27424
+ message: "\uBC88\uC5ED\uD22C \uBC18\uBCF5",
27425
+ suggest: "\uC5D0 \uC788\uC5B4\u2192\uC5D0\uC11C, \uB418\uC5B4\uC9C0\uB2E4\u2192\uB418\uB2E4, \uAC00\uC9C0\uACE0 \uC788\uB2E4\u2192\uC788\uB2E4, \uC5D0 \uB300\uD574\uC11C\u2192\uB97C"
27426
+ },
27427
+ {
27428
+ code: "SLOP_TRIAD",
27429
+ severity: "warning",
27430
+ pattern: /, 그리고 /g,
27431
+ message: "'A, B, \uADF8\uB9AC\uACE0 C' \uB098\uC5F4 \u2014 \uC601\uC5B4\uC2DD 3\uD56D \uBCD1\uB82C",
27432
+ suggest: "A\xB7B\xB7C \uB610\uB294 A, B \uBC0F C"
27433
+ },
27434
+ // 메타 진입 — 줄 첫머리의 지시어가 앞 줄을 다시 받는 글쓰기(개조식은 줄마다 새 사실)
27435
+ {
27436
+ code: "SLOP_META_LEAD",
27437
+ severity: "warning",
27438
+ minCount: 2,
27439
+ pattern: /^[ \t]*(?:[-□○ㅇ❍◦ㆍ·•*>][ \t]*)?(?:이는|이를 통해|이러한|이와 같이|이에 따라|이로써)[ \t]/g,
27440
+ message: "\uC904 \uCCAB\uBA38\uB9AC \uC9C0\uC2DC\uC5B4 \uBC18\uBCF5 \u2014 \uAC1C\uC870\uC2DD\uC740 \uC904\uB9C8\uB2E4 \uC0C8 \uC0AC\uC2E4",
27441
+ suggest: "\uC9C0\uC2DC\uC5B4\uB97C \uC9C0\uC6B0\uACE0 \uC8FC\uC5B4\xB7\uB300\uC0C1\uC744 \uBC14\uB85C \uC500"
27442
+ },
27443
+ {
27444
+ code: "SLOP_EMOJI",
27445
+ severity: "warning",
27446
+ pattern: /[✅✨❌⭐\u{1F300}-\u{1FAFF}]/gu,
27447
+ message: "\uADF8\uB9BC \uBB38\uC790\uB294 \uACF5\uBB38\uC11C\uC5D0 \uC4F0\uC9C0 \uC54A\uC74C",
27448
+ suggest: "\uC9C0\uC6C0 \uB610\uB294 \u25A1\xB7\u3147\xB7\u203B \uBD80\uD638\uB85C"
27449
+ },
27450
+ // 쌍점을 앞말에 붙인 "제목: 부제" 만 — 서울 방침서 과제 소제목 "○○ 운영 : ’30년 ○○개소"(쌍점 앞뒤 띄움)는 실물 관행
27451
+ {
27452
+ code: "SLOP_COLON_HEADING",
27453
+ severity: "warning",
27454
+ pattern: /^#{1,6}[ \t]+[^\n::]{1,19}[^\s::][::][ \t]+\S/g,
27455
+ message: "'\uC81C\uBAA9: \uBD80\uC81C' \uAF34 \uD5E4\uB529 \u2014 \uC7A5 \uC774\uB984\uC740 \uBA85\uC0AC\uAD6C \uD558\uB098",
27456
+ suggest: "\uC608) \u2161. \uD604\uD669 \uBC0F \uBB38\uC81C\uC810"
27457
+ }
27458
+ ];
27459
+ var VAGUE_END = /(?:강화|제고|도모|증진|향상|활성화|구축|기여|확립|정착|내실화|극대화|선도|실현|조성|확산|고도화)[ \t]*(?:필요|추진|도모|기대)?[.。]?$/;
27460
+ var CONCRETE = /\d|○○|「|[A-Z]{2,}/;
27461
+ var BULLET = /^[ \t]*(?:[-□○ㅇ❍◦ㆍ·•▸*]|\d+[.)])[ \t]*(.+)$/;
27462
+ var VAGUE_MIN_LINES = 10;
27463
+ var VAGUE_RATIO = 0.15;
27464
+ function lintSlopText(text) {
27465
+ const findings = [];
27466
+ const pending = /* @__PURE__ */ new Map();
27467
+ const lines = text.split(/\r?\n/);
27468
+ let fence = null;
27469
+ let bullets = 0;
27470
+ const vague = [];
27471
+ for (let i = 0; i < lines.length; i++) {
27472
+ const line = lines[i];
27473
+ const f = line.match(/^\s*(```+|~~~+)/);
27474
+ if (f) {
27475
+ if (fence === null) fence = f[1][0];
27476
+ else if (f[1][0] === fence) fence = null;
27477
+ continue;
27478
+ }
27479
+ if (fence !== null) continue;
27480
+ for (const r of RULES2) {
27481
+ r.pattern.lastIndex = 0;
27482
+ for (const m of line.matchAll(r.pattern)) {
27483
+ const hit = { line: i + 1, match: m[0].trim() || m[0], rule: r.code, severity: r.severity, message: r.message, suggest: r.suggest };
27484
+ if (r.minCount) {
27485
+ const list = pending.get(r.code) ?? [];
27486
+ list.push(hit);
27487
+ pending.set(r.code, list);
27488
+ } else findings.push(hit);
27489
+ }
27490
+ }
27491
+ if (/^\s*\|/.test(line)) continue;
27492
+ const b = BULLET.exec(line);
27493
+ if (!b) continue;
27494
+ const body = b[1].replace(/\*\*/g, "").trim();
27495
+ if (body.length < 6) continue;
27496
+ bullets++;
27497
+ if (VAGUE_END.test(body) && !CONCRETE.test(body)) vague.push({ line: i + 1, body });
27498
+ }
27499
+ for (const r of RULES2) {
27500
+ const list = r.minCount ? pending.get(r.code) : void 0;
27501
+ if (list && list.length >= r.minCount) findings.push(...list);
27502
+ }
27503
+ if (bullets >= VAGUE_MIN_LINES && vague.length >= 3 && vague.length / bullets >= VAGUE_RATIO) {
27504
+ const pct = Math.round(vague.length / bullets * 100);
27505
+ for (const v of vague) {
27506
+ findings.push({
27507
+ line: v.line,
27508
+ match: v.body.slice(0, 60),
27509
+ rule: "SLOP_VAGUE_OUTCOME",
27510
+ severity: "warning",
27511
+ message: `\uC22B\uC790\xB7\uB300\uC0C1 \uC5C6\uC774 \uCD94\uC0C1 \uC131\uACFC\uB85C \uB05D\uB098\uB294 \uC904(\uBB38\uC11C ${pct}% \u2014 \uC2E4\uACB0\uC7AC \uBCF4\uACE0\uC11C \uCD5C\uB300 11%)`,
27512
+ suggest: "\uBB34\uC5C7\uC744\xB7\uC5BC\uB9C8\uB098\xB7\uC5B8\uC81C\uAE4C\uC9C0 \u2014 \uC608) \uC0C1\uC2B5 \uD22C\uAE30\uC9C0\uC810 42\uACF3 CCTV \uC124\uCE58(12\uC6D4)"
27513
+ });
27514
+ }
27515
+ }
27516
+ return findings.sort((a, b) => a.line - b.line);
27517
+ }
27518
+ function slopLintWarnings(text, limit = 10) {
27519
+ const findings = lintSlopText(text);
27520
+ const shown = findings.slice(0, limit).map(
27521
+ (f) => `\uC724\uBB38 [${f.rule}] L${f.line} "${f.match}" \u2014 ${f.message}${f.suggest ? ` (${f.suggest})` : ""}`
27522
+ );
27523
+ if (findings.length > limit) shown.push(`\uC724\uBB38 \uACBD\uACE0 ${findings.length - limit}\uAC74 \uB354 \uC788\uC74C \u2014 kordoc lint --gate \uB85C \uC804\uCCB4 \uD655\uC778`);
27524
+ return shown;
27525
+ }
27526
+
27527
+ // src/hwpx/logic-lint.ts
27528
+ function usesLogicStructure(preset) {
27529
+ const p = normalizeGongmunPreset(preset);
27530
+ return p === "report" || p === "plan" || p === "gaejosik" || p === "ministry" || p === "bangchim";
27531
+ }
27532
+ var ESSAY = /^(?:서론|본론|결론|맺음말|맺는말|들어가며|들어가는 ?말|나가며|나오며|마치며|결어|요약 ?및 ?결론)$/;
27533
+ var ROLE_RE = [
27534
+ ["exec", /일정|예산|재원|소요|행정사항|협조|조치계획|향후|추진체계|역할|건의|사후관리|성과관리/],
27535
+ ["effect", /기대효과|효과/],
27536
+ ["basis", /배경|근거|필요성|목적|경위|경과|계기/],
27537
+ ["problem", /문제|한계|애로/],
27538
+ ["status", /현황|실태|여건|진단|분석/],
27539
+ ["plan", /방안|대책|개선|방향|목표|전략|과제|세부|내용|추진계획|운영계획|사업계획|기본계획|개요|검토의견|대안|결과|실적/]
27540
+ ];
27541
+ function headingText(raw) {
27542
+ let t = raw.trim().replace(/^\*\*(.+)\*\*$/, "$1");
27543
+ if (/^#\s/.test(t)) return null;
27544
+ const md = /^#{2,6}\s+(.+)$/.exec(t);
27545
+ if (md) t = md[1];
27546
+ else if (/^□/.test(t)) t = t.slice(1);
27547
+ else if (!/^(?:[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]+\s*[..]?|\d{1,2}\s*[..])\s*[^\s\d]/.test(t) || /^\s/.test(raw)) return null;
27548
+ t = t.replace(/\*\*|__/g, "").replace(/^\s*(?:[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]+\s*[..]?|(?:\d{1,2}|[가-하])\s*[..)])\s*/, "").trim();
27549
+ if (t.length < 2 || t.length > 24 || /[.。]$|다$|함$|음$|,/.test(t)) return null;
27550
+ return t;
27551
+ }
27552
+ function rolesOf(title) {
27553
+ const c = title.replace(/\s/g, "");
27554
+ const roles = /* @__PURE__ */ new Set();
27555
+ for (const [r, re] of ROLE_RE) if (re.test(c)) roles.add(r);
27556
+ if (roles.has("exec") && !/방안|대책|전략|과제|내용/.test(c)) roles.delete("plan");
27557
+ return roles;
27558
+ }
27559
+ var PURPOSE = /(?:고자|려고)\s*함\.?$|보고(?:드림|함)\.?$/;
27560
+ function splitSections(text) {
27561
+ const sections = [];
27562
+ let purpose = false;
27563
+ const essay = [];
27564
+ let fence = null;
27565
+ const lines = text.split(/\r?\n/);
27566
+ for (let i = 0; i < lines.length; i++) {
27567
+ const raw = lines[i];
27568
+ const f = raw.match(/^\s*(```+|~~~+)/);
27569
+ if (f) {
27570
+ if (fence === null) fence = f[1][0];
27571
+ else if (f[1][0] === fence) fence = null;
27572
+ continue;
27573
+ }
27574
+ if (fence !== null || !raw.trim()) continue;
27575
+ const h = /^\s*\|/.test(raw) ? null : headingText(raw);
27576
+ if (h !== null) {
27577
+ if (ESSAY.test(h.replace(/\s+/g, " "))) {
27578
+ essay.push({
27579
+ line: i + 1,
27580
+ match: h,
27581
+ rule: "LOGIC_ESSAY_HEADING",
27582
+ severity: "error",
27583
+ message: "\uC7A5 \uC774\uB984\uC744 \uC11C\uB860\xB7\uBCF8\uB860\xB7\uACB0\uB860\uC73C\uB85C \uC801\uC74C \u2014 \uACF5\uBB38\uC740 \uADF8 \uD750\uB984\uC744 \uC2E4\uBB34 \uC7A5 \uC774\uB984\uC73C\uB85C \uB4DC\uB7EC\uB0C4",
27584
+ suggest: "\uC65C: \uCD94\uC9C4\uBC30\uACBD\xB7\uAD00\uB828\uADFC\uAC70\xB7\uD604\uD669 \uBC0F \uBB38\uC81C\uC810 \u2192 \uBB34\uC5C7: \uCD94\uC9C4\uAC1C\uC694\xB7\uAC1C\uC120\uBC29\uC548\xB7\uC138\uBD80\uB0B4\uC6A9 \u2192 \uC5B4\uB5BB\uAC8C: \uCD94\uC9C4\uC77C\uC815\xB7\uC18C\uC694\uC608\uC0B0\xB7\uD5A5\uD6C4\uACC4\uD68D"
27585
+ });
27586
+ }
27587
+ const roles = rolesOf(h);
27588
+ if (roles.size) {
27589
+ sections.push({ line: i + 1, title: h, roles, body: [] });
27590
+ continue;
27591
+ }
27592
+ }
27593
+ if (!sections.length && PURPOSE.test(raw.replace(/^\s*>\s*|\*\*/g, "").trim())) purpose = true;
27594
+ sections.at(-1)?.body.push({ line: i + 1, text: raw });
27595
+ }
27596
+ return { sections, essay, purpose };
27597
+ }
27598
+ var FACT = /\d|「|○○|○[가-힣%]|\[\s*\]/;
27599
+ var STOP = /* @__PURE__ */ new Set([
27600
+ "\uBB38\uC81C",
27601
+ "\uBB38\uC81C\uC810",
27602
+ "\uBD80\uC871",
27603
+ "\uBBF8\uD761",
27604
+ "\uD55C\uACC4",
27605
+ "\uACE4\uB780",
27606
+ "\uD544\uC694",
27607
+ "\uC99D\uAC00",
27608
+ "\uAC10\uC18C",
27609
+ "\uBC1C\uC0DD",
27610
+ "\uD604\uD669",
27611
+ "\uC704\uD55C",
27612
+ "\uB300\uD55C",
27613
+ "\uAD00\uB828",
27614
+ "\uAC1C\uC120",
27615
+ "\uAC15\uD654",
27616
+ "\uC6B4\uC601",
27617
+ "\uCD94\uC9C4",
27618
+ "\uD655\uB300",
27619
+ "\uCCB4\uACC4",
27620
+ "\uAD00\uB9AC",
27621
+ "\uC9C0\uC6D0",
27622
+ "\uC6B0\uB824",
27623
+ "\uC800\uD558",
27624
+ "\uC5B4\uB824\uC6C0",
27625
+ "\uBD80\uC7AC",
27626
+ "\uC911\uC2EC",
27627
+ "\uC704\uC8FC",
27628
+ "\uC788\uC74C",
27629
+ "\uC5C6\uC74C",
27630
+ "\uB300\uC751",
27631
+ "\uC2E4\uC2DC",
27632
+ "\uAE30\uC874",
27633
+ "\uD604\uD589",
27634
+ "\uB530\uB978",
27635
+ "\uC778\uD55C",
27636
+ "\uD1B5\uD55C",
27637
+ "\uB4F1\uC758"
27638
+ ]);
27639
+ var JOSA = /(?:으로|에서|에게|까지|부터|이나|과의|와의|으로의|에의|의|은|는|이|가|을|를|에|로|과|와|도|만)$/;
27640
+ function keyTerms(s) {
27641
+ const out = [];
27642
+ for (let w of s.replace(/\*\*|\([^)]*\)/g, " ").split(/[^가-힣]+/)) {
27643
+ if (w.length > 2) w = w.replace(JOSA, "");
27644
+ if (w.length >= 2 && !STOP.has(w)) out.push(w);
27645
+ }
27646
+ return out;
27647
+ }
27648
+ var TOP_BULLET = /^(?:[-□○ㅇ❍◦•*]|\d+[.)])\s*(.+)$/;
27649
+ function problemItems(sections) {
27650
+ const items = [];
27651
+ for (const s of sections) {
27652
+ const pure = s.roles.has("problem") && !s.roles.has("status");
27653
+ let inProblem = pure;
27654
+ let markIndent = pure ? -1 : -2;
27655
+ let base = -1;
27656
+ let col = -1;
27657
+ for (const b of s.body) {
27658
+ const t = b.text;
27659
+ if (/^\s*\|/.test(t)) {
27660
+ const cells = t.trim().replace(/^\||\|$/g, "").split("|").map((c) => c.trim());
27661
+ if (cells.every((c) => /^:?-{2,}:?$/.test(c))) continue;
27662
+ const hc = cells.findIndex((c) => /문제/.test(c) && c.length <= 8);
27663
+ if (hc >= 0) {
27664
+ col = hc;
27665
+ continue;
27666
+ }
27667
+ if (col >= 0 && cells[col]) items.push({ line: b.line, text: cells[col] });
27668
+ continue;
27669
+ }
27670
+ col = -1;
27671
+ const indent = t.length - t.trimStart().length;
27672
+ if (!pure && /문제점|\(문제\)|<\s*문제/.test(t) && t.replace(/[^가-힣]/g, "").length <= 8) {
27673
+ inProblem = true;
27674
+ markIndent = indent;
27675
+ base = -1;
27676
+ continue;
27677
+ }
27678
+ if (!inProblem) continue;
27679
+ const m = TOP_BULLET.exec(t.trim());
27680
+ if (!m) continue;
27681
+ if (indent <= markIndent) {
27682
+ inProblem = false;
27683
+ continue;
27684
+ }
27685
+ if (base < 0) base = indent;
27686
+ const item = m[1].replace(/\*/g, "").trim();
27687
+ if (indent === base && !/^[【\[<(].*[】\]>)]$/.test(item) && item.replace(/[^가-힣]/g, "").length >= 4) items.push({ line: b.line, text: item });
27688
+ }
27689
+ }
27690
+ return items;
27691
+ }
27692
+ var bodyText = (ss) => ss.flatMap((s) => [s.title, ...s.body.map((b) => b.text)]).join("\n");
27693
+ var has = (s, ...rs) => rs.some((r) => s.roles.has(r));
27694
+ function lintLogicText(text) {
27695
+ const { sections, essay, purpose } = splitSections(text);
27696
+ const findings = [...essay];
27697
+ if (sections.length < 3) return findings;
27698
+ const why = sections.filter((s) => has(s, "basis", "status", "problem"));
27699
+ const what = sections.filter((s) => has(s, "plan"));
27700
+ const how = sections.filter((s) => has(s, "exec"));
27701
+ const effect = sections.filter((s) => has(s, "effect"));
27702
+ const at = (s) => s?.line ?? 1;
27703
+ const add = (line, match, rule, severity, message, suggest) => findings.push({ line, match: match.slice(0, 60), rule, severity, message, suggest });
27704
+ const proposal = what.find((s) => /방안|대책|방향|전략|과제|세부|추진계획|운영계획|사업계획|기본계획|대안|검토의견|추진내용|주요내용/.test(s.title));
27705
+ if (proposal && !why.length && !purpose) {
27706
+ add(
27707
+ proposal.line,
27708
+ proposal.title,
27709
+ "LOGIC_NO_WHY",
27710
+ "error",
27711
+ "\uC65C \uD574\uC57C \uD558\uB294\uC9C0(\uCD94\uC9C4\uBC30\uACBD\xB7\uAD00\uB828\uADFC\uAC70\xB7\uD604\uD669\xB7\uBB38\uC81C\uC810) \uC7A5\uC774 \uC5C6\uC74C \u2014 \uBC29\uC548\uBD80\uD130 \uB0B4\uBC00\uBA74 \uACB0\uC7AC\uAD8C\uC790\uAC00 \uC124\uB4DD\uB418\uC9C0 \uC54A\uC74C",
27712
+ "\uCCAB \uC7A5\uC5D0 \uCD94\uC9C4\uBC30\uACBD(\uACC4\uAE30\xB7\uBC95\uB839\xB7\uC9C0\uC2DC\xB7\uC218\uCE58) \uB610\uB294 \uD604\uD669 \uBC0F \uBB38\uC81C\uC810"
27713
+ );
27714
+ }
27715
+ if (proposal && !how.length) {
27716
+ add(
27717
+ at(what.at(-1)),
27718
+ what.at(-1).title,
27719
+ "LOGIC_NO_HOW",
27720
+ "warning",
27721
+ "\uC5B4\uB5BB\uAC8C\xB7\uC5B8\uC81C\xB7\uC5BC\uB9C8(\uCD94\uC9C4\uC77C\uC815\xB7\uC18C\uC694\uC608\uC0B0\xB7\uD5A5\uD6C4\uACC4\uD68D\xB7\uD589\uC815\uC0AC\uD56D) \uC7A5\uC774 \uC5C6\uC74C \u2014 \uACB0\uC7AC \uB4A4 \uC2E4\uD589\uC774 \uC548 \uBCF4\uC784",
27722
+ "\uB9C8\uC9C0\uB9C9 \uC7A5\uC5D0 \uCD94\uC9C4\uC77C\uC815\xB7\uC18C\uC694\uC608\uC0B0\xB7\uD5A5\uD6C4\uACC4\uD68D(\uB2F4\uB2F9\xB7\uAE30\uD55C)"
27723
+ );
27724
+ }
27725
+ if (why.length && proposal && why[0].line > proposal.line) {
27726
+ add(
27727
+ at(why[0]),
27728
+ why[0].title,
27729
+ "LOGIC_ORDER",
27730
+ "warning",
27731
+ `'${why[0].title}'\uC774(\uAC00) '${proposal.title}' \uB4A4\uC5D0 \uC634 \u2014 \uC65C(\uBC30\uACBD\xB7\uD604\uD669)\uB97C \uBA3C\uC800 \uC138\uC6B0\uACE0 \uBB34\uC5C7(\uBC29\uC548)\uC744 \uB0C4`
27732
+ );
27733
+ }
27734
+ if (why.length && !FACT.test(bodyText(why))) {
27735
+ add(
27736
+ at(why[0]),
27737
+ why[0].title,
27738
+ "LOGIC_BASIS_NO_FACT",
27739
+ "warning",
27740
+ "\uBC30\uACBD\xB7\uD604\uD669 \uC7A5\uC5D0 \uC0AC\uC2E4(\uC218\uCE58\xB7\uBC95\uB839\xB7\uC9C0\uC2DC\xB7\uB0A0\uC9DC)\uC774 \uC5C6\uC74C \u2014 \uC65C \uC9C0\uAE08 \uD574\uC57C \uD558\uB294\uC9C0\uAC00 \uC548 \uBCF4\uC784",
27741
+ "\uC608) \u300C\u25CB\u25CB\uBC95\u300D \uAC1C\uC815(2026. 3. 1. \uC2DC\uD589), \uCD5C\uADFC 3\uB144 \uBBFC\uC6D0 1,240\uAC74(\uC5F0 18%\u2191), \uAD6C\uCCAD\uC7A5 \uC9C0\uC2DC(2026. 9. 1.)"
27742
+ );
27743
+ }
27744
+ const problems = problemItems(sections);
27745
+ if (problems.length && what.length) {
27746
+ const sol = bodyText([...what, ...how, ...effect]);
27747
+ for (const p of problems) {
27748
+ const terms = keyTerms(p.text);
27749
+ if (terms.length && !terms.some((t) => sol.includes(t))) {
27750
+ add(
27751
+ p.line,
27752
+ p.text,
27753
+ "LOGIC_PROBLEM_UNANSWERED",
27754
+ "warning",
27755
+ "\uC774 \uBB38\uC81C\uC810\uC5D0 \uB300\uC751\uD558\uB294 \uBC29\uC548\uC774 \uBCF4\uC774\uC9C0 \uC54A\uC74C \u2014 \uBB38\uC81C\uC810\uACFC \uBC29\uC548\uC740 \uC9DD\uC744 \uC774\uB8F8",
27756
+ "\uBC29\uC548 \uC7A5\uC5D0 \uC774 \uBB38\uC81C\uC758 \uD574\uBC95\uC744 \uD55C \uC904 \uC774\uC0C1"
27757
+ );
27758
+ }
27759
+ }
27760
+ }
27761
+ if (problems.length && !what.length) {
27762
+ add(
27763
+ problems[0].line,
27764
+ problems[0].text,
27765
+ "LOGIC_PROBLEM_UNANSWERED",
27766
+ "warning",
27767
+ "\uBB38\uC81C\uC810\uB9CC \uC788\uACE0 \uBC29\uC548\xB7\uB300\uCC45 \uC7A5\uC774 \uC5C6\uC74C",
27768
+ "\uAC1C\uC120\uBC29\uC548 \uC7A5\uC744 \uBB38\uC81C\uC810 \uC21C\uC11C\uB300\uB85C"
27769
+ );
27770
+ }
27771
+ const remedy = what.find((s) => /개선|대책|방안/.test(s.title));
27772
+ if (remedy && !sections.some((s) => has(s, "status", "problem"))) {
27773
+ add(
27774
+ remedy.line,
27775
+ remedy.title,
27776
+ "LOGIC_UNGROUNDED",
27777
+ "warning",
27778
+ "\uAC1C\uC120\uBC29\uC548\uC758 \uADFC\uAC70(\uD604\uD669\xB7\uBB38\uC81C\uC810) \uC7A5\uC774 \uC5C6\uC74C \u2014 \uBB34\uC5C7\uC744 \uACE0\uCE58\uB294\uC9C0 \uBA3C\uC800 \uBCF4\uC5EC\uC57C \uD568",
27779
+ "\uAC1C\uC120\uBC29\uC548 \uC55E\uC5D0 \uD604\uD669 \uBC0F \uBB38\uC81C\uC810"
27780
+ );
27781
+ }
27782
+ for (const s of effect) {
27783
+ if (s.body.length && !FACT.test(bodyText([s]))) {
27784
+ add(
27785
+ s.line,
27786
+ s.title,
27787
+ "LOGIC_EFFECT_VAGUE",
27788
+ "warning",
27789
+ "\uAE30\uB300\uD6A8\uACFC\uC5D0 \uBAA9\uD45C\uCE58\uAC00 \uC5C6\uC74C \u2014 \uBB38\uC81C\uC810\uC5D0\uC11C \uB4E0 \uC9C0\uD45C\uAC00 \uC5BC\uB9C8\uB098 \uB098\uC544\uC9C0\uB294\uC9C0\uB85C",
27790
+ "\uC608) \uC0C1\uC2B5 \uD22C\uAE30\uC9C0\uC810 \uBBFC\uC6D0 \uC5F0 412\uAC74 \u2192 200\uAC74(51%\u2193)"
27791
+ );
27792
+ }
27793
+ }
27794
+ for (const s of how) {
27795
+ if (s.body.length && !FACT.test(bodyText([s])) && !/협조|역할|체계|건의|행정/.test(s.title)) {
27796
+ add(
27797
+ s.line,
27798
+ s.title,
27799
+ "LOGIC_EXEC_VAGUE",
27800
+ "warning",
27801
+ "\uC77C\uC815\xB7\uC608\uC0B0 \uC7A5\uC5D0 \uB0A0\uC9DC\xB7\uAE08\uC561\xB7\uAE30\uD55C\uC774 \uC5C6\uC74C",
27802
+ "\uC608) 2026. 11.\u223C12. \uC124\uCE58, \uCD1D 84,000,000\uC6D0(\uAD6C\uBE44 100%)"
27803
+ );
27804
+ }
27805
+ }
27806
+ return findings.sort((a, b) => a.line - b.line);
27807
+ }
27808
+ function logicLintWarnings(text, limit = 10) {
27809
+ const findings = lintLogicText(text);
27810
+ const shown = findings.slice(0, limit).map(
27811
+ (f) => `\uAD6C\uC131 [${f.rule}] L${f.line} "${f.match}" \u2014 ${f.message}${f.suggest ? ` (${f.suggest})` : ""}`
27812
+ );
27813
+ if (findings.length > limit) shown.push(`\uAD6C\uC131 \uACBD\uACE0 ${findings.length - limit}\uAC74 \uB354 \uC788\uC74C \u2014 kordoc lint --gate \uB85C \uC804\uCCB4 \uD655\uC778`);
27814
+ return shown;
27815
+ }
27816
+
27817
+ // src/hwpx/gongmun-gate.ts
27818
+ function gongmunGate(text, opts) {
27819
+ const tag = (axis, fs) => fs.map((f) => ({ line: f.line, match: f.match, rule: f.rule, severity: f.severity, message: f.message, suggest: f.suggest, axis }));
27820
+ const preset = opts?.preset;
27821
+ const munche = opts?.munche || preset !== void 0 && usesGaejosikMunche(preset);
27822
+ const logic = preset === void 0 || usesLogicStructure(preset);
27823
+ const strict = [
27824
+ ...tag("\uD45C\uAE30\uBC95", lintGongmunText(text, { document: opts?.document, parsed: opts?.parsed })),
27825
+ ...munche ? tag("\uBB38\uCCB4", lintMuncheText(text)) : []
27826
+ ];
27827
+ const blocking = [
27828
+ ...strict.filter((f) => f.severity === "error"),
27829
+ ...tag("\uC724\uBB38", lintSlopText(text)),
27830
+ ...logic ? tag("\uAD6C\uC131", lintLogicText(text)) : []
27831
+ ].sort((a, b) => a.line - b.line);
27832
+ return { pass: blocking.length === 0, blocking, advisory: strict.filter((f) => f.severity !== "error") };
27833
+ }
27834
+ function formatGateFinding(f) {
27835
+ return `${f.axis} [${f.rule}] L${f.line} "${f.match}" \u2014 ${f.message}${f.suggest ? ` (${f.suggest})` : ""}`;
27836
+ }
27837
+
27375
27838
  // src/roundtrip/patcher.ts
27376
27839
  import JSZip10 from "jszip";
27377
27840
 
@@ -29321,6 +29784,7 @@ async function patchHwp(original, editedMarkdown, options) {
29321
29784
  }
29322
29785
  }
29323
29786
  let data;
29787
+ const warnings = [];
29324
29788
  const dirty = scans.some((s) => s.repl.size > 0 || s.inserts.size > 0);
29325
29789
  if (!dirty) {
29326
29790
  data = new Uint8Array(original);
@@ -29333,6 +29797,12 @@ async function patchHwp(original, editedMarkdown, options) {
29333
29797
  const content = compressed ? compressWithTail(newStream) : newStream;
29334
29798
  out = replaceOleStream(out, sectionPaths[i], content);
29335
29799
  }
29800
+ if (flags & FLAG_TRACK_CHANGES) {
29801
+ const header = Buffer.from(fhEntry.content);
29802
+ header.writeUInt32LE((flags & ~FLAG_TRACK_CHANGES) >>> 0, 36);
29803
+ out = replaceOleStream(out, "/FileHeader", header);
29804
+ warnings.push("\uBCC0\uACBD \uB0B4\uC6A9 \uCD94\uC801 \uBB38\uC11C\uB77C \uD55C\uAE00\uC774 \uACE0\uCE5C \uBCF8\uBB38\uC744 \uADF8\uB9AC\uB3C4\uB85D \uCD94\uC801 \uD45C\uC2DC\uB97C \uAED0\uC2B5\uB2C8\uB2E4. \uD55C\uAE00\uC5D0\uC11C\uB294 \uC9C0\uAE08 \uB0B4\uC6A9\uB9CC \uBCF4\uC774\uACE0 \uB204\uAC00 \uBB34\uC5C7\uC744 \uBC14\uAFE8\uB294\uC9C0\uC758 \uAE30\uB85D\uC740 \uBCF4\uC774\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC6D0\uBCF8 \uD30C\uC77C\uC5D0\uB294 \uB0A8\uC544 \uC788\uC74C).");
29805
+ }
29336
29806
  data = new Uint8Array(out);
29337
29807
  } catch (err) {
29338
29808
  return { success: false, applied: 0, skipped, error: `HWP \uC139\uD130 \uC218\uC220 \uC2E4\uD328: ${msg(err)}` };
@@ -29348,7 +29818,7 @@ async function patchHwp(original, editedMarkdown, options) {
29348
29818
  return { success: false, applied, skipped, error: `\uD328\uCE58\uBCF8 \uC7AC\uD30C\uC2F1 \uC2E4\uD328 \u2014 \uD328\uCE58 \uC911\uB2E8: ${msg(err)}` };
29349
29819
  }
29350
29820
  }
29351
- return { success: true, data, applied, skipped, verification };
29821
+ return { success: true, data, applied, skipped, verification, ...warnings.length ? { warnings } : {} };
29352
29822
  function fail(error) {
29353
29823
  return { success: false, applied: 0, skipped, error };
29354
29824
  }
@@ -30022,7 +30492,7 @@ async function patchHwpx(original, editedMarkdown, options) {
30022
30492
  const editedUnits = splitMarkdownUnits(editedMarkdown);
30023
30493
  const pairs = alignUnits(origUnits.map((u) => looseRaw(u.raw)), editedUnits.map((u) => looseRaw(u.raw)));
30024
30494
  const paraMap = resolveParagraphMappings(origBlocks, scans);
30025
- const scanTables = scans.flatMap((s) => s.tables.filter((t) => t.rows.length > 0));
30495
+ const scanTables = scans.flatMap((s) => s.tables.filter((t) => t.rows.length > 0 && !t.channel));
30026
30496
  const obTableOrdinals = buildTableOrdinals(origBlocks);
30027
30497
  const sectionSplices = scans.map(() => []);
30028
30498
  const structural = options?.structure !== false && origUnits.length * editedUnits.length <= 4e6;
@@ -30138,7 +30608,9 @@ function buildTableOrdinals(blocks) {
30138
30608
  function resolveParagraphMappings(blocks, scans) {
30139
30609
  const buckets = /* @__PURE__ */ new Map();
30140
30610
  for (const scan of scans) {
30141
- for (const para2 of scan.bodyParagraphs) {
30611
+ const channel = scan.tables.flatMap((t) => t.channelCell?.paragraphs ?? []);
30612
+ const paras = channel.length ? [...scan.bodyParagraphs, ...channel].sort((a, b) => a.start - b.start) : scan.bodyParagraphs;
30613
+ for (const para2 of paras) {
30142
30614
  const key = normForMatch(para2.text);
30143
30615
  if (!key) continue;
30144
30616
  let list = buckets.get(key);
@@ -30588,7 +31060,7 @@ async function buildState(bytes) {
30588
31060
  scans.push(scanSectionXml(xml, i));
30589
31061
  }
30590
31062
  const paraMap = resolveParagraphMappings(parsed.blocks, scans);
30591
- const scanTables = scans.flatMap((s) => s.tables.filter((t) => t.rows.length > 0));
31063
+ const scanTables = scans.flatMap((s) => s.tables.filter((t) => t.rows.length > 0 && !t.channel));
30592
31064
  const tableOrdinals = buildTableOrdinals(parsed.blocks);
30593
31065
  const fragmentBlocks = /* @__PURE__ */ new Set();
30594
31066
  const unitBlocks = /* @__PURE__ */ new Set();
@@ -31025,7 +31497,7 @@ function scriptsOff(r, off) {
31025
31497
  }
31026
31498
  async function parseImage(buffer, options) {
31027
31499
  try {
31028
- const { parseImageDocument } = await import("./image-ocr-HH4WLRMA.js");
31500
+ const { parseImageDocument } = await import("./image-ocr-6J7B5UVA.js");
31029
31501
  const { blocks, warnings, lines } = await parseImageDocument(buffer, options);
31030
31502
  stripScriptTags({ blocks });
31031
31503
  return {
@@ -31089,7 +31561,7 @@ async function parseHwp(buffer, options) {
31089
31561
  async function parsePdf(buffer, options) {
31090
31562
  let parsePdfDocument;
31091
31563
  try {
31092
- const mod = await import("./parser-J37C64DT.js");
31564
+ const mod = await import("./parser-UYBCCGY4.js");
31093
31565
  parsePdfDocument = mod.parsePdfDocument;
31094
31566
  } catch {
31095
31567
  return {
@@ -31289,6 +31761,13 @@ export {
31289
31761
  usesGaejosikMunche,
31290
31762
  lintMuncheText,
31291
31763
  muncheLintWarnings,
31764
+ lintSlopText,
31765
+ slopLintWarnings,
31766
+ usesLogicStructure,
31767
+ lintLogicText,
31768
+ logicLintWarnings,
31769
+ gongmunGate,
31770
+ formatGateFinding,
31292
31771
  replaceOleStream,
31293
31772
  wipeFreeSectors,
31294
31773
  patchHwp,
@@ -31312,4 +31791,4 @@ export {
31312
31791
  parseHwpml,
31313
31792
  fillForm
31314
31793
  };
31315
- //# sourceMappingURL=chunk-VTVPHZJ6.js.map
31794
+ //# sourceMappingURL=chunk-MI2YNEQJ.js.map