22 KiB
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.py 에 commands 타깃으로 추가한다.
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미지원)./skillspicker 또는$name멘션으로 명시 호출 + description 의미 매칭. - Antigravity workflows (Google Codelabs / atamel.dev):
<repo>/.agents/workflows/<name>.md, frontmatterdescription만(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 ...
- Codelab 실제 예시(verbatim):
- 경로 규약 결정: 이 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()의targetchoices에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.md13개 +.agents/workflows/<cmd>.md13개 생성·검증.python3 scripts/sync_automation.py commands --checkexit 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차 포착.