지식위키

FBZ_70 — 설치 & 플러그인 구조

FBZ_70 — 설치 & 플러그인 구조

한 줄 요약

fablize는 “플러그인 설치만 하면 기본 라우팅이 켜지고, 원하면 한 번 더 셋업해서 모든 세션에 규범을 상주(always-on)“시키는 2단계 설치 방식을 쓴다. 왜 배우나 — 좋은 하네스는 기능만큼이나 **설치 경험(DX)**이 중요하다. fablize가 “사용자 기존 설정을 절대 안 망가뜨리면서 깔끔하게 설치·해제”하는 패턴은 그대로 따라 쓸 수 있는 모범 사례다.

그림

flowchart TD
    A["마켓플레이스 add → install"] --> B["router.sh 자동 등록<br/>(설치 즉시 라우팅 가동)"]
    B --> C{"always-on도 켤까?"}
    C -->|"첫 실행 시 1회 질문"| D["Local / Global 선택"]
    D --> E["setup.sh 실행"]
    E --> E1["① CLAUDE.md 백업"]
    E1 --> E2["② 운영 블록 멱등 주입"]
    E2 --> E3["③ progress.json 상태 기록"]
    E3 --> E4["④ star.sh 자동 스타<br/>(fail-silent)"]
    E4 --> F["다음 세션부터 규범 상주"]
    C -->|"안 켜도 됨"| G["기본 라우팅만으로 동작"]
    F --> H["uninstall.sh + /plugin uninstall<br/>로 깔끔히 해제"]

쉽게 풀기

스마트폰 앱에 비유하면 — fablize 설치는 “앱 설치”와 “알림 권한 켜기” 두 단계로 나뉜다.

  1. 앱 설치 = 플러그인 install (자동) /plugin install fablize 한 번이면 끝. 이때 fablize의 hooks/hooks.json이 라우터 훅(router.sh)을 알아서 등록한다. 사용자가 손댈 게 없고, 곧바로 자동 라우팅이 켜진다. 핵심은 이 과정이 사용자의 settings.json을 전혀 건드리지 않는다는 점이다. 훅 등록은 fablize 자기 파일(hooks.json)이 책임지므로, 사용자가 이미 써 둔 다른 훅·설정이 망가질 위험이 없다.

  2. 알림 권한 켜기 = always-on 셋업 (선택, 1회) “모든 작업에서 항상 fablize 규범을 적용”하고 싶으면 setup.sh를 한 번 돌린다. 이게 하는 일은 CLAUDE.md(클로드가 매 세션 읽는 운영 지침서)에 fablize 운영 블록을 끼워 넣는 것이다.

이때 설치가 지키는 4가지 안전장치가 핵심이다.

  • 백업 먼저 — 블록을 넣기 전에 CLAUDE.md*.fablize-bak.<시각>으로 복사해 둔다. 잘못돼도 되돌릴 수 있다.
  • 멱등(여러 번 해도 한 번과 같음)<!-- FABLIZE:BEGIN ... END --> 마커를 표시로 삼아, 다시 셋업하면 옛 블록을 지우고 새 블록을 넣는다. 두 번 실행해도 블록이 두 개로 늘어나지 않는다.
  • 의존성 0 — 추가 설치 없이 bash + python3만 있으면 돈다.
  • 상태 기억~/.fablize/progress.json에 “셋업 완료” 표시를 남겨, 다음부터 “셋업할까요?”를 다시 묻지 않는다.

마지막으로 셋업이 끝나면 star.sh가 GitHub에서 레포에 별(스타)을 자동으로 누른다. 단, 이건 절대 작업을 막지 않는 “조용한(fail-silent)” 동작이다 — 이미 별을 눌렀거나 gh 로그인이 안 돼 있으면 그냥 조용히 넘어간다.

[!note] 멱등(idempotent)이란? 같은 동작을 한 번 하든 열 번 하든 결과가 똑같은 성질이다. fablize는 마커로 자기 블록을 식별해 “지우고 다시 넣기”를 하므로, 셋업을 반복해도 CLAUDE.md가 지저분해지지 않는다.

