AGENT SKILL

persona

페르소나 프리셋 선택·해제 + 질문-답변 커스텀 프리셋 생성. "페르소나 골라줘/만들어줘/바꿔줘/꺼줘" 류 요청에서 호출.

License 许可
MIT
所属仓库
JayJihyunKim/rein
最近更新
2026-10-15
Stars
★ 1
Forks
0

01 前言 Front Matter

name
persona
description
페르소나 프리셋 선택·해제 + 질문-답변 커스텀 프리셋 생성. "페르소나 골라줘/만들어줘/바꿔줘/꺼줘" 류 요청에서 호출.

03 SKILL.md 正文

persona

페르소나 레이어를 사용자가 직접 고르거나(선택 흐름), 질문-답변으로 새 커스텀 프리셋을 만들도록(생성 흐름) 돕는다. 이 스킬이 흐름을 완결한다 — 사용자에게 설정 파일을 직접 편집하라고 안내하지 않는다.

spec: docs/specs/2026-07-22-persona-user-selection.md §5 (질문 세트·순서 고정).

로더·lint 호출 규약 (경로 해석 — 모든 스크립트 호출에 적용)

plugin 루트는 이 스킬 파일이 놓인 디렉토리의 상위 2단계(스킬 호출 시 주어지는 base directory 의 ../..)로 유도하고, 그 값을 CLAUDE_PLUGIN_ROOT 로 명시 전달한다:

PR="<이 스킬의 base directory>/../.."   # 예: /path/to/plugins/rein-core
CLAUDE_PLUGIN_ROOT="$PR" python3 "$PR/scripts/rein-policy-loader.py" <옵션>

셸 상태는 명령 호출 사이에 유지되지 않는다 — 앞선 호출에서 PR 을 정의했더라도 다음 호출에는 남아 있지 않다. 따라서 아래 모든 예시의 $PR 은 같은 명령 안에서 PR= 을 다시 정의한 상태를 전제한다. PR 정의 없이 $PR 을 쓰면 빈 문자열로 전개되어 조용히 실패한다.

환경변수 CLAUDE_PLUGIN_ROOT 는 스킬이 명령을 실행하는 환경에 설정되어 있지 않을 수 있다(실측 2026-07-27). 이때 로더는 스크립트 파일 자체는 찾더라도 내부 경로 해석 단계에서 실패해 오류 없이 빈 출력을 돌려준다. 호출자가 이를 "비활성" 으로 오해하면 조용히 중립으로 빠지거나 인사말을 즉석 창작하게 된다 — 실사용 결함(이름 오독·직전 호칭 잔존·인사말 창작)의 직접 원인이다. 그러므로 ${CLAUDE_PLUGIN_ROOT:-…} 처럼 변수가 있기를 기대하는 형태로 호출하지 않는다.

선택 흐름

