diff --git a/README.md b/README.md index d526660..c8bea48 100644 --- a/README.md +++ b/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 | diff --git a/README.zh-CN.md b/README.zh-CN.md index 85fda2c..276e590 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -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` | 仓库成员与邀请链接管理 | diff --git a/doc/changes/reaction-shortcut.md b/doc/changes/reaction-shortcut.md new file mode 100644 index 0000000..f370df9 --- /dev/null +++ b/doc/changes/reaction-shortcut.md @@ -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. diff --git a/internal/i18n/locales/en-US.json b/internal/i18n/locales/en-US.json index 5974e78..9c7bc35 100644 --- a/internal/i18n/locales/en-US.json +++ b/internal/i18n/locales/en-US.json @@ -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}", diff --git a/internal/i18n/locales/zh-CN.json b/internal/i18n/locales/zh-CN.json index a46fb46..62614b2 100644 --- a/internal/i18n/locales/zh-CN.json +++ b/internal/i18n/locales/zh-CN.json @@ -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}", diff --git a/shortcuts/reaction/reaction.go b/shortcuts/reaction/reaction.go new file mode 100644 index 0000000..3012245 --- /dev/null +++ b/shortcuts/reaction/reaction.go @@ -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 "" +} diff --git a/shortcuts/reaction/reaction_test.go b/shortcuts/reaction/reaction_test.go new file mode 100644 index 0000000..7f7ef34 --- /dev/null +++ b/shortcuts/reaction/reaction_test.go @@ -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) + } +} diff --git a/shortcuts/register.go b/shortcuts/register.go index f32312a..608dbd1 100644 --- a/shortcuts/register.go +++ b/shortcuts/register.go @@ -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"), diff --git a/shortcuts/register_test.go b/shortcuts/register_test.go index 6c2795b..a54baf3 100644 --- a/shortcuts/register_test.go +++ b/shortcuts/register_test.go @@ -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{} diff --git a/skills/README.md b/skills/README.md index 7cbab5f..ba0e5d2 100644 --- a/skills/README.md +++ b/skills/README.md @@ -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` | diff --git a/skills/gitlink-reaction/SKILL.md b/skills/gitlink-reaction/SKILL.md new file mode 100644 index 0000000..838c855 --- /dev/null +++ b/skills/gitlink-reaction/SKILL.md @@ -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.