내부 아키텍처
agent-harness Python 패키지의 내부 구조를 이해합니다.
새 기능을 추가하거나 버그를 수정할 때 어떤 모듈을 수정해야 하는지 파악합니다.

패키지 구조

agent_harness/          # Python 패키지
├── models.py           # 데이터 모델 (Item, Category, Preset, Manifest)
├── store.py            # 저장소 발견 & 콘텐츠 로딩
├── installer.py        # 설치 로직 (파일 복사 + manifest 업데이트)
├── block.py            # CLAUDE.md managed block 삽입/교체
├── marketplace.py      # build-marketplace 로직
└── adapters/
    ├── base.py         # 추상 기반 클래스
    ├── claude_code.py  # Claude Code 어댑터 (CLAUDE.md)
    ├── codex.py        # OpenAI Codex 어댑터 (AGENTS.md)
    └── opencode.py     # OpenCode 어댑터 (AGENTS.md)

핵심 모듈 역할

models.py
데이터 모델 정의
Item, Category, Preset, ManifestItem, Manifest
item_key(): "rule:workflow/plan-first"
Manifest.union_items(): 기존 항목과 병합
store.py
디스크에서 콘텐츠 발견
find_store_root(): 환경변수 → walk-up
categories(): rules/, skills/ 스캔
presets(): presets/*.yaml 파싱
installer.py
선택 항목 프로젝트에 설치
install(selections, project_path, tool_id)
_dest_for(): 목적지 경로 계산
_write_manifest(): manifest.json 저장
block.py
CLAUDE.md managed block 관리
upsert_block(): 삽입 or 교체
wrap(): START/END 마커로 감쌈
_strip_orphan_markers(): 고아 마커 정리
marketplace.py
플러그인 빌드
build_marketplace(): 전체 빌드 진입점
_convert_rule_to_skill(): disable-model-invocation 추가
_namespaced_skill_id(): <category>-<id>
adapters/
도구별 instruction 파일 처리
instruction_filename = "CLAUDE.md" (claude_code)
새 도구 추가 = 3줄 서브클래스
tool_id로 어댑터 선택

전체 실행 흐름

TUI에서 사용자가 "Install" 버튼 누름 │ ▼ Store.find_store_root() 환경변수 AGENT_HARNESS_STORE 확인 없으면 현재 디렉터리부터 위로 walk rules/ + skills/ 폴더 있는 디렉터리 찾기 │ ▼ installer.install(selections, project_path, tool_id) selections = [Item(kind="rule", category="workflow", id="plan-first"), ...] │ ├── _dest_for(item) → {project}/.agents/rules/workflow/plan-first.md │ ├── 파일 복사 (소스 → 목적지) │ 이미 있으면 source_hash 비교 후 변경 시만 덮어씀 │ ├── load_manifest({project}/.agents/manifest.json) │ 없으면 빈 Manifest() │ ├── Manifest.union_items(new_items) │ 기존 항목과 병합 (key로 중복 제거) │ ├── _write_manifest(manifest, path) │ manifest.json 저장 │ └── adapter = get_adapter(tool_id) # "claude-code" adapter.render_block(manifest) block.upsert_block(claude_md_path, rendered_block)

Store 발견 로직 (find_store_root)

TUI가 콘텐츠 저장소를 찾는 방법입니다:

  1. 환경변수 AGENT_HARNESS_STORE가 설정되어 있으면 그 경로를 사용
  2. 설정 안 되어 있으면 현재 디렉터리부터 위로 walk-uprules/skills/ 폴더가 함께 있는 디렉터리 탐색
  3. 그것도 없으면 패키지 설치 위치에서 찾기 (패키지와 함께 배포된 기본 콘텐츠)
# 수동 설정 (다른 경로에 있는 store 사용)
export AGENT_HARNESS_STORE=/path/to/your/store
agent-harness-tui

데이터 모델

# models.py
@dataclass
class Item:
    kind: str        # "rule" | "skill"
    category: str    # "workflow", "auth", ...
    id: str          # "plan-first", "azure-ad-sso-flask"
    name: str        # "Plan before code"
    description: str
    path: Path       # 실제 파일 경로

def item_key(item: Item) -> str:
    return f"{item.kind}:{item.category}/{item.id}"
    # "rule:workflow/plan-first"

@dataclass
class Manifest:
    items: list[ManifestItem]

    def union_items(self, new_items: list[Item]) -> "Manifest":
        # 기존 + 신규를 item_key로 병합 (중복 제거)
        # 같은 key면 신규로 메타데이터 업데이트
        ...

block.py — Managed Block 로직

CLAUDE.md에 managed block을 삽입하거나 기존 블록을 교체합니다.

# block.py
START = "<!-- AGENT-HARNESS_START -->"
END   = "<!-- AGENT-HARNESS_END -->"

def upsert_block(claude_md_path: Path, new_content: str) -> None:
    existing = claude_md_path.read_text()
    wrapped = wrap(new_content)           # START...END로 감쌈

    if START in existing and END in existing:
        # 기존 블록 교체 (replace_or_append)
        new_text = _replace_or_append(existing, wrapped)
    else:
        # 파일 끝에 추가
        new_text = existing + "\n\n" + wrapped

    claude_md_path.write_text(new_text)
⚠️ 고아 마커 (orphan markers): START 없이 END만 있거나 그 반대인 경우 _strip_orphan_markers()로 자동 정리됩니다. 수동으로 마커를 편집하다 생기는 상황입니다.

Adapters — 새 AI 도구 지원 추가

새로운 AI 도구(예: Cursor, Windsurf)를 지원하려면 3줄의 서브클래스만 작성하면 됩니다.

# adapters/claude_code.py
from .base import BaseAdapter

class ClaudeCodeAdapter(BaseAdapter):
    instruction_filename = "CLAUDE.md"

# 새 도구 추가 예시
class CursorAdapter(BaseAdapter):
    instruction_filename = ".cursorrules"
도구어댑터instruction_filename
Claude CodeClaudeCodeAdapterCLAUDE.md
OpenAI CodexCodexAdapterAGENTS.md
OpenCodeOpenCodeAdapterAGENTS.md

테스트 전략

agent-harness는 실제 파일시스템 효과를 검증하는 통합 테스트 방식을 씁니다. Mock 없이 임시 디렉터리를 만들어서 실제로 설치하고 결과를 확인합니다.

# 테스트 예시
def test_install_creates_agents_directory(tmp_path):
    store = Store(test_store_root)
    items = [store.resolve("rule:workflow/plan-first")]

    result = install(items, project_path=tmp_path, tool_id="claude-code")

    assert (tmp_path / ".agents" / "rules" / "workflow" / "plan-first.md").exists()
    assert (tmp_path / ".agents" / "manifest.json").exists()
    assert "AGENT-HARNESS_START" in (tmp_path / "CLAUDE.md").read_text()
ℹ️ Mock을 쓰지 않는 이유: installer의 핵심 가치는 실제 파일이 올바른 경로에 생성되는 것입니다. 파일시스템을 Mock하면 테스트가 의미를 잃습니다.