Merge PR #58: feat: add repository reaction shortcuts

# Conflicts:
#	README.md
#	README.zh-CN.md
#	internal/i18n/locales/en-US.json
#	internal/i18n/locales/zh-CN.json
#	shortcuts/register_test.go
This commit is contained in:
wbtiger 2026-07-14 22:50:11 +08:00
commit 2e2891c4b2
11 changed files with 550 additions and 458 deletions

View File

@ -96,7 +96,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 |
| Wiki | Create, update, view, list, and delete wiki pages |
| ⭐ Reaction | Follow, unfollow, like, unlike, list watchers and stargazers |
| 🐛 Issue | Create, update, close, batch close, comment on issues |
| 🔖 Label | Create, list, update, delete issue labels |
| 🔀 PR | Create, merge, review pull requests, view changed files |
@ -247,21 +247,22 @@ gitlink-cli repo +create -n my-project -d "Project description"
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
```
### Wiki Management
### Repository Reactions
```bash
# List and view wiki pages
gitlink-cli wiki +pages --owner Gitlink --repo forgeplus --project-id 123
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --project-id 123 --page Home
# List repository watchers
gitlink-cli reaction +watchers --owner Gitlink --repo forgeplus
# Create or update wiki pages
gitlink-cli wiki +create --owner Gitlink --repo forgeplus \
--project-id 123 --page Home --title Home --content "Welcome to Wiki"
gitlink-cli wiki +update --owner Gitlink --repo forgeplus \
--project-id 123 --page Home --title Home --content "Updated content"
# List repository stargazers
gitlink-cli reaction +stargazers --owner Gitlink --repo forgeplus
# Preview wiki page deletion
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --project-id 123 --page Home --dry-run
# Follow or unfollow a repository
gitlink-cli reaction +follow --owner Gitlink --repo forgeplus
gitlink-cli reaction +unfollow --owner Gitlink --repo forgeplus
# Like or unlike a repository
gitlink-cli reaction +like --owner Gitlink --repo forgeplus
gitlink-cli reaction +unlike --owner Gitlink --repo forgeplus
```
### Webhook Management
@ -656,7 +657,7 @@ See [skills/README.md](skills/README.md) for details.
|-------|-------------|
| `gitlink-shared` | Authentication, global parameters, safety rules, API notes |
| `gitlink-repo` | Repository operations (create, view, delete, fork, insights, etc.) |
| `gitlink-wiki` | Wiki operations (create, update, view, list, delete) |
| `gitlink-reaction` | Repository reactions (follow, like, watchers, stargazers) |
| `gitlink-issue` | Issue operations (create, update, close, comment, etc.) |
| `gitlink-pr` | Pull request operations (create, merge, review, etc.) |
| `gitlink-member` | Repository member and invite link management |

View File

@ -96,7 +96,7 @@
| 分类 | 能力 |
|------|------|
| 📦 仓库 | 列出、创建、Fork、删除仓库查看仓库信息、洞察数据和互动状态 |
| Wiki | 创建、更新、查看、列出和删除 Wiki 页面 |
| ⭐ 互动 | 关注、取消关注、点赞、取消点赞,查看关注者和点赞者 |
| 🐛 Issue | 创建、更新、关闭、批量关闭、评论 Issue |
| 🔖 标签 | 创建、列出、更新、删除 Issue 标签 |
| 🔀 PR | 创建、合并、Review Pull Request查看变更文件 |
@ -258,21 +258,22 @@ gitlink-cli repo +create -n my-project -d "项目描述"
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
```
### Wiki 管理
### 仓库互动
```bash
# 列出和查看 Wiki 页面
gitlink-cli wiki +pages --owner Gitlink --repo forgeplus --project-id 123
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --project-id 123 --page Home
# 列出仓库关注者
gitlink-cli reaction +watchers --owner Gitlink --repo forgeplus
# 创建或更新 Wiki 页面
gitlink-cli wiki +create --owner Gitlink --repo forgeplus \
--project-id 123 --page Home --title Home --content "Welcome to Wiki"
gitlink-cli wiki +update --owner Gitlink --repo forgeplus \
--project-id 123 --page Home --title Home --content "Updated content"
# 列出仓库点赞者
gitlink-cli reaction +stargazers --owner Gitlink --repo forgeplus
# 预览删除 Wiki 页面
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --project-id 123 --page Home --dry-run
# 关注或取消关注仓库
gitlink-cli reaction +follow --owner Gitlink --repo forgeplus
gitlink-cli reaction +unfollow --owner Gitlink --repo forgeplus
# 点赞或取消点赞仓库
gitlink-cli reaction +like --owner Gitlink --repo forgeplus
gitlink-cli reaction +unlike --owner Gitlink --repo forgeplus
```
### Webhook 管理
@ -535,7 +536,7 @@ git push gitlink
|-------|------|
| `gitlink-shared` | 认证、全局参数、安全规则、API 注意事项 |
| `gitlink-repo` | 仓库操作创建、查看、删除、Fork、洞察数据等 |
| `gitlink-wiki` | Wiki 操作(创建、更新、查看、列表、删除 |
| `gitlink-reaction` | 仓库互动(关注、点赞、关注者、点赞者 |
| `gitlink-issue` | Issue 操作(创建、更新、关闭、评论等) |
| `gitlink-pr` | Pull Request 操作创建、合并、Review 等) |
| `gitlink-member` | 仓库成员与邀请链接管理 |

View File

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

View File

@ -1,13 +1,6 @@
{
"cmd.api.long": "Send arbitrary HTTP requests to the GitLink API. Authentication is injected automatically.",
"cmd.api.short": "Make raw API requests to GitLink",
"cmd.attachment.delete.long": "Delete a platform attachment by id or uuid. Only the attachment owner can delete it.",
"cmd.attachment.delete.short": "Delete an attachment by id",
"cmd.attachment.download.long": "Download a platform attachment by id and stream it to a local file.",
"cmd.attachment.download.short": "Download an attachment to a local file",
"cmd.attachment.short": "Attachment upload and download",
"cmd.attachment.upload.long": "Upload a local file to GitLink as an attachment via multipart form data. The returned attachment id can be used with `release +create --attachment-ids`.",
"cmd.attachment.upload.short": "Upload a local file as a platform attachment",
"cmd.auth.login.short": "Login to GitLink",
"cmd.auth.logout.short": "Logout from GitLink",
"cmd.auth.short": "Authentication commands",
@ -18,39 +11,18 @@
"cmd.branch.protect.short": "Set branch protection",
"cmd.branch.short": "Branch operations",
"cmd.branch.unprotect.short": "Remove branch protection",
"cmd.catalog.ignores.short": "List repository .gitignore templates",
"cmd.catalog.licenses.short": "List repository license templates",
"cmd.catalog.short": "GitLink platform catalogs",
"cmd.ci.builds.short": "List CI builds",
"cmd.ci.logs.short": "View build logs",
"cmd.ci.restart.short": "Restart a build",
"cmd.ci.short": "CI/CD operations",
"cmd.ci.stop.short": "Stop a build",
"cmd.compare.files.short": "List changed files between two refs",
"cmd.compare.short": "Compare branches, tags, or commits",
"cmd.compare.view.short": "Compare two branches, tags, or commits",
"cmd.config.get.short": "Get a configuration value",
"cmd.config.init.short": "Initialize configuration file",
"cmd.config.list.short": "List all configuration values",
"cmd.config.set.short": "Set a configuration value",
"cmd.config.short": "Manage gitlink-cli configuration",
"cmd.dataset.create.long": "Create the dataset of a repository with a title, description, optional license and research paper content.",
"cmd.dataset.create.short": "Create a repository's dataset",
"cmd.dataset.delete_attachment.long": "Delete a dataset attachment by its UUID. This is destructive: preview with --dry-run, then pass --yes to confirm.",
"cmd.dataset.delete_attachment.short": "Delete a dataset attachment by UUID",
"cmd.dataset.list.long": "List datasets for one or more GitLink projects by their numeric project IDs.",
"cmd.dataset.list.short": "List datasets by project IDs",
"cmd.dataset.short": "Dataset operations",
"cmd.dataset.update.long": "Update the dataset of a repository (title, description, optional license and research paper content).",
"cmd.dataset.update.short": "Update a repository's dataset",
"cmd.dataset.view.long": "View a repository's dataset and its attachments. Use --page/--limit to paginate attachments.",
"cmd.dataset.view.short": "View a repository's dataset",
"cmd.doctor.long": "Run local diagnostics for gitlink-cli configuration, authentication, repository context and API connectivity.",
"cmd.doctor.short": "Diagnose gitlink-cli environment problems",
"cmd.health.fetch.short": "Fetch PR and Issue data into SQLite for health analysis",
"cmd.health.short": "Project health data collection",
"cmd.ignore.list.short": "List available ignore-file templates",
"cmd.ignore.short": "Ignore file template operations",
"cmd.issue.batch_close.long": "Close filtered issues in bulk.\n\nThis command defaults to dry-run mode and only prints matching issues.\nPass --yes to execute remote close operations. Use restrictive filters and a small limit.\n\nExamples:\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20 --yes",
"cmd.issue.batch_close.short": "Close filtered issues in bulk. Defaults to dry-run; pass --yes to execute.",
"cmd.issue.batch_label.long": "Add a label to filtered issues in bulk.\n\nThis command defaults to dry-run mode and only prints matching issues.\nPass --yes to execute remote label operations. The current implementation does not fake label writes when the API endpoint is unavailable.\n\nExamples:\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50 --yes",
@ -64,46 +36,11 @@
"cmd.issue.short": "Issue operations",
"cmd.issue.update.short": "Update an issue",
"cmd.issue.view.short": "View issue details",
"cmd.label.create.short": "Create an issue label",
"cmd.label.delete.short": "Delete an issue label",
"cmd.label.list.short": "List issue labels",
"cmd.label.short": "Issue label operations",
"cmd.label.update.short": "Update an issue label while preserving unspecified fields",
"cmd.license.list.short": "List available licenses",
"cmd.license.short": "License operations",
"cmd.member.accept-invite.short": "Accept a repository invite link",
"cmd.member.add.short": "Add a repository member by user ID",
"cmd.member.batch-add.short": "Add multiple repository members by user IDs or a CSV file",
"cmd.member.invite-info.short": "Show repository invite link information",
"cmd.member.invite-link.short": "Get or create a repository invite link",
"cmd.member.list.short": "List repository members",
"cmd.member.remove.short": "Remove a repository member by user ID",
"cmd.member.role.short": "Change a repository member role",
"cmd.member.short": "Repository member operations",
"cmd.milestone.close.short": "Close a milestone",
"cmd.milestone.create.short": "Create a milestone",
"cmd.milestone.delete.short": "Delete a milestone",
"cmd.milestone.list.short": "List milestones",
"cmd.milestone.reopen.short": "Reopen a milestone",
"cmd.milestone.short": "Milestone operations",
"cmd.milestone.update.short": "Update a milestone",
"cmd.milestone.view.short": "View milestone details and linked issues",
"cmd.org.create.short": "Create an organization",
"cmd.org.info.short": "Show organization details",
"cmd.org.list.short": "List organizations",
"cmd.org.members.short": "List organization members",
"cmd.org.short": "Organization operations",
"cmd.pipeline.delete.short": "Delete a pipeline",
"cmd.pipeline.disable.short": "Disable a pipeline workflow",
"cmd.pipeline.enable.short": "Enable a pipeline workflow",
"cmd.pipeline.list.short": "List platform pipelines",
"cmd.pipeline.logs.short": "Query pipeline run logs",
"cmd.pipeline.results.short": "Show pipeline run report results",
"cmd.pipeline.run.short": "Run a pipeline workflow",
"cmd.pipeline.runs.short": "List pipeline run records",
"cmd.pipeline.save-yaml.short": "Save a visual pipeline YAML graph",
"cmd.pipeline.short": "Pipeline operations",
"cmd.pipeline.view.short": "Show pipeline details",
"cmd.pr.close.short": "Close a pull request",
"cmd.pr.comment.short": "Add a comment to a pull request",
"cmd.pr.create.short": "Create a pull request",
@ -117,21 +54,15 @@
"cmd.pr.version_diff.short": "Show diff for a pull request patchset version",
"cmd.pr.versions.short": "List pull request patchset versions",
"cmd.pr.view.short": "View pull request details",
"cmd.profile.ability.long": "Show a user's development ability scores (influence, contribution, activity, experience, language) and language breakdown, sourced from the GitLink platform statistics API.",
"cmd.profile.ability.short": "Show a user's development ability scores",
"cmd.profile.activity.long": "Show a user's recent activity statistics: daily issues, pull requests, and commits over a time window.",
"cmd.profile.activity.short": "Show a user's recent activity statistics",
"cmd.profile.contribution.long": "Show a user's contribution heatmap (daily contribution counts) for a given year.",
"cmd.profile.contribution.short": "Show a user's contribution heatmap",
"cmd.profile.major.long": "Show a user's major/discipline categories (e.g. deep learning, quantum computing) inferred from their projects.",
"cmd.profile.major.short": "Show a user's major/discipline categories",
"cmd.profile.role.long": "Show a user's role positioning derived from the GitLink platform statistics API.",
"cmd.profile.role.short": "Show a user's role positioning",
"cmd.profile.short": "User profile and statistics operations",
"cmd.reaction.follow.short": "Follow a repository",
"cmd.reaction.like.short": "Like a repository",
"cmd.reaction.short": "Repository reaction operations",
"cmd.reaction.stargazers.short": "List users who liked the repository",
"cmd.reaction.unfollow.short": "Unfollow a repository",
"cmd.reaction.unlike.short": "Unlike a repository",
"cmd.reaction.watchers.short": "List repository watchers",
"cmd.release.create.short": "Create a release",
"cmd.release.delete.short": "Delete a release",
"cmd.release.download.long": "Fetch the release by id or tag and download every attachment to a local directory, mirroring `gh release download`.",
"cmd.release.download.short": "Download all attachments of a release",
"cmd.release.list.short": "List releases",
"cmd.release.short": "Release operations",
"cmd.release.view.short": "View release details",
@ -159,26 +90,12 @@
"cmd.webhook.test.short": "Trigger a test delivery for a webhook",
"cmd.webhook.update.short": "Update a repository webhook while preserving unspecified fields when available",
"cmd.webhook.view.short": "View webhook details",
"cmd.wiki.create.short": "Create a new wiki page",
"cmd.wiki.delete.short": "Delete a wiki page",
"cmd.wiki.list.short": "List wiki pages",
"cmd.wiki.short": "Wiki page management",
"cmd.wiki.update.short": "Update an existing wiki page",
"cmd.wiki.view.short": "View a wiki page by page name",
"cmd.workflow.health.short": "Score repository health with local workflow rules",
"cmd.workflow.pr-summary.short": "Generate a read-only pull request review summary",
"cmd.workflow.repo-report.short": "Generate a read-only repository workflow report",
"cmd.workflow.short": "AI agent workflow analysis",
"cmd.workflow.triage.short": "Analyze issues with local workflow triage rules",
"error.auth.delete_token_failed": "failed to delete token: {message}",
"error.auth.login_failed": "login failed: {message}",
"error.auth.store_token_failed": "failed to store token: {message}",
"error.auth.token_empty": "token cannot be empty",
"error.config.save_failed": "failed to save config: {message}",
"error.dataset.delete_confirm": "dataset attachment deletion is destructive; run --dry-run first, then pass --yes to confirm",
"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.release.no_attachments": "the release has no attachments to download",
"error.unsupported_language": "unsupported language: {lang}",
"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",
@ -189,41 +106,18 @@
"flag.api.body_stdin": "Read request body JSON from stdin",
"flag.api.header": "Additional headers (key:value)",
"flag.api.query": "Query parameters (key=val&key2=val2)",
"flag.attachment.concurrency": "Concurrent uploads when passing multiple comma-separated files (default 3)",
"flag.attachment.description": "Attachment description",
"flag.attachment.file": "Path of the local file to upload",
"flag.attachment.id": "Attachment ID",
"flag.attachment.output": "Output file path (defaults to the attachment id)",
"flag.auth.token": "Login by pasting an existing token",
"flag.branch.from": "Source branch or commit",
"flag.branch.name": "Branch name",
"flag.catalog.name": "Filter templates by name",
"flag.ci.build": "Build number",
"flag.ci.stage": "Stage number",
"flag.ci.step": "Step number",
"flag.comment.body": "Comment body",
"flag.compare.base": "Target branch, tag, or commit",
"flag.compare.file": "Filter by file path",
"flag.compare.head": "Source branch, tag, or commit",
"flag.dataset.description": "Dataset description",
"flag.dataset.dry_run": "Preview the request without writing the dataset",
"flag.dataset.dry_run_delete": "Preview the request without deleting the attachment",
"flag.dataset.ids": "Comma-separated project IDs to query datasets for",
"flag.dataset.license_id": "License ID",
"flag.dataset.limit": "Attachment page size",
"flag.dataset.page": "Attachment page number",
"flag.dataset.paper_content": "Research paper content",
"flag.dataset.title": "Dataset title",
"flag.dataset.uuid": "Attachment UUID",
"flag.dataset.yes": "Confirm the destructive deletion",
"flag.debug": "Enable debug output",
"flag.description": "Description",
"flag.doctor.skip_network": "Skip authenticated API connectivity checks",
"flag.dry_run": "Preview the request without creating it",
"flag.format": "Output format: json, table, yaml (default: table)",
"flag.health.db": "SQLite database path (default: ~/.agents/skills/gitlink-health/data/gitlink_health.db)",
"flag.health.max_pages": "Maximum pages per query (default: unlimited)",
"flag.ignore.name": "Filter ignore templates by name",
"flag.issue.add_label": "Label to add to each matching issue",
"flag.issue.assignee": "Assignee login",
"flag.issue.assignee_id": "Assignee user ID",
@ -249,57 +143,13 @@
"flag.issue.status_id": "Issue status ID",
"flag.issue.tag_ids": "Comma-separated issue tag IDs",
"flag.issue.title": "Issue title",
"flag.label.color": "Label color in hex, for example: #1E90FF",
"flag.label.description": "Label description",
"flag.label.id": "Label ID",
"flag.label.keyword": "Filter labels by keyword",
"flag.label.name": "Label name",
"flag.label.only_name": "Return only label id and name: true or false",
"flag.label.sort_by": "Sort field: updated_on, created_on, issues_count",
"flag.label.sort_direction": "Sort direction: asc or desc",
"flag.lang": "Display language",
"flag.license.name": "Filter licenses by name",
"flag.limit": "Items per page",
"flag.member.apply": "Whether joining by invite requires approval: true or false",
"flag.member.dry_run": "Preview members that would be added without changing them",
"flag.member.from": "Read user IDs from a CSV file. Supports a user_id/id column or first column without header",
"flag.member.role": "Member role: Manager, Developer, or Reporter",
"flag.member.role_2": "Invite role: manager, developer, or reporter",
"flag.member.sign": "Invite link sign",
"flag.member.user_id": "GitLink user ID to add",
"flag.member.user_id_2": "GitLink user ID to update",
"flag.member.user_ids": "Comma-separated GitLink user IDs, for example: 101,102",
"flag.milestone.assigner_id": "Filter issues by assignee ID",
"flag.milestone.author_id": "Filter issues by author ID",
"flag.milestone.category": "Filter by category: opening, closed",
"flag.milestone.category_2": "Filter issues by category: all, opened, closed",
"flag.milestone.description": "Milestone description",
"flag.milestone.due_date": "Due date in YYYY-MM-DD format",
"flag.milestone.id": "Milestone ID",
"flag.milestone.issue_tag_ids": "Comma-separated issue tag IDs",
"flag.milestone.name": "Milestone name",
"flag.milestone.only_name": "Return only milestone id and name: true or false",
"flag.milestone.sort_by": "Sort field: created_on, updated_on, effective_date, issues_count, percent",
"flag.milestone.sort_by_2": "Sort field: issues.created_on, issues.updated_on, issue_priorities.position",
"flag.milestone.sort_direction": "Sort direction: asc or desc",
"flag.org.id": "Organization ID",
"flag.org.id_or_login": "Organization ID or login",
"flag.org.name": "Organization name",
"flag.owner": "Repository owner (auto-detected from git remote)",
"flag.page": "Page number",
"flag.pipeline.cursor": "Log cursor",
"flag.pipeline.dry_run": "Preview the run request without starting a pipeline",
"flag.pipeline.dry_run_2": "Preview the request without changing pipeline state",
"flag.pipeline.expanded": "Whether the log cursor is expanded",
"flag.pipeline.id": "Pipeline ID",
"flag.pipeline.index": "Run index",
"flag.pipeline.job": "Job index",
"flag.pipeline.owner_id": "Owner user or organization ID",
"flag.pipeline.pipeline_json": "Pipeline graph JSON object or string",
"flag.pipeline.ref": "Branch, tag, or commit SHA",
"flag.pipeline.run_id": "Pipeline run ID",
"flag.pipeline.step": "Log step",
"flag.pipeline.workflow": "Workflow file name",
"flag.pr.assignee_id": "Assignee user ID",
"flag.pr.base": "Target branch",
"flag.pr.body": "PR description",
@ -318,16 +168,13 @@
"flag.pr.tag_id": "Issue tag ID",
"flag.pr.title": "PR title",
"flag.pr.version_id": "Patchset version ID",
"flag.profile.end_time": "End time (Unix timestamp)",
"flag.profile.start_time": "Start time (Unix timestamp)",
"flag.profile.user": "Target user login (defaults to the authenticated user)",
"flag.profile.year": "Year for the contribution heatmap (e.g. 2025)",
"flag.release.attachment_files": "Comma-separated local files to upload and attach",
"flag.reaction.end_at": "End Unix timestamp",
"flag.reaction.project_id": "GitLink project ID. If omitted, it is resolved from --owner/--repo.",
"flag.reaction.start_at": "Start Unix timestamp",
"flag.release.body": "Release notes",
"flag.release.id": "Release ID",
"flag.release.id_or_tag": "Release ID or tag",
"flag.release.name": "Release name",
"flag.release.output_dir": "Directory to save downloaded files (default current directory)",
"flag.release.prerelease": "Mark as prerelease (true/false)",
"flag.release.tag": "Tag name",
"flag.release.target": "Target branch",
@ -353,58 +200,6 @@
"flag.webhook.secret_update": "Webhook secret. Pass it again if the server does not return existing secrets.",
"flag.webhook.type": "Webhook type: gitea/slack/discord/dingtalk/telegram/msteams/feishu/matrix/jianmu/softbot",
"flag.webhook.url": "Webhook target URL",
"flag.wiki.content": "Wiki page content (markdown)",
"flag.wiki.gateway": "Use gateway API endpoint",
"flag.wiki.message": "Commit message",
"flag.wiki.page_name": "Wiki page name (slug)",
"flag.wiki.project_id": "GitLink project ID",
"flag.wiki.title": "Wiki page title",
"flag.workflow.agent_readiness_known": "Whether agent readiness score is known",
"flag.workflow.agent_readiness_score": "Agent readiness score from 0 to 10",
"flag.workflow.author": "Issue author for single-issue local analysis",
"flag.workflow.body": "Issue body for single-issue local analysis",
"flag.workflow.ci_known": "Whether CI status is known",
"flag.workflow.ci_passing": "Whether CI is passing",
"flag.workflow.dry_run": "Preview workflow recommendations without remote writes",
"flag.workflow.from": "Read issue inputs from a JSON file. Supports a single issue, an array, or an object with an issues field",
"flag.workflow.from_2": "Read health input from a JSON file",
"flag.workflow.has_contributing": "Whether CONTRIBUTING exists",
"flag.workflow.has_license": "Whether LICENSE exists",
"flag.workflow.has_readme": "Whether README exists",
"flag.workflow.has_recent_release": "Whether a recent release exists",
"flag.workflow.labels": "Comma-separated labels for single-issue local analysis",
"flag.workflow.lang": "Output language: en or zh-CN",
"flag.workflow.limit": "Maximum issues to analyze",
"flag.workflow.number": "Issue number for single-issue local analysis",
"flag.workflow.open_issues": "Open issue count",
"flag.workflow.open_prs": "Open pull request count",
"flag.workflow.page": "API page number for remote triage",
"flag.workflow.pr_summary.from": "Read PR summary input from a JSON file",
"flag.workflow.pr_summary.include_commits": "Fetch commits in remote mode",
"flag.workflow.pr_summary.include_files": "Fetch changed files in remote mode",
"flag.workflow.pr_summary.lang": "Output language: en or zh-CN",
"flag.workflow.pr_summary.max_commits": "Maximum commits to analyze",
"flag.workflow.pr_summary.max_files": "Maximum changed files to analyze",
"flag.workflow.pr_summary.number": "Pull request number for remote read-only analysis",
"flag.workflow.recent_activity_days": "Days since recent activity",
"flag.workflow.recent_activity_known": "Whether recent activity is known",
"flag.workflow.release_known": "Whether release status is known",
"flag.workflow.repo_report.from": "Read repository report input from a JSON file",
"flag.workflow.repo_report.include_health": "Include repository health summary",
"flag.workflow.repo_report.include_issues": "Include issue triage summary",
"flag.workflow.repo_report.include_prs": "Include pull request summary",
"flag.workflow.repo_report.issue_limit": "Maximum issues to fetch and analyze",
"flag.workflow.repo_report.lang": "Output language: en or zh-CN",
"flag.workflow.repo_report.pr_limit": "Maximum pull requests to fetch and summarize",
"flag.workflow.repo_report.stale_days": "Days before an issue or PR is considered stale",
"flag.workflow.repository": "Repository name, for example owner/repo",
"flag.workflow.since": "Optional remote issue filter for updated time",
"flag.workflow.stale_days": "Days before an issue or PR is considered stale",
"flag.workflow.stale_issues": "Stale issue count",
"flag.workflow.stale_prs": "Stale pull request count",
"flag.workflow.state": "Filter or assign issue state",
"flag.workflow.title": "Issue title for single-issue local analysis",
"flag.workflow.url": "Issue URL for single-issue local analysis",
"output.auth.env_hint": " Or set {env} environment variable",
"output.auth.login_hint": " Run: gitlink-cli auth login",
"output.config.file": "Config file: {path}",

View File

@ -1,13 +1,6 @@
{
"cmd.api.long": "向 GitLink API 发送任意 HTTP 请求。认证信息会自动注入。",
"cmd.api.short": "向 GitLink 发起原始 API 请求",
"cmd.attachment.delete.long": "按 id 或 uuid 删除平台附件。仅附件所有者可删除。",
"cmd.attachment.delete.short": "按 id 删除附件",
"cmd.attachment.download.long": "按 id 下载平台附件并流式写入本地文件。",
"cmd.attachment.download.short": "下载附件到本地文件",
"cmd.attachment.short": "附件上传与下载",
"cmd.attachment.upload.long": "通过 multipart 表单将本地文件上传到 GitLink 作为附件。返回的附件 id 可用于 `release +create --attachment-ids`。",
"cmd.attachment.upload.short": "将本地文件上传为平台附件",
"cmd.auth.login.short": "登录 GitLink",
"cmd.auth.logout.short": "退出 GitLink 登录",
"cmd.auth.short": "认证命令",
@ -18,39 +11,18 @@
"cmd.branch.protect.short": "设置分支保护",
"cmd.branch.short": "分支操作",
"cmd.branch.unprotect.short": "移除分支保护",
"cmd.catalog.ignores.short": "列出仓库 .gitignore 模板",
"cmd.catalog.licenses.short": "列出仓库许可证模板",
"cmd.catalog.short": "GitLink 平台模板目录",
"cmd.ci.builds.short": "列出 CI 构建",
"cmd.ci.logs.short": "查看构建日志",
"cmd.ci.restart.short": "重启构建",
"cmd.ci.short": "CI/CD 操作",
"cmd.ci.stop.short": "停止构建",
"cmd.compare.files.short": "列出两个引用之间变更的文件",
"cmd.compare.short": "比较分支、标签或提交",
"cmd.compare.view.short": "比较两个分支、标签或提交",
"cmd.config.get.short": "获取配置项",
"cmd.config.init.short": "初始化配置文件",
"cmd.config.list.short": "列出所有配置项",
"cmd.config.set.short": "设置配置项",
"cmd.config.short": "管理 gitlink-cli 配置",
"cmd.dataset.create.long": "为仓库创建数据集,包含标题、描述、可选许可证和研究论文内容。",
"cmd.dataset.create.short": "创建仓库数据集",
"cmd.dataset.delete_attachment.long": "按 UUID 删除数据集附件。该操作具有破坏性:先用 --dry-run 预览,再传 --yes 确认。",
"cmd.dataset.delete_attachment.short": "按 UUID 删除数据集附件",
"cmd.dataset.list.long": "按数字项目 ID 列出一个或多个 GitLink 项目的数据集。",
"cmd.dataset.list.short": "按项目 ID 列出数据集",
"cmd.dataset.short": "数据集操作",
"cmd.dataset.update.long": "更新仓库数据集(标题、描述、可选许可证和研究论文内容)。",
"cmd.dataset.update.short": "更新仓库数据集",
"cmd.dataset.view.long": "查看仓库的数据集及其附件。用 --page/--limit 对附件分页。",
"cmd.dataset.view.short": "查看仓库数据集",
"cmd.doctor.long": "诊断 gitlink-cli 的配置、认证、仓库上下文和 API 连通性问题。",
"cmd.doctor.short": "诊断 gitlink-cli 环境问题",
"cmd.health.fetch.short": "抓取 PR 与 Issue 数据到 SQLite 用于健康度分析",
"cmd.health.short": "项目健康数据采集",
"cmd.ignore.list.short": "列出可用的 ignore 文件模板",
"cmd.ignore.short": "忽略文件模板操作",
"cmd.issue.batch_close.long": "批量关闭筛选后的议题。\n\n该命令默认处于 dry-run 模式,只打印匹配的议题。\n传入 --yes 后执行远端关闭操作。请使用严格筛选条件和较小 limit。\n\n示例\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20 --yes",
"cmd.issue.batch_close.short": "批量关闭筛选后的议题。默认 dry-run传入 --yes 后执行。",
"cmd.issue.batch_label.long": "给筛选后的议题批量添加标签。\n\n该命令默认处于 dry-run 模式,只打印匹配的议题。\n传入 --yes 后执行远端标签操作。当前实现不会在 API 端点不可用时伪造写入结果。\n\n示例\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50 --yes",
@ -64,46 +36,11 @@
"cmd.issue.short": "议题操作",
"cmd.issue.update.short": "更新议题",
"cmd.issue.view.short": "查看议题详情",
"cmd.label.create.short": "创建 issue 标签",
"cmd.label.delete.short": "删除 issue 标签",
"cmd.label.list.short": "列出 issue 标签",
"cmd.label.short": "Issue 标签操作",
"cmd.label.update.short": "更新 issue 标签(未指定字段保持不变)",
"cmd.license.list.short": "列出可用的开源许可证",
"cmd.license.short": "许可证操作",
"cmd.member.accept-invite.short": "接受仓库邀请链接",
"cmd.member.add.short": "按用户 ID 添加仓库成员",
"cmd.member.batch-add.short": "按用户 ID 列表或 CSV 文件批量添加仓库成员",
"cmd.member.invite-info.short": "查看仓库邀请链接信息",
"cmd.member.invite-link.short": "获取或创建仓库邀请链接",
"cmd.member.list.short": "列出仓库成员",
"cmd.member.remove.short": "按用户 ID 移除仓库成员",
"cmd.member.role.short": "变更仓库成员角色",
"cmd.member.short": "仓库成员操作",
"cmd.milestone.close.short": "关闭里程碑",
"cmd.milestone.create.short": "创建里程碑",
"cmd.milestone.delete.short": "删除里程碑",
"cmd.milestone.list.short": "列出里程碑",
"cmd.milestone.reopen.short": "重新打开里程碑",
"cmd.milestone.short": "里程碑操作",
"cmd.milestone.update.short": "更新里程碑",
"cmd.milestone.view.short": "查看里程碑详情及关联 issue",
"cmd.org.create.short": "创建组织",
"cmd.org.info.short": "显示组织详情",
"cmd.org.list.short": "列出组织",
"cmd.org.members.short": "列出组织成员",
"cmd.org.short": "组织操作",
"cmd.pipeline.delete.short": "删除流水线",
"cmd.pipeline.disable.short": "停用流水线工作流",
"cmd.pipeline.enable.short": "启用流水线工作流",
"cmd.pipeline.list.short": "列出平台流水线",
"cmd.pipeline.logs.short": "查询流水线运行日志",
"cmd.pipeline.results.short": "查看流水线运行报告结果",
"cmd.pipeline.run.short": "运行流水线工作流",
"cmd.pipeline.runs.short": "列出流水线运行记录",
"cmd.pipeline.save-yaml.short": "保存可视化流水线 YAML 图",
"cmd.pipeline.short": "流水线操作",
"cmd.pipeline.view.short": "查看流水线详情",
"cmd.pr.close.short": "关闭拉取请求",
"cmd.pr.comment.short": "给拉取请求添加评论",
"cmd.pr.create.short": "创建拉取请求",
@ -117,21 +54,15 @@
"cmd.pr.version_diff.short": "显示拉取请求补丁集版本 diff",
"cmd.pr.versions.short": "列出拉取请求补丁集版本",
"cmd.pr.view.short": "查看拉取请求详情",
"cmd.profile.ability.long": "显示用户的开发能力评分(影响力、贡献度、活跃度、项目经验、语言能力)及语言分布,数据来自 GitLink 平台统计接口。",
"cmd.profile.ability.short": "显示用户开发能力评分",
"cmd.profile.activity.long": "显示用户近期活动统计:时间范围内每日的疑修、合并请求和提交数量。",
"cmd.profile.activity.short": "显示用户近期活动统计",
"cmd.profile.contribution.long": "显示用户在指定年份的贡献热力图(每日贡献次数)。",
"cmd.profile.contribution.short": "显示用户贡献热力图",
"cmd.profile.major.long": "显示根据用户项目推断的专业/学科领域(如深度学习、量子计算)。",
"cmd.profile.major.short": "显示用户专业/学科定位",
"cmd.profile.role.long": "显示来自 GitLink 平台统计接口的用户角色定位。",
"cmd.profile.role.short": "显示用户角色定位",
"cmd.profile.short": "用户画像与统计操作",
"cmd.reaction.follow.short": "关注仓库",
"cmd.reaction.like.short": "点赞仓库",
"cmd.reaction.short": "仓库互动操作",
"cmd.reaction.stargazers.short": "列出仓库点赞用户",
"cmd.reaction.unfollow.short": "取消关注仓库",
"cmd.reaction.unlike.short": "取消点赞仓库",
"cmd.reaction.watchers.short": "列出仓库关注用户",
"cmd.release.create.short": "创建发布",
"cmd.release.delete.short": "删除发布",
"cmd.release.download.long": "按 id 或 tag 获取发行版并把全部附件下载到本地目录,对标 `gh release download`。",
"cmd.release.download.short": "下载发行版的全部附件",
"cmd.release.list.short": "列出发布",
"cmd.release.short": "发布操作",
"cmd.release.view.short": "查看发布详情",
@ -159,26 +90,12 @@
"cmd.webhook.test.short": "触发 Webhook 测试投递",
"cmd.webhook.update.short": "更新仓库 Webhook并在可用时保留未指定字段",
"cmd.webhook.view.short": "查看 Webhook 详情",
"cmd.wiki.create.short": "创建 wiki 页面",
"cmd.wiki.delete.short": "删除 wiki 页面",
"cmd.wiki.list.short": "列出 wiki 页面",
"cmd.wiki.short": "Wiki 页面管理",
"cmd.wiki.update.short": "更新已有 wiki 页面",
"cmd.wiki.view.short": "按页面名查看 wiki 页面",
"cmd.workflow.health.short": "用本地工作流规则为仓库健康度打分",
"cmd.workflow.pr-summary.short": "生成只读的 PR 评审摘要",
"cmd.workflow.repo-report.short": "生成只读的仓库工作流报告",
"cmd.workflow.short": "AI 智能体工作流分析",
"cmd.workflow.triage.short": "用本地工作流分诊规则分析 issue",
"error.auth.delete_token_failed": "删除 Token 失败:{message}",
"error.auth.login_failed": "登录失败:{message}",
"error.auth.store_token_failed": "保存 Token 失败:{message}",
"error.auth.token_empty": "Token 不能为空",
"error.config.save_failed": "保存配置失败:{message}",
"error.dataset.delete_confirm": "删除数据集附件具有破坏性;请先 --dry-run 预览,再传 --yes 确认",
"error.missing_required_flag": "缺少必需参数 --{name}",
"error.profile.user_required": "无法确定目标用户;请通过 --user 指定,或先运行 gitlink-cli auth login 登录",
"error.release.no_attachments": "该发行版没有可下载的附件",
"error.unsupported_language": "不支持的语言:{lang}",
"flag.api.batch_continue_on_error": "批处理请求失败后继续执行后续请求",
"flag.api.batch_dry_run": "预览批处理请求,不发送远端请求",
@ -189,41 +106,18 @@
"flag.api.body_stdin": "从标准输入读取 JSON 请求体",
"flag.api.header": "附加请求头key:value",
"flag.api.query": "查询参数key=val&key2=val2",
"flag.attachment.concurrency": "多文件(逗号分隔)上传时的并发数(默认 3",
"flag.attachment.description": "附件描述",
"flag.attachment.file": "要上传的本地文件路径",
"flag.attachment.id": "附件 ID",
"flag.attachment.output": "输出文件路径(默认为附件 id",
"flag.auth.token": "通过粘贴已有 Token 登录",
"flag.branch.from": "源分支或 Commit",
"flag.branch.name": "分支名称",
"flag.catalog.name": "按模板名称筛选",
"flag.ci.build": "构建编号",
"flag.ci.stage": "阶段编号",
"flag.ci.step": "步骤编号",
"flag.comment.body": "评论内容",
"flag.compare.base": "目标分支、标签或提交",
"flag.compare.file": "按文件路径过滤",
"flag.compare.head": "源分支、标签或提交",
"flag.dataset.description": "数据集描述",
"flag.dataset.dry_run": "预览请求,不写入数据集",
"flag.dataset.dry_run_delete": "预览请求,不删除附件",
"flag.dataset.ids": "用于查询数据集的项目 ID逗号分隔",
"flag.dataset.license_id": "许可证 ID",
"flag.dataset.limit": "附件每页数量",
"flag.dataset.page": "附件页码",
"flag.dataset.paper_content": "研究论文内容",
"flag.dataset.title": "数据集标题",
"flag.dataset.uuid": "附件 UUID",
"flag.dataset.yes": "确认执行破坏性删除",
"flag.debug": "启用调试输出",
"flag.description": "描述",
"flag.doctor.skip_network": "跳过需要访问 GitLink 的认证连通性检查",
"flag.dry_run": "预览请求,不实际创建",
"flag.format": "输出格式json、table、yaml默认table",
"flag.health.db": "SQLite 数据库路径(默认:~/.agents/skills/gitlink-health/data/gitlink_health.db",
"flag.health.max_pages": "每次查询的最大页数(默认:不限)",
"flag.ignore.name": "按名称过滤 ignore 模板",
"flag.issue.add_label": "要添加到每个匹配议题的标签",
"flag.issue.assignee": "负责人登录名",
"flag.issue.assignee_id": "负责人用户 ID",
@ -249,57 +143,13 @@
"flag.issue.status_id": "议题状态 ID",
"flag.issue.tag_ids": "逗号分隔的议题标签 ID",
"flag.issue.title": "议题标题",
"flag.label.color": "标签颜色(十六进制),例如:#1E90FF",
"flag.label.description": "标签描述",
"flag.label.id": "标签 ID",
"flag.label.keyword": "按关键词过滤标签",
"flag.label.name": "标签名称",
"flag.label.only_name": "仅返回标签 id 和名称true 或 false",
"flag.label.sort_by": "排序字段updated_on、created_on、issues_count",
"flag.label.sort_direction": "排序方向asc 或 desc",
"flag.lang": "显示语言",
"flag.license.name": "按名称过滤许可证",
"flag.limit": "每页条目数",
"flag.member.apply": "通过邀请加入是否需要审批true 或 false",
"flag.member.dry_run": "仅预览将添加的成员,不实际变更",
"flag.member.from": "从 CSV 文件读取用户 ID。支持 user_id/id 列或无表头的第一列",
"flag.member.role": "成员角色Manager、Developer 或 Reporter",
"flag.member.role_2": "邀请角色manager、developer 或 reporter",
"flag.member.sign": "邀请链接签名",
"flag.member.user_id": "要添加的 GitLink 用户 ID",
"flag.member.user_id_2": "要更新的 GitLink 用户 ID",
"flag.member.user_ids": "逗号分隔的 GitLink 用户 ID例如101,102",
"flag.milestone.assigner_id": "按负责人 ID 过滤 issue",
"flag.milestone.author_id": "按作者 ID 过滤 issue",
"flag.milestone.category": "按类别过滤opening、closed",
"flag.milestone.category_2": "按类别过滤 issueall、opened、closed",
"flag.milestone.description": "里程碑描述",
"flag.milestone.due_date": "截止日期YYYY-MM-DD 格式)",
"flag.milestone.id": "里程碑 ID",
"flag.milestone.issue_tag_ids": "逗号分隔的 issue 标签 ID",
"flag.milestone.name": "里程碑名称",
"flag.milestone.only_name": "仅返回里程碑 id 和名称true 或 false",
"flag.milestone.sort_by": "排序字段created_on、updated_on、effective_date、issues_count、percent",
"flag.milestone.sort_by_2": "排序字段issues.created_on、issues.updated_on、issue_priorities.position",
"flag.milestone.sort_direction": "排序方向asc 或 desc",
"flag.org.id": "组织 ID",
"flag.org.id_or_login": "组织 ID 或登录名",
"flag.org.name": "组织名称",
"flag.owner": "仓库所有者(自动从 git remote 检测)",
"flag.page": "页码",
"flag.pipeline.cursor": "日志游标",
"flag.pipeline.dry_run": "仅预览运行请求,不实际启动流水线",
"flag.pipeline.dry_run_2": "仅预览请求,不变更流水线状态",
"flag.pipeline.expanded": "日志游标是否展开",
"flag.pipeline.id": "流水线 ID",
"flag.pipeline.index": "运行序号",
"flag.pipeline.job": "任务序号",
"flag.pipeline.owner_id": "所有者用户或组织 ID",
"flag.pipeline.pipeline_json": "流水线图 JSON 对象或字符串",
"flag.pipeline.ref": "分支、标签或提交 SHA",
"flag.pipeline.run_id": "流水线运行 ID",
"flag.pipeline.step": "日志步骤",
"flag.pipeline.workflow": "工作流文件名",
"flag.pr.assignee_id": "指派人用户 ID",
"flag.pr.base": "目标分支",
"flag.pr.body": "PR 描述",
@ -318,16 +168,13 @@
"flag.pr.tag_id": "议题标签 ID",
"flag.pr.title": "PR 标题",
"flag.pr.version_id": "补丁集版本 ID",
"flag.profile.end_time": "结束时间Unix 时间戳)",
"flag.profile.start_time": "开始时间Unix 时间戳)",
"flag.profile.user": "目标用户登录名(默认为当前认证用户)",
"flag.profile.year": "贡献热力图的年份(如 2025",
"flag.release.attachment_files": "以逗号分隔的本地文件路径,自动上传并附加到发行版",
"flag.reaction.end_at": "结束 Unix 时间戳",
"flag.reaction.project_id": "GitLink 项目 ID。未提供时会根据 --owner/--repo 自动解析。",
"flag.reaction.start_at": "开始 Unix 时间戳",
"flag.release.body": "发布说明",
"flag.release.id": "发布 ID",
"flag.release.id_or_tag": "发布 ID 或标签",
"flag.release.name": "发布名称",
"flag.release.output_dir": "下载文件保存目录(默认当前目录)",
"flag.release.prerelease": "标记为预发布true/false",
"flag.release.tag": "标签名称",
"flag.release.target": "目标分支",
@ -353,58 +200,6 @@
"flag.webhook.secret_update": "Webhook 密钥。如果服务端不返回已有密钥,请再次传入。",
"flag.webhook.type": "Webhook 类型gitea/slack/discord/dingtalk/telegram/msteams/feishu/matrix/jianmu/softbot",
"flag.webhook.url": "Webhook 目标 URL",
"flag.wiki.content": "wiki 页面内容markdown",
"flag.wiki.gateway": "使用网关 API 端点",
"flag.wiki.message": "提交说明",
"flag.wiki.page_name": "wiki 页面名slug",
"flag.wiki.project_id": "GitLink 项目 ID",
"flag.wiki.title": "wiki 页面标题",
"flag.workflow.agent_readiness_known": "Agent 就绪度得分是否已知",
"flag.workflow.agent_readiness_score": "Agent 就绪度得分0 到 10",
"flag.workflow.author": "单 issue 本地分析的 issue 作者",
"flag.workflow.body": "单 issue 本地分析的 issue 正文",
"flag.workflow.ci_known": "CI 状态是否已知",
"flag.workflow.ci_passing": "CI 是否通过",
"flag.workflow.dry_run": "仅预览工作流建议,不做远程写入",
"flag.workflow.from": "从 JSON 文件读取 issue 输入。支持单个 issue、数组或含 issues 字段的对象",
"flag.workflow.from_2": "从 JSON 文件读取健康度输入",
"flag.workflow.has_contributing": "是否存在 CONTRIBUTING",
"flag.workflow.has_license": "是否存在 LICENSE",
"flag.workflow.has_readme": "是否存在 README",
"flag.workflow.has_recent_release": "是否有近期 release",
"flag.workflow.labels": "单 issue 本地分析的逗号分隔标签",
"flag.workflow.lang": "输出语言en 或 zh-CN",
"flag.workflow.limit": "分析的最大 issue 数",
"flag.workflow.number": "单 issue 本地分析的 issue 编号",
"flag.workflow.open_issues": "打开的 issue 数",
"flag.workflow.open_prs": "打开的 PR 数",
"flag.workflow.page": "远程分诊的 API 页码",
"flag.workflow.pr_summary.from": "从 JSON 文件读取 PR 摘要输入",
"flag.workflow.pr_summary.include_commits": "远程模式下抓取提交",
"flag.workflow.pr_summary.include_files": "远程模式下抓取变更文件",
"flag.workflow.pr_summary.lang": "输出语言en 或 zh-CN",
"flag.workflow.pr_summary.max_commits": "分析的最大提交数",
"flag.workflow.pr_summary.max_files": "分析的最大变更文件数",
"flag.workflow.pr_summary.number": "远程只读分析的 PR 编号",
"flag.workflow.recent_activity_days": "距最近活动的天数",
"flag.workflow.recent_activity_known": "最近活动是否已知",
"flag.workflow.release_known": "release 状态是否已知",
"flag.workflow.repo_report.from": "从 JSON 文件读取仓库报告输入",
"flag.workflow.repo_report.include_health": "包含仓库健康度摘要",
"flag.workflow.repo_report.include_issues": "包含 issue 分诊摘要",
"flag.workflow.repo_report.include_prs": "包含 PR 摘要",
"flag.workflow.repo_report.issue_limit": "抓取并分析的最大 issue 数",
"flag.workflow.repo_report.lang": "输出语言en 或 zh-CN",
"flag.workflow.repo_report.pr_limit": "抓取并汇总的最大 PR 数",
"flag.workflow.repo_report.stale_days": "issue 或 PR 视为停滞的天数",
"flag.workflow.repository": "仓库名,例如 owner/repo",
"flag.workflow.since": "可选的远程 issue 更新时间过滤",
"flag.workflow.stale_days": "issue 或 PR 视为停滞的天数",
"flag.workflow.stale_issues": "停滞 issue 数",
"flag.workflow.stale_prs": "停滞 PR 数",
"flag.workflow.state": "过滤或指定 issue 状态",
"flag.workflow.title": "单 issue 本地分析的 issue 标题",
"flag.workflow.url": "单 issue 本地分析的 issue URL",
"output.auth.env_hint": " 或设置 {env} 环境变量",
"output.auth.login_hint": " 运行gitlink-cli auth login",
"output.config.file": "配置文件:{path}",

View File

@ -0,0 +1,211 @@
package reaction
import (
"fmt"
"net/url"
"strconv"
"strings"
"github.com/gitlink-org/gitlink-cli/internal/i18n"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
tr := i18n.Default()
if len(translators) > 0 && translators[0] != nil {
tr = translators[0]
}
return []*common.Shortcut{
{
Name: "watchers",
Description: tr.T("cmd.reaction.watchers.short"),
Flags: timeRangeFlags(tr),
Run: runWatchers,
},
{
Name: "stargazers",
Description: tr.T("cmd.reaction.stargazers.short"),
Flags: timeRangeFlags(tr),
Run: runStargazers,
},
{
Name: "follow",
Description: tr.T("cmd.reaction.follow.short"),
Flags: projectIDFlags(tr),
Run: runFollow,
},
{
Name: "unfollow",
Description: tr.T("cmd.reaction.unfollow.short"),
Flags: projectIDFlags(tr),
Run: runUnfollow,
},
{
Name: "like",
Description: tr.T("cmd.reaction.like.short"),
Flags: projectIDFlags(tr),
Run: runLike,
},
{
Name: "unlike",
Description: tr.T("cmd.reaction.unlike.short"),
Flags: projectIDFlags(tr),
Run: runUnlike,
},
}
}
func timeRangeFlags(tr *i18n.Translator) []common.Flag {
return []common.Flag{
{Name: "start-at", Usage: tr.T("flag.reaction.start_at")},
{Name: "end-at", Usage: tr.T("flag.reaction.end_at")},
}
}
func projectIDFlags(tr *i18n.Translator) []common.Flag {
return []common.Flag{
{Name: "project-id", Usage: tr.T("flag.reaction.project_id")},
}
}
func runWatchers(ctx *common.RuntimeContext) error {
return runUserList(ctx, "watchers")
}
func runStargazers(ctx *common.RuntimeContext) error {
return runUserList(ctx, "stargazers")
}
func runUserList(ctx *common.RuntimeContext, kind string) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
query, err := timeRangeQuery(ctx)
if err != nil {
return err
}
env, err := ctx.CallAPIWithQuery("GET", ctx.RepoPath()+"/"+kind, query)
if err != nil {
return err
}
return ctx.Output(env)
}
func runFollow(ctx *common.RuntimeContext) error {
return runFollowAction(ctx, "POST", "/watchers/follow")
}
func runUnfollow(ctx *common.RuntimeContext) error {
return runFollowAction(ctx, "DELETE", "/watchers/unfollow")
}
func runFollowAction(ctx *common.RuntimeContext, method, path string) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
projectID, err := resolveProjectID(ctx)
if err != nil {
return err
}
query := url.Values{}
query.Set("target_type", "project")
query.Set("id", projectID)
env, err := ctx.CallAPIWithQuery(method, path, query)
if err != nil {
return err
}
return ctx.Output(env)
}
func runLike(ctx *common.RuntimeContext) error {
return runPraiseAction(ctx, "POST", "like")
}
func runUnlike(ctx *common.RuntimeContext) error {
return runPraiseAction(ctx, "DELETE", "unlike")
}
func runPraiseAction(ctx *common.RuntimeContext, method, action string) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
projectID, err := resolveProjectID(ctx)
if err != nil {
return err
}
env, err := ctx.CallAPI(method, fmt.Sprintf("/projects/%s/praise_tread/%s", projectID, action), nil)
if err != nil {
return err
}
return ctx.Output(env)
}
func timeRangeQuery(ctx *common.RuntimeContext) (url.Values, error) {
query := url.Values{}
if start := strings.TrimSpace(ctx.Arg("start-at")); start != "" {
if err := validateUnixTimestamp("start-at", start); err != nil {
return nil, err
}
query.Set("start_at", start)
}
if end := strings.TrimSpace(ctx.Arg("end-at")); end != "" {
if err := validateUnixTimestamp("end-at", end); err != nil {
return nil, err
}
query.Set("end_at", end)
}
return query, nil
}
func validateUnixTimestamp(name, value string) error {
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil || parsed < 0 {
return fmt.Errorf("invalid --%s %q: use a non-negative Unix timestamp", name, value)
}
return nil
}
func resolveProjectID(ctx *common.RuntimeContext) (string, error) {
if raw := strings.TrimSpace(ctx.Arg("project-id")); raw != "" {
return normalizeProjectID(raw)
}
env, err := ctx.CallAPI("GET", ctx.RepoPath(), nil)
if err != nil {
return "", fmt.Errorf("resolve project id: %w", err)
}
data, ok := env.Data.(map[string]interface{})
if !ok {
return "", fmt.Errorf("resolve project id: unexpected repository response")
}
for _, key := range []string{"id", "project_id"} {
if id := projectIDString(data[key]); id != "" {
return id, nil
}
}
return "", fmt.Errorf("resolve project id: repository response did not include id")
}
func normalizeProjectID(value string) (string, error) {
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil || parsed <= 0 {
return "", fmt.Errorf("invalid --project-id %q: use a positive numeric project ID", value)
}
return value, nil
}
func projectIDString(value interface{}) string {
switch v := value.(type) {
case float64:
return strconv.FormatInt(int64(v), 10)
case int:
return strconv.Itoa(v)
case int64:
return strconv.FormatInt(v, 10)
case string:
id, err := normalizeProjectID(strings.TrimSpace(v))
if err == nil {
return id
}
}
return ""
}

