Files
teai/README.md

258 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# teai
teai 是 Gitea 的命令列(CLI)工具,以更完整、更可靠的方式操作 Gitea。
名稱取「**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)進行中
- **範圍(#3):** 不避開 tea 已有的功能;單一工具涵蓋完整操作
## 安裝與建置
需要 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. **功能自主完整(#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 為相容參考。已實作:
```sh
# 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 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`)時會正確傳遞。
### 全域介面
```sh
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 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` — 下一件該做的事
```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 <owner>/<repo> <number>` — 引用解析
解析該 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)。