diff --git a/.gitignore b/.gitignore index 3b1d5e7a..6c28c103 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,10 @@ app/src/androidTest/assets/npu_suites/ # Native libs come from staged SDK AARs (QHexRT/LlamaCPP/ONNX). Do not vendor QAIRT here. app/src/main/jniLibs/ + +# Claude / cross-agent tooling. `.claude/skills/` (if present) is the canonical, human-edited +# skill tree; `.agents/skills/` is a generated mirror for non-Claude tooling (e.g. Codex), kept +# local/ignored for now pending a decision on what is safe to publish from this public repo. +# See scripts/sync-skills.sh. +.claude/* +.agents/* diff --git a/scripts/sync-skills.sh b/scripts/sync-skills.sh new file mode 100755 index 00000000..feef1320 --- /dev/null +++ b/scripts/sync-skills.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Skill trees: ONE canonical source, one generated mirror (DRY). +# +# `.claude/skills/` is the CANONICAL, human-edited skill tree (Claude Code reads it directly). +# `.agents/skills/` is a GENERATED MIRROR for non-Claude agent tooling (e.g. Codex, which has no native +# "skills" mechanism but can be pointed at plain markdown runbooks from AGENTS.md) — never hand-edit it. +# This script regenerates the mirror; `--check` verifies they are in sync. +# +# Why a copy and not a symlink: a checkout without core.symlinks=true (common on Windows) materializes +# git symlinks as text files, which would break Codex's reads. A generated copy + a drift check is portable. +# +# Both `.claude/*` and `.agents/*` are currently git-ignored in this repo — the skill content is being kept +# local until a public/private decision is made, but the two trees still need to agree with each other for +# whichever agent happens to read the mirror. +# +# Usage: +# scripts/sync-skills.sh # regenerate .agents/skills from .claude/skills +# scripts/sync-skills.sh --check # exit non-zero if the mirror is stale +set -euo pipefail + +# Repo root by WALKING UP to the nearest `.git`, not by counting parent hops. +_walk_up_to_repo_root() { + local d; d="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + while [ "$d" != "/" ]; do + if [ -e "$d/.git" ]; then printf '%s\n' "$d"; return 0; fi + d="$(dirname "$d")" + done + echo "FATAL: no repo root above ${BASH_SOURCE[0]} (looked for .git)" >&2 + return 1 +} +REPO="$(_walk_up_to_repo_root)" || exit 1 +SRC="$REPO/.claude/skills" +DST="$REPO/.agents/skills" + +if [[ ! -d "$SRC" ]]; then + echo "No .claude/skills/ yet — nothing to mirror." >&2 + exit 0 +fi + +if [[ "${1:-}" == "--check" ]]; then + if diff -rq "$SRC" "$DST" >/dev/null 2>&1; then + echo "skills in sync: .agents/skills mirrors .claude/skills" + exit 0 + fi + echo "ERROR: .agents/skills is STALE. Run scripts/sync-skills.sh (edit .claude/skills, never .agents/skills)." >&2 + diff -rq "$SRC" "$DST" || true + exit 1 +fi + +rm -rf "$DST" +mkdir -p "$(dirname "$DST")" +cp -R "$SRC" "$DST" +echo "regenerated .agents/skills from .claude/skills"