feat(org): add organization team shortcuts
This commit is contained in:
parent
71ca2bb683
commit
906bdb9d6d
18
README.md
18
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ./...
|
||||
```
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": "创建拉取请求",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"}'
|
||||
|
||||
# 移除成员
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
Loading…
Reference in New Issue