# 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//SKILL.md`)와 Antigravity CLI **workflows**(`.agents/workflows/.md`)로 생성하는 기능을 `scripts/sync_automation.py` 에 `commands` 타깃으로 추가한다. **Architecture:** Phase 0의 SSOT→생성 모델을 commands로 확장한다. SSOT = `.claude/commands/.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`): 디스커버리 `/.agents/skills//SKILL.md`(repo 커밋). frontmatter 필수 2필드 `name`(폴더명과 일치 권장) + `description`(언제 발동). **인자 placeholder 없음 — 자연어로 전달**(`$ARGUMENTS`/`$1` 미지원). `/skills` picker 또는 `$name` 멘션으로 명시 호출 + description 의미 매칭. - **Antigravity workflows** (Google Codelabs / atamel.dev): `/.agents/workflows/.md`, frontmatter **`description`만**(name=파일명), 본문은 prose 지시, 인자는 `` 식 각괄호 prose(placeholder 없음), `/ ` 호출. - Codelab 실제 예시(verbatim): ``` --- description: Start the Autonomous AI Developer Pipeline sequence with a new idea --- When the user types `/startcycle `, 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.py` — `COMMAND_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//SKILL.md` ×13 (codex), `.agents/workflows/.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: 설치 여부 확인** ```bash 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/`를 읽는지 확인: ```bash 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//SKILL.md` | 공식 디스커버리 경로 | | antigravity workflow 경로 | `.agents/workflows/.md` | repo `.agents/` 복수 규약 일치 | | `$ARGUMENTS` 매핑 | argument-hint 각괄호 prose로 치환 | 양 플랫폼 placeholder 미지원 | | `.agents/skills/` antigravity 이중로드 | {{읽음→완화 필요 / 안읽음→무관 / UNVERIFIED}} | Step 3 결과 | - [ ] **Step 5: Commit** ```bash 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** ```python # 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** ```python # 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** ```bash 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** ```python 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** ```python @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` 추가하고 분기. ```python 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** ```bash 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//SKILL.md` ×13, `.agents/workflows/.md` ×13 - [ ] **Step 1: 생성 + 검증** ```bash 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** ```bash 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** ```bash 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 (노트 갱신 시)** ```bash 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//SKILL.md` (13개, `$cmd` 호출), 생성 `python3 scripts/sync_automation.py commands`. - Antigravity: `.agents/workflows/.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** ```bash 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//SKILL.md` 13개 + `.agents/workflows/.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/*.md`는 `Read tool` 류 미사용, shell·서브에이전트명·repo 경로만 참조). 만약 특정 command가 Claude 전용 표기를 쓰면 그 command만 생성 후 hand-review 필요 — Task 4 검증의 `$ARGUMENTS`/`{{`grep으로 1차 포착.