hanlint 0.0.9 → 0.0.12

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 (56) hide show
  1. package/README.md +105 -31
  2. package/data/checkResult.schema.json +75 -0
  3. package/data/checkResultV2.schema.json +98 -0
  4. package/data/exemplars.json +99 -0
  5. package/data/finding.schema.json +38 -0
  6. package/data/holeKinds.json +12 -0
  7. package/data/patch.schema.json +14 -0
  8. package/data/patchResult.schema.json +86 -0
  9. package/data/patchResultV2.schema.json +71 -0
  10. package/data/profiles.json +1 -1
  11. package/data/pythonPackages.txt +3 -2
  12. package/data/readerContract.schema.json +20 -0
  13. package/data/readerContractConformanceV1.json +168 -0
  14. package/data/readerContractV2.schema.json +64 -0
  15. package/data/ruleCategories.json +3 -0
  16. package/data/ruleDocs.json +6 -3
  17. package/data/ruleMechanisms.json +3 -0
  18. package/data/screenNarration.json +29 -0
  19. package/data/screenSentence.json +34 -0
  20. package/data/screenTone.json +19 -0
  21. package/data/version.json +1 -1
  22. package/data/writingBrief.schema.json +1 -1
  23. package/data/writingBriefV2.schema.json +1 -1
  24. package/package.json +1 -1
  25. package/src/cli/help.js +34 -0
  26. package/src/cli/main.js +243 -29
  27. package/src/config/patch.js +48 -0
  28. package/src/config/readerContract.js +245 -0
  29. package/src/config/settings.js +84 -4
  30. package/src/data/load.js +2 -7
  31. package/src/data/profiles.js +14 -2
  32. package/src/data/read.js +21 -0
  33. package/src/digest.js +44 -0
  34. package/src/fingerprint/codeMarkers.js +1 -1
  35. package/src/fingerprint/dictionaries.js +3 -0
  36. package/src/guard/contract.js +174 -0
  37. package/src/guard/editLocality.js +27 -0
  38. package/src/guard/outline.js +46 -0
  39. package/src/guard/receipt.js +180 -0
  40. package/src/guard/surface.js +184 -0
  41. package/src/index.js +83 -6
  42. package/src/learn/edits.js +31 -0
  43. package/src/learn/operations.js +28 -0
  44. package/src/learn/pairs.js +57 -0
  45. package/src/report/compactReport.js +1 -1
  46. package/src/report/writingSpec.js +174 -0
  47. package/src/rules/code/inputFileSource.js +5 -3
  48. package/src/rules/code/installImport.js +6 -5
  49. package/src/rules/document/noQuestion.js +4 -4
  50. package/src/rules/readerDebt.js +9 -0
  51. package/src/rules/registry.js +34 -0
  52. package/src/rules/screen/screenNarration.js +10 -0
  53. package/src/rules/screen/screenSentence.js +10 -0
  54. package/src/rules/screen/screenTone.js +10 -0
  55. package/src/rules/sentence/nounPile.js +1 -1
  56. package/src/rules/shared/dictionaryRule.js +30 -0
package/README.md CHANGED
@@ -1,50 +1,124 @@
1
1
  # hanlint
2
2
 
3
- 한국어 글에서 반복되는 결함을 결정적으로 잡는 린터다. 번역투, 상투어, 이중 피동, 명사 나열, 조각난
4
- 문단과 코드 튜토리얼의 계약 위반을 집는다. 맞춤법 전체를 추측하지 않고 앞뒤 낱말로 확정되는 일부 표기만
5
- 본다. 런타임 의존성이 없고 Node 18 이상이면 된다.
3
+ 한국어 글에서 반복되는 결함을 찾는 린터다. 번역투, 상투어, 이중 피동과 문장 구조를 검사하고,
4
+ 지적마다 위치, 이유와 고칠 수 있는 표기를 돌려준다. Node.js 18 이상에서 런타임 의존성 없이 실행한다.
6
5
 
7
- ```powershell
8
- npx hanlint
6
+ **[설치 없이 써 보기](https://eddmpython.github.io/hanlint/)** ·
7
+ [전체 소개](https://github.com/eddmpython/hanlint#readme) ·
8
+ [명령과 설정](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/cli.md) ·
9
+ [변경 이력](https://github.com/eddmpython/hanlint/blob/main/CHANGELOG.md)
10
+
11
+ ## 빠른 시작
12
+
13
+ ```sh
9
14
  npx hanlint 글.md
