org-lint
~/.claude/rules/org-babel.md에 문서화된 org/md 파일 산문 서식 규칙을 검사하는 린터입니다.
목표는 매번 각 규칙을 수동으로 기억하는 데 의존하지 않고(이로 인해 방금 언급한 규칙만 고치고 나머지는 놓치는 위험이 있음), 명시적인 목록을 통해 텍스트를 검사하는 것입니다.
적용 시점
.org/.md파일에서 새 산문 단락을 편집하거나 작성한 직후(각 규칙을 수동으로 기억하여 확인하는 대신) — 결과를 사용자에게 보여주기 전에.- 사용자의 명시적 요청("org 규칙 확인", "린트 실행",
clippy와 비교 등) 시. #+begin_src/#+begin_example블록(코드) 내용에는 적용되지 않음 — 오직 산문 단락과 제목에만 적용.
1단계 — 기계적 검사
편집한 파일에 대해 스크립트를 실행하세요:
python3 ~/.claude/skills/org-lint/scripts/lint.py 파일/경로.org
스크립트가 정규식으로 잡아내는 항목:
~...~/=...=주변에 공백 없는/(~a~/~b~대신~a~ / ~b~) — 확실한 위반.[[file:../— 현재 디렉터리 기준 상대 경로 대신root:약어 사용 — 확실한 위반.- 파일 끝에 개행(newline) 없음 — 확실한 위반.
=...=안에 대문자 약어 (예:=CSS=) — 확인 필요 (드문 예외 허용).- 이탤릭
/단어/— 확인 필요: 프로젝트 용어집의 용어인지(spcfctn/glossary.org에 해당 섹션이 있는가? 이 파일에서 첫 언급 시 링크가 있는가?), 아니면 실수로 변환되지 않은 섹션/파일 링크인지([[*섹션][...]]여야 함). - 파일 언급이
=경로/파일.org=형태 — 확인 필요: 실제[[file:...]]링크여야 하는 것은 아닌지. - "매개변수"/"매개변수들"/... 단어 — 확인 필요: Rust/Yew 컴포넌트 속성(
struct ...Props)을 의미한다면 올바른 번역은 "속성들"/"속성"이며, "매개변수들"/"매개변수"는 구식 용어입니다. Yew 컴포넌트 외부(함수 매개변수, CLI, REST 프로시저 등)에서는 "매개변수"가 적절하므로 건드리지 마세요. :noweb-ref 정의블록이:noweb-ref 선언블록보다 앞에 있음 — 확인 필요: 선언이 먼저 와야 하며, 파일을 위에서 아래로 읽는 순서를 따라야 함 (org-babel.md 참고).- 동일한
:tangle 경로를 가진 여러#+begin_src블록이 있고, 파일 내에:noweb yes/<<...>>가 하나도 없음 — 확실한 위반 (구식 연결 방식, 이전gitignore.org와 같음 — org-babel.md 참고). 하나의 컨테이너 블록으로:tangle+:noweb yes+<<이름>>플레이스홀더와:noweb-ref 이름섹션으로 재구성하세요. - 파일이 여러 다른 대상 파일로 탱글되고, 그중 최소 두 개가 noweb을 소비(스켈레톤
#+begin_src에:noweb yes있음, 예:config.org→.env.example+parameters.php)하지만, 하나 이상의:noweb-ref/<<...>>블록에 대상 접두사가 없음 (이름대신env:이름) — 확실한 위반 (multi-target org 파일의 접두사 규칙, org-babel.md 참고). 하나의 대상 파일만 있는 경우, 그리고 하나의 대상만 noweb을 소비하고 나머지는<<...>>없는 독립적인 단일 블록:tangle대상(예:gitignore.org: 5개의:tangle대상이지만 오직 루트.gitignore만 noweb 스켈레톤 사용)인 경우에는 접두사가 필요하지 않습니다. 실제 모호성이 없기 때문입니다. #+begin_src헤더의 매개변수 순서가 잘못됨 — 확실한 위반. 올바른 순서: 블록 타입 →:noweb yes→:noweb-sep "..."→ 나머지 매개변수 →:tangle 경로또는:noweb-ref 이름(가장 끝, org-babel.md 참고).- 파일 이름이
cargo.org인 경우 — 확실한 위반 (Rust 프로젝트의 구식 이름;config.org로 변경, org-babel.md 참고). :tangle이 절대 경로(/...,~/...) 또는 상위 디렉터리..를 사용 — 확실한 위반 (org-babel.md 참고); 대상은 org 파일에서 하위 디렉터리로만 향하는 상대 경로여야 합니다. 대상이 실제로 소스 트리 외부에 있다면, symlink나 다른 디렉터리 구조를 사용해야 하며../절대 경로를 사용하지 않아야 합니다(사례:farba-override→farba-themevia../farba-theme/..., 작업FS-1085).=...=밖의 대문자 (예:아님,오직) — 확인 필요: 단어 강조라면 약어/축약어가 아닌 경우, 대문자 대신 org bold (*아님*)로 대체.- "훅"/"각" (React/Yew "hook" 번역) — 구식; 올바른 번역은 "갈고리"(산문에만 해당,
use_effect_with같은 코드 식별자는 제외). - 105자를 초과하는 산문 줄 —
emacsclient를 통한fill-paragraph필요 (아래 참고).
"확실한" 발견은 항상 수정하세요. "수동 확인 필요"는 각각 검토하되, 모두 오류는 아닙니다(예: =rest=/=forge-yew=는 합법적인 약어/크레이트 이름으로 위반이 아님).
2단계 — 수동 체크리스트 (스크립트가 잡지 못하는 항목)
- 코드 식별자/타입/함수/변수/모듈 — 그리고 모든 코드 조각이나 표현식(예:
id=1,value == 0,items()) — 틸드~...~. 약어/두문자어와 크레이트/라이브러리의 "공개" 형태 이름(하이픈 포함) — 등호=...=. 정규화된 경로크레이트::타입은 코드 타입 이름이므로, 크레이트 접두사가 있더라도 전체를 틸드 안에 넣습니다. - 약어/두문자어 — 문장 중간에서는 소문자, 문장 시작에서만 첫 글자 대문자(모두 대문자 금지), 드문 확립된 예외는 제외.
- 같은 파일 내 섹션 링크 —
[[*헤딩 제목][제목]], 이탤릭이나 일반 텍스트가 아님. 같은 디렉터리의 파일 링크 — 그냥[[file:이웃.org][설명]]; 다른 디렉터리의 파일(특히 공통 작업 디렉터리 내 다른 git 리포지토리) —root:약어 사용 ([[root:code/ads-back/파일.org::N][설명]]),../../../현재 디렉터리 기준 상대 경로 아님. 약어는 작업 디렉터리 루트의.dir-locals.el에 선언됩니다(locate-dominating-file을 사용한eval형식) — 프로젝트에 아직 없으면 생성하세요. - 이탤릭
/.../— 오직 프로젝트 용어집 용어만 해당(바로 뒤에 번역/translation/포함); 각 파일에서 용어 첫 언급 시 — 해당 섹션에 대한[[file:.../spcfctn/glossary.org::*용어][...]]링크, 정의 반복 금지.
3단계 — 정렬
편집한 각 단락에 대해 수동 정렬 대신 emacsclient --eval을 통해 실제 fill-paragraph를 호출하세요 (org-babel.md의 해당 규칙 참고). 단락 텍스트를 new_string/파일 생성 시 한 줄로 작성하고, 스스로 줄 바꿈을 추측하지 마세요 — 어차피 fill-paragraph가 완전히 결정합니다.
반드시 org-fill-paragraph 전에 (font-lock-ensure)를 호출하세요: find-file-noselect로 headless로 연 버퍼는 폰트화가 진행되지 않아 org 링크 [[경로][설명]]가 짧은 가시 텍스트 설명으로 "접히지" 않습니다. font-lock-ensure 없이 fill-paragraph는 링크의 긴 원시 텍스트를 기준으로 줄 바꿈을 계산하므로, 라이브 대화형 Emacs에서 M-q가 제공하는 결과와 달라집니다. 예:
emacsclient --eval '
(let ((buf (find-file-noselect "파일/경로.org")))
(with-current-buffer buf
(org-mode)
(font-lock-ensure)
(goto-char (point-min))
(search-forward "단락의 고유 부분")
(org-fill-paragraph)
(save-buffer)))
'