crumbs-spec 0.1.0 → 0.2.0
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.ko.md +58 -27
- package/README.md +117 -86
- package/dist/src/check.js +7 -5
- package/dist/src/check.js.map +1 -1
- package/dist/src/cli.js +2 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.js +16 -1
- package/dist/src/config.js.map +1 -1
- package/dist/src/dates.js +7 -0
- package/dist/src/dates.js.map +1 -1
- package/dist/src/graph.js +75 -13
- package/dist/src/graph.js.map +1 -1
- package/dist/src/init.js +22 -6
- package/dist/src/init.js.map +1 -1
- package/dist/src/markdown.js +155 -25
- package/dist/src/markdown.js.map +1 -1
- package/dist/src/messages.js +38 -13
- package/dist/src/messages.js.map +1 -1
- package/dist/src/model.js +2 -3
- package/dist/src/model.js.map +1 -1
- package/dist/src/report.js +1 -1
- package/dist/src/report.js.map +1 -1
- package/dist/src/rules/broken-link.js +4 -1
- package/dist/src/rules/broken-link.js.map +1 -1
- package/dist/src/rules/duplicate-policy.js +11 -2
- package/dist/src/rules/duplicate-policy.js.map +1 -1
- package/dist/src/rules/log-body.js +3 -3
- package/dist/src/rules/log-body.js.map +1 -1
- package/dist/src/rules/log-format.js +6 -0
- package/dist/src/rules/log-format.js.map +1 -1
- package/dist/src/rules/missing-backlink.js +4 -1
- package/dist/src/rules/missing-backlink.js.map +1 -1
- package/dist/src/rules/missing-section.js +14 -2
- package/dist/src/rules/missing-section.js.map +1 -1
- package/dist/src/rules/unreachable.js +1 -1
- package/dist/src/rules/unreachable.js.map +1 -1
- package/dist/src/rules/unterminated-comment.js +13 -0
- package/dist/src/rules/unterminated-comment.js.map +1 -0
- package/dist/src/skills.js +12 -1
- package/dist/src/skills.js.map +1 -1
- package/dist/src/templates/en.js +5 -5
- package/dist/src/templates/ko.js +1 -1
- package/dist/src/templates/skills-en.js +14 -13
- package/dist/src/templates/skills-en.js.map +1 -1
- package/dist/src/templates/skills-ko.js +7 -6
- package/dist/src/templates/skills-ko.js.map +1 -1
- package/dist/src/version.js +7 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +2 -1
package/dist/src/messages.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { VOCAB_KEYS } from './headings.js';
|
|
2
|
+
// 사용자에게 보이는 문자열 모음(12.2). 한국어 문구는 1단계 출력과 바이트 단위로 같다.
|
|
3
|
+
// 명령·옵션 이름·규칙 id·파일 경로·식별자는 두 언어 모두 원문 그대로 둔다.
|
|
4
|
+
// 영어 수 표기: 1이면 단수, 그 밖이면 복수다.
|
|
5
|
+
const counted = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
|
|
1
6
|
export const MESSAGES = {
|
|
2
7
|
en: {
|
|
3
8
|
usage: 'usage: crumbs <init|check|log add> [options] (details: crumbs --help)',
|
|
@@ -21,6 +26,7 @@ init options
|
|
|
21
26
|
check options
|
|
22
27
|
--format <text|json> output format (default: text)
|
|
23
28
|
--config <path> config file path (default: <root>/crumbs.json)
|
|
29
|
+
exclude entries in crumbs.json are paths relative to the spec root; an entry ending in "/" excludes a directory
|
|
24
30
|
|
|
25
31
|
log add options
|
|
26
32
|
--kind <ingest|query|lint|drift|change|decision> entry kind (required)
|
|
@@ -42,14 +48,16 @@ Example
|
|
|
42
48
|
titleEmpty: 'title is empty',
|
|
43
49
|
titleLineBreak: 'title cannot contain a line break',
|
|
44
50
|
bodyHeading: 'body cannot contain a heading line (#)',
|
|
51
|
+
commentForbidden: 'title and body cannot contain "<!--"',
|
|
45
52
|
configKept: (option) => `note: crumbs.json already exists, so its settings are used (${option} ignored)`,
|
|
46
53
|
skipped: (path) => `skipped: ${path} (already exists)`,
|
|
47
54
|
created: (path) => `created: ${path}`,
|
|
48
55
|
updated: (path) => `updated: ${path}`,
|
|
49
|
-
next: 'next: crumbs check',
|
|
56
|
+
next: 'next: npx crumbs-spec check',
|
|
50
57
|
claudeGuide: 'guide: add "@AGENTS.md" to CLAUDE.md so Claude Code reads the crumbs rules',
|
|
51
58
|
agentsMarkers: 'AGENTS.md: crumbs markers are not paired',
|
|
52
|
-
|
|
59
|
+
rootMissing: (root) => `root directory not found: ${root}`,
|
|
60
|
+
fileMissing: (where) => `crumbs.json not found in ${where}. Run "npx crumbs-spec init" first.`,
|
|
53
61
|
configNotObject: 'crumbs.json: must be an object',
|
|
54
62
|
unknownKey: (key) => `crumbs.json: unknown key "${key}"`,
|
|
55
63
|
invalid: (key, reason) => `crumbs.json: "${key}" has an invalid value: ${reason}`,
|
|
@@ -60,12 +68,15 @@ Example
|
|
|
60
68
|
reasonOne: 'only 1 is allowed',
|
|
61
69
|
reasonDot: '"." or an empty value is not allowed',
|
|
62
70
|
reasonObject: 'must be an object',
|
|
63
|
-
reasonUnknownHeading: (name) => `unknown heading key "${name}"`,
|
|
71
|
+
reasonUnknownHeading: (name) => `unknown heading key "${name}" (valid: ${VOCAB_KEYS.join(', ')})`,
|
|
64
72
|
reasonStringArray: 'must be an array of strings',
|
|
65
73
|
reasonInsideSpec: 'cannot be inside specDir',
|
|
66
|
-
|
|
74
|
+
reasonInsideRoot: 'must stay inside the repository root',
|
|
75
|
+
reasonRelativePath: 'must be a path relative to the spec root',
|
|
76
|
+
logDocMissing: (path) => `log document not found: ${path}. Run "npx crumbs-spec init" first.`,
|
|
67
77
|
logAdded: (path, heading) => `added: ${path} — ${heading}`,
|
|
68
|
-
|
|
78
|
+
logDateFuture: (date) => `date is in the future: ${date}`,
|
|
79
|
+
summary: (documents, errors, warnings) => `crumbs check: ${counted(documents, 'document')}, ${counted(errors, 'error')}, ${counted(warnings, 'warning')}`,
|
|
69
80
|
h1Title: 'first heading must be an H1',
|
|
70
81
|
indexMissing: (path) => `index document not found: ${path}`,
|
|
71
82
|
linkMissing: (rawTarget) => `link target not found: ${rawTarget}`,
|
|
@@ -76,12 +87,16 @@ Example
|
|
|
76
87
|
backlinkOneWay: (sourcePath) => `linked from ${sourcePath} but no link back`,
|
|
77
88
|
codePathMissing: (raw) => `code path not found: ${raw}`,
|
|
78
89
|
duplicatePolicy: (name, others) => `duplicate policy "${name}": also in ${others}`,
|
|
79
|
-
|
|
90
|
+
duplicatePolicyInDoc: (name, line) => `duplicate policy "${name}": already defined in this document at line ${line}`,
|
|
91
|
+
noSpecSections: (target) => `no spec sections found; if this is not a crumbs document, add "${target}" to "exclude" in crumbs.json`,
|
|
92
|
+
unterminatedComment: 'HTML comment opened here is never closed; everything after it is invisible to check',
|
|
93
|
+
spellingMismatch: (rawTarget, actual) => `link spelling differs from the file on disk: ${rawTarget} (file: ${actual})`,
|
|
94
|
+
openQuestions: (count) => `${count} open question(s)`,
|
|
80
95
|
logHeadingFormat: (text) => `log entry heading must be "## [YYYY-MM-DD] kind | title": "${text}"`,
|
|
81
96
|
logKindUnknown: (kind, kinds) => `unknown kind "${kind}" (one of ${kinds})`,
|
|
82
97
|
logDateInvalid: (date) => `date is not valid: ${date}`,
|
|
83
98
|
logOrder: (previous, date) => `log entries are out of date order: ${date} is before ${previous}`,
|
|
84
|
-
decisionLink: 'decision entry has no document link',
|
|
99
|
+
decisionLink: 'decision entry has no document link (expected: Document: [policy](domain/file.md#anchor))',
|
|
85
100
|
flowMissing: (steps) => `current behavior has ${steps} steps but no flow diagram or table`,
|
|
86
101
|
sectionFirst: (title) => `section order differs from the template: first section must be "## ${title}"`,
|
|
87
102
|
sectionLast: (title) => `section order differs from the template: last section must be "## ${title}"`,
|
|
@@ -109,6 +124,7 @@ init 옵션
|
|
|
109
124
|
check 옵션
|
|
110
125
|
--format <text|json> 출력 형식 (기본값: text)
|
|
111
126
|
--config <path> 설정 파일 경로 (기본값: <root>/crumbs.json)
|
|
127
|
+
crumbs.json의 exclude 항목은 스펙 루트 기준 경로이며, "/"로 끝나면 디렉터리를 제외한다
|
|
112
128
|
|
|
113
129
|
log add 옵션
|
|
114
130
|
--kind <ingest|query|lint|drift|change|decision> 항목 종류 (필수)
|
|
@@ -130,14 +146,16 @@ log add 옵션
|
|
|
130
146
|
titleEmpty: '제목이 비어 있음',
|
|
131
147
|
titleLineBreak: '제목에 줄바꿈을 넣을 수 없음',
|
|
132
148
|
bodyHeading: '본문에 제목 줄(#)을 넣을 수 없음',
|
|
149
|
+
commentForbidden: '제목과 본문에 "<!--"를 넣을 수 없음',
|
|
133
150
|
configKept: (option) => `참고: crumbs.json이 이미 있어 그 설정을 사용함 (${option} 무시)`,
|
|
134
151
|
skipped: (path) => `이미 있음, 건너뜀: ${path}`,
|
|
135
152
|
created: (path) => `생성: ${path}`,
|
|
136
153
|
updated: (path) => `갱신: ${path}`,
|
|
137
|
-
next: '다음: crumbs check',
|
|
154
|
+
next: '다음: npx crumbs-spec check',
|
|
138
155
|
claudeGuide: '안내: Claude Code가 crumbs 규칙을 읽도록 CLAUDE.md에 "@AGENTS.md"를 추가하세요',
|
|
139
156
|
agentsMarkers: 'AGENTS.md: crumbs 표시가 짝이 맞지 않음',
|
|
140
|
-
|
|
157
|
+
rootMissing: (root) => `저장소 루트 디렉터리를 찾을 수 없음: ${root}`,
|
|
158
|
+
fileMissing: (where) => `${where}에 crumbs.json이 없음. 먼저 "npx crumbs-spec init"을 실행하세요.`,
|
|
141
159
|
configNotObject: 'crumbs.json: 타입이 객체이어야 함',
|
|
142
160
|
unknownKey: (key) => `crumbs.json: 알 수 없는 키 "${key}"`,
|
|
143
161
|
invalid: (key, reason) => `crumbs.json: "${key}" 값이 잘못됨: ${reason}`,
|
|
@@ -147,11 +165,14 @@ log add 옵션
|
|
|
147
165
|
reasonOne: '1만 허용',
|
|
148
166
|
reasonDot: '"."이나 빈 값은 안 됨',
|
|
149
167
|
reasonObject: '타입이 객체이어야 함',
|
|
150
|
-
reasonUnknownHeading: (name) => `알 수 없는 어휘 키 "${name}"`,
|
|
168
|
+
reasonUnknownHeading: (name) => `알 수 없는 어휘 키 "${name}" (가능한 키: ${VOCAB_KEYS.join(', ')})`,
|
|
151
169
|
reasonStringArray: '타입이 문자열 배열이어야 함',
|
|
152
170
|
reasonInsideSpec: 'specDir 안에 둘 수 없음',
|
|
153
|
-
|
|
171
|
+
reasonInsideRoot: '저장소 루트 밖을 가리킬 수 없음',
|
|
172
|
+
reasonRelativePath: '스펙 루트 기준 상대 경로여야 함',
|
|
173
|
+
logDocMissing: (path) => `로그 문서가 없음: ${path}. 먼저 "npx crumbs-spec init"을 실행하세요.`,
|
|
154
174
|
logAdded: (path, heading) => `추가: ${path} — ${heading}`,
|
|
175
|
+
logDateFuture: (date) => `날짜가 미래임: ${date}`,
|
|
155
176
|
summary: (documents, errors, warnings) => `crumbs check: 문서 ${documents}개, 오류 ${errors}개, 경고 ${warnings}개`,
|
|
156
177
|
h1Title: '첫 제목은 H1이어야 함',
|
|
157
178
|
indexMissing: (path) => `인덱스 문서가 없음: ${path}`,
|
|
@@ -163,12 +184,16 @@ log add 옵션
|
|
|
163
184
|
backlinkOneWay: (sourcePath) => `${sourcePath}에서 링크했지만 되돌아가는 링크가 없음`,
|
|
164
185
|
codePathMissing: (raw) => `코드 경로가 없음: ${raw}`,
|
|
165
186
|
duplicatePolicy: (name, others) => `중복 정책 "${name}": ${others}에도 있음`,
|
|
166
|
-
|
|
187
|
+
duplicatePolicyInDoc: (name, line) => `중복 정책 "${name}": 이 문서의 ${line}줄에 이미 있음`,
|
|
188
|
+
noSpecSections: (target) => `스펙 절이 하나도 없음. crumbs 문서가 아니면 crumbs.json의 "exclude"에 "${target}"를 추가하세요`,
|
|
189
|
+
unterminatedComment: '여기서 연 HTML 주석이 닫히지 않음: 이후 내용은 check가 볼 수 없음',
|
|
190
|
+
spellingMismatch: (rawTarget, actual) => `링크 철자가 디스크의 파일과 다름: ${rawTarget} (파일: ${actual})`,
|
|
191
|
+
openQuestions: (count) => `미해결 질문 ${count}개`,
|
|
167
192
|
logHeadingFormat: (text) => `로그 항목 제목 형식이 틀림: "${text}" (형식: [YYYY-MM-DD] kind | 제목)`,
|
|
168
193
|
logKindUnknown: (kind, kinds) => `알 수 없는 kind "${kind}" (${kinds} 중 하나)`,
|
|
169
194
|
logDateInvalid: (date) => `날짜가 유효하지 않음: ${date}`,
|
|
170
195
|
logOrder: (previous, date) => `이전 항목(${previous})보다 날짜가 앞섬: ${date}`,
|
|
171
|
-
decisionLink: '결정 항목에 문서 링크가 없음',
|
|
196
|
+
decisionLink: '결정 항목에 문서 링크가 없음 (형식: 문서: [정책](domain/file.md#앵커))',
|
|
172
197
|
flowMissing: (steps) => `현재 동작이 ${steps}단계인데 흐름 도식이나 표가 없음`,
|
|
173
198
|
sectionFirst: (title) => `첫 절은 "## ${title}"이어야 함`,
|
|
174
199
|
sectionLast: (title) => `마지막 절은 "## ${title}"이어야 함`,
|
package/dist/src/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/messages.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,eAAe,CAAC;AAEtD,sDAAsD;AACtD,+CAA+C;AAC/C,8BAA8B;AAC9B,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,IAAY,EAAU,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAiFzF,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC9C,EAAE,EAAE;QACF,KAAK,EAAE,wEAAwE;QAC/E,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BT;QACG,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,IAAI,GAAG;QACnD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,IAAI,wBAAwB;QAC9D,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,IAAI,eAAe;QACxD,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,IAAI,GAAG;QACrD,SAAS,EAAE,kBAAkB;QAC7B,YAAY,EAAE,+BAA+B;QAC7C,YAAY,EAAE,kBAAkB;QAChC,UAAU,EAAE,4CAA4C;QACxD,UAAU,EAAE,gBAAgB;QAC5B,cAAc,EAAE,mCAAmC;QACnD,WAAW,EAAE,wCAAwC;QACrD,gBAAgB,EAAE,sCAAsC;QAExD,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,+DAA+D,MAAM,WAAW;QACxG,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,IAAI,mBAAmB;QACtD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,IAAI,EAAE;QACrC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,IAAI,EAAE;QACrC,IAAI,EAAE,6BAA6B;QACnC,WAAW,EAAE,4EAA4E;QACzF,aAAa,EAAE,0CAA0C;QACzD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,6BAA6B,IAAI,EAAE;QAE1D,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,4BAA4B,KAAK,qCAAqC;QAC9F,eAAe,EAAE,gCAAgC;QACjD,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,6BAA6B,GAAG,GAAG;QACxD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,iBAAiB,GAAG,2BAA2B,MAAM,EAAE;QACjF,+DAA+D;QAC/D,WAAW,EAAE,0CAA0C;QACvD,YAAY,EAAE,kBAAkB;QAChC,YAAY,EAAE,kBAAkB;QAChC,SAAS,EAAE,mBAAmB;QAC9B,SAAS,EAAE,sCAAsC;QACjD,YAAY,EAAE,mBAAmB;QACjC,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,wBAAwB,IAAI,aAAa,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACjG,iBAAiB,EAAE,6BAA6B;QAChD,gBAAgB,EAAE,0BAA0B;QAC5C,gBAAgB,EAAE,sCAAsC;QACxD,kBAAkB,EAAE,0CAA0C;QAE9D,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,2BAA2B,IAAI,qCAAqC;QAC7F,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,UAAU,IAAI,MAAM,OAAO,EAAE;QAC1D,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,0BAA0B,IAAI,EAAE;QAEzD,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CACvC,iBAAiB,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE;QAEjH,OAAO,EAAE,6BAA6B;QACtC,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,6BAA6B,IAAI,EAAE;QAC3D,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,0BAA0B,SAAS,EAAE;QACjE,aAAa,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,qBAAqB,SAAS,EAAE;QAC9D,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,oBAAoB,SAAS,EAAE;QAC3D,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,sBAAsB;QAC7D,eAAe,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,mBAAmB,UAAU,EAAE;QAChE,cAAc,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,eAAe,UAAU,mBAAmB;QAC5E,eAAe,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,wBAAwB,GAAG,EAAE;QACvD,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,qBAAqB,IAAI,cAAc,MAAM,EAAE;QAClF,oBAAoB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,qBAAqB,IAAI,+CAA+C,IAAI,EAAE;QACpH,cAAc,EAAE,CAAC,MAAM,EAAE,EAAE,CACzB,kEAAkE,MAAM,+BAA+B;QACzG,mBAAmB,EAAE,qFAAqF;QAC1G,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,gDAAgD,SAAS,WAAW,MAAM,GAAG;QACtH,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,mBAAmB;QACrD,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,8DAA8D,IAAI,GAAG;QACjG,cAAc,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,iBAAiB,IAAI,aAAa,KAAK,GAAG;QAC3E,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,IAAI,EAAE;QACtD,QAAQ,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,sCAAsC,IAAI,cAAc,QAAQ,EAAE;QAChG,YAAY,EAAE,2FAA2F;QACzG,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,wBAAwB,KAAK,qCAAqC;QAC1F,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,sEAAsE,KAAK,GAAG;QACvG,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,qEAAqE,KAAK,GAAG;QACrG,YAAY,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAC9B,gDAAgD,MAAM,0BAA0B,KAAK,GAAG;KAC3F;IAED,EAAE,EAAE;QACF,KAAK,EAAE,6DAA6D;QACpE,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BT;QACG,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,IAAI,GAAG;QAC9C,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,WAAW;QAC7C,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,OAAO;QAC5C,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,IAAI,GAAG;QAC/C,SAAS,EAAE,QAAQ;QACnB,YAAY,EAAE,qBAAqB;QACnC,YAAY,EAAE,QAAQ;QACtB,UAAU,EAAE,wBAAwB;QACpC,UAAU,EAAE,WAAW;QACvB,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,sBAAsB;QACnC,gBAAgB,EAAE,yBAAyB;QAE3C,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,qCAAqC,MAAM,MAAM;QACzE,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,IAAI,EAAE;QACxC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,EAAE;QAChC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,EAAE;QAChC,IAAI,EAAE,2BAA2B;QACjC,WAAW,EAAE,gEAAgE;QAC7E,aAAa,EAAE,gCAAgC;QAC/C,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,yBAAyB,IAAI,EAAE;QAEtD,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,sDAAsD;QACtF,eAAe,EAAE,0BAA0B;QAC3C,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,0BAA0B,GAAG,GAAG;QACrD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,iBAAiB,GAAG,aAAa,MAAM,EAAE;QACnE,WAAW,EAAE,0CAA0C;QACvD,YAAY,EAAE,cAAc;QAC5B,YAAY,EAAE,aAAa;QAC3B,SAAS,EAAE,OAAO;QAClB,SAAS,EAAE,gBAAgB;QAC3B,YAAY,EAAE,aAAa;QAC3B,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,IAAI,aAAa,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACzF,iBAAiB,EAAE,iBAAiB;QACpC,gBAAgB,EAAE,mBAAmB;QACrC,gBAAgB,EAAE,oBAAoB;QACtC,kBAAkB,EAAE,oBAAoB;QAExC,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,IAAI,qCAAqC;QAChF,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,MAAM,OAAO,EAAE;QACvD,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,IAAI,EAAE;QAE3C,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,oBAAoB,SAAS,SAAS,MAAM,SAAS,QAAQ,GAAG;QAE1G,OAAO,EAAE,eAAe;QACxB,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,IAAI,EAAE;QAC7C,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,cAAc,SAAS,EAAE;QACrD,aAAa,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,WAAW,SAAS,EAAE;QACpD,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,GAAG,SAAS,aAAa;QACrD,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,SAAS;QAChD,eAAe,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,UAAU,eAAe;QAC7D,cAAc,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,UAAU,uBAAuB;QACpE,eAAe,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,GAAG,EAAE;QAC7C,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,IAAI,MAAM,MAAM,OAAO;QACpE,oBAAoB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,IAAI,YAAY,IAAI,UAAU;QAC9E,cAAc,EAAE,CAAC,MAAM,EAAE,EAAE,CACzB,yDAAyD,MAAM,UAAU;QAC3E,mBAAmB,EAAE,6CAA6C;QAClE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,uBAAuB,SAAS,SAAS,MAAM,GAAG;QAC3F,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,KAAK,GAAG;QAC5C,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,IAAI,gCAAgC;QACrF,cAAc,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,IAAI,MAAM,KAAK,QAAQ;QACxE,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,IAAI,EAAE;QAChD,QAAQ,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,SAAS,QAAQ,eAAe,IAAI,EAAE;QACpE,YAAY,EAAE,oDAAoD;QAClE,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,KAAK,oBAAoB;QAC3D,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,KAAK,QAAQ;QAClD,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,KAAK,QAAQ;QACnD,YAAY,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,MAAM,YAAY,KAAK,WAAW;KAC3E;CACF,CAAC"}
|
package/dist/src/model.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return line === undefined ? { rule, severity, file, message } : { rule, severity, file, line, message };
|
|
1
|
+
export function finding(rule, severity, file, message, line = 1) {
|
|
2
|
+
return { rule, severity, file, line, message };
|
|
4
3
|
}
|
|
5
4
|
//# sourceMappingURL=model.js.map
|
package/dist/src/model.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/model.ts"],"names":[],"mappings":"AAyFA,MAAM,UAAU,OAAO,CACrB,IAAY,EACZ,QAA6B,EAC7B,IAAY,EACZ,OAAe,EACf,IAAI,GAAG,CAAC;IAER,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC"}
|
package/dist/src/report.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MESSAGES } from './messages.js';
|
|
2
2
|
// 발견마다 한 줄, 발견이 있으면 빈 줄 하나, 그다음 요약 한 줄.
|
|
3
3
|
export function formatText(report, lang) {
|
|
4
|
-
const lines = report.findings.map((finding) => `${finding.file}
|
|
4
|
+
const lines = report.findings.map((finding) => `${finding.file}:${finding.line} ${finding.severity} ${finding.rule}: ${finding.message}`);
|
|
5
5
|
if (lines.length > 0)
|
|
6
6
|
lines.push('');
|
|
7
7
|
const { documents, errors, warnings } = report.summary;
|
package/dist/src/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/report.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wCAAwC;AACxC,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,IAAU;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAC/B,CAAC,OAAO,EAAE,EAAE,CACV,GAAG,OAAO,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/report.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wCAAwC;AACxC,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,IAAU;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAC/B,CAAC,OAAO,EAAE,EAAE,CACV,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CAC5F,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;IACvD,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChE,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC"}
|
|
@@ -8,7 +8,10 @@ export function brokenLink(graph) {
|
|
|
8
8
|
for (const link of doc.links) {
|
|
9
9
|
if (link.kind !== 'internal' || link.path === undefined)
|
|
10
10
|
continue;
|
|
11
|
-
if (
|
|
11
|
+
if (link.spellingMismatch !== undefined) {
|
|
12
|
+
out.push(finding('broken-link', 'error', doc.file, MESSAGES[graph.lang].spellingMismatch(link.rawTarget, link.spellingMismatch), link.line));
|
|
13
|
+
}
|
|
14
|
+
else if (!isFile(link.path)) {
|
|
12
15
|
out.push(finding('broken-link', 'error', doc.file, MESSAGES[graph.lang].linkMissing(link.rawTarget), link.line));
|
|
13
16
|
}
|
|
14
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broken-link.js","sourceRoot":"","sources":["../../../src/rules/broken-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,mEAAmE;AACnE,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS;YAClE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"broken-link.js","sourceRoot":"","sources":["../../../src/rules/broken-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,mEAAmE;AACnE,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS;YAClE,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;gBACxC,GAAG,CAAC,IAAI,CACN,OAAO,CACL,aAAa,EACb,OAAO,EACP,GAAG,CAAC,IAAI,EACR,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,EAC5E,IAAI,CAAC,IAAI,CACV,CACF,CAAC;YACJ,CAAC;iBAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACnH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { MESSAGES } from '../messages.js';
|
|
2
2
|
import { finding } from '../model.js';
|
|
3
3
|
// 정책 이름은 문서 하나만 소유한다. 로그 문서는 면제다.
|
|
4
|
-
// 같은 정규화 이름이
|
|
4
|
+
// 같은 정규화 이름이 한 문서 안에 두 번 이상 있으면, 두 번째부터의 제목마다 발견 하나를 낸다.
|
|
5
|
+
// 같은 이름이 서로 다른 두 문서 이상에 있으면, 관련 문서마다 발견 하나를 낸다.
|
|
5
6
|
export function duplicatePolicy(graph) {
|
|
7
|
+
const out = [];
|
|
6
8
|
const groups = new Map();
|
|
7
9
|
for (const doc of graph.docs) {
|
|
8
10
|
if (doc.role === 'log')
|
|
9
11
|
continue;
|
|
12
|
+
const firstInDoc = new Map();
|
|
10
13
|
for (const policy of doc.policies) {
|
|
14
|
+
const earlier = firstInDoc.get(policy.normalized);
|
|
15
|
+
if (earlier === undefined) {
|
|
16
|
+
firstInDoc.set(policy.normalized, policy.heading);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
out.push(finding('duplicate-policy', 'error', doc.file, MESSAGES[graph.lang].duplicatePolicyInDoc(policy.heading.text, earlier.line), policy.heading.line));
|
|
20
|
+
}
|
|
11
21
|
const entries = groups.get(policy.normalized) ?? [];
|
|
12
22
|
entries.push({ doc, heading: policy.heading });
|
|
13
23
|
groups.set(policy.normalized, entries);
|
|
14
24
|
}
|
|
15
25
|
}
|
|
16
|
-
const out = [];
|
|
17
26
|
for (const entries of groups.values()) {
|
|
18
27
|
const docs = [];
|
|
19
28
|
for (const entry of entries) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicate-policy.js","sourceRoot":"","sources":["../../../src/rules/duplicate-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAA4C,MAAM,aAAa,CAAC;AAEhF,kCAAkC;AAClC,
|
|
1
|
+
{"version":3,"file":"duplicate-policy.js","sourceRoot":"","sources":["../../../src/rules/duplicate-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAA4C,MAAM,aAAa,CAAC;AAEhF,kCAAkC;AAClC,yDAAyD;AACzD,gDAAgD;AAChD,MAAM,UAAU,eAAe,CAAC,KAAY;IAC1C,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAgD,CAAC;IACvE,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK;YAAE,SAAS;QACjC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC9C,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAClD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACpD,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,IAAI,CACN,OAAO,CACL,kBAAkB,EAClB,OAAO,EACP,GAAG,CAAC,IAAI,EACR,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAC5E,MAAM,CAAC,OAAO,CAAC,IAAI,CACpB,CACF,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAc,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;YACzD,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,MAAM,MAAM,GAAG,IAAI;iBAChB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,GAAG,CAAC;iBAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;iBAC1B,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,GAAG,CAAC,IAAI,CACN,OAAO,CACL,kBAAkB,EAClB,OAAO,EACP,GAAG,CAAC,IAAI,EACR,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,EAChE,KAAK,CAAC,OAAO,CAAC,IAAI,CACnB,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { parseLogHeading } from '../log.js';
|
|
2
2
|
import { MESSAGES } from '../messages.js';
|
|
3
3
|
import { finding } from '../model.js';
|
|
4
|
-
// kind가 decision인 항목은 본문(제목 줄 다음부터 다음 H2 직전까지)에 내부 링크가 하나 이상 있어야 한다. 없으면 제목 줄마다 경고 하나다.
|
|
5
|
-
// 링크는 파싱된 doc.links를 쓰고, 항목 경계는 H2 제목 줄로 정한다.
|
|
4
|
+
// kind가 decision인 항목은 본문(제목 줄 다음부터 다음 H2 직전까지)에 다른 문서로 가는 내부 링크가 하나 이상 있어야 한다. 없으면 제목 줄마다 경고 하나다.
|
|
5
|
+
// 같은 로그 안을 가리키는 #앵커 링크는 문서 링크로 세지 않는다. 링크는 파싱된 doc.links를 쓰고, 항목 경계는 H2 제목 줄로 정한다.
|
|
6
6
|
export function logBody(graph) {
|
|
7
7
|
const log = graph.docs.find((doc) => doc.role === 'log');
|
|
8
8
|
if (log === undefined)
|
|
@@ -14,7 +14,7 @@ export function logBody(graph) {
|
|
|
14
14
|
if (!parsed.ok || parsed.kind !== 'decision')
|
|
15
15
|
return;
|
|
16
16
|
const end = entries[index + 1]?.line ?? Number.MAX_SAFE_INTEGER;
|
|
17
|
-
const linked = log.links.some((link) => link.kind === 'internal' && link.line > heading.line && link.line < end);
|
|
17
|
+
const linked = log.links.some((link) => link.kind === 'internal' && link.path !== log.abs && link.line > heading.line && link.line < end);
|
|
18
18
|
if (!linked)
|
|
19
19
|
out.push(finding('log-body', 'warning', log.file, MESSAGES[graph.lang].decisionLink, heading.line));
|
|
20
20
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-body.js","sourceRoot":"","sources":["../../../src/rules/log-body.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,
|
|
1
|
+
{"version":3,"file":"log-body.js","sourceRoot":"","sources":["../../../src/rules/log-body.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,kGAAkG;AAClG,mFAAmF;AACnF,MAAM,UAAU,OAAO,CAAC,KAAY;IAClC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;IACzD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACjC,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU;YAAE,OAAO;QACrD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,IAAI,IAAI,MAAM,CAAC,gBAAgB,CAAC;QAChE,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAC3B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,GAAG,CAC3G,CAAC;QACF,IAAI,CAAC,MAAM;YAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { LOG_KINDS, parseLogHeading } from '../log.js';
|
|
2
2
|
import { MESSAGES } from '../messages.js';
|
|
3
3
|
import { finding } from '../model.js';
|
|
4
|
+
import { addDays, localDate } from '../dates.js';
|
|
4
5
|
// 로그 문서의 H2 항목 제목이 "[YYYY-MM-DD] kind | 제목" 형식이고 날짜가 실제 달력 날짜인지 본다. 어긋난 제목마다 오류 하나다.
|
|
6
|
+
// 날짜가 내일보다 뒤(미래)이면 오류 하나다. 오늘과 내일은 시간대 차이를 고려해 허용한다.
|
|
5
7
|
export function logFormat(graph) {
|
|
6
8
|
const log = graph.docs.find((doc) => doc.role === 'log');
|
|
7
9
|
if (log === undefined)
|
|
8
10
|
return [];
|
|
9
11
|
const out = [];
|
|
12
|
+
const latest = addDays(localDate(), 1);
|
|
10
13
|
for (const heading of log.headings.filter((h) => h.level === 2)) {
|
|
11
14
|
const parsed = parseLogHeading(heading.text);
|
|
12
15
|
if (!parsed.ok) {
|
|
13
16
|
out.push(finding('log-format', 'error', log.file, message(MESSAGES[graph.lang], parsed, heading.text), heading.line));
|
|
14
17
|
}
|
|
18
|
+
else if (parsed.date > latest) {
|
|
19
|
+
out.push(finding('log-format', 'error', log.file, MESSAGES[graph.lang].logDateFuture(parsed.date), heading.line));
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
return out;
|
|
17
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-format.js","sourceRoot":"","sources":["../../../src/rules/log-format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAyB,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAiB,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"log-format.js","sourceRoot":"","sources":["../../../src/rules/log-format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAyB,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAiB,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEjD,qFAAqF;AACrF,qDAAqD;AACrD,MAAM,UAAU,SAAS,CAAC,KAAY;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;IACzD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACvC,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QAChE,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACxH,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACpH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,CAAW,EAAE,MAA+C,EAAE,IAAY;IACzF,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAClC,KAAK,MAAM;YACT,OAAO,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7D,KAAK,MAAM;YACT,OAAO,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
|
|
@@ -12,7 +12,10 @@ export function missingBacklink(graph) {
|
|
|
12
12
|
continue;
|
|
13
13
|
const linksToParent = doc.links.some((link) => link.kind === 'internal' && link.path === parent.abs);
|
|
14
14
|
if (!linksToParent) {
|
|
15
|
-
|
|
15
|
+
// 발견의 줄은 관련 문서 절의 제목 줄이고, 그 절이 없으면 제목 줄, 그것도 없으면 1줄이다.
|
|
16
|
+
const related = doc.sections.find((section) => section.key === 'related');
|
|
17
|
+
const line = related?.heading.line ?? doc.title?.line ?? 1;
|
|
18
|
+
out.push(finding('missing-backlink', 'error', doc.file, MESSAGES[graph.lang].backlinkMissing(parent.file), line));
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
for (const source of graph.docs) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"missing-backlink.js","sourceRoot":"","sources":["../../../src/rules/missing-backlink.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,yCAAyC;AACzC,mDAAmD;AACnD,MAAM,UAAU,eAAe,CAAC,KAAY;IAC1C,MAAM,GAAG,GAAc,EAAE,CAAC;IAE1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QACnC,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;QACrG,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"missing-backlink.js","sourceRoot":"","sources":["../../../src/rules/missing-backlink.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,yCAAyC;AACzC,mDAAmD;AACnD,MAAM,UAAU,eAAe,CAAC,KAAY;IAC1C,MAAM,GAAG,GAAc,EAAE,CAAC;IAE1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QACnC,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;QACrG,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,uDAAuD;YACvD,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACpH,CAAC;IACH,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QACnC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS;YAClE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;gBAAE,SAAS;YACpF,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;gBAAE,SAAS;YACvC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;YACpG,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,GAAG,CAAC,IAAI,CACN,OAAO,CACL,kBAAkB,EAClB,OAAO,EACP,MAAM,CAAC,IAAI,EACX,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,EAChD,MAAM,CAAC,KAAK,EAAE,IAAI,CACnB,CACF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import { REQUIRED_SECTIONS, isRequiredRole } from '../headings.js';
|
|
2
|
+
import { relPath } from '../graph.js';
|
|
2
3
|
import { MESSAGES } from '../messages.js';
|
|
3
4
|
import { finding } from '../model.js';
|
|
4
5
|
// 개요와 상세 문서는 역할별 필수 H2 절을 모두 가져야 한다. 빠진 절마다 발견 하나를 낸다.
|
|
6
|
+
// 필수 절이 하나도 없으면 crumbs 문서가 아닐 가능성이 크므로, 발견 하나로 exclude 설정을 안내한다.
|
|
5
7
|
export function missingSection(graph) {
|
|
6
8
|
const out = [];
|
|
7
9
|
for (const doc of graph.docs) {
|
|
8
10
|
if (!isRequiredRole(doc.role))
|
|
9
11
|
continue;
|
|
10
12
|
const present = new Set(doc.sections.map((section) => section.key));
|
|
11
|
-
|
|
13
|
+
const required = REQUIRED_SECTIONS[doc.role];
|
|
14
|
+
const line = doc.title?.line ?? 1;
|
|
15
|
+
if (required.every((key) => !present.has(key))) {
|
|
16
|
+
// 스펙 루트 바로 아래의 파일은 파일 이름을, 하위 디렉터리의 파일은 디렉터리(끝 "/")를 안내한다.
|
|
17
|
+
const rel = relPath(graph.specAbs, doc.abs);
|
|
18
|
+
const slash = rel.lastIndexOf('/');
|
|
19
|
+
const target = slash < 0 ? rel : rel.slice(0, slash + 1);
|
|
20
|
+
out.push(finding('missing-section', 'error', doc.file, MESSAGES[graph.lang].noSpecSections(target), line));
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
for (const key of required) {
|
|
12
24
|
if (!present.has(key)) {
|
|
13
|
-
out.push(finding('missing-section', 'error', doc.file, MESSAGES[graph.lang].sectionMissing(graph.vocab[key]),
|
|
25
|
+
out.push(finding('missing-section', 'error', doc.file, MESSAGES[graph.lang].sectionMissing(graph.vocab[key]), line));
|
|
14
26
|
}
|
|
15
27
|
}
|
|
16
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"missing-section.js","sourceRoot":"","sources":["../../../src/rules/missing-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"missing-section.js","sourceRoot":"","sources":["../../../src/rules/missing-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,uDAAuD;AACvD,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,KAAY;IACzC,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACxC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC;QAClC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC/C,2DAA2D;YAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YACzD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;YAC3G,SAAS;QACX,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;YACvH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -22,6 +22,6 @@ export function unreachable(graph) {
|
|
|
22
22
|
const indexPath = relPath(graph.root, graph.indexAbs);
|
|
23
23
|
return graph.docs
|
|
24
24
|
.filter((doc) => !seen.has(doc))
|
|
25
|
-
.map((doc) => finding('unreachable', 'error', doc.file, MESSAGES[graph.lang].unreachable(indexPath)));
|
|
25
|
+
.map((doc) => finding('unreachable', 'error', doc.file, MESSAGES[graph.lang].unreachable(indexPath), doc.title?.line ?? 1));
|
|
26
26
|
}
|
|
27
27
|
//# sourceMappingURL=unreachable.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unreachable.js","sourceRoot":"","sources":["../../../src/rules/unreachable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,wCAAwC;AACxC,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS;YAClE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACjB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACtD,OAAO,KAAK,CAAC,IAAI;SACd,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SAC/B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"unreachable.js","sourceRoot":"","sources":["../../../src/rules/unreachable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,wCAAwC;AACxC,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS;YAClE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACjB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACtD,OAAO,KAAK,CAAC,IAAI;SACd,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SAC/B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACX,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC,CAC7G,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MESSAGES } from '../messages.js';
|
|
2
|
+
import { finding } from '../model.js';
|
|
3
|
+
// 파일 끝까지 닫히지 않은 HTML 주석이 있으면 주석이 시작한 줄마다 오류 하나다. 그 뒤의 내용은 check가 보지 못한다.
|
|
4
|
+
export function unterminatedComment(graph) {
|
|
5
|
+
const out = [];
|
|
6
|
+
for (const doc of graph.docs) {
|
|
7
|
+
if (doc.unterminatedComment === undefined)
|
|
8
|
+
continue;
|
|
9
|
+
out.push(finding('unterminated-comment', 'error', doc.file, MESSAGES[graph.lang].unterminatedComment, doc.unterminatedComment));
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=unterminated-comment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unterminated-comment.js","sourceRoot":"","sources":["../../../src/rules/unterminated-comment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,KAAY;IAC9C,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,mBAAmB,KAAK,SAAS;YAAE,SAAS;QACpD,GAAG,CAAC,IAAI,CACN,OAAO,CAAC,sBAAsB,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,mBAAmB,EAAE,GAAG,CAAC,mBAAmB,CAAC,CACtH,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/src/skills.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { skillsEn } from './templates/skills-en.js';
|
|
2
2
|
import { skillsKo } from './templates/skills-ko.js';
|
|
3
|
+
import { readPackageVersion } from './version.js';
|
|
3
4
|
// 스킬 이름. 디렉터리 이름이자 frontmatter의 name이다(6.3.1).
|
|
4
5
|
export const SKILL_NAMES = ['crumbs-writer', 'crumbs-finder', 'crumbs-interview', 'crumbs-ingest'];
|
|
5
6
|
// 도구별 스킬 루트(저장소 루트 기준). Codex 위치는 5.1 Q10에서 확인했다.
|
|
@@ -8,9 +9,19 @@ export const SKILL_ROOTS = { claude: '.claude/skills', codex: '.agents/skills' }
|
|
|
8
9
|
export function agentsOf(option) {
|
|
9
10
|
return option === 'all' ? ['claude', 'codex'] : [option];
|
|
10
11
|
}
|
|
12
|
+
// frontmatter 끝(두 번째 "---\n") 바로 뒤에 생성 표식 줄과 빈 줄을 넣는다. 어느 버전이 만들었는지와 갱신 방법을 적는다.
|
|
13
|
+
function withVersionMarker(text, agent, version) {
|
|
14
|
+
const end = text.indexOf('---\n', text.indexOf('---\n') + 4) + 4;
|
|
15
|
+
const marker = `<!-- generated by crumbs-spec ${version}. To upgrade, delete this file and run: npx crumbs-spec init --skills ${agent} -->`;
|
|
16
|
+
return `${text.slice(0, end)}${marker}\n${text.slice(end)}`;
|
|
17
|
+
}
|
|
11
18
|
// [저장소 기준 경로, 내용] 목록을 돌려준다. 순서는 agents 순서 × SKILL_NAMES 순서다. 파일은 쓰지 않는다.
|
|
12
19
|
export function skillFiles(agents, paths, lang) {
|
|
13
20
|
const skills = lang === 'en' ? skillsEn : skillsKo;
|
|
14
|
-
|
|
21
|
+
const version = readPackageVersion();
|
|
22
|
+
return agents.flatMap((agent) => SKILL_NAMES.map((name) => [
|
|
23
|
+
`${SKILL_ROOTS[agent]}/${name}/SKILL.md`,
|
|
24
|
+
withVersionMarker(skills[name](paths), agent, version),
|
|
25
|
+
]));
|
|
15
26
|
}
|
|
16
27
|
//# sourceMappingURL=skills.js.map
|
package/dist/src/skills.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/skills.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/skills.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,+CAA+C;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,eAAe,CAAU,CAAC;AAK5G,kDAAkD;AAClD,MAAM,CAAC,MAAM,WAAW,GAA+B,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;AAE7G,+BAA+B;AAC/B,MAAM,UAAU,QAAQ,CAAC,MAAkC;IACzD,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,iFAAiF;AACjF,SAAS,iBAAiB,CAAC,IAAY,EAAE,KAAiB,EAAE,OAAe;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,iCAAiC,OAAO,yEAAyE,KAAK,MAAM,CAAC;IAC5I,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,MAAoB,EAAE,KAAoB,EAAE,IAAU;IAC/E,MAAM,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IACnD,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9B,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAoB,EAAE,CAAC;QAC1C,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,WAAW;QACxC,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC;KACvD,CAAC,CACH,CAAC;AACJ,CAAC"}
|
package/dist/src/templates/en.js
CHANGED
|
@@ -11,7 +11,7 @@ document covers one area of the system, and each detail document owns one respon
|
|
|
11
11
|
<!-- One line per domain: - [Domain name](domain/${p.overview}) — one-line scope -->
|
|
12
12
|
|
|
13
13
|
## Records
|
|
14
|
-
- [Log](${p.log}) —
|
|
14
|
+
- [Log](${p.log}) — what was done to the documents, newest last; add an entry after applying source material or changing a document
|
|
15
15
|
`,
|
|
16
16
|
overview: (p) => `# <Domain name>
|
|
17
17
|
<!-- Read ../${p.index} first. After changing this document, add an entry to ../${p.log} -->
|
|
@@ -20,7 +20,7 @@ document covers one area of the system, and each detail document owns one respon
|
|
|
20
20
|
<The responsibility this area owns and what it does not cover, in one paragraph>
|
|
21
21
|
|
|
22
22
|
## Flow
|
|
23
|
-
<One Mermaid flowchart with one node per detail document. Arrows follow the order the user experiences. If there is only one document, "None">
|
|
23
|
+
<One Mermaid flowchart with one node per detail document. Arrows follow the order the user experiences. If there is only one document, write "None">
|
|
24
24
|
|
|
25
25
|
## Documents
|
|
26
26
|
- [<detail document title>](<file>.md) — <one-line responsibility>
|
|
@@ -43,7 +43,7 @@ document covers one area of the system, and each detail document owns one respon
|
|
|
43
43
|
|
|
44
44
|
## Flow
|
|
45
45
|
<If current behavior has two or more steps, one Mermaid diagram (flowchart; use stateDiagram-v2 for screen or state transitions).
|
|
46
|
-
A branch with two or more comparison axes, such as channel or state, goes in a table. If there is only one step, "None">
|
|
46
|
+
A branch with two or more comparison axes, such as channel or state, goes in a table. If there is only one step, write "None">
|
|
47
47
|
|
|
48
48
|
## Policies
|
|
49
49
|
### <policy name>
|
|
@@ -51,7 +51,7 @@ A branch with two or more comparison axes, such as channel or state, goes in a t
|
|
|
51
51
|
then write [needs confirmation] and the question on the next line>
|
|
52
52
|
|
|
53
53
|
## Evidence
|
|
54
|
-
- \`<path/to/file.ext>\` — <what it implements>
|
|
54
|
+
- \`<path/to/file.ext>\` — <what it implements> (the path must contain a "/"; write a root file as ./file)
|
|
55
55
|
|
|
56
56
|
## Open questions
|
|
57
57
|
- [needs confirmation] [<policy name>](#<policy-anchor>) — <question that would change behavior or scope>
|
|
@@ -62,7 +62,7 @@ then write [needs confirmation] and the question on the next line>
|
|
|
62
62
|
log: (today) => `# Spec log
|
|
63
63
|
|
|
64
64
|
A chronological record of what was done to the documents. The newest entry comes last.
|
|
65
|
-
Entry heading: \`## [YYYY-MM-DD] kind | title
|
|
65
|
+
Entry heading: \`## [YYYY-MM-DD] kind | title\`; kind is one of ingest, query, lint, drift, change, decision.
|
|
66
66
|
|
|
67
67
|
## [${today}] change | crumbs-spec initialized
|
|
68
68
|
`,
|
package/dist/src/templates/ko.js
CHANGED