hanlint 0.0.7 → 0.0.8

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 (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -5
  3. package/data/adjectiveStems.txt +118 -0
  4. package/data/baselineAnchors.txt +3 -0
  5. package/data/blindEvaluation.schema.json +26 -0
  6. package/data/bridgeOpeners.txt +10 -0
  7. package/data/candidateActionNouns.txt +39 -0
  8. package/data/candidateSplitEndings.txt +13 -0
  9. package/data/cliches.json +1 -1
  10. package/data/confusable.json +2 -2
  11. package/data/deixis.txt +10 -3
  12. package/data/draftHistory.txt +10 -0
  13. package/data/easyWords.json +0 -42
  14. package/data/enoughMarkers.txt +6 -0
  15. package/data/entailmentPredictions.schema.json +52 -0
  16. package/data/euiNouns.txt +3 -4
  17. package/data/evidenceEntailmentBenchmark.schema.json +126 -0
  18. package/data/exemplars.json +347 -0
  19. package/data/holeKinds.json +1 -2
  20. package/data/iVerbStems.txt +26 -0
  21. package/data/inNouns.txt +147 -0
  22. package/data/irregularStems.txt +64 -0
  23. package/data/nonNouns.txt +153 -0
  24. package/data/panelAssignmentReview.schema.json +117 -0
  25. package/data/panelJudgePredictions.schema.json +78 -0
  26. package/data/panelReviewBatch.schema.json +79 -0
  27. package/data/panelTrialSet.schema.json +44 -0
  28. package/data/patterns.json +114 -0
  29. package/data/profiles.json +1 -0
  30. package/data/readerCalls.txt +12 -7
  31. package/data/redundantPair.json +1 -1
  32. package/data/rieulStems.txt +30 -0
  33. package/data/ruleCategories.json +56 -0
  34. package/data/ruleDocs.json +29 -17
  35. package/data/ruleMechanisms.json +56 -0
  36. package/data/spacing.json +0 -12
  37. package/data/spelling.json +6 -24
  38. package/data/translationese.json +10 -0
  39. package/data/verbTails.txt +17 -0
  40. package/data/version.json +1 -1
  41. package/data/writingBrief.schema.json +64 -0
  42. package/data/writingBriefV2.schema.json +116 -0
  43. package/data/writingTrial.schema.json +36 -0
  44. package/package.json +13 -5
  45. package/src/analysis/grammar/ending.js +336 -0
  46. package/src/analysis/grammar/hangul.js +94 -0
  47. package/src/analysis/grammar/index.js +5 -0
  48. package/src/analysis/grammar/josa.js +73 -0
  49. package/src/analysis/grammar/register.js +115 -0
  50. package/src/analysis/grammar/voice.js +43 -0
  51. package/src/analysis/index.js +4 -31
  52. package/src/analysis/josa.js +2 -0
  53. package/src/analysis/{surface/splitSentences.js → splitSentences.js} +1 -1
  54. package/src/analysis/{surface/tokenize.js → tokenize.js} +70 -13
  55. package/src/baseline/store.js +114 -0
  56. package/src/cli/main.js +506 -58
  57. package/src/cli/walk.js +30 -0
  58. package/src/cli/welcome.js +52 -0
  59. package/src/config/settings.js +103 -8
  60. package/src/data/exemplars.js +177 -0
  61. package/src/data/load.js +10 -0
  62. package/src/data/operations.js +278 -0
  63. package/src/data/patches.js +125 -0
  64. package/src/data/patterns.js +48 -0
  65. package/src/data/profiles.js +102 -0
  66. package/src/document/model.js +2 -0
  67. package/src/document/parseMarkdown.js +84 -11
  68. package/src/edit/applyFixes.js +0 -0
  69. package/src/fingerprint/build.js +50 -32
  70. package/src/{rules/shared → fingerprint}/codeBlocks.js +11 -11
  71. package/src/fingerprint/codeMarkers.js +73 -0
  72. package/src/fingerprint/markers.js +9 -0
  73. package/src/fingerprint/readerState.js +126 -0
  74. package/src/fingerprint/sourceSentence.js +37 -0
  75. package/src/fingerprint/topics.js +1 -1
  76. package/src/index.js +2 -16
  77. package/src/report/compactReport.js +6 -1
  78. package/src/report/fingerprintJson.js +8 -3
  79. package/src/report/jsonReport.js +50 -3
  80. package/src/report/operationMatch.js +31 -0
  81. package/src/report/patchMatch.js +44 -0
  82. package/src/report/registerMatch.js +40 -0
  83. package/src/report/textReport.js +41 -5
  84. package/src/rules/code/duplicateBlock.js +4 -3
  85. package/src/rules/code/firstResultDistance.js +1 -0
  86. package/src/rules/code/inputFileSource.js +23 -68
  87. package/src/rules/code/installImport.js +4 -4
  88. package/src/rules/code/platformApi.js +7 -6
  89. package/src/rules/document/countMismatch.js +22 -6
  90. package/src/rules/document/enoughOnce.js +25 -0
  91. package/src/rules/document/fieldEcho.js +38 -0
  92. package/src/rules/document/keywordHeading.js +27 -0
  93. package/src/rules/document/keywordMissing.js +1 -0
  94. package/src/rules/document/noQuestion.js +5 -1
  95. package/src/rules/document/promiseRecall.js +4 -2
  96. package/src/rules/document/tableOddCell.js +60 -0
  97. package/src/rules/finding.js +24 -2
  98. package/src/rules/orthography/confusable.js +1 -0
  99. package/src/rules/orthography/spacing.js +1 -0
  100. package/src/rules/orthography/spelling.js +1 -0
  101. package/src/rules/paragraph/factListParagraph.js +1 -0
  102. package/src/rules/paragraph/paraFragment.js +21 -11
  103. package/src/rules/paragraph/topicBreak.js +1 -0
  104. package/src/rules/registry.js +72 -3
  105. package/src/rules/sentence/cliche.js +1 -0
  106. package/src/rules/sentence/connectorRepeat.js +8 -7
  107. package/src/rules/sentence/danglingDeixis.js +8 -3
  108. package/src/rules/sentence/dash.js +2 -1
  109. package/src/rules/sentence/deixis.js +7 -3
  110. package/src/rules/sentence/doubleNegative.js +4 -1
  111. package/src/rules/sentence/doublePassive.js +38 -1
  112. package/src/rules/sentence/draftHistory.js +24 -0
  113. package/src/rules/sentence/ellipsis.js +17 -3
  114. package/src/rules/sentence/endingRepeat.js +6 -2
  115. package/src/rules/sentence/euiChain.js +3 -2
  116. package/src/rules/sentence/fillerOpener.js +1 -0
  117. package/src/rules/sentence/hardWord.js +1 -0
  118. package/src/rules/sentence/imperativePeriod.js +2 -1
  119. package/src/rules/sentence/japaneseLoan.js +1 -0
  120. package/src/rules/sentence/longSentence.js +3 -1
  121. package/src/rules/sentence/negationRedefine.js +10 -1
  122. package/src/rules/sentence/nounPile.js +1 -0
  123. package/src/rules/sentence/numberOrphan.js +45 -0
  124. package/src/rules/sentence/outsideProfile.js +68 -0
  125. package/src/rules/sentence/redundantPair.js +1 -0
  126. package/src/rules/sentence/translationese.js +1 -0
  127. package/src/rules/shared/candidates.js +129 -0
  128. package/src/rules/shared/dictionaryRule.js +12 -2
  129. package/src/rules/shared/localAntecedent.js +12 -0
  130. package/src/rules/shared/repeat.js +48 -0
  131. package/src/rules/structure/blockUnread.js +33 -0
  132. package/src/rules/structure/bridgeRepeat.js +40 -0
  133. package/src/rules/structure/emojiBullet.js +1 -0
  134. package/src/rules/structure/headingQuestion.js +19 -0
  135. package/src/rules/structure/headingSentence.js +9 -4
  136. package/src/rules/structure/headingSkip.js +1 -0
  137. package/src/rules/structure/headingUniform.js +9 -17
  138. package/src/rules/structure/introImage.js +19 -0
  139. package/src/rules/structure/introLong.js +1 -0
  140. package/src/rules/structure/loneSubheading.js +42 -0
  141. package/src/rules/structure/moreLater.js +48 -0
  142. package/src/rules/structure/sectionNoProse.js +1 -0
  143. package/src/rules/structure/sectionResult.js +3 -2
  144. package/src/text.js +19 -0
  145. package/src/rules/document/readerAbsent.js +0 -11
  146. package/src/rules/shared/runs.js +0 -22
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 eddmpython
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,15 +4,33 @@
4
4
  헷갈리는 말, 조각난 문단, 코드 튜토리얼의 계약 위반까지 집는다. 의존성이 없고 Node 18 이상이면 된다.
5
5
 
6
6
  ```powershell
7
+ npx hanlint
7
8
  npx hanlint 글.md
8
9
  npx hanlint fix 글.md
9
10
  npx hanlint 글.md --format compact --errors-only
10
11
  ```
11
12
 
12
- 지적마다 규칙 이름, 줄 번호, 인용 문장, 왜 문제인지가 붙는다. 고친 표기가 확정된 자리는 `fix` 가 원문에
13
- 적용한다. `--format json` 은 기계가 읽는 꼴이고 종료 코드는 지적이 없으면 0, error 가 있으면 1 이라 발행
14
- 게이트에 그대로 물린다. stdin 은 `npx hanlint - --path 이름.md` 로 받는다. 규칙 목록은 `npx hanlint rules`,
15
- 규칙의 기술서는 `npx hanlint explain <규칙>` 이다.
13
+ 인자 없이 치면 첫 화면이 나온다. 이 폴더의 마크다운 이름으로 만든 예시와 지금 칠 수 있는 명령이 거기 있다.
14
+ 파일 자리에 폴더를 주면 그 아래 마크다운을 전부 찾는다.
15
+
16
+ 지적마다 규칙 이름, 줄 번호, 인용 문장, 왜 문제인지가 붙고 마지막 줄이 다음에 무엇을 하면 되는지 말한다.
17
+ 고친 표기가 확정된 자리는 `fix` 가 원문에 적용한다. `--format json` 은 기계가 읽는 꼴이고 종료 코드는
18
+ 지적이 없으면 0, error 가 있으면 1 이라 발행 게이트에 그대로 물린다. stdin 은 `npx hanlint - --path 이름.md`
19
+ 로 받는다. 규칙 목록은 `npx hanlint rules`, 규칙의 기술서는 `npx hanlint explain <규칙>`, 지금 어느 설정으로
20
+ 도는지는 `npx hanlint doctor` 다.
21
+
22
+ 파이썬 판의 `hanlint learn 전.md 승인본.md --format toml`로 승인한 `[[patches]]`를 같은
23
+ `hanlint.toml`에 두면 npm 판도 같은 패치를 읽는다. 정규화한 원문, 규칙, 프리셋, 국소 표지, 독자 상태가
24
+ 모두 같은 유일한 패치만 JSON 지적의 `patch`로 내고, 원문이 다르면 유사 문장이라도 기권한다.
25
+
26
+ 같은 `learn` 출력에서 뜻과 적용 범위를 확인한 `[[operations]]`도 두 판이 함께 읽는다. 승인 조각이 현재
27
+ 문장의 단어 경계 한 자리에만 있고 숫자, URL, 식별자, 경로, 코드, 링크 목적지가 그대로일 때 파일 JSON의
28
+ `operations[].operation.result`를 낸다. 한국어 고유명사와 프로젝트 용어는 `protectedTerms`에 적어 잠근다.
29
+ 지시어와 의미 고침, 여러 자리 일치는 기권하며 원문 완전 일치 패치와 확정 fix가 먼저다.
30
+
31
+ 글의 종류가 블로그가 아니면 프리셋을 먼저 고른다. `npx hanlint init --preset docs` 가 참고 문서에 맞지 않는
32
+ 규칙을 끈 설정 파일을 만든다. `blog`, `report`, `docs`, `guide`, `essay`, `fiction`, `encyclopedia` 일곱이고
33
+ `npx hanlint rules` 가 지금 도는 목록을 낸다.
16
34
 
17
35
  ```js
18
36
  import { lintFile } from "hanlint";
@@ -21,7 +39,7 @@ for (const finding of lintFile("글.md")) console.log(finding.line, finding.rule
21
39
  ```
22
40
 
23
41
  파이썬 패키지 (`pip install hanlint`) 와 같은 규칙, 같은 fixture, 같은 출력이다. 지문 지도 (`audit`, `map`),
24
- 문체 프로파일, 초안 비교 (`diff`), 평가자 겹침 (`coverage`), 형태소 정밀 모드 (kiwi) 는 파이썬 쪽에만 있다. 무엇을 잡고 무엇을 잡지 않는지는
42
+ 문체 프로파일, 초안 비교 (`diff`), 평가자 겹침 (`coverage`) 은 파이썬 쪽에만 있다. 무엇을 잡고 무엇을 잡지 않는지는
25
43
  [저장소의 product 문서](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/product.md) 에 있다.
26
44
 
27
45
  MIT.
@@ -0,0 +1,118 @@
1
+ # 형용사 어간. 한다체 현재형이 `는다` 가 아니라 `다` 로 끝나는 서술어다 (크다, 필요하다, 있다).
2
+ # 동사와 형용사는 표층으로 갈리지 않아 목록이 필요하다. `하` 로 끝나는 것은 `하` 까지 적는다.
3
+ #
4
+ # 출처: 본보기와 문형의 서술어, 기준 말뭉치 17,420문장의 평서 종결형 (2026-08-28 실측).
5
+ # 첫 측정에서 원문 재현에 실패한 형용사 가운데 두 번 이상 나온 싶다, 틀림없다, 싫다, 비어있다,
6
+ # 필요없다, 밉다, 다름없다, 귀찮다, 싱겁다, 되어있다를 더했다. 적다는 동사와 갈려 넣지 않았다.
7
+ 있
8
+ 없
9
+ 같
10
+ 아니
11
+ 크
12
+ 작
13
+ 많
14
+ 좋
15
+ 나쁘
16
+ 느리
17
+ 빠르
18
+ 다르
19
+ 길
20
+ 짧
21
+ 높
22
+ 낮
23
+ 어렵
24
+ 쉽
25
+ 멀
26
+ 가깝
27
+ 늦
28
+ 이르
29
+ 싸
30
+ 비싸
31
+ 넓
32
+ 좁
33
+ 무겁
34
+ 가볍
35
+ 새롭
36
+ 낯설
37
+ 옳
38
+ 낫
39
+ 괜찮
40
+ 맞
41
+ 드물
42
+ 빨갛
43
+ 파랗
44
+ 하얗
45
+ 노랗
46
+ 까맣
47
+ 심하
48
+ 밝
49
+ 어둡
50
+ 춥
51
+ 덥
52
+ 뜨겁
53
+ 차갑
54
+ 깊
55
+ 얕
56
+ 얇
57
+ 굵
58
+ 가늘
59
+ 그렇
60
+ 이렇
61
+ 저렇
62
+ 어떻
63
+ 자연스럽
64
+ 필요하
65
+ 충분하
66
+ 중요하
67
+ 가능하
68
+ 불가능하
69
+ 다양하
70
+ 간단하
71
+ 복잡하
72
+ 불편하
73
+ 편리하
74
+ 유용하
75
+ 비슷하
76
+ 적절하
77
+ 정확하
78
+ 명확하
79
+ 확실하
80
+ 분명하
81
+ 유명하
82
+ 심각하
83
+ 특별하
84
+ 안전하
85
+ 위험하
86
+ 익숙하
87
+ 흔하
88
+ 당연하
89
+ 무관하
90
+ 동일하
91
+ 유사하
92
+ 상당하
93
+ 조용하
94
+ 깨끗하
95
+ 건강하
96
+ 편하
97
+ 불안하
98
+ 급하
99
+ 강하
100
+ 약하
101
+ 부족하
102
+ 풍부하
103
+ 정직하
104
+ 솔직하
105
+ 친절하
106
+ 뛰어나
107
+ 불필요하
108
+ 부자연스럽
109
+ 싶
110
+ 틀림없
111
+ 싫
112
+ 비어있
113
+ 필요없
114
+ 밉
115
+ 다름없
116
+ 귀찮
117
+ 싱겁
118
+ 되어있
@@ -0,0 +1,3 @@
1
+ # 기준값의 출처를 밝히는 말 (정규식). 이 말이 값 바로 앞에 있으면 독자가 어디서 온 값인지 안다.
2
+ # numberOrphan 이 이 자리를 건너뛴다. 실측: 005 의 `1년 전 26.14%에서 18.53%로` 가 오탐이었다.
3
+ (?:1년|한 해|작년|지난해|지난|이전|예전|처음|원래|기존|기본값?|앞서|위에서|방금|릴리스|버전)
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/blindEvaluation.schema.json",
4
+ "title": "hanlint blind evaluation",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["version", "kind", "blindSha256", "evaluatorId", "evaluatorKind", "decisions", "note"],
8
+ "properties": {
9
+ "version": { "const": 1 },
10
+ "kind": { "const": "hanlint.blindEvaluation" },
11
+ "blindSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
12
+ "evaluatorId": { "type": "string", "minLength": 1 },
13
+ "evaluatorKind": { "enum": ["human", "llm"] },
14
+ "decisions": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "required": ["naturalness", "taskUtility", "voice"],
18
+ "properties": {
19
+ "naturalness": { "enum": ["left", "right", "tie"] },
20
+ "taskUtility": { "enum": ["left", "right", "tie"] },
21
+ "voice": { "enum": ["left", "right", "tie"] }
22
+ }
23
+ },
24
+ "note": { "type": "string", "minLength": 1 }
25
+ }
26
+ }
@@ -0,0 +1,10 @@
1
+ # 절을 닫는 문장의 이음 표지 (정규식). 문장 첫머리에서만 본다. bridgeRepeat 이 이렇게 닫히는 절이 몇 개인지 센다.
2
+ # 낱개로는 정상인 말이다. 실측: 기준 말뭉치 390편에서 문장 첫머리 `이제` 는 93건, `다음으로` 16건, `마지막으로` 15건,
3
+ # `이번에는` 1건. 절 끝에서 셋 이상 되풀이될 때만 틀이라 fillerOpener 와 달리 문장 하나로는 잡지 않는다.
4
+ # 실측 사례: eddmpython-course 여섯 편의 `앞에서 A 를 확인했습니다. 이번에는 B 를 확인합니다` 53건.
5
+ ^이번에는(?=[\s,])
6
+ ^이제(?=[\s,])
7
+ ^그다음(?:에는|으로|에)?(?=[\s,])
8
+ ^다음(?:으로|에는| (?:절|단계|장|글)(?:에서는|에는|은|는))(?=[\s,])
9
+ ^이어서(?=[\s,])
10
+ ^마지막(?:으로|에는)(?=[\s,])
@@ -0,0 +1,39 @@
1
+ # nounPile 후보. 기준 말뭉치의 명사 나열에서 실제로 나온 동작성 명사만 둔다.
2
+ # 출력은 이 명사에 하다를 붙인 어근이고, 뜻에 맞는지는 사람이나 LLM이 고른다.
3
+ 생성
4
+ 설치
5
+ 확인
6
+ 실행
7
+ 분석
8
+ 처리
9
+ 관리
10
+ 사용
11
+ 지원
12
+ 제공
13
+ 저장
14
+ 삭제
15
+ 변환
16
+ 비교
17
+ 측정
18
+ 추출
19
+ 입력
20
+ 출력
21
+ 전송
22
+ 요청
23
+ 응답
24
+ 분리
25
+ 연결
26
+ 등록
27
+ 조회
28
+ 적용
29
+ 수정
30
+ 개선
31
+ 해결
32
+ 제거
33
+ 구성
34
+ 선택
35
+ 검증
36
+ 배포
37
+ 개발
38
+ 운영
39
+ 설정
@@ -0,0 +1,13 @@
1
+ # longSentence 후보. 기준 말뭉치의 긴 문장에서 실제로 나온 연결 어미다. 긴 꼴부터 대조한다.
2
+ 기 때문에
3
+ 으므로
4
+ 지만
5
+ 는데
6
+ 면서
7
+ 으며
8
+ 라서
9
+ 어서
10
+ 아서
11
+ 거나
12
+ 고
13
+ 며
package/data/cliches.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "entry": [
3
3
  {
4
- "pattern": "핵심은",
4
+ "pattern": "^핵심은",
5
5
  "why": "결론을 포장하는 말이다. 핵심이 무엇인지 그 자리에서 직접 쓴다",
6
6
  "source": "글쓰기 스킬"
7
7
  },
@@ -61,13 +61,13 @@
61
61
  "source": "표준국어대사전 표제어 낫다, 낳다, 상담 사례 5890"
62
62
  },
