Merge pull request 'feat/change-requests/pr-comment-replies' (#28) from feat/change-requests/pr-comment-replies into feat/change-requests/main

Reviewed-on: #28
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #28.
This commit is contained in:
2026-08-28 01:46:43 +00:00
6 changed files with 38 additions and 12 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-gitea",
"version": "0.1.6",
"version": "0.1.7",
"description": "Gitea API 工具、Wiki 讀寫、議題轉換、HTML 匯出與存取庫批次同步",
"skills": "./skills",
"author": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-gitea",
"version": "0.1.6",
"version": "0.1.7",
"description": "Gitea API 工具、Wiki 讀寫、議題轉換、HTML 匯出與存取庫批次同步",
"skills": "./skills"
}
+4 -2
View File
@@ -39,7 +39,9 @@ gitea.sh pr-get <owner>/<repo> <pr-index> # 印出 PR 的標題、base
# 前三行固定 title、base、body 三個標記,第 4 行起是描述原文
gitea.sh pr-edit <owner>/<repo> <pr-index> <title> <body-file>
# 更新 PR 的標題與描述;描述從檔案讀,裝得下多行
gitea.sh pr-comments <owner>/<repo> <pr-index> # 印出所有留言(issue 留言、審查評語、行內留言),依時間排序
gitea.sh pr-comments <owner>/<repo> <pr-index> # 印出所有留言(issue 留言、審查評語、行內留言),第三欄帶 #id,依時間排序
gitea.sh comment-reply <owner>/<repo> <pr-index> <issue|review|inline> <comment-id> <body-file>
# 回覆本輪處理過的 PR 留言;inline 走 review comment reply,其餘補一則 PR 留言
gitea.sh pr-depend <owner>/<repo> <pr-index> <dep-owner>/<dep-repo> <dep-index>
# 把 PR 掛上前置 PR 依賴;依賴未關閉前 Gitea 會阻擋合併
gitea.sh repo-set <owner>/<repo> <description> [website] # 設定 repo 描述與網頁
@@ -109,7 +111,7 @@ Gitea wiki 頁讀寫的統一入口:依頁面類型(QUESTION / PLAN / ANALYZ
### `repo-sync`
存取庫批次同步:列出 owner → 使用者選擇 → 逐 repo(sub agent)呼叫 `tools/repo-sync.sh` clone 或更新;回報 `dirty {分支}` 的存取庫交給 `jsc-git:pr`,base 直接用腳本帶出來的那個分支。
存取庫批次同步:列出 owner → 使用者選擇 → 逐 repo(sub agent)呼叫 `tools/repo-sync.sh` clone 或更新;回報 `dirty {分支}` 的存取庫交給 `jsc-git:pr`,base 直接用腳本帶出來的那個分支;PR 依 `jsc-meta/references/pr-report.md` 集中成表。
### `html-export`
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-gitea",
"version": "0.1.6",
"version": "0.1.7",
"description": "Gitea API 工具、Wiki 讀寫、議題轉換、HTML 匯出與存取庫批次同步",
"skills": "./skills/"
}
+2 -2
View File
@@ -13,5 +13,5 @@ description: Batch-sync all readable repos of a chosen Gitea owner into the work
4. Run `tools/gitea.sh repos {owner}` to list every readable `{repo}` under that owner. Done when the command has printed the full `{owner}/{repo}` list for the chosen owner.
5. Sync each `{repo}` one by one. This step **MUST run as a sub agent** (one sub agent per repo):
1. Run `tools/repo-sync.sh {owner}/{repo}`. The script owns the clone-versus-pull decision and the base-branch precedence, so run no `git clone`, `git checkout` or `git pull` by hand, and derive no branch name yourself. Route on its single line of output: `cloned` or `updated` → this repo is done; `dirty {branch}` → go to substep 2; `failed {reason}` → record that reason and stop this repo. Done when exactly one of those four outcomes is recorded for this repo.
2. `dirty {branch}` → call `jsc-git:pr` with `{branch}` from that same output line as the base, passed through verbatim. It commits, branches, pushes and opens the PR itself, so add none of those steps. Done when `jsc-git:pr` returns the PR URL.
6. Report the sync result for every repo: cloned, updated, PR URL, or the failure reason. Done when every `{repo}` from step 4 carries one of those four results.
2. `dirty {branch}` → call `jsc-git:pr` with `{branch}` from that same output line as the base, passed through verbatim. It commits, branches, pushes and opens the PR itself, so add none of those steps. Done when `jsc-git:pr` returns the PR URL and reports it with the table format in `jsc-meta/references/pr-report.md`.
6. Report the sync result for every repo: cloned, updated, PR table row, or the failure reason. Done when every `{repo}` from step 4 carries one of those four results, and all PR rows share one table when more than one PR exists.
+29 -5
View File
@@ -17,7 +17,9 @@
# gitea.sh pr-status <owner>/<repo> <pr-index> # 印出「{state} {merged} {mergeable}」
# gitea.sh pr-get <owner>/<repo> <pr-index> # 印出 PR 的標題、base 分支與描述,供比對用
# gitea.sh pr-edit <owner>/<repo> <pr-index> <title> <body-file> # 更新 PR 的標題與描述
# gitea.sh pr-comments <owner>/<repo> <pr-index> # 印出所有留言(issue 留言、審查評語、行內留言),依時間排序
# gitea.sh pr-comments <owner>/<repo> <pr-index> # 印出所有留言(issue 留言、審查評語、行內留言),第三欄帶 #id
# gitea.sh comment-reply <owner>/<repo> <pr-index> <issue|review|inline> <comment-id> <body-file>
# 回覆本輪處理過的 PR 留言;inline 走 review comment reply,其餘補一則 PR 留言
# gitea.sh pr-depend <owner>/<repo> <pr-index> <dep-owner>/<dep-repo> <dep-index>
# 把 PR 掛上前置 PR 依賴;依賴未關閉前 Gitea 會阻擋合併
# gitea.sh repo-set <owner>/<repo> <description> [website] # 設定 repo 描述與網頁
@@ -258,15 +260,16 @@ print(json.dumps({"title":sys.argv[1],"body":open(sys.argv[2],encoding="utf-8").
rm -f "$tmp"
echo "OK $or#$idx" ;;
pr-comments)
# 印出 PR 的所有留言,每行「{時間}<TAB>{作者}<TAB>{類型}<TAB>{內容單行化}」。
# 印出 PR 的所有留言,每行「{時間}<TAB>{作者}<TAB>{類型}#{id}<TAB>{內容單行化}」。
# 三個來源都要讀:issue 留言、review 本體的評語、review 內逐行的程式碼留言。
# 只讀 issue 留言會漏掉真正的審查意見,那正是需要修正的部分。
# 類型欄保留在第三欄,僅追加 id;呼叫端用第一欄做 since 比對時不受影響。
or="${1:?owner/repo required}"; idx="${2:?pr index required}"
{ req GET "/repos/$or/issues/$idx/comments?limit=100" | python3 -c '
import json,sys
for c in json.load(sys.stdin):
body=" ".join((c.get("body") or "").split())
if body: print("%s\t%s\t留言\t%s" % (c.get("created_at",""), (c.get("user") or {}).get("login","?"), body))
if body: print("%s\t%s\t留言#%s\t%s" % (c.get("created_at",""), (c.get("user") or {}).get("login","?"), c.get("id","?"), body))
'
reviews=$(req GET "/repos/$or/pulls/$idx/reviews?limit=100")
printf '%s' "$reviews" | python3 -c '
@@ -276,7 +279,7 @@ for r in json.load(sys.stdin):
st=r.get("state","")
# 沒有評語的審查照樣要印:APPROVED 代表可以合併,REQUEST_CHANGES 代表被要求修改,
# 兩者都是呼叫端要據以決策的事實,過濾掉會看不見 PR 真正的狀態。
print("%s\t%s\t審查(%s)\t%s" % (r.get("submitted_at",""), (r.get("user") or {}).get("login","?"), st, body or "(無評語)"))
print("%s\t%s\t審查#%s(%s)\t%s" % (r.get("submitted_at",""), (r.get("user") or {}).get("login","?"), r.get("id","?"), st, body or "(無評語)"))
'
for rid in $(printf '%s' "$reviews" | python3 -c '
import json,sys
@@ -289,10 +292,31 @@ try: d=json.load(sys.stdin)
except Exception: raise SystemExit
for c in d:
body=" ".join((c.get("body") or "").split())
if body: print("%s\t%s\t行內(%s:%s)\t%s" % (c.get("created_at",""), (c.get("user") or {}).get("login","?"), c.get("path",""), c.get("original_position") or c.get("position") or "", body))
if body: print("%s\t%s\t行內#%s(%s:%s)\t%s" % (c.get("created_at",""), (c.get("user") or {}).get("login","?"), c.get("id","?"), c.get("path",""), c.get("original_position") or c.get("position") or "", body))
' || true
done
} | sort ;;
comment-reply)
or="${1:?owner/repo required}"; idx="${2:?pr index required}"
kind="${3:?kind required}"; cid="${4:?comment id required}"; body_file="${5:?body file required}"
[ -f "$body_file" ] || { echo "找不到回覆檔: $body_file" >&2; exit 2; }
case "$kind" in issue|review|inline) ;; *) echo "kind must be issue, review, or inline" >&2; exit 2 ;; esac
tmp=$(mktemp)
python3 -c '
import json,sys
print(json.dumps({"body":open(sys.argv[1],encoding="utf-8").read()}))
' "$body_file" > "$tmp"
case "$kind" in
inline)
path="/repos/$or/pulls/$idx/comments/$cid/replies" ;;
issue|review)
path="/repos/$or/issues/$idx/comments" ;;
esac
if ! out=$(req POST "$path" "$tmp"); then
rm -f "$tmp"; printf '%s\n' "$out" >&2; exit 4
fi
rm -f "$tmp"
printf '%s' "$out" | json_field html_url ;;
pr-depend)
or="${1:?owner/repo required}"; idx="${2:?pr index required}"
dep_or="${3:?dep owner/repo required}"; dep_idx="${4:?dep index required}"