UNPKG

miyabi-agent-sdk

Version:

Miyabi Autonomous Agent SDK - 7 Agents based on Shikigaku Theory with 100% cost reduction mode

302 lines (298 loc) 10.8 kB
/** * IssueAgent - Issue分析Agent * * 識学理論適用: * - 責任: GitHubのIssueを解析し、適切なラベルと複雑度を判定 * - 権限: ラベル自動付与、複雑度推定、優先度判定 * - 階層: Specialist Layer * * Phase 8-1: Real API Integration */ import { AnthropicClient } from "../clients/AnthropicClient.js"; import { ClaudeCodeClient } from "../clients/ClaudeCodeClient.js"; import { GitHubClient } from "../clients/GitHubClient.js"; /** * IssueAgent実装 * * GitHub Issue取得 → Claude分析 → ラベル付与 → 複雑度判定 */ export class IssueAgent { anthropicClient; claudeCodeClient; githubClient; constructor(config) { if (config) { if (config.useClaudeCode) { this.claudeCodeClient = new ClaudeCodeClient(); } else if (config.anthropicApiKey) { this.anthropicClient = new AnthropicClient(config.anthropicApiKey); } if (config.githubToken) { this.githubClient = new GitHubClient(config.githubToken); } } } /** * メイン実行ロジック */ async analyze(input) { try { // Select client to use (real API or mock) const githubClient = input.githubClient || this.githubClient; const anthropicClient = input.anthropicClient || this.anthropicClient; const claudeCodeClient = input.claudeCodeClient || this.claudeCodeClient; const useRealAPI = input.useRealAPI !== false && !!(githubClient || anthropicClient || claudeCodeClient); // 1. Issue取得(GitHub API - Phase 8で実API統合) const issue = await this.fetchIssue(input, githubClient); // 2. Claude分析(Anthropic API or Claude Code - Phase 9統合) const analysis = await this.analyzeWithClaude({ ...issue, number: issue.number }, anthropicClient, claudeCodeClient, useRealAPI); // 3. ラベル付与(GitHub API - Phase 8で実API統合) if (useRealAPI) { await this.applyLabels(input, analysis.labels, githubClient); } return { success: true, data: { number: issue.number, title: issue.title, body: issue.body, labels: analysis.labels, complexity: analysis.complexity, priority: analysis.priority, type: analysis.type, tokensUsed: analysis.tokensUsed, cost: analysis.cost, }, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }; } } /** * GitHub IssueをAPI経由で取得 * * Phase 8: Real GitHub API integration */ async fetchIssue(input, githubClient) { if (githubClient) { // Real API implementation const issue = await githubClient.getIssue(input.owner, input.repository, input.issueNumber); return { number: issue.number, title: issue.title, body: issue.body, }; } else { // Mock implementation (fallback) return { number: input.issueNumber, title: `Mock Issue #${input.issueNumber}`, body: "Mock issue body for testing", }; } } /** * Claude Sonnet 4で自然言語解析 * * Phase 8: Real Claude API integration * Phase 9: Claude Code CLI integration */ async analyzeWithClaude(issue, anthropicClient, claudeCodeClient, useRealAPI) { if (useRealAPI && claudeCodeClient) { // Phase 9: Claude Code implementation const result = await claudeCodeClient.analyzeIssue({ title: issue.title, body: issue.body || "", number: issue.number || 0, }); return { labels: result.labels, complexity: result.complexity, priority: result.priority, type: result.type, tokensUsed: { input: 0, output: 0 }, // Claude Code doesn't track tokens cost: 0, // Free! }; } else if (useRealAPI && anthropicClient) { // Phase 8: Real Anthropic API implementation const result = await anthropicClient.analyzeIssue(issue.title, issue.body); // Calculate cost const cost = anthropicClient.calculateCost(result.tokensUsed); return { labels: result.labels, complexity: result.complexity, priority: result.priority, type: result.type, tokensUsed: result.tokensUsed, cost, }; } else { // Mock implementation (fallback) const analysis = this.keywordBasedAnalysis(issue); return { ...analysis, tokensUsed: undefined, cost: undefined, }; } } /** * Claude分析用プロンプト生成 */ buildAnalysisPrompt(issue) { return ` 以下のGitHub Issueを解析し、適切なラベル、複雑度、優先度、種類を判定してください。 ## Issue情報 **Title**: ${issue.title} **Body**: ${issue.body || "(空白)"} ## ラベル体系(116ラベル、15カテゴリ) - 🏷️ type: bug, feature, refactor, docs, test, chore - 🎯 priority: P0-Critical, P1-High, P2-Medium, P3-Low - 📊 complexity: small (<4h), medium (4-8h), large (1-3d), xlarge (>3d) - 🏗️ state: backlog, implementing, review, done - 🤖 agent: coordinator, issue, codegen, review, pr, test, deploy - 🔧 tech: rust, typescript, python, go - 🌐 area: cli, tui, mcp, sdk, core - 📦 scope: api, ui, db, infra, security - 🎨 ux: a11y, i18n, perf, responsive - 🔬 testing: unit, integration, e2e - 📚 docs: readme, guide, api-docs - 🚀 release: major, minor, patch, alpha, beta - 🔒 security: vuln, cve, audit - 💰 cost: low, medium, high - 📈 impact: breaking, enhancement, fix ## 出力形式(JSON) { "labels": ["🏷️ type:bug", "🎯 priority:P1-High", ...], "complexity": "medium", "priority": "P1", "type": "bug" } **重要**: 116ラベルシステムに厳密に従ってください。 `.trim(); } /** * キーワードベースの簡易分析 * * Claude統合前のフォールバック実装 */ keywordBasedAnalysis(issue) { const text = `${issue.title} ${issue.body || ""}`.toLowerCase(); // Type判定 let type = "feature"; if (text.includes("bug") || text.includes("error") || text.includes("fix")) type = "bug"; else if (text.includes("refactor") || text.includes("cleanup")) type = "refactor"; else if (text.includes("docs") || text.includes("documentation")) type = "docs"; else if (text.includes("test")) type = "test"; else if (text.includes("chore") || text.includes("deps") || text.includes("dependency")) type = "chore"; // Priority判定 let priority = "P2"; if (text.includes("critical") || text.includes("urgent") || text.includes("security")) priority = "P0"; else if (text.includes("high") || text.includes("important")) priority = "P1"; else if (text.includes("low") || text.includes("nice-to-have")) priority = "P3"; // Complexity判定 let complexity = "medium"; if (text.includes("simple") || text.includes("quick") || text.includes("typo")) complexity = "small"; else if (text.includes("large") || text.includes("major") || text.includes("refactor")) complexity = "large"; else if (text.includes("rewrite") || text.includes("migration") || text.includes("breaking")) complexity = "xlarge"; // ラベル生成 const labels = [ `🏷️ type:${type}`, `🎯 priority:${priority}-${this.getPriorityLabel(priority)}`, `📊 complexity:${complexity}`, ]; // 技術スタック推定 if (text.includes("rust")) labels.push("🔧 tech:rust"); if (text.includes("typescript") || text.includes("ts")) labels.push("🔧 tech:typescript"); if (text.includes("python") || text.includes("py")) labels.push("🔧 tech:python"); // エリア推定 if (text.includes("cli")) labels.push("🌐 area:cli"); if (text.includes("tui")) labels.push("🌐 area:tui"); if (text.includes("mcp")) labels.push("🌐 area:mcp"); if (text.includes("sdk")) labels.push("🌐 area:sdk"); return { labels, complexity, priority, type }; } /** * Priority label取得 */ getPriorityLabel(priority) { switch (priority) { case "P0": return "Critical"; case "P1": return "High"; case "P2": return "Medium"; case "P3": return "Low"; } } /** * ラベル付与(GitHub API) * * Phase 8: Real GitHub API integration */ async applyLabels(input, labels, githubClient) { if (githubClient) { // Real API implementation await githubClient.addLabels(input.owner, input.repository, input.issueNumber, labels); console.log(`[IssueAgent] Applied labels to #${input.issueNumber}:`, labels); } else { // Mock implementation (fallback) console.log(`[IssueAgent] Would apply labels to #${input.issueNumber}:`, labels); } } /** * 工数推定(complexityから推定時間に変換) */ estimateEffort(complexity) { switch (complexity) { case "small": return "1h"; case "medium": return "4h"; case "large": return "1d"; case "xlarge": return "1w"; } } } //# sourceMappingURL=IssueAgent.js.map