Files
code/skills/code-sync/SKILL.md
T

13 KiB
Raw Blame History

name, description, argument-hint
name description argument-hint
code-sync 透過 `GITEA_TOKEN` 取得使用者有讀取權限的所有 Gitea 專案,依擁有者(owner)分組後列出讓使用者多選,再把選定擁有者底下有讀取權限的專案,於目標目錄(預設家目錄)依擁有者建立資料夾並逐一 clone;若專案已存在,則切換到 develop(再退而 master)並更新到最新。當使用者說同步 Gitea 專案、批次 clone、把某些擁有者的 repo 全抓下來、依擁有者分類 clone、批次更新本機專案到最新時觸發。不適用於:建立/刪除遠端 repo、單一專案的 clone(直接用 git 即可)、非 Gitea 的來源,或需要 push/改動遠端的操作。 [--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host <gitea 主機>] [--include-forks] [--ssh] [--yes]

code-sync — 依擁有者批次 clone/更新 Gitea 專案

五階段 skill:先做前置設定(解析 token、gitea 主機、目標根目錄),再取得使用者有讀取權限的所有專案並依擁有者分組,接著列出所有擁有者讓使用者多選,然後列出選定擁有者底下有讀取權限的專案,最後在目標根目錄依擁有者建立資料夾並逐一 clone;專案已存在時改為切換到 develop(再退而 master)並更新到最新

階段 動作
A. 前置設定 確認 GITEA_TOKEN → 決定 gitea 主機(--host$GITEA_HOST/目前 repo 的 origin/詢問)→ 決定目標根目錄(--target-dir,預設家目錄)
B. 取得可讀專案 以 token 呼叫 Gitea API 分頁取回使用者有讀取(pull)權限的所有專案 → 依 owner.login 分組
C. 選擇擁有者 列出所有擁有者(含各自專案數)→ 讓使用者多選--owner 已指定則沿用,不再詢問)
D. 列出選定專案 列出選定擁有者底下所有有讀取權限的專案(含是否已存在於本機)
E. Clone/更新 依擁有者建立資料夾 → 不存在則 clone;已存在則 fetch → 切到 develop(再退而 master)→ pull 更新到最新

共用規範(generic plugin,必要前置)

執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;任一載入不到(generic plugin 未安裝)時,先詢問使用者是否安裝 generic pluginhttps://gitea.jsc.idv.tw/plugins/generic.git),使用者不安裝則直接中斷本 skill,不得只憑下方一行摘要繼續執行:

  • /jsc:spec-output:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼。
  • /jsc:spec-execution:自動執行原則(必要決策才中斷)、不臆測/需人工確認。
  • /jsc:spec-giteaGITEA_TOKEN 機密保護(不 echo、遮蔽、clone 後還原乾淨 origin)、API 分頁完整讀取、host 決定順序。
  • /jsc:spec-git-safety:不破壞既有工作(未提交變更不強切、絕不 reset --hardclean)、develop → master 後備、pull --ff-only

本 skill 特有補充:

  • 必要決策(會中斷詢問):缺 token、無法決定 gitea 主機、目標目錄不可寫、單一專案更新時工作區有未提交變更需使用者裁示。唯一一定會中斷的點是階段 C 的擁有者多選(除非已帶 --owner)。
  • 唯讀本意:本 skill 只做 clone 與本機分支更新,不 push、不改遠端、不刪本機未追蹤檔

參數

格式:[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host <gitea 主機>] [--include-forks] [--ssh] [--yes]

  • --target-dir <根目錄>clone 的根目錄。省略時預設家目錄$HOMEWindows 為 %USERPROFILE%)。實際每個專案會 clone 到 <根目錄>/<擁有者>/<專案名>
  • --owner <擁有者,以逗號分隔>:預先指定要同步的擁有者(可多個,以逗號分隔,例如 plugins,jeffery)。帶此參數時跳過階段 C 的多選詢問,直接同步這些擁有者;若指定的擁有者不在可讀清單內,回報並略過該擁有者。
  • --host <gitea 主機>gitea 主機(如 gitea.jsc.idv.tw)。省略時依 A2 規則決定。
  • --include-forks:一併納入 fork 來的專案;省略時預設排除 forkfork=true 的 repo 不列入)。
  • --ssh:改用 SSHssh_urlclone,不帶 token省略時預設用 HTTPStoken
  • --yes:全自動。即使未帶此參數,也依「自動執行原則」盡量不中斷;但階段 C 的擁有者多選仍會詢問(除非已帶 --owner)。

