@tuzi-ince/hi-loop
v0.6.1
Published
CLI와 MCP 모드를 지원하는 초경량 자율형 자가 치유 엔진
Maintainers
Readme
hi-loop
CLI와 MCP 모드를 지원하는 초경량 자율형 자가 치유 엔진.
날것의 아이디어(goal)를 던지면, AI 에이전트를 PDCA 루프로 반복 구동해 테스트가 실제로 통과할 때까지 스스로 고쳐 나간다.
- 요구사항(무엇을):
docs/SPEC.md - 설계(어떻게·왜):
docs/DESIGN.md - 배포·설치·사용:
docs/guide.md
루프 엔지니어링 — 우리의 개발 철학
이 엔진은 "AI가 코드를 잘 짜준다"에 기대지 않는다. AI는 자주 틀리고, 더 자주 틀린 것을 다 됐다고 말한다. 그래서 우리는 모델의 자기보고를 판정에서 배제하고, 기계가 검증할 수 있는 종료 조건 위에 루프를 세운다. 세 문장이 전부다.
- 산문은 요청이고, 메커니즘은 사실이다. 보고서에 "이건 검증 안 됨"이라고 적는 것은 요청일 뿐이다. 한 번 더 돌려서 측정하면 그 문장이 사실이 된다. 그래서 이 엔진은 문장을 늘리기보다 검사를 하나 더 돌린다.
- 에이전트의 "다 됐어요"는 판정이 아니다.
성패는 언제나
testCommand의 exit code다. 엔진이 직접 실행해서 확인한다. - 종료 조건 없는 단계는 엔진의 단계가 아니라 문서 생성기다. 모든 단계는 기계가 판정할 통과 기준을 가진다. 없으면 그 단계는 만들지 않는다.
이 세 원칙에서 뒤의 안전장치들이 전부 파생된다.
설치
# 1) 전역 설치 — hi-loop / hi-loop-setup 명령 등록
npm install -g @tuzi-ince/hi-loop
# 2) 프로젝트에 주입 (멱등 — 여러 번 돌려도 안전)
cd /path/to/my-project
hi-loop-setup # .mcp.json / .gitignore / docs·tests / CLAUDE.md / docs/DESIGN.md 자동 주입
hi-loop-setup --host cursor # Cursor 용 .cursor/mcp.json 에 등록
hi-loop-setup --host opencode # opencode 용 opencode.json 에 등록MCP 서버는 표준 stdio 라 Cursor·opencode 등 다른 호스트에도
--host로 등록된다. 단 스킬·CLAUDE.md 자동 라우팅은 Claude Code 전용이고, 다른 호스트에선 루프의 일꾼도 claude 가 아니라면HILOOP_AGENT_PROVIDER=generic이 필요하다(claude 외 CLI 이식 — 세션·비용 미지원). 자세히는 가이드 §4-2/§4-3.
hi-loop-setup 은 표준 설계 문서 docs/DESIGN.md 를 함께 만든다(있으면 보존). 이 문서를 채우면
문서↔소스 정합 장치(--reconcile-spec / --spec)의 오라클이 된다 — 아래 문서↔소스 정합 참조.
소스에서 개발용으로 설치하려면:
git clone <이 저장소> hi-loop && cd hi-loop
npm install && npm test # node:test, devDependency 0개
npm link # hi-loop / hi-loop-setup 명령 등록자세한 절차·문제해결·배포는 docs/guide.md.
두 가지 실행 모드
# 1) CLI 직접 실행 — 터미널, 백그라운드 데몬, 텔레그램 봇에서 구동
hi-loop run --goal "JWT 인증 미들웨어를 만들어라" --test "npm test" --max-loops 10 --budget-usd 5
# 2) MCP 서버 — 클로드코드/커서가 도구로 로드 (인자 없으면 기본 동작)
hi-loop mcp
hi-loop status # 현재 루프 상태 요약MCP 도구: hiloop_run, hiloop_answer, hiloop_status, hiloop_reset, hiloop_rollback, hiloop_setup, hiloop_docsync.
동작 원리
PLAN ──► SPEC.md + tests/app.test.js 를 먼저 강제 (구현 코드 금지)
DO ──► 테스트를 통과시킬 최소 구현
CHECK ──► 엔진이 testCommand 를 직접 실행 (에이전트의 "다 됐어요"는 안 믿는다)
ACT ──► stderr 를 그대로 에이전트에 들이밀고 "이 에러를 고쳐라" (최대 10회)기본은 이 자가 치유 루프 하나다. --full 을 붙이면 앞뒤 단계까지 돈다.
DISCOVER → PLAN → DESIGN_REVIEW → BUILD(DO ⇄ CHECK ⇄ HEAL) → CODE_REVIEW → SHIP → WATCH → DONE
▲ │ │ │
└── reject ┘ reject ──┘ fail ────┘추가되는 모든 단계는 기계가 판정할 종료 조건을 가진다.
| 단계 | 무엇을 하는가 | 종료 조건 | |---|---|---| | DISCOVER | goal 의 모호함을 가정으로 확정하고 문서화 | 가정 목록 확정 | | PLAN | 스펙 + 테스트를 먼저 쓴다 (구현 금지) | 산출물 작성 | | DESIGN_REVIEW | 구현 착수 전 스펙을 심판 | 독립 검증자 pass | | BUILD | DO ⇄ CHECK ⇄ HEAL 자가 치유 루프 | testCommand exit 0 | | CODE_REVIEW | diff 를 정확성·보안·YAGNI 축으로 심판 | 독립 검증자 pass | | SHIP | 배포 명령 실행 | exit code 0 | | WATCH | 헬스체크 반복 | 지정 시간 동안 버팀 |
배포·감시는 에이전트를 부르지 않는다. "배포했다고 모델이 말했다"가 아니라 "헬스체크 exit code 가 0이다"가 통과 조건이다. 그래서 이 두 단계의 비용은 0이다.
실전 활용 가이드 (프로젝트 관리 관점)
무엇을 하려는지에 따라 어떻게 goal을 쓰고 어떤 플래그를 켜는지가 달라진다. 아래는 실제 프로젝트를 굴리며 자주 만나는 상황별 레시피다.
1. 프로젝트 생성 — 아이디어 한 줄 → 동작하는 코드
모호한 요구를 발굴부터 구현·리뷰까지 한 번에 돌린다. --full 이 앞뒤 단계를 켠다.
hi-loop run --goal "JWT 인증 미들웨어를 만들어라" --full --budget-usd 5- DISCOVER 가 goal의 빈칸(토큰 저장 위치, 만료 정책 등)을 가정으로 확정하고 문서화한다.
- PLAN 이 스펙과 테스트를 먼저 쓴다 — 구현보다 판정 기준이 앞선다.
- DESIGN_REVIEW → BUILD → CODE_REVIEW 순으로 만들고, 만든 것을 스스로 심판한다.
- 상호배타 분기(예: "users 확장 vs auth_tokens 신설")를 만나면 거기서만 사람에게 묻는다.
기존 파일을 덮어쓰지 않는다. docs/SPEC.md / tests/app.test.js 가 비어 있으면 그대로
쓰고, 이미 뭔가 있으면 goal 해시로 비켜간다(docs/spec-5d88cc7f.md). 같은 프로젝트에서
goal만 바꿔 여러 번 돌려도 앞의 산출물이 살아남는다.
발굴 단계만 먼저 돌려 요구사항을 확정하고 싶다면:
hi-loop discover --goal "..." # 가정 확정 + 문서만, 구현은 안 함
2. 프로젝트 개선 — 기존 코드베이스 손질
리팩터링·성능 개선·기술부채 정리처럼 이미 테스트가 있는 코드를 고칠 때. 기존 테스트를 가드레일로 두고, 스펙 대비 검증을 더 얹는다.
hi-loop run --goal "결제 모듈의 중복 검증 로직을 하나로 합쳐라" \
--test "npm test" \
--spec docs/SPEC.md \
--verify-spec- 기존
npm test가 회귀 방지선이다 — 개선하다 무언가 깨면 Tier 1에서 걸린다. --verify-spec은 테스트 통과 후 별도 검증자가 스펙 대비 구현을 심판한다. 구조가 아니라 의도를 본다("리팩터링했다"는데 동작이 달라졌으면 기각).--spec docs/SPEC.md는 사람이 관리하는 표준 문서를 오라클로 고정한다 — 단말적 개선 요청이 문서와 어긋나는 것을 잡는다(→ 문서↔소스 정합).- 개선 중 락파일·설정·마이그레이션을 건드리면 보고서 Gaps에 ⚠️로 공개된다 (→ 영향도 분석).
3. 장애 분석 — 버그·회귀 재현 → 수정
버그를 "고쳤다"가 아니라 "재현 테스트로 못박고 고쳤다"로 끝낸다. 회귀 방지가 공짜로 남는다.
hi-loop run --goal "재현 테스트를 먼저 작성하고, 동시 요청 시 잔액이 음수가 되는 버그를 고쳐라" \
--test "npm test" \
--stagnation 3- PLAN이 실패하는 재현 테스트를 먼저 강제한다 → 버그가 테스트로 고정된다.
- HEAL이 실제 stderr를 연료로 삼아 고친다. 모델의 추측이 아니라 진짜 에러를 본다.
- 같은 벽에 열 번 부딪히지 않는다: 같은 실패가 3회 연속이면
stagnated로 조기 종료한다 — "이 접근으론 안 된다"를--max-loops소진 전에 잡는다.--no-stagnation으로 끈다. - 에이전트가 멀쩡한 코드를 오히려 망가뜨렸으면 되돌린다:
hi-loop rollback # 최신 체크포인트로 파일 복원 hi-loop rollback --to 3 # 3회차 직전 상태로
4. 영향도 분석 — 변경 위험 파악
이 변경이 어디까지 번지는가를 두 층위로 잡는다.
(a) 블라스트 반경 — 자동, 비용 0. 실행이 위험 분류 파일을 건드리면 판정과 무관하게 보고서 Gaps에 ⚠️로 공개한다. 분류: 의존성(lock/package.json), 스키마 마이그레이션, CI·배포 설정, 빌드·러너 설정, 환경 변수. "테스트는 통과했는데 왜 락파일이 바뀌었지"를 사람이 놓치지 않게 한다.
(b) 조건부 검사 — 바뀐 파일에 따라 게이트를 켠다. 무거운 검사(e2e 등)를 매 회차 돌리는
대신, 특정 경로에 변경이 있을 때만 돌린다. --when 은 git status(HEAD 대비 워킹트리 변경)로
매칭한다 — 즉 "그 경로를 건드린 그 회차만"이 아니라 커밋 전 변경분에 그 경로가 있는 한 이후
회차마다 검사가 돈다(2회차에 e2e 가 사라지지 않게 하려는 의도).
hi-loop run --goal "..." \
--check "npm test" \
--check "npm run typecheck" \
--check "npx playwright test" --when "src/ui/**"- 검사는 short-circuit 하지 않는다. 유닛이 깨져도 typecheck·e2e 결과를 같은 회차에 다 본다
(
&&로 이으면 첫 실패에서 멈춰 나머지를 영영 모른다). --when글롭에 안 맞은 검사는 건너뜀으로 기록된다 — 안 돌린 것이 통과처럼 보이지 않게 Gaps에 남는다.
MCP 에서도 같은 걸 쓴다 — "UI 변경 회차에만 e2e"를 엔진이 강제한다. hiloop_run 이 checks
인자를 받는다(CLI --check/--when 의 MCP 노출):
"checks": [
{ "cmd": "npm test" },
{ "cmd": "npx playwright test", "when": "src/**/*.tsx" }
]- e2e 를 "스킬이 사람에게 기억해서 실행"(제안)이 아니라 엔진이 결정론적으로 강제(메커니즘)하는 길이다. UI 파일이 바뀐 회차마다 엔진이 e2e 를 돌리고 통과해야 pass 로 인정한다 — 조용히 빠지지 않는다.
- e2e 는 셸 명령이어야 한다. 엔진은 Playwright MCP 도구를 부를 수 없으므로, MCP 만 있는
프로젝트는
flow스킬이 루프 통과 후 MCP 로 돌린다(폴백). 셸 e2e 명령도 없으면 스킵. flow스킬이 UI 작업을 감지하면 사용자에게 물은 뒤 이checks를 자동 구성해hiloop_run에 넘긴다.
diff만 리뷰하고 싶다면 — 지금 워킹트리 변경분을 정확성·보안·YAGNI 축으로 심판:
hi-loop review # 현재 변경분만 코드 리뷰 (구현/배포는 안 함)5. 테스트·품질 게이트
테스트 설계만 뽑고 싶을 때 (구현은 사람이 하거나 나중에):
hi-loop plan --goal "장바구니 할인 규칙" # 스펙 + 테스트까지만, 구현 전플레이키(불안정) 테스트를 걸러내고 싶을 때:
hi-loop run --goal "..." --flaky-probe- 통과한 회차에서만 같은 테스트를 한 번 더 돌린다. 두 번의 결과가 갈리면 통과로 인정하지 않는다("두 번 돌려 갈리는 초록불은 초록불이 아니다"). 에이전트 호출 0 — 비용은 테스트 한 번뿐.
여러 게이트를 한 판정에 묶고 싶을 때 — 위 조건부 검사의
--check 를 여러 번 준다. lint·typecheck·유닛·e2e를 각각 독립된 게이트로 세운다.
6. 배포 & 감시 — 사람 개입 최소
리뷰를 통과한 변경을 배포하고, 배포 후 일정 시간 헬스체크로 버티는지 지켜본다.
hi-loop run --goal "..." --full \
--ship "npm publish --access public" \
--watch "curl -f https://api.example.com/health" --watch-for 5m--ship을 주면 리뷰 단계들이 자동으로 켜진다 ("리뷰 없는 자동 배포는 위험하다"의 근거가 여기서만 성립).- SHIP/WATCH는 에이전트를 부르지 않는다 — 통과 조건은 오직 exit code다.
- 실패 대응을 지정할 수 있다:
--on-ship-fail stop|heal,--on-watch-fail stop|rollback|heal. - 비가역 배포 직전에는 사람에게 확인을 구한다. CI·봇에서는
--yes로 생략.
7. 단계별로 따로 부르기
전체를 한 번에 돌리지 않고, 필요한 구간만 실행할 수 있다. 어느 경로로 들어와도 같은 상태 머신을 쓴다.
hi-loop discover --goal "..." # 발굴만 (가정 확정 + 문서)
hi-loop plan --goal "..." # 스펙·테스트까지만 (구현 전)
hi-loop review # 현재 변경분만 코드 리뷰
hi-loop ship --ship "npm publish"
hi-loop watch --watch "curl -f ..."또는 --start-from / --stop-after 로 한 실행의 구간을 자른다:
hi-loop run --goal "..." --start-from BUILD --stop-after CODE_REVIEW후반 단계는
--goal을 다시 받지 않는다 — 저장된 상태에서 읽는다(goal이 한 글자만 달라도 새 루프로 인식돼 진행 상황이 버려진다). PLAN은 BUILD 안쪽의 회차라--start-from대상이 아니다 — BUILD로 시작하면 자연히 PLAN부터 돈다.
8. MCP에서 팀으로 쓰기 (클로드코드/커서)
hi-loop mcp 로 띄우면 호스트 LLM이 위 시나리오를 도구로 호출한다. 차단형 입력을 쓰지 않고
상태를 저장하고 종료하므로(exit 3) 대화형 세션·백그라운드 데몬·봇에서 그대로 성립한다.
호스트 LLM ──hiloop_run──► 루프 구동
◄──분기 질문─── (상호배타 선택 필요)
──hiloop_answer──► 사람의 선택을 "가정"으로 기록하고 재개
──hiloop_status──► 진행/비용/Gaps 조회9. 코드 어시스턴트가 요청을 처리하는 방식 — 직접 호출 vs 평문 요청
클로드코드·커서 같은 코드 어시스턴트(호스트 LLM) 안에서 hi-loop은 두 갈래로 불린다. 핵심은 "LLM이 요청을 읽는 순간, hi-loop을 떠올릴 근거가 눈앞에 있느냐" 다.
(a) 직접 호출 — 명시적으로 엔진을 지목
사용자가 hi-loop을 대놓고 부른다. LLM은 바로 hiloop_run을 구동한다(목표만 확정).
| 사용자가 이렇게 말하면 | LLM이 하는 일 |
|---|---|
| /flow 로그인 폼 만들어줘 (슬래시 스킬, /hi-loop:flow 도 동일) | flow 스킬 기동 → hiloop_run(full=true, step=true) 실행 — 대화형이라 스텝 모드로 매 스텝을 보이며 진행 |
| /flow plan 로그인 폼 (단계어) | 첫 단어가 단계면 그 단계까지만: stopAfter:"PLAN" → 스펙·테스트만 만들고 멈춰 다음(구현) 제안. do/review/ship 등도 동일 |
| "hiloop_run 도구로 결제 버그 고쳐줘" | 지목된 MCP 도구를 로드해 바로 호출 |
| 터미널에서 hi-loop run --goal "..." --full | 엔진을 CLI로 직접 구동(LLM 개입 없음) |
(b) 평문 요청 — 그냥 하고 싶은 일을 말함
사용자는 hi-loop을 언급하지 않는다. 이때 자동으로 hi-loop에 걸리는 건 트리거가 박힌
스킬 description + CLAUDE.md 지침이 매 세션 떠 있기 때문이다(설치 + hi-loop-setup 전제).
그 표면이 없으면 LLM은 hi-loop을 모른 채 그냥 직접 구현한다.
| 사용자가 이렇게 말하면 | 어디에 걸리나 | LLM이 하는 일 |
|---|---|---|
| "워크스페이스 폴더 드래그앤드롭 개선해줘" | 트리거 개선 | flow 스킬 후보로 뜸 → 기획·설계 필요 판단 → flow 실행(대화형이라 step 으로 매 스텝 보이며 진행) |
| "장바구니 기능 하나 만들어줘" | 트리거 기능/design | 발굴→스펙·테스트 우선(PLAN)→구현·리뷰 루프 |
| "결제 모듈 리팩터해줘" | 트리거 리팩터 | 기존 테스트를 가드레일로 --verify-spec 성격의 개선 루프 |
| "동시 요청 시 잔액 음수 버그 고쳐줘" | 트리거 구현/버그성 | 재현 테스트 먼저 → HEAL 루프로 수정 |
| "이 오타 고쳐줘" / "변수명 바꿔줘" | 예외(사소) | 루프 없이 바로 처리 — README·CLAUDE.md가 사소한 작업은 제외하라고 명시 |
왜 이렇게 갈리나 (push vs pull)
- 트리거 스킬 description·CLAUDE.md = push 표면. LLM이 찾지 않아도 매 세션 상주하며,
평문에
개선/설계/버그같은 단어가 있으면 자동으로 hi-loop을 후보로 올린다. - MCP 도구 = pull 표면. 세션에선 이름만 있고(때로 deferred), LLM이 먼저 "hi-loop이
필요하다"고 떠올려
ToolSearch로 끌어와야 설명이 보인다. - 그래서 평문 요청이 자동 라우팅되려면 push 표면이 반드시 있어야 한다.
hi-loop-setup이CLAUDE.md지침을 심고, 플러그인이 트리거 스킬을 등록하는 이유가 이것이다. 이게 없으면 "개선해줘"라고 해도 LLM은 hi-loop을 거치지 않고 곧장 코드부터 짠다.
확실히 hi-loop을 태우고 싶으면 (a) 직접 호출이 가장 안전하다. (b) 평문 자동 라우팅은 편하지만, 설치·주입이 안 됐거나 트리거에 안 걸리는 표현이면 우회될 수 있다.
브랜치 & 커밋 워크플로 (FR-16)
코드 변경 작업을 보호 브랜치에서 분기 → 구현·테스트 → 로컬 커밋으로 감싼다. push/PR 은 하지 않는다.
정책은 커밋되는 .hi-loop.json 한 파일에 산다(팀 공유, 스킬·엔진 공용). .hi-loop/STATE.json
(임시·gitignore)과 다르다. 기본값:
{
"protectedBranches": ["main", "master", "develop", "dev"],
"branchPolicy": "ask", // always=자동 분기 | ask=매번 확인 | never
"commitPolicy": "confirm", // confirm=메시지+diff 확인 후 | auto | off
"commitStyle": "match-log" // match-log=git log 관행 미러 | conventional | korean
}두 실행 층 (같은 정책 파일):
- 대화형(스킬/코드 어시스턴트): 코드변경 요청 시
.hi-loop.json이 없으면 최초 1회 정책을 묻고 저장(지연 발동). 이후 보호 브랜치면 분기 게이트, 테스트 통과 후 커밋 게이트(diff 보여주고 승인)를 태운다. "이번만 커밋하지 마"(일회성)와 "앞으로 커밋하지 마"(정책 변경)를 구분한다. - 헤드리스(CLI/봇/CI): 물어볼 수 없으니 플래그 = 동의.
hi-loop run --goal "..." --branch --commit # 보호 브랜치면 자동 분기 + 통과 후 자동 커밋
hi-loop run --goal "..." --branch feature/login # 브랜치명 지정
hi-loop run --goal "..." --commit --commit-message "feat: 로그인".hi-loop.json 의 branchPolicy: always / commitPolicy: auto 는 CLI 에서 플래그 없이도 발동한다
(플래그가 우선). 커밋 스테이지는 상태 머신에서 CODE_REVIEW → COMMIT → SHIP 사이에 든다 —
리뷰된 코드를 커밋한 뒤 배포한다.
정책 관리:
hi-loop config # 현재 정책 출력
hi-loop config set commitPolicy auto # 정책 변경 (branchPolicy|commitPolicy|commitStyle)
hi-loop config add-branch release # 보호 브랜치 추가
hi-loop config remove-branch dev # 보호 브랜치 제거상태 초기화
새 goal로 깨끗이 다시 시작하려면 상태 파일을 지운다:
hi-loop reset # .hi-loop/STATE.json 삭제 (CLI)
# MCP: hiloop_reset안전장치 — 루프 엔지니어링 원칙
위 세 원칙에서 파생된, 이 엔진이 false green을 막는 구체적 장치들이다.
세션 핸드오프 & 컨텍스트 다이어트
에이전트는 같은 세션에서 오래 굴릴수록 토큰이 쌓여 멍청해진다. 그래서 4회마다 세션을
버리고, .hi-loop/STATE.json 에 압축된 상태(goal / 스펙 요약 / 마지막 에러 / 최근 이력 5건)만
새 세션에 넘겨 이어서 작업한다(auto-resume). 같은 goal로 다시 실행하면 중단 지점부터 재개한다.
TDD 자가 치유
성패 판정은 항상 testCommand 의 exit code다. 에이전트의 자기보고는 판정에 쓰지 않는다.
테스트를 지워서 통과하는 것을 막는다
CHECK는 2단이다 — check.ok && integrity.ok 여야 통과. 판정 기준인 테스트를 에이전트가
지우거나 skip/always-true로 무력화하면 exit 0이 나와도 통과로 인정하지 않는다.
탐지 패턴은 케이스 감소, .skip, .only, assert.ok(true) 등이다. 테스트 추가는 위반이 아니다.
통과가 무엇을 확인 안 했는지 공개한다
이 엔진의 passed 는 조용한 거짓말이 될 수 있다 — 테스트를 PLAN 단계에서 에이전트 자신이
썼기 때문이다. 그래서 통과할 때마다 Evidence와 Gaps를 함께 출력한다:
관측하지 않은 것(Gaps):
- 이 테스트는 에이전트 자신이 작성했다. 외부 오라클이 아니다.
- 스펙의 어떤 수용 기준이 커버 안 됐는지는 검증하지 않았다.
- 런타임·성능·보안은 관측 범위 밖이다.false green을 disclosed green으로 바꾼다.
망가뜨려도 되돌린다
각 에이전트 호출 전에 git stash create 로 워킹트리를 스냅샷한다(비파괴 — 워킹트리를
안 건드리고 SHA만 남긴다). 에이전트가 멀쩡한 코드를 망가뜨리면 hi-loop rollback 으로
복원한다. 자동 롤백은 없다 — 전경 실행이 기본이라 사람이 본다. git 저장소가 아니면
조용히 생략한다.
스펙을 오라클로 — 2단 판정 (--verify-spec)
이 엔진의 exit 0은 "에이전트가 자기가 쓴 테스트를 통과시켰다"일 뿐이다. --verify-spec 을
켜면 통과 후 별도 검증자가 스펙 대비 구현을 심판한다.
- Tier 1 (기계): exit code + 무결성. 최종 권한. 실패면 끝.
- Tier 2 (모델): 스펙 대조. 오직 기각만 가능 — 통과를 되돌릴 순 있어도 Tier 1 실패를
통과로 못 올린다. 쓰기 권한 없이(
--permission-mode plan) 새 세션으로 호출.
구조가 아니라 의도를 본다. 비용이 늘어 opt-in이다.
문서를 상시 오라클로 — 표준 문서 고정 (--spec)
구현하다 보면 단말적 요청들이 작성된 문서와 상이하게 들어와 문서↔소스 갭이 생기고,
그 갭이 환각의 빌미가 된다. 기본 동작은 goal마다 스펙을 새로 만들어(goal 해시 분기) 이 갭을
막지 못한다 — 스펙 파일만 여러 개로 늘 뿐이다. --spec <path> 가 이 구멍을 닫는다.
hi-loop run --goal "..." --spec docs/SPEC.md --verify-spec- 스펙 오라클을 이 경로로 고정한다. DESIGN_REVIEW·CODE_REVIEW·
--verify-spec이 모두state.specPath하나에서 읽으므로, 셋 다 이 표준 문서를 기준으로 심판한다. - goal이 달라도 같은 문서를 오라클로 공유한다 — 새 요청이 문서와 어긋나면 오라클이 잡는다.
- 고정한 문서에 이미 내용이 있으면 PLAN이 덮어쓰지 않는다. 사람이 관리하는 표준 문서를 진실로 받아들이고, 그에 맞춰 테스트만 쓴다. 비어 있을 때만 스펙을 작성한다.
기본(자기서술 스펙)은 "에이전트가 자기가 쓴 테스트를 통과"일 뿐이다(Gaps가 이를 공개한다).
--spec으로 외부 오라클을 주면 그 한계를 좁힌다.
문서와 어긋나는 요청을 조용히 넘기지 않는다 (--reconcile)
--spec이 "이 문서를 오라클로 써라"라면, --reconcile은 **"이 요청이 기존 문서와 모순되는지
먼저 확인하라"**다. 표준 문서를 고정하지 않아도, 요청이 문서를 배신하는 순간을 잡는다.
hi-loop run --goal "결제에서 음수 잔액도 허용하라" --reconcile # --full 이면 자동 켜짐- 기존 표준 문서가 있고 이번 요청이 그것과 별개 스펙으로 포크될 참이면, 포크 직전에 별도 판정자(plan 모드, 새 세션)가 goal과 표준 문서를 대조한다.
- 대조 대상 우선순위:
--reconcile-spec <path>(명시) →docs/SPEC.md가 아니라docs/DESIGN.md(setup이 만드는 사람 관리 표준 문서) → 없으면docs/SPEC.md폴백. 즉hi-loop-setup을 했다면--full/--reconcile만으로 별도 지정 없이 DESIGN.md가 자동으로 오라클이 된다. (SPEC.md는 PLAN이 쓴 에이전트 산출물이라 오라클로 약하다 — 사람 문서가 있으면 그쪽이 옳다.) - 모순이면 멈추고 사람에게 묻는다(상호배타 3지선다): (a) 표준 문서를 오라클로 고정하고 진행 / (b) 새 스펙으로 분기(요청이 문서를 갱신) / (c) 중단하고 문서를 먼저 정리.
- 모순이 없으면(또는 표준 문서가 없으면) 판정자를 부르지도 않는다 — 비용 0. 판정 실패는 통과로 흘린다(애매한 것까지 막으면 정상 작업이 멈춘다). opt-in.
이것이 "단말적 요청이 문서와 상이하게 들어와 갭이 벌어지는" 상황을 엔진 차원에서 막는 장치다.
--spec(고정)과--reconcile(감지)은 짝으로 쓰면 문서↔소스 정합이 가장 단단하다.
소스를 바꿨으면 문서도 맞춘다 — hiloop_docsync / hi-loop docsync / /flow doc-sync
--spec/--reconcile이 drift를 예방하는 게이트라면, doc-sync는 이미 바뀐 소스에 맞춰 문서를
갱신하는 단발 패스다. git diff로 소스 변경을 읽어 일꾼 에이전트가 문서만(README·docs/*) 고친다.
hi-loop docsync # 소스 변경 → 문서 반영 (한 번의 패스, 진행 표시)
# 또는 스킬: /flow doc-sync · MCP: hiloop_docsyncdocs/DESIGN.md(사람 오라클)와 소스·테스트는 건드리지 않는다. 문서 외 편집은stray로 경고.- diff로 확인되는 것만 반영한다(없는 내용을 지어내지 않음). 커밋은 사람이 판단한다.
- 자가 치유 루프가 아니라 짧은 단발이라 타임아웃/블랙박스가 없다. MCP/CLI라 소비 프로젝트에서도 동작.
에이전트를 못 부르면 스택이 아니라 지침을 준다 (preflight)
에이전트(claude) 미설치·인증 만료·API 키 부재는 이 엔진의 버그가 아니라 환경 문제다. 루프 한복판의 스택트레이스가 아니라 행동지침 한 줄로 바꾼다.
- 에이전트를 부르는 명령은 루프 진입 전
claude --version(비용 0)으로 실행 가능성을 확인한다. 미설치(ENOENT)·권한(EACCES)이면 회차를 태우지 않고 멈추며 설치·HILOOP_AGENT_CMD안내를 준다. - 인증/구독/키 문제는
--version으론 알 수 없다(비용을 안 들인다). 첫 호출 stderr를 패턴으로 읽어 힌트로 번역하고, 못 알아본 실패는 원문을 그대로 보여준다(감추지 않는다). - 환경 오류는 CLI에서 스택 없이 메시지만, MCP에선 접두 없이 그대로 전달한다. 진짜 버그만 스택을 노출한다.
MCP는 호스트가 정한 프로젝트를 앵커로 삼는다
MCP 도구의 cwd 기본값은 CLAUDE_PROJECT_DIR || process.cwd() 다 — 호스트(클로드코드/커서)가
"어느 프로젝트인가"로 주입하는 값을 따른다. 호출 시 cwd를 명시하면 그것이 최우선. (CLI는
반대로 순수 process.cwd() — 터미널에서 cd한 곳이 사용자의 명시 의도이고, 통합 터미널에
새어든 env가 그것을 덮으면 하위 프로젝트 겨냥이 깨지기 때문이다.)
🛑 실패를 어떻게 멈출까 — 카운트 대신 사람 판단 (--on-fail)
기본은 --max-loops(10)까지 자동 치유다(무인 환경 백스톱). 하지만 대화형·비싼 검사(e2e)에서는
카운트보다 사람 판단이 낫다 — --on-fail 로 정한다:
| 값 | 동작 |
|---|---|
| heal (기본) | --max-loops 안에서 자동 치유 (종전) |
| stop | 첫 실패에서 즉시 종료 |
| ask | 실패/불능에서 "재시도할까 / 종료할까"를 사람에게 물음 (pause/resume). MCP 에선 awaiting 반환 → hiloop_answer 로 답하고 재호출 |
ask 는 비싼 e2e 루프에 특히 유용하다 — 8.5분·수달러짜리 회차를 무인으로 반복하지 않고,
**판정 불능(무결성·환경)**까지 사람에게 표면화한다. 무인(--ask never)에선 물을 수 없으니 안전하게 종료한다.
⏭ 스텝 모드 — LLM 이 루프를 몰고, 매 스텝이 보인다 (step)
기본은 한 콜에서 엔진이 루프 전체를 돈다(모델 A: 무인·결정론에 최적). 대화형에선 이게
불투명하고(진행이 콘솔에 안 보임) 길어서 타임아웃 위험이 있으며 에이전트를 두 번(바깥 LLM +
서브프로세스 claude) 쓴다. step 을 주면 매 단위 작업(BUILD 회차·각 stage) 후 제어를 LLM 에게
돌려준다(모델 B):
hiloop_run({ goal, step: true })
└▶ ⏭ 스텝 완료 (PLAN) → LLM 이 진행을 알리고 같은 goal 로 재호출
└▶ ⏭ 스텝 완료 (DO) … └▶ ✅ 통과 / ❌ 실패 / ⏸ awaiting매 스텝이 콘솔에 보이고, 콜당 에이전트 1콜이라 타임아웃이 없다. 상태가 STATE.json 에 저장돼
재개는 자동이며, 통과 판정은 그대로 엄격하다(continue 는 통과가 아니다). flow 스킬이
대화형에서 이 모드를 기본으로 쓴다. CLI 는 --step(스텝 후 exit 3, 재실행으로 이어감).
무인·완료까지 자동은 step 을 빼면 모델 A 그대로다.
💸 비용은 엔진이 센다 — 그리고 싸지 않다
--max-loops 는 비용 상한이 아니다. 실패 루프가 10회를 다 쓰면 비용이 크게 뛸 수 있다.
그래서 --budget-usd 를 쓴다 — 누적이 상한에 닿으면 다음 에이전트 호출 전에 멈춘다.
hi-loop run --goal "..." --max-loops 5 --budget-usd 1 --no-stagnation
# [hi-loop] 💸 예산 $1 소진 (누적 $1.61) — 2회차에서 중단합니다.hi-loop status 와 완료 로그에 누적 비용이 찍힌다. 모델은 사용자 기본값을 상속하므로
HILOOP_AGENT_ARGS="--model sonnet" 으로 낮출 수 있다.
환경변수
| 변수 | 설명 |
|---|---|
| HILOOP_AGENT_CMD | 에이전트 실행 명령 (기본 claude) |
| HILOOP_AGENT_PROVIDER | 에이전트 CLI 계약 (기본 claude). generic 은 프롬프트를 마지막 위치인자로, stdout 을 결과로 읽는 최소 계약 — opencode 등 claude 외 CLI 를 일꾼으로 쓸 때. 단 세션 재개·비용 집계는 없다 |
| HILOOP_AGENT_ARGS | 에이전트에 덧붙일 인자 (공백 구분). 예: --model sonnet — 비용에 직결된다. generic 에선 프롬프트 앞 서브커맨드로 쓰임(예: run) |
| HILOOP_PERMISSION_MODE | 권한 모드 (기본 acceptEdits). 편집 허용이 이 엔진의 전제다 |
| HILOOP_VERIFY_CMD / HILOOP_VERIFY_MODEL | --verify-spec 검증자의 실행 명령·모델 (미설정 시 구현자와 동일). 검증에 더 센 모델을 쓸 수 있다 |
| HILOOP_REVIEW_CMD / HILOOP_REVIEW_MODEL | 설계·코드 리뷰어의 실행 명령·모델 (미설정 시 HILOOP_VERIFY_* → 구현자 순으로 폴백) |
| HILOOP_DISCOVER_CMD / HILOOP_DISCOVER_MODEL | 발굴 단계의 실행 명령·모델 |
| HILOOP_RECONCILE_CMD / HILOOP_RECONCILE_MODEL | 문서 정합 판정자(--reconcile)의 실행 명령·모델 (미설정 시 HILOOP_VERIFY_* → 구현자 순 폴백) |
| HILOOP_AGENT_TIMEOUT_MS / HILOOP_TEST_TIMEOUT_MS | 에이전트(30분)·테스트(10분) 타임아웃. 쓰레기값은 기본값으로 되돌림 |
| TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID | 시작/핸드오프/성공/실패 알림. 미설정 시 조용히 생략 |
개발
npm test # node:test, devDependency 0개구조
bin/hi-loop.js CLI 엔트리포인트 (CLI / MCP 분기 + rollback + answer + 단계별 실행)
bin/setup.js 프로젝트·클로드코드 설정 자동 주입
src/loop.js 라이프사이클 stage 머신 — 다음 stage 를 정하는 권한은 여기 한 곳뿐
src/build.js 안쪽 자가 치유 루프 — 예산·정체·무결성·2단 판정·핸드오프 집행
src/stages.js DISCOVER / CODE_REVIEW / SHIP / WATCH 핸들러 (지시만 반환)
src/discover.js DISCOVER — 요구사항 발굴, 가정 확정, 분기 감지
src/review.js DESIGN_REVIEW / CODE_REVIEW — 격리된 Tier 2 심판
src/ship.js SHIP / WATCH — 배포·헬스체크. 에이전트를 부르지 않는다
src/vcs.js 브랜치·커밋 메커니즘 — 프리스텝·COMMIT 스테이지·git log 스타일 감지
src/config.js .hi-loop.json 정책 저장소 — 브랜치·커밋 정책의 단일 진실 원천
src/ask.js ask 모드 — pause/resume 상태머신, 가정 기록
src/checks.js 다중 조건 검사 — --check/--when, short-circuit 없음
src/blast.js 블라스트 반경 — 변경의 위험 분류를 공개
src/report.js 완료 보고 — Gaps 공개 + 상태 요약
src/cli-options.js CLI 인자 → runLoop 옵션 번역 (잘못된 값은 조용히 흘리지 않는다)
src/state.js .hi-loop/STATE.json (원자적 저장 + 다이어트 + 산출물 경로 + resume + Gaps)
src/runners.js 에이전트/테스트 실행 어댑터 (주입 가능) + 비용 파싱 + 타임아웃
src/preflight.js 에이전트 실행 preflight + 실패 진단 (미설치·인증·키 → 행동지침)
src/reconcile.js 문서 정합 게이트 — 요청↔표준 문서 모순 판정 + ask 표면화 (FR-21)
src/cli-commands.js CLI 보조 명령 — answer / rollback / config (bin 은 라우팅만)
src/stage-context.js stage 핸들러 실행 맥락 조립
src/prompts.js PDCA 단계별 프롬프트 + 검증자 프롬프트
src/integrity.js 테스트 무결성 지문·비교 — 보상 해킹 방어
src/checkpoint.js git stash 체크포인트·롤백 + 삭제 관측
src/verify.js 스펙 오라클 — Tier 2 검증자
src/lock.js 동시 실행 pid 락
src/mcp-server.js MCP 프로토콜 통신
src/telegram.js 텔레그램 비동기 알림
src/args.js 의존성 없는 인자 파서
src/is-main.js "직접 실행인가?" 판정 (심링크 안전)