Jarvis
Skill pinguding/Project-Machine-in-the-loop/skills/ko/jarvis
Machine in the Loop — 키보드는 사람이, 기계는 루프 안에서 보조. 사람 중심으로 AX를 재정의하는 Claude Code 스킬셋(jarvis).
npx -y skills add pinguding/Project-Machine-in-the-loop --skill jarvisAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
git 변경량을 self-paced로 폴링하다 직전 리뷰 이후 새 변경이 쌓이거나·위험 경로가 바뀌거나·새 커밋이 생기면 jarvis-once 단발 리뷰를 자동 실행하는 워치 루프. 반복은 /loop의 self-paced 모드 위에서 도므로 반드시 /loop /jarvis로 띄운다. 고정 라인/파일 임계값은 없다. args(key=value)로 강도 프리셋(strength)·폴링 간격·경로·위험 경로를 설정한다. args 없이 부르면 최초 1회 강도를 물어본다(이전 설정은 .jarvis/args에서 자동 복원). 첫 로드 직후엔 잠깐 빠르게 폴링한다(warmup). "jarvis", "변경 감지 리뷰 루프", "jarvis 워치" 요청 시 사용.
SKILL.md
50.6 KB, ~20.0k tokens by cl100k_base, as published. Nobody here has run it
jarvis
사람이 직접 코드를 짜는 동안 곁에서 git 변경량을 싸게 폴링하다가, 직전 리뷰 이후 새 변경이 쌓이면(분량 임계 없음) jarvis-once(단발 navigator) 스킬을 자동으로 한 번 돌려주는 self-paced 워치 루프다.
- 폴링은 토큰을 거의 쓰지 않는다 (
git diff --shortstat한 번). 비싼jarvis-once풀 리뷰는 직전 리뷰 이후 새 변경이 쌓였거나·위험 경로가 바뀌었거나·새 커밋이 생겼을 때만 실행한다. - 이벤트 기반이 아니다. 사람 손 편집을 실시간으로 깨우는 OS 이벤트는 없으므로 주기적 wake로 확인한다. 대신 확인 자체를 극도로 싸게 유지한다.
jarvis-once는 코드를 직접 고치지 않는다 (리뷰/제안/다음 스텝만). 사람이 "이거 고쳐줘"라고 하면 그 턴은 일반 개발 모드로 전환한다.
이름 정리:
jarvis(이 스킬) = 지속 워치 루프(진입점).jarvis-once= 트리거 시 1회만 실행되는 단발 navigator. 워치가 깨우면jarvis-once를 호출한다.
실행 — /loop로 자기 반복
이 스킬은 반드시 /loop /jarvis [args] 로 띄운다. 반복의 엔진은 /loop의 self-paced(dynamic) 모드이고, 그 위에서 절차 4의 ScheduleWakeup이 다음 tick 간격을 스스로 정한다.
⚠️
ScheduleWakeup은/loopdynamic 모드 안에서만 동작한다. 맨/jarvis(=/loop없이 호출)에서 부르면 호출은 성공한 것처럼 리턴하지만 아무것도 예약되지 않고 조용히 무시된다. 그래서/loop없이 띄우면 첫 tick 뒤 루프가 그대로 죽는다. 진입점은 항상/loop /jarvis다.
⚠️
/loop에 간격을 직접 주지 말 것./loop /jarvis처럼 간격 없이 띄워야 dynamic 모드가 되어 절차 4의ScheduleWakeup(=active/idle/debounce)이 주기를 몬다./loop 5m /jarvis처럼/loop에 고정 간격을 주면 그 고정 스케줄이 이겨서 jarvis의active/idle이 무시된다. 주기 조절은 항상 jarvis args로 한다 — 예:/loop /jarvis active=3m idle=20m또는/loop /jarvis strength=high. (Bedrock/Vertex/Foundry 환경은 간격 없는/loop도 고정 스케줄로 돌아 self-pace가 적용되지 않는다.)
- 시작: 사용자가
/loop /jarvis [args]를 한 번 호출한다. → 이번 tick을 수행하고, 마지막에 반드시ScheduleWakeup으로 다음 실행을 예약한다(prompt에 현재 args echo)./loop이 이 예약을 받아 다음 tick을 발화시킨다. - 지속: 예약된 wake가 발화하면
/jarvis [args]가 다시 실행되고, 또 다음 wake를 예약한다. 이렇게 사용자가 추가 입력 없이도 루프가 유지된다. - 불변 규칙: 종료 의사(아래 "중지")가 없는 한, 모든 tick은 끝에서 반드시
ScheduleWakeup을 호출한다. 이 호출을 빠뜨리면 다음 wake가 예약되지 않아 루프가 죽는다. - 맨
/jarvis(=/loop없이)로 띄운 경우: 스킬은 자신이/loop안인지 밖인지 런타임에 구분할 수 없다. 그래서 이번 1회 tick은 정상 수행하되, 최초 부팅 tick 출력 끝에 "/loop없이 띄웠다면 이 tick 뒤 멈춘다 — 지속 감시는/loop /jarvis로 다시 띄워라" 안내를 남긴다(절차 0.7)./loop위였다면 그대로 반복되고, 아니었다면 안내대로 여기서 멈춘다.
인자 (args, key=value)
| key | 의미 | 기본값 |
|---|---|---|
strength | 강도 프리셋. 폴링 간격 노브들을 한 번에 묶어 세팅한다(low/medium/high). 개별 노브를 같이 주면 그 항목만 덮어쓴다. 아래 "강도 프리셋" 참조. | medium |
active | 직전 wake에서 jarvis-once를 실행했거나 워킹트리에 변경이 있을 때의 다음 wake 간격 | 4m |
idle | 변경이 없어 잠잠할 때(워킹트리 깨끗)의 다음 wake 간격 | 25m |
debounce | 대화가 멎었다고 볼 정지 시간. 리뷰할 조건이 됐어도 사람이 대화 중이면 실행을 미루고 이 간격으로 짧게 재예약한다. 사람이 한 마디 할 때마다 타이머가 리셋된다. | 90s |
warmup | 첫 로드 빠른 폴링 tick 수. 부팅 직후 이 횟수만큼은 변경이 없어도 idle 대신 active 간격으로 재확인한다(처음 켰을 때 반응을 빠르게). 0이면 끔. | 3 |
paths | 감시 대상 경로 한정 (생략 시 전체) | (전체) |
risk | 위험 경로 glob — 여기에 매칭되는 파일은 분량과 무관하게(단 한 줄이라도 새로 바뀌면) jarvis-once를 깨운다. 쉼표로 여러 개. | (없음/off) |
focus | 집중 영역 디렉토리. 여기 모인 .md를 리뷰 시 우선 렌즈로 본다(아래 "컨벤션 문서 수집" 4번). | .claude/jarvis/focus/ |
mirror | 거울(gray zone 가시화). 누적 변경의 상당 부분이 이 세션에서 생성된 코드면 "직접 파악했는지" 한 줄 메모로 짚는다(비강제). off로 끔. 아래 절차 3.5 참조. | on |
plan | 계획 모드. 첫 부팅에서 이 작업의 계획+체크리스트를 만들지 물어본다(절차 0.55). off면 질문을 건너뛴다(지금과 완전히 동일 동작). 소스(plan=PROJ-123, URL, 파일 경로)를 주면 옵트인 과 동시에 소스를 지정해 두 질문을 모두 건너뛴다. 계획이 생기면(.jarvis/checklist.md 존재) 워치가 게이트가 걸리는 tick마다 그것을 기준으로 리뷰한다. | (첫 부팅에 질문) |
커밋 경계 트리거는 args와 무관하게 항상 켜져 있다. 새 커밋이 감지되면 변경 누적과 무관하게 그 커밋 변경에 대해
jarvis-once를 1회 실행한다. 착오가 코드로 봉인되기 직전/직후가 경고의 골든타임이기 때문이다. (manifesto: "사람의 착오에는 AI가 적극적으로 경고")
예시:
/loop /jarvis # args 없음 → 최초 1회 강도를 물어봄 (이후 medium 기본)
/loop /jarvis strength=high # 강하게 — 촘촘히 폴링 (새 변경을 빨리 잡음)
/loop /jarvis strength=low # 약하게 — 드물게 폴링
/loop /jarvis strength=high idle=30m # 강 프리셋 + idle만 30m으로 덮어쓰기
/loop /jarvis paths=src/billing active=3m # 특정 경로만, 더 촘촘히
/loop /jarvis risk=**/*payment*,**/*auth* # 결제·인증은 한 줄만 바뀌어도 즉시 경고
진입은 항상
/loop /jarvis다(/loop없이는 반복 안 됨, 위 "실행" 참조). ScheduleWakeup의 echo prompt에는/loop접두어 없이/jarvis [args]만 넣는다 — 다음 tick은/loop이 그 prompt로 발화시킨다(절차 4).
인자 파싱 규칙:
key=value형태만 인식한다. 인식 못 한 토큰은 무시하고 기본값을 쓴다.strength를 먼저 해석해 프리셋 노브 묶음을 깐 뒤, 나머지 개별 key=value를 그 위에 덮어쓴다(개별 노브 우선).strength미지정 시medium과 동일.strength값은 별칭을 허용한다(아래 "강도 프리셋" 표). 인식 못 한 값이면medium으로 폴백한다.- 시간 간격은
s/m/h접미사를 허용한다(90s,4m,1h). 숫자만 오면 분으로 간주한다. active/idle은 런타임에서 [60s, 3600s]로 클램프된다.paths/risk는 쉼표(,)로 여러 값을 구분한다. glob 패턴을 허용한다.
강도 프리셋 (strength)
strength 하나로 "얼마나 촘촘히 폴링할지"를 묶어 조절한다. 각 프리셋은 아래 노브 묶음으로 전개되며, 같은 호출에 개별 노브(active= 등)가 있으면 그 항목만 프리셋 값을 덮어쓴다. (라인/파일 임계값은 없으므로 프리셋은 분량 민감도가 아니라 폴링 빈도만 바꾼다.)
strength | 별칭 | active | idle | debounce | 성격 |
|---|---|---|---|---|---|
low | 약, 약하게, 느슨, relaxed, 1 | 8m | 40m | 120s | 드물게 폴링. 소음 최소·비용 최소 |
medium | 중, 보통, normal, 2 | 4m | 25m | 90s | 기본. 균형 |
high | 강, 강하게, 촘촘, aggressive, 3 | 3m | 12m | 60s | 촘촘히 폴링. 새 변경·커밋을 가장 빨리 잡음 |
규칙:
- 프리셋은 위 3개 노브(
active/idle/debounce)만 건드린다.paths/risk는 프리셋과 무관하게 별도로 지정한다. strength=high라도jarvis-once자체의 severity 문턱은 그대로다 — 강도는 폴링 빈도(=새 변경을 잡는 속도)를 높일 뿐 잔소리를 늘리지 않는다(비용 가이드 참조). 즉 안심하고 올려도 된다.- 다음 wake 예약 시 echo하는 args에는 전개된 개별 노브가 아니라
strength=<값>(+ 개별 덮어쓰기)을 그대로 직렬화한다. 그래야 프리셋 의미가 보존된다.
마커 파일
직전 리뷰 시점의 기준선을 .jarvis/baseline에 저장한다. 워킹 트리에 있으므로 .gitignore에 .jarvis/를 등록해 커밋되지 않게 하고(클론마다 독립적인 로컬 상태로 동작), 디렉토리가 없으면 쓰기 전에 mkdir -p .jarvis로 생성한다.
.git/을 쓰지 않는 이유:.git/내부는 민감 경로라 쓰기마다 권한 승인을 받아야 해서 무인 루프에 부적합하다..jarvis/는 일반 워킹 트리 경로라 권한 마찰이 없고,.gitignore로 비추적을 보장하면 "커밋 안 됨 + 로컬 전용"이라는 원래 이점을 그대로 유지한다.
형식(한 줄): lines=<정수> risk=<정수> head=<커밋 SHA> deferred=<0|1> boot=<정수> mirrored=<0|1>
파일이 없으면 lines=0 risk=0 head= deferred=0 boot=0 mirrored=0으로 간주한다. (구버전 파일에 boot·mirrored가 없으면 0으로 보고, files 필드가 남아 있으면 무시한다.)
lines: 직전 리뷰 시점의 working tree 변경 라인 수. "직전 리뷰 이후 새 변경이 쌓였나"를 판정하는 기준선이다.risk: 직전 리뷰 시점의 위험 경로(riskglob) 변경 라인 수 (risk 미설정 시 0)head: 직전 tick에서 관측한git rev-parse HEAD값. 커밋 경계 감지에 쓴다.deferred: 리뷰할 조건이 됐지만 대화 중이라 미뤄둔 리뷰가 있는지(debounce 대기). 1이면 대화가 멎는 순간 flush한다.boot: 남은 웜업 tick 수. 부팅 시warmup으로 초기화되고 매 tick 1씩 줄어든다.>0이면 변경이 없는 tick도idle대신active간격으로 폴링한다(첫 로드 직후 빠른 반응).mirrored: 거울 쿨다운 플래그. 거울(절차 3.5)을 띄운 뒤 1로 둬 매 tick 반복 발화를 막는다. 사람이 직접 타이핑한 변경이 다시 주를 이루거나 리뷰를 flush하면 0으로 풀린다.
제어 상태 파일 (.jarvis/ 하위)
워치의 진행 상태는 아래 2개 파일로 제어된다. 둘 다 .jarvis/ 하위라 .gitignore로 비추적·로컬 전용이고, 각각 한 줄짜리 고정 크기 상태 파일이라 매 tick 덮어쓰기만 된다(누적 캐시가 아니다).
| 파일 | 역할 | 생성/삭제 주체 |
|---|---|---|
.jarvis/baseline | 위 형식의 워치 기준선. 루프 진행 상태의 핵심. 디스크에 남아 있으면 다음 /loop /jarvis가 그 지점에서 이어서 감시한다(=재개). | 매 tick 기록 / /jarvis-reset이 삭제 |
.jarvis/args | 직전 tick의 유효 args 문자열(예: strength=medium). 매 tick 갱신. 다음 시작이 args 없이 호출되면 jarvis가 이 값을 읽어 같은 설정으로 자동 재개한다(절차 0.4). 없으면 기본값(medium). | 매 tick 기록 / /jarvis-reset이 삭제 |
.jarvis/status | 생존 신호용 상태 한 줄(아래 "생존 가시화"). 매 tick 끝에서 다음 wake 시각(next_wake epoch)·간격·강도를 기록한다. statusline 스크립트가 이 파일을 읽어 tick 사이에도 "감시 중 / 멈춤?"을 연속 표시한다. | 매 tick 기록 / /jarvis-reset이 삭제 |
.jarvis/stopped | 이벤트 기반 "멈춤" 플래그(아래 "생존 가시화 (B)"). Stop 훅(assets/loop-watch-hook.sh)이 턴 종료 시 /jarvis wake가 사라진 걸 감지하면 남긴다. 있으면 statusline이 시간 추론 없이 즉시 "멈춤"을 표시. wake가 살아있으면 훅이 지운다. | Stop 훅이 기록/삭제 / /jarvis-reset이 삭제 |
.jarvis/plan.md | 승인된 계획 문서(목표/스코프/접근/열린 질문). 사람이 승인한 뒤 jarvis-plan이 1회 기록. 이 파일 + checklist.md가 있으면 계획 모드 활성. 지속 파일(매 tick 한 줄짜리가 아님). | 옵트인 시 jarvis-plan이 기록 / /jarvis-reset이 삭제(확인 후) |
.jarvis/checklist.md | 작업 체크리스트 — 마크다운 - [ ]/- [x] 항목. 체크마크는 사람이 소유하고, 워치는 게이트 리뷰마다 이 파일을 읽어 공식적으로 뭐가 됐는지 알고 나머지를 재평가한다. 진행률 = 체크됨/전체. 이 파일의 존재가 워치의 "계획 모드 활성" 판정 키다. | jarvis-plan이 생성, 사람이 체크 / /jarvis-reset이 삭제(확인 후) |
생애주기(
/loop위): 시작·재개는/loop /jarvis— baseline/args가 남아 있으면 그 상태에서 이어진다. 멈춤·일시정지는/loop자체를 인터럽트(Esc) — 다음 wake가 예약되지 않아 루프가 끝나고, baseline/args는 디스크에 그대로 남아 재개를 기다린다. 완전 초기화는/jarvis-reset(독립 스킬: baseline/args 삭제 → 다음 시작은 최초 부팅). 루프 종료엔 teardown 훅이 없으므로 상태 정리는 시작 시점(자가보정)과/jarvis-reset에서만 일어난다. (구버전.jarvis/paused플래그·/jarvis-pause·/jarvis-resume·/jarvis-stop은 더 이상 쓰지 않는다.)
컨벤션 문서 수집 (jarvis-once 호출 직전에만 수행)
jarvis-once를 실제로 부르기로 결정한 순간(절차 2a 또는 3 충족)에만 수행한다. 리뷰하지 않는 tick에서는 토큰 낭비이므로 하지 않는다.
목적: jarvis-once가 "이 패키지·이 디렉토리 규칙"까지 알고 리뷰하게 한다. CLAUDE.md와 .claude/rules/**는 하네스가 자동 주입하므로 중복 수집하지 않는다. on-demand 문서만 모은다.
변경된 파일 목록을 먼저 얻는다:
git diff --name-only HEAD [-- paths] # 게이트로 깨운 경우 (working tree)
git diff --name-only <base_head>..<cur_head> # 커밋 경계로 깨운 경우
그 경로들을 기준으로 다음을 수집한다(존재하는 것만, 이미 컨텍스트에 있으면 재읽기 금지):
- 작업 계획 & 체크리스트 (계획 모드 활성 —
.jarvis/checklist.md존재 시에만) —.jarvis/plan.md·.jarvis/checklist.md를 읽어jarvis-once에 **"작업 스펙(최우선)"**으로 넘긴다. 이건 변경이 무엇을 이루려 하는가이므로 집중 영역보다도 우선한다: jarvis-once가 변경을 체크리스트에 대고 리뷰한다(어느 항목을 전진시키나, 완료로 보이나, 항목별로 뭐가 빠졌거나 위험한가). 절차 3.6 참조. - 가장 가까운
AGENTS.md— 각 변경 파일의 디렉토리에서 위로 올라가며 처음 만나는AGENTS.md. (예:packages/billing/**변경 →packages/billing/AGENTS.md) - 디렉토리
README.md— 변경 파일이 위치한 디렉토리에README.md가 있으면 그 파일. - 변경 파일에 해당하는
.claude/rules/**규칙 문서 — 변경 파일의 종류·경로에 맞는 규칙만 추려서(전부 말고) 참조한다. 프로젝트가.claude/rules/를 어떤 축으로 조직했든(언어별·레이어별·기능별·디렉토리별) 변경된 파일과 가장 관련된 1~3개만 고른다. 규칙 파일명·디렉토리명이 곧 적용 힌트다(예: UI 파일을 바꿨으면ui·view류, 테스트를 바꿨으면test류, 결제를 바꿨으면payment·billing류). 하드코딩된 언어별 매핑에 기대지 말고 그 프로젝트가 실제로 가진 규칙 파일에서 발견한다. 이미 자동 주입된 규칙은 생략한다. - 집중 영역 문서 (
focus디렉토리, 기본.claude/jarvis/focus/**) — 사용자가 "이 프로젝트에서 특히 주의 깊게 리뷰받고 싶은 것"을 모아둔 곳. 존재하면 그 안의.md(README 제외)를 모아jarvis-once에 "집중 영역" 맥락으로 넘긴다. 위 1~3의 일반 컨벤션과 달리 이건 우선순위 렌즈다 — jarvis-once가 일반 감지 카탈로그보다 먼저 본다.focus=<경로>인자로 위치를 바꿀 수 있다. (이 디렉토리는 변경 파일과 무관하게, 리뷰를 실제로 하기로 한 tick에서 항상 수집한다.)
중복·과수집을 피한다: 같은 AGENTS.md를 여러 변경 파일이 공유하면 한 번만 읽는다. 수집량이 과하면 변경 파일이 가장 많은 상위 디렉토리 1~2곳으로 한정한다.
수집한 내용을 jarvis-once 호출 시 함께 전달한다 — 0은 "작업 스펙(최우선)", 1~3은 "참고 컨벤션", 4는 "집중 영역(우선)" 으로 구분해 넘긴다. (페르소나(persona.md)는 jarvis-once가 자기 디렉토리에서 직접 읽으므로 워치가 수집하지 않는다.)
Jarvis 출력 마커 (필수)
jarvis-once 리뷰 결과를 사용자에게 전달할 때는, 그 내용이 Jarvis가 자동 생성한 것임을 보이도록 반드시 시작/끝에 아래 마커로 감싼다. 워치가 자동으로 내놓은 출력과 일반 대화를 구분하기 위함이다.
시작 마커:
╭─🤖 JARVIS ─────────────── (jarvis 자동 관찰)
끝 마커:
╰─🤖 JARVIS ─────────────── 키보드는 당신이 잡습니다 · 판단은 당신 몫
규칙:
jarvis-once가 실제로 무언가 말할 때만(리뷰 내용이 있을 때) 감싼다. "특별히 걸리는 건 없어" 수준의 침묵·"새 변경 없음" 한 줄 알림에는 마커를 붙이지 않는다.- 마커 사이 본문은
jarvis-once가 생성한 내용 그대로 둔다. 워치(jarvis) 자신의 운영 메시지(예약·debounce 안내 등)는 마커 밖에 둔다. - 끝 마커의 꼬리 문구는 저자성이 사람에게 있음을 상기시키는 고정 문구다.
시작 배너 (최초 tick 전용)
/jarvis가 처음 가동되는 tick에서만 JARVIS 아스키 로고 배너를 1회 출력한다. 판정 기준: .jarvis/baseline 파일이 아직 없으면 최초 부팅 tick이다(이후 tick엔 baseline이 생기므로 자동으로 재출력 안 됨). 예약 wake·이후 tick에서는 출력하지 않는다 — 반복은 노이즈다.
항상 보이게 — 반드시 "어시스턴트 응답 본문"에 코드펜스로 직접 출력한다.
cat으로만 내보내면 Claude Code UI가 긴 tool 출력을 접어버려(Ctrl+O 필요) 배너가 바로 안 보인다. 따라서:
assets/jarvis-banner.txt(이미 컨텍스트에 있으면 재읽기 불필요)를 읽어 내용을 확보하고,- 그 내용을 응답 메시지 안의 삼중 백틱(```) 코드블록에 그대로 붙여 출력한다. (tool 출력이 아니라 본문이라 항상 펼쳐져 보인다)
- 색: 본문 마크다운은 ANSI 색을 못 살리므로 모노크롬으로 나간다. 이 로고는 블록 글자(
█)와 박스 모서리(╗╝╚╔║═)로JARVIS형태가 또렷이 드러난다. (굳이 컬러가 필요하면assets/jarvis-banner.ansi를 raw 터미널에서cat— 단 Claude Code UI에선 접힘.) - 원본 아트:
assets/jarvis-banner.txt. (colorize.sh→.ansi는 raw 터미널용 컬러 fallback으로만 유지) - 배너는 워치 운영 메시지이므로 "Jarvis 출력 마커"(╭─🤖) 밖에 둔다. 배너 아래 캡션 한 줄을 덧붙인다:
J · A · R · V · I · S — watch online · 키보드는 당신이 잡습니다
매 실행(wake) 절차
이 스킬은 매 wake마다 아래를 처음부터 끝까지 수행한다.
0. 사람 우선 + 대화 활성도 판정 (debounce)
active_chat 판정: 직전 tick(직전 ScheduleWakeup 발화) 이후 사람이 보낸 메시지가 있는가? transcript 끝부분으로 판정한다.
- 사람의 의논/구현 메시지가 있었으면 →
active_chat = true(대화 중) - 이번 tick이 순수 예약 발화뿐이면 →
active_chat = false(조용)
active_chat == true이고 사람이 무언가 요청 중이면, 그 요청(질문 답변·코드 작성·명령 실행)에 먼저 집중한다. 워치 출력으로 대화를 끊지 않는다. 리뷰는 미루고, 절차 3·2a의 debounce 분기가 처리한다(절차 4에서 debounce 간격으로 재예약).
이 active_chat 값은 절차 2a·3의 "말해도 되는가" 판단에 쓰인다.
0.4. 저장된 설정 자동 복원 (재개 시)
이번 호출에 인식된 args가 하나도 없고 .jarvis/args 파일이 존재하면, 그 한 줄을 읽어 이번 tick의 유효 args로 삼는다(이전 설정으로 자동 재개). 이로써 루프를 인터럽트(Esc)로 멈춘 뒤 /loop /jarvis(인자 없이)로 다시 띄워도 직전 strength 등이 그대로 복원된다 — 별도 resume 명령이 필요 없다.
cat .jarvis/args 2>/dev/null || true
- 명시적으로 준 args가 하나라도 있으면 그쪽이 우선이고
.jarvis/args는 읽지 않는다(명시 > 저장 > 기본 medium). - 예약 wake(자동 발화) tick은 args가 항상 echo되므로 이 분기에 들어오지 않는다.
.jarvis/args도 없으면 기본값(medium)으로 둔다. (복원했든 안 했든, 이렇게 정해진 유효 args는 이후 0.6·절차 4가 그대로 쓴다.)
0.5. 시작 배너 (최초 부팅 1회)
.jarvis/baseline 파일이 없으면 이번이 최초 가동 tick이다 → "시작 배너" 섹션 규칙대로 JARVIS 아스키 로고를 응답 본문 코드펜스에 1회 출력한다(마커 밖, 캡션 포함, 항상 보임). 파일이 이미 있으면 건너뛴다. 배너 출력 후에도 절차 1~4는 정상 진행한다.
0.55. 계획 옵트인 (최초 부팅 + 인터랙티브일 때만 1회)
조건: 이번이 최초 가동 tick(0.5에서 baseline 없음 판정)이고 인터랙티브 시작(예약 자동 wake가 아님)일 때만 수행한다. 강도(0.6)와 같은 가드: 예약 자동 발화 tick에서는 절대 묻지 않는다 — 자리를 비운 사이 질문이 루프를 막는다.
plan=off→ 계획을 완전히 건너뛴다. 루프는 이 기능이 없을 때와 완전히 동일하게(as-is) 동작한다. plan/checklist 파일도, plan-review도 없다.plan=<소스>(Jira 키·URL·파일 경로) → 소스까지 지정된 옵트인으로 본다. 곧장jarvis-plan에 넘긴다(예/아니오 질문 건너뜀).- 그 외엔 1회 묻는다 —
AskUserQuestion: "이 작업의 계획+체크리스트를 만들까요?" — 선택지 "예, 계획 만들기" / "아니오, 그냥 감시."- 아니오 → 계획 없이 as-is로 진행. 여기서 더 할 일 없음.
- 예 →
Skill('jarvis-plan')을 호출한다. 소스 선택(Jira / Confluence / Notion / 문서 / 직접 작성) → 읽기 →plan.md·checklist.md초안 → 사람 승인 →.jarvis/에 기록까지 처리한다. 돌아오면 0.6으로 이어간다.
- 순서: 이건 강도(0.6) 앞이다 — 무엇을 하는지를 정하고 얼마나 촘촘히 볼지를 정한다.
- 첫 부팅 한정. 재개(baseline 존재) 시 다시 묻지 않는다.
.jarvis/checklist.md가 있으면 계획 모드가 그냥 활성이고 워치가 그걸 기준으로 리뷰한다. 나중에 (다시) 계획하려면:/jarvis-reset후 재시작, 또는/loop /jarvis plan=<소스>.
0.6. 강도 선택 (최초 부팅 + args 없을 때만 1회)
조건: 이번이 최초 가동 tick(0.5에서 baseline 없음 판정)이고 인식된 args가 하나도 없을 때만 수행한다.
AskUserQuestion으로 강도를 1회 묻는다. 선택지는강하게(high)/보통(medium)/약하게(low)3개 + 각 프리셋의 노브 요약을 description에 적는다. (사용자는 "Other"로 직접 값을 줄 수도 있다.)- 사용자가 고른 값을 이번 tick의
strength로 확정하고, 절차 4의 wake 예약 prompt에strength=<선택값>을 echo한다. 이후 wake부터는 args가 비어있지 않으므로 다시 묻지 않는다. - 예약 wake(자동 발화) tick에서는 절대 묻지 않는다 — args(
strength=포함)가 항상 echo되므로 이 분기에 들어오지 않는다. 사용자가 자리를 비운 사이 질문으로 루프가 막히는 일을 방지한다. - args가 하나라도 있으면(예:
/loop /jarvis strength=high,/loop /jarvis idle=30m) 질문 없이 그 값을 그대로 쓴다.
이 질문은 "처음 켤 때 한 번"만이다. 강도를 나중에 바꾸려면
/loop /jarvis strength=<값>으로 다시 부르면 된다(진행 중인 예약을 덮어쓴다).
0.7. /loop 안내 (최초 부팅 1회 — 맨 호출 대비)
조건: 이번이 최초 가동 tick(0.5에서 baseline 없음 판정)일 때만 수행한다. (예약 wake·이후 tick에서는 출력하지 않는다.)
-
스킬은 자신이
/loop안에서 도는지 맨/jarvis로 띄워졌는지 런타임에 구분할 수 없다. 그래서 최초 부팅 tick에서는 항상 마커 밖에 안내 한 줄을 남긴다:↻ 지속 감시는 /loop /jarvis 로 띄워야 반복됩니다. /loop 없이 띄웠다면 이 1회 tick 뒤 멈춥니다. -
이 안내는 정보용이다.
/loop위에서 돌고 있었다면 절차 4의ScheduleWakeup이 정상 동작해 그대로 반복되고(안내는 무해), 맨 호출이었다면ScheduleWakeup이 조용히 무시돼 안내대로 이번 tick 뒤 멈춘다. -
절차 1~4는 평소대로 진행한다(이번 1회 tick은 정상 수행).
1. 측정 (저비용)
paths가 있으면 -- <paths>를 붙여서 working tree 변경량을 잰다. 추적 파일 변경과 미추적(새로 생성된) 파일을 둘 다 센다:
git diff --shortstat HEAD -- [paths] # ① 추적 파일 변경
git ls-files --others --exclude-standard -- [paths] # ② 미추적(새) 파일 목록
- ①에서
insertions(+),deletions(-)를 파싱한다. - ②의 각 파일 라인 수(
wc -l)를 더한다. cur_lines = (① insertions + deletions) + (② 새 파일들의 총 라인 수)- 비어 있으면 0.
⚠️ 왜 ②가 필수인가:
git diff HEAD는 미추적 파일을 포함하지 않는다. 이게 빠지면 사람이 새 파일을 손으로 짤 때(이 프로젝트의 핵심 사용 사례) jarvis가 stage/commit 전까지 0으로 보고 침묵한다.git add -N(intent-to-add)로 인덱스에 끌어들이는 방법도 있으나, 인덱스를 변조해 무인 루프에 부작용을 주므로 쓰지 않는다 — 읽기 전용ls-files로 따로 세어 더한다.
risk가 설정돼 있으면 위험 경로만 따로(추적+미추적 동일 방식으로) 잰다:
git diff --shortstat HEAD -- <risk globs>
git ls-files --others --exclude-standard -- <risk globs>
→ risk_lines = (추적 ins+del) + (미추적 새 파일 라인 수) (위험 경로 기준). risk 미설정 시 0.
현재 커밋도 확인한다:
git rev-parse HEAD
→ cur_head.
측정은 staged·unstaged·미추적을 모두 포함하는 working tree 기준(
HEAD대비)이다.
2. 경계 판단 (커밋·되돌림 자동 보정)
.jarvis/baseline에서 base_lines, base_risk, base_head를 읽는다.
(0) stale baseline 자가보정 (재개 위생 — 가벼운 1회 체크):
루프를 인터럽트(Esc)로 멈춘 뒤 밖에서 rebase·reset·브랜치 삭제 등을 하면, base_head가 레포에 더는 존재하지 않는 SHA가 될 수 있다. base_head가 비어있지 않을 때 그 존재 여부를 1회 확인한다:
git cat-file -e <base_head>^{commit} 2>/dev/null # 존재하면 exit 0
→ 존재하지 않으면(exit 0 아님) 직전 기준선이 의미를 잃은 것이다. 이번 tick을 최초 부팅처럼 다루지 말고(배너 재출력 X), 조용히 baseline을 현재 측정값으로 리셋한다(lines=cur_lines risk=risk_lines head=cur_head deferred=0, boot/mirrored는 보존). jarvis-once는 실행하지 않고 절차 4로 간다. (커밋 경계·되돌림 보정은 head가 유효할 때만 의미가 있으므로, 무효 head로 (a)·(b)를 돌리지 않기 위해 먼저 친다.) base_head가 비어있으면 이 체크는 건너뛴다.
(a) 커밋 경계 — 강제 관찰 (항상 켜짐):
base_head가 비어있지 않고 cur_head != base_head이면, 직전 tick 이후 새 커밋이 생겼다(commit/merge). 분량과 무관하게 jarvis-once로 1회 점검한다.
- 단,
active_chat이면(대화 중) 이 커밋 점검도 미룬다:deferred = 1로 두고base_head를 갱신하지 않은 채 절차 4에서debounce간격으로 재예약한다. 조용해지면 다음 tick에서 이 분기로 다시 들어와 실행한다. (커밋 점검이 유실되지 않도록 head를 일부러 안 옮긴다) - 조용하면(
active_chat == false) 바로 실행:- 보여줄 diff:
base_head가cur_head의 조상이면git diff <base_head>..<cur_head>, 아니면(rebase/checkout/force 등)git show <cur_head> - 먼저 "컨벤션 문서 수집"을 수행해 함께 넘긴다.
jarvis-once에 "막 커밋된 변경이다. 봉인 직전 마지막 점검 관점으로 착오·누락을 봐달라"는 맥락을 전달한다.- 리뷰 결과를 "Jarvis 출력 마커"로 감싸 전달한다.
- 실행 후 절차 3(게이트)은 건너뛴다. 기준선을 현재값으로 갱신하고
deferred=0으로 절차 4로 간다.
- 보여줄 diff:
- 단, 사용자가 종료를 요청한 상태면 실행하지 않는다.
(b) 되돌림(discard) 감지:
cur_head == base_head인데 cur_lines < base_lines이면, 사용자가 변경을 되돌린 것이다. 기준선을 현재값으로 리셋하고 jarvis-once는 실행하지 않는다. (절차 4로)
(c) 그 외 — 증가분 계산:
delta_lines = cur_lines - base_lines(직전 리뷰 이후 새로 쌓인 라인 수; 양수면 새 변경 있음)delta_risk = risk_lines - base_risk(risk 미설정 시 0)
3. 게이트 + debounce 판단
게이트 충족 여부 — 다음 중 하나라도 충족하면 gate_met = true:
delta_lines >= 1— 직전 리뷰 이후 새 변경이 쌓였으면 리뷰한다(고정 분량 임계 없음). 이미 리뷰한 변경은 절차 끝에서base_lines로 흡수되므로 같은 변경을 다시 발동하지 않는다.risk설정 시delta_risk >= 1— 위험 경로는 분량 무관. 단 한 줄이라도 새로 바뀌면 경고. (tenet 7: 위험에 반응)
리뷰 필요 여부: should_review = gate_met OR (base_deferred == 1)
→ 이전에 미뤄둔 리뷰가 있으면 게이트가 다시 충족되지 않아도 리뷰 대상이다(미뤄둔 변경은 사라지지 않는다).
말해도 되는가: may_speak_now = (active_chat == false) — 대화가 멎은 상태.
분기:
-
should_review && may_speak_now → 실행 (flush):
- "컨벤션 문서 수집"을 수행한다.
Skill('jarvis-once')를 호출해 현재 변경분(working tree diff)을 리뷰받는다. 수집한 참고 컨벤션 + "직전 리뷰 이후 새로 쌓인 변경 위주로 봐달라" 맥락을 함께 전달한다. 위험 경로/미뤄둔 리뷰 때문이면 그 사실을 명시한다.- 리뷰 결과를 "Jarvis 출력 마커"로 감싸 사용자에게 간결히 전달한다.
deferred = 0으로 클리어.
-
should_review && !may_speak_now → 미룸 (debounce):
jarvis-once를 실행하지 않는다.deferred = 1로 표시.- 워치 운영 메시지를 마커 밖에 한 줄로 남긴다(예: "변경 감지 — 대화 중이라 보류, 멎으면 리뷰할게").
- 절차 4에서
debounce간격으로 짧게 재예약한다.
-
그 외 (should_review == false): 새로 쌓인 변경이 없는 경우다. 침묵하지 말고 하트비트 한 줄(아래 "생존 가시화 (A)")을 마커 밖에 남긴다 — 단 대화 중(
active_chat == true)이면 생략한다(statusline이 메운다).
기준선 기록 규칙 (절차 끝에서 항상 적용):
head: 매 tick 끝에cur_head로 기록한다. 단, 커밋 경계(2a)를 debounce로 미룬 경우는 예외 — 다음 조용한 tick에서 다시 감지되도록head를 갱신하지 않는다.lines/risk:jarvis-once를 실행(flush)했거나 되돌림 리셋(2b)한 경우에만 현재값(cur_lines/risk_lines)으로 갱신한다. 갱신하면 그 변경이 base로 흡수돼 다음 tick부터는 "새 변경"으로 잡히지 않는다(같은 변경 반복 리뷰 방지). 미룸·미충족 tick에서는 기존 값을 유지해 변경이 계속 누적되게 둔다.deferred: 위 분기 결과(0/1)를 기록한다.boot:boot_now에서 1 줄여 기록한다(최소 0). 즉 부팅 후warmuptick 동안만 웜업 폴링이 유지되고, 그 뒤 자동으로idle로 내려간다.warmup=0이면 처음부터 웜업 없음.mirrored: 절차 3.5 결과(0/1)를 기록한다. 거울을 띄웠으면 1, 풀림 조건(사람 직접 타이핑이 주를 이룸 · 리뷰 flush · 되돌림 리셋)이면 0.
3.5. 거울 — gray zone 가시화 (비강제)
저자성 보존을 위한 보조 장치다. 코드를 막지도 고치지도 않는다 — 사람이 자기도 모르게 조종석에서 일어나려 할 때 거울만 비춘다. mirror=off면 이 절차 전체를 건너뛴다(mirrored는 0으로 유지).
신호 (싸게 — transcript+diff만, jarvis-once 호출 없음): 직전 tick 이후 쌓인 변경이 어떻게 만들어졌는지 본다.
- 이 세션 transcript에서 어시스턴트의
Edit/Write/NotebookEdit호출이 변경 파일을 직접 만들었으면 → AI 생성분. - 그런 도구 호출 없이 diff에만 나타난 변경 → 사람이 직접 타이핑.
- severity 게이트와 독립이다 — 생성 코드가 깔끔해
jarvis-once가 침묵해도 저자성 신호는 따로 뜬다.
발화 조건 (모두 충족): ① 누적 변경의 상당 부분이 AI 생성(대략 과반) · ② 절대량이 사소하지 않음(한두 줄 수정 수준이 아님) · ③ base_mirrored == 0(아직 안 띄움). 충족 시 마커 밖에 건조한 메모 한 줄을 남기고 mirrored=1로 기록한다:
🪞 이번 변경의 상당 부분이 이 세션에서 생성된 코드로 보여 — 네 이름으로 커밋될 텐데 직접 파악했는지 확인 필요.
풀림(쿨다운 해제): 이후 tick에서 사람이 직접 타이핑한 변경이 다시 주를 이루거나, 리뷰를 flush했거나, 되돌림 리셋(2b)이면 mirrored=0으로 되돌려 다음 생성 버스트에 다시 뜰 수 있게 한다.
register · 한계:
- 설교·비난·강요가 아니다. 짚고 비킨다 —
jarvis-once의 "떠먹이지도 떠보지도 않는다"와 같은 건조한 메모 register. - 보이는 범위에서만. 이 세션 transcript만 본다 — 다른 터미널/세션 생성이나 외부 붙여넣기는 못 본다. 그래서 단정("~다")하지 않고 "~로 보여"로 짚고, 추궁하지 않는다.
- 끄려면
mirror=off. 사람이 거울을 원치 않으면 그 선택도 존중한다 — 거울조차 비강제다. (리트머스 ①: 강요는 울트론, 거울은 자비스)
3.6. 계획 리뷰 & 완료 (계획 모드 활성일 때만)
.jarvis/checklist.md가 없으면 이 절차 전체를 건너뛴다. 있으면 체크리스트가 워치가 기준으로 navigate 하는 스펙이다.
리뷰당 체크리스트 평가 (게이트에 얹힘 — 추가 비용 없음): 이건 리뷰를 실제로 flush한 tick(절차 3의 실행 분기, 또는 커밋 경계 리뷰 2a)에서만 돈다 — 매 tick이 아니라 같은 비싼 순간들. jarvis-once 호출 시 계획/체크리스트는 이미 "작업 스펙"(수집 항목 0)으로 넘어가 있다. 일반 리뷰와 함께 체크리스트에 대고 보고하게 한다:
- 이 변경이 어느 항목(들)을 전진시키나, 그리고 어느 항목이 이제 완료로 보이나;
- 항목별로 뭐가 빠졌거나 위험한가(해피 패스는 됐지만 에러/빈 입력 경로는 안 된 항목);
- 완료 오판정 — 사람이 이미 체크한 항목(
- [x])인데 코드가 뒷받침하지 않으면 짚는다(설익은 "완료"를 반박하는 게 내비게이터의 일이지, 도장 찍어주는 게 아니다). - 이 평가는 Jarvis 마커 안에 리뷰 본문의 일부로 넣는다.
체크마크는 사람이 소유한다. 워치는 - [ ] → - [x]를 스스로 체크하지 않는다 — 그 판단(그리고 거기 실리는 책임)은 사람의 몫으로 남는다. 워치는 자기 평가를 보고만 하고, 사람이 동의하면 .jarvis/checklist.md에서 항목을 체크한다. (매 리뷰에 파일을 읽어 공식적으로 뭐가 됐고 뭐가 열려 있는지 안다.)
무엇부터 할지는 사람의 몫. 물으면 다음 항목 하나와 왜인지 한 줄을 추천하고 — 비킨다. 작업 순서를 대신 짜지 않는다.
완료 → 최종 리뷰, 그다음 종료 추천(확정은 사람): 모든 체크리스트 항목이 완료로 보이면(전부 체크됨, 또는 열린 공백 없이 전부 완료로 평가됨) 그 tick에서 jarvis-once에 계획 전체에 대고 종합 최종 리뷰를 시킨다 — "전부 봉인되기 직전"의 골든타임을 가장 큰 스케일로. 마커로 감싸 전달한 뒤, 마커 밖에 한 줄 추천을 남긴다:
✅ 체크리스트 전 항목 완료로 보임 — 위 최종 리뷰 참고. 루프를 닫을까요? (Esc로 멈춤 · 또는 "done" / 계속)
- 이 tick에서 스스로 루프를 끝내지 않는다 — 사람이 정하는 동안 워치가 살아있도록 다음 wake는 그대로 예약한다(절차 4). 루프 종료는 사람의 행위다: 사람이 확정("done")하거나 그냥 인터럽트(Esc)하면 그때 다음 wake가 예약되지 않는다. ("모든 방아쇠는 사람이 당긴다"와 일관)
- 완료 신호 후에도 사람이 계속 편집하면, 루프는 그냥 계속 리뷰한다 — 특별히 할 일 없음.
4. 다음 wake 예약 (루프 유지의 핵심)
ScheduleWakeup을 호출해 다음 실행을 예약한다.
-
웜업 판정:
boot_now를 정한다 — 최초 부팅 tick(0.5에서 baseline 없음으로 판정)이면warmup값, 아니면 baseline의boot값(없으면 0). -
간격 결정:
- 이번 tick에서 리뷰를 미뤘으면(
deferred를 1로 세팅) →debounce(짧게, 곧 다시 확인) - 이번 tick에서
jarvis-once를 실행(flush)했으면 →active - 웜업 중(
boot_now > 0) →active(첫 로드 직후엔 idle 대신 빠르게 재확인) - 워킹트리에 변경이 있으면(
cur_lines > 0) →active(작업이 진행 중이니 촘촘히 폴링해 새 변경·커밋을 빨리 잡는다) - 그 외(워킹트리 깨끗 · 웜업 종료) →
idle
- 이번 tick에서 리뷰를 미뤘으면(
-
prompt에는 이번에 받은 args를 그대로 echo 한다. 그래야 다음 wake에서도 설정이 유지된다.
strength를 쓴 경우 전개된 개별 노브가 아니라strength=<값>(+ 개별 덮어쓰기) 형태로 직렬화한다(강도 의미 보존). 예:/jarvis strength=high paths=src/billing risk=**/*payment* /jarvis strength=high idle=30m # 프리셋 + 개별 덮어쓰기도 그대로 echo /jarvis active=4m idle=25m debounce=90s # strength 미사용 시는 개별 노브로 echo⚠️ 이 echo를 빠뜨리면 두 번째 wake부터 모든 설정이 기본값으로 돌아간다. 반드시 현재 유효 args 전체(
strength또는 개별 노브 +paths·risk)를 직렬화해 넘긴다. -
재개용 args 저장: wake 예약과 함께, prompt에 echo한 것과 동일한 args 문자열을
.jarvis/args에 1줄로 기록한다(/jarvis접두어 없이 args만). 루프를 인터럽트(Esc)로 멈춘 뒤/loop /jarvis(인자 없이)로 다시 띄우면 절차 0.4가 이 값을 읽어 같은 설정으로 자동 재개한다. -
reason에는 무엇을 기다리는지 구체적으로 적는다(예: "변경 누적 폴링, 다음 4분 뒤 확인"). -
생존 상태 기록(
.jarvis/status):ScheduleWakeup직후, 다음 wake 시각을 epoch로 환산해.jarvis/status에 한 줄로 덮어쓴다.<delay_s>는 이번에 예약한 간격(초),<interval>은 그 간격 라벨(active/idle/debounce),<strength>는 유효 강도(개별 노브만 쓴 경우 생략 가능):mkdir -p .jarvis now=$(date +%s) plan="" # 계획 모드: 체크리스트 진행률을 붙여 statusline이 표시하게 함 if [ -f .jarvis/checklist.md ]; then done=$(grep -cE '^[[:space:]]*-[[:space:]]*\[[xX]\]' .jarvis/checklist.md); total=$(grep -cE '^[[:space:]]*-[[:space:]]*\[[ xX]\]' .jarvis/checklist.md) plan=" plan=${done}/${total}" fi echo "state=watching next_wake=$((now + <delay_s>)) interval=<interval> strength=<strength>${plan} tick=$now" > .jarvis/status rm -f .jarvis/stopped # tick이 돈다 = 루프 살아있음 → 이벤트 멈춤 플래그 해제(있다면)이 한 줄이 statusline의 연속 표시(감시 중 / 멈춤?)와 stall 추론의 근거다(아래 "생존 가시화"). 기록을 빠뜨리면 statusline이 옛
next_wake만 보고 곧 "멈춤?"으로 뜬다.plan=<완료>/<전체>필드는 선택 — 계획 모드에서만 있고, statusline이· plan 3/7구획으로 그린다.
생존 가시화 (하트비트 + statusline)
"지금 루핑 중인지" 사용자가 늘 알 수 있게 두 겹으로 신호를 낸다. /loop dynamic 모드는 상주 프로세스가 없어 tick과 tick 사이엔 아무것도 안 돈다 — 그래서 (A) tick마다 본문에 한 줄, (B) tick 사이에도 보이는 statusline, 둘을 함께 쓴다.
(A) tick 하트비트 — 본문 한 줄
조용한 tick(새 변경 없음)이라도 마커 밖에 alive 신호 한 줄을 남긴다. 이 한 줄이 "루프가 살아있고 다음 확인은 언제"임을 매 tick 보여준다.
⏱ jarvis · alive · 다음 확인 ~4m 뒤 (active) · strength=medium
- 표기는 절대 시각이 아니라 상대(~Nm 뒤) 로 한다(절차 4에서 정한 간격을 그대로 쓰면 되고
date계산이 필요 없다). - 간격 라벨(
active/idle/debounce)과 유효 강도를 함께 적는다. - 대화 중(
active_chat == true) tick에서는 하트비트를 찍지 않는다 — 사람이 곁에 있어 루프 생존이 자명하고, 대화를 끊지 않기 위함이다(절차 0). 이때의 연속 신호는 (B) statusline이 메운다. 단 미룸(debounce) 분기의 운영 메시지는 평소대로 남긴다. - 리뷰를 flush한 tick은 마커로 감싼 리뷰 본문 뒤에 이 하트비트 한 줄을 덧붙인다(다음 확인 ETA 제공).
절차 3의 "그 외(새 변경 없음)" 분기는 이제 침묵 대신 이 하트비트 한 줄을 낸다(대화 중이면 생략).
(B) statusline — tick 사이에도 연속 표시
.jarvis/status(절차 4에서 매 tick 기록)를 읽어 Claude Code 하단 상태줄에 워치 상태를 렌더마다 그린다. tick이 안 도는 사이에도 보이고, next_wake가 한참 지났는데 갱신이 없으면 **"멈춤?"**까지 추론한다(=루프가 죽었거나 Esc로 멈춤).
🤖 jarvis · 감시 중 · 다음 ~3m (active) (정상 — 살아 있음)
🤖 jarvis · 확인 중… (예약 시각 도달, 곧 tick)
🤖 jarvis · ⚠ 멈춤? 11m 동안 tick 없음 — /loop /jarvis 로 재개 (죽었을 가능성)
스크립트는 이 스킬에 동봉돼 있다: assets/statusline.sh. .jarvis/status가 없으면(=워치 미가동) 아무것도 출력하지 않아, 항상 켜둬도 무해하다. 활성화는 비파괴적으로 사용자가 직접 settings.json에 등록한다(설치 시 자동으로 건드리지 않는다):
{
"statusLine": {
"type": "command",
"command": "bash ~/.claude/skills/jarvis/assets/statusline.sh"
}
}
- 프로젝트 단위 설치면 경로를
.claude/skills/jarvis/assets/statusline.sh로 바꾼다. - 이미 다른
statusLine을 쓰고 있으면 Claude Code는 statusLine을 하나만 허용하므로, 기존 스크립트 안에서bash .../statusline.sh를 호출해 그 출력을 한 구획으로 합쳐 쓴다.
(C) Stop 훅 — 멈춤을 시간 추론 없이 즉시 확정 (선택)
(B)의 statusline은 next_wake가 지나야 죽음을 추론하므로 최대 interval + grace만큼 지연된다. 이를 이벤트 기반으로 끌어올리는 보조 장치가 Stop 훅이다.
assets/loop-watch-hook.sh를 Stop·StopFailure 훅으로 걸면, 매 턴 종료 시 payload의 session_crons(현재 예약된 wake 목록)를 검사한다. 거기에 /jarvis wake가 없으면 = 루프가 더는 예약돼 있지 않다(ScheduleWakeup 누락·에러, 또는 Esc로 죽은 뒤 사용자가 무언가 입력한 턴) → .jarvis/stopped를 남겨 statusline이 즉시 "멈춤"을 표시하게 한다. wake가 살아있으면 플래그를 지운다.
{
"hooks": {
"Stop": [ { "hooks": [ { "type": "command", "command": "bash ~/.claude/skills/jarvis/assets/loop-watch-hook.sh" } ] } ],
"StopFailure": [ { "hooks": [ { "type": "command", "command": "bash ~/.claude/skills/jarvis/assets/loop-watch-hook.sh" } ] } ]
}
}
- 타이밍: Esc 그 순간엔 못 잡는다 — Claude Code엔 인터럽트 훅이 없다(Stop은 정상 턴 종료에만 발화). 대신 Esc 후 다음 턴(=사용자의 다음 입력)이 끝나는 순간 확정된다. 보통 곧바로 손대므로 체감상 거의 즉시이고, "Esc 후 아무 입력 없이 방치"한 경우만 (B)의 시간 추론이 받는다.
- 오탐 방지:
last_assistant_message등에 "/jarvis" 텍스트가 있어도 무시하고 오직session_crons안의 wake prompt만 본다. - 훅이 없으면 (B)만으로도 동작한다(시간 추론). 훅은 감지 속도를 높이는 선택적 보강이다.
멈춤 / 재개 / 초기화
/loop을 엔진으로 쓰므로 생애주기는 /loop의 수명주기에 얹힌다. 별도 pause/resume 명령은 없다.
| 동작 | 방법 | 상태(.jarvis/) |
|---|---|---|
| 시작 · 재개 | /loop /jarvis [args] | baseline/args가 남아 있으면 그 지점에서 이어감. args 없이 띄우면 .jarvis/args 자동 복원(절차 0.4) |
| 멈춤 · 일시정지 | /loop 인터럽트(Esc) | 손대지 않음 — baseline/args 보존돼 재개 대기 |
| 완전 초기화 | /jarvis-reset (독립 스킬) | .jarvis/baseline·.jarvis/args 삭제(그리고 확인 후 plan.md·checklist.md) → 다음 시작은 최초 부팅(배너 · 계획 옵트인 · 강도 질문) |
작동 원리:
- 가벼운 양보(암묵): 사용자가 그냥 다른 메시지를 보내면 그 턴은 절차 0에 따라 의논/구현에 양보한다(워치 자체는 살아 있음). 명령 없이 일어나는 암묵적 양보다.
- 멈춤/일시정지 = 루프 인터럽트(Esc):
/loopdynamic 모드에서 다음 tick은ScheduleWakeup을 부를 때만 생긴다. 루프를 인터럽트하면 다음 wake가 예약되지 않아 루프가 끝난다. baseline·args는 디스크에 그대로 남으므로 손실 없이 재개를 기다린다. (루프 종료엔 teardown 훅이 없어 종료 시점에 정리 코드를 돌릴 수 없다 — 그래서 별도 pause 플래그를 두지 않는다.) - 재개 =
/loop /jarvis: 다시 띄우면 baseline이 있어 배너·강도 질문을 건너뛰고(절차 0.5·0.6), args 없이 띄웠으면 절차 0.4가.jarvis/args를 복원해 같은 설정으로 이어간다. 밖에서 rebase/reset 했더라도 절차 2(0) stale 자가보정이 무효 head를 현재값으로 조용히 맞춘다. /jarvis-reset:.jarvis/baseline·.jarvis/args를 지우는 독립 명령이다. 돌고 있는 루프를 멈추진 않는다(그건 Esc) — 멈춘 뒤 "처음부터 새로"를 원할 때 상태를 비운다. ("jarvis 초기화", "처음부터 다시"도 동일하게 처리. "그냥 멈춰"는 Esc 안내.)
비용 가이드 (사용자 안내용)
- wake 간격이 5분 미만이면 prompt 캐시(TTL 5분)가 살아 폴링 턴이 거의 공짜다. 그래서
active기본값은 4분이다. - 잠잠할 때는
idle을 길게(기본 25분) 두어 캐시 미스 누적 비용을 줄인다. - 실제 토큰의 대부분은 "
jarvis-once가 도는 순간"(새 변경 누적·위험 경로·커밋 경계)에 쓰인다. 새 변경이 쌓일 때마다 리뷰하므로, 폴링이 촘촘할수록(강도high) 리뷰가 잦아져 비용이 는다.idle을 길게 잡거나 강도를 낮추면 준다. - 폴링을 촘촘히 해도 소음은 늘지 않는다.
jarvis-once는 자체 severity 문턱 아래선 스스로 조용히 있기 때문이다. 폴링을 높이면 호출 비용만 늘릴 뿐 잔소리를 늘리지 않는다 — 반응 속도를 높이고 싶으면 안심하고 올려도 된다. - 커밋 경계 트리거는 커밋당 1회뿐이라 비용이 예측 가능하고, "착오 봉인 직전" 단 한 번의 점검이라 비용 대비 가치가 가장 높은 실행이다.
의존성 주의
이 스킬은 jarvis-once(단발 navigator) 스킬을, 계획 옵트인 시엔 jarvis-plan(계획 navigator) 스킬을 호출한다. 팀에 공유하려면 둘 다 함께 접근 가능해야 한다(레포 .claude/skills/jarvis-once/·jarvis-plan/ 또는 공통 레포). 개인 글로벌(~/.claude/skills/…)에만 있으면 다른 팀원 환경에서는 호출이 실패할 수 있다.