hanlint 0.0.10 → 0.0.13

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.
package/README.md CHANGED
@@ -1,85 +1,125 @@
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
+ npx hanlint sheet src/ --preset screen
18
+ ```
19
+
20
+ 인자 없이 실행하면 현재 폴더의 파일을 바탕으로 시작 안내를 보여 준다.
21
+ 파일 대신 폴더를 주면 아래의 마크다운 파일을 찾는다. 표준 입력은 `npx hanlint - --path 글.md`로 받는다.
22
+
23
+ ```sh
10
24
  npx hanlint fix 글.md
11
- npx hanlint 글.md --format compact --errors-only
12
- npx hanlint spec --preset blog --register 합니다 --chars 800
13
- npx hanlint hook
14
- npx hanlint hook --reply
15
- npx hanlint contract init 초안.md --reader "배포를 결정할 운영자" --goal "예산을 확인한다"
16
- npx hanlint contract init 초안.md --reader "개발자" --goal "섹션별로 비교한다" --outline h2
17
- npx hanlint check contract.json 초안.md --format text
18
- npx hanlint verify-patch contract.json 초안.md patch.json
25
+ npx hanlint 글.md
19
26
  ```
20
27
 
21
- 인자 없이 치면 첫 화면이 나온다. 이 폴더의 마크다운 이름으로 만든 예시와 지금 칠 수 있는 명령이 거기 있다.
22
- 파일 자리에 폴더를 주면 그 아래 마크다운을 전부 찾는다.
28
+ `fix`는 고친 표기가 확정된 자리를 **파일에 직접 반영**한다. 변경을 확인하고 다시 검사한다.
29
+ 일반 검사는 error가 없으면 0, 있으면 1, 입력이나 설정이 잘못되면 2로 끝난다.
30
+ notice만 있으면 0이며, `--errors-only`를 주면 notice 표시를 제외한다.
31
+ 명령마다 다른 판정은 [종료 코드 안내](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/cli.md#종료-코드)를 따른다.
23
32
 
24
- 지적마다 규칙 이름, 줄 번호, 인용 문장, 왜 문제인지가 붙고 마지막 줄이 다음에 무엇을 하면 되는지 말한다.
25
- 고친 표기가 확정된 자리는 `fix` 가 원문에 적용한다. `--format json` 은 기계가 읽는 꼴이고 종료 코드는
26
- 지적이 없으면 0, error 가 있으면 1 이라 발행 게이트에 그대로 물린다. stdin 은 `npx hanlint - --path 이름.md`
27
- 로 받는다. 규칙 목록은 `npx hanlint rules`, 규칙의 기술서는 `npx hanlint explain <규칙>`, 지금 어느 설정으로
28
- 도는지는 `npx hanlint doctor` 다.
33
+ ## JavaScript에서 검사하기
29
34
 
30
- 파이썬 판의 `hanlint learn 전.md 승인본.md --format toml`로 승인한 `[[patches]]`를 같은
31
- `hanlint.toml`에 두면 npm 판도 같은 패치를 읽는다. 정규화한 원문, 규칙, 프리셋, 국소 표지, 독자 상태가
32
- 모두 같은 유일한 패치만 JSON 지적의 `patch`로 내고, 원문이 다르면 유사 문장이라도 기권한다.
35
+ ```sh
36
+ npm install hanlint
37
+ ```
38
+
39
+ ```js
40
+ import { configFromMapping, lintText } from "hanlint";
41
+
42
+ const text = "이 도구는 다양한 기능을 제공합니다.";
43
+ const config = configFromMapping({ preset: "docs" });
44
+ for (const finding of lintText(text, config)) {
45
+ console.log(finding.line, finding.rule, finding.why);
46
+ }
47
+ ```
33
48
 
34
- 같은 `learn` 출력에서 뜻과 적용 범위를 확인한 `[[operations]]`도 두 판이 함께 읽는다. 승인 조각이 현재
35
- 문장의 단어 경계 한 자리에만 있고 숫자, URL, 식별자, 경로, 코드, 링크 목적지가 그대로일 때 파일 JSON의
36
- `operations[].operation.result`를 낸다. 한국어 고유명사와 프로젝트 용어는 `protectedTerms`에 적어 잠근다.
37
- 지시어와 의미 고침, 여러 자리 일치는 기권하며 원문 완전 일치 패치와 확정 fix가 먼저다.
49
+ 파일은 `lintFile("글.md", config)`로 검사한다. `loadConfig()`는 현재 위치에서 프로젝트 설정을 찾는다.
50
+ 파일과 설정 경로를 읽는 API는 Node 전용이다. 문자열 API에 설정을 생략하면 기본 설정을 쓴다.
38
51
 
39
- 글의 종류가 블로그가 아니면 프리셋을 먼저 고른다. `npx hanlint init --preset docs` 가 참고 문서에 맞지 않는
40
- 규칙을 끈 설정 파일을 만든다. `blog`, `report`, `docs`, `guide`, `essay`, `fiction`, `encyclopedia` 일곱이고
41
- `npx hanlint rules` 가 지금 도는 목록을 낸다.
52
+ `blog`, `report`, `docs`, `guide`, `essay`, `fiction`, `encyclopedia`, `chat`, `screen`의 아홉 프리셋이 있다.
53
+ `screen`은 화면의 글 (단추, 이름표, 상태, 빈 상태) 이다. 낱말 자리의 문장과 화면 해설, 접객 말투를 잡고 산문 규칙은 끈다.
54
+ 프로젝트 설정은 `npx hanlint init --preset docs`로 만들고 `npx hanlint doctor`로 확인한다.
55
+ 규칙 목록은 `npx hanlint rules`, 개별 규칙의 근거는 `npx hanlint explain translationese`에서 본다.
42
56
 
43
- 글을 쓰기 전에는 `npx hanlint spec --preset <종류> --chars <글자 수>`로 같은 규칙판에서 숫자 사양을 받는다.
44
- 배포 프로파일에서 관찰한 값과 현재 켜진 규칙 임계를 함께 낸다. JSON의 `rows[].basis`가 각 수의 정본을 밝힌다.
45
- 새 점수나 품질 판정은 만들지 않는다. `chat`은 견줄 프로파일이 없어 사양을 내지 않는다.
57
+ ## 수정하면서 원문 보호하기
46
58
 
47
- Claude Code의 PostToolUse JSON을 stdin으로 `npx hanlint hook`에 연결하면 `Edit|Write`가 저장한 마크다운
48
- 한 파일을 검사한다. Finding이 있을 때만 다음 모델 요청용 `additionalContext` JSON을 내며 언제나 종료 코드
49
- 0이라 쓰기를 막지 않는다. Stop에는 `npx hanlint hook --reply`를 연결한다. 마지막 답변을 `chat` 프리셋으로
50
- 한 번 검사하고 `stop_hook_active` 재진입에서는 침묵한다. 전체 `.claude/settings.json` 예시는 루트 README에 있다.
59
+ Reader Contract는 사람이 확인한 독자, 목적과 사실을 받아 숫자, URL, 인라인 코드와 링크 목적지를 보호한다.
51
60
 
52
61
  ```js
