diff --git a/README.md b/README.md
index 8969ac3..b13f1ef 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[](https://golang.org)
[](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, issue tracking, pull requests, webhooks, notifications, 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)**
@@ -78,12 +78,20 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
jiangtx
+
+
## 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
-- **Wide Coverage** — Repository, Issue, PR, Webhook, Notification, Member, Branch, Release, CI, Pipeline, Org, Search, and User workflows are covered by high-level commands
+- **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, 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
- **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`
@@ -96,7 +104,7 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
| Category | Capabilities |
|----------|-------------|
| 📦 Repo | List, create, fork, delete repositories, view repo info, insights, and interactions |
-| 🐛 Issue | Create, update, close, batch close, comment on issues |
+| 🐛 Issue | Create, update, close, batch close/update/delete, comment on issues |
| 🔖 Label | Create, list, update, delete issue labels |
| 🔀 PR | Create, merge, review pull requests, view changed files |
| 👥 Member | List, add, remove repository members, change roles, create and accept invite links |
@@ -106,9 +114,11 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
| 🔧 CI | View builds, logs, CI/CD operations |
| ⚙️ Pipeline | Run, inspect, enable, disable, delete pipeline workflows and logs |
| 🔔 Webhook | Manage repo webhooks and test deliveries |
-| 🔔 Notification | List messages, mark read, delete, create @me notifications, manage message settings |
+| 📖 Wiki | List, view, create, update, and delete wiki pages |
| 🔍 Search | Search repositories, users |
+| 📊 Dataset | Query research datasets by project |
| 👤 User | View user profiles and info |
+| 📊 Profile | User ability, role, major, activity, and contribution statistics |
| 📋 PM | Sprint management, kanban boards, weekly reports |
| 🤖 Workflow | AI-powered issue triage, PR review, release notes |
@@ -264,6 +274,28 @@ gitlink-cli webhook +test --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
```bash
@@ -313,6 +345,14 @@ gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,12
# Batch close issues from a CSV file
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
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "Fixed"
@@ -468,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
```
+### Ignore File Templates
+
+```bash
+# List all available .gitignore templates
+gitlink-cli ignore +list
+
+# Filter templates by name
+gitlink-cli ignore +list --name Go
+```
+
### Search
```bash
@@ -478,25 +528,39 @@ gitlink-cli search +repos -k "machine learning"
gitlink-cli search +users -k "zhangsan"
```
-### Notification Management
+### User Profile
+
+`profile` surfaces GitLink's native user statistics (ability, role, major, activity,
+contribution). When `--user` is omitted it defaults to the authenticated user.
+`user` also provides contributor-oriented shortcuts for heatmaps, aggregate
+statistics, and project trends.
```bash
-# List unread @me messages
-gitlink-cli notification +list --user zhangsan --type atme --status unread
+# Current authenticated user
+gitlink-cli user +me
-# Preview marking messages as read
-gitlink-cli notification +mark-read --user zhangsan --ids 101,102 --dry-run
+# User contribution heatmap and aggregate statistics
+gitlink-cli user +heatmap --user zhangsan --year 2026
+gitlink-cli user +statistics --user zhangsan --start-time 1704067200 --end-time 1735689600
-# Delete messages by IDs
-gitlink-cli notification +delete --user zhangsan --ids 101,102 --type notification
+# Project trend data (short alias: user +trends)
+gitlink-cli user +project-trends --user zhangsan
-# Create @me notifications attached to an issue, PR, or journal
-gitlink-cli notification +create-atme --user zhangsan --receivers lisi,wangwu --atmeable-type Issue --atmeable-id 99 --dry-run
+# Development ability scores + language breakdown
+gitlink-cli profile +ability --user zhangsan
-# Inspect and update message settings while preserving unspecified keys
-gitlink-cli notification +platform-settings
-gitlink-cli notification +settings --user zhangsan
-gitlink-cli notification +settings-update --user zhangsan --notification Normal::Project=true --email Normal::Project=false --dry-run
+# 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
@@ -598,6 +662,32 @@ Safety:
- `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.
+### 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 --dry-run
+gitlink-cli dataset +delete-attachment --owner me --repo proj --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
For endpoints not covered by shortcuts, use the Raw API directly:
@@ -654,13 +744,13 @@ git push gitlink
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 |
|-------|-------------|
| `gitlink-shared` | Authentication, global parameters, safety rules, API notes |
| `gitlink-repo` | Repository operations (create, view, delete, fork, insights, etc.) |
-| `gitlink-issue` | Issue operations (create, update, close, comment, etc.) |
+| `gitlink-issue` | Issue operations (create, update, close, batch update/delete, comment, etc.) |
| `gitlink-pr` | Pull request operations (create, merge, review, etc.) |
| `gitlink-member` | Repository member and invite link management |
| `gitlink-branch` | Branch management (create, delete, list, protect, unprotect) |
@@ -669,8 +759,7 @@ See [skills/README.md](skills/README.md) for details.
| `gitlink-pipeline` | Pipeline workflow operations (runs, logs, enable, disable, delete, etc.) |
| `gitlink-search` | Search (repositories, users, etc.) |
| `gitlink-org` | Organization management (members, teams, etc.) |
-| `gitlink-user` | User management (profile info, etc.) |
-| `gitlink-notification` | Notification/message operations and settings |
+| `gitlink-user` | User management (profile info, heatmaps, statistics, project trends, etc.) |
| `gitlink-pm` | Project management (sprints, kanban, weekly reports, etc.) |
| `gitlink-workflow` | AI-powered workflows (issue triage, PR review, release notes, etc.) |
| `gitlink-health` | Project health analysis (PR/Issue metrics aggregation, health reports) |
@@ -704,7 +793,6 @@ gitlink-cli/
│ ├── pipeline/ # Pipeline shortcuts
│ ├── search/ # Search shortcuts
│ ├── user/ # User shortcuts
-│ ├── notification/ # Notification shortcuts
│ └── register.go # Registration entry point
├── skills/ # AI Agent Skills
│ ├── README.md # Skills guide
@@ -712,7 +800,6 @@ gitlink-cli/
│ ├── gitlink-repo/ # Repository skill
│ ├── gitlink-issue/ # Issue skill
│ ├── gitlink-pr/ # PR skill
-│ ├── gitlink-notification/ # Notification skill
│ ├── gitlink-pm/ # Project management skill
│ └── ...
├── doc/ # Design documents
@@ -727,7 +814,7 @@ gitlink-cli/
## 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
## FAQ
@@ -792,23 +879,8 @@ gitlink-cli uses Windows Credential Manager for secure token storage. If Credent
### 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
[MulanPSL-2.0](https://license.coscl.org.cn/MulanPSL2)
-
-### Commits
-
-```bash
-# List commits (optionally from a ref)
-gitlink-cli commit +list --owner Gitlink --repo forgeplus -s develop
-
-# Recent commits filtered by keyword
-gitlink-cli commit +recent --owner Gitlink --repo forgeplus -k fix
-
-# Diff and changed files of a commit
-gitlink-cli commit +diff --owner Gitlink --repo forgeplus -s
-gitlink-cli commit +files --owner Gitlink --repo forgeplus -s -f app/models/user.rb
-```
-
diff --git a/README.zh-CN.md b/README.zh-CN.md
index c873aa9..04499a3 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -5,7 +5,7 @@
[](https://golang.org)
[](https://www.npmjs.com/package/@gitlink-ai/cli)
-[GitLink(确实开源)](https://www.gitlink.org.cn) 官方 CLI 工具 — 为人类和 AI Agent 双重设计。支持 **macOS、Linux、Windows**,覆盖仓库管理、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)**
@@ -78,12 +78,20 @@
jiangtx
+
+
## 为什么选择 gitlink-cli?
-- **Agent-Native 设计** — 开箱即用结构化 [Skills](./skills/),兼容 Claude Code — Agent 零配置即可操作 GitLink
-- **广泛覆盖** — 仓库、Issue、PR、Webhook、通知消息、成员、分支、Release、CI、Pipeline、组织、搜索、用户等常用工作流均提供高层命令
+- **Agent-Native 设计** — 开箱即用结构化 [Skills](./skills/README.md),兼容 Claude Code — Agent 零配置即可操作 GitLink
+- **广泛覆盖** — 仓库、Issue、PR、Webhook、成员、分支、Release、CI、Pipeline、组织、搜索、用户等常用工作流均提供高层命令
- **AI 友好 & 优化** — 每条命令都经过真实 Agent 测试,简洁参数、智能默认值、结构化输出
- **跨平台** — macOS、Linux、Windows (x64/arm64) 全支持,`npm` 一条命令安装
- **开源零门槛** — 木兰宽松许可证第2版(MulanPSL-2.0),`npm install` 即用
@@ -96,7 +104,7 @@
| 分类 | 能力 |
|------|------|
| 📦 仓库 | 列出、创建、Fork、删除仓库,查看仓库信息、洞察数据和互动状态 |
-| 🐛 Issue | 创建、更新、关闭、批量关闭、评论 Issue |
+| 🐛 Issue | 创建、更新、关闭、批量关闭/更新/删除、评论 Issue |
| 🔖 标签 | 创建、列出、更新、删除 Issue 标签 |
| 🔀 PR | 创建、合并、Review Pull Request,查看变更文件 |
| 👥 成员 | 列出、添加、移除仓库成员,调整角色,生成和接受邀请链接 |
@@ -105,9 +113,11 @@
| 🏢 组织 | 管理组织、成员、团队 |
| 🔧 CI | 查看构建、日志、CI/CD 操作 |
| ⚙️ Pipeline | 运行、查看、启停、删除流水线工作流并查询日志 |
-| 🔔 通知消息 | 查看消息、标记已读、删除消息、创建 @我通知、管理消息设置 |
+| 📖 Wiki | 列出、查看、创建、更新、删除 Wiki 页面 |
| 🔍 搜索 | 搜索仓库、用户 |
+| 📊 数据集 | 按项目查询科研数据集 |
| 👤 用户 | 查看用户资料和信息 |
+| 📊 画像 | 用户开发能力、角色定位、专业定位、近期活动、贡献热力图统计 |
| 📋 项目管理 | Sprint 管理、看板、周报 |
| 🤖 工作流 | AI 驱动的 Issue 分类、PR Review、Release Notes |
@@ -275,6 +285,28 @@ gitlink-cli webhook +test --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
@@ -324,6 +356,14 @@ gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,12
# 从 CSV 文件批量关闭 Issue
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 "已修复"
@@ -446,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
```
+### 忽略文件模板
+
+```bash
+# 列出所有可用的 .gitignore 模板
+gitlink-cli ignore +list
+
+# 按名称筛选模板
+gitlink-cli ignore +list --name Go
+```
+
### 搜索
```bash
@@ -456,27 +506,61 @@ gitlink-cli search +repos -k "machine learning"
gitlink-cli search +users -k "zhangsan"
```
-### 通知消息管理
+### 用户画像
+
+`profile` 暴露 GitLink 原生的用户画像统计(开发能力、角色定位、专业定位、近期活动、贡献热力图)。
+省略 `--user` 时默认使用当前认证用户。
+`user` 同时提供面向贡献者分析的热力图、聚合统计和项目趋势快捷入口。
```bash
-# 查看未读 @我消息
-gitlink-cli notification +list --user zhangsan --type atme --status unread
+# 当前认证用户
+gitlink-cli user +me
-# 预览标记已读,不修改线上数据
-gitlink-cli notification +mark-read --user zhangsan --ids 101,102 --dry-run
+# 用户贡献热力图和聚合统计
+gitlink-cli user +heatmap --user zhangsan --year 2026
+gitlink-cli user +statistics --user zhangsan --start-time 1704067200 --end-time 1735689600
-# 按 ID 删除消息
-gitlink-cli notification +delete --user zhangsan --ids 101,102 --type notification
+# 项目趋势数据(短别名:user +trends)
+gitlink-cli user +project-trends --user zhangsan
-# 创建绑定到 Issue、PR 或 Journal 的 @我通知
-gitlink-cli notification +create-atme --user zhangsan --receivers lisi,wangwu --atmeable-type Issue --atmeable-id 99 --dry-run
+# 开发能力评分 + 语言分布
+gitlink-cli profile +ability --user zhangsan
-# 查看并更新消息设置,未指定的配置会保留原值
-gitlink-cli notification +platform-settings
-gitlink-cli notification +settings --user zhangsan
-gitlink-cli notification +settings-update --user zhangsan --notification Normal::Project=true --email Normal::Project=false --dry-run
+# 角色定位 / 专业(学科)定位
+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 --dry-run
+gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid --yes
+```
+
+> 注意:`dataset +list`(平台数据集查询)已在生产 gitlink.org.cn 验证可用。按仓库的 `+view`/`+create`/`+update` 遵循已发布的 OpenAPI 契约,但生产环境尚未部署(当前返回 404),待平台上线后即可生效。
### Raw API
Shortcuts 未覆盖的接口可通过 Raw API 直接调用:
@@ -533,13 +617,13 @@ git push gitlink
`skills/` 目录包含 Claude Code Agent Skill 文件,支持 AI 自动化操作 GitLink 平台。
-详见 [skills/README.md](skills/README.md)
+详见 [skills/README.md](./skills/README.md)
| Skill | 说明 |
|-------|------|
| `gitlink-shared` | 认证、全局参数、安全规则、API 注意事项 |
| `gitlink-repo` | 仓库操作(创建、查看、删除、Fork、洞察数据等) |
-| `gitlink-issue` | Issue 操作(创建、更新、关闭、评论等) |
+| `gitlink-issue` | Issue 操作(创建、更新、关闭、批量更新/删除、评论等) |
| `gitlink-pr` | Pull Request 操作(创建、合并、Review 等) |
| `gitlink-member` | 仓库成员与邀请链接管理 |
| `gitlink-release` | 发布管理(创建、编辑、更新、查看、删除等) |
@@ -547,8 +631,7 @@ git push gitlink
| `gitlink-ci` | CI/CD 操作(构建、日志等) |
| `gitlink-pipeline` | 流水线工作流操作(运行、日志、启停、删除等) |
| `gitlink-search` | 搜索功能(仓库、用户等) |
-| `gitlink-user` | 用户管理(个人信息等) |
-| `gitlink-notification` | 通知消息操作与设置管理 |
+| `gitlink-user` | 用户管理(个人信息、热力图、统计、项目趋势等) |
| `gitlink-pm` | 项目管理(Sprint、看板、周报等) |
| `gitlink-workflow` | AI 自动化工作流(Issue 分类、PR Review、Release Notes 等) |
@@ -581,7 +664,6 @@ gitlink-cli/
│ ├── pipeline/ # Pipeline shortcuts
│ ├── search/ # 搜索 shortcuts
│ ├── user/ # 用户 shortcuts
-│ ├── notification/ # 通知消息 shortcuts
│ └── register.go # 注册入口
├── skills/ # AI Agent Skills
│ ├── README.md # Skills 使用指南
@@ -589,7 +671,6 @@ gitlink-cli/
│ ├── gitlink-repo/ # 仓库 Skill
│ ├── gitlink-issue/ # Issue Skill
│ ├── gitlink-pr/ # PR Skill
-│ ├── gitlink-notification/ # 通知消息 Skill
│ ├── gitlink-pm/ # 项目管理 Skill
│ └── ...
├── doc/ # 设计文档
@@ -604,7 +685,7 @@ gitlink-cli/
## 文档
-- [Skills 使用指南](skills/README.md) — AI Agent Skills 详细说明
+- [Skills 使用指南](./skills/README.md) — AI Agent Skills 详细说明
- [设计文档](doc/design.md) — 架构设计和开发计划
## 常见问题
@@ -667,7 +748,7 @@ gitlink-cli 使用 Windows Credential Manager 安全存储 Token。如果 Creden
### 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)
## 许可证
diff --git a/doc/changes/user-statistics-shortcuts.md b/doc/changes/user-statistics-shortcuts.md
index 7ace445..bafebc3 100644
--- a/doc/changes/user-statistics-shortcuts.md
+++ b/doc/changes/user-statistics-shortcuts.md
@@ -1,36 +1,41 @@
# User Statistics Shortcuts
-## Background
+## Summary
-GitLink OpenAPI exposes user analytics endpoints, including activity, contribution heatmap, development capability, role distribution, and professional categories. gitlink-cli previously only exposed `user +me` and `user +info`.
-
-## What Changed
-
-Extended the `user` shortcut group with read-only statistics commands:
-
-- `user +activity`
-- `user +headmap`
-- `user +develop`
-- `user +role`
-- `user +major`
-
-The commands support `--login`, with fallback to `--owner` or `/users/me`. Range-based endpoints support `--start-time` and `--end-time`; heatmap supports `--year`.
-
-## OpenAPI Coverage
-
-- `GET /users/{owner}/statistics/activity`
-- `GET /users/{owner}/headmaps`
-- `GET /users/{owner}/statistics/develop`
-- `GET /users/{owner}/statistics/role`
-- `GET /users/{owner}/statistics/major`
-
-## Validation
+Adds user-centered statistics shortcuts for contributor analysis workflows:
```bash
-git diff --check
-GOPROXY=https://goproxy.cn,direct go test ./shortcuts/user ./shortcuts
-go vet ./shortcuts/user ./shortcuts
-go run . user --help
-GOPROXY=https://goproxy.cn,direct go test ./...
-go vet ./...
+gitlink-cli user +heatmap --user zhangsan --year 2026
+gitlink-cli user +statistics --user zhangsan
+gitlink-cli user +stats --user zhangsan
+gitlink-cli user +project-trends --user zhangsan
+gitlink-cli user +trends --user zhangsan
+```
+
+## Behavior
+
+- `user +heatmap` calls `GET /users/{user}/headmaps`.
+- `user +statistics` and alias `user +stats` call `GET /users/{user}/statistics`.
+- `user +project-trends` and alias `user +trends` call `GET /users/{user}/project_trends`.
+- `--user` is optional. When omitted, the shortcut resolves the current authenticated user via `/users/me`.
+- `--year` is supported by `user +heatmap`.
+- `--start-time` and `--end-time` are supported by statistics and project trend commands.
+
+## Why
+
+The `gitlink-user` Skill previously documented heatmaps, statistics, and project trends as Raw API calls. Contributor insight workflows also marked `user +heatmap`, `user +stats`, and `user +trends` as unavailable, forcing agents to approximate data from PR lists. These shortcuts expose the read-only user statistics endpoints directly and make contributor analysis more accurate.
+
+## Documentation
+
+- Updates README examples in English and Chinese.
+- Updates `skills/README.md`.
+- Updates `skills/gitlink-user/SKILL.md` to prefer shortcuts over Raw API.
+- Adds dedicated `gitlink-user` reference pages for heatmaps, statistics, and project trends.
+- Updates contributor insight guidance to use the new shortcuts when available.
+
+## Verification
+
+```bash
+go test ./shortcuts/user ./shortcuts
+go test ./...
```
diff --git a/internal/i18n/locales/en-US.json b/internal/i18n/locales/en-US.json
index 00f6cdd..aee9560 100644
--- a/internal/i18n/locales/en-US.json
+++ b/internal/i18n/locales/en-US.json
@@ -98,9 +98,14 @@
"cmd.search.repos.short": "Search repositories",
"cmd.search.short": "Search operations",
"cmd.search.users.short": "Search users",
+ "cmd.user.heatmap.short": "Show user contribution heatmap",
"cmd.user.info.short": "Show user profile",
"cmd.user.me.short": "Show current authenticated user",
+ "cmd.user.project_trends.short": "Show user project trends",
"cmd.user.short": "User operations",
+ "cmd.user.statistics.short": "Show user statistics",
+ "cmd.user.stats.short": "Show user statistics",
+ "cmd.user.trends.short": "Show user project trends",
"cmd.version.short": "Print version information",
"cmd.webhook.create.short": "Create a repository webhook",
"cmd.webhook.delete.short": "Delete a repository webhook",
@@ -119,6 +124,7 @@
"error.missing_required_flag": "required flag --{name} is missing",
"error.profile.user_required": "could not determine target user; pass --user or run gitlink-cli auth login",
"error.unsupported_language": "unsupported language: {lang}",
+ "error.user.required": "could not determine target user; pass --user or run gitlink-cli auth login",
"flag.api.batch_continue_on_error": "Continue running remaining batch requests after a failure",
"flag.api.batch_dry_run": "Preview batch requests without sending remote requests",
"flag.api.batch_file": "Read an API batch plan from a JSON file",
@@ -227,7 +233,10 @@
"flag.sort_by": "Sort field",
"flag.sort_direction": "Sort direction: asc, desc",
"flag.user": "User login (default: current user)",
+ "flag.user.end_time": "End time (Unix timestamp)",
"flag.user.login": "User login name",
+ "flag.user.start_time": "Start time (Unix timestamp)",
+ "flag.user.year": "Heatmap year (for example: 2026)",
"flag.webhook.active": "Whether the webhook is active: true or false",
"flag.webhook.branch_filter": "Branch glob filter for push/create/delete events",
"flag.webhook.content_type": "Payload content type: json or form",
diff --git a/internal/i18n/locales/zh-CN.json b/internal/i18n/locales/zh-CN.json
index 6b6cbf1..6fb482c 100644
--- a/internal/i18n/locales/zh-CN.json
+++ b/internal/i18n/locales/zh-CN.json
@@ -98,9 +98,14 @@
"cmd.search.repos.short": "搜索仓库",
"cmd.search.short": "搜索操作",
"cmd.search.users.short": "搜索用户",
+ "cmd.user.heatmap.short": "显示用户贡献热力图",
"cmd.user.info.short": "显示用户资料",
"cmd.user.me.short": "显示当前认证用户",
+ "cmd.user.project_trends.short": "显示用户项目趋势",
"cmd.user.short": "用户操作",
+ "cmd.user.statistics.short": "显示用户统计信息",
+ "cmd.user.stats.short": "显示用户统计信息",
+ "cmd.user.trends.short": "显示用户项目趋势",
"cmd.version.short": "打印版本信息",
"cmd.webhook.create.short": "创建仓库 Webhook",
"cmd.webhook.delete.short": "删除仓库 Webhook",
@@ -119,6 +124,7 @@
"error.missing_required_flag": "缺少必需参数 --{name}",
"error.profile.user_required": "无法确定目标用户;请通过 --user 指定,或先运行 gitlink-cli auth login 登录",
"error.unsupported_language": "不支持的语言:{lang}",
+ "error.user.required": "无法确定目标用户;请通过 --user 指定,或先运行 gitlink-cli auth login 登录",
"flag.api.batch_continue_on_error": "批处理请求失败后继续执行后续请求",
"flag.api.batch_dry_run": "预览批处理请求,不发送远端请求",
"flag.api.batch_file": "从 JSON 文件读取 API 批处理计划",
@@ -227,7 +233,10 @@
"flag.sort_by": "排序字段",
"flag.sort_direction": "排序方向:asc、desc",
"flag.user": "用户登录名(默认:当前用户)",
+ "flag.user.end_time": "结束时间(Unix 时间戳)",
"flag.user.login": "用户登录名",
+ "flag.user.start_time": "开始时间(Unix 时间戳)",
+ "flag.user.year": "热力图年份(例如:2026)",
"flag.webhook.active": "Webhook 是否启用:true 或 false",
"flag.webhook.branch_filter": "用于 push/create/delete 事件的分支 glob 筛选",
"flag.webhook.content_type": "Payload 内容类型:json 或 form",
diff --git a/shortcuts/user/user.go b/shortcuts/user/user.go
index db2a02e..4a2ba22 100644
--- a/shortcuts/user/user.go
+++ b/shortcuts/user/user.go
@@ -3,17 +3,22 @@ package user
import (
"fmt"
"net/url"
- "os"
- "path/filepath"
- "strconv"
- "strings"
"github.com/gitlink-org/gitlink-cli/internal/i18n"
+ "github.com/gitlink-org/gitlink-cli/internal/output"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
tr := shortcutTranslator(translators...)
+ userFlag := common.Flag{Name: "user", Short: "u", Usage: tr.T("flag.user")}
+ yearFlag := common.Flag{Name: "year", Usage: tr.T("flag.user.year")}
+ timeFlags := []common.Flag{
+ {Name: "start-time", Usage: tr.T("flag.user.start_time")},
+ {Name: "end-time", Usage: tr.T("flag.user.end_time")},
+ }
+ windowFlags := append([]common.Flag{userFlag}, timeFlags...)
+
return []*common.Shortcut{
{
Name: "me",
@@ -45,17 +50,19 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
},
},
{
- Name: "keys",
- Description: tr.T("cmd.user.keys.short"),
- Flags: []common.Flag{
- {Name: "page", Short: "p", Usage: tr.T("flag.page"), Default: "1"},
- {Name: "limit", Short: "l", Usage: tr.T("flag.limit"), Default: "20"},
- },
+ Name: "heatmap",
+ Description: tr.T("cmd.user.heatmap.short"),
+ Flags: []common.Flag{userFlag, yearFlag},
Run: func(ctx *common.RuntimeContext) error {
+ user, err := resolveUser(ctx)
+ if err != nil {
+ return err
+ }
q := url.Values{}
- q.Set("page", ctx.Arg("page"))
- q.Set("limit", ctx.Arg("limit"))
- env, err := ctx.CallAPIWithQuery("GET", publicKeysPath(), q)
+ if v := ctx.Arg("year"); v != "" {
+ q.Set("year", v)
+ }
+ env, err := ctx.CallAPIWithQuery("GET", fmt.Sprintf("/users/%s/headmaps", user), q)
if err != nil {
return err
}
@@ -63,121 +70,87 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
},
},
{
- Name: "add-key",
- Description: tr.T("cmd.user.add_key.short"),
- Flags: []common.Flag{
- {Name: "title", Short: "t", Usage: tr.T("flag.user.key.title")},
- {Name: "key", Short: "k", Usage: tr.T("flag.user.key.content")},
- {Name: "from", Short: "f", Usage: tr.T("flag.user.key.from")},
- },
+ Name: "statistics",
+ Description: tr.T("cmd.user.statistics.short"),
+ Flags: windowFlags,
Run: func(ctx *common.RuntimeContext) error {
- key, err := publicKeyContent(ctx.Arg("key"), ctx.Arg("from"))
- if err != nil {
- return err
- }
- title, err := publicKeyTitle(ctx.Arg("title"), ctx.Arg("from"))
- if err != nil {
- return err
- }
- env, err := ctx.CallAPI("POST", publicKeysPath(), map[string]interface{}{
- "title": title,
- "key": key,
- })
- if err != nil {
- return err
- }
- return ctx.Output(env)
+ return runWindowedUserGet(ctx, "/users/%s/statistics")
},
},
{
- Name: "delete-key",
- Description: tr.T("cmd.user.delete_key.short"),
- Flags: []common.Flag{
- {Name: "id", Short: "i", Usage: tr.T("flag.user.key.id"), Required: true},
- },
+ Name: "stats",
+ Description: tr.T("cmd.user.stats.short"),
+ Flags: windowFlags,
Run: func(ctx *common.RuntimeContext) error {
- id, err := ctx.RequireArg("id")
- if err != nil {
- return err
- }
- id = strings.TrimSpace(id)
- if id == "" {
- return fmt.Errorf("required flag --id is missing")
- }
- keyID, err := parsePublicKeyID(id)
- if err != nil {
- return err
- }
- env, err := ctx.CallAPI("DELETE", fmt.Sprintf("%s/%d", publicKeysPath(), keyID), nil)
- if err != nil {
- return err
- }
- return ctx.Output(env)
+ return runWindowedUserGet(ctx, "/users/%s/statistics")
+ },
+ },
+ {
+ Name: "project-trends",
+ Description: tr.T("cmd.user.project_trends.short"),
+ Flags: windowFlags,
+ Run: func(ctx *common.RuntimeContext) error {
+ return runWindowedUserGet(ctx, "/users/%s/project_trends")
+ },
+ },
+ {
+ Name: "trends",
+ Description: tr.T("cmd.user.trends.short"),
+ Flags: windowFlags,
+ Run: func(ctx *common.RuntimeContext) error {
+ return runWindowedUserGet(ctx, "/users/%s/project_trends")
},
},
}
}
+func runWindowedUserGet(ctx *common.RuntimeContext, pathFormat string) error {
+ user, err := resolveUser(ctx)
+ if err != nil {
+ return err
+ }
+ q := url.Values{}
+ if v := ctx.Arg("start-time"); v != "" {
+ q.Set("start_time", v)
+ }
+ if v := ctx.Arg("end-time"); v != "" {
+ q.Set("end_time", v)
+ }
+ env, err := ctx.CallAPIWithQuery("GET", fmt.Sprintf(pathFormat, user), q)
+ if err != nil {
+ return err
+ }
+ return ctx.Output(env)
+}
+
+func resolveUser(ctx *common.RuntimeContext) (string, error) {
+ if v := ctx.Arg("user"); v != "" {
+ return v, nil
+ }
+ env, err := ctx.CallAPI("GET", "/users/me", nil)
+ if err != nil {
+ return "", err
+ }
+ if login := extractLogin(env); login != "" {
+ return login, nil
+ }
+ return "", fmt.Errorf("%s", ctx.Tr.T("error.user.required"))
+}
+
+func extractLogin(env *output.Envelope) string {
+ data, ok := env.Data.(map[string]interface{})
+ if !ok {
+ return ""
+ }
+ if v, ok := data["login"].(string); ok {
+ return v
+ }
+ return ""
+}
+
func shortcutTranslator(translators ...*i18n.Translator) *i18n.Translator {
if len(translators) > 0 && translators[0] != nil {
return translators[0]
}
return i18n.Default()
}
-
-func parsePublicKeyID(raw string) (int, error) {
- id, err := strconv.Atoi(strings.TrimSpace(raw))
- if err != nil || id <= 0 {
- return 0, fmt.Errorf("SSH key ID must be a positive integer")
- }
- return id, nil
-}
-
-func publicKeysPath() string {
- return "/public_keys"
-}
-
-func publicKeyTitle(title, from string) (string, error) {
- title = strings.TrimSpace(title)
- if title != "" {
- return title, nil
- }
- from = strings.TrimSpace(from)
- if from != "" {
- if base := filepath.Base(from); base != "." && base != string(filepath.Separator) {
- return base, nil
- }
- }
- return "", fmt.Errorf("required SSH key title is missing; use --title or provide --from")
-}
-
-func publicKeyContent(inline, from string) (string, error) {
- inline = strings.TrimSpace(inline)
- from = strings.TrimSpace(from)
- if inline != "" && from != "" {
- return "", fmt.Errorf("use either --key or --from, not both")
- }
- if from != "" {
- content, err := os.ReadFile(from)
- if err != nil {
- return "", fmt.Errorf("read SSH public key file: %w", err)
- }
- inline = strings.TrimSpace(string(content))
- }
- if inline == "" {
- return "", fmt.Errorf("required SSH public key content is missing; use --key or --from")
- }
- if !hasPublicKeyPrefix(inline) {
- return "", fmt.Errorf("SSH public key content should start with ssh-rsa, ssh-dss, ssh-ed25519, ecdsa-sha2-, or sk-")
- }
- return inline, nil
-}
-
-func hasPublicKeyPrefix(key string) bool {
- for _, prefix := range []string{"ssh-rsa", "ssh-dss", "ssh-ed25519", "ecdsa-sha2-", "sk-"} {
- if strings.HasPrefix(key, prefix) {
- return true
- }
- }
- return false
-}
diff --git a/shortcuts/user/user_test.go b/shortcuts/user/user_test.go
index 3aad3ab..51d931a 100644
--- a/shortcuts/user/user_test.go
+++ b/shortcuts/user/user_test.go
@@ -4,8 +4,6 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
- "os"
- "path/filepath"
"testing"
"github.com/gitlink-org/gitlink-cli/internal/client"
@@ -36,12 +34,9 @@ func findShortcut(t *testing.T, name string) *common.Shortcut {
return nil
}
-func writeJSON(t *testing.T, w http.ResponseWriter, v interface{}) {
- t.Helper()
+func writeJSON(w http.ResponseWriter, v interface{}) {
w.Header().Set("Content-Type", "application/json")
- if err := json.NewEncoder(w).Encode(v); err != nil {
- t.Fatalf("write response: %v", err)
- }
+ json.NewEncoder(w).Encode(v)
}
// --- me ---
@@ -51,7 +46,7 @@ func TestUserMe(t *testing.T) {
if r.URL.Path != "/users/me.json" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
- writeJSON(t, w, map[string]interface{}{
+ writeJSON(w, map[string]interface{}{
"login": "currentuser",
"name": "Current User",
"id": float64(1),
@@ -72,7 +67,7 @@ func TestUserInfo(t *testing.T) {
if r.URL.Path != "/users/alice.json" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
- writeJSON(t, w, map[string]interface{}{
+ writeJSON(w, map[string]interface{}{
"login": "alice",
"name": "Alice",
})
@@ -97,189 +92,146 @@ func TestUserInfoMissingLogin(t *testing.T) {
}
}
-// --- SSH public keys ---
+// --- heatmap ---
-func TestUserKeysUsesPublicKeysEndpoint(t *testing.T) {
+func TestUserHeatmapExplicitUserWithYear(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method != "GET" || r.URL.Path != "/public_keys.json" {
- t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
+ if r.URL.Path != "/users/alice/headmaps.json" {
+ t.Fatalf("unexpected path: %s", r.URL.Path)
}
- if got := r.URL.Query().Get("page"); got != "2" {
- t.Fatalf("page query = %q, want 2", got)
+ if got := r.URL.Query().Get("year"); got != "2026" {
+ t.Fatalf("year = %q, want 2026", got)
}
- if got := r.URL.Query().Get("limit"); got != "50" {
- t.Fatalf("limit query = %q, want 50", got)
- }
- writeJSON(t, w, map[string]interface{}{
- "total_count": 1,
- "public_keys": []interface{}{
- map[string]interface{}{"id": 1, "name": "laptop"},
- },
+ writeJSON(w, map[string]interface{}{
+ "total_contributions": float64(12),
})
}))
defer server.Close()
- err := runShortcut(t, server, "keys", map[string]string{
- "page": "2",
- "limit": "50",
- })
+ err := runShortcut(t, server, "heatmap", map[string]string{"user": "alice", "year": "2026"})
if err != nil {
- t.Fatalf("keys shortcut failed: %v", err)
+ t.Fatalf("heatmap failed: %v", err)
}
}
-func TestUserAddKeySendsTitleAndInlineKey(t *testing.T) {
- const key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDemoKey user@example.com"
+func TestUserHeatmapDefaultsToCurrentUser(t *testing.T) {
+ var calls []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method != "POST" || r.URL.Path != "/public_keys.json" {
- t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
+ calls = append(calls, r.URL.Path)
+ switch r.URL.Path {
+ case "/users/me.json":
+ writeJSON(w, map[string]interface{}{"login": "currentuser"})
+ case "/users/currentuser/headmaps.json":
+ writeJSON(w, map[string]interface{}{"headmaps": []interface{}{}})
+ default:
+ t.Fatalf("unexpected path: %s", r.URL.Path)
}
- var body map[string]string
- if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
- t.Fatalf("decode request: %v", err)
- }
- if body["title"] != "laptop" {
- t.Fatalf("title = %q, want laptop", body["title"])
- }
- if body["key"] != key {
- t.Fatalf("key = %q, want %q", body["key"], key)
- }
- writeJSON(t, w, map[string]interface{}{
- "id": 2,
- "name": "laptop",
- "fingerprint": "SHA256:demo",
- })
}))
defer server.Close()
- err := runShortcut(t, server, "add-key", map[string]string{
- "title": "laptop",
- "key": key,
- })
+ err := runShortcut(t, server, "heatmap", map[string]string{})
if err != nil {
- t.Fatalf("add-key shortcut failed: %v", err)
+ t.Fatalf("heatmap failed: %v", err)
+ }
+ if len(calls) != 2 {
+ t.Fatalf("calls = %v, want 2 calls", calls)
}
}
-func TestUserAddKeyReadsKeyFromFileAndDefaultsTitle(t *testing.T) {
- const key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDemo user@example.com"
- path := filepath.Join(t.TempDir(), "id_rsa.pub")
- if err := os.WriteFile(path, []byte(" "+key+"\n"), 0o600); err != nil {
- t.Fatalf("write key file: %v", err)
- }
+// --- statistics ---
+func TestUserStatisticsWithTimeWindow(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method != "POST" || r.URL.Path != "/public_keys.json" {
- t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
+ if r.URL.Path != "/users/alice/statistics.json" {
+ t.Fatalf("unexpected path: %s", r.URL.Path)
}
- var body map[string]string
- if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
- t.Fatalf("decode request: %v", err)
+ if got := r.URL.Query().Get("start_time"); got != "100" {
+ t.Fatalf("start_time = %q, want 100", got)
}
- if body["title"] != "id_rsa.pub" {
- t.Fatalf("title = %q, want id_rsa.pub", body["title"])
+ if got := r.URL.Query().Get("end_time"); got != "200" {
+ t.Fatalf("end_time = %q, want 200", got)
}
- if body["key"] != key {
- t.Fatalf("key = %q, want %q", body["key"], key)
- }
- writeJSON(t, w, map[string]interface{}{"id": 3})
+ writeJSON(w, map[string]interface{}{
+ "issues_count": float64(3),
+ })
}))
defer server.Close()
- err := runShortcut(t, server, "add-key", map[string]string{"from": path})
+ args := map[string]string{"user": "alice", "start-time": "100", "end-time": "200"}
+ err := runShortcut(t, server, "statistics", args)
if err != nil {
- t.Fatalf("add-key from file failed: %v", err)
+ t.Fatalf("statistics failed: %v", err)
}
}
-func TestUserAddKeyRejectsAmbiguousKeySourcesBeforeRequest(t *testing.T) {
- called := false
+func TestUserStatsAlias(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- called = true
- }))
- defer server.Close()
-
- err := runShortcut(t, server, "add-key", map[string]string{
- "title": "laptop",
- "key": "ssh-ed25519 AAAA",
- "from": "id_ed25519.pub",
- })
- if err == nil {
- t.Fatalf("expected ambiguous key source error")
- }
- if called {
- t.Fatalf("server was called for invalid key sources")
- }
-}
-
-func TestUserAddKeyRejectsInlineKeyWithoutTitleBeforeRequest(t *testing.T) {
- called := false
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- called = true
- }))
- defer server.Close()
-
- err := runShortcut(t, server, "add-key", map[string]string{"key": "ssh-rsa AAAA"})
- if err == nil {
- t.Fatalf("expected missing title error")
- }
- if called {
- t.Fatalf("server was called without title")
- }
-}
-
-func TestUserDeleteKeyUsesPublicKeyID(t *testing.T) {
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method != "DELETE" || r.URL.Path != "/public_keys/12.json" {
- t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
+ if r.URL.Path != "/users/alice/statistics.json" {
+ t.Fatalf("unexpected path: %s", r.URL.Path)
}
- writeJSON(t, w, map[string]interface{}{
- "status": 0,
- "message": "success",
- })
+ writeJSON(w, map[string]interface{}{})
}))
defer server.Close()
- if err := runShortcut(t, server, "delete-key", map[string]string{"id": "12"}); err != nil {
- t.Fatalf("delete-key shortcut failed: %v", err)
+ err := runShortcut(t, server, "stats", map[string]string{"user": "alice"})
+ if err != nil {
+ t.Fatalf("stats alias failed: %v", err)
}
}
-func TestUserDeleteKeyRejectsNonNumericIDBeforeRequest(t *testing.T) {
- called := false
+// --- project trends ---
+
+func TestUserProjectTrendsWithTimeWindow(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- called = true
+ if r.URL.Path != "/users/alice/project_trends.json" {
+ t.Fatalf("unexpected path: %s", r.URL.Path)
+ }
+ if got := r.URL.Query().Get("start_time"); got != "100" {
+ t.Fatalf("start_time = %q, want 100", got)
+ }
+ if got := r.URL.Query().Get("end_time"); got != "200" {
+ t.Fatalf("end_time = %q, want 200", got)
+ }
+ writeJSON(w, map[string]interface{}{
+ "trends": []interface{}{},
+ })
}))
defer server.Close()
- err := runShortcut(t, server, "delete-key", map[string]string{"id": "12/34"})
+ args := map[string]string{"user": "alice", "start-time": "100", "end-time": "200"}
+ err := runShortcut(t, server, "project-trends", args)
+ if err != nil {
+ t.Fatalf("project-trends failed: %v", err)
+ }
+}
+
+func TestUserTrendsAlias(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/users/alice/project_trends.json" {
+ t.Fatalf("unexpected path: %s", r.URL.Path)
+ }
+ writeJSON(w, map[string]interface{}{})
+ }))
+ defer server.Close()
+
+ err := runShortcut(t, server, "trends", map[string]string{"user": "alice"})
+ if err != nil {
+ t.Fatalf("trends alias failed: %v", err)
+ }
+}
+
+func TestUserDefaultUserMissingLogin(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/users/me.json" {
+ t.Fatalf("unexpected path: %s", r.URL.Path)
+ }
+ writeJSON(w, map[string]interface{}{"name": "no login"})
+ }))
+ defer server.Close()
+
+ err := runShortcut(t, server, "statistics", map[string]string{})
if err == nil {
- t.Fatalf("expected invalid key id error")
- }
- if called {
- t.Fatalf("server was called for invalid key id")
- }
-}
-
-func TestPublicKeyContentAcceptsCommonOpenSSHPrefixes(t *testing.T) {
- for _, key := range []string{
- "ssh-rsa AAAA",
- "ssh-dss AAAA",
- "ssh-ed25519 AAAA",
- "ecdsa-sha2-nistp256 AAAA",
- "sk-ssh-ed25519@openssh.com AAAA",
- } {
- t.Run(key, func(t *testing.T) {
- if _, err := publicKeyContent(key, ""); err != nil {
- t.Fatalf("publicKeyContent(%q) returned error: %v", key, err)
- }
- })
- }
-}
-
-func TestPublicKeyContentRejectsNonPublicKeyPrefix(t *testing.T) {
- if _, err := publicKeyContent("not-a-key", ""); err == nil {
- t.Fatalf("expected invalid public key prefix error")
+ t.Fatal("expected error when /users/me has no login")
}
}
@@ -298,6 +250,19 @@ func TestUserMeHTTPError(t *testing.T) {
}
}
+func TestUserHeatmapHTTPError(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("server error"))
+ }))
+ defer server.Close()
+
+ err := runShortcut(t, server, "heatmap", map[string]string{"user": "alice"})
+ if err == nil {
+ t.Fatal("expected error for HTTP 500")
+ }
+}
+
func TestUserInfoHTTPError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
diff --git a/skills/README.md b/skills/README.md
index 0336886..6735c07 100644
--- a/skills/README.md
+++ b/skills/README.md
@@ -32,7 +32,7 @@ gitlink-cli auth status
gitlink-cli user +me
```
-详见: [gitlink-shared/examples/auth-workflow.md](gitlink-shared/examples/auth-workflow.md)
+详见: [gitlink-shared/SKILL.md](gitlink-shared/SKILL.md)
### 2. 查看可用命令
@@ -64,25 +64,19 @@ skills/
├── README.md # 本文件
├── gitlink-shared/ # 共享基础规则
│ ├── SKILL.md # 认证、全局参数、安全规则、分支约定
-│ ├── REFERENCE.md # API 详细参考、错误处理
-│ ├── TROUBLESHOOTING.md # 常见问题排查
-│ └── examples/
-│ └── auth-workflow.md # 认证工作流示例
+│ └── references/
+│ ├── api-reference.md # API 详细参考、错误处理
+│ ├── raw-api-batch.md # 批量 Raw API 调用参考
+│ └── troubleshooting.md # 常见问题排查
├── gitlink-repo/ # 仓库管理
│ ├── SKILL.md # 仓库操作指南
-│ ├── REFERENCE.md # 仓库 API 参考
-│ └── examples/
-│ └── repo-workflow.md # 仓库管理工作流
+│ └── references/ # 仓库命令参考文档
├── gitlink-issue/ # Issue 管理
│ ├── SKILL.md # Issue 操作指南
-│ ├── REFERENCE.md # Issue API 参考
-│ └── examples/
-│ └── issue-workflow.md # Issue 全流程工作流
+│ └── references/ # Issue 命令参考文档
├── gitlink-pr/ # Pull Request
│ ├── SKILL.md # PR 操作指南
-│ ├── REFERENCE.md # PR API 参考
-│ └── examples/
-│ └── pr-workflow.md # PR 工作流
+│ └── references/ # PR 命令参考文档
├── gitlink-member/ # 仓库成员管理
│ └── SKILL.md # 成员与邀请链接操作指南
├── gitlink-branch/ # 分支管理
@@ -91,31 +85,24 @@ skills/
│ └── branch-workflow.md # 分支工作流
├── gitlink-release/ # 版本发布
│ ├── SKILL.md # Release 操作指南
-│ ├── REFERENCE.md # Release API 参考
-│ └── examples/
-│ └── release-workflow.md # Release 工作流
+│ └── references/ # Release 命令参考文档
+├── gitlink-release-auto/ # 自动化 Release 管理
+│ └── SKILL.md # 自动发版、版本号推荐、Release Notes 生成
├── gitlink-search/ # 搜索功能
│ ├── SKILL.md # 搜索操作指南
-│ └── examples/
-│ └── search-workflow.md # 搜索工作流
-├── gitlink-attachment/ # 附件管理
-│ └── SKILL.md # 附件上传与删除指南
-├── gitlink-meta/ # 公开元数据查询
-│ └── SKILL.md # 许可证和 .gitignore 模板查询指南
+│ └── references/ # 搜索命令参考文档
├── gitlink-user/ # 用户管理
-│ └── SKILL.md # 用户操作指南
-├── gitlink-notification/ # 通知消息管理
-│ └── SKILL.md # 消息与设置操作指南
+│ ├── SKILL.md # 用户操作指南
+│ └── references/ # 用户命令参考文档
├── gitlink-org/ # 组织管理
│ ├── SKILL.md # 组织操作指南
-│ └── examples/
-│ └── org-workflow.md # 组织工作流
+│ └── references/ # 组织命令参考文档
├── gitlink-ci/ # CI/CD
-│ ├── SKILL.md # CI 操作指南
-│ └── examples/
-│ └── ci-workflow.md # CI 工作流
+│ └── SKILL.md # CI 操作指南
├── gitlink-pipeline/ # 流水线工作流
│ └── SKILL.md # Pipeline 操作指南
+├── gitlink-wiki/ # Wiki 页面管理
+│ └── SKILL.md # Wiki 操作指南
├── gitlink-pm/ # 项目管理
│ └── SKILL.md # PM 操作指南
├── gitlink-health/ # 项目健康度分析
@@ -141,24 +128,22 @@ skills/
|-------|------|----------|
| **gitlink-shared** | 认证、全局参数、API 参考、安全规则、分支约定 | `auth login`, `auth status` |
| **gitlink-repo** | 仓库管理与洞察 | `repo +list`, `repo +info`, `repo +languages`, `repo +contributors`, `repo +code-stats`, `repo +follow`, `repo +like` |
-| **gitlink-issue** | Issue 管理 | `issue +create`, `issue +list`, `issue +view`, `issue +close`, `issue +batch-close` |
-| **gitlink-pr** | Pull Request | `pr +list`, `pr +create`, `pr +view`, `pr +checkout`, `pr +merge`, `pr +versions`, `pr +version-diff`, `pr +reviews`, `pr +review` |
+| **gitlink-issue** | Issue 管理 | `issue +create`, `issue +list`, `issue +view`, `issue +close`, `issue +batch-close`, `issue +batch-update`, `issue +batch-delete` |
+| **gitlink-pr** | Pull Request | `pr +list`, `pr +create`, `pr +view`, `pr +merge`, `pr +versions`, `pr +version-diff`, `pr +reviews`, `pr +review` |
| **gitlink-member** | 仓库成员管理 | `member +list`, `member +add`, `member +batch-add`, `member +role`, `member +invite-link` |
| **gitlink-branch** | 分支管理 | `branch +list`, `branch +create`, `branch +delete`, `branch +protect` |
-| **gitlink-release** | 版本发布 | `release +list`, `release +create`, `release +view` |
+| **gitlink-release** | 版本发布 | `release +list`, `release +create`, `release +edit`, `release +update`, `release +view` |
### 辅助 Skills
| Skill | 说明 | 常用命令 |
|-------|------|----------|
| **gitlink-search** | 搜索功能 | `search +repos`, `search +users` |
-| **gitlink-user** | 用户管理 | `user +me`, `user +info` |
-| **gitlink-notification** | 通知消息管理 | `notification +list`, `notification +mark-read`, `notification +settings-update` |
+| **gitlink-user** | 用户管理 | `user +me`, `user +info`, `user +heatmap`, `user +statistics`, `user +project-trends` |
| **gitlink-org** | 组织管理 | `org +list`, `org +info`, `org +members` |
| **gitlink-ci** | CI/CD | `ci +builds`, `ci +logs` |
| **gitlink-pipeline** | 流水线工作流 | `pipeline +runs`, `pipeline +run`, `pipeline +logs` |
-| **gitlink-attachment** | 附件管理 | `attachment +upload`, `attachment +delete` |
-| **gitlink-meta** | 公开元数据查询 | `meta +licenses`, `meta +ignores` |
+| **gitlink-wiki** | Wiki 页面管理 | `wiki +list`, `wiki +view`, `wiki +create`, `wiki +update`, `wiki +delete` |
| **gitlink-pm** | 项目管理 | 通过 Raw API 访问 |
| **gitlink-workflow** | AI 工作流 | Issue 分类、PR Review、Release Notes |
| **gitlink-health** | 开源项目健康度 | 详情见SKILL.md |
@@ -178,7 +163,7 @@ gitlink-cli repo +info
gitlink-cli repo +info --owner wbtiger --repo gitlink-cli
```
-详见: [gitlink-repo/examples/repo-workflow.md](gitlink-repo/examples/repo-workflow.md)
+详见: [gitlink-repo/SKILL.md](gitlink-repo/SKILL.md)
### 场景 2:创建和管理 Issue
@@ -199,7 +184,7 @@ gitlink-cli issue +close -i 123
gitlink-cli issue +batch-close --numbers 123,124 --dry-run
```
-详见: [gitlink-issue/examples/issue-workflow.md](gitlink-issue/examples/issue-workflow.md)
+详见: [gitlink-issue/SKILL.md](gitlink-issue/SKILL.md)
### 场景 3:管理分支和发布
@@ -217,7 +202,7 @@ gitlink-cli release +create -t v1.0.0 -n "v1.0.0 正式版" -b "更新内容..."
gitlink-cli release +view -i
```
-详见: [gitlink-release/examples/release-workflow.md](gitlink-release/examples/release-workflow.md)
+详见: [gitlink-release/SKILL.md](gitlink-release/SKILL.md)
### 场景 4:搜索和发现
@@ -233,21 +218,7 @@ gitlink-cli org +list
gitlink-cli org +info -i Gitlink
```
-详见: [gitlink-search/examples/search-workflow.md](gitlink-search/examples/search-workflow.md)
-
-### 场景 5:附件和模板元数据
-
-```bash
-# 查询创建仓库时可用的模板元数据
-gitlink-cli meta +licenses --name MIT
-gitlink-cli meta +ignores --name Go
-
-# 先 dry-run,再上传附件
-gitlink-cli attachment +upload --file screenshot.png --description "复现截图" --dry-run
-gitlink-cli attachment +upload --file screenshot.png --description "复现截图"
-```
-
-详见: [gitlink-attachment/SKILL.md](gitlink-attachment/SKILL.md)、[gitlink-meta/SKILL.md](gitlink-meta/SKILL.md)
+详见: [gitlink-search/SKILL.md](gitlink-search/SKILL.md)
---
@@ -256,8 +227,8 @@ gitlink-cli attachment +upload --file screenshot.png --description "复现截图
### 快速查找
- **我想了解认证**: [gitlink-shared/SKILL.md](gitlink-shared/SKILL.md)
-- **我想查看 API 细节**: [gitlink-shared/REFERENCE.md](gitlink-shared/REFERENCE.md)
-- **我遇到了错误**: [gitlink-shared/TROUBLESHOOTING.md](gitlink-shared/TROUBLESHOOTING.md)
+- **我想查看 API 细节**: [gitlink-shared/references/api-reference.md](gitlink-shared/references/api-reference.md)
+- **我遇到了错误**: [gitlink-shared/references/troubleshooting.md](gitlink-shared/references/troubleshooting.md)
- **我想看工作流示例**: 查看各 Skill 下的 `examples/` 目录
### 按功能分类
@@ -265,24 +236,21 @@ gitlink-cli attachment +upload --file screenshot.png --description "复现截图
**仓库操作**:
- [gitlink-repo/SKILL.md](gitlink-repo/SKILL.md) - 仓库命令
- [gitlink-branch/SKILL.md](gitlink-branch/SKILL.md) - 分支命令
-- [gitlink-repo/examples/repo-workflow.md](gitlink-repo/examples/repo-workflow.md) - 完整工作流
+- [gitlink-repo/SKILL.md](gitlink-repo/SKILL.md) - 完整工作流
**Issue 和 PR**:
- [gitlink-issue/SKILL.md](gitlink-issue/SKILL.md) - Issue 命令
- [gitlink-pr/SKILL.md](gitlink-pr/SKILL.md) - PR 命令
-- [gitlink-issue/examples/issue-workflow.md](gitlink-issue/examples/issue-workflow.md) - Issue 工作流
+- [gitlink-issue/SKILL.md](gitlink-issue/SKILL.md) - Issue 工作流
-**发布、搜索和附件**:
+**发布和搜索**:
- [gitlink-release/SKILL.md](gitlink-release/SKILL.md) - Release 命令
- [gitlink-pipeline/SKILL.md](gitlink-pipeline/SKILL.md) - Pipeline 命令
- [gitlink-search/SKILL.md](gitlink-search/SKILL.md) - 搜索命令
-- [gitlink-attachment/SKILL.md](gitlink-attachment/SKILL.md) - 附件命令
-- [gitlink-meta/SKILL.md](gitlink-meta/SKILL.md) - 公开元数据命令
-**组织、用户和通知**:
+**组织和用户**:
- [gitlink-org/SKILL.md](gitlink-org/SKILL.md) - 组织命令
- [gitlink-user/SKILL.md](gitlink-user/SKILL.md) - 用户命令
-- [gitlink-notification/SKILL.md](gitlink-notification/SKILL.md) - 通知消息命令
---
@@ -315,11 +283,11 @@ gitlink-cli auth login
### Q: 如何查看完整的 API 参考?
-A: 查看 [gitlink-shared/REFERENCE.md](gitlink-shared/REFERENCE.md)
+A: 查看 [gitlink-shared/references/api-reference.md](gitlink-shared/references/api-reference.md)
### Q: 遇到错误怎么办?
-A: 查看 [gitlink-shared/TROUBLESHOOTING.md](gitlink-shared/TROUBLESHOOTING.md)
+A: 查看 [gitlink-shared/references/troubleshooting.md](gitlink-shared/references/troubleshooting.md)
---
@@ -355,7 +323,7 @@ AI 代理可以:
- 所有边界情况处理正确
- 完整的文档和示例
-详见: [../doc/SKILLS_TEST_REPORT_2026-04-02.md](../doc/SKILLS_TEST_REPORT_2026-04-02.md)
+详见: [../doc/design.md](../doc/design.md)
---
@@ -363,8 +331,7 @@ AI 代理可以:
- [主项目 README](../README.md) - gitlink-cli 项目说明
- [设计文档](../doc/design.md) - 架构设计和开发计划
-- [测试报告](../doc/SKILLS_TEST_REPORT_2026-04-02.md) - 功能测试报告
-- [代码同步方案](../doc/CODE_SYNC_STRATEGY_FINAL.md) - GitHub ↔ GitLink 同步设计
+- [API 参考文档](../doc/gitlink_api_reference.md) - GitLink API 参考文档
- [gitlink-bisync](https://www.gitlink.org.cn/wbtiger/gitlink-bisync) - 代码双向同步系统
---
@@ -372,8 +339,8 @@ AI 代理可以:
## 📞 获取帮助
- **命令帮助**: `gitlink-cli --help`
-- **故障排查**: [gitlink-shared/TROUBLESHOOTING.md](gitlink-shared/TROUBLESHOOTING.md)
-- **API 参考**: [gitlink-shared/REFERENCE.md](gitlink-shared/REFERENCE.md)
+- **故障排查**: [gitlink-shared/references/troubleshooting.md](gitlink-shared/references/troubleshooting.md)
+- **API 参考**: [gitlink-shared/references/api-reference.md](gitlink-shared/references/api-reference.md)
- **工作流示例**: 查看各 Skill 下的 `examples/` 目录
---
@@ -381,7 +348,7 @@ AI 代理可以:
## 🎓 下一步
1. 阅读 [gitlink-shared/SKILL.md](gitlink-shared/SKILL.md) 了解基础
-2. 查看 [gitlink-shared/examples/auth-workflow.md](gitlink-shared/examples/auth-workflow.md) 完成认证
+2. 查看 [gitlink-shared/SKILL.md](gitlink-shared/SKILL.md) 完成认证
3. 根据需求选择相应的 Skill 文档
4. 参考 `examples/` 目录中的工作流示例
5. 使用 AI 代理自动化你的工作流
diff --git a/skills/gitlink-contributor-insight/EXAMPLES.md b/skills/gitlink-contributor-insight/EXAMPLES.md
index b0fb4fb..2b90504 100644
--- a/skills/gitlink-contributor-insight/EXAMPLES.md
+++ b/skills/gitlink-contributor-insight/EXAMPLES.md
@@ -31,7 +31,9 @@ gitlink-cli issue +list --owner jiangtx --repo gitlink-cli --format json
# → 0 个 Issue
```
-### 不可用命令确认
+### 不可用命令确认(gitlink-cli 0.1.18 历史记录)
+
+> 当前版本已新增 `user +heatmap`、`user +stats`、`user +trends`。下表仅记录本样例在 0.1.18 上的历史执行结果。
| 命令 | 结果 |
|------|------|
@@ -129,8 +131,8 @@ gitlink-cli issue +list --owner jiangtx --repo gitlink-cli --format json
| 贡献者数量 | repo +info | ✅ 可靠 |
| PR 贡献数据 | pr +list 全量 | ✅ 可靠 |
| 用户信息 | user +info | ✅ 可靠 |
-| 贡献热力图 | 不可用(命令未实现) | ❌ 缺失 |
-| 统计信息 | 不可用(命令未实现) | ❌ 缺失 |
+| 贡献热力图 | 0.1.18 未实现,当前版本可用 `user +heatmap` | 版本相关 |
+| 统计信息 | 0.1.18 未实现,当前版本可用 `user +stats` | 版本相关 |
| 趋势数据 | PR 时间序列推算 | ⚠️ 推算 |
```
@@ -163,10 +165,10 @@ gitlink-cli issue +list --owner jiangtx --repo gitlink-cli --format json
### Agent 决策过程
-Agent 读取 skill 后,**正确遵循了更新后的工作流**:
+Agent 读取 skill 后,**正确遵循了当时版本的工作流**:
1. **未尝试 `repo +contributors`**:skill 的"命令可用性声明"表标注该命令不可用
-2. **未尝试 `user +heatmap/+stats/+trends`**:skill 标注不可用,直接从 PR 列表推算
+2. **未尝试 `user +heatmap/+stats/+trends`**:0.1.18 中这些命令不可用,直接从 PR 列表推算
3. **未尝试 Raw API**:skill 不推荐此路径,全程使用 Shortcut 命令
4. **正确应用"年轻项目"规则**:识别项目仅 3 天,放宽分级标准,2 人均标记为 🔥 核心
5. **自主增强分析**:Agent 额外分析了工作时段偏好、PR 类型统计、新老比例
@@ -197,9 +199,9 @@ Agent 生成了完整的五段式报告(团队概览 → 排行榜 → 个人
✅ skill v1.1.0 验证通过:
- Agent 正确遵循了"命令可用性声明",未尝试不可用命令
- Agent 正确从 `pr +list` 提取贡献者数据(替代不存在的 `repo +contributors`)
-- Agent 正确从 PR 时间戳推算活跃天数(替代不存在的 `user +heatmap`)
-- Agent 正确从 PR 聚合获得产出量(替代不存在的 `user +stats`)
-- Agent 正确从 PR 时间分布判断趋势(替代不存在的 `user +trends`)
+- Agent 正确从 PR 时间戳推算活跃天数(0.1.18 中 `user +heatmap` 不可用)
+- Agent 正确从 PR 聚合获得产出量(0.1.18 中 `user +stats` 不可用)
+- Agent 正确从 PR 时间分布判断趋势(0.1.18 中 `user +trends` 不可用)
- Agent 正确应用"年轻项目放宽标准"规则
- Agent 正确标注数据来源局限性
- Agent 未使用 `gh` 或其他平台工具
@@ -212,9 +214,9 @@ Agent 生成了完整的五段式报告(团队概览 → 排行榜 → 个人
| 场景 | 检测方式 | 数据表现 | 处理 |
|------|----------|----------|------|
| `repo +contributors` 不可用 | 命令返回帮助文本 | 无 `+contributors` 子命令 | 从 `pr +list` 提取 `author_login` |
-| `user +heatmap` 不可用 | 命令不存在 | user 仅 `+info`/`+me` | 从 PR 时间戳推算活跃天数 |
-| `user +stats` 不可用 | 命令不存在 | 同上 | 从 `pr +list` 聚合 PR/Issue 数 |
-| `user +trends` 不可用 | 命令不存在 | 同上 | 从 PR 按日聚合判断趋势 |
+| `user +heatmap` 返回空或权限不足 | 无热力图数据 | API 响应为空或无权限 | 从 PR 时间戳推算活跃天数 |
+| `user +stats` 返回空或权限不足 | 无统计数据 | API 响应为空或无权限 | 从 `pr +list` 聚合 PR/Issue 数 |
+| `user +trends` 返回空或权限不足 | 无趋势数据 | API 响应为空或无权限 | 从 PR 按日聚合判断趋势 |
| Raw API 返回 HTML | `api GET` 返回 HTML | 非 JSON 响应 | 仅使用 Shortcut 命令 |
| 项目 < 30 天 | PR 时间跨度 < 30 天 | 全部 PR 在近期 | 放宽分级标准,标注"早期阶段" |
| 贡献者 ≤ 2 人 | `contributor_users_count` ≤ 2 | Bus Factor 极低 | 报告标注风险 + 提供吸引新人建议 |
@@ -230,7 +232,7 @@ Agent 生成了完整的五段式报告(团队概览 → 排行榜 → 个人
| CLI 版本 | 贡献者分析可用命令 | 缺失命令 |
|----------|-------------------|----------|
| 0.1.18 | `repo +info`, `pr +list`, `issue +list`, `user +info` | `repo +contributors`, `user +heatmap`, `user +stats`, `user +trends` |
-| 未来版本 | 可能新增 `user +heatmap` 等 | — |
+| 当前版本 | `repo +info`, `pr +list`, `issue +list`, `user +info`, `user +heatmap`, `user +stats`, `user +trends` | `repo +contributors` |
当 CLI 版本更新后,重新验证可用命令:
```bash
diff --git a/skills/gitlink-contributor-insight/SKILL.md b/skills/gitlink-contributor-insight/SKILL.md
index 9ad7ba8..14954ce 100644
--- a/skills/gitlink-contributor-insight/SKILL.md
+++ b/skills/gitlink-contributor-insight/SKILL.md
@@ -25,9 +25,9 @@ gitlink-cli 的命令集在持续演进中。以下命令**当前版本可能不
| 命令 | 状态 | 替代方案 |
|------|------|----------|
| `repo +contributors` | ❌ 不可用 | 从 `pr +list` 提取 `author_login` + `repo +info` 获取 `contributor_users_count` |
-| `user +heatmap` | ❌ 不可用 | 从 PR 时间戳手动推算活跃天数 |
-| `user +stats` | ❌ 不可用 | 从 `pr +list` 统计 PR 数;Issue 数通过 `issue +list` 获取 |
-| `user +trends` | ❌ 不可用 | 从 PR 时间分布手动判断趋势(上升/平稳/下降) |
+| `user +heatmap` | ✅ 可用 | 贡献热力图 |
+| `user +stats` | ✅ 可用 | 用户聚合统计 |
+| `user +trends` | ✅ 可用 | 用户项目趋势 |
| `repo +info` | ✅ 可用 | — |
| `pr +list` | ✅ 可用 | — |
| `user +info` | ✅ 可用 | — |
@@ -90,11 +90,16 @@ gitlink-cli issue +list --owner --repo --format json
```bash
# 用户基本信息
gitlink-cli user +info --login --format json
+
+# 贡献热力图、聚合统计、项目趋势
+gitlink-cli user +heatmap --user --format json
+gitlink-cli user +stats --user --format json
+gitlink-cli user +trends --user --format json
```
从 `user +info` 提取:`login`、`name`、`created_time`(注册时间)、`user_projects_count`、`user_org_count`、`user_identity`。
-**如果 `user +heatmap/+stats/+trends` 可用**(未来版本),补充执行。当前版本用以下替代方案:
+从 `user +heatmap/+stats/+trends` 补充贡献频率、贡献产出和项目趋势。如果这些端点返回空或权限不足,再使用 PR/Issue 列表推算:
| 维度 | 替代数据源 | 分析要点 |
|------|----------|----------|
@@ -223,9 +228,9 @@ gitlink-cli user +info --login --format json
| PR 贡献数据 | `pr +list` 全量 | ✅ 可靠 |
| Issue 数据 | `issue +list` | ✅ 可靠 |
| 用户信息 | `user +info` | ✅ 可靠 |
-| 贡献热力图 | 不可用(命令未实现) | ❌ 缺失 |
-| 统计信息 | 不可用(命令未实现) | ❌ 缺失 |
-| 趋势数据 | 不可用(命令未实现) | ❌ 缺失 |
+| 贡献热力图 | `user +heatmap` | ✅ 可靠 |
+| 统计信息 | `user +stats` | ✅ 可靠 |
+| 趋势数据 | `user +trends` | ✅ 可靠 |
> **局限性**:本报告仅反映 GitLink 平台活动,不包括其他平台(GitHub、GitLab 等)的数据。
```
@@ -237,7 +242,7 @@ gitlink-cli user +info --login --format json
| 场景 | 处理方式 |
|------|----------|
| `repo +contributors` 不可用(当前版本常态) | 从 `pr +list` 的 `author_login` 提取贡献者列表 |
-| `user +heatmap` / `+stats` / `+trends` 不可用 | 从 PR 时间戳推算活跃天数,PR 聚合得产出量,时间分布得趋势 |
+| `user +heatmap` / `+stats` / `+trends` 返回空或权限不足 | 从 PR 时间戳推算活跃天数,PR 聚合得产出量,时间分布得趋势 |
| `pr +list` 返回空 | 标注"仓库暂无 PR 数据",仅展示 `repo +info` 基本信息 |
| `user +info` 返回空 | 标注"用户信息不可用",仅展示 PR 统计 |
| 贡献者 > 15 人 | 仅分析 PR 数最高的前 10 位,报告中注明"基于 Top 10 分析" |
@@ -251,7 +256,7 @@ gitlink-cli user +info --login --format json
- ✅ **所有命令使用 `--format json`**,确保可解析
- ✅ **本 Skill 为纯只读分析**,不会修改任何仓库
- ✅ **Owner/repo 优先从 `git remote` 自动解析**,无 git 上下文时询问用户
-- ⚠️ **核心数据来源为 `pr +list`**:当前版本 gitlink-cli 中 `user +heatmap/+stats/+trends` 不可用,分析主要依赖 PR 列表数据
+- ✅ **优先使用用户统计快捷命令**:`user +heatmap/+stats/+trends` 可直接提供贡献热力图、聚合统计和项目趋势;PR/Issue 列表用于补充仓库内贡献明细
- ⚠️ **`repo +contributors` 不可用**:贡献者列表从 PR 作者提取,可能与实际 `contributor_users_count` 有差异(后者包含未提 PR 的参与者)
- ⚠️ **数据仅反映 GitLink 平台活动**:不包括 GitHub 或其他平台的数据
- ℹ️ **参照样例**:[`EXAMPLES.md`](EXAMPLES.md) 包含手动执行和 Agent 调用两种场景的完整样例,[`examples/jiangtx-gitlink-cli.md`](examples/jiangtx-gitlink-cli.md) 包含原始命令输出数据
diff --git a/skills/gitlink-contributor-insight/examples/jiangtx-gitlink-cli.md b/skills/gitlink-contributor-insight/examples/jiangtx-gitlink-cli.md
index 57b4df5..61a5ef1 100644
--- a/skills/gitlink-contributor-insight/examples/jiangtx-gitlink-cli.md
+++ b/skills/gitlink-contributor-insight/examples/jiangtx-gitlink-cli.md
@@ -130,7 +130,9 @@ PR 详细列表:
}
```
-### 5. 不可用的命令
+### 5. 不可用的命令(gitlink-cli 0.1.18 历史记录)
+
+> 当前版本已新增 `user +heatmap`、`user +stats`、`user +trends`。下表仅记录本样例在 0.1.18 上的历史执行结果。
| 命令 | 结果 |
|------|------|
diff --git a/skills/gitlink-user/SKILL.md b/skills/gitlink-user/SKILL.md
index 55f0492..8ab9c71 100644
--- a/skills/gitlink-user/SKILL.md
+++ b/skills/gitlink-user/SKILL.md
@@ -1,7 +1,7 @@
---
name: gitlink-user
-version: 1.1.0
-description: "用户操作:查看当前用户、用户详情、贡献热力图、活跃度、开发能力、角色定位和专业定位统计。当用户需要查看 GitLink 用户信息或用户统计画像时触发。"
+version: 1.0.0
+description: "用户操作:查看当前用户、用户详情、贡献热力图、统计和项目趋势。当用户需要查看 GitLink 用户信息时触发。"
metadata:
requires:
bins: ["gitlink-cli"]
@@ -22,66 +22,33 @@ metadata:
|----------|------|----------|
| `user +me` | 当前登录用户 | 是 |
| `user +info` | 查看用户详情 | 否 |
-| `user +activity` | 查看用户近期活跃度统计 | 否 |
-| `user +headmap` | 查看用户贡献热力图,可按年份过滤 | 否 |
-| `user +develop` | 查看用户开发能力统计 | 否 |
-| `user +role` | 查看用户角色定位统计 | 否 |
-| `user +major` | 查看用户专业定位 / 项目分类统计 | 否 |
-
-## SSH Public Keys
-
-Use these shortcuts to manage SSH public keys for the current authenticated user:
-
-```bash
-gitlink-cli user +keys
-gitlink-cli user +add-key --title laptop --from ~/.ssh/id_ed25519.pub
-gitlink-cli user +add-key --from ~/.ssh/id_rsa.pub
-gitlink-cli user +add-key --title laptop --key "ssh-ed25519 AAAA..."
-gitlink-cli user +delete-key --id 123
-```
-
-`user +add-key` accepts either `--key` or `--from`, not both. When `--from` is used without `--title`, the filename is used as the default title. `user +delete-key` requires a positive integer key ID.
+| `user +heatmap` | 用户贡献热力图 | 省略 `--user` 时需要 |
+| `user +statistics` | 用户聚合统计 | 省略 `--user` 时需要 |
+| `user +stats` | `user +statistics` 的短别名 | 省略 `--user` 时需要 |
+| `user +project-trends` | 用户项目趋势 | 省略 `--user` 时需要 |
+| `user +trends` | `user +project-trends` 的短别名 | 省略 `--user` 时需要 |
## 使用示例
```bash
# 查看当前用户
-gitlink-cli user +me --format json
+gitlink-cli user +me
# 查看其他用户
-gitlink-cli user +info --login zhangsan --format json
-
-# 用户近期活跃度
-gitlink-cli user +activity --login zhangsan --format json
+gitlink-cli user +info --login zhangsan
# 用户贡献热力图
-gitlink-cli user +headmap --login zhangsan --year 2026 --format json
-
-# 用户开发能力、角色定位、专业定位
-gitlink-cli user +develop --login zhangsan --start-time 1717200000 --end-time 1719800000 --format json
-gitlink-cli user +role --login zhangsan --format json
-gitlink-cli user +major --login zhangsan --format json
-```
-
-## 参数说明
-
-- `--login` 不传时优先使用全局 `--owner`,否则通过 `/users/me` 解析当前登录用户。
-- `--start-time` / `--end-time` 为 Unix 时间戳,必须是非负整数,且 `start-time <= end-time`。
-- `--year` 必须是四位年份。
-- 新增统计命令全部是只读 `GET` 操作,适合 Agent 做开源贡献画像、科研仓库成员分析和自动报告。
-
-## Raw API 补充
-
-```bash
-# 用户贡献热力图
-gitlink-cli api GET /users/:user_id/headmaps
+gitlink-cli user +heatmap --user zhangsan --year 2026
# 用户统计
-gitlink-cli api GET /users/:user_id/statistics/activity
-gitlink-cli api GET /users/:user_id/statistics/develop
-gitlink-cli api GET /users/:user_id/statistics/role
-gitlink-cli api GET /users/:user_id/statistics/major
+gitlink-cli user +statistics --user zhangsan --start-time 1704067200 --end-time 1735689600
# 用户项目动态
-gitlink-cli api GET /users/:user_id/project_trends
+gitlink-cli user +project-trends --user zhangsan
```
+
+## 注意事项
+
+- `user +heatmap`、`user +statistics`、`user +project-trends` 都是只读命令。
+- 省略 `--user` 时会先调用 `user +me` 等价的 `/users/me` 解析当前登录用户,因此需要已登录。
+- `user +stats` 和 `user +trends` 是为贡献者分析工作流保留的短别名。
diff --git a/skills/gitlink-user/references/gitlink-user-heatmap.md b/skills/gitlink-user/references/gitlink-user-heatmap.md
new file mode 100644
index 0000000..e290f51
--- /dev/null
+++ b/skills/gitlink-user/references/gitlink-user-heatmap.md
@@ -0,0 +1,39 @@
+# user +heatmap
+
+> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
+
+查看用户贡献热力图。
+
+## 命令
+
+```bash
+# 指定用户和年份
+gitlink-cli user +heatmap --user zhangsan --year 2026
+
+# 省略 --user 时使用当前认证用户
+gitlink-cli user +heatmap --year 2026
+
+# JSON 格式,便于 Agent 解析
+gitlink-cli user +heatmap --user zhangsan --format json
+```
+
+## 参数
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| `--user` / `-u` | 否 | 用户登录名;省略时使用当前认证用户 |
+| `--year` | 否 | 热力图年份,例如 `2026` |
+| `--format` | 否 | 输出格式:json / table / yaml |
+
+## 输出字段
+
+| 字段 | 说明 |
+|------|------|
+| `total_contributions` | 贡献总数 |
+| `headmaps[].date` | 贡献日期 |
+| `headmaps[].contributions` | 当日贡献数 |
+
+## References
+
+- [gitlink-user](../SKILL.md)
+- [gitlink-shared](../../gitlink-shared/SKILL.md)
diff --git a/skills/gitlink-user/references/gitlink-user-project-trends.md b/skills/gitlink-user/references/gitlink-user-project-trends.md
new file mode 100644
index 0000000..76ac784
--- /dev/null
+++ b/skills/gitlink-user/references/gitlink-user-project-trends.md
@@ -0,0 +1,36 @@
+# user +project-trends
+
+> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
+
+查看用户项目趋势。`user +trends` 是该命令的短别名。
+
+## 命令
+
+```bash
+# 指定用户
+gitlink-cli user +project-trends --user zhangsan
+
+# 指定时间窗口(Unix 时间戳)
+gitlink-cli user +project-trends --user zhangsan --start-time 1704067200 --end-time 1735689600
+
+# 使用短别名
+gitlink-cli user +trends --user zhangsan --format json
+```
+
+## 参数
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| `--user` / `-u` | 否 | 用户登录名;省略时使用当前认证用户 |
+| `--start-time` | 否 | 开始时间(Unix 时间戳) |
+| `--end-time` | 否 | 结束时间(Unix 时间戳) |
+| `--format` | 否 | 输出格式:json / table / yaml |
+
+## 输出字段
+
+返回 GitLink API 的用户项目趋势结构。字段可能随平台返回扩展,建议 Agent 使用 `--format json` 并按实际字段解析。
+
+## References
+
+- [gitlink-user](../SKILL.md)
+- [gitlink-shared](../../gitlink-shared/SKILL.md)
diff --git a/skills/gitlink-user/references/gitlink-user-statistics.md b/skills/gitlink-user/references/gitlink-user-statistics.md
new file mode 100644
index 0000000..4ff14f7
--- /dev/null
+++ b/skills/gitlink-user/references/gitlink-user-statistics.md
@@ -0,0 +1,43 @@
+# user +statistics
+
+> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
+
+查看用户聚合统计。`user +stats` 是该命令的短别名。
+
+## 命令
+
+```bash
+# 指定用户
+gitlink-cli user +statistics --user zhangsan
+
+# 指定时间窗口(Unix 时间戳)
+gitlink-cli user +statistics --user zhangsan --start-time 1704067200 --end-time 1735689600
+
+# 使用短别名
+gitlink-cli user +stats --user zhangsan --format json
+```
+
+## 参数
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| `--user` / `-u` | 否 | 用户登录名;省略时使用当前认证用户 |
+| `--start-time` | 否 | 开始时间(Unix 时间戳) |
+| `--end-time` | 否 | 结束时间(Unix 时间戳) |
+| `--format` | 否 | 输出格式:json / table / yaml |
+
+## 输出字段
+
+返回 GitLink API 的用户统计结构。常见字段包括:
+
+| 字段 | 说明 |
+|------|------|
+| `issues_count` | Issue 数量 |
+| `pull_requests_count` | Pull Request 数量 |
+| `commits_count` | 提交数量 |
+| `projects_count` | 项目数量 |
+
+## References
+
+- [gitlink-user](../SKILL.md)
+- [gitlink-shared](../../gitlink-shared/SKILL.md)