階段 A:前置設定

A1. 確認 token

/jsc:spec-gitea 確認 GITEA_TOKEN(只輸出「已設定/未設定」,不可 echo token 本身);未設定 → 回報「缺少 GITEA_TOKEN 環境變數,無法存取 Gitea API」並停止(不要請使用者把 token 貼進對話)。

A2. 決定 gitea 主機

/jsc:spec-gitea 的 host 決定順序:--host$GITEA_HOST → 目前 repo 的 origin → 詢問使用者(不臆測)。主機僅取 host 部分(如 gitea.jsc.idv.tw),組 API base 為 https://<host>/api/v1

A3. 決定目標根目錄

  • --target-dir <根目錄> → 採用(可含 ~,需展開為實際家目錄)。
  • 省略 → 預設家目錄($HOME)。
  • 確認根目錄存在且可寫;不存在時先建立(mkdir -p)。若無法建立或不可寫,回報並停止。

輸出本次設定摘要(主機、根目錄、是否含 fork、HTTPS/SSH),再進入階段 B。


階段 B:取得使用者有讀取權限的所有專案並依擁有者分組

B1. 分頁取回可讀專案

以 token 呼叫 GET /api/v1/user/repos(回傳 token 對應使用者有權限存取的 repo:自有、組織成員、協作者),逐頁抓到取回筆數不足一頁為止(每頁上限以 API 為準,常見 limit=50):

# 不可印出含 token 的指令;token 以 Header 帶入,輸出時遮蔽
page=1
curl -sS \
  -H "Authorization: token ${GITEA_TOKEN}" \
  "https://<host>/api/v1/user/repos?page=${page}&limit=50"
  • 持續累加 page 直到某頁回傳筆數 < limit(或回空陣列)為止,確保取得全部而非只第一頁。
  • API 失敗(401/403/網路錯誤)→ 回報錯誤(遮蔽 token)並停止;401/403 多半是 token 失效或權限不足。

B2. 篩選與正規化

每筆 repo 取下列欄位:owner.loginnamefull_nameclone_urlssh_urlpermissions.pullforkdefault_branch

  • 只保留 permissions.pull == true 的專案(確有讀取權限)。
  • 預設排除 fork == true;帶 --include-forks 才納入。
  • 去除重複(以 full_name 為鍵)。

B3. 依擁有者分組

owner.login 分組,統計每個擁有者底下的可讀專案數,並依擁有者名稱排序(專案數多者可優先列出)。無任何可讀專案 → 回報並結束。


階段 C:列出所有擁有者讓使用者多選

輸出一張「擁有者清單」表,含編號、擁有者、可讀專案數:

# 擁有者 可讀專案數

選擇方式:

  • --owner 參數 → 直接採用其中的擁有者,跳過詢問;清單中沒有的擁有者回報並略過。
  • 未帶 --owner → 這是不可忽略的必要決策,必須讓使用者多選
    • 擁有者數量 ≤ 4 時,可用助理的多選提問元件(如 Claude Code 的多選問題)。
    • 擁有者較多時,請使用者直接以編號或擁有者名稱回覆(支援多個,以逗號或空白分隔),並支援回覆 all全部 代表全選。
    • 解析使用者回覆為擁有者集合;無法對應的輸入請回報並請使用者重選,不臆測

選定後輸出「將同步的擁有者:…」再進入階段 D。


階段 D:列出選定擁有者底下有讀取權限的專案

對每個選定擁有者,列出其底下所有可讀專案,並標示本機是否已存在(<根目錄>/<擁有者>/<專案名>/.git 是否存在):

擁有者 專案 狀態
🆕 將 clone♻️ 已存在將更新

輸出總計(將 clone N 個、將更新 M 個),除非使用者要求先確認,否則直接進入階段 E 執行。


階段 E:依擁有者建立資料夾並逐一 clone/更新

對選定擁有者底下的每個專案,依序處理(一次一個,互不干擾):

E1. 建立擁有者資料夾

mkdir -p "<根目錄>/<擁有者>"

專案目標路徑為 <根目錄>/<擁有者>/<專案名>

