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.
Files changed (49) hide show
  1. package/README.ko.md +58 -27
  2. package/README.md +117 -86
  3. package/dist/src/check.js +7 -5
  4. package/dist/src/check.js.map +1 -1
  5. package/dist/src/cli.js +2 -0
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/config.js +16 -1
  8. package/dist/src/config.js.map +1 -1
  9. package/dist/src/dates.js +7 -0
  10. package/dist/src/dates.js.map +1 -1
  11. package/dist/src/graph.js +75 -13
  12. package/dist/src/graph.js.map +1 -1
  13. package/dist/src/init.js +22 -6
  14. package/dist/src/init.js.map +1 -1
  15. package/dist/src/markdown.js +155 -25
  16. package/dist/src/markdown.js.map +1 -1
  17. package/dist/src/messages.js +38 -13
  18. package/dist/src/messages.js.map +1 -1
  19. package/dist/src/model.js +2 -3
  20. package/dist/src/model.js.map +1 -1
  21. package/dist/src/report.js +1 -1
  22. package/dist/src/report.js.map +1 -1
  23. package/dist/src/rules/broken-link.js +4 -1
  24. package/dist/src/rules/broken-link.js.map +1 -1
  25. package/dist/src/rules/duplicate-policy.js +11 -2
  26. package/dist/src/rules/duplicate-policy.js.map +1 -1
  27. package/dist/src/rules/log-body.js +3 -3
  28. package/dist/src/rules/log-body.js.map +1 -1
  29. package/dist/src/rules/log-format.js +6 -0
  30. package/dist/src/rules/log-format.js.map +1 -1
  31. package/dist/src/rules/missing-backlink.js +4 -1
  32. package/dist/src/rules/missing-backlink.js.map +1 -1
  33. package/dist/src/rules/missing-section.js +14 -2
  34. package/dist/src/rules/missing-section.js.map +1 -1
  35. package/dist/src/rules/unreachable.js +1 -1
  36. package/dist/src/rules/unreachable.js.map +1 -1
  37. package/dist/src/rules/unterminated-comment.js +13 -0
  38. package/dist/src/rules/unterminated-comment.js.map +1 -0
  39. package/dist/src/skills.js +12 -1
  40. package/dist/src/skills.js.map +1 -1
  41. package/dist/src/templates/en.js +5 -5
  42. package/dist/src/templates/ko.js +1 -1
  43. package/dist/src/templates/skills-en.js +14 -13
  44. package/dist/src/templates/skills-en.js.map +1 -1
  45. package/dist/src/templates/skills-ko.js +7 -6
  46. package/dist/src/templates/skills-ko.js.map +1 -1
  47. package/dist/src/version.js +7 -0
  48. package/dist/src/version.js.map +1 -0
  49. package/package.json +2 -1
@@ -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
- fileMissing: (where) => `crumbs.json not found in ${where}. Run "crumbs init" first.`,
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
- logDocMissing: (path) => `log document not found: ${path}. Run "crumbs init" first.`,
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
- summary: (documents, errors, warnings) => `crumbs check: ${documents} documents, ${errors} errors, ${warnings} warnings`,
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
- openQuestions: (count) => `${count} open question marker(s)`,
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
- fileMissing: (where) => `${where}에 crumbs.json이 없음. 먼저 "crumbs init"을 실행하세요.`,
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
- logDocMissing: (path) => `로그 문서가 없음: ${path}. 먼저 "crumbs init"을 실행하세요.`,
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
- openQuestions: (count) => `미해결 질문 표시 ${count}개`,
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}"이어야 함`,
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/messages.ts"],"names":[],"mappings":"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"}
package/dist/src/model.js CHANGED
@@ -1,5 +1,4 @@
1
- // line이 없는 발견은 line 키 자체를 두지 않아 JSON에서도 빠진다.
2
- export function finding(rule, severity, file, message, line) {
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
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/model.ts"],"names":[],"mappings":"AAoFA,6CAA6C;AAC7C,MAAM,UAAU,OAAO,CACrB,IAAY,EACZ,QAA6B,EAC7B,IAAY,EACZ,OAAe,EACf,IAAa;IAEb,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC1G,CAAC"}
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"}
@@ -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}${finding.line === undefined ? '' : `:${finding.line}`} ${finding.severity} ${finding.rule}: ${finding.message}`);
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;
@@ -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,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CACnI,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"}
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 (!isFile(link.path)) {
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;gBACvB,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
+ {"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,oDAAoD;AACpD,MAAM,UAAU,eAAe,CAAC,KAAY;IAC1C,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,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YAClC,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,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,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
+ {"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,wFAAwF;AACxF,8CAA8C;AAC9C,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,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,GAAG,CAClF,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
+ {"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;AAEpD,qFAAqF;AACrF,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,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;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"}
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
- out.push(finding('missing-backlink', 'error', doc.file, MESSAGES[graph.lang].backlinkMissing(parent.file)));
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;QAC9G,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
+ {"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
- for (const key of REQUIRED_SECTIONS[doc.role]) {
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]), doc.title?.line));
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;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAEpD,uDAAuD;AACvD,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,KAAK,MAAM,GAAG,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,IAAI,CACN,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,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CACtH,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,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,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAC1G,CAAC"}
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"}
@@ -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
- return agents.flatMap((agent) => SKILL_NAMES.map((name) => [`${SKILL_ROOTS[agent]}/${name}/SKILL.md`, skills[name](paths)]));
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
@@ -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;AAEpD,+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,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,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9B,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAoB,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC7G,CAAC;AACJ,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"}
@@ -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}) — read this index first; after reflecting source material or changing a document, add a log entry
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\`, kind ∈ ingest | query | lint | drift | change | decision.
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
  `,
@@ -50,7 +50,7 @@ export const ko = {
50
50
  다음 줄에 [확인 필요]와 질문을 적는다>
51
51
 
52
52
  ## 근거
53
- - \`<path/to/file.ext>\` — <구현하는 내용>
53
+ - \`<path/to/file.ext>\` — <구현하는 내용> (경로에는 /가 있어야 하며 루트 파일은 ./파일 로 쓴다)
54
54
 
55
55
  ## 확인 필요 사항
56
56
  - [확인 필요] [<정책 이름>](#<정책-앵커>) — <동작이나 범위를 바꾸는 질문>