Skip to main content
Glama
init.ts9.11 kB
/** * Workflow Init Command */ import chalk from 'chalk'; import * as fs from 'fs'; import * as path from 'path'; import prompts from 'prompts'; import { ProjectType, generateDefaultConfig, serializeConfig, getLabelPrefixes, } from '../../../utils/workflow-config.js'; export interface WorkflowInitOptions { type?: ProjectType; language?: string; owner?: string; repo?: string; interactive?: boolean; force?: boolean; } /** * 检测项目类型 */ function detectProjectType(): ProjectType | null { const cwd = process.cwd(); // 检查前端项目 const packageJsonPath = path.join(cwd, 'package.json'); if (fs.existsSync(packageJsonPath)) { try { const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); const deps = { ...pkg.dependencies, ...pkg.devDependencies }; // 检查是否是前端框架 if (deps.react || deps.vue || deps.angular || deps['@angular/core'] || deps.svelte) { return 'frontend'; } // 检查是否是全栈框架 if (deps.next || deps.nuxt || deps['@nestjs/core'] || deps.express) { return 'fullstack'; } } catch { // 忽略解析错误 } return 'library'; // 默认 Node.js 项目作为 library } // Go 项目 if (fs.existsSync(path.join(cwd, 'go.mod'))) { // 检查是否是 CLI/后端 if (fs.existsSync(path.join(cwd, 'cmd')) || fs.existsSync(path.join(cwd, 'main.go'))) { return 'backend'; } return 'library'; } // Python 项目 if ( fs.existsSync(path.join(cwd, 'requirements.txt')) || fs.existsSync(path.join(cwd, 'pyproject.toml')) ) { return 'backend'; } // Rust 项目 if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) { return 'library'; } return null; } /** * 检测主要编程语言 */ function detectLanguage(): string | null { const cwd = process.cwd(); if (fs.existsSync(path.join(cwd, 'package.json'))) { // 检查是否是 TypeScript 项目 if (fs.existsSync(path.join(cwd, 'tsconfig.json'))) { return 'typescript'; } return 'javascript'; } if (fs.existsSync(path.join(cwd, 'go.mod'))) { return 'go'; } if (fs.existsSync(path.join(cwd, 'requirements.txt')) || fs.existsSync(path.join(cwd, 'pyproject.toml'))) { return 'python'; } if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) { return 'rust'; } return null; } /** * 获取仓库信息 */ function getRepoInfo(): { owner: string; repo: string } | null { // 尝试从 .gitea-mcp.json 读取 const configPath = path.join(process.cwd(), '.gitea-mcp.json'); if (fs.existsSync(configPath)) { try { const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); if (config.owner && config.repo) { return { owner: config.owner, repo: config.repo }; } } catch { // 忽略 } } // 尝试从 git remote 获取 try { const { execSync } = require('child_process'); const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8' }).trim(); let owner = ''; let repo = ''; if (remoteUrl.startsWith('https://')) { const urlPath = remoteUrl.replace(/^https:\/\/[^/]+\//, '').replace(/\.git$/, ''); const parts = urlPath.split('/'); if (parts.length >= 2) { owner = parts[0]; repo = parts[1]; } } else if (remoteUrl.startsWith('git@')) { const urlPath = remoteUrl.replace(/^git@[^:]+:/, '').replace(/\.git$/, ''); const parts = urlPath.split('/'); if (parts.length >= 2) { owner = parts[0]; repo = parts[1]; } } if (owner && repo) { return { owner, repo }; } } catch { // 忽略 } return null; } /** * 初始化工作流配置 */ export async function initWorkflow(options: WorkflowInitOptions): Promise<void> { console.log(chalk.bold('\n🔧 Issue 工作流配置初始化\n')); // 检测项目信息 const detectedType = detectProjectType(); const detectedLanguage = detectLanguage(); const repoInfo = getRepoInfo(); if (detectedType) { console.log(chalk.gray(`📦 检测到项目类型: ${detectedType}`)); } if (detectedLanguage) { console.log(chalk.gray(`💻 检测到编程语言: ${detectedLanguage}`)); } if (repoInfo) { console.log(chalk.gray(`📁 仓库: ${repoInfo.owner}/${repoInfo.repo}`)); } // 检查现有配置 const configPath = path.join(process.cwd(), '.gitea', 'issue-workflow.yaml'); if (fs.existsSync(configPath)) { console.log(chalk.yellow('\n⚠️ 检测到现有工作流配置')); if (!options.force) { const { confirm } = await prompts({ type: 'confirm', name: 'confirm', message: '是否覆盖现有配置?', initial: false, }); if (!confirm) { console.log(chalk.gray('\n已取消')); return; } } } // 收集配置参数 let projectType: ProjectType = options.type || detectedType || 'backend'; let language: string = options.language || detectedLanguage || 'typescript'; let owner: string = options.owner || repoInfo?.owner || ''; let repo: string = options.repo || repoInfo?.repo || ''; if (options.interactive !== false) { const response = await prompts([ { type: 'select', name: 'projectType', message: '选择项目类型', choices: [ { title: 'Backend - 后端项目', value: 'backend' }, { title: 'Frontend - 前端项目', value: 'frontend' }, { title: 'Fullstack - 全栈项目', value: 'fullstack' }, { title: 'Library - 库项目', value: 'library' }, ], initial: ['backend', 'frontend', 'fullstack', 'library'].indexOf(projectType), }, { type: 'select', name: 'language', message: '选择主要编程语言', choices: [ { title: 'TypeScript', value: 'typescript' }, { title: 'JavaScript', value: 'javascript' }, { title: 'Go', value: 'go' }, { title: 'Python', value: 'python' }, { title: 'Rust', value: 'rust' }, { title: 'Java', value: 'java' }, { title: 'Other', value: 'other' }, ], initial: ['typescript', 'javascript', 'go', 'python', 'rust', 'java', 'other'].indexOf(language) || 0, }, { type: 'text', name: 'owner', message: '仓库所有者', initial: owner, }, { type: 'text', name: 'repo', message: '仓库名称', initial: repo, }, ]); if (!response.projectType) { console.log(chalk.gray('\n已取消')); return; } projectType = response.projectType; language = response.language; owner = response.owner; repo = response.repo; } // 生成配置 console.log(chalk.bold('\n📝 生成工作流配置...\n')); const config = generateDefaultConfig(repo, projectType, `${owner}/${repo}`, language); const configContent = serializeConfig(config); // 确保目录存在 const configDir = path.dirname(configPath); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } // 写入配置文件 fs.writeFileSync(configPath, configContent); console.log(chalk.green(` ✓ ${configPath}`)); console.log(chalk.bold('\n✅ 工作流配置完成!\n')); // 显示配置摘要 console.log(chalk.bold('📋 配置摘要:\n')); const prefixes = getLabelPrefixes(config); console.log(chalk.cyan(' 标签分类:')); console.log(chalk.gray(` 前缀: status='${prefixes.status}', priority='${prefixes.priority}', type='${prefixes.type}', area='${prefixes.area}', workflow='${prefixes.workflow}'`)); console.log(chalk.gray(` - status (${prefixes.status || '无前缀'}) : ${Object.keys(config.labels.status).length} 个状态标签`)); console.log(chalk.gray(` - priority (${prefixes.priority || '无前缀'}) : ${Object.keys(config.labels.priority).length} 个优先级标签`)); console.log(chalk.gray(` - type (${prefixes.type || '无前缀'}) : ${Object.keys(config.labels.type).length} 个类型标签`)); console.log(chalk.gray(` - area (${prefixes.area || '无前缀'}) : ${Object.keys(config.labels.area || {}).length} 个领域标签`)); console.log(chalk.gray(` - workflow (${prefixes.workflow || '无前缀'}) : ${Object.keys(config.labels.workflow || {}).length} 个工作流标签`)); console.log(chalk.cyan('\n 看板列:')); for (const column of config.board.columns) { console.log(chalk.gray(` - ${column.name} (${column.maps_to})`)); } console.log(chalk.bold('\n📋 后续步骤:\n')); console.log(chalk.cyan(' 1. 同步标签到仓库')); console.log(chalk.white(' keactl workflow sync-labels')); console.log(chalk.cyan('\n 2. 创建项目看板')); console.log(chalk.white(' keactl workflow sync-board')); console.log(chalk.cyan('\n 3. 检查现有 Issue')); console.log(chalk.white(' keactl workflow check-issues')); console.log(); }

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