Skip to main content
Glama
index.ts36.2 kB
#!/usr/bin/env node /** * keactl - Gitea Command Line Tool * 独立的命令行工具,提供完整的 Gitea API 操作能力 */ import { Command } from 'commander'; import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import chalk from 'chalk'; import { createInitCommand } from './commands/init/index.js'; import { createCICDCommand } from './commands/cicd/index.js'; import { createWorkflowCommand } from './commands/workflow/index.js'; // 获取 package.json 的版本信息 const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packageJson = JSON.parse( readFileSync(join(__dirname, '../../package.json'), 'utf-8') ); // 创建主命令 const program = new Command(); program .name('keactl') .description('Gitea Command Line Tool - 完整的 Gitea API 操作工具') .version(packageJson.version, '-v, --version', '显示版本号') .option('-t, --token <token>', 'Gitea API Token') .option('-s, --server <url>', 'Gitea Server URL') .option('-o, --owner <owner>', '仓库所有者(用户名或组织名)') .option('-r, --repo <repo>', '仓库名称') .option('--json', '以 JSON 格式输出结果') .option('--no-color', '禁用彩色输出'); // Help 头部显示版本信息 program.addHelpText('beforeAll', ` ◆ keactl v${packageJson.version} - Gitea Command Line Tool `); // Help 尾部显示仓库链接 program.addHelpText('afterAll', ` GitHub: https://github.com/SupenBysz/gitea-mcp-tool Wiki: https://github.com/SupenBysz/gitea-mcp-tool/wiki Issues: https://github.com/SupenBysz/gitea-mcp-tool/issues `); // 初始化命令 (放在最前面,最常用) program.addCommand(createInitCommand()); // CI/CD 管理命令 program.addCommand(createCICDCommand()); // Issue 工作流管理命令 program.addCommand(createWorkflowCommand()); // 上下文管理命令 program .command('context') .description('管理默认上下文') .addCommand( new Command('get') .description('获取当前上下文') .action(async () => { const { contextGet } = await import('./commands/context.js'); await contextGet(program.opts()); }) ) .addCommand( new Command('set') .description('设置默认上下文') .option('-o, --owner <owner>', '默认所有者') .option('-r, --repo <repo>', '默认仓库') .option('--org <org>', '默认组织') .option('--project <id>', '默认项目 ID') .action(async (options) => { const { contextSet } = await import('./commands/context.js'); await contextSet({ ...program.opts(), ...options }); }) ); // 用户命令 program .command('user') .description('用户管理') .addCommand( new Command('current') .description('获取当前用户信息') .action(async () => { const { userCurrent } = await import('./commands/user.js'); await userCurrent(program.opts()); }) ) .addCommand( new Command('get') .description('获取用户详情') .argument('<username>', '用户名') .action(async (username) => { const { userGet } = await import('./commands/user.js'); await userGet(username, program.opts()); }) ); // 仓库管理命令 program .command('repo') .description('仓库管理') .addCommand( new Command('list') .description('列出仓库') .option('-u, --owner <owner>', '所有者(留空则列出当前用户的仓库)') .option('-l, --limit <number>', '每页数量', '20') .option('-p, --page <number>', '页码', '1') .action(async (options) => { const { repoList } = await import('./commands/repo.js'); await repoList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取仓库详情') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { repoGet } = await import('./commands/repo.js'); await repoGet({ ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建新仓库') .requiredOption('-n, --name <name>', '仓库名称') .option('-d, --description <desc>', '仓库描述') .option('--private', '创建私有仓库') .option('--auto-init', '自动初始化(创建 README)') .option('-o, --owner <owner>', '所有者(组织名,留空则创建在个人账户下)') .action(async (options) => { const { repoCreate } = await import('./commands/repo.js'); await repoCreate({ ...program.opts(), ...options }); }) ) .addCommand( new Command('delete') .description('删除仓库') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-y, --yes', '跳过确认') .action(async (options) => { const { repoDelete } = await import('./commands/repo.js'); await repoDelete({ ...program.opts(), ...options }); }) ) .addCommand( new Command('search') .description('搜索仓库') .argument('<query>', '搜索关键词') .option('-l, --limit <number>', '每页数量', '10') .option('-p, --page <number>', '页码', '1') .action(async (query, options) => { const { repoSearch } = await import('./commands/repo.js'); await repoSearch(query, { ...program.opts(), ...options }); }) ); // Branch 分支管理命令 program .command('branch') .description('分支管理') .addCommand( new Command('list') .description('列出分支') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-l, --limit <number>', '每页数量', '30') .option('-p, --page <number>', '页码', '1') .action(async (options) => { const { branchList } = await import('./commands/branch.js'); await branchList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取分支详情') .argument('<name>', '分支名称') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (name, options) => { const { branchGet } = await import('./commands/branch.js'); await branchGet(name, { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建分支') .argument('<name>', '新分支名称') .option('--from <ref>', '从指定分支/tag/commit 创建') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (name, options) => { const { branchCreate } = await import('./commands/branch.js'); await branchCreate(name, { ...program.opts(), ...options }); }) ) .addCommand( new Command('delete') .description('删除分支') .argument('<name>', '分支名称') .option('-y, --yes', '跳过确认') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (name, options) => { const { branchDelete } = await import('./commands/branch.js'); await branchDelete(name, { ...program.opts(), ...options }); }) ) .addCommand( new Command('rename') .description('重命名分支') .argument('<old-name>', '原分支名称') .argument('<new-name>', '新分支名称') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (oldName, newName, options) => { const { branchRename } = await import('./commands/branch.js'); await branchRename(oldName, newName, { ...program.opts(), ...options }); }) ) .addCommand( new Command('protection') .description('分支保护规则管理') .addCommand( new Command('list') .description('列出分支保护规则') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { protectionList } = await import('./commands/branch.js'); await protectionList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取分支保护规则详情') .argument('<name>', '规则名称') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (name, options) => { const { protectionGet } = await import('./commands/branch.js'); await protectionGet(name, { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建分支保护规则') .requiredOption('--rule-name <name>', '规则名称(可使用通配符如 main, release/*)') .option('--enable-push', '允许推送') .option('--required-approvals <number>', '要求的审批数量') .option('--enable-status-check', '启用状态检查') .option('--require-signed-commits', '要求签名提交') .option('--block-on-rejected-reviews', '有拒绝审查时阻止合并') .option('--block-on-outdated-branch', '分支过时时阻止合并') .option('--dismiss-stale-approvals', '推送后取消过时审批') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { protectionCreate } = await import('./commands/branch.js'); await protectionCreate({ ...program.opts(), ...options }); }) ) .addCommand( new Command('update') .description('更新分支保护规则') .argument('<name>', '规则名称') .option('--enable-push', '允许推送') .option('--no-enable-push', '禁止推送') .option('--required-approvals <number>', '要求的审批数量') .option('--enable-status-check', '启用状态检查') .option('--no-enable-status-check', '禁用状态检查') .option('--require-signed-commits', '要求签名提交') .option('--no-require-signed-commits', '不要求签名提交') .option('--block-on-rejected-reviews', '有拒绝审查时阻止合并') .option('--block-on-outdated-branch', '分支过时时阻止合并') .option('--dismiss-stale-approvals', '推送后取消过时审批') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (name, options) => { const { protectionUpdate } = await import('./commands/branch.js'); await protectionUpdate(name, { ...program.opts(), ...options }); }) ) .addCommand( new Command('delete') .description('删除分支保护规则') .argument('<name>', '规则名称') .option('-y, --yes', '跳过确认') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (name, options) => { const { protectionDelete } = await import('./commands/branch.js'); await protectionDelete(name, { ...program.opts(), ...options }); }) ) ); // Issue 管理命令 program .command('issue') .description('Issue 管理') .addCommand( new Command('list') .description('列出 Issues') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-s, --state <state>', '状态过滤 (open/closed/all)', 'open') .option('-l, --limit <number>', '每页数量', '20') .option('-p, --page <number>', '页码', '1') .action(async (options) => { const { issueList } = await import('./commands/issue.js'); await issueList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取 Issue 详情') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, options) => { const { issueGet } = await import('./commands/issue.js'); await issueGet(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建新 Issue') .requiredOption('-t, --title <title>', 'Issue 标题') .option('-b, --body <body>', 'Issue 内容') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-a, --assignees <users...>', '指派给(用户名列表)') .option('-l, --labels <ids...>', '标签 ID 列表') .action(async (options) => { const { issueCreate } = await import('./commands/issue.js'); await issueCreate({ ...program.opts(), ...options }); }) ) .addCommand( new Command('update') .description('更新 Issue') .argument('<index>', 'Issue 编号') .option('-t, --title <title>', '新标题') .option('-b, --body <body>', '新内容') .option('-s, --state <state>', '新状态 (open/closed)') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, options) => { const { issueUpdate } = await import('./commands/issue.js'); await issueUpdate(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('close') .description('关闭 Issue') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, options) => { const { issueClose } = await import('./commands/issue.js'); await issueClose(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('comment') .description('添加 Issue 评论') .argument('<index>', 'Issue 编号') .requiredOption('-b, --body <body>', '评论内容') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, options) => { const { issueComment } = await import('./commands/issue.js'); await issueComment(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('comments') .description('列出 Issue 评论') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-l, --limit <number>', '每页数量', '30') .option('-p, --page <number>', '页码', '1') .action(async (index, options) => { const { issueCommentsList } = await import('./commands/issue.js'); await issueCommentsList(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('comment-get') .description('获取评论详情') .argument('<id>', '评论 ID') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { issueCommentGet } = await import('./commands/issue.js'); await issueCommentGet(parseInt(id), { ...program.opts(), ...options }); }) ) .addCommand( new Command('comment-edit') .description('编辑评论') .argument('<id>', '评论 ID') .requiredOption('-b, --body <body>', '新评论内容') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { issueCommentEdit } = await import('./commands/issue.js'); await issueCommentEdit(parseInt(id), { ...program.opts(), ...options }); }) ) .addCommand( new Command('comment-delete') .description('删除评论') .argument('<id>', '评论 ID') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { issueCommentDelete } = await import('./commands/issue.js'); await issueCommentDelete(parseInt(id), { ...program.opts(), ...options }); }) ) .addCommand( new Command('deps') .description('查看 Issue 依赖列表') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-l, --limit <number>', '每页数量', '30') .option('-p, --page <number>', '页码', '1') .action(async (index, options) => { const { issueDeps } = await import('./commands/issue.js'); await issueDeps(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('dep-add') .description('添加 Issue 依赖关系') .argument('<index>', 'Issue 编号') .argument('<dependency-index>', '依赖的 Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, dependencyIndex, options) => { const { issueDepAdd } = await import('./commands/issue.js'); await issueDepAdd(parseInt(index), parseInt(dependencyIndex), { ...program.opts(), ...options }); }) ) .addCommand( new Command('dep-remove') .description('移除 Issue 依赖关系') .argument('<index>', 'Issue 编号') .argument('<dependency-index>', '要移除的依赖 Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, dependencyIndex, options) => { const { issueDepRemove } = await import('./commands/issue.js'); await issueDepRemove(parseInt(index), parseInt(dependencyIndex), { ...program.opts(), ...options }); }) ) .addCommand( new Command('blocks') .description('查看哪些 Issue 依赖当前 Issue(反向查询)') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-l, --limit <number>', '每页数量', '30') .option('-p, --page <number>', '页码', '1') .action(async (index, options) => { const { issueBlocks } = await import('./commands/issue.js'); await issueBlocks(parseInt(index), { ...program.opts(), ...options }); }) ) // ===== 工作流集成命令 ===== .addCommand( new Command('infer-labels') .description('智能推断 Issue 标签') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('--apply', '自动应用推断的标签') .action(async (index, options) => { const { inferLabels } = await import('./commands/workflow/infer-labels.js'); await inferLabels({ ...program.opts(), ...options, issue: index, autoApply: options.apply, }); }) ) .addCommand( new Command('check') .description('检查单个 Issue 是否符合工作流规范') .argument('<index>', 'Issue 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('--json', '以 JSON 格式输出') .action(async (index, options) => { const { checkIssues } = await import('./commands/workflow/check-issues.js'); await checkIssues({ ...program.opts(), ...options, issue: index, }); }) ) .addCommand( new Command('check-all') .description('检查所有开放 Issue 是否符合工作流规范') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('--json', '以 JSON 格式输出') .action(async (options) => { const { checkIssues } = await import('./commands/workflow/check-issues.js'); await checkIssues({ ...program.opts(), ...options }); }) ) .addCommand( new Command('check-blocked') .description('检测阻塞或超期的 Issue') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('--threshold <hours>', 'SLA 阈值(小时)') .option('--json', '以 JSON 格式输出') .action(async (options) => { const { checkBlocked } = await import('./commands/workflow/check-blocked.js'); await checkBlocked({ ...program.opts(), ...options }); }) ) .addCommand( new Command('escalate') .description('自动升级老化 Issue 的优先级') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('--dry-run', '预览变更但不执行') .action(async (options) => { const { escalatePriority } = await import('./commands/workflow/escalate.js'); await escalatePriority({ ...program.opts(), ...options, dryRun: options.dryRun }); }) ); // PR 管理命令 program .command('pr') .description('Pull Request 管理') .addCommand( new Command('list') .description('列出 Pull Requests') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-s, --state <state>', '状态过滤 (open/closed/all)', 'open') .option('-l, --limit <number>', '每页数量', '20') .option('-p, --page <number>', '页码', '1') .action(async (options) => { const { prList } = await import('./commands/pr.js'); await prList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取 PR 详情') .argument('<index>', 'PR 编号') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, options) => { const { prGet } = await import('./commands/pr.js'); await prGet(parseInt(index), { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建新 PR') .requiredOption('-t, --title <title>', 'PR 标题') .requiredOption('--head <branch>', '源分支') .requiredOption('--base <branch>', '目标分支') .option('-b, --body <body>', 'PR 描述') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { prCreate } = await import('./commands/pr.js'); await prCreate({ ...program.opts(), ...options }); }) ) .addCommand( new Command('merge') .description('合并 PR') .argument('<index>', 'PR 编号') .option('-m, --method <method>', '合并方式 (merge/rebase/squash)', 'merge') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (index, options) => { const { prMerge } = await import('./commands/pr.js'); await prMerge(parseInt(index), { ...program.opts(), ...options }); }) ); // Wiki 管理命令 program .command('wiki') .description('Wiki 页面管理') .addCommand( new Command('list') .description('列出 Wiki 页面') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-l, --limit <number>', '每页数量', '30') .option('-p, --page <number>', '页码', '1') .action(async (options) => { const { wikiList } = await import('./commands/wiki.js'); await wikiList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取 Wiki 页面内容') .argument('<page>', '页面名称') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (page, options) => { const { wikiGet } = await import('./commands/wiki.js'); await wikiGet(page, { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建 Wiki 页面') .requiredOption('-t, --title <title>', '页面标题') .requiredOption('-c, --content <content>', '页面内容 (Markdown)') .option('-m, --message <message>', '提交信息') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { wikiCreate } = await import('./commands/wiki.js'); await wikiCreate({ ...program.opts(), ...options }); }) ) .addCommand( new Command('update') .description('更新 Wiki 页面') .argument('<page>', '页面名称') .option('-t, --title <title>', '新标题') .option('-c, --content <content>', '新内容 (Markdown)') .option('-m, --message <message>', '提交信息') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (page, options) => { const { wikiUpdate } = await import('./commands/wiki.js'); await wikiUpdate(page, { ...program.opts(), ...options }); }) ) .addCommand( new Command('delete') .description('删除 Wiki 页面') .argument('<page>', '页面名称') .option('-y, --yes', '跳过确认') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (page, options) => { const { wikiDelete } = await import('./commands/wiki.js'); await wikiDelete(page, { ...program.opts(), ...options }); }) ) .addCommand( new Command('search') .description('搜索 Wiki 页面') .argument('<query>', '搜索关键词') .option('-l, --limit <number>', '结果数量', '20') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (query, options) => { const { wikiSearch } = await import('./commands/wiki.js'); await wikiSearch(query, { ...program.opts(), ...options }); }) ) .addCommand( new Command('revisions') .description('查看 Wiki 页面修订历史') .argument('<page>', '页面名称') .option('-l, --limit <number>', '每页数量', '20') .option('-p, --page <number>', '页码', '1') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (page, options) => { const { wikiRevisions } = await import('./commands/wiki.js'); await wikiRevisions(page, { ...program.opts(), ...options }); }) ) .addCommand( new Command('revision-get') .description('获取 Wiki 页面特定版本') .argument('<page>', '页面名称') .argument('<revision>', '版本 SHA') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (page, revision, options) => { const { wikiRevisionGet } = await import('./commands/wiki.js'); await wikiRevisionGet(page, revision, { ...program.opts(), ...options }); }) ); // Release 发布管理命令 program .command('release') .description('发布版本管理') .addCommand( new Command('list') .description('列出发布版本') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-l, --limit <number>', '每页数量', '30') .option('-p, --page <number>', '页码', '1') .option('--draft', '只显示草稿') .option('--prerelease', '只显示预发布版本') .action(async (options) => { const { releaseList } = await import('./commands/release.js'); await releaseList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取发布版本详情(按 ID)') .argument('<id>', '发布 ID') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { releaseGet } = await import('./commands/release.js'); await releaseGet(id, { ...program.opts(), ...options }); }) ) .addCommand( new Command('get-by-tag') .description('获取发布版本详情(按 Tag)') .argument('<tag>', 'Tag 名称') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (tag, options) => { const { releaseGetByTag } = await import('./commands/release.js'); await releaseGetByTag(tag, { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建发布版本') .requiredOption('-t, --tag <tag>', 'Tag 名称') .option('-n, --name <name>', '发布名称') .option('-b, --body <body>', '发布说明') .option('--draft', '创建为草稿') .option('--prerelease', '标记为预发布版本') .option('--target <branch>', '目标分支或 commit SHA') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { releaseCreate } = await import('./commands/release.js'); await releaseCreate({ ...program.opts(), ...options }); }) ) .addCommand( new Command('update') .description('更新发布版本') .argument('<id>', '发布 ID') .option('-t, --tag <tag>', '新 Tag 名称') .option('-n, --name <name>', '新发布名称') .option('-b, --body <body>', '新发布说明') .option('--draft', '设置为草稿') .option('--no-draft', '取消草稿状态') .option('--prerelease', '设置为预发布版本') .option('--no-prerelease', '取消预发布状态') .option('--target <branch>', '新目标分支或 commit SHA') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { releaseUpdate } = await import('./commands/release.js'); await releaseUpdate(id, { ...program.opts(), ...options }); }) ) .addCommand( new Command('delete') .description('删除发布版本') .argument('<id>', '发布 ID') .option('-y, --yes', '跳过确认') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { releaseDelete } = await import('./commands/release.js'); await releaseDelete(id, { ...program.opts(), ...options }); }) ) .addCommand( new Command('attachments') .description('列出发布版本附件') .argument('<id>', '发布 ID') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { releaseAttachments } = await import('./commands/release.js'); await releaseAttachments(id, { ...program.opts(), ...options }); }) ) .addCommand( new Command('attachment-get') .description('获取发布附件详情') .argument('<release-id>', '发布 ID') .argument('<attachment-id>', '附件 ID') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (releaseId, attachmentId, options) => { const { releaseAttachmentGet } = await import('./commands/release.js'); await releaseAttachmentGet(releaseId, attachmentId, { ...program.opts(), ...options }); }) ) .addCommand( new Command('attachment-delete') .description('删除发布附件') .argument('<release-id>', '发布 ID') .argument('<attachment-id>', '附件 ID') .option('-y, --yes', '跳过确认') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (releaseId, attachmentId, options) => { const { releaseAttachmentDelete } = await import('./commands/release.js'); await releaseAttachmentDelete(releaseId, attachmentId, { ...program.opts(), ...options }); }) ); // 项目看板管理命令 program .command('project') .description('项目看板管理') .addCommand( new Command('list') .description('列出项目看板') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .option('-s, --state <state>', '状态过滤 (open/closed/all)', 'open') .action(async (options) => { const { projectList } = await import('./commands/project.js'); await projectList({ ...program.opts(), ...options }); }) ) .addCommand( new Command('get') .description('获取项目详情') .argument('<id>', '项目 ID') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (id, options) => { const { projectGet } = await import('./commands/project.js'); await projectGet(parseInt(id), { ...program.opts(), ...options }); }) ) .addCommand( new Command('create') .description('创建项目看板') .requiredOption('-t, --title <title>', '项目标题') .option('-d, --description <desc>', '项目描述') .option('-o, --owner <owner>', '仓库所有者') .option('-r, --repo <repo>', '仓库名称') .action(async (options) => { const { projectCreate } = await import('./commands/project.js'); await projectCreate({ ...program.opts(), ...options }); }) ); // 升级命令 (顶层命令,方便使用) program .command('upgrade') .description('检查并升级到新版本') .argument('[version]', '指定版本号(如 1.7.5)') .option('-b, --beta', '升级到 beta 版本') .option('-c, --check', '仅检查版本,不执行升级') .option('-y, --yes', '跳过确认,直接执行') .action(async (version, options) => { const { upgrade } = await import('./commands/upgrade.js'); await upgrade(version, { ...program.opts(), ...options }); }); // 配置管理命令 program .command('config') .description('配置管理') .addCommand( new Command('init') .description('初始化项目配置') .option('--gitea-url <url>', 'Gitea 服务器 URL') .option('--owner <owner>', '仓库所有者') .option('--repo <repo>', '仓库名称') .option('--force', '强制覆盖已有配置') .action(async (options) => { const { configInit } = await import('./commands/config.js'); await configInit({ ...program.opts(), ...options }); }) ) .addCommand( new Command('show') .description('显示当前配置') .action(async () => { const { configShow } = await import('./commands/config.js'); await configShow(program.opts()); }) ); // 错误处理 program.on('command:*', () => { console.error(chalk.red(`\n错误: 未知命令 '${program.args.join(' ')}'`)); console.log(chalk.yellow('\n使用 --help 查看可用命令\n')); process.exit(1); }); // 解析命令行参数 program.parse();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SupenBysz/gitea-mcp-tool'

If you have feedback or need assistance with the MCP directory API, please join our Discord server