Community코딩 & 개발github.com

lenneTech/running-check-script

package.json의 check 스크립트를 실행하고(모노레포 인식), 녹색이 될 때까지 자동 수정하며, 보안 감사 결과를 에스컬레이션하여 작업의 실행 가능성을 증명합니다. "pnpm run check" 명령어에 반응합니다.

running-check-script란 무엇인가요?

이 스킬은 lt-dev 워크플로우에서 `package.json`의 `check` 스크립트 실행을 위한 단일 진실 공급원 역할을 합니다. 모든 리뷰어, 리베이서, 오케스트레이터는 이 절차를 반드시 따라야 하며, 프로젝트가 실행 가능한 상태임을 보장해야 합니다. 스킬은 `check` 스크립트를 자동으로 발견하고, 실패 시 반복적인 자동 수정 루프를 통해 진정한 녹색(exit 0) 상태를 달성합니다. 또한 보안 감사 결과에 대한 강제적인 수정 에스컬레이션 사다리(8단계)를 제공하며, 호출 모드(Advisory/Blocking)에 따라 실패 처리 방식을 다르게 합니다. Blocking 모드에서는 빨간 `check`가 워크플로우를 중단시켜 병합을 방지하고, Advisory 모드에서는 Critical 이슈로 보고하지만 워크플로우는 계속 진행됩니다. 스킬은 `CLAUDE_PLUGIN_ROOT` 환경 변수를 기반으로 하는 전용 헬퍼 스크립트를 사용하여 프로젝트와 패키지 매니저를 감지하며, macOS, Linux, Windows(WSL) 환경을 지원합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lenneTech/claude-code/tree/HEAD/plugins/lt-dev/skills/running-check-script

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

check 스크립트 실행하기

이 스킬은 lt-dev 워크플로우에서 package.json의 check 스크립트를 실행하기 위한 단일 진실 공급원입니다. 프로젝트의 실행 가능성을 보장해야 하는 모든 리뷰어, 리베이서, 오케스트레이터는 이 절차를 그대로 따라야 합니다. 여러 에이전트에 걸쳐 규칙을 복제하면 표류가 발생합니다.

목표: 진정한 녹색 check 실행(exit 0)은 모든 리뷰 또는 리베이스가 완료된 것으로 간주되기 위한 필수 전제 조건입니다. 유일하게 허용되는 잔여 항목은 전체 수정 에스컬레이션 사다리가 소진된 업스트림 의존성 취약점뿐입니다.

이 스킬을 사용해야 하는 경우

호출자단계트리거모드
/lt-dev:review1.5단계전문화된 리뷰어를 생성하기 전Advisory
lt-dev:code-reviewer1.5단계단일 패스 리뷰 전 (오케스트레이터가 이미 실행한 경우 건너뜀)Advisory
lt-dev:test-reviewer(입력 브리핑)여기에 정의된 건너뛰기 의미를 따름Advisory
/lt-dev:take-ticketSTEP 8모든 테스트 기둥이 녹색이 된 후Blocking
/lt-dev:git:shipSTEP 1, STEP 4b사전 점검, 리베이스 후 재확인Blocking
/lt-dev:ticket-cycletake-ticket + git:ship을 통해전체 사이클Blocking
lt-dev:branch-rebaser6.5단계린트/포맷 후, 테스트 전Blocking

호출자 모드

절차는 두 모드에서 동일합니다. 해결되지 않은 오류가 발생했을 때의 처리만 다르며, 그 차이가 구분의 핵심입니다:

  • Advisory — 호출자의 역할은 보고하는 것입니다. 빨간 check는 Critical finding으로 표시되고 워크플로우는 계속됩니다. 사용자가 여전히 나머지 리뷰를 원하기 때문입니다.
  • Blocking — 호출자의 역할은 배송하는 것입니다. 빨간 check는 워크플로우를 중단시킵니다. 푸시되지 않으며, MR/PR이 생성되지 않고, 병합되지 않습니다.

