fix: Issue 操作切换到 v1 API,统一使用 project_issues_index 替代数据库 ID #11

Merged
wbtiger merged 2 commits from wbtiger/gitlink-cli:fix/issue-use-v1-index into master 2026-05-12 17:06:29 +08:00
8 changed files with 96 additions and 68 deletions

View File

@ -9,6 +9,11 @@ import (
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
// v1RepoPath returns the v1 API path prefix: /v1/{owner}/{repo}
func v1RepoPath(ctx *common.RuntimeContext) string {
return fmt.Sprintf("/v1/%s/%s", ctx.Owner, ctx.Repo)
}
type existingIssue struct {
Subject string
Description string
@ -34,7 +39,7 @@ func Shortcuts() []*common.Shortcut {
if s := ctx.Arg("state"); s != "" {
q.Set("state", s)
}
env, err := ctx.CallAPIWithQuery("GET", ctx.RepoPath()+"/issues", q)
env, err := ctx.CallAPIWithQuery("GET", v1RepoPath(ctx)+"/issues", q)
if err != nil {
return err
}
@ -61,8 +66,9 @@ func Shortcuts() []*common.Shortcut {
}
body := map[string]interface{}{
"subject": title,
"status_id": 1, // 1 = open (required by v1 API)
"priority_id": 2, // 2 = normal
"done_ratio": 0,
"priority_id": 2,
}
if desc := ctx.Arg("body"); desc != "" {
body["description"] = desc
@ -73,7 +79,7 @@ func Shortcuts() []*common.Shortcut {
if m := ctx.Arg("milestone"); m != "" {
body["fixed_version_id"] = m
}
env, err := ctx.CallAPI("POST", ctx.RepoPath()+"/issues", body)
env, err := ctx.CallAPI("POST", v1RepoPath(ctx)+"/issues", body)
if err != nil {
return err
}
@ -84,17 +90,17 @@ func Shortcuts() []*common.Shortcut {
Name: "view",
Description: "View issue details",
Flags: []common.Flag{
{Name: "id", Short: "i", Usage: "Issue ID or number", Required: true},
{Name: "number", Short: "n", Usage: "Issue number (as shown in the web URL)", Required: true},
},
Run: func(ctx *common.RuntimeContext) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
id, err := ctx.RequireArg("id")
number, err := ctx.RequireArg("number")
if err != nil {
return err
}
env, err := ctx.CallAPI("GET", fmt.Sprintf("%s/issues/%s", ctx.RepoPath(), id), nil)
env, err := ctx.CallAPI("GET", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), nil)
if err != nil {
return err
}
@ -105,17 +111,17 @@ func Shortcuts() []*common.Shortcut {
Name: "close",
Description: "Close an issue",
Flags: []common.Flag{
{Name: "id", Short: "i", Usage: "Issue ID", Required: true},
{Name: "number", Short: "n", Usage: "Issue number (as shown in the web URL)", Required: true},
},
Run: func(ctx *common.RuntimeContext) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
id, err := ctx.RequireArg("id")
number, err := ctx.RequireArg("number")
if err != nil {
return err
}
current, err := fetchExistingIssue(ctx, id)
current, err := fetchExistingIssue(ctx, number)
if err != nil {
return err
}
@ -125,7 +131,7 @@ func Shortcuts() []*common.Shortcut {
"description": current.Description,
"status_id": 5, // 5 = closed
}
env, err := ctx.CallAPI("PUT", fmt.Sprintf("%s/issues/%s", ctx.RepoPath(), id), body)
env, err := ctx.CallAPI("PATCH", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), body)
if err != nil {
return err
}
@ -136,7 +142,7 @@ func Shortcuts() []*common.Shortcut {
Name: "update",
Description: "Update an issue",
Flags: []common.Flag{
{Name: "id", Short: "i", Usage: "Issue ID", Required: true},
{Name: "number", Short: "n", Usage: "Issue number (as shown in the web URL)", Required: true},
{Name: "title", Short: "t", Usage: "New title"},
{Name: "body", Short: "b", Usage: "New description"},
{Name: "state", Short: "s", Usage: "New state: open, closed, or numeric status_id"},
@ -145,7 +151,7 @@ func Shortcuts() []*common.Shortcut {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
id, err := ctx.RequireArg("id")
number, err := ctx.RequireArg("number")
if err != nil {
return err
}
@ -156,7 +162,7 @@ func Shortcuts() []*common.Shortcut {
return fmt.Errorf("at least one of --title, --body, or --state is required")
}
current, err := fetchExistingIssue(ctx, id)
current, err := fetchExistingIssue(ctx, number)
if err != nil {
return err
}
@ -178,7 +184,7 @@ func Shortcuts() []*common.Shortcut {
}
body["status_id"] = statusID
}
env, err := ctx.CallAPI("PUT", fmt.Sprintf("%s/issues/%s", ctx.RepoPath(), id), body)
env, err := ctx.CallAPI("PATCH", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), body)
if err != nil {
return err
}
@ -189,14 +195,14 @@ func Shortcuts() []*common.Shortcut {
Name: "comment",
Description: "Add a comment to an issue",
Flags: []common.Flag{
{Name: "id", Short: "i", Usage: "Issue ID", Required: true},
{Name: "number", Short: "n", Usage: "Issue number (as shown in the web URL)", Required: true},
{Name: "body", Short: "b", Usage: "Comment body", Required: true},
},
Run: func(ctx *common.RuntimeContext) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
id, err := ctx.RequireArg("id")
number, err := ctx.RequireArg("number")
if err != nil {
return err
}
@ -205,9 +211,9 @@ func Shortcuts() []*common.Shortcut {
return err
}
payload := map[string]interface{}{
"content": body,
"notes": body,
}
env, err := ctx.CallAPI("POST", fmt.Sprintf("/issues/%s/journals", id), payload)
env, err := ctx.CallAPI("POST", fmt.Sprintf("%s/issues/%s/journals", v1RepoPath(ctx), number), payload)
if err != nil {
return err
}
@ -217,8 +223,8 @@ func Shortcuts() []*common.Shortcut {
}
}
func fetchExistingIssue(ctx *common.RuntimeContext, id string) (*existingIssue, error) {
getEnv, err := ctx.CallAPI("GET", fmt.Sprintf("%s/issues/%s", ctx.RepoPath(), id), nil)
func fetchExistingIssue(ctx *common.RuntimeContext, number string) (*existingIssue, error) {
getEnv, err := ctx.CallAPI("GET", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), nil)
if err != nil {
return nil, err
}

View File

@ -1,6 +1,6 @@
---
name: gitlink-issue
version: 1.0.0
version: 2.0.0
description: "Issue 管理:创建、查看、更新、关闭 Issue添加评论。当用户需要操作 GitLink Issue 时触发。"
metadata:
requires:
@ -36,36 +36,34 @@ gitlink-cli issue +list --owner Gitlink --repo forgeplus --state open
# 创建 Issue
gitlink-cli issue +create --owner myuser --repo myrepo --title "Bug: 登录失败" --body "复现步骤:..."
# 查看 Issue 详情
gitlink-cli issue +view --owner Gitlink --repo forgeplus --id 123
# 查看 Issue 详情(使用网页可见的 Issue 编号)
gitlink-cli issue +view --owner Gitlink --repo forgeplus --number 4
# 更新 Issue
gitlink-cli issue +update --id 123 --title "新标题" --body "更新描述"
gitlink-cli issue +update --number 4 --title "新标题" --body "更新描述"
# 关闭 Issue
gitlink-cli issue +close --id 123
gitlink-cli issue +close --number 4
# 添加评论
gitlink-cli issue +comment --id 123 --body "已修复,请验证"
gitlink-cli issue +comment --number 4 --body "已修复,请验证"
```
## Raw API 补充
```bash
# 获取 Issue 评论列表
gitlink-cli api GET /issues/:issue_id/journals
# 获取 Issue 评论列表(使用 v1 API按 issue number 查询)
gitlink-cli api GET /v1/:owner/:repo/issues/:number/journals
# 批量更新 Issue
# 批量更新 Issue(仍使用旧版 API需传数据库 ID
gitlink-cli api POST /:owner/:repo/issues/series_update --body '{"ids":[1,2,3],"status_id":"closed"}'
# Issue 认领
gitlink-cli api POST /issues/:issue_id/claims
```
## GitLink Issue 字段映射
| gitlink-cli 参数 | GitLink API 字段 | 说明 |
|------------------|-----------------|------|
| `--number` / `-n` | `project_issues_index` | Issue 编号(网页 URL 中的序号) |
| `--title` | `subject` | Issue 标题 |
| `--body` | `description` | Issue 描述 |
| `--assignee` | `assigned_to_id` | 指派人 ID |
@ -74,7 +72,17 @@ gitlink-cli api POST /issues/:issue_id/claims
## API 注意事项
- **创建 Issue 时必须包含 `done_ratio: 0`**否则数据库报错CLI 已自动处理)
- **Issue 编号(`--number`)是网页 URL 中看到的序号**(如 `issues/4` 中的 `4`),不是数据库内部 ID
- Issue 操作使用 v1 API`/api/v1/`),支持按 Issue 编号查询和操作
- **创建 Issue 时 CLI 会自动设置 `status_id: 1`(新增)和 `priority_id: 2`(正常)**
- **更新/关闭 Issue 时必须保留当前 `subject` 和 `description`**即使只修改状态CLI 会先读取当前 Issue 并自动带回)
- 使用 Raw API 操作 Issue 时需先 `GET issue`,再把当前 `subject`、`description` 与要修改的字段一起提交,避免清空描述
- Issue 评论路径为 `/issues/:id/journals`(不带 owner/repo 前缀)
- v1 API 写操作必须使用 `access_token`(非 `token`认证CLI 已自动处理
## Issue 状态映射status_id
| status_id | 名称 | 说明 |
|-----------|------|------|
| 1 | 新增 | 新建 Issue 的默认状态 |
| 2 | 正在解决 | 处理中 |
| 3 | 已解决 | 已修复 |
| 5 | 关闭 | 关闭(`+close` 命令使用此值) |

View File

@ -7,15 +7,15 @@ Close an issue. Automatically fetches the current issue subject and description,
## 命令
```bash
# Close issue #42
gitlink-cli issue +close -i 42
# Close issue #4
gitlink-cli issue +close -n 4
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--id, -i` | **是** | Issue ID |
| `--number, -n` | **是** | Issue 编号(网页 URL 中的序号) |
| `--owner` | 否 | 仓库所有者(自动从 git remote 解析) |
| `--repo` | 否 | 仓库名称(自动从 git remote 解析) |
| `--format` | 否 | 输出格式: `json`/`table`/`yaml` |
@ -27,18 +27,18 @@ The command performs two API calls:
1. **Fetch** the issue to get the current `subject` and `description`:
```
GET /{owner}/{repo}/issues/{id}
GET /v1/{owner}/{repo}/issues/{number}
```
2. **Update** the issue with `status_id=5`, preserving the current description:
```
PUT /{owner}/{repo}/issues/{id}
PATCH /v1/{owner}/{repo}/issues/{number}
Body: { "subject": <current subject>, "description": <current description>, "status_id": 5 }
```
## Workflow
1. **Confirm** with the user which issue to close (by ID).
2. **Execute** `gitlink-cli issue +close -i {id}`.
1. **Confirm** with the user which issue to close (by number).
2. **Execute** `gitlink-cli issue +close -n {number}`.
3. **Report** that the issue has been closed successfully.
> [!CAUTION]

View File

@ -7,11 +7,11 @@ Add a comment to an existing issue.
## 命令
```bash
# Add a comment to issue #42
gitlink-cli issue +comment -i 42 -b "This has been fixed in commit abc123"
# Add a comment to issue #4
gitlink-cli issue +comment -n 4 -b "This has been fixed in commit abc123"
# Add a multi-line comment
gitlink-cli issue +comment -i 42 -b "Investigation results:
gitlink-cli issue +comment -n 4 -b "Investigation results:
- Root cause: null pointer in auth module
- Fix: add nil check before dereference"
```
@ -20,8 +20,8 @@ gitlink-cli issue +comment -i 42 -b "Investigation results:
| 参数 | 必填 | 说明 |
|------|------|------|
| `--id, -i` | **是** | Issue ID |
| `--body, -b` | **是** | 评论内容(映射为 API 字段 `content` |
| `--number, -n` | **是** | Issue 编号(网页 URL 中的序号) |
| `--body, -b` | **是** | 评论内容(映射为 v1 API 字段 `notes` |
| `--owner` | 否 | 仓库所有者(自动从 git remote 解析) |
| `--repo` | 否 | 仓库名称(自动从 git remote 解析) |
| `--format` | 否 | 输出格式: `json`/`table`/`yaml` |
@ -30,14 +30,14 @@ gitlink-cli issue +comment -i 42 -b "Investigation results:
## API
```
POST /issues/{id}/journals
Body: { "content": body }
POST /v1/{owner}/{repo}/issues/{number}/journals
Body: { "notes": body }
```
## Workflow
1. **Confirm** the comment content with the user before posting.
2. **Execute** `gitlink-cli issue +comment -i {id} -b "..."`.
2. **Execute** `gitlink-cli issue +comment -n {number} -b "..."`.
3. **Report** that the comment was added successfully.
> [!CAUTION]

View File

@ -2,7 +2,7 @@
> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
Create a new issue in the current repository. The `done_ratio` field is automatically set to `0`.
Create a new issue in the current repository. Uses the v1 API which requires `status_id` and `priority_id` — the CLI automatically sets defaults (`status_id: 1` = open, `priority_id: 2` = normal).
## 命令
@ -34,15 +34,15 @@ gitlink-cli issue +create -t "Fix CI pipeline" -b "Flaky tests" -a user123 -m 5
## API
```
POST /{owner}/{repo}/issues
Body: { "subject": title, "done_ratio": 0, "description": body, ... }
POST /v1/{owner}/{repo}/issues
Body: { "subject": title, "status_id": 1, "priority_id": 2, "done_ratio": 0, "description": body, ... }
```
## Workflow
1. **Confirm** the issue title (and optional body) with the user before creating.
2. **Execute** `gitlink-cli issue +create -t "..." -b "..."`.
3. **Report** the created issue ID and URL to the user.
3. **Report** the created issue number and URL to the user.
> [!CAUTION]
> This is a **Write Operation** -- confirm user intent before executing.

View File

@ -2,7 +2,7 @@
> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
List issues for the current repository, with optional state filter and pagination.
List issues for the current repository, with optional state filter and pagination. Uses the v1 API which returns `project_issues_index` (the issue number visible in the web URL).
## 命令
@ -35,9 +35,23 @@ gitlink-cli issue +list --format json
## API
```
GET /{owner}/{repo}/issues?state={state}&page={page}&limit={limit}
GET /v1/{owner}/{repo}/issues?state={state}&page={page}&limit={limit}
```
## 返回字段v1
每个 Issue 包含以下关键字段:
| 字段 | 说明 |
|------|------|
| `project_issues_index` | Issue 编号(网页 URL 中的序号) |
| `id` | 数据库内部 ID |
| `subject` | 标题 |
| `status_name` / `status_id` | 状态名 / 状态 ID |
| `author.login` | 作者 |
| `assigners` | 负责人列表 |
| `created_at` / `updated_at` | 创建 / 更新时间 |
## References
- [gitlink-issue](../SKILL.md)

View File

@ -10,20 +10,20 @@ The shortcut reads the current issue first and preserves existing `subject` and
```bash
# Update issue title
gitlink-cli issue +update -i 42 -t "New title"
gitlink-cli issue +update -n 4 -t "New title"
# Update issue description
gitlink-cli issue +update -i 42 -b "Updated description"
gitlink-cli issue +update -n 4 -b "Updated description"
# Update both title and state
gitlink-cli issue +update -i 42 -t "Revised title" -s closed
gitlink-cli issue +update -n 4 -t "Revised title" -s closed
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--id, -i` | **是** | Issue ID |
| `--number, -n` | **是** | Issue 编号(网页 URL 中的序号) |
| `--title, -t` | 否 | 新标题(映射为 API 字段 `subject` |
| `--body, -b` | 否 | 新描述(映射为 API 字段 `description` |
| `--state, -s` | 否 | 新状态: `open`、`closed`,或数字状态 ID映射为 API 字段 `status_id`open=1closed=5 |
@ -35,14 +35,14 @@ gitlink-cli issue +update -i 42 -t "Revised title" -s closed
## API
```
PUT /{owner}/{repo}/issues/{id}
PATCH /v1/{owner}/{repo}/issues/{number}
Body: { "subject": current_or_new_title, "description": current_or_new_body, "status_id": state }
```
## Workflow
1. **Confirm** the fields to update and their new values with the user.
2. **Execute** `gitlink-cli issue +update -i {id} -t "..." -b "..."`.
2. **Execute** `gitlink-cli issue +update -n {number} -t "..." -b "..."`.
3. **Report** the updated issue details to the user.
When using Raw API instead of the shortcut, fetch the issue first and include the current `subject` and `description` in the update payload.

View File

@ -2,23 +2,23 @@
> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
View details of a specific issue by its ID.
View details of a specific issue by its number (as shown in the web URL).
## 命令
```bash
# View issue #42
gitlink-cli issue +view -i 42
# View issue #4
gitlink-cli issue +view -n 4
# View issue with JSON output
gitlink-cli issue +view -i 42 --format json
gitlink-cli issue +view -n 4 --format json
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--id, -i` | **是** | Issue ID 或编号 |
| `--number, -n` | **是** | Issue 编号(网页 URL 中的序号,如 `issues/4` 中的 `4` |
| `--owner` | 否 | 仓库所有者(自动从 git remote 解析) |
| `--repo` | 否 | 仓库名称(自动从 git remote 解析) |
| `--format` | 否 | 输出格式: `json`/`table`/`yaml` |
@ -27,7 +27,7 @@ gitlink-cli issue +view -i 42 --format json
## API
```
GET /{owner}/{repo}/issues/{id}
GET /v1/{owner}/{repo}/issues/{number}
```
## References