Skip to content

feat: align skill installs with the open agent skills standard#1

Open
chordpli wants to merge 5 commits into
mainfrom
feat/open-agent-skills-layout
Open

feat: align skill installs with the open agent skills standard#1
chordpli wants to merge 5 commits into
mainfrom
feat/open-agent-skills-layout

Conversation

@chordpli

Copy link
Copy Markdown
Owner

요약

Codex CLI가 open agent skills 표준(agentskills.io)을 채택하면서 기존 .codex/prompts/ + .codex/scripts/ 설치 레이아웃이 deprecated 되었고, 설치본이 Codex discovery에서 인식되지 않는 문제를 해결한다. spec-kit의 Codex integration(.agents/skills/speckit-<name>/SKILL.md, "Commands are deprecated")과 동일한 방향.

변경 사항

설치 레이아웃

  • Codex: .agents/skills/q-<name>/ 에 SKILL.md 설치 (Codex가 CWD→레포 루트로 네이티브 스캔). frontmatter strip + # /<name> 헤더 + sidecar 구조 제거
  • 공통 q- prefix: 디렉토리명과 frontmatter name: 모두 네임스페이스 — 대상 프로젝트 자체 스킬과 충돌 방지. 호출명은 /q-ripple(Claude), $q-ripple(Codex)
  • 스킬명을 validator 요건 ^[a-z0-9-]+$ 에 맞게 정규화 (_- 등)
  • 두 어댑터의 install 로직을 BaseAdapter.install() 하나로 통합

Frontmatter 정규화

  • validator 허용 키(name/description/license/allowed-tools)만 top-level 유지
  • 비허용 키(version, scope, agents, risk, policy_injection, outputs)는 표준 자유 형식 키인 metadata: 아래로 이동 — 이게 없으면 Codex가 스킬을 invalid로 보고 discovery에서 제외함

컨텍스트 파일 (spec-kit 차용)

  • CLAUDE.md / AGENTS.md 에 마커로 구분된 스킬 목록 관리 블록 유지 (init/sync 시 재생성, 마커 밖 내용 보존, 멱등)

레거시 정리 + 매니페스트 (spec-kit 차용)

  • init/sync 가 구버전 레이아웃(.codex/prompts|scripts, prefix 없는 디렉토리) 자동 제거 — 락파일에 기록된 quivo 관리 스킬만 대상
  • sync 에 missing 감지 추가: 버전이 같아도 현재 레이아웃에 설치본이 없으면 재설치 → 구버전 프로젝트가 quivo sync 한 번으로 수렴
  • .quivo-lock.json 에 스킬별 설치 파일 목록 기록 (향후 uninstall 기반)

검증

  • pytest 27개 통과, scripts/test-skill.sh 전체 통과 (스모크를 q- prefix·컨텍스트 파일 검증으로 강화)
  • 원격 설치 테스트: 캐시 비운 뒤 GitHub Release(skills-v0.1.0) 번들 다운로드 → --agent both 설치 → 설치본 6개 SKILL.md 전부 Codex validator 규칙(허용 키 집합, name 정규식, 디렉토리=name 일치) 통과
  • 레거시 레이아웃 → sync 마이그레이션 시나리오 통과 (구 경로 제거 → 새 경로 재설치 → 컨텍스트 파일 생성)
  • 기존 CLAUDE.md 사용자 내용 보존 + 관리 블록 멱등성 확인

- Codex adapter installs to .agents/skills/ (native Codex skill discovery)
  instead of the deprecated .codex/prompts + .codex/scripts layout
- namespace installed skills as q-<name> (directory + frontmatter name),
  normalized to the validators' ^[a-z0-9-]+$ requirement
- normalize frontmatter on install: non-validator keys (version, scope,
  risk, outputs, ...) move under metadata: so Codex discovery accepts them
- maintain managed skill-list blocks in CLAUDE.md / AGENTS.md
- clean up legacy install layouts; sync reinstalls installs missing at the
  current layout
- record installed file paths per skill in .quivo-lock.json

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the skill installation layout for AI agents, transitioning Claude Code skills to .claude/skills/q-<name>/ and Codex CLI skills to .agents/skills/q-<name>/ (aligning with the open agent skills standard). It introduces a q- namespace prefix to prevent collisions, normalizes frontmatter keys by moving non-standard keys under metadata:, manages skill-list blocks in CLAUDE.md and AGENTS.md, and automatically cleans up legacy layouts. The review feedback highlights three key areas for improvement: handling potential OSError exceptions during legacy file cleanup to prevent command crashes, verifying registry existence during synchronization to avoid infinite sync loops for deleted/renamed skills, and catching yaml.YAMLError during frontmatter parsing to gracefully handle malformed YAML files.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/quivo/cleanup.py
Comment on lines +31 to +36
if p.is_dir():
shutil.rmtree(p)
removed.append(p)
elif p.exists():
p.unlink()
removed.append(p)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

cleanup_legacy 함수는 이전 버전의 quivo가 남긴 레거시 디렉토리나 파일을 정리하는 보조적인 역할을 합니다. 하지만 shutil.rmtree(p) 또는 p.unlink() 실행 중 권한 문제(PermissionError)나 파일 잠금 등으로 인해 OSError가 발생할 경우, 전체 quivo init 또는 quivo sync 명령이 중단(crash)될 수 있습니다. 레거시 정리는 실패하더라도 메인 설치/동기화 흐름에 영향을 주지 않도록 try-except OSError 블록으로 감싸서 예외를 안전하게 처리하는 것이 좋습니다.

Suggested change
if p.is_dir():
shutil.rmtree(p)
removed.append(p)
elif p.exists():
p.unlink()
removed.append(p)
try:
if p.is_dir() and not p.is_symlink():
shutil.rmtree(p)
removed.append(p)
elif p.exists() or p.is_symlink():
p.unlink()
removed.append(p)
except OSError:
pass

