Skip to main content
Glama
infer-labels.ts5.64 kB
/** * Workflow Infer Labels Command */ import chalk from 'chalk'; import * as fs from 'fs'; import * as path from 'path'; import { parseConfig } from '../../../utils/workflow-config.js'; import { LabelInferenceEngine } from '../../../utils/label-inference.js'; import { createClient as createClientAsync, getContextFromConfig } from '../../utils/client.js'; export interface InferLabelsOptions { token?: string; server?: string; issue: string; owner?: string; repo?: string; autoApply?: boolean; } /** * 智能推断 Issue 标签 */ export async function inferLabels(options: InferLabelsOptions): Promise<void> { const configPath = path.join(process.cwd(), '.gitea', 'issue-workflow.yaml'); if (!fs.existsSync(configPath)) { console.log(chalk.red(`\n❌ 未找到工作流配置文件: ${configPath}`)); console.log(chalk.yellow('\n💡 提示: 运行 `keactl workflow init` 初始化配置')); return; } // 读取并解析配置 const configContent = fs.readFileSync(configPath, 'utf-8'); const parseResult = parseConfig(configContent); if (!parseResult.success || !parseResult.config) { console.log(chalk.red('\n❌ 配置文件解析失败')); return; } const config = parseResult.config; // 获取上下文 const context = getContextFromConfig(); const owner = options.owner || context.owner; const repo = options.repo || context.repo; const issueNumber = parseInt(options.issue); if (!owner || !repo) { console.log(chalk.red('\n❌ 请提供仓库信息: --owner 和 --repo')); return; } if (isNaN(issueNumber)) { console.log(chalk.red('\n❌ 无效的 Issue 编号')); return; } console.log(chalk.bold(`\n🤖 智能标签推断 - ${owner}/${repo}#${issueNumber}\n`)); // 创建客户端 const client = await createClientAsync({ token: options.token, server: options.server, }); if (!client) { console.log(chalk.red('\n❌ 无法创建 API 客户端,请检查配置')); return; } try { // 定义 Issue 类型 type IssueType = { number?: number; title?: string; body?: string; labels?: Array<{ id?: number; name?: string }>; }; // 获取 Issue 详情 const issue = await client.get<IssueType>(`/repos/${owner}/${repo}/issues/${issueNumber}`); console.log(chalk.gray(`标题: ${issue.title}`)); console.log(); // 创建推断引擎 const engine = new LabelInferenceEngine(config); // 推断标签 const inferResult = engine.inferAll({ id: issue.number || 0, number: issue.number || 0, title: issue.title || '', body: issue.body || '', labels: (issue.labels || []).map((l) => ({ id: l.id || 0, name: l.name || '' })), created_at: '', updated_at: '', }); // 显示推断结果 console.log(chalk.bold('📋 推断结果:\n')); const inferences = [ { name: '类型', result: inferResult.type }, { name: '优先级', result: inferResult.priority }, ...inferResult.areas.map((a, i) => ({ name: `领域 ${i + 1}`, result: a })), ]; const labelsToAdd: string[] = []; for (const inf of inferences) { if (inf.result) { const confidence = Math.round(inf.result.confidence * 100); const confidenceColor = confidence >= 80 ? chalk.green : confidence >= 60 ? chalk.yellow : chalk.gray; console.log(`${inf.name}: ${chalk.cyan(inf.result.value)}`); console.log(` 置信度: ${confidenceColor(confidence + '%')}`); console.log(` 原因: ${chalk.gray(inf.result.reason)}`); console.log(); labelsToAdd.push(inf.result.value); } } if (labelsToAdd.length === 0) { console.log(chalk.yellow('未能推断出任何标签')); return; } // 检查现有标签 const existingLabels = (issue.labels || []).map((l) => l.name || ''); const newLabels = labelsToAdd.filter((l) => !existingLabels.includes(l)); if (newLabels.length === 0) { console.log(chalk.green('✓ 推断的标签已存在,无需更新')); return; } console.log(chalk.bold('🏷️ 将添加以下标签:\n')); for (const label of newLabels) { console.log(chalk.green(` + ${label}`)); } console.log(); // 自动应用标签 if (options.autoApply) { console.log(chalk.gray('正在应用标签...')); // 获取仓库所有标签以找到 ID type LabelType = { id?: number; name?: string }; const repoLabels = await client.get<LabelType[]>(`/repos/${owner}/${repo}/labels`); const labelIds: number[] = []; for (const labelName of newLabels) { const found = repoLabels.find((l) => l.name === labelName); if (found && found.id) { labelIds.push(found.id); } else { console.log(chalk.yellow(` ⚠ 标签 "${labelName}" 不存在,请先运行 sync-labels`)); } } if (labelIds.length > 0) { // 添加到现有标签 const existingIds = (issue.labels || []) .map((l) => l.id) .filter((id): id is number => id !== undefined); await client.put(`/repos/${owner}/${repo}/issues/${issueNumber}/labels`, { labels: [...existingIds, ...labelIds], }); console.log(chalk.green('\n✅ 标签已应用!')); } } else { console.log(chalk.yellow('💡 使用 --auto-apply 自动应用推断的标签')); } } catch (error) { console.log(chalk.red(`\n❌ 推断失败: ${error instanceof Error ? error.message : String(error)}`)); } 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