63
63
  {
64
- "pattern": "더 낳은",
64
+ "pattern": "(?<![을를명] )(?<!둘 )(?<!셋 )(?<!넷 )(?<!하나 )(?<!다섯 )(?<!여섯 )(?<!일곱 )(?<!여덟 )(?<!아홉 )(?<!열 )더 낳은",
65
65
  "fix": "더 나은",
66
66
  "why": "더 좋다는 뜻은 낫다 이고 관형형은 나은 이다",
67
67
  "source": "표준국어대사전 표제어 낫다, 상담 사례 5890"
68
68
  },
69
69
  {
70
- "pattern": "더 낳(다|아|았)",
70
+ "pattern": "(?<![을를명] )(?<!둘 )(?<!셋 )(?<!넷 )(?<!하나 )(?<!다섯 )(?<!여섯 )(?<!일곱 )(?<!여덟 )(?<!아홉 )(?<!열 )더 낳(다|아|았)",
71
71
  "fix": "더 나$1",
72
72
  "why": "더 좋다는 뜻은 낫다 다. 낫고, 낫지, 나아, 나았다 로 활용한다",
73
73
  "source": "표준국어대사전 표제어 낫다, 상담 사례 5890"
package/data/deixis.txt CHANGED
@@ -1,8 +1,15 @@
1
1
  # 독자가 스크롤을 되돌려야 하는 지시어 (정규식). 회수 표지 (앞서 말한 X) 는 이름을 다시 부르는 정상 용법이라 넣지 않는다.
2
2
  # 앞에 한글이 붙으면 낱말의 일부다 (순위의 정본, 부위의 이름). 실측: moduleLayers.md 의 `순위의 정본은` 이 위의 로 잡혔다.
3
+ # `위의` 와 `아래의` 는 앞이 비었거나 뜻이 분명한 조사 뒤일 때만 지시어다. 앞에 한글 어절이 붙어 있으면
4
+ # `종이 위의 크기` 처럼 공간 표현이다 (실측: 이슈 3번). 조사 `이` 와 `가` 는 `종이` 같은 명사의 끝 음절과
5
+ # 표층으로 안 갈려서 허용 목록에서 뺐다. 그래서 `그림이 위의 표를` 은 놓친다. 잡는 쪽이 아니라 놓치는 쪽으로 틀린다.
6
+ # 영문과 숫자 어절 뒤도 공간 표현이다. 백틱은 지문에서 벗겨지므로 `if` 아래의 빈칸 은 if 를 기준으로 한 자리이지
7
+ # 글의 위아래가 아니다 (실측: eddmpython-course 02 의 `if 아래의 빈칸이` 가 danglingDeixis 로 잡혔다).
3
8
  (?<![가-힣])이것(?:은|이|을|으로|도|만)?(?![가-힣])
4
9
  (?<![가-힣])해당 [가-힣]+
5
- (?<![가-힣])위의 [가-힣]+
6
- (?<![가-힣])아래의 [가-힣]+
7
- (?<![가-힣])이러한
10
+ (?:(?<![가-힣A-Za-z0-9])(?<![가-힣A-Za-z0-9]\s)|(?<=[은는을를도만에]\s))위의 [가-힣]+
11
+ (?:(?<![가-힣A-Za-z0-9])(?<![가-힣A-Za-z0-9]\s)|(?<=[은는을를도만에]\s))아래의 [가-힣]+
12
+ # 이러한 은 뒤의 명사까지 잡는다. 같은 문장 앞쪽에 그 명사가 있으면 (kube- 접두사로 시작하는 네임스페이스는 ... 이러한
13
+ # 네임스페이스를) 되돌아갈 필요가 없다. 실측: 기준 말뭉치 표본을 다시 읽다 오탐으로 나왔다.
14
+ (?<![가-힣])이러한 [가-힣]+
8
15
  (?<![가-힣])이처럼
@@ -0,0 +1,10 @@
1
+ # 글쓴이의 수정 이력과 자기 검증 기록 (정규식). 독자가 본 적 없는 초고를 고치는 장면이라 본문에서 뺀다.
2
+ # 출처: cinch 의 전역 blog-writing 스킬, 무엇을 빼는가의 반드시 빼는 것 첫 항목.
3
+ # 합니다체의 넓은 자기 검증 꼴과 실측 본보기 `그대로 도는 것을 확인`의 세 문체를 본다.
4
+ # 한다체 참고 문서의 재현 절차와 측정 방법은 아래 좁은 표지에 들지 않는다.
5
+ 처음에는[^.!?]{0,25}(?:썼|적었|썼다가|적었다가|쓴 적)
6
+ (?:초고|초안|첫 판)(?:에서|에는|에|을|를)[^.!?]{0,20}(?:고쳤|바꿨|지웠|썼)
7
+ 줄 알았[^.!?]{0,30}(?:확인|알고 보니|아니었)
8
+ (?:것|줄)을 확인했습니다
9
+ 그대로 도는 것을 (?:확인했다|확인했어요)
10
+ 직접 확인해 (?:봤습니다|보았습니다|봤다|보았다|봤어요|보았어요)
@@ -6,12 +6,6 @@
6
6
  "why": "쉬운 말 오늘 이 있다",
7
7
  "source": "표준국어대사전 (금일 = 오늘)"
8
8
  },
