diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 2c2dafc..12df680 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -1,11 +1,74 @@ { - "name": "template", + "name": "jsc", "plugins": [ { - "name": "jsc-template", + "name": "jsc-ask", "source": { "source": "url", - "url": "https://gitea.jsc.idv.tw/plugins/template.git" + "url": "https://gitea.jsc.idv.tw/plugins/ask.git" + } + }, + { + "name": "jsc-cli", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/cli.git" + } + }, + { + "name": "jsc-git", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/git.git" + } + }, + { + "name": "jsc-gitea", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/gitea.git" + } + }, + { + "name": "jsc-hooks", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/hooks.git" + } + }, + { + "name": "jsc-log", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/log.git" + } + }, + { + "name": "jsc-meta", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/meta.git" + } + }, + { + "name": "jsc-pkg", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/pkg.git" + } + }, + { + "name": "jsc-review", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/review.git" + } + }, + { + "name": "jsc-sdlc", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/sdlc.git" } } ] diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index e3d2436..28bb223 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,14 +1,89 @@ { - "name": "template", - "description": "JSC 跨 AI 助理共用 skills 的 Claude Code marketplace。", + "name": "jsc", + "description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。", "owner": { "name": "JSC" }, "plugins": [ { - "name": "jsc-template", - "source": "./", - "description": "JSC 共用 skills(跨 AI 助理)" + "name": "jsc-ask", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/ask.git" + }, + "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)" + }, + { + "name": "jsc-cli", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/cli.git" + }, + "description": "CLI 偵測、模型能力標籤與技能庫批次部署" + }, + { + "name": "jsc-git", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/git.git" + }, + "description": "Commit 分組認可與 Push Request 建立" + }, + { + "name": "jsc-gitea", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/gitea.git" + }, + "description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步" + }, + { + "name": "jsc-hooks", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/hooks.git" + }, + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄" + }, + { + "name": "jsc-log", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/log.git" + }, + "description": "工作日誌(LOG_* wiki 頁)與技能使用統計" + }, + { + "name": "jsc-meta", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/meta.git" + }, + "description": "技能組自我管理:新建/更新/刪除技能與技能準則" + }, + { + "name": "jsc-pkg", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/pkg.git" + }, + "description": "套件批次更新(nodejs/python/dotnet),失敗還原" + }, + { + "name": "jsc-review", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/review.git" + }, + "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組" + }, + { + "name": "jsc-sdlc", + "source": { + "source": "url", + "url": "https://gitea.jsc.idv.tw/plugins/sdlc.git" + }, + "description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index a5c2dd3..b0d0b21 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,12 +1,17 @@ { - "name": "jsc-template", - "version": "0.0.3", - "description": "JSC 跨 AI 助理共用 plugin 模板(Claude Code / Codex / Antigravity / OpenCode)。所有 skills 以 SKILL.md 為共通標準,於 Claude Code 以 /jsc-template: 前綴呼叫。", + "name": "jsc-gitea", + "version": "0.0.1", + "description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步", "skills": "./skills", "author": { "name": "JSC" }, - "homepage": "https://gitea.jsc.idv.tw/plugins/template", - "repository": "https://gitea.jsc.idv.tw/plugins/template.git", - "keywords": ["template", "skills", "cross-tool", "jsc"] + "homepage": "https://gitea.jsc.idv.tw/plugins/gitea", + "repository": "https://gitea.jsc.idv.tw/plugins/gitea.git", + "keywords": [ + "jsc", + "gitea", + "skills", + "cross-tool" + ] } diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 71a3c32..e42cfcb 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { - "name": "jsc-template", - "version": "0.0.3", - "description": "JSC 跨 AI 助理共用 plugin 模板。所有 skills 以 SKILL.md 為共通標準。", + "name": "jsc-gitea", + "version": "0.0.1", + "description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步", "skills": "./skills" } diff --git a/AGENTS.md b/AGENTS.md index a86a1f1..7538fce 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,15 @@ -# jsc-template — 共用 Skills(跨 AI 助理) +# jsc-gitea — 給 AI 助理的指引 -本 repo 是一組以 **Agent Skills(`SKILL.md`)** 標準撰寫的共用 skills,可同時被 Claude Code、Codex、Antigravity、OpenCode 使用。 +本 repo 是 jsc 技能組的 `gitea` domain(Gitea API 工具、Wiki 讀寫與存取庫批次同步),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 -## 給 AI 助理的指引 +## 規則 -- 所有可用的 skills 位於本 repo 的 `skills//SKILL.md`。 -- 在處理任務前,先比對使用者需求與各 skill `SKILL.md` frontmatter 的 `description`,若相符請載入並依其步驟執行。 -- **呼叫慣例**:在 Claude Code 與 Antigravity 中,這些 skill 以 `/jsc-template:` 呼叫;Codex 以 `$`、OpenCode 由模型依描述自動觸發 — 兩者沒有 `/jsc-template:` 前綴,不需強制加。 -- 完整清單與每個 skill 的用途,請見 `README.md` 的「Skills 目錄」。 +1. 所有交談與輸出內容使用 STE100 繁體中文,帶擬人台灣感:短句、一句一指令、台灣用語、全形標點、去 AI 味、直接講重點。完整規則的唯一來源:`plugins/meta` 的 `references/ste100.md`。 +2. 技能位於 `skills/{name}/SKILL.md`;處理任務前先比對需求與各技能的 `description`,相符就載入並依其步驟執行。 +3. 技能準則的唯一來源:`plugins/meta` 存取庫的 `references/guidelines.md`。 +4. 所有 hook 只放在 `jsc-hooks`;gitea 操作一律經由 `jsc-gitea` 的 `tools/gitea.sh`;問使用者一律依 `jsc-ask:ask` 的決策樹規則。 +5. 主 agent 不需要處理細節的流程,一律建立 sub agent 處理。 -## 慣例 +## 呼叫慣例 -- 新增 skill 一律放在 `skills//`,且 `` 使用小寫與連字號。 -- `description` 要寫清楚觸發條件(何時用、何時不用),這是跨助理自動載入的唯一依據。 +Claude Code / Antigravity:`/jsc-gitea:{name}`;Codex:`$name`;Copilot / Kiro / OpenCode:描述需求自動觸發。 diff --git a/README.md b/README.md index 6c6669e..974c9f2 100644 --- a/README.md +++ b/README.md @@ -1,228 +1,64 @@ -# jsc-template — 跨 AI 助理 Plugin 模板 +# jsc-gitea — Gitea 操作 -一個可同時被 **Claude Code、Codex、Antigravity、OpenCode、GitHub Copilot** 安裝的 plugin 模板。 -核心是以 [Agent Skills(`SKILL.md`)](https://agentskills.io) 標準撰寫的共用 skills(唯一真實來源放在 `skills/`), -搭配各助理各自的 plugin manifest,讓**同一個 repo** 可用各家**原生 plugin CLI** 安裝。 -在 Claude Code 與 Antigravity 中,skill 以 **`/jsc-template:` 前綴**呼叫(例如 `/jsc-template:hello`)。 +jsc 技能組的 gitea domain:Gitea API 的統一入口。所有 jsc 技能需要操作 gitea(wiki、repo、PR)時,一律經由本 domain 的工具或技能,不可自行拼 API 呼叫。 ---- +## 安裝、更新、移除 -## 前綴與呼叫方式 +Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安裝 token 為 `jsc-gitea@jsc`。每個指令一行: -| 助理 | 安裝方式 | 呼叫 | `/jsc-template:` 前綴 | +| CLI | 安裝 | 更新 | 移除 | | --- | --- | --- | --- | -| Claude Code | `claude plugin`(marketplace) | `/jsc-template:` 或自動觸發 | ✅ | -| Codex | `codex plugin`(marketplace) | `$` 或 `/skills` 選單 | ❌(用 `$name`) | -| Antigravity | `agy plugin install` | `/jsc-template:` 或自動觸發 | ✅ | -| OpenCode | skills 目錄(複製/clone) | 描述需求自動觸發 | ❌(依名稱) | -| GitHub Copilot CLI | `copilot plugin`(marketplace) | 自然語言或 plugin skills | ❌(無 `/jsc-template:` 前綴) | +| claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && claude plugin install jsc-gitea@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-gitea@jsc` | `claude plugin uninstall jsc-gitea@jsc` | +| codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && codex plugin add jsc-gitea@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-gitea@jsc` | +| copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && copilot plugin install jsc-gitea@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-gitea@jsc` | `copilot plugin uninstall jsc-gitea@jsc` | +| antigravity | `git clone https://gitea.jsc.idv.tw/plugins/gitea.git ~/plugins/gitea && agy plugin install ~/plugins/gitea` | `git -C ~/plugins/gitea pull && agy plugin uninstall jsc-gitea && agy plugin install ~/plugins/gitea` | `agy plugin uninstall jsc-gitea` | +| kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && kiro-cli plugin install jsc-gitea@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-gitea@jsc` | `kiro-cli plugin uninstall jsc-gitea@jsc` | -> Codex 不支援自訂前綴(skill 以 `$name` 呼叫);OpenCode 由模型依描述自動呼叫;Copilot CLI 透過原生 plugin 安裝後以自然語言或 plugin skills 使用。三者皆**不強制**前綴。 +> antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用 `/jsc-cli:deploy`。 ---- +## 工具 -## 目錄結構 - -同一個 repo 同時帶四種 manifest,彼此以路徑隔離、互不干擾;各助理都讀同一份 `skills/`。 +`tools/gitea.sh`(POSIX shell + curl): ``` -template/ -├── .claude-plugin/ -│ ├── plugin.json # Claude 外掛定義(name: "jsc-template") -│ └── marketplace.json # Claude marketplace(name: "template",source 指向本 repo) -├── .codex-plugin/ -│ └── plugin.json # Codex 外掛定義(name: "jsc-template",skills: "./skills") -├── .agents/plugins/ -│ └── marketplace.json # Codex marketplace(name: "template",url source 指向本 repo) -├── plugin.json # Antigravity 外掛定義(name: "jsc-template",skills: "./skills/") -├── skills/ # ★ 唯一真實來源:所有 skills -│ └── hello/SKILL.md -├── AGENTS.md # 跨助理共用指引 -└── README.md +gitea.sh owners # 列出可讀取的 owner +gitea.sh repos # 列出 owner 的 repo 全名 +gitea.sh default-branch / +gitea.sh clone-url / +gitea.sh wiki-repo # 解析頁面類型的 wiki 位置(見環境變數) +gitea.sh wiki-list / +gitea.sh wiki-get / # 不存在 exit 4 +gitea.sh wiki-put / # 自動判斷新建或更新 +gitea.sh pr-create / <body-file> +gitea.sh api <METHOD> <path> [json-file] ``` ---- - -## 安裝 / 更新 / 移除(各助理) - -> 指令中的 repo 網址換成你的:`https://gitea.jsc.idv.tw/plugins/template.git` -> -> **Claude / Codex 從 git URL 安裝(會 clone 遠端),請先把本 repo `push` 到 gitea。** -> **Antigravity 的 `agy plugin install <url>` 目前只支援 github.com**;gitea 請改用「clone + 本地路徑」(見 Antigravity 節)。 -> 本機/離線:Claude 可用本地路徑加 marketplace;Antigravity 用本地路徑安裝。 - -> **⚠ 從 0.0.1 升上來的人請先移除再安裝。** 0.0.2 起 marketplace 名由 `jsc-plugins` 改為 `template` -> (舊名與 `jsc-persona` 的 marketplace 撞名)。各助理以 `<plugin 名>@<marketplace 名>` 當安裝識別鍵, -> 改名後是兩筆獨立條目,**不能用 update 遷移**: -> -> ```bash -> claude plugin uninstall jsc-template@jsc-plugins -> claude plugin marketplace remove jsc-plugins # 若你沒有裝 jsc-persona 才移除,它也用這個名字 -> claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/template.git -> claude plugin install jsc-template@template -> ``` -> -> 移除後記得清掉 `~/.claude/settings.json` 裡 `enabledPlugins` 的舊鍵 `jsc-template@jsc-plugins`, -> 並重開工作階段。Codex/Copilot 同理(`remove`/`uninstall` 後重新 `add`+`install`)。 - -### Claude Code - -```bash -# 安裝 -claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/template.git -claude plugin install jsc-template@template - -# 更新 -claude plugin marketplace update template -claude plugin update jsc-template@template - -# 移除 -claude plugin uninstall jsc-template@template -claude plugin marketplace remove template -``` - -- 工作階段內 slash 版(等價):把 `claude plugin` 換成 `/plugin`。 -- 本機開發(免 push):`claude plugin marketplace add C:\Users\h3285\source\repos.plugins\template`(本地路徑)後再 install。 -- **呼叫**:`/jsc-template:<name>`(例 `/jsc-template:hello`)。 - -### Codex - -```bash -# 安裝 -codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/template.git -codex plugin add jsc-template@template - -# 更新(重新抓取 marketplace 的 git 快照) -codex plugin marketplace upgrade template - -# 移除 -codex plugin remove jsc-template@template -codex plugin marketplace remove template -``` - -- 安裝 token `jsc-template@template` = plugin 名(`.codex-plugin/plugin.json` 的 `name`)@ marketplace 名(`.agents/plugins/marketplace.json` 的 `name`)。 -- 本 repo 的 Codex marketplace 以 `url` 來源指向自己,故 Codex **一律從 gitea 安裝**(需先 push);安裝後重啟 Codex。 -- **呼叫**:`$<name>`(例 `$hello`),或用 `/skills` 選單。 - -### Antigravity(`agy`) - -> `agy plugin install <url>` 目前**只支援 github.com**;gitea 等自架 git 不支援 URL 安裝,請先 `git clone` 再用**本地路徑**安裝。 - -```bash -# 安裝:clone 後用本地路徑 -git clone https://gitea.jsc.idv.tw/plugins/template.git ~/plugins/template -agy plugin install ~/plugins/template - -# 更新(agy 無 update 子指令 → git pull 後重裝) -git -C ~/plugins/template pull -agy plugin uninstall jsc-template -agy plugin install ~/plugins/template - -# 移除 -agy plugin uninstall jsc-template -``` - -- 若把 skills 放到 GitHub,則可直接 `agy plugin install https://github.com/<owner>/<repo>`。 -- 其他:`agy plugin list`、`agy plugin enable jsc-template` / `disable jsc-template`、`agy plugin validate <path>`。安裝後重啟工作階段。 -- **呼叫**:`/jsc-template:<name>`(例 `/jsc-template:hello`)或依描述自動觸發。 - -### OpenCode - -OpenCode 的「plugin」是 TypeScript/npm 套件,不適用於 skill 包;skills 改用**目錄安裝**。 -OpenCode 會讀 `~/.config/opencode/skills/`(也會讀 `~/.claude/skills/`、`~/.agents/skills/`)。 - -```bash -# 安裝 -git clone https://gitea.jsc.idv.tw/plugins/template.git ~/plugins/template -mkdir -p ~/.config/opencode/skills -cp -r ~/plugins/template/skills/* ~/.config/opencode/skills/ - -# 更新 -git -C ~/plugins/template pull -cp -r ~/plugins/template/skills/* ~/.config/opencode/skills/ - -# 移除 -rm -rf ~/.config/opencode/skills/hello -``` - -> **Windows PowerShell**:`cp -r A B` → `Copy-Item A B -Recurse -Force`、`rm -rf X` → `Remove-Item X -Recurse -Force`、`~` → `$HOME`。 - -- **呼叫**:直接描述需求,模型會依 skill 描述自動透過 skill 工具呼叫。 - -### GitHub Copilot CLI - -Copilot CLI 支援與 Claude Code 類似的原生 plugin / marketplace 指令,可直接從 marketplace 安裝、更新與移除本 plugin。 - -```bash -# 安裝 -copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/template.git -copilot plugin install jsc-template@template - -# 更新 -copilot plugin marketplace update template -copilot plugin update jsc-template@template - -# 移除 -copilot plugin uninstall jsc-template@template -copilot plugin marketplace remove template -``` - -- 安裝 token `jsc-template@template` = plugin 名(plugin manifest 的 `name`)@ marketplace 名。 -- `copilot plugin marketplace add` 支援 GitHub `owner/repo`、git URL 與本地路徑;Gitea repo 可用上方 HTTPS URL。 -- **呼叫**:在 Copilot CLI 中用自然語言描述需求,例如 `copilot -i "跑 hello 確認 plugin 裝好了"`。 - ---- - -## 用 CLI 直接執行 skill(headless / 一次性) - -安裝好之後,不必進互動介面,一行指令就能叫某個 skill 跑完並印出結果: - -| 助理 | headless 指令 | 執行 `hello` skill | -| --- | --- | --- | -| Claude Code | `claude -p "<prompt>"` | `claude -p "/jsc-template:hello"` | -| Codex | `codex exec "<prompt>"` | `codex exec '$hello'` | -| Antigravity | `agy -p "<prompt>"` | `agy -p "/jsc-template:hello"` | -| OpenCode | `opencode run "<message>"` | `opencode run "用 hello skill 打個招呼"` | -| GitHub Copilot CLI | `copilot -p "<message>"` | `copilot -p "用 hello skill 打個招呼"` | - -- Claude / Antigravity 支援 `/jsc-template:` 前綴,直接 `-p "/jsc-template:<name>"` 即可。 -- Codex 以 `$<name>` 觸發;在 shell 請用**單引號**避免 `$` 被展開:`codex exec '$hello'`。 -- OpenCode 與 Copilot 沒有前綴,用自然語言描述需求;Copilot CLI 會讀取已安裝 plugin 提供的 skills。 -- 帶引數就接在後面,例如 `claude -p "/jsc-template:hello 參數"`、`codex exec '$hello 參數'`。 - ---- - ## Skills 目錄 -> 此區塊列出本 plugin 內含的所有 skills(名稱/描述/使用方法)。 -> 新增或修改 skill 後,請同步手動更新標記之間的內容。 +呼叫方式:Claude / Antigravity `/jsc-gitea:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。 <!-- JSC-SKILLS:START --> -### `hello` +### `wiki` -範例 skill,用來驗證 jsc plugin 是否安裝成功,也是新增 skill 的範本。當使用者輸入 hello、想測試 plugin、或想看 skill 模板長什麼樣子時觸發;回覆一句問候並簡述此 plugin 的用途。 +Gitea wiki 頁讀寫的統一入口:依頁面類型(QUESTION / PLAN / ANALYZE / MAINTAIN / REPO / LOG)解析 wiki 所在的 `{owner}/{repo}`,未設定環境變數時以決策樹詢問。 -- **Claude Code / Antigravity**:`/jsc-template:hello` -- **Codex**:`$hello`,或用 `/skills` 選單 -- **OpenCode / GitHub Copilot CLI**:描述需求自動觸發 +### `repo-sync` + +存取庫批次同步:列出 owner → 使用者選擇 → 逐 repo(sub agent)clone 或切 develop/master 更新;有變更就開分支 commit、push、PR。 <!-- JSC-SKILLS:END --> ---- +## 環境變數 -## 新增一個 skill +| 變數 | 用途 | 未設定時 | +| --- | --- | --- | +| `GITEA_HOST` | Gitea 站台(可省略 scheme,預設 https) | 詢問使用者 | +| `GITEA_TOKEN` | Gitea API token | 詢問使用者 | +| `JSC_WIKI_REPO_{TYPE}` | 各類型 wiki 頁的 `{owner}/{repo}`;TYPE = QUESTION / PLAN / ANALYZE / MAINTAIN / REPO / LOG | 退回 `JSC_WIKI_REPO` | +| `JSC_WIKI_REPO` | 共用預設的 wiki `{owner}/{repo}` | 詢問使用者 | -1. 複製範本:`cp -r skills/hello skills/<your-skill-name>` -2. 編輯 `skills/<your-skill-name>/SKILL.md` 的 frontmatter: - - `name`:小寫、數字、連字號(`-`),最長 64 字元。**這就是 Claude Code / Antigravity 的 `/jsc-template:<name>`**。 - - `description`:第三人稱,寫清楚「何時用、何時不用」與觸發關鍵字 — 這是各助理自動載入的唯一依據。 -3. 在內文寫下 skill 的具體步驟。 -4. 手動把這個 skill 補進上方「Skills 目錄」區塊。 -5. **bump 版本並 push**:各助理都以 git 內容/版本判斷更新,請把 `.claude-plugin/plugin.json`、`.codex-plugin/plugin.json`、`plugin.json` 三個 manifest 的 `version` 一起 bump,commit 後 push 到 gitea。 -6. 讓各助理更新: - - Claude:`claude plugin update jsc-template@template` - - Codex:`codex plugin marketplace upgrade template` - - Antigravity:`git -C ~/jsc-plugin pull && agy plugin uninstall jsc-template && agy plugin install ~/jsc-plugin` - - OpenCode:`git pull` 後重新複製 `skills/` - - Copilot:`copilot plugin marketplace update template && copilot plugin update jsc-template@template` +## 相關 domain + +- [`jsc-ask`](https://gitea.jsc.idv.tw/plugins/ask):wiki 位置未設定時的決策樹詢問 +- [`jsc-git`](https://gitea.jsc.idv.tw/plugins/git):repo-sync 的 commit 與 PR diff --git a/plugin.json b/plugin.json index 4873395..0f65b1e 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { - "name": "jsc-template", - "version": "0.0.3", - "description": "JSC 跨 AI 助理共用 plugin 模板。所有 skills 以 SKILL.md 為共通標準;於 Antigravity 以 /jsc-template: 前綴呼叫。", + "name": "jsc-gitea", + "version": "0.0.1", + "description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步", "skills": "./skills/" -} \ No newline at end of file +} diff --git a/skills/hello/SKILL.md b/skills/hello/SKILL.md deleted file mode 100644 index c5be22f..0000000 --- a/skills/hello/SKILL.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: hello -description: 範例 skill,用來驗證 jsc plugin 是否安裝成功,也是新增 skill 的範本。當使用者輸入 hello、想測試 plugin、或想看 skill 模板長什麼樣子時觸發;回覆一句問候並簡述此 plugin 的用途。 ---- - -# hello(範例 skill) - -這是 `jsc-template` plugin 的範例 skill。它有兩個用途: - -1. **驗證安裝** — 跨各家 AI 助理確認 skill 已被正確載入。 -2. **作為範本** — 複製這個資料夾即可新增一個新的 skill。 - -## 呼叫方式 - -| 助理 | 呼叫方式 | -| --- | --- | -| Claude Code | `/jsc-template:hello` | -| Antigravity | `/jsc-template:hello`,或描述需求自動觸發 | -| Codex | 在提示詞輸入 `$hello`,或用 `/skills` 選單 | -| OpenCode | 直接描述需求,模型會透過 skill 工具自動呼叫 | -| GitHub Copilot CLI | 直接描述需求,Copilot 會讀取已安裝 plugin 的 skills | - -## 行為 - -當這個 skill 被觸發時: - -1. 回覆「Hello from **jsc** 👋」。 -2. 用一句話說明 `jsc-template` 是一個跨 AI 助理的共用 skill 集合。 -3. 提示使用者可以在 README 的「Skills 目錄」查看所有可用的 skills。 - -## 如何以此為範本新增 skill - -1. 複製 `skills/hello/` 為 `skills/<your-skill-name>/`。 -2. 修改 `SKILL.md` 的 frontmatter: - - `name`:小寫、數字、連字號(`-`),最長 64 字元。**這個名稱會成為 Claude Code / Antigravity 的 `/jsc-template:<name>` 指令**。 - - `description`:第三人稱,寫清楚「什麼時候該用、什麼時候不該用」與觸發關鍵字 — 各家助理靠這段文字決定是否自動載入。 -3. 在內文寫下 skill 的具體步驟。 -4. 手動把新 skill 補進 README 的「Skills 目錄」區塊。 diff --git a/skills/repo-sync/SKILL.md b/skills/repo-sync/SKILL.md new file mode 100644 index 0000000..564d9b1 --- /dev/null +++ b/skills/repo-sync/SKILL.md @@ -0,0 +1,17 @@ +--- +name: repo-sync +description: Batch-sync all readable repos of a chosen Gitea owner into the working directory. List owners, let the user pick, then clone or update each repo; local changes become a branch, commit, push, and PR to develop or master. Use for workspace bootstrap or bulk refresh; not for a single repo. +--- + +# repo-sync — batch-sync repositories + +## Steps + +1. Run `tools/gitea.sh owners` to list every `{owner}` the user can read. +2. Ask the user which `{owner}` to sync, per the `jsc-ask:ask` rules. Every option states the owner's repo count and impact scope. +3. Run `tools/gitea.sh repos {owner}` to list every readable `{repo}` under that owner. +4. Sync each `{repo}` one by one. This step **MUST run as a sub agent** (one sub agent per repo): + 1. Missing locally → `git clone` into the working directory (clone URL from `tools/gitea.sh clone-url`). + 2. Present locally → switch to `develop`, else `master` (or the result of `tools/gitea.sh default-branch`), then `git pull`. + 3. Local file changes → create a branch from develop or master, commit via `jsc-git:commit`, push, then open a PR back to develop or master via `jsc-git:pr`. +5. Report the sync result for every repo: cloned, updated, PR created, or the failure reason. diff --git a/skills/wiki/SKILL.md b/skills/wiki/SKILL.md new file mode 100644 index 0000000..8e85c5a --- /dev/null +++ b/skills/wiki/SKILL.md @@ -0,0 +1,29 @@ +--- +name: wiki +description: Read or write a Gitea wiki page through tools/gitea.sh with GITEA_TOKEN. Resolve the wiki repo per page type (QUESTION/PLAN/ANALYZE/MAINTAIN/REPO/LOG) via gitea.sh wiki-repo, falling back to JSC_WIKI_REPO or a decision-tree question. Used by jsc-ask, jsc-sdlc, and jsc-log for all wiki pages; not for repo code files. +--- + +# wiki — read and write Gitea wiki pages + +Every wiki operation in the jsc skill set goes through this skill. One entry point, one permission path. + +## Resolve the wiki location + +Different page types can live in different `{owner}/{repo}` repos, classified by page-name prefix: `QUESTION`, `PLAN`, `ANALYZE`, `MAINTAIN`, `REPO`, `LOG`. + +1. Run `tools/gitea.sh wiki-repo {TYPE}` (TYPE = the page-name prefix). Resolution order: `JSC_WIKI_REPO_{TYPE}` > `JSC_WIKI_REPO`. When either is set, use it and skip the question. +2. On exit 3 (neither is set), ask the user for that page type's `{owner}/{repo}` per the `jsc-ask:ask` rules, and suggest setting `JSC_WIKI_REPO_{TYPE}` (can differ per type) or `JSC_WIKI_REPO` (shared default). + +## Operations + +| Action | Command | +| --- | --- | +| list pages | `tools/gitea.sh wiki-list {owner}/{repo}` | +| read page | `tools/gitea.sh wiki-get {owner}/{repo} {page}` (exit 4 when missing) | +| write page | write the content to a temp file first, then `tools/gitea.sh wiki-put {owner}/{repo} {page} {file}` (creates or updates automatically) | + +## Rules + +1. Page names must follow the wiki naming table in the skill guidelines (see `jsc-meta/references/guidelines.md`). +2. To update a contents page (`*_CONTENTS`): `wiki-get` it first, apply the template to append or modify, then `wiki-put` the whole page back. Never overwrite entries owned by others. +3. Write all wiki content in UTF-8 Traditional Chinese, per the STE100 output rule. diff --git a/tools/gitea.sh b/tools/gitea.sh new file mode 100755 index 0000000..a31d21b --- /dev/null +++ b/tools/gitea.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env sh +# gitea.sh — Gitea API 工具(curl + GITEA_TOKEN)。 +# 用法: +# gitea.sh owners # 列出可讀取的 owner(自己 + 組織) +# gitea.sh repos <owner> # 列出 owner 底下可讀取的 repo(全名) +# gitea.sh default-branch <owner>/<repo> # 印出預設分支 +# gitea.sh clone-url <owner>/<repo> # 印出 clone URL +# gitea.sh wiki-repo <TYPE> # 解析頁面類型的 wiki 位置: +# TYPE = QUESTION|PLAN|ANALYZE|MAINTAIN|REPO|LOG(即頁名前綴) +# 依序取 JSC_WIKI_REPO_{TYPE} > JSC_WIKI_REPO;都未設定 exit 3(呼叫端須詢問使用者) +# gitea.sh wiki-list <owner>/<repo> # 列出 wiki 頁名 +# gitea.sh wiki-get <owner>/<repo> <page> # 印出 wiki 頁 markdown;不存在時 exit 4 +# gitea.sh wiki-put <owner>/<repo> <page> <file> # 建立或更新 wiki 頁(內容取自檔案) +# gitea.sh pr-create <owner>/<repo> <head> <base> <title> <body-file> # 建立 PR,印出 PR URL +# gitea.sh pr-depend <owner>/<repo> <pr-index> <dep-owner>/<dep-repo> <dep-index> +# 把 PR 掛上前置 PR 依賴;依賴未關閉前 Gitea 會阻擋合併 +# gitea.sh repo-set <owner>/<repo> <description> [website] # 設定 repo 描述與網頁 +# gitea.sh api <METHOD> <path> [json-file] # 原始 API 呼叫(path 以 /repos/... 起始) +# 環境變數: GITEA_HOST(例 https://gitea.jsc.idv.tw)、GITEA_TOKEN +set -eu + +: "${GITEA_HOST:?GITEA_HOST is required}" +: "${GITEA_TOKEN:?GITEA_TOKEN is required}" +case "$GITEA_HOST" in http://*|https://*) HOST="$GITEA_HOST" ;; *) HOST="https://$GITEA_HOST" ;; esac +API="${HOST%/}/api/v1" + +req() { # METHOD path [json-file] -> body(HTTP >= 400 時 exit 4) + method="$1"; path="$2"; body_file="${3:-}" + if [ -n "$body_file" ]; then + out=$(curl -sS -w '\n%{http_code}' -X "$method" \ + -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" \ + --data-binary "@$body_file" "$API$path") + else + out=$(curl -sS -w '\n%{http_code}' -X "$method" \ + -H "Authorization: token $GITEA_TOKEN" "$API$path") + fi + code=$(printf '%s' "$out" | tail -n1) + printf '%s\n' "$out" | sed '$d' + [ "$code" -lt 400 ] +} + +json_field() { # stdin JSON -> 每個物件的指定欄位一行(陣列或單一物件皆可) + python3 -c ' +import json,sys +d=json.load(sys.stdin); k=sys.argv[1] +items=d if isinstance(d,list) else [d] +for i in items: + v=i.get(k,"") + if v!="" and v is not None: print(v) +' "$1" +} + +cmd="${1:?usage: gitea.sh <command> ...}"; shift +case "$cmd" in + owners) + { req GET "/user" | json_field login + req GET "/user/orgs?limit=50" | json_field username; } | sort -u ;; + repos) + owner="${1:?owner required}" + # 分頁抓 owner 的 repo(org 與 user 端點擇一成功) + page=1 + while :; do + out=$(req GET "/orgs/$owner/repos?limit=50&page=$page" 2>/dev/null) \ + || out=$(req GET "/users/$owner/repos?limit=50&page=$page") + names=$(printf '%s' "$out" | json_field full_name) + [ -n "$names" ] || break + printf '%s\n' "$names" + page=$((page+1)) + done ;; + default-branch) + or="${1:?owner/repo required}" + req GET "/repos/$or" | json_field default_branch ;; + clone-url) + or="${1:?owner/repo required}" + req GET "/repos/$or" | json_field clone_url ;; + wiki-repo) + type=$(printf '%s' "${1:?TYPE required}" | tr a-z A-Z) + eval "v=\${JSC_WIKI_REPO_$type:-}" + [ -n "$v" ] || v="${JSC_WIKI_REPO:-}" + if [ -n "$v" ]; then printf '%s\n' "$v"; else + echo "no wiki repo configured for $type (set JSC_WIKI_REPO_$type or JSC_WIKI_REPO)" >&2; exit 3 + fi ;; + wiki-list) + or="${1:?owner/repo required}" + req GET "/repos/$or/wiki/pages?limit=200" | json_field title ;; + wiki-get) + or="${1:?owner/repo required}"; page="${2:?page required}" + if ! out=$(req GET "/repos/$or/wiki/page/$page" 2>/dev/null); then + echo "wiki page not found: $page" >&2; exit 4 + fi + printf '%s' "$out" | python3 -c ' +import json,sys,base64 +d=json.load(sys.stdin) +sys.stdout.write(base64.b64decode(d.get("content_base64","")).decode("utf-8")) +' ;; + wiki-put) + or="${1:?owner/repo required}"; page="${2:?page required}"; file="${3:?content file required}" + tmp=$(mktemp) + python3 -c ' +import json,sys,base64 +title,path=sys.argv[1],sys.argv[2] +content=open(path,"rb").read() +print(json.dumps({"title":title,"content_base64":base64.b64encode(content).decode()})) +' "$page" "$file" > "$tmp" + if req GET "/repos/$or/wiki/page/$page" >/dev/null 2>&1; then + req PATCH "/repos/$or/wiki/page/$page" "$tmp" >/dev/null + else + req POST "/repos/$or/wiki/new" "$tmp" >/dev/null + fi + rm -f "$tmp" + echo "OK $page" ;; + pr-create) + or="${1:?owner/repo required}"; head="${2:?head required}"; base="${3:?base required}" + title="${4:?title required}"; body_file="${5:?body file required}" + tmp=$(mktemp) + python3 -c ' +import json,sys +print(json.dumps({"head":sys.argv[1],"base":sys.argv[2],"title":sys.argv[3], + "body":open(sys.argv[4],encoding="utf-8").read()})) +' "$head" "$base" "$title" "$body_file" > "$tmp" + req POST "/repos/$or/pulls" "$tmp" | json_field html_url + rm -f "$tmp" ;; + pr-depend) + or="${1:?owner/repo required}"; idx="${2:?pr index required}" + dep_or="${3:?dep owner/repo required}"; dep_idx="${4:?dep index required}" + tmp=$(mktemp) + python3 -c ' +import json,sys +o,r=sys.argv[1].split("/",1) +print(json.dumps({"owner":o,"repo":r,"index":int(sys.argv[2])})) +' "$dep_or" "$dep_idx" > "$tmp" + if ! out=$(req POST "/repos/$or/issues/$idx/dependencies" "$tmp"); then + rm -f "$tmp"; printf '%s\n' "$out" >&2; exit 4 + fi + rm -f "$tmp" + echo "OK $or#$idx depends on $dep_or#$dep_idx" ;; + repo-set) + or="${1:?owner/repo required}"; desc="${2:?description required}"; site="${3:-}" + tmp=$(mktemp) + python3 -c ' +import json,sys +b={"description":sys.argv[1]} +if len(sys.argv)>2 and sys.argv[2]: b["website"]=sys.argv[2] +print(json.dumps(b)) +' "$desc" "$site" > "$tmp" + if ! out=$(req PATCH "/repos/$or" "$tmp"); then + rm -f "$tmp"; printf '%s\n' "$out" >&2; exit 4 + fi + rm -f "$tmp" + echo "OK $or" ;; + api) + method="${1:?METHOD}"; path="${2:?path}"; body="${3:-}" + req "$method" "$path" $body ;; + *) + echo "unknown command: $cmd" >&2; exit 2 ;; +esac