Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

818 changed files with 6232 additions and 90277 deletions

View File

@ -1,41 +0,0 @@
version: 2
name: gitlink_cli_ci
description: "gitlink-cli 代码提交时自动执行 CI 检查(构建、测试、格式化)"
trigger:
webhook: gitlink@1.0.0
event:
- ref: push
ruleset-operator: AND
global:
concurrent: 1
workflow:
- ref: start
name: 开始
task: start
- ref: git_clone_0
name: 拉取代码
task: git_clone@1.2.9
input:
remote_url: '"https://gitlink.org.cn/jiangtx/gitlink-cli.git"'
ref: '"refs/heads/wyx_branch"'
commit_id: '""'
depth: 1
needs:
- start
- ref: ssh_cmd_0
name: CI 检查
task: ssh_cmd@1.1.1
input:
ssh_pass: ((gitlink_cli_ci.ssh_pass))
ssh_ip: '"121.41.212.97"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_cmd: >-
"cd /root && rm -rf gitlink-cli && git clone --depth=1 -b wyx_branch https://gitlink.org.cn/jiangtx/gitlink-cli.git && cd gitlink-cli && export PATH=$PATH:/usr/local/go/bin && export GOPROXY=https://goproxy.cn,direct && go version && go build ./... && go vet ./... && go test -race ./... && output=$(gofmt -s -l .) && if [ -n \"$output\" ]; then echo '格式化检查失败:' && echo \"$output\" && exit 1; fi && echo '所有 CI 检查通过'"
needs:
- git_clone_0
- ref: end
name: 结束
task: end
needs:
- ssh_cmd_0

View File

@ -1,42 +0,0 @@
version: 2
name: 自动部署
description: ""
global:
concurrent: 1
trigger:
webhook: gitlink@1.0.0
event:
- ref: push
ruleset-operator: AND
workflow:
- ref: start
name: 开始
task: start
- ref: git_clone_0
name: git clone
task: git_clone@1.2.9
input:
username: ((gitlink_cli.ylly_git_user))
password: ((gitlink_cli.ylly_git_pass))
remote_url: '"https://gitlink.org.cn/ylly/gitlink-cli.git"'
ref: '"refs/heads/master"'
commit_id: '""'
depth: 1
needs:
- start
- ref: ssh_cmd_0
name: ssh执行命令
task: ssh_cmd@1.1.1
input:
ssh_ip: '"8.136.61.14"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_private_key: ((gitlink_cli.ecs_ssh_key))
ssh_cmd: '"cd /opt/gitlink-cli && git pull && go build -o /usr/local/bin/gitlink-cli . && gitlink-cli version"'
needs:
- git_clone_0
- ref: end
name: 结束
task: end
needs:
- ssh_cmd_0

2
.gitattributes vendored
View File

