feat(org): add organization team shortcuts

This commit is contained in:
NeeNe 2026-07-03 10:17:42 +08:00
parent 71ca2bb683
commit 906bdb9d6d
10 changed files with 246 additions and 9 deletions

View File

@ -110,7 +110,7 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
| 👥 Member | List, add, remove repository members, change roles, create and accept invite links |
| 🌿 Branch | Create, delete, list, protect, unprotect branches |
| 🏷️ Release | Create, edit, update, view, delete releases |
| 🏢 Org | Manage organizations, members, teams |
| 🏢 Org | Manage organizations, list members, and inspect teams |
| 🔧 CI | View builds, logs, CI/CD operations |
| ⚙️ Pipeline | Run, inspect, enable, disable, delete pipeline workflows and logs |
| 🔔 Webhook | Manage repo webhooks and test deliveries |
@ -318,6 +318,22 @@ gitlink-cli member +role --owner Gitlink --repo forgeplus --user-id 101 --role D
gitlink-cli member +invite-link --owner Gitlink --repo forgeplus --role developer --apply true
```
### Organization Management
```bash
# List organizations
gitlink-cli org +list
# View organization details
gitlink-cli org +info --id Gitlink
# List organization members
gitlink-cli org +members --id Gitlink --page 1 --limit 20
# List organization teams
gitlink-cli org +teams --id Gitlink --page 1 --limit 20
```
### Issue Management
```bash

View File

@ -110,7 +110,7 @@
| 👥 成员 | 列出、添加、移除仓库成员,调整角色,生成和接受邀请链接 |
| 🌿 分支 | 创建、删除、保护分支 |
| 🏷️ 发布 | 创建、编辑、更新、查看、删除 Release |
| 🏢 组织 | 管理组织、成员、团队 |
| 🏢 组织 | 管理组织、列出成员、查看团队 |
| 🔧 CI | 查看构建、日志、CI/CD 操作 |
| ⚙️ Pipeline | 运行、查看、启停、删除流水线工作流并查询日志 |
| 📖 Wiki | 列出、查看、创建、更新、删除 Wiki 页面 |
@ -329,6 +329,22 @@ gitlink-cli member +role --owner Gitlink --repo forgeplus --user-id 101 --role D
gitlink-cli member +invite-link --owner Gitlink --repo forgeplus --role developer --apply true
```
### 组织管理
```bash
# 列出组织
gitlink-cli org +list
# 查看组织详情
gitlink-cli org +info --id Gitlink
# 列出组织成员
gitlink-cli org +members --id Gitlink --page 1 --limit 20
# 列出组织团队
gitlink-cli org +teams --id Gitlink --page 1 --limit 20
```
### Issue 管理
```bash

View File

@ -0,0 +1,33 @@
# Organization Team Shortcuts
## Summary
Adds a read-only organization team shortcut so users and agents can inspect teams without dropping down to Raw API calls.
## Commands
```bash
gitlink-cli org +teams --id Gitlink
gitlink-cli org +teams --id Gitlink --page 1 --limit 50
gitlink-cli org +teams --id Gitlink --format json
```
## Behavior
- Calls `GET /organizations/{id}/teams`.
- Supports `--page` and `--limit`, matching `org +list` and `org +members` pagination behavior.
- Requires `--id` to avoid accidental ambiguous organization lookup.
- Leaves team creation and destructive team management in Raw API because only the read-only list endpoint is currently documented by the GitLink org Skill.
## Documentation
- Updates the `gitlink-org` Skill index to include `org +teams`.
- Adds a dedicated `gitlink-org-teams` reference page for Agent usage.
- Updates README feature wording to state that organization teams can be inspected through shortcuts.
## Verification
```bash
go test ./shortcuts/org ./shortcuts
go test ./...
```

View File

@ -52,6 +52,7 @@
"cmd.org.list.short": "List organizations",
"cmd.org.members.short": "List organization members",
"cmd.org.short": "Organization operations",
"cmd.org.teams.short": "List organization teams",
"cmd.pr.close.short": "Close a pull request",
"cmd.pr.comment.short": "Add a comment to a pull request",
"cmd.pr.create.short": "Create a pull request",

View File

