valkor-ai/loom. Claude Code·Codex·OpenCode 같은 이미 쓰고 있는 코딩 에이전트를 갈아치우지 않고, 그 바깥에 둘러 하나의 목표를 기획 → 설계 → 구현 → 검증 → 수리 → 프리뷰 → 인수인계라는 반복 가능한 루프로 만들어 주는 딜리버리 하네스(delivery harness)다. 모든 진행 상태(요구사항·태스크 계약·테스트 결과·수리 노트·인수인계 리포트)를 프로젝트 폴더의 .loom/ 아래 JSON 파일로 남겨, 다음 세션·다른 에이전트·다른 CLI가 처음부터 다시 시작하지 않고 이어받게 한다. (저장소: valkor-ai/loom · 언어 TypeScript 65% + JavaScript 35% · Node ≥ 20 · commander + zod · 라이선스 Apache-2.0 · ★ 약 100 — TrendShift 라이브 멘션)
"코딩 에이전트는 '코드를 쓸' 줄은 안다. Loom은 그 에이전트가 '약속한 결과물까지 끝까지 배달'하게 만든다."
이름 그대로 Loom(베틀)입니다. 보통 AI 코딩 에이전트에게 "이거 만들어 줘"라고 하면, 데모는 한 번 그럴듯하게 나옵니다. 그런데 그걸 믿고 출시할 수 있는 제품으로 만들려면 — 요구사항을 분명히 하고, 구조를 정하고, 일을 잘게 쪼개고, 구현하고, 검사하고, 실패를 고치고, 다시 검증하고, 미리보기로 확인하고, 증거를 모아 넘겨야 합니다. 에이전트는 긴 작업 중에 맥락이 압축(compaction)되면 길을 잃고, 절반만 하고 "다 됐다"고 선언하고, 자기 결과를 자기가 검사하며 편향됩니다. Loom은 그 위에 '상태를 가진 작업 지시 체계'를 둘러, 흩어진 실을 끊기지 않는 한 장의 천으로 엮습니다.
좀 더 구체적으로, Loom은 TypeScript로 만든 명령줄 도구(CLI)입니다. npm install 후 npm run plugin:install-claude(또는 codex/opencode)로 어댑터를 깔면, 에이전트 안에서 Codex는 @loom build ..., Claude Code·opencode는 /loom build ...처럼 부를 수 있습니다. 그러면 Loom이 그 프로젝트에 .loom/ 폴더를 만들고, "지금 이 에이전트가 다음에 해야 할 단 하나의 행동"을 계산해 돌려줍니다. 사용자는 대부분 continue(이어가기)만 반복해 부르면 됩니다 — 내부 단계 이름을 외울 필요가 없습니다.
이 문서가 파고드는 건 사용법이 아니라 그 안의 설계입니다 — "어떻게 에이전트가 멋대로 굴지 못하게 강제하나(지시 봉투 + 훅)", "왜 상태를 데이터베이스가 아니라 파일로 두나", "어떻게 한 번 검증한 걸 다시 안 믿고 또 검증하나". 에이전트 하네스·상태 머신·계약 기반 설계를 처음 공부하는 사람에게 살아 있는 교본입니다.
"2026년 에이전트 코딩의 진짜 난제는 '코드 생성'이 아니라 '끝까지 믿고 배달하기'다. Loom은 그 지점을 정조준한다."
"웹사이트·앱을 자동 생성"은 이제 기본기가 됐습니다. 더 어려운 문제는 신뢰할 수 있는 배달입니다 — 맥락이 압축돼도 정렬을 유지하고, 수십 턴을 거쳐도 요구사항을 보존하고, 자기 일을 편향 없이 검증하고, 실패를 고치고, 중단된 지점부터 정확히 재개하기. README는 이 붕괴를 다섯 가지 실패 모드로 정리하고, 각각에 Loom의 대응을 붙입니다.
| 실패 모드 | 증상 | Loom의 대응 |
|---|---|---|
| 부분 완성 (partial completion) | 절반만 해 놓고 "다 됐다"고 선언 | 바운디드 태스크 · 명시적 결과 파일 · continue 라우팅 · 최종응답 가드로 조기 종료 차단 |
| 목표 표류 (goal drift) | 턴이 길어지며 원래 목표에서 벗어남 | 확정된 범위 · 아키텍처 계약 · 태스크 계획 · 압축 컨텍스트 팩으로 목표 보존 |
| 자기검증 편향 (self-check bias) | 만든 사람이 만든 걸 검사 → 통과시켜 줌 | 리뷰 · 검증 · 수리 요청 · 증거 기록으로 구현과 검증을 분리 |
| 토큰 낭비 (token waste) | 매 턴 레포 전체를 다시 읽음 | 프로젝트 요약 · 태스크 그래프 · 런타임 상태 · 테스트 결과를 저장해 재독 감소 |
| 인수인계 공백 (handoff gaps) | "끝났다"는데 무엇이 어떤 상태인지 모름 | 배달 리포트 · 프리뷰 점검 · 로그 · 수리 이력으로 최종 상태를 사람·에이전트가 들여다보게 |
긴 작업에서 가장 흔한 실패는 테스트가 통과했거나 소스 수정이 끝났다는 이유로 "완료"를 선언하는 것입니다. 하지만 그건 완료가 아닙니다. Loom의 어댑터 규약(SKILL.md)은 못 박습니다: "통과한 테스트, 끝난 소스 수정, 내부 할 일 목록, 다음 태스크가 보이는 것 — 어느 것도 완료가 아니다. 결과 파일이 resultFile 위치에 존재하고 submitCommand가 성공해야만 완료다."
흔한 접근은 CLAUDE.md·AGENTS.md·.cursorrules 같은 거대한 프롬프트 파일입니다. 유용하지만 점점 길어지고, 상태가 없어 중단되면 처음으로 돌아갑니다. Loom은 그 위에 ① 상태 저장(.loom/), ② 에이전트-중립 CLI, ③ 검증·수리·프리뷰·인수인계를 1급(first-class) 단계로 더합니다. 즉 "무엇을 하라"는 글이 아니라 "지금 무엇을 했고, 다음에 무엇을 해야 하는지"를 기억하는 기계입니다.
"에이전트 하네스·스킬"은 2026년 가장 붐비는 카테고리입니다(superpowers, gstack, ECC, loop-library 등 이미 이 폴더에 여럿 정리돼 있습니다). 그 속에서 Loom의 자리는 분명합니다.
| 관점 | Loom | 프롬프트 파일 방식 (CLAUDE.md / .cursorrules) |
|---|---|---|
| 형태 | 상태를 가진 CLI 프로토콜 | 정적 텍스트 프롬프트 |
| 중단 후 재개 | .loom/ 상태로 이어받기 | 맥락이 날아가면 처음부터 |
| 검증 | 구현과 분리된 리뷰·수리·증거 | 에이전트 자기 판단에 의존 |
| 에이전트 호환 | 중립 — Claude Code·Codex·opencode 동일 프로토콜 | 도구마다 따로 작성 |
| 조기 완료 | 가드 플래그로 강제 차단 | 막을 장치 없음 |
| 증거 | 배달 리포트·로그·프리뷰로 검사 가능 | 대화 로그뿐 |
요약하면 Loom의 셀링 포인트는 넷입니다. ① 상태의 영속성(중단·압축·에이전트 교체를 견딤), ② 에이전트-중립(하나의 배달 프로토콜을 여러 에이전트에), ③ 검증을 1급으로(구현자와 검증자를 분리, "wired 증거"까지 요구), ④ 행동 강제(지시 봉투의 가드 플래그 + Claude Code 훅이 에이전트가 멋대로 기획·조기종료·잡담하는 것을 막음)입니다.
맨몸 에이전트 = 재능 있지만 산만한 신입. 시키면 잘 만들지만, 길어지면 처음 요구를 잊고, "대충 됐죠?"라며 절반에서 손을 떼고, 자기가 짠 코드를 자기가 "이상 없음" 처리합니다.
Loom = 그 신입에게 붙은 빈틈없는 출하 관리자(PM). "지금 단계는 이거, 다음은 이거"를 칠판(.loom/)에 적어 두고, "결과 파일 제출 전엔 완료 아님", "네가 짠 건 다른 단계에서 검사함", "버튼이 진짜로 API를 부르는지 증거 가져와"라고 못 박습니다. 관리자가 자리를 비워도(세션 종료) 칠판이 남아 다음 사람이 이어받습니다.
솔직하게 짚겠습니다. README의 벤치마크는 11개 케이스에서 "Codex + Loom이 Codex 단독 대비 토큰 15.8% 절감, 완료율은 둘 다 100% 유지"(468,067 → 394,037 토큰)라고 밝힙니다. 하지만 이건 개발팀이 자체 실행한 수치이고, 11개 중 8개에서만 Loom이 더 쌌으며 3개는 오히려 토큰을 더 썼습니다(백엔드 준비 +9.9% 등). 게다가 완료율 향상이 아니라 '이어가기·인수인계 작업의 토큰 효율'이 이득의 본질입니다(원래도 둘 다 100% 완료). 또 아직 프로덕션 배포는 지원하지 않고(로컬 Docker Compose 프리뷰만), 별 약 100개의 신생 프로젝트입니다. 방향성은 설득력 있지만 — "상태·검증·재개를 1급으로 둔다"는 발상은 에이전트 작업이 길어질수록 더 중요해지는 정공법 — 수치는 작업·레포에 따라 편차가 큼을 전제로 보셔야 합니다.
"코드의 65%가 TypeScript다. 하지만 진짜 핵심은 'zod로 짠 계약'과 '에이전트가 따를 수밖에 없는 JSON 지시 봉투'에 있다."
Loom은 화면도 GPU도 데이터베이스도 없는 순수 Node.js CLI 엔진입니다. 언어 통계는 TypeScript 65.1% / JavaScript 34.9%인데, TypeScript는 src/의 엔진(약 5만 줄·106개 파일)이고 JavaScript는 어댑터 접착 코드·설치 스크립트·테스트 하네스입니다. 무게중심이 "상태를 안전하게 정의·검증·라우팅하는 코어"에 완전히 쏠려 있습니다.
| 기술 | 역할 | 한 줄 설명 |
|---|---|---|
| TypeScript (ES2022, strict) | 전체 엔진 언어 | tsc로 dist/cli.js를 빌드. strict:true로 타입을 빡빡하게 잠근다. |
| commander ^12 | CLI 프레임워크 | init/status/plan/continue/review/deploy/knowledge… 40여 개 하위 명령·플래그를 만든다. 모든 명령이 --project-root --json --compact 지원. |
| zod ^3 | 스키마 · 검증 | 이 프로젝트의 심장. 설정·계약·태스크·리뷰 등 모든 아티팩트를 zod 스키마로 정의하고 런타임 검증한다. schemaVersion으로 버전까지 관리. |
| yaml ^2 | 설정 파싱 | 사람이 읽기 좋은 설정/참조 문서 처리. |
| Node.js ≥ 20 | 런타임 | 외부 서비스 0. 상태는 전부 로컬 파일(JSON). |
z.object({...})), 실행 중에 들어온 값이 그 모양인지 검사하고 동시에 타입까지 뽑아 줍니다. Loom처럼 에이전트가 만든 JSON(계획·태스크·리뷰 결과)을 받아 다루는 도구에서는, "에이전트가 형식을 어긴 산출물"을 즉시 잡아내는 방어선이 됩니다. Loom의 contracts.ts(약 2,000줄)·schemas.ts가 전부 zod로 짜여 있습니다.| 기술 | 역할 |
|---|---|
| pdf-parse | PDF 문서에서 텍스트 추출 — 요구사항·스펙 PDF를 읽어 들인다. |
| mammoth | Word(DOCX) 문서를 텍스트로 변환. |
| yauzl | zip 압축 해제 — 문서 묶음 입력 처리. |
이 셋은 loom knowledge 명령군을 떠받칩니다. 사용자가 PDF·DOCX·zip로 된 자료를 주면 Loom이 로컬에서 파싱·색인해, 에이전트가 배달 중 참고할 수 있는 지식으로 만듭니다(외부 API·임베딩 서비스 없이).
| 어댑터 | 호출 방식 | 설치되는 것 |
|---|---|---|
| Claude Code | /loom … | 플러그인 패키지 · skills/loom/SKILL.md · 훅(loom-workflow-guard.js) · 런처 |
| Codex | @loom … | Codex 로컬 플러그인 + 공유 런처 |
| opencode | /loom … | 슬래시 명령 · 플러그인 훅 · 참조 문서 · 런처 |
| (공통) | — | ~/.loom/bin/loom-cli(안정 런처) · ~/.loom/adapters/<agent> 메타데이터 |
npm run plugin:install-*가 ~/.loom/bin/loom-cli라는 작은 Node 실행기를 깔아 두고, 모든 어댑터가 셸 PATH에 loom이 있든 없든 이 런처를 통해 같은 dist/cli.js를 부릅니다. 그래서 "한 번 빌드한 엔진"을 여러 에이전트가 공유합니다.| 항목 | 내용 |
|---|---|
| Docker + Compose | loom deploy가 쓰는 로컬 프리뷰 수단. 타깃 프로젝트용 Dockerfile/Compose를 Loom이 생성해 빌드·기동·헬스체크·로그·수리까지 본다. (프로덕션 배포는 아직 미지원) |
| 빌드 | tsc -p tsconfig.json && chmod +x dist/cli.js. bin은 "loom": "./dist/cli.js". |
| 테스트 | tests/ 아래 architecture·brainstorm·deploy·knowledge·protocol·task·review 등 ~13개 스위트. node tests/run-suite.js <suite>로 실행. |
| 저장소 자신엔 Dockerfile 없음 | Loom은 남의 프로젝트를 위한 컨테이너 구성을 만들지, 자기 것을 싣지 않는다. |
"핵심은 단 하나의 발상 — 에이전트는 스스로 순서를 정하지 않는다. CLI가 '다음 한 수'를 적힌 봉투로 건네고, 에이전트는 그대로 둔다."
한 바퀴는 이렇게 돕니다. ① 사용자가 에이전트 안에서 /loom build … 또는 /loom continue를 부르면, ② continue 라우터가 .loom/의 저장 상태를 읽어 "지금 활성 단계에서 다음에 할 단 하나의 route action"을 정합니다. ③ 그 route action을 실제 CLI 하위명령으로 매핑하고, ④ 에이전트가 따라야 할 지시 봉투(instruction envelope)를 JSON으로 돌려줍니다. ⑤ 에이전트는 봉투대로 실행→결과 파일 기록→제출하고 다시 continue를 부릅니다. ⑥ Claude Code에서는 훅이 에이전트의 네이티브 기획 모드를 막아, Loom의 순서를 우회하지 못하게 합니다.
가장 핵심적인 발상입니다. 보통 에이전트는 "무엇을 할지"를 스스로 정합니다. Loom은 그 자유를 의도적으로 줄입니다. CLI가 돌려주는 봉투에는 단순 데이터만 있는 게 아니라 행동을 강제하는 플래그가 가득합니다.
// 지시 봉투(CliSuccessEnvelope)의 actionRequired — 행동 강제 플래그
actionRequired: {
mode: "execute_task", // 지금 모드(태스크 실행/후보 생성/리뷰…)
autoContinue: true, // 멈추지 말고 이어가라
mustRunImmediately: true, // 즉시 실행(설명부터 늘어놓지 마라)
mustNotReportProgress: true, // "진행 중입니다" 같은 잡담 금지
mustNotAskBeforeExecuting: true,
resultFile: ".loom/.../result.json", // 결과는 반드시 여기에
submitCommand: "loom record-result …", // 이걸 성공시켜야
completionBarrier: "...", // 이 장벽을 넘기 전엔 완료 아님
finalResponseGuard: "...", // 조기 "다 됐다" 차단
forbiddenStops: [...], // 여기서 멈추면 안 됨
stopOnlyWhen: ["user_gated","blocked","done","failed"]
}
이 플래그들이 2장에서 본 "다 됐어요 함정"과 "잡담·조기 질문"을 데이터 차원에서 막습니다. 어댑터의 SKILL.md와 Claude Code 훅이 이 봉투를 읽어 강제 집행합니다.
.loom/Loom은 데이터베이스를 쓰지 않습니다. 한 배달의 모든 것이 .loom/deliveries/<deliveryId>/ 아래 버전드 JSON으로 쌓입니다. 폴더 이름만 봐도 파이프라인이 그대로 읽힙니다.
| 단계(폴더) | 무엇을 담나 |
|---|---|
| requirements/ | 요구사항 컨텍스트 · 정규화 텍스트 · 키워드 힌트 · 입력/추출 문서 |
| brainstorms/ | 요구사항 명료화(질문/결정/확정) — 무엇을 만들지 합의 |
| contracts/ (PGC) | 기술 베이스라인 · 기획 계약(Planning Contract) |
| artifacts/architecture/ (AAC) | 아키텍처 계약(Architecture Artifact Contract) · 섹션 · 세션 |
| tasks/<phase>/ | 태스크 계획 · 실행 요청 · 실행(runs) · 결과(results) |
| reviews/ · repairs/ · decisions/ | 리뷰 패킷/결과 · 수리 요청 · 사용자 결정 게이트 |
| operations/active-lease.json | 동시성 락 — 상태 변경 작업이 둘 동시에 못 돌게 |
건축 현장의 '현장 사무소 캐비닛'을 떠올리세요. 설계 승인서(기획 계약), 구조 도면(아키텍처 계약), 작업 지시서(태스크), 검측 보고서(리뷰), 하자 보수 요청(수리)이 칸칸이 정리돼 있습니다. 담당자가 바뀌어도(에이전트 교체·세션 종료) 새 사람이 캐비닛만 열면 어디까지 됐는지를 압니다. active-lease는 "한 캐비닛을 두 사람이 동시에 못 헤집게" 하는 자물쇠고요.
Loom의 단계는 코드 안에 열거형(enum)으로 박혀 있습니다. continue 라우터는 저장 상태를 보고 이 중 하나를 "다음 행동"으로 반환합니다.
// src/core/schemas.ts — 배달 단계 = route action
routeAction = [
"brainstorm_start", "brainstorm_clarification", "brainstorm_confirmation",
"technical_baseline_request", "repository_context_request",
"planning_contract_create", "architecture_artifact_contract",
"taskplan_generation", "continue_execution", "review",
"execution_repair", "task_result_repair", "taskplan_repair",
"architecture_artifact_repair",
"needs_user_decision", "manual_review", // ← 사람이 개입하는 두 게이트
"continue_to_next_phase", "done"
]
한 배달(delivery)은 여러 단계(phase)의 연속이고, 각 단계가 위 파이프라인을 한 바퀴 돕니다. continue_to_next_phase는 그 단계의 리뷰가 승인된 뒤 다음 단계로 넘깁니다. needs_user_decision·manual_review는 자동으로 못 넘기는 지점에서 사람을 부르는 두 개의 게이트입니다 — 에이전트가 임의로 결정하지 않도록.
Loom은 큰 목표를 바운디드 태스크(bounded task)로 쪼갭니다. 각 태스크는 그냥 할 일이 아니라 계약입니다 — 건드려도 되는/안 되는 경로, 수용 기준, 검증 의도가 명시됩니다.
// src/core/contracts.ts — 태스크 계약(요약)
task = {
taskId, title, objective,
scopeRefs, acceptanceRefs, // 범위·수용 기준
writeBoundary: {
forbiddenPaths: [...], // 이 태스크가 건드리면 안 되는 파일
},
verificationIntents: [...], // 무엇을 어떻게 검증할지
}
여기에 Loom의 가장 까다로운 가드가 붙습니다. workflow-closure는 UI가 실제로 작동(wired)하는지를 증거로 요구합니다 — 정적·목업 화면은 통과 불가.
// src/core/workflow-closure.ts — "버튼이 진짜로 작동하는가"
requiredDataBindingMode: "wired", // 실제 연결만 인정
staticModePolicy: "not_satisfied", // 정적/목업은 불통과
requiredEvidence: [
"user_action", // 사용자가 실제로 눌렀다
"declared_interface_invocation", // 선언한 API가 실제로 호출됐다
"state_or_persistence_change", // 상태/저장이 실제로 바뀌었다
"success_or_blocking_feedback", // 성공/실패 피드백이 떴다
]
AI가 만든 화면은 그럴듯해 보여도 버튼이 아무것도 안 하는 경우가 흔합니다(목업). Loom은 그걸 "완료"로 인정하지 않으려고 위처럼 "사용자 행동 → API 호출 → 상태 변화 → 피드백"의 네 증거를 요구합니다. 또 runtime-delivery-closure는 빌드→기동→프리뷰 체인이 코드 레벨에서 실제로 도는지 최종 확인하는 태스크를 강제합니다. 이게 "데모에서 배달로(From Demo to Delivery)"라는 README 슬로건의 실체입니다.
봉투와 SKILL.md만으로는 에이전트가 규약을 어길 여지가 남습니다. 그래서 Claude Code 어댑터는 훅(plugins/claude-code/hooks/loom-workflow-guard.js)을 PreToolUse·UserPromptSubmit·Stop 시점에 겁니다. 핵심은 Loom 작업이 활성일 때 Claude Code의 네이티브 Plan Mode(EnterPlanMode/ExitPlanMode)를 막는 것 — 에이전트가 자기 식 기획으로 갈아타 Loom의 순서를 우회하지 못하게 하는 하드 집행 계층입니다.
PreToolUse 훅은 에이전트가 도구(예: Bash, Plan Mode)를 쓰기 직전에 실행돼, 그 행동을 막거나 바꿀 수 있습니다. Loom은 이를 이용해 "지금은 Loom 루프 안이니 네 마음대로 기획하지 마"를 코드로 강제합니다. 프롬프트로 부탁하는 것과 달리, 훅은 거부권을 가집니다."src/는 보이지 않는 엔진, plugins/는 에이전트가 실제로 보는 얼굴. 둘을 런처가 잇는다."
src/ → dist/cli.js)을 만들고, 여러 어댑터(plugins/)가 각 에이전트의 말투(/loom vs @loom)로 그 엔진을 부르게 합니다. 비유하면 엔진은 '발전소', 어댑터는 나라마다 다른 '콘센트 모양'입니다. 새 에이전트를 지원하려면 어댑터 하나만 추가하면 되고, 배달 로직(엔진)은 건드리지 않습니다. 이것이 '에이전트-중립'의 구현 방식입니다.| 파일 | 줄 수(약) | 역할 |
|---|---|---|
| core/operations/tasks.ts | 4,900 | 태스크 계획 생성 · next-task 라우팅 · 결과 기록 · 완료 장벽 |
| core/operations/brainstorm.ts | 4,100 | 요구사항 명료화 엔진(질문 블록·후보 생성) |
| core/operations/contracts.ts | 4,000 | 기획/아키텍처/베이스라인 계약의 요청·승인 라이프사이클 |
| core/validators.ts | 2,400 | 교차 검증 → 이슈를 수리 가능/사용자 결정 필요/차단으로 분류 |
| core/operations/continue.ts | 2,100 | 라우터 — 상태를 보고 다음 route action 결정, command→argv 매핑 |
| core/operations/repository-context.ts | 2,000 | 압축 레포 컨텍스트 팩 생성(토큰 절감의 실체) |
무게중심이 operations/(특히 continue·tasks·brainstorm)에 쏠려 있다는 건, 이 프로젝트가 결국 "상태를 보고 다음 한 수를 정하는 라우팅"과 "요구사항을 계약으로 굳히는 일"에 가장 많은 코드를 쓴다는 뜻입니다.
"Loom 한 레포 안에 에이전트 하네스 설계·계약 기반 프로그래밍·파일 상태 머신·플러그인/훅이 다 들어 있다."
배울 것: "에이전트에게서 판단을 덜어내고, 다음 행동을 데이터로 건넨다"는 발상. 봉투의 finalResponseGuard·completionBarrier·stopOnlyWhen이 어떻게 '조기 완료'와 '딴짓'을 막는지. LLM을 제어 가능한 부품으로 다루는 컨트롤 이론에 가깝습니다.
실습: 작은 CLI를 만들어, 호출할 때마다 { instruction, mustRunImmediately, stopOnlyWhen } 같은 JSON을 돌려주게 하고, 그 JSON을 Claude Code/Codex에 "그대로 따르라"고 물려 보기. 가드 플래그를 켰을 때와 껐을 때 행동 차이를 관찰.
배울 것: 에이전트가 만든 JSON을 믿지 않고 검증하는 법. schemaVersion으로 포맷을 진화시키는 법(구버전 산출물 호환). 타입과 런타임 검증을 한 소스로 묶는 zod의 힘.
실습: zod로 "태스크 계약" 스키마(writeBoundary.forbiddenPaths, acceptanceRefs 포함)를 정의하고, 일부러 형식을 어긴 JSON을 넣어 검증이 어떻게 막는지 확인. schemaVersion: 1 → 2 마이그레이션도 흉내 내 보기.
배울 것: 40여 개 하위 명령을 --json·--compact·--project-root 같은 공통 플래그로 일관되게 묶는 법. 에이전트가 파싱하기 좋은 compact 출력 vs 사람이 읽는 출력의 분리.
실습: commander로 mytool plan / continue / review 세 명령을 만들고, --json이면 기계용 JSON을, 아니면 사람용 표를 출력하게 분기. LOOM_COMPACT_OUTPUT=1처럼 환경변수로 모드를 바꾸는 패턴도 따라 하기.
배울 것: DB 없이 폴더+JSON으로 상태 머신을 만드는 법. state/paths.ts처럼 "경로를 한 곳에서만 정의"해 흩어지지 않게 하는 규율, active-lease.json 같은 파일 락으로 동시성 다루기.
실습: ./.mini/<id>/state.json에 "현재 단계"를 저장하고, continue를 부를 때마다 다음 단계로 넘어가는 미니 워크플로를 만들기. 프로세스를 강제 종료한 뒤 다시 continue로 이어지는지 확인(재개 가능성의 핵심).
배울 것: commands/*.md(슬래시 명령), skills/*/SKILL.md(행동 규약), hooks/*.js(PreToolUse/Stop 거부권)의 3종 세트. 프롬프트로 "부탁"하는 것과 훅으로 "강제"하는 것의 차이.
실습: 아주 단순한 Claude Code 훅을 작성해 특정 명령(예: 특정 파일 쓰기)을 PreToolUse에서 막아 보기. Loom의 loom-workflow-guard.js가 Plan Mode를 막는 방식이 정답지.
배울 것: 한 엔진을 여러 에이전트가 공유하게 하는 런처 + 어댑터 구조. ~/.loom/bin/loom-cli처럼 PATH에 의존하지 않는 안정 실행기를 두는 이유(에이전트마다 셸 환경이 다름).
실습: "엔진 1개 + 어댑터 2개(가짜 에이전트 A/B)" 구조를 만들어, 두 어댑터가 같은 ~/.mytool/bin/cli를 부르게 하기. 어댑터별 메타데이터를 ~/.mytool/adapters/<agent>에 남기는 것까지.
배울 것: "데모가 아니라 배달"을 코드로 강제하는 법 — workflow-closure의 wired 증거 4종. 그리고 레포를 매번 다시 읽지 않게 압축 컨텍스트 팩을 만들어 두는 토큰 경제.
실습: "버튼 클릭 → API 호출 → 상태 변경 → 피드백" 4단계 증거가 모두 있어야만 PASS를 주는 미니 검증기를 작성. 그다음 같은 기능을 (a) 목업 (b) 실제 연결로 만들어 검증기가 (a)를 떨어뜨리는지 확인.
"GPU·클라우드·DB 전부 필요 없다. Node와, 네가 이미 쓰는 에이전트 CLI와, (배포 프리뷰엔) Docker만 있으면 된다."
| 항목 | 요구사항 / 메모 |
|---|---|
| 런타임 | Node.js ≥ 20 + npm. 엔진은 순수 TypeScript→JS라 별도 런타임 의존성 없음. |
| 에이전트 CLI | 설치할 어댑터에 맞는 CLI가 있어야 함 — Codex CLI / Claude Code CLI / opencode CLI 중 하나. |
| Docker (선택) | loom deploy(로컬 Docker Compose 프리뷰)에만 필요. 배달의 코어 루프엔 불필요. |
| 외부 서비스 | 없음. 데이터베이스·벡터DB·클라우드 API 불필요. 상태는 전부 .loom/ 로컬 파일. |
| GPU | 불필요. 모델 추론은 네가 쓰는 에이전트가 담당하고, Loom은 그 바깥의 하네스일 뿐. |
| 디스크/메모리 | 가벼움. 상태 JSON과 컨텍스트 팩 위주라 부담이 작다. |
| OS | 크로스 플랫폼(Node 동작 환경). 런처는 ~/.loom/bin/loom-cli에 설치. |
어댑터를 깐 뒤에는 반드시 새 에이전트 세션을 열어야 갱신된 로컬 플러그인이 로드됩니다. 스모크 체크는 "$HOME/.loom/bin/loom-cli" --version과 status로 합니다. 아직 Loom을 안 쓴 프로젝트에서 status가 STATE_NOT_INITIALIZED를 내는 건 정상입니다 — "런처는 잘 돌고, 배달이 아직 시작 안 됨"이라는 뜻이지 오류가 아닙니다. 또 보통 loom init을 손으로 칠 필요는 없습니다 — /loom build …가 필요할 때 .loom/을 알아서 초기화합니다.
"설치해 스모크 → 작은 기능을 굴려 .loom/ 들여다보기 → 봉투 해부 → 미니 하네스 만들기, 단계별로."
git clone → npm install → npm run plugin:install-claude(또는 codex/opencode). 그다음 "$HOME/.loom/bin/loom-cli" --version과 빈 프로젝트에서 status를 돌려 STATE_NOT_INITIALIZED가 나오는지 확인.
목표: 런처·어댑터가 제대로 깔렸는지 감 잡기. STATE_NOT_INITIALIZED가 "정상 스모크 결과"임을 이해하는 게 포인트.
.loom/를 해부하기샘플 프로젝트에서 /loom build "간단한 방명록"을 시작한 뒤, 몇 번 /loom continue를 부르며 .loom/deliveries/<id>/ 아래에 requirements/·brainstorms/·tasks/ 폴더가 어떻게 채워지는지 직접 열어 본다.
목표: 추상적인 "단계"가 실제 파일·폴더로 어떻게 떨어지는지 눈으로 확인. index.json의 nextAction이 바뀌는 걸 추적해 보기.
런처로 직접 loom-cli continue --project-root /경로 --json을 호출해, 돌아오는 actionRequired JSON을 통째로 들여다본다. mode·resultFile·submitCommand·finalResponseGuard·stopOnlyWhen이 각각 무엇을 시키는지 주석을 달아 본다.
목표: 에이전트가 "왜 그렇게 행동하는지"의 근원이 이 봉투임을 체득. 같은 상태에서 단계가 바뀌면 봉투가 어떻게 달라지는지 비교.
zod로 "태스크 계약" 스키마를 정의하고, continue 라우터(저장된 단계를 보고 다음 명령을 반환)를 흉내 낸 작은 CLI를 작성한다. 상태는 ./.mini/<id>/state.json에 저장하고, 중단 후 재개되는지 확인.
목표: Loom의 뼈대(상태→라우팅→봉투→실행→기록→반복)를 100줄 규모로 재현. "에이전트 없이도 하네스의 골격은 순수 로직"임을 깨닫기.
(a) plugins/ 구조를 읽고 가상의 새 에이전트용 어댑터(슬래시 명령 + 런처 연결)를 추가하거나, (b) workflow-closure의 4종 증거(user_action·interface_invocation·state_change·feedback)를 실제로 수집·판정하는 검증기를 구현한다.
목표: 에이전트-중립 설계 또는 "데모→배달" 검증의 본질을 손으로 구현. 둘 다 Loom이 풀려는 핵심 난제다.
"TypeScript CLI에서 출발해 계약 기반 상태 머신·에이전트 하네스·플러그인까지, Loom을 길잡이 삼아."
| 주차 | 주제 | 핵심 학습 + Loom 연결점 |
|---|---|---|
| 1주 | TypeScript + commander CLI | 타입·strict 모드, commander로 하위명령·플래그, --json/--compact 분리 → src/cli.ts·commands/ |
| 2주 | zod 계약 + 파일 상태 | 스키마 선언·런타임 검증·schemaVersion, 폴더+JSON 상태 → core/schemas.ts·contracts.ts·state/paths.ts |
| 3주 | 에이전트 하네스 개념 | 지시 봉투·라우팅·완료 장벽·가드 플래그 → core/operations/continue.ts + commands/types.ts |
| 4주 | Claude Code 플러그인/훅/스킬 | 슬래시 명령·SKILL.md·PreToolUse 훅의 거부권 → plugins/claude-code/ |
| 5주 | 검증 루프 + 토큰 절감 | 구현/검증 분리, wired 증거, 압축 컨텍스트 팩 → workflow-closure.ts·repository-context.ts |
| 6주 | 멀티 에이전트 + 배포 프리뷰 | 런처·어댑터 패턴, Dockerfile/Compose 생성·기동 → scripts/lib·core/deployment/ |
이 로드맵은 "CLI 기초 → 데이터 계약 → 제어(하네스) → 집행(훅) → 품질(검증) → 확장(어댑터)"을 한 줄로 꿴 코스입니다. 'LLM을 어떻게 프롬프트하나'가 아니라 'LLM을 어떻게 신뢰할 수 있는 시스템의 부품으로 가두나'를 배우는 길이라, 에이전트 시대의 소프트웨어 공학 그 자체를 실전 코드로 복습하는 셈입니다.
"이 문서와 저장소에서 반복되는 용어를 한곳에 모았다."
| 용어 | 의미 |
|---|---|
| 딜리버리 하네스 (delivery harness) | 모델·에디터를 갈아치우지 않고 그 바깥에 둘러, 출하 과정 전체를 반복·검증·재개 가능하게 만드는 골격. Loom의 정체 |
| 동적 워크플로 (dynamic workflows) | 목표 성격에 따라 거쳐야 할 단계(경로)를 그때그때 고르고, 그 경로를 상태로 굳히는 방식 |
| 지시 봉투 (instruction envelope) | CLI가 에이전트에게 "다음 한 수"를 건네는 JSON. mode·resultFile·submitCommand + 강제 플래그를 담는다 |
| finalResponseGuard / completionBarrier | "절반에서 다 됐다고 선언"을 막는 가드 — 결과 파일 존재 + 제출 성공 전엔 완료 불가 |
| stopOnlyWhen / mustNotReportProgress | "여기서만 멈춰라(사용자게이트·차단·완료·실패)" / "진행 중 잡담 금지" 등 행동 강제 플래그 |
| .loom/ (상태 디렉토리) | 한 배달의 모든 산출물을 담는 프로젝트-로컬 JSON 폴더. 진실의 원천(source of truth) |
| route action | 배달의 '단계'를 나타내는 열거형. continue 라우터가 상태를 보고 다음 값을 반환 |
| delivery / phase | 배달=목표 하나 / phase=그 안의 단계. 한 배달은 여러 phase의 연속, 각 phase가 파이프라인 1회전 |
| brainstorm | 무엇을 만들지 합의하는 요구사항 명료화 단계(질문→결정→확정) |
| PGC (Planning Contract) | 기획 계약 — 무엇을 어떤 범위로 할지 굳힌 문서 |
| AAC (Architecture Artifact Contract) | 아키텍처 계약 — 구조·인터페이스·화면 흐름을 굳힌 문서 |
| 태스크 계약 (task contract) | 바운디드 태스크. writeBoundary(forbiddenPaths)·acceptanceRefs·verificationIntents 포함 |
| writeBoundary / forbiddenPaths | 한 태스크가 건드려도 되는/안 되는 파일 경계 — 범위 이탈 방지 |
| workflow-closure (wired 증거) | UI가 실제로 작동하는지 4종 증거(행동·API호출·상태변화·피드백)를 요구. 정적/목업 불통과 |
| runtime-delivery-closure | 빌드→기동→프리뷰 체인이 실제로 도는지 코드 레벨로 최종 검증하는 강제 태스크 |
| needs_user_decision / manual_review | 자동으로 못 넘기는 지점에서 사람을 부르는 두 게이트 |
| active-lease.json | 상태 변경 작업이 둘 동시에 못 돌게 하는 파일 기반 동시성 락 |
| 에이전트-중립 (agent-neutral) | 같은 배달 프로토콜을 Claude Code·Codex·opencode에 동일 적용 |
| 어댑터 / 런처 (launcher) | 어댑터=에이전트별 입구(/loom vs @loom) / 런처=PATH에 안 기대는 안정 실행기 ~/.loom/bin/loom-cli |
| 훅 (hook) · PreToolUse | 에이전트 행동 직전에 끼어들어 막거나 바꾸는 콜백. Loom은 네이티브 Plan Mode를 차단 |
| 컨텍스트 팩 (context pack) | 레포를 매 턴 다시 읽지 않게 미리 만들어 둔 압축 요약 — 토큰 절감의 핵심 |
| compaction (압축) | 대화가 길어지면 맥락을 줄이는 과정. 이때 정렬이 깨지는 걸 Loom의 상태가 보완 |
| zod / commander | 스키마+런타임 검증 라이브러리 / Node CLI 프레임워크 — Loom 엔진의 두 기둥 |