UNPKG

miyabi-agent-sdk

Version:

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

308 lines (293 loc) 10.1 kB
/** * AnthropicClient - Claude Sonnet 4 API wrapper for Agent SDK * * Phase 8-1: Real API integration */ import Anthropic from "@anthropic-ai/sdk"; /** * AnthropicClient for Agent SDK * * Provides Claude Sonnet 4 API integration for: * - Issue analysis (IssueAgent) * - Code generation (CodeGenAgent) * - Code review (ReviewAgent) */ export class AnthropicClient { client; model = "claude-sonnet-4-20250514"; constructor(apiKey) { const key = apiKey || process.env.ANTHROPIC_API_KEY; if (!key) { throw new Error("ANTHROPIC_API_KEY is required. Set it as environment variable or pass to constructor."); } this.client = new Anthropic({ apiKey: key }); } /** * Analyze GitHub Issue with Claude Sonnet 4 * * Used by IssueAgent for intelligent issue classification */ async analyzeIssue(title, body) { const prompt = this.buildIssueAnalysisPrompt(title, body); const response = await this.client.messages.create({ model: this.model, max_tokens: 1024, messages: [ { role: "user", content: prompt, }, ], }); const content = response.content[0]; if (content.type !== "text") { throw new Error("Unexpected response type from Claude API"); } const result = this.parseJSON(content.text); console.log(`[AnthropicClient] Issue analyzed: ${result.type}, ${result.complexity}, ${result.priority}`); return { ...result, tokensUsed: { input: response.usage.input_tokens, output: response.usage.output_tokens, }, }; } /** * Generate code with Claude Sonnet 4 * * Used by CodeGenAgent for high-quality code generation */ async generateCode(requirements, context, language = "typescript") { const prompt = this.buildCodeGenPrompt(requirements, context, language); const response = await this.client.messages.create({ model: this.model, max_tokens: 32768, // Increased to 32K (max: 64K) for complex code generation messages: [ { role: "user", content: prompt, }, ], }); const content = response.content[0]; if (content.type !== "text") { throw new Error("Unexpected response type from Claude API"); } const result = this.parseJSON(content.text); console.log(`[AnthropicClient] Code generated: ${result.files.length} files, quality: ${result.qualityScore}`); return { ...result, tokensUsed: { input: response.usage.input_tokens, output: response.usage.output_tokens, }, }; } /** * Review code with Claude Sonnet 4 * * Used by ReviewAgent for quality assessment */ async reviewCode(files, standards) { const prompt = this.buildReviewPrompt(files, standards); const response = await this.client.messages.create({ model: this.model, max_tokens: 4096, messages: [ { role: "user", content: prompt, }, ], }); const content = response.content[0]; if (content.type !== "text") { throw new Error("Unexpected response type from Claude API"); } const result = this.parseJSON(content.text); console.log(`[AnthropicClient] Code reviewed: quality ${result.qualityScore}, ${result.passed ? "PASSED" : "FAILED"}`); return { ...result, tokensUsed: { input: response.usage.input_tokens, output: response.usage.output_tokens, }, }; } /** * Build prompt for issue analysis */ buildIssueAnalysisPrompt(title, body) { return `以下のGitHub Issueを解析し、適切なラベル、複雑度、優先度、種類を判定してください。 # Issue情報 Title: ${title} Body: ${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", ...], "complexity": "small|medium|large|xlarge", "estimatedEffort": "1h|4h|1d|3d|1w|2w", "priority": "P0|P1|P2|P3", "type": "bug|feature|refactor|docs|test|chore", "reasoning": "判定理由を簡潔に説明" } **重要**: 必ずJSON形式で回答してください。JSON以外のテキストは含めないでください。`; } /** * Build prompt for code generation */ buildCodeGenPrompt(requirements, context, language) { return `以下の要件に基づいて、${language}のコードを生成してください。 # 要件 ${requirements} # コンテキスト(既存コード・関連ファイル) ${context} # 出力形式(JSON) { "files": [ { "path": "src/example.ts", "content": "// コード内容(完全なコード)", "action": "create|modify|delete" } ], "tests": [ { "path": "src/example.test.ts", "content": "// テストコード(完全なテストコード)", "action": "create|modify|delete" } ], "qualityScore": 85 } # 品質スコア基準 - 90-100: 優秀(型安全、テストカバレッジ90%以上、ドキュメント完備) - 80-89: 良好(型安全、テストカバレッジ80%以上) - 70-79: 可(一部型エラー、テストカバレッジ70%以上) - 70未満: 不可 # コード生成ガイドライン - TypeScript strict mode準拠 - ESLint準拠(警告0件目標) - 完全な型アノテーション - エラーハンドリング実装 - テストコード必須(カバレッジ80%以上) - ドキュメントコメント(JSDoc) **重要**: 必ずJSON形式で回答してください。JSON以外のテキストは含めないでください。`; } /** * Build prompt for code review */ buildReviewPrompt(files, standards) { const filesContent = files .map((f) => `## ${f.path}\n\`\`\`\n${f.content}\n\`\`\``) .join("\n\n"); return `以下のコードをレビューしてください。 # コード ${filesContent} # 品質基準 - 最低品質スコア: ${standards.minQualityScore} - テスト必須: ${standards.requireTests ? "はい" : "いいえ"} - セキュリティスキャン: ${standards.securityScan ? "はい" : "いいえ"} # 出力形式(JSON) { "qualityScore": 85, "passed": true, "issues": [ { "severity": "error|warning|info", "file": "src/example.ts", "line": 42, "message": "問題の詳細説明" } ], "suggestions": [ "改善提案1", "改善提案2" ] } # レビュー観点 - 型安全性(TypeScript strict mode準拠) - エラーハンドリング - セキュリティ(インジェクション、機密情報漏洩) - テストカバレッジ - コードの可読性・保守性 - パフォーマンス **重要**: 必ずJSON形式で回答してください。JSON以外のテキストは含めないでください。`; } /** * Parse JSON from Claude response * * Handles responses with or without ```json``` code blocks */ parseJSON(text) { // Try multiple patterns to extract JSON // Pattern 1: ```json ... ``` (with or without newlines) let jsonMatch = text.match(/```json\s*([\s\S]*?)\s*```/); if (jsonMatch) { try { return JSON.parse(jsonMatch[1].trim()); } catch { // Try next pattern } } // Pattern 2: ``` ... ``` (generic code block) jsonMatch = text.match(/```\s*([\s\S]*?)\s*```/); if (jsonMatch) { try { return JSON.parse(jsonMatch[1].trim()); } catch { // Try next pattern } } // Pattern 3: Look for first { to last } jsonMatch = text.match(/(\{[\s\S]*\})/); if (jsonMatch) { try { return JSON.parse(jsonMatch[1].trim()); } catch { // Try next pattern } } // Pattern 4: Try parsing the entire text as-is try { return JSON.parse(text.trim()); } catch (error) { console.error("[AnthropicClient] Failed to parse JSON:", text.substring(0, 500)); throw new Error(`Failed to parse Claude response as JSON: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Calculate API cost (USD) * * Claude Sonnet 4 pricing (as of 2025-01): * - Input: $3 / 1M tokens * - Output: $15 / 1M tokens */ calculateCost(tokensUsed) { const inputCost = (tokensUsed.input / 1_000_000) * 3.0; const outputCost = (tokensUsed.output / 1_000_000) * 15.0; return inputCost + outputCost; } } //# sourceMappingURL=AnthropicClient.js.map