View File

@ -0,0 +1,215 @@
package reaction
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/gitlink-org/gitlink-cli/internal/client"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func TestReactionWatchersBuildsTimeRangeQuery(t *testing.T) {
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
assertRequest(t, r, "GET", "/owner/repo/watchers.json")
assertEqual(t, r.URL.Query().Get("start_at"), "1700000000")
assertEqual(t, r.URL.Query().Get("end_at"), "1700003600")
writeJSON(t, w, map[string]interface{}{"count": 0, "users": []interface{}{}})
})
defer server.Close()
err := runReactionShortcut(t, server, "watchers", map[string]string{
"start-at": "1700000000",
"end-at": "1700003600",
})
if err != nil {
t.Fatalf("watchers shortcut failed: %v", err)
}
}
func TestReactionStargazers(t *testing.T) {
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
assertRequest(t, r, "GET", "/owner/repo/stargazers.json")
writeJSON(t, w, map[string]interface{}{"count": 1, "users": []interface{}{}})
})
defer server.Close()
if err := runReactionShortcut(t, server, "stargazers", nil); err != nil {
t.Fatalf("stargazers shortcut failed: %v", err)
}
}
func TestReactionFollowResolvesProjectID(t *testing.T) {
requests := 0
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
requests++
switch requests {
case 1:
assertRequest(t, r, "GET", "/owner/repo.json")
writeJSON(t, w, map[string]interface{}{"id": float64(123)})
case 2:
assertRequest(t, r, "POST", "/watchers/follow.json")
assertEqual(t, r.URL.Query().Get("target_type"), "project")
assertEqual(t, r.URL.Query().Get("id"), "123")
writeJSON(t, w, map[string]interface{}{"status": 0, "message": "success", "watched": true})
default:
t.Fatalf("unexpected extra request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
if err := runReactionShortcut(t, server, "follow", nil); err != nil {
t.Fatalf("follow shortcut failed: %v", err)
}
assertEqual(t, requests, 2)
}
func TestReactionUnfollowUsesExplicitProjectID(t *testing.T) {
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
assertRequest(t, r, "DELETE", "/watchers/unfollow.json")
assertEqual(t, r.URL.Query().Get("target_type"), "project")
assertEqual(t, r.URL.Query().Get("id"), "456")
writeJSON(t, w, map[string]interface{}{"status": 0, "message": "success", "watched": false})
})
defer server.Close()
if err := runReactionShortcut(t, server, "unfollow", map[string]string{"project-id": "456"}); err != nil {
t.Fatalf("unfollow shortcut failed: %v", err)
}
}
func TestReactionLikeUsesExplicitProjectID(t *testing.T) {
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
assertRequest(t, r, "POST", "/projects/456/praise_tread/like.json")
writeJSON(t, w, map[string]interface{}{"status": 0, "message": "success"})
})
defer server.Close()
if err := runReactionShortcut(t, server, "like", map[string]string{"project-id": "456"}); err != nil {
t.Fatalf("like shortcut failed: %v", err)
}
}
func TestReactionUnlikeResolvesStringProjectID(t *testing.T) {
requests := 0
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
requests++
switch requests {
case 1:
assertRequest(t, r, "GET", "/owner/repo.json")
writeJSON(t, w, map[string]interface{}{"project_id": "789"})
case 2:
assertRequest(t, r, "DELETE", "/projects/789/praise_tread/unlike.json")
writeJSON(t, w, map[string]interface{}{"status": 0, "message": "success"})
default:
t.Fatalf("unexpected extra request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
if err := runReactionShortcut(t, server, "unlike", nil); err != nil {
t.Fatalf("unlike shortcut failed: %v", err)
}
assertEqual(t, requests, 2)
}
func TestReactionValidation(t *testing.T) {
server := newReactionTestServer(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("invalid input should not call API, got: %s %s", r.Method, r.URL.Path)
})
defer server.Close()
cases := []struct {
name string
shortcut string
args map[string]string
}{
{
name: "invalid start",
shortcut: "watchers",
args: map[string]string{"start-at": "soon"},
},
{
name: "negative end",
shortcut: "stargazers",
args: map[string]string{"end-at": "-1"},
},
{
name: "invalid project id",
shortcut: "like",
args: map[string]string{"project-id": "repo"},
},
{
name: "negative project id",
shortcut: "like",
args: map[string]string{"project-id": "-1"},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if err := runReactionShortcut(t, server, tc.shortcut, tc.args); err == nil {
t.Fatal("expected validation error")
}
})
}
}
func runReactionShortcut(t *testing.T, server *httptest.Server, name string, args map[string]string) error {
t.Helper()
shortcut := findReactionShortcut(t, name)
ctx := &common.RuntimeContext{
Client: &client.Client{
HTTP: server.Client(),
BaseURL: server.URL,
},
Owner: "owner",
Repo: "repo",
Format: "json",
Args: args,
}
if ctx.Args == nil {
ctx.Args = map[string]string{}
}
return shortcut.Run(ctx)
}
func findReactionShortcut(t *testing.T, name string) *common.Shortcut {
t.Helper()
for _, shortcut := range Shortcuts() {
if shortcut.Name == name {
return shortcut
}
}
t.Fatalf("shortcut %q not found", name)
return nil
}
func newReactionTestServer(t *testing.T, handler http.HandlerFunc) *httptest.Server {
t.Helper()
return httptest.NewServer(handler)
}
func assertRequest(t *testing.T, r *http.Request, method, path string) {
t.Helper()
if r.Method != method || r.URL.Path != path {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
}
func writeJSON(t *testing.T, w http.ResponseWriter, payload interface{}) {
t.Helper()
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(payload); err != nil {
t.Fatalf("failed to write response: %v", err)
}
}
func assertEqual(t *testing.T, got interface{}, want interface{}) {
t.Helper()
if fmt.Sprintf("%v", got) != fmt.Sprintf("%v", want) {
t.Fatalf("got %v (%T), want %v (%T)", got, got, want, want)
}
}

View File

@ -17,6 +17,7 @@ import (
"github.com/gitlink-org/gitlink-cli/shortcuts/org"
"github.com/gitlink-org/gitlink-cli/shortcuts/pipeline"
"github.com/gitlink-org/gitlink-cli/shortcuts/pr"
"github.com/gitlink-org/gitlink-cli/shortcuts/reaction"
"github.com/gitlink-org/gitlink-cli/shortcuts/release"
"github.com/gitlink-org/gitlink-cli/shortcuts/repo"
"github.com/gitlink-org/gitlink-cli/shortcuts/search"
@ -41,6 +42,7 @@ func RegisterAll(root *cobra.Command, translators ...*i18n.Translator) {
"milestone": milestone.Shortcuts(),
"pipeline": pipeline.Shortcuts(),
"pr": pr.Shortcuts(tr),
"reaction": reaction.Shortcuts(tr),
"release": release.Shortcuts(tr),
"branch": branch.Shortcuts(tr),
"org": org.Shortcuts(tr),
@ -63,6 +65,7 @@ func RegisterAll(root *cobra.Command, translators ...*i18n.Translator) {
"milestone": "Milestone operations",
"pipeline": "Pipeline operations",
"pr": tr.T("cmd.pr.short"),
"reaction": tr.T("cmd.reaction.short"),
"release": tr.T("cmd.release.short"),
"branch": tr.T("cmd.branch.short"),
"org": tr.T("cmd.org.short"),

View File

@ -11,10 +11,10 @@ func TestRegisterAll(t *testing.T) {
RegisterAll(root)
expectedGroups := []string{
"repo", "catalog", "issue", "label", "license", "pr", "release", "branch",
"repo", "issue", "label", "license", "pr", "release", "branch",
"org", "user", "search", "ci", "workflow",
"compare", "member", "milestone", "pipeline", "webhook",
"health", "wiki",
"health", "reaction",
}
groupSet := map[string]bool{}

View File

@ -73,6 +73,8 @@ skills/
│ ├── REFERENCE.md # 仓库 API 参考
│ └── examples/
│ └── repo-workflow.md # 仓库管理工作流
├── gitlink-reaction/ # 仓库互动
│ └── SKILL.md # 关注、点赞和用户列表操作指南
├── gitlink-issue/ # Issue 管理
│ ├── SKILL.md # Issue 操作指南
│ ├── REFERENCE.md # Issue API 参考
@ -137,6 +139,7 @@ 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-reaction** | 仓库互动 | `reaction +watchers`, `reaction +stargazers`, `reaction +follow`, `reaction +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 +merge`, `pr +versions`, `pr +version-diff`, `pr +reviews`, `pr +review` |
| **gitlink-member** | 仓库成员管理 | `member +list`, `member +add`, `member +batch-add`, `member +role`, `member +invite-link` |

View File

@ -0,0 +1,42 @@
---
name: gitlink-reaction
version: 1.0.0
description: "Repository reactions: list watchers/stargazers, follow or unfollow repositories, and like or unlike repositories."
metadata:
requires:
bins: ["gitlink-cli"]
cliHelp: "gitlink-cli reaction --help"
---
# gitlink-reaction
Use this skill when an agent needs to inspect or change repository social interaction state in GitLink.
## Shortcuts
| Shortcut | Purpose |
|----------|---------|
| `reaction +watchers` | List repository watchers |
| `reaction +stargazers` | List repository stargazers |
| `reaction +follow` | Follow a repository |
| `reaction +unfollow` | Unfollow a repository |
| `reaction +like` | Like a repository |
| `reaction +unlike` | Unlike a repository |
## Examples
```bash
gitlink-cli reaction +watchers --owner Gitlink --repo forgeplus
gitlink-cli reaction +watchers --owner Gitlink --repo forgeplus --start-at 1700000000 --end-at 1700003600
gitlink-cli reaction +stargazers --owner Gitlink --repo forgeplus
gitlink-cli reaction +follow --owner Gitlink --repo forgeplus
gitlink-cli reaction +unfollow --owner Gitlink --repo forgeplus
gitlink-cli reaction +like --owner Gitlink --repo forgeplus
gitlink-cli reaction +unlike --owner Gitlink --repo forgeplus
```
## Safety Notes
- Confirm the target repository before follow, unfollow, like, or unlike actions.
- Use `--project-id` when the caller already knows the numeric project ID to avoid the extra repository lookup.
- `--start-at` and `--end-at` must be Unix timestamps.