diff --git a/.SourceSageignore b/.SourceSageignore index e29d512..8d8735b 100644 --- a/.SourceSageignore +++ b/.SourceSageignore @@ -1,38 +1,56 @@ -.git -__pycache__ -LICENSE -output.md -assets -Style-Bert-VITS2 -output -streamlit -SourceSage.md -data +# バージョン管理システム関連 +.git/ .gitignore -.SourceSageignore -*.png -Changelog -SourceSageAssets -SourceSageAssetsDemo -__pycache__ -.pyc + +# キャッシュファイル +__pycache__/ +.pytest_cache/ **/__pycache__/** -modules\__pycache__ -.svg -sourcesage.egg-info -.pytest_cache -dist -build -.env -example - -.gaiah.md -.Gaiah.md -tmp.md -tmp2.md -.SourceSageAssets -tests -template -aira.egg-info -aira.Gaiah.md -README_template.md \ No newline at end of file +*.pyc + +# ビルド・配布関連 +build/ +dist/ +*.egg-info/ + +# 一時ファイル・出力 +output/ +output.md +test_output/ +.SourceSageAssets/ +.SourceSageAssetsDemo/ + +# アセット +*.png +*.svg +*.jpg +*.jepg +assets/ + +# その他 +LICENSE +example/ +package-lock.json +.DS_Store + +# 特定のディレクトリを除外 +tests/temp/ +docs/drafts/ + +# パターンの例外(除外対象から除外) +!docs/important.md +!.github/workflows/ +repository_summary.md + +# Terraform関連 +.terraform +*.terraform.lock.hcl +*.backup +*.tfstate + +# Python仮想環境 +venv +.venv + +.harmon_ai +.github diff --git a/.aira/config.dev.yml b/.aira/config.dev.yml deleted file mode 100644 index 4385932..0000000 --- a/.aira/config.dev.yml +++ /dev/null @@ -1,9 +0,0 @@ -aira: - gaiah: - run: true - develop: - config_path: .gaiah/config.dev.yml - harmon_ai: - run: true - config_path: .harmon_ai/config.yml - instructions_prompt: .aira/instructions.md \ No newline at end of file diff --git a/.aira/config.yml b/.aira/config.yml deleted file mode 100644 index a7a4e48..0000000 --- a/.aira/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -aira: - gaiah: - run: true - init: - config_path: .gaiah/config.init.yml - develop: - config_path: .gaiah/config.yml - harmon_ai: - run: true - config_path: .harmon_ai/config.yml - instructions_prompt: .aira/instructions.md \ No newline at end of file diff --git a/.aira/instructions.md b/.aira/instructions.md deleted file mode 100644 index 57b6c36..0000000 --- a/.aira/instructions.md +++ /dev/null @@ -1,2 +0,0 @@ -サンプルリポジトリを作成したいです -シンプルな計算や文字の表示を行うリポジトリを提案して \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1198d0c --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# AIRAの基本設定 +GAIAH_RUN=true +COMMIT_MSG_PATH=.Gaiah.md + +# LLMの設定 +LLM_MODEL=gemini/gemini-1.5-pro-latest +GEMINI_API_KEY=your-api-key-here + +# GitHubの設定(必要な場合のみ) +GITHUB_ACCESS_TOKEN=your-github-token-here diff --git a/.gaiah/config.dev.yml b/.gaiah/config.dev.yml deleted file mode 100644 index f59f4b0..0000000 --- a/.gaiah/config.dev.yml +++ /dev/null @@ -1,12 +0,0 @@ -gaiah: - repo: - create_repo: false - - local: - init_repo: false - repo_dir: "C:/Prj/AIRA" - - commit: - process_commits: true - commit_msg_path: ".Gaiah.md" - branch_name: null \ No newline at end of file diff --git a/.gaiah/config.init.yml b/.gaiah/config.init.yml deleted file mode 100644 index da8ddc2..0000000 --- a/.gaiah/config.init.yml +++ /dev/null @@ -1,16 +0,0 @@ -gaiah: - repo: - create_repo: true - repo_name: "AIRA-Sample00" - description: "" - private: false - - local: - init_repo: true - repo_dir: "C:/Prj/AIRA-Sample/AIRA-Sample00" - no_initial_commit: false - - commit: - process_commits: true - commit_msg_path: "C:/Prj/AIRA-Sample/.Gaiah.md" - branch_name: null \ No newline at end of file diff --git a/.gaiah/config.yml b/.gaiah/config.yml deleted file mode 100644 index 0cc4c65..0000000 --- a/.gaiah/config.yml +++ /dev/null @@ -1,16 +0,0 @@ -gaiah: - repo: - create_repo: false - repo_name: "AIRA-Sample00" - description: "" - private: false - - local: - init_repo: false - repo_dir: "C:/Prj/AIRA-Sample/AIRA-Sample00" - no_initial_commit: false - - commit: - process_commits: true - commit_msg_path: ".aira/aira.Gaiah.md" - branch_name: null \ No newline at end of file diff --git a/.github/config.py b/.github/config.py new file mode 100644 index 0000000..dcccd54 --- /dev/null +++ b/.github/config.py @@ -0,0 +1,18 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic import Field +from functools import lru_cache + +class Settings(BaseSettings): + LITELLM_MODEL: str = "gemini/gemini-1.5-pro-latest" + GITHUB_TOKEN: str = Field(..., env="GITHUB_TOKEN") + GITHUB_REPOSITORY: str = Field(..., env="GITHUB_REPOSITORY") + ISSUE_NUMBER: int = Field(0, env="ISSUE_NUMBER") + REPOSITORY_SUMMARY_PATH: str = Field("", env="REPOSITORY_SUMMARY_PATH") + model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8', extra='ignore') + YOUR_PERSONAL_ACCESS_TOKEN: str = Field("", env="YOUR_PERSONAL_ACCESS_TOKEN") + YOUR_PERSONAL_ACCESS_TOKEN_IRIS: str = Field("", env="YOUR_PERSONAL_ACCESS_TOKEN_IRIS") + # ALLOWED_USERS = ["github-actions[bot]", "Sunwood-ai-labs", "user2"] + +@lru_cache() +def get_settings(): + return Settings() diff --git a/.github/labels.csv b/.github/labels.csv new file mode 100644 index 0000000..c59c419 --- /dev/null +++ b/.github/labels.csv @@ -0,0 +1,11 @@ +label,description +bug,何かが正常に動作していません +documentation,ドキュメントの改善または追加 +duplicate,このイシューまたはプルリクエストは既に存在します +enhancement,新機能または要望 +feature,新機能または要望 +good first issue,初心者に適しています +help wanted,追加の注意が必要です +invalid,これは適切ではないようです +question,さらなる情報が必要です +wontfix,これは対応されません \ No newline at end of file diff --git a/.github/scripts/apply_suggestion.py b/.github/scripts/apply_suggestion.py new file mode 100644 index 0000000..3c9f133 --- /dev/null +++ b/.github/scripts/apply_suggestion.py @@ -0,0 +1,127 @@ +import sys +import os + +# Add the parent directory of 'scripts' to the Python path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from loguru import logger +from config import get_settings +from services.llm_service import LLMService +from services.github_service import GitHubService +from services.git_service import GitService +from utils.diff_utils import DiffUtils, create_comment +from utils.patch_utils import apply_patch, get_patch_file_path + +class SuggestionApplier: + def __init__(self): + self.settings = get_settings() + self.llm_service = LLMService() + self.github_service = GitHubService() + self.git_service = GitService() + # self.allowed_users = self.settings.ALLOWED_USERS # 設定から許可されたユーザーのリストを取得 + + def run(self): + logger.info("変更提案の適用処理を開始します。") + + issue = self.github_service.get_issue() + all_comments = self.github_service.get_comments(issue) + + # コメントユーザーの一覧を表示 + self._display_comment_users(all_comments) + + # 許可されたユーザーのコメントのみをフィルタリング + # comments = self._filter_allowed_comments(all_comments) + comments = all_comments + + if not self._validate_comments(comments): + return + + previous_comment = self._find_previous_bot_comment(comments) + if not previous_comment: + logger.error("Error: github-actions[bot] のコメントが見つかりませんでした。") + raise ValueError("github-actions[bot] のコメントが見つかりません") + + print(previous_comment.body) + diffs = DiffUtils.extract_diff(previous_comment.body) + if diffs is None: + logger.error("有効なdiffを抽出できませんでした。処理を終了します。") + return + + try: + modified_files = self._process_diffs(diffs) + self._create_pull_request(issue, modified_files) + except Exception as e: + logger.error(f"エラーが発生しました: {str(e)}") + raise + + # def _filter_allowed_comments(self, comments): + # """許可されたユーザーのコメントのみをフィルタリングする""" + # return [comment for comment in comments if comment.user.login in self.allowed_users] + + def _display_comment_users(self, comments): + """コメントをしているユーザーの一覧を表示する""" + user_list = list(set(comment.user.login for comment in comments)) + logger.info(f"コメントをしているユーザーの一覧:") + for user in user_list: + logger.info(f"- {user}") + + def _validate_comments(self, comments): + comment_count = len(comments) + logger.info(f"イシュー #{self.settings.ISSUE_NUMBER} のコメントを {comment_count}件取得しました。") + + if comment_count == 0 or comments[-1].body.strip().lower() != "ok": + logger.warning("最後のコメントが 'ok' ではありません。処理を終了します。") + # return False + return True + + logger.info("'ok' コメントを確認しました。") + return True + + def _find_previous_bot_comment(self, comments, bot_name="github-actions[bot]"): + logger.info("直前の github-actions[bot] のコメントを探しています。") + return next((comment for comment in reversed(comments[:-1]) if comment.user.login == "Sunwood-ai-labs"), None) + + def _process_diffs(self, diffs): + modified_files = [] + for file_path, diff_content in diffs.items(): + logger.info(f"{file_path} のパッチ適用を試みます。") + patch_file_path = get_patch_file_path(file_path) + with open(patch_file_path, 'w', encoding='utf-8') as f: + f.write(diff_content) + print(diff_content) + if apply_patch(patch_file_path, file_path): + modified_files.append(file_path) + else: + logger.info(f"{file_path} のパッチ適用に失敗しました。LLMを使用して変更を生成します。") + self._apply_llm_changes(file_path, diff_content) + modified_files.append(file_path) + return modified_files + + def _apply_llm_changes(self, file_path, diff_content): + with open(file_path, "r", encoding="utf-8") as f: + original_content = f.read() + modified_content = self.llm_service.apply_diff(original_content, diff_content) + with open(file_path, "w", encoding="utf-8") as f: + f.write(modified_content) + + def _create_pull_request(self, issue, modified_files): + branch_name = f"suggestion-issue-{self.settings.ISSUE_NUMBER}" + self.git_service.setup_credentials() + self.git_service.create_branch(branch_name) + self.git_service.commit_changes(modified_files, f"🤖 #{self.settings.ISSUE_NUMBER} の提案を適用") + self.git_service.push_changes(branch_name) + + comment = create_comment(modified_files, {file: open(file, "r").read() for file in modified_files}) + self.github_service.add_comment(issue, comment) + logger.info(f"イシュー #{issue.number} にコメントを追加しました。") + + pr_title = f"イシュー #{self.settings.ISSUE_NUMBER} の提案" + pr_body = f"このPRはイシュー #{self.settings.ISSUE_NUMBER} の提案を適用しています。" + self.github_service.create_pull_request(pr_title, pr_body, branch_name) + +def main(): + applier = SuggestionApplier() + applier.run() + +if __name__ == "__main__": + main() diff --git a/.github/scripts/deep_comment.py b/.github/scripts/deep_comment.py new file mode 100644 index 0000000..7724f11 --- /dev/null +++ b/.github/scripts/deep_comment.py @@ -0,0 +1,51 @@ +from loguru import logger +import sys +import os + +# Add the parent directory of 'scripts' to the Python path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from config import get_settings +from services.llm_service import LLMService +from services.github_service import GitHubService + +def main(): + logger.info("イシューの深堀処理を開始します。") + + settings = get_settings() + llm_service = LLMService() + github_service = GitHubService() + + logger.info("GitHubからイシューを取得中...") + issue = github_service.get_issue() + logger.info(f"イシュー「{issue.title}」を取得しました。") + + logger.info("リポジトリの概要を読み込み中...") + with open(settings.REPOSITORY_SUMMARY_PATH, "r", encoding="utf-8") as f: + repository_summary = f.read() + logger.info("リポジトリの概要を読み込みました。") + + prompt = f""" +以下のGitHubイシューに対して、リポジトリの情報を踏まえた詳細なコメントを生成してください: + +イシュータイトル: {issue.title} +イシュー本文: {issue.body} + +リポジトリの概要: +{repository_summary} + +詳細なコメント: + """ + + logger.info("LLMを使用して深いコメントを生成中...") + deep_comment = llm_service.get_response(prompt) + logger.info("深いコメントの生成が完了しました。") + + logger.info("生成したコメントをGitHubイシューに追加中...") + github_service.add_comment(issue, deep_comment) + logger.info("コメントをイシューに追加しました。") + + logger.info("イシューの深堀処理が正常に完了しました。") + +if __name__ == "__main__": + main() diff --git a/.github/scripts/label_adder.py b/.github/scripts/label_adder.py new file mode 100644 index 0000000..a0f3542 --- /dev/null +++ b/.github/scripts/label_adder.py @@ -0,0 +1,68 @@ +import sys +import os +import csv + +# Add the parent directory of 'scripts' to the Python path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from loguru import logger +from config import get_settings +from services.llm_service import LLMService +from services.github_service import GitHubService + +def load_labels_from_csv(csv_path): + labels = [] + with open(csv_path, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + labels.append(row['label']) + return labels + +def main(): + logger.info("イシューの処理を開始します。") + + settings = get_settings() + llm_service = LLMService() + github_service = GitHubService() + + logger.info("GitHubからイシューを取得しています...") + issue = github_service.get_issue() + logger.info(f"イシュー #{issue.number} を取得しました: {issue.title}") + + logger.info("labels.csvからラベルのリストを読み込んでいます...") + csv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'labels.csv') + existing_labels = load_labels_from_csv(csv_path) + logger.info(f"読み込まれたラベル: {', '.join(existing_labels)}") + + logger.info("LLMを使用してイシューを分析し、ラベルを提案しています...") + suggested_labels = llm_service.analyze_issue(issue.title, issue.body, existing_labels) + + label_list = [label.strip().replace("*", "") for label in suggested_labels.split(',')] + logger.info(f"提案されたラベル: {', '.join(label_list)}") + + # 提案されたラベルを検証し、未登録のラベルをスキップ + validated_labels = [] + skipped_labels = [] + for label in label_list: + if label in existing_labels: + validated_labels.append(label) + else: + skipped_labels.append(label) + + logger.info(f"検証済みのラベル: {', '.join(validated_labels)}") + if skipped_labels: + logger.warning(f"未登録のためスキップされたラベル: {', '.join(skipped_labels)}") + + logger.info("検証済みのラベルをイシューに適用しています...") + github_service.add_labels(issue, validated_labels) + + logger.info("イシューにコメントを追加しています...") + comment = f"@iris-s-coon が以下のラベルを提案し、適用しました:\n\n" + "\n".join([f"- {label}" for label in validated_labels]) + if skipped_labels: + comment += f"\n\n以下のラベルは未登録のためスキップされました:\n\n" + "\n".join([f"- {label}" for label in skipped_labels]) + github_service.add_comment(issue, comment) + + logger.info("イシューの処理が完了しました。") + +if __name__ == "__main__": + main() diff --git a/.github/scripts/suggest_changes.py b/.github/scripts/suggest_changes.py new file mode 100644 index 0000000..00f97f7 --- /dev/null +++ b/.github/scripts/suggest_changes.py @@ -0,0 +1,64 @@ +import sys +import os + +# Add the parent directory of 'scripts' to the Python path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from loguru import logger +from config import get_settings +from services.llm_service import LLMService +from services.github_service import GitHubService +import os + +def save_prompt(prompt, issue_number): + prompt_dir = "generated_prompts" + os.makedirs(prompt_dir, exist_ok=True) + file_path = os.path.join(prompt_dir, f"prompt_issue_{issue_number}.md") + with open(file_path, "w", encoding="utf-8") as f: + f.write(prompt) + logger.info(f"プロンプトを {file_path} に保存しました。") + +def main(): + logger.info("変更提案の生成を開始します。") + + settings = get_settings() + llm_service = LLMService() + github_service = GitHubService() + + issue = github_service.get_issue() + logger.info(f"イシュー '{issue.title}' を取得しました。") + + # リポジトリの概要を取得する + with open(settings.REPOSITORY_SUMMARY_PATH, "r", encoding="utf-8") as f: + repository_summary = f.read() + logger.info("リポジトリの概要を読み込みました。") + + prompt = f""" +以下のGitHubイシューに対して、リポジトリの情報を踏まえた具体的なコード変更提案を生成してください: + +イシュータイトル: {issue.title} +イシュー本文: {issue.body} + +リポジトリの概要: +{repository_summary} + +変更提案(diff形式で記述してください): +```diff +# ここにdiff形式の変更提案を記述 +``` + """ + + # プロンプトを保存 + save_prompt(prompt, issue.number) + + logger.info("LLMにプロンプトを送信します。") + suggestion = llm_service.get_response(prompt) + logger.info("LLMから変更提案を受け取りました。") + + github_service.add_comment(issue, f"## 変更提案\n\n{suggestion}") + logger.info("変更提案をイシューにコメントとして追加しました。") + + logger.info("処理が正常に完了しました。") + +if __name__ == "__main__": + main() diff --git a/.github/services/__init__.py b/.github/services/__init__.py new file mode 100644 index 0000000..e362394 --- /dev/null +++ b/.github/services/__init__.py @@ -0,0 +1,3 @@ +from .llm_service import LLMService +from .github_service import GitHubService +from .git_service import GitService diff --git a/.github/services/git_service.py b/.github/services/git_service.py new file mode 100644 index 0000000..fc909a7 --- /dev/null +++ b/.github/services/git_service.py @@ -0,0 +1,35 @@ +import subprocess +from typing import List +from loguru import logger +from config import get_settings + +class GitService: + def __init__(self): + self.settings = get_settings() + + def setup_credentials(self): + repo_url = f"https://x-access-token:{self.settings.GITHUB_TOKEN}@github.com/{self.settings.GITHUB_REPOSITORY}.git" + subprocess.run(["git", "remote", "set-url", "origin", repo_url]) + logger.info("Git credentials setup completed.") + + def create_branch(self, branch_name: str): + subprocess.run(["git", "checkout", "-b", branch_name]) + logger.info(f"ブランチ '{branch_name}' を作成しました。") + + def commit_changes(self, file_paths: List[str], commit_message: str): + for file_path in file_paths: + subprocess.run(["git", "add", file_path]) + + cmd = ["git", "commit", "-m", commit_message] + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + logger.error(f"Commit failed: {result.stderr}") + raise RuntimeError("Git commit failed") + logger.info(f"変更をコミットしました: {commit_message}") + + def push_changes(self, branch_name: str): + result = subprocess.run(["git", "push", "-u", "origin", branch_name], capture_output=True, text=True) + if result.returncode != 0: + logger.error(f"Push failed: {result.stderr}") + raise RuntimeError("Git push failed") + logger.info(f"変更を {branch_name} ブランチにプッシュしました。") diff --git a/.github/services/github_service.py b/.github/services/github_service.py new file mode 100644 index 0000000..c3baa26 --- /dev/null +++ b/.github/services/github_service.py @@ -0,0 +1,33 @@ +from github import Github +from loguru import logger +from config import get_settings + +class GitHubService: + def __init__(self): + self.settings = get_settings() + # self.g = Github(self.settings.GITHUB_TOKEN) + # self.g = Github(self.settings.YOUR_PERSONAL_ACCESS_TOKEN) + self.g = Github(self.settings.YOUR_PERSONAL_ACCESS_TOKEN_IRIS) + self.repo = self.g.get_repo(self.settings.GITHUB_REPOSITORY) + logger.debug(f"Using token: {self.settings.YOUR_PERSONAL_ACCESS_TOKEN[:5]}...") + + def get_issue(self, issue_number: int = None): + issue_number = issue_number or self.settings.ISSUE_NUMBER + logger.debug(f"issue_number : {issue_number}") + return self.repo.get_issue(number=issue_number) + + def get_comments(self, issue): + return list(issue.get_comments()) + + def add_comment(self, issue, comment): + issue.create_comment(comment) + logger.info(f"コメントを追加しました: \n{comment[:200]}...") + + def add_labels(self, issue, labels): + issue.add_to_labels(*labels) + logger.info(f"ラベルを追加しました: {', '.join(labels)}") + + def create_pull_request(self, title, body, head, base="main"): + pr = self.repo.create_pull(title=title, body=body, head=head, base=base) + logger.info(f"Pull Requestを作成しました: {pr.html_url}") + return pr diff --git a/.github/services/llm_service.py b/.github/services/llm_service.py new file mode 100644 index 0000000..c583ca8 --- /dev/null +++ b/.github/services/llm_service.py @@ -0,0 +1,52 @@ +from loguru import logger +from litellm import completion +from config import get_settings + +class LLMService: + def __init__(self): + self.settings = get_settings() + self.model = self.settings.LITELLM_MODEL + + def get_response(self, prompt: str) -> str: + try: + response = completion( + model=self.model, + messages=[{"role": "user", "content": prompt}] + ) + return response.choices[0].message.content.strip() + except Exception as e: + logger.error(f"LLMからのレスポンス取得中にエラーが発生しました: {str(e)}") + raise + + def apply_diff(self, original_content: str, diff: str) -> str: + prompt = f"""```diff + {diff} + ``` + + 上記のdiffを適用した結果を、ファイル全体の内容として出力してください。 + ファイルの内容: + ``` + {original_content} + ```""" + + return self.get_response(prompt) + + def analyze_issue(self, issue_title: str, issue_body: str, existing_labels: list) -> str: + prompt = f""" + 以下のGitHubイシューを分析し、適切なラベルを提案してください: + + タイトル: {issue_title} + + 本文: + {issue_body} + + 既存のラベルのリスト: + {', '.join(existing_labels)} + + 上記の既存のラベルのリストから、このイシューに最も適切なラベルを最大3つ選んでください。 + 選んだラベルをカンマ区切りで提案してください。既存のラベルにない新しいラベルは提案しないでください。 + + 回答は以下の形式でラベルのみを提供してください: + label1, label2, label3 + """ + return self.get_response(prompt) diff --git a/.github/utils/__init__.py b/.github/utils/__init__.py new file mode 100644 index 0000000..3072880 --- /dev/null +++ b/.github/utils/__init__.py @@ -0,0 +1 @@ +from .diff_utils import DiffUtils, process_diffs, save_files, create_comment diff --git a/.github/utils/diff_utils.py b/.github/utils/diff_utils.py new file mode 100644 index 0000000..219221f --- /dev/null +++ b/.github/utils/diff_utils.py @@ -0,0 +1,83 @@ +import re +from typing import Optional, Dict, List +from bs4 import BeautifulSoup +import markdown +from loguru import logger + +class DiffUtils: + @staticmethod + def convert_md_to_html(md_content: str) -> str: + return markdown.markdown(md_content, extensions=['fenced_code', 'codehilite']) + + @staticmethod + def extract_diff(comment_body: str) -> Optional[Dict[str, str]]: + logger.info("コメント本文から diff を抽出しています...") + html_content = DiffUtils.convert_md_to_html(comment_body) + + soup = BeautifulSoup(html_content, 'html.parser') + diff_blocks = soup.find_all('pre', class_='codehilite') + + if not diff_blocks: + logger.error("diff が見つかりません。") + return None + + diffs = {} + for diff_block in diff_blocks: + diff_code = diff_block.find('code', class_='language-diff') + if diff_code: + diff_content = diff_code.get_text() + "\n" + file_name = DiffUtils.extract_file_name(diff_content) + if file_name: + diffs[file_name] = diff_content + + if not diffs: + logger.error("有効な diff が見つかりません。") + return None + + logger.info(f"抽出された diff: {len(diffs)} ファイル") + return diffs + + @staticmethod + def extract_file_name(diff_content: str) -> Optional[str]: + file_name_match = re.search(r'^\+\+\+ b/(.+)$', diff_content, re.MULTILINE) + if file_name_match: + return file_name_match.group(1) + return None + + @staticmethod + def extract_code_block_content(html_content: str) -> str: + """HTMLコンテンツからコードブロック内の内容を抽出する""" + soup = BeautifulSoup(html_content, 'html.parser') + code_block = soup.find('pre', class_='codehilite') + if code_block: + code = code_block.find('code') + if code: + return code.get_text().strip() + return "" + +def process_diffs(diffs: Dict[str, str], llm_service) -> Dict[str, str]: + modified_contents = {} + for file_name, diff in diffs.items(): + with open(file_name, "r", encoding="utf-8") as f: + original_content = f.read() + modified_content = llm_service.apply_diff(original_content, diff) + html_content = DiffUtils.convert_md_to_html(modified_content) + extracted_content = DiffUtils.extract_code_block_content(html_content) + modified_contents[file_name] = extracted_content if extracted_content else modified_content + return modified_contents + +def save_files(modified_contents: Dict[str, str]) -> List[str]: + saved_files = [] + for file_name, content in modified_contents.items(): + with open(file_name, "w", encoding="utf-8") as f: + f.write(content) + logger.info(f"{file_name} に変更を保存しました。") + saved_files.append(file_name) + return saved_files + +def create_comment(modified_files: List[str], modified_contents: Dict[str, str]) -> str: + comment = "以下のファイルが変更されました:\n\n" + for file in modified_files: + comment += f"- {file}\n\n" + comment += f"```python\n{modified_contents[file]}\n```\n\n" + return comment diff --git a/.github/utils/patch_utils.py b/.github/utils/patch_utils.py new file mode 100644 index 0000000..7b74110 --- /dev/null +++ b/.github/utils/patch_utils.py @@ -0,0 +1,51 @@ +import tempfile +import subprocess +import os +from loguru import logger +import datetime + +def create_patch_directory(file_path): + # ベースとなるパッチディレクトリ + base_patches_dir = os.path.join(os.getcwd(), 'applied_patches') + + # ファイルパスに基づいてサブディレクトリを作成 + relative_dir = os.path.dirname(file_path) + patches_dir = os.path.join(base_patches_dir, relative_dir) + + # ディレクトリが存在しない場合は作成 + os.makedirs(patches_dir, exist_ok=True) + + return patches_dir + +def apply_patch(patch_file_path, file_path): + # パッチを保存するディレクトリを作成 + # patches_dir = create_patch_directory(file_path) + + # 現在の日時をファイル名に使用 + # now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + # patch_file_name = f"patch_{now}_{os.path.basename(file_path)}.diff" + # patch_file_path = os.path.join(patches_dir, patch_file_name) + + # print(patch_content) + + # パッチをローカルに保存 + # with open(patch_file_path, 'w', encoding='utf-8') as f: + # f.write(patch_content) + logger.info(f"パッチを {patch_file_path} に保存しました。") + + try: + # パッチを適用 + cmd = ['git', 'apply', patch_file_path, '--ignore-whitespace'] + logger.info("cmd : {}".format(" ".join(cmd))) + subprocess.run(cmd, check=True) + logger.info(f"{file_path} にパッチを適用しました。") + return True + except subprocess.CalledProcessError: + logger.error(f"{file_path} へのパッチ適用に失敗しました。") + return False + +def get_patch_file_path(file_path): + patches_dir = create_patch_directory(file_path) + now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + patch_file_name = f"patch_{now}_{os.path.basename(file_path)}.diff" + return os.path.join(patches_dir, patch_file_name) diff --git a/.github/utils/vis.py b/.github/utils/vis.py new file mode 100644 index 0000000..98b2450 --- /dev/null +++ b/.github/utils/vis.py @@ -0,0 +1,24 @@ +import sys + +def visualize_invisible_chars(filename): + with open(filename, 'r', encoding='utf-8') as file: + content = file.read() + + # 空白文字を可視化 + content = content.replace(' ', '·') + # タブを可視化 + content = content.replace('\t', '→ ') + # 改行を可視化 + content = content.replace('\n', '↵\n') + # キャリッジリターンを可視化 + content = content.replace('\r', '←') + + print(f"Visualized content of {filename}:") + print(content) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python script.py ") + sys.exit(1) + + visualize_invisible_chars(sys.argv[1]) diff --git a/.github/workflows/apply-suggestion.yml b/.github/workflows/apply-suggestion.yml new file mode 100644 index 0000000..3310f4a --- /dev/null +++ b/.github/workflows/apply-suggestion.yml @@ -0,0 +1,42 @@ +name: 提案された変更の適用 + +on: + issue_comment: + types: [created] + +jobs: + apply-changes: + runs-on: ubuntu-latest + if: ${{ github.event.issue.pull_request == null && github.event.comment.body == 'ok' }} + steps: + - name: リポジトリのチェックアウト + uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }} + + - name: Git の設定 + run: | + git config --global user.name 'github-actionsA[bot]' + git config --global user.email 'github-actionsA[bot]@users.noreply.github.com' + + - name: Python のセットアップ + uses: actions/setup-python@v3 + with: + python-version: '3.9' + + - name: 依存関係のインストール + run: | + pip install -r requirements.txt + + - name: スクリプトの実行 + run: python .github/scripts/apply_suggestion.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + YOUR_PERSONAL_ACCESS_TOKEN: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + # OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + YOUR_PERSONAL_ACCESS_TOKEN_IRIS: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN_IRIS }} diff --git a/.github/workflows/issue-deep-comment.yml b/.github/workflows/issue-deep-comment.yml new file mode 100644 index 0000000..53ff238 --- /dev/null +++ b/.github/workflows/issue-deep-comment.yml @@ -0,0 +1,88 @@ +name: イシューへの詳細コメントと変更提案 + +on: + issues: + types: [opened] + +permissions: + issues: write + contents: read + +jobs: + comment-on-issue: + runs-on: ubuntu-latest + steps: + - name: リポジトリのチェックアウト + uses: actions/checkout@v2 + + - name: Python のセットアップ + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: SourceSage のインストール + run: | + python -m pip install --upgrade pip + pip install sourcesage + + - name: SourceSage の実行 + run: | + mkdir -p .SourceSageAssets/DOCUMIND/ + sourcesage --ignore-file=".iris.SourceSageignore" + + - name: 依存関係のインストール + run: | + pip install -r requirements.txt + + - name: 詳細コメントの生成と追加 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + REPOSITORY_SUMMARY_PATH: .SourceSageAssets/DOCUMIND/Repository_summary.md + YOUR_PERSONAL_ACCESS_TOKEN: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }} + YOUR_PERSONAL_ACCESS_TOKEN_IRIS: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN_IRIS }} + run: python .github/scripts/deep_comment.py + + suggest-changes: + runs-on: ubuntu-latest + needs: comment-on-issue # comment-on-issue の後に実行 + if: ${{ github.event.issue.pull_request == null }} # PR 以外にのみ適用 + steps: + - name: リポジトリのチェックアウト + uses: actions/checkout@v2 + + - name: Python のセットアップ + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: SourceSage のインストール + run: | + python -m pip install --upgrade pip + pip install sourcesage + + - name: SourceSage の実行 + run: | + mkdir -p .SourceSageAssets/DOCUMIND/ + sourcesage --ignore-file=".iris.SourceSageignore" + + - name: 依存関係のインストール + run: | + pip install -r requirements.txt + + - name: 変更提案の生成と追加 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + REPOSITORY_SUMMARY_PATH: .SourceSageAssets/DOCUMIND/Repository_summary.md + YOUR_PERSONAL_ACCESS_TOKEN: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }} + YOUR_PERSONAL_ACCESS_TOKEN_IRIS: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN_IRIS }} + run: python .github/scripts/suggest_changes.py diff --git a/.github/workflows/issue-review.yml b/.github/workflows/issue-review.yml new file mode 100644 index 0000000..dfb7474 --- /dev/null +++ b/.github/workflows/issue-review.yml @@ -0,0 +1,39 @@ +name: イシュー自動レビュー + +on: + issues: + types: [opened] + +permissions: + issues: write + contents: read + pull-requests: write + +jobs: + review-issue: + runs-on: ubuntu-latest + steps: + - name: リポジトリのチェックアウト + uses: actions/checkout@v3 + + - name: Python のセットアップ + uses: actions/setup-python@v3 + with: + python-version: '3.9' + + - name: 依存関係のインストール + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: スクリプトの実行 + run: python .github/scripts/label_adder.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + # OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + YOUR_PERSONAL_ACCESS_TOKEN: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }} + YOUR_PERSONAL_ACCESS_TOKEN_IRIS: ${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN_IRIS }} diff --git a/.gitignore b/.gitignore index b6b25b1..a454261 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,5 @@ tmp2.md .Gaiah.md .SourceSageAssets .aira/aira.Gaiah.md -.harmon_ai/README_template.md +.harmon_ai +generated_prompts/ diff --git a/.harmon_ai/README_template.md b/.harmon_ai/README_template.md deleted file mode 100644 index 643eb96..0000000 --- a/.harmon_ai/README_template.md +++ /dev/null @@ -1,49 +0,0 @@ -