@ -52,6 +52,7 @@
"cmd.org.list.short": "列出组织",
"cmd.org.members.short": "列出组织成员",
"cmd.org.short": "组织操作",
"cmd.org.teams.short": "列出组织团队",
"cmd.pr.close.short": "关闭拉取请求",
"cmd.pr.comment.short": "给拉取请求添加评论",
"cmd.pr.create.short": "创建拉取请求",

View File

@ -36,7 +36,10 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
{Name: "id", Short: "i", Usage: tr.T("flag.org.id_or_login"), Required: true},
},
Run: func(ctx *common.RuntimeContext) error {
id, _ := ctx.RequireArg("id")
id, err := ctx.RequireArg("id")
if err != nil {
return err
}
env, err := ctx.CallAPI("GET", fmt.Sprintf("/organizations/%s", id), nil)
if err != nil {
return err
@ -53,7 +56,10 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
{Name: "limit", Short: "l", Usage: tr.T("flag.limit"), Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
id, _ := ctx.RequireArg("id")
id, err := ctx.RequireArg("id")
if err != nil {
return err
}
q := url.Values{}
q.Set("page", ctx.Arg("page"))
q.Set("limit", ctx.Arg("limit"))
@ -64,6 +70,29 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
return ctx.Output(env)
},
},
{
Name: "teams",
Description: tr.T("cmd.org.teams.short"),
Flags: []common.Flag{
{Name: "id", Short: "i", Usage: tr.T("flag.org.id"), Required: true},
{Name: "page", Short: "p", Usage: tr.T("flag.page"), Default: "1"},
{Name: "limit", Short: "l", Usage: tr.T("flag.limit"), Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
id, err := ctx.RequireArg("id")
if err != nil {
return err
}
q := url.Values{}
q.Set("page", ctx.Arg("page"))
q.Set("limit", ctx.Arg("limit"))
env, err := ctx.CallAPIWithQuery("GET", fmt.Sprintf("/organizations/%s/teams", id), q)
if err != nil {
return err
}
return ctx.Output(env)
},
},
{
Name: "create",
Description: tr.T("cmd.org.create.short"),
@ -72,7 +101,10 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
{Name: "description", Short: "d", Usage: tr.T("flag.description")},
},
Run: func(ctx *common.RuntimeContext) error {
name, _ := ctx.RequireArg("name")
name, err := ctx.RequireArg("name")
if err != nil {
return err
}
payload := map[string]interface{}{
"name": name,
}

View File

@ -76,6 +76,18 @@ func TestOrgInfo(t *testing.T) {
}
}
func TestOrgInfoRequiresID(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected API request: %s %s", r.Method, r.URL.String())
}))
defer server.Close()
err := runShortcut(t, server, "info", map[string]string{})
if err == nil {
t.Fatal("expected error for missing organization id")
}
}
// --- members ---
func TestOrgMembers(t *testing.T) {
@ -95,6 +107,55 @@ func TestOrgMembers(t *testing.T) {
}
}
func TestOrgMembersRequiresID(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected API request: %s %s", r.Method, r.URL.String())
}))
defer server.Close()
err := runShortcut(t, server, "members", map[string]string{"page": "1", "limit": "20"})
if err == nil {
t.Fatal("expected error for missing organization id")
}
}
// --- teams ---
func TestOrgTeams(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/organizations/myorg/teams.json" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
if got := r.URL.Query().Get("page"); got != "2" {
t.Fatalf("page query = %q, want 2", got)
}
if got := r.URL.Query().Get("limit"); got != "50" {
t.Fatalf("limit query = %q, want 50", got)
}
writeJSON(w, []interface{}{
map[string]interface{}{"id": 1, "name": "maintainers"},
})
}))
defer server.Close()
err := runShortcut(t, server, "teams", map[string]string{"id": "myorg", "page": "2", "limit": "50"})
if err != nil {
t.Fatalf("teams failed: %v", err)
}
}
func TestOrgTeamsRequiresID(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected API request: %s %s", r.Method, r.URL.String())
}))
defer server.Close()
err := runShortcut(t, server, "teams", map[string]string{"page": "1", "limit": "20"})
if err == nil {
t.Fatal("expected error for missing organization id")
}
}
// --- create ---
func TestOrgCreate(t *testing.T) {
@ -127,6 +188,18 @@ func TestOrgCreateNoDescription(t *testing.T) {
}
}
func TestOrgCreateRequiresName(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected API request: %s %s", r.Method, r.URL.String())
}))
defer server.Close()
err := runShortcut(t, server, "create", map[string]string{})
if err == nil {
t.Fatal("expected error for missing organization name")
}
}
// --- HTTP error paths ---
func TestOrgListHTTPError(t *testing.T) {
@ -168,6 +241,19 @@ func TestOrgMembersHTTPError(t *testing.T) {
}
}
func TestOrgTeamsHTTPError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("server error"))
}))
defer server.Close()
err := runShortcut(t, server, "teams", map[string]string{"id": "myorg", "page": "1", "limit": "20"})
if err == nil {
t.Fatal("expected error for HTTP 500")
}
}
func TestOrgCreateHTTPError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)