E2. 專案不存在 → clone

  • HTTPS(預設):以 token 組 clone URL用變數帶入、不可印出clone 完成後還原乾淨 origin

    dest="<根目錄>/<擁有者>/<專案名>"
    # GITEA_TOKEN 來自環境變數;clone_url 形如 https://<host>/<owner>/<repo>.git
    git clone "https://oauth2:${GITEA_TOKEN}@<host>/<owner>/<repo>.git" "${dest}"
    # 還原成不含 token 的乾淨 URL,避免 token 落地在 .git/config
    git -C "${dest}" remote set-url origin "https://<host>/<owner>/<repo>.git"
    
  • SSH--ssh:直接用 ssh_url,不帶 token

    git clone "<ssh_url>" "<根目錄>/<擁有者>/<專案名>"
    
  • clone 後若遠端有 develop,將工作分支切到 develop(沒有則維持 default_branchmaster);clone 失敗回報原因(遮蔽 token)並繼續下一個專案,不中斷整批。

E3. 專案已存在 → 切到 develop(再退而 master)並更新到最新

先盤點該專案狀態,有未提交變更則不強切

dest="<根目錄>/<擁有者>/<專案名>"
git -C "${dest}" rev-parse --is-inside-work-tree   # 確認是 git repo
git -C "${dest}" status --porcelain                # 是否乾淨
git -C "${dest}" fetch --all --prune
  • 目錄存在但不是 git repo(無 .git)→ 回報「目標已存在且非 git repo,略過」並繼續下一個;不刪除、不覆蓋

  • 工作區有未提交變更status --porcelain 非空)→ 回報「有未提交變更,略過更新以免覆蓋」並繼續下一個;絕不強制丟棄。

  • 工作區乾淨 → 依序選擇要更新的分支並切換、拉取:

    if git -C "${dest}" rev-parse --verify --quiet origin/develop; then
        git -C "${dest}" switch develop 2>/dev/null || git -C "${dest}" switch -c develop --track origin/develop
    elif git -C "${dest}" rev-parse --verify --quiet origin/master; then
        git -C "${dest}" switch master 2>/dev/null || git -C "${dest}" switch -c master --track origin/master
    fi
    git -C "${dest}" pull --ff-only
    
    • origin/develop 存在 → 切到 develop;否則 origin/master 存在 → 切到 master;兩者都不存在 → 回報「找不到 develop/master」並維持原分支、略過該專案。
    • pull --ff-only 無法快進(本地與遠端分歧)→ 回報該專案需人工處理(自動 mergerebasereset),繼續下一個。
    • 已在目標分支時直接 pull --ff-only 更新即可。

E4. 逐一處理、彙整結果

  • 每個專案處理完記錄結果:🆕 已 clone♻️ 已更新到最新/⏭️ 已略過(原因)/ 失敗(原因,遮蔽 token)。
  • 單一專案失敗或略過不應中斷整批;全部處理完再輸出總結。

總結

各階段執行後輸出:

  • 階段 A:使用的 gitea 主機、目標根目錄、是否含 fork、HTTPS/SSH。
  • 階段 BC:可讀專案總數、擁有者總數、使用者選定的擁有者。
  • 階段 DE:本批處理結果統計 —— 新 clone N 個、更新 M 個、略過 K 個(列原因)、失敗 J 個(列原因,遮蔽 token),並列出各專案的本機路徑。
  • 若過程曾在指令中帶入 token,提醒確認輸出與 log 無明文 token。

呼叫方式

格式:[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host <gitea 主機>] [--include-forks] [--ssh] [--yes] — 全部可省略(根目錄預設家目錄;未帶 --owner 時於階段 C 詢問多選)。token 一律由環境變數(如 GITEA_TOKEN)提供,不寫死、不印出。

助理 呼叫
Claude Code / Antigravity /jsc:code-sync,或 /jsc:code-sync --owner plugins --target-dir ~/work/jsc:code-sync --include-forks --ssh
Codex $code-sync,或 $code-sync --owner plugins --target-dir ~/work,或用 /skills 選單
OpenCode 描述需求(如「用 GITEA_TOKEN 取得我有讀取權限的所有 Gitea 專案,依擁有者分組讓我多選,把選定擁有者的專案 clone 到家目錄;已存在的切到 develop/master 更新到最新」)自動觸發