MR/PR 또는 병합으로 이어지는 모든 경로는 Blocking 모드로 실행됩니다. 빨간 check가 베이스 브랜치에 도달하면 팀 전체에 비용이 발생합니다: 모든 사람의 CI가 빨간색이 되고, 다음 take-ticket은 손상된 코드에서 브랜치를 따며, 결국 수정하는 사람은 어떤 변경 사항이 원인인지 알 수 없습니다. 이것이 3단계의 사다리에 반복 제한이 없고 5단계의 잔여 목록이 매우 좁은 이유입니다.

절차

1단계 — check 스크립트 발견

전용 헬퍼 스크립트(lt-dev 플러그인에 위치)를 사용합니다:

bash "${CLAUDE_PLUGIN_ROOT}/scripts/discover-check-scripts.sh"

출력은 TSV이며, 한 줄에 하나의 프로젝트입니다:

<package.json 경로>\t<check 스크립트>\t<includes_tests:no|unit+api|yes>\t<패키지 매니저>

includes_tests 값 — 호출자는 각 값으로 무엇을 할지 결정하며, 발견 자체는 어떤 동작도 규정하지 않습니다:

값의미Playwright 적용 범위
nocheck 체인에서 테스트 러너가 감지되지 않음.포함되지 않음.
unit+apicheck 체인이 lt check.mjs 래퍼에 위임하며, 이는 Unit (app) + API (api) 테스트 단계를 실행하고 의도적으로 Playwright를 건너뜁니다 (Playwright는 lt dev test / CI를 통해 실행됨 — scripts/check.mjs 참조).포함되지 않음 — Playwright는 필요한 경우 별도로 실행해야 합니다.
yescheck 체인에서 test, vitest, jest 또는 playwright에 대한 직접적인 패턴 일치 (전이적으로, 한 수준의 합성 확인까지). 범위는 스크립트가 정의하는 대로입니다.알 수 없음 — Playwright 적용이 필요한 경우 호출자가 로컬에서 확인해야 합니다.

헬퍼:

  • git ls-files "package.json" "**/package.json"을 사용하므로 .gitignore를 존중하고 node_modules를 건너뜁니다.
  • JSON을 jq → node -e → grep+sed 폴백 체인을 통해 구문 분석합니다 (jq가 설치되지 않은 경우에도 작동).
  • 단일 수준 합성 스크립트를 확인합니다 ("check": "pnpm run ci" → ci 조회).
  • 프로젝트별 패키지 매니저를 잠금 파일을 통해 감지합니다 (pnpm-lock.yaml → pnpm 등; 기본값 pnpm).

헬퍼가 아무것도 반환하지 않으면 → check를 정의하는 프로젝트가 없음 → 이 스킬의 나머지를 건너뜁니다.

1a단계 — oxlint 제안 수정 없음