Comment thread src/quivo/commands/sync.py Outdated
Comment on lines +126 to +130
for name, old_ver in installed.items():
new_ver = latest.get(name)
if new_ver and new_ver != old_ver:
to_update.append((name, old_ver, new_ver))
missing = any(not a.skill_md_path(name).exists() for a in adapters)
if (new_ver and new_ver != old_ver) or missing:
to_update.append((name, old_ver, new_ver or old_ver))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

만약 락파일(.quivo-lock.json)에 기록된 스킬이 업스트림 레포지토리나 레지스트리에서 삭제/이름 변경되어 all_registry에 존재하지 않는 경우, missing이 True가 되어 to_update에 추가됩니다. 하지만 all_registry에 없기 때문에 skills_to_install에서 제외되어 실제로는 설치되지 않고, 락파일에는 계속 남아있게 됩니다. 이로 인해 quivo sync를 실행할 때마다 매번 해당 스킬을 동기화하려고 시도하지만 아무 작업도 수행하지 않는 무한 루프(ghost sync loop) 상태에 빠지게 됩니다. 동기화 대상 스킬이 현재 레지스트리(all_registry)에 존재하는지 먼저 확인하고, 존재하지 않는 경우 경고 메시지를 출력하고 건너뛰도록 개선하는 것이 좋습니다.

    for name, old_ver in installed.items():
        if name not in all_registry:
            console.print(f\"[yellow]Warning: Skill '{name}' is installed but not found in the registry. Skipping sync.[/yellow]\")
            continue
        new_ver = latest.get(name)
        missing = any(not a.skill_md_path(name).exists() for a in adapters)
        if (new_ver and new_ver != old_ver) or missing:
            to_update.append((name, old_ver, new_ver or old_ver))

m = _FRONTMATTER_RE.match(content)
if not m:
return content
data = yaml.safe_load(m.group(1)) or {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

yaml.safe_load는 프론트매터(frontmatter)의 YAML 문법이 잘못되었을 경우 yaml.YAMLError 예외를 발생시킵니다. 외부 저장소나 로컬에서 잘못 작성된 SKILL.md 파일을 다운로드하거나 참조할 때, 이 예외가 처리되지 않으면 CLI 전체가 크래시(crash)될 수 있습니다. 예외를 안전하게 잡아내고(catch), 문법 오류가 있는 경우 원본 본문을 그대로 반환하거나 적절히 처리할 수 있도록 try-except 블록을 추가하는 것이 안전합니다.

Suggested change
data = yaml.safe_load(m.group(1)) or {}
try:
data = yaml.safe_load(m.group(1)) or {}
except yaml.YAMLError:
return content

- init/sync remove the previous install (every file recorded in
  .quivo-lock.json's per-skill manifests) before installing fresh, so
  layout/prefix convention changes never leave stale copies behind
- hardcoded legacy-layout cleanup remains only as a fallback for locks
  written before the file manifest existed
- sync always reinstalls all locked skills and uninstalls skills that
  were dropped from the skill source
- lock now records internal skills too, and recorded paths that escape
  the target directory are ignored on removal
- extract .quivo-lock.json helpers into quivo.lockfile
@chordpli

Copy link
Copy Markdown
Owner Author

추가 커밋 e5eb33c: 레거시 정리 방식을 클린 재설치 개념으로 교체.

  • init/sync가 이전 설치본(락파일의 스킬별 files 매니페스트에 기록된 모든 파일)을 먼저 제거한 뒤 새로 설치 — 레이아웃/prefix 컨벤션이 바뀌어도 하드코딩 정리 코드 추가 없이 항상 수렴
  • 하드코딩된 구버전 레이아웃 정리(.codex/prompts 등)는 매니페스트 이전 락 전용 fallback으로 격하
  • sync는 항상 전체 클린 재설치, 스킬 소스에서 제거된 스킬은 언인스톨
  • 락에 internal 스킬도 기록, target 밖을 가리키는 경로는 제거 시 무시(traversal 방지)
  • 검증: 재설치 멱등성 / 컨벤션 변경 시뮬레이션 / 매니페스트 없는 구버전 락 fallback / 소스 제거 스킬 언인스톨 / traversal 가드 시나리오 전부 통과, pytest 27 + test-skill.sh 통과

chordpli added 3 commits June 11, 2026 07:36
Extend quivo to install skills for 6 agents (up from 2).

New adapters:
- GeminiAdapter  — .gemini/commands/v-<name>.md
- QwenAdapter    — .qwen/commands/v-<name>.md
- WindsurfAdapter — .windsurf/workflows/v-<name>.md
- CursorAdapter  — .cursor/skills/q-<name>/SKILL.md
                   (context: .cursor/rules/quivo-skills.mdc, alwaysApply: true)

Architecture:
- CommandsBaseAdapter: new abstract base for single-file slash-command
  adapters (v- prefix). Strips YAML frontmatter, writes body + policy.
- BaseAdapter: gains context_file_mdc flag and install_display_name().
- context.py: update_context_file() now accepts mdc= (prepends MDC
  frontmatter preamble to new files) and name_fn= (so GEMINI.md shows
  v-<name>, CLAUDE.md shows q-<name>).
- AgentChoice: expanded to claude/codex/gemini/qwen/windsurf/cursor/both/all.
  Interactive prompt updated; both stays as backward-compat claude+codex alias.
- _build_adapters(): centralised factory shared by init and sync.
- Per-adapter agents filtering: skills with agents: [...] are only installed
  for listed agents.
- cleanup.py: legacy paths extended to cover all new adapter directories.
- skill.yamls: agents field updated to include all six agents.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant