diff --git a/README.md b/README.md index e5e4318..b13f1ef 100644 --- a/README.md +++ b/README.md @@ -532,8 +532,20 @@ gitlink-cli search +users -k "zhangsan" `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 +# Current authenticated user +gitlink-cli user +me + +# 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 + +# Project trend data (short alias: user +trends) +gitlink-cli user +project-trends --user zhangsan + # Development ability scores + language breakdown gitlink-cli profile +ability --user zhangsan @@ -747,7 +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-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) | diff --git a/README.zh-CN.md b/README.zh-CN.md index 6a8879d..04499a3 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -510,8 +510,19 @@ gitlink-cli search +users -k "zhangsan" `profile` 暴露 GitLink 原生的用户画像统计(开发能力、角色定位、专业定位、近期活动、贡献热力图)。 省略 `--user` 时默认使用当前认证用户。 +`user` 同时提供面向贡献者分析的热力图、聚合统计和项目趋势快捷入口。 ```bash +# 当前认证用户 +gitlink-cli user +me + +# 用户贡献热力图和聚合统计 +gitlink-cli user +heatmap --user zhangsan --year 2026 +gitlink-cli user +statistics --user zhangsan --start-time 1704067200 --end-time 1735689600 + +# 项目趋势数据(短别名:user +trends) +gitlink-cli user +project-trends --user zhangsan + # 开发能力评分 + 语言分布 gitlink-cli profile +ability --user zhangsan @@ -620,7 +631,7 @@ git push gitlink | `gitlink-ci` | CI/CD 操作(构建、日志等) | | `gitlink-pipeline` | 流水线工作流操作(运行、日志、启停、删除等) | | `gitlink-search` | 搜索功能(仓库、用户等) | -| `gitlink-user` | 用户管理(个人信息等) | +| `gitlink-user` | 用户管理(个人信息、热力图、统计、项目趋势等) | | `gitlink-pm` | 项目管理(Sprint、看板、周报等) | | `gitlink-workflow` | AI 自动化工作流(Issue 分类、PR Review、Release Notes 等) | diff --git a/doc/changes/user-statistics-shortcuts.md b/doc/changes/user-statistics-shortcuts.md new file mode 100644 index 0000000..bafebc3 --- /dev/null +++ b/doc/changes/user-statistics-shortcuts.md @@ -0,0 +1,41 @@ +# User Statistics Shortcuts + +## Summary + +Adds user-centered statistics shortcuts for contributor analysis workflows: + +```bash +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 0739395..d5120be 100644 --- a/internal/i18n/locales/en-US.json +++ b/internal/i18n/locales/en-US.json @@ -93,9 +93,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", @@ -114,6 +119,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", @@ -218,7 +224,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 2e6fc4d..a03fa04 100644 --- a/internal/i18n/locales/zh-CN.json +++ b/internal/i18n/locales/zh-CN.json @@ -93,9 +93,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", @@ -114,6 +119,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 批处理计划", @@ -218,7 +224,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 563db88..4a2ba22 100644 --- a/shortcuts/user/user.go +++ b/shortcuts/user/user.go @@ -2,13 +2,23 @@ package user import ( "fmt" + "net/url" "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", @@ -39,9 +49,105 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { return ctx.Output(env) }, }, + { + 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{} + 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 + } + return ctx.Output(env) + }, + }, + { + Name: "statistics", + Description: tr.T("cmd.user.statistics.short"), + Flags: windowFlags, + Run: func(ctx *common.RuntimeContext) error { + return runWindowedUserGet(ctx, "/users/%s/statistics") + }, + }, + { + Name: "stats", + Description: tr.T("cmd.user.stats.short"), + Flags: windowFlags, + Run: func(ctx *common.RuntimeContext) error { + 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] diff --git a/shortcuts/user/user_test.go b/shortcuts/user/user_test.go index 44d504f..51d931a 100644 --- a/shortcuts/user/user_test.go +++ b/shortcuts/user/user_test.go @@ -92,6 +92,149 @@ func TestUserInfoMissingLogin(t *testing.T) { } } +// --- heatmap --- + +func TestUserHeatmapExplicitUserWithYear(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/users/alice/headmaps.json" { + t.Fatalf("unexpected path: %s", r.URL.Path) + } + if got := r.URL.Query().Get("year"); got != "2026" { + t.Fatalf("year = %q, want 2026", got) + } + writeJSON(w, map[string]interface{}{ + "total_contributions": float64(12), + }) + })) + defer server.Close() + + err := runShortcut(t, server, "heatmap", map[string]string{"user": "alice", "year": "2026"}) + if err != nil { + t.Fatalf("heatmap failed: %v", err) + } +} + +func TestUserHeatmapDefaultsToCurrentUser(t *testing.T) { + var calls []string + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + 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) + } + })) + defer server.Close() + + err := runShortcut(t, server, "heatmap", map[string]string{}) + if err != nil { + t.Fatalf("heatmap failed: %v", err) + } + if len(calls) != 2 { + t.Fatalf("calls = %v, want 2 calls", calls) + } +} + +// --- statistics --- + +func TestUserStatisticsWithTimeWindow(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/users/alice/statistics.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{}{ + "issues_count": float64(3), + }) + })) + defer server.Close() + + args := map[string]string{"user": "alice", "start-time": "100", "end-time": "200"} + err := runShortcut(t, server, "statistics", args) + if err != nil { + t.Fatalf("statistics failed: %v", err) + } +} + +func TestUserStatsAlias(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/users/alice/statistics.json" { + t.Fatalf("unexpected path: %s", r.URL.Path) + } + writeJSON(w, map[string]interface{}{}) + })) + defer server.Close() + + err := runShortcut(t, server, "stats", map[string]string{"user": "alice"}) + if err != nil { + t.Fatalf("stats alias failed: %v", err) + } +} + +// --- project trends --- + +func TestUserProjectTrendsWithTimeWindow(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) + } + 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() + + 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.Fatal("expected error when /users/me has no login") + } +} + // --- HTTP error paths --- func TestUserMeHTTPError(t *testing.T) { @@ -107,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 d507074..6735c07 100644 --- a/skills/README.md +++ b/skills/README.md @@ -139,7 +139,7 @@ skills/ | Skill | 说明 | 常用命令 | |-------|------|----------| | **gitlink-search** | 搜索功能 | `search +repos`, `search +users` | -| **gitlink-user** | 用户管理 | `user +me`, `user +info` | +| **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` | 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-insight/SKILL.md b/skills/gitlink-insight/SKILL.md index 436c66c..8daa6cf 100644 --- a/skills/gitlink-insight/SKILL.md +++ b/skills/gitlink-insight/SKILL.md @@ -271,7 +271,7 @@ gitlink-cli api GET /:owner/:repo/sub_entries --query 'filepath=&ref=master' gitlink-cli api GET /users/:user_id --format json # 用户贡献热力图 -gitlink-cli api GET /users/:user_id/headmaps --format json +gitlink-cli user +heatmap --user --format json ``` ## 注意事项 diff --git a/skills/gitlink-user/SKILL.md b/skills/gitlink-user/SKILL.md index 0d1aa4f..8ab9c71 100644 --- a/skills/gitlink-user/SKILL.md +++ b/skills/gitlink-user/SKILL.md @@ -1,7 +1,7 @@ --- name: gitlink-user version: 1.0.0 -description: "用户操作:查看当前用户、用户详情。当用户需要查看 GitLink 用户信息时触发。" +description: "用户操作:查看当前用户、用户详情、贡献热力图、统计和项目趋势。当用户需要查看 GitLink 用户信息时触发。" metadata: requires: bins: ["gitlink-cli"] @@ -22,6 +22,11 @@ metadata: |----------|------|----------| | `user +me` | 当前登录用户 | 是 | | `user +info` | 查看用户详情 | 否 | +| `user +heatmap` | 用户贡献热力图 | 省略 `--user` 时需要 | +| `user +statistics` | 用户聚合统计 | 省略 `--user` 时需要 | +| `user +stats` | `user +statistics` 的短别名 | 省略 `--user` 时需要 | +| `user +project-trends` | 用户项目趋势 | 省略 `--user` 时需要 | +| `user +trends` | `user +project-trends` 的短别名 | 省略 `--user` 时需要 | ## 使用示例 @@ -31,17 +36,19 @@ gitlink-cli user +me # 查看其他用户 gitlink-cli user +info --login zhangsan -``` -## 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 +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)