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