9
- {
10
- "pattern": "(?<![가-힣])명일{조사}",
11
- "fix": "내일",
12
- "why": "쉬운 말 내일 이 있다",
13
- "source": "표준국어대사전 (명일 = 내일)"
14
- },
15
9
  {
16
10
  "pattern": "(?<![가-힣])익월{조사}",
17
11
  "fix": "다음 달",
@@ -54,12 +48,6 @@
54
48
  "why": "쉬운 말 정해진 이 있다",
55
49
  "source": "국립국어원 다듬은 말"
56
50
  },
57
- {
58
- "pattern": "(?<![가-힣])일체{조사}",
59
- "fix": "모두",
60
- "why": "쉬운 말 모두 가 있다",
61
- "source": "국립국어원 다듬은 말"
62
- },
63
51
  {
64
52
  "pattern": "(?<![가-힣])여타{조사}",
65
53
  "fix": "다른",
@@ -138,12 +126,6 @@
138
126
  "why": "쉬운 말 남은 이 있다",
139
127
  "source": "국립국어원 다듬은 말"
140
128
  },
141
- {
142
- "pattern": "(?<![가-힣])고지{조사}",
143
- "fix": "알림",
144
- "why": "쉬운 말 알림 이 있다",
145
- "source": "국립국어원 다듬은 말"
146
- },
147
129
  {
148
130
  "pattern": "(?<![가-힣])사료(?=됩|된|되)",
149
131
  "fix": "생각",
@@ -162,30 +144,12 @@
162
144
  "why": "쉬운 말 비법 이 있다",
163
145
  "source": "국립국어원 다듬은 말"
164
146
  },
