crumbs-spec 0.1.0 → 0.3.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 (53) hide show
  1. package/README.ko.md +139 -101
  2. package/README.md +159 -121
  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 +81 -14
  12. package/dist/src/graph.js.map +1 -1
  13. package/dist/src/init.js +24 -8
  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 +40 -15
  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/agents.js +2 -2
  42. package/dist/src/templates/agents.js.map +1 -1
  43. package/dist/src/templates/en.js +5 -5
  44. package/dist/src/templates/en.js.map +1 -1
  45. package/dist/src/templates/ko.js +1 -1
  46. package/dist/src/templates/ko.js.map +1 -1
  47. package/dist/src/templates/skills-en.js +16 -15
  48. package/dist/src/templates/skills-en.js.map +1 -1
  49. package/dist/src/templates/skills-ko.js +10 -9
  50. package/dist/src/templates/skills-ko.js.map +1 -1
  51. package/dist/src/version.js +7 -0
  52. package/dist/src/version.js.map +1 -0
  53. package/package.json +3 -2
@@ -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"}
@@ -18,7 +18,7 @@ function agentsBlockKo(p) {
18
18
  - 모든 정책은 정확히 하나의 상세 문서가 소유한다. 아무도 문서 갱신을 요청하지 않았더라도
19
19
  코드와 같은 변경에서 그 문서를 갱신한다.
20
20
  - 변경이 아니라 현재 동작을 설명한다. 변경 이력이나 요구사항 ID를 넣지 않는다.
21
- - 근거로 삼은 코드를 "근거" 절에 적는다. 링크는 양방향으로 유지한다: 상세 문서는 개요와
21
+ - 근거로 삼은 코드를 "근거" 절에 \`crumbs.json\`의 \`codeRoot\` 기준 경로로 적고, 원본 자료는 \`${p.rawDir}/<파일>\`로 적는다. 링크는 양방향으로 유지한다: 상세 문서는 개요와
22
22
  관련 문서로 링크하고, 그 문서들도 되돌아오는 링크를 둔다.
23
23
  - 코드·테스트·승인된 자료로 확인할 수 없는 정책은 \`[확인 필요]\`와 질문을 적는다.
24
24
  추측하지 않는다.
@@ -42,7 +42,7 @@ Rules when behavior changes:
42
42
  - Every policy is owned by exactly one detail document. Even if nobody asks you to update the document,
43
43
  update it in the same change as the code.
44
44
  - Describe the current behavior, not the change. Do not add change history or requirement IDs.
45
- - List the code you relied on in the "Evidence" section. Keep links in both directions: detail documents link to the overview
45
+ - List the code you relied on in the "Evidence" section, with paths relative to \`codeRoot\` in \`crumbs.json\`; cite source material as \`${p.rawDir}/<file>\`. Keep links in both directions: detail documents link to the overview
46
46
  and related documents, and those documents link back.
47
47
  - If code, tests, or approved material cannot confirm a policy, write \`[needs confirmation]\` and a question.
48
48
  Do not guess.
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/templates/agents.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAC;AACpD,MAAM,CAAC,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAShD,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,CAAc,EAAE,IAAU;IACpD,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,aAAa,CAAC,CAAc;IACnC,OAAO,GAAG,YAAY;;;WAGb,CAAC,CAAC,OAAO;OACb,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,iBAAiB,CAAC,CAAC,MAAM;;;;;;;;;;;;;6BAavB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;;;;EAI7C,UAAU,EAAE,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,CAAc;IACnC,OAAO,GAAG,YAAY;;;8BAGM,CAAC,CAAC,OAAO;gBACvB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,yCAAyC,CAAC,CAAC,MAAM;;;;;;;;;;;;;4EAaT,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;;;EAG5F,UAAU,EAAE,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/templates/agents.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAC;AACpD,MAAM,CAAC,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAShD,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,CAAc,EAAE,IAAU;IACpD,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,aAAa,CAAC,CAAc;IACnC,OAAO,GAAG,YAAY;;;WAGb,CAAC,CAAC,OAAO;OACb,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,iBAAiB,CAAC,CAAC,MAAM;;;;;;;;;yEASqB,CAAC,CAAC,MAAM;;;;6BAIpD,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;;;;EAI7C,UAAU,EAAE,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,CAAc;IACnC,OAAO,GAAG,YAAY;;;8BAGM,CAAC,CAAC,OAAO;gBACvB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,yCAAyC,CAAC,CAAC,MAAM;;;;;;;;;6IASwD,CAAC,CAAC,MAAM;;;;4EAIzE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;;;EAG5F,UAAU,EAAE,CAAC;AACf,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> (code paths are relative to codeRoot in crumbs.json and source material is written as ${p.rawDir}/<file>; 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
  `,
@@ -1 +1 @@
1
- {"version":3,"file":"en.js","sourceRoot":"","sources":["../../../src/templates/en.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,sDAAsD;AACtD,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,KAAK,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;mDAOY,CAAC,CAAC,QAAQ;;;UAGnD,CAAC,CAAC,GAAG;CACd;IAEC,QAAQ,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;eAC3B,CAAC,CAAC,KAAK,4DAA4D,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;oBAgBnE,CAAC,CAAC,KAAK;CAC1B;IAEC,MAAM,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;eACzB,CAAC,CAAC,KAAK,4DAA4D,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;wBAwB/D,CAAC,CAAC,QAAQ;CACjC;IAEC,GAAG,EAAE,CAAC,KAAa,EAAU,EAAE,CAAC;;;;;MAK5B,KAAK;CACV;IAEC,SAAS,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;0EAG+B,CAAC,CAAC,OAAO;CAClF;CACA,CAAC"}
1
+ {"version":3,"file":"en.js","sourceRoot":"","sources":["../../../src/templates/en.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,sDAAsD;AACtD,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,KAAK,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;mDAOY,CAAC,CAAC,QAAQ;;;UAGnD,CAAC,CAAC,GAAG;CACd;IAEC,QAAQ,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;eAC3B,CAAC,CAAC,KAAK,4DAA4D,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;oBAgBnE,CAAC,CAAC,KAAK;CAC1B;IAEC,MAAM,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;eACzB,CAAC,CAAC,KAAK,4DAA4D,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;;wIAkBiD,CAAC,CAAC,MAAM;;;;;;wBAMxH,CAAC,CAAC,QAAQ;CACjC;IAEC,GAAG,EAAE,CAAC,KAAa,EAAU,EAAE,CAAC;;;;;MAK5B,KAAK;CACV;IAEC,SAAS,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;0EAG+B,CAAC,CAAC,OAAO;CAClF;CACA,CAAC"}
@@ -50,7 +50,7 @@ export const ko = {
50
50
  다음 줄에 [확인 필요]와 질문을 적는다>
51
51
 
52
52
  ## 근거
53
- - \`<path/to/file.ext>\` — <구현하는 내용>
53
+ - \`<path/to/file.ext>\` — <구현하는 내용> (코드 경로는 crumbs.json의 codeRoot 기준이고 원본 자료는 ${p.rawDir}/<파일>로 쓴다. 경로에는 /가 있어야 하며 루트 파일은 ./파일 로 쓴다)
54
54
 
55
55
  ## 확인 필요 사항
56
56
  - [확인 필요] [<정책 이름>](#<정책-앵커>) — <동작이나 범위를 바꾸는 질문>
@@ -1 +1 @@
1
- {"version":3,"file":"ko.js","sourceRoot":"","sources":["../../../src/templates/ko.ts"],"names":[],"mappings":"AAQA,qDAAqD;AACrD,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,KAAK,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;oCAOH,CAAC,CAAC,QAAQ;;;SAGrC,CAAC,CAAC,GAAG;CACb;IAEC,QAAQ,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;UAChC,CAAC,CAAC,KAAK,2BAA2B,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;gBAgBjC,CAAC,CAAC,KAAK;CACtB;IAEC,MAAM,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;UAC9B,CAAC,CAAC,KAAK,2BAA2B,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;eAwBlC,CAAC,CAAC,QAAQ;CACxB;IAEC,GAAG,EAAE,CAAC,KAAa,EAAU,EAAE,CAAC;;;;;MAK5B,KAAK;CACV;IAEC,SAAS,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;iCAGV,CAAC,CAAC,OAAO;;CAEzC;CACA,CAAC"}
1
+ {"version":3,"file":"ko.js","sourceRoot":"","sources":["../../../src/templates/ko.ts"],"names":[],"mappings":"AAQA,qDAAqD;AACrD,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,KAAK,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;oCAOH,CAAC,CAAC,QAAQ;;;SAGrC,CAAC,CAAC,GAAG;CACb;IAEC,QAAQ,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;UAChC,CAAC,CAAC,KAAK,2BAA2B,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;gBAgBjC,CAAC,CAAC,KAAK;CACtB;IAEC,MAAM,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;UAC9B,CAAC,CAAC,KAAK,2BAA2B,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;;iFAkBgC,CAAC,CAAC,MAAM;;;;;;eAM1E,CAAC,CAAC,QAAQ;CACxB;IAEC,GAAG,EAAE,CAAC,KAAa,EAAU,EAAE,CAAC;;;;;MAK5B,KAAK;CACV;IAEC,SAAS,EAAE,CAAC,CAAgB,EAAU,EAAE,CAAC;;;iCAGV,CAAC,CAAC,OAAO;;CAEzC;CACA,CAAC"}
@@ -13,7 +13,7 @@ Writes or updates a living spec document that explains one responsibility. The d
13
13
 
14
14
  1. \`${p.specDir}/${p.index}\`. Always read it first. It lists the domains and overview documents.
15
15
  2. The \`${p.overview}\` of the domain the request touches, then the detail documents and their "Related" sections.
16
- 3. \`crumbs.json\`. If \`specDir\`, \`overview\`, \`log\`, \`rawDir\`, or \`headings\` differ from the defaults, use those values.
16
+ 3. \`crumbs.json\`. If \`specDir\`, \`overview\`, \`log\`, \`rawDir\`, or \`headings\` differ from the defaults, use those values. Code and test paths under "Evidence" are relative to \`codeRoot\` (the repository root by default); source material under \`${p.rawDir}/\` is always written from the repository root.
17
17
  4. \`${p.specDir}/templates/detail.md\` and \`${p.specDir}/templates/overview.md\`. Start new documents from these templates.
18
18
 
19
19
  ## Choose a mode
@@ -28,8 +28,8 @@ Writes or updates a living spec document that explains one responsibility. The d
28
28
  1. Find the owning document. Go down from the index to the overview to the detail document. Find the document whose "Evidence" lists the changed code path with \`grep -rl '<path>' ${p.specDir}\`, and find the changed policy name with \`grep -rn '^### ' ${p.specDir}\`. If an API number or endpoint changed, also find the owning document under \`${p.specDir}/api/\`.
29
29
  2. If more than one document looks like the owner, stop and ask which one owns it. Do not write one policy in two documents.
30
30
  3. Rewrite "Current behavior" as the behavior after the change. Do not add historical narration such as "previously … now …", change dates, or requirement IDs. If the diagram or table under "Flow" holds that behavior, update it too.
31
- 4. Under "Policies", edit the body of each changed policy, add a new policy as \`### policy name\`, and delete a policy that was removed. If a policy with the same name already exists in another document, that document owns it: edit it there and keep only a link in this document. A confirmed policy may end with one decision link, \`(decision: [YYYY-MM-DD](../${p.log}#<entry-anchor>))\`, after its last sentence. This is the only exception to the rule against change history. Do not write the reason or the source; the link text carries only the decision date. The entry anchor is the slug of the \`decision\` entry title (for \`## [2026-11-02] decision | Session lifetime: 30 days\` the slug is \`2026-11-02-decision-session-lifetime-30-days\`).
32
- 5. Update the code and test paths under "Evidence" to match the changed code. Write each path as a backtick path relative to the repository root. If a line range is needed, append it like \`src/auth/session.ts:12-48\`. Attach only one range to each path; if one file has several ranges, list them on separate lines.
31
+ 4. Under "Policies", edit the body of each changed policy, add a new policy as \`### policy name\`, and delete a policy that was removed. If a policy with the same name already exists in another document, that document owns it: edit it there and keep only a link in this document. A confirmed policy may end with one decision link, \`(decision: [YYYY-MM-DD](../${p.log}#<entry-anchor>))\`, after its last sentence. This is the only exception to the rule against change history. Do not write the reason or the source; the link text carries only the decision date. The entry anchor is the slug of the \`decision\` entry title (format in step 1 of "Document existing code").
32
+ 5. Update the code and test paths under "Evidence" to match the changed code. Write each path as a backtick path relative to \`codeRoot\` in \`crumbs.json\` (the repository root by default). If a line range is needed, append it like \`src/auth/session.ts:12-48\`. Attach only one range to each path; if one file has several ranges, list them on separate lines. A path must contain a "/"; write a repository-root file as \`./package.json\`.
33
33
  6. Do not describe a policy that code, tests, or material the user supplied does not confirm. Write it as \`[needs confirmation]\` and a question, in the format of step 5 of "Document existing code".
34
34
  7. Keep links in both directions. If you added a link to another detail document, add the link back under that document's "Related". If a document's title or scope changed, update its one-line entry in the overview's "Documents" and its line in the index.
35
35
  8. Add a log entry. The title is one sentence stating the user-visible result, not the task name. The body is written in the order \`Result:\`, \`Reason:\`, \`Document:\` and does not list files. If the same task touched several documents, group it into one entry per domain and give the count under \`Result:\`.
@@ -48,6 +48,7 @@ The only sources are code and tests. Do not read or copy other explanatory docum
48
48
  - If a domain directory has more than 15 detail documents, group the ones with close flows into subdirectories and put a \`${p.overview}\` in each subdirectory too (the check rules support nested overviews: the parent of a sub-overview is the upper overview).
49
49
  - Server API and SDK contracts (request and response fields, the meaning of response and error codes, retry, idempotency, authentication) are owned by \`${p.specDir}/api/<endpoint>.md\`, not by a screen document. \`api/\` is one domain with its own overview; if an API number exists, put it in the file name and the policy name. A screen document lists only the user-visible result for each code and links to the policy in that api/ document. Common transport contracts such as headers, authentication, cookies, and retries live once, in one detail document inside \`api/\`. Whenever a screen document cites an API number, check that the api/ document exists; if it does not, create it from the same template.
50
50
  - Concept pages. \`${p.specDir}/concepts/<concept>.md\` owns a concept when a flow in two or more domains uses the same rule or value table, when a code decision criterion (flag or enum) differs from the user's wording so that it has to be explained each time, or when flow documents use a concept as a branch axis (channel, member state, currency type). A term that has a definition but no rules does not get a page; keep it as one line under the overview's "Terms". If \`concepts/\` does not exist, create it like a domain and add \`- [Concepts](concepts/${p.overview}) — definitions and cross-cutting rules for concepts shared by several domains\` to the index.
51
+ - Decision link format (shared by every skill): a confirmed policy may end with \`(decision: [YYYY-MM-DD](../${p.log}#<slug>))\`. The slug is the entry heading text after "## ", lowercased, with punctuation other than "-" and "_" removed and each space replaced by "-"; for \`## [2026-11-02] decision | Session lifetime: 30 days\` it is \`2026-11-02-decision--session-lifetime-30-days\`.
51
52
  2. Start from the entry point (a route, handler, command, or screen), follow the calls while reading the code path, and check the expected behavior in the tests that cover the same path. Record only the paths that are the evidence for "Current behavior" or "Policies". Files from other areas that you read for context do not go into "Evidence".
52
53
  3. Place the file.
53
54
  - Domain directory \`${p.specDir}/<domain>/\`. If it does not exist, create it and copy \`${p.specDir}/templates/overview.md\` to make \`${p.overview}\`.
@@ -58,8 +59,8 @@ The only sources are code and tests. Do not read or copy other explanatory docum
58
59
  - "Current behavior": if there are three or more steps, give each step a \`### step name\` subheading and write bullets under it. Describe what the user experiences, in the present tense. Explain the result, not the code structure.
59
60
  - Do not drop values that carry conditions: if a width, size, or time has a branch condition (for example, if the card width is narrower than 220, the cards stack vertically and the inner margin shrinks), write the condition and the result together. Write every branch that splits by channel, platform, permission, or login state (for example, channels A and B always, iOS only inside the review build, channel C does not show it). Values that only decorate without a condition, such as colors, fixed margins, or animations, may be omitted.
60
61
  - Sentence rules: one rule per bullet, one condition and one result per sentence. Put the default behavior first and the exceptions and branches after it. Within a sentence, put the condition first (for example, if the user is not logged in, show the guidance screen).
61
- - Wording rules: write screen text exactly as it appears, inside quotation marks. Do not alternate between two names for the same thing; use one name throughout the document. Spell out a term in parentheses once, at its first use. For a concept that has its own page, link it at first use, as in \`[Channel](../concepts/channel.md)\`, do not define it again, and use the value names from that page exactly in the rows of branch tables.
62
- - Style: the body is declarative sentences, and each bullet is a complete sentence. Do not mix styles. Keep the subject as the actor (user, app, server, SDK). Do not use passive voice or translated phrasing such as "is made to be ...", double negatives, or future-tense narration such as "soon", "planned", or "going forward".
62
+ - Wording rules: write screen text exactly as it appears, inside quotation marks. Do not alternate between two names for the same thing; use one name throughout the document. Expand an abbreviation or alias once, in parentheses, at its first use. For a concept that has its own page, link it at first use, as in \`[Channel](../concepts/channel.md)\`, do not define it again, and use the value names from that page exactly in the rows of branch tables.
63
+ - Style: the body is declarative sentences, and each bullet is a complete sentence. Do not mix styles. Keep the subject as the actor (user, app, server, SDK). When the actor is known, do not use the passive voice. Do not use double negatives or future-tense narration such as "soon", "planned", or "going forward".
63
64
  - Quantities and filler: write quantities as numbers with "or more", "or less", "more than", or "fewer than"; do not write "many", "etc.", or "other". Remove words that add no meaning, such as "simply", "easily", or "note that".
64
65
  - Subheadings: a \`###\` is a noun phrase naming a user action or result. Do not use generic names such as "Other" or "Note", or directional phrases such as "above" or "below". Do not use the same subheading twice in one document.
65
66
  - If a bullet has more than three sentences, or a list inside one bullet has more than four items, split it into bullets or \`###\` subheadings.
@@ -73,7 +74,7 @@ The only sources are code and tests. Do not read or copy other explanatory docum
73
74
  - Code structure and call order are not rules. What the user sees on screen, such as layout, goes into "Current behavior"; implementation structure goes into the explanation under "Evidence". Write numbers, deadlines, and conditions exactly as read from the code.
74
75
  - A rule that exists only in code and has no tests is written as in step 5. When the tests confirm only part of a rule, split it into a confirmed policy for the confirmed part and a question policy for the rest.
75
76
  - A policy name must be unique across the repository (the \`duplicate-policy\` check). If the same name might appear in another domain, put the subject in the name (for example, "Email signup expiry notice", not "Expiry notice"). The name must read correctly without context and must not be a question.
76
- - "Evidence": list the code and test paths you read as backtick paths, and after \` — \` write what that path implements. Do not use backticks in the explanation (identifiers such as \`Cart.add\` are also checked as paths).
77
+ - "Evidence": list the code and test paths you read as backtick paths that contain a "/", and after \` — \` write what that path implements. Do not put identifiers in backticks in the explanation; \`check\` ignores spans without a "/", but identifiers belong in plain text.
77
78
  - "Open questions": every question that the code and tests do not answer. The format is in step 5.
78
79
  - "Related": one link to the overview, and the other detail documents that share the same flow.
79
80
  5. Write any unconfirmed policy as a question, not as a statement. If a branch exists but you cannot tell why, there is no test, or you do not know the basis of a constant, write \`### policy name\` (the heading is the policy name, not a question), then under it write in the first paragraph the current behavior you read in the code as declarative sentences, using several sentences if needed so that no condition, number, or branch result is lost (one paragraph). On the next line write \`[needs confirmation] <question>\`. Ask about what you do not know, as is (for example: is this intended, and if not, what should it be? / what is the basis of this constant? / which is right, the comment or the code?). Do not turn a question about basis or alternatives into a question about intent. This description is an observation, not a confirmed fact, so never leave it without the marker line. Under "Open questions" put \`- [needs confirmation] [policy name](#policy-name) — <same question>\` (the link is checked by the broken-anchor rule of \`check\`). Do not invent a plausible value or intent.
@@ -95,7 +96,7 @@ The only sources are code and tests. Do not read or copy other explanatory docum
95
96
 
96
97
  ## Before finishing
97
98
 
98
- 1. Run \`npx crumbs-spec check\` and fix every error. A \`open-question\` warning is the result of leaving questions, so leave it as is. A \`missing-flow\` warning means "Current behavior" has six or more steps but neither a diagram nor a table, so remove it by filling in "Flow". A \`section-order\` warning means the section order differs from the template, so remove it by moving the sections.
99
+ 1. Run \`npx crumbs-spec check\` and fix every error. An \`open-question\` warning means the document still has open questions; leave it as is. A \`missing-flow\` warning means "Current behavior" has six or more steps but neither a diagram nor a table, so remove it by filling in "Flow". A \`section-order\` warning means the section order differs from the template, so remove it by moving the sections.
99
100
  2. Review again: every policy you added or changed is traceable to a path under "Evidence" or is a \`[needs confirmation]\` question; every rule verified by the tests named under "Evidence" appears in some \`###\` policy; and every branch in the code you read that splits by channel, platform, permission, or login state appears in some sentence of "Current behavior".
100
101
  3. Confirm that the log entry was added with \`grep '^## \\[' ${p.specDir}/${p.log} | tail -3\`.
101
102
  `;
@@ -115,7 +116,7 @@ Finds an answer in the spec documents and answers with the source attached. As s
115
116
  3. Read "Documents" in that domain's \`${p.overview}\` and pick the detail document whose responsibility matches. If the question's wording appears as an alias under "Terms", the link on that line is the owning document.
116
117
  4. Read "Current behavior" and "Policies" in the detail document. If the policy's \`### heading\` is here, this document is the owner. Stop here.
117
118
  5. If not, follow the document's "Related" links one step only. If it is still not there, the question is not in the documents. Do not search every other domain; answer "not in the documents".
118
- 6. If the documents are silent and the user still wants an answer, read the code paths under "Evidence" and answer. State that the answer came from code.
119
+ 6. If the documents are silent and the user still wants an answer, read the code paths under "Evidence" (relative to \`codeRoot\` in \`crumbs.json\`) and answer. State that the answer came from code.
119
120
 
120
121
  ## How to answer
121
122
 
@@ -126,7 +127,7 @@ Finds an answer in the spec documents and answers with the source attached. As s
126
127
  - If neither the documents nor the code contain it, answer "None". Do not invent a plausible answer.
127
128
  - Give the answer first and the source after it. For one policy, use one paragraph of five sentences or fewer; for several policies, one bullet per policy; if there are two or more comparison axes, answer in a table.
128
129
  - Answer with the document's standard term. If the asker used a different expression, connect the two once in parentheses in the first sentence (for example, Points (miles)). Spell out abbreviations at their first use.
129
- - Do not rely on the conversation's position, such as "as mentioned above" or "the document below"; point to the path every time.
130
+ - Do not refer to earlier turns ("as mentioned above", "the document below"); point to the path every time.
130
131
 
131
132
  ## Feedback (write the answer back into the document)
132
133
 
@@ -162,7 +163,7 @@ description: Before writing or changing a crumbs spec document, lists every poli
162
163
 
163
164
  # crumbs-interview
164
165
 
165
- Reveal what is unknown before writing a document. Do not describe unconfirmed policies; leave them as questions, and do not start writing that changes scope until the answers arrive.
166
+ Surface what is unknown before a document is written. Do not describe unconfirmed policies; leave them as questions, and do not start writing that changes scope until the answers arrive.
166
167
 
167
168
  ## Procedure
168
169
 
@@ -194,7 +195,7 @@ Write the question list in the same format as the "Open questions" section so th
194
195
  - [needs confirmation] Session expiry notice — Is the user warned before the session expires, and if so, how many days before? (owner: auth/login.md, new)
195
196
  \`\`\`
196
197
 
197
- This format matches the \`open-question\` count of \`crumbs check\`. When it is copied into a document, the number of questions is counted as is.
198
+ Each question becomes one line of the document's "Open questions" section; \`crumbs check\` reports one \`open-question\` warning per document that still has questions.
198
199
 
199
200
  ## Criteria for choosing questions
200
201
 
@@ -205,12 +206,12 @@ This format matches the \`open-question\` count of \`crumbs check\`. When it is
205
206
  ## Do not
206
207
 
207
208
  - This skill does not change documents. It outputs only the table and the questions.
208
- - Do not leave a log entry. crumbs-writer records the answers in the log as it reflects them. When a question is confirmed, leave a \`decision\` entry whose body is written in the order \`Result:\`, \`Reason:\`, \`Document:\`.
209
+ - Do not leave a log entry. crumbs-writer records the answers as it reflects them, including the \`decision\` entry (body in the order \`Result:\`, \`Reason:\`, \`Document:\`) and the decision link at the end of the confirmed policy (format in step 1 of crumbs-writer "Document existing code").
209
210
  - Do not put a default value into an unconfirmed policy and propose fixing it later.
210
211
 
211
212
  ## Before finishing
212
213
 
213
- If you did not change any document, there is nothing to run. If the user told you to proceed with the marks left in place and you wrote the document, run \`npx crumbs-spec check\` and fix every error. When a question policy is confirmed, leave a \`decision\` entry, and put a decision link at the end of the confirmed policy text.
214
+ If you did not change any document, there is nothing to run. If the user told you to proceed with the marks left in place and you wrote the document, run \`npx crumbs-spec check\` and fix every error. When a question policy is confirmed, crumbs-writer leaves the \`decision\` entry and the decision link.
214
215
  `;
215
216
  const ingest = (p) => `---
216
217
  name: crumbs-ingest
@@ -219,7 +220,7 @@ description: Reads newly placed source material under ${p.rawDir}/ (contracts, p
219
220
 
220
221
  # crumbs-ingest
221
222
 
222
- Reflects one source material file in the spec documents. The material is read only and is never edited. Show what will change and get confirmation before changing any document. Process one file at a time.
223
+ Applies one source file to the spec documents. The material is read only and is never edited. Show what will change and get confirmation before changing any document. Process one file at a time.
223
224
 
224
225
  ## Procedure
225
226
 
@@ -232,7 +233,7 @@ Reflects one source material file in the spec documents. The material is read on
232
233
  - From the "Related" section of each document you found, check one more step for documents the change carries to.
233
234
  4. Make the change list and show it. For each item, give the owning document, the section ("Current behavior" or \`### policy name\`), the current wording → the wording the material implies, and the part of the material that is the evidence. If the material conflicts with a current document, mark it as a conflict and ask which is right. If the material states a new policy that no document has, mark it "new" and propose which document should own it.
234
235
  5. Wait for confirmation. Do not change documents without it. If only some items are approved, reflect only the approved items.
235
- 6. Update the owning documents. Edit only one owning document per policy; in other documents keep only a link to that policy. When a question policy is confirmed, leave a \`decision\` entry and put a decision link at the end of the confirmed policy text. If the material confirms a value, write it as a statement; if the material is ambiguous, write it as a \`[needs confirmation]\` question (format in step 5 of crumbs-writer "Document existing code"). Cite the material in "Evidence" as a backtick path (\`${p.rawDir}/<file>\` — \`check\` confirms that it exists). If you need to point to a specific part of the material, give the page or section after \` — \`.
236
+ 6. Update the owning documents. Edit only one owning document per policy; in other documents keep only a link to that policy. When a question policy is confirmed, leave a \`decision\` entry and put a decision link at the end of the confirmed policy text (format in step 1 of crumbs-writer "Document existing code"). If the material confirms a value, write it as a statement; if the material is ambiguous, write it as a \`[needs confirmation]\` question (format in step 5 of crumbs-writer "Document existing code"). Cite the material in "Evidence" as a backtick path (\`${p.rawDir}/<file>\`, written from the repository root even when \`codeRoot\` is set — \`check\` confirms that it exists). If you need to point to a specific part of the material, give the page or section after \` — \`.
236
237
  7. If the scope changed, update the one-line entry in the overview's "Documents" and in the index. If you created a new document, follow steps 3 to 6 of crumbs-writer "Document existing code": start from the template and link it from the overview and the index.
237
238
  8. Add a log entry. Name the material file and the changed document in the body.
238
239
  \`npx crumbs-spec log add --kind ingest "<one sentence stating the user-visible result>" --body "Result: <changed user-visible result>. Reason: ${p.rawDir}/<file>. Document: [<policy name>](<domain>/<document>.md#<anchor>)"\`
@@ -1 +1 @@
1
- {"version":3,"file":"skills-en.js","sourceRoot":"","sources":["../../../src/templates/skills-en.ts"],"names":[],"mappings":"AAGA,qFAAqF;AACrF,kFAAkF;AAElF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;yFAE4C,CAAC,CAAC,OAAO;;;;;;;;;OAS3F,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK;WAChB,CAAC,CAAC,QAAQ;;OAEd,CAAC,CAAC,OAAO,gCAAgC,CAAC,CAAC,OAAO;;;;;;;;;;;sLAW6H,CAAC,CAAC,OAAO,gEAAgE,CAAC,CAAC,OAAO,mFAAmF,CAAC,CAAC,OAAO;;;2WAGO,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;gIAiBhP,CAAC,CAAC,QAAQ;8JACoB,CAAC,CAAC,OAAO;wBAC/I,CAAC,CAAC,OAAO,ihBAAihB,CAAC,CAAC,QAAQ;;;0BAGliB,CAAC,CAAC,OAAO,4DAA4D,CAAC,CAAC,OAAO,sCAAsC,CAAC,CAAC,QAAQ;gCACxH,CAAC,CAAC,OAAO;0BACf,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;mJA0BgH,CAAC,CAAC,QAAQ,6CAA6C,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,+DAA+D,CAAC,CAAC,KAAK;;;;;;;;;;;8BAWtQ,CAAC,CAAC,MAAM;;;;;;;;gEAQ0B,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;CACjF,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;iFAEoC,CAAC,CAAC,OAAO;;;;;;;;;YAS9E,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,+HAA+H,CAAC,CAAC,OAAO;+GACzD,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,QAAQ;yCACtG,CAAC,CAAC,QAAQ;;;;;;;8EAO2B,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCtF,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;;;;;YAWpC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,QAAQ;;;gDAGZ,CAAC,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CvD,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;wDAEW,CAAC,CAAC,MAAM;;;;;;;;;sFASsB,CAAC,CAAC,MAAM,kEAAkE,CAAC,CAAC,MAAM;;;gBAGxJ,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,qBAAqB,CAAC,CAAC,QAAQ;6EACU,CAAC,CAAC,OAAO;oIAC8C,CAAC,CAAC,OAAO;;;;8fAIiX,CAAC,CAAC,MAAM;;;qJAGjX,CAAC,CAAC,MAAM;;;;;;;+CAO9G,CAAC,CAAC,MAAM;;;;;;CAMtD,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,MAAM,QAAQ,GAAoD;IACvE,eAAe,EAAE,MAAM;IACvB,eAAe,EAAE,MAAM;IACvB,kBAAkB,EAAE,SAAS;IAC7B,eAAe,EAAE,MAAM;CACxB,CAAC"}
1
+ {"version":3,"file":"skills-en.js","sourceRoot":"","sources":["../../../src/templates/skills-en.ts"],"names":[],"mappings":"AAGA,qFAAqF;AACrF,kFAAkF;AAElF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;yFAE4C,CAAC,CAAC,OAAO;;;;;;;;;OAS3F,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK;WAChB,CAAC,CAAC,QAAQ;iQAC4O,CAAC,CAAC,MAAM;OAClQ,CAAC,CAAC,OAAO,gCAAgC,CAAC,CAAC,OAAO;;;;;;;;;;;sLAW6H,CAAC,CAAC,OAAO,gEAAgE,CAAC,CAAC,OAAO,mFAAmF,CAAC,CAAC,OAAO;;;2WAGO,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;gIAiBhP,CAAC,CAAC,QAAQ;8JACoB,CAAC,CAAC,OAAO;wBAC/I,CAAC,CAAC,OAAO,ihBAAihB,CAAC,CAAC,QAAQ;kHAC1c,CAAC,CAAC,GAAG;;;0BAG7F,CAAC,CAAC,OAAO,4DAA4D,CAAC,CAAC,OAAO,sCAAsC,CAAC,CAAC,QAAQ;gCACxH,CAAC,CAAC,OAAO;0BACf,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;mJA0BgH,CAAC,CAAC,QAAQ,6CAA6C,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,+DAA+D,CAAC,CAAC,KAAK;;;;;;;;;;;8BAWtQ,CAAC,CAAC,MAAM;;;;;;;;gEAQ0B,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;CACjF,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;iFAEoC,CAAC,CAAC,OAAO;;;;;;;;;YAS9E,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,+HAA+H,CAAC,CAAC,OAAO;+GACzD,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,QAAQ;yCACtG,CAAC,CAAC,QAAQ;;;;;;;8EAO2B,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCtF,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;;;;;YAWpC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,QAAQ;;;gDAGZ,CAAC,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CvD,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;wDAEW,CAAC,CAAC,MAAM;;;;;;;;;sFASsB,CAAC,CAAC,MAAM,kEAAkE,CAAC,CAAC,MAAM;;;gBAGxJ,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,qBAAqB,CAAC,CAAC,QAAQ;6EACU,CAAC,CAAC,OAAO;oIAC8C,CAAC,CAAC,OAAO;;;;2jBAI8a,CAAC,CAAC,MAAM;;;qJAG9a,CAAC,CAAC,MAAM;;;;;;;+CAO9G,CAAC,CAAC,MAAM;;;;;;CAMtD,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,MAAM,QAAQ,GAAoD;IACvE,eAAe,EAAE,MAAM;IACvB,eAAe,EAAE,MAAM;IACvB,kBAAkB,EAAE,SAAS;IAC7B,eAAe,EAAE,MAAM;CACxB,CAAC"}
@@ -14,7 +14,7 @@ description: crumbs 스펙 문서(${p.specDir}/ 아래의 책임 문서)를 쓰
14
14
 
15
15
  1. \`${p.specDir}/${p.index}\`. 매번 먼저 읽는다. 도메인과 개요 문서의 목록이다.
16
16
  2. 요청이 건드리는 도메인의 \`${p.overview}\`, 그다음 상세 문서와 그 문서의 "관련 문서".
17
- 3. \`crumbs.json\`. \`specDir\`, \`overview\`, \`log\`, \`rawDir\`, \`headings\`가 기본값과 다르면 그 값을 쓴다.
17
+ 3. \`crumbs.json\`. \`specDir\`, \`overview\`, \`log\`, \`rawDir\`, \`headings\`가 기본값과 다르면 그 값을 쓴다. "근거"의 코드·테스트 경로는 \`codeRoot\`(기본값은 저장소 루트) 기준이고, \`${p.rawDir}/\` 아래 원본 자료는 언제나 저장소 루트 기준으로 쓴다.
18
18
  4. \`${p.specDir}/templates/detail.md\`와 \`${p.specDir}/templates/overview.md\`. 새 문서는 이 템플릿에서 시작한다.
19
19
 
20
20
  ## 모드 고르기
@@ -29,8 +29,8 @@ description: crumbs 스펙 문서(${p.specDir}/ 아래의 책임 문서)를 쓰
29
29
  1. 소유 문서를 찾는다. 인덱스 → 개요 → 상세 순으로 내려가고, 바뀐 코드 경로를 "근거"에 적은 문서를 \`grep -rl '<경로>' ${p.specDir}\`로, 바뀐 정책 이름을 \`grep -rn '^### ' ${p.specDir}\`로 찾는다. API 번호나 엔드포인트가 바뀌었으면 \`${p.specDir}/api/\`의 소유 문서도 찾는다.
30
30
  2. 소유 문서가 둘 이상으로 보이면 멈추고 어느 문서가 소유자인지 묻는다. 정책 하나를 두 문서에 적지 않는다.
31
31
  3. "현재 동작"을 바뀐 뒤의 동작으로 고쳐 쓴다. "이전에는 … 지금은 …" 같은 이력 서술, 변경 날짜, 요구사항 ID를 넣지 않는다. "흐름"의 도식·표가 그 동작을 담고 있으면 같이 고친다.
32
- 4. "정책"에서 바뀐 정책의 본문을 고치고, 새 정책은 \`### 정책 이름\`으로 더하고, 없어진 정책은 지운다. 같은 이름의 정책이 다른 문서에 이미 있으면 그 문서가 소유자이므로 거기서 고치고, 이 문서에는 링크만 둔다. 확정된 정책은 본문 마지막 문장 뒤에 결정 링크 \`(결정: [YYYY-MM-DD](../${p.log}#<항목 앵커>))\`를 하나 둘 수 있다. 이것이 변경 이력을 넣지 않는다는 규칙의 유일한 예외다. 이유와 원본은 적지 않고, 링크 글자에 결정 날짜만 쓴다. 항목 앵커는 \`decision\` 항목 제목의 슬러그다(\`## [2026-11-02] decision | 세션 유효 기간: 30일\`이면 \`2026-11-02-decision-세션-유효-기간-30일\`).
33
- 5. "근거"의 코드·테스트 경로를 바뀐 코드에 맞춘다. 경로는 저장소 루트 기준 백틱 경로로 적고, 줄 범위가 필요하면 \`src/auth/session.ts:12-48\`처럼 붙인다. 범위는 경로 하나에 하나만 붙이고, 한 파일에 범위가 여럿이면 줄을 나눠 적는다.
32
+ 4. "정책"에서 바뀐 정책의 본문을 고치고, 새 정책은 \`### 정책 이름\`으로 더하고, 없어진 정책은 지운다. 같은 이름의 정책이 다른 문서에 이미 있으면 그 문서가 소유자이므로 거기서 고치고, 이 문서에는 링크만 둔다. 확정된 정책은 본문 마지막 문장 뒤에 결정 링크 \`(결정: [YYYY-MM-DD](../${p.log}#<항목 앵커>))\`를 하나 둘 수 있다. 이것이 변경 이력을 넣지 않는다는 규칙의 유일한 예외다. 이유와 원본은 적지 않고, 링크 글자에 결정 날짜만 쓴다. 항목 앵커는 \`decision\` 항목 제목의 슬러그다(형식은 "기존 코드 문서화" 1단계).
33
+ 5. "근거"의 코드·테스트 경로를 바뀐 코드에 맞춘다. 경로는 \`crumbs.json\`의 \`codeRoot\`(기본값은 저장소 루트) 기준 백틱 경로로 적고, 줄 범위가 필요하면 \`src/auth/session.ts:12-48\`처럼 붙인다. 범위는 경로 하나에 하나만 붙이고, 한 파일에 범위가 여럿이면 줄을 나눠 적는다. 경로에는 \`/\`가 있어야 하며 루트 파일은 \`./package.json\`처럼 쓴다.
34
34
  6. 코드·테스트·사용자가 준 자료로 확인되지 않은 정책은 서술하지 않고 "기존 코드 문서화" 5번 형식대로 \`[확인 필요]\`와 질문으로 적는다.
35
35
  7. 링크를 양방향으로 맞춘다. 다른 상세 문서로 링크를 더했으면 그 문서의 "관련 문서"에 되돌아오는 링크를 더한다. 문서 제목이나 범위가 바뀌었으면 개요의 "문서 목록" 한 줄과 인덱스의 한 줄을 고친다.
36
36
  8. 로그 항목을 추가한다. 제목은 작업명이 아니라 사용자 결과 한 문장이고, 본문은 \`결과:\`·\`이유:\`·\`문서:\` 순서로 쓰며 파일 목록은 적지 않는다. 같은 작업을 여러 문서에 반복했으면 도메인마다 한 항목으로 묶고 \`결과:\`에 수량을 적는다.
@@ -49,6 +49,7 @@ description: crumbs 스펙 문서(${p.specDir}/ 아래의 책임 문서)를 쓰
49
49
  - 도메인 디렉터리의 상세 문서가 15개를 넘으면 흐름이 가까운 것끼리 하위 디렉터리로 묶고 그 디렉터리에도 \`${p.overview}\`를 둔다(검사 규칙은 중첩 개요를 지원한다: 하위 개요의 부모는 상위 개요다).
50
50
  - 서버 API·SDK 계약(요청·응답 필드, 응답·오류 코드의 뜻, 재시도·멱등성·인증)은 화면 문서가 아니라 \`${p.specDir}/api/<엔드포인트>.md\`가 소유한다. \`api/\`는 개요를 둔 도메인 하나이고, API 번호가 있으면 파일 이름과 정책 이름에 넣는다. 화면 문서는 코드별로 사용자가 보는 결과만 적고 그 api/ 문서의 정책으로 링크한다. 헤더·인증·쿠키·재시도 같은 공통 전송 계약은 \`api/\` 안의 상세 문서 하나에 한 번만 둔다. 화면 문서에 API 번호를 적을 때마다 그 api/ 문서가 있는지 확인하고, 없으면 같은 템플릿으로 만든다.
51
51
  - 개념 페이지. 두 도메인 이상의 흐름이 같은 규칙이나 값 표를 쓰거나, 코드의 판정 기준(플래그·enum)이 사용자 문구와 달라 매번 풀어 써야 하거나, 흐름 문서들이 분기 축으로 쓰는 개념(채널, 회원 상태, 재화 종류)은 \`${p.specDir}/concepts/<개념>.md\`가 소유한다. 정의만 있고 규칙이 없는 용어는 페이지를 만들지 않고 개요의 "용어" 한 줄로 둔다. \`concepts/\`가 없으면 도메인처럼 만들고 인덱스에 \`- [개념](concepts/${p.overview}) — 여러 도메인이 함께 쓰는 개념의 정의와 횡단 규칙\`을 더한다.
52
+ - 결정 링크 형식(모든 스킬 공통): 확정된 정책은 본문 마지막 문장 뒤에 \`(결정: [YYYY-MM-DD](../${p.log}#<슬러그>))\`를 둘 수 있다. 슬러그는 \`## \` 뒤의 항목 제목을 소문자로 바꾸고, 문장부호 중 \`-\`와 \`_\`가 아닌 것을 지운 뒤, 공백마다 \`-\`를 넣은 것이다. \`## [2026-11-02] decision | 세션 유효 기간: 30일\`이면 \`2026-11-02-decision--세션-유효-기간-30일\`이다.
52
53
  2. 진입점(라우트, 핸들러, 명령, 화면)부터 호출을 따라가며 코드 경로를 읽고, 같은 경로를 다루는 테스트에서 기대 동작을 확인한다. 읽은 경로 중 "현재 동작"이나 "정책"의 근거가 된 것만 적어 둔다. 맥락을 보려고 읽은 다른 영역의 파일은 "근거"에 넣지 않는다.
53
54
  3. 파일을 놓는다.
54
55
  - 도메인 디렉터리 \`${p.specDir}/<도메인>/\`. 없으면 만들고 \`${p.specDir}/templates/overview.md\`를 복사해 \`${p.overview}\`를 만든다.
@@ -75,7 +76,7 @@ description: crumbs 스펙 문서(${p.specDir}/ 아래의 책임 문서)를 쓰
75
76
  - 코드 구조와 호출 순서는 규칙이 아니다. 화면 배치처럼 사용자가 보는 것은 "현재 동작"에, 구현 구조는 "근거"의 설명에 둔다. 숫자·기한·조건은 코드에서 읽은 값 그대로 적는다.
76
77
  - 코드에만 있고 테스트가 없는 규칙은 5번대로 쓴다. 테스트가 규칙의 일부만 확정하면 확정된 부분을 확정 정책으로, 나머지를 질문 정책으로 나눈다.
77
78
  - 정책 이름은 저장소 전체에서 유일해야 한다(\`check\`의 duplicate-policy). 다른 도메인에 같은 이름이 생길 수 있으면 대상을 이름에 넣는다(예: "가입 유효시간 만료 안내"가 아니라 "이메일 가입 유효시간 만료 안내"). 이름은 맥락 없이 읽혀야 하고 질문형이 아니다.
78
- - "근거": 읽은 코드·테스트 경로를 백틱으로 적고 \` — \` 뒤에 그 경로가 구현하는 내용을 적는다. 설명에는 백틱을 쓰지 않는다(\`Cart.add\` 같은 식별자도 경로로 검사된다).
79
+ - "근거": 읽은 코드·테스트 경로를 \`/\`가 들어간 백틱 경로로 적고 \` — \` 뒤에 그 경로가 구현하는 내용을 적는다. 설명에는 식별자를 백틱으로 쓰지 않는다. \`check\`는 \`/\`가 없는 백틱 구간을 무시하지만 식별자는 평문으로 쓴다. 경로에는 \`/\`가 있어야 하며 루트 파일은 \`./package.json\`처럼 쓴다.
79
80
  - "확인 필요 사항": 코드·테스트가 답하지 않는 질문 전부. 형식은 5번.
80
81
  - "관련 문서": 개요로 가는 링크 하나와, 같은 흐름을 나누어 맡은 상세 문서들.
81
82
  5. 확인되지 않은 정책은 서술이 아니라 질문으로 쓴다. 분기는 있는데 이유를 알 수 없거나, 테스트가 없거나, 상수의 근거를 모르면 \`### 정책 이름\`(제목은 질문이 아니라 정책 이름) 아래 첫 단락에 코드에서 읽은 지금 동작을 평서문으로 적되, 조건·수치·분기 결과가 빠지지 않게 필요하면 여러 문장을 쓴다(단락 하나). 그다음 줄에 \`[확인 필요] <질문>\`을 적는다. 질문은 모르는 것을 그대로 묻는다(예: 그것이 의도인가, 아니라면 무엇이어야 하는가 / 이 상수의 근거는 무엇인가 / 주석과 코드 중 어느 쪽이 맞는가). 근거나 대안을 묻던 질문을 의도 확인으로 바꾸지 않는다. 이 서술은 관찰이지 확정이 아니므로 마커 줄 없이 두지 않는다. "확인 필요 사항"에는 \`- [확인 필요] [정책 이름](#정책-이름) — <같은 질문>\`을 둔다(링크는 \`check\`의 broken-anchor가 검사한다). 그럴듯한 값이나 의도를 지어내지 않는다.
@@ -97,7 +98,7 @@ description: crumbs 스펙 문서(${p.specDir}/ 아래의 책임 문서)를 쓰
97
98
 
98
99
  ## 마치기 전에
99
100
 
100
- 1. \`npx crumbs-spec check\`를 실행하고 오류를 모두 고친다. 경고 \`open-question\`은 질문을 남긴 결과이므로 그대로 둔다. 경고 \`missing-flow\`는 "현재 동작"이 여섯 단계 이상인데 도식도 표도 없다는 뜻이므로 "흐름"을 채워서 없앤다. 경고 \`section-order\`는 절 순서가 템플릿과 다르다는 뜻이므로 절을 옮겨서 없앤다.
101
+ 1. \`npx crumbs-spec check\`를 실행하고 오류를 모두 고친다. \`open-question\` 경고는 문서에 아직 질문이 남아 있다는 뜻이므로 그대로 둔다. 경고 \`missing-flow\`는 "현재 동작"이 여섯 단계 이상인데 도식도 표도 없다는 뜻이므로 "흐름"을 채워서 없앤다. 경고 \`section-order\`는 절 순서가 템플릿과 다르다는 뜻이므로 절을 옮겨서 없앤다.
101
102
  2. 더하거나 바꾼 모든 정책이 "근거"의 경로로 추적되거나 \`[확인 필요]\` 질문인지, "근거"에 적은 테스트가 검증하는 규칙이 빠짐없이 어느 \`###\` 정책에 적혔는지, 그리고 읽은 코드에서 채널·플랫폼·권한·로그인 상태로 갈리는 분기가 모두 "현재 동작"의 어느 문장에 적혔는지 다시 본다.
102
103
  3. 로그 항목이 들어갔는지 \`grep '^## \\[' ${p.specDir}/${p.log} | tail -3\`으로 확인한다.
103
104
  `;
@@ -118,7 +119,7 @@ description: crumbs 스펙 문서(${p.specDir}/)에서 현재 동작과 정책
118
119
  3. 그 도메인의 \`${p.overview}\`에서 "문서 목록"을 읽고 책임이 맞는 상세 문서를 고른다. 질문의 표현이 "용어" 절의 별칭에 있으면 그 줄의 링크가 소유 문서다.
119
120
  4. 상세 문서의 "현재 동작"과 "정책"을 읽는다. 질문한 정책의 \`### 제목\`이 여기 있으면 이 문서가 소유자다. 여기서 멈춘다.
120
121
  5. 없으면 그 문서의 "관련 문서"를 한 단계만 따라간다. 그래도 없으면 질문은 문서에 없는 것이다. 다른 도메인을 모두 뒤지지 말고 "문서에 없음"으로 답한다.
121
- 6. 문서가 침묵하고 사용자가 원하면 "근거"의 코드 경로를 읽어 답한다. 이때 답이 코드에서 왔다고 밝힌다.
122
+ 6. 문서가 침묵하고 사용자가 원하면 "근거"의 코드 경로(\`crumbs.json\`의 \`codeRoot\` 기준)를 읽어 답한다. 이때 답이 코드에서 왔다고 밝힌다.
122
123
 
123
124
  ## 답하는 방식
124
125
 
@@ -209,12 +210,12 @@ description: crumbs 스펙 문서를 쓰거나 바꾸기 전에, 요청이 건
209
210
  ## 하지 않는 것
210
211
 
211
212
  - 이 스킬은 문서를 바꾸지 않는다. 표와 질문만 낸다.
212
- - 로그 항목을 남기지 않는다. 답은 crumbs-writer가 반영하면서 로그에 적는다. 질문이 확정되면 \`decision\` 항목을 남기고 본문은 \`결과:\`·\`이유:\`·\`문서:\` 순서로 쓴다.
213
+ - 로그 항목을 남기지 않는다. 답을 반영할 때 crumbs-writer가 \`decision\` 항목(본문은 \`결과:\`, \`이유:\`, \`문서:\` 순)과 확정 정책 끝의 결정 링크를 남긴다(형식은 crumbs-writer "기존 코드 문서화" 1단계).
213
214
  - 미확인 정책에 기본값을 넣어 두고 나중에 고치자고 제안하지 않는다.
214
215
 
215
216
  ## 마치기 전에
216
217
 
217
- 문서를 바꾸지 않았으면 실행할 것이 없다. 사용자가 표시를 남긴 채 진행하라고 해서 작성까지 했다면 \`npx crumbs-spec check\`를 실행하고 오류를 모두 고친다. 질문 정책이 확정되면 \`decision\` 항목을 남기고, 확정한 정책 본문 끝에 결정 링크를 둔다.
218
+ 문서를 바꾸지 않았으면 실행할 것이 없다. 사용자가 표시를 남긴 채 진행하라고 해서 작성까지 했다면 \`npx crumbs-spec check\`를 실행하고 오류를 모두 고친다. 질문 정책이 확정되면 crumbs-writer가 \`decision\` 항목과 결정 링크를 남긴다.
218
219
  `;
219
220
  const ingest = (p) => `---
220
221
  name: crumbs-ingest
@@ -237,7 +238,7 @@ description: ${p.rawDir}/ 아래에 새로 놓인 원본 자료(계약서, 기
237
238
  - 찾은 문서의 "관련 문서"에서 영향이 이어지는 문서를 한 단계 더 본다.
238
239
  4. 변경 목록을 만들어 보여 준다. 항목마다 소유 문서, 절("현재 동작" 또는 \`### 정책 이름\`), 지금 서술 → 자료에 따른 서술, 자료의 어느 부분이 근거인지를 적는다. 자료와 현재 문서가 충돌하면 충돌로 표시하고 어느 쪽이 맞는지 묻는다. 자료가 기존 문서에 없는 새 정책을 말하면 "신규"로 표시하고 어느 문서가 소유할지 제안한다.
239
240
  5. 확인을 기다린다. 확인 없이 문서를 바꾸지 않는다. 일부만 승인되면 승인된 항목만 반영한다.
240
- 6. 소유 문서를 갱신한다. 정책마다 소유 문서 하나만 고치고, 다른 문서에는 그 정책으로 가는 링크만 둔다. 질문 정책이 확정되면 \`decision\` 항목을 남기고, 확정한 정책 본문 끝에 결정 링크를 둔다. 자료가 값을 확정하면 서술로 적고, 자료가 모호하면 \`[확인 필요]\` 질문으로 적는다(형식은 crumbs-writer "기존 코드 문서화" 5번). "근거"에 자료를 백틱 경로로 인용한다(\`${p.rawDir}/<파일>\` — \`check\`가 존재를 확인한다). 자료의 특정 부분을 가리켜야 하면 \` — \` 뒤에 쪽이나 절을 적는다.
241
+ 6. 소유 문서를 갱신한다. 정책마다 소유 문서 하나만 고치고, 다른 문서에는 그 정책으로 가는 링크만 둔다. 질문 정책이 확정되면 \`decision\` 항목을 남기고, 확정한 정책 본문 끝에 결정 링크를 둔다. 자료가 값을 확정하면 서술로 적고, 자료가 모호하면 \`[확인 필요]\` 질문으로 적는다(형식은 crumbs-writer "기존 코드 문서화" 5번). "근거"에 자료를 백틱 경로로 인용한다(\`${p.rawDir}/<파일>\`, \`codeRoot\`를 바꿨어도 저장소 루트 기준 — \`check\`가 존재를 확인한다). 자료의 특정 부분을 가리켜야 하면 \` — \` 뒤에 쪽이나 절을 적는다.
241
242
  7. 범위가 바뀌었으면 개요의 "문서 목록" 한 줄과 인덱스의 한 줄을 고친다. 새 문서를 만들었으면 crumbs-writer의 "기존 코드 문서화" 3~6단계대로 템플릿에서 시작해 개요와 인덱스에 연결한다.
242
243
  8. 로그 항목을 추가한다. 본문에 자료 파일과 수정한 문서를 적는다.
243
244
  \`npx crumbs-spec log add --kind ingest "<사용자 결과 한 문장>" --body "결과: <바뀐 사용자 결과>. 이유: ${p.rawDir}/<파일>. 문서: [<정책 이름>](<도메인>/<문서>.md#<앵커>)"\`
@@ -1 +1 @@
1
- {"version":3,"file":"skills-ko.js","sourceRoot":"","sources":["../../../src/templates/skills-ko.ts"],"names":[],"mappings":"AAGA,2EAA2E;AAC3E,uDAAuD;AAEvD,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;4BAEjB,CAAC,CAAC,OAAO;;;;;;;;;;OAU9B,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK;qBACN,CAAC,CAAC,QAAQ;;OAExB,CAAC,CAAC,OAAO,6BAA6B,CAAC,CAAC,OAAO;;;;;;;;;;;kFAW4B,CAAC,CAAC,OAAO,qCAAqC,CAAC,CAAC,OAAO,mCAAmC,CAAC,CAAC,OAAO;;;oLAGD,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;oEAiBrH,CAAC,CAAC,QAAQ;sEACR,CAAC,CAAC,OAAO;uIACwD,CAAC,CAAC,OAAO,kIAAkI,CAAC,CAAC,QAAQ;;;kBAG1Q,CAAC,CAAC,OAAO,wBAAwB,CAAC,CAAC,OAAO,mCAAmC,CAAC,CAAC,QAAQ;gBACzF,CAAC,CAAC,OAAO;SAChB,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;SA2BT,CAAC,CAAC,QAAQ,6DAA6D,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,iCAAiC,CAAC,CAAC,QAAQ,kDAAkD,CAAC,CAAC,KAAK;;;;;;;;;;;MAWlM,CAAC,CAAC,MAAM;;;;;;;;mCAQqB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;CACpD,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;4BAEjB,CAAC,CAAC,OAAO;;;;;;;;;;OAU9B,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,0DAA0D,CAAC,CAAC,OAAO;0BACpE,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,QAAQ;cAC5C,CAAC,CAAC,QAAQ;;;;;;;iCAOS,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCzC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;;;;;;OAYzC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,2BAA2B,CAAC,CAAC,QAAQ;;;uBAGzC,CAAC,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C9B,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;eAE9B,CAAC,CAAC,MAAM;;;;;;;;;;iCAUU,CAAC,CAAC,MAAM,4CAA4C,CAAC,CAAC,MAAM;;;SAGpF,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,QAAQ;0CACX,CAAC,CAAC,OAAO;mEACgB,CAAC,CAAC,OAAO;;;;uOAI2J,CAAC,CAAC,MAAM;;;0FAGrJ,CAAC,CAAC,MAAM;;;;;;;MAO5F,CAAC,CAAC,MAAM;;;;;;CAMb,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,MAAM,QAAQ,GAAoD;IACvE,eAAe,EAAE,MAAM;IACvB,eAAe,EAAE,MAAM;IACvB,kBAAkB,EAAE,SAAS;IAC7B,eAAe,EAAE,MAAM;CACxB,CAAC"}
1
+ {"version":3,"file":"skills-ko.js","sourceRoot":"","sources":["../../../src/templates/skills-ko.ts"],"names":[],"mappings":"AAGA,2EAA2E;AAC3E,uDAAuD;AAEvD,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;4BAEjB,CAAC,CAAC,OAAO;;;;;;;;;;OAU9B,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK;qBACN,CAAC,CAAC,QAAQ;yJAC0H,CAAC,CAAC,MAAM;OAC1J,CAAC,CAAC,OAAO,6BAA6B,CAAC,CAAC,OAAO;;;;;;;;;;;kFAW4B,CAAC,CAAC,OAAO,qCAAqC,CAAC,CAAC,OAAO,mCAAmC,CAAC,CAAC,OAAO;;;oLAGD,CAAC,CAAC,GAAG;;;;;;;;;;;;;;;;;oEAiBrH,CAAC,CAAC,QAAQ;sEACR,CAAC,CAAC,OAAO;uIACwD,CAAC,CAAC,OAAO,kIAAkI,CAAC,CAAC,QAAQ;uEACrN,CAAC,CAAC,GAAG;;;kBAG1D,CAAC,CAAC,OAAO,wBAAwB,CAAC,CAAC,OAAO,mCAAmC,CAAC,CAAC,QAAQ;gBACzF,CAAC,CAAC,OAAO;SAChB,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;SA2BT,CAAC,CAAC,QAAQ,6DAA6D,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,iCAAiC,CAAC,CAAC,QAAQ,kDAAkD,CAAC,CAAC,KAAK;;;;;;;;;;;MAWlM,CAAC,CAAC,MAAM;;;;;;;;mCAQqB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG;CACpD,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;4BAEjB,CAAC,CAAC,OAAO;;;;;;;;;;OAU9B,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,0DAA0D,CAAC,CAAC,OAAO;0BACpE,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,QAAQ;cAC5C,CAAC,CAAC,QAAQ;;;;;;;iCAOS,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCzC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;;;;;;;;;;;OAYzC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,2BAA2B,CAAC,CAAC,QAAQ;;;uBAGzC,CAAC,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C9B,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC;;eAE9B,CAAC,CAAC,MAAM;;;;;;;;;;iCAUU,CAAC,CAAC,MAAM,4CAA4C,CAAC,CAAC,MAAM;;;SAGpF,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,QAAQ;0CACX,CAAC,CAAC,OAAO;mEACgB,CAAC,CAAC,OAAO;;;;uOAI2J,CAAC,CAAC,MAAM;;;0FAGrJ,CAAC,CAAC,MAAM;;;;;;;MAO5F,CAAC,CAAC,MAAM;;;;;;CAMb,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,MAAM,QAAQ,GAAoD;IACvE,eAAe,EAAE,MAAM;IACvB,eAAe,EAAE,MAAM;IACvB,kBAAkB,EAAE,SAAS;IAC7B,eAAe,EAAE,MAAM;CACxB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { readFileSync } from 'node:fs';
2
+ // 패키지 버전을 package.json에서 읽는다. 컴파일 뒤 위치(dist/src)에서 두 단계 위가 저장소 루트다. cli.ts의 readVersion과 같은 규칙이다.
3
+ export function readPackageVersion() {
4
+ const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
5
+ return pkg.version;
6
+ }
7
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,kGAAkG;AAClG,MAAM,UAAU,kBAAkB;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAwB,CAAC;IACpH,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC"}