15
+ npx hanlint docs/ --preset docs
16
+ npx hanlint 글.md --format json
17
+ ```
18
+
19
+ 인자 없이 실행하면 현재 폴더의 파일을 바탕으로 시작 안내를 보여 준다.
20
+ 파일 대신 폴더를 주면 아래의 마크다운 파일을 찾는다. 표준 입력은 `npx hanlint - --path 글.md`로 받는다.
21
+
22
+ ```sh
10
23
  npx hanlint fix 글.md
11
- npx hanlint 글.md --format compact --errors-only
24
+ npx hanlint 글.md
12
25
  ```
13
26
 
14
- 인자 없이 치면 첫 화면이 나온다. 이 폴더의 마크다운 이름으로 만든 예시와 지금 칠 수 있는 명령이 거기 있다.
15
- 파일 자리에 폴더를 주면 그 아래 마크다운을 전부 찾는다.
27
+ `fix`는 고친 표기가 확정된 자리를 **파일에 직접 반영**한다. 변경을 확인하고 다시 검사한다.
28
+ 일반 검사는 error가 없으면 0, 있으면 1, 입력이나 설정이 잘못되면 2로 끝난다.
29
+ notice만 있으면 0이며, `--errors-only`를 주면 notice 표시를 제외한다.
30
+ 명령마다 다른 판정은 [종료 코드 안내](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/cli.md#종료-코드)를 따른다.
31
+
32
+ ## JavaScript에서 검사하기
33
+
34
+ ```sh
35
+ npm install hanlint
36
+ ```
37
+
38
+ ```js
39
+ import { configFromMapping, lintText } from "hanlint";
40
+
41
+ const text = "이 도구는 다양한 기능을 제공합니다.";
42
+ const config = configFromMapping({ preset: "docs" });
43
+ for (const finding of lintText(text, config)) {
44
+ console.log(finding.line, finding.rule, finding.why);
45
+ }
46
+ ```
16
47
 
17
- 지적마다 규칙 이름, 줄 번호, 인용 문장, 왜 문제인지가 붙고 마지막 줄이 다음에 무엇을 하면 되는지 말한다.
18
- 고친 표기가 확정된 자리는 `fix` 가 원문에 적용한다. `--format json` 은 기계가 읽는 꼴이고 종료 코드는
19
- 지적이 없으면 0, error 가 있으면 1 이라 발행 게이트에 그대로 물린다. stdin 은 `npx hanlint - --path 이름.md`
20
- 로 받는다. 규칙 목록은 `npx hanlint rules`, 규칙의 기술서는 `npx hanlint explain <규칙>`, 지금 어느 설정으로
21
- 도는지는 `npx hanlint doctor` 다.
48
+ 파일은 `lintFile("글.md", config)`로 검사한다. `loadConfig()`는 현재 위치에서 프로젝트 설정을 찾는다.
49
+ 파일과 설정 경로를 읽는 API는 Node 전용이다. 문자열 API에 설정을 생략하면 기본 설정을 쓴다.
22
50
 
23
- 파이썬 판의 `hanlint learn 전.md 승인본.md --format toml`로 승인한 `[[patches]]`를 같은
24
- `hanlint.toml`에 두면 npm 판도 같은 패치를 읽는다. 정규화한 원문, 규칙, 프리셋, 국소 표지, 독자 상태가
25
- 모두 같은 유일한 패치만 JSON 지적의 `patch`로 내고, 원문이 다르면 유사 문장이라도 기권한다.
51
+ `blog`, `report`, `docs`, `guide`, `essay`, `fiction`, `encyclopedia`, `chat`, `screen`의 아홉 프리셋이 있다.
52
+ `screen`은 화면의 글 (단추, 이름표, 상태, 빈 상태) 이다. 낱말 자리의 문장과 화면 해설, 접객 말투를 잡고 산문 규칙은 끈다.
53
+ 프로젝트 설정은 `npx hanlint init --preset docs`로 만들고 `npx hanlint doctor`로 확인한다.
54
+ 규칙 목록은 `npx hanlint rules`, 개별 규칙의 근거는 `npx hanlint explain translationese`에서 본다.
26
55
 
27
- 같은 `learn` 출력에서 뜻과 적용 범위를 확인한 `[[operations]]`도 두 판이 함께 읽는다. 승인 조각이 현재
28
- 문장의 단어 경계 한 자리에만 있고 숫자, URL, 식별자, 경로, 코드, 링크 목적지가 그대로일 때 파일 JSON의
29
- `operations[].operation.result`를 낸다. 한국어 고유명사와 프로젝트 용어는 `protectedTerms`에 적어 잠근다.
30
- 지시어와 의미 고침, 여러 자리 일치는 기권하며 원문 완전 일치 패치와 확정 fix가 먼저다.
56
+ ## 수정하면서 원문 보호하기
31
57
 
32
- 글의 종류가 블로그가 아니면 프리셋을 먼저 고른다. `npx hanlint init --preset docs` 가 참고 문서에 맞지 않는
33
- 규칙을 끈 설정 파일을 만든다. `blog`, `report`, `docs`, `guide`, `essay`, `fiction`, `encyclopedia` 일곱이고
34
- `npx hanlint rules` 가 지금 도는 목록을 낸다.
58
+ Reader Contract는 사람이 확인한 독자, 목적과 사실을 받아 숫자, URL, 인라인 코드와 링크 목적지를 보호한다.
35
59
 
36
60
  ```js