165
- {
166
- "pattern": "(?<![가-힣])인프라{조사}",
167
- "fix": "기반 시설",
168
- "why": "쉬운 말 기반 시설 이 있다",
169
- "source": "국립국어원 다듬은 말"
170
- },
171
147
  {
172
148
  "pattern": "(?<![가-힣])매뉴얼{조사}",
173
149
  "fix": "설명서",
174
150
  "why": "쉬운 말 설명서 가 있다",
175
151
  "source": "표준국어대사전 (매뉴얼 = 설명서)"
176
152
  },
177
- {
178
- "pattern": "(?<![가-힣])이슈{조사}",
179
- "fix": "쟁점",
180
- "why": "쉬운 말 쟁점 이 있다",
181
- "source": "국립국어원 다듬은 말"
182
- },
183
- {
184
- "pattern": "(?<![가-힣])프로세스{조사}",
185
- "fix": "과정",
186
- "why": "쉬운 말 과정 이 있다",
187
- "source": "국립국어원 다듬은 말"
188
- },
189
153
  {
190
154
  "pattern": "(?<![가-힣])피드백{조사}",
191
155
  "fix": "의견",
@@ -252,12 +216,6 @@
252
216
  "why": "쉬운 말 성과급 이 있다",
253
217
  "source": "국립국어원 다듬은 말"
254
218
  },
