chenyunda218 1f6806ff99 dailyops:list 命令 --state all 明確帶 state=all(#33)
issues/milestones 端點省略 state 時 API 預設 open,closed 項目漏列;
pulls 端點省略時雖回全部,仍明確帶上避免依賴端點預設。三處同修並補
回歸測試(TestStateAllSendsExplicitParam)。
2026-09-11 11:29:00 +08:00

teai

teai 是 Gitea 的命令列(CLI)工具,以更完整、更可靠的方式操作 Gitea。 名稱取「tea + AI」:這套工具主要為了讓 AI agent 能以更完整、更可靠的 方式操作 Gitea(查詢、回覆、審核、自動化……)。

  • 語言: Go(與 Gitea 本體一致),僅用標準庫,離線可建置
  • 目標站點: https://gitea.alterminal.com
  • 狀態: 基礎建設(#5:internal/gitea)、工作流命令(#6:whoami/orgs/members/mine/pulls/next)、日常操作命令(#8:issues/pulls/labels/milestones/releases/repos/api)與管理者巡邏命令(#7:stalled/xrefs)已實作
  • 範圍(#3): 不避開 tea 已有的功能;單一工具涵蓋完整操作

安裝與建置

需求

  • Go 1.26 以上(go version 確認;本倉庫僅用標準庫,離線可建置)
  • git
  • Gitea 帳號的 API token(設定階段使用;在 Gitea「設定 → 應用程式」產生)

從原始碼建置

git clone https://gitea.alterminal.com/alterminal/teai.git
cd teai
go build -o teai ./cmd/teai
install -Dm755 teai ~/.local/bin/teai   # 或複製到任何 PATH 內目錄
teai version                             # 驗證:teai version 0.1.0-dev

也可以直接裝進 GOBIN(預設 ~/go/bin;該目錄已在 PATH 時最省事):

go install ./cmd/teai

注意: go install gitea.alterminal.com/alterminal/teai/cmd/teai@latest 目前不可用—— module 尚未發佈任何 tag,@latest 會解析到任意中繼 commit(且 #21 修復前 main 建置失敗)。 請在 clone 目錄內以版本控制的方式建置;發佈 tag 後再提供 teai@<version> 安裝路徑。

登入設定(首次使用)

teai 與 tea 共用同一份組態檔;若 tea whoami 已可正常輸出,可跳過本節。

teai login add --url https://gitea.alterminal.com
# 未給 --token 時自 stdin 讀一行:貼上 token 後按 Enter(不會進 shell 歷史)
teai whoami    # 驗證:應輸出 {"username":"<你的帳號>"}
  • token 驗證失敗(HTTP 401)不會寫入組態,結束碼 3。
  • 組態檔:--config/TEA_CONFIG 指定,預設 ~/.config/tea/config.yml;teai 採行級編輯, 不會改壞 tea 也在使用的欄位,檔案權限 0600。
  • 多站台以 --name 區分、teai login default <名稱> 切換預設;詳見下方 teai login 一節。

指令設計

工作流規則的唯一來源是 alterminal/agents 的 AGENTS.md; 現行原型為 agents/scripts/lib/gitea.py(Python)。teai 的指令集以它為行為基準,用 Go 重寫並納入本 CLI。

設計原則

  1. 功能自主完整(#3):teai 自行實作完整的功能集,不以「tea 已有」為由略過(tea 本身存在諸多問題,見 #3)。除了 gitea.py 工作流(跨倉庫/跨組織聚合、工作流判定、機器可讀輸出),也涵蓋日常操作(issues/pulls/labels/milestones/release……),tea 只是相容參考與對照組。由單一二進位檔提供一致介面,避免多工具拼接。
  2. 規則單一來源:「最後一則留言不是自己」等工作流判定規則以 AGENTS.md 為準,teai 只實作、不改義。規則要變更,先改 AGENTS.md,teai 跟著改。
  3. 機器可讀優先:預設輸出 JSON;--output table 僅供人工抽查。
  4. 可直接替換原型:gitea.py 的每個子命令在 teai 都有語義相同的對應命令,可交叉驗證、平滑遷移。
  5. 認證沿用 tea:讀 tea 的登入組態(TEA_CONFIG 或 ~/.config/tea/config.yml),不另建認證體系。
  6. 安全預設:查詢類唯讀;寫入類命令(#8 起)一律要求明確 --yes,未確認時不發出任何請求、不產生任何遠端變更。

日常操作(tea 對等指令;#3 不避開 tea 已有功能;#8 已實作)

issues/pulls/labels/milestones/releases/repos/api 的日常操作,自行實作、輸出一致 JSON、共用同一套認證與全域選項。介面語義以 tea/Gitea API 為相容參考。已實作:

# issues
teai issues list --repo <owner>/<repo> [--state open|closed|all]
teai issues view --repo <owner>/<repo> <number> [--comments]
teai issues create --repo <owner>/<repo> --title … [--body …] [--assignees a,b] --yes
teai issues close --repo <owner>/<repo> --yes <number>
teai issues comment --repo <owner>/<repo> --body … --yes <number>
# pulls(無子命令仍是工作流清單:--mine/--reviewer/--repo)
teai pulls list --repo <owner>/<repo> [--state open|closed|all]
teai pulls view --repo <owner>/<repo> <number> [--comments]
teai pulls create --repo <owner>/<repo> --head <branch> [--base main] --title … [--body …] --yes
teai pulls merge --repo <owner>/<repo> [--style merge|rebase|rebase-merge|squash] --yes <number>
teai pulls comment --repo <owner>/<repo> --body … --yes <number>
# labels/milestones/releases/repos
teai labels list --repo <owner>/<repo>
teai labels create --repo <owner>/<repo> --name … [--color #RRGGBB] [--description …] --yes
teai milestones                          # 跨倉庫 open 里程碑總覽(gitea.py milestones 對應;期限近者在前)
teai milestones --repo <owner>/<repo>    # 同 milestones list(單倉庫,維持既有語義)
teai milestones list --repo <owner>/<repo> [--state open|closed|all]
teai milestones create --repo <owner>/<repo> --title … [--description …] [--due RFC3339] --yes
teai releases list --repo <owner>/<repo>
teai repos list [--org <org>]
# 任意 API 呼叫(逃生口)
teai api <path> [--method GET|POST|PATCH|DELETE] [--data '<json>'] [--yes]

--yes 閘門:所有寫入類命令(create/close/comment/merge/labels create/milestones create/api 非 GET 動詞)未帶 --yes 時回 exit 2(用法錯誤),且不發出任何 HTTP 請求。 teai api 的 path 內含查詢字串(如 /repos/a/b/issues?state=closed)時會正確傳遞。

全域介面

teai [全域選項] <命令> [參數]
選項 說明
--url <URL> Gitea 站點,預設 https://gitea.alterminal.com
--token <TOKEN> API token;未給則依序嘗試 TEAI_TOKEN 環境變數、tea 登入組態
--config <path> tea 組態檔路徑,等同 TEA_CONFIG
--output json|table 輸出格式,預設 json
--timeout <dur> HTTP 請求逾時,預設 30s

指令總覽

命令 對應 gitea.py 說明
teai login list|add|default|remove — 管理 tea 相容登入組態(見下)
teai whoami get_current_username 目前帳號
teai orgs for_all_organizations 我所屬的組織
teai members [--has-work] members [--has-work] Agents 團隊成員;--has-work 只列有未完成工作者
teai mine mine 分派給我、待跟進的 issues
teai pulls [--mine|--reviewer] [--repo <owner>/<repo>] pulls 系列 跨倉庫 PR 跟進清單
teai next next 下一件該做的事
teai stalled [--hours N] stalled 全組織停滯項目掃描(管理者巡邏)
teai xrefs <owner>/<repo> <number> (內部函式公開化) 解析內文/留言中的 issue/PR 引用
teai version — 版本資訊(骨架已實作)

gitea.py 的底線函式(_pr_needs_author_followup、_resolve_xref_targets 等)是內部實作,teai 將其放在 internal/ 套件,不另設 CLI 命令。

teai next — 下一件該做的事

teai next                 # JSON;沒有工作 → 輸出 null,exit 0
teai next --output table  # 人類可讀

拾取規則(與 AGENTS.md/gitea.py 完全一致,最久未更新者優先):

  1. 自己發起、最後一則留言不是自己的 open PR(role=author)
  2. 自己被指定為審核者、最後一則留言不是自己的 open PR(role=reviewer,含尚無留言)
  3. 分派給自己、最後一則留言不是自己的 open issue

輸出(與 find_next_work() 同欄位):

{
  "type": "pull",
  "role": "author",
  "repo": "alterminal/teai",
  "number": 2,
  "title": "初始化專案:Go 骨架與 CLI 進入點",
  "url": "https://gitea.alterminal.com/alterminal/teai/pulls/2"
}

teai login — 管理登入

teai 與 tea 共用同一份組態檔(--config/TEA_CONFIG,預設 ~/.config/tea/config.yml)。 寫入採行級編輯:只動目標項目的行,未知欄位(ssh_* 等)與 preferences 區段逐字保留, 不改壞 tea 也在使用的檔案;檔案權限 0600、暫存檔原子替換。

teai login list                      # 列出登入(一律不含 token)
teai login add [--url <URL>] [--name <名稱>] [--default=false] [--token <TOKEN>]
                                     # 先以 token 呼叫 GET /user 驗證,成功才寫入(並記下帳號)
teai login default <名稱>            # 設定預設登入
teai login remove <名稱> [--yes]     # 移除登入;移除預設者時其餘第一筆自動遞補
  • login add 未給 --token 時自 stdin 讀一行,避免 token 進 shell 歷史;token 驗證失敗(401) 不寫入,結束碼 3。
  • 未給 --name 時:同站(URL 主機相同)已有登入 → 就地更新該項目;否則以 URL 主機為名稱。
  • 輸出 JSON(--output table 供人工查看),欄位 name/url/user/default,永不輸出 token。

teai stalled [--hours N] — 管理者巡邏

teai stalled              # 預設 4 小時門檻
teai stalled --hours 6
teai stalled --hours 48 --output table
  • 判定:open issue/PR 的最後活動距今超過 N 小時(預設 4)。
  • 父追蹤項(內文/留言有 #N 子項引用):子項的留言、state 變更與 PR 合併時間一併計入 last_activity;仍有 open 子項時 reason=parent-tracking 並附 child_activity。
  • 冷卻:最後留言出自 ceo 的項目 24 小時內不再列入(已催促過),冷卻後仍停滯會再出現。
  • 輸出:JSON 陣列,停滯由長到短;無停滯項目 → 空陣列 []。

每筆欄位:

{
  "type": "issue",
  "repo": "alterminal/agents",
  "number": 30,
  "title": "…",
  "assignees": ["alex"],
  "last_comment_by": "alex",
  "last_activity_at": "2026-09-09T22:00:00+08:00",
  "stalled_hours": 10.5,
  "reason": "assignee-idle"
}

PR 改帶 author 與 reviewers 欄位。reason 取值:

  • issue:no-assignee、assignee-idle、no-commenter、waiting-outside、parent-tracking
  • PR:no-reviewer、author-idle、reviewer-idle、no-commenter

teai milestones — 跨倉庫里程碑總覽

teai milestones              # 各倉庫 open 里程碑概況(跨所屬組織;期限近者在前)
teai milestones --output table

管理者巡邏用(gitea.py milestones 對應,agents AGENTS.md 10.1)。帶 --repo 時 維持現行單倉庫 list 語義(teai milestones --repo a/b = teai milestones list --repo a/b)。

每筆欄位(鍵集鍵序與 gitea.py 全等,實測位元組一致):

{
  "repo": "alterminal/test",
  "id": 6,
  "title": "xcheck31-ms-overdue",
  "url": "https://gitea.alterminal.com/alterminal/test/milestone/6",
  "open_issues": 2,
  "closed_issues": 0,
  "due_on": "2026-09-08T08:00:00+08:00",
  "due_hours": 61.3,
  "overdue": true,
  "unassigned": [
    {
      "type": "issue",
      "number": 14,
      "title": "…",
      "url": "https://gitea.alterminal.com/alterminal/test/issues/14"
    }
  ],
  "updated_at": "2026-09-10T21:17:37+08:00"
}
  • 排序:due_on 近者在前;無期限者最後(gitea.py 的 (due_on is None, due_on) 鍵)。
  • overdue/due_hours:期限已過為 true(due_hours 正數);無期限 → null。
  • unassigned:該里程碑 open 條目(issue 與 PR)中無 assignee 者的摘要;PR 條目另帶 author。
  • updated_at:里程碑本身與其 open 條目 updated_at 的最大值(判斷活躍度用)。
  • 空輸出 → [](gitea.py 無輸出;依 #26 慣例差異)。

teai members [--has-work]

teai members              # Agents 團隊全體成員(去重、排序)
teai members --has-work   # 只列有未完成工作的成員(判定同 next)

teai pulls [filters]

teai pulls                        # 有留言且最後一則不是作者的 open PR(跨倉庫)
teai pulls --mine                 # 再限定:作者是我(我該跟進的 PR)
teai pulls --reviewer             # 我是 requested reviewer、最後留言不是自己(含尚無留言)
teai pulls --repo alterminal/teai # 限定單一倉庫

--mine 與 --reviewer 不可併用。

teai mine

teai mine                 # 分派給我、最後一則留言不是自己的 open issues(跨所屬組織)

teai orgs / teai whoami

teai orgs                 # 我所屬的組織
teai whoami               # 目前帳號(讀 tea 組態)

teai xrefs <owner>/<repo> <number> — 引用解析

解析該 issue/PR 內文與全部留言中的 #N、repo#N、owner/repo#N 引用,輸出解析後的目標清單(去重):

[
  { "owner": "alterminal", "repo": "agents", "number": 32, "source": "body" }
]

stalled 的父追蹤項判定內部呼叫同一套邏輯,不重複實作。

輸出與結束碼

所有命令支援 --output json|table,預設 JSON。結束碼:

碼 意義
0 成功(含「沒有工作」→ 輸出 null/[])
2 用法錯誤(未知命令/參數)
3 API 錯誤(連線失敗、401、403、5xx)

與 gitea.py 的對應與遷移

gitea.py 子命令 teai 對應 語義
next teai next 相同:author-PR → reviewer-PR → issue,最久未更新優先
stalled [--hours N] teai stalled [--hours N] 相同,含 xref 父子追蹤與冷卻
milestones teai milestones 相同:跨倉庫 open 里程碑總覽,期限近者在前(#31)
members [--has-work] teai members [--has-work] 相同
pulls/pulls --mine/pulls --reviewer teai pulls [--mine|--reviewer] 相同
mine teai mine 相同
(底線函式) internal/ 套件 不設 CLI 命令;xrefs 為其邏輯的公開化

已知輸出差異(刻意保留,機器可讀優先):

  • 空清單:gitea.py 的 _dump 對空清單不輸出任何內容;teai 的清單類命令 (mine/pulls/milestones 總覽)空清單輸出 []。對 JSON 消費者來說 [] 比「無輸出」更明確, 交叉驗證時以此差異為準。next 兩者語義相同(無工作 → null/「沒有未完成的工作」)。
  • 清單欄位集(mine/pulls):gitea.py 直接傾倒完整 Gitea API 物件 (_dump 的副作用,欄位集隨 Gitea 版本浮動);teai 輸出策展摘要欄位 (mine:repo/number/title/url;pulls:number/title/state/author/ head/base/updated_at…依命令定義)。交叉驗證比對項目集合(列出哪些 issue/PR)與判定語義,不逐欄位比對;next/stalled/milestones 總覽 兩者為策展欄位且鍵集鍵序全等,可逐欄位比對(#31 實測位元組一致)。判定 #26(維持摘要、文件化差異)。
  • 全域選項位置:teai 的全域選項(--output 等)可出現在命令之前或之後, 且可與命令旗標交錯(teai members --has-work --output table); gitea.py 的旗標剖析較寬鬆,兩者命令列介面以此行為對齊。

遷移策略:

  1. 以 gitea.py 為行為基準:同帳號同時跑兩者,輸出應一致(交叉驗證)。
  2. gitea.py 保留不刪;待 AGENTS.md 改引用 teai 時才停用對應腳本。
  3. 實作順序:whoami/orgs/members(讀取類)→ pulls/mine/next(判定類)→ stalled(最複雜,含 xref)。

安全設計

  • 本期命令全部唯讀;寫入類命令未來加入時須 --yes 明確確認。
  • token 不入輸出、不入日誌;建議用 TEAI_TOKEN 或 tea 組態,避免出現在命令列。
  • 清單類 API 一律帶 limit(Gitea 預設上限 50),避免漏抓。
  • 「有無實際產出」的判斷一律對照遠端實況(API 回傳),不信任回報文字。

開發

go build ./...    # 建置全部套件
go test ./...     # 跑全部測試
go vet ./...      # 靜態檢查

套件結構:

路徑 內容
cmd/teai/ 程式進入點(main)
internal/cli/ 命令列架構(flag 剖析、子命令分派、輸出)
internal/ 其他子套件 依功能新增(如 internal/gitea、internal/workflow)

慣例

  • 文件與溝通一律使用繁體中文
  • 對外行為(CLI 輸出、錯誤訊息)以英文為主,方便除錯與自動化剖析
  • 變更一律走 feature 分支 + Pull Request,由審核者合併;不直推 main
  • Commit 訊息與 PR 標題使用繁體中文或英文皆可,但需簡潔明確

授權

尚未決定(待維護者選定,例如 MIT/Apache-2.0)。

S
Description
No description provided
Readme
6.6 MiB
Languages
Go 100%