miyabi-agent-sdk
Version:
Miyabi Autonomous Agent SDK - 7 Agents based on Shikigaku Theory with 100% cost reduction mode
256 lines (247 loc) • 8.16 kB
JavaScript
/**
* PRAgent - PR作成Agent
*
* 識学理論適用:
* - 責任: Draft Pull Requestを作成
* - 権限: ブランチ作成、PR作成(Draft)、ラベル付与
* - 階層: Specialist Layer
*
* Phase 8-2: Real API Integration
*/
import { GitHubClient } from "../clients/GitHubClient.js";
/**
* PRAgent実装
*
* Branch作成 → Files commit → Draft PR作成 → PR本文生成
*/
export class PRAgent {
githubClient;
constructor(config) {
if (config?.githubToken) {
this.githubClient = new GitHubClient(config.githubToken);
}
}
/**
* メイン実行ロジック
*/
async create(input) {
try {
const githubClient = input.githubClient || this.githubClient;
const baseBranch = input.baseBranch || "main";
// 1. Feature branch名生成
const branchName = this.generateBranchName(input.issueNumber);
// 2. Branch作成(GitHub API)
await this.createBranch(input.owner, input.repository, branchName, baseBranch, githubClient);
// 3. Files commit
await this.commitFiles(input.owner, input.repository, branchName, input.files, this.generateCommitMessage(input), githubClient);
// 4. Draft PR作成
const pr = await this.createPullRequest({
owner: input.owner,
repository: input.repository,
base: baseBranch,
head: branchName,
title: this.generatePRTitle(input),
body: this.generatePRBody(input),
draft: true,
githubClient,
});
return {
success: true,
data: pr,
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error occurred",
};
}
}
/**
* Branch名生成
*/
generateBranchName(issueNumber) {
const timestamp = Date.now();
return `agent/issue-${issueNumber}-${timestamp}`;
}
/**
* Branch作成
*
* Phase 8-2: Real GitHub API integration
*/
async createBranch(owner, repository, branchName, baseBranch, githubClient) {
if (githubClient) {
// Real API implementation
await githubClient.createBranch(owner, repository, branchName, baseBranch);
console.log(`[PRAgent] Created branch: ${branchName} from ${baseBranch}`);
}
else {
// Mock implementation (fallback)
console.log(`[PRAgent] Would create branch: ${branchName} from ${baseBranch}`);
}
}
/**
* Files commit
*
* Phase 8-2: Real GitHub API integration
*/
async commitFiles(owner, repository, branch, files, message, githubClient) {
if (githubClient) {
// Real API implementation
await githubClient.commitFiles({
owner,
repo: repository,
branch,
files: files.map(f => ({ path: f.path, content: f.content })),
message,
});
console.log(`[PRAgent] Committed ${files.length} files to ${branch}`);
}
else {
// Mock implementation (fallback)
console.log(`[PRAgent] Would commit ${files.length} files to ${branch}:`, message);
}
}
/**
* Commit message生成(Conventional Commits準拠)
*/
generateCommitMessage(input) {
// Conventional Commits形式
// type(scope): subject
//
// body
//
// footer
const type = this.inferCommitType(input.files);
const scope = this.inferScope(input.files);
const subject = `autonomous implementation for issue #${input.issueNumber}`;
const body = `
Quality Score: ${input.qualityReport.qualityScore}/100
Coverage: ${input.qualityReport.coverage}%
Files changed: ${input.files.length}
`.trim();
const footer = `
Closes #${input.issueNumber}
🤖 Generated by Miyabi Autonomous Agent
`.trim();
return `${type}${scope ? `(${scope})` : ""}: ${subject}\n\n${body}\n\n${footer}`;
}
/**
* Commit type推論(Conventional Commits)
*/
inferCommitType(files) {
const actions = files.map((f) => f.action);
if (actions.includes("delete"))
return "refactor";
if (actions.every((a) => a === "create"))
return "feat";
if (actions.some((a) => a === "modify"))
return "fix";
return "feat";
}
/**
* Scope推論(ファイルパスから)
*/
inferScope(files) {
// 最初のファイルのディレクトリをscopeとする
if (files.length === 0)
return "";
const firstFile = files[0].path;
const parts = firstFile.split("/");
// src/agents/Foo.ts → "agents"
if (parts.length > 1 && parts[0] === "src") {
return parts[1];
}
return parts[0];
}
/**
* PR title生成
*/
generatePRTitle(input) {
const type = this.inferCommitType(input.files);
return `${type}: autonomous implementation for issue #${input.issueNumber}`;
}
/**
* PR本文生成
*/
generatePRBody(input) {
const passedEmoji = input.qualityReport.passed ? "✅" : "❌";
return `
## 🤖 Autonomous Agent Implementation
**Issue**: #${input.issueNumber}
**Quality Score**: ${input.qualityReport.qualityScore}/100
**Coverage**: ${input.qualityReport.coverage}%
### Quality Report
${passedEmoji} Quality check ${input.qualityReport.passed ? "passed" : "failed"}
${input.qualityReport.issues.length > 0
? `
### Issues Found
${input.qualityReport.issues
.map((issue) => `- [${issue.severity}] ${issue.file}${issue.line ? `:${issue.line}` : ""} - ${issue.message}`)
.join("\n")}
`
: ""}
${input.qualityReport.suggestions.length > 0
? `
### Suggestions
${input.qualityReport.suggestions.map((s) => `- ${s}`).join("\n")}
`
: ""}
### Files Changed
${input.files.map((f) => `- [${f.action}] \`${f.path}\``).join("\n")}
### Checklist
- [x] Code generated
- [x] Tests generated
- [x] Quality check (≥80): ${input.qualityReport.qualityScore}/100
- [ ] Manual review required
- [ ] Ready to merge
Closes #${input.issueNumber}
---
🤖 Generated by Miyabi Autonomous Agent
`.trim();
}
/**
* Pull Request作成
*
* Phase 8-2: Real GitHub API integration
*/
async createPullRequest(params) {
if (params.githubClient) {
// Real API implementation
const prInfo = await params.githubClient.createPullRequest({
owner: params.owner,
repo: params.repository,
title: params.title,
body: params.body,
head: params.head,
base: params.base,
draft: params.draft,
});
console.log(`[PRAgent] Created PR #${prInfo.number}: ${prInfo.html_url}`);
return {
number: prInfo.number,
url: prInfo.html_url,
branch: params.head,
status: params.draft ? "draft" : "open",
};
}
else {
// Mock implementation (fallback)
const mockPrNumber = Math.floor(Math.random() * 1000) + 1;
const mockPrUrl = `https://github.com/${params.owner}/${params.repository}/pull/${mockPrNumber}`;
console.log(`[PRAgent] Would create PR:`, {
title: params.title,
base: params.base,
head: params.head,
draft: params.draft,
});
return {
number: mockPrNumber,
url: mockPrUrl,
branch: params.head,
status: "draft",
};
}
}
}
//# sourceMappingURL=PRAgent.js.map