255
- {
256
- "pattern": "(?<![가-힣])모니터링{조사}",
257
- "fix": "점검",
258
- "why": "쉬운 말 점검 이 있다",
259
- "source": "국립국어원 다듬은 말"
260
- },
261
219
  {
262
220
  "pattern": "(?<![가-힣])바우처{조사}",
263
221
  "fix": "이용권",
@@ -0,0 +1,6 @@
1
+ # 기본 사용법이 끝나는 지점을 끊는 표지 (정규식). 글 전체에 한 번만 쓴다. enoughOnce 가 두 번째부터 짚는다.
2
+ # 출처: cinch 의 전역 blog-writing 스킬, 절은 앞 절이 못 한 일에서 시작한다.
3
+ 여기까지[가면는]?[^.!?]{0,20}충분
4
+ 여기까지[가면는]?[^.!?]{0,20}(?:쓰|만들|하)는 데
5
+ 여기까지[가면는]?[^.!?]{0,15}기본
6
+ 이 정도[면가]?[^.!?]{0,20}충분
@@ -0,0 +1,52 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/entailmentPredictions.schema.json",
4
+ "title": "hanlint entailment predictions",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["version", "kind", "benchmarkId", "benchmarkSha256", "evaluator", "predictions"],
8
+ "properties": {
9
+ "version": { "const": 1 },
10
+ "kind": { "const": "hanlint.entailmentPredictions" },
11
+ "benchmarkId": { "type": "string", "minLength": 1 },
12
+ "benchmarkSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
13
+ "evaluator": {
14
+ "type": "object",
15
+ "additionalProperties": false,
16
+ "required": ["id", "kind", "revision", "promptSha256"],
17
+ "properties": {
18
+ "id": { "type": "string", "minLength": 1 },
19
+ "kind": { "enum": ["human", "llm", "rule"] },
20
+ "revision": {
21
+ "type": "string",
22
+ "minLength": 1,
23
+ "not": {
24
+ "pattern": "^(?:[Hh][Ee][Aa][Dd]|[Ll][Aa][Tt][Ee][Ss][Tt]|[Mm][Aa][Ii][Nn]|[Mm][Aa][Ss][Tt][Ee][Rr])$"
25
+ }
26
+ },
27
+ "promptSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
28
+ }
29
+ },
30
+ "predictions": {
31
+ "type": "array",
32
+ "minItems": 36,
33
+ "maxItems": 36,
34
+ "items": {
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "required": ["caseId", "label", "confidence"],
38
+ "properties": {
39
+ "caseId": { "type": "string", "pattern": "^KEN[0-9]{3}$" },
40
+ "label": { "enum": ["supported", "contradicted", "insufficient", "abstain"] },
41
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
42
+ },
43
+ "allOf": [
44
+ {
45
+ "if": { "properties": { "label": { "const": "abstain" } }, "required": ["label"] },
46
+ "then": { "properties": { "confidence": { "const": 0 } } }
47
+ }
48
+ ]
49
+ }
50
+ }
51
+ }
52
+ }
package/data/euiNouns.txt CHANGED
@@ -1,5 +1,7 @@
1
1
  # 의 로 끝나는 두 음절 명사. 뒤에 공백이 와도 관형격 조사가 아니라 euiCount 에서 뺀다.
