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:
commit
2e2891c4b2
27
README.md
27
README.md
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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` | 仓库成员与邀请链接管理 |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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}",
|
||||
|
|
|
|||
|
|
@ -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": "按类别过滤 issue:all、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}",
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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` |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
Loading…
Reference in New Issue