핵심 정리

2계층 설치 모델

계층무엇을 하나발동 시점
자동router.sh 라우터 훅 등록/plugin install 즉시
always-on(선택)CLAUDE.md에 운영 블록 주입setup.sh 1회 실행

설치가 만들거나 고치는 파일

경로역할생성/수정 주체
~/.claude/CLAUDE.md 또는 $PWD/CLAUDE.mdalways-on 운영 블록 주입 대상setup.sh
*.fablize-bak.<ts>주입 전 백업setup.sh
~/.fablize/progress.json셋업 상태(재질문 방지)setup.sh
./.fablize/goals.json, ledger.jsonl작업 상태·감사 로그goals.py 런타임

[!note] 설치가 지키는 원칙 6가지

  • 의존성 0 (bash + python3만)
  • 주입 전 항상 백업
  • 마커 기반 멱등 (재주입해도 1개)
  • settings.json 무손상 (훅은 hooks.json이 자동 등록)
  • fail-silent 부가작업 (스타는 절대 차단 안 함)
  • 상태파일로 재질문 방지

실제 예시

플러그인 매니페스트plugin.jsonskills만 선언하고, 훅은 hooks/hooks.json이 자동 로드한다.

// .claude-plugin/plugin.json
{ "name": "fablize", "version": "2.0.0",
  "description": "A harness that makes Opus ... behave like Fable ...",
  "author": { "name": "fivetaku", "email": "gptaku.ai@gmail.com" },
  "keywords": ["harness","verification","completion","opus","fable","agentic"],
  "skills": ["./skills/fablize"] }
// .claude-plugin/marketplace.json
{ "name": "fablize", "owner": {...},
  "plugins": [ { "name": "fablize", "source": "./", "category": "workflow", "description": "..." } ] }

설치 순서: /plugin marketplace add fivetaku/fablize/plugin install fablize.

멱등 주입의 핵심 (setup.sh) — 백업 → 마커 제거 후 재삽입 → 상태 기록 → 스타.

# setup/setup.sh — settings.json은 건드리지 않음(훅은 hooks.json이 자동 등록)
ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
case "$scope" in
  global) CLAUDE_MD="$HOME/.claude/CLAUDE.md";;
  local)  CLAUDE_MD="$PWD/CLAUDE.md";;
esac
ts=$(python3 -c "import time;print(int(time.time()))")
cp "$CLAUDE_MD" "$CLAUDE_MD.fablize-bak.$ts"           # 주입 전 백업

# __PLUGIN_ROOT__ → 실제경로 치환 후, 기존 마커 제거하고 재삽입(멱등)
python3 - "$CLAUDE_MD" "$BLOCK_TPL" "$ROOT" <<'PY'
block = pathlib.Path(tpl).read_text().strip().replace("__PLUGIN_ROOT__", root)
cur = re.sub(r"<!-- FABLIZE:BEGIN.*?FABLIZE:END -->\n?", "", cur, flags=re.S).rstrip()
p.write_text((cur + "\n\n" + block + "\n") if cur else (block + "\n"))
PY

# 셋업 상태 기록(스킬이 재셋업 안 묻게)
python3 - "$scope" "$ts" <<'PY'
json.dump({"setup_done": True, "scope": sys.argv[1], "version": "2.0.0", "ts": int(sys.argv[2])},
          open(os.path.expanduser("~/.fablize/progress.json"), "w"))
PY

bash "$ROOT/setup/star.sh" || true                      # 마지막에 스타(결정론적, fail-silent)

fail-silent 스타링 (star.sh)gh로 별을 누르되, 어떤 경우에도 exit 0으로 작업을 막지 않는다.

# setup/star.sh — gh로 레포 스타, 절대 차단 안 함
if gh auth status &>/dev/null; then
  if gh api "user/starred/$REPO" &>/dev/null;       then echo "already starred";
  elif gh api -X PUT "/user/starred/$REPO" &>/dev/null; then echo "Starred";
  else echo "Could not star automatically — ..."; fi
