Files
llm-wiki/docs/superpowers/plans/2026-06-04-three-platform-sync-phase1-commands.md
T

22 KiB
Raw Blame History

3-플랫폼 동기화 Phase 1 — commands Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Claude의 13개 슬래시 command(.claude/commands/*.md)를 Codex CLI skills(.agents/skills/<cmd>/SKILL.md)와 Antigravity CLI workflows(.agents/workflows/<cmd>.md)로 생성하는 기능을 scripts/sync_automation.pycommands 타깃으로 추가한다.

Architecture: Phase 0의 SSOT→생성 모델을 commands로 확장한다. SSOT = .claude/commands/<cmd>.md(frontmatter description+argument-hint + 본문). 생성기는 (1) frontmatter를 플랫폼별 헤더로 변환, (2) $ARGUMENTS 토큰을 자연어 각괄호 인자(argument-hint 값)로 치환, (3) 본문의 platform-neutral 절차는 그대로 보존, (4) codex skill / antigravity workflow 두 포맷으로 직렬화한다. command 본문은 agent 본문과 달리 Claude 전용 tool 표현(Read tool 등)을 쓰지 않고 절차적 prose + shell + 서브에이전트 이름(Phase 0에서 3 플랫폼에 모두 포팅됨)만 참조하므로 기계 변환이 충분하다.

Tech Stack: Python 3.12 (stdlib), pytest (venv: /home/donghyeon/dev/llm-wiki-private/.venv/bin/python).


배경 / 확정된 포맷 사실 (공식문서 + 리서치)

  • Codex skills (developers.openai.com/codex/skills): 디스커버리 <repo>/.agents/skills/<name>/SKILL.md(repo 커밋). frontmatter 필수 2필드 name(폴더명과 일치 권장) + description(언제 발동). 인자 placeholder 없음 — 자연어로 전달($ARGUMENTS/$1 미지원). /skills picker 또는 $name 멘션으로 명시 호출 + description 의미 매칭.
  • Antigravity workflows (Google Codelabs / atamel.dev): <repo>/.agents/workflows/<name>.md, frontmatter description(name=파일명), 본문은 prose 지시, 인자는 <idea> 식 각괄호 prose(placeholder 없음), /<name> <args> 호출.
    • Codelab 실제 예시(verbatim):
      ---
      description: Start the Autonomous AI Developer Pipeline sequence with a new idea
      ---
      
      When the user types `/startcycle <idea>`, orchestrate the development process ...
      
  • 경로 규약 결정: 이 repo는 이미 .agents/(복수) 규약(.agents/agents/, .agents/hooks.json)을 쓰고 antigravity가 그것을 로드 중 → antigravity workflows도 .agents/workflows/(복수) 로 통일. codex skills는 공식 고정 경로 .agents/skills/.
  • 알려진 충돌(미해결, 경험적 확인 필요): 일부 antigravity 빌드는 .agents/skills/도 skill로 읽어, codex command-skill이 antigravity에 semantic-trigger skill로 이중 등록될 수 있음(workflow + skill). 공식문서로 확정 불가 → Task 1에서 실제 설치된 CLI로 경험적 확인 후 필요 시 완화.

13개 command (SSOT: .claude/commands/*.md)

blogify · branch · branch-spec · coverage · daily · depth · ingest · interviewize · lint · migrate-claims · projectize · query · tag

frontmatter 공통: description: + argument-hint:. 본문은 $ARGUMENTS 토큰 + 절차(## 작업 절차) + 규칙. 일부는 .claude/hooks/wiki_structure_lint.py(실제 repo 스크립트, 크로스플랫폼 실행 가능 — 유지)와 포팅된 서브에이전트(branch-depth-auditor 등) 참조.


File Structure

  • Modify: scripts/sync_automation.pyCOMMAND_NAMES 리스트, command frontmatter 파서 재사용(parse_frontmatter), transform_command_body, render_codex_skill, render_antigravity_workflow, generate_command_one, main()target choices에 commands 추가.
  • Modify: scripts/test_sync_automation.py — command 변환 단위 테스트.
  • Create (생성물): .agents/skills/<cmd>/SKILL.md ×13 (codex), .agents/workflows/<cmd>.md ×13 (antigravity).
  • Create: docs/superpowers/notes/2026-06-04-phase1-empirical-format-check.md — Task 1 경험적 확인 결과 기록.
  • Modify (말미): CLAUDE.md §2 표 + .codex/agents/README.md — commands→skills/workflows 매핑 1단락.

책임 경계: 생성기는 frontmatter 변환 + $ARGUMENTS 치환 + 포맷 직렬화만. 본문 절차 prose는 platform-neutral이므로 보존. codex skill 본문에서 antigravity 전용 표기를 만들지 않고, antigravity workflow 본문에서 codex 전용 표기를 만들지 않는다(생성기가 플랫폼별로 분기).


Task 1: 경험적 포맷·충돌 확인 + 결정 잠금

Files:

  • Create: docs/superpowers/notes/2026-04-phase1-empirical-format-check.md (디렉토리 없으면 생성)

목적: 공식문서가 확정 못한 두 가지를 실제 설치된 CLI로 확인하고, 생성기 타깃 경로/인자 규약을 잠근다. CLI가 없으면 "가정 + 보류" 로 명시 기록(생성물은 가정대로 진행, 추후 검증).

  • Step 1: 설치 여부 확인
cd /home/donghyeon/dev/llm-wiki-private
command -v codex && codex --version 2>&1 | head -1 || echo "codex: NOT INSTALLED"
ls -d ~/.gemini/antigravity-cli 2>/dev/null && echo "antigravity-cli home present" || echo "antigravity: home absent"
command -v agy 2>&1 || echo "agy (antigravity CLI) not on PATH"
  • Step 2: (codex 있으면) skill 디스커버리 경로 확인

scratch skill을 만들어 codex가 .agents/skills/를 읽는지 확인:

mkdir -p /tmp/codex-skill-probe/.agents/skills/probe-skill
printf -- '---\nname: probe-skill\ndescription: probe codex skill discovery\n---\nprobe body\n' > /tmp/codex-skill-probe/.agents/skills/probe-skill/SKILL.md
# codex의 skills 목록 확인 (codex 설치 시): 예) `codex` TUI에서 /skills, 또는 가능한 CLI 서브커맨드
codex --help 2>&1 | grep -iE "skill|prompt" || echo "no skill subcommand surfaced in --help"

결과(읽힘/안읽힘/불명)를 노트에 기록.

  • Step 3: (antigravity 있으면) .agents/skills/ 이중 로드 여부 확인

antigravity가 .agents/skills/를 skill로 읽는지(=codex command-skill 이중 등록 위험) 실제 빌드에서 확인. 가능한 방법: antigravity CLI의 skill/workflow 목록 출력 커맨드, 또는 ~/.gemini/antigravity-cli/ 로그/registry 확인. 결과를 노트에 기록.

  • Step 4: 결정 기록

노트에 다음을 표로 확정(경험 결과 또는 "UNVERIFIED — 가정"):

항목 결정 근거
codex skill 경로 .agents/skills/<cmd>/SKILL.md 공식 디스커버리 경로
antigravity workflow 경로 .agents/workflows/<cmd>.md repo .agents/ 복수 규약 일치
$ARGUMENTS 매핑 argument-hint 각괄호 prose로 치환 양 플랫폼 placeholder 미지원
.agents/skills/ antigravity 이중로드 {{읽음→완화 필요 / 안읽음→무관 / UNVERIFIED}} Step 3 결과
  • Step 5: Commit
cd /home/donghyeon/dev/llm-wiki-private
git add docs/superpowers/notes/
git commit -m "docs(phase1): empirical format + skills-collision check, lock target paths"

Task 2: command 파싱 + skill/workflow 직렬화 (순수 함수, TDD)

Files:

  • Modify: scripts/sync_automation.py

  • Test: scripts/test_sync_automation.py

  • Step 1: Write the failing test

# append to scripts/test_sync_automation.py

_SAMPLE_CMD = (
    "---\n"
    "description: 브랜치 노트의 구현 착수 깊이 점검\n"
    "argument-hint: <브랜치 이름>\n"
    "---\n\n"
    "브랜치 노트 1개의 깊이를 점검합니다.\n\n"
    "**브랜치 이름:** $ARGUMENTS\n\n"
    "## 작업 절차\n1. `branch-depth-auditor` 서브에이전트를 디스패치한다.\n"
)


def test_transform_command_body_replaces_arguments():
    out = s.transform_command_body(_body_of(_SAMPLE_CMD), "<브랜치 이름>")
    assert "$ARGUMENTS" not in out
    assert "<브랜치 이름>" in out
    # platform-neutral 절차/에이전트 참조는 보존
    assert "branch-depth-auditor 서브에이전트를 디스패치" in out


def test_render_codex_skill_frontmatter():
    out = s.render_codex_skill(
        name="depth",
        description="브랜치 노트의 구현 착수 깊이 점검",
        arg_hint="<브랜치 이름>",
        body="**브랜치 이름:** <브랜치 이름>\n\n## 작업 절차\n1. ...\n",
    )
    fm, body = s.parse_frontmatter(out)
    assert fm["name"] == "depth"
    assert fm["description"]  # non-empty
    assert "$ARGUMENTS" not in body
    assert "## 작업 절차" in body


def test_render_antigravity_workflow_frontmatter_and_invocation():
    out = s.render_antigravity_workflow(
        name="depth",
        description="브랜치 노트의 구현 착수 깊이 점검",
        arg_hint="<브랜치 이름>",
        body="## 작업 절차\n1. ...\n",
    )
    fm, body = s.parse_frontmatter(out)
    assert set(fm.keys()) == {"description"}  # name comes from filename, not frontmatter
    # 호출 안내가 본문 상단에 있어야 함 (Codelab 패턴)
    assert "/depth <브랜치 이름>" in body
    assert "## 작업 절차" in body

(헬퍼 _body_of 는 테스트 상단에 추가: def _body_of(md): return s.parse_frontmatter(md)[1].)

  • Step 2: Run test to verify it fails

Run: cd /home/donghyeon/dev/llm-wiki-private && .venv/bin/python -m pytest scripts/test_sync_automation.py -k "command or codex_skill or workflow" -v Expected: FAIL with AttributeError: ... 'transform_command_body'.

  • Step 3: Write minimal implementation
# add near COMMAND-related code in sync_automation.py

COMMAND_NAMES = [
    "blogify", "branch", "branch-spec", "coverage", "daily", "depth",
    "ingest", "interviewize", "lint", "migrate-claims", "projectize",
    "query", "tag",
]

CLAUDE_COMMANDS = REPO / ".claude" / "commands"
CODEX_SKILLS = REPO / ".agents" / "skills"
ANTIGRAV_WORKFLOWS = REPO / ".agents" / "workflows"


def transform_command_body(body: str, arg_hint: str) -> str:
    # Neither Codex skills nor Antigravity workflows support a $ARGUMENTS
    # placeholder; both take arguments as natural language. Replace the token
    # with the argument-hint's angle-bracket prose so the body still reads well.
    return body.replace("$ARGUMENTS", arg_hint)


def render_codex_skill(name: str, description: str, arg_hint: str, body: str) -> str:
    # description drives implicit trigger; fold in the arg hint for clarity.
    desc = f"{description} (입력: {arg_hint})"
    return (
        "---\n"
        f"name: {name}\n"
        f"description: {desc}\n"
        "---\n\n"
        f"{body.rstrip()}\n"
    )


def render_antigravity_workflow(name: str, description: str, arg_hint: str, body: str) -> str:
    invocation = f"사용자가 `/{name} {arg_hint}` 를 입력하면 아래 절차를 수행한다.\n\n"
    return (
        "---\n"
        f"description: {description}\n"
        "---\n\n"
        f"{invocation}{body.rstrip()}\n"
    )
  • Step 4: Run test to verify it passes

Run: cd /home/donghyeon/dev/llm-wiki-private && .venv/bin/python -m pytest scripts/test_sync_automation.py -k "command or codex_skill or workflow" -v Expected: PASS.

  • Step 5: Commit
cd /home/donghyeon/dev/llm-wiki-private
git add scripts/sync_automation.py scripts/test_sync_automation.py
git commit -m "feat(sync): command body transform + codex skill / antigravity workflow renderers"

Task 3: command 로더 + CLI commands 타깃 (TDD)

Files:

  • Modify: scripts/sync_automation.py

  • Test: scripts/test_sync_automation.py

  • Step 1: Write the failing test

def test_load_command_inputs_for_existing_command():
    ci = s.load_command_inputs("depth")
    assert ci.name == "depth"
    assert ci.description
    assert ci.arg_hint.startswith("<") or "--" in ci.arg_hint
    assert "$ARGUMENTS" in ci.body or "작업 절차" in ci.body  # raw body still has token


def test_generate_command_writes_both(tmp_path, monkeypatch):
    monkeypatch.setattr(s, "CODEX_SKILLS", tmp_path / "skills")
    monkeypatch.setattr(s, "ANTIGRAV_WORKFLOWS", tmp_path / "workflows")
    s.generate_command_one("depth", check=False)
    skill = tmp_path / "skills" / "depth" / "SKILL.md"
    wf = tmp_path / "workflows" / "depth.md"
    assert skill.exists() and wf.exists()
    sfm, sbody = s.parse_frontmatter(skill.read_text())
    assert sfm["name"] == "depth" and "$ARGUMENTS" not in sbody
    wfm, wbody = s.parse_frontmatter(wf.read_text())
    assert "/depth" in wbody and "$ARGUMENTS" not in wbody


def test_main_commands_target_check_is_clean_after_generate():
    # after generation (Task 4), --check must be drift-free
    rc = s.main(["commands", "--check"])
    assert rc == 0

마지막 테스트는 Task 4 생성 이후에만 통과한다. Task 3 단계에서는 앞 두 테스트만 대상으로 실행하고, 세 번째는 Task 4 검증에서 green 확인.

  • Step 2: Run test to verify it fails

Run: cd /home/donghyeon/dev/llm-wiki-private && .venv/bin/python -m pytest scripts/test_sync_automation.py -k "load_command or generate_command" -v Expected: FAIL with AttributeError: ... 'load_command_inputs'.

  • Step 3: Write minimal implementation
@dataclass
class CommandInputs:
    name: str
    description: str
    arg_hint: str
    body: str


def load_command_inputs(name: str) -> CommandInputs:
    fm, body = parse_frontmatter((CLAUDE_COMMANDS / f"{name}.md").read_text())
    return CommandInputs(
        name=name,
        description=fm.get("description", "").strip(),
        arg_hint=fm.get("argument-hint", "").strip(),
        body=body,
    )


def generate_command_one(name: str, check: bool, drift: list[str] | None = None) -> None:
    drift = drift if drift is not None else []
    ci = load_command_inputs(name)
    tbody = transform_command_body(ci.body, ci.arg_hint)
    skill_text = render_codex_skill(ci.name, ci.description, ci.arg_hint, tbody)
    wf_text = render_antigravity_workflow(ci.name, ci.description, ci.arg_hint, tbody)
    _write_or_check(CODEX_SKILLS / name / "SKILL.md", skill_text, check, drift)
    _write_or_check(ANTIGRAV_WORKFLOWS / f"{name}.md", wf_text, check, drift)

그리고 main() 을 확장: target choices에 commands 추가하고 분기.

def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(description="Sync Claude agents/commands -> Codex/Antigravity native files")
    ap.add_argument("target", choices=["agents", "commands"], help="what to sync")
    ap.add_argument("--check", action="store_true", help="fail (exit 2) if outputs drift from SSOT")
    ap.add_argument("--only", help="single name (default: all)")
    args = ap.parse_args(argv)

    if args.target == "agents":
        names = [args.only] if args.only else AGENT_NAMES
        gen = generate_one
        label = "agents (codex .toml + antigravity agent.json)"
    else:
        names = [args.only] if args.only else COMMAND_NAMES
        gen = generate_command_one
        label = "commands (codex skill + antigravity workflow)"

    drift: list[str] = []
    for name in names:
        try:
            gen(name, check=args.check, drift=drift)
        except FileNotFoundError as e:
            print(f"ERROR: missing SSOT for '{name}': {e.filename}", file=sys.stderr)
            return 1
    if args.check and drift:
        print("DRIFT detected in:\n  " + "\n  ".join(drift), file=sys.stderr)
        return 2
    action = "checked" if args.check else "generated"
    print(f"{action} {len(names)} {label}")
    return 0
  • Step 4: Run test to verify it passes

Run: cd /home/donghyeon/dev/llm-wiki-private && .venv/bin/python -m pytest scripts/test_sync_automation.py -k "load_command or generate_command" -v Expected: PASS. Also run the FULL suite to confirm the main() refactor didn't break agents: Run: .venv/bin/python -m pytest scripts/test_sync_automation.py -q Expected: all green. Then sanity: python3 scripts/sync_automation.py agents --check still exits 0.

  • Step 5: Commit
cd /home/donghyeon/dev/llm-wiki-private
git add scripts/sync_automation.py scripts/test_sync_automation.py
git commit -m "feat(sync): commands target — load + generate codex skills / antigravity workflows"

Task 4: 13개 command 전체 생성 + 검증 + drift-0

Files:

  • Generate: .agents/skills/<cmd>/SKILL.md ×13, .agents/workflows/<cmd>.md ×13

  • Step 1: 생성 + 검증

cd /home/donghyeon/dev/llm-wiki-private
python3 scripts/sync_automation.py commands
echo "--- counts ---"
echo "skills: $(ls .agents/skills/*/SKILL.md | wc -l) | workflows: $(ls .agents/workflows/*.md | wc -l)"
echo "--- validate ---"
.venv/bin/python -c "
import sys; sys.path.insert(0,'scripts'); import sync_automation as s
for n in s.COMMAND_NAMES:
    sfm,sbody=s.parse_frontmatter(open(f'.agents/skills/{n}/SKILL.md').read())
    assert sfm['name']==n and sfm['description'] and '\$ARGUMENTS' not in sbody, n
    wfm,wbody=s.parse_frontmatter(open(f'.agents/workflows/{n}.md').read())
    assert 'description' in wfm and f'/{n}' in wbody and '\$ARGUMENTS' not in wbody, n
print('all 13 commands ok (skill name+desc, workflow desc+invocation, no \$ARGUMENTS)')
"
echo "--- no {{ }} placeholders leaked into workflows (antigravity G3) ---"
! grep -l '{{' .agents/workflows/*.md || echo "WARNING: {{ }} found"

Expected: skills: 13 | workflows: 13, validation ok, no {{ warning.

  • Step 2: drift-0
cd /home/donghyeon/dev/llm-wiki-private
python3 scripts/sync_automation.py commands --check; echo "exit=$?"

Expected: checked 13 commands ... + exit=0.

  • Step 3: 전체 테스트(세 번째 main commands check 테스트 포함)

Run: .venv/bin/python -m pytest scripts/test_sync_automation.py -q Expected: all green.

  • Step 4: Commit
cd /home/donghyeon/dev/llm-wiki-private
git add .agents/skills/ .agents/workflows/
git commit -m "feat(sync): generate 13 commands as codex skills + antigravity workflows"

Task 5: 대표 command 실호출 스모크 (CLI 있으면)

Phase 0 agents와 달리 commands는 사용자가 실제로 호출하는 표면이므로, 설치된 CLI에서 1~2개를 실호출해 형식이 맞는지 확인한다. CLI 미설치면 SKIP + 노트 기록.

  • Step 1: codex skill 인식 확인 (codex 있으면)

.agents/skills/depth/SKILL.md 가 codex의 /skills 또는 $depth 로 인식되는지 확인. 인식 안 되면 frontmatter/경로를 Task 1 노트와 대조해 진단.

  • Step 2: antigravity workflow 인식 확인 (antigravity 있으면)

.agents/workflows/depth.md 가 antigravity에서 /depth 로 등록되는지 확인. 동시에 Task 1의 이중로드 가설(코덱스 skill이 antigravity에 새는지)을 /skills 목록으로 재확인.

  • Step 3: 결과를 Task 1 노트에 추가 기록 + (필요 시) 완화

  • 이중로드가 실제로 발생하고 바람직하지 않으면: 완화안을 노트에 적고 사용자에게 에스컬레이션(예: codex skill만 두고 antigravity는 workflow만 쓰도록 build 설정, 또는 경로 분리). 이 단계에서 임의로 큰 구조 변경하지 말 것 — 결과만 보고.

  • Step 4: Commit (노트 갱신 시)

cd /home/donghyeon/dev/llm-wiki-private
git add docs/superpowers/notes/
git commit -m "docs(phase1): empirical smoke results for codex skills / antigravity workflows"

Task 6: 문서 갱신 (commands→skills/workflows 매핑)

Files:

  • Modify: CLAUDE.md (§2 디렉터리 역할 표 또는 자동화 섹션)

  • Modify: .codex/agents/README.md

  • Step 1: CLAUDE.md 에 1단락

§2의 자동화 목록에 codex/antigravity의 command 등가물을 명시:

  • Codex: .agents/skills/<cmd>/SKILL.md (13개, $cmd 호출), 생성 python3 scripts/sync_automation.py commands.

  • Antigravity: .agents/workflows/<cmd>.md (13개, /cmd 호출).

  • 인자는 placeholder 없이 자연어(각괄호 prose).

  • Step 2: .codex/agents/README.md 의 "Native generation" 노트에 commands 줄 추가

sync_automation.py commands 로 13개 command가 .agents/skills/(codex) + .agents/workflows/(antigravity)에 생성됨을 1줄 추가.

  • Step 3: 검증 + Commit
cd /home/donghyeon/dev/llm-wiki-private
grep -c "sync_automation.py commands" CLAUDE.md .codex/agents/README.md
git add CLAUDE.md .codex/agents/README.md
git commit -m "docs(sync): document commands -> codex skills + antigravity workflows"

Phase 1 완료 기준 (Definition of Done)

  • sync_automation.py commands 타깃 동작 + 테스트 green.
  • .agents/skills/<cmd>/SKILL.md 13개 + .agents/workflows/<cmd>.md 13개 생성·검증.
  • python3 scripts/sync_automation.py commands --check exit 0 (drift 0).
  • $ARGUMENTS 토큰이 생성물에 0개, antigravity workflow에 {{ }} 0개.
  • Task 1/5 경험적 확인 결과가 노트에 기록(또는 CLI 미설치 시 가정으로 명시).
  • CLAUDE.md + codex README에 매핑 문서화.

이후: Phase 2(hooks → .codex/hooks.json, AGENTS.md, CLAUDE.md의 구식 codex "수동 cat" 서술 정리 — line 76)는 별도 plan.


Self-Review (작성자 체크)

  • Spec coverage: 설계 §4 Phase 1(commands → codex skills + antigravity workflows, 생성기 commands 타깃 확장)의 모든 요소가 Task 2~4에 매핑. 설계 §6 리스크의 .agents/skills/ 충돌은 Task 1/5 경험적 확인으로 처리.
  • Placeholder scan: 코드 스텝은 실제 코드 포함. Task 1/5는 경험적 절차라 결과가 환경 의존 — 명령 + 기록 표 + "UNVERIFIED 시 가정 명시" 로 결정론화.
  • Type consistency: transform_command_body(body, arg_hint), render_codex_skill(name,description,arg_hint,body), render_antigravity_workflow(name,description,arg_hint,body), CommandInputs, load_command_inputs(name), generate_command_one(name,check,drift) — 전 Task 일관. _write_or_check/parse_frontmatter는 Phase 0 함수 재사용.
  • 알려진 한계: command 본문이 platform-neutral하다는 가정에 의존(실측: .claude/commands/*.mdRead tool 류 미사용, shell·서브에이전트명·repo 경로만 참조). 만약 특정 command가 Claude 전용 표기를 쓰면 그 command만 생성 후 hand-review 필요 — Task 4 검증의 $ARGUMENTS/{{grep으로 1차 포착.