스킬 크리에이터
스크립트, 참조 및 자산이 포함된 재사용 가능한 기술의 범위 지정, 구조화 및 패키징을 위한 상담원 기술 수명 주기 가이드입니다.
출처: 인류학/기술(MIT)에서 채택한 콘텐츠.
새로운 기술을 창출하고 이를 반복적으로 향상시키는 기술입니다.
높은 수준에서 기술을 만드는 과정은 다음과 같습니다.
- 기술로 수행할 작업과 대략적인 수행 방법을 결정합니다.
- 스킬 초안 작성
- 몇 가지 테스트 프롬프트를 만들고 여기에 clude-with-access-to-the-skill을 실행하세요.
- 사용자가 결과를 정성적, 정량적으로 평가할 수 있도록 지원
- 실행이 백그라운드에서 수행되는 동안 정량적 평가가 없는 경우 몇 가지 정량적 평가 초안을 작성합니다(있는 경우 있는 그대로 사용하거나 변경이 필요하다고 생각되면 수정할 수 있습니다). 그런 다음 사용자에게 설명합니다(또는 이미 존재하는 경우 이미 존재하는 것을 설명).
eval-viewer/generate_review.py스크립트를 사용하여 사용자가 볼 수 있는 결과를 보여주고 정량적 측정항목도 볼 수 있도록 하세요.
- 사용자의 결과 평가 피드백을 기반으로 스킬을 다시 작성합니다(또한 정량적 벤치마크에서 명백하게 드러나는 결함이 있는 경우).
- 만족할 때까지 반복하세요.
- 테스트 세트를 확장하고 더 큰 규모로 다시 시도하세요.
이 기술을 사용할 때 귀하의 임무는 사용자가 이 프로세스의 어느 위치에 있는지 파악한 다음 뛰어들어 이러한 단계를 진행하도록 돕는 것입니다. 예를 들어, "X를 위한 스킬을 만들고 싶어요"와 같은 것일 수도 있습니다. 의미를 좁히고, 초안을 작성하고, 테스트 사례를 작성하고, 평가 방법을 파악하고, 모든 프롬프트를 실행하고, 반복하는 데 도움을 줄 수 있습니다.
반면에 그들은 이미 기술 초안을 가지고 있을 수도 있습니다. 이 경우 루프의 평가/반복 부분으로 바로 이동할 수 있습니다.
물론, 항상 유연해야 하며 사용자가 "많은 평가를 실행할 필요가 없습니다. 그냥 저와 함께 느낌을 주세요"와 같은 경우 대신 그렇게 할 수 있습니다.
그런 다음 스킬이 완료된 후(순서는 유연함) 스킬 트리거를 최적화하기 위해 완전히 별도의 스크립트가 있는 스킬 설명 개선 프로그램을 실행할 수도 있습니다.
멋지나요? 시원한.
사용자와의 소통
스킬 크리에이터는 코딩 전문 용어에 대해 폭넓게 익숙한 사람들이 사용할 수 있습니다. 아직 들어보지 못했다면(어떻게 그럴 수 있었는지, 시작된 지 아주 최근에야) Claude의 힘이 배관공에게 터미널을 열도록 영감을 주고 부모와 조부모가 Google에서 "npm 설치 방법"을 검색하는 추세가 있습니다. 반면에, 대부분의 사용자는 아마도 컴퓨터에 능숙할 것입니다.
따라서 의사소통 방법을 이해하려면 상황 신호에 주의를 기울이십시오! 기본 사례에서는 몇 가지 아이디어를 제공합니다.
- '평가'와 '벤치마크'는 경계선이지만 OK
- "JSON" 및 "어설션"의 경우 설명 없이 사용하기 전에 해당 항목이 무엇인지 알고 있다는 사용자의 심각한 단서를 보고 싶습니다.
의심스러운 경우 용어를 간략하게 설명해도 괜찮고, 사용자가 이해할 수 있을지 확실하지 않은 경우 짧은 정의로 용어를 명확하게 설명해도 됩니다.
스킬 만들기
캡처 의도
사용자의 의도를 이해하는 것부터 시작하세요. 현재 대화에는 사용자가 캡처하려는 워크플로가 이미 포함되어 있을 수 있습니다(예: "이것을 기술로 전환"이라고 말합니다). 그렇다면 먼저 대화 기록에서 답변(사용된 도구, 단계 순서, 사용자가 수정한 사항, 관찰된 입력/출력 형식)을 추출하세요. 사용자는 공백을 메워야 할 수 있으며 다음 단계를 진행하기 전에 확인해야 합니다.
- 이 기술을 통해 Claude는 무엇을 할 수 있습니까?
- 이 스킬은 언제 발동해야 합니까? (사용자 문구/컨텍스트)
- 예상되는 출력 형식은 무엇입니까?
- 기술이 작동하는지 확인하기 위해 테스트 사례를 설정해야 합니까? 객관적으로 검증 가능한 출력(파일 변환, 데이터 추출, 코드 생성, 고정된 워크플로 단계)을 갖춘 기술은 테스트 사례의 이점을 얻습니다. 주관적인 결과물(작문 스타일, 예술)을 갖춘 기술에는 종종 이러한 기술이 필요하지 않습니다. 기술 유형에 따라 적절한 기본값을 제안하되 결정은 사용자에게 맡기십시오.
인터뷰 및 연구
극단적인 사례, 입력/출력 형식, 예제 파일, 성공 기준 및 종속성에 대해 사전에 질문하세요. 이 부분이 해결될 때까지 테스트 프롬프트 작성을 기다리십시오.
사용 가능한 MCP 확인 - 연구에 유용한 경우(문서 검색, 유사한 기술 찾기, 모범 사례 검색), 가능한 경우 하위 에이전트를 통해 병렬로 연구하고, 그렇지 않으면 인라인으로 연구합니다. 사용자의 부담을 줄이기 위해 컨텍스트를 준비하세요.
SKILL.md를 작성하세요.
사용자 인터뷰를 기반으로 다음 구성 요소를 작성합니다.
- 이름: 스킬 식별자
- 설명: 트리거 시기 및 수행 작업. 이것이 기본 트리거 메커니즘입니다. 스킬이 수행하는 작업과 스킬을 언제 사용해야 하는지에 대한 특정 컨텍스트를 모두 포함합니다. 모든 "사용 시기" 정보는 본문이 아닌 여기에 있습니다. 참고: 현재 Claude는 스킬이 유용할 때 사용하지 않는 "미발동" 경향이 있습니다. 이 문제를 해결하려면 스킬 설명을 약간 "강제"하게 만드세요. 따라서 예를 들어 "내부 인류 데이터를 표시하기 위해 간단하고 빠른 대시보드를 구축하는 방법" 대신 "내부 인류 데이터를 표시하기 위해 간단하고 빠른 대시보드를 구축하는 방법"이라고 쓸 수 있습니다. 사용자가 대시보드, 데이터 시각화, 내부 지표를 언급하거나 '대시보드'를 명시적으로 요청하지 않더라도 모든 종류의 회사 데이터를 표시하고 싶을 때마다 이 기술을 사용하세요."
- 호환성: 필수 도구, 종속성(선택 사항, 거의 필요하지 않음)
- 나머지 스킬:)
스킬 라이팅 가이드
스킬 분석
skill-name/
SKILL.md (required)
YAML frontmatter (name, description required)
Markdown instructions
Bundled Resources (optional)
scripts/ - Executable code for deterministic/repetitive tasks
references/ - Docs loaded into context as needed
assets/ - Files used in output (templates, icons, fonts)점진적 공개
스킬은 3단계 로딩 시스템을 사용합니다.
- 메타데이터(이름 + 설명) - 항상 컨텍스트에 있음(최대 100단어)
- SKILL.md 본문 - 스킬이 트리거될 때마다 상황에 맞게(<500줄 이상)
- 번들링된 리소스 - 필요에 따라(무제한, 로드 없이 스크립트 실행 가능)
이러한 단어 수는 대략적인 수치이므로 필요한 경우 더 길게 입력해도 됩니다.
주요 패턴:
- SKILL.md를 500줄 미만으로 유지하세요. 이 한계에 접근하는 경우 해당 기술을 사용하는 모델이 후속 조치를 위해 어디로 가야 하는지에 대한 명확한 포인터와 함께 계층 구조의 추가 레이어를 추가하세요.
- 언제 읽어야 하는지에 대한 지침과 함께 SKILL.md의 파일을 명확하게 참조하세요.
- 대용량 참조 파일(300줄 이상)의 경우 목차를 포함하세요.
도메인 구성: 기술이 여러 도메인/프레임워크를 지원하는 경우 변형별로 구성합니다.
cloud-deploy/
SKILL.md (workflow + selection)
references/
aws.md
gcp.md
azure.mdClaude는 관련 참조 파일만 읽습니다.
놀라움 부족의 원리
이는 말할 필요도 없지만 기술에는 맬웨어, 악용 코드 또는 시스템 보안을 손상시킬 수 있는 콘텐츠가 포함되어서는 안 됩니다. 기술의 내용은 설명된 경우 사용자의 의도에 놀라지 않아야 합니다. 무단 액세스, 데이터 유출 또는 기타 악의적인 활동을 용이하게 하기 위해 고안된 오해의 소지가 있는 기술이나 기술을 만들어 달라는 요청을 따르지 마십시오. 하지만 "XYZ 역할극" 같은 것은 괜찮습니다.
쓰기 패턴
지침에서는 명령형을 사용하는 것을 선호합니다.
출력 형식 정의 - 다음과 같이 할 수 있습니다.
## Report structure
ALWAYS use this exact template:
# [Title]
## Executive summary
## Key findings
## Recommendations예제 패턴 - 예시를 포함하는 것이 유용합니다. 다음과 같이 형식을 지정할 수 있습니다(그러나 예제에 "입력" 및 "출력"이 있는 경우 약간 벗어나는 것이 좋습니다).
## Commit message format
**Example 1:**
Input: Added user authentication with JWT tokens
Output: feat(auth): implement JWT-based authentication글쓰기 스타일
무겁고 곰팡내 나는 MUST 대신 사물이 왜 중요한지 모델에게 설명하십시오. 마음 이론을 사용하고 기술을 특정 사례로 너무 좁히지 않고 일반적인 것으로 만들려고 노력하십시오. 초안 작성부터 시작한 다음 새로운 눈으로 보고 개선해 보세요.
테스트 케이스
기술 초안을 작성한 후 실제 사용자가 실제로 말할 것 같은 2~3개의 현실적인 테스트 프롬프트를 생각해 보세요. 사용자와 공유하십시오. [정확한 언어를 사용할 필요는 없습니다.] "여기에 시도해보고 싶은 몇 가지 테스트 사례가 있습니다. 이것이 올바르게 보이는지, 아니면 더 추가하시겠습니까?" 그런 다음 실행하십시오.
테스트 사례를evals/evals.json에 저장합니다. 아직 주장을 작성하지 말고 프롬프트만 작성하세요. 실행이 진행되는 동안 다음 단계에서 어설션 초안을 작성합니다.
{
"skill_name": "example-skill",
"evals": [
{
"id": 1,
"prompt": "User's task prompt",
"expected_output": "Description of expected result",
"files": []
}
]
}전체 스키마(나중에 추가할assertions필드 포함)는references/schemas.md를 참조하세요.
테스트 케이스 실행 및 평가
이 섹션은 하나의 연속적인 시퀀스입니다. 중간에 멈추지 마세요./skill-test또는 기타 테스트 기술을 사용하지 마십시오.
<skill-name>-workspace/에 결과를 기술 디렉터리의 형제로 넣습니다. 작업 공간 내에서 반복(iteration-1/,iteration-2/등)별로 결과를 구성하고 그 내에서 각 테스트 사례에 디렉터리(eval-0/,eval-1/등)가 제공됩니다. 이 모든 것을 미리 만들지 말고 진행하면서 디렉터리를 만드십시오.
1단계: 같은 턴에 모든 실행(스킬 포함 및 기준선 포함) 생성
각 테스트 사례에 대해 동일한 턴에 두 개의 하위 에이전트를 생성합니다. 하나는 스킬이 있고 다른 하나는 스킬이 없습니다. 이는 중요합니다. 스킬이 있는 실행을 먼저 생성한 다음 나중에 기준선을 위해 다시 돌아오지 마십시오. 모든 것을 한 번에 실행하면 거의 같은 시간에 완료됩니다.
스킬 달리기:
Execute this task:
- Skill path: <path-to-skill>
- Task: <eval prompt>
- Input files: <eval files if any, or "none">
- Save outputs to: <workspace>/iteration-<N>/eval-<ID>/with_skill/outputs/
- Outputs to save: <what the user cares about - e.g., "the .docx file", "the final CSV">기준 실행(동일한 프롬프트이지만 기준은 상황에 따라 다름):
- 새 스킬 만들기: 스킬이 전혀 없습니다. 동일한 프롬프트, 기술 경로 없음,
without_skill/outputs/에 저장합니다. - 기존 스킬 개선: 이전 버전. 편집하기 전에 스킬(
cp -r <skill-path> <workspace>/skill-snapshot/)의 스냅샷을 찍은 다음 스냅샷에서 기준 하위 에이전트를 가리킵니다.old_skill/outputs/에 저장합니다.
각 테스트 케이스에 대해eval_metadata.json를 작성합니다(지금은 어설션이 비어 있을 수 있음). 단순히 "eval-0"이 아닌 테스트 대상을 기반으로 각 평가에 설명적인 이름을 지정합니다. 디렉토리에도 이 이름을 사용하십시오. 이 반복에서 새 평가 프롬프트나 수정된 평가 프롬프트를 사용하는 경우 각각의 새 평가 디렉터리에 대해 이러한 파일을 생성하세요. 이전 반복에서 가져온다고 가정하지 마세요.
{
"eval_id": 0,
"eval_name": "descriptive-name-here",
"prompt": "The user's task prompt",
"assertions": []
}2단계: 실행이 진행되는 동안 초안 어설션
실행이 끝날 때까지 기다리지 마십시오. 이 시간을 생산적으로 사용할 수 있습니다. 각 테스트 케이스에 대한 정량적 주장 초안을 작성하고 이를 사용자에게 설명합니다.evals/evals.json에 어설션이 이미 존재하는 경우 이를 검토하고 무엇을 확인하는지 설명하세요.
좋은 주장은 객관적으로 검증 가능하며 설명적인 이름을 가지고 있습니다. 결과를 보는 사람이 각 주장이 무엇을 확인하는지 즉시 이해할 수 있도록 벤치마크 뷰어에서 명확하게 읽어야 합니다. 주관적인 기술(작문 스타일, 디자인 품질)은 질적으로 더 잘 평가됩니다. 인간의 판단이 필요한 것에 대해 주장을 강요하지 마십시오.
초안이 작성된 어설션으로eval_metadata.json파일과evals/evals.json를 업데이트합니다. 또한 시청자에게 무엇이 표시되는지(정성적 결과와 정량적 벤치마크 모두) 사용자에게 설명합니다.
3단계: 실행이 완료되면 타이밍 데이터 캡처
각 하위 에이전트 작업이 완료되면total_tokens및duration_ms가 포함된 알림을 받습니다. 이 데이터를 실행 디렉터리의timing.json에 즉시 저장합니다.
{
"total_tokens": 84852,
"duration_ms": 23332,
"total_duration_seconds": 23.3
}이는 이 데이터를 캡처할 수 있는 유일한 기회입니다. 이는 작업 알림을 통해 제공되며 다른 곳에는 유지되지 않습니다. 각 알림을 일괄 처리하는 대신 도착하는 대로 처리하세요.
4단계: 뷰어 채점, 집계 및 실행
모든 실행이 완료되면:
-
각 실행 평가 -
agents/grader.md를 읽고 출력에 대해 각 어설션을 평가하는 그레이더 하위 에이전트(또는 인라인 등급)를 생성합니다. 각 실행 디렉터리의grading.json에 결과를 저장합니다. grading.json 기대 배열은text,passed및evidence필드(name/met/details또는 기타 변형 아님)를 사용해야 합니다. 뷰어는 이러한 정확한 필드 이름에 따라 달라집니다. 프로그래밍 방식으로 확인할 수 있는 어설션의 경우 눈으로 확인하기보다는 스크립트를 작성하고 실행하세요. 스크립트는 더 빠르고 안정적이며 반복 전반에 걸쳐 재사용할 수 있습니다. -
벤치마크로 집계 - Skill-creator 디렉터리에서 집계 스크립트를 실행합니다.
python -m scripts.aggregate_benchmark <workspace>/iteration-N --skill-name <name>이는 평균 stddev 및 델타와 함께 각 구성에 대한 pass_rate, 시간 및 토큰을 포함하는
benchmark.json및benchmark.md를 생성합니다. benchmark.json을 수동으로 생성하는 경우 뷰어가 기대하는 정확한 스키마는references/schemas.md를 참조하세요. 각 with_skill 버전을 기준 버전 앞에 놓으십시오. -
분석가 통과 - 벤치마크 데이터를 읽고 집계 통계가 숨길 수 있는 패턴을 찾아보세요. 찾아야 할 내용은
agents/analyzer.md("벤치마크 결과 분석" 섹션)를 참조하세요. 기술(비차별), 높은 분산 평가(불안정할 수 있음) 및 시간/토큰 트레이드오프에 관계없이 항상 통과하는 어설션과 같은 것입니다. -
정성적 결과와 정량적 데이터가 모두 포함된 뷰어 실행:
nohup python <skill-creator-path>/eval-viewer/generate_review.py \ <workspace>/iteration-N \ --skill-name "my-skill" \ --benchmark <workspace>/iteration-N/benchmark.json \ > /dev/null 2>&1 & VIEWER_PID=$!2회 이상 반복의 경우
--previous-workspace <workspace>/iteration-<N-1>도 전달합니다.Cowork/headless 환경:
webbrowser.open()를 사용할 수 없거나 환경에 디스플레이가 없는 경우 서버를 시작하는 대신--static <output_path>를 사용하여 독립형 HTML 파일을 작성합니다. 사용자가 "모든 리뷰 제출"을 클릭하면 피드백이feedback.json파일로 다운로드됩니다. 다운로드 후 다음 반복을 위해feedback.json를 작업 공간 디렉터리에 복사합니다.
참고: 뷰어를 생성하려면 generate_review.py를 사용하세요. 사용자 정의 HTML을 작성할 필요가 없습니다.
- 사용자에게 다음과 같이 말하세요. "브라우저에서 결과를 열었습니다. 두 개의 탭이 있습니다. '출력'을 사용하면 각 테스트 사례를 클릭하고 피드백을 남길 수 있고, '벤치마크'를 사용하면 정량적 비교가 표시됩니다. 완료되면 여기로 돌아와 알려주세요."
사용자가 뷰어에서 보는 것
"출력" 탭에는 한 번에 하나의 테스트 사례가 표시됩니다.
- 프롬프트: 주어진 작업
- 출력: 기술이 생성하고 가능한 경우 인라인으로 렌더링된 파일
- 이전 출력(반복 2+): 마지막 반복의 출력을 표시하는 축소된 섹션
- 공식 성적(채점이 실행된 경우): 어설션 통과/실패를 표시하는 축소된 섹션
- 피드백: 입력과 동시에 자동 저장되는 텍스트 상자
- 이전 피드백(반복 2+): 지난번 댓글이 텍스트 상자 아래에 표시됩니다.
"벤치마크" 탭에는 평가별 분석 및 분석가 관찰과 함께 각 구성의 합격률, 타이밍 및 토큰 사용량과 같은 통계 요약이 표시됩니다.
탐색은 이전/다음 버튼이나 화살표 키를 통해 이루어집니다. 완료되면 "모든 리뷰 제출"을 클릭하여 모든 피드백을feedback.json에 저장합니다.
5단계: 피드백 읽기
사용자가 완료되었다고 말하면feedback.json를 읽어보세요.
{
"reviews": [
{"run_id": "eval-0-with_skill", "feedback": "the chart is missing axis labels", "timestamp": "..."},
{"run_id": "eval-1-with_skill", "feedback": "", "timestamp": "..."},
{"run_id": "eval-2-with_skill", "feedback": "perfect, love this", "timestamp": "..."}
],
"status": "complete"
}빈 피드백은 사용자가 괜찮다고 생각했다는 의미입니다. 사용자가 특정 불만을 제기한 테스트 사례에 개선점을 집중하세요.
작업이 끝나면 뷰어 서버를 종료합니다.
kill $VIEWER_PID 2>/dev/null스킬 향상
이것이 루프의 핵심입니다. 테스트 사례를 실행했고 사용자가 결과를 검토했으며 이제 피드백을 기반으로 기술을 개선해야 합니다.
개선을 생각하는 방법
-
피드백을 통해 일반화합니다. 여기서 일어나는 큰 그림은 우리가 다양한 프롬프트에서 백만 번(문자 그대로, 어쩌면 더 많이) 사용할 수 있는 기술을 만들려고 노력하고 있다는 것입니다. 여기에서는 더 빠르게 진행하는 데 도움이 되기 때문에 귀하와 사용자는 몇 가지 예만 반복해서 반복하고 있습니다. 사용자는 이러한 예를 안팎으로 알고 있으며 새로운 출력을 빠르게 평가할 수 있습니다. 그러나 당신과 사용자가 공동 개발하는 기술이 해당 예제에만 작동한다면 그것은 쓸모가 없습니다. 까다로운 문제가 있는 경우 지나치게 과적합된 변경을 적용하거나 억압적으로 제한하는 MUST를 적용하는 대신 다른 은유를 사용하거나 다른 작업 패턴을 권장해 볼 수 있습니다. 시도하는 것은 상대적으로 저렴하며 아마도 뭔가 훌륭한 일을 할 수 있을 것입니다.
-
즉시 간결하게 유지하세요. 무게를 끌지 못하는 것들을 제거하세요. 최종 출력뿐만 아니라 기록도 읽어야 합니다. 기술로 인해 모델이 비생산적인 작업을 수행하는 데 많은 시간을 낭비하게 만드는 것처럼 보이면 해당 기술의 일부를 제거하고 무슨 일이 일어나는지 확인할 수 있습니다.
-
이유를 설명하세요. 모델에게 요청하는 모든 것 뒤에 있는 이유를 설명하기 위해 열심히 노력하세요. 오늘날의 LLM은 스마트합니다. 그들은 좋은 마음 이론을 가지고 있으며, 좋은 도구가 제공되면 기계적인 지시를 뛰어넘어 실제로 일을 실현할 수 있습니다. 사용자의 피드백이 간결하거나 답답하더라도 실제로 작업을 이해하고 사용자가 작성한 내용을 왜 작성하는지, 실제로 작성한 내용을 이해하도록 노력한 다음 이러한 이해를 지침에 전달하십시오. ALWAYS 또는 NEVER를 모두 대문자로 쓰거나 매우 견고한 구조를 사용하는 경우 이는 노란색 플래그입니다. 가능하다면 모델이 요청하는 사항이 왜 중요한지 이해할 수 있도록 추론을 재구성하고 설명하세요. 이는 보다 인도적이고 강력하며 효과적인 접근 방식입니다.
-
테스트 사례 전체에서 반복되는 작업을 찾아보세요. 테스트 실행의 기록을 읽고 하위 에이전트가 모두 독립적으로 유사한 도우미 스크립트를 작성했는지 또는 무언가에 대해 동일한 다단계 접근 방식을 취했는지 확인합니다. 3가지 테스트 사례 모두 하위 에이전트가
create_docx.py또는build_chart.py를 작성하는 결과를 가져온 경우 이는 기술이 해당 스크립트를 번들로 묶어야 한다는 강력한 신호입니다. 한번 작성해서scripts/에 넣고 스킬에게 사용하라고 알려주세요. 이렇게 하면 향후 모든 호출에서 바퀴를 재발명하지 않아도 됩니다.
이 작업은 매우 중요하며(우리는 여기서 연간 수십억 달러의 경제적 가치를 창출하려고 노력하고 있습니다!) 생각하는 시간이 방해가 되지 않습니다. 시간을 갖고 정말 곰곰이 생각해 보세요. 초안 개정판을 작성한 다음 다시 살펴보고 개선하는 것이 좋습니다. 사용자의 머리 속으로 들어가 그들이 원하고 필요한 것이 무엇인지 이해하기 위해 최선을 다하십시오.
반복 루프
스킬을 향상시킨 후:
- 기술에 개선 사항을 적용하십시오.
- 기준 실행을 포함하여 모든 테스트 사례를 새로운
iteration-<N+1>/디렉터리로 다시 실행합니다. 새 기술을 생성하는 경우 기준은 항상without_skill(기술 없음)입니다. 이는 반복 전반에 걸쳐 동일하게 유지됩니다. 기존 기술을 개선하는 경우 기준으로 적합한 것이 무엇인지 판단하십시오(사용자가 사용한 원래 버전 또는 이전 반복). - 이전 반복을 가리키는
--previous-workspace를 사용하여 리뷰어를 시작합니다. - 사용자가 검토하고 완료되었다고 말할 때까지 기다립니다.
- 새로운 피드백을 읽고, 다시 개선하고, 반복하세요
다음까지 계속 진행하세요.
- 사용자가 행복하다고 말합니다.
- 피드백이 다 비어있습니다(다 괜찮아 보이네요)
- 의미 있는 진전을 이루지 못하고 있습니다.
고급: 블라인드 비교
두 가지 버전의 기술을 보다 엄격하게 비교하려는 경우(예: 사용자가 "새 버전이 실제로 더 나은가요?"라고 묻는 경우)에는 블라인드 비교 시스템이 있습니다. 자세한 내용은agents/comparator.md및agents/analyzer.md를 읽어보세요. 기본 아이디어는 어느 것이 어느 것인지 알려주지 않고 독립 에이전트에 두 개의 출력을 제공하고 품질을 판단하도록 하는 것입니다. 그런 다음 승자가 승리한 이유를 분석해 보세요.
이는 선택 사항이며 하위 에이전트가 필요하며 대부분의 사용자에게는 필요하지 않습니다. 일반적으로 사람의 검토 루프로 충분합니다.
설명 최적화
SKILL.md 앞부분의 설명 필드는 Claude가 스킬을 호출하는지 여부를 결정하는 기본 메커니즘입니다. 스킬을 생성하거나 개선한 후 더 나은 트리거 정확도를 위해 설명을 최적화하도록 제안하십시오.
1단계: 트리거 평가 쿼리 생성
트리거해야 하는 쿼리와 트리거하지 말아야 하는 쿼리를 혼합하여 20개의 평가 쿼리를 만듭니다. JSON으로 저장:
[
{"query": "the user prompt", "should_trigger": true},
{"query": "another prompt", "should_trigger": false}
]쿼리는 현실적이어야 하며 Claude Code 또는 Claude.ai 사용자가 실제로 입력하는 내용이어야 합니다. 추상적인 요청이 아니라 구체적이고 구체적이며 상당한 양의 세부정보가 포함된 요청입니다. 예를 들어 파일 경로, 사용자의 직업이나 상황에 대한 개인적인 맥락, 열 이름과 값, 회사 이름, URL 등이 있습니다. 약간의 뒷이야기. 일부는 소문자이거나 약어, 오타 또는 일상적인 말이 포함되어 있을 수 있습니다. 다양한 길이를 혼합하여 사용하고 명확하게 만드는 대신 극단적인 사례에 집중하세요(사용자가 승인할 기회를 얻습니다).
불량:"Format this data","Extract text from PDF","Create a chart"
좋음:"ok so my boss just sent me this xlsx file (its in my downloads, called something like 'Q4 sales final FINAL v2.xlsx') and she wants me to add a column that shows the profit margin as a percentage. The revenue is in column C and costs are in column D i think"
트리거해야 함 쿼리(8-10)의 경우 적용 범위를 고려하세요. 동일한 의도를 가진 다른 표현을 원합니다. 일부는 격식을 차리고 일부는 캐주얼하게 표현합니다. 사용자가 기술이나 파일 형식의 이름을 명시적으로 지정하지 않지만 분명히 필요한 경우를 포함합니다. 이 기술이 다른 기술과 경쟁하지만 승리해야 하는 몇 가지 흔하지 않은 사용 사례와 사례를 던져보세요.
트리거해서는 안 되는 쿼리(8-10)의 경우 가장 가치 있는 쿼리는 기술과 키워드 또는 개념을 공유하지만 실제로는 다른 것이 필요한 쿼리인 아차 실패입니다. 인접한 도메인, 순진한 키워드 일치가 실행되지만 실행되어서는 안 되는 모호한 표현, 쿼리가 기술이 수행하는 작업을 다루지만 다른 도구가 더 적합한 상황에서 발생하는 경우를 생각해 보세요.
피해야 할 핵심 사항: 트리거하면 안 되는 쿼리를 분명히 관련성이 없게 만들지 마세요. PDF 기술에 대한 부정적인 테스트로서 "피보나치 함수 작성"은 너무 쉽습니다. 아무것도 테스트하지 않습니다. 부정적인 사례는 정말로 까다로워야 합니다.
2단계: 사용자와 함께 검토
HTML 템플릿을 사용하여 검토할 수 있도록 평가 세트를 사용자에게 제공합니다.
assets/eval_review.html에서 템플릿 읽기- 자리 표시자를 교체합니다.
__EVAL_DATA_PLACEHOLDER__-> 평가 항목의 JSON 배열(따옴표 없음 - JS 변수 할당)__SKILL_NAME_PLACEHOLDER__-> 스킬 이름__SKILL_DESCRIPTION_PLACEHOLDER__-> 스킬의 현재 설명
- 임시 파일(예:
/tmp/eval_review_<skill-name>.html)에 쓰고 엽니다:open /tmp/eval_review_<skill-name>.html - 사용자는 쿼리를 편집하고, 트리거해야 하는 항목을 전환하고, 항목을 추가/제거한 다음 "평가 세트 내보내기"를 클릭할 수 있습니다.
- 파일이
~/Downloads/eval_set.json에 다운로드됩니다. 파일이 여러 개인 경우(예:eval_set (1).json) 다운로드 폴더에서 최신 버전을 확인하세요.
이 단계는 중요합니다. 잘못된 평가 쿼리는 잘못된 설명으로 이어집니다.
3단계: 최적화 루프 실행
사용자에게 다음과 같이 말합니다. "시간이 좀 걸립니다. 백그라운드에서 최적화 루프를 실행하고 주기적으로 확인하겠습니다."
평가 세트를 작업 공간에 저장한 후 백그라운드에서 실행합니다.
python -m scripts.run_loop \
--eval-set <path-to-trigger-eval.json> \
--skill-path <path-to-skill> \
--model <model-id-powering-this-session> \
--max-iterations 5 \
--verbose시스템 프롬프트(현재 세션을 지원하는 모델)의 모델 ID를 사용하여 트리거 테스트가 사용자가 실제로 경험하는 것과 일치하도록 합니다.
실행되는 동안 주기적으로 출력을 추적하여 반복이 진행 중인 것과 점수가 어떻게 보이는지에 대한 업데이트를 사용자에게 제공합니다.
이는 전체 최적화 루프를 자동으로 처리합니다. 평가 세트를 60% 학습 및 40% 유지 테스트로 분할하고 현재 설명을 평가한 다음(신뢰할 수 있는 트리거 속도를 얻기 위해 각 쿼리를 3번 실행) Claude를 호출하여 실패한 항목을 기반으로 개선 사항을 제안합니다. 학습과 테스트 모두에서 각각의 새로운 설명을 재평가하여 최대 5회 반복합니다. 완료되면 반복당 결과를 표시하는 HTML 보고서를 브라우저에서 열고best_description와 함께 JSON을 반환합니다. 이는 과적합을 방지하기 위해 훈련 점수가 아닌 테스트 점수로 선택됩니다.
스킬 발동 방식
트리거링 메커니즘을 이해하면 더 나은 평가 쿼리를 설계하는 데 도움이 됩니다. 기술은 이름 + 설명과 함께 Claude의available_skills목록에 표시되며 Claude는 해당 설명을 기반으로 기술을 참조할지 여부를 결정합니다. 알아야 할 중요한 점은 Claude가 자체적으로 쉽게 처리할 수 없는 작업에 대해서만 기술을 참조한다는 것입니다. "이 PDF 읽기"와 같은 간단한 1단계 쿼리는 설명이 완벽하게 일치하더라도 Claude가 기본 도구를 사용하여 직접 처리할 수 있기 때문에 기술을 트리거하지 않을 수 있습니다. 설명이 일치하면 복잡하거나 다단계 또는 특수 쿼리가 기술을 안정적으로 트리거합니다.
이는 평가 쿼리가 Claude가 실제로 기술 컨설팅을 통해 이익을 얻을 수 있을 만큼 충분히 실질적이어야 함을 의미합니다. "파일 X 읽기"와 같은 간단한 쿼리는 좋지 않은 테스트 사례입니다. 설명 품질에 관계없이 기술을 트리거하지 않습니다.
4단계: 결과 적용
JSON 출력에서best_description를 가져와 스킬의 SKILL.md 앞부분을 업데이트합니다. 사용자에게 전후를 보여주고 점수를 보고합니다.
패키지 및 선물(present_files도구를 사용할 수 있는 경우에만)
present_files도구에 대한 액세스 권한이 있는지 확인하세요. 그렇지 않은 경우 이 단계를 건너뛰세요. 그렇게 하는 경우 기술을 패키지하고.skill 파일을 사용자에게 제공합니다.
python -m scripts.package_skill <path/to/skill-folder>패키징한 후 사용자가 설치할 수 있도록 결과.skill파일 경로를 안내합니다.
Claude.ai 관련 지침
Claude.ai의 핵심 워크플로는 동일하지만(초안 -> 테스트 -> 검토 -> 개선 -> 반복) Claude.ai에는 하위 에이전트가 없기 때문에 일부 메커니즘이 변경됩니다. 적응해야 할 사항은 다음과 같습니다.
테스트 사례 실행: 하위 에이전트가 없다는 것은 병렬 실행이 없음을 의미합니다. 각 테스트 사례에 대해 해당 기술의 SKILL.md를 읽은 다음 해당 지침에 따라 테스트 프롬프트를 직접 수행하세요. 한 번에 하나씩 수행하십시오. 이는 독립 하위 에이전트보다 덜 엄격하지만(스킬을 작성하고 실행하므로 전체 컨텍스트를 갖습니다) 유용한 온전성 검사이며 사람의 검토 단계가 보상합니다. 기준 실행을 건너뛰고 기술을 사용하여 요청한 대로 작업을 완료하세요.
결과 검토 중: 브라우저를 열 수 없는 경우(예: Claude.ai의 VM에 디스플레이가 없거나 원격 서버에 있는 경우) 브라우저 검토자를 완전히 건너뜁니다. 대신 대화에서 직접 결과를 제시하세요. 각 테스트 사례에 대해 프롬프트와 출력을 표시합니다. 출력이 사용자가 확인해야 하는 파일(예:.docx 또는.xlsx)인 경우 파일 시스템에 저장하고 사용자가 다운로드하고 검사할 수 있도록 위치를 알려줍니다. 인라인으로 피드백을 요청하세요. "이거 어때요? 변경할 사항이 있나요?"
벤치마킹: 정량적 벤치마킹을 건너뜁니다. 하위 에이전트 없이는 의미가 없는 기준 비교에 의존합니다. 사용자의 정성적인 피드백에 중점을 둡니다.
반복 루프: 이전과 동일합니다. 기술을 향상하고, 테스트 사례를 다시 실행하고, 피드백을 요청합니다. 중간에 브라우저 검토자가 없습니다. 결과가 있는 경우 파일 시스템의 반복 디렉터리에 결과를 구성할 수 있습니다.
설명 최적화: 이 섹션에는 Claude Code에서만 사용할 수 있는claudeCLI 도구(구체적으로claude -p)가 필요합니다. Claude.ai를 사용 중이라면 건너뛰세요.
블라인드 비교: 하위 에이전트가 필요합니다. 건너뛰세요.
패키징:package_skill.py스크립트는 Python 및 파일 시스템과 함께 어디서나 작동합니다. Claude.ai에서 이를 실행할 수 있으며 사용자는 결과.skill파일을 다운로드할 수 있습니다.
기존 기술 업데이트: 사용자가 새 기술을 만드는 것이 아니라 기존 기술을 업데이트하도록 요청할 수 있습니다. 이 경우:
- 원래 이름을 유지하세요. 스킬의 디렉터리 이름과
name머리말 필드를 기록해 두세요. 변경하지 않고 사용하세요. 예를 들어, 장착된 스킬이research-helper라면research-helper.skill(research-helper-v2아님)를 출력합니다. - 편집하기 전에 쓰기 가능한 위치에 복사하세요. 설치된 스킬 경로가 읽기 전용일 수 있습니다.
/tmp/skill-name/에 복사하고 거기에서 편집한 다음 복사본에서 패키지화합니다. - 수동으로 패키징하는 경우 먼저
/tmp/로 스테이지한 후 출력 디렉터리에 복사합니다. 권한으로 인해 직접 쓰기가 실패할 수 있습니다.
동료별 지침
Cowork를 사용하는 경우 알아야 할 주요 사항은 다음과 같습니다.
- 하위 에이전트가 있으므로 기본 워크플로(병렬로 테스트 사례 생성, 기준선 실행, 등급 지정 등)가 모두 작동합니다. (그러나 시간 초과로 인해 심각한 문제가 발생하는 경우 테스트 프롬프트를 병렬이 아닌 직렬로 실행하는 것이 좋습니다.)
- 브라우저나 디스플레이가 없으므로 평가 뷰어를 생성할 때 서버를 시작하는 대신
--static <output_path>를 사용하여 독립형 HTML 파일을 작성하세요. 그런 다음 사용자가 클릭하여 브라우저에서 HTML을 열 수 있는 링크를 제공합니다. - 어떤 이유로든 Cowork 설정은 Claude가 테스트 실행 후 평가 뷰어를 생성하는 것을 방해하는 것 같습니다. 따라서 다시 강조하자면 Cowork에 있든 Claude Code에 있든 테스트를 실행한 후
generate_review.py를 사용하여 기술을 직접 수정하고 수정을 시도하기 전에 항상 사람이 예제를 볼 수 있도록 평가 뷰어를 생성해야 합니다(자신의 부티크 HTML 코드를 작성하지 않음). 미리 죄송하지만 여기서는 대문자로 표시하겠습니다. 입력을 직접 평가하기 전에 평가자 뷰어를 생성하세요. 당신은 최대한 빨리 인간 앞에 그들을 데려오고 싶어합니다! - 피드백은 다르게 작동합니다. 실행 중인 서버가 없기 때문에 뷰어의 "모든 리뷰 제출" 버튼을 누르면
feedback.json가 파일로 다운로드됩니다. 그런 다음 거기에서 읽을 수 있습니다(먼저 액세스를 요청해야 할 수도 있음). - 패키징 작업 -
package_skill.py에는 Python과 파일 시스템만 필요합니다. - 설명 최적화(
run_loop.py/run_eval.py)는 브라우저가 아닌 하위 프로세스를 통해claude -p를 사용하므로 Cowork에서 제대로 작동해야 합니다. 하지만 스킬 만들기가 완전히 완료되고 사용자가 상태가 양호하다고 동의할 때까지 저장하세요. - 기존 기술 업데이트: 사용자가 새 기술을 만드는 것이 아니라 기존 기술을 업데이트하도록 요청할 수 있습니다. 위의 clude.ai 섹션에 있는 업데이트 지침을 따르세요.
참조 파일
Agents/ 디렉터리에는 특수 하위 에이전트에 대한 지침이 포함되어 있습니다. 관련 하위 에이전트를 생성해야 할 때 이를 읽으십시오.
agents/grader.md- 출력에 대한 어설션을 평가하는 방법agents/comparator.md- 두 출력 간의 블라인드 A/B 비교를 수행하는 방법agents/analyzer.md- 한 버전이 다른 버전보다 뛰어난 이유를 분석하는 방법
reference/ 디렉토리에는 추가 문서가 있습니다:
references/schemas.md- evals.json, grading.json 등에 대한 JSON 구조
강조하기 위해 여기서 핵심 루프를 한 번 더 반복합니다.
- 스킬이 무엇인지 알아보세요.
- 스킬 초안 작성 또는 편집
- 테스트 프롬프트에서 clude-with-access-to-the-skill 실행
- 사용자와 함께 출력을 평가합니다.
- benchmark.json을 만들고
eval-viewer/generate_review.py를 실행하여 사용자가 검토할 수 있도록 돕습니다. - 정량적 평가 실행
- benchmark.json을 만들고
- 귀하와 사용자가 만족할 때까지 반복하십시오.
- 최종 스킬을 패키징하여 사용자에게 반환합니다.
그런 일이 있으면 TodoList에 단계를 추가하여 잊지 않도록 하세요. Cowork를 사용하는 경우 TodoList에 "평가 JSON을 생성하고eval-viewer/generate_review.py를 실행하여 사람이 테스트 사례를 검토할 수 있도록"을 구체적으로 넣어서 이러한 일이 발생하는지 확인하세요.
행운을 빕니다!
리소스 파일
라이센스.txt
바이너리 리소스
에이전트/analyzer.md
바이너리 리소스
에이전트/비교기.md
바이너리 리소스
에이전트/grader.md
바이너리 리소스
자산/eval_review.html
바이너리 리소스
평가 뷰어/generate_review.py
eval-viewer/generate_review.py 다운로드
바이너리 리소스
평가-뷰어/viewer.html
바이너리 리소스
참조/schemas.md
바이너리 리소스
스크립트/init.py
바이너리 리소스
스크립트/aggregate_benchmark.py
스크립트 다운로드/aggregate_benchmark.py
바이너리 리소스
스크립트/generate_report.py
바이너리 리소스
스크립트/improve_description.py
스크립트 다운로드/improve_description.py
바이너리 리소스
스크립트/package_skill.py
#!/usr/bin/env python3
"""
Skill Packager - Creates a distributable .skill file of a skill folder
Usage:
python utils/package_skill.py <path/to/skill-folder> [output-directory]
Example:
python utils/package_skill.py skills/public/my-skill
python utils/package_skill.py skills/public/my-skill ./dist
"""
import fnmatch
import sys
import zipfile
from pathlib import Path
from scripts.quick_validate import validate_skill
# Patterns to exclude when packaging skills.
EXCLUDE_DIRS = {"__pycache__", "node_modules"}
EXCLUDE_GLOBS = {"*.pyc"}
EXCLUDE_FILES = {".DS_Store"}
# Directories excluded only at the skill root (not when nested deeper).
ROOT_EXCLUDE_DIRS = {"evals"}
def should_exclude(rel_path: Path) -> bool:
"""Check if a path should be excluded from packaging."""
parts = rel_path.parts
if any(part in EXCLUDE_DIRS for part in parts):
return True
# rel_path is relative to skill_path.parent, so parts[0] is the skill
# folder name and parts[1] (if present) is the first subdir.
if len(parts) > 1 and parts[1] in ROOT_EXCLUDE_DIRS:
return True
name = rel_path.name
if name in EXCLUDE_FILES:
return True
return any(fnmatch.fnmatch(name, pat) for pat in EXCLUDE_GLOBS)
def package_skill(skill_path, output_dir=None):
"""
Package a skill folder into a .skill file.
Args:
skill_path: Path to the skill folder
output_dir: Optional output directory for the .skill file (defaults to current directory)
Returns:
Path to the created .skill file, or None if error
"""
skill_path = Path(skill_path).resolve()
# Validate skill folder exists
if not skill_path.exists():
print(f"❌ Error: Skill folder not found: {skill_path}")
return None
if not skill_path.is_dir():
print(f"❌ Error: Path is not a directory: {skill_path}")
return None
# Validate SKILL.md exists
skill_md = skill_path / "SKILL.md"
if not skill_md.exists():
print(f"❌ Error: SKILL.md not found in {skill_path}")
return None
# Run validation before packaging
print("🔍 Validating skill...")
valid, message = validate_skill(skill_path)
if not valid:
print(f"❌ Validation failed: {message}")
print(" Please fix the validation errors before packaging.")
return None
print(f"✅ {message}\n")
# Determine output location
skill_name = skill_path.name
if output_dir:
output_path = Path(output_dir).resolve()
output_path.mkdir(parents=True, exist_ok=True)
else:
output_path = Path.cwd()
skill_filename = output_path / f"{skill_name}.skill"
# Create the .skill file (zip format)
try:
with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
# Walk through the skill directory, excluding build artifacts
for file_path in skill_path.rglob('*'):
if not file_path.is_file():
continue
arcname = file_path.relative_to(skill_path.parent)
if should_exclude(arcname):
print(f" Skipped: {arcname}")
continue
zipf.write(file_path, arcname)
print(f" Added: {arcname}")
print(f"\n✅ Successfully packaged skill to: {skill_filename}")
return skill_filename
except Exception as e:
print(f"❌ Error creating .skill file: {e}")
return None
def main():
if len(sys.argv) < 2:
print("Usage: python utils/package_skill.py <path/to/skill-folder> [output-directory]")
print("\nExample:")
print(" python utils/package_skill.py skills/public/my-skill")
print(" python utils/package_skill.py skills/public/my-skill ./dist")
sys.exit(1)
skill_path = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else None
print(f"📦 Packaging skill: {skill_path}")
if output_dir:
print(f" Output directory: {output_dir}")
print()
result = package_skill(skill_path, output_dir)
if result:
sys.exit(0)
else:
sys.exit(1)
if __name__ == "__main__":
main()스크립트/quick_validate.py
#!/usr/bin/env python3
"""
Quick validation script for skills - minimal version
"""
import sys
import os
import re
import yaml
from pathlib import Path
def validate_skill(skill_path):
"""Basic validation of a skill"""
skill_path = Path(skill_path)
# Check SKILL.md exists
skill_md = skill_path / 'SKILL.md'
if not skill_md.exists():
return False, "SKILL.md not found"
# Read and validate frontmatter
content = skill_md.read_text()
if not content.startswith('---'):
return False, "No YAML frontmatter found"
# Extract frontmatter
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
if not match:
return False, "Invalid frontmatter format"
frontmatter_text = match.group(1)
# Parse YAML frontmatter
try:
frontmatter = yaml.safe_load(frontmatter_text)
if not isinstance(frontmatter, dict):
return False, "Frontmatter must be a YAML dictionary"
except yaml.YAMLError as e:
return False, f"Invalid YAML in frontmatter: {e}"
# Define allowed properties
ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata', 'compatibility'}
# Check for unexpected properties (excluding nested keys under metadata)
unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
if unexpected_keys:
return False, (
f"Unexpected key(s) in SKILL.md frontmatter: {', '.join(sorted(unexpected_keys))}. "
f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}"
)
# Check required fields
if 'name' not in frontmatter:
return False, "Missing 'name' in frontmatter"
if 'description' not in frontmatter:
return False, "Missing 'description' in frontmatter"
# Extract name for validation
name = frontmatter.get('name', '')
if not isinstance(name, str):
return False, f"Name must be a string, got {type(name).__name__}"
name = name.strip()
if name:
# Check naming convention (kebab-case: lowercase with hyphens)
if not re.match(r'^[a-z0-9-]+$', name):
return False, f"Name '{name}' should be kebab-case (lowercase letters, digits, and hyphens only)"
if name.startswith('-') or name.endswith('-') or '--' in name:
return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens"
# Check name length (max 64 characters per spec)
if len(name) > 64:
return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters."
# Extract and validate description
description = frontmatter.get('description', '')
if not isinstance(description, str):
return False, f"Description must be a string, got {type(description).__name__}"
description = description.strip()
if description:
# Check for angle brackets
if '<' in description or '>' in description:
return False, "Description cannot contain angle brackets (< or >)"
# Check description length (max 1024 characters per spec)
if len(description) > 1024:
return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters."
# Validate compatibility field if present (optional)
compatibility = frontmatter.get('compatibility', '')
if compatibility:
if not isinstance(compatibility, str):
return False, f"Compatibility must be a string, got {type(compatibility).__name__}"
if len(compatibility) > 500:
return False, f"Compatibility is too long ({len(compatibility)} characters). Maximum is 500 characters."
return True, "Skill is valid!"
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python quick_validate.py <skill_directory>")
sys.exit(1)
valid, message = validate_skill(sys.argv[1])
print(message)
sys.exit(0 if valid else 1)스크립트/run_eval.py
바이너리 리소스
스크립트/run_loop.py
바이너리 리소스
스크립트/utils.py
"""Shared utilities for skill-creator scripts."""
from pathlib import Path
def parse_skill_md(skill_path: Path) -> tuple[str, str, str]:
"""Parse a SKILL.md file, returning (name, description, full_content)."""
content = (skill_path / "SKILL.md").read_text()
lines = content.split("\n")
if lines[0].strip() != "---":
raise ValueError("SKILL.md missing frontmatter (no opening ---)")
end_idx = None
for i, line in enumerate(lines[1:], start=1):
if line.strip() == "---":
end_idx = i
break
if end_idx is None:
raise ValueError("SKILL.md missing frontmatter (no closing ---)")
name = ""
description = ""
frontmatter_lines = lines[1:end_idx]
i = 0
while i < len(frontmatter_lines):
line = frontmatter_lines[i]
if line.startswith("name:"):
name = line[len("name:"):].strip().strip('"').strip("'")
elif line.startswith("description:"):
value = line[len("description:"):].strip()
# Handle YAML multiline indicators (>, |, >-, |-)
if value in (">", "|", ">-", "|-"):
continuation_lines: list[str] = []
i += 1
while i < len(frontmatter_lines) and (frontmatter_lines[i].startswith(" ") or frontmatter_lines[i].startswith("\t")):
continuation_lines.append(frontmatter_lines[i].strip())
i += 1
description = " ".join(continuation_lines)
continue
else:
description = value.strip('"').strip("'")
i += 1
return name, description, content
클로데스킬스 문서