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
JavaScript
/**
* 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