View File

@ -140,7 +140,7 @@ skills/
|-------|------|----------|
| **gitlink-search** | 搜索功能 | `search +repos`, `search +users` |
| **gitlink-user** | 用户管理 | `user +me`, `user +info` |
| **gitlink-org** | 组织管理 | `org +list`, `org +info`, `org +members` |
| **gitlink-org** | 组织管理 | `org +list`, `org +info`, `org +members`, `org +teams` |
| **gitlink-ci** | CI/CD | `ci +builds`, `ci +logs` |
| **gitlink-pipeline** | 流水线工作流 | `pipeline +runs`, `pipeline +run`, `pipeline +logs` |
| **gitlink-wiki** | Wiki 页面管理 | `wiki +list`, `wiki +view`, `wiki +create`, `wiki +update`, `wiki +delete` |
@ -216,6 +216,7 @@ gitlink-cli search +users -k "zhangsan"
# 查看组织
gitlink-cli org +list
gitlink-cli org +info -i Gitlink
gitlink-cli org +teams -i Gitlink
```
详见: [gitlink-search/SKILL.md](gitlink-search/SKILL.md)

View File

@ -1,7 +1,7 @@
---
name: gitlink-org
version: 1.0.0
description: "组织管理:查看组织列表、详情、成员,创建组织。当用户需要操作 GitLink 组织时触发。"
description: "组织管理:查看组织列表、详情、成员、团队,创建组织。当用户需要操作 GitLink 组织时触发。"
metadata:
requires:
bins: ["gitlink-cli"]
@ -23,6 +23,7 @@ metadata:
| `org +list` | 组织列表 |
| `org +info` | 组织详情 |
| `org +members` | 成员列表 |
| `org +teams` | 团队列表 |
| `org +create` | 创建组织 |
## 使用示例
@ -31,14 +32,14 @@ metadata:
gitlink-cli org +list
gitlink-cli org +info --id Gitlink
gitlink-cli org +members --id Gitlink
gitlink-cli org +teams --id Gitlink --page 1 --limit 20
gitlink-cli org +create --name my-org --description "我的组织"
```
## Raw API 补充
```bash
# 组织团队管理
gitlink-cli api GET /organizations/:id/teams
# 创建组织团队
gitlink-cli api POST /organizations/:id/teams --body '{"name":"dev-team"}'
# 移除成员

View File

@ -0,0 +1,50 @@
# org +teams
> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
列出指定组织下的团队。
## 命令
```bash
# 列出组织团队
gitlink-cli org +teams --id Gitlink
# 分页
gitlink-cli org +teams --id Gitlink --page 1 --limit 50
# JSON 格式输出,便于 Agent 解析
gitlink-cli org +teams --id Gitlink --format json
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--id` / `-i` | 是 | 组织标识login name 或 ID |
| `--page` / `-p` | 否 | 页码(默认 1 |
| `--limit` / `-l` | 否 | 每页数量(默认 20 |
| `--format` | 否 | 输出格式json / table / yaml |
## 输出字段
返回组织团队列表,字段取决于 GitLink API 返回结构,常见字段包括:
| 字段 | 说明 |
|------|------|
| `id` | 团队 ID |
| `name` | 团队名称 |
| `description` | 团队描述 |
| `members_count` | 团队成员数量 |
| `projects_count` | 团队项目数量 |
## 注意事项
- 该命令是只读操作,不会修改组织团队配置。
- 创建团队、删除团队或批量调整团队项目仍需使用 Raw API执行写入/删除前必须确认用户意图。
- 如果需要查看团队成员,请先确认 GitLink API 是否公开对应团队成员端点,再使用 Raw API。
## References
- [gitlink-org](../SKILL.md)
- [gitlink-shared](../../gitlink-shared/SKILL.md)