Author SHA1 Message Date
admin bbc9dca714 Merge pull request '行為清單那三支腳本的路徑也補上外掛目錄名,補送上一輪沒趕上的修正' (#36) from fix/pr-skill-tool-paths-resolve-to-plugin-root into develop
Reviewed-on: #36
2026-09-04 03:26:47 +00:00
jiantw83andClaude Opus 5 6965e3b24c chore(plugin): 三份 manifest 升版至 0.1.8
上一輪的行為清單修正沒趕上合併,這一筆帶著它重新送出。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 11:20:16 +08:00
jiantw83 68c3279062 Merge develop 2026-09-04 11:20:07 +08:00
jiantw83andClaude Opus 5 003593c73a fix(pr): 行為清單那三支腳本的路徑也補上外掛目錄名
上一筆只改了技能本文,行為清單漏掉。新的路徑檢核腳本一跑就指出來——關鍵
步驟與外部呼叫兩欄還留著八處不帶前綴的寫法,而那兩欄正是別人拿來確認這支
技能會呼叫誰的地方。

順手改掉技能本文開頭那一句的舉例方式。原本把不帶前綴的錯誤寫法原樣寫出來
當例子,那一行自己就會被路徑檢核當成一處待改。改成描述後果、不照抄那個
寫法,順便講得更準:省掉外掛目錄名之後,路徑會相對於當下的工作目錄解,
可能解到自己這個存取庫裡對的那一支、可能解到別的 domain 底下另一支,也可能
什麼都解不到。中間那一種最糟,它會跑起來,跑的是另一支腳本。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 10:51:31 +08:00
admin eedd2255b4 Merge pull request 'PR 技能自家三支腳本的路徑補上外掛目錄名,不再解到不存在的位置' (#35) from fix/pr-skill-tool-paths-resolve-to-plugin-root into develop
Reviewed-on: #35
2026-09-04 02:46:53 +00:00
jiantw83andClaude Opus 5 5d3f761677 fix(pr): 自家三支腳本的路徑補上外掛目錄名,不再相對於技能目錄
base-branch.sh、pick-type.sh、slugify.sh 三支在外掛根目錄的 tools/ 底下,
但技能本文寫成不帶前綴的 tools/…,照字面解會落到 skills/pr/tools/,那個
路徑不存在、結束碼 127。同一份文件引用別的外掛時本來就寫 jsc-gitea/tools/
與 jsc-hooks/tools/,只有自家的掉了前綴,十三處全部補齊。

這個坑比看起來嚴重。第 4 步明寫不准自己挑基底分支,而跑不動推導腳本的
代理人離「自己挑一個」只差一步——路徑打錯就變成 PR 開在錯的階梯上,而且
PR 會照樣開出來,不會有任何錯誤。開頭補一段講明每一條腳本路徑相對於誰解,
以及這個 127 為什麼不只是跑不動而已。

三份 manifest 版號 0.1.5 升到 0.1.6。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 10:43:54 +08:00
admin bc59208d67 Merge pull request '收尾寫一筆 skill-end 事件,執行狀態才回報得到助理' (#34) from feat/status-report into develop
Reviewed-on: #34
2026-09-02 08:04:40 +00:00
jiantw83 2e42badea0 chore(plugin 版本): 三份 manifest 升版至 0.1.5 2026-09-02 16:01:14 +08:00
jiantw83 bf68ee3436 feat(狀態回報): 收尾寫一筆 skill-end 事件
現行紀錄只記「被叫用」,沒有成敗也沒有結束碼。跑完整輪的技能與開場就
中止的技能,在紀錄裡長得一模一樣。

start 由技能用量 hook 順手發,不必改技能文件。end 只能由技能自己在收尾
步驟寫——hook 接在技能工具呼叫上,而實際工作發生在之後的模型輪次,它在
原理上看不到成敗。有 start 沒有配對的 end,就是那一輪中止了。

status 五選一,每支技能各自寫明什麼情況選哪一個。找不到回報腳本就安靜
跳過,回報失敗一律不改變技能自己的結論。
2026-09-02 16:01:14 +08:00
admin 9f7865b428 Merge pull request '放行 jsc-assist 的 marketplace 條目到預設分支' (#32) from chore/marketplace-assist-registry/main into develop
Reviewed-on: #32
2026-09-01 04:55:13 +00:00
admin 61d22ae572 Merge pull request 'chore/marketplace-assist-registry/sync-copies' (#31) from chore/marketplace-assist-registry/sync-copies into chore/marketplace-assist-registry/main
Reviewed-on: #31
2026-09-01 04:53:03 +00:00
jiantw83 3e796c79a7 chore(marketplace): 把 jsc-assist 登錄進統一 marketplace
What:
- 兩份 marketplace 檔各加一個 jsc-assist 條目,來源網址指向 assist 存放庫。

Why:
- 準則要求每個 domain 存放庫都帶同一份 marketplace 檔,任何一個存放庫都能當註冊入口。副本之間只要有一份沒跟上,稽核就會報出不一致。
- 正本少了這個條目,各 CLI 的安裝指令就找不到 jsc-assist,這個 domain 等於發佈不出去。

How:
- 條目由 meta 的 sync-marketplace.sh 產生,同時寫進正本與每個 domain 存放庫的副本,寫完逐檔比對位元組。這一支存放庫的兩份副本就是那一輪的產物。
- 條目依名稱排序,縮排與非 ASCII 描述的處理都交給同一支腳本,不手改 JSON。
- 這一批是從最新的預設分支重新產生的。前一輪的分支基底早於監控頁型別那批改動,直接合併會把那些改動回退掉,所以整批重做而不是解衝突。

Who:
助理 domain 落地的註冊步驟在這個存放庫的同步。
2026-09-01 12:50:04 +08:00
jiantw83 57315ea44f Merge pull request '收攏 commit 技能的 frontmatter 語法修正' (#28) from feat/cli-hook-rewire/main into develop 2026-09-01 00:58:40 +00:00
jiantw83 1ef818e1b4 Merge pull request '修正 commit 技能 SKILL.md frontmatter 的 YAML 純量語法' (#27) from feat/cli-hook-rewire/quote-description into feat/cli-hook-rewire/main 2026-09-01 00:56:08 +00:00
jiantw83 4e04de1101 fix(frontmatter): 修正 commit 技能 SKILL.md frontmatter 的 YAML 純量語法錯誤
What:
- 修正 skills/commit/SKILL.md frontmatter 裡 description 欄位的 YAML 語法錯誤。
- 整串 description 加上單引號,內部撇號改寫成兩個單引號,內容文字一個字都沒變。
- 同步更新 plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三個 manifest 版本號,從 0.1.3 進到 0.1.4。

Why:
- description 內含「冒號加空白」,屬於未加引號的 YAML plain scalar,違反 YAML 語法規定。
- Antigravity 解析 frontmatter 時當場中斷,整支技能被靜默丟棄,沒有任何錯誤訊息;磁碟上 34 支技能,Antigravity 只認得 28 支。
- 準則要求 description 用英文撰寫,不能把「: 」改成全形冒號迴避語法問題,只能加引號修正。

How:
- 整串 description 值加上單引號,內部撇號寫成兩個單引號跳脫,其餘字元不動。
- 用 git show HEAD: 取出改前的原始值,把改後的單引號純量還原後做字串相等比對,確認逐字相同、字元數一致。
- 執行 ste100-lint.sh、check-behaviors.sh、lint-frontmatter.sh 三支檢查腳本,退出碼皆為 0;git diff --numstat 顯示只動了 frontmatter 那一行。

Who:
- 本次修到 git 技能組的 commit 技能,屬分組 commit 訊息並依 What/Why/How/Who 撰寫本文的功能。
2026-08-31 19:03:18 +08:00
jiantw83 fd5b364f0f Merge pull request '把 commit、pr 兩支技能的行為清單收攏進 develop' (#24) from feat/skill-behaviors-and-version-block/main into develop 2026-08-31 08:10:33 +00:00
jiantw83 717561fa08 Merge pull request 'PR 標題改用繁體中文摘要,不再套用分支名' (#25) from feat/skill-behaviors-and-version-block/pr-title-language into feat/skill-behaviors-and-version-block/main 2026-08-31 08:09:17 +00:00
jiantw83 ec9f1c1559 Merge pull request '新增 commit、pr 兩支技能的行為清單,標清兩者的邊界' (#23) from feat/skill-behaviors-and-version-block/behavior-list into feat/skill-behaviors-and-version-block/main 2026-08-31 08:09:16 +00:00
jiantw83 5d6b73a5cc chore(version): 升版 jsc-git 至 0.1.3
What:
- 三份 plugin manifest 的 version 由 0.1.2 升到 0.1.3。

Why:
- `pr` 技能的標題行為改了,版本要跟著動,各 CLI 才收得到更新。

How:
- 跑 `sh /root/plugins/meta/tools/sync-skill-manifest.sh /root/plugins/git`,退出碼 0。

Who:
- jsc-git 修改代理執行。
2026-08-31 14:38:42 +08:00
jiantw83 9017e4cbf5 feat(pr-title-language): PR 標題改用繁體中文摘要
What:
- `jsc-git:pr` 步驟 7 的 PR 標題改成一句繁體中文摘要,不再等於分支名。
- 步驟 8 的標題校準改成只問標題還描述不描述得了 PR 目前的內容。
- 步驟 3.2 註明摘要就是 PR 標題,規則 4 補上標題與分支名互不套用。
- `references/behaviors.md` 的 pr 一節與 README 的 pr 說明同步新行為。

Why:
- 語言規則明列 PR 標題與描述一律繁體中文,且有 hook 在程式層強制,技能內文擋不住。
- 審查列表全是長串 ASCII slug,看不出這條 PR 改了什麼。
- 分支名給機器判階梯,標題給人看,兩者本來就承擔不同任務。
- 舊校準拿分支名比對標題,每跑一次就把繁中標題改回 slug,還通知所有審查者。

How:
- 步驟 7 改寫標題來源,並在完成條件加上「送出的標題不是分支名」。
- 步驟 8 第一項改成內容比對,寫明分支名不進入這項比對。
- 分支名維持 ASCII,`tools/slugify.sh` 與 `tools/base-branch.sh` 的行為不動。

Who:
- 使用者裁定標題讓位給語言規則,jsc-git 修改代理執行。
2026-08-31 14:38:42 +08:00
jiantw83 fbfae7ed38 chore(plugin): 三份 manifest 版本同步至 0.1.2
What:把 `.claude-plugin/plugin.json`、`.codex-plugin/plugin.json`、`plugin.json` 的 `version` 從 `0.1.1` 改成 `0.1.2`。

Why:本 repo 新增 `references/behaviors.md`,內容有變就要出新版。三份 manifest 分別給 Claude、Codex 與根目錄讀取,版本必須一致,否則版本檢查會判定不相符。

How:只動 `version` 一個欄位,三份檔案改成相同的 0.1.2。描述、技能路徑、`jsc.requires` 的相依範圍都不動。

Who:jsc-git 這個 plugin 的版本標示,供各 CLI 的 plugin 版本檢查使用。
2026-08-31 13:38:52 +08:00
jiantw83 1c85d079ee feat(behaviors): 新增 jsc-git 技能行為清單
What:新增 `references/behaviors.md`。檔內收錄 jsc-git 的 commit、pr 兩節,每節一張五列表,欄位為觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象。

Why:技能驗證原本沒有共同的比對基準,只能回頭讀 SKILL.md 逐段推敲。清單放在 jsc-git 自己的 repo,技能改動與清單走同一個 PR,不會互相漂移,也不用跨 repo 開兩條 PR 互卡。

How:逐支技能盤點行為並寫成固定格式。節標題用技能名稱、欄位順序固定,格式由 `jsc-meta/tools/check-behaviors.sh` 在程式層檢查。觸發時機同時寫該叫用與不該叫用的情形,把 commit 與 pr 的邊界標清楚。外部呼叫逐一列出腳本、`git` 指令、`gitea.sh` 子命令與被叫用的技能。可驗證跡象寫成外部觀察得到的結果,並明講兩支技能都不寫 wiki 頁。

Who:jsc-git 的 commit、pr 兩支技能,供技能驗證使用。日後任何技能異動都要在同一個 PR 內同步更新這一頁。
2026-08-31 13:38:52 +08:00
admin 0a66f64ea0 Merge pull request 'fix/skill-check-compliance-and-flow' (#21) from fix/skill-check-compliance-and-flow into develop
Reviewed-on: #21
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-31 03:24:26 +00:00
jiantw83 b952af32b8 chore(plugin): 提高 jsc-gitea 相依下限
技能已改用只在新版才有的子命令,下限跟著提高,避免舊版環境跑到那一步失敗。
2026-08-31 11:20:54 +08:00
jiantw83 113b044bb6 docs(pr): 併入上游的流程確認說明並接到重排後的步驟
把上游剛進來的三處確認說明接到重排後的正確步驟,並依準則改寫成英文。
2026-08-31 11:20:27 +08:00
jiantw83 368f6c5f11 chore(plugin): 宣告 jsc-meta 相依
技能改成引用 jsc-meta 的指引正本。相依關係要跟著寫進外掛資訊檔,否則沒裝 jsc-meta 的機器讀不到階梯規則。

- requires 加入 jsc-meta。
- 三份外掛資訊檔一起推進,避免彼此不一致。
2026-08-31 11:05:49 +08:00
jiantw83 25792fd279 docs(tools): 補上腳本結束碼說明,並改指階梯規則正本
腳本標頭沒把每個結束碼代表什麼、該怎麼處理寫清楚,呼叫端只能用猜的。階梯表又同時抄在說明檔與技能內文,改規則時兩邊容易不同步。

- 推導基底的腳本標頭逐碼說明狀況與處置,並標明該碼屬於哪一種模式。
- 產生分支名的腳本標頭補上參數不足的結束碼。
- 說明檔刪掉重複的階梯表,改指向指引的階梯章節。
- 說明檔補上型別優先序腳本的說明,並更新兩支技能的摘要。
2026-08-31 11:05:49 +08:00
jiantw83 fcd0b3b8c0 fix(skills): 補齊失敗分支並解開分組步驟的循環相依
兩支技能的步驟有幾處寫不完整。出錯時流程會走偏,步驟之間也互相卡住。

- 建立 PR、修改 PR、回覆留言、查詢開啟中的 PR,四處原本只寫成功路徑。API 失敗會被當成「沒有開啟中的 PR」,於是在同一支分支上重開一支。現在四處都補上失敗分支。
- 推導基底的腳本結束碼原本只分流三個,其餘落空。現在每個結束碼都有處置與下一步。
- 交叉引用指到收尾步驟,改指回真正做校準的那一步。
- 分組原本被拉進平行區塊,但分組要等盤點的檔案清單,盤點的完成條件又要等分組結果。現在只有註解掃描與盤點平行,分組排在盤點之後。
- 認可與開 PR 原本各查一次開啟中的 PR。改成共用單次查詢,並拿掉認可回呼開 PR 的遞迴。
- 呼叫方傳入的基底改在最前面驗證。基底不合法就當場擋下,不必等命名做完。
- 重複的階梯表從技能內文刪掉,改指向指引正本。
2026-08-31 11:05:49 +08:00
jiantw83 bd277b6e55 feat(tools): 新增 commit 型別優先序腳本
型別優先序原本抄在技能內文裡。改一次要跟著改好幾份,很容易對不起來。
現在把順序收進腳本,讓它成為唯一真實來源。

- 參數與標準輸入都收,commit 標題整行餵進來也認得出型別。
- 沒收到輸入回傳 2,輸入裡沒有階梯表型別回傳 3,呼叫端據此分流。
- 錯誤訊息一律印繁體中文到 stderr。
2026-08-31 11:05:49 +08:00
admin c440ee7b8f Merge pull request 'fix/pr-confirm' (#20) from fix/pr-confirm into develop
Reviewed-on: #20
2026-08-28 10:00:40 +00:00
Jeffery 0106e959a5 docs(pr): PR 流程補確認說明 2026-08-28 17:58:34 +08:00
admin cb62c352e1 Merge pull request 'feat/plugin-dependencies/main' (#18) from feat/plugin-dependencies/main into develop
Reviewed-on: #18
2026-08-28 04:04:57 +00:00
admin 57f2756524 Merge pull request 'feat/plugin-dependencies/declare-requires' (#17) from feat/plugin-dependencies/declare-requires into feat/plugin-dependencies/main
Reviewed-on: #17
2026-08-28 04:03:00 +00:00
jiantw83 a47d1649e9 feat(manifest): 宣告 git 技能相依版本 2026-08-28 11:59:16 +08:00
admin e8a155689f Merge pull request 'feat/change-requests/main' (#15) from feat/change-requests/main into develop
Reviewed-on: #15
2026-08-28 01:51:05 +00:00
admin 99301272ff Merge pull request 'feat/change-requests/pr-reporting' (#14) from feat/change-requests/pr-reporting into feat/change-requests/main
Reviewed-on: #14
2026-08-28 01:46:47 +00:00
jiantw83 f557d9e750 feat(pr): 統一 PR 收尾回報與留言回覆 2026-08-28 09:30:08 +08:00
jiantw83 ad4414ab7d Merge pull request 'feat(git): PR 目標分支改走階梯推導,既有 PR 改為校準不重開' (#12) from feat/sdlc-flow-rules/main into develop 2026-08-27 03:39:40 +00:00
admin 8e42050718 Merge pull request 'feat(git): PR 分支階梯推導與既有 PR 校準' (#11) from feat/sdlc-flow-rules/pr-ladder-and-sync into feat/sdlc-flow-rules/main
Reviewed-on: #11
2026-08-27 03:26:23 +00:00
jiantw83 f62428a4ac chore(manifest): 三份 manifest 版本升到 0.0.8
What:`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 的 `version` 由 0.0.7 改為 0.0.8。

Why:本次改了 `base-branch.sh` 的介面與 `commit`、`pr` 兩支技能的步驟,屬於行為變更,版本要跟著往上走,各 CLI 才知道要更新。

How:三份只改 `version` 一個欄位,其餘內容不動,三份保持同一版號。

Who:`jsc-git` 外掛的套件描述檔。
2026-08-27 11:20:28 +08:00
jiantw83 7adee55dad docs(git): README 補上 PR 階梯與兩支技能的新行為
What:README 新增「PR 階梯」一節(兩種型別的階梯表、多層子功能的推導方式、推不出就回 `7` 中止、功能主幹自動建立),工具表補上 `base-branch.sh --derive` 與 `slugify.sh` 連叫兩次組多層分支名,`commit` 與 `pr` 兩段說明改寫成新行為。

Why:階梯與校準是這次的行為變更,README 是對外說明。說明沒跟上,使用者會照舊以為 base 可以自己挑、PR 要重開一支。

How:階梯表在 README 只寫一份摘要,唯一來源仍是 `jsc-meta` 的 `references/guidelines.md`。技能說明兩段各補上推導與校準兩件事,用詞與 SKILL.md 一致。

Who:讀 `jsc-git` 說明的人,以及要接 `base-branch.sh` 的其他 domain。
2026-08-27 11:20:28 +08:00
jiantw83 82fb8077fc feat(pr): 目標分支改走階梯,既有 PR 改為校準不重開
What:`pr` 技能新增「PR ladder」一節與階梯形狀的分支命名(`fix` 叫一次 `slugify.sh`,其餘型別叫兩次組出 `{類型}/{功能}/{子功能}`),base 一律由 `base-branch.sh --derive` 推導;新增步驟 6 查目標分支有沒有開啟中的 PR,有就跳到新的步驟 9 比對標題、描述、前置 PR 依賴三項,只有不一樣的那幾項才送 API。`commit` 技能新增步驟 5:認可完成後,目前分支已有 PR 就交給 `pr` 校準。

Why:越級開 PR 與「同一條分支重開第二支 PR」是同一個病灶的兩面——base 與 PR 現況都靠人記。改成先推導、先比對之後,base 由腳本決定,PR 只在真的有差時才更新。

How:呼叫方傳入的 base 仍然照收,但要與推導結果一致;不一致就當成越級擋下,說明正確階梯再問使用者,不會悄悄改目標。校準走 `jsc-gitea` 的 `pr-get` 讀現況、`pr-edit` 一次帶標題與描述、`pr-depend` 補依賴;三項都相同就什麼都不做——每一次多餘的編修都會通知所有審查者,安靜才是對的結果。

Who:`jsc-git:pr` 與 `jsc-git:commit` 兩支技能,以及呼叫它們的 `jsc-sdlc:implement`、`jsc-sdlc:maintain` 與 `jsc-meta` 各技能。
2026-08-27 11:20:28 +08:00
jiantw83 cc23722f31 feat(base-branch): 新增 --derive 從分支名推出階梯上一階
What:`tools/base-branch.sh` 新增 `--derive [分支]` 模式,從分支名推出唯一合法的上一階基底。`feat` 這一類走 `{類型}/{功能}/{子功能}` → `{類型}/{功能}/main` → `develop` → `master`,`fix` 走 `fix/{修改}` → `develop` → `master`。新增退出碼 `7`(推不出唯一合法基底)、`8`(推導出的基底不在遠端又不能自動建立)、`9`(自動建立功能主幹失敗)。既有的呼叫方模式一字未改。

Why:PR 階梯禁止越級,但基底原本靠呼叫端自己挑,挑錯就是把一個子功能直接併進 `develop`,中間那層功能主幹整個被跳過。判斷寫進程式,越級就不會發生在「這次剛好沒注意」的時候。

How:`{子功能}` 可以多層,推導一律把最後一段換成 `main`。推不出唯一合法基底就中止並回 `7`,由呼叫端問使用者,不猜、也不退回 `develop`——退回 `develop` 正是這支腳本要擋的那件事。功能主幹不在 origin 時,直接把 `origin/develop` 推成新分支自動補一條,不動本機工作區,再把建立了哪一條分支印到 stderr。分支名只收 ASCII 小寫、數字、連字號與斜線,中文簡述請先過 `tools/slugify.sh`。

Who:`jsc-git:pr` 決定 PR base 的那一步,以及所有經由它開 PR 的技能。
2026-08-27 11:20:27 +08:00
admin 37cbbdcebd Merge pull request 'feat/comment-scope-sweep-before-commit' (#9) from feat/comment-scope-sweep-before-commit into develop
Reviewed-on: #9
2026-08-27 01:28:40 +00:00
jiantw83 dcd4f3ff04 feat(manifest): 三份 manifest 同步升版到 0.0.7
What:plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三份版本一起從 0.0.6 升到 0.0.7。

Why:commit 技能新增了認可前的註解掃描步驟,屬於實質行為變更,各 CLI 要靠版本號才知道本機這份已經落後、需要更新。

How:跑 jsc-meta 的 tools/sync-skill-manifest.sh 同步 README 技能目錄並升版,三份版本號一致,README 的技能小節沒有增減。

Who:五支 CLI 的外掛版本檢查與更新流程。
2026-08-27 09:10:31 +08:00
jiantw83 1748166666 feat(commit): 認可前先掃註解是否夾帶文件相關資訊
What:在 commit 技能的清點步驟之後、實際認可之前,新增一個掃描步驟,呼叫 jsc-hooks 的 comment-scope.sh sweep 掃過整個工作區,並在 README 的技能目錄補上同一句說明。原步驟編號往後順延。

Why:註解禁止夾帶文件相關資訊這條規則,目前只有 claude 接得到寫檔後的即時掃描;codex、copilot、antigravity、kiro 都得等到每輪結束或工作階段結束才有機會發現。commit 是五支 CLI 共用的提交路徑,在這裡掃一次,違規註解就進不了 commit。

How:新步驟只指向 jsc-review 的 references/comment-scope.md,不抄規則清單。腳本找不到 git 工作區或 jsc-hooks 不在本機時會安靜結束,此時跳過這一步並在回報中說明,不中止認可;exit 2 代表命中,就地修好再重跑到 exit 0 才認可。警告是給人與模型判讀,不是硬性阻擋,確認誤判就照實說明後繼續。步驟附可檢核的完成條件。

Who:jsc-git 的 commit 技能,以及所有經由它提交的 CLI。
2026-08-27 09:10:31 +08:00
admin a471e0b883 Merge pull request 'fix/skillset-audit-compliance-and-guard-fixes' (#8) from fix/skillset-audit-compliance-and-guard-fixes into develop
Reviewed-on: #8
2026-08-25 07:14:49 +00:00
jiantw83andClaude Opus 5 104bc38633 chore(git): 三份 manifest 同步升版並同步 marketplace 正本
What:三份 plugin manifest 版本同步 bump,兩份 marketplace 檔與 plugins/meta 正本對齊。

Why:準則要求技能異動必須同步升版;marketplace 副本必須與正本完全一致。

How:以 jsc-meta 的 tools/sync-skill-manifest.sh 升版,marketplace 檔由正本複製。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:58:54 +08:00
jiantw83andClaude Opus 5 f67f7ebd6f docs(git): 同步文件與參考資料
What:更新 README、AGENTS.md、templates 與 references,讓文件敘述與實際行為一致。

Why:稽核發現多處文件與程式行為分歧,違反「每個意義只有單一真實來源」。

How:以實際程式行為為準改寫敘述,重複的規則收成單一來源並以一行指引指過去。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:58:54 +08:00
jiantw83andClaude Opus 5 4c9c9421c6 fix(git): 補齊稽核缺失並修掉護欄失效
What:依 jsc-meta:skill-check 的稽核結果修正技能與工具——補上每個步驟的可檢核完成條件、
把留在內文的標準輸入輸出流程下放 tools/、修正查表與退碼路由造成的誤判。

Why:稽核發現這些缺失會讓技能在實際執行時走錯分支或靜默通過。
完成條件缺漏是最常被違反的一項;退碼誤判與查表錯誤則會讓良性狀況被當成失敗。

How:逐項對照 references/guidelines.md 的審核檢查清單修正,新增的工具都有
documented exit codes,並以真實執行驗證每條路徑。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:58:54 +08:00
admin b94fae5c36 Merge pull request '發佈 jsc-git 0.0.4:PR base 優先採用呼叫端指定值' (#7) from develop into master
Reviewed-on: #7
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-25 04:02:09 +00:00
jiantw83andClaude Opus 5 41762db1b8 chore(plugin 版本): 三份 manifest 升版至 0.0.4
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 11:58:11 +08:00
jiantw83andClaude Opus 5 e5584bee80 fix(pr): base 分支優先採用呼叫端指定值,不再自行退回 develop
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 11:58:11 +08:00
admin 99e2e09175 Merge pull request '發佈 jsc-git 0.0.3:marketplace 正本移至 plugins/meta' (#6) from develop into master
Reviewed-on: #6
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 10:18:13 +00:00
admin 9f5657c108 Merge pull request 'chore(marketplace 正本): 正本移至 plugins/meta(升版 0.0.3)' (#5) from chore/marketplace-canon-to-meta into develop
Reviewed-on: #5
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 10:06:27 +00:00
jiantw83andClaude Opus 5 80e0b495c6 chore(plugin 版本): 三份 manifest 升版至 0.0.3
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 18:00:35 +08:00
jiantw83andClaude Opus 5 13c9684246 docs(README): 安裝入口改為 plugins/meta 並補上遷移說明
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 18:00:35 +08:00
admin 2319ef6bbb Merge pull request '發佈 jsc-git 0.0.2:pr 技能新增 slugify.sh 分支命名工具與稽核修正' (#4) from develop into master
Reviewed-on: #4
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 08:26:42 +00:00
admin 9ce424e28f Merge pull request 'fix/sync-marketplace-metadata-and-pr-skill-audit-fixes' (#3) from fix/sync-marketplace-metadata-and-pr-skill-audit-fixes into develop
Reviewed-on: #3
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 07:59:34 +00:00
jiantw83andClaude Sonnet 5 49e4f3e35b feat(pr): 新增 slugify.sh 分支命名工具並修正 pr 技能稽核項目
What: 新增 tools/slugify.sh 腳本,將分支類型與短語轉成 ASCII slug;修改 skills/pr/SKILL.md,把原本內嵌在文字說明中的 slugify 規則改為呼叫 tools/slugify.sh,並把 Rules 段落中「分支標題摘要」與「描述撰寫」都標記為必須交由 sub agent 執行(原本只有描述撰寫一項),同時把一個非英文範例字串替換成全英文範例;plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三份 manifest 版本號由 0.0.1 升到 0.0.2。
Why: 因應 jsc-meta:skill-check 稽核結果,pr 技能有兩項不合規:決定性的分支名稱 slugify 流程應該工具化而非寫成文字規則,避免各 agent 各自理解出現落差;另外,需要判斷力的步驟(分支標題摘要、描述撰寫)標記不一致,只標了描述撰寫一項會讓分支標題摘要漏掉必要的 sub agent 隔離;範例字串混雜非英文也不符合技能文件全英文的要求。
How: 新增可執行的 tools/slugify.sh(小寫化、非 a-z0-9 字元轉連字號、合併連續連字號、去除頭尾連字號),並在 SKILL.md 步驟 3.2 改為呼叫此腳本;同步更新 Rules 第 3 項,把「分支標題摘要」與「描述撰寫」都列為必須交由 sub agent 執行;把範例改成全英文;三份 plugin manifest 版本號同步升版以反映此次修改。
Who: 影響 jsc-git 的 pr 技能與其新增的 slugify.sh 工具,屬 pr 流程分支命名與稽核合規的功能性變更。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 14:47:31 +08:00
jiantw83andClaude Sonnet 5 be70cd58ee fix(marketplace): 補齊 marketplace.json 缺漏的 description 與 owner 欄位
What: 在 .agents/plugins/marketplace.json 加入頂層 owner 欄位(name: JSC)與整體說明,並為 jsc-ask、jsc-cli、jsc-git、jsc-gitea、jsc-hooks、jsc-log、jsc-meta、jsc-pkg、jsc-review、jsc-sdlc 十個 plugin 逐一補上 description;同時把 .claude-plugin/marketplace.json 裡 jsc-meta 的說明標點從斜線改成頓號,與正本用字一致。
Why: 兩份 marketplace.json 是 plugins/jsc 正本的副本,先前副本落後於正本,缺少 description 與 owner 欄位會讓各 CLI 的 marketplace 清單顯示不完整,也造成多份副本之間內容不一致。
How: 逐一比對 plugins/jsc 正本內容,把缺漏欄位補齊到 .agents/plugins/marketplace.json,並修正 .claude-plugin/marketplace.json 的用字差異,使兩份副本與正本同步。
Who: 影響 jsc-git 倉庫內所有 marketplace.json 副本,屬全倉庫層級的基礎設施修正,非單一技能功能。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 14:47:12 +08:00
admin 06471548d9 Merge pull request 'feat(git): 匯入 jsc-git 技能組並統一 marketplace 為 jsc' (#2) from develop into master
Reviewed-on: #2
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-21 06:42:26 +00:00
jiantw83andClaude Fable 5 121cc93c38 chore(git): 加入統一 jsc marketplace 副本(與正本一致,任一 repo 可作註冊入口)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:37:45 +08:00
jiantw83andClaude Fable 5 89f30f9c5e style(git): 中文並列改頓號
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:29:45 +08:00
jiantw83andClaude Fable 5 226dcc4a6b docs(git): translate SKILL.md into English per guidelines
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:29:45 +08:00
jiantw83andClaude Fable 5 a308aed68f style(git): 依 STE100 擬人台灣感規則改寫語感
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:15:55 +08:00
jiantw83andClaude Fable 5 e8d1c28b5a docs(git): AGENTS 語言規則改指向 jsc-meta 的 references/ste100.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:13:27 +08:00
13 changed files with 676 additions and 64 deletions
+97
View File
@@ -0,0 +1,97 @@
{
"name": "jsc",
"description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。",
"owner": {
"name": "JSC"
},
"plugins": [
{
"name": "jsc-ask",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/ask.git"
},
"description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)"
},
{
"name": "jsc-assist",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/assist.git"
},
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_* 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 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
},
{
"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 追蹤)"
}
]
}
+97
View File
@@ -0,0 +1,97 @@
{
"name": "jsc",
"description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。",
"owner": {
"name": "JSC"
},
"plugins": [
{
"name": "jsc-ask",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/ask.git"
},
"description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)"
},
{
"name": "jsc-assist",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/assist.git"
},
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_* 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 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
},
{
"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 追蹤)"
}
]
}
+9 -2
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-git",
"version": "0.0.1",
"version": "0.1.8",
"description": "Commit 分組認可與 Push Request 建立",
"skills": "./skills",
"author": {
@@ -13,5 +13,12 @@
"git",
"skills",
"cross-tool"
]
],
"jsc": {
"requires": {
"jsc-gitea": ">=0.1.8",
"jsc-hooks": ">=0.2.8",
"jsc-meta": ">=0.2.2"
}
}
}
+9 -2
View File
@@ -1,6 +1,13 @@
{
"name": "jsc-git",
"version": "0.0.1",
"version": "0.1.8",
"description": "Commit 分組認可與 Push Request 建立",
"skills": "./skills"
"skills": "./skills",
"jsc": {
"requires": {
"jsc-gitea": ">=0.1.8",
"jsc-hooks": ">=0.2.8",
"jsc-meta": ">=0.2.2"
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
## 規則
1. 所有交談與輸出內容基於 STE100 使用繁體中文:短句、一句一指令、主動語態、術語一致、UTF-8 無亂碼。
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` 的決策樹規則。
+26 -10
View File
@@ -1,21 +1,37 @@
# jsc-git — Commit 與 Push Request
jsc 技能組的 git domain:把檔案變更依「類型 + 需求/功能」分組認可,並建立符合範本的 Push Request。
jsc 技能組的 git domain:把檔案變更依同類型與同需求或功能分組認可,並建立符合範本的 Push Request。
## 安裝 / 更新 / 移除
## 安裝、更新、移除
Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安裝 token 為 `jsc-git@jsc`。每個指令一行:
Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安裝 token 為 `jsc-git@jsc`。每個指令一行:
| CLI | 安裝 | 更新 | 移除 |
| --- | --- | --- | --- |
| claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && claude plugin install jsc-git@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-git@jsc` | `claude plugin uninstall jsc-git@jsc` |
| codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && codex plugin add jsc-git@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-git@jsc` |
| copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && copilot plugin install jsc-git@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-git@jsc` | `copilot plugin uninstall jsc-git@jsc` |
| claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && claude plugin install jsc-git@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-git@jsc` | `claude plugin uninstall jsc-git@jsc` |
| codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && codex plugin add jsc-git@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-git@jsc` |
| copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && copilot plugin install jsc-git@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-git@jsc` | `copilot plugin uninstall jsc-git@jsc` |
| antigravity | `git clone https://gitea.jsc.idv.tw/plugins/git.git ~/plugins/git && agy plugin install ~/plugins/git` | `git -C ~/plugins/git pull && agy plugin uninstall jsc-git && agy plugin install ~/plugins/git` | `agy plugin uninstall jsc-git` |
| kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && kiro-cli plugin install jsc-git@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-git@jsc` | `kiro-cli plugin uninstall jsc-git@jsc` |
| kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && kiro-cli plugin install jsc-git@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-git@jsc` | `kiro-cli plugin uninstall jsc-git@jsc` |
> antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用 `/jsc-cli:deploy`。
> 舊入口 `plugins/jsc` 已移除,marketplace 正本移到 `plugins/meta`。marketplace 名稱仍是 `jsc`(取自 marketplace.json 的 `name` 欄位,與存取庫名無關),安裝 token 不變;已從舊入口安裝過的人先執行 `claude plugin marketplace remove jsc`,再依上表重新 add。
## 工具
| 檔案 | 用途 |
| --- | --- |
| `tools/base-branch.sh` | 決定 PR 基底分支。兩種模式:不帶旗標時,呼叫方傳入的分支最優先,沒傳才依序試 develop、main、master;`--derive [分支]` 從分支名推出階梯的上一階。一律確認分支存在於遠端,找不到就回傳非零。「沒傳」看參數個數,傳入空字串算錯誤,不會退回 develop |
| `tools/slugify.sh` | 把類型與英文短語組成 ASCII 分支名 `{type}/{slug}`;輸入含非 ASCII 或 slug 化後為空,就回傳非零並要求先翻譯成英文短語。第一個參數可以帶斜線,所以連叫兩次就組得出 `feat/{功能}/{子功能}` |
| `tools/pick-type.sh` | 從一組 commit 型別中選出優先度最高的一個,是型別優先序的唯一真實來源。參數或標準輸入都收,commit 標題整行餵進來也認得出型別;沒有輸入回傳 2,輸入裡沒有階梯表型別回傳 3 |
## PR 階梯
階梯規則的唯一真實來源是 `jsc-meta` 的 `references/guidelines.md` 「PR 分支階梯」一節,本檔不再抄一份。基底一律由 `tools/base-branch.sh --derive` 推導,不手挑。
腳本這一側的行為:推不出唯一合法基底就回傳 7 並中止,由呼叫端問使用者,不猜也不退回 develop。功能主幹不在遠端時,自動以 develop 為起點建立並推上去,再把建立了哪一條分支印到 stderr。分支名只允許 ASCII(小寫、數字、連字號、斜線),中文簡述先過 `tools/slugify.sh`,`--derive` 不收非 ASCII 分支名。
## Skills 目錄
呼叫方式:Claude / Antigravity `/jsc-git:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。
@@ -24,11 +40,11 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安
### `commit`
追蹤所有檔案變更,依 Commit 格式 `{類型}({需求 or 功能}): {訊息}` 將同類型與同需求的變更認可在一起。訊息格式三選一:完整版(What/Why/How/Who)、簡易版(依 git diff 總結一句)、自訂。
追蹤所有檔案變更,依 Commit 格式 `{類型}({需求 or 功能}): {訊息}` 將同類型與同需求的變更認可在一起。盤點與註解掃描兩件事同時跑,分組草擬要吃盤點的檔案清單,排在盤點之後,可以與掃描並行;掃描要求的修正仍在第一個 commit 之前完成;註解掃描跑 `jsc-hooks` 的 `comment-scope.sh sweep`,攔下夾帶文件相關資訊的註解,腳本不在本機就跳過並在回報中說明,不中止認可。`git add -A` 後單次提交與非繁中訊息由 `jsc-hooks` 的 PreToolUse `Bash` 閘門在程式層擋下,只有 claude 有這道閘門,其餘四支 CLI 仍靠技能內文的規則。訊息格式三選一:完整版(What/Why/How/Who)、簡易版(依 git diff 總結一句)、自訂。由 `pr` 呼叫進來時不查 PR,結果由 `pr` 傳入;單獨呼叫時查一次 `gitea.sh pr-of-branch`,查到就交給 `pr` 校準標題、描述與前置 PR 依賴。
### `pr`
先認可所有變更,再從 develop / main 建立目標分支(分支名只允許 ASCII:類型取 commit 優先度最高者,標題翻譯成英文短語後 slug 化)、push、以範本描述建立 Gitea PR。
呼叫方傳入的基底先驗合法性,再認可所有變更,然後依階梯命名目標分支、push、以範本描述建立 Gitea PR。基底分支由 `base-branch.sh --derive` 從分支名推出上一階;呼叫方傳入的基底與推導結果不同,就當成越級擋下並說明正確階梯,不會悄悄改目標。分支名只允許 ASCII:類型交給 `pick-type.sh` 選,功能與標題先翻譯成英文短語再 slug 化。PR 標題另寫一句繁體中文摘要,說明這條 PR 做了什麼,不套用分支名:分支名給機器判階梯,標題給人看審查列表。分支命名完成就同時啟動 PR 查詢與描述草擬,不等 push。整條呼叫鏈只查一次 `gitea.sh pr-of-branch`;分支已有開啟中的 PR 時不重開,改用該次查詢帶回的標題、base 與描述比對三項,只有不一樣的那幾項才送出 API 呼叫;標題只問還描述不描述得了目前的內容,不拿分支名比對,免得每跑一次就把繁中標題改回 slug 並通知所有審查者。收尾回報使用 `jsc-meta/references/pr-report.md` 的 PR 資訊表格。
<!-- JSC-SKILLS:END -->
@@ -36,7 +52,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安
| 檔案 | 用途 |
| --- | --- |
| `templates/pr-description.md` | PR 描述:摘要 / 變更內容 / 設計重點 / 測試結果 / 前置 PR |
| `templates/pr-description.md` | PR 描述:摘要、變更內容、設計重點、測試結果、前置 PR |
## 相關 domain
+9 -2
View File
@@ -1,6 +1,13 @@
{
"name": "jsc-git",
"version": "0.0.1",
"version": "0.1.8",
"description": "Commit 分組認可與 Push Request 建立",
"skills": "./skills/"
"skills": "./skills/",
"jsc": {
"requires": {
"jsc-gitea": ">=0.1.8",
"jsc-hooks": ">=0.2.8",
"jsc-meta": ">=0.2.2"
}
}
}
+23
View File
@@ -0,0 +1,23 @@
# jsc-git 技能行為清單
本頁記錄 jsc-git 每支技能的行為基準,供技能驗證比對。技能異動時,在同一個 PR 內一起更新這一頁。
## commit
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 工作區有待提交的檔案變更時叫用。`jsc-git:pr` 的步驟 2 也會叫用它。要推送或開 PR 時不叫用這一支,改叫 `jsc-git:pr`。 |
| 關鍵步驟 | 用 `git status --porcelain` 盤點所有變更路徑、同時跑 `jsc-hooks/hooks/comment-scope.sh sweep` 掃過工作區的註解、依「同型別加同需求或功能」把路徑分組、每組一次 `git add` 加一次 `git commit`、訊息寫成 `{type}({scope}): {message}`、校準本分支既有的 PR、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-git:commit {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過。 |
| 外部呼叫 | `git status --porcelain`、`git add`、`git commit`、`git diff`、`jsc-hooks/hooks/comment-scope.sh sweep`、`jsc-gitea/tools/gitea.sh pr-of-branch`(僅獨立執行時)、`jsc-git:pr`(校準既有 PR)、`jsc-ask:ask`(問訊息風格)。分組與訊息草稿交給 sub agent 處理。 |
| 完成條件 | 註解掃描退出 0,或每一則剩餘警告都被判為誤判並說明理由,或腳本不在這台機器上並回報。`git status --porcelain` 印出空白。步驟 5 回報四種結果之一:因為 `jsc-git:pr` 是呼叫方而略過查詢、本分支沒有開啟中的 PR、`jsc-git:pr` 回報三個項目各自相符或已更新、查詢失敗並指名失敗原因。收尾一定要寫一筆 `skill-end` 狀態事件:每一組都提交完且工作區乾淨是 `ok`,`jsc-hooks` 的 Bash 閘門把提交擋在門外是 `blocked`,提交都進去了但既有 PR 沒校準成功是 `degraded`,`git add` 或 `git commit` 中途回非零、工作區還髒是 `failed`,盤點結果是空的、根本沒有變更可提交是 `aborted`。 |
| 可驗證跡象 | 本地 git 歷史多出一批 commit,`git log --oneline` 看得到,每一筆標題是 `{type}({scope}): {message}` 且訊息含繁體中文。工作區乾淨。註解掃描要求的修正直接改在原始碼檔案裡。獨立執行且本分支有開啟中的 PR 時,Gitea 上那條 PR 的標題、描述、前置依賴由 `jsc-git:pr` 更新。這一支不推送、不建立 PR、不寫 wiki 頁。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-git:commit`,`status` 與 `exit` 兩欄對得上上一列講的判準,沒有變更可提交那一輪看得到 `aborted`;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完。 |
## pr
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 工作做完要送審時叫用。既有 PR 在新 commit 之後要重新同步時也叫用。只想提交不想推送時不叫用這一支,改叫 `jsc-git:commit`。 |
| 關鍵步驟 | 先用 `jsc-git/tools/base-branch.sh {呼叫方基底}` 驗證呼叫方傳進來的基底、叫 `jsc-git:commit` 提交全部變更、用 `jsc-git/tools/pick-type.sh` 從 commit 標題選出型別、用 `jsc-git/tools/slugify.sh` 組出階梯狀目標分支名、用 `jsc-git/tools/base-branch.sh --derive {目標分支}` 推導基底並和呼叫方基底比對、以 `git checkout -b {目標分支} origin/{基底分支}` 建分支並 `git push -u origin` 推上去、用 `gitea.sh pr-of-branch` 查一次開啟中的 PR、沒有就用 `gitea.sh pr-create` 開新 PR 並用 `gitea.sh pr-depend` 掛前置依賴、已經有就只更新標題、描述、前置依賴三項裡不同的那幾項、回覆已處理的 PR 意見、回報,最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-git:pr {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過。PR 標題寫一句繁體中文摘要,說明這條 PR 做了什麼,不套用分支名;校準既有 PR 時只問標題還描述不描述得了目前的內容,不拿分支名比對。 |
| 外部呼叫 | `jsc-git/tools/base-branch.sh`、`jsc-git/tools/pick-type.sh`、`jsc-git/tools/slugify.sh`、`templates/pr-description.md`、`jsc-git:commit`、`jsc-gitea/tools/gitea.sh` 的 `pr-of-branch`、`pr-create`、`pr-edit`、`pr-depend`、`comment-reply`、`jsc-gitea:wiki`(讀 PLAN 頁與 ANALYZE 頁)、`jsc-ask:ask`(基底衝突與分支不存在時發問)、`jsc-meta/references/guidelines.md` 的「PR 分支階梯」、`jsc-meta/references/pr-report.md`、`git checkout`、`git cherry-pick`、`git push`、`git ls-remote`。分支標題摘要與描述草稿交給 sub agent 處理。 |
| 完成條件 | 握有一個 PR 網址,送出的標題是一句繁體中文摘要,不是分支名。前置依賴已經處理完:`pr-depend` 印出 `OK` 行,或 PR 標題冠上 `WIP:` 且描述指名前置 PR,或描述寫「無」前置 PR 並在回報裡說明。既有 PR 的三個校準項目各自回報為相符或已更新,標題那一項說明拿什麼內容去判定,送出的 API 呼叫數等於不同的項目數。每一則已處理的意見握有回覆連結或記下失敗理由。回報含 `{owner}/{repo}`、PR 編號、PR 網址、PR 摘要四欄,並指名基底分支、目標分支、更新過的校準項目、自動建立的功能主幹、沒回覆到的意見。收尾一定要寫一筆 `skill-end` 狀態事件:拿到 PR 網址且前置依賴處理完是 `ok`,`base-branch.sh` 回 3、4、7、8、9 讓分支還沒推上去就停住是 `blocked`,`pr-depend` 回 4 退成 `WIP:` 或有意見沒回覆到是 `degraded`,`pick-type.sh` 回 3、推送失敗、`pr-create` 回 4、`pr-edit` 回非零是 `failed`,`pick-type.sh` 回 2 代表沒有可提交的變更、沒有 PR 好開,是 `aborted` 不是 `failed`,使用者在確認關卡喊停也是 `aborted`。 |
| 可驗證跡象 | origin 上多出目標分支的 ref,`git ls-remote --heads origin` 查得到。Gitea 上多出一條 PR,標題是一句繁體中文摘要、分支名仍是 ASCII,兩者不一樣。既有 PR 的標題與描述只在內容變了才被 `pr-edit` 改過,依賴被 `pr-depend` 掛上。PR 描述照 `templates/pr-description.md` 生成,各節不留空。功能主幹不在 origin 上時,`jsc-git/tools/base-branch.sh --derive` 會自動從 develop 建出 `{類型}/{功能}/main` 並推上 origin。本地 git 歷史含 `jsc-git:commit` 建立的 commit,目前分支切到目標分支。PR 意見的回覆留在 Gitea 那幾則意見底下。描述檔草稿寫在暫存檔。這一支不寫 wiki 頁,只讀 PLAN 頁與 ANALYZE 頁。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-git:pr`,`status` 與 `exit` 兩欄對得上上一列講的判準;沒有可提交的變更那一輪,事件上是 `aborted` 加結束碼 2,和開出 PR 的那一輪一眼分得開;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完。 |
+57 -26
View File
@@ -1,40 +1,71 @@
---
name: commit
description: Group all pending file changes by conventional type and feature, then commit each group as {type}({scope}): {message}. Message style is full (What/Why/How/Who), brief (one line from git diff), or custom, chosen via decision tree. Use whenever changes must be committed; not for push or PR creation.
description: 'Group all pending file changes by conventional type and feature, then commit each group as {type}({scope}): {message}. Before committing, sweep the working tree with jsc-hooks/hooks/comment-scope.sh so no comment carrying document tracking information enters a commit. Message style is full (What/Why/How/Who), brief (one line from git diff), or custom, chosen via decision tree. Once the commits land, an open PR on the current branch gets its title, description, and prerequisite dependency calibrated through jsc-git:pr. Use whenever changes must be committed; not for push or PR creation.'
---
# commit — 分組認可檔案變更
# commit — group and commit file changes
## 步驟
## Steps
1. 追蹤所有檔案變更:先 `git status --porcelain` 檢視全部變更,再逐組 `git add`(不可盲目 `git add -A` 後一次 commit)。
2. 依「同類型 + 同需求/功能」將檔案變更分組,一組一個 commit。
3. 每組依格式認可:`{類型}({需求 or 功能}): {訊息}`。
1. Track every file change: inspect all changes with `git status --porcelain` first. That inventory is this step's own work; the `git add` that follows is group by group, one add per group as step 4 commits it, never one bulk add here. `git add -A` followed by one bulk commit is blocked in code by the `jsc-hooks` PreToolUse `Bash` guard, which rejects that command pair before it runs. Only claude has a PreToolUse stage; on codex, copilot, antigravity and kiro that guard never fires, so on those four CLIs this step is the only thing holding the rule — add group by group there as well. Done when `git status --porcelain` has run and its full path list is held. That list is what step 3 groups, so this step never waits on the grouping.
2. Sweep the comments about to be committed: run `jsc-hooks/hooks/comment-scope.sh sweep` over this working tree. Start the sweep at the same time as step 1's inventory: both only read the working tree, and neither needs the other's result. Step 3's grouping is not part of that pair — it needs step 1's path list — but it may run while this sweep is still going. Every fix the sweep demands still lands before step 4 runs the first commit. A code comment states why the code is written this way, never where the work is documented; the rule text and its allow list live in one place only, `jsc-review/references/comment-scope.md`.
- The script exits 0 in silence when it finds no git working tree and when `jsc-hooks` is not on this machine. **If the script is not found, skip this step, say so in the report and commit anyway** — missing infrastructure is not a violation.
- Exit 2 means a hit. Fix every comment line the warning points at, in place, then run the sweep again until it exits 0, and commit only after that.
- The warning is written for a person and a model to judge, **not a hard block**. When it is a false positive, say plainly why and carry on with the commit; never delete a useful comment just to keep the script quiet.
- Done when one of these is true and reported: the sweep exited 0, or every remaining warning is reported with the reason it is a false positive, or the script was not found on this machine.
3. Group the changes by same type plus same requirement or feature. One group is one commit. Grouping takes step 1's path list as its input, so it starts once that inventory is held; step 2's sweep may still be running alongside it. Done when every path on step 1's list sits in exactly one group, and each group carries one type and one requirement or feature.
4. Commit each group with the format `{type}({requirement or feature}): {message}`. Done when `git status --porcelain` returns empty; report done only then.
5. Calibrate the open PR of this branch. Who called this run decides whether a lookup happens at all:
- `jsc-git:pr` called this run → skip the lookup. That skill runs the single open-PR lookup of the chain in its own step 6 and owns the calibration from there. Done when the report names `jsc-git:pr` as the caller and states that the lookup was skipped.
- This run is standalone → look the PR up once with `jsc-gitea/tools/gitea.sh pr-of-branch {owner}/{repo} {current branch}`. On exit 0 the script prints line 1 `number<TAB>{PR number}`, line 2 `title<TAB>{title}`, line 3 `base<TAB>{base}`, line 4 the marker `body`, and line 5 onward the description.
- Exit 0 → hand the PR number together with the returned title, base and description to `jsc-git:pr`, which compares title, description and prerequisite dependency and updates only what differs, without repeating the lookup. That hand-off does not loop back here: the tree is already clean, and `jsc-git:pr` as the caller makes this step skip its lookup.
- Exit 3 (no open PR on this branch) → nothing to calibrate.
- Exit 2 (usage error) → the `{owner}/{repo}` or the branch argument is missing or malformed. Correct the arguments and rerun.
- Exit 4 (API call failed) → report the failure and leave the PR state as unknown. Never read a failed call as "no open PR": that leaves a stale title on a branch that just gained commits.
- Any other non-zero → treat it as exit 4.
- Done when the report states exactly one of these: the lookup was skipped because `jsc-git:pr` called this run, the branch has no open PR, `jsc-git:pr` reported each of the three items as matched or updated, or the lookup failed and the failure is named.
6. Record how this run ended, as the very last thing this skill does:
## 類型表
`jsc-hooks/tools/report-status.sh skill-end jsc-git:commit {status} {exit} "{detail}"`
| 類型 | 用途 |
Resolve that path the way step 2 already resolves `jsc-hooks/hooks/comment-scope.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands**, the same way step 2 commits anyway when the sweep is not installed. The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. Commits that landed stay landed whether or not the run could be recorded.
| status | This skill's case |
| --- | --- |
| `ok` | Every group is committed, `git status --porcelain` prints nothing, and step 5 stated one of its four outcomes. A step 2 sweep that was skipped because the script is not on this machine is still `ok` — say so in `{detail}`, since a run judged without the sweep is worth telling apart from one the sweep passed |
| `blocked` | The `jsc-hooks` PreToolUse `Bash` guard rejected the commit before git ran — a bulk `git add -A` pair, or a message carrying no Traditional Chinese — so no commit landed and the working tree is exactly as it was |
| `degraded` | Every group is committed and the tree is clean, but the close-out is short: step 5's `pr-of-branch` returned 4, or the `jsc-git:pr` calibration failed, so the open PR still carries a title written before these commits |
| `failed` | A `git add` or `git commit` returned non-zero part-way through, leaving some groups committed and the tree dirty. Report the group that broke; a partial commit set is what the next run has to reconcile |
| `aborted` | Step 1's inventory came back empty, so there was nothing to commit and nothing was attempted. Also the user stopping the run at the grouping or the message-style question. This is not a success: a run that committed nothing must not read like a run that committed everything |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: group and commit counts plus exit codes, never commit messages, branch names, or personal data.
Done when the command has run, or the script was absent and this step was skipped.
## Type table
| Type | Purpose |
| --- | --- |
| feat | 新增/修改功能 |
| fix | 修補 bug |
| docs | 文件 |
| style | 格式(不影響程式碼運行的變動) |
| refactor | 重構(既不是新增功能,也不是修補 bug 的程式碼變動) |
| perf | 改善效能 |
| test | 增加測試 |
| chore | 建構程序或輔助工具的變動 |
| revert | 撤銷回覆先前的 commit |
| feat | add or change a feature |
| fix | fix a bug |
| docs | documentation |
| style | formatting; no change to how the code runs |
| refactor | code change that neither adds a feature nor fixes a bug |
| perf | improve performance |
| test | add tests |
| chore | build process or tooling change |
| revert | revert an earlier commit |
## 訊息格式(三選一)
## Message format (pick one of three)
依 `jsc-ask:ask` 規則詢問使用者;問詢紀錄或本 session 已有慣例就不再問。
Ask the user per the `jsc-ask:ask` rules. Skip the question when the question record or this session already holds a convention.
1. **完整版**:包含做什麼(What)、為什麼做(Why)、怎麼做(How)、哪個功能做(Who)。
2. **簡易版**:根據 `git diff` 總結出一句描述。
3. **自訂**:使用者自行輸入訊息。
1. **Full**: covers What, Why, How, and Who (which feature).
2. **Brief**: one sentence summarized from `git diff`.
3. **Custom**: the user types the message.
## 規則
## Rules
1. 分組與訊息草擬的細節**必須以 sub agent 執行**,主 agent 只確認分組結果與執行 commit。
2. 訊息一律 UTF-8 繁體中文(類型與 scope 除外)。
3. 不可 push;push 與 PR 由 `jsc-git:pr` 負責。
1. The grouping and message drafting details **MUST run as a sub agent**. The main agent only confirms the grouping and runs the commits.
2. Write messages in UTF-8 Traditional Chinese (the type and scope stay in English), per the STE100 output rule. The `jsc-hooks` PreToolUse `Bash` guard rejects a `git commit` whose message carries no Traditional Chinese. That guard runs on claude only; on codex, copilot, antigravity and kiro nothing blocks such a message, so on those four CLIs this rule carries the whole load.
3. Never push, and never create a PR. Step 5 only calibrates a PR that already exists; pushing and creating PRs belong to `jsc-git:pr`.
+97 -21
View File
@@ -1,29 +1,105 @@
---
name: pr
description: Commit all changes via jsc-git:commit, create a target branch named from the highest-priority commit type plus a summarized title, push, then open a Gitea PR with the templated description. Branch priority is revert > fix > feat > perf > refactor > test > docs > style > chore. Use when work is ready for review; not for plain commits.
description: Commit all changes via jsc-git:commit, name a ladder-shaped target branch, derive the base with jsc-git/tools/base-branch.sh (sub-feature → feat/{feature}/main → develop → master; fix → develop → master, no level skipping), push, then open a Gitea PR with the templated description. When the branch already has an open PR, compare its title, description, and prerequisite dependency, and update only the items that differ. Use when work is ready for review, or when an open PR needs re-syncing after new commits; not for plain commits.
---
# pr — 建立 Push Request
# pr — create a Push Request
## 步驟
The ladder rules live in one place only: section 「PR 分支階梯」 of `jsc-meta/references/guidelines.md`. `jsc-git/tools/base-branch.sh --derive` is the running implementation of that section. Read the rung a branch belongs to there; never hand-pick a base, and never restate the ladder table in this file.
1. 呼叫 `jsc-git:commit` 將所有檔案變更認可完成。
2. 決定來源分支:遠端存在 `develop` 就用 `develop`,否則用 `main`(再否則用 `master`)。
3. 從遠端來源分支建立目標分支:
1. 類型取所有 commit 中優先度最高者:`revert > fix > feat > perf > refactor > test > docs > style > chore`。
2. 標題從所有 commit 的訊息總結出一句。
3. 分支名**只允許 ASCII**,一律 slug 化:`{類型}/{需求 or 功能}-{標題}`。需求/功能與標題先翻譯成英文短語,再轉小寫、非 `a-z0-9` 字元以連字號取代、連續連字號合併、頭尾連字號移除(例:`feat/order-匯出報表` → `feat/order-export-report`)。不可含空白、括號、冒號與任何非 ASCII 字元。
4. `git push -u origin {目標分支}`。
5. 建立 PR:`jsc-gitea/tools/gitea.sh pr-create {owner}/{repo} {目標分支} {來源分支} "{分支名}" {描述檔}`。
- 標題 = 分支名。
- 描述先套用 `templates/pr-description.md` 寫入暫存檔再帶入。
6. **前置 PR 阻擋**:描述中有前置 Push Request 時,執行
`jsc-gitea/tools/gitea.sh pr-depend {owner}/{repo} {本 PR 編號} {前置 owner}/{repo} {前置 PR 編號}`
把本 PR 掛上依賴;前置 PR 未關閉前 Gitea 會阻擋合併,避免誤合併。API 不可用時降級:PR 標題加 `WIP:` 前綴(Gitea 原生阻擋合併),前置完成後移除。
7. 回報 PR URL。
Every script path in this file starts with its plugin's own directory name — `jsc-git/tools/…` for this plugin's, `jsc-gitea/tools/…` and `jsc-hooks/tools/…` for the others — and is resolved against the tool root the caller supplies, never against this skill's own directory. These three scripts sit at the plugin root, not under `skills/pr/`, so dropping the plugin directory name leaves a path that resolves against whatever the current directory happens to be — the right script inside this repo, a different domain's script outside it, or nothing at all and exit 127. That failure is worse than it looks: step 4 says never hand-pick a base, and an agent that cannot run the deriver is one step away from picking one anyway, which turns a wrong path into a PR opened against the wrong rung.
## 規則
## Steps
1. 描述範本各節不可留空:沒有計畫/分析頁就填「無」,沒有前置 PR 就填「無」。
2. 計畫頁與分析頁連結由 `jsc-sdlc` 的 wiki 頁取得;分析頁連結必須導向工作包標題錨點。
3. 描述草擬的細節**必須以 sub agent 執行**。
1. **Validate the caller's base first.** A caller may pass a base in (`jsc-sdlc:implement` passes the analysis page's source branch). Whether that base is legal depends on origin alone, not on the target branch name, so settle it before any naming work. Skip the whole step when no caller base was passed, and record "no caller base" for step 4. Otherwise run `jsc-git/tools/base-branch.sh {caller base}` and branch on its exit code:
- 0 → keep the printed name; step 4 cross-checks it against the derived base.
- 2 (too many arguments) → the base arrived as several words. Quote it as one argument and rerun.
- 3 (cannot reach origin) → stop and report that origin is unreachable. Nothing downstream works without the remote.
- 4 (caller base not on origin) → stop, report the caller base, and ask the user per the `jsc-ask:ask` rules which existing branch was meant. Never substitute another branch.
- 5 (develop, main and master all missing on origin) → the script only reaches this code with no argument, so the caller base was dropped on the way in. Stop and report that the argument never reached the script.
- 6 (empty string) → the caller's base variable is unset. Stop and ask the caller for the real branch name. Never rerun with the argument removed: that silently falls back to `develop`.
- 7, 8, 9 → these belong to `--derive` mode only. Seeing one means the wrong mode ran. Stop and report which command line was used.
- Done when the script printed one branch name, or the run is recorded as having no caller base.
2. Call `jsc-git:commit` to commit every file change. Tell it that `jsc-git:pr` is the caller, so it skips its own open-PR lookup — step 6 here is the only such lookup in this chain. Done when `git status --porcelain` prints nothing.
3. Build the target branch name in ladder shape:
1. Pipe the subjects of step 2's commits into `jsc-git/tools/pick-type.sh` (`git log --format=%s {range} | jsc-git/tools/pick-type.sh`), which owns the type priority order. Exit 0 → take the printed type. Exit 2 (no input) → step 2 produced no commits, so there is nothing to open a PR for; stop and report. Exit 3 (no ladder type in the input) → stop, report the subjects, and correct them to `{type}({scope}): {message}` before retrying. Done when the script printed exactly one type.
2. Summarize one title from all commit messages, as a single Traditional Chinese sentence saying what this PR does. This sentence is the PR title steps 7 and 8 use; step 3.3 only borrows its meaning to build the ASCII slug. Done when one title line covers every commit in the range and reads as one Traditional Chinese sentence.
3. Translate the feature and the title into short English phrases, then build the name with `jsc-git/tools/slugify.sh`. `fix` takes one call: `jsc-git/tools/slugify.sh fix {change phrase}` → `fix/order-export-crash`. Every other type takes two calls, feeding the first result back in as the type: `jsc-git/tools/slugify.sh feat {feature phrase}` → `feat/order-export`, then `jsc-git/tools/slugify.sh feat/order-export {sub-feature phrase}` → `feat/order-export/report-filter`. Done when the name has the shape its ladder row requires; exit 2 (non-ASCII input) or exit 3 (empty slug) → re-translate into an English phrase and retry, never hand-build the branch name.
- The name is the only input step 6's lookup and the description draft need. Start both the moment this step ends, and run them alongside steps 4 and 5; neither waits for the push.
4. Resolve the base branch:
- Run `jsc-git/tools/base-branch.sh --derive {target branch}`. Exit 0 → the script printed one branch name.
- Exit 2 (too many arguments) → the branch name arrived as several words. Quote it as one argument and rerun.
- Exit 3 (cannot reach origin) → stop and report that origin is unreachable.
- Exit 4, 5 or 6 → these belong to caller mode. Seeing one means the `--derive` flag was dropped. Rerun with the flag.
- Exit 7 (no unique legal base), 8 (derived base missing on origin) or 9 (auto-create failed) → report the script's message and stop. Never fall back to `develop`.
- Step 1 held a caller base → compare the two names. Same → use it. Different → the pair skips a rung, so stop, report the caller base, the derived base, and the ladder row that applies per `jsc-meta/references/guidelines.md`, and ask the user per the `jsc-ask:ask` rules whether to rename the branch or correct the caller base. Never silently retarget the PR.
- The feature trunk missing on origin is not an error: the script creates it from `develop`, pushes it, and prints 「已自動從 develop 建立功能主幹 {分支}」 to stderr. Capture that line; step 9 has to report it.
- Done when one base name is held, and the caller base either matched it or the user settled the mismatch.
5. Put the branch on origin, in one pass: run `git checkout -b {target branch} origin/{base branch}` so the branch starts at the remote base, bring step 2's commits onto it (`git cherry-pick` them when they landed on another branch), then run `git push -u origin {target branch}`. Done when `git branch --show-current` prints the target branch, `git log --oneline origin/{base branch}..HEAD` lists exactly step 2's commits, and `git ls-remote --heads origin` shows the target branch's ref.
6. Look up the open PR of the target branch: `jsc-gitea/tools/gitea.sh pr-of-branch {owner}/{repo} {target branch}`. Launch it as soon as step 3 named the branch. This is the only open-PR lookup in the whole chain: step 8 reuses this output and never calls `pr-get`. On exit 0 the script prints line 1 `number<TAB>{PR number}`, line 2 `title<TAB>{title}`, line 3 `base<TAB>{base}`, line 4 the marker `body`, and line 5 onward the description.
- Exit 0 → hold the PR number, title, base and body; skip step 7 and go to step 8, because the PR exists and only needs calibrating.
- Exit 3 (no open PR on that branch) → go to step 7 and create one.
- Exit 2 (usage error) → the `{owner}/{repo}` or the branch argument is missing or malformed. Correct the arguments and rerun.
- Exit 4 (API call failed) → stop and report the failure. Never read a failed call as "no open PR": that opens a second PR on a branch that already has one.
- Any other non-zero → treat it as exit 4 and stop.
- Done when either the PR number plus its title, base and body are held, or exit 3 confirmed the branch has no open PR.
7. Create the PR, then hang its prerequisite on it. Run `jsc-gitea/tools/gitea.sh pr-create {owner}/{repo} {target branch} {base branch} "{branch name}" {description file}`. Ask the user to confirm before this call runs.
- Title = step 3.2's summary: one Traditional Chinese sentence saying what this PR does. Never pass the branch name as the title. A branch name and a title carry different jobs — the branch name is a machine-readable ASCII slug that the ladder and `jsc-git/tools/base-branch.sh` parse, while the title is the one line a human reads in the review list, where a column of long slugs shows who touched the repo but never what changed. The STE100 output rule in `jsc-meta/references/ste100.md` names PR titles and descriptions explicitly, and a hook enforces it; binding the title to the branch name is what put the two rules in conflict, so the title gives way to the language rule and the branch name stays ASCII per rule 4.
- Write the description into a temp file first, using `templates/pr-description.md` (a Traditional Chinese template; the generated description stays in Traditional Chinese per the STE100 output rule).
- Exit 0 → the command prints a PR URL; keep it.
- Exit 2 (description file not found) → write the description file, then rerun.
- Exit 1 → an older `gitea.sh`, whose `pr-create` never checks the description file and lets the read throw instead. Same cause and same handling as exit 2: correct the description file path, then rerun.
- Exit 4 (the API rejected the request) → report the script's message and stop. Never retry against a different base.
- Any other non-zero → treat it as exit 4 and stop.
**Prerequisite PR blocking**: with the PR URL in hand, and only when the description lists a prerequisite Push Request, run
`jsc-gitea/tools/gitea.sh pr-depend {owner}/{repo} {this PR number} {prerequisite owner}/{repo} {prerequisite PR number}`
to add the dependency. Gitea then blocks merging until the prerequisite PR closes.
- Exit 0 → the command prints `OK {owner}/{repo}#{number} depends on ...`.
- Exit 2 (usage error) → an argument is missing or malformed. Correct it and rerun.
- Exit 4 (the dependency API call failed) → degrade: prefix the PR title with `WIP:`, which Gitea blocks merging on natively, and record in the PR description that the prefix comes off once the prerequisite PR closes.
- Any other non-zero → treat it as exit 4 and degrade the same way.
Done when a PR URL is held, the title sent to `pr-create` was the Traditional Chinese sentence and not the branch name, **and** the prerequisite is settled — `pr-depend` printed its `OK` line, or the PR title starts with `WIP:` and the description names the prerequisite PR, or the description names no prerequisite and that is stated. A PR created here goes straight to step 9.
8. **Calibrate an existing PR** (step 6 returned a PR number): compare three items against what this run produced, using the title, base and description step 6 already returned. Send an API call only for the items that differ.
1. Title against what this PR now contains. Read the existing title and ask one question only: does it still describe the PR's current content, now that step 2's commits are in? Yes → leave it alone. No → draft a replacement, again one Traditional Chinese sentence saying what this PR does. The branch name never enters this comparison. Comparing them would rewrite a good Traditional Chinese title back into an ASCII slug on every run and notify every reviewer each time, which is the opposite of what a title is for: the branch name is the machine's handle on the ladder, the title is what a human reads in the review list.
2. Description against a freshly drafted description from `templates/pr-description.md`.
3. Prerequisite PR dependency against the one the description names.
- Title or description differs → write the new description to a temp file and run `jsc-gitea/tools/gitea.sh pr-edit {owner}/{repo} {pr number} "{title}" {description file}`, which carries both items in one call. Ask the user to confirm before this call runs. Exit 0 → updated. Exit 2 (description file not found) → write the file and rerun. Exit 4 or any other non-zero → report the script's message and stop, and say plainly that the PR still holds its old title and description.
- The dependency differs → run `pr-depend` with the exit code branching of step 7. Ask the user to confirm before this call runs.
- All three match → change nothing. Every needless edit notifies every reviewer, so silence is the correct outcome here.
- Done when each of the three items is reported as either matched or updated, the title verdict states which content the title was judged against rather than any branch name, and the number of API calls made equals the number of items that differed.
9. Reply to the handled comments, then report the run.
If this run follows PR comment fixes and the caller supplied handled comment ids, reply to each comment first with `jsc-gitea/tools/gitea.sh comment-reply`. The reply content and the comment type mapping live in `jsc-meta/references/pr-report.md`; the exit codes belong here. Run one call per handled comment and branch on each call's own exit code:
- Exit 0 → the command prints the reply link. Keep it against that comment id.
- Exit 2 (the reply file is missing, or the kind is not `issue`, `review` or `inline`) → correct the reply file path or the kind, then rerun that one call.
- Exit 4 (the API call failed), or any other non-zero → record that comment id with the reason it failed and carry on with the remaining comments. One failed reply never ends the round.
Then report the PR with the table format in `jsc-meta/references/pr-report.md`, followed by the base branch, the target branch, which of the three calibration items were updated, the feature trunk the script auto-created when step 4 printed that line, and every comment that got no reply.
Done when every handled comment holds either a reply link or a recorded failure reason, and the report includes the PR table columns `{owner}/{repo}`, PR number, PR URL and PR summary, names all branch and calibration details, and names the comments that got no reply.
10. Record how this run ended, as the very last thing this skill does:
`jsc-hooks/tools/report-status.sh skill-end jsc-git:pr {status} {exit} "{detail}"`
Resolve that path the way this file already resolves `jsc-gitea/tools/gitea.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A PR that is open stays open whether or not the run could be recorded.
| status | This skill's case |
| --- | --- |
| `ok` | A PR URL is held, the prerequisite is settled by a `pr-depend` exit 0 or by a description that names none, and every handled comment holds a reply link. Calibrating an existing PR and changing none of the three items is `ok` as well: silence is the correct outcome there |
| `blocked` | The ladder refused before anything was pushed: `base-branch.sh` exit 3 (origin unreachable), exit 4 (the caller base is not on origin), exit 7 (no unique legal base), exit 8 (the derived base is missing on origin) or exit 9 (the auto-create failed). No branch reached origin and no PR was opened |
| `degraded` | The PR is open but part of the close-out did not land: `pr-depend` exit 4 sent the run to the `WIP:` fallback, so the dependency is not on the PR, or a handled comment ended with a recorded failure instead of a reply link |
| `failed` | The run got partway and then git or the API refused: `pick-type.sh` exit 3 (commits exist but no subject carries a ladder type), a push that failed, `pr-create` exit 4, or a non-zero `pr-edit` that left the old title and description in place |
| `aborted` | **`pick-type.sh` exit 2 — step 2 produced no commit, so there is nothing to open a PR for.** That is a run which correctly stopped, not a run that failed and not a run that succeeded; recording it as anything else is what made a round with no change look identical to a round that shipped eight PRs. The user declining the confirmation before `pr-create`, `pr-edit` or `pr-depend` is `aborted` too |
`{exit}` is the exit code of whatever decided the status, `0` for `ok` — so the no-change round above carries `aborted` with `2`. `{detail}` is one short line well under 200 characters: counts and exit codes plus which of the three calibration items moved, never the PR title, the PR number, branch names, or personal data.
Done when the command has run, or the script was absent and this step was skipped.
## Rules
1. No template section may stay empty: fill the literal 「無」 when there is no plan page, analyze page, or prerequisite PR.
2. Read the `jsc-sdlc` plan page and analyze page through `jsc-gitea:wiki`, which resolves `JSC_WIKI_REPO_PLAN` for the plan page and `JSC_WIKI_REPO_ANALYZE` for the analyze page from the inherited environment, falls back to `JSC_WIKI_REPO`, and asks only when neither is set. Each page type reads its own variable only; a page type never borrows another type's variable. Take both links from the pages read this way; the analyze link must point at the work package heading anchor.
3. Branch title summarization (step 3.2) and description drafting MUST run as a sub agent. The description sub agent starts right after step 3 and runs alongside steps 4 and 5, so steps 7 and 8 already hold a draft.
4. Branch names are ASCII only (`a-z0-9`, `/`, `-`). Traditional Chinese phrases go through `jsc-git/tools/slugify.sh` first; `jsc-git/tools/base-branch.sh --derive` rejects anything else. PR titles and descriptions take the opposite rule: they stay Traditional Chinese per `jsc-meta/references/ste100.md`. The two never copy each other.
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env sh
# base-branch.sh — 決定 PR 的基底分支。
# 用法: base-branch.sh [caller-base]
# base-branch.sh --derive [branch]
#
# 模式一(既有): 呼叫方傳入的分支最優先;呼叫方沒傳,才依序試 develop、main、master。
# 「有沒有傳」看參數個數,不看參數內容:呼叫方寫 base-branch.sh "$SOURCE_BRANCH"
# 而變數沒設定時,那是空字串,不是沒傳。若當成沒傳就會悄悄退回 develop,
# 把單一工作包直接合進 develop——這支腳本存在的目的就是擋這件事。
#
# 模式二(--derive): 從分支名推出唯一合法的上一階基底,禁止越級。
# 沒傳 branch 就取目前分支。階梯如下:
# feat、docs、style、refactor、perf、test、chore、revert:
# {類型}/{功能}/{子功能} → {類型}/{功能}/main → develop → master
# {子功能} 可以多層(例:feat/a/b/c),推導一律「去掉最後一段後接 /main」。
# fix:
# fix/{修改} → develop → master
# 推不出唯一合法基底就中止(退出碼 7),由呼叫端問使用者,不猜、也不退回 develop。
# 功能主幹 {類型}/{功能}/main 不在 origin 時,自動以 origin/develop 為起點建立並推上去,
# 再把建立了哪一條分支印到 stderr。
#
# 分支名只允許 ASCII(a-z0-9 與 /、-)。中文簡述先交給同目錄的 slugify.sh 轉成 ASCII slug,
# 再組成分支名,這支腳本不接受非 ASCII 分支名。
#
# 輸出: 選中的分支名(一行)。錯誤訊息一律印繁中到 stderr。
# 結束碼: 0=stdout 印出一個基底分支名,兩種模式共用。直接拿它開 PR。
# 2=參數過多(兩種模式共用)。分支名要用引號包成單一參數,再重跑。
# 3=連不上 origin,git fetch 失敗(兩種模式共用)。先確認遠端可以連線,再重跑。
# 4=(呼叫方模式)呼叫方指定的分支不在 origin 上。停下來問使用者原本要的是哪一條,
# 不要自行改用其他分支。
# 5=(呼叫方模式)origin 上找不到 develop、main、master。這個碼只在完全沒傳參數時
# 才會出現,所以真正的問題通常是基底參數在路上掉了。請由呼叫方指定基底分支。
# 6=(呼叫方模式)傳進來的是空字串。回去補上分支變數的值,不要改成整個參數不傳——
# 不傳會悄悄退回 develop。
# 7=(--derive 模式)推不出唯一合法基底:站在斷頭狀態、站在 master、分支名含非 ASCII
# 或其他不允許的字元、類型不在階梯表內、feat 這類階梯少了功能層,或 fix 寫成多層。
# 照 stderr 的訊息修分支名再重跑,不要退回 develop。
# 8=(--derive 模式)推導出的基底不在 origin 上,而且它不是可以自動建立的功能主幹。
# 先把那條分支建出來並推上 origin,再重跑。
# 9=(--derive 模式)自動建立功能主幹失敗:origin 上沒有 develop,或推送被拒。
# 先建好 develop,或確認推送權限,再重跑。
set -u
MODE=caller
if [ "$#" -ge 1 ] && [ "$1" = "--derive" ]; then
MODE=derive
shift
fi
if [ "$#" -gt 1 ]; then
echo "用法: base-branch.sh [caller-base] 或 base-branch.sh --derive [branch]" >&2
exit 2
fi
ARGC=$#
ARG=${1:-}
if [ "$ARGC" -eq 1 ] && [ -z "$ARG" ]; then
echo "錯誤: 傳入空字串當分支名。請確認分支變數有值,或整個參數不要傳。" >&2
exit 6
fi
if ! git fetch --prune origin >/dev/null 2>&1; then
echo "錯誤: 無法向 origin 取得遠端分支。請確認遠端可以連線,再重試。" >&2
exit 3
fi
remote_has() {
[ -n "$(git ls-remote --heads origin "refs/heads/$1" 2>/dev/null)" ]
}
if [ "$MODE" = "caller" ]; then
if [ "$ARGC" -eq 1 ]; then
if remote_has "$ARG"; then
printf '%s\n' "$ARG"
exit 0
fi
echo "錯誤: 呼叫方指定的基底分支 $ARG 不在 origin 上。請確認分支名,不要自行改用其他分支。" >&2
exit 4
fi
for candidate in develop main master; do
if remote_has "$candidate"; then
printf '%s\n' "$candidate"
exit 0
fi
done
echo "錯誤: origin 上找不到 develop、main、master。請由呼叫方指定基底分支。" >&2
exit 5
fi
# 以下是 --derive 模式。
BRANCH=$ARG
if [ -z "$BRANCH" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)
fi
if [ -z "$BRANCH" ] || [ "$BRANCH" = "HEAD" ]; then
echo "錯誤: 取不到目前分支名(可能在斷頭狀態)。請先切到分支,或直接把分支名當參數傳進來。" >&2
exit 7
fi
if printf '%s' "$BRANCH" | LC_ALL=C grep -q '[^a-z0-9/-]'; then
echo "錯誤: 分支名 $BRANCH 含分支名不允許的字元。分支名只允許 ASCII 小寫、數字、連字號與斜線;中文簡述請先交給 slugify.sh。" >&2
exit 7
fi
TYPE=${BRANCH%%/*}
SEGMENTS=$(printf '%s' "$BRANCH" | tr '/' '\n' | grep -c '')
LAST=${BRANCH##*/}
HEAD_PART=${BRANCH%/*}
ladder_type() {
case "$1" in
feat|docs|style|refactor|perf|test|chore|revert) return 0 ;;
*) return 1 ;;
esac
}
BASE=""
TRUNK_AUTOCREATE=no
if [ "$BRANCH" = "develop" ]; then
BASE=master
elif [ "$BRANCH" = "master" ]; then
echo "錯誤: master 已經是階梯頂端,推不出上一階基底分支。請確認是不是站錯分支。" >&2
exit 7
elif [ "$TYPE" = "fix" ]; then
if [ "$SEGMENTS" -eq 2 ]; then
BASE=develop
else
echo "錯誤: 分支 $BRANCH 不符合 fix 階梯。fix 只有 fix/{修改} → develop → master 一條路,不接受多層分支名。" >&2
exit 7
fi
elif ladder_type "$TYPE"; then
if [ "$SEGMENTS" -lt 3 ]; then
echo "錯誤: 分支 $BRANCH 少了功能層,推不出唯一合法基底。$TYPE 階梯為 $TYPE/{功能}/{子功能} → $TYPE/{功能}/main → develop → master,請補上功能層再重試。" >&2
exit 7
elif [ "$LAST" = "main" ]; then
BASE=develop
else
BASE="$HEAD_PART/main"
TRUNK_AUTOCREATE=yes
fi
else
echo "錯誤: 分支 $BRANCH 的類型 $TYPE 不在階梯表內,推不出唯一合法基底。請改用 feat、fix、docs、style、refactor、perf、test、chore、revert 其中一種類型。" >&2
exit 7
fi
if remote_has "$BASE"; then
printf '%s\n' "$BASE"
exit 0
fi
if [ "$TRUNK_AUTOCREATE" = "no" ]; then
echo "錯誤: 推導出的基底分支 $BASE 不在 origin 上,而且它不是可以自動建立的功能主幹。請先建立 $BASE,再重試。" >&2
exit 8
fi
# 功能主幹不存在就自動補一條:直接把 origin/develop 推成新分支,
# 不動本機工作區,省下切分支與切回來的來回。
if ! remote_has develop; then
echo "錯誤: origin 上沒有 develop,無法自動建立功能主幹 $BASE。請先建立 develop,再重試。" >&2
exit 9
fi
if ! git push origin "refs/remotes/origin/develop:refs/heads/$BASE" >/dev/null 2>&1; then
echo "錯誤: 自動建立功能主幹 $BASE 失敗。請確認遠端推送權限,或手動從 develop 建立 $BASE。" >&2
exit 9
fi
echo "已自動從 develop 建立功能主幹 $BASE,並推上 origin。" >&2
printf '%s\n' "$BASE"
exit 0
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env sh
# pick-type.sh — 從一組 commit 型別中選出優先度最高的一個。
# 用法: pick-type.sh {型別或 commit 標題}...
# git log --format=%s {範圍} | pick-type.sh
#
# 說明: 優先序固定為 revert > fix > feat > perf > refactor > test > docs > style > chore。
# 這支腳本是該優先序的唯一真實來源,SKILL.md 不再抄一份。
# 帶參數就讀參數,一個參數算一項;沒帶參數就讀標準輸入,一行算一項。
# 每一項只取冒號、左括號、驚嘆號之前的字,所以 commit 標題整行餵進來也認得出型別。
# 認不得的項目直接略過,不影響其他項目。
#
# 輸出: 勝出的型別(一行,例如: feat)。
# 結束碼: 0 選出型別;2 沒有收到任何輸入;3 輸入裡沒有階梯表內的型別。
# 錯誤訊息一律印繁中到 stderr。
set -u
if [ "$#" -gt 0 ]; then
RAW=$(printf '%s\n' "$@")
else
RAW=$(cat)
fi
if [ -z "$(printf '%s' "$RAW" | tr -d '[:space:]')" ]; then
echo "錯誤: 沒有收到任何型別。請把 git log 取出的型別集合當參數傳入,或從標準輸入餵進來。" >&2
exit 2
fi
NORM=$(printf '%s\n' "$RAW" \
| sed -e 's/[(:!].*$//' -e 's/[[:space:]]//g' \
| tr '[:upper:]' '[:lower:]')
for candidate in revert fix feat perf refactor test docs style chore; do
if printf '%s\n' "$NORM" | grep -qx "$candidate"; then
printf '%s\n' "$candidate"
exit 0
fi
done
echo "錯誤: 輸入裡沒有階梯表內的型別。請確認 commit 訊息符合 {類型}({範圍}): {訊息} 格式,型別限 revert、fix、feat、perf、refactor、test、docs、style、chore。" >&2
exit 3
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env sh
# slugify.sh — 把分支類型與描述轉成 ASCII 分支名 slug。
# 用法: slugify.sh {type} {phrase}
# 規則: 全部轉小寫,非 a-z0-9 的字元換成連字號,
# 連續連字號合併成一個,並去除開頭與結尾的連字號。
# 輸出: {type}/{slug}(例如: slugify.sh feat "export report" → feat/export-report)
# 結束碼: 0=stdout 印出 {type}/{slug},直接拿去當分支名。
# 1=參數少於兩個。補上 type 與 phrase 再重跑。
# 2=type 或 phrase 含非 ASCII 字元。先把描述翻成英文短語再重跑,
# 不要自己動手拼分支名。
# 3=phrase slug 化之後是空字串(裡面一個 a-z0-9 都沒有)。換一句英文短語再重跑。
# 錯誤訊息都印到 stderr,其中 2 與 3 印繁體中文。
set -u
if [ "$#" -lt 2 ]; then
echo "用法: slugify.sh {type} {phrase}" >&2
exit 1
fi
TYPE="$1"
shift
PHRASE="$*"
slug=$(printf '%s' "$PHRASE" | tr '[:upper:]' '[:lower:]' | sed -e 's/[^a-z0-9]/-/g' -e 's/-\{2,\}/-/g' -e 's/^-//' -e 's/-$//')
if printf '%s%s' "$TYPE" "$PHRASE" | LC_ALL=C grep -q '[^ -~]'; then
echo "錯誤: 分支名只允許 ASCII。請先把標題翻譯成英文短語,再交給 slugify.sh。" >&2
exit 2
fi
if [ -z "$slug" ]; then
echo "錯誤: 標題 slug 化後是空字串,組不出分支名。請先把標題翻譯成英文短語,再交給 slugify.sh。" >&2
exit 3
fi
printf '%s/%s\n' "$TYPE" "$slug"