else echo "Sign in to gh ... or star here: ..."; fi
exit 0

멱등 해제 (uninstall.sh)CLAUDE.md에서 마커 블록만 도려낸다. 라우터 훅은 /plugin uninstall이 제거한다.

# setup/uninstall.sh — CLAUDE.md에서 마커 블록만 제거(라우터 훅은 /plugin uninstall로 제거됨)
new = re.sub(r"\n*<!-- FABLIZE:BEGIN.*?FABLIZE:END -->\n?", "\n", cur, flags=re.S)
# 백업(.fablize-bak.*)은 수동 삭제 안내

직접 만들 재사용 템플릿 — 어떤 도구든 “설정 파일에 안전하게 내 블록을 넣고 빼는” 패턴이 필요할 때 그대로 쓴다.

# 멱등 블록 주입 패턴(재사용 가능)
cp "$CLAUDE_MD" "$CLAUDE_MD.bak.$(date +%s)"            # 1) 백업
python3 - "$CLAUDE_MD" "$TPL" "$ROOT" <<'PY'            # 2) 마커 제거 후 재삽입
block = open(tpl).read().strip().replace("__ROOT__", root)
cur   = re.sub(r"<!-- MARK:BEGIN.*?MARK:END -->\n?", "", cur, flags=re.S).rstrip()
open(md,"w").write((cur+"\n\n"+block+"\n") if cur else block+"\n")
PY

[!warning] DX 호불호 포인트 셋업이 별 누르기를 함께 수행한다. 별도 동의 프롬프트 없이 셋업 프롬프트의 표기로 묵시 동의로 간주한다. 편하다는 평과 “동의를 끼워 넣었다”는 평이 갈리는 부분 — 분석축 9(DX)에서 논쟁/감점 요소다.

요약 & 셀프체크

  • fablize 설치는 자동(install로 라우터 켜짐) + 선택(setup.sh로 always-on) 2계층이다.
  • always-on 셋업은 백업 → 멱등 주입 → 상태 기록 → fail-silent 스타 순으로, 사용자의 settings.json을 절대 건드리지 않는다.
  • 해제는 uninstall.sh(블록 제거) + /plugin uninstall(훅 제거)이며 백업은 보존한다.

스스로 답해보기

  1. setup.sh를 실수로 두 번 돌리면 CLAUDE.md에 운영 블록이 두 개 생길까? 왜 그런가/아닌가?
  2. fablize가 라우터 훅을 등록할 때 사용자의 settings.json을 안 건드릴 수 있는 이유는?
  3. 스타링이 GitHub 로그인이 안 된 상태에서도 설치를 멈추지 않는 이유는 코드의 어느 부분 때문인가?

연결

FBZ_개요 · _분석축_루브릭 · FBZ_10_훅-라우터-주입 · FBZ_60_스킬-운영블록

[!tip] Codex 교차검증 보존 소스(/home/seunghyeong/harness-fablize/setup/setup.sh, star.sh, uninstall.sh, .claude-plugin/plugin.json, marketplace.json)와 대조해 다음을 확인함: ① setup.shsettings.json을 전혀 수정하지 않고 CLAUDE.md에만 마커 블록을 주입한다(주석 명시: “does NOT touch settings.json”). ② 라우터 훅은 hooks.json이 install 시 자동 등록한다. ③ setup.sh 말미 주석에 따르면 early-stop 훅(finish-the-work)은 보통 이미 전역 등록돼 있으므로, 미등록일 때만 Stop 훅으로 추가하라(중복 방지)는 안내가 있다 — 라우터와 달리 자동 등록 대상이 아님에 유의. ④ star.shset -uo pipefail(set -e 없음)에 마지막 exit 0으로 어떤 분기에서도 설치를 차단하지 않는다. 근거 파일: setup/setup.sh, setup/star.sh, setup/uninstall.sh, .claude-plugin/plugin.json, .claude-plugin/marketplace.json.