- -
-

AIRA

-

- ~AI-Integrated Repository for Accelerated Development~ -
- PyPI - Version -PyPI - Format -PyPI - Implementation -PyPI - Status -PyPI - Downloads -PyPI - Downloads - -GitHub Repo stars -forks - Sunwood-ai-labs -GitHub Last Commit -GitHub Top Language -GitHub Release -GitHub Tag -GitHub Actions Workflow Status -
-

- [🌐 Website] • - [🐱 GitHub] - [🐦 Twitter] • - [🍀 Official Blog] -

- -

- -

- ->[!IMPORTANT] ->このリポジトリのリリースノートやREADME、コミットメッセージの9割近くは[claude.ai](https://claude.ai/)や[ChatGPT4](https://chatgpt.com/)を活用した[AIRA](https://github.com/Sunwood-ai-labs/AIRA), [SourceSage](https://github.com/Sunwood-ai-labs/SourceSage), [Gaiah](https://github.com/Sunwood-ai-labs/Gaiah), [HarmonAI_II](https://github.com/Sunwood-ai-labs/HarmonAI_II)で生成しています。 - -## 🌟 Introduction - -## 🎥 Demo - -## 🚀 Getting Started - -## 📝 Updates - -## 🤝 Contributing - -## 📄 License - -## 🙏 Acknowledgements diff --git a/.harmon_ai/config.yml b/.harmon_ai/config.yml deleted file mode 100644 index 34de9e7..0000000 --- a/.harmon_ai/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -harmon_ai: - environment: - repo_name: "AIRA" - owner_name: "Sunwood-ai-labs" - package_name: "AIRA" - icon_url: "https://huggingface.co/datasets/MakiAi/IconAssets/resolve/main/AIRA.png" - title: "AIRA" - subtitle: "~AI-Integrated Repository for Accelerated Development~" - website_url: "https://hamaruki.com/" - github_url: "https://github.com/Sunwood-ai-labs" - twitter_url: "https://x.com/hAru_mAki_ch" - blog_url: "https://hamaruki.com/" - - product: - important_message_file: "important_template.md" - sections_content_file: "sections_template.md" - output_file: "README_template.md" - cicd_file_path: "publish-to-pypi.yml" - cicd_main_path: "publish-to-pypi.yml" - github_cicd_dir: ".github/workflows" - - development: - output_dir: "C:/Prj/AIRA-Sample/AIRA-Sample00/.harmon_ai" - - main: - main_dir: "C:/Prj/AIRA-Sample/AIRA-Sample00/" - replace_readme: true diff --git a/.harmon_ai/important_template.md b/.harmon_ai/important_template.md deleted file mode 100644 index 57c3801..0000000 --- a/.harmon_ai/important_template.md +++ /dev/null @@ -1 +0,0 @@ -このリポジトリのリリースノートやREADME、コミットメッセージの9割近くは[claude.ai](https://claude.ai/)や[ChatGPT4](https://chatgpt.com/)を活用した[AIRA](https://github.com/Sunwood-ai-labs/AIRA), [SourceSage](https://github.com/Sunwood-ai-labs/SourceSage), [Gaiah](https://github.com/Sunwood-ai-labs/Gaiah), [HarmonAI_II](https://github.com/Sunwood-ai-labs/HarmonAI_II)で生成しています。 \ No newline at end of file diff --git a/.harmon_ai/publish-to-pypi.yml b/.harmon_ai/publish-to-pypi.yml deleted file mode 100644 index 42b2a45..0000000 --- a/.harmon_ai/publish-to-pypi.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Publish Python Package - -on: - push: - tags: - - '*' -jobs: - publish: - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/AIRA - permissions: - id-token: write - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@release/v1 - with: - skip-existing: true \ No newline at end of file diff --git a/.harmon_ai/sections_template.md b/.harmon_ai/sections_template.md deleted file mode 100644 index f7f05ff..0000000 --- a/.harmon_ai/sections_template.md +++ /dev/null @@ -1,13 +0,0 @@ -## 🌟 Introduction - -## 🎥 Demo - -## 🚀 Getting Started - -## 📝 Updates - -## 🤝 Contributing - -## 📄 License - -## 🙏 Acknowledgements \ No newline at end of file diff --git a/.iris.SourceSageignore b/.iris.SourceSageignore new file mode 100644 index 0000000..9ddddf0 --- /dev/null +++ b/.iris.SourceSageignore @@ -0,0 +1,57 @@ +.git +__pycache__ +LICENSE +output.md +assets +Style-Bert-VITS2 +output +streamlit +SourceSage.md +data +.gitignore +.SourceSageignore +*.png +Changelog +SourceSageAssets +SourceSageAssetsDemo +__pycache__ +.pyc +**/__pycache__/** +modules\__pycache__ +.svg +sourcesage.egg-info +.pytest_cache +dist +build +.env +example + +.gaiah.md +.Gaiah.md +tmp.md +tmp2.md +.SourceSageAssets +tests +template +aira.egg-info +aira.Gaiah.md +README_template.md + +.SourceSageAssets +issue_creator.log +.aira + +app.py +style.css +utils.py +.cache +converted_comment.html +modified_README.html +modified_README.md + +generated_prompts +temp.patch +applied_patches +.github +.harmon_ai +.gaiah diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..d70b1e8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,64 @@ +## CONTRIBUTING.md + +# コントリビューションガイドライン + +まず、AIRAに興味を持っていただきありがとうございます! 🙏 + +AIRAは、オープンソースプロジェクトであり、皆さんのコントリビューションを歓迎します! 😄 + +バグの報告、機能のリクエスト、ドキュメントの改善など、どのような形でも構いません。 + +## コントリビューションの方法 + +### Issueの作成 + +バグの報告や機能のリクエストを行う場合は、[Issueページ](https://github.com/Sunwood-ai-labs/AIRA/issues)に新しいIssueを作成してください。 + +Issueを作成する際には、以下の内容を記載するようにしてください。 + +* Issueのタイトル +* Issueの内容 +* 再現手順(バグの場合) +* 期待される動作 +* 実際の動作 +* スクリーンショット(必要であれば) + +### Pull Requestの作成 + +AIRAのコードを修正したり、新しい機能を追加する場合は、Pull Requestを作成してください。 + +Pull Requestを作成する際には、以下の手順に従ってください。 + +1. リポジトリをフォークする +2. フォークしたリポジトリをクローンする +3. 新しいブランチを作成する +4. コードを修正または機能を追加する +5. コミットしてプッシュする +6. Pull Requestを作成する + +Pull Requestを作成する際には、以下の内容を記載するようにしてください。 + +* Pull Requestのタイトル +* Pull Requestの内容 +* 関連するIssue +* スクリーンショット(必要であれば) + +### IRISを使った開発支援 + +本リポジトリでは、[IRIS](https://github.com/Sunwood-ai-labs/IRIS) を利用した開発支援を行っています。 + +具体的には、以下の機能が自動化されています。 + +* **ラベルの自動付与:** イシューの内容に基づいて、適切なラベルが自動的に付与されます。 +* **変更提案の自動生成:** イシューの内容に基づいて、コードの変更提案が自動生成されます。 +* **イシューの詳細化:** イシューの内容に基づいて、詳細なコメントが自動生成されます。 + +これらの機能により、開発者はより効率的に開発を進めることができます。 +例えば、イシューの詳細化機能によって、開発者はイシューの内容を理解するために必要な情報を迅速に得ることができ、より早く解決策を見つけることができます。 +また、変更提案機能によって、開発者は修正案を検討する時間を短縮し、より多くの時間を他のタスクに充てることができます。 + +これらの機能は、開発者の作業効率向上に大きく貢献します。 + +## ライセンス + +AIRAは、[MITライセンス](https://opensource.org/licenses/MIT)の下で公開されています。 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c5891c2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +# Dockerfile +FROM python:3.9 + +WORKDIR /app + +RUN apt-get update && apt-get install -y git + +# Git設定をDockerfile内で行う +RUN git config --global user.email 'yukihiko.fuyuki@gmail.com' && \ + git config --global user.name 'Yukihiko' + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7df1042 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Maki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 643eb96..bdc32c6 100644 --- a/README.md +++ b/README.md @@ -34,16 +34,131 @@ >[!IMPORTANT] >このリポジトリのリリースノートやREADME、コミットメッセージの9割近くは[claude.ai](https://claude.ai/)や[ChatGPT4](https://chatgpt.com/)を活用した[AIRA](https://github.com/Sunwood-ai-labs/AIRA), [SourceSage](https://github.com/Sunwood-ai-labs/SourceSage), [Gaiah](https://github.com/Sunwood-ai-labs/Gaiah), [HarmonAI_II](https://github.com/Sunwood-ai-labs/HarmonAI_II)で生成しています。 -## 🌟 Introduction +## 🌟 はじめに -## 🎥 Demo +AIRAは、リポジトリの管理や開発を加速するためのAIインテグレーション開発ツールです。 +Githubリポジトリの作成、ローカルリポジトリの初期化、コミットメッセージの自動生成、READMEの自動生成などを行うことができます。 -## 🚀 Getting Started +開発者の皆さんは、AIRAを使うことで以下のようなメリットを得ることができます。 -## 📝 Updates +- リポジトリ管理の自動化による開発の加速 +- コミットメッセージやREADMEの自動生成による手間の削減 +- 開発者同士のコミュニケーションの円滑化 -## 🤝 Contributing +AIRAは、開発者の皆さんの開発効率を高め、よりクリエイティブな活動に集中できるようサポートします。 -## 📄 License +## 🚀 インストール方法 -## 🙏 Acknowledgements +AIRAは、以下の手順でインストールすることができます。 + +1. Python 3.7以上がインストールされていることを確認してください。 +2. ターミナルまたはコマンドプロンプトを開きます。 +3. 以下のコマンドを実行して、AIRAをインストールします。 + + ```bash + pip install aira + ``` + +これで、AIRAのインストールは完了です。 +`aira --help`コマンドを実行して、使い方を確認してみましょう。 + +## 📝 使い方 + +### 環境設定 + +`.env`ファイルを作成し、必要な設定を記述します。 +`.env.example`をコピーして使用することができます。 + +```bash +cp .env.example .env +``` + +主な設定項目: +```plaintext +# AIRAの基本設定 +GAIAH_RUN=true +COMMIT_MSG_PATH=.Gaiah.md + +# LLMの設定 +LLM_MODEL=gemini/gemini-1.5-pro-latest +GEMINI_API_KEY=your-api-key-here + +# GitHubの設定(必要な場合のみ) +GITHUB_ACCESS_TOKEN=your-github-token-here +``` + +### コミットメッセージの自動生成 + +AIRAには2つのコミット生成モードがあります: + +1. 基本的なコミットモード: +```bash +aira --mode commit +``` + +2. SourceSageを使用した高度なコミットモード: +```bash +aira --mode sourcesage commit --ss-model-name="gemini/gemini-1.5-flash-002" +``` + +このコマンドを実行すると、以下の処理が行われます: + +1. 変更内容の取得と解析 +2. AIによるコミットメッセージの自動生成 +3. ファイルのステージング +4. コミットの実行 + +#### コミットモードの違い + +- **基本モード(--mode commit)** + - シンプルな変更に適しています + - 高速な処理が可能 + - 基本的なコミットメッセージを生成 + +- **SourceSageモード(--mode sourcesage commit)** + - 複雑な変更に適しています + - より詳細なコード解析を実行 + - 高品質なコミットメッセージを生成 + - カスタムモデルの指定が可能(--ss-model-name) + +## 🤝 コントリビューション + +AIRAは、オープンソースプロジェクトです。 +皆さんのコントリビューションを歓迎します! + +バグ報告や機能リクエストがある場合は、[Issueページ](https://github.com/Sunwood-ai-labs/AIRA/issues)からお願いします。 +また、プルリクエストも大歓迎です。 + +コントリビューションガイドラインについては、[CONTRIBUTING.md](CONTRIBUTING.md)を参照してください。 + +## 開発者用 + +### SourceSageリリースノートを作成コマンド + +```shell +sourcesage --mode DocuMind --docuMind-model "gemini/gemini-1.5-pro-latest" --docuMind-db ".SourceSageAssets\DOCUMIND\Repository_summary.md" --docuMind-release-report ".SourceSageAssets\RELEASE_REPORT\Report_v0.2.2.md" --docuMind-changelog ".SourceSageAssets\Changelog\CHANGELOG_release_0.2.2.md" --docuMind-output ".SourceSageAssets/DOCUMIND/RELEASE_NOTES_v0.2.2.md" --docuMind-prompt-output ".SourceSageAssets/DOCUMIND/_PROMPT_v0.2.2.md" --repo-name "SourceSage" --repo-version "v0.2.2" +``` + +## 📄 ライセンス + +AIRAは、[MITライセンス](https://opensource.org/licenses/MIT)の下で公開されています。 +詳細は、[LICENSE](LICENSE)ファイルを参照してください。 + +## 🙏 謝辞 + +AIRAの開発にあたり、以下のオープンソースプロジェクトを活用させていただきました。 +この場を借りて、お礼申し上げます。 + +- [SourceSage](https://github.com/Sunwood-ai-labs/SourceSage) +- [Gaiah](https://github.com/Sunwood-ai-labs/Gaiah) +- [HarmonAI_II](https://github.com/Sunwood-ai-labs/HarmonAI_II) + +また、AIRAの開発には、以下のAIモデルを活用させていただきました。 + +- [claude.ai](https://claude.ai/) +- [ChatGPT4](https://chat.openai.com/) + +最後に、AIRAを使ってくださる開発者の皆さんに感謝いたします。 +皆さんのフィードバックを元に、より良いツールを目指して開発を続けていきます。 + +これからもAIRAをよろしくお願いします! diff --git a/aira/__init__.py b/aira/__init__.py index 5717afa..40d89bf 100644 --- a/aira/__init__.py +++ b/aira/__init__.py @@ -1,5 +1,5 @@ from .cli import main -__version__ = '0.1.0' +__version__ = '0.6.0' -__all__ = ['aira', 'main', '__version__'] \ No newline at end of file +__all__ = ['aira', 'main', '__version__'] diff --git a/aira/aira.py b/aira/aira.py index 2e365a6..d817dcf 100644 --- a/aira/aira.py +++ b/aira/aira.py @@ -1,10 +1,84 @@ from loguru import logger -from .gaiah_repo import GaiahRepo -from .gaiah_commit import GaiahCommit +from .repository_manager import RepositoryManager +from .commit_manager import CommitManager + +from litellm import completion +import os + +# SourceSageのモジュールをインポート +from sourcesage.core import SourceSage +from sourcesage.modules.ReleaseDiffReportGenerator import GitDiffGenerator, MarkdownReportGenerator +from sourcesage.modules.CommitCraft import CommitCraft +from sourcesage.modules.DocuMind import DocuMind +from sourcesage.modules.IssueWize import IssueWize + +from dotenv import load_dotenv +dotenv_path=os.path.join(os.getcwd(), '.env') +logger.debug(f"dotenv_path : {dotenv_path}") +load_dotenv(dotenv_path=dotenv_path, verbose=True, override=True) class Aira: + """AIRAメインクラス""" def __init__(self, args): - pass + """初期化""" + self.args = args + self.model = args.model + + # リポジトリとコミットマネージャーを初期化 + self.repo_manager = RepositoryManager() + self.commit_manager = CommitManager({"commit_msg_path": os.getenv('COMMIT_MSG_PATH', '.SourceSageAssets/COMMIT_CRAFT/llm_output.md')}) + + def run_sourcesage(self): + """SourceSageの各モジュールを実行する""" + args = self.args + + # ----------------------------------------------- + # SourceSageの実行 + if 'all' in args.ss_mode or 'Sage' in args.ss_mode: + logger.info("SourceSageを起動します...") + sourcesage = SourceSage(args.ss_output, args.repo, args.owner, args.repository, + args.ignore_file, args.language_map, args.changelog_start_tag, + args.changelog_end_tag) + sourcesage.run() + + # ----------------------------------------------- + # IssueWizeを使用してIssueを作成 + if 'all' in args.ss_mode or 'IssueWize' in args.ss_mode: + issuewize = IssueWize(model=args.issuewize_model) + if args.issue_summary and args.project_name and args.repo_overview_file: + logger.info("IssueWizeを使用してIssueを作成します...") + issuewize.create_optimized_issue(args.issue_summary, args.project_name, + args.milestone_name, args.repo_overview_file) + else: + logger.warning("IssueWizeの実行に必要なパラメータが不足しています。") + + # ----------------------------------------------- + # レポートの生成 + if 'all' in args.ss_mode or 'GenerateReport' in args.ss_mode: + logger.info("git diff レポートの生成を開始します...") + git_diff_generator = GitDiffGenerator(args.repo_path, args.git_fetch_tags, args.git_tag_sort, args.git_diff_command) + diff, latest_tag, previous_tag = git_diff_generator.get_git_diff() + + if diff is not None: + report_file_name = args.report_file_name.format(latest_tag=latest_tag) + os.makedirs(args.ss_output_path, exist_ok=True) + output_path = os.path.join(args.ss_output_path, report_file_name) + + markdown_report_generator = MarkdownReportGenerator(diff, latest_tag, previous_tag, + args.report_title, args.report_sections, + output_path) + markdown_report_generator.generate_markdown_report() + + # ----------------------------------------------- + # CommitCraftを使用してLLMにステージ情報を送信し、コミットメッセージを生成 + if 'all' in args.ss_mode or 'CommitCraft' in args.ss_mode: + stage_info_file = args.stage_info_file + llm_output_file = os.path.join(args.commit_craft_output, args.llm_output) + os.makedirs(args.commit_craft_output, exist_ok=True) + commit_craft = CommitCraft(args.ss_model_name, stage_info_file, llm_output_file) + commit_craft.generate_commit_messages() + + # コミットメッセージが生成されたら自動コミットを実行 + logger.info("自動コミットを実行します...") + self.commit_manager.process_commits() - def run(self): - pass \ No newline at end of file diff --git a/aira/cli.py b/aira/cli.py index 29cd474..ea32190 100644 --- a/aira/cli.py +++ b/aira/cli.py @@ -1,96 +1,48 @@ import argparse from art import tprint from loguru import logger -from gaiah.gaiah import Gaiah -from gaiah.cli import load_config - -from harmon_ai.harmon_ai import HarmonAI - import sys import os import shutil -import yaml +from dotenv import load_dotenv +from .aira import Aira +from sourcesage.cli import add_arguments as sourcesage_add_arguments logger.configure( handlers=[ { "sink": sys.stderr, - "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level:<8} | {name:<45}:{line:<5} | {message}", + "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level:<8} | {name:<35}:{function:<35}:{line:<5} | {message}", "colorize": True, } ] ) def parse_arguments(): - """ - コマンドライン引数を解析する - """ - parser = argparse.ArgumentParser(description='Gaiah - シンプルなGitリポジトリ管理ツール') - - parser.add_argument('--config', default='.aira/config.yml', help='設定ファイルのパス') - + """コマンドライン引数を解析する""" + load_dotenv() + default_model = os.getenv('LLM_MODEL', 'gemini/gemini-1.5-pro-latest') + parser = argparse.ArgumentParser(description='AIRA - Automated Source Code Analysis Tool') + parser.add_argument('--mode', nargs='+', default=['commit'], + help='処理モード(commit: 自動コミット, sourcesage: SourceSage実行)') + parser.add_argument('--model', default=default_model, help='使用するLLMモデル名') + sourcesage_add_arguments(parser) return parser.parse_args() def main(): + """メイン処理""" tprint("! Welcome to AIRA !") - - # .aira/config.ymlが存在するかチェック args = parse_arguments() - aira_config_path = args.config - if not os.path.exists(aira_config_path): - # aira\template\config.ymlをコピー - template_config_path = "aira/template/config.yml" - os.makedirs(os.path.dirname(aira_config_path), exist_ok=True) - shutil.copy(template_config_path, aira_config_path) - logger.info(f"{aira_config_path}が見つかりませんでした。{template_config_path}からコピーしました。") - else: - logger.info(f"{aira_config_path}が見つかりました。") - - # .aira/config.ymlからconfig_pathを取得 - with open(aira_config_path, "r") as f: - aira_config = yaml.safe_load(f) - - - # ------------------ - # make abst - # - - # ------------------ - # gaiah init - # - if(aira_config["aira"]["gaiah"]["run"]): - logger.info("gaiah init section ....") - gaiah_config_path = aira_config["aira"]["gaiah"]["develop"]["config_path"] - gaiah_config = load_config(gaiah_config_path) - logger.info(f"Gaiah config path : {gaiah_config_path}") - if not os.path.exists(os.path.join(gaiah_config["gaiah"]["local"]["repo_dir"], ".git")): - logger.info("初期化を行います...") - gaiah_config_path = aira_config["aira"]["gaiah"]["init"]["config_path"] - gaiah_config = load_config(gaiah_config_path) + aira = Aira(args=args) + + for mode in args.mode: + if mode == "commit": + logger.info("mode is << commit >>") + aira.commit_manager.process_commits() + elif mode == "sourcesage": + logger.info("mode is << sourcesage >>") + aira.run_sourcesage() else: - logger.info(".gitが発見されました...") - - tprint("-- Gaiah --") - logger.info("Gaiahの処理を開始します...") - gaiah = Gaiah(gaiah_config) - gaiah.run() - - # ------------------ - # Harmon AI - # - if(aira_config["aira"]["harmon_ai"]["run"]): - logger.info("Harmon AIの処理を開始します...") - harmon_ai = HarmonAI() - harmon_ai.run() - - # ------------------ - # gaiah run - # - logger.info("gaiah run section ....") - gaiah_config_path = aira_config["aira"]["gaiah"]["develop"]["config_path"] - gaiah_config = load_config(gaiah_config_path) - gaiah = Gaiah(gaiah_config) - gaiah.run() - + logger.warning(f"Unknown mode: {mode}") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/aira/commit_manager.py b/aira/commit_manager.py new file mode 100644 index 0000000..e567404 --- /dev/null +++ b/aira/commit_manager.py @@ -0,0 +1,186 @@ +import os +import re +from loguru import logger +from .utils import run_command, tqdm_sleep +from .managers.staging_manager import StagingManager +from .managers.branch_manager import BranchManager + +class CommitManager: + FILENAME_REGEX = r'(?m)^###\s(.+)' + COMMIT_MESSAGE_REGEX = r'```commit-msg\n(.*?)\n```' + + def __init__(self, config): + self.config = config + self.repo_dir = "./" + self.commit_msg_path = config.get('commit_msg_path', '.Gaiah.md') + self.staging_manager = StagingManager(self.repo_dir) + self.branch_manager = BranchManager(self.repo_dir) + + def commit_changes(self, commit_message, branch_name=None): + """変更をコミットする""" + try: + if branch_name: + self.branch_manager.checkout_branch(branch_name, create=True) + + # Gitの設定を確認 + try: + user_name = run_command(["git", "config", "user.name"], cwd=self.repo_dir) + user_email = run_command(["git", "config", "user.email"], cwd=self.repo_dir) + + if not user_name or not user_email: + logger.warning("Git user configuration is missing") + run_command(["git", "config", "user.name", "AIRA Bot"], cwd=self.repo_dir) + run_command(["git", "config", "user.email", "aira@example.com"], cwd=self.repo_dir) + logger.info("Set default Git configuration") + except Exception as e: + logger.warning(f"Error checking Git configuration: {e}") + run_command(["git", "config", "user.name", "AIRA Bot"], cwd=self.repo_dir) + run_command(["git", "config", "user.email", "aira@example.com"], cwd=self.repo_dir) + + # ステージングされたファイルがあるか確認 + staged_files = run_command(["git", "diff", "--staged", "--name-only"], cwd=self.repo_dir) + if not staged_files.strip(): + logger.warning("No staged changes to commit") + return False + + # コミットメッセージファイルを作成 + commit_message_file = os.path.join(self.repo_dir, ".aira_commit_message.txt") + try: + with open(commit_message_file, "w", encoding="utf-8") as f: + f.write(commit_message) + except Exception as e: + logger.error(f"Error writing commit message file: {e}") + return False + + try: + # コミットを実行 + output = run_command(["git", "commit", "-F", commit_message_file], cwd=self.repo_dir, check=False) + if "nothing to commit" in output.lower(): + logger.warning("Nothing to commit") + return False + + logger.success("Committed changes.") + return True + except Exception as commit_error: + logger.error(f"Error during commit operation: {commit_error}") + return False + finally: + # コミットメッセージファイルを削除 + try: + if os.path.exists(commit_message_file): + os.remove(commit_message_file) + except Exception as e: + logger.warning(f"Error removing commit message file: {e}") + + except Exception as e: + logger.error(f"Error while committing changes: {e}") + return False + + def process_commits(self, branch_name=None): + """コミットメッセージファイルからコミットを処理する""" + content = self._read_commit_messages() + if not content: + return + + # ブランチごとのコミット内容を整理 + branch_commits = {} + branch_sections = re.split(r'(?m)^##\s(.+)', content)[1:] + self.staging_manager.unstage_files() + tqdm_sleep(5) + + # 各セクションをブランチごとにグループ化 + for i in range(0, len(branch_sections), 2): + branch = branch_sections[i].strip() + content = branch_sections[i + 1] + if branch not in branch_commits: + branch_commits[branch] = [] + branch_commits[branch].append(content) + + # ブランチごとにまとめて処理 + for branch, contents in branch_commits.items(): + try: + # ブランチ名とコンテンツを直接渡す + current_branch = branch_name or branch + branch_content = "\n".join(contents) + if current_branch != "develop": + self.branch_manager.checkout_branch(current_branch, create=True) + self._process_commits_for_branch(branch_content, current_branch) + except Exception as e: + logger.error(f"Error processing branch {branch}: {e}") + + def _read_commit_messages(self): + try: + with open(self.commit_msg_path, "r", encoding="utf-8") as file: + return file.read() + except FileNotFoundError: + logger.warning(f"Commit messages file not found: {self.commit_msg_path}") + logger.info(f"Creating an empty commit messages file: {self.commit_msg_path}") + with open(self.commit_msg_path, "w", encoding="utf-8") as file: + file.write("") + return None + + def _process_commits_for_branch(self, branch_content, branch_name): + """ブランチ内の全コミットを処理する""" + try: + commits = re.split(self.FILENAME_REGEX, branch_content) + if commits and not commits[0].strip(): + commits = commits[1:] + + for j in range(0, len(commits), 2): + filename = commits[j].strip() + commit_message_section = commits[j + 1] + self.process_commit_section(filename, commit_message_section, branch_name) + + # developブランチ以外の場合、コミットが成功したらマージを試みる + if branch_name != "develop": + try: + self.branch_manager.merge_to_develop(branch_name) + except Exception as merge_error: + logger.error(f"Failed to merge branch {branch_name} to develop: {merge_error}") + except Exception as e: + logger.error(f"Error processing commits for branch {branch_name}: {e}") + raise + def process_commit_section(self, filename, commit_message_section, branch_name): + """ + コミットセクションを処理する + """ + commit_message_match = re.search(self.COMMIT_MESSAGE_REGEX, commit_message_section, re.DOTALL) + if commit_message_match: + commit_message = commit_message_match.group(1) + msg = f"{'-'*10} Commit message: [{branch_name}][{filename}]{'-'*10} " + logger.info(f"{msg}") + for commit_msg in commit_message.split("\n"): + logger.info(f"{commit_msg}") + commit_message = commit_message.strip() + logger.info(f"{'-'*len(msg)}") + else: + logger.warning("No commit message found in the commit section. Skipping...") + return + + self.process_file(filename, commit_message, branch_name) + + def process_file(self, filename, commit_message, branch_name=None): + """ + ファイルを処理する + """ + try: + # ファイルの存在チェックとステージング + if os.path.exists(os.path.join(self.repo_dir, filename)): + logger.info("file is modified") + self.staging_manager.stage_file(filename, "modified") + else: + logger.info("file is deleted") + self.staging_manager.stage_file(filename, "deleted") + + # ステージされたファイルの確認 + changed_files = run_command(["git", "diff", "--staged", "--name-only"], cwd=self.repo_dir).splitlines() + + logger.info(f"changed_files is {changed_files}") + if filename in changed_files: + self.commit_changes(commit_message, branch_name) + else: + logger.info(f"No changes detected in file: {filename}") + + except Exception as e: + logger.error(f"Error while processing file: {filename} - {e}") + raise diff --git a/aira/managers/__init__.py b/aira/managers/__init__.py new file mode 100644 index 0000000..47c1628 --- /dev/null +++ b/aira/managers/__init__.py @@ -0,0 +1,7 @@ +from .branch_manager import BranchManager +from .staging_manager import StagingManager + +__all__ = [ + 'BranchManager', + 'StagingManager' +] diff --git a/aira/managers/branch_manager.py b/aira/managers/branch_manager.py new file mode 100644 index 0000000..2872573 --- /dev/null +++ b/aira/managers/branch_manager.py @@ -0,0 +1,112 @@ +from loguru import logger +from ..utils import run_command + +class BranchManager: + def __init__(self, repo_dir="./"): + self.repo_dir = repo_dir + + def get_current_branch(self): + """現在のブランチ名を取得する""" + return run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=self.repo_dir).strip() + + def branch_exists(self, branch_name): + """指定したブランチが存在するかチェックする""" + try: + run_command(["git", "rev-parse", "--verify", branch_name], cwd=self.repo_dir) + return True + except Exception: + return False + + def has_uncommitted_changes(self): + """未コミットの変更があるかチェックする""" + try: + status = run_command(["git", "status", "--porcelain"], cwd=self.repo_dir) + return bool(status.strip()) + except Exception: + return False + + def stash_changes(self): + """現在の変更を一時保存する""" + if self.has_uncommitted_changes(): + logger.info("Stashing uncommitted changes...") + run_command(["git", "stash", "save", "AIRA: Temporary stash"], cwd=self.repo_dir) + + def pop_stashed_changes(self): + """一時保存した変更を復元する""" + try: + run_command(["git", "stash", "pop"], cwd=self.repo_dir) + logger.info("Restored stashed changes") + except Exception as e: + logger.warning(f"No stashed changes to restore or conflict occurred: {e}") + + def checkout_branch(self, branch_name, create=False): + """ブランチをチェックアウトする""" + try: + current = self.get_current_branch() + if current == branch_name: + logger.info(f"Already on branch {branch_name}") + return + + # 未コミットの変更がある場合は一時保存 + had_changes = self.has_uncommitted_changes() + if had_changes: + self.stash_changes() + + try: + if create and not self.branch_exists(branch_name): + run_command(["git", "checkout", "-b", branch_name], cwd=self.repo_dir) + else: + if not self.branch_exists(branch_name): + run_command(["git", "checkout", "-b", branch_name], cwd=self.repo_dir) + else: + run_command(["git", "checkout", branch_name], cwd=self.repo_dir) + logger.success(f"Checked out branch: {branch_name}") + finally: + # 一時保存した変更を復元 + if had_changes: + self.pop_stashed_changes() + + except Exception as e: + logger.error(f"Error while checking out branch: {e}") + raise + + def merge_to_develop(self, source_branch): + """developブランチへのマージと元ブランチの削除を行う""" + if not self.branch_exists(source_branch): + logger.warning(f"Branch {source_branch} does not exist. Skipping merge.") + return + + try: + current_branch = self.get_current_branch() + + # 未コミットの変更がある場合は一時保存 + had_changes = self.has_uncommitted_changes() + if had_changes: + self.stash_changes() + + try: + self.checkout_branch("develop") + run_command(["git", "merge", "--no-ff", source_branch], cwd=self.repo_dir) + logger.success(f"Successfully merged {source_branch} into develop") + + if source_branch != "main": + run_command(["git", "branch", "-d", source_branch], cwd=self.repo_dir) + logger.success(f"Deleted branch: {source_branch}") + + except Exception as merge_error: + logger.error(f"Merge conflict occurred: {merge_error}") + run_command(["git", "merge", "--abort"], cwd=self.repo_dir) + raise + finally: + if current_branch != source_branch or source_branch == "main": + self.checkout_branch(current_branch) + else: + self.checkout_branch("develop") + + # 一時保存した変更を復元 + if had_changes: + self.pop_stashed_changes() + + except Exception as e: + logger.error(f"Error during merge to develop: {e}") + raise diff --git a/aira/managers/staging_manager.py b/aira/managers/staging_manager.py new file mode 100644 index 0000000..1692545 --- /dev/null +++ b/aira/managers/staging_manager.py @@ -0,0 +1,34 @@ +import os +from loguru import logger +from ..utils import run_command + +class StagingManager: + def __init__(self, repo_dir="./"): + self.repo_dir = repo_dir + + def unstage_files(self): + """ステージにある全てのファイルをアンステージする""" + logger.info("Unstaging all files...") + try: + staged_files = run_command(["git", "diff", "--name-only", "--cached"], cwd=self.repo_dir).splitlines() + if staged_files: + run_command(["git", "reset", "HEAD", "--"] + staged_files, cwd=self.repo_dir) + logger.success(f"Unstaged files: {', '.join(staged_files)}") + else: + logger.info("No staged files found.") + except Exception as e: + logger.error(f"Error while unstaging files: {e}") + raise + + def stage_file(self, filename, action): + """ファイルをステージする""" + try: + if action == "deleted": + logger.info(f"Deleted file: {filename}") + run_command(["git", "rm", filename], cwd=self.repo_dir) + else: + logger.info(f"Staged file: {filename}") + run_command(["git", "add", filename], cwd=self.repo_dir) + except Exception as e: + logger.error(f"Error while staging file: {filename} - {e}") + raise diff --git a/aira/repository_manager.py b/aira/repository_manager.py new file mode 100644 index 0000000..a35d16b --- /dev/null +++ b/aira/repository_manager.py @@ -0,0 +1,24 @@ +from loguru import logger +from .utils import run_command + +class RepositoryManager: + def __init__(self): + self.repo_dir = "./" + + def get_staged_files(self): + """ステージングされているファイルの一覧を取得する""" + staged_files = run_command(["git", "diff", "--name-only", "--cached"], cwd=self.repo_dir) + return staged_files.splitlines() if staged_files else [] + + def get_current_branch(self): + """現在のブランチ名を取得する""" + return run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=self.repo_dir).strip() + + def checkout_branch(self, branch_name, create=False): + """指定したブランチをチェックアウトする""" + if create: + run_command(["git", "checkout", "-b", branch_name], cwd=self.repo_dir) + else: + run_command(["git", "checkout", branch_name], cwd=self.repo_dir) + logger.success(f"Checked out branch: {branch_name}") + return True diff --git a/aira/template/config.yml b/aira/template/config.yml deleted file mode 100644 index 2d300a1..0000000 --- a/aira/template/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -aira: - gaiah: - run: false - config_path: .gaiah/config.yml - harmon_ai: - run: true - config_path: .harmon_ai/config.yml - instructions_prompt: .aira/instructions.md \ No newline at end of file diff --git a/aira/utils.py b/aira/utils.py new file mode 100644 index 0000000..7847929 --- /dev/null +++ b/aira/utils.py @@ -0,0 +1,63 @@ +import subprocess +from loguru import logger +import time +from tqdm import tqdm + +def run_command(command, cwd=None, check=True): + """ + コマンドを実行し、結果を返す + + Args: + command (list): 実行するコマンドとその引数のリスト + cwd (str, optional): コマンドを実行するディレクトリ + check (bool, optional): エラー時に例外を発生させるかどうか + + Returns: + str: コマンドの実行結果 + """ + try: + logger.info(f"実行コマンド: {' '.join(command)}") + result = subprocess.run(command, cwd=cwd, check=check, capture_output=True, text=True, encoding='utf-8') + time.sleep(1) + return result.stdout.strip() + except subprocess.CalledProcessError as e: + error_message = e.stderr.strip() + logger.warning(f"Error while running command: {' '.join(command)}") + logger.warning(f"Error message: {error_message}") + raise + +def tqdm_sleep(n): + """ + 進捗バーを表示しながらスリープする + + Args: + n (int): スリープする秒数 + """ + for _ in tqdm(range(n)): + time.sleep(1) + +def create_config_template(): + """ + デフォルトの設定テンプレートを生成する + + Returns: + dict: デフォルトの設定 + """ + return { + "gaiah": { + "run": True, + "repo": { + "repo_name": "AIRA", + "description": "AI-Integrated Repository for Accelerated Development", + "private": False + }, + "local": { + "repo_dir": "./", + "no_initial_commit": False + }, + "commit": { + "process_commits": True, + "commit_msg_path": ".Gaiah.md" + } + } + } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..55eb2ae --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +# docker-compose.yml +version: '3.8' +services: + app: + build: . + volumes: + - .:/app + # - ~/.ssh:/root/.ssh:ro # SSHキーをマウント + # - ./.cache:/root/.cache # キャッシュディレクトリをマウント + env_file: + - .env + # command: python /app/apply_suggestion.py + tty: true diff --git a/docs/.sourcesage_releasenotes.yml b/docs/.sourcesage_releasenotes.yml new file mode 100644 index 0000000..ac71239 --- /dev/null +++ b/docs/.sourcesage_releasenotes.yml @@ -0,0 +1,14 @@ +ss-mode: + - DocuMind +docuMind-model: "gemini/gemini-1.5-pro-latest" +docuMind-db: ".SourceSageAssets/DOCUMIND/Repository_summary.md" +docuMind-release-report: ".SourceSageAssets/RELEASE_REPORT/Report_v0.4.0.md" +docuMind-changelog: ".SourceSageAssets/Changelog/CHANGELOG_main.md" +docuMind-output: ".SourceSageAssets/DOCUMIND/RELEASE_NOTES_v0.4.0.md" +docuMind-prompt-output: ".SourceSageAssets/DOCUMIND/_PROMPT_v0.4.0.md" +repo-name: "AIRA" +repo-version: "v0.4.0" + +# changelog-start-tag: "v0.1.0" +# changelog-end-tag: "v0.5.0" +# sourcesage --ss-mode=DocuMind --yaml-file=docs\.sourcesage_releasenotes.yml --ignore-file=".iris.SourceSageignore" diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..861b700 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,5 @@ +# 準備中 + +## sample note + +## sample note 2 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0f5aa1e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +litellm +PyGithub +google-generativeai +loguru +pydantic-settings diff --git a/setup.py b/setup.py index 383e2fb..52696c5 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,10 @@ def get_version(): 'art', 'loguru', 'tqdm', + 'harmon_ai', + 'gaiah_toolkit', + 'litellm', + 'google-generativeai', ], entry_points={ 'console_scripts': [