@ -1,2 +0,0 @@
.gitattributes text eol=lf
internal/i18n/locales/*.json text eol=lf

View File

@ -15,16 +15,11 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.26.1' go-version: '1.22'
- name: Build - name: Build
run: go build ./... run: go build ./...
- name: Validate i18n and skill metadata
run: |
go run ./internal/i18n/cmd/check
go run ./internal/skillmeta/cmd/check
- name: Lint - name: Lint
run: make lint run: make lint

View File

@ -16,7 +16,7 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version: '1.22'
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:

View File

@ -26,14 +26,5 @@ jobs:
- name: Scan i18n key references - name: Scan i18n key references
run: go run ./internal/i18n/cmd/check --scan-code run: go run ./internal/i18n/cmd/check --scan-code
- name: Test Feishu shortcuts
run: go test ./shortcuts/feishu
- name: Test workflow shortcuts
run: go test ./shortcuts/workflow
- name: Run Go tests - name: Run Go tests
run: go test ./... run: go test ./...
- name: Vet
run: go vet ./...

16
.gitignore vendored
View File

@ -1,9 +1,7 @@
docs/
gitlink-cli.exe doc/
/gitlink-cli # Python & demo artifacts
.local/* __pycache__/
!.local/ *.pyc
!.local/feishu-gitlink.env.example.ps1 demo/bin/
*.local.ps1 data/
*.secret.*
reports/feishu-real-smoke-terminal.log

View File

@ -1,31 +0,0 @@
# Stable Feishu webhook
$env:FEISHU_WEBHOOK_URL=""
$env:FEISHU_WEBHOOK_SECRET=""
# Feishu Open Platform
$env:FEISHU_APP_ID=""
$env:FEISHU_APP_SECRET=""
# Feishu DocX / Wiki
$env:FEISHU_WIKI_URL=""
$env:FEISHU_WIKI_NODE_TOKEN=""
$env:FEISHU_FOLDER_TOKEN=""
$env:FEISHU_DOCUMENT_ID=""
# Feishu Base / Bitable
$env:FEISHU_BASE_APP_TOKEN=""
$env:FEISHU_REPORT_TABLE_ID=""
$env:FEISHU_ISSUE_TABLE_ID=""
$env:FEISHU_PR_TABLE_ID=""
$env:FEISHU_CONTRIBUTOR_TABLE_ID=""
$env:FEISHU_TASK_TABLE_ID=""
# Feishu Task
$env:FEISHU_TASK_PROJECT_ID=""
$env:FEISHU_TASK_SECTION_ID=""
# GitLink real test input
$env:GITLINK_OWNER=""
$env:GITLINK_REPO=""
$env:GITLINK_TEST_PR_IDS=""
$env:GITLINK_TOKEN=""

139
README.md
View File

@ -5,7 +5,7 @@
[![Go Version](https://img.shields.io/badge/Go-1.26%2B-blue.svg)](https://golang.org) [![Go Version](https://img.shields.io/badge/Go-1.26%2B-blue.svg)](https://golang.org)
[![npm version](https://img.shields.io/npm/v/@gitlink-ai/cli.svg)](https://www.npmjs.com/package/@gitlink-ai/cli) [![npm version](https://img.shields.io/npm/v/@gitlink-ai/cli.svg)](https://www.npmjs.com/package/@gitlink-ai/cli)
The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans and AI Agents. Supports **macOS, Linux, and Windows**. Covers repository management, wiki pages, issue tracking, pull requests, webhooks, member collaboration, CI/CD, and AI-powered workflows, with 40+ commands and AI Agent [Skills](./skills/). The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans and AI Agents. Supports **macOS, Linux, and Windows**. Covers repository management, issue tracking, pull requests, webhooks, member collaboration, CI/CD, and AI-powered workflows, with 40+ commands and AI Agent [Skills](./skills/README.md).
**[中文文档](./README.zh-CN.md)** **[中文文档](./README.zh-CN.md)**
@ -78,12 +78,20 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
<a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a> <a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a>
<br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub> <br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub>
</div> </div>
<div align="center">
<a href="https://www.gitlink.org.cn/luwanzhou" title="luwanzhou"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/L/165_135_246/120.png" width="40" height="40" alt="luwanzhou" style="border-radius: 50%;"></a>
<br><sub><a href="https://www.gitlink.org.cn/luwanzhou">luwanzhou</a></sub>
</div>
<div align="center">
<a href="https://www.gitlink.org.cn/whale_hihihi" title="whale_hihihi"><img src="https://www.gitlink.org.cn/images/avatars/User/137722?t=1778575729" width="40" height="40" alt="whale_hihihi" style="border-radius: 50%;"></a>
<br><sub><a href="https://www.gitlink.org.cn/whale_hihihi">whale_hihihi</a></sub>
</div>
</div> </div>
## Why gitlink-cli? ## Why gitlink-cli?
- **Agent-Native Design** — Structured [Skills](./skills/) out of the box, compatible with Claude Code, OpenClaw, and other AI platforms — Agents can operate GitLink with zero extra setup - **Agent-Native Design** — Structured [Skills](./skills/README.md) out of the box, compatible with Claude Code, OpenClaw, and other AI platforms — Agents can operate GitLink with zero extra setup
- **Wide Coverage** — Repository, Wiki, Issue, PR, Webhook, Member, Branch, Release, CI, Pipeline, Org, Search, and User workflows are covered by high-level commands - **Wide Coverage** — Repository, Issue, PR, Webhook, Member, Branch, Release, CI, Pipeline, Org, Search, and User workflows are covered by high-level commands
- **AI-Friendly & Optimized** — Every command is tested with real Agents, featuring concise parameters, smart defaults, and structured output - **AI-Friendly & Optimized** — Every command is tested with real Agents, featuring concise parameters, smart defaults, and structured output
- **Cross-Platform** — Runs on macOS, Linux, and Windows (x64/arm64), install via `npm install -g @gitlink-ai/cli` in one command, binary auto-downloaded - **Cross-Platform** — Runs on macOS, Linux, and Windows (x64/arm64), install via `npm install -g @gitlink-ai/cli` in one command, binary auto-downloaded
- **Open Source, Zero Barriers** — MulanPSL-2.0 license, ready to use, just `npm install` - **Open Source, Zero Barriers** — MulanPSL-2.0 license, ready to use, just `npm install`
@ -96,8 +104,7 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
| Category | Capabilities | | Category | Capabilities |
|----------|-------------| |----------|-------------|
| 📦 Repo | List, create, fork, delete repositories, view repo info, insights, and interactions | | 📦 Repo | List, create, fork, delete repositories, view repo info, insights, and interactions |
| 📚 Wiki | List, view, create, update, and delete wiki pages | | 🐛 Issue | Create, update, close, batch close/update/delete, comment on issues |
| 🐛 Issue | Create, update, close, batch close, comment on issues |
| 🔖 Label | Create, list, update, delete issue labels | | 🔖 Label | Create, list, update, delete issue labels |
| 🔀 PR | Create, merge, review pull requests, view changed files | | 🔀 PR | Create, merge, review pull requests, view changed files |
| 👥 Member | List, add, remove repository members, change roles, create and accept invite links | | 👥 Member | List, add, remove repository members, change roles, create and accept invite links |
@ -107,8 +114,11 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
| 🔧 CI | View builds, logs, CI/CD operations | | 🔧 CI | View builds, logs, CI/CD operations |
| ⚙️ Pipeline | Run, inspect, enable, disable, delete pipeline workflows and logs | | ⚙️ Pipeline | Run, inspect, enable, disable, delete pipeline workflows and logs |
| 🔔 Webhook | Manage repo webhooks and test deliveries | | 🔔 Webhook | Manage repo webhooks and test deliveries |
| 📖 Wiki | List, view, create, update, and delete wiki pages |
| 🔍 Search | Search repositories, users | | 🔍 Search | Search repositories, users |
| 📊 Dataset | Query research datasets by project |
| 👤 User | View user profiles and info | | 👤 User | View user profiles and info |
| 📊 Profile | User ability, role, major, activity, and contribution statistics |
| 📋 PM | Sprint management, kanban boards, weekly reports | | 📋 PM | Sprint management, kanban boards, weekly reports |
| 🤖 Workflow | AI-powered issue triage, PR review, release notes | | 🤖 Workflow | AI-powered issue triage, PR review, release notes |
@ -247,27 +257,6 @@ gitlink-cli repo +create -n my-project -d "Project description"
gitlink-cli repo +fork --owner Gitlink --repo forgeplus gitlink-cli repo +fork --owner Gitlink --repo forgeplus
``` ```
### Wiki Management
```bash
# List wiki pages
gitlink-cli wiki +list --owner Gitlink --repo forgeplus
# View a wiki page
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --page Home
# Create a wiki page from inline content
gitlink-cli wiki +create --owner Gitlink --repo forgeplus \
--page Home --title Home --content "Welcome to the project wiki"
# Update a wiki page from a Markdown file
gitlink-cli wiki +update --owner Gitlink --repo forgeplus \
--page Home --file docs/wiki-home.md --message "Update Home"
# Delete a wiki page
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --page Home
```
### Webhook Management ### Webhook Management
```bash ```bash
@ -285,6 +274,28 @@ gitlink-cli webhook +test --owner Gitlink --repo forgeplus --id 68
gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68 gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68
``` ```
### Wiki Management
```bash
# List wiki pages (table of contents)
gitlink-cli wiki +list --owner Gitlink --repo forgeplus --project-id 12345
# View a wiki page by page name
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --project-id 12345 -n home
# Create a wiki page
gitlink-cli wiki +create --owner Gitlink --repo forgeplus --project-id 12345 \
-n getting-started -t "Getting Started" -c "# Getting Started Guide"
# Update a wiki page title and/or content
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "New Title"
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -c "# Updated content"
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "New Title" -c "New content"
# Delete a wiki page
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --project-id 12345 -n old-page
```
### Member Management ### Member Management
```bash ```bash
@ -334,6 +345,14 @@ gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,12
# Batch close issues from a CSV file # Batch close issues from a CSV file
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv
# Preview batch metadata update by API issue IDs
# Note: --ids uses API issue IDs, not web URL issue numbers.
gitlink-cli issue +batch-update --owner Gitlink --repo forgeplus --ids 101,102 --status-id 3 --priority-id 2 --dry-run
# Destructive batch delete requires both dry-run first and --yes for real execution
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --dry-run
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --yes
# Add a comment # Add a comment
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "Fixed" gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "Fixed"
@ -489,6 +508,16 @@ gitlink-cli pipeline +disable --owner Gitlink --repo forgeplus --id 7 --workflow
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
``` ```
### Ignore File Templates
```bash
# List all available .gitignore templates
gitlink-cli ignore +list
# Filter templates by name
gitlink-cli ignore +list --name Go
```
### Search ### Search
```bash ```bash
@ -499,6 +528,29 @@ gitlink-cli search +repos -k "machine learning"
gitlink-cli search +users -k "zhangsan" gitlink-cli search +users -k "zhangsan"
``` ```
### User Profile
`profile` surfaces GitLink's native user statistics (ability, role, major, activity,
contribution). When `--user` is omitted it defaults to the authenticated user.
```bash
# Development ability scores + language breakdown
gitlink-cli profile +ability --user zhangsan
# Role positioning / major (discipline) categories
gitlink-cli profile +role --user zhangsan
gitlink-cli profile +major --user zhangsan
# Ability within a time window (Unix timestamps)
gitlink-cli profile +ability --user zhangsan --start-time 1704067200 --end-time 1735689600
# Recent activity (issues / PRs / commits per day) for the current user
gitlink-cli profile +activity
# Contribution heatmap for a given year
gitlink-cli profile +contribution --user zhangsan --year 2025
```
### Workflow Agent Commands ### Workflow Agent Commands
`workflow` provides rule-based repository analysis for maintainers and AI Agents. It currently supports: `workflow` provides rule-based repository analysis for maintainers and AI Agents. It currently supports:
@ -598,6 +650,32 @@ Safety:
- `workflow +pr-summary` does not comment, approve, reject, or merge pull requests. - `workflow +pr-summary` does not comment, approve, reject, or merge pull requests.
- `workflow +repo-report` aggregates health, issue triage, and PR review summary signals without remote writes. - `workflow +repo-report` aggregates health, issue triage, and PR review summary signals without remote writes.
### Dataset
`dataset` manages and queries GitLink research datasets (title, description,
paper content, license, owning project).
```bash
# List datasets for one or more projects (by numeric project ID)
gitlink-cli dataset +list --ids 5988
# View a repository's dataset and attachments
gitlink-cli dataset +view --owner Gitlink --repo forgeplus
# Create / update a repository's dataset (preview first with --dry-run)
gitlink-cli dataset +create --owner me --repo proj -t "My dataset" -d "..." --license-id 359 --dry-run
gitlink-cli dataset +update --owner me --repo proj -t "My dataset" -d "updated"
# Delete a dataset attachment (destructive: preview, then confirm with --yes)
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --dry-run
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --yes
```
> Note: `dataset +list` (platform dataset query) is verified on production
> gitlink.org.cn. The per-repo `+view`/`+create`/`+update` routes follow the
> published OpenAPI contract but are not yet deployed on production (they return
> 404 there); they will work once the platform enables them.
### Raw API ### Raw API
For endpoints not covered by shortcuts, use the Raw API directly: For endpoints not covered by shortcuts, use the Raw API directly:
@ -654,14 +732,13 @@ git push gitlink
The `skills/` directory contains Agent Skill files for AI-automated GitLink operations. The `skills/` directory contains Agent Skill files for AI-automated GitLink operations.
See [skills/README.md](skills/README.md) for details. See [skills/README.md](./skills/README.md) for details.
| Skill | Description | | Skill | Description |
|-------|-------------| |-------|-------------|
| `gitlink-shared` | Authentication, global parameters, safety rules, API notes | | `gitlink-shared` | Authentication, global parameters, safety rules, API notes |
| `gitlink-repo` | Repository operations (create, view, delete, fork, insights, etc.) | | `gitlink-repo` | Repository operations (create, view, delete, fork, insights, etc.) |
| `gitlink-wiki` | Wiki operations (list, view, create, update, delete) | | `gitlink-issue` | Issue operations (create, update, close, batch update/delete, comment, etc.) |
| `gitlink-issue` | Issue operations (create, update, close, comment, etc.) |
| `gitlink-pr` | Pull request operations (create, merge, review, etc.) | | `gitlink-pr` | Pull request operations (create, merge, review, etc.) |
| `gitlink-member` | Repository member and invite link management | | `gitlink-member` | Repository member and invite link management |
| `gitlink-branch` | Branch management (create, delete, list, protect, unprotect) | | `gitlink-branch` | Branch management (create, delete, list, protect, unprotect) |
@ -725,7 +802,7 @@ gitlink-cli/
## Documentation ## Documentation
- [Skills Guide](skills/README.md) — AI Agent Skills detailed documentation - [Skills Guide](./skills/README.md) — AI Agent Skills detailed documentation
- [Design Document](doc/design.md) — Architecture design and development plan - [Design Document](doc/design.md) — Architecture design and development plan
## FAQ ## FAQ
@ -790,7 +867,7 @@ gitlink-cli uses Windows Credential Manager for secure token storage. If Credent
### Q: Where can I find the full API reference? ### Q: Where can I find the full API reference?
See [skills/gitlink-shared/REFERENCE.md](skills/gitlink-shared/REFERENCE.md). See [skills/gitlink-shared/references/api-reference.md](./skills/gitlink-shared/references/api-reference.md).
## License ## License

View File

@ -5,7 +5,7 @@
[![Go Version](https://img.shields.io/badge/Go-1.26%2B-blue.svg)](https://golang.org) [![Go Version](https://img.shields.io/badge/Go-1.26%2B-blue.svg)](https://golang.org)
[![npm version](https://img.shields.io/npm/v/@gitlink-ai/cli.svg)](https://www.npmjs.com/package/@gitlink-ai/cli) [![npm version](https://img.shields.io/npm/v/@gitlink-ai/cli.svg)](https://www.npmjs.com/package/@gitlink-ai/cli)
[GitLink确实开源](https://www.gitlink.org.cn) 官方 CLI 工具 — 为人类和 AI Agent 双重设计。支持 **macOS、Linux、Windows**,覆盖仓库管理、Wiki、Issue 追踪、Pull Request、Webhook、成员协作、CI/CD 和 AI 自动化工作流,包含 40+ 命令和 AI Agent [Skills](./skills/)。 [GitLink确实开源](https://www.gitlink.org.cn) 官方 CLI 工具 — 为人类和 AI Agent 双重设计。支持 **macOS、Linux、Windows**覆盖仓库管理、Issue 追踪、Pull Request、Webhook、成员协作、CI/CD 和 AI 自动化工作流,包含 40+ 命令和 AI Agent [Skills](./skills/README.md)。
**[English](./README.md)** **[English](./README.md)**
@ -78,12 +78,20 @@
<a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a> <a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a>
<br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub> <br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub>
</div> </div>
<div align="center">
<a href="https://www.gitlink.org.cn/luwanzhou" title="luwanzhou"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/L/165_135_246/120.png" width="40" height="40" alt="luwanzhou" style="border-radius: 50%;"></a>
<br><sub><a href="https://www.gitlink.org.cn/luwanzhou">luwanzhou</a></sub>
</div>
<div align="center">
<a href="https://www.gitlink.org.cn/whale_hihihi" title="whale_hihihi"><img src="https://www.gitlink.org.cn/images/avatars/User/137722?t=1778575729" width="40" height="40" alt="whale_hihihi" style="border-radius: 50%;"></a>
<br><sub><a href="https://www.gitlink.org.cn/whale_hihihi">whale_hihihi</a></sub>
</div>
</div> </div>
## 为什么选择 gitlink-cli ## 为什么选择 gitlink-cli
- **Agent-Native 设计** — 开箱即用结构化 [Skills](./skills/),兼容 Claude Code — Agent 零配置即可操作 GitLink - **Agent-Native 设计** — 开箱即用结构化 [Skills](./skills/README.md),兼容 Claude Code — Agent 零配置即可操作 GitLink
- **广泛覆盖** — 仓库、Wiki、Issue、PR、Webhook、成员、分支、Release、CI、Pipeline、组织、搜索、用户等常用工作流均提供高层命令 - **广泛覆盖** — 仓库、Issue、PR、Webhook、成员、分支、Release、CI、Pipeline、组织、搜索、用户等常用工作流均提供高层命令
- **AI 友好 & 优化** — 每条命令都经过真实 Agent 测试,简洁参数、智能默认值、结构化输出 - **AI 友好 & 优化** — 每条命令都经过真实 Agent 测试,简洁参数、智能默认值、结构化输出
- **跨平台** — macOS、Linux、Windows (x64/arm64) 全支持,`npm` 一条命令安装 - **跨平台** — macOS、Linux、Windows (x64/arm64) 全支持,`npm` 一条命令安装
- **开源零门槛** — 木兰宽松许可证第2版MulanPSL-2.0`npm install` 即用 - **开源零门槛** — 木兰宽松许可证第2版MulanPSL-2.0`npm install` 即用
@ -96,8 +104,7 @@
| 分类 | 能力 | | 分类 | 能力 |
|------|------| |------|------|
| 📦 仓库 | 列出、创建、Fork、删除仓库查看仓库信息、洞察数据和互动状态 | | 📦 仓库 | 列出、创建、Fork、删除仓库查看仓库信息、洞察数据和互动状态 |
| 📚 Wiki | 列出、查看、创建、更新、删除 Wiki 页面 | | 🐛 Issue | 创建、更新、关闭、批量关闭/更新/删除、评论 Issue |
| 🐛 Issue | 创建、更新、关闭、批量关闭、评论 Issue |
| 🔖 标签 | 创建、列出、更新、删除 Issue 标签 | | 🔖 标签 | 创建、列出、更新、删除 Issue 标签 |
| 🔀 PR | 创建、合并、Review Pull Request查看变更文件 | | 🔀 PR | 创建、合并、Review Pull Request查看变更文件 |
| 👥 成员 | 列出、添加、移除仓库成员,调整角色,生成和接受邀请链接 | | 👥 成员 | 列出、添加、移除仓库成员,调整角色,生成和接受邀请链接 |
@ -106,8 +113,11 @@
| 🏢 组织 | 管理组织、成员、团队 | | 🏢 组织 | 管理组织、成员、团队 |
| 🔧 CI | 查看构建、日志、CI/CD 操作 | | 🔧 CI | 查看构建、日志、CI/CD 操作 |
| ⚙️ Pipeline | 运行、查看、启停、删除流水线工作流并查询日志 | | ⚙️ Pipeline | 运行、查看、启停、删除流水线工作流并查询日志 |
| 📖 Wiki | 列出、查看、创建、更新、删除 Wiki 页面 |
| 🔍 搜索 | 搜索仓库、用户 | | 🔍 搜索 | 搜索仓库、用户 |
| 📊 数据集 | 按项目查询科研数据集 |
| 👤 用户 | 查看用户资料和信息 | | 👤 用户 | 查看用户资料和信息 |
| 📊 画像 | 用户开发能力、角色定位、专业定位、近期活动、贡献热力图统计 |
| 📋 项目管理 | Sprint 管理、看板、周报 | | 📋 项目管理 | Sprint 管理、看板、周报 |
| 🤖 工作流 | AI 驱动的 Issue 分类、PR Review、Release Notes | | 🤖 工作流 | AI 驱动的 Issue 分类、PR Review、Release Notes |
@ -258,27 +268,6 @@ gitlink-cli repo +create -n my-project -d "项目描述"
gitlink-cli repo +fork --owner Gitlink --repo forgeplus gitlink-cli repo +fork --owner Gitlink --repo forgeplus
``` ```
### Wiki 管理
```bash
# 列出 Wiki 页面
gitlink-cli wiki +list --owner Gitlink --repo forgeplus
# 查看 Wiki 页面
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --page Home
# 使用命令行内容创建 Wiki 页面
gitlink-cli wiki +create --owner Gitlink --repo forgeplus \
--page Home --title Home --content "欢迎来到项目 Wiki"
# 使用 Markdown 文件更新 Wiki 页面
gitlink-cli wiki +update --owner Gitlink --repo forgeplus \
--page Home --file docs/wiki-home.md --message "更新 Home"
# 删除 Wiki 页面
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --page Home
```
### Webhook 管理 ### Webhook 管理
```bash ```bash
@ -296,6 +285,28 @@ gitlink-cli webhook +test --owner Gitlink --repo forgeplus --id 68
gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68 gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68
``` ```
### Wiki 管理
```bash
# 列出 Wiki 页面(目录结构)
gitlink-cli wiki +list --owner Gitlink --repo forgeplus --project-id 12345
# 查看 Wiki 页面
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --project-id 12345 -n home
# 创建 Wiki 页面
gitlink-cli wiki +create --owner Gitlink --repo forgeplus --project-id 12345 \
-n getting-started -t "快速开始" -c "# 快速开始指南"
# 更新 Wiki 页面标题和/或内容
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "新标题"
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -c "# 更新后的内容"
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "新标题" -c "新内容"
# 删除 Wiki 页面
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --project-id 12345 -n old-page
```
### 成员管理 ### 成员管理
```bash ```bash
@ -345,6 +356,14 @@ gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,12
# 从 CSV 文件批量关闭 Issue # 从 CSV 文件批量关闭 Issue
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv
# 按 API issue id 预览批量更新元数据
# 注意:--ids 是 API issue id不是网页 URL 中的 Issue 编号。
gitlink-cli issue +batch-update --owner Gitlink --repo forgeplus --ids 101,102 --status-id 3 --priority-id 2 --dry-run
# 危险批量删除必须先 dry-run真实执行还要显式 --yes
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --dry-run
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --yes
# 添加评论 # 添加评论
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "已修复" gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "已修复"
@ -467,6 +486,16 @@ gitlink-cli pipeline +disable --owner Gitlink --repo forgeplus --id 7 --workflow
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
``` ```
### 忽略文件模板
```bash
# 列出所有可用的 .gitignore 模板
gitlink-cli ignore +list
# 按名称筛选模板
gitlink-cli ignore +list --name Go
```
### 搜索 ### 搜索
```bash ```bash
@ -477,6 +506,50 @@ gitlink-cli search +repos -k "machine learning"
gitlink-cli search +users -k "zhangsan" gitlink-cli search +users -k "zhangsan"
``` ```
### 用户画像
`profile` 暴露 GitLink 原生的用户画像统计(开发能力、角色定位、专业定位、近期活动、贡献热力图)。
省略 `--user` 时默认使用当前认证用户。
```bash
# 开发能力评分 + 语言分布
gitlink-cli profile +ability --user zhangsan
# 角色定位 / 专业(学科)定位
gitlink-cli profile +role --user zhangsan
gitlink-cli profile +major --user zhangsan
# 指定时间范围的开发能力Unix 时间戳)
gitlink-cli profile +ability --user zhangsan --start-time 1704067200 --end-time 1735689600
# 当前用户的近期活动(每日 疑修 / 合并请求 / 提交)
gitlink-cli profile +activity
# 指定年份的贡献热力图
gitlink-cli profile +contribution --user zhangsan --year 2025
```
### 数据集
`dataset` 管理并查询 GitLink 科研数据集(标题、描述、论文内容、许可证、所属项目)。
```bash
# 按数字项目 ID 列出一个或多个项目的数据集
gitlink-cli dataset +list --ids 5988
# 查看仓库的数据集及其附件
gitlink-cli dataset +view --owner Gitlink --repo forgeplus
# 创建 / 更新仓库数据集(先用 --dry-run 预览)
gitlink-cli dataset +create --owner me --repo proj -t "我的数据集" -d "..." --license-id 359 --dry-run
gitlink-cli dataset +update --owner me --repo proj -t "我的数据集" -d "更新"
# 删除数据集附件(破坏性:先预览,再用 --yes 确认)
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --dry-run
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --yes
```
> 注意:`dataset +list`(平台数据集查询)已在生产 gitlink.org.cn 验证可用。按仓库的 `+view`/`+create`/`+update` 遵循已发布的 OpenAPI 契约,但生产环境尚未部署(当前返回 404待平台上线后即可生效。
### Raw API ### Raw API
Shortcuts 未覆盖的接口可通过 Raw API 直接调用: Shortcuts 未覆盖的接口可通过 Raw API 直接调用:
@ -533,14 +606,13 @@ git push gitlink
`skills/` 目录包含 Claude Code Agent Skill 文件,支持 AI 自动化操作 GitLink 平台。 `skills/` 目录包含 Claude Code Agent Skill 文件,支持 AI 自动化操作 GitLink 平台。
详见 [skills/README.md](skills/README.md) 详见 [skills/README.md](./skills/README.md)
| Skill | 说明 | | Skill | 说明 |
|-------|------| |-------|------|
| `gitlink-shared` | 认证、全局参数、安全规则、API 注意事项 | | `gitlink-shared` | 认证、全局参数、安全规则、API 注意事项 |
| `gitlink-repo` | 仓库操作创建、查看、删除、Fork、洞察数据等 | | `gitlink-repo` | 仓库操作创建、查看、删除、Fork、洞察数据等 |
| `gitlink-wiki` | Wiki 操作(列出、查看、创建、更新、删除) | | `gitlink-issue` | Issue 操作(创建、更新、关闭、批量更新/删除、评论等) |
| `gitlink-issue` | Issue 操作(创建、更新、关闭、评论等) |
| `gitlink-pr` | Pull Request 操作创建、合并、Review 等) | | `gitlink-pr` | Pull Request 操作创建、合并、Review 等) |
| `gitlink-member` | 仓库成员与邀请链接管理 | | `gitlink-member` | 仓库成员与邀请链接管理 |
| `gitlink-release` | 发布管理(创建、编辑、更新、查看、删除等) | | `gitlink-release` | 发布管理(创建、编辑、更新、查看、删除等) |
@ -602,7 +674,7 @@ gitlink-cli/
## 文档 ## 文档
- [Skills 使用指南](skills/README.md) — AI Agent Skills 详细说明 - [Skills 使用指南](./skills/README.md) — AI Agent Skills 详细说明
- [设计文档](doc/design.md) — 架构设计和开发计划 - [设计文档](doc/design.md) — 架构设计和开发计划
## 常见问题 ## 常见问题
@ -665,7 +737,7 @@ gitlink-cli 使用 Windows Credential Manager 安全存储 Token。如果 Creden
### Q: 如何查看完整的 API 参考? ### Q: 如何查看完整的 API 参考?
查看 [skills/gitlink-shared/REFERENCE.md](skills/gitlink-shared/REFERENCE.md) 查看 [skills/gitlink-shared/references/api-reference.md](./skills/gitlink-shared/references/api-reference.md)
## 许可证 ## 许可证

View File

@ -1,119 +0,0 @@
package alias
import (
"fmt"
"os"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
"github.com/gitlink-org/gitlink-cli/internal/config"
)
// AliasConfig represents the aliases section of the CLI config.
type AliasConfig struct {
Aliases map[string]string `yaml:"aliases,omitempty"`
}
// NewAliasCmd creates the alias command with subcommands.
func NewAliasCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "alias",
Short: "管理命令别名(把长命令变短)",
Long: `管理 gitlink-cli 的命令别名
别名允许你为常用命令创建简短的名称例如
gitlink-cli alias +set rl "repo +list"
之后可以使用: gitlink-cli rl
别名存储在 ~/.config/gitlink-cli/aliases.yaml `,
Example: ` gitlink-cli alias +list
gitlink-cli alias +set rl "repo +list"
gitlink-cli alias +set ri "repo +info --owner Gitlink --repo gitlink-cli"
gitlink-cli alias +delete rl`,
}
cmd.AddCommand(
&cobra.Command{
Use: "+list",
Short: "列出所有已定义的别名",
Long: "列出所有已定义的命令别名。如果没有任何别名,会给出创建提示。",
RunE: func(cmd *cobra.Command, args []string) error {
aliases, _ := loadAliases()
if len(aliases) == 0 {
fmt.Println("(未定义任何别名)")
fmt.Println("使用 alias +set <名称> <命令> 来创建别名")
return nil
}
for k, v := range aliases {
fmt.Printf(" %-15s → %s\n", k, v)
}
return nil
},
},
&cobra.Command{
Use: "+set <name> <command>",
Short: "设置别名",
Long: "为一条命令设置别名。如果别名已存在,会被覆盖。",
Args: cobra.ExactArgs(2),
Example: ` gitlink-cli alias +set rl "repo +list"
gitlink-cli alias +set ri "repo +info"`,
RunE: func(cmd *cobra.Command, args []string) error {
aliases, _ := loadAliases()
aliases[args[0]] = args[1]
if err := saveAliases(aliases); err != nil {
return err
}
fmt.Printf("别名已设置: %s → %s\n", args[0], args[1])
return nil
},
},
&cobra.Command{
Use: "+delete <name>",
Short: "删除别名",
Long: "删除一个已定义的命令别名。",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
aliases, _ := loadAliases()
if _, ok := aliases[args[0]]; !ok {
return fmt.Errorf("别名 %s 不存在", args[0])
}
delete(aliases, args[0])
if err := saveAliases(aliases); err != nil {
return err
}
fmt.Printf("别名已删除: %s\n", args[0])
return nil
},
},
)
return cmd
}
func aliasesPath() string {
return config.ConfigDir() + "/aliases.yaml"
}
func loadAliases() (map[string]string, error) {
data, err := os.ReadFile(aliasesPath())
if err != nil {
return make(map[string]string), nil
}
var ac AliasConfig
if err := yaml.Unmarshal(data, &ac); err != nil {
return make(map[string]string), nil
}
if ac.Aliases == nil {
ac.Aliases = make(map[string]string)
}
return ac.Aliases, nil
}
func saveAliases(a map[string]string) error {
data, err := yaml.Marshal(AliasConfig{Aliases: a})
if err != nil {
return err
}
os.MkdirAll(config.ConfigDir(), 0700)
return os.WriteFile(aliasesPath(), data, 0600)
}

View File

@ -1,193 +0,0 @@
package alias
import (
"bytes"
"os"
"strings"
"testing"
"github.com/spf13/cobra"
)
func TestLoadAliasesEmpty(t *testing.T) {
// 设置临时配置目录
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
aliases, err := loadAliases()
if err != nil {
t.Fatalf("loadAliases failed: %v", err)
}
if len(aliases) != 0 {
t.Fatalf("expected empty aliases, got %d", len(aliases))
}
}
func TestSaveAndLoadAliases(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
// 保存
original := map[string]string{
"rl": "repo +list",
"ri": "repo +info",
}
if err := saveAliases(original); err != nil {
t.Fatalf("saveAliases failed: %v", err)
}
// 加载
loaded, err := loadAliases()
if err != nil {
t.Fatalf("loadAliases failed: %v", err)
}
if len(loaded) != 2 {
t.Fatalf("expected 2 aliases, got %d", len(loaded))
}
if loaded["rl"] != "repo +list" {
t.Errorf("expected rl -> repo +list, got %s", loaded["rl"])
}
if loaded["ri"] != "repo +info" {
t.Errorf("expected ri -> repo +info, got %s", loaded["ri"])
}
}
func TestSaveAliasesOverwrite(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
// 第一次保存
saveAliases(map[string]string{"rl": "repo +list"})
// 覆盖保存
saveAliases(map[string]string{"rl": "repo +list --owner Gitlink"})
loaded, _ := loadAliases()
if loaded["rl"] != "repo +list --owner Gitlink" {
t.Errorf("alias should be overwritten, got %s", loaded["rl"])
}
}
func TestLoadAliasesInvalidYAML(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
// 写入无效 YAML
os.WriteFile(tmpDir+"/aliases.yaml", []byte("{{invalid yaml}}"), 0600)
aliases, err := loadAliases()
if err != nil {
t.Fatalf("should not error on invalid YAML, got: %v", err)
}
if len(aliases) != 0 {
t.Fatalf("should return empty map on invalid YAML, got %d", len(aliases))
}
}
func TestNewAliasCmd(t *testing.T) {
cmd := NewAliasCmd()
if cmd.Use != "alias" {
t.Errorf("expected Use 'alias', got %s", cmd.Use)
}
if !cmd.HasSubCommands() {
t.Error("alias command should have subcommands")
}
subcmds := cmd.Commands()
if len(subcmds) != 3 {
t.Fatalf("expected 3 subcommands, got %d", len(subcmds))
}
}
func TestAliasListSubcommand(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
cmd := NewAliasCmd()
// 找到 +list 子命令
var listCmd *cobra.Command
for _, sub := range cmd.Commands() {
if sub.Use == "+list" {
listCmd = sub
break
}
}
if listCmd == nil {
t.Fatal("+list subcommand not found")
}
// 无别名时运行
buf := new(bytes.Buffer)
listCmd.SetOut(buf)
listCmd.SetArgs([]string{})
if err := listCmd.Execute(); err != nil {
t.Fatalf("list failed: %v", err)
}
if !strings.Contains(buf.String(), "未定义任何别名") {
t.Errorf("expected hint for no aliases, got: %s", buf.String())
}
}
func TestAliasSetAndDeleteSubcommands(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
cmd := NewAliasCmd()
// 找到 +set 子命令
var setCmd, deleteCmd *cobra.Command
for _, sub := range cmd.Commands() {
if strings.HasPrefix(sub.Use, "+set") {
setCmd = sub
}
if strings.HasPrefix(sub.Use, "+delete") {
deleteCmd = sub
}
}
// +set
setCmd.SetArgs([]string{"rl", "repo +list"})
if err := setCmd.Execute(); err != nil {
t.Fatalf("set failed: %v", err)
}
// 验证文件写入
aliases, _ := loadAliases()
if aliases["rl"] != "repo +list" {
t.Fatalf("alias not saved correctly: %v", aliases)
}
// +delete
deleteCmd.SetArgs([]string{"rl"})
if err := deleteCmd.Execute(); err != nil {
t.Fatalf("delete failed: %v", err)
}
// 验证已删除
aliases, _ = loadAliases()
if _, ok := aliases["rl"]; ok {
t.Fatal("alias should have been deleted")
}
}
func TestAliasDeleteNonExistent(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
cmd := NewAliasCmd()
var deleteCmd *cobra.Command
for _, sub := range cmd.Commands() {
if strings.HasPrefix(sub.Use, "+delete") {
deleteCmd = sub
break
}
}
deleteCmd.SetArgs([]string{"nonexistent"})
err := deleteCmd.Execute()
if err == nil {
t.Fatal("expected error when deleting nonexistent alias")
}
if !strings.Contains(err.Error(), "不存在") {
t.Errorf("error should mention alias does not exist: %v", err)
}
}

View File

@ -7,25 +7,16 @@ import (
"io" "io"
"net/url" "net/url"
"os" "os"
"regexp"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil" "github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
"github.com/gitlink-org/gitlink-cli/internal/client" "github.com/gitlink-org/gitlink-cli/internal/client"
"github.com/gitlink-org/gitlink-cli/internal/context"
"github.com/gitlink-org/gitlink-cli/internal/i18n" "github.com/gitlink-org/gitlink-cli/internal/i18n"
"github.com/gitlink-org/gitlink-cli/internal/output" "github.com/gitlink-org/gitlink-cli/internal/output"
) )
// apiOwnerPlaceholder and apiRepoPlaceholder match the REST-style :owner / :repo
// path placeholders used throughout the GitLink API docs and shortcut commands.
var (
apiOwnerPlaceholder = regexp.MustCompile(`:owner\b`)
apiRepoPlaceholder = regexp.MustCompile(`:repo\b`)
)
func NewAPICmd(translators ...*i18n.Translator) *cobra.Command { func NewAPICmd(translators ...*i18n.Translator) *cobra.Command {
tr := i18n.Default() tr := i18n.Default()
if len(translators) > 0 && translators[0] != nil { if len(translators) > 0 && translators[0] != nil {
@ -69,32 +60,6 @@ func validateAPIArgs(c *cobra.Command, args []string) error {
return cobra.ExactArgs(2)(c, args) return cobra.ExactArgs(2)(c, args)
} }
// msysPathRe matches Windows drive-letter prefixes produced by MSYS2/Git Bash
// path conversion, e.g. "C:/Program Files/Git/v1/owner/repo" for input "/v1/owner/repo".
var msysPathRe = regexp.MustCompile(`^[A-Za-z]:/`)
// restoreAPIPath restores an API path polluted by MSYS2/Git Bash path
// conversion on Windows, e.g. "C:/Program Files/Git/v1/owner/repo" -> "/v1/owner/repo".
// If the path does not start with a drive letter, or no known API prefix is
// found, the original path is returned unchanged.
func restoreAPIPath(path string) string {
if !msysPathRe.MatchString(path) {
return path
}
// Pick the EARLIEST occurrence among known API prefixes, so a path like
// ".../api/v1/users" restores to "/api/v1/users" rather than "/v1/users".
bestIdx := -1
for _, prefix := range []string{"/v1/", "/v2/", "/api/", "/users/", "/projects/"} {
if idx := strings.Index(path, prefix); idx >= 0 && (bestIdx == -1 || idx < bestIdx) {
bestIdx = idx
}
}
if bestIdx >= 0 {
return path[bestIdx:]
}
return path
}
func runAPI(c *cobra.Command, args []string) error { func runAPI(c *cobra.Command, args []string) error {
batchFile, _ := c.Flags().GetString("batch-file") batchFile, _ := c.Flags().GetString("batch-file")
if batchFile != "" { if batchFile != "" {
@ -102,14 +67,10 @@ func runAPI(c *cobra.Command, args []string) error {
} }
method := strings.ToUpper(args[0]) method := strings.ToUpper(args[0])
path := args[1]
// Fix MSYS2/Git Bash path auto-conversion on Windows first: if !strings.HasPrefix(path, "/") {
// "/v1/owner/repo" is rewritten to "C:/Program Files/Git/v1/owner/repo". path = "/" + path
rawPath := restoreAPIPath(args[1])
path, err := resolveAPIPath(c, rawPath)
if err != nil {
return err
} }
cli, err := client.New() cli, err := client.New()
@ -146,40 +107,6 @@ func runAPI(c *cobra.Command, args []string) error {
return output.Print(env, resolveFormat()) return output.Print(env, resolveFormat())
} }
// resolveAPIPath prepares a single-call path: it renders {{var}} templates
// supplied via --var (consistent with batch mode), substitutes the REST-style
// :owner / :repo placeholders (resolved from --owner/--repo or the git remote,
// exactly like the shortcut commands), and ensures a leading slash.
func resolveAPIPath(c *cobra.Command, rawPath string) (string, error) {
path := rawPath
overrides, err := parseBatchVars(c)
if err != nil {
return "", err
}
if len(overrides) > 0 {
rendered, rerr := renderTemplate(path, overrides)
if rerr != nil {
return "", rerr
}
path = rendered
}
if apiOwnerPlaceholder.MatchString(path) || apiRepoPlaceholder.MatchString(path) {
owner, repo, rerr := context.ResolveOwnerRepo(cmdutil.Owner, cmdutil.Repo)
if rerr != nil {
return "", fmt.Errorf("path contains :owner/:repo placeholders but they could not be resolved: %w", rerr)
}
path = apiOwnerPlaceholder.ReplaceAllLiteralString(path, owner)
path = apiRepoPlaceholder.ReplaceAllLiteralString(path, repo)
}
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return path, nil
}
func readJSONBody(c *cobra.Command) (interface{}, error) { func readJSONBody(c *cobra.Command) (interface{}, error) {
bodyStr, _ := c.Flags().GetString("body") bodyStr, _ := c.Flags().GetString("body")
bodyFile, _ := c.Flags().GetString("body-file") bodyFile, _ := c.Flags().GetString("body-file")

View File

@ -131,63 +131,6 @@ func TestRunAPIBadQuery(t *testing.T) {
} }
} }
func TestRunAPIPaginate(t *testing.T) {
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
page := r.URL.Query().Get("page")
w.Header().Set("Content-Type", "application/json")
switch page {
case "1":
json.NewEncoder(w).Encode(map[string]interface{}{
"total_count": 3,
"issues": []interface{}{
map[string]interface{}{"id": 1},
map[string]interface{}{"id": 2},
},
})
default:
json.NewEncoder(w).Encode(map[string]interface{}{
"total_count": 3,
"issues": []interface{}{
map[string]interface{}{"id": 3},
},
})
}
})
cmdutil.Format = "json"
cmd := NewAPICmd()
cmd.SetArgs([]string{"GET", "/owner/repo/issues", "--paginate", "--query", "limit=2"})
if err := cmd.Execute(); err != nil {
t.Fatalf("runAPI paginate error: %v", err)
}
}
func TestRunAPIPaginateRejectsNonGET(t *testing.T) {
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("server should not be reached")
})
cmdutil.Format = "json"
cmd := NewAPICmd()
cmd.SetArgs([]string{"POST", "/owner/repo/issues", "--paginate"})
if err := cmd.Execute(); err == nil {
t.Fatal("expected error for --paginate with POST")
}
}
func TestRunAPIPaginateRejectsBatchFile(t *testing.T) {
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("server should not be reached")
})
cmdutil.Format = "json"
cmd := NewAPICmd()
cmd.SetArgs([]string{"--batch-file", "plan.json", "--paginate"})
if err := cmd.Execute(); err == nil {
t.Fatal("expected error for --paginate with --batch-file")
}
}
func TestRunAPIHTTPError(t *testing.T) { func TestRunAPIHTTPError(t *testing.T) {
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) { setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
@ -259,105 +202,6 @@ func TestRunAPINoPrefix(t *testing.T) {
} }
} }
func TestRunAPIRendersOwnerRepoColonPlaceholders(t *testing.T) {
oldOwner, oldRepo := cmdutil.Owner, cmdutil.Repo
cmdutil.Owner, cmdutil.Repo = "Gitlink", "gitlink-cli"
defer func() {
cmdutil.Owner, cmdutil.Repo = oldOwner, oldRepo
}()
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/Gitlink/gitlink-cli/issues.json" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
})
cmdutil.Format = "json"
cmd := NewAPICmd()
cmd.SetArgs([]string{"GET", "/:owner/:repo/issues"})
if err := cmd.Execute(); err != nil {
t.Fatalf("runAPI placeholder error: %v", err)
}
}
func TestRunAPIRendersVarsInQueryAndBody(t *testing.T) {
var gotBody map[string]interface{}
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v1/Gitlink/gitlink-cli/issues/42/journals.json" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
if r.URL.Query().Get("notify") != "true" {
t.Fatalf("notify query = %q", r.URL.Query().Get("notify"))
}
if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil {
t.Fatalf("decode body: %v", err)
}
json.NewEncoder(w).Encode(map[string]interface{}{"id": 99})
})
cmdutil.Format = "json"
bodyPath := filepath.Join(t.TempDir(), "body.json")
if err := os.WriteFile(bodyPath, []byte(`{"notes":"hello {{actor}}","meta":{"repo":"{{repo}}"}}`), 0600); err != nil {
t.Fatalf("write body: %v", err)
}
cmd := NewAPICmd()
cmd.SetArgs([]string{
"POST", "/v1/{{owner}}/{{repo}}/issues/{{number}}/journals",
"--query", "notify={{notify}}",
"--body-file", bodyPath,
"--var", "owner=Gitlink",
"--var", "repo=gitlink-cli",
"--var", "number=42",
"--var", "notify=true",
"--var", "actor=bot",
})
if err := cmd.Execute(); err != nil {
t.Fatalf("runAPI rendered vars error: %v", err)
}
if gotBody["notes"] != "hello bot" {
t.Fatalf("notes = %#v", gotBody["notes"])
}
meta := gotBody["meta"].(map[string]interface{})
if meta["repo"] != "gitlink-cli" {
t.Fatalf("meta.repo = %#v", meta["repo"])
}
}
func TestRunAPIDryRunDoesNotReachServer(t *testing.T) {
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("dry-run should not reach server")
})
cmdutil.Format = "json"
cmd := NewAPICmd()
cmd.SetArgs([]string{
"POST", "/v1/{{owner}}/{{repo}}/issues",
"--body", `{"subject":"{{title}}"}`,
"--dry-run",
"--var", "owner=Gitlink",
"--var", "repo=gitlink-cli",
"--var", "title=Bug report",
})
if err := cmd.Execute(); err != nil {
t.Fatalf("runAPI dry-run error: %v", err)
}
}
func TestRunAPIMissingSingleRequestVar(t *testing.T) {
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("should not reach server")
})
cmdutil.Format = "json"
cmd := NewAPICmd()
cmd.SetArgs([]string{"GET", "/v1/{{owner}}/{{repo}}/issues/{{number}}"})
if err := cmd.Execute(); err == nil {
t.Fatal("expected missing variable error")
}
}
func TestRenderBatchRequestsTemplateVars(t *testing.T) { func TestRenderBatchRequestsTemplateVars(t *testing.T) {
requests, err := renderBatchRequests([]batchRequest{ requests, err := renderBatchRequests([]batchRequest{
{ {

View File

@ -2,15 +2,11 @@ package auth
import ( import (
"bufio" "bufio"
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os" "os"
"os/signal"
"strings" "strings"
"syscall"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/term" "golang.org/x/term"
@ -38,7 +34,6 @@ func NewAuthCmd(translators ...*i18n.Translator) *cobra.Command {
cmd.AddCommand(newLoginCmd(tr)) cmd.AddCommand(newLoginCmd(tr))
cmd.AddCommand(newLogoutCmd(tr)) cmd.AddCommand(newLogoutCmd(tr))
cmd.AddCommand(newStatusCmd(tr)) cmd.AddCommand(newStatusCmd(tr))
cmd.AddCommand(newCheckinCmd(tr))
return cmd return cmd
} }
@ -186,99 +181,3 @@ func newStatusCmd(tr *i18n.Translator) *cobra.Command {
}, },
} }
} }
func newCheckinCmd(tr *i18n.Translator) *cobra.Command {
var intervalMinutes int
cmd := &cobra.Command{
Use: "checkin",
Short: tr.T("cmd.auth.checkin.short"),
Long: tr.T("cmd.auth.checkin.long"),
RunE: func(cmd *cobra.Command, args []string) error {
return runCheckin(cmd.OutOrStdout(), intervalMinutes, tr)
},
}
cmd.Flags().IntVarP(&intervalMinutes, "time", "t", 30, tr.T("flag.auth.checkin.time"))
return cmd
}
func runCheckin(out io.Writer, intervalMinutes int, tr *i18n.Translator) error {
// Check if user is logged in
token, err := loadToken()
if err != nil || token == "" {
if os.Getenv(envTokenVar) == "" {
return errors.New(tr.T("error.auth.not_logged_in"))
}
}
// Convert minutes to duration
interval := time.Duration(intervalMinutes) * time.Minute
// Print startup message
fmt.Fprintln(out, tr.Tf("output.auth.checkin.start", i18n.Args{"interval": intervalMinutes}))
fmt.Fprintln(out, tr.T("output.auth.checkin.stop_hint"))
// Setup signal handling for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
fmt.Fprintln(out, "\n"+tr.T("output.auth.checkin.stopping"))
cancel()
}()
// Create ticker
ticker := time.NewTicker(interval)
defer ticker.Stop()
// Do first check immediately
if err := doCheckin(out, tr, interval); err != nil {
fmt.Fprintln(out, tr.Tf("error.auth.checkin.failed", i18n.Args{"message": err.Error()}))
}
// Then check periodically
for {
select {
case <-ctx.Done():
fmt.Fprintln(out, tr.T("output.auth.checkin.stopped"))
return nil
case <-ticker.C:
if err := doCheckin(out, tr, interval); err != nil {
fmt.Fprintln(out, tr.Tf("error.auth.checkin.failed", i18n.Args{"message": err.Error()}))
}
}
}
}
func doCheckin(out io.Writer, tr *i18n.Translator, interval time.Duration) error {
timestamp := time.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(out, "[%s] "+tr.T("output.auth.checkin.checking")+"\n", timestamp)
user, err := internalAuth.GetCurrentUser()
if err != nil {
return err
}
login, _ := user["login"].(string)
name, _ := user["name"].(string)
if login != "" {
msg := tr.Tf("output.auth.checkin.success", i18n.Args{"login": login})
if name != "" {
msg = fmt.Sprintf("%s (%s)", msg, name)
}
fmt.Fprintf(out, "[%s] %s\n", timestamp, msg)
} else {
fmt.Fprintf(out, "[%s] "+tr.T("output.auth.checkin.success_no_user")+"\n", timestamp)
}
// Print next refresh time
nextTime := time.Now().Add(interval).Format("2006-01-02 15:04:05")
fmt.Fprintln(out, tr.Tf("output.auth.checkin.interval", i18n.Args{"time": nextTime}))
return nil
}

View File

@ -35,7 +35,7 @@ func TestNewAuthCmd(t *testing.T) {
} }
expectedSubs := map[string]bool{ expectedSubs := map[string]bool{
"login": false, "logout": false, "status": false, "checkin": false, "login": false, "logout": false, "status": false,
} }
for _, sub := range cmd.Commands() { for _, sub := range cmd.Commands() {
if _, ok := expectedSubs[sub.Use]; !ok { if _, ok := expectedSubs[sub.Use]; !ok {

View File

@ -1,58 +0,0 @@
package browse
import (
"fmt"
"os/exec"
"runtime"
"github.com/spf13/cobra"
"github.com/gitlink-org/gitlink-cli/internal/context"
)
// NewBrowseCmd creates the browse command for opening GitLink pages in a browser.
func NewBrowseCmd() *cobra.Command {
return &cobra.Command{
Use: "browse [resource]",
Short: "在浏览器中打开 GitLink 页面",
Long: `打开当前仓库或指定资源 GitLink 页面
如果不带参数打开当前仓库主页
资源格式: issues/42, pulls/42, wiki
浏览器打开命令:
- macOS: open
- Windows: start
- Linux: xdg-open`,
Example: ` gitlink-cli browse
gitlink-cli browse issues/42
gitlink-cli browse pulls/128
gitlink-cli browse wiki`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
owner, repo, err := context.ResolveOwnerRepo("", "")
if err != nil {
return fmt.Errorf("无法推断仓库信息: %w", err)
}
url := fmt.Sprintf("https://gitlink.org.cn/%s/%s", owner, repo)
if len(args) > 0 {
url += "/" + args[0]
}
fmt.Printf("正在打开: %s\n", url)
return openBrowser(url)
},
}
}
func openBrowser(url string) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "darwin":
cmd = exec.Command("open", url)
case "windows":
cmd = exec.Command("cmd", "/c", "start", url)
default:
cmd = exec.Command("xdg-open", url)
}
return cmd.Start()
}

View File

@ -1,58 +0,0 @@
package browse
import (
"strings"
"testing"
)
func TestNewBrowseCmd(t *testing.T) {
cmd := NewBrowseCmd()
if cmd.Use != "browse [resource]" {
t.Errorf("expected Use 'browse [resource]', got %s", cmd.Use)
}
if cmd.Short == "" {
t.Error("Short description should not be empty")
}
if cmd.Long == "" {
t.Error("Long description should not be empty")
}
}
func TestBrowseCmdHasCorrectArgs(t *testing.T) {
cmd := NewBrowseCmd()
// MaximumNArgs(1) should allow 0 or 1 args
if err := cmd.Args(cmd, []string{}); err != nil {
t.Errorf("should accept 0 args: %v", err)
}
if err := cmd.Args(cmd, []string{"issues/42"}); err != nil {
t.Errorf("should accept 1 arg: %v", err)
}
if err := cmd.Args(cmd, []string{"a", "b"}); err == nil {
t.Error("should reject more than 1 arg")
}
}
func TestBrowseCmdSubcommandStructure(t *testing.T) {
cmd := NewBrowseCmd()
// browse 不应该有子命令
if cmd.HasSubCommands() {
t.Error("browse should not have subcommands")
}
}
func TestBrowseCmdExample(t *testing.T) {
cmd := NewBrowseCmd()
if cmd.Example == "" {
t.Error("Example should not be empty")
}
if !strings.Contains(cmd.Example, "browse") {
t.Error("Example should contain 'browse'")
}
}
func TestOpenBrowserReturnsNoError(t *testing.T) {
// openBrowser 在所有平台都应该返回 nil 或一个 error
// 在无头环境下可能会失败,但不应该 panic
_ = openBrowser("https://gitlink.org.cn")
// 只要不 panic 就行
}

View File

@ -55,9 +55,9 @@ func TestRootCmdHasSubcommands(t *testing.T) {
} }
names := map[string]bool{} names := map[string]bool{}
for _, sub := range root.Commands() { for _, sub := range root.Commands() {
names[sub.Name()] = true names[sub.Use] = true
} }
for _, want := range []string{"auth", "completion", "config", "doctor", "version"} { for _, want := range []string{"auth", "config", "doctor", "version"} {
if !names[want] { if !names[want] {
t.Fatalf("missing subcommand: %s", want) t.Fatalf("missing subcommand: %s", want)
} }

View File

@ -1,71 +0,0 @@
package cmd
import (
"bytes"
"strings"
"testing"
)
func TestCompletionCmdGeneratesSupportedShells(t *testing.T) {
cases := []struct {
shell string
want string
}{
{shell: "bash", want: "__gitlink-cli"},
{shell: "zsh", want: "#compdef gitlink-cli"},
{shell: "fish", want: "complete -c gitlink-cli"},
{shell: "powershell", want: "Register-ArgumentCompleter"},
}
for _, tc := range cases {
root, err := NewRootCmd(RootOptions{Version: "test", Args: []string{"completion", tc.shell}}, nil)
if err != nil {
t.Fatal(err)
}
var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
if err := root.Execute(); err != nil {
t.Fatalf("%s completion error: %v", tc.shell, err)
}
if !strings.Contains(out.String(), tc.want) {
t.Fatalf("%s completion missing %q, got:\n%s", tc.shell, tc.want, out.String()[:min(len(out.String()), 400)])
}
}
}
func TestCompletionCmdNoDescriptions(t *testing.T) {
root, err := NewRootCmd(RootOptions{
Version: "test",
Args: []string{"completion", "bash", "--no-descriptions"},
}, nil)
if err != nil {
t.Fatal(err)
}
var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
if err := root.Execute(); err != nil {
t.Fatal(err)
}
if strings.Contains(out.String(), "GitLink CLI - command-line tool for GitLink") {
t.Fatalf("expected descriptions to be omitted")
}
}
func TestCompletionCmdRejectsUnsupportedShell(t *testing.T) {
root, err := NewRootCmd(RootOptions{Version: "test", Args: []string{"completion", "xonsh"}}, nil)
if err != nil {
t.Fatal(err)
}
var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
err = root.Execute()
if err == nil {
t.Fatal("expected unsupported shell error")
}
if !strings.Contains(err.Error(), "invalid argument") {
t.Fatalf("expected invalid argument error, got %q", err.Error())
}
}

208
cmd/do.go
View File

@ -1,208 +0,0 @@
package cmd
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/spf13/cobra"
)
// NLCommand 自然语言命令路由
// 用法: gitlink-cli do "列出issue" 或 gitlink-cli do wiki
// 关键词匹配 → 推荐命令 + 显示参数 + 简短/完全示例
// 输入模块名(如 wiki→ 列出该模块全部命令
func newDoCmd() *cobra.Command {
return &cobra.Command{
Use: `do "自然语言描述或模块名"`,
Short: "Natural language command helper (e.g. do \"list issues\" or do wiki)",
Long: `用自然语言描述你想做的事自动匹配命令并显示参数
也可以直接输入模块名查看该模块全部命令
示例:
gitlink-cli do 列出issue # 匹配到 issue +list
gitlink-cli do wiki # 列出 wiki 全部命令
gitlink-cli do 创建标签 # 匹配到 label +create`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
input := strings.ToLower(strings.Join(args, " "))
input = strings.TrimSpace(input)
exePath, _ := os.Executable()
// 1. 先检查是否是模块名(如 wiki/issue/pr/label...
modules := []string{"wiki", "issue", "pr", "label", "notification", "snippet",
"repo", "release", "branch", "member", "milestone", "webhook",
"ci", "search", "org", "user", "compare", "workflow"}
for _, mod := range modules {
if input == mod {
fmt.Printf("📦 %s 模块全部命令:\n", mod)
fmt.Println(strings.Repeat("=", 50))
helpCmd := exec.Command(exePath, mod, "--help")
helpCmd.Stdout = os.Stdout
helpCmd.Stderr = os.Stderr
helpCmd.Run()
fmt.Println(strings.Repeat("=", 50))
fmt.Println("\n💡 选择一个命令运行,例如:")
fmt.Printf(" gitlink-cli %s +list\n", mod)
fmt.Printf(" gitlink-cli %s +list --owner ylly --repo gitlink-cli --format json (完全版)\n", mod)
return
}
}
// 2. 关键词匹配
matched := matchNL(input)
if matched == "" {
fmt.Println("❌ 未识别。试试这些:")
fmt.Println(" 输入模块名wiki/issue/pr/label/notification/snippet/repo...")
fmt.Println(" 或描述操作列出issue/创建标签/登录/搜索仓库...")
fmt.Println("\n示例:")
fmt.Println(" gitlink-cli do wiki # 查看 wiki 全部命令")
fmt.Println(" gitlink-cli do 列出issue # 匹配 issue +list")
os.Exit(1)
}
parts := strings.Fields(matched)
fmt.Printf("✅ 匹配到命令: %s\n\n", matched)
// 显示该命令的 --help
if len(parts) >= 2 {
group := parts[0]
sub := parts[1]
fmt.Println("📋 命令参数说明:")
fmt.Println(strings.Repeat("-", 50))
helpCmd := exec.Command(exePath, group, sub, "--help")
helpCmd.Stdout = os.Stdout
helpCmd.Stderr = os.Stderr
helpCmd.Run()
fmt.Println(strings.Repeat("-", 50))
// 显示简短版 + 完全版示例
fmt.Println("\n💡 命令示例:")
fmt.Printf(" 简短版(在自己的仓库目录里):\n")
fmt.Printf(" gitlink-cli %s\n", matched)
fmt.Printf(" 完全版(任何目录都能用):\n")
fmt.Printf(" gitlink-cli %s --owner ylly --repo gitlink-cli --format json\n", matched)
} else {
// auth login / auth status 等无子命令的
fmt.Printf("\n💡 运行gitlink-cli %s\n", matched)
}
},
}
}
// matchNL 关键词匹配自然语言 → 命令
func matchNL(input string) string {
type rule struct {
keywords []string
cmd string
}
rules := []rule{
// Issue
{[]string{"issue", "疑修", "问题", "list", "列", "查看"}, "issue +list"},
{[]string{"issue", "create", "新建", "创建", "提"}, "issue +create"},
{[]string{"issue", "view", "详情", "看"}, "issue +view"},
{[]string{"issue", "close", "关闭"}, "issue +close"},
{[]string{"issue", "update", "更新", "修改"}, "issue +update"},
{[]string{"issue", "comment", "评论", "回复"}, "issue +comment"},
{[]string{"issue", "batch", "批量", "关闭"}, "issue +batch-close"},
{[]string{"issue", "assigners", "负责人", "分配", "候选"}, "issue +assigners"},
{[]string{"issue", "authors", "作者"}, "issue +authors"},
// PR
{[]string{"pr", "pull", "合并请求", "merge", "list"}, "pr +list"},
{[]string{"pr", "create", "新建", "提交"}, "pr +create"},
{[]string{"pr", "view", "详情", "看"}, "pr +view"},
{[]string{"pr", "merge", "合并"}, "pr +merge"},
{[]string{"pr", "diff", "差异", "变更"}, "pr +diff"},
{[]string{"pr", "files", "文件", "变更文件"}, "pr +files"},
{[]string{"pr", "review", "审查", "review"}, "pr +review"},
{[]string{"pr", "close", "关闭"}, "pr +close"},
{[]string{"pr", "reopen", "重开", "重新打开"}, "pr +reopen"},
// Label
{[]string{"label", "tag", "标签", "list"}, "label +list"},
{[]string{"label", "tag", "标签", "create", "新建", "创建"}, "label +create"},
{[]string{"label", "tag", "标签", "update", "更新", "修改"}, "label +update"},
{[]string{"label", "tag", "标签", "delete", "删除"}, "label +delete"},
{[]string{"label", "tag", "标签", "batch", "批量"}, "label +batch-create"},
// Wiki
{[]string{"wiki", "文档", "知识库", "list"}, "wiki +list"},
{[]string{"wiki", "文档", "create", "新建", "创建"}, "wiki +create"},
{[]string{"wiki", "文档", "view", "查看"}, "wiki +view"},
{[]string{"wiki", "文档", "update", "更新"}, "wiki +update"},
{[]string{"wiki", "文档", "delete", "删除"}, "wiki +delete"},
// Release
{[]string{"release", "发布", "版本", "list"}, "release +list"},
{[]string{"release", "发布", "create", "新建"}, "release +create"},
{[]string{"release", "发布", "view", "查看"}, "release +view"},
// Repo
{[]string{"repo", "仓库", "info", "信息"}, "repo +info"},
{[]string{"repo", "仓库", "create", "新建", "创建"}, "repo +create"},
{[]string{"repo", "仓库", "readme"}, "repo +readme"},
{[]string{"repo", "仓库", "fork", "复刻"}, "repo +fork"},
{[]string{"repo", "仓库", "list", "列"}, "repo +list"},
// Auth
{[]string{"auth", "login", "登录", "认证"}, "auth login"},
{[]string{"auth", "status", "状态"}, "auth status"},
// Snippet
{[]string{"snippet", "片段", "代码片段", "list"}, "snippet +list"},
{[]string{"snippet", "片段", "代码片段", "create", "新建", "保存"}, "snippet +create"},
{[]string{"snippet", "片段", "代码片段", "view", "查看"}, "snippet +view"},
{[]string{"snippet", "片段", "代码片段", "delete", "删除"}, "snippet +delete"},
// Notification
{[]string{"notification", "通知", "消息", "list"}, "notification +list"},
{[]string{"notification", "通知", "read", "已读"}, "notification +read"},
{[]string{"notification", "通知", "delete", "删除"}, "notification +delete"},
// Member
{[]string{"member", "成员", "list"}, "member +list"},
{[]string{"member", "成员", "add", "添加"}, "member +add"},
{[]string{"member", "成员", "invite", "邀请"}, "member +invite-link"},
// Branch
{[]string{"branch", "分支", "list"}, "branch +list"},
{[]string{"branch", "分支", "create", "新建"}, "branch +create"},
// CI
{[]string{"ci", "构建", "流水线", "list"}, "ci +list"},
{[]string{"ci", "构建", "log", "日志"}, "ci +logs"},
// Search
{[]string{"search", "搜索", "查找", "repos"}, "search +repos"},
{[]string{"search", "搜索", "查找", "user", "用户"}, "search +users"},
// Milestone
{[]string{"milestone", "里程碑", "list"}, "milestone +list"},
{[]string{"milestone", "里程碑", "create", "新建"}, "milestone +create"},
// Webhook
{[]string{"webhook", "钩子", "list"}, "webhook +list"},
{[]string{"webhook", "钩子", "create", "新建"}, "webhook +create"},
}
bestMatch := ""
bestScore := 0
for _, r := range rules {
score := 0
for _, kw := range r.keywords {
if strings.Contains(input, kw) {
score++
}
}
if score >= 2 && score > bestScore {
bestScore = score
bestMatch = r.cmd
}
}
// 降级只匹配1个关键词
if bestMatch == "" {
for _, r := range rules {
for _, kw := range r.keywords {
if strings.Contains(input, kw) {
bestMatch = r.cmd
break
}
}
if bestMatch != "" {
break
}
}
}
return bestMatch
}

View File

@ -1,90 +0,0 @@
package cmd
import (
"strings"
"github.com/spf13/cobra"
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
)
// expandAlias rewrites args when the first positional token names a saved alias.
// Built-in commands always take precedence, so an alias can never shadow a real
// command; a colliding alias simply never expands.
func expandAlias(root *cobra.Command, args []string) ([]string, bool) {
if len(args) == 0 {
return args, false
}
name := args[0]
if name == "" || strings.HasPrefix(name, "-") {
return args, false
}
if isBuiltinCommand(root, name) {
return args, false
}
cfg, err := internalConfig.Load()
if err != nil {
return args, false
}
expansion, ok := cfg.Aliases[name]
if !ok {
return args, false
}
parts := splitArgs(expansion)
if len(parts) == 0 {
return args, false
}
return append(parts, args[1:]...), true
}
func isBuiltinCommand(root *cobra.Command, name string) bool {
for _, c := range root.Commands() {
if c.Name() == name {
return true
}
for _, alias := range c.Aliases {
if alias == name {
return true
}
}
}
return false
}
// splitArgs breaks an alias expansion into argv tokens, honoring single and
// double quotes so expansions can carry multi-word flag values.
func splitArgs(s string) []string {
var args []string
var buf strings.Builder
inWord := false
var quote rune
for _, r := range s {
switch {
case quote != 0:
if r == quote {
quote = 0
} else {
buf.WriteRune(r)
}
inWord = true
case r == '\'' || r == '"':
quote = r
inWord = true
case r == ' ' || r == '\t' || r == '\n':
if inWord {
args = append(args, buf.String())
buf.Reset()
inWord = false
}
default:
buf.WriteRune(r)
inWord = true
}
}
if inWord {
args = append(args, buf.String())
}
return args
}

View File

@ -1,68 +0,0 @@
package cmd
import (
"reflect"
"testing"
"github.com/spf13/cobra"
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
)
func TestSplitArgs(t *testing.T) {
cases := []struct {
in string
want []string
}{
{"", nil},
{"pr +view", []string{"pr", "+view"}},
{" issue +list ", []string{"issue", "+list"}},
{`pr +view --title "needs review"`, []string{"pr", "+view", "--title", "needs review"}},
{`repo +create --name 'my repo'`, []string{"repo", "+create", "--name", "my repo"}},
}
for _, tc := range cases {
if got := splitArgs(tc.in); !reflect.DeepEqual(got, tc.want) {
t.Fatalf("splitArgs(%q) = %#v, want %#v", tc.in, got, tc.want)
}
}
}
func TestExpandAlias(t *testing.T) {
t.Setenv("GITLINK_CONFIG_DIR", t.TempDir())
cfg := internalConfig.DefaultConfig()
cfg.Aliases = map[string]string{
"co": "pr +view",
"config": "should never expand",
}
if err := internalConfig.Save(cfg); err != nil {
t.Fatalf("Save error: %v", err)
}
root := &cobra.Command{Use: "gitlink-cli"}
root.AddCommand(&cobra.Command{Use: "config"})
cases := []struct {
name string
args []string
want []string
expand bool
}{
{"alias match", []string{"co", "42"}, []string{"pr", "+view", "42"}, true},
{"builtin wins", []string{"config", "list"}, nil, false},
{"flag first", []string{"--lang", "zh-CN"}, nil, false},
{"unknown token", []string{"nope"}, nil, false},
{"empty args", nil, nil, false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, ok := expandAlias(root, tc.args)
if ok != tc.expand {
t.Fatalf("expandAlias ok = %v, want %v", ok, tc.expand)
}
if tc.expand && !reflect.DeepEqual(got, tc.want) {
t.Fatalf("expandAlias = %#v, want %#v", got, tc.want)
}
})
}
}

View File

@ -7,15 +7,14 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
aliasCmd "github.com/gitlink-org/gitlink-cli/cmd/alias"
apiCmd "github.com/gitlink-org/gitlink-cli/cmd/api" apiCmd "github.com/gitlink-org/gitlink-cli/cmd/api"
browseCmd "github.com/gitlink-org/gitlink-cli/cmd/browse"
statusCmd "github.com/gitlink-org/gitlink-cli/cmd/status"
authCmd "github.com/gitlink-org/gitlink-cli/cmd/auth" authCmd "github.com/gitlink-org/gitlink-cli/cmd/auth"
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil" "github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
configCmd "github.com/gitlink-org/gitlink-cli/cmd/config" configCmd "github.com/gitlink-org/gitlink-cli/cmd/config"
doctorCmd "github.com/gitlink-org/gitlink-cli/cmd/doctor"
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config" internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
"github.com/gitlink-org/gitlink-cli/internal/i18n" "github.com/gitlink-org/gitlink-cli/internal/i18n"
serverCmd "github.com/gitlink-org/gitlink-cli/cmd/server"
"github.com/gitlink-org/gitlink-cli/shortcuts" "github.com/gitlink-org/gitlink-cli/shortcuts"
) )
@ -59,10 +58,9 @@ func NewRootCmd(opts RootOptions, tr *i18n.Translator) (*cobra.Command, error) {
rootCmd.AddCommand(authCmd.NewAuthCmd(tr)) rootCmd.AddCommand(authCmd.NewAuthCmd(tr))
rootCmd.AddCommand(apiCmd.NewAPICmd(tr)) rootCmd.AddCommand(apiCmd.NewAPICmd(tr))
rootCmd.AddCommand(configCmd.NewConfigCmd(tr)) rootCmd.AddCommand(configCmd.NewConfigCmd(tr))
rootCmd.AddCommand(doctorCmd.NewDoctorCmd(tr))
rootCmd.AddCommand(serverCmd.NewServerCmd())
rootCmd.AddCommand(newVersionCmd(version, tr)) rootCmd.AddCommand(newVersionCmd(version, tr))
rootCmd.AddCommand(aliasCmd.NewAliasCmd())
rootCmd.AddCommand(browseCmd.NewBrowseCmd())
rootCmd.AddCommand(statusCmd.NewStatusCmd())
shortcuts.RegisterAll(rootCmd, tr) shortcuts.RegisterAll(rootCmd, tr)

View File

@ -1,68 +0,0 @@
package status
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/gitlink-org/gitlink-cli/internal/auth"
"github.com/gitlink-org/gitlink-cli/internal/config"
"github.com/gitlink-org/gitlink-cli/internal/context"
)
// NewStatusCmd creates the status command that displays login state and context.
func NewStatusCmd() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "显示当前登录状态和上下文信息",
Long: `显示 gitlink-cli 的当前状态包括
- 认证状态是否已登录Token 来源
- API 地址
- 当前目录
- 自动推断的仓库信息`,
Example: ` gitlink-cli status`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, _ := config.Load()
token, _ := auth.LoadToken()
if token == "" {
token = os.Getenv("GITLINK_TOKEN")
}
cwd, _ := os.Getwd()
fmt.Println("GitLink CLI 状态")
fmt.Println("───────────────")
// 认证状态
if token != "" {
fmt.Println(" 认证状态: 已登录")
fmt.Printf(" Token 来源: %s\n", tokenSource(token))
} else {
fmt.Println(" 认证状态: 未登录(运行 gitlink-cli auth login")
}
// API 地址
fmt.Printf(" API 地址: %s\n", cfg.BaseURL)
// 当前目录
fmt.Printf(" 当前目录: %s\n", cwd)
// 推断的仓库
owner, repo, err := context.ResolveOwnerRepo("", "")
if err == nil {
fmt.Printf(" 推断仓库: %s/%s\n", owner, repo)
} else {
fmt.Println(" 推断仓库: (不在 Git 仓库中)")
}
return nil
},
}
}
func tokenSource(token string) string {
if token == os.Getenv("GITLINK_TOKEN") {
return "环境变量 GITLINK_TOKEN"
}
return "keyring / 配置文件"
}

View File

@ -1,58 +0,0 @@
package status
import (
"strings"
"testing"
)
func TestNewStatusCmd(t *testing.T) {
cmd := NewStatusCmd()
if cmd.Use != "status" {
t.Errorf("expected Use 'status', got %s", cmd.Use)
}
if cmd.Short == "" {
t.Error("Short description should not be empty")
}
if cmd.Long == "" {
t.Error("Long description should not be empty")
}
}
func TestNewStatusCmdExample(t *testing.T) {
cmd := NewStatusCmd()
if !strings.Contains(cmd.Example, "status") {
t.Errorf("Example should contain 'status', got: %s", cmd.Example)
}
}
func TestNewStatusCmdHasNoSubcommands(t *testing.T) {
cmd := NewStatusCmd()
if cmd.HasSubCommands() {
t.Error("status should not have subcommands")
}
}
func TestTokenSourceFromEnv(t *testing.T) {
t.Setenv("GITLINK_TOKEN", "test-token-123")
result := tokenSource("test-token-123")
if result != "环境变量 GITLINK_TOKEN" {
t.Errorf("expected env source, got: %s", result)
}
}
func TestTokenSourceFromKeyring(t *testing.T) {
// 不设置环境变量,或用不同的值
t.Setenv("GITLINK_TOKEN", "")
result := tokenSource("some-stored-token")
if result != "keyring / 配置文件" {
t.Errorf("expected keyring source, got: %s", result)
}
}
func TestTokenSourceMismatch(t *testing.T) {
t.Setenv("GITLINK_TOKEN", "env-token")
result := tokenSource("different-token")
if result != "keyring / 配置文件" {
t.Errorf("should fallback to keyring when token differs from env, got: %s", result)
}
}

View File

@ -1,20 +0,0 @@
# 新增命令别名alias
新增 `gitlink-cli alias` 根命令组,用于把常用命令行保存为用户自定义的快捷方式,行为对齐 `gh alias`。别名统一存放在配置文件(`config.yaml`)的 `aliases` 字段,通过 yaml.v3 与其他配置项一起读写,无需额外文件。
## 子命令
- `alias set <name> <expansion>...`:创建或更新别名。`name` 之后的所有参数会拼成展开内容,因此多词展开在 shell 上加不加引号都可以,例如 `alias set bugs issue +list --label bug`
- `alias list`:按名称排序列出全部别名;没有别名时给出提示。
- `alias delete <name>`:删除别名,别名不存在时返回明确错误。
- `alias import`:从 YAML 的 `name: expansion` 映射批量导入,来源可用 `--file` 指定文件,或从标准输入读取(便于管道注入);导入采用合并语义,同名覆盖。
## 别名展开
别名展开已接入根命令分发:在 `cmd.Execute()` 里,若 `os.Args` 的第一个位置参数命中已保存的别名,就把该 token 替换为别名展开(按 shell 风格拆分,识别单双引号)后再交给 cobra 分发。展开发生在翻译器解析之后,因此 `--lang` 等全局标志不受影响。
内置命令始终优先:展开前会先检查该 token 是否为已注册的根命令(或其 cobra 别名),命中则不展开。这样别名无法遮蔽真实命令——与内置命令同名的别名只是永远不会被触发,因此 `set` 不做额外的命名冲突校验。
## 本次变更
`internal/config``Config` 增加 `Aliases map[string]string` 字段并验证读写往返;新增 `cmd/alias` 命令组与 `cmd/expand.go` 展开逻辑,并在 `cmd/root.go` 注册与接线;中英文帮助与提示文案补齐到 `en-US.json``zh-CN.json`。测试覆盖配置往返、set/list/delete、文件与标准输入导入、非法 YAML以及展开的别名命中、内置优先、首个为标志、未知 token 等分支和引号拆分。

View File

@ -1,35 +0,0 @@
# Fix: Windows Git Bash 下 `gitlink-cli api` 路径被 MSYS2 污染导致 404
## 问题
在 Windows Git BashMSYS2环境下执行
```bash
gitlink-cli api GET /v1/owner/repo
```
路径参数 `/v1/owner/repo` 会被 MSYS2 自动改写为类似 `C:/Program Files/Git/v1/owner/repo` 的 Windows 路径——MSYS2 把以 `/` 开头的命令行参数当成 Unix 路径,转换为 Git 安装目录。结果 API 请求路径错误,返回 404。影响所有 Windows Git Bash 用户。
## 根因
MSYS2 的 POSIX→Windows 路径转换会对命令行参数中以 `/` 开头的字符串生效,且无法通过 shell 转义稳定规避(`MSYS_NO_PATHCONV` 等环境变量依赖用户配置,不可靠)。
## 修复
`cmd/api``runAPI` 中,对取到的 `path` 调用 `restoreAPIPath` 还原:
- 检测首部是否为盘符(正则 `^[A-Za-z]:/`
- 若是,按常见 API 前缀(`/v1/` `/v2/` `/api/` `/users/` `/projects/`)在污染后的路径里定位原始起点并截取;
- 无盘符或无匹配前缀时原样返回,不影响其他平台与正常路径。
`restoreAPIPath` 为纯函数,便于单元测试。
## 影响
仅 Windows 受益,其他平台行为不变。改动集中在 `cmd/api/api.go`(约 +25 行,含函数与注释)。
## Tests
```bash
go test ./cmd/api/... -run TestRestoreAPIPath -v
```

View File

@ -1,7 +0,0 @@
# 新增 `api --paginate` 自动翻页
`gitlink-cli api GET <PATH> --paginate` 对齐 `gh api --paginate`:自动逐页抓取并把所有条目拼接成一个数组输出,免去手动传 `page`/`limit` 逐页拉取。仅支持 GET其它方法会明确报错而不是静默退化。
配套修复了 `PaginateAll` 无法解包真实 GitLink 列表响应的问题。此前它只认顶层裸数组或 `data` 键下的数组,而 GitLink 列表接口把数组包在资源专属键下(`{"total_count":N,"pulls":[...]}`、`{"issues":[...]}`、`{"branches":[...]}` 等),这类响应会被当成单个对象直接返回、根本不翻页。现在解析顺序为:优先取 `data` 数组;否则取 map 中唯一的数组字段(覆盖 pulls/issues/branches/labels 等);无数组字段或存在多个数组字段(歧义)时,保留“单对象作为单元素返回”的旧行为。短页终止(本页条目数小于 limit 即停止)与既有的裸数组、`data` 包裹用例保持不变。
本次变更包含 `PaginateAll` 解包逻辑修复、`--paginate` 标志与 `runAPIPaginate` 路由、中英文帮助文案以及单元测试client 层验证 `{total_count, issues:[...]}` 两页拼接并正确解包 `issues`cmd 层端到端验证 `--paginate` 合并多页输出与非 GET 报错。

View File

@ -1,38 +0,0 @@
# 修复 `api` 命令单次调用不替换 `:owner/:repo` 占位符
## 背景
`gitlink-cli api <METHOD> <PATH>` 单次调用此前直接把 `<PATH>` 原样发送给服务端,**不会替换 REST 风格的 `:owner` / `:repo` 占位符**。这导致:
- 该命令自身的帮助 `Example`(如 `api POST /:owner/:repo/issues`)跑不通;
- 依赖 `:owner/:repo` 写法的 Skill / 文档(如 `api GET /:owner/:repo/commits`)报错;
- 占位符替换能力此前只存在于 `--batch-file` 批处理模式的 `{{var}}` 模板中,单次调用无法复用。
对应 Issue`bug: api 命令单次调用不替换 :owner/:repo 占位符0.2.0`。
## 变更
`api` 单次调用现在按以下顺序处理路径:
1. **`{{var}}` 模板渲染**:若提供了 `--var key=value`,复用与批处理模式相同的模板引擎渲染 `<PATH>` 中的 `{{key}}`,缺失变量时报错。
2. **`:owner` / `:repo` 占位符替换**:当路径包含 `:owner` / `:repo` 时,使用与所有 shortcut 一致的解析逻辑 `context.ResolveOwnerRepo(--owner, --repo → git remote origin)` 解析仓库归属并替换;无法解析时给出明确错误提示。
3. 保持原有的「缺失前导 `/` 自动补全」行为。
不含占位符、且未传 `--var` 的调用(如 `api GET /users/me`)行为完全不变。
## 示例
```bash
# 自动从当前 git 仓库或 --owner/--repo 解析
gitlink-cli api GET /:owner/:repo/commits --owner Gitlink --repo gitlink-cli
# 单次调用也支持 {{var}} 模板
gitlink-cli api GET /v1/{{owner}}/gitlink-cli/issues --var owner=Gitlink
```
## 实现与测试
- 改动集中在 `cmd/api/api.go`:新增 `resolveAPIPath` 辅助函数与 `:owner` / `:repo` 占位符正则(`\b` 边界避免误伤 `:owner_id` 等更长 token`ReplaceAllLiteralString` 避免 `$` 被当作正则替换引用)。
- 复用既有 `parseBatchVars` / `renderTemplate``cmd/api/batch.go`)与 `internal/context.ResolveOwnerRepo`,无新增依赖。
- 更新命令 `Example` 帮助文案。
- 新增单元测试:`:owner/:repo` 解析替换、单次调用 `{{var}}` 渲染、缺失模板变量报错;既有测试全部通过。

View File

@ -1,19 +0,0 @@
# api 单次调用模板变量与预演能力
这次改动把 `gitlink-cli api` 的单次调用模式和 batch 模式拉齐了。
- 单次调用现在支持 `--var key=value`,可以在路径、查询参数和 JSON 请求体里复用 `{{var}}` 模板变量。
- 路径里的 `:owner``:repo` 会自动使用当前 `--owner` / `--repo` 或 git remote 上下文渲染,修复了单次调用不替换占位符的问题。
- `--dry-run` 不再只属于 batch 模式,单次调用也可以先预览渲染后的 method、path、query、body 和 variables再决定是否真正发请求。
这样做的目的不是单纯补一个 bug而是让 Raw API 更适合脚本和 Agent 复用:同一份模板写法既能用在 `api --batch-file`,也能平滑退化成一次性的单条请求。
本地验证:
```bash
go test ./cmd/api
go test ./...
go build ./...
git diff --check
go run . api --help
```

View File

@ -1,23 +0,0 @@
# Attachment Shortcut
## Summary
This change adds a new `attachment` shortcut group to `gitlink-cli` so users can upload and delete standalone attachments without dropping down to raw API calls.
## Commands
```bash
gitlink-cli attachment +upload -f ./build.log -d "CI build log"
gitlink-cli attachment +upload -f ./release-notes.md --container-id 42 --container-type VersionRelease
gitlink-cli attachment +delete -i 791eccbf-2e35-4301-ad95-8c937a117f40
```
## API Coverage
- `POST /api/attachments.json`
- `DELETE /api/attachments/{uuid}.json`
## Notes
- Upload uses multipart form data and works with the same `GITLINK_TOKEN` access token flow already used by the CLI.
- Delete accepts the attachment UUID returned by the upload API.

View File

@ -1,28 +0,0 @@
# Branch Lifecycle Shortcuts
This change expands branch management coverage so repository maintainers can complete more of the branch lifecycle from `gitlink-cli` without falling back to manual API calls.
## Commands
- `branch +all`
- `branch +set-default`
- `branch +restore`
## Improvements
- `branch +list` now supports `--state` so users can inspect visible branches, deleted branches, or all branch records.
- `branch +list` now supports `--keyword` to filter branches by name on the server side.
- The README examples document the deleted-branch recovery flow so users can retrieve `branch_id` and restore the branch in one CLI workflow.
## API Mapping
| Shortcut | Method | API path |
|----------|--------|----------|
| `branch +all` | GET | `/api/v1/{owner}/{repo}/branches/all.json` |
| `branch +set-default` | PATCH | `/api/v1/{owner}/{repo}/branches/update_default_branch.json?name=...` |
| `branch +restore` | POST | `/api/v1/{owner}/{repo}/branches/restore.json` |
## Verification
- Unit tests cover request methods, paths, query parameters, restore payloads, invalid `branch-id` validation, and HTTP error handling.
- Documentation now includes branch filtering, default-branch switching, and deleted-branch restore examples.

View File

@ -1,40 +0,0 @@
# Branch OpenAPI Shortcuts
补齐 GitLink 分支 OpenAPI 的生命周期操作,并增强现有 branch shortcut 的安全性和参数能力。
## 新增 / 增强命令
- `branch +list`:新增 `--keyword``--state all|deleted`,对齐 OpenAPI 查询参数。
- `branch +all`:调用无分页分支列表接口。
- `branch +create`:新增 `--dry-run`,预览创建分支请求。
- `branch +delete`:切换到 OpenAPI 文档中的 `DELETE /api/v1/{owner}/{repo}/branches/{branch}.json`,并新增 `--dry-run`
- `branch +set-default`:设置仓库默认分支,支持 `--dry-run`
- `branch +restore`:恢复已删除分支,支持 `--dry-run`
## OpenAPI 对齐
- `GET /api/v1/{owner}/{repo}/branches.json`
- `POST /api/v1/{owner}/{repo}/branches.json`
- `GET /api/v1/{owner}/{repo}/branches/all.json`
- `DELETE /api/v1/{owner}/{repo}/branches/{branch}.json`
- `PATCH /api/v1/{owner}/{repo}/branches/update_default_branch.json`
- `POST /api/v1/{owner}/{repo}/branches/restore.json`
## 安全设计
- `branch +create`、`branch +delete`、`branch +set-default`、`branch +restore` 都支持 `--dry-run`
- `branch +delete` 会对包含 `/` 的分支名进行路径转义,避免把 `feature/foo` 误解析为多级路径。
- `branch +restore` 校验 `--branch-id` 必须为正整数。
- `branch +list --state` 仅允许 `all``deleted`,避免无效状态参数。
## 测试
新增单元测试覆盖:
- list 查询参数。
- all endpoint。
- create payload 与 dry-run。
- delete v1 endpoint 与路径转义。
- set-default query 参数。
- restore payload。
- 无效 state / branch-id 不触发 API。

View File

@ -1,20 +0,0 @@
# Browse Shortcut
新增 `browse` Shortcut 组,在浏览器中打开仓库的各类页面,对标 `gh browse`
- `browse +repo`
- `browse +issue --number <n>`
- `browse +pr --number <n>`
- `browse +commit --sha <sha>`
- `browse +branch [--name <branch>]`
- `browse +file --path <path> [--ref <branch>]`
- `browse +releases`
- `browse +wiki`
说明:
- Web 地址由配置的 `base_url` 推导(去掉 `/api` 后缀),因此自建实例同样适用。
- `-n/--no-browser` 只打印地址而不打开浏览器,便于脚本取用与 CI 环境。
- 浏览器启动优先使用 `$BROWSER`否则回退到平台默认Windows/macOS/Linux
同时补充了 `internal/browser` 跨平台启动器与 URL 构造的单元测试。

View File

@ -1,14 +0,0 @@
# Capability shortcut
新增 `capability` 命令组 + `internal/capability` 包,提供 GitLink 后端 API 能力探测:
- `capability +check` — 向后端发送探测请求,检查各命令模块依赖的 API 是否就绪,结果缓存 24 小时
- `capability +list` — 查看已缓存的能力探测结果
实现要点:
- 新增 `internal/capability` 包:`Registry` 记录各域label/notification/pm/wiki/pipeline/webhook/member/milestone/export/search/workflow 等的可用状态Available/Unavailable/Error/Unknown带 24 小时缓存(`~/.config/gitlink-cli/capabilities.json`)。
- 探测复用 `internal/client`,对需要 owner/repo 上下文的域自动从 git remote 推断或 `--owner/--repo` 指定。
- `+check` 输出表格(模块 / 状态 / 说明);`+list` 直接读缓存不发请求,过期会提示。
背景:不同 GitLink 实例后端能力不一致,命令调用前无法预知某模块是否可用。`capability` 组让用户/Agent 在调用前自检,提升跨实例兼容性与错误可诊断性。含完整单元测试(状态判定、探测 200/401/403/404、HTML 响应、repo 上下文等)。

View File

@ -1,9 +0,0 @@
# Catalog Shortcuts
Adds a `catalog` shortcut group for GitLink platform template lookups:
- `catalog +licenses` lists repository license templates.
- `catalog +ignores` lists repository `.gitignore` templates.
- Both commands support `--name` filtering and return the original API response in the configured output format.
This closes a small but useful OpenAPI coverage gap for repository bootstrap workflows. Agents and scripts can now discover valid license and ignore template names before creating repositories, without falling back to raw API paths.

View File

@ -1,63 +0,0 @@
# CI control shortcuts
## Background
The CI shortcut group already supported build listing, log inspection, restart,
and stop operations. Repository-level CI activation, deactivation, and
authorization checks were still documented as Raw API calls in `gitlink-ci`.
This change adds first-class CI control shortcuts.
## New shortcuts
- `ci +activate` activates CI for a repository.
- `ci +deactivate` deactivates CI for a repository.
- `ci +authorize` shows CI authorization state for a repository.
## Safety model
`ci +authorize` is read-only and can run directly:
```bash
gitlink-cli ci +authorize --owner Gitlink --repo forgeplus
```
`ci +activate` and `ci +deactivate` change repository CI state, so they require
an explicit confirmation flag and support dry-run previews:
```bash
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --dry-run
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --yes
```
```bash
gitlink-cli ci +deactivate --owner Gitlink --repo forgeplus --dry-run
gitlink-cli ci +deactivate --owner Gitlink --repo forgeplus --yes
```
## Documentation updates
- README and README.zh-CN include CI control examples.
- `skills/gitlink-ci` now prefers `ci +activate`, `ci +deactivate`, and
`ci +authorize` instead of Raw API calls.
## Tests
Unit tests cover:
- endpoint method/path mapping for activate, deactivate, and authorize;
- dry-run behavior for state-changing commands;
- `--yes` confirmation guards;
- HTTP error propagation.
Suggested verification:
```bash
go test ./shortcuts/ci ./shortcuts
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,86 +0,0 @@
# Code history and batch file shortcuts
## Background
Agent workflows such as PR review, commit quality checks, repository health
reports, and research reproducibility audits need commit timelines, changed
files, commit diffs, and sometimes controlled multi-file updates. Before this
change, several of these operations required Raw API calls.
This change adds a larger Subtask 1 feature set around repository code history
and file operations.
## New shortcuts
Repository shortcuts:
- `repo +files` searches repository files with optional `--search` and `--ref`.
- `repo +commits` lists commits for a branch, tag, or commit ref with pagination.
- `repo +commit-files` lists files changed by a commit, with optional file-path filtering.
- `repo +commit-diff` returns a commit diff.
- `repo +tags` lists repository tags with pagination and optional name filtering.
- `repo +tag` returns one tag's metadata and target commit.
- `repo +delete-tag` deletes a repository tag after explicit confirmation.
- `repo +batch-commit` creates, updates, or deletes multiple files in one commit.
Pull request shortcuts:
- `pr +commits` lists commits included in a pull request.
## Safety model
All history and file inspection commands are read-only.
`repo +delete-tag` and `repo +batch-commit` can modify repository content, so
they require an explicit confirmation flag for remote writes:
```bash
gitlink-cli repo +delete-tag --owner me --repo proj --name v0.1.0 --dry-run
gitlink-cli repo +delete-tag --owner me --repo proj --name v0.1.0 --yes
```
```bash
gitlink-cli repo +batch-commit --owner me --repo proj \
--branch master --message "docs: update" \
--files 'update:README.md:# Updated' \
--dry-run
gitlink-cli repo +batch-commit --owner me --repo proj \
--branch master --message "docs: update" \
--files 'update:README.md:# Updated' \
--yes
```
The `--files` format is:
```text
action:path[:content][;action:path[:content]...]
```
Supported actions are `create`, `update`, and `delete`. `create` and `update`
require content; `delete` does not.
## Tests
Unit tests cover:
- repository file search query mapping;
- commit list pagination and ref mapping;
- commit changed-file and diff endpoints;
- repository tag list/detail/delete endpoint mapping;
- PR commit list endpoint;
- `repo +batch-commit` dry-run behavior;
- `repo +batch-commit` remote write protection without `--yes`;
- batch file operation payload construction and validation.
Suggested verification:
```bash
go test ./shortcuts/repo ./shortcuts/pr
```
Full project verification:
```bash
make test
```

View File

@ -1,18 +0,0 @@
# Commit Inspect Shortcuts
Added `gitlink-cli commit` for read-only commit inspection.
| Command | Endpoint |
| --- | --- |
| `commit +list` | `GET /v1/{owner}/{repo}/commits` |
| `commit +files` | `GET /v1/{owner}/{repo}/commits/{sha}/files` |
| `commit +diff` | `GET /v1/{owner}/{repo}/commits/{sha}/diff` |
| `commit +blame` | `GET /v1/{owner}/{repo}/blame` |
Validation:
```bash
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/commit ./shortcuts
go vet ./shortcuts/commit ./shortcuts
go run . commit --help
```

View File

@ -1,44 +0,0 @@
# Commit shortcut
新增 `commit` Shortcut 组,封装 GitLink 仓库提交Commit相关 OpenAPI 的常用操作,支持查看提交列表、单条提交变更文件、提交 Diff 与文件 Blame
- `commit +list`
- `commit +view`
- `commit +diff`
- `commit +blame`
实现要点:
- `+list` 映射 `GET /v1/{owner}/{repo}/commits`,支持 `--sha`(分支 / 标签 / 提交 SHA 过滤)、`--page`(默认 `1`)、`--limit`(默认 `20`),通过查询参数传给 API。
- `+view` 映射 `GET /v1/{owner}/{repo}/commits/{sha}/files`,返回某次提交涉及的文件清单;`--sha` 为必填,同时支持分页参数。
- `+diff` 映射 `GET /v1/{owner}/{repo}/commits/{sha}/diff`,返回指定提交的 Diff 内容;`--sha` 为必填。
- `+blame` 映射 `GET /v1/{owner}/{repo}/blame`,按文件展示逐行归属;`--path` 为必填,`--sha` 缺省为 `master`,二者以 `filepath` / `sha` 查询参数提交。
- 四个命令均通过 `ResolveOwnerRepo()` 解析 `--owner` / `--repo`(支持 `-R owner/repo` 缩写与仓库默认推断),结果统一经 `ctx.Output(env)` 输出,兼容 `--format`table / json等全局参数。
- 路径沿用 `/v1/` 前缀约定,与 webhook / milestone / label 等组保持一致;`.json` 后缀由底层 client 自动补全。
## Examples
```bash
# 列出 develop 分支最近 10 条提交
gitlink-cli commit +list --owner Gitlink --repo forgeplus --sha develop --limit 10
# 查看某次提交涉及的文件
gitlink-cli commit +view --owner Gitlink --repo forgeplus --sha abc123def
# 查看提交 Diff
gitlink-cli commit +diff --owner Gitlink --repo forgeplus --sha abc123def
# 查看 README.md 在 master 分支的 Blame 信息
gitlink-cli commit +blame --owner Gitlink --repo forgeplus --path README.md
# 使用 -R 缩写并以 JSON 输出
gitlink-cli commit +list -R Gitlink/forgeplus --page 2 --format json
```
## Tests
```bash
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/commit/...
go test ./...
go run . commit +list --help
```

View File

@ -1,27 +0,0 @@
# Compare Summary Shortcuts
## Summary
Adds higher-level compare shortcuts so users and AI agents can inspect commit lists and summarize branch differences without manually stitching together raw compare responses.
## Commands
| Command | Purpose |
|---------|---------|
| `gitlink-cli compare +commits` | List commits between two refs with optional author, keyword, limit, and reverse filters. |
| `gitlink-cli compare +summary` | Summarize compare metadata, commit sample, changed file totals, file status counts, top files, path groups, and extension groups. |
## Behavior
- Reuse the existing compare endpoint so branch, tag, and commit refs keep the same URL-safe encoding behavior.
- Normalize commit output into stable fields such as `subject`, `author_login`, and `committer_login`.
- Aggregate compare file data into top changed files, directory groups, extension groups, and created / modified / deleted / renamed counts.
- Mark truncated summaries when `--max-files` analyzes only part of a large compare result.
- Validate `compare +files`, `compare +commits`, and `compare +summary` numeric flags before sending API requests.
## Tests
- `go test ./shortcuts/compare/...`
- `go build ./...`
- `go test ./...`
- `go run . compare +summary --owner Gitlink --repo gitlink-cli --head Mengz:mengz/compare-summary-shortcuts --base master --format json`

View File

@ -1,15 +0,0 @@
# Export shortcut
新增 `export` Shortcut 组,将仓库数据导出为 CSV / JSON 文件,支撑离线分析、科研数据抽取与外部报表:
- `export +issues` — 导出 Issue 列表GET `/v1/:owner/:repo/issues`
- `export +prs` — 导出 PR 列表GET `/v1/:owner/:repo/pulls`
- `export +contributors` — 导出贡献者统计GET `/:owner/:repo/contributors`
实现要点:
- 统一 `--format csv|json`(默认 csv`--output` 输出路径;`issues`/`prs` 额外支持 `--state open|closed|all` 过滤与 `--page/--limit` 分页。
- CSV 表头固定(`id,title,state,created_at` 等),便于直接导入 Excel / pandasJSON 保留原始字段,供 `workflow` 模块与科研 Skill 二次处理。
- 导出过程只读、分页拉取全量,避免一次性请求超限。
背景:此前要做仓库数据导出只能手工拼 Raw API 并自行解析分页。`export` 组将其提升为一等命令是子任务四科研场景贡献排行、Issue 趋势、PR 效率)的数据入口,并与 `gitlink-contributor-insight`、`gitlink-research-tracker` 等 Skill 衔接。关联 PR #15

View File

@ -1,28 +0,0 @@
# PR View Merged Timestamp
## Summary
`pr +view` now surfaces the merge timestamp at the top level of its output.
The non-v1 detail endpoint `/{owner}/{repo}/pulls/{id}` nests the merge time under
`pull_request.merged_at` (an ISO-8601 string such as `2026-07-05T12:52:05+08:00`),
but the CLI previously only lifted `closed_at`. Merged PRs therefore showed no merge
time, mirroring upstream issue #14.
The `closed_at` enrichment is renamed to `enrichPullRequestTimestamps` and extended so
that, when `pull_request.merged_at` is present, it is copied to `merged_at` at the top
level (and the boolean `merged`, when present, is surfaced alongside it). This matches
`gh pr view`, which exposes `mergedAt`. The existing `closed_at` behavior is unchanged.
## Example
```bash
gitlink-cli pr +view --owner Gitlink --repo forgeplus --id 42
```
```json
{
"merged_at": "2026-07-05T12:52:05+08:00",
"merged": true
}
```

View File

@ -1,26 +0,0 @@
# Feedback shortcut
This change adds a dedicated `feedback` shortcut group for submitting GitLink platform feedback from the CLI.
New command:
- `feedback +create`
The command wraps `POST /api/v1/{owner}/feedbacks.json` and improves the CLI experience around the narrow API payload:
- Resolves the current authenticated user with `GET /users/me` when `--user` is omitted.
- Accepts feedback text from `--content`, `--from`, and `--stdin`, combining multiple sources with blank lines.
- Adds optional metadata lines for `--category`, `--contact`, and `--repo-ref` before the body.
- Supports `--dry-run` to preview method, path, payload, and content length without submitting.
- Rejects empty feedback before making any API request.
Documentation was added to README, README.zh-CN, and `skills/gitlink-feedback/SKILL.md`.
Verification:
- `go test ./shortcuts/feedback`
- `go test ./shortcuts`
- `go test ./...`
- `go build ./...`
- `git diff --check`
- UTF-8 mojibake scan on touched files

View File

@ -1,33 +0,0 @@
# File shortcut
新增 `file` Shortcut 组,补齐 GitLink 仓库文件与目录内容操作的常用封装:
- `file +list` 列出仓库文件(`--ref` 指定分支/标签/commit`--search` 关键词过滤)
- `file +tree` 列出文件树(`--sha` 默认 master`--recursive` 递归,支持分页)
- `file +get` 获取文件或目录内容(`--path` 必填,`--ref` 默认 master
- `file +create` 创建文件(`--path`/`--content`/`--message` 必填content 自动 Base64 编码)
- `file +delete` 删除文件(`--path`/`--sha`/`--message` 必填SHA 取自 `file +list`
实现要点:
- `+tree``/v1/{owner}/{repo}/git/trees/{sha}`,与 git 树对象语义一致,支持 `--recursive` 与分页。
- `+get` / `+list``/sub_entries`、`/files` 等接口读取文件或目录内容。
- `+create` 调用 `/create_file`,文件内容 Base64 编码后提交;`+delete` 调用 `/delete_file`,需先从 `file +list` 取得文件 blob SHA。
- 路径统一使用 `/v1/{owner}/{repo}/` 前缀,与现有 Shortcut 组保持一致。
补充单元测试 `shortcuts/file/file_test.go`,覆盖各命令的参数解析与路径构造。
## Examples
```bash
gitlink-cli file +list --owner Gitlink --repo gitlink-cli
gitlink-cli file +tree --owner Gitlink --repo gitlink-cli --recursive
gitlink-cli file +get --owner Gitlink --repo gitlink-cli --path README.md
gitlink-cli file +create --owner Gitlink --repo gitlink-cli --path docs/note.md --content "hello" --message "add note"
```
## Tests
```bash
go test ./shortcuts/file/...
```

View File

@ -1,64 +0,0 @@
# File Content Shortcuts
## Summary
Adds a `file` shortcut group so users and AI agents can read, search, and write
repository file contents without cloning or falling back to Raw API calls.
Directory listing and README viewing remain covered by `repo +tree` and
`repo +readme`.
## Commands
| Command | Purpose |
|---------|---------|
| `gitlink-cli file +view` | View a file's contents; `--raw` prints only the decoded content |
| `gitlink-cli file +search` | Search repository files by name |
| `gitlink-cli file +create` | Create a file and commit it to a branch |
| `gitlink-cli file +update` | Update a file and commit it to a branch |
| `gitlink-cli file +delete` | Delete a file and commit the removal to a branch |
## Validation
- `file +view` accepts `--ref` (branch, tag, or commit SHA) and `--raw`; `--raw`
fails with a clear error when the path is a directory.
- Write commands require `--path` and `--branch`; `--message` defaults to
`<action> <path>` when omitted.
- `file +create` / `file +update` accept exactly one of `--content` or
`--content-file`; providing both or neither is rejected before any request.
- `--new-branch` commits the change to a new branch created from `--branch`.
- File content is transported with `text` encoding (verified against production
gitlink.org.cn; the documented `base64` encoding is rejected there).
## Tests
Unit tests cover endpoint paths, query parameter mapping, request payload
construction, content-source validation, default commit messages, `--new-branch`
propagation, and raw content extraction from entries/README-shaped responses.
## 中文说明
### 变更内容
- 新增 `file` 快捷命令组:`+view`(查看文件内容,`--raw` 仅输出解码后的正文)、
`+search`(按文件名搜索)、`+create` / `+update` / `+delete`(通过
contents/batch API 直接提交文件增删改)。
- 无需克隆仓库即可读写文件,适合 AI Agent 读取 README、修改单个文件等场景
(响应社区 issueAPI 是否支持自动读取仓库内文件)。
- 内容支持 `--content` 内联或 `--content-file` 从本地文件读取text 编码,
已在生产环境验证,文档中的 base64 编码在生产环境会被拒绝);支持
`--new-branch` 提交到新分支。
- 更新 README 与 README.zh-CN 的功能表和使用示例。
### 国际化
命令与全部 flag 文案已接入 i18n`cmd.file.*` / `flag.file.*`,含 en-US 与
zh-CN 两套 locale`GITLINK_LANG=zh-CN` 下 `file --help` 输出中文帮助。
### 验证
- `go test ./...`
- `go vet ./...`
- `go run . file --help`
- `go run . file +view --help`
- 在生产 gitlink.org.cn 真实仓库验证 `+view --raw`、`+search`、`+create`、
`+update`、`+delete` 全链路

View File

@ -1,6 +0,0 @@
# Ignore group i18n key
`register.go``tr.T("cmd.ignore.short")` 设置 `ignore` 组描述,但该键在两个语言包中都缺失,导致 `--help` 里直接显示字面量 `cmd.ignore.short`
- 补齐 `en-US.json` / `zh-CN.json``cmd.ignore.short`
- `TestRegisterAllGroupDescriptions` 增加断言:组描述不得是未解析的 i18n 键

View File

@ -1,21 +0,0 @@
# Ignore Template Shortcuts
## Summary
Added `gitlink-cli ignore +list` to expose GitLink's `.gitignore` template catalog from the CLI. This fills the OpenAPI wrapper gap for `GET /ignores` and helps repository bootstrap workflows pick a valid ignore template before creating a project.
## Commands
| Command | Method / Endpoint | Purpose |
| --- | --- | --- |
| `ignore +list` | `GET /ignores` | List all built-in `.gitignore` templates |
| `ignore +list --name Go` | `GET /ignores?name=Go` | Filter templates by name |
## Validation
```bash
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/ignore ./shortcuts
go vet ./shortcuts/ignore ./shortcuts
go run . ignore --help
go run . ignore +list --help
```

View File

@ -1,186 +0,0 @@
# Issue batch operations enhancement
## Summary
Add new Issue batch operation shortcuts to enhance issue management capabilities:
- `issue +batch-reopen` — Batch reopen closed issues by web URL issue numbers.
- `issue +batch-label` — Batch add/remove labels from issues by API issue IDs.
- `issue +batch-assign` — Batch assign/unassign users from issues by API issue IDs.
- `issue +batch-comment` — Batch add comments to issues by web URL issue numbers.
- `issue +batch-export` — Export issues to CSV or JSON format with optional filters.
- `issue +batch-import` — Create issues from CSV file.
These commands complement the existing `issue +batch-close`, `issue +batch-update`, and `issue +batch-delete` commands.
## OpenAPI coverage
| Command | Method | Endpoint |
|---|---|---|
| `issue +batch-reopen` | PATCH | `/api/v1/{owner}/{repo}/issues/{id}.json` |
| `issue +batch-label` | GET + PATCH | `/api/v1/{owner}/{repo}/issues/{id}.json` + `/api/v1/{owner}/{repo}/issues/batch_update.json` |
| `issue +batch-assign` | GET + PATCH | `/api/v1/{owner}/{repo}/issues/{id}.json` + `/api/v1/{owner}/{repo}/issues/batch_update.json` |
| `issue +batch-comment` | POST | `/api/v1/{owner}/{repo}/issues/{number}/journals.json` |
| `issue +batch-export` | GET | `/api/v1/{owner}/{repo}/issues.json` |
| `issue +batch-import` | POST | `/api/v1/{owner}/{repo}/issues.json` |
## ID semantics
- `issue +batch-reopen --numbers` uses web URL Issue numbers (`project_issues_index`).
- `issue +batch-comment --numbers` uses web URL Issue numbers (`project_issues_index`).
- `issue +batch-label --ids` uses API Issue IDs returned by Issue APIs.
- `issue +batch-assign --ids` uses API Issue IDs returned by Issue APIs.
The docs and help text explicitly call this out to avoid mixing the two ID types.
## Safety and usability
- All commands support `--dry-run` for preview.
- `issue +batch-label` and `issue +batch-assign` preserve existing labels/assigners and only add/remove specified ones.
- `issue +batch-export` supports filtering by status, assigner, milestone, keyword, and more.
- `issue +batch-import` requires a CSV file with `subject` column (required) and optional columns (`description`, `priority_id`, etc.).
- ID lists are validated as positive integers and de-duplicated.
## Examples
### Batch reopen issues
```bash
gitlink-cli issue +batch-reopen \
--owner Gitlink \
--repo forgeplus \
--numbers 42,43,44 \
--dry-run
gitlink-cli issue +batch-reopen \
--owner Gitlink \
--repo forgeplus \
--numbers 42,43,44
```
### Batch add/remove labels
```bash
# Add labels to issues
gitlink-cli issue +batch-label \
--owner Gitlink \
--repo forgeplus \
--ids 101,102,103 \
--add 1,2 \
--dry-run
# Remove labels from issues
gitlink-cli issue +batch-label \
--owner Gitlink \
--repo forgeplus \
--ids 101,102,103 \
--remove 3,4
# Add and remove labels in one command
gitlink-cli issue +batch-label \
--owner Gitlink \
--repo forgeplus \
--ids 101,102,103 \
--add 1,2 \
--remove 3,4
```
### Batch assign/unassign users
```bash
# Assign users to issues
gitlink-cli issue +batch-assign \
--owner Gitlink \
--repo forgeplus \
--ids 101,102,103 \
--add 5,6 \
--dry-run
# Unassign users from issues
gitlink-cli issue +batch-assign \
--owner Gitlink \
--repo forgeplus \
--ids 101,102,103 \
--remove 5,6
```
### Batch add comments
```bash
gitlink-cli issue +batch-comment \
--owner Gitlink \
--repo forgeplus \
--numbers 42,43,44 \
--message "This issue has been resolved in v2.0.0" \
--dry-run
gitlink-cli issue +batch-comment \
--owner Gitlink \
--repo forgeplus \
--numbers 42,43,44 \
--message "Closing as duplicate of #100"
```
### Export issues
```bash
# Export to CSV (default)
gitlink-cli issue +batch-export \
--owner Gitlink \
--repo forgeplus \
--output issues.csv
# Export to JSON
gitlink-cli issue +batch-export \
--owner Gitlink \
--repo forgeplus \
--format json \
--output issues.json
# Export with filters
gitlink-cli issue +batch-export \
--owner Gitlink \
--repo forgeplus \
--status-id 5 \
--assigner-id 10 \
--keyword "bug" \
--output closed_bugs.csv
```
### Import issues from CSV
```bash
# Create issues from CSV file
gitlink-cli issue +batch-import \
--owner Gitlink \
--repo forgeplus \
--file issues.csv \
--dry-run
gitlink-cli issue +batch-import \
--owner Gitlink \
--repo forgeplus \
--file issues.csv
```
CSV file format:
```csv
subject,description,priority_id
"Fix login bug","Users cannot login with special characters",1
"Add dark mode","Implement dark mode for the UI",2
"Update documentation","Add API reference for new endpoints",3
```
## Tests
```bash
GOPROXY=https://goproxy.cn,direct go test -v -run "TestBatch" ./shortcuts/issue/...
go vet ./...
go run . issue +batch-reopen --help
go run . issue +batch-label --help
go run . issue +batch-assign --help
go run . issue +batch-comment --help
go run . issue +batch-export --help
go run . issue +batch-import --help
```

View File

@ -1,30 +0,0 @@
# issue 批量运维能力增强
本次变更把 Issue 的批量运维能力从“只能批量关闭”扩展为更完整的日常工作流:
- 新增 `issue +batch-comment`,支持按 `--numbers``--from issues.csv` 给多个 Issue 统一追加评论。
- 新增 `issue +batch-update`,支持批量更新状态、优先级、标签、负责人、关联分支、开始日期和截止日期。
- `issue +create`、`issue +update`、`issue +comment` 现在支持 `--body-file`,适合读取 Markdown 文件中的长文本。
设计上延续了现有 `issue +batch-close` 的安全思路:
- 批量命令统一支持 `--dry-run`
- `--body``--body-file` 互斥;
- 批量更新会先读取当前 Issue再保留已有标题、描述和元数据避免误清空字段
- 输出统一包含逐条结果汇总,便于 Agent 或脚本继续处理。
相关文档已同步更新:
- `README.md`
- `skills/gitlink-issue/SKILL.md`
本地验证:
```bash
go test ./shortcuts/issue/...
go test ./shortcuts/...
go build ./...
git diff --check
go run . issue +batch-comment --help
go run . issue +batch-update --help
```

View File

@ -1,73 +0,0 @@
# Issue 批量重开 / 批量评论 shortcuts
## Summary
`issue` shortcut 组新增两条批量操作命令,补齐 Issue 生命周期批量运维能力:
- `issue +batch-reopen` — 按网页 Issue 编号或 CSV 文件批量重新打开已关闭的 Issue。
- `issue +batch-comment` — 按网页 Issue 编号或 CSV 文件对多个 Issue 批量追加同一段评论。
两条命令复用了 `issue +batch-close` 的基础设施(`collectIssueNumbers`、`--numbers` / `--from` / `--dry-run`、`batchCloseSummary` 汇总结构),仅替换最后的写操作,保持与批量关闭一致的使用体验。
## 命令清单
- issue +batch-reopen
- issue +batch-comment
## OpenAPI coverage
| Command | Method | Endpoint |
|---|---|---|
| `issue +batch-reopen` | PATCH | `/api/v1/{owner}/{repo}/issues/{number}.json` |
| `issue +batch-comment` | POST | `/api/v1/{owner}/{repo}/issues/{number}/journals.json` |
## 实现要点
- 输入与 `batch-close` 一致:`--numbers/-n` 接逗号分隔的网页 Issue 编号,`--from` 读 CSV识别 `number` / `issue_number` / `project_issues_index` 列或无表头首列),二者可叠加并自动去重;编号统一校验为正整数。
- `batch-reopen`:先 `GET` 取回 Issue 当前的 `subject` / `description`,再 `PATCH /v1/{owner}/{repo}/issues/{number}` 回传原内容并把 `status_id` 设为打开状态常量 `openIssueStatusID = 1`,避免重开时丢失标题与描述。
- `batch-comment``--body/-b` 为必填,对应 journal 的 `notes` 字段,逐条 `POST /v1/{owner}/{repo}/issues/{number}/journals`
- 两条命令均支持 `--dry-run`:不发起写请求,逐条返回 `planned` 计划态,便于预览影响范围。
- 结果以 `batchCloseSummary``repository` / `dry_run` / `total` / `succeeded` / `failed` / `results`)输出,逐条记录 `action` / `status` / `error`;存在失败时以非零错误码退出并报失败计数。
## Examples
```bash
# 批量重开指定编号的 Issue
gitlink-cli issue +batch-reopen \
--owner Gitlink \
--repo forgeplus \
--numbers 1,2,3
# 先预览,不实际改动
gitlink-cli issue +batch-reopen \
--owner Gitlink \
--repo forgeplus \
--from issues.csv \
--dry-run
# 批量给多个 Issue 追加同一段评论
gitlink-cli issue +batch-comment \
--owner Gitlink \
--repo forgeplus \
--numbers 4,5 \
--body "已在新版本修复,请验证。"
# 输出 JSON 便于脚本处理
gitlink-cli issue +batch-comment \
--owner Gitlink \
--repo forgeplus \
--numbers 4,5 \
--body "已在新版本修复,请验证。" \
--format json
```
## Tests
```bash
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/issue/...
go vet ./...
go run . issue +batch-reopen --help
go run . issue +batch-comment --help
go run . issue +batch-reopen --owner Gitlink --repo forgeplus --numbers 1,2,3 --dry-run --format json
go run . issue +batch-comment --owner Gitlink --repo forgeplus --numbers 4,5 --body "test" --dry-run --format json
```

View File

@ -1,23 +0,0 @@
# Issue comment management shortcuts
This change expands issue comment support from create-only to a full comment
management workflow.
- `issue +comment` now supports threaded replies through `--parent-id` and
`--reply-id`, attachment IDs, and mentioned users.
- `issue +comments` lists comments and operation records with category,
keyword, sorting, and pagination filters.
- `issue +comment-update` and `issue +comment-delete` edit or remove existing
issue comments.
- `issue +comment-replies` lists child comments for threaded conversations.
The implementation keeps the existing `issue +comment -b` behavior compatible
and adds validation for numeric comment, parent, reply, and attachment IDs
before any API request is sent.
Verification:
- `go test ./shortcuts/issue`
- `go test ./shortcuts`
- `go build ./...`
- `git diff --check`

View File

@ -1,7 +0,0 @@
# Issue 批量导出命令
新增 `gitlink-cli issue +export`,用于把筛选后的 Issue 跨页导出为 CSV、JSON 或 Markdown。维护者经常需要把 Issue 列表带出 GitLink用于周报、迁移、离线排查或交给脚本/AI Agent 做进一步分析;过去只能手动翻页复制或依赖原始 API 拼参数,容易漏页,也不方便统一字段。
命令复用 `issue +list` 的常用筛选条件,包括状态、关键词、参与范围、作者、负责人、里程碑、状态 ID、标签和排序参数同时增加 `--limit`、`--max` 控制导出规模,`--fields` 控制输出字段,`--export-format` 选择 CSV/JSON/Markdown`--output` 写入文件。不传 `--output` 时会直接把导出内容输出到 stdout方便管道处理。
实现上新增独立的 Issue 导出分页逻辑,兼容 GitLink Issue 列表返回的 `issues` 包装结构,并把嵌套的状态、优先级、作者、负责人、标签等字段规范化为稳定列。已补充单元测试覆盖筛选参数、多页导出、`--max` 截断、Markdown 转义和非法参数校验。

View File

@ -1,63 +0,0 @@
# Issue journal shortcuts
## Background
Issue comments and journal events are useful for stale issue detection, audit
trails, and triage workflows. Those reads were still documented as Raw API calls
against `/v1/:owner/:repo/issues/:number/journals`.
This change adds first-class read-only shortcuts for issue journals.
## New shortcuts
- `issue +journals` lists raw issue journal records.
- `issue +activity` uses the same journal endpoint as an activity-oriented
alias.
Both commands preserve the existing issue number convention:
- `--number` / `-n` is the preferred web-visible issue number.
- `--id` / `-i` remains a compatibility alias for the same web-visible number,
not the database ID.
## Options
- `--category` filters journal category, for example `comment`.
- `--page` defaults to `1`.
- `--limit` defaults to `50`.
## Examples
```bash
gitlink-cli issue +journals --owner Gitlink --repo forgeplus --number 123 --page 1 --limit 50
gitlink-cli issue +journals --owner Gitlink --repo forgeplus --number 123 --category comment
gitlink-cli issue +activity --owner Gitlink --repo forgeplus --number 123 --category comment
```
## Documentation updates
- README and README.zh-CN include journal and activity examples.
- `skills/gitlink-issue` documents the new read-only shortcuts.
- `skills/gitlink-stale-issue-manager` now uses `issue +journals` instead of
Raw API for comment history lookup.
## Tests
Unit tests cover:
- endpoint path and query mapping;
- `--number` and `--id` alias behavior;
- missing issue number validation;
- HTTP error propagation.
Suggested verification:
```bash
go test ./shortcuts/issue ./shortcuts
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,24 +0,0 @@
# Issue Reopen
## Summary
`issue +reopen` reopens a closed issue, mirroring `issue +close`. It brings the
issue shortcut group to parity with `milestone +reopen` and `pr +reopen`, which
already had the counterpart to their close command.
Like `issue +close`, the v1 PATCH is read-modify-write, so the current issue is
fetched first and its metadata (priority, tags, assigners, linked branch, dates)
is replayed alongside the new status so unrelated fields are not reset. Only
`status_id` is flipped: `1` (open) for reopen, `5` (closed) for close. Both
commands share the same helper, so `+reopen` preserves exactly the fields
`+close` already does.
## Examples
```bash
gitlink-cli issue +reopen --owner Gitlink --repo forgeplus --number 123
gitlink-cli issue +reopen --owner Gitlink --repo forgeplus -i 123
```
`--number` / `-n` is the project-level issue number from the web URL; `--id` /
`-i` is accepted as a compatibility alias.

View File

@ -1,9 +0,0 @@
# issue 详情增强与更新保护
这个变更聚焦修复 `issue` 快捷命令里两个容易影响实际使用的问题。
`issue +view` 之前只读取 v1 详情接口,返回结果里缺少网页端常见的状态、优先级、跟踪器和标签信息,用户很难直接把 CLI 输出和网页上的 issue 页面对应起来。这次调整后,命令会继续以 v1 接口为主,再补充读取旧版详情与编辑接口,在不影响主流程可用性的前提下,把 `number`、`database_id`、`tracker_id`、`issue_type`、`issue_tag_ids`、`issue_tag_names` 等信息一起带出来。
`issue +update`、`issue +close` 和 `issue +batch-close` 之前只保留了部分字段,更新时可能把现有 issue 的 `tracker_id`、`fixed_version_id`、`assigned_to_id`、`issue_type` 等服务端依赖字段丢掉,导致网页上出现状态异常或字段被误清空。现在这些命令会先读取 issue 的编辑元数据,再把关键字段一并回写;如果编辑元数据拉取失败,就直接终止更新,避免发送不完整的 PATCH 请求。
为了防止这类问题回归,这次补充了 `shortcuts/issue` 的单元测试,覆盖了详情增强、元数据保留、编辑元数据失败时停止写入,以及批量关闭复用同一套保护逻辑的场景。

View File

@ -1,52 +0,0 @@
# Issue and PR Journal Shortcuts
补齐 Issue 评论/操作记录与 Pull Request Review 评论相关 OpenAPI 封装。
## Issue comments / journals
新增或增强:
- `issue +comment`:添加评论,新增 `--parent-id`、`--reply-id`、`--attachment-ids`、`--receivers`、`--dry-run`。
- `issue +comments`:查看 Issue 评论和操作记录,支持 `category`、关键字、排序、分页。
- `issue +comment-update`:更新 Issue 评论,支持附件、@接收人和 `--dry-run`
- `issue +comment-delete`:删除 Issue 评论,支持 `--dry-run`
- `issue +comment-children`:查看指定评论的子评论。
覆盖 OpenAPI
- `GET /api/v1/{owner}/{repo}/issues/{index}/journals.json`
- `POST /api/v1/{owner}/{repo}/issues/{index}/journals.json`
- `PATCH /api/v1/{owner}/{repo}/issues/{index}/journals/{id}.json`
- `DELETE /api/v1/{owner}/{repo}/issues/{index}/journals/{id}.json`
- `GET /api/v1/{owner}/{repo}/issues/{index}/journals/{id}/children_journals.json`
## PR review comments
新增:
- `pr +review-comments`:查看 PR Review 行评论,支持 review/state/path/parent/keyword 等过滤。
- `pr +review-comment`:创建 PR Review 行评论,支持 `comment` / `problem` 类型和 `--dry-run`
- `pr +review-comment-update`:更新 Review 评论内容、commit 或状态,支持 `--dry-run`
- `pr +review-comment-delete`:删除 Review 评论,支持 `--dry-run`
覆盖 OpenAPI
- `GET /api/v1/{owner}/{repo}/pulls/{index}/journals.json`
- `POST /api/v1/{owner}/{repo}/pulls/{index}/journals.json`
- `PUT /api/v1/{owner}/{repo}/pulls/{index}/journals/{id}.json`
- `DELETE /api/v1/{owner}/{repo}/pulls/{index}/journals/{id}.json`
## 安全设计
- 所有写入/删除评论的命令支持 `--dry-run`,先输出 method/path/body不直接改远端数据。
- PR Review 行评论创建支持 `--diff-json` / `--diff-file`,复杂 diff payload 由用户或 Agent 明确传入,避免 CLI 猜测 line diff。
- 参数校验覆盖 Issue journal category、PR review comment type/state、布尔查询参数、正整数 ID 和 JSON diff。
## 测试
新增单元测试覆盖:
- Issue comments list/create/update/delete/children 的 method、path、query、payload。
- PR review comments list/create/update/delete 的 method、path、query、payload。
- dry-run 不触发 API。
- 参数校验失败不触发 API。

View File

@ -1,77 +0,0 @@
# Label batch safety shortcuts
## Background
The label shortcut group already supported listing, creating, updating, and
deleting issue labels. Deletion executed immediately, and larger taxonomy setup
or cleanup workflows still required repeated manual commands or Raw API calls.
This change adds safer destructive operations and first-class batch helpers.
## New and changed shortcuts
- `label +delete` now supports `--dry-run` and requires `--yes` for real
deletion.
- `label +batch-create` creates multiple labels from a semicolon-separated
`name:color:description` list.
- `label +batch-delete` deletes multiple labels from a comma-separated ID list.
## Safety model
Destructive or multi-write commands should be previewed first:
```bash
gitlink-cli label +delete --owner Gitlink --repo forgeplus -i 42 --dry-run
gitlink-cli label +batch-create --owner Gitlink --repo forgeplus \
--labels 'bug:#ee0701:Bug fixes;feature:#0075ca:New features' --dry-run
gitlink-cli label +batch-delete --owner Gitlink --repo forgeplus --ids 3,5,8 --dry-run
```
After confirmation, pass `--yes`:
```bash
gitlink-cli label +delete --owner Gitlink --repo forgeplus -i 42 --yes
gitlink-cli label +batch-create --owner Gitlink --repo forgeplus \
--labels 'bug:#ee0701:Bug fixes;feature:#0075ca:New features' --yes
gitlink-cli label +batch-delete --owner Gitlink --repo forgeplus --ids 3,5,8 --yes
```
## Parsing rules
- `label +batch-create --labels` uses semicolons between labels and colons
inside each label spec: `name:color:description`.
- Missing colors default to `#1E90FF`.
- Colors are validated as `#RGB` or `#RRGGBB` before any API call.
- `label +batch-delete --ids` accepts comma-separated positive integer IDs and
removes duplicates before making requests.
## Documentation updates
- README and README.zh-CN include safe delete and batch examples.
- `skills/gitlink-label` documents the new shortcuts and safety model.
- `skills/gitlink-issue-tag` now recommends label shortcuts instead of Raw API
calls for common label workflows.
- `skills/gitlink-stale-issue-manager` uses `label +batch-create` for stale
label bootstrap steps.
## Tests
Unit tests cover:
- single delete dry-run and `--yes` confirmation;
- batch-create dry-run/default preview and real API calls;
- batch-delete dry-run/default preview, de-duplication, and real API calls;
- parser validation for label specs, colors, and ID lists;
- partial batch failure reporting.
Suggested verification:
```bash
go test ./shortcuts/label ./shortcuts
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,15 +0,0 @@
# Label clone shortcut
新增 `label +clone`,对齐 `gh label clone`:把源仓库的全部 Issue 标签复制到当前仓库。
- 用法:`label +clone --source owner/repo [--force]`。
- 语义与 `gh` 一致:按**名称**判重,目标已存在的同名标签默认**跳过**;仅在 `--force` 下就地**覆盖**`PATCH` 标签 id保留标签 id 与其 Issue 关联)。
- 纯组合已有端点:`GET issue_tags` 列举 + `POST` 新建 / `PATCH` 更新,不新增 API。
- 返回 `created` / `updated` / `skipped` 三组名称,便于查看每个标签的去向。
实现要点:
- 新增自包含的 `fetchLabelsForRepo(ctx, owner, repo)`,按 `page`/`limit` 翻页遍历 `issue_tags` 数组(与 `workflow``fetchAllListItems` 同一翻页范式),源仓库或目标仓库标签超过一页也能完整镜像。
- **未改动既有 `fetchLabel`**:上游 PR #363`fix/label-update-pagination`)正在为 `fetchLabel` 加翻页clone 走独立的 `fetchLabelsForRepo` 以避免合并冲突、也不重新引入单页 bug。
- 补充路径辅助 `repoLabelPath` / `repoLabelItemPath` 支持任意 owner/repo`labelPath` / `labelItemPath` 改为其薄封装;`splitOwnerRepo` 解析 `owner/repo`(容忍首尾斜杠与多余尾部路径)。
- 单测覆盖:默认跳过同名、新建缺失标签、`--force` 就地 `PATCH`,以及 `fetchLabelsForRepo` 翻页遍历两页。

View File

@ -1,10 +1,18 @@
# label shortcut # Label shortcut
新增 `label` 命令组,支持仓库标签管理 新增 `label` Shortcut 组,补齐 GitLink Issue 标签(项目标记 / `issue_tags`OpenAPI 的常用操作封装
| 命令 | 功能 | - `label +list`
|------|------| - `label +create`
| `label +list` | 列出仓库所有标签 | - `label +update`
| `label +create --name <name> --color <hex>` | 创建标签 | - `label +delete`
| `label +update --id <id> [--name <name>] [--color <hex>]` | 更新标签 |
| `label +delete --id <id>` | 删除标签 | 实现要点:
- 列表支持 `--keyword` 关键词过滤、`--only-name` 精简返回、`--sort-by` / `--sort-direction` 排序,映射到 API 的 `order_by` / `order_direction`
- `+create``--color` 缺省为 `#1E90FF`;颜色统一做十六进制(`#RGB` / `#RRGGBB`)客户端校验,非法颜色在调用 API 前即报错。
- `+update` 先从列表接口取标签当前值并与传入字段合并,避免漏传字段被清空(更新接口要求 `name`/`description`/`color` 同时提交);无任何变更字段时直接报错。
- 路径使用 `/api/v1/{owner}/{repo}/issue_tags`,与 webhook/milestone 等组保持一致的 `/v1/` 前缀约定。
- 补充单元测试覆盖各命令的 HTTP 方法、路径、查询参数、payload以及颜色校验和 id 归一化逻辑。
背景在此之前Issue 标签只能通过 Raw API`issue_tags`)手工管理;`gitlink-code-review`、`gitlink-insight` 等 Skill 在做 Issue 分拣 / 打标签时都需要拼接原始请求。`label` 组将其提升为一等命令,并配套 `skills/gitlink-label/` Skill 文档,方便人类与 AI Agent 直接复用。

View File

@ -1,12 +0,0 @@
# label +update 分页取值修复
修复 `label +update` 在标签数量超过一页时静默覆盖服务端数据的问题。
`+update` 需要先取标签当前的 `name`/`description`/`color`(更新接口要求三者同时提交),此前 `fetchLabel` 只对列表接口做单次 `GET`,没有翻页;虽然注释声称“分页匹配 id”实际当目标标签落在第二页及以后时返回“未找到”。随后 `+update` 用空描述与缺省颜色 `#1E90FF` 回填PATCH 便把服务端真实的描述与颜色抹掉,造成数据丢失。
变更:
- `fetchLabel` 改为真正翻页(按 `page`/`limit=50` 循环,页内条数少于一页即视为末页),与仓库其他列表接口的翻页方式一致。
- 只有用户显式传入的字段才会覆盖,未传字段一律保留服务端现值,不再用缺省值静默重置。
- 全量翻页后仍找不到该 id 时直接返回明确错误,不再带缺省值发起 PATCH。
- 单元测试新增“目标标签在第二页”用例,断言 PATCH 报文保留原描述与颜色;并补充“标签不存在时报错、不 PATCH”用例。

View File

@ -1,98 +0,0 @@
# list 命令 --all 自动翻页
## 背景
`issue +list`、`pr +list`、`branch +list`、`release +list` 此前一次只能取一页,
用户或 AI Agent 想拿到全量列表必须手动循环 `--page`。代码中虽有 `PaginateAll`
翻页助手,但它只识别 `data` 包裹键;而 GitLink 生产 API 的列表响应实际用
资源名包裹数组(如 `{"total_count":N,"issues":[...]}`、`"pulls"`、`"branches"`、
`"releases"`),导致该助手在真实端点上退化为「单对象」返回,从未被任何命令使用。
## 变更内容
- `internal/client`:翻页助手对齐生产响应形状
- 新增 `PaginateAllKey(path, params, listKey)`:按指定资源键提取数组;
`listKey` 为空时自动探测(顶层数组 / `data` 包裹 / 唯一数组字段)。
- 遵循 `total_count`:达到总数即停止;另设最大页数护栏,防止
忽略 `page` 参数的端点造成死循环。
- `PaginateAll` 保持原签名,委托给 `PaginateAllKey`
- 十九个分页 list 命令新增 `--all` 布尔参数(默认 false
- `issue +list --all`(合并结果同样应用 number/database_id 规范化)
- `pr +list --all`、`branch +list --all`、`release +list --all`
- `milestone +list --all`、`org +list --all`、`repo +list --all`
- `search +repos --all`、`search +users --all`
- `label +list --all`、`member +list --all`、`webhook +list --all`
- `issue +comments --all`(新增子命令,见下)
- `tag +list --all`、`commit +list --all`(新增命令组,见下)
- `repo +watchers/+stargazers/+forks --all`、`org +members --all`
(资源键 `organization_users`,生产实测 gitlink 组织 71 名成员全量合并)
- `org +repos --all`(新增子命令:组织仓库列表
`/organizations/:name/projects`,此前只能裸 api 访问;
生产实测 gitlink 组织 28 个仓库全量合并)
- 对应资源键:`issues`/`pulls`/`branches`/`releases`/`milestones`/
`organizations`/`projects`/`users`/`issue_tags`/`collaborators`/`webhooks`
(均生产实测确认)
- 输出与单页响应同构:`{"total_count": N, "<资源名>": [...]}`。
- 总数字段兼容 `total_count``count`(如 `/users/:login/projects`)。
- 修复三个既有分页语义缺口(均生产实测确认端点本身分页):
- `label +list` 完全没有 `--page/--limit`(端点实际返回 `total_count`),现已补齐;
- `member +list` 既无分页又走遗留路径(非管理员直接 403现改走
`/v1/:owner/:repo/collaborators`(支持分页且普通成员可读);
- `webhook +list` 完全没有 `--page/--limit`(探针实测:建 3 个 webhook 后
`page=2&limit=1` 返回第二条,确认端点分页),现已补齐;
- `repo +watchers/+stargazers` 完全没有分页 flag端点实测分页
总数键 `count`forgeplus watchers 264 / stargazers 577现已补齐。
- 新增 `tag +list` 命令组:平台暴露分页的 `/v1/:owner/:repo/tags`
端点(轻量 tag 与 release 不同),但 CLI 此前完全没有 tag 命令;
生产实测 forgeplus 16 个 tag 分页与 --all 合并均通过。
- 新增 `commit +list` 命令组:分页的 `/v1/:owner/:repo/commits`
端点此前只能通过裸 api 命令访问;支持 `--ref` 指定分支/tag/SHA
(映射 sha 参数,生产实测 forgeplus 6762 commits、develop 5346
另新增 `commit +view --sha` 单提交详情v1 无此端点,走遗留
`/api/:owner/:repo/commits/:sha`,生产实测含 diff 统计与文件列表)。
- 新增 `repo +forks`:分页的 forks 列表(总数键 `count`
生产实测 forgeplus 77 个 fork 全量合并);此前只有 fork 创建命令。
- 修复翻页助手服务端封顶 limit 丢数据 bug当端点把请求的 limit
封顶(如请求 100 每页只返 20旧逻辑因「页内条数 < limit提前
终止只拿到首页;现已知 total 时以 total 为准watchers 264 条全量
合并生产实测),新增回归单测。
- 新增 `issue +comments` 子命令(对标 `gh issue view --comments`
此前 CLI 只能发评论(`issue +comment`无法读评论流Agent 无法获取
issue 讨论上下文;现接 `/v1/:owner/:repo/issues/:number/journals`
(资源键 `journals`,生产实测分页 + --all 合并通过),
复用 --number/--id 语义。
- 新增 `pr +commits` 子命令:读 PR 的提交列表commits 端点);
生产实测该端点同样忽略 page/limit 始终全量,故不暴露分页 flag。
- 新增 `pr +comments` 子命令:读 PR 评论流
`/v1/:owner/:repo/pulls/:number/journals`)。生产实测该端点
忽略 page/limit 始终返回全量,故不暴露分页 flag避免假分页语义
- 未加 `--all` 的 list 端点均经生产验证为非分页或未部署:
`licenses`/`ignores` 返回全量数组;`pm/pipelines` 404 未部署;
`pipeline +runs``total_data` 非标准包裹dataset 端点未部署(平台 issue #144255)。
- 中英文 i18n 新增 `flag.all` 文案。
## 命令示例
```bash
# 拉取仓库全部 open issue自动翻页合并
gitlink-cli issue +list --state open --all --format json
# 全部分支 / 全部 PR / 全部 release / 全部里程碑
gitlink-cli branch +list --all
gitlink-cli pr +list --state all --all
gitlink-cli release +list --all
gitlink-cli milestone +list --all
# 全部组织 / 某用户全部仓库 / 搜索结果全量
gitlink-cli org +list --all
gitlink-cli repo +list --user Taoyouce --all
gitlink-cli search +repos --keyword gitlink --all
```
## 测试
- `internal/client/pagination_test.go`:资源键包裹多页合并、`total_count`
截断(模拟忽略 page 的异常端点)、`data` 包裹、唯一数组字段自动探测、
单对象回退、指定键缺失回退,共 6 个用例。
- `shortcuts/issue``--all` 端到端用例验证按页请求序列与合并。
- `go test ./...`、`go vet`、`gofmt` 全部通过。

View File

@ -1,43 +0,0 @@
# 维护者工作流 Skills
本次补充了两条面向仓库维护者的新 Skill重点解决“交接信息散落”和“旧分支不敢清理”这两类高频但容易被忽略的问题。
## 新增内容
### 1. `gitlink-maintainer-handoff`
- 汇总站内消息、仓库健康度、开放 PR、开放 Issue 和最近发布
- 输出可直接交给下一位维护者的 Markdown 交接摘要
- 默认只读,适合值班交接、周报、比赛期间的维护汇总
### 2. `gitlink-branch-hygiene`
- 面向分支治理场景,识别默认分支、主分支别名、保护分支、无 PR 分支和可删除候选
- 把 compare 的零差异返回 `[-2] 分支内容相同,无需创建合并请求` 解释为正常治理信号,而不是失败
- 先输出计划,再要求用户确认删除,避免误删分支
## 文档与示例
- 新增 `skills/gitlink-maintainer-handoff/SKILL.md`
- 新增 `skills/gitlink-maintainer-handoff/examples/gitlink-cli-maintainer-handoff.md`
- 新增 `skills/gitlink-branch-hygiene/SKILL.md`
- 新增 `skills/gitlink-branch-hygiene/examples/gitlink-cli-branch-hygiene.md`
- 更新 `skills/README.md`
## 平台验证
本次示例在 Codex 中完成验证,触发方式为自然语言请求 + `gitlink-cli` 实际命令执行。Skill 本身仅依赖 Markdown 规则和标准 CLI 调用,也兼容 Claude Code、Cursor 等可读取 `SKILL.md` 的主流 Agent 平台。
## 本地验证命令
```bash
go run . user +me --format json
go run . api GET "users/Mengz/messages.json" --query "status=1&limit=20" --format json
go run . workflow +repo-report --owner Gitlink --repo gitlink-cli --lang zh-CN --format markdown
go run . workflow +pr-summary --owner Gitlink --repo gitlink-cli --number 209 --lang zh-CN --format markdown
go run . issue +list --owner Gitlink --repo gitlink-cli --state open --limit 10 --format json
go run . release +list --owner Gitlink --repo gitlink-cli --limit 3 --format json
go run . branch +list --owner Gitlink --repo gitlink-cli --format json
go run . compare +view --owner Gitlink --repo gitlink-cli --head fix/pr-review-journal-sync --base master --format json
go run . compare +view --owner Gitlink --repo gitlink-cli --head main --base master --format json
```

View File

@ -1,33 +0,0 @@
# Member application shortcuts
This change extends `gitlink-cli member` from direct collaborator and invite-link operations to the project membership application workflow.
New shortcuts:
- `member +applications` lists project membership applications for a user inbox with `--user`, `--page`, and `--per-page`.
- `member +accept-application` accepts an application by `applied_projects[].id` and supports `--dry-run`.
- `member +refuse-application` refuses an application by `applied_projects[].id` and supports `--dry-run`.
- `member +apply` applies to join a project with an application code and requested role, also supporting `--dry-run`.
The implementation follows the documented GitLink OpenAPI endpoints:
- `GET /api/users/{owner}/applied_projects.json`
- `POST /api/users/{owner}/applied_projects/{id}/accept.json`
- `POST /api/users/{owner}/applied_projects/{id}/refuse.json`
- `POST /api/applied_projects.json`
Safety details:
- Application decisions validate positive integer IDs before calling the API.
- Application role values are normalized to `manager`, `developer`, or `reporter`.
- Dry-run output includes the method, path, and request body where applicable.
- When `--user` is omitted, the shortcut uses `--owner` first and falls back to `GET /users/me`.
Verification:
- `go test ./shortcuts/member`
- `go test ./shortcuts`
- `go test ./...`
- `go build ./...`
- `go run ./internal/i18n/cmd/check --scan-code`
- `git diff --check`

View File

@ -1,12 +0,0 @@
# Message Settings Shortcut
Added a new `message-settings` shortcut group for managing notification delivery preferences from the CLI.
Included commands:
- `message-settings +catalog`
- `message-settings +view`
- `message-settings +update`
- `message-settings +preset`
This change normalizes the setting catalog into stable `Group::Key` identifiers, enriches user settings with group and display names, supports safe partial updates with `--dry-run`, and preserves unspecified settings while updating only the selected keys or groups.

View File

@ -1,25 +0,0 @@
# Message Center Shortcuts
## Summary
This change adds a new `message` shortcut group for personal inbox management in GitLink CLI.
It covers message listing, unread counters, batch mark-as-read, and batch delete workflows without forcing users to drop down to raw API calls.
## Included Commands
- `message +list` filters inbox items by message type, read status, page, and limit.
- `message +stats` returns unread counters for notifications and `@me` messages.
- `message +read` marks selected message IDs, or all unread messages of a given type, as read.
- `message +delete` deletes selected message IDs, or all unread messages of a given type.
## Usability Details
- `--login` defaults to the authenticated user when omitted.
- `--dry-run` is supported for write operations so users can inspect destructive requests first.
- `content_text` is added to list output to expose HTML-free plain text that is easier to grep, diff, and script.
## Validation
- Added shortcut tests for list, stats, read, delete, ID parsing, and content normalization.
- Verified registration by wiring the `message` group into the global shortcut registry.
- Updated `README.md` with feature coverage and usage examples.

View File

@ -1,21 +0,0 @@
# Meta and Attachment Shortcuts
## Summary
This change adds high-level shortcuts for GitLink OpenAPI endpoints that were previously only reachable through Raw API:
- `meta +licenses``GET /api/licenses.json`
- `meta +ignores``GET /api/ignores.json`
- `attachment +upload``POST /api/attachments.json`
- `attachment +delete``DELETE /api/attachments/{uuid}.json`
## User Value
- Maintainers can query license and `.gitignore` templates before creating repositories.
- Agents can upload files once, capture the returned attachment UUID/URL, and reuse it in Issue/PR/comment workflows.
- Destructive attachment deletion supports `--dry-run` to preview the request before remote mutation.
## Validation
- Unit tests cover query parameters, multipart upload fields, dry-run behavior, missing local files, and deletion.
- README and Skill docs include command examples and Agent safety guidance.

View File

@ -1,20 +0,0 @@
# milestone +create 放宽必填参数
## 背景
`milestone +create` 之前把 `--description``--due-date` 标记为必填,
但平台 v1 API 创建里程碑仅要求 `name`(已在生产环境
`POST /api/v1/:owner/:repo/milestones` 用仅含 `{"name": ...}` 的请求验证成功)。
CLI 侧的额外限制迫使用户为可选字段编造内容。
## 变更
- `--description``--due-date` 改为可选;未提供时请求体中省略对应字段。
- `milestonePayload``requireAll` 参数收敛为 `requireName`,仅校验 `--name`
## 验证
- `go test ./shortcuts/milestone/`(新增 `TestMilestoneCreateNameOnly`
断言未提供的可选字段不出现在请求体中)
- `go vet ./...`
- 生产 gitlink.org.cn 实测:`milestone +create -n <name>` 仅带名称创建成功。

View File

@ -1,64 +0,0 @@
# milestone +report shortcut
## Overview
This change adds a new `gitlink-cli milestone +report` shortcut for repository
maintainers and AI agents who need a quick milestone health summary before a
release or iteration close-out.
The command accepts either `--id` or `--name`, resolves the target milestone,
collects all linked issues across pages, and outputs a structured report with:
- milestone metadata and completion percentage
- total/open/closed issue counts
- close-readiness blockers and warnings
- open issue breakdown by status, priority, assignee, and tag
- sample open issues for recent activity, unassigned work, and commented threads
## Why it matters
The existing milestone shortcuts cover CRUD and status changes, but they do not
help a maintainer answer practical questions such as:
- Is this milestone ready to close?
- How many open issues are still unassigned?
- Which priorities or tags dominate the remaining work?
- Which open issues should I inspect first?
`milestone +report` turns those checks into one command and keeps the output
machine-friendly for scripts and AI agents.
## Pagination safeguard
The report implementation now fetches milestone issues until the API-reported
total is fully collected, instead of assuming the server always honors the
requested `limit`.
This avoids undercounting when the service caps each response page below the
requested size. For filtered milestone issue views, the implementation uses the
matching filtered totals (`opened_issues_count` / `closed_issues_count`) so it
does not over-fetch extra pages.
## Example commands
```bash
gitlink-cli milestone +report --owner Gitlink --repo forgeplus --id 2438 --sample-limit 3
gitlink-cli milestone +report --owner Gitlink --repo forgeplus --name "v1.0"
```
## Files changed
- `shortcuts/milestone/milestone.go`
- `shortcuts/milestone/report.go`
- `shortcuts/milestone/report_test.go`
- `README.md`
## Validation
```bash
go test ./shortcuts/milestone/...
go test ./shortcuts/...
go test ./...
go build ./...
go run . milestone +report --owner Gitlink --repo forgeplus --id 2438 --sample-limit 3 --format json
```

View File

@ -1,85 +0,0 @@
# 变更说明:新增 Shortcut 模块wiki/commit/file/star/watch+ 批量操作
## 概述
本 PR 新增 5 个 Shortcut 模块(共 23 个命令)和 3 个批量操作模块。
## 新增模块
### 1. Wiki 模块5 个命令)
| 命令 | 说明 |
|------|------|
| `wiki +list` | 列出 Wiki 页面 |
| `wiki +view` | 查看 Wiki 页面内容 |
| `wiki +create` | 创建 Wiki 页面 |
| `wiki +update` | 更新 Wiki 页面 |
| `wiki +delete` | 删除 Wiki 页面(自动清理 Sidebar |
**技术要点**
- Wiki API 使用独立网关 `gateway.gitlink.org.cn`,不带 `.json` 后缀
- 使用 `client.DoRaw()` 方法处理非标准 API 响应
- delete 命令会自动清理 `_Sidebar` 中的残留链接
### 2. Commit 模块4 个命令)
| 命令 | 说明 |
|------|------|
| `commit +list` | 提交历史列表 |
| `commit +view` | 查看提交详情 |
| `commit +diff` | 查看提交差异 |
| `commit +blame` | 代码追溯 |
### 3. File 模块5 个命令)
| 命令 | 说明 |
|------|------|
| `file +list` | 列出目录文件 |
| `file +tree` | 文件树 |
| `file +get` | 获取文件内容 |
| `file +create` | 创建文件(自动 base64 编码) |
| `file +delete` | 删除文件 |
### 4. Star 模块3 个命令)
| 命令 | 说明 |
|------|------|
| `star +star` | 点赞仓库 |
| `star +unstar` | 取消点赞 |
| `star +stars` | 查看点赞列表 |
### 5. Watch 模块3 个命令)
| 命令 | 说明 |
|------|------|
| `watch +watch` | 关注仓库 |
| `watch +unwatch` | 取消关注 |
| `watch +watchers` | 查看关注者列表 |
## 新增批量操作
| 模块 | 命令 | 说明 |
|------|------|------|
| member | `batch-add` | 批量添加成员 |
| org | `batch-invite` | 批量邀请成员自动解析用户名→ID |
| repo | `batch-create` | 批量创建仓库 |
| repo | `batch-fork` | 批量 Fork 仓库 |
| repo | `batch-delete` | 批量删除仓库 |
所有批量操作支持 `--dry-run` 预览模式和 `--from CSV` 文件输入。
## 测试覆盖
| 模块 | 测试数 |
|------|--------|
| wiki | 12 |
| commit | 4+ |
| file | 5+ |
| star | 3+ |
| watch | 3+ |
## 注意事项
> ⚠️ 本 PR 的代码基于旧版 API 签名(`Shortcuts()` 无参数),需适配上游新版 i18n 翻译器接口(`Shortcuts(tr *i18n.Translator)`)后方可编译通过。
>
> Wiki 模块依赖 `client.DoRaw()` 方法(用于不带 `.json` 后缀的网关 API需合入 client.go 的相关变更。

View File

@ -1,12 +0,0 @@
# notification shortcut
新增 `notification` 命令组,支持用户通知管理:
| 命令 | 功能 |
|------|------|
| `notification +list` | 列出通知列表 |
| `notification +view --id <id>` | 查看通知详情 |
| `notification +read --id <id>` | 标记通知已读 |
| `notification +delete --id <id>` | 删除通知 |
修复:`issue +create` 命令的 `--label` 参数现在会正确传入请求 body。

View File

@ -1,82 +0,0 @@
# Notification shortcuts
## Background
GitLink exposes user messages and notifications through the messages API. The
existing `gitlink-notification-digest` Skill had to call Raw API paths directly
to list notifications and mark messages as read. That made agent workflows more
fragile and forced users to remember GitLink's "messages" terminology.
This change adds a first-class `notification` shortcut group.
## New shortcuts
- `notification +list` lists messages for the authenticated user or a specified
user, with type/status/page/limit filters.
- `notification +read` marks selected messages as read, or marks all unread
messages of a selected type as read.
- `notification +delete` deletes selected messages.
- `notification +send-atme` sends @ mention messages for `Journal`, `Issue`, or
`PullRequest` targets.
## Safety model
Read-only listing runs directly:
```bash
gitlink-cli notification +list --status unread --limit 20
```
Remote write operations require explicit confirmation and support dry-run
previews:
```bash
gitlink-cli notification +read --ids 740214,740213 --dry-run
gitlink-cli notification +read --ids 740214,740213 --yes
```
```bash
gitlink-cli notification +delete --ids 740214,740213 --dry-run
gitlink-cli notification +delete --ids 740214,740213 --yes
```
```bash
gitlink-cli notification +send-atme --receivers alice,bob \
--atmeable-type Issue --atmeable-id 123 --dry-run
```
`notification +read --all-unread` maps to GitLink's `ids: [-1]` convention.
The delete command does not expose `--all-unread` to avoid accidental broad
deletion.
## Documentation updates
- README and README.zh-CN include notification usage examples.
- `skills/gitlink-notification/` documents the new shortcut group.
- `skills/gitlink-notification-digest` now prefers `notification +list` and
`notification +read` instead of Raw API calls.
- The Skills overview lists the new notification Skill.
## Tests
Unit tests cover:
- list endpoint path, filters, pagination, and current-user fallback;
- dry-run behavior for all write operations;
- confirmation guard without `--yes`;
- read payload construction for selected IDs and all unread messages;
- delete payload construction;
- send-atme payload construction and validation;
- invalid argument handling before remote calls.
Suggested verification:
```bash
go test ./shortcuts/notification
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,23 +0,0 @@
# org team shortcuts
This change expands the `org` shortcut group from basic organization lookup into an operational workflow for team and membership management.
The updated command set keeps `org +list`, `org +info`, and `org +create`, and adds stronger organization administration coverage:
- `org +members` now supports `--all`, `--team`, `--login`, and `--keyword`, and automatically fetches all pages before applying filters.
- `org +teams` lists organization teams with `--authorize`, `--unit`, `--keyword`, and `--include-users` support, returning normalized team summaries.
- `org +team-create` creates a team and supports `--dry-run` for request preview.
- `org +member-remove` removes an organization member by `--user-id` or resolves `--login` automatically, with `--dry-run` support.
The output shape is normalized for automation use. Member results now include matched counts, team summaries, and consistent user fields. Team results expose permission-level aggregation, unit information, and optional normalized user details.
The command examples in `README.md` were extended so the new team and member management flows are discoverable from the main project documentation.
Validation:
```bash
go test ./shortcuts/org/...
go test ./shortcuts/...
go test ./...
go build ./...
```

View File

@ -1,29 +0,0 @@
# 全局 `--jq` 输出字段提取
## 动机
对标 `gh --jq`Agent 与 shell 脚本消费 CLI 输出时经常只需要单个字段
(如最新 commit 的 sha、issue 总数),此前必须依赖外部 `jq` 或自行解析
完整 JSON 信封。新增零依赖的点分路径提取,纯 Go 标准库实现,
不引入任何第三方 JSON 查询库(避免供应链风险)。
## 行为
- 新增全局持久 flag `--jq`,对所有命令生效(不占用 `--query`,与 api 子命令的查询参数 flag 无冲突)。
- 路径语法:点分段;段为对象键,或非负整数作为数组下标。
例:`--jq data.commits.0.sha`、`--jq data.total_issues_count`。
- 标量(字符串/数字/布尔/null输出裸值方便 shell 管道直接消费;
对象与数组输出缩进 JSON。
- 错误信息可操作:键不存在时列出该层全部可用键(排序后);
数组下标越界/非数字段给出数组长度提示。
## 生产实测
- `issue +list --limit 2 --jq data.issues.0.subject` → 裸标题字符串
- `issue +list --jq data.total_issues_count``4197`
- 误键 `--jq data.issues.0.name` → 报错并列出 29 个可用键(含 `subject`
## 测试
`internal/output/query_test.go` 6 个单测:字符串/数字标量裸值输出、
对象 JSON 输出、缺键错误含可用键列表、下标越界、数组段非数字。

View File

@ -1,30 +0,0 @@
# 表格输出确定性排序与错误状态码展示
## 背景
`--format table` 的渲染依赖 Go map 的遍历顺序,而 Go 的 map 遍历是**随机的**
- `printMapTable`(单对象 KEY/VALUE 表)每次运行的行顺序都不一样;
- `collectKeys` 在补全非优先列时直接 `for k := range m`,导致 `printSliceTable`(列表表)优先列之后的**列顺序**也随机。
这会让同一条命令两次运行的表格输出不一致,难以肉眼对比、`diff`、截图或在脚本/测试中稳定断言。
此外table 模式的错误输出只显示 `Error: <message>`**不显示状态码**,用户难以快速区分 404 / 422 / 500。
## 变更
- `collectKeys`优先列id/name/login/title/status/state/created_at/updated_at之后的剩余列改为 `sort.Strings` 排序,列顺序稳定且可预期。
- `printMapTable`:改为按 `collectKeys` 的顺序输出,行顺序确定,并与列表表的列顺序保持一致。
- 错误输出:当存在错误码时显示 `Error [<code>]: <message>`(无错误码时保持 `Error: <message>`),便于快速识别状态码。
不影响 json / yaml 输出,也不改变成功数据的内容,仅稳定其呈现顺序与丰富错误提示。
## 测试
`internal/output/formatter_test.go` 新增:
- `collectKeys` 非优先列按字典序排序;
- 单对象表渲染 25 次输出完全一致(确定性);
- 列表表表头顺序 25 次渲染一致;
- 错误输出包含状态码 `Error [500]: ...`,无错误码时回退为 `Error: ...`
`go build ./...`、`go vet ./...`、`go test ./...`、`gofmt -s` 全部通过。

View File

@ -1,18 +0,0 @@
# PM (项目管理) shortcut
新增 `pm` Shortcut 组,封装 GitLink 项目管理相关只读接口,补齐仓库协作元数据的命令化访问:
- `pm +dashboards` — 查看项目仪表盘数据
- `pm +sprints` — 查看 Sprint 任务列表
- `pm +weekly` — 查看周报任务
- `pm +tags` — 查看项目 Issue 标签
- `pm +pipelines` — 查看项目 CI/CD 流水线列表
- `pm +runs` — 查看项目 Action 运行记录
实现要点:
- 全部为只读GET命令通过 Raw API 访问项目管理后端,统一 `owner/repo` 自动解析与 `--format json|table|yaml` 输出。
- 面向「项目经理 / 科研课题负责人」视角一条命令拿到仪表盘、Sprint、周报、流水线运行等聚合视图无需在 Web 上多次跳转。
- 与 `gitlink-pm` Skill 配套,供 AI Agent 做项目健康巡检与进度跟踪。
背景:项目管理数据此前散落在多个 Web 页面,无命令行入口。`pm` 组将其收敛为 6 条命令,是子任务三「项目一键初始化 / 进度跟踪」与子任务四「科研进度智能跟踪与预警」的基础数据层。关联 PR #12

View File

@ -1,60 +0,0 @@
# PM shortcuts
## Background
The `gitlink-pm` Skill documented GitLink project-management workflows but had
to call Raw API paths under `/pm/...` directly. That made Sprint reports, weekly
issue summaries, board inspection, and PM pipeline checks less discoverable for
users and agents.
This change adds a first-class `pm` shortcut group for read-only PM data.
## New shortcuts
- `pm +dashboards` lists PM dashboards for a project.
- `pm +sprint-issues` lists Sprint issues.
- `pm +weekly-issues` lists weekly issues.
- `pm +issue-tags` lists PM issue tags.
- `pm +pipelines` lists PM pipelines.
- `pm +action-runs` lists PM action run records.
All commands require `--project-id` and accept `--page`/`--limit`.
## Examples
```bash
gitlink-cli pm +dashboards --project-id 123 --limit 20
gitlink-cli pm +sprint-issues --project-id 123 --page 1 --limit 20
gitlink-cli pm +weekly-issues --project-id 123
gitlink-cli pm +issue-tags --project-id 123
gitlink-cli pm +pipelines --project-id 123
gitlink-cli pm +action-runs --project-id 123
```
## Documentation updates
- README and README.zh-CN include PM usage examples.
- `skills/gitlink-pm` now prefers `pm +...` shortcuts instead of Raw API calls.
- The Skills overview lists the new PM commands.
## Tests
Unit tests cover:
- all PM shortcut endpoint mappings;
- `project_id`, `page`, and `limit` query construction;
- default pagination;
- required `--project-id` validation;
- HTTP error propagation.
Suggested verification:
```bash
go test ./shortcuts/pm ./shortcuts
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,7 +0,0 @@
# PR 本地检出命令
新增 `gitlink-cli pr +checkout`,用于把指定 PR 的源分支拉取到本地并切换到 review 分支。维护者和贡献者在审查 PR 时,经常需要从“查看 PR 元信息”进入“本地构建、测试、复现”的流程,之前只能手动查看 fork 信息、拼接远端地址、fetch 分支,再 checkout新命令把这套流程收敛为一个 Shortcut。
命令会读取 GitLink PR 详情,自动识别同仓库 PR 与 fork PR 的源仓库和源分支,不依赖本地 remote 必须叫 `origin`。默认创建 `pr-<id>` 本地分支,也支持 `--branch` 指定分支名、`--force` 重置已存在分支、`--dry-run` 预览将执行的 git 命令。
本次变更补充了单元测试、README 示例、PR Skill 说明和独立参考文档,覆盖 fork PR 解析、同仓库 head 解析、dry-run、force checkout 以及非法分支名校验。

View File

@ -1,65 +0,0 @@
# pr +checks 合并请求 CI 构建状态命令
## 背景
`gitlink-cli pr` 已覆盖合并请求的列表、详情、评审、评论等操作,`gitlink-cli ci +builds` 可列出仓库的 CI 构建,但两者相互独立。用户或 AI Agent 想确认「某个合并请求的最新提交是否通过了 CI」此前需要手动读取 PR 的源分支/源提交,再逐条比对构建列表。
本次变更新增 `pr +checks`,对齐 `gh pr checks` 的语义:解析合并请求 head自动关联并汇总对应的 CI 构建状态。
## 变更内容
- 新增 `gitlink-cli pr +checks --id N` Shortcut。
- 先 `GET /{owner}/{repo}/pulls/{id}` 读取合并请求详情,取源分支 `head` 与源提交 `head_commit_sha`(字段依据 API 文档「获取一个合并请求」章节)。
- 再 `GET /{owner}/{repo}/builds` 拉取 CI 构建列表,在客户端按 head 提交/分支筛选。
- 输出规范化的状态摘要:`matched_by`、`total_builds` 以及每条构建的 `id / stage / status / conclusion / branch / sha`
- 复用现有仓库上下文解析、API 调用与统一输出封装;新增中英文 i18n 文案。
## 匹配策略
| 优先级 | 条件 | `matched_by` |
|--------|------|--------------|
| 1 | 构建提交 SHA 与 head 提交一致(支持缩写前缀比对) | `sha` |
| 2 | 无 SHA 命中,但构建分支等于 head 分支 | `branch` |
| 3 | 构建未暴露任何分支/提交字段,无法建立关联 | `unlinkable` |
| 4 | 构建暴露了分支/提交字段但均不匹配 | `none` |
## 命令示例
```bash
gitlink-cli pr +checks --owner Gitlink --repo forgeplus --id 42
gitlink-cli pr +checks --id 42 --format json
```
## 已知限制
GitLink 的 `/{owner}/{repo}/builds` 端点未纳入官方 OpenAPI 参考文档,构建对象中承载分支与提交的字段名无法从文档确证。为避免臆造字段:
- 分支字段按 `branch / head_branch / source_branch / ref` 依次探测(`ref` 会去除 `refs/heads/` 前缀)。
- 提交字段按 `head_commit_sha / commit_sha / commit_id / sha / after / revision` 依次探测。
- 若某次构建两类字段均缺失,则判定为无法关联(`matched_by = unlinkable`),此时**降级返回全部最近构建并附带说明**,由使用者依据 `head_sha` 手动核对,而非丢弃结果或猜测字段。
后续若 `/builds` 响应结构被官方文档化,可据实收敛探测键集合。
## 测试覆盖
- 表驱动 httptest先 mock `GET pulls/{id}`、再 mock `GET builds`,断言四种 `matched_by` 分支sha 优先于 branch、缩写 SHA 命中、branch 回退、unlinkable 全量降级、none 无命中)与选中的构建 id。
- head 字段缺失、builds 请求 HTTP 失败的错误路径。
- 纯函数单测:`extractPullRequestHead`、`buildsFromEnvelope`(含客户端把顶层数组作为字符串返回的情形)、`commitMatches`、仅有分支的 PR。
验证命令:
```bash
go build ./...
go test ./shortcuts/pr/ ./shortcuts/ci/
```
## 交付要求核对
- 功能代码:`shortcuts/pr/pr.go`、`shortcuts/pr/checks.go`
- 单元测试:`shortcuts/pr/checks_test.go`
- i18n 文案:`internal/i18n/locales/en-US.json`、`internal/i18n/locales/zh-CN.json`
- 变更说明文档:`doc/changes/pr-checks.md`
## 兼容性
该变更只新增 Shortcut、辅助函数、单元测试、i18n 文案与文档,不修改任何已有命令的参数或输出结构。

View File

@ -1,30 +0,0 @@
# PR Create Fork Support
## Summary
This change makes `gitlink-cli pr +create` work with the fork syntax already documented in the README:
```bash
gitlink-cli pr +create --owner Gitlink --repo forgeplus \
-t "feat: New feature" \
--head your_username/forgeplus:feature/my-feature \
--base master
```
## What changed
- Parse `owner/repo:branch` fork heads in `pr +create`
- Auto-resolve the fork repository metadata required by GitLink:
- `merge_user_login`
- `merge_project_identifier`
- `fork_project_id`
- Auto-fill compare counts when available so the request matches GitLink's real PR create flow more closely
- Keep same-repo PR creation behavior unchanged
## Validation
- `go test ./...`
- Unit tests for:
- same-repo PR creation payload
- fork PR creation payload
- invalid fork head syntax

View File

@ -1,18 +0,0 @@
# PR Edit Shortcut
This change adds a `pr +edit` verb, bringing pull requests in line with `issue`, `label`, `release`, `milestone`, and `webhook`, which already expose an edit/update command.
## Commands
- Add `pr +edit` for `PUT /api/{owner}/{repo}/pulls/{index}.json`.
## Behavior
- `pr +edit` first GETs the current PR (the same `/pulls/{index}` path used by `pr +view`), then merges the requested changes onto its current values before the PUT. The update endpoint requires `title`, `body`, `head`, `base`, `issue_tag_ids`, and `receivers_login` together, so unspecified flags fall back to the existing values to avoid clobbering them.
- Flags: `--title`, `--body`, `--base`, `--head`, and `--tag-ids` (comma-separated). At least one is required.
- `--tag-ids` replaces the tag set when given; otherwise the PR's current tag IDs are preserved.
- PR detail fields are read defensively across the top-level, `pull_request`, and `issue` scopes, matching how the health checks parse the same endpoint.
## Verification
- Unit tests cover field preservation, tag-id override, the nested `pull_request` response shape, the missing-field guard, and the HTTP error path.

View File

@ -1,20 +0,0 @@
# PR 行级审查评论快捷命令
这次改动把 `pull request` 里的行级审查评论补成了一套完整的快捷命令,而不只是停留在普通会话评论或 review 总览。
- 新增 `pr +review-comments`,可以按 `review_id`、`state`、`path`、`need_respond` 等条件筛选 inline review comments。
- 新增 `pr +review-comment`、`pr +update-review-comment`、`pr +delete-review-comment`,把创建、更新、删除行级评论的常用操作补齐。
- `pr +review-comment` 默认会自动调用 PR files 接口,按 `--path` 提取对应文件的 diff 并转换成评论接口可用的结构,减少手工拼接大段 `diff` JSON 的负担;如果需要完全自定义,也可以通过 `--diff-file` 直接提供 diff JSON。
- 创建、更新、删除都支持 `--dry-run`,方便在脚本或 Agent 场景里先预览最终请求内容。
这条能力比较适合代码审查自动化、Agent 辅助 review、或者把外部静态分析结果回写到具体变更行上比单纯暴露原始接口更容易直接落到实际工作流里。
本地验证:
```bash
go test ./shortcuts/pr
go test ./...
go build ./...
git diff --check
go run . pr --help
```

View File

@ -1,33 +0,0 @@
# PR 列表按作者过滤
## Summary
`pr +list` 新增 `--login` 参数,可按 Issue 作者(`issue.author.login`)过滤返回的 Pull Request 列表。
## 新增 Flag
| Flag | 说明 |
|------|------|
| `--login` | 按 Issue 作者的 login 过滤 PR不区分大小写 |
## 实现细节
GitLink PR 列表 API 不支持服务端按作者过滤,因此本功能在客户端对返回结果进行过滤。过滤逻辑:
- 匹配 `issue.author.login` 字段
- 不区分大小写
- 过滤后自动更新 `total_count``meta.total_count` 以反映实际数量
## 示例
```bash
# 列出 alice 提交的所有 PR
gitlink-cli pr +list --owner Gitlink --repo gitlink-cli --login alice
# 结合其他筛选条件
gitlink-cli pr +list --owner Gitlink --repo gitlink-cli --login alice --state open
# 列出已合并的 PR
gitlink-cli pr +list --login bob --state merged
```

View File

@ -1,13 +0,0 @@
## PR list output now shows the user-facing PR number
`pr +list` already returned the GitLink PR sequence as `index`, but the default
table output did not make that value easy to spot. This change copies the same
value into a stable `number` field during list normalization and prioritizes the
`number` column in table rendering.
As a result:
- `gitlink-cli pr +list --format table` shows the PR number in a dedicated
leading column.
- JSON and YAML output also include `number`, making the list output align with
`pr +view --id <number>` semantics and with the PR number shown in the web UI.

View File

@ -1,10 +0,0 @@
## PR list supports direct lookup by PR number
`pr +list` previously only exposed keyword-based search, which made it awkward
to jump to a known PR from the web UI or from review notes. This change adds
`--number` / `-n` and a compatibility alias `--id` / `-i` to the list command.
When a PR number is provided, the CLI now reads that PR through the dedicated
detail endpoint and wraps the result into the usual list payload shape. This
keeps the output stable for automation while making exact-number lookup work
even when the target PR is not on the current list page.

View File

@ -1,65 +0,0 @@
# PR readiness shortcuts
## Background
Pull request workflows already support list, create, view, merge, review,
comments, changed files, and patchset/version inspection. Two helper endpoints
were still documented as Raw API calls:
- `GET /:owner/:repo/pulls/get_branches`
- `POST /:owner/:repo/pulls/check_can_merge`
This change adds first-class shortcuts for those PR preparation workflows.
## New shortcuts
- `pr +branches` lists PR source/target branch candidates.
- `pr +check-can-merge` checks whether a source branch can merge into a target
branch.
## Safety model
`pr +branches` is read-only and can run directly:
```bash
gitlink-cli pr +branches --owner Gitlink --repo forgeplus
```
`pr +check-can-merge` uses a remote POST endpoint, so it supports dry-run and
requires explicit confirmation for real execution:
```bash
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus \
--head feature/search --base master --dry-run
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus \
--head feature/search --base master --yes
```
## Documentation updates
- README and README.zh-CN include branch helper and merge readiness examples.
- `skills/gitlink-pr` now prefers `pr +branches` and `pr +check-can-merge`
over Raw API calls.
- `skills/gitlink-gatekeeper` includes merge readiness as a PR preflight signal.
## Tests
Unit tests cover:
- endpoint method/path mapping for `pr +branches`;
- `pr +check-can-merge` dry-run behavior;
- `--yes` confirmation guard;
- request payload and default `--base master`;
- HTTP error propagation for both shortcuts.
Suggested verification:
```bash
go test ./shortcuts/pr ./shortcuts
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,25 +0,0 @@
# Pull request review comment management shortcuts
This change adds first-class shortcuts for GitLink pull request review comment
threads. It complements `pr +review`, which creates an overall review decision,
with commands for line-level and threaded discussion records.
- `pr +review-comments` lists review comments with keyword, review ID,
need-response, state, parent, path, full-thread, and sorting filters.
- `pr +review-comment` creates review comments or replies and supports
`comment`/`problem` types, review IDs, line codes, commit IDs, paths,
parent IDs, raw diff JSON, and dry-run previews.
- `pr +review-comment-update` edits the note, commit, or state (`opened`,
`resolved`, `disabled`) with dry-run support.
- `pr +review-comment-delete` deletes a review comment by ID.
The implementation validates numeric IDs, boolean filters, comment type, state,
and diff JSON before sending API requests.
Verification:
- `go test ./shortcuts/pr`
- `go test ./shortcuts`
- `go test ./...`
- `go build ./...`
- `git diff --check`

View File

@ -1,75 +0,0 @@
# pr +status 与我相关的合并请求概览命令
## 背景
`gitlink-cli pr` 已经提供列表、创建、查看、合并、评审等能力,但缺少一个类似 `gh pr status` 的入口,用于快速回答“当前登录用户在本仓库里有哪些待处理的合并请求”。用户或 AI Agent 过去需要先查自己的身份,再手动拼 `pr +list` 的过滤参数,还要区分“我提的”和“等我评审的”。
本次变更把这一常见诉求封装为 `pr +status`,一次调用给出两组结果:你创建的、以及请求你评审的开启中合并请求。
## 变更内容
- 新增 `gitlink-cli pr +status` Shortcut只读
- 先调用 `GET /users/me` 解析当前用户的 `login` 与数值 `id`(复用 `user +me` 的接口)。
- 复用合并请求列表接口 `GET /v1/{owner}/{repo}/pulls`api_ref「获取合并请求列表」拉取数据
- **你创建的**:以 `status=0` 拉取开启中的合并请求,再按 `issue.author.login` 与当前用户在**客户端**匹配。该列表接口没有 author 过滤参数,故只能客户端过滤。
- **请求你评审的**:以 `status=0``reviewer_id={当前用户 id}` 由**服务端**过滤(`reviewer_id` 是列表接口文档化的审查人员过滤参数)。
- 输出统一封装为结构化数据:`login`、`created`、`review_requested` 两组合并请求数组沿用现有输出格式json/table/yaml
- 补充中英文 i18n 文案(`cmd.pr.status.short` / `cmd.pr.status.long`),避免命令帮助信息硬编码。
## 命令示例
```bash
# 查看与你相关的合并请求owner/repo 可从 git remote 自动解析)
gitlink-cli pr +status --owner Gitlink --repo forgeplus
# Agent 场景建议 JSON 输出
gitlink-cli pr +status --owner Gitlink --repo forgeplus --format json
```
## 输出结构
```json
{
"ok": true,
"data": {
"login": "currentuser",
"created": [ /* 你创建的开启中合并请求 */ ],
"review_requested": [ /* 请求你评审的开启中合并请求 */ ]
}
}
```
## 设计说明
- 该命令刻意只使用列表接口文档化的查询参数(`status`、`reviewer_id`),不引入未在 api_ref 中出现的参数。
- author 侧过滤放在客户端,是因为列表接口只支持 `reviewer_id` / `assign_user_id` 等数值过滤,没有 author 过滤参数;这一点在上文与代码注释中都做了说明。
- 全流程只读,不修改任何合并请求状态。
## 测试覆盖
`shortcuts/pr/pr_test.go` 中新增表驱动单元测试mock `/users/me` 与合并请求列表接口):
- 分组正确:混合作者的开启中合并请求被正确拆分为“你创建的”与“请求你评审的”。
- author 客户端过滤:他人创建的合并请求不进入“你创建的”分组。
- 空仓库:两组均为空。
- `reviewer_id` 断言:确认按当前用户数值 id 向服务端发起评审过滤查询。
- 错误路径:`/users/me` 返回 500、或响应缺少 `login` 时命令报错。
- 端到端:`pr +status` 走完整 Run 路径(含输出)不报错。
验证命令:
```bash
go build ./...
go test ./shortcuts/pr/
```
## 交付要求核对
- 功能代码:`shortcuts/pr/pr.go`
- 单元测试:`shortcuts/pr/pr_test.go`
- i18n 文案:`internal/i18n/locales/en-US.json`、`internal/i18n/locales/zh-CN.json`
- 变更说明文档:`doc/changes/pr-status.md`
## 兼容性
该变更只新增一个只读 Shortcut、对应单元测试、i18n 文案与文档,不修改已有命令的参数或输出结构,对现有功能无破坏性影响。

View File

@ -1,26 +0,0 @@
# PR View Timestamp Normalization
## Summary
`gitlink-cli pr +view` now normalizes pull request lifecycle timestamps so merged and closed pull requests expose stable top-level time fields in JSON output.
## Command
| Command | Purpose |
|---------|---------|
| `gitlink-cli pr +view` | Return PR detail and normalize `created_at`, `merged_at`, `closed_at`, and `closed_on` when GitLink provides them directly or journals can infer them. |
## Behavior
- Promote `created_at` and `merged_at` from nested response objects to the top-level payload.
- Backfill `closed_at` and `closed_on` for merged pull requests when GitLink omits an explicit close timestamp.
- Read issue journals only when a merged or closed pull request is still missing lifecycle timestamps.
- Recognize merge and close journal operations after stripping HTML tags and whitespace.
- Support both `pull_request_status` and `pull_request_staus` status shapes returned by GitLink APIs.
## Tests
- `go test ./shortcuts/pr/...`
- `go build ./...`
- `go test ./...`
- `go run . pr +view --owner Gitlink --repo gitlink-cli -i 15 --format json`

View File

@ -1,26 +0,0 @@
# Reaction Shortcut
## Summary
Adds a `reaction` shortcut group for repository social interactions. The group exposes watcher and stargazer lists plus follow/unfollow and like/unlike actions.
## Commands
| Command | Purpose |
|---------|---------|
| `gitlink-cli reaction +watchers` | List repository watchers |
| `gitlink-cli reaction +stargazers` | List repository stargazers |
| `gitlink-cli reaction +follow` | Follow a repository |
| `gitlink-cli reaction +unfollow` | Unfollow a repository |
| `gitlink-cli reaction +like` | Like a repository |
| `gitlink-cli reaction +unlike` | Unlike a repository |
## Behavior
- `+watchers` and `+stargazers` support optional `--start-at` and `--end-at` Unix timestamp filters.
- Write actions accept optional `--project-id`; if omitted, the shortcut resolves the project ID from `--owner/--repo` automatically.
- Timestamp and project ID inputs are validated before API calls are sent.
## Tests
The unit tests verify list query parameters, project ID auto-resolution, explicit project ID handling, follow/unfollow endpoints, like/unlike endpoints, and validation failures.

View File

@ -1,25 +0,0 @@
# Release Asset Shortcuts
Submitter: Mengz
This change fills the missing release asset workflow in `gitlink-cli`, so users can upload files, attach existing assets, inspect attached assets, and detach assets without dropping the rest of the release metadata.
## Commands
- Add `release +assets` to inspect the assets currently attached to a release.
- Add `release +attach` to merge existing attachment IDs into a release.
- Add `release +detach` to remove attachment IDs from a release.
- Add `release +upload` to upload a local file and attach it to a release in one command.
## Behavior
- Add multipart upload support to `internal/client` and expose it through `shortcuts/common.RuntimeContext`.
- Keep the `RuntimeContext` encapsulation instead of calling the low-level client directly from shortcuts.
- Preserve existing release fields when attaching, detaching, or uploading assets, so these commands do not accidentally overwrite `name`, `tag_name`, `body`, `target_commitish`, `draft`, or `prerelease`.
- Accept release tags for asset operations by resolving them to the internal release version ID before write operations.
- Clean up the uploaded attachment automatically if the follow-up release update fails, avoiding orphaned assets.
## Verification
- Add multipart client tests covering field values, uploaded filename, content type, and file body.
- Add release shortcut tests covering asset listing, attach/detach dry-run behavior, attachment merging, upload-and-attach flow, and cleanup after failed release update.

View File

@ -1,7 +0,0 @@
# 新增 Release 资产下载能力
Release 命令组现在支持 `release +assets``release +download`可以先查看某个发布包含的附件、zip/tar 源码包,再按附件 ID、附件文件名或源码包类型下载到本地目录。
下载链路新增了 client 层原始字节获取能力,并通过 `RuntimeContext.Download()` 暴露给 shortcut避免 release 命令直接绕过统一客户端封装。附件 URL 支持 GitLink 返回的 `/api/attachments/<id>` 形式,源码包也支持完整 URL。
`release +download` 默认不覆盖已有文件;发布只有一个附件时可以直接下载,存在多个附件时要求显式传入 `--asset`,减少自动化脚本误下文件的风险。本次变更补充了 client、release shortcut、帮助文案、README 和 Skill reference 测试与文档。

View File

@ -1,43 +0,0 @@
# Release Enhance Shortcuts
Submitter: Wang Yue
This change adds two new release shortcuts for getting the latest release and auto-generating release notes.
## Commands
- Add `release +latest` for getting the latest release version.
- Add `release +auto-notes` for auto-generating release notes from commits and issues.
## Behavior
### release +latest
- Fetches releases from the repository and returns the first matching release.
- By default, skips draft and prerelease versions.
- Supports `--include-prerelease` flag to include prerelease versions.
- Supports `--include-draft` flag to include draft versions.
- Returns error if no matching release is found.
### release +auto-notes
- Generates formatted release notes from commit messages and closed issues.
- Automatically categorizes commits by prefix:
- `feat:` → 🚀 新功能 (New Features)
- `fix:` → 🐛 Bug 修复 (Bug Fixes)
- Others → 📝 其他变更 (Other Changes)
- Supports `--from-tag` to specify the starting tag for comparison.
- If `--from-tag` is not specified, uses the last 50 commits.
- Includes closed issues in the "Related Issues" section.
- Supports `--format json` to output with statistics (commits_count, issues_count).
## Verification
- Unit tests cover:
- `TestReleaseLatest`: Basic latest release retrieval
- `TestReleaseLatestWithPrerelease`: Including prerelease versions
- `TestReleaseLatestSkipsDraft`: Skipping draft versions
- `TestReleaseLatestNoReleases`: Error handling when no releases
- `TestReleaseAutoNotes`: Basic auto-notes generation
- `TestReleaseAutoNotesWithFromTag`: Using from-tag parameter
- `TestReleaseAutoNotesJSONFormat`: JSON format output with statistics

View File

@ -1,88 +0,0 @@
# Repository File Read, Search, and Batch Commit Shortcuts
## Summary
This change adds repository file workflow shortcuts for users and automation agents that need to read file content, find files, and commit multiple file changes without manually assembling Raw API calls.
## Commands
| Command | Purpose |
|---------|---------|
| `gitlink-cli repo +file` | Read one repository file with content, commit info, and download metadata |
| `gitlink-cli repo +files` | Search repository files by name with optional branch, tag, or commit filtering |
| `gitlink-cli repo +commit-files` | Commit one file operation or a batch JSON operation list through the `contents/batch` API |
## Examples
Search files on a branch:
```bash
gitlink-cli repo +files --owner Gitlink --repo forgeplus --search README --ref main
```
Read one file from a branch:
```bash
gitlink-cli repo +file --owner Gitlink --repo forgeplus --path README.md --ref main
```
Update one text file:
```bash
gitlink-cli repo +commit-files --owner Gitlink --repo forgeplus \
--branch main --message "docs: update README" \
--path README.md --content "# Project"
```
Create or update a binary file by reading local bytes and encoding them as base64:
```bash
gitlink-cli repo +commit-files --owner Gitlink --repo forgeplus \
--branch main --message "assets: update logo" \
--action update --path assets/logo.png --from ./logo.png --encoding base64
```
Preview a multi-file commit before sending it:
```bash
gitlink-cli repo +commit-files --owner Gitlink --repo forgeplus \
--branch main --new-branch docs/batch-update \
--message "docs: batch update" --ops changes.json --dry-run
```
`changes.json` can be either an array of file operations:
```json
[
{
"action_type": "create",
"file_path": "docs/guide.md",
"content": "# Guide\n",
"encoding": "text"
},
{
"action_type": "delete",
"file_path": "docs/old-guide.md"
}
]
```
or an object with a `files` array. The CLI supplies `branch`, `message`, optional author and committer fields, and optional `new_branch` from command flags.
## Validation
- `repo +commit-files` requires `--branch` and `--message`.
- Single-file mode requires `--path`; `create` and `update` require exactly one of `--content` or `--from`.
- `delete` operations reject `content` and `encoding` so the request body matches the API intent.
- `--encoding` accepts only `text` and `base64`.
- `--ops` cannot be combined with single-file flags.
- Author and committer names must be provided together with their matching email fields.
- `--dry-run` prints the resolved request and does not call the remote API.
## Tests
Unit tests cover direct file reads through `sub_entries`, file search query mapping, single-file request bodies, base64 local file reading, JSON batch operation files, dry-run behavior, and validation failures that must not perform an API request.
## 中文说明
本次变更把仓库文件工作流补成了一个更完整的闭环:先用 `repo +file` 直接读取指定文件内容和元数据,用 `repo +files` 按文件名和分支搜索仓库文件,再用 `repo +commit-files` 把单个或多个文件变更提交到目标分支。批量提交支持创建新分支、设置提交信息、指定作者和提交者、从本地文件读取内容、对二进制内容做 base64 编码,并提供 `--dry-run` 预览请求体适合脚本、CI 和 AI Agent 在真正写入仓库前检查即将提交的内容。验证覆盖了文件读取、端点路径、查询参数、请求体字段、JSON 批量文件、base64 编码和无效参数不触网等关键路径。

View File

@ -1,19 +0,0 @@
# repo +file 仓库文件读取快捷命令
`gitlink-cli repo` 已经支持查看仓库信息、README 和目录树,但当用户想直接读取 `go.mod`、`.gitignore`、配置文件、脚本或许可证内容时,仍然需要回退到 Raw API。对于脚本、Agent 和日常排查来说,这是一个很常见的能力缺口。
这次新增 `gitlink-cli repo +file`,把仓库任意文件读取封装成高层 Shortcut。命令基于 `GET /{owner}/{repo}/sub_entries` 的文件模式实现,支持 `--path` 指定仓库内文件路径,支持 `--ref` 读取指定分支、标签或提交,也支持 `--content-only` 只输出文件正文,方便直接做管道消费或作为 Agent 上下文输入。
为了让这个命令在真实使用里更顺手,这次还补了两个常见边界处理:
- `--path` 设为必填,并对空路径或仅 `/` 这类无效输入给出明确报错。
- 如果用户传入的是目录路径,而不是文件路径,命令会直接提示改用 `repo +tree`,避免得到难以理解的 API 结果。
测试覆盖了默认分支、显式 `--ref`、路径归一化、帮助参数注册、目录误传报错、`--content-only` 缺少内容报错以及结果扁平化输出等关键分支。
本次交付包含:
- 功能代码:`shortcuts/repo/repo.go`
- 单元测试:`shortcuts/repo/repo_test.go`
- 帮助文档更新:`README.md`、`README.zh-CN.md`、`skills/gitlink-repo/SKILL.md`、`skills/gitlink-repo/references/gitlink-repo-file.md`
- 变更说明:本文档

View File

@ -1,13 +0,0 @@
# Repo Mirror Sync Shortcut
Added `gitlink-cli repo-mirror +sync` for `POST /repositories/{id}/sync_mirror`.
Safety: supports `--dry-run`; validates `--id` as integer.
Validation:
```bash
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/repomirror ./shortcuts
go vet ./shortcuts/repomirror ./shortcuts
go run . repo-mirror --help
```

View File

@ -1,75 +0,0 @@
# Repository raw file shortcuts
## Background
Repository inspection workflows often need to read files such as `LICENSE`,
`README.md`, dependency manifests, and source files. Those workflows previously
had to use Raw API calls for `/raw/<ref>/<path>` and `sub_entries`.
This change adds first-class read-only shortcuts for raw file content and common
manifest discovery.
## New shortcuts
- `repo +raw` reads raw file content at a branch, tag, or commit ref.
- `repo +file-exists` checks whether a repository file exists without returning
the full content.
- `repo +manifest` reads common dependency manifests for `go`, `node`,
`python`, `rust`, and `java`.
## Examples
```bash
gitlink-cli repo +raw --owner Gitlink --repo forgeplus --path LICENSE --ref master
gitlink-cli repo +file-exists --owner Gitlink --repo forgeplus --path package.json --ref master
gitlink-cli repo +manifest --owner Gitlink --repo forgeplus --kind go --ref master
```
## Manifest mapping
| Kind | Candidate files |
|------|-----------------|
| `go` | `go.mod` |
| `node` | `package.json` |
| `python` | `requirements.txt`, `pyproject.toml`, `Pipfile`, `setup.py` |
| `rust` | `Cargo.toml` |
| `java` | `pom.xml`, `build.gradle`, `build.gradle.kts` |
## Safety and path handling
- These shortcuts are read-only.
- `--ref` defaults to `master`.
- File paths are cleaned of leading/trailing slashes.
- Parent path segments (`..`) are rejected before making API requests.
- Raw paths are escaped segment by segment for paths containing spaces or other
special characters.
## Documentation updates
- README and README.zh-CN include raw file, file existence, and manifest examples.
- `skills/gitlink-repo` documents the new shortcuts.
- `skills/gitlink-compliance` and `skills/gitlink-code-review` now prefer
shortcuts over Raw API calls for file tree and content reads.
## Tests
Unit tests cover:
- raw file path construction and default refs;
- file existence checks through `sub_entries`;
- manifest kind mapping and fallback behavior;
- missing manifests;
- invalid paths and unknown manifest kinds;
- HTTP error propagation.
Suggested verification:
```bash
go test ./shortcuts/repo ./shortcuts
```
Full project verification:
```bash
go test ./...
```

View File

@ -1,7 +0,0 @@
# 完善仓库 README 快捷命令
`repo +readme` 现在补齐了中英文帮助文案、README 使用示例和 `gitlink-repo` Skill 说明,日常查看仓库 README 或子目录 README 时不再需要退回到 Raw API。
命令会把 `--ref` 传给 README API 的 `ref` 查询参数,把 `--path` 规范化后传给 `filepath` 查询参数,支持类似 `--path docs`、`--path /docs/guide/` 的子目录 README 查询方式。
本次变更同时补充了仓库 README 快捷命令的单元测试覆盖默认查询、指定分支和子目录、HTTP 错误返回等路径,确保后续调整不会破坏请求路径或查询参数。

View File

@ -1,66 +0,0 @@
# repo +rename 仓库重命名命令
## 背景
`gitlink-cli repo` 已提供创建、Fork、删除等仓库管理能力但缺少对标 `gh repo rename` 的重命名命令。用户或 AI Agent 想改名当前仓库时,过去只能手动调用 PATCH 更新项目接口。
本次变更把重命名封装为 `repo +rename`,与 GitHub CLI 的 `gh repo rename` 语义对齐。
## 变更内容
- 新增 `gitlink-cli repo +rename` Shortcut。
- 调用 `PATCH /{owner}/{repo}` 更新项目,请求体同时设置 `name`(项目名称)与 `identifier`(项目标识)。
- 支持 `--name, -n` 指定新名称,必填;空值或纯空白会在调用接口前被拒绝。
- 复用现有仓库上下文解析、API 调用和统一输出格式。
- 补充中英文 i18n 文案,避免命令帮助信息硬编码。
## 命令示例
```bash
# 重命名当前仓库owner/repo 从 git remote 自动解析)
gitlink-cli repo +rename --name new-name
# 显式指定仓库
gitlink-cli repo +rename --owner Gitlink --repo forgeplus --name new-name
```
## 参数说明
| 参数 | 必填 | 说明 |
|------|------|------|
| `--name, -n` | 是 | 新的仓库名称与标识 |
| `--owner` | 否 | 全局参数,仓库所有者,可从 git remote 自动解析 |
| `--repo` | 否 | 全局参数,仓库名称,可从 git remote 自动解析 |
| `--format` | 否 | 全局参数,输出格式:`json`、`table` 或 `yaml` |
## API 映射
| Shortcut | Method | API path | 请求体字段 |
|----------|--------|----------|-----------|
| `repo +rename` | PATCH | `/api/{owner}/{repo}.json` | `name`、`identifier` |
参照 API 文档「PATCH 更新项目」与「PATCH 更新项目(完整)」,`name` 与 `identifier` 均为必填。`name` 是展示用项目名称,`identifier` 是 URL 标识(等价于 `gh repo rename` 改动的仓库 URL 标识),两者都从 `--name` 取值。
## 兼容性提示
重命名会同时修改 `identifier`,也就是仓库的 URL 标识,因此**克隆地址会随之变化**(与 `gh repo rename` 行为一致)。执行后本地已有的 remote 需要相应更新。
## 测试覆盖
- 表驱动单元测试断言请求方法为 `PATCH`、路径为 `/owner/repo.json`,且请求体 `name``identifier` 均为新名称。
- 覆盖前后空白被裁剪的场景。
- 缺失或纯空白的 `--name` 在调用接口前即返回校验错误,不触发任何 HTTP 请求。
验证命令:
```bash
go build ./...
go test ./shortcuts/repo/
```
## 交付要求核对
- 功能代码:`shortcuts/repo/repo.go`
- 单元测试:`shortcuts/repo/repo_test.go`
- i18n 文案:`internal/i18n/locales/en-US.json`、`internal/i18n/locales/zh-CN.json`
- 变更说明文档:`doc/changes/repo-rename.md`

Some files were not shown because too many files have changed in this diff Show More