사용자가 "페르소나 골라줘 / 바꿔줘 / 꺼줘" 류를 요청하면:

  1. 최상위 3개 고정 메뉴 — 실존 프리셋 수와 무관하게 AskUserQuestion 은 항상 정확히 3개를 제시한다: 페르소나 목록 / 내 페르소나 만들기(설명: "새 페르소나 만들기") / 페르소나 끄기. 예전에 내장/커스텀 갈래를 먼저 나누던 1단 구분은 폐지됐다.
    • 내 페르소나 만들기 를 고르면 2단 없이 곧바로 §생성 흐름 으로 인계한다.
    • 페르소나 끄기 를 고르면 2단 없이 곧바로 해당 처리로 간다.
  2. 페르소나 목록 선택 시 — flat 목록 + frontmatter read: 내장 3종 (boss-ace/jennie/choi-haengbae)과 실존 커스텀(.rein/policy/persona/*.md)을 tier 구분 없이 하나의 목록으로 합쳐 등록순(내장 고정 순서 boss-ace → jennie → choi-haengbae, 그 다음 커스텀은 기존 열거 순서 그대로)으로 나열한다. 각 항목의 표시 이름·요약은 해당 프리셋 .md 파일의 frontmatter 에서 직접 read 한다(display_name:/summary:) — 내장이라 해서 SKILL.md 프로즈에 이름·요약을 하드코딩하지 않는다(파일이 단일 진실원).
    • 항목 label 은 {display_name} — {summary}(slug·영문 alias 미노출)이며, display_name 이 없는 (구)커스텀은 이름 없는 페르소나 — {summary} (summary 도 없으면 이름 없는 페르소나)로 대체한다.
    • 전역 label dedup: 페이지네이션 이전에 목록에 들어갈 모든 프리셋의 최종 label 을 등록순으로 스캔해, 동일 label 이 2개 이상이면 두 번째부터 인덱스 suffix (2), (3) … 를 붙인다(전체 목록 등록순 기준 — 페이지 구성과 무관하게 안정).
    • 활성 표식: 로더 --persona 로 얻는 resolved 이름과 일치하는 항목의 요약 뒤에 · 지금 켜짐 을 붙인다(한 항목에만).
    • 페이지네이션(단일 임계 규칙): 선택지 상한 4개 안에서, 잔여 항목이 4개 이하이면 그 페이지에 전부 표시한다(필러 없음). 5개 이상이면 3개 + 다음 목록 보기(합 4개)를 표시하고 나머지를 다음 페이지로 넘긴다. 이 규칙에서 마지막 페이지는 항상 2~4개다.
    • 도달 가능성 불변식: 어떤 페이지 구성에서도 실존 프리셋 전부가 도달 가능해야 한다 — 상한 때문에 목록에서 조용히 빠지는 프리셋이 있어서는 안 된다.
  3. 기록 — mkdir -p .rein/policy 후 .rein/policy/persona.yaml 에 기록:
    • 프리셋 선택 시:
      enabled: true
      preset: <선택한 이름>
      
    • 페르소나 끄기 선택 시:
      enabled: false
      
    • 내 페르소나 만들기 선택 시: persona.yaml 을 쓰지 않고 곧바로 §생성 흐름 으로 인계한다. 기록은 생성 흐름 끝의 "지금 적용할까요?" 가 담당한다.
  4. 새 말투 본문을 지금 세션에 반영 — 기록만으로는 진행 중인 세션의 말투가 바뀌지 않는다. 캐릭터 본문은 세션 시작 시 1회 주입될 뿐이고, 매 턴 들어오는 것은 활성 프리셋: <이름> — <요약> 1줄뿐이다. 이 단계를 건너뛰면 직전 프리셋 본문 + 신규 요약 이 섞인 상태로 응답하게 된다 (실사용 결함: 이름 오독, 직전 호칭 잔존, 인사말 즉석 창작). 프리셋으로 전환/재선택한 경우 기록 직후 반드시 수행한다:
    PR="<이 스킬의 base directory>/../.."
    SRC="$(CLAUDE_PLUGIN_ROOT="$PR" python3 "$PR/scripts/rein-policy-loader.py" --persona-file)"
    
    • 출력 경로가 있으면 그 파일과 불변층($PR/rules/persona/_invariant.md)을 읽되, 본문뿐 아니라 선두 display_name:(+display_name_en:) frontmatter 도 함께 읽어 이름을 확정한다 — boss-ace 는 본문에 캐릭터 고유명사가 없으므로(호칭 "보스"·자기 지칭 "에이스"만 존재), display_name: 마르코 는 frontmatter 로만 존재하는 새 정보다. 이번 턴부터 그 본문의 캐릭터(이름·호칭·말투·언어분기)를 적용한다. 요약 1줄로 캐릭터를 추측하지 않는다 — 이름·호칭은 반드시 본문에서 읽는다.
    • 출력이 비어 있으면 먼저 호출을 의심한다 — 위 규약대로 PR 이 같은 명령 안에 정의됐는지 확인하고 정확히 1회 재시도한다. 재시도에도 비면 캐릭터를 추측하지 말고 중립으로 두고, 말투가 적용되지 않았음을 평문으로 보고한다.
    • 페르소나 끄기 선택 시: 로더를 호출하지 않고, 직전 프리셋 본문의 말투를 즉시 버리고 중립으로 돌아간다.
    • 내 페르소나 만들기로 인계된 경우 수행하지 않는다 — 아직 바뀐 상태가 없다.
  5. 인사말 prepend + 결과 보고 — persona.yaml 에 쓰려는 상태를 기준으로(stateless — 덮어쓰기 전 이전 preset 을 읽지 않는다) 시그니처 인사말 한 줄을 상태 보고 앞에 붙인다:
    • 내 페르소나 만들기로 인계된 경우 이 단계를 수행하지 않는다 — 아직 바뀐 상태가 없으므로 전환 인사말을 내지 않는다. 인사말은 생성 흐름의 적용 질문에서 "예" 를 골라 실제로 활성화될 때 낸다.
    • enabled: true, preset: P (활성 / 전환 / 동일 재선택 모두) → 로더로 시그니처를 얻어 상태 보고 앞에 prepend 한다. 동일 재선택도 다시 인사한다 (고정 시그니처 각인 재확인). 호출은 기존 lint 호출 관례를 따른다:
      PR="<이 스킬의 base directory>/../.."
      GREET="$(CLAUDE_PLUGIN_ROOT="$PR" python3 "$PR/scripts/rein-policy-loader.py" --persona-greeting "<name>")"
      
    • enabled: false (중립 / off 전환) → 캐릭터 인사말이 아닌 고정 평문 중립 한 줄(이 스킬에 하드코딩, tone-neutral, 예: "중립 톤으로 돌아갈게요.")을 상태 보고 앞에 붙인다. 로더를 호출하지 않는다.
    • fallback degrade 체인 (fail-open) — 어떤 경로도 흐름을 중단하지 않는다: (1) 저장된 greeting: 을 로더로 얻는다 → (1-b) 출력이 비면 즉석 생성으로 바로 넘어가지 않는다 — 위 규약대로 PR 을 같은 명령에 정의했는지 확인하고 정확히 1회 재시도한다. 대상이 내장 프리셋인데 재시도에도 비면 그것은 "인사말 없음" 이 아니라 호출 결함이다 (내장은 항상 curated greeting 보유) — 즉석 생성 금지, 상태 보고만 내고 그 사실을 평문으로 알린다 → (2) 재시도에도 비어 있고 대상이 커스텀이면(구 커스텀 프리셋) 후보 수집 단계에서 이미 확보한 선택 프리셋의 summary 텍스트를 seed 로 ≤60자 시그니처 한 줄을 즉석 생성한다 → (3) summary 도 없으면 상태 보고만 낸다. 즉석 생성의 seed 는 이미 확보한 summary 문자열뿐이다 — 인사말을 위해 프리셋 .md 를 새로 열지 않는다(§경계 인사말 신뢰 경계와 정합). 내장 boss-ace·jennie·choi-haengbae 는 항상 curated greeting 이 있어 fallback 에 진입하지 않는다. 즉석 문구 불변식: tone-only + L4-clean + ≤60자.
    • 대화 언어 적응 — 저장된 greeting: 은 정본(canonical) 이다 (내장 프리셋은 한국어). 응답 언어가 정본과 다르면(예: 영어 대화) 저장 문자열을 그대로 내지 않고 그 언어로 등가의 시그니처 인사말을 낸다 — 캐릭터 목소리(본문 언어분기 준용)·≤60자·tone-only·L4-clean 유지. 응답 언어가 정본과 같으면 그대로 낸다. 중립 평문 한 줄·fallback 즉석 생성도 동일하게 응답 언어로 낸다. 로더는 정본만 반환하며, 언어 적응은 스킬 출력 시점의 책임이다 (저장 스키마·로더 불변).
    • 출력 예산 (간결 cap): 최종 출력 = <시그니처 인사말 1줄 (≤60자)> + 개행 + <전환 확인 1줄> = 짧은 2줄. 인사말은 ≤60자이며, 장황한 여러 문장 나열·이모지 스팸을 금지한다 — 느낌표·마침표·물음표로 나뉜 짧은 다절 인사말은 허용한다(한 호흡에 읽히는 한 줄이면 된다. 내장 boss-ace·jennie·choi-haengbae 도 다절이다). 전환 보고 어디에도 (인사말이든 상태 행이든) 인사말이 '무엇인지' 설명하지 않는다 — "이건 전환 신호다 / 1회성이다 / 다음 세션부터 적용" 류의 메타 서술을 붙이지 않는다. 전환 확인은 "<이름>로 바꿨어요" 수준의 한 줄로 끝낸다.

생성 흐름 (7문항 Q&A — 순서 고정)

사용자가 "페르소나 만들어줘" 류를 요청하면, AskUserQuestion(자유 입력 항목은 채팅 답변)으로 아래 7문항을 순서 그대로 진행한다:

  1. 이름 — 파일명이 된다. 영문 소문자·숫자·하이픈, 32자 이내. 내장 프리셋(boss-ace, jennie, choi-haengbae)과 동일 이름은 사용할 수 없음을 질문 시점에 사전 고지한다.
  2. 호칭 — 사용자를 뭐라고 부를까 (자유 입력).
  3. 캐릭터 컨셉 한 줄 — 예: "츤데레 선배", "무뚝뚝한 집사".
  4. 표현 수위 — 절제 / 보통 / 진하게 3단계 중 선택.
  5. 언어분기 — EN 응답 시 톤. 미지정 시 기본: 직역 금지 + self-aware 완화 (내장 프리셋 관례).
  6. 차단·경고 순간의 말투 — 판단의 냉정·직설은 불변층이 고정하므로, 캐릭터가 그 순간을 "어떤 말투로" 전달하는지만 묻는다.
  7. 예시 멘트 — 답변을 반영한 참고 예시 2개(Done 1 + 차단 1)를 생성기가 초안으로 제시하고, 사용자가 수정·삭제할 수 있다.

초안 제시 → 확인 → lint → 저장

  1. 7문항 답변으로 본문 초안을 작성해 전문을 채팅에 제시한다. 구조는 내장 프리셋 준용:
    • frontmatter summary: 는 캐릭터 컨셉 기반으로 자동 작성한다 (사용자에게 묻지 않음).
    • frontmatter display_name: 은 8번째 질문을 만들지 않고 summary: 와 같은 방식으로 자동 작성한다 — 주로 Q3(캐릭터 컨셉 한 줄)·Q2(호칭)에서 사람이 읽는 이름을 도출한다. 자동 제안은 Q1 의 파일명 slug 를 그대로 복사하지 않는다 — Q1 은 영문 파일명만 받으므로 이를 display_name 으로 쓰면 목록·turn-brief·본문 제목 세 경로에 slug 가 자동 노출된다. 마땅한 이름을 못 뽑으면 초안 확인 단계에서 사용자에게 직접 입력받는다. 초안 본문 제목도 slug 대신 # Persona: {display_name}(또는 컨셉)으로 생성한다. 사용자는 초안 확인 단계에서 표시 이름을 수정·삭제할 수 있다(필드 자체 생략 가능). lint L5 는 확장하지 않는다(display_name 은 계속 선택 필드).
    • frontmatter greeting: 은 캐릭터 컨셉(Q3)에서 파생한 시그니처 인사말 한 줄을 자동 작성하되, 하나로 못 박지 않고 후보 2~3개를 제시해 사용자가 고르거나 직접 작성하게 한다. 제시 방법: AskUserQuestion 으로 후보를 보여주고 마지막 선택지를 직접 쓸게요 로 두어, 그걸 고르면 문구를 채팅으로 받는다. 확정된 한 줄을 전문 초안에 포함해 참고 예시 멘트(Q7)와 나란히 보이게 제시하며, 이후에도 사용자가 수정·삭제할 수 있다. 그 후 lint → 저장으로 진행한다. 이 제시는 초안 확인 단계의 일부이지 질문 세트의 일부가 아니다 — summary 자동 작성 관례와 동일하게 greeting 도 자동 작성하며, 8번째 질문을 만들지 않는다 — 7문항의 순서·개수는 불변이다.
    • 본문: 캐릭터 / 언어분기 / 간결 cap / 참고 예시.
    • 불변 조항(판단 냉정·응답 규칙 우선 등)은 본문에 넣지 않는다 — 불변층이 구조로 담당하므로 중복 금지.
    • 생성기가 산출한 greeting: 은 저장 파일의 일부이므로 lint L3/L4 대상이다 (정확한 ---(LF) fence · tone-only · L4-clean). 사용자 편집이 L4 를 위반하거나 (P)∧¬(A) fence 를 넣으면 강화 lint 가 저장 전에 잡고, lint 를 우회해 저장하더라도 로더의 런타임 경계가 로드를 거부한다.
  2. 사용자 확인을 받는다 (수정 요청 시 해당 부분만 고쳐 재제시).
  3. 초안을 임시 파일에 두고 lint 를 실행한다:
    PR="<이 스킬의 base directory>/../.."
    LINT="$PR/scripts/rein-persona-lint.py"
    CLAUDE_PLUGIN_ROOT="$PR" python3 "$LINT" --name "<name>" --body-file "<draft-tmp>"
    
  4. lint 통과 시에만 저장: mkdir -p .rein/policy/persona 후 .rein/policy/persona/<name>.md 로 저장. 통과 전에는 어떤 부분 파일도 저장하지 않는다.
  5. lint 실패 시: 위반 규칙(L1~L5)과 원인이 된 답변 항목을 짚어 해당 항목만 재질문하고 초안을 재생성한다. 전체 재시작 금지.
  6. 동명 커스텀 존재 시: .rein/policy/persona/<name>.md 가 이미 있으면 덮어쓰기 여부를 AskUserQuestion 으로 확인한 뒤 진행한다.

적용 질문

저장 후 "지금 적용할까요?" 를 묻는다:

  • 예 → 선택 흐름과 동일하게 .rein/policy/persona.yaml 에 enabled: true + preset: <name> 기록 + 선택 흐름 4단계(새 말투 본문 반영) + 선택 흐름 5단계(인사말 prepend + 결과 보고)를 동일하게 수행. 선택 흐름에서 인계돼 온 경우 전환 인사말은 여기서 처음 나온다.
  • 아니오 → 저장만 하고 종료 (persona.yaml 은 건드리지 않음).

경계

  • lint 우회 저장 금지 — lint 실패 상태의 본문을 어떤 경로로도 .rein/policy/persona/ 에 쓰지 않는다.
  • 이 스킬이 수정하는 설정 파일은 .rein/policy/persona.yaml 과 .rein/policy/persona/<name>.md 뿐이다. 그 외 파일 수정 금지.
  • 인사말 신뢰 경계 (정적 계약): 활성 프리셋 인사말은 오직 로더 --persona-greeting <name> 로 얻고, fallback seed 는 후보 수집 단계에서 이미 확보한 summary 문자열로만 쓴다. 인사말 기능을 위해 프리셋 .md 파일을 새로 열지 않는다. (후보 수집이 각 커스텀 summary: 를 읽는 것은 이 기능이 건드리지 않는 기존 동작이다 — 제거·변경 아님.)
  • 본문 반영 경계 (선택 흐름 4단계): 본문 반영이 여는 파일은 로더 --persona-file 이 해석해 돌려준 경로와 불변층 파일뿐이다 — 이름에서 경로를 스킬이 자체 조합하지 않는다(단일 신뢰 경계). 이 단계는 말투 적용 전용이며 인사말 출처를 바꾸지 않는다: 인사말은 여전히 --persona-greeting 에서만 얻고, 본문을 읽었다고 해서 인사말을 본문에서 만들지 않는다.
  • 불변층 종속: 인사말은 불변층(rules/persona/_invariant.md) 아래 tone-only 로 종속된다 — 판단·경고·차단(게이트 hard warning 포함)을 약화·대체·완곡화하지 않으며, 충돌 시 응답 규칙이 항상 이긴다.
  • 내부 운영 경로(trail/, hooks/ 등)를 사용자 채팅 본문에 노출하지 않는다 — 결과는 평문으로 보고.

04 同仓库更多 Skills