diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 51978fa..2d55f6c 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,12 +1,12 @@ { "name": "jsc", - "version": "0.1.0", - "description": "JSC 跨 AI 助理共用 plugin 模板(Claude Code / Codex / Antigravity / OpenCode)。所有 skills 以 SKILL.md 為共通標準,於 Claude Code 以 /jsc: 前綴呼叫。", + "version": "0.0.1", + "description": "JSC 跨 AI 助理文件化 skill 集合(Claude Code / Codex / Antigravity / OpenCode):對齊 docker-compose 行內註解(doc-docker)、為專案 function 補齊 XML 文件並更新 README 功能目錄(doc-funcs)。所有 skills 以 SKILL.md 為共通標準,於 Claude Code 以 /jsc: 前綴呼叫。", "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/doc", + "repository": "https://gitea.jsc.idv.tw/plugins/doc.git", + "keywords": ["doc", "documentation", "docker-compose", "xml-doc", "skills", "cross-tool", "jsc"] } diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 2457172..689b3f2 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc", - "version": "0.1.0", - "description": "JSC 跨 AI 助理共用 plugin 模板。所有 skills 以 SKILL.md 為共通標準。", + "version": "0.0.1", + "description": "JSC 跨 AI 助理文件化 skill 集合:對齊 docker-compose 行內註解(doc-docker)、為專案 function 補齊 XML 文件並更新 README 功能目錄(doc-funcs)。所有 skills 以 SKILL.md 為共通標準。", "skills": "./skills" } diff --git a/README.md b/README.md index b5a3719..6434006 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -# jsc — 跨 AI 助理 Plugin 模板 +# jsc — 跨 AI 助理文件化 Skill 集合 -一個可同時被 **Claude Code、Codex、Antigravity、OpenCode** 安裝的 plugin 模板。 +一個可同時被 **Claude Code、Codex、Antigravity、OpenCode** 安裝的文件化 skill 集合。 +目前內含:對齊 docker-compose 行內註解(`doc-docker`)、為專案 function 補齊 XML 文件並更新 README 功能目錄(`doc-funcs`)。 核心是以 [Agent Skills(`SKILL.md`)](https://agentskills.io) 標準撰寫的共用 skills(唯一真實來源放在 `skills/`), 搭配各助理各自的 plugin manifest,讓**同一個 repo** 可用各家**原生 plugin CLI** 安裝。 -在 Claude Code 與 Antigravity 中,skill 以 **`/jsc:` 前綴**呼叫(例如 `/jsc:hello`)。 +在 Claude Code 與 Antigravity 中,skill 以 **`/jsc:` 前綴**呼叫(例如 `/jsc:doc-docker`)。 --- @@ -16,7 +17,7 @@ | Antigravity | `agy plugin install` | `/jsc:` 或自動觸發 | ✅ | | OpenCode | skills 目錄(複製/clone) | 描述需求自動觸發 | ❌(依名稱) | -> Codex 不支援自訂前綴(skill 以 `$name` 呼叫);OpenCode 由模型依描述自動呼叫。兩者皆**不強制**前綴。 +> Codex 不支援自訂前綴(skill 以 `$name` 呼叫,例 `$doc-docker`);OpenCode 由模型依描述自動呼叫。兩者皆**不強制**前綴。 --- @@ -25,7 +26,7 @@ 同一個 repo 同時帶四種 manifest,彼此以路徑隔離、互不干擾;四家都讀同一份 `skills/`。 ``` -template/ +doc/ ├── .claude-plugin/ │ ├── plugin.json # Claude 外掛定義(name: "jsc") │ └── marketplace.json # Claude marketplace(name: "jsc-plugins",source 指向本 repo) @@ -35,7 +36,10 @@ template/ │ └── marketplace.json # Codex marketplace(name: "jsc-plugins",url source 指向本 repo) ├── plugin.json # Antigravity 外掛定義(name: "jsc",skills: "./skills/") ├── skills/ # ★ 唯一真實來源:所有 skills -│ └── hello/SKILL.md +│ ├── doc-docker/ # 對齊 docker-compose 註解(含 scripts/) +│ │ ├── SKILL.md +│ │ └── scripts/ +│ └── doc-funcs/SKILL.md # 為 function 補齊 XML 文件 ├── AGENTS.md # 跨助理共用指引 └── README.md ``` @@ -169,6 +173,22 @@ rm -rf ~/.config/opencode/skills/hello - **Codex**:`$hello`,或用 `/skills` 選單 - **OpenCode**:描述需求自動觸發 +### `doc-docker` + +整理並對齊 docker-compose.yaml 的行內註解與標題區塊。當使用者要對齊 docker-compose 註解、整理 compose 檔註解欄位、更新 compose 標題日期,或提到 docker-compose、dc-tidy、align_comments、註解對齊時使用此 skill。 + +- **Claude Code / Antigravity**:`/jsc:doc-docker` +- **Codex**:`$doc-docker`,或用 `/skills` 選單 +- **OpenCode**:描述需求自動觸發 + +### `doc-funcs` + +為目前專案的每個 function 建立 .docs/ 草稿並補齊 XML 文件,最後更新 README 功能目錄。當使用者要補齊 function 文件、產生 XML doc、為每個 method 加 summary/param/remarks、建立 .docs 草稿,或提到 doc-funcs、function 文件化、XML documentation comments 時使用此 skill。 + +- **Claude Code / Antigravity**:`/jsc:doc-funcs` +- **Codex**:`$doc-funcs`,或用 `/skills` 選單 +- **OpenCode**:描述需求自動觸發 + --- diff --git a/plugin.json b/plugin.json index 9bc4496..e789728 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc", "version": "0.1.0", - "description": "JSC 跨 AI 助理共用 plugin 模板。所有 skills 以 SKILL.md 為共通標準;於 Antigravity 以 /jsc: 前綴呼叫。", + "description": "JSC 跨 AI 助理文件化 skill 集合:對齊 docker-compose 行內註解(doc-docker)、為專案 function 補齊 XML 文件並更新 README 功能目錄(doc-funcs)。所有 skills 以 SKILL.md 為共通標準;於 Antigravity 以 /jsc: 前綴呼叫。", "skills": "./skills/" } \ No newline at end of file diff --git a/skills/doc-docker/SKILL.md b/skills/doc-docker/SKILL.md new file mode 100644 index 0000000..d588ef7 --- /dev/null +++ b/skills/doc-docker/SKILL.md @@ -0,0 +1,56 @@ +--- +name: doc-docker +description: 整理並對齊 docker-compose.yaml 的行內註解與標題區塊。當使用者要對齊 docker-compose 註解、整理 compose 檔註解欄位、更新 compose 標題日期,或提到 docker-compose、dc-tidy、align_comments、註解對齊時使用此 skill。 +--- + +# 對齊 docker-compose 註解 + +整理並對齊 `docker-compose.yaml` 的行內註解與標題。請優先使用自動化腳本執行。 + +以下範例中的 `skill_dir` 是本 skill 所在目錄,也就是包含此 `SKILL.md` 與 `scripts/` 的資料夾。不要假設目標專案內存在 `plugins/skills/doc-docker/`。 + +## 全專案批次處理(未指定檔案時) + +```bash +bash "${skill_dir}/scripts/align_comments.sh" --yes +``` + +## 單一檔案處理(指定檔案時) + +`${file}` 為使用者指定的目標檔案: + +```bash +bash "${skill_dir}/scripts/align_comments.sh" "${file}" --yes +``` + +## 手動執行流程參考 + +1. **[步驟 0] 還原空白基準**:移除既有對齊用空白,回到可重新計算的狀態。 + + ```bash + tmp="$(mktemp "${file}.tmp.XXXXXX")" && awk -f "${skill_dir}/scripts/awk/strip_col.awk" "${file}" > "$tmp" && mv -- "$tmp" "${file}" + ``` + +2. **[步驟 1] 校閱註解**:修正註解文字與數值正確性。 + +3. **[步驟 2] 計算對齊欄位**:取得對齊欄位。 + + ```bash + awk -f "${skill_dir}/scripts/awk/find_col.awk" "${file}" + ``` + + 取得 `TARGET=XX`。 + +4. **[步驟 3] 執行對齊**:依 `TARGET` 欄位對齊註解。 + + ```bash + tmp="$(mktemp "${file}.tmp.XXXXXX")" && awk -v TARGET= -f "${skill_dir}/scripts/awk/align_col.awk" "${file}" > "$tmp" && mv -- "$tmp" "${file}" + ``` + +5. **[步驟 4] 驗證對齊**:驗證對齊結果。 + + ```bash + awk -v TARGET= -f "${skill_dir}/scripts/awk/verify_col.awk" "${file}" + ``` + +6. **[步驟 5] 更新日期**:更新標題區塊的 `更新日期` 為今日。 diff --git a/skills/doc-docker/scripts/align_comments.sh b/skills/doc-docker/scripts/align_comments.sh new file mode 100644 index 0000000..00e1d24 --- /dev/null +++ b/skills/doc-docker/scripts/align_comments.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# docker-compose 註解對齊工具 +# 用法: bash align_comments.sh [file_or_directory] [--yes] +# +# 功能: +# 1. 如果未指定檔案,則搜尋專案中所有 docker-compose.yaml。 +# 2. 每個檔案獨立計算註解對齊欄位。 +# 3. 更新標題中的更新日期。 + +set -euo pipefail + +# 參數處理 +target_path="" +auto_confirm=false +had_failure=false + +for arg in "$@"; do + if [[ "$arg" == "--yes" ]] || [[ "$arg" == "-y" ]]; then + auto_confirm=true + else + target_path="$arg" + fi +done + +AWK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/awk" + +write_with_tmp() { + local source_file="$1" + local awk_file="$2" + shift 2 + + local tmp_file + tmp_file="$(mktemp "${source_file}.tmp.XXXXXX")" + if awk "$@" -f "$awk_file" "$source_file" > "$tmp_file"; then + mv -- "$tmp_file" "$source_file" + else + rm -f -- "$tmp_file" + return 1 + fi +} + +process_file() { + local file="$1" + if [[ ! -f "$file" ]]; then + echo "Skip: $file is not a file." + return + fi + + echo ">>> Processing: $file" + + # 步驟 0:還原多餘空白 (Strip) + write_with_tmp "$file" "$AWK_DIR/strip_col.awk" + echo "[0] Strip done." + + # 步驟 1:人工修正註解內容 (Review) + if [[ "$auto_confirm" == "false" ]]; then + echo "[1] 請檢查註解內容是否正確 ($file)。" + read -r -p "完成後按 Enter 繼續 (或輸入 's' 跳過此檔, 'a' 全部自動完成): " input + if [[ "$input" == "s" ]]; then + echo "Skipped." + return + fi + if [[ "$input" == "a" ]]; then + auto_confirm=true + fi + fi + + # 步驟 2:找出最大對齊欄位 (Find) + local target + target=$(awk -f "$AWK_DIR/find_col.awk" "$file" | awk -F= '{print $2}') + + if [[ -z "$target" ]] || [[ "$target" -eq 0 ]]; then + echo "[2] No inline comments found. Skipping alignment." + else + echo "[2] Calculated TARGET column: $target" + + # 步驟 3:對齊 (Align) + write_with_tmp "$file" "$AWK_DIR/align_col.awk" -v TARGET="$target" + echo "[3] Align done." + + # 步驟 4:驗證 (Verify) + local verify + verify=$(awk -v TARGET="$target" -f "$AWK_DIR/verify_col.awk" "$file") + if [[ -z "$verify" ]]; then + echo "[4] Verify OK." + else + echo "[4] Verify FAILED for $file:" + echo "$verify" + had_failure=true + return 1 + fi + fi + + # 步驟 5:更新標題日期 (Update) + local today + today=$(date +%Y-%m-%d) + # 支援不同的 sed 版本 (Linux/GNU vs BSD/MacOS) + if sed --version >/dev/null 2>&1; then + # GNU sed + sed -i "s/# 更新日期: [0-9-]*/# 更新日期: $today/" "$file" + else + # BSD sed (MacOS) + sed -i '' "s/# 更新日期: [0-9-]*/# 更新日期: $today/" "$file" + fi + echo "[5] Updated '更新日期' to $today." + echo "" +} + +find_compose_files() { + local root="$1" + + find "$root" \ + \( -path '*/node_modules' -o -path '*/.git' -o -path '*/bin' -o -path '*/obj' \) -prune \ + -o -name 'docker-compose.yaml' -type f -print0 +} + +process_found_files() { + local root="$1" + local found=false + + while IFS= read -r -d '' file; do + found=true + process_file "$file" || had_failure=true + done < <(find_compose_files "$root") + + if [[ "$found" == "false" ]]; then + echo "No docker-compose.yaml files found." + fi +} + +# 執行邏輯 +if [[ -z "$target_path" ]]; then + echo "No target specified. Searching for all docker-compose.yaml files in the project..." + process_found_files "." +elif [[ -d "$target_path" ]]; then + echo "Target is a directory. Searching for docker-compose.yaml files within: $target_path" + process_found_files "$target_path" +else + process_file "$target_path" || had_failure=true +fi + +if [[ "$had_failure" == "true" ]]; then + echo "Done with failures." + exit 1 +fi + +echo "Done!" diff --git a/skills/doc-docker/scripts/awk/align_col.awk b/skills/doc-docker/scripts/awk/align_col.awk new file mode 100644 index 0000000..a754c5b --- /dev/null +++ b/skills/doc-docker/scripts/awk/align_col.awk @@ -0,0 +1,9 @@ +!/^[[:space:]]*#/ && / #/ { + idx = index($0, " #") + if (idx > 0 && idx != TARGET) { + pad = "" + for (i = idx; i < TARGET; i++) pad = pad " " + sub(/ #/, pad " #") + } +} +{ print } diff --git a/skills/doc-docker/scripts/awk/find_col.awk b/skills/doc-docker/scripts/awk/find_col.awk new file mode 100644 index 0000000..8279b81 --- /dev/null +++ b/skills/doc-docker/scripts/awk/find_col.awk @@ -0,0 +1,8 @@ +!/^[[:space:]]*#/ && / #/ { + idx = index($0, " #") + content = substr($0, 1, idx - 1) + gsub(/ +$/, "", content) + col = length(content) + 2 + if (col > max_col) max_col = col +} +END { print "max_col=" max_col } diff --git a/skills/doc-docker/scripts/awk/strip_col.awk b/skills/doc-docker/scripts/awk/strip_col.awk new file mode 100644 index 0000000..d43d8d8 --- /dev/null +++ b/skills/doc-docker/scripts/awk/strip_col.awk @@ -0,0 +1,8 @@ +!/^[[:space:]]*#/ && / #/ { + idx = index($0, " #") + content = substr($0, 1, idx - 1) + gsub(/ +$/, "", content) + comment = substr($0, idx) + $0 = content comment +} +{ print } diff --git a/skills/doc-docker/scripts/awk/verify_col.awk b/skills/doc-docker/scripts/awk/verify_col.awk new file mode 100644 index 0000000..5dd06cb --- /dev/null +++ b/skills/doc-docker/scripts/awk/verify_col.awk @@ -0,0 +1,4 @@ +!/^[[:space:]]*#/ && / #/ { + idx = index($0, " #") + if (idx != TARGET) print NR, "col=" idx, substr($0, 1, 80) +} diff --git a/skills/doc-docker/scripts/fix_comments.sh b/skills/doc-docker/scripts/fix_comments.sh new file mode 100644 index 0000000..30f578d --- /dev/null +++ b/skills/doc-docker/scripts/fix_comments.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# 自動補全與修正 docker-compose.yaml 行內註解內容 +# 用法: bash scripts/fix_comments.sh + +set -euo pipefail +file="${1:-}" +if [ -z "$file" ]; then + echo "請指定要處理的檔案" + exit 1 +fi + +tmp_file="$(mktemp "${file}.tmp.XXXXXX")" +trap 'rm -f -- "$tmp_file"' EXIT + +awk ' + /^[[:space:]]*#/ { print; next } + /[[:space:]]+#/ { + match($0, /[[:space:]]+#/); + code=substr($0, 1, RSTART - 1); + comment=substr($0, RSTART + RLENGTH); + gsub(/^ +| +$/, "", comment); + if (length(comment)==0) comment="TODO: 補充說明"; + print code " # " comment; + next; + } + { print } +' "$file" > "$tmp_file" + +mv -- "$tmp_file" "$file" +trap - EXIT diff --git a/skills/doc-funcs/SKILL.md b/skills/doc-funcs/SKILL.md new file mode 100644 index 0000000..d586beb --- /dev/null +++ b/skills/doc-funcs/SKILL.md @@ -0,0 +1,30 @@ +--- +name: doc-funcs +description: 為目前專案的每個 function 建立 .docs/ 草稿並補齊 XML 文件,最後更新 README 功能目錄。當使用者要補齊 function 文件、產生 XML doc、為每個 method 加 summary/param/remarks、建立 .docs 草稿,或提到 doc-funcs、function 文件化、XML documentation comments 時使用此 skill。 +--- + +# 補齊 function 文件 + +你要替目前工作區內的專案補齊 function XML 文件。請依序完成: + +1. 掃描專案內所有可文件化的 function/method。以目前 repo 的主要語言為準;若是 C#,包含 public/internal/protected/private method、constructor、extension method、operator,排除 generated/bin/obj/.git/.docs 與第三方依賴。 +2. 建立 .docs/ 目錄(若不存在)。先產生總索引 .docs/doc-funcs-index.md,列出所有 function:檔案、型別、簽名、是否已有 XML doc、草稿檔路徑。 +3. 針對每一個 function 派出一個 subagent。每個 subagent 只分析指定 function 與必要上下文,不直接改 code;回傳並在 .docs/ 底下建立一份草稿。草稿檔名需可追溯來源,例如 .docs/doc-funcs/{relative-path}.{type}.{function}.md。草稿內容必須包含: + - 位置:檔案與行號 + - 簽名:完整簽名 + - 行為分析:方法實際做什麼、重要分支、副作用、例外/失敗行為 + - 建議 ``:根據功能產生,不要只改寫方法名稱 + - 建議 ``:每個參數的用途、限制、null/empty 行為(能從 code 推論才寫;不能確定就標註需人工確認) + - 建議 ``:至少一個使用情境,描述何時呼叫、前置條件、結果或注意事項 +4. 所有草稿完成後,主 agent 要閱讀草稿並實作到原始碼。若是 C#,使用 XML documentation comments:``、``、``。註解盡量使用繁體中文;保留既有正確文件,僅補齊缺漏或明顯不足處;不要為了文件改變 runtime 行為。 +5. 若遇到大量 function,仍要分批持續處理,不要只做示範。若 token 或時間不足,先完成已列入 index 的批次,並在 .docs/doc-funcs-index.md 標記 pending。 +6. 補齊後,產生或更新專案根目錄的 README.md 功能目錄。功能目錄必須包含更新時間、功能名稱、使用範例;若 README.md 已有既有內容,保留既有內容並以最小變更更新或新增功能目錄區塊。 +7. 完成後執行合適的格式化/建置或至少語法驗證;若無法執行,說明原因。 + +## 重要限制 + +- 不要修改 generated/bin/obj/.git/.docs 以外的非原始碼檔,除非是建立草稿與索引。 +- 不要新增與文件無關的 helper、測試或重構。 +- 草稿是實作依據,不能跳過。 +- 若 function 行為無法可靠推論,文件中要保守描述並標註不確定點,不要編造。 +- 原始碼註解與 README 功能目錄盡量使用繁體中文;專有名詞、API 名稱、型別名稱與程式碼範例可保留英文。 diff --git a/skills/hello/SKILL.md b/skills/hello/SKILL.md deleted file mode 100644 index 4e54c6c..0000000 --- a/skills/hello/SKILL.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: hello -description: 範例 skill,用來驗證 jsc plugin 是否安裝成功,也是新增 skill 的範本。當使用者輸入 hello、想測試 plugin、或想看 skill 模板長什麼樣子時觸發;回覆一句問候並簡述此 plugin 的用途。 ---- - -# hello(範例 skill) - -這是 `jsc` plugin 的範例 skill。它有兩個用途: - -1. **驗證安裝** — 跨各家 AI 助理確認 skill 已被正確載入。 -2. **作為範本** — 複製這個資料夾即可新增一個新的 skill。 - -## 呼叫方式 - -| 助理 | 呼叫方式 | -| --- | --- | -| Claude Code | `/jsc:hello` | -| Antigravity | `/jsc:hello`,或描述需求自動觸發 | -| Codex | 在提示詞輸入 `$hello`,或用 `/skills` 選單 | -| OpenCode | 直接描述需求,模型會透過 skill 工具自動呼叫 | - -## 行為 - -當這個 skill 被觸發時: - -1. 回覆「Hello from **jsc** 👋」。 -2. 用一句話說明 `jsc` 是一個跨 AI 助理的共用 skill 集合。 -3. 提示使用者可以在 README 的「Skills 目錄」查看所有可用的 skills。 - -## 如何以此為範本新增 skill - -1. 複製 `skills/hello/` 為 `skills//`。 -2. 修改 `SKILL.md` 的 frontmatter: - - `name`:小寫、數字、連字號(`-`),最長 64 字元。**這個名稱會成為 Claude Code / Antigravity 的 `/jsc:` 指令**。 - - `description`:第三人稱,寫清楚「什麼時候該用、什麼時候不該用」與觸發關鍵字 — 各家助理靠這段文字決定是否自動載入。 -3. 在內文寫下 skill 的具體步驟。 -4. 手動把新 skill 補進 README 的「Skills 目錄」區塊。