37
- import { lintFile } from "hanlint";
61
+ import { Contract, Patch, check, renderCheck, verifyPatch } from "hanlint";
38
62
 
39
- for (const finding of lintFile("글.md")) console.log(finding.line, finding.rule, finding.why);
63
+ const text = "예산은 400,000원이다.";
64
+ const contract = new Contract(
65
+ "배포를 결정할 운영자",
66
+ "예산을 확인한다",
67
+ ["예산은 380,000원이다."],
68
+ );
69
+ console.log(renderCheck(check(text, contract)));
70
+
71
+ const patch = new Patch("unexpectedNumbers", "400,000", "380,000");
72
+ const result = verifyPatch(text, patch, contract);
73
+ console.log(result);
40
74
  ```
41
75
 
42
- 파이썬 패키지 (`pip install hanlint`) 와 같은 규칙, 같은 fixture, 같은 출력이다. 지문 지도 (`audit`, `map`),
43
- 문체 프로파일, 초안 비교 (`diff`), 평가자 겹침 (`coverage`) 은 파이썬 쪽에만 있다. 무엇을 잡고 무엇을 잡지 않는지는
44
- [전체 사용 안내](https://github.com/eddmpython/hanlint#readme)와
45
- [제품 경계](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/product.md)에서 확인할 수 있다.
76
+ `contractFromText(text, reader, goal)`은 원문에서 사실 후보를 모은다. 사람이 확인한 뒤 사용한다.
77
+ `contractFromTextV2(text, reader, goal, 2)`는 H2 제목의 수와 순서도 보호할 계약을 만든다.
78
+ 패치는 원문 한 자리에 정확히 맞고 기존 위반을 줄이며 새 보호 위반이나 error를 만들지 않아야 검증된다.
79
+ 사실의 진실, 의미 보존과 자연스러움은 사람이 판단한다.
80
+
81
+ JSON 형식과 전체 조건은
82
+ [Reader Contract](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/readerContract.md)가 정본이다.
83
+
84
+ ## 브라우저와 수정 사례
85
+
86
+ [한린트 편집기](https://eddmpython.github.io/hanlint/)는 같은 npm 코어로 검사, 고침,
87
+ 원문 비교와 개인 기록을 제공한다. 사용 방법은
88
+ [편집기 안내](https://eddmpython.github.io/hanlint/guide.html)에서 확인한다.
89
+
90
+ 다음 API는 **main과 현재 웹 편집기에 먼저 반영되어 있으며, npm 0.0.10에는 없다.**
91
+ 배포 여부는 [변경 이력](https://github.com/eddmpython/hanlint/blob/main/CHANGELOG.md)에서 확인한다.
92
+
93
+ | API | 결과 |
94
+ |---|---|
95
+ | `inspectText(text, config)` | 지적, 문체 본보기, 승인 고침 후보와 지문 |
96
+ | `learnText(before, after, config)` | 사람이 확인하고 승인할 문장 고침과 표면 치환 후보 |
97
+ | `compareRevision(before, after, config)` | 자유 원고의 숫자, 링크, 코드와 H2 제목 변화 |
98
+
99
+ 브라우저에서 코어를 실행하려면 사전과 모듈을 함께 조립한다.
100
+ 저장소의 [정적 사이트 빌드 절차](https://github.com/eddmpython/hanlint/blob/main/skills/specs/operation/webEditor.md)를 따른다.
101
+
102
+ Python의 `hanlint learn 전.md 승인본.md --format toml`로 만든 후보는 사람이 검토한 뒤
103
+ `hanlint.toml`의 `[[patches]]`와 `[[operations]]`로 승인할 수 있다. 두 판이 같은 설정을 읽는다.
104
+ 원문 일치와 문맥 조건을 통과한 고침만 제안하며, 모호한 곳에서는 기권한다.
105
+ 보호할 고유명사와 프로젝트 용어는 `protectedTerms`에 넣는다.
106
+ 후보 생성, 개인 승인과 공통 규칙 반영은 별도 단계다.
107
+
108
+ ## 자동화와 지원 범위
109
+
110
+ GitHub Actions, pre-commit과 Claude Code 훅의 전체 설정은
111
+ [자동화 연결 안내](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/integrations.md)에 있다.
112
+ 훅은 후속 작업에 지적을 전달하며, CI의 종료 코드 검사와는 목적이 다르다.
113
+
114
+ Python 판과 공유하는 검사는 같은 사전과 적합성 자료로 검증한다.
115
+ `audit`, `map`, `diff`, `learn`, `profile`, `coverage` 등 분석·실험 명령은 Python 쪽에 있다.
116
+ 전체 명령의 구분은 [명령과 설정](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/cli.md)을 참고한다.
117
+
118
+ 한린트는 모든 맞춤법이나 글의 품질을 판정하지 않는다. 지적이 없어도 좋은 글이라는 보증은 아니다.
119
+ 오탐과 수정 사례는 [GitHub Issues](https://github.com/eddmpython/hanlint/issues/new/choose)로 받는다.
46
120
 
47
121
  ## 라이선스
48
122
 
49
- 코드와 나머지 데이터는 [MIT](LICENSE)다. `data/easyWords.json`은 국립국어원 자료의 파생물이며
50
- [공공누리 제1유형 고지](koglType1.LICENSE.md)가 적용된다.
123
+ 코드는 [MIT](https://github.com/eddmpython/hanlint/blob/main/LICENSE)다.
124
+ 쉬운 말 자료에는 [공공누리 제1유형 고지](https://github.com/eddmpython/hanlint/blob/main/npm/koglType1.LICENSE.md)가 적용된다.
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/checkResult.schema.json",
4
+ "title": "hanlint Check Result",
5
+ "description": "A deterministic receipt for a Reader Contract and a prose draft.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "version",
10
+ "kind",
11
+ "violationCount",
12
+ "contractSha256",
13
+ "draftSha256",
14
+ "surface",
15
+ "lint",
16
+ "meaning"
17
+ ],
18
+ "properties": {
19
+ "version": { "const": 1 },
20
+ "kind": { "const": "hanlint.checkResult" },
21
+ "violationCount": { "type": "integer", "minimum": 0 },
22
+ "contractSha256": { "$ref": "#/$defs/sha256" },
23
+ "draftSha256": { "$ref": "#/$defs/sha256" },
24
+ "surface": {
25
+ "type": "object",
26
+ "additionalProperties": false,
27
+ "required": [
28
+ "missingNumbers",
29
+ "unexpectedNumbers",
30
+ "missingUrls",
31
+ "unexpectedUrls",
32
+ "missingCode",
33
+ "unexpectedCode",
34
+ "missingLinks",
35
+ "unexpectedLinks"
36
+ ],
37
+ "properties": {
38
+ "missingNumbers": { "$ref": "#/$defs/strings" },
39
+ "unexpectedNumbers": { "$ref": "#/$defs/strings" },
40
+ "missingUrls": { "$ref": "#/$defs/strings" },
41
+ "unexpectedUrls": { "$ref": "#/$defs/strings" },
42
+ "missingCode": { "$ref": "#/$defs/strings" },
43
+ "unexpectedCode": { "$ref": "#/$defs/strings" },
44
+ "missingLinks": { "$ref": "#/$defs/strings" },
45
+ "unexpectedLinks": { "$ref": "#/$defs/strings" }
46
+ }
47
+ },
48
+ "lint": {
49
+ "type": "object",
50
+ "additionalProperties": false,
51
+ "required": ["errorCount", "noticeCount", "errorRules", "items"],
52
+ "properties": {
53
+ "errorCount": { "type": "integer", "minimum": 0 },
54
+ "noticeCount": { "type": "integer", "minimum": 0 },
55
+ "errorRules": {
56
+ "type": "object",
57
+ "additionalProperties": { "type": "integer", "minimum": 1 }
58
+ },
59
+ "items": {
60
+ "type": "array",
61
+ "items": { "$ref": "finding.schema.json" }
62
+ }
63
+ }
64
+ },
65
+ "meaning": { "type": "string", "minLength": 1 }
66
+ },
67
+ "$defs": {
68
+ "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
69
+ "strings": {
70
+ "type": "array",
71
+ "uniqueItems": true,
72
+ "items": { "type": "string", "minLength": 1 }
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,98 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/checkResultV2.schema.json",
4
+ "title": "hanlint Check Result v2",
5
+ "description": "A deterministic receipt for protected surface atoms, an exact heading outline, document shape, and lint findings.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "version",
10
+ "kind",
11
+ "violationCount",
12
+ "contractSha256",
13
+ "draftSha256",
14
+ "surface",
15
+ "missingFacts",
16
+ "lint",
17
+ "meaning",
18
+ "outline",
19
+ "document"
20
+ ],
21
+ "properties": {
22
+ "version": { "const": 2 },
23
+ "kind": { "const": "hanlint.checkResult" },
24
+ "violationCount": { "type": "integer", "minimum": 0 },
25
+ "contractSha256": { "$ref": "#/$defs/sha256" },
26
+ "draftSha256": { "$ref": "#/$defs/sha256" },
27
+ "missingFacts": { "type": "array", "items": { "type": "string" } },
28
+ "surface": { "$ref": "checkResult.schema.json#/properties/surface" },
29
+ "lint": { "$ref": "checkResult.schema.json#/properties/lint" },
30
+ "meaning": { "type": "string", "minLength": 1 },
31
+ "outline": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "required": ["matches", "level", "expected", "actual", "mismatches"],
35
+ "properties": {
36
+ "matches": { "type": "boolean" },
37
+ "level": { "type": "integer", "minimum": 1, "maximum": 6 },
38
+ "expected": { "$ref": "#/$defs/strings" },
39
+ "actual": { "$ref": "#/$defs/strings" },
40
+ "mismatches": {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": ["position", "expected", "actual"],
46
+ "properties": {
47
+ "position": { "type": "integer", "minimum": 1 },
48
+ "expected": { "type": ["string", "null"] },
49
+ "actual": { "type": ["string", "null"] }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ },
55
+ "document": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": [
59
+ "sentenceCount",
60
+ "paragraphCount",
61
+ "sectionCount",
62
+ "wordCount",
63
+ "questionCount",
64
+ "readerCallCount",
65
+ "sections"
66
+ ],
67
+ "properties": {
68
+ "sentenceCount": { "type": "integer", "minimum": 0 },
69
+ "paragraphCount": { "type": "integer", "minimum": 0 },
70
+ "sectionCount": { "type": "integer", "minimum": 0 },
71
+ "wordCount": { "type": "integer", "minimum": 0 },
72
+ "questionCount": { "type": "integer", "minimum": 0 },
73
+ "readerCallCount": { "type": "integer", "minimum": 0 },
74
+ "sections": {
75
+ "type": "array",
76
+ "items": {
77
+ "type": "object",
78
+ "additionalProperties": false,
79
+ "required": ["heading", "line", "paragraphCount", "codeBlockCount"],
80
+ "properties": {
81
+ "heading": { "type": "string" },
82
+ "line": { "type": "integer", "minimum": 1 },
83
+ "paragraphCount": { "type": "integer", "minimum": 0 },
84
+ "codeBlockCount": { "type": "integer", "minimum": 0 }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ },
91
+ "$defs": {
92
+ "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
93
+ "strings": {
94
+ "type": "array",
95
+ "items": { "type": "string" }
96
+ }
97
+ }
98
+ }
@@ -318,6 +318,105 @@
318
318
  "before": "모든 분야에 있어서 기준이 필요합니다.",
319
319
  "after": "모든 분야에서 기준이 필요합니다.",
320
320
  "moved": "번역투를 우리말 어순으로 되돌렸다"
321
+ },
322
+ {
323
+ "rule": "screenSentence",
324
+ "before": "요청을 완료하지 못했습니다",
325
+ "after": "요청 실패",
326
+ "moved": "종결어미를 떼고 실패를 낱말로 적었다"
327
+ },
328
+ {
329
+ "rule": "screenNarration",
330
+ "before": "계약 신청이 도착하면 여기에 표시됩니다.",
331
+ "after": "계약 없음",
332
+ "moved": "예고 문장을 빈 상태의 낱말로 바꿨다"
333
+ },
334
+ {
335
+ "rule": "screenTone",
336
+ "before": "관리하실 수임처를 체크해 주시면 그 회사의 자료만 가져옵니다.",
337
+ "after": "관리할 수임처",
338
+ "moved": "접객 말투의 안내문을 칸의 이름으로 줄였다"
339
+ },
340
+ {
341
+ "rule": "dash",
342
+ "presets": [
343
+ "screen"
344
+ ],
345
+ "before": "설정 {em} 저장",
346
+ "after": "설정 저장",
347
+ "moved": "긴 줄표를 지우고 낱말만 남겼다"
348
+ },
349
+ {
350
+ "rule": "spelling",
351
+ "presets": [
352
+ "screen"
353
+ ],
354
+ "before": "몇일 남음",
355
+ "after": "며칠 남음",
356
+ "moved": "표기를 바로잡았다"
357
+ },
358
+ {
359
+ "rule": "spacing",
360
+ "presets": [
361
+ "screen"
362
+ ],
363
+ "before": "저장할수 없음",
364
+ "after": "저장할 수 없음",
365
+ "moved": "띄어쓰기를 바로잡았다"
366
+ },
367
+ {
368
+ "rule": "confusable",
369
+ "presets": [
370
+ "screen"
371
+ ],
372
+ "before": "회원으로써 신청",
373
+ "after": "회원으로서 신청",
374
+ "moved": "자격을 뜻하는 조사로 바로잡았다"
375
+ },
376
+ {
377
+ "rule": "hardWord",
378
+ "presets": [
379
+ "screen"
380
+ ],
381
+ "before": "금일 수집",
382
+ "after": "오늘 수집",
383
+ "moved": "어려운 한자어를 쉬운 말로 바꿨다"
384
+ },
385
+ {
386
+ "rule": "japaneseLoan",
387
+ "presets": [
388
+ "screen"
389
+ ],
390
+ "before": "잔고 조회",
391
+ "after": "잔액 조회",
392
+ "moved": "일본어투 낱말을 바꿨다"
393
+ },
394
+ {
395
+ "rule": "doublePassive",
396
+ "presets": [
397
+ "screen"
398
+ ],
399
+ "before": "보여지는 항목",
400
+ "after": "보이는 항목",
401
+ "moved": "피동을 하나로 줄였다"
402
+ },
403
+ {
404
+ "rule": "translationese",
405
+ "presets": [
406
+ "screen"
407
+ ],
408
+ "before": "승인에 있어서 필요한 값",
409
+ "after": "승인에 필요한 값",
410
+ "moved": "번역투를 지웠다"
411
+ },
412
+ {
413
+ "rule": "redundantPair",
414
+ "presets": [
415
+ "screen"
416
+ ],
417
+ "before": "매 시간마다 확인",
418
+ "after": "매시간 확인",
419
+ "moved": "겹말을 하나로 줄였다"
321
420
  }
322
421
  ]
323
422
  }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/finding.schema.json",
4
+ "title": "hanlint Finding",
5
+ "description": "A deterministic prose issue with its location, reason, and optional local repair data.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["rule", "line", "severity", "scope", "at", "quote", "why"],
9
+ "properties": {
10
+ "rule": { "type": "string", "minLength": 1 },
11
+ "line": { "type": "integer", "minimum": 1 },
12
+ "severity": { "enum": ["error", "notice"] },
13
+ "scope": { "enum": ["sentence", "paragraph", "section", "document"] },
14
+ "at": { "type": "integer", "minimum": -1 },
15
+ "quote": { "type": "string", "minLength": 1 },
16
+ "why": { "type": "string", "minLength": 1 },
17
+ "fix": { "type": "string", "minLength": 1 },
18
+ "fragment": { "type": ["string", "null"] },
19
+ "replacement": { "type": "string" },
20
+ "candidates": {
21
+ "type": "array",
22
+ "minItems": 1,
23
+ "items": {
24
+ "type": "object",
25
+ "additionalProperties": false,
26
+ "required": ["text", "why"],
27
+ "properties": {
28
+ "text": { "type": "string", "minLength": 1 },
29
+ "why": { "type": "string", "minLength": 1 }
30
+ }
31
+ }
32
+ }
33
+ },
34
+ "dependentRequired": {
35
+ "fragment": ["replacement"],
36
+ "replacement": ["fragment"]
37
+ }
38
+ }
@@ -128,6 +128,18 @@
128
128
  "dash",
129
129
  "imperativePeriod"
130
130
  ]
131
+ },
132
+ {
133
+ "id": "screen",
134
+ "name": "화면",
135
+ "symbol": "U",
136
+ "ansi": 178,
137
+ "hex": "#E69F00",
138
+ "rules": [
139
+ "screenSentence",
140
+ "screenNarration",
141
+ "screenTone"
142
+ ]
131
143
  }
132
144
  ]
133
145
  }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/patch.schema.json",
4
+ "title": "hanlint Patch",
5
+ "description": "An exact local replacement connected to a named existing violation.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["reason", "before", "after"],
9
+ "properties": {
10
+ "reason": { "type": "string", "minLength": 1 },
11
+ "before": { "type": "string", "minLength": 1 },
12
+ "after": { "type": "string", "minLength": 1 }
13
+ }
14
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://hanlint.dev/schemas/patchResult.schema.json",
4
+ "title": "hanlint Patch Result",
5
+ "description": "A deterministic verification receipt for an exact prose Patch.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "version",
10
+ "kind",
11
+ "verified",
12
+ "violationCount",
13
+ "contractSha256",
14
+ "sourceSha256",
15
+ "patchSha256",
16
+ "resultSha256",
17
+ "matchCount",
18
+ "reason",
19
+ "newSurfaceIssues",
20
+ "newErrors",
21
+ "meaning"
22
+ ],
23
+ "properties": {
24
+ "version": { "const": 1 },
25
+ "kind": { "const": "hanlint.patchResult" },
26
+ "verified": { "type": "boolean" },
27
+ "violationCount": { "type": "integer", "minimum": 0 },
28
+ "contractSha256": { "$ref": "#/$defs/sha256" },
29
+ "sourceSha256": { "$ref": "#/$defs/sha256" },
30
+ "patchSha256": { "$ref": "#/$defs/sha256" },
31
+ "resultSha256": {
32
+ "oneOf": [
33
+ { "$ref": "#/$defs/sha256" },
34
+ { "type": "null" }
35
+ ]
36
+ },
37
+ "matchCount": { "type": "integer", "minimum": 0 },
38
+ "reason": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "required": ["name", "before", "after", "reduced"],
42
+ "properties": {
43
+ "name": { "type": "string", "minLength": 1 },
44
+ "before": { "type": "integer", "minimum": 0 },
45
+ "after": {
46
+ "oneOf": [
47
+ { "type": "integer", "minimum": 0 },
48
+ { "type": "null" }
49
+ ]
50
+ },
51
+ "reduced": { "type": "boolean" }
52
+ }
53
+ },
54
+ "newSurfaceIssues": {
55
+ "type": "array",
56
+ "items": {
57
+ "type": "object",
58
+ "additionalProperties": false,
59
+ "required": ["kind", "value"],
60
+ "properties": {
61
+ "kind": {
62
+ "enum": [
63
+ "missingNumbers",
64
+ "unexpectedNumbers",
65
+ "missingUrls",
66
+ "unexpectedUrls",
67
+ "missingCode",
68
+ "unexpectedCode",
69
+ "missingLinks",
70
+ "unexpectedLinks"
71
+ ]
72
+ },
73
+ "value": { "type": "string", "minLength": 1 }
74
+ }
75
+ }
76
+ },
77
+ "newErrors": {
78
+ "type": "array",
79
+ "items": { "$ref": "finding.schema.json" }
80
+ },
81
+ "meaning": { "type": "string", "minLength": 1 }
82
+ },
83
+ "$defs": {
84
+ "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
85
+ }
86
+ }