|
import streamlit as st |
|
import tempfile |
|
import git |
|
import os |
|
from pathlib import Path |
|
from datetime import datetime |
|
from config.settings import Settings |
|
from core.file_scanner import FileScanner, FileInfo |
|
from services.llm_service import LLMService |
|
from typing import List, Set |
|
from main import DirectoryStructureScanner, MarkdownGenerator |
|
|
|
class StreamlitFileWriter: |
|
def __init__(self, output_file: Path): |
|
self.output_file = output_file |
|
|
|
def create_markdown(self, files: List[FileInfo]) -> str: |
|
content = [] |
|
for file_info in files: |
|
content.append(f"## {file_info.path}") |
|
content.append("------------") |
|
if file_info.content is not None: |
|
content.append(file_info.content) |
|
else: |
|
content.append("# Failed to read content") |
|
content.append("\n") |
|
return "\n".join(content) |
|
|
|
st.set_page_config( |
|
page_title="Repository Code Analysis", |
|
page_icon="🔍", |
|
layout="wide" |
|
) |
|
|
|
st.markdown(""" |
|
<style> |
|
.stApp { |
|
background-color: #0e1117; |
|
color: #ffffff; |
|
} |
|
.chat-message { |
|
padding: 1rem; |
|
margin: 1rem 0; |
|
border-radius: 0.5rem; |
|
} |
|
.assistant-message { |
|
background-color: #1e2329; |
|
color: #ffffff; |
|
} |
|
.directory-structure { |
|
font-family: monospace; |
|
white-space: pre; |
|
background-color: #1e2329; |
|
padding: 1rem; |
|
border-radius: 0.5rem; |
|
margin: 1rem 0; |
|
} |
|
.markdown-preview { |
|
background-color: #1e2329; |
|
padding: 1rem; |
|
border-radius: 0.5rem; |
|
margin: 1rem 0; |
|
max-height: 500px; |
|
overflow-y: auto; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if 'repo_content' not in st.session_state: |
|
st.session_state.repo_content = None |
|
if 'directory_structure' not in st.session_state: |
|
st.session_state.directory_structure = None |
|
if 'content_md' not in st.session_state: |
|
st.session_state.content_md = None |
|
if 'structure_md' not in st.session_state: |
|
st.session_state.structure_md = None |
|
if 'temp_dir' not in st.session_state: |
|
st.session_state.temp_dir = None |
|
if 'llm_service' not in st.session_state: |
|
try: |
|
api_key = os.getenv("ANTHROPIC_API_KEY") |
|
if not api_key: |
|
st.error("ANTHROPIC_API_KEY環境変数が設定されていません") |
|
st.stop() |
|
st.session_state.llm_service = LLMService(api_key) |
|
except Exception as e: |
|
st.error(str(e)) |
|
st.stop() |
|
|
|
st.title("🔍 リポジトリ解析・質問システム") |
|
|
|
with st.sidebar: |
|
st.subheader("📌 使い方") |
|
st.markdown(""" |
|
1. GitHubリポジトリのURLを入力 |
|
2. スキャンを実行 |
|
3. ディレクトリ構造と内容を確認 |
|
4. Markdownファイルをダウンロード |
|
5. コードについて質問 |
|
""") |
|
|
|
repo_url = st.text_input( |
|
"GitHubリポジトリのURLを入力", |
|
placeholder="https://github.com/username/repository.git" |
|
) |
|
|
|
if st.button("スキャン開始", disabled=not repo_url): |
|
try: |
|
with st.spinner('リポジトリを解析中...'): |
|
generator = MarkdownGenerator(repo_url) |
|
content_md, structure_md, files = generator.generate_markdowns() |
|
|
|
writer = StreamlitFileWriter(Path("dummy")) |
|
formatted_content = writer.create_markdown(files) |
|
|
|
st.session_state.content_md = formatted_content |
|
st.session_state.structure_md = structure_md |
|
st.session_state.repo_content = LLMService.format_code_content(files) |
|
|
|
st.success(f"スキャン完了: {len(files)}個のファイルを検出") |
|
|
|
st.session_state.llm_service.clear_history() |
|
|
|
except Exception as e: |
|
st.error(f"エラーが発生しました: {str(e)}") |
|
|
|
if st.session_state.structure_md and st.session_state.content_md: |
|
tab1, tab2 = st.tabs(["📁 ディレクトリ構造", "📄 ファイル内容"]) |
|
|
|
with tab1: |
|
st.markdown(f'<div class="markdown-preview">{st.session_state.structure_md}</div>', |
|
unsafe_allow_html=True) |
|
|
|
st.download_button( |
|
label="ディレクトリ構造をダウンロード", |
|
data=st.session_state.structure_md, |
|
file_name=f"directory_structure_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md", |
|
mime="text/markdown" |
|
) |
|
|
|
with tab2: |
|
st.markdown(f'<div class="markdown-preview">{st.session_state.content_md}</div>', |
|
unsafe_allow_html=True) |
|
|
|
st.download_button( |
|
label="ファイル内容をダウンロード", |
|
data=st.session_state.content_md, |
|
file_name=f"repository_content_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md", |
|
mime="text/markdown" |
|
) |
|
|
|
if st.session_state.repo_content: |
|
st.divider() |
|
st.subheader("💭 コードについて質問する") |
|
|
|
for message in st.session_state.llm_service.conversation_history: |
|
if message.role == "assistant": |
|
st.markdown(f'<div class="chat-message assistant-message">{message.content}</div>', |
|
unsafe_allow_html=True) |
|
|
|
query = st.text_area( |
|
"質問を入力してください", |
|
placeholder="例: このコードの主な機能は何ですか?" |
|
) |
|
|
|
col1, col2 = st.columns([1, 5]) |
|
with col1: |
|
if st.button("履歴クリア"): |
|
st.session_state.llm_service.clear_history() |
|
st.rerun() |
|
|
|
with col2: |
|
if st.button("質問する", disabled=not query): |
|
with st.spinner('回答を生成中...'): |
|
response, error = st.session_state.llm_service.get_response( |
|
st.session_state.repo_content, |
|
query |
|
) |
|
|
|
if error: |
|
st.error(error) |
|
else: |
|
st.rerun() |
|
|