53
- import { Contract, Patch, check, contractFromText, contractFromTextV2, lintFile, renderCheck, verifyPatch } from "hanlint";
62
+ import { Contract, Patch, check, renderCheck, verifyPatch } from "hanlint";
54
63
 
55
- for (const finding of lintFile("글.md")) console.log(finding.line, finding.rule, finding.why);
64
+ const text = "예산은 400,000원이다.";
65
+ const contract = new Contract(
66
+ "배포를 결정할 운영자",
67
+ "예산을 확인한다",
68
+ ["예산은 380,000원이다."],
69
+ );
70
+ console.log(renderCheck(check(text, contract)));
56
71
 
57
- const draftContract = contractFromText(text, "배포를 결정할 운영자", "예산을 확인한다");
58
- const structuredContract = contractFromTextV2(text, "개발자", "섹션별로 비교한다", 2);
59
- const contract = new Contract("배포를 결정할 운영자", "예산을 확인한다", ["예산은 380,000원이다."]);
60
- const receipt = check(text, contract);
61
- console.log(renderCheck(check(text, structuredContract)));
62
72
  const patch = new Patch("unexpectedNumbers", "400,000", "380,000");
63
- const verified = verifyPatch(text, patch, contract);
73
+ const result = verifyPatch(text, patch, contract);
74
+ console.log(result);
64
75
  ```
65
76
 
66
- Reader Contract는 `reader`, `goal`, `facts`에서 숫자, URL, 인라인 코드와 링크 목적지를 자동으로 보호한다.
67
- `contractFromText`와 `contract init`은 보호 원자를 많이 덮는 원문 줄부터 골라 facts 후보를 줄인다.
68
- 사실의 진실과 보호 원자가 없는 의미는 추측하지 않으므로 사람이 초안을 확인한다.
69
- 제목 수와 순서가 요구사항이면 `contractFromTextV2` 또는 `contract init --outline h2`를 쓴다. version 2는
70
- 사람이 승인한 facts, 자동으로 모은 surface, 한 수준의 정확한 outline을 분리한다. `check --format text`는
71
- 보호 원자, 제목 구조, 전체 절 제목, lint와 다음 행동을 한 화면에 보여 준다.
72
- check 결과는 Contract와 초안 해시, 보호 원자 차이와 기존 Finding을 담는다. Patch는 원문 한 자리에 정확히
73
- 맞고 명시한 기존 위반을 줄이며 새 보호 원자 위반과 새 error를 만들지 않을 때만 검증된다. 이 조건은 의미나
74
- 진실, 자연스러움의 승인이 아니다. version 2 Patch는 새 outline 위반도 거부한다. Python과 npm은 배포물의
75
- 같은 version 1 적합성 JSON을 독립 실행하고 version 2 결과도 동등성 게이트로 견준다.
76
-
77
- 파이썬 패키지 (`pip install hanlint`) 와 같은 규칙, 같은 fixture, 같은 출력이다. 지문 지도 (`audit`, `map`),
78
- 문체 프로파일, 초안 비교 (`diff`), 평가자 겹침 (`coverage`) 은 파이썬 쪽에만 있다. 무엇을 잡고 무엇을 잡지 않는지는
79
- [전체 사용 안내](https://github.com/eddmpython/hanlint#readme)와
80
- [제품 경계](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/product.md)에서 확인할 수 있다.
77
+ `contractFromText(text, reader, goal)`은 원문에서 사실 후보를 모은다. 사람이 확인한 뒤 사용한다.
78
+ `contractFromTextV2(text, reader, goal, 2)`는 H2 제목의 수와 순서도 보호할 계약을 만든다.
79
+ 패치는 원문 한 자리에 정확히 맞고 기존 위반을 줄이며 새 보호 위반이나 error를 만들지 않아야 검증된다.
80
+ 사실의 진실, 의미 보존과 자연스러움은 사람이 판단한다.
81
+
82
+ JSON 형식과 전체 조건은
83
+ [Reader Contract](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/readerContract.md)가 정본이다.
84
+
85
+ ## 브라우저와 수정 사례
86
+
87
+ [한린트 편집기](https://eddmpython.github.io/hanlint/)는 같은 npm 코어로 검사, 고침,
88
+ 원문 비교와 개인 기록을 제공한다. 사용 방법은
89
+ [편집기 안내](https://eddmpython.github.io/hanlint/guide.html)에서 확인한다.
90
+
91
+ 다음 API는 **main과 현재 웹 편집기에 먼저 반영되어 있으며, npm 0.0.10에는 없다.**
92
+ 배포 여부는 [변경 이력](https://github.com/eddmpython/hanlint/blob/main/CHANGELOG.md)에서 확인한다.
93
+
94
+ | API | 결과 |
95
+ |---|---|
96
+ | `inspectText(text, config)` | 지적, 문체 본보기, 승인 고침 후보와 지문 |
97
+ | `learnText(before, after, config)` | 사람이 확인하고 승인할 문장 고침과 표면 치환 후보 |
98
+ | `compareRevision(before, after, config)` | 자유 원고의 숫자, 링크, 코드와 H2 제목 변화 |
99
+
100
+ 브라우저에서 코어를 실행하려면 사전과 모듈을 함께 조립한다.
101
+ 저장소의 [정적 사이트 빌드 절차](https://github.com/eddmpython/hanlint/blob/main/skills/specs/operation/webEditor.md)를 따른다.
102
+
103
+ Python의 `hanlint learn 전.md 승인본.md --format toml`로 만든 후보는 사람이 검토한 뒤
104
+ `hanlint.toml`의 `[[patches]]`와 `[[operations]]`로 승인할 수 있다. 두 판이 같은 설정을 읽는다.
105
+ 원문 일치와 문맥 조건을 통과한 고침만 제안하며, 모호한 곳에서는 기권한다.
106
+ 보호할 고유명사와 프로젝트 용어는 `protectedTerms`에 넣는다.
107
+ 후보 생성, 개인 승인과 공통 규칙 반영은 별도 단계다.
108
+
109
+ ## 자동화와 지원 범위
110
+
111
+ GitHub Actions, pre-commit과 Claude Code 훅의 전체 설정은
112
+ [자동화 연결 안내](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/integrations.md)에 있다.
113
+ 훅은 후속 작업에 지적을 전달하며, CI의 종료 코드 검사와는 목적이 다르다.
114
+
115
+ Python 판과 공유하는 검사는 같은 사전과 적합성 자료로 검증한다.
116
+ `audit`, `map`, `diff`, `learn`, `profile`, `coverage` 등 분석·실험 명령은 Python 쪽에 있다.
117
+ 전체 명령의 구분은 [명령과 설정](https://github.com/eddmpython/hanlint/blob/main/skills/specs/start/cli.md)을 참고한다.
118
+
119
+ 한린트는 모든 맞춤법이나 글의 품질을 판정하지 않는다. 지적이 없어도 좋은 글이라는 보증은 아니다.
120
+ 오탐과 수정 사례는 [GitHub Issues](https://github.com/eddmpython/hanlint/issues/new/choose)로 받는다.
81
121
 
82
122
  ## 라이선스
83
123
 
84
- 코드와 나머지 데이터는 [MIT](LICENSE)다. `data/easyWords.json`은 국립국어원 자료의 파생물이며
85
- [공공누리 제1유형 고지](koglType1.LICENSE.md)가 적용된다.
124
+ 코드는 [MIT](https://github.com/eddmpython/hanlint/blob/main/LICENSE)다.
125
+ 쉬운 말 자료에는 [공공누리 제1유형 고지](https://github.com/eddmpython/hanlint/blob/main/npm/koglType1.LICENSE.md)가 적용된다.
@@ -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
  }
@@ -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
  }
@@ -44,6 +44,9 @@
44
44
  "platformApi": "code",
45
45
  "promiseRecall": "document",
46
46
  "redundantPair": "sentence",
47
+ "screenNarration": "screen",
48
+ "screenSentence": "screen",
49
+ "screenTone": "screen",
47
50
  "sectionNoProse": "structure",
48
51
  "spacing": "orthography",
49
52
  "spelling": "orthography",
@@ -44,6 +44,9 @@
44
44
  "platformApi": "한 운영체제에서만 도는 API 나 경로를 쓴 코드.\n\n왜: 다른 운영체제 독자는 답까지 다 구해 놓고 그 줄에서 AttributeError 로 멈춘다. 글쓴이 컴퓨터에서는 돌아서\n 글쓴이는 모른다.\n어디서: 실측. 블로그 004 의 peak_wset 이 윈도우에서만 나와 사람 평가자가 집었다. 목록은 data/platformApis.txt.\n고치기: 그 줄이 어느 운영체제 전용인지 적고 대안을 준다. 또는 이식 가능한 API 로 바꾼다.\n안 잡는 것: 목록에 없는 API. 산문 안의 언급. notice 로만 낸다.",
45
45
  "promiseRecall": "뒤에서 다루겠습니다 라고 미뤄 놓고 앞에서 미룬 것을 부르는 표지가 글 어디에도 없는 것.\n\n왜: 뒤에서 다루겠습니다 를 써 놓고 끝까지 안 나오면 정보 배분이 아니라 누락이다. 회수할 때는 미뤘다는\n 사실을 밝히고 앞에서 쓴 이름을 다시 부른다.\n어디서: 글쓰기 스킬의 무엇을 빼는가 (미룬 것은 같은 글 안에서 회수한다). 이런 검사를 하는 선행 도구가\n 없다 (조사 결과). 표지 목록은 data/promiseMarkers.txt 와 recallMarkers.txt. 약속과 회수는 독자 상태\n (fingerprint/readerState.py) 가 문장 순서로 쌓고, 글을 다 읽은 독자 (final) 에게 묻는다.\n고치기: 뒤 절에서 앞에서 미룬 X 로 부르며 다룬다. 회수하지 못할 것은 애초에 미루지 말고 지운다.\n안 잡는 것: 회수 표지가 하나라도 있는 글. 어느 약속을 어느 회수가 갚았는지는 대조하지 않는다.\n 표지 근사라 notice 로만 낸다.",
46
46
  "redundantPair": "역전 앞, 그때 당시, 매 시간마다 같은 겹말.\n\n왜: 뜻이 같은 두 말을 겹쳐 쓴 것이라 한 쪽이 군더더기다.\n어디서: 국립국어원 새국어생활 2005 한규희 겹말은 가능한 한 줄이자, 법조신문 넘쳐나는 겹말들.\n 사전은 data/redundantPair.toml.\n고치기: 하나만 남긴다. 역전 앞 은 역 앞, 그때 당시 는 그때.\n안 잡는 것: 뜻을 강조하려고 일부러 겹친 말. 사전에 없는 겹말.",
47
+ "screenNarration": "화면이 자기를 해설하는 말. 빈 자리의 예고, 이 화면은, 잠시 뒤 다시, 할 수 있습니다.\n\n왜: 빈 상태에 \"오면 여기에 표시됩니다\" 를 두면 사용자는 없는 것을 읽는다. \"이 화면은 비밀번호로 엽니다\" 는 제목이\n 이미 한 말이다. \"잠시 뒤 다시 시도해 주세요\" 는 실패를 감싸는 위로이고 다시 시도는 단추다. \"볼 수 있습니다\" 는\n 단추와 칸이 이미 보이는 능력을 글로 되풀이한다. 넷 다 정보가 아니라 해설이다.\n어디서: Taxly 화면 낱말 규약 (2026-09-17, 운영자 지시). 실측: 운영 화면 \"계약 신청이 도착하면 여기에 표시됩니다\",\n \"내려받기 페이지에서 요청이 오면 여기에 표시됩니다\", \"이 화면은 비밀번호로 엽니다. 비밀번호는 이 탭에서만 기억합니다\",\n 수집 화면 \"진행 상태를 갱신하지 못했습니다. 저장된 자료는 유지되며 잠시 뒤 다시 확인합니다\".\n 사전은 data/screenNarration.toml 이고 설정의 dictionary.screenNarration 으로 더한다.\n고치기: 빈 상태는 `<대상> 없음` (요청 없음, 계약 없음). 자기 설명과 능력 서술은 지운다. 재시도 서술은\n `<대상> <동작> 실패` 로 줄이고 다시 시도 단추를 둔다. 문장이 허용된 글에서도 같은 말은 사족이라\n enforceStyle 로 켜서 잰다.\n안 잡는 것: 사전에 없는 해설. \"이 컴퓨터\", \"이 회사\" 처럼 화면이 아닌 것을 가리키는 지시어. 프리셋 screen 밖에서는\n enforceStyle 에 넣어야 돈다.",
48
+ "screenSentence": "낱말 자리에 놓인 문장. 진행, 실패, 요구, 완료를 종결어미로 쓴 화면의 글.\n\n왜: 화면의 글은 데이터의 이름과 사용자 동작의 낱말이다. 빈 상태는 `<대상> 없음`, 진행은 `<동작> 중`, 실패는\n `<대상> <동작> 실패`, 요구는 `<대상> 필요`, 끝난 일은 상태 한 낱말 (승인됨) 이다. 문장은 제목이 이미 말한 것을\n 되풀이하거나 화면을 해설하고, 사용자는 낱말을 읽고 문장은 건너뛴다.\n어디서: Taxly 화면 낱말 규약 (2026-09-17, 운영자 지시). 실측: 운영 화면 \"요청을 완료하지 못했습니다\", 설치 페이지\n \"상태를 확인하지 못했습니다.\", 온보딩 \"대시보드를 열고 있습니다\", 진단 \"자료 갱신을 위한 로그인이 필요합니다\".\n 사전은 data/screenSentence.toml 이고 설정의 dictionary.screenSentence 로 더한다.\n고치기: 종결어미를 떼고 낱말로 만든다. 요청을 완료하지 못했습니다 는 요청 실패, 대시보드를 열고 있습니다 는\n 대시보드 여는 중, 로그인이 필요합니다 는 로그인 필요, 변경을 완료했습니다 는 변경됨. 문장이 정말 필요한 글\n (장표, 고지, 실패 원인) 은 문장이 허용된 글 모듈로 옮기고 chat 프리셋으로 잰다.\n안 잡는 것: 종결어미가 없는 글. 프리셋 screen 에서만 켜진다. 산문 프리셋은 문장이 정상이라 끈다. 문장 하나에\n 무늬가 여럿이면 가장 앞의 것 하나만 짚는다.",
49
+ "screenTone": "접객 말투. 언제든, 안심, 살펴보세요, 하시면, 보여드립니다.\n\n왜: 화면은 손님을 맞는 점원이 아니다. 안심시키는 말 (언제든 바꿀 수 있습니다) 은 정보가 없고, 초대하는 말\n (준비된 회사부터 살펴보세요) 은 단추가 할 일을 글로 대신하며, 높이는 말 (로그인하시면 보여드립니다) 은 낱말이어야\n 할 자리를 문장으로 늘린다. 사용자는 그 말을 읽지 않고 건너뛰거나, 읽고 나서 화면을 못 믿는다.\n어디서: Taxly 화면 낱말 규약 (2026-09-17, 운영자 지시). 실측: 온보딩 \"설정에서 언제든 지울 수 있습니다\",\n \"위하고에 로그인하시면 담당 수임처를 찾아 보여드립니다\", \"이렇게 부르겠습니다\", 수집 화면 \"준비된 회사부터 바로\n 살펴보세요\". 사전은 data/screenTone.toml 이고 설정의 dictionary.screenTone 으로 더한다.\n고치기: 지운다. 그 자리에 정보가 있었으면 낱말로 남긴다 (적지 않으셔도 됩니다 는 선택, 이렇게 부르겠습니다 는\n 호칭 미리보기). 문장이 허용된 글에서도 같은 말은 사족이라 enforceStyle 로 켜서 잰다.\n안 잡는 것: 사전에 없는 말투. 보고서와 장표의 권고 (받으십시오) 는 초대가 아니라 조치라 사전에 없다. 프리셋 screen\n 밖에서는 enforceStyle 에 넣어야 돈다.",
47
50
  "sectionNoProse": "설명글 없이 코드, 표, 이미지만 있는 절.\n\n왜: 절은 제목, 시각 자료, 설명글, 그리고 설명을 보조하는 예시로 짠다. 코드와 표만 쌓인 절은 독자가\n 무엇을 보는지 모른다.\n어디서: 운영자 규칙 (섹션은 타이틀, 서브타이틀, 시각, 설명글, 예시). 글쓰기 스킬의 절의 짜임.\n고치기: 코드가 하는 일을 한 문장으로 먼저 말하고 그다음에 이름을 하나씩 푼다.\n안 잡는 것: 목록만 있는 절 (더 해 볼 것). 설명이 목록 안에 있다. 빈 절.",
48
51
  "spacing": "할수 있다, 것같다, 떠난지 3년, 한번도, 두번, 수 밖에, 뿐만아니라 처럼 자주 틀리는 띄어쓰기.\n\n왜: 의존 명사와 단위 명사는 띄고 조사는 붙인다는 규칙이 가장 자주 무너지는 자리다. 틀리면 독자가\n 낱말 경계를 다시 세운다.\n어디서: 국립국어원 한글 맞춤법 제5장 띄어쓰기 (제41항 조사, 제42항 의존 명사, 제43항 단위 명사).\n 항목마다 근거가 붙어 있다 (data/spacing.toml). 관형형 ㄹ 뒤의 의존 명사는 받침 부류 {ㄹ} 로 잡는다.\n고치기: 항목마다 띄거나 붙인 꼴을 fix 로 낸다. hanlint fix 가 적용한다.\n안 잡는 것: 뿐, 만큼, 대로 (체언 뒤 조사와 관형형 뒤 의존 명사를 표층으로 가르지 못한다). 못하다/못 하다,\n 안되다/안 되다 (뜻이 갈린다). 실수, 술수 처럼 ㄹ 받침 명사 뒤의 수. 백틱과 따옴표 안의 인용.",
49
52
  "spelling": "됬, 돼고, 않 해, 왠일, 몇일, 금새, 오랫만, 어떻해, 할께, 바램 처럼 자주 틀리는 표기.\n\n왜: 틀린 표기는 독자가 먼저 알아채고 그 순간 글보다 글쓴이를 본다. 사람 작가가 가장 자주 지적받는\n 자리이고 전부 결정적으로 잡힌다.\n어디서: 국립국어원 한글 맞춤법 (제27항 붙임 2 며칠, 제35항 붙임 2 되어→돼, 제53항 -ㄹ게, 제56항 -든지),\n 온라인가나다 답변, 표준국어대사전 표제어. 항목마다 근거가 붙어 있다 (data/spelling.toml).\n고치기: 항목마다 고친 표기를 fix 로 낸다. hanlint fix 가 적용한다.\n안 잡는 것: 뜻에 따라 둘 다 맞는 자리 (데/대, 이따가/있다가, 못하다/못 하다, 한번/한 번, 안되다/안 되다).\n 백틱과 따옴표 안의 인용. 사전에 없는 표기.",
@@ -44,6 +44,9 @@
44
44
  "platformApi": "dictionary",
45
45
  "promiseRecall": "reader",
46
46
  "redundantPair": "dictionary",
47
+ "screenNarration": "dictionary",
48
+ "screenSentence": "dictionary",
49
+ "screenTone": "dictionary",
47
50
  "sectionNoProse": "threshold",
48
51
  "spacing": "dictionary",
49
52
  "spelling": "dictionary",
@@ -0,0 +1,29 @@
1
+ {
2
+ "entry": [
3
+ {
4
+ "pattern": "(?:오면|되면|도착하면|쌓이면|끝나면|생기면|들어오면)[^.]*여기(?:에|서)",
5
+ "why": "는 빈 자리를 예고하는 말이다. 빈 상태는 `<대상> 없음` 한 낱말로 쓴다 (요청 없음, 계약 없음)",
6
+ "source": "Taxly 화면 낱말 규약"
7
+ },
8
+ {
9
+ "pattern": "여기(?:에|서)[^.]*(?:표시|보입|보인|열립|열린|쌓입|쌓인|나타|나옵|나온)",
10
+ "why": "는 빈 자리를 예고하는 말이다. 빈 상태는 `<대상> 없음` 한 낱말로 쓴다 (요청 없음, 계약 없음)",
11
+ "source": "Taxly 화면 낱말 규약"
12
+ },
13
+ {
14
+ "pattern": "(?<![가-힣])(?:이|현재) (?:화면|탭|창|페이지|목록|카드|표|칸)(?:은|는|이|가|에서|에|을|를|만|으로)",
15
+ "why": "는 화면이 자기를 설명하는 말이다. 제목과 데이터가 무엇인지 말하므로 지운다",
16
+ "source": "Taxly 화면 낱말 규약"
17
+ },
18
+ {
19
+ "pattern": "(?:잠시 뒤|잠시 후|나중에 다시|잠시만 기다)",
20
+ "why": "는 재시도를 서술하는 말이다. 실패는 `<대상> <동작> 실패` 로 쓰고 다시 시도는 단추다",
21
+ "source": "Taxly 화면 낱말 규약"
22
+ },
23
+ {
24
+ "pattern": "(?:할|볼|쓸|열|받을|고를|바꿀|지울|더할|뺄|열람할|확인할|계산할|이어갈|사용할) 수 (?:있습니다|없습니다|있다|없다|있어요|없어요)",
25
+ "why": "는 할 수 있는 일을 서술하는 말이다. 할 수 있는 것은 단추와 칸이 보이고 할 수 없는 것은 `<동작> 불가` 로 쓴다",
26
+ "source": "Taxly 화면 낱말 규약"
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "entry": [
3
+ {
4
+ "pattern": "(?:하고|보고|가고|오고|열고|만들고|찾고|기다리고|계산하고|확인하고|연결하고|정리하고|읽고|쓰고) (?:있습니다|있다|있어요)",
5
+ "why": "는 진행을 문장으로 쓴 것이다. `<동작> 중` 한 낱말로 쓴다 (대시보드 여는 중, 시작 준비 확인 중)",
6
+ "source": "Taxly 화면 낱말 규약"
7
+ },
8
+ {
9
+ "pattern": "(?:하지|되지|찾지|열지|만들지|가지|오지|읽지|쓰지|받지) (?:못했습니다|않았습니다|못합니다|못했다|않았다|못한다|못했어요|않았어요|못해요)",
10
+ "why": "는 실패를 문장으로 쓴 것이다. `<대상> <동작> 실패` 로 쓴다 (요청 실패, 자료 불러오기 실패)",
11
+ "source": "Taxly 화면 낱말 규약"
12
+ },
13
+ {
14
+ "pattern": "(?:필요합니다|필요하다|필요해요|해야 합니다|해야 한다|해야 해요|하셔야 합니다)",
15
+ "why": "는 요구를 문장으로 쓴 것이다. `<대상> 필요` 로 쓴다 (로그인 필요, 사전 승인 IP 필요)",
16
+ "source": "Taxly 화면 낱말 규약"
17
+ },
18
+ {
19
+ "pattern": "(?:입력해 주세요|입력하세요|입력하십시오|선택해 주세요|선택하세요|체크해 주세요|눌러 주세요|누르세요)",
20
+ "why": "는 칸의 이름표가 할 말이다. 이름표가 무엇을 넣는지 말하고 빠진 값은 `<대상> 필요` 로 쓴다",
21
+ "source": "Taxly 화면 낱말 규약"
22
+ },
23
+ {
24
+ "pattern": "(?:했습니다|됐습니다|되었습니다|끝났습니다|했다|됐다|되었다|끝났다|했어요|됐어요|되었어요|끝났어요)",
25
+ "why": "는 끝난 일을 문장으로 쓴 것이다. 상태 한 낱말로 쓴다 (승인됨, 변경됨, 완료)",
26
+ "source": "Taxly 화면 낱말 규약"
27
+ },
28
+ {
29
+ "pattern": "(?:습니다|입니다|합니다|됩니다|세요|십시오|해요|돼요|예요|이에요|있어요|없어요)|(?:한다|된다|있다|없다|이다|아니다|았다|었다|였다|겠다|하다|되다|같다|다르다|않다|는다|온다|간다|본다|낸다|준다|든다)(?=\\s*[.!]?\\s*$)|[가-힣]다\\.|까\\?",
30
+ "why": "는 종결어미다. 낱말 자리에 문장을 두지 않는다. 문장이 정말 필요한 글 (장표, 고지, 실패 원인) 은 문장이 허용된 글 모듈로 옮기고 chat 프리셋으로 잰다",
31
+ "source": "Taxly 화면 낱말 규약"
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "entry": [
3
+ {
4
+ "pattern": "(?:언제든|안심하|안심해|안심시|걱정 없이|걱정하지|편하게|간편하게|자유롭게|바로 살펴|그대로 두셔도)",
5
+ "why": "는 안심시키는 말이다. 정보가 아니므로 지운다",
6
+ "source": "Taxly 화면 낱말 규약"
7
+ },
8
+ {
9
+ "pattern": "(?:살펴보세요|살펴보십시오|살펴봐요|해 보세요|해 보십시오|해 봐요|확인해 보세요|시작해 보세요|이어가세요)",
10
+ "why": "는 사용자를 초대하는 말이다. 다음 동작은 단추다",
11
+ "source": "Taxly 화면 낱말 규약"
12
+ },
13
+ {
14
+ "pattern": "(?:하시면|하셔도|하실|하신|해 주시면|주시고|드립니다|드리겠습니다|드린다|드려요|보여드립니다|보여 드립니다|부르겠습니다|부르겠다|부를게요)",
15
+ "why": "는 접객 말투다. 화면의 글은 낱말이므로 지운다",
16
+ "source": "Taxly 화면 낱말 규약"
17
+ }
18
+ ]
19
+ }
package/data/version.json CHANGED
@@ -1 +1 @@
1
- {"version": "0.0.10"}
1
+ {"version": "0.0.13"}
@@ -18,7 +18,7 @@
18
18
  "properties": {
19
19
  "version": { "const": 1 },
20
20
  "preset": {
21
- "enum": ["blog", "report", "docs", "guide", "essay", "fiction", "encyclopedia", "chat"]
21
+ "enum": ["blog", "report", "docs", "guide", "essay", "fiction", "encyclopedia", "chat", "screen"]
22
22
  },
23
23
  "reader": { "type": "string", "minLength": 1 },
24
24
  "task": { "type": "string", "minLength": 1 },
@@ -19,7 +19,7 @@
19
19
  "properties": {
20
20
  "version": { "const": 2 },
21
21
  "preset": {
22
- "enum": ["blog", "report", "docs", "guide", "essay", "fiction", "encyclopedia", "chat"]
22
+ "enum": ["blog", "report", "docs", "guide", "essay", "fiction", "encyclopedia", "chat", "screen"]
23
23
  },
24
24
  "reader": { "type": "string", "minLength": 1 },
25
25
  "task": { "type": "string", "minLength": 1 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanlint",
3
- "version": "0.0.10",
3
+ "version": "0.0.13",
4
4
  "description": "한국어 글 검사기. Korean prose linter for Markdown: translationese, noun piles, double passives, weak structure. Zero dependencies",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli/help.js CHANGED
@@ -23,6 +23,7 @@ export function rootHelp() {
23
23
  hanlint spec --preset <종류> --chars 800
24
24
  쓰기 전 숫자 사양을 만든다
25
25
  hanlint primer --preset <종류> 쓰기 전에 규칙의 고치는 법과 본보기를 한 장으로 본다
26
+ hanlint sheet src/ --preset screen 소스의 화면 글을 표 하나로 떨군다. 고친 표는 sheet apply 로 되돌려 쓴다
26
27
 
27
28
  추가 명령
28
29
  baseline, print