From 9017e4cbf5673ebce7d74f2ef7922ada6be76f59 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 31 Aug 2026 14:37:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(pr-title-language):=20PR=20=E6=A8=99?= =?UTF-8?q?=E9=A1=8C=E6=94=B9=E7=94=A8=E7=B9=81=E9=AB=94=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E6=91=98=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 修改代理執行。 --- README.md | 2 +- references/behaviors.md | 6 +++--- skills/pr/SKILL.md | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3daa05e..e0bb243 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 ### `pr` -呼叫方傳入的基底先驗合法性,再認可所有變更,然後依階梯命名目標分支、push、以範本描述建立 Gitea PR。基底分支由 `base-branch.sh --derive` 從分支名推出上一階;呼叫方傳入的基底與推導結果不同,就當成越級擋下並說明正確階梯,不會悄悄改目標。分支名只允許 ASCII:類型交給 `pick-type.sh` 選,功能與標題先翻譯成英文短語再 slug 化。分支命名完成就同時啟動 PR 查詢與描述草擬,不等 push。整條呼叫鏈只查一次 `gitea.sh pr-of-branch`;分支已有開啟中的 PR 時不重開,改用該次查詢帶回的標題、base 與描述比對三項,只有不一樣的那幾項才送出 API 呼叫。收尾回報使用 `jsc-meta/references/pr-report.md` 的 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 資訊表格。 diff --git a/references/behaviors.md b/references/behaviors.md index 9509a27..a4ba0ca 100644 --- a/references/behaviors.md +++ b/references/behaviors.md @@ -17,7 +17,7 @@ | 項目 | 內容 | | --- | --- | | 觸發時機 | 工作做完要送審時叫用。既有 PR 在新 commit 之後要重新同步時也叫用。只想提交不想推送時不叫用這一支,改叫 `jsc-git:commit`。 | -| 關鍵步驟 | 先用 `tools/base-branch.sh {呼叫方基底}` 驗證呼叫方傳進來的基底、叫 `jsc-git:commit` 提交全部變更、用 `tools/pick-type.sh` 從 commit 標題選出型別、用 `tools/slugify.sh` 組出階梯狀目標分支名、用 `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 意見、最後回報。 | +| 關鍵步驟 | 先用 `tools/base-branch.sh {呼叫方基底}` 驗證呼叫方傳進來的基底、叫 `jsc-git:commit` 提交全部變更、用 `tools/pick-type.sh` 從 commit 標題選出型別、用 `tools/slugify.sh` 組出階梯狀目標分支名、用 `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 意見、最後回報。PR 標題寫一句繁體中文摘要,說明這條 PR 做了什麼,不套用分支名;校準既有 PR 時只問標題還描述不描述得了目前的內容,不拿分支名比對。 | | 外部呼叫 | `tools/base-branch.sh`、`tools/pick-type.sh`、`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 摘要四欄,並指名基底分支、目標分支、更新過的校準項目、自動建立的功能主幹、沒回覆到的意見。 | -| 可驗證跡象 | origin 上多出目標分支的 ref,`git ls-remote --heads origin` 查得到。Gitea 上多出一條 PR,或既有 PR 的標題與描述被 `pr-edit` 改過、依賴被 `pr-depend` 掛上。PR 描述照 `templates/pr-description.md` 生成,各節不留空。功能主幹不在 origin 上時,`tools/base-branch.sh --derive` 會自動從 develop 建出 `{類型}/{功能}/main` 並推上 origin。本地 git 歷史含 `jsc-git:commit` 建立的 commit,目前分支切到目標分支。PR 意見的回覆留在 Gitea 那幾則意見底下。描述檔草稿寫在暫存檔。這一支不寫 wiki 頁,只讀 PLAN 頁與 ANALYZE 頁。 | +| 完成條件 | 握有一個 PR 網址,送出的標題是一句繁體中文摘要,不是分支名。前置依賴已經處理完:`pr-depend` 印出 `OK` 行,或 PR 標題冠上 `WIP:` 且描述指名前置 PR,或描述寫「無」前置 PR 並在回報裡說明。既有 PR 的三個校準項目各自回報為相符或已更新,標題那一項說明拿什麼內容去判定,送出的 API 呼叫數等於不同的項目數。每一則已處理的意見握有回覆連結或記下失敗理由。回報含 `{owner}/{repo}`、PR 編號、PR 網址、PR 摘要四欄,並指名基底分支、目標分支、更新過的校準項目、自動建立的功能主幹、沒回覆到的意見。 | +| 可驗證跡象 | origin 上多出目標分支的 ref,`git ls-remote --heads origin` 查得到。Gitea 上多出一條 PR,標題是一句繁體中文摘要、分支名仍是 ASCII,兩者不一樣。既有 PR 的標題與描述只在內容變了才被 `pr-edit` 改過,依賴被 `pr-depend` 掛上。PR 描述照 `templates/pr-description.md` 生成,各節不留空。功能主幹不在 origin 上時,`tools/base-branch.sh --derive` 會自動從 develop 建出 `{類型}/{功能}/main` 並推上 origin。本地 git 歷史含 `jsc-git:commit` 建立的 commit,目前分支切到目標分支。PR 意見的回覆留在 Gitea 那幾則意見底下。描述檔草稿寫在暫存檔。這一支不寫 wiki 頁,只讀 PLAN 頁與 ANALYZE 頁。 | diff --git a/skills/pr/SKILL.md b/skills/pr/SKILL.md index 4438226..4ac82ae 100644 --- a/skills/pr/SKILL.md +++ b/skills/pr/SKILL.md @@ -21,7 +21,7 @@ The ladder rules live in one place only: section 「PR 分支階梯」 of `jsc-m 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 `tools/pick-type.sh` (`git log --format=%s {range} | 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. Done when one title line covers every commit in the range. + 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 `tools/slugify.sh`. `fix` takes one call: `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: `tools/slugify.sh feat {feature phrase}` → `feat/order-export`, then `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: @@ -42,7 +42,7 @@ The ladder rules live in one place only: section 「PR 分支階梯」 of `jsc-m - 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 = branch name. + - 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 `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. @@ -58,15 +58,15 @@ The ladder rules live in one place only: section 「PR 分支階梯」 of `jsc-m - 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 **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. + 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 the target branch name. + 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, and the number of API calls made equals the number of items that differed. + - 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: @@ -83,4 +83,4 @@ The ladder rules live in one place only: section 「PR 分支階梯」 of `jsc-m 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 `tools/slugify.sh` first; `tools/base-branch.sh --derive` rejects anything else. +4. Branch names are ASCII only (`a-z0-9`, `/`, `-`). Traditional Chinese phrases go through `tools/slugify.sh` first; `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. -- 2.53.0 From 5d6b73a5cc7525ce551b9e5bec63c5a88f05f63c Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 31 Aug 2026 14:37:20 +0800 Subject: [PATCH 2/2] =?UTF-8?q?chore(version):=20=E5=8D=87=E7=89=88=20jsc-?= =?UTF-8?q?git=20=E8=87=B3=200.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 修改代理執行。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index aeb5080..b5c0a8f 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-git", - "version": "0.1.2", + "version": "0.1.3", "description": "Commit 分組認可與 Push Request 建立", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index e7377b5..1406ba1 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-git", - "version": "0.1.2", + "version": "0.1.3", "description": "Commit 分組認可與 Push Request 建立", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index 4b01e46..ff95642 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-git", - "version": "0.1.2", + "version": "0.1.3", "description": "Commit 分組認可與 Push Request 建立", "skills": "./skills/", "jsc": { -- 2.53.0