2
2
  # 실측: codeStyle.md 의 `정의 측 (함수 정의, 인자, 대입)` 이 의 2회로 잡혔다.
3
+ # 상의, 대의, 안의, 명의 는 뺐다. 글에서는 `디스크 상의 파일`, `여러 대의 서버`, `집 안의`, `두 명의` 처럼 자리와 수 뒤의
4
+ # 관형격이 훨씬 잦다. 실측: 말뭉치 390편에서 형태소 분석기와 갈린 문장을 읽어 보니 이 넷이 그 차이였다 (2026-08-29).
3
5
  정의
4
6
  회의
5
7
  논의
@@ -19,16 +21,13 @@
19
21
  항의
20
22
  심의
21
23
  토의
22
- 상의
23
24
  의의
24
25
  이의
25
26
  사의
26
- 대의
27
27
  신의
28
28
  민의
29
29
  발의
30
30
  건의
31
- 명의
32
31
  도의
33
32
  부의
34
33
  선의
@@ -37,4 +36,4 @@
37
36
  호의
38
37
  경의
39
38
  품의
40
- 안의
39
+ 거의
@@ -0,0 +1,126 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/evidenceEntailmentBenchmark.schema.json",
4
+ "title": "hanlint evidence entailment benchmark",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["version", "kind", "benchmarkId", "source", "selection", "cases", "contentSha256"],
8
+ "properties": {
9
+ "version": { "const": 1 },
10
+ "kind": { "const": "hanlint.evidenceEntailmentBenchmark" },
11
+ "benchmarkId": { "const": "klue-nli-v1.1-dev-balanced-36" },
12
+ "source": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": [
16
+ "name",
17
+ "repositoryUrl",
18
+ "paperUrl",
19
+ "sourceUrl",
20
+ "revision",
21
+ "sourceSha256",
22
+ "license",
23
+ "attribution"
24
+ ],
25
+ "properties": {
26
+ "name": { "type": "string", "minLength": 1 },
27
+ "repositoryUrl": { "type": "string", "format": "uri" },
28
+ "paperUrl": { "type": "string", "format": "uri" },
29
+ "sourceUrl": {
30
+ "const": "https://raw.githubusercontent.com/KLUE-benchmark/KLUE/3efd98708a40ff49251fddde35453f8fbb11f536/klue_benchmark/klue-nli-v1.1/klue-nli-v1.1_dev.json"
31
+ },
32
+ "revision": { "const": "3efd98708a40ff49251fddde35453f8fbb11f536" },
33
+ "sourceSha256": { "const": "0699db82be17766b26e199864e6260443e17ec6e91d1870e876419e388f245b1" },
34
+ "license": { "const": "CC-BY-SA-4.0" },
35
+ "attribution": { "type": "string", "minLength": 1 }
36
+ }
37
+ },
38
+ "selection": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "required": [
42
+ "method",
43
+ "sources",
44
+ "sourceLabels",
45
+ "labelMap",
46
+ "casesPerSourceLabel",
47
+ "minimumAgreement",
48
+ "premiseDuplicates",
49
+ "hypothesisDuplicates"
50
+ ],
51
+ "properties": {
52
+ "method": { "type": "string", "minLength": 1 },
53
+ "sources": { "const": ["NSMC", "airbnb", "policy", "wikinews", "wikipedia", "wikitree"] },
54
+ "sourceLabels": { "const": ["contradiction", "entailment", "neutral"] },
55
+ "labelMap": {
56
+ "const": {
57
+ "contradiction": "contradicted",
58
+ "entailment": "supported",
59
+ "neutral": "insufficient"
60
+ }
61
+ },
62
+ "casesPerSourceLabel": { "const": 2 },
63
+ "minimumAgreement": { "const": 4 },
64
+ "premiseDuplicates": { "const": 0 },
65
+ "hypothesisDuplicates": { "const": 0 }
66
+ }
67
+ },
68
+ "cases": {
69
+ "type": "array",
70
+ "minItems": 36,
71
+ "maxItems": 36,
72
+ "items": {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "required": [
76
+ "id",
77
+ "sourceGuid",
78
+ "domain",
79
+ "evidenceExcerpt",
80
+ "atomicFact",
81
+ "goldLabel",
82
+ "annotation",
83
+ "sourceUrl",
84
+ "revision",
85
+ "locator",
86
+ "excerptSha256",
87
+ "factSha256",
88
+ "license",
89
+ "caseSha256"
90
+ ],
91
+ "properties": {
92
+ "id": { "type": "string", "pattern": "^KEN[0-9]{3}$" },
93
+ "sourceGuid": { "type": "string", "minLength": 1 },
94
+ "domain": { "type": "string", "minLength": 1 },
95
+ "evidenceExcerpt": { "type": "string", "minLength": 1 },
96
+ "atomicFact": { "type": "string", "minLength": 1 },
97
+ "goldLabel": { "enum": ["supported", "contradicted", "insufficient"] },
98
+ "annotation": {
99
+ "type": "object",
100
+ "additionalProperties": false,
101
+ "required": ["authorLabel", "validatorLabels", "agreeingVotes", "totalVotes"],
102
+ "properties": {
103
+ "authorLabel": { "enum": ["supported", "contradicted", "insufficient"] },
104
+ "validatorLabels": {
105
+ "type": "array",
106
+ "minItems": 4,
107
+ "maxItems": 4,
108
+ "items": { "enum": ["supported", "contradicted", "insufficient"] }
109
+ },
110
+ "agreeingVotes": { "type": "integer", "minimum": 4, "maximum": 5 },
111
+ "totalVotes": { "const": 5 }
112
+ }
113
+ },
114
+ "sourceUrl": { "type": "string", "format": "uri" },
115
+ "revision": { "const": "3efd98708a40ff49251fddde35453f8fbb11f536" },
116
+ "locator": { "type": "string", "minLength": 1 },
117
+ "excerptSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
118
+ "factSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
119
+ "license": { "const": "CC-BY-SA-4.0" },
120
+ "caseSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
121
+ }
122
+ }
123
+ },
124
+ "contentSha256": { "const": "864e8af5bb88e16521e630c9f8987b32b36916c1042fee6a8ee54e76d7d8f5d8" }
125
+ }
126
+ }