# teai teai 是 Gitea 的命令列(CLI)輔助工具,補足官方 `tea` CLI 缺少的功能。 名稱取「**te**a + **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)已實作;stalled/xrefs(#7)進行中 ## 安裝與建置 需要 Go 1.26 以上。 ```sh go build -o teai ./cmd/teai ``` ## 指令設計 > 工作流規則的唯一來源是 [`alterminal/agents`](https://gitea.alterminal.com/alterminal/agents) 的 `AGENTS.md`; > 現行原型為 `agents/scripts/lib/gitea.py`(Python)。teai 的指令集以它為行為基準,用 Go 重寫並納入本 CLI。 ### 設計原則 1. **與 tea 互補,不重複**:`tea` 已有的功能(clone、issues、pulls、labels、milestones、release、api……)一律用 `tea`。teai 只做 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 對等指令;#8) issues/pulls/labels/milestones/releases/repos/api 的日常操作,自行實作、輸出一致 JSON、共用同一套認證與全域選項。介面語義以 `tea`/Gitea API 為相容參考。已實作: ```sh # issues teai issues list --repo / [--state open|closed|all] teai issues view --repo / [--comments] teai issues create --repo / --title … [--body …] [--assignees a,b] --yes teai issues close --repo / --yes teai issues comment --repo / --body … --yes # pulls(無子命令仍是工作流清單:--mine/--reviewer/--repo) teai pulls list --repo / [--state open|closed|all] teai pulls view --repo / [--comments] teai pulls create --repo / --head [--base main] --title … [--body …] --yes teai pulls merge --repo / [--style merge|rebase|rebase-merge|squash] --yes teai pulls comment --repo / --body … --yes # labels/milestones/releases/repos teai labels list --repo / teai labels create --repo / --name … [--color #RRGGBB] [--description …] --yes teai milestones list --repo / [--state open|closed|all] teai milestones create --repo / --title … [--description …] [--due RFC3339] --yes teai releases list --repo / teai repos list [--org ] # 任意 API 呼叫(逃生口) teai api [--method GET|POST|PATCH|DELETE] [--data ''] [--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`)時會正確傳遞。 ### 全域介面 ```sh teai [全域選項] <命令> [參數] ``` | 選項 | 說明 | | --- | --- | | `--url ` | Gitea 站點,預設 `https://gitea.alterminal.com` | | `--token ` | API token;未給則依序嘗試 `TEAI_TOKEN` 環境變數、tea 登入組態 | | `--config ` | tea 組態檔路徑,等同 `TEA_CONFIG` | | `--output json\|table` | 輸出格式,預設 `json` | | `--timeout ` | HTTP 請求逾時,預設 `30s` | ### 指令總覽 | 命令 | 對應 gitea.py | 說明 | | --- | --- | --- | | `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 /]` | `pulls` 系列 | 跨倉庫 PR 跟進清單 | | `teai next` | `next` | 下一件該做的事 | | `teai stalled [--hours N]` | `stalled` | 全組織停滯項目掃描(管理者巡邏) | | `teai xrefs / ` | (內部函式公開化) | 解析內文/留言中的 issue/PR 引用 | | `teai version` | — | 版本資訊(骨架已實作) | > `gitea.py` 的底線函式(`_pr_needs_author_followup`、`_resolve_xref_targets` 等)是內部實作,teai 將其放在 `internal/` 套件,不另設 CLI 命令。 ### `teai next` — 下一件該做的事 ```sh 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()` 同欄位): ```json { "type": "pull", "role": "author", "repo": "alterminal/teai", "number": 2, "title": "初始化專案:Go 骨架與 CLI 進入點", "url": "https://gitea.alterminal.com/alterminal/teai/pulls/2" } ``` ### `teai stalled [--hours N]` — 管理者巡邏 ```sh 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 陣列,停滯由長到短;無停滯項目 → 空陣列 `[]`。 每筆欄位: ```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 members [--has-work]` ```sh teai members # Agents 團隊全體成員(去重、排序) teai members --has-work # 只列有未完成工作的成員(判定同 next) ``` ### `teai pulls [filters]` ```sh teai pulls # 有留言且最後一則不是作者的 open PR(跨倉庫) teai pulls --mine # 再限定:作者是我(我該跟進的 PR) teai pulls --reviewer # 我是 requested reviewer、最後留言不是自己(含尚無留言) teai pulls --repo alterminal/teai # 限定單一倉庫 ``` `--mine` 與 `--reviewer` 不可併用。 ### `teai mine` ```sh teai mine # 分派給我、最後一則留言不是自己的 open issues(跨所屬組織) ``` ### `teai orgs` / `teai whoami` ```sh teai orgs # 我所屬的組織 teai whoami # 目前帳號(讀 tea 組態) ``` ### `teai xrefs / ` — 引用解析 解析該 issue/PR 內文與全部留言中的 `#N`、`repo#N`、`owner/repo#N` 引用,輸出解析後的目標清單(去重): ```json [ { "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 父子追蹤與冷卻 | | `members [--has-work]` | `teai members [--has-work]` | 相同 | | `pulls`/`pulls --mine`/`pulls --reviewer` | `teai pulls [--mine\|--reviewer]` | 相同 | | `mine` | `teai mine` | 相同 | | (底線函式) | `internal/` 套件 | 不設 CLI 命令;`xrefs` 為其邏輯的公開化 | 遷移策略: 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 回傳),不信任回報文字。 ## 開發 ```sh 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)。