oxlint --fix-suggestions는 oxlint가 동작을 변경할 가능성이 있다고 표시한 수정 사항을 적용합니다: no-console 제안은 호출을 삭제합니다. --fix-dangerously는 더 나아갑니다. 현재 템플릿은 둘 다 전달하지 않지만, 이전 템플릿의 프로젝트는 여전히 check 래퍼, lint:fix, lint-staged 또는 프로젝트 자체의 scripts/check.mjs에서 전달합니다. lt fullstack update는 워크스페이스 루트와 앱에서 이를 제거합니다 (추적되고, 깨끗한 파일만 해당); API 프로젝트는 자체 파일을 유지하므로, 거기서 적중이 발생하면 수동으로 또는 현재 nest-server-starter 파일을 가져와서 제거합니다. nest-server (2026-09, claude-code PR #10)에서 관찰됨: 커밋 훅이 console.log를 빈 함수 본문으로 바꾸고 여전히 "통과"를 보고했습니다. 2단계 전에 다음 플래그의 사용을 찾으십시오; 주석에서의 언급은 계산하지 않습니다:

git grep -n -E "(^|[[:space:]\"'])(--fix-(suggestions|dangerously))" -- '*package.json' '*.lintstagedrc*' '*lint-staged.config.*' '*scripts/check.mjs' \
  | grep -v -E '^[^:]+:[0-9]+:[[:space:]]*(//|/?\*|#)'

두 번째 필터는 주석 줄을 제거합니다: 현재 nest-server-starter 래퍼는 주석에서 일반 --fix만 전달하는 이유를 설명합니다.

적중 시, check는 자동 수정 없이 실행됩니다:

  • lt 래퍼를 읽기 전용 게이트로 실행: <pm> run check --no-fix (npm: npm run check -- --no-fix). 여전히 플래그 중 하나를 전달하는 모든 래퍼는 이미 --no-fix를 알고 있습니다. 프로젝트 자체 체인이 lint:fix를 호출하거나, 자체 scripts/check.mjs가 적중 중 하나인 경우, 해당 프로젝트의 단계를 lint:fix 대신 lint로 실행합니다.
  • 린트 결과를 수동으로 또는 일반 oxlint --fix로 수정합니다. 프로젝트의 포맷터로 평소처럼 포맷합니다.
  • 8단계 보고서에서 모든 적중 (파일:줄)을 수정 방법과 함께 명명합니다: 루트 및 앱의 경우 lt fullstack update, API 프로젝트의 경우 수동으로 플래그 제거 (또는 현재 nest-server-starter 파일).

2단계 — 프로젝트별로 check 실행

발견된 각 프로젝트에 대해, 해당 디렉토리로 cd하고 감지된 패키지 매니저로 check 스크립트를 실행합니다:

cd "$(dirname <package.json 경로>)"
<패키지 매니저> run check

stdout, stderr, 종료 코드를 캡처합니다. 1부터 시작하는 반복 카운터를 추적합니다.

3단계 — 자동 수정 루프 (진정한 녹색이 될 때까지 반복)

프로젝트의 check가 0이 아닌 값으로 종료되면:

  1. 현재 실행에서 모든 오류를 구문 분석합니다 (타입 검사, 린트, 빌드, 누락된 임포트, 타입 불일치, 사용되지 않는 심볼, 감사 결과 등).
  2. Read + Edit을 통해 모든 오류를 근본 원인에서 수정합니다. 기존 오류도 수정됩니다 — 이 diff에서 발생했든 이미 거기에 있었든 프로젝트 실행 여부에는 차이가 없으며, 빨간 check는 어느 쪽이든 다음 사람을 똑같이 막습니다. 범위 경계는 실행 가능성에 적용되지 않습니다. 여기서 모든 것을 결정하는 유일한 질문은 "이것을 고칠 수 있는가?"이며, 대답이 '예'인 동안에는 수정됩니다.
  3. 처음부터 check를 다시 실행합니다. 부분 상태를 신뢰하지 마십시오.
  4. 다음 종료 조건 중 하나가 충족될 때까지 반복을 계속합니다:
    • (a) GREEN — check가 0으로 종료됨 → 이 프로젝트 완료
    • (b) STALLED — 전체 반복에서 오류 수의 순 감소가 발생하지 않음
  5. 하드 반복 제한 없음. 각 반복이 엄격하게 오류 수를 줄이는 한 계속 진행합니다. 목표는 진정한 녹색입니다.

한 가지 좁은 예외가 있으며, 이는 범위보다는 충돌에 관한 것입니다. 2번 항목은 그대로 유지됩니다: 사전 존재 오류는 수정됩니다. 빨간 check는 작성자와 관계없이 다음 사람을 차단하기 때문입니다. 2번 항목이 다루지 않는 것은 다른 활성 세션이 작업 중인 커밋되지 않은 작업 내부의 오류입니다. 이를 재구성하는 것은 범위 문제가 아니라 두 세션이 하나의 파일을 편집하는 것이며, 동료가 다시 돌아왔을 때 리팩터링된 코드를 발견하고 수정으로 절약한 시간보다 더 많은 시간을 잃게 됩니다. 트리가 더럽고 bash "${CLAUDE_PLUGIN_ROOT}/scripts/change-provenance.sh"가 실패 경로를 활성 동료에게 귀속시키는 경우: 오류가 사소하고 자체 포함된 경우 (누락된 임포트, 포맷 위반, 방치된 console.log) 수정하고, 그렇지 않으면 작성자를 명명하는 블로커로 보고하고, 진단을 포함한 SOLVED 하나를 추가하여 동료가 자신의 작업을 한 단계로 수정할 수 있도록 합니다. 활성 상태인 사람이 없거나 귀속될 수 없는 경우: 2번 항목이 변경 없이 적용됩니다.

STALLED는 접근 방식을 변경하라는 신호이지, 중지할 권한이 아닙니다. 동일한 수정이 재시도될 때 카운트 감소가 중단되므로, 다음 반복은 오류를 다르게 공격해야 합니다: 보고된 줄 주변을 패치하는 대신 실패한 파일 전체를 읽고, 두 오류가 하나의 원인을 공유하는지 확인하고, 작동하는 형제 모듈이 동일한 문제를 어떻게 해결하는지 살펴보고, 실패한 단계만单独 실행하여 집계 실행이 삼킨 출력을 확인하고, 6.5~6.8단계의 알려진 위험 중 하나인지 확인합니다. 진정으로 다른 접근 방식이 시도되었고 카운트가 여전히 유지될 때만 5단계의 잔여 분류가 적용됩니다. 그리고 Blocking 모드에서는 고갈된 사다리 의존성 취약점을 제외한 모든 것이 여전히 워크플로우를 중단시킵니다.

4단계 — 감사 결과: 필수 수정 에스컬레이션 사다리

pnpm audit / npm audit / yarn audit (check에 의해 호출됨)이 취약점을 보고하면, 발견을 수락됨으로 분류하기 전에 이 사다리를 모두 소진합니다. 각 단계 후 check를 다시 실행합니다.

#단계명령어 (pnpm / npm / yarn)
1최신 호환 버전으로 업데이트pnpm update <패키지> / npm update <패키지> / yarn upgrade <패키지>
2자동 수정pnpm audit --fix / npm audit fix / yarn audit --fix
3강제 semver-major 업그레이드pnpm audit --fix --force / npm audit fix --force
4권고에 수정 사항이 나열된 경우 직접 의존성을 다음 메이저로 범프pnpm add <패키지>@<메이저> / npm install <패키지>@<메이저> / yarn add <패키지>@<메이저>
5전이 의존성 버전 강제pnpm.overrides / resolutions (yarn) / overrides (npm) 블록을 package.json에 추가한 후 재설치
6취약한 패키지가 아닌 소비자를 올림이를 당기는 직접 의존성, 그리고 프레임워크가 고정하는 경우 lt 프레임워크 (@lenne.tech/nest-server / @lenne.tech/nuxt-extensions) — 전체 /lt-dev:maintenance:maintain이 철저한 형태입니다
7소비자 패치 — 아래 조건에서만pnpm patch <패키지> → patchedDependencies
8패키지 교체유지 관리되지 않는 경우 유지 관리되는 대안으로 전환

6단계는 건너뛰어지는 단계이며, 가장 자주 효과가 있는 단계입니다. 1~5단계는 모두 취약한 패키지를 앞으로 밀어냅니다. 이것이 실패하면, 그 이유는 일반적으로 취약한 패키지 자체가 아니라, 취약한 범위를 고정하거나 패치된 메이저가 제거한 API를 사용하여 이를 붙잡고 있는 소비자입니다. 따라서 물어야 할 질문은 "누가 이 버전을 여기에 유지하는가?"이며, 답은 직접 의존성 또는 재정의가 이길 수 없는 핀을 가진 프레임워크입니다. 재정의는 핀이며, 핀은 실제 버전 범프보다 엄격하게 나쁩니다: 업스트림이 테스트하는 트리에서 분기하고 조용히 썩습니다. 범프가 존재하는 곳에서 범프를 가져가십시오.

7단계(패치)는 의도적으로 좁으며, 그 좁음이 핵심입니다. 패치된 메이저가 로드에만 실패하는 경우 — 제거된 기본 내보내기, 이름이 변경된 진입점 — 한 줄 패치는 권고를 억제하는 것이 아니라 적절히 종결하는 것처럼 보입니다. 때로는 그렇습니다. 세 가지 조건이 먼저 충족되어야 하며, 세 번째가 일반적으로 실패합니다:

  1. 패치는 취약한 패키지의 안정적인 경로를 대상으로 하며, 정확히 하나의 버전에 고정됩니다. 패치를 깨뜨리는 것은 dist/ 자체가 아닙니다 — 게시된 패키지는 dist/와 그 외 아무것도 제공하지 않습니다 — 릴리스 간에 이동하는 경로입니다. dist/chunks/<해시>.mjs는 다음 빌드에서 이름이 변경되고 패치는 팀의 모든 사람에게 실패합니다; dist/index.mjs는 패키지 자체의 진입점이며 유지됩니다. 축소된 심볼을 명명하는 패치는 여전히 버전 핀과 함께 허용됩니다. 핀이 위험을 조용한 잘못된 패치 대신 큰 소리로 pnpm install 실패로 바꾸기 때문입니다. 우연히 이를 가져오는 소비자가 아닌 취약한 의존성 자체를 패치하십시오 — 아래 측정된 사례를 참조하십시오. 첫 번째 평가는 소비자의 청크를 보고 그 근거로 사다리 단계를 거부했습니다.
  2. 프로덕션 폐쇄와 유지 관리 비용을 저울질하십시오. 패치는 모든 future bump에 대한 지속적인 의무이므로, 프로덕션에 도달할 수 없는 개발 도구 경로의 경우 단일 프로젝트에서 지불하는 것보다 더 많은 비용을 소비할 수 있습니다. TEMPLATE은 그 산술을 뒤집습니다: 생성된 모든 프로젝트는 하나의 패치를 상속하는 반면, 잔여물은 각 프로젝트에서 다시 논의되어야 합니다. 이는 판단이며, 거부권이 아닙니다; 조건 1과 3이 어려운 조건입니다.
  3. 로딩이 작동과 동일하지 않습니다. 내보내기를 제거한 메이저는 일반적으로 동작도 변경했으므로, 패치된 임포트는 모듈을 가져오도록 만들지만 소비자는 테스트된 적이 없는 API에 대해 실행하게 됩니다. 해당 메이저에서 무엇이 변경되었고 왜 여기서 중요하지 않은지 말할 수 없다면, 패치는 수정 복장을 한 추측입니다.

취약한 패키지를 패치하십시오. 소비자가 아닙니다. 이것이 사다리 단계가 작동하는지 여부를 결정하는 구분이며, 오류가 소비자를 가리키기 때문에 잘못 이해하기 쉽습니다. 측정됨, lt-crm 2026-10-06: @nuxt/[email protected]은 import Git from 'simple-git'을 수행하고, simple-git을 유일하게 수정된 라인(4.x, 기본 내보내기를 제거함)으로 올리면 nuxt prepare가 does not provide an export named 'default'로 중단되었습니다. 첫 번째 평가는 devtools 자체의 dist/chunks/module-main.mjs를 보고 그곳에서 임포트를 찾았으며, 조건 1(컴파일된 청크)에서 패칭을 거부했으며, 이는 잘못된 파일에 대한 올바른 판결이었습니다. 패치는 한 수준 아래인 simple-git의 자체 dist/index.mjs에 속합니다. 여기서 한 줄이 추가되어 모듈이 이미 simpleGit으로 내보내는 정확한 팩토리를 default로 다시 내보냅니다. 강화된 코드에 대한 어떤 것도 변경되지 않습니다; 모듈의 표면만 복원됩니다.

이로써 세 가지 조건이 모두 충족됩니다: 소비자 아티팩트가 아닌 취약한 패키지이며, 정확히 하나의 버전에 고정되고 (patchedDependencies: { '[email protected]': … } plus the override target), devtools가 실제로 호출하는 API — branch, revparse 등 —는 기본 내보내기와 전혀 관련이 없으며, 패키지가 단순히 로드되지 못하게 하는 것 외에는 패치가 devtools의 동작을 변경하지 않습니다. rung의 모든 세 가지 조건이 충족되었습니다.

조건 3은 devtools가 devtools에서 git 조작에 사용하는 simple-git의 public API가 git CLI 자체의 표면과 동일한 것임을 확인해야 합니다. branch, revparse, log, status 등은 패치된 메이저에서 동일하게 유지됩니다. 패치가 동작을 변경하지 않는다고 확신할 수 있다면, 작동합니다.

8단계는 마지막 수단입니다. 대체품이 존재하고, 전환이 기술적으로 실현 가능하며, 가져올 새로운 의존성이 기존 것보다 덜 위험하다는 것을 의미합니다. 패키지를 교체하는 것은 재작성 범위에 따라 7단계의 비용을 초과할 수 있습니다.

고갈된 사다리 감지: 8단계가 소진되면 또는 6단계 이후(실패가 애플리케이션 코드가 아니라 프레임워크의 핀을 명확히 지목하는 경우) check가 호출되면 → 영향을 받는 패키지를 5단계 보고서에 고갈된 업스트림 의존성 취약점으로 기록하고 check를 수용 가능한 잔여물로 표시합니다. 모든 리뷰 및 모든 배송 워크플로우는 하나의 잔여 패키지를 건너뛰지만 두 번째 잔여 패키지는 건너뛰지 않습니다.

5단계 — 잔여 오류 분류 (다른 모든 방법이 실패한 경우에만)

자동 수정 루프가 STALLED에 도달하고, 에스컬레이션 사다리가 소진된 후, 남은 각 오류를 분류합니다:

  • 수정됨 — 수정되어 check가 통과함
  • 고갈된 업스트림 의존성 취약점 — 4단계 사다리 소진, 하나의 잔여물은 통과할 수 있지만, 2개 이상은 모든 워크플로우에서 차단됨
  • 차단기 — 다른 모든 것: 워크플로우가 Blocking 모드인 경우 중단, Advisory 모드인 경우 Critical finding

6단계 — 보고 (모드별)

Advisory 모드: 작성을 위해 파일 시스템에 결과 요약을 씁니다. 'check'를 통과한 각 프로젝트를 선으로, 빨간 check를 Critical finding으로 표시하여 Critical findings 섹션에 에스컬레이션 사다리 상태 및 수동 수정이 필요한 파일을 포함하여 나열합니다.

Blocking 모드: 결과 요약을 작성하고 워크플로우를 중단합니다. 'check'를 통과하는 것은 통과로 표시됩니다. 빨간 check는 워크플로우를 중단시키고, 차단기와 에스컬레이션 사다리 상태(고갈된 취약점은 허용, 다른 모든 것은 차단)를 보고합니다.

6a단계 — git:ship 블로커: 리베이스 후 재확인

/lt-dev:git:ship이 빈번한 리베이스(keep-ours 없음)를 수행할 때마다, 리베이스가 완료된 후 전체 check 절차(1~5단계)를 다시 실행합니다. 리베이스는 충돌 해결을 통해 오류를 도입할 수 있으며, 배송 전에 포착해야 합니다.

6.5단계 — Lint-staged 오작동: 훅과 CI가 다름

lint-staged가 check(staged 파일에만 실행)를 단계 파일에 통과시키지만 전체 check가 실패하는 경우가 있습니다. lint-staged가 린트 또는 테스트를 무시하는 경우, lint는 staged diff에만 실행되므로 통과하지만 ci는 전체 저장소에 실행되고 실패합니다. lint-staged를 수정하기 위해 staged 파일을 수정하지 마십시오; 대신 전체 저장소의 린트 오류를 수정하십시오.

6.6단계 — TypeScript 빠른 수정 수입: 버그인 경우 수동 수정

TypeScript의 자동 수정이 프로젝트가 의존하지 않는 패키지에서 타입을 올바르지 않게 수입하는 경우가 있습니다. 이는 버그입니다. 수동으로 수정하십시오.

6.7단계 — eslint.config.*에서의 SequenceExpression: 오래된 파일 제거

eslint가 코드와 충돌하는 경우, 오래된 eslint 구성 파일(eslint.config.js/eslint.config.mjs)이 프로젝트에 없어야 합니다. 제거하십시오.

6.8단계 — TypeScript 다운레벨 오류: 제네릭에 대한 더블 네임스페이스, 타입 전용 수입

TypeScript의 다운레벨 변환은 때때로 제네릭에 대한 더블 네임스페이스(예: this._v<Namespace.InnerType>)를 생성합니다. 더블 네임스페이스를 수정하여 한 번만 참조하고, 수입이 타입 전용인지 확인하십시오. 또는 tsconfig에서 isolatedDeclarations를 비활성화하고 (변경 사항에 속하는 경우) 또는 skipLibCheck를 활성화하십시오.

플랫폼 호환성

이 헬퍼를 사용하려면 bash, jq (폴백, 필요한 경우), node, git이 필요합니다. macOS, Linux, Windows(WSL)에서 테스트되었습니다.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

관련 스킬