Commit 3c51ab07 authored by Mahmoud Aglan's avatar Mahmoud Aglan

lol

parent 1cd1b7d6
...@@ -42,14 +42,13 @@ def _run_migrations(): ...@@ -42,14 +42,13 @@ def _run_migrations():
conn.execute(text("ALTER TABLE chats ADD COLUMN reasoning_budget INTEGER DEFAULT 0")) conn.execute(text("ALTER TABLE chats ADD COLUMN reasoning_budget INTEGER DEFAULT 0"))
if "linked_repo_id" not in columns: if "linked_repo_id" not in columns:
conn.execute(text("ALTER TABLE chats ADD COLUMN linked_repo_id VARCHAR(36)")) conn.execute(text("ALTER TABLE chats ADD COLUMN linked_repo_id VARCHAR(36)"))
if "linked_repo_branch" not in columns:
conn.execute(text("ALTER TABLE chats ADD COLUMN linked_repo_branch VARCHAR(100)"))
conn.commit() conn.commit()
if "chat_attachments" not in existing_tables: if "chat_attachments" not in existing_tables:
from backend.models import ChatAttachment from backend.models import ChatAttachment
ChatAttachment.__table__.create(bind=engine, checkfirst=True) ChatAttachment.__table__.create(bind=engine, checkfirst=True)
# Create user_permissions table if missing
if "user_permissions" not in existing_tables: if "user_permissions" not in existing_tables:
from backend.models import UserPermissions from backend.models import UserPermissions
UserPermissions.__table__.create(bind=engine, checkfirst=True) UserPermissions.__table__.create(bind=engine, checkfirst=True)
......
...@@ -54,6 +54,7 @@ class UserPermissions(Base): ...@@ -54,6 +54,7 @@ class UserPermissions(Base):
unique=True, nullable=False, index=True, unique=True, nullable=False, index=True,
) )
# Feature access
can_use_web_search = Column(Boolean, default=False) can_use_web_search = Column(Boolean, default=False)
can_use_ui_design = Column(Boolean, default=False) can_use_ui_design = Column(Boolean, default=False)
can_use_knowledge_base = Column(Boolean, default=True) can_use_knowledge_base = Column(Boolean, default=True)
...@@ -62,8 +63,10 @@ class UserPermissions(Base): ...@@ -62,8 +63,10 @@ class UserPermissions(Base):
can_export_pptx = Column(Boolean, default=True) can_export_pptx = Column(Boolean, default=True)
can_export_docx = Column(Boolean, default=True) can_export_docx = Column(Boolean, default=True)
# Model access — "all" or comma-separated model IDs
allowed_models = Column(Text, default="eu.anthropic.claude-haiku-4-5-20251001-v1:0") allowed_models = Column(Text, default="eu.anthropic.claude-haiku-4-5-20251001-v1:0")
# Limits (0 = unlimited for count-based limits)
max_tokens_cap = Column(Integer, default=4096) max_tokens_cap = Column(Integer, default=4096)
max_reasoning_budget = Column(Integer, default=0) max_reasoning_budget = Column(Integer, default=0)
max_chats = Column(Integer, default=50) max_chats = Column(Integer, default=50)
...@@ -87,7 +90,6 @@ class Chat(Base): ...@@ -87,7 +90,6 @@ class Chat(Base):
model = Column(String(100), default="eu.anthropic.claude-opus-4-6-v1") model = Column(String(100), default="eu.anthropic.claude-opus-4-6-v1")
knowledge_base_id = Column(String(36), nullable=True) knowledge_base_id = Column(String(36), nullable=True)
linked_repo_id = Column(String(36), nullable=True) linked_repo_id = Column(String(36), nullable=True)
linked_repo_branch = Column(String(100), nullable=True)
max_tokens = Column(Integer, default=4096) max_tokens = Column(Integer, default=4096)
reasoning_budget = Column(Integer, default=0) reasoning_budget = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow) created_at = Column(DateTime, default=datetime.utcnow)
......
...@@ -28,7 +28,6 @@ class CreateChatBody(BaseModel): ...@@ -28,7 +28,6 @@ class CreateChatBody(BaseModel):
model: str = "eu.anthropic.claude-opus-4-6-v1" model: str = "eu.anthropic.claude-opus-4-6-v1"
knowledge_base_id: Optional[str] = None knowledge_base_id: Optional[str] = None
linked_repo_id: Optional[str] = None linked_repo_id: Optional[str] = None
linked_repo_branch: Optional[str] = None
max_tokens: int = 4096 max_tokens: int = 4096
reasoning_budget: int = 0 reasoning_budget: int = 0
...@@ -40,7 +39,6 @@ class UpdateChatBody(BaseModel): ...@@ -40,7 +39,6 @@ class UpdateChatBody(BaseModel):
reasoning_budget: Optional[int] = None reasoning_budget: Optional[int] = None
knowledge_base_id: Optional[str] = None knowledge_base_id: Optional[str] = None
linked_repo_id: Optional[str] = None linked_repo_id: Optional[str] = None
linked_repo_branch: Optional[str] = None
class SendMessageBody(BaseModel): class SendMessageBody(BaseModel):
...@@ -87,7 +85,6 @@ def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db ...@@ -87,7 +85,6 @@ def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db
model=check_model_allowed(user.id, body.model, db), model=check_model_allowed(user.id, body.model, db),
knowledge_base_id=body.knowledge_base_id or None, knowledge_base_id=body.knowledge_base_id or None,
linked_repo_id=body.linked_repo_id or None, linked_repo_id=body.linked_repo_id or None,
linked_repo_branch=body.linked_repo_branch or None,
max_tokens=min(body.max_tokens, perms.get("max_tokens_cap", 4096)), max_tokens=min(body.max_tokens, perms.get("max_tokens_cap", 4096)),
reasoning_budget=min(body.reasoning_budget, perms.get("max_reasoning_budget", 0)), reasoning_budget=min(body.reasoning_budget, perms.get("max_reasoning_budget", 0)),
) )
...@@ -119,8 +116,6 @@ def update_chat(chat_id: str, body: UpdateChatBody, user: User = Depends(get_cur ...@@ -119,8 +116,6 @@ def update_chat(chat_id: str, body: UpdateChatBody, user: User = Depends(get_cur
if body.linked_repo_id and not perms.get("can_use_gitlab"): if body.linked_repo_id and not perms.get("can_use_gitlab"):
raise HTTPException(403, "GitLab not enabled.") raise HTTPException(403, "GitLab not enabled.")
chat.linked_repo_id = body.linked_repo_id or None chat.linked_repo_id = body.linked_repo_id or None
if body.linked_repo_branch is not None:
chat.linked_repo_branch = body.linked_repo_branch or None
db.commit() db.commit()
return _chat_dict(chat, db) return _chat_dict(chat, db)
...@@ -210,6 +205,11 @@ async def commit_from_chat( ...@@ -210,6 +205,11 @@ async def commit_from_chat(
user: User = Depends(get_current_user), user: User = Depends(get_current_user),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
"""
Commit files from chat to linked GitLab repo.
Auto-detects whether each file should be 'create' or 'update'
by checking the repo tree, so it never fails on wrong action type.
"""
check_feature(user.id, "use_gitlab", db) check_feature(user.id, "use_gitlab", db)
chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first() chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
...@@ -226,44 +226,73 @@ async def commit_from_chat( ...@@ -226,44 +226,73 @@ async def commit_from_chat(
if not settings or not settings.is_active: if not settings or not settings.is_active:
raise HTTPException(400, "GitLab not configured") raise HTTPException(400, "GitLab not configured")
# ── Fetch repo tree to know which files already exist ──
existing_paths = set() existing_paths = set()
try: try:
tree = await gitlab_service.get_tree( tree = await gitlab_service.get_tree(
settings.gitlab_url, settings.gitlab_token, settings.gitlab_url,
repo.gitlab_project_id, ref=body.branch, recursive=True, settings.gitlab_token,
repo.gitlab_project_id,
ref=body.branch,
recursive=True,
) )
existing_paths = {item["path"] for item in tree if item["type"] == "blob"} existing_paths = {
item["path"] for item in tree if item["type"] == "blob"
}
except Exception: except Exception:
# If tree fetch fails (empty repo, network issue, etc.),
# we'll try all as "create" since we can't know what exists
pass pass
# ── Build actions with auto-detected create/update ──
actions = [] actions = []
for f in body.files: for f in body.files:
file_path = f.get("file_path", "") file_path = f.get("file_path", "")
content = f.get("content", "") content = f.get("content", "")
requested_action = f.get("action", "auto") requested_action = f.get("action", "auto")
if not file_path or not content: if not file_path or not content:
continue continue
file_exists = file_path in existing_paths file_exists = file_path in existing_paths
# Smart action resolution
if requested_action in ("auto", "upsert"): if requested_action in ("auto", "upsert"):
# Auto-detect: update if exists, create if not
actual_action = "update" if file_exists else "create" actual_action = "update" if file_exists else "create"
elif requested_action == "update" and not file_exists: elif requested_action == "update" and not file_exists:
# User said update but file doesn't exist → create instead
actual_action = "create" actual_action = "create"
elif requested_action == "create" and file_exists: elif requested_action == "create" and file_exists:
# User said create but file already exists → update instead
actual_action = "update" actual_action = "update"
else: else:
actual_action = requested_action actual_action = requested_action
actions.append({"action": actual_action, "file_path": file_path, "content": content})
actions.append({
"action": actual_action,
"file_path": file_path,
"content": content,
})
if not actions: if not actions:
raise HTTPException(400, "No valid files to commit") raise HTTPException(400, "No valid files to commit")
try: try:
result = await gitlab_service.commit_files( result = await gitlab_service.commit_files(
settings.gitlab_url, settings.gitlab_token, settings.gitlab_url,
repo.gitlab_project_id, body.branch, body.commit_message, actions, settings.gitlab_token,
repo.gitlab_project_id,
body.branch,
body.commit_message,
actions,
) )
gen_manager.invalidate_repo_cache(repo.id) gen_manager.invalidate_repo_cache(repo.id)
return {"ok": True, "commit": result, "files_committed": len(actions)} return {
"ok": True,
"commit": result,
"files_committed": len(actions),
}
except gitlab_service.GitLabError as e: except gitlab_service.GitLabError as e:
raise HTTPException(e.status_code, f"Commit failed: {e.detail}") raise HTTPException(e.status_code, f"Commit failed: {e.detail}")
...@@ -285,7 +314,6 @@ def _chat_dict(c, db=None): ...@@ -285,7 +314,6 @@ def _chat_dict(c, db=None):
"id": c.id, "title": c.title, "model": c.model, "id": c.id, "title": c.title, "model": c.model,
"knowledge_base_id": c.knowledge_base_id, "knowledge_base_id": c.knowledge_base_id,
"linked_repo_id": c.linked_repo_id, "linked_repo_id": c.linked_repo_id,
"linked_repo_branch": getattr(c, "linked_repo_branch", None) or None,
"max_tokens": c.max_tokens or 4096, "max_tokens": c.max_tokens or 4096,
"reasoning_budget": c.reasoning_budget or 0, "reasoning_budget": c.reasoning_budget or 0,
"created_at": str(c.created_at), "created_at": str(c.created_at),
......
""" """
GitLab CE integration routes — superadmin + user-facing. GitLab CE integration routes — superadmin only.
Son of Anton v4.2.0 Son of Anton v4.2.0
""" """
...@@ -13,9 +13,9 @@ from fastapi import APIRouter, Depends, HTTPException, Query ...@@ -13,9 +13,9 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from backend.database import get_db from backend.database import get_db
from backend.models import User, GitLabSettings, LinkedRepo, PendingAction, KnowledgeBase, KnowledgeDocument from backend.models import User, GitLabSettings, LinkedRepo, PendingAction
from backend.auth import require_superadmin, get_current_user, check_feature from backend.auth import require_superadmin
from backend.services import gitlab_service, code_analyzer, rag_service from backend.services import gitlab_service, code_analyzer
router = APIRouter() router = APIRouter()
...@@ -64,10 +64,6 @@ class ActionBody(BaseModel): ...@@ -64,10 +64,6 @@ class ActionBody(BaseModel):
title: str = "" title: str = ""
payload: str payload: str
class CreateKBFromRepoBody(BaseModel):
branch: Optional[str] = None
name: Optional[str] = None
def _get_settings(db: Session) -> GitLabSettings: def _get_settings(db: Session) -> GitLabSettings:
s = db.query(GitLabSettings).first() s = db.query(GitLabSettings).first()
...@@ -350,10 +346,13 @@ async def create_branch(repo_id: str, body: BranchBody, admin: User = Depends(re ...@@ -350,10 +346,13 @@ async def create_branch(repo_id: str, body: BranchBody, admin: User = Depends(re
@router.post("/repos/{repo_id}/commit") @router.post("/repos/{repo_id}/commit")
async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(require_superadmin), db: Session = Depends(get_db)): async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(require_superadmin), db: Session = Depends(get_db)):
"""Commit multiple files. Auto-detects create vs update per file.""" """
Commit multiple files. Auto-detects create vs update per file.
"""
s = _get_settings(db) s = _get_settings(db)
repo = _get_repo(repo_id, db) repo = _get_repo(repo_id, db)
# Fetch tree to know which files exist
existing_paths = set() existing_paths = set()
try: try:
tree = await gitlab_service.get_tree( tree = await gitlab_service.get_tree(
...@@ -369,9 +368,12 @@ async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(requ ...@@ -369,9 +368,12 @@ async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(requ
file_path = a.get("file_path", "") file_path = a.get("file_path", "")
content = a.get("content", "") content = a.get("content", "")
requested = a.get("action", "auto") requested = a.get("action", "auto")
if not file_path: if not file_path:
continue continue
file_exists = file_path in existing_paths file_exists = file_path in existing_paths
if requested in ("auto", "upsert"): if requested in ("auto", "upsert"):
actual = "update" if file_exists else "create" actual = "update" if file_exists else "create"
elif requested == "update" and not file_exists: elif requested == "update" and not file_exists:
...@@ -380,7 +382,12 @@ async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(requ ...@@ -380,7 +382,12 @@ async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(requ
actual = "update" actual = "update"
else: else:
actual = requested actual = requested
resolved_actions.append({"action": actual, "file_path": file_path, "content": content})
resolved_actions.append({
"action": actual,
"file_path": file_path,
"content": content,
})
if not resolved_actions: if not resolved_actions:
raise HTTPException(400, "No valid files to commit") raise HTTPException(400, "No valid files to commit")
...@@ -402,10 +409,13 @@ async def commit_single( ...@@ -402,10 +409,13 @@ async def commit_single(
admin: User = Depends(require_superadmin), admin: User = Depends(require_superadmin),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
"""Commit a single file. Auto-detects create vs update.""" """
Commit a single file. Auto-detects create vs update.
"""
s = _get_settings(db) s = _get_settings(db)
repo = _get_repo(repo_id, db) repo = _get_repo(repo_id, db)
# Auto-detect whether file exists
action = body.action action = body.action
if action in ("update", "create", "auto"): if action in ("update", "create", "auto"):
try: try:
...@@ -537,153 +547,6 @@ def reject_action(action_id: str, admin: User = Depends(require_superadmin), db: ...@@ -537,153 +547,6 @@ def reject_action(action_id: str, admin: User = Depends(require_superadmin), db:
return {"ok": True} return {"ok": True}
# ═══════════════════════════════════════════════════
# USER-FACING ENDPOINTS (permission-gated, not superadmin-only)
# ═══════════════════════════════════════════════════
@router.get("/user/repos")
def user_list_repos(user: User = Depends(get_current_user), db: Session = Depends(get_db)):
"""List linked repos — available to any user with can_use_gitlab."""
check_feature(user.id, "use_gitlab", db)
repos = db.query(LinkedRepo).order_by(LinkedRepo.created_at.desc()).all()
return [_repo_dict(r) for r in repos]
@router.get("/user/repos/{repo_id}/branches")
async def user_list_branches(repo_id: str, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
"""List branches for a linked repo — available to any user with can_use_gitlab."""
check_feature(user.id, "use_gitlab", db)
s = _get_settings(db)
repo = _get_repo(repo_id, db)
try:
branches = await gitlab_service.list_branches(s.gitlab_url, s.gitlab_token, repo.gitlab_project_id)
return branches
except gitlab_service.GitLabError as e:
raise HTTPException(e.status_code, e.detail)
@router.post("/user/repos/{repo_id}/create-kb")
async def user_create_kb_from_repo(
repo_id: str,
body: CreateKBFromRepoBody,
user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
"""Create a Knowledge Base from all code files in a repo. Runs in background."""
check_feature(user.id, "use_gitlab", db)
check_feature(user.id, "use_knowledge_base", db)
repo = _get_repo(repo_id, db)
s = _get_settings(db)
branch = body.branch or repo.default_branch
kb_name = body.name or f"Repo: {repo.name} ({branch})"
kb = KnowledgeBase(
user_id=user.id,
name=kb_name,
description=f"Auto-generated from {repo.path_with_namespace} branch:{branch} — processing...",
)
db.add(kb)
db.commit()
db.refresh(kb)
rag_service.create_collection(kb.id)
asyncio.create_task(_kb_from_repo_background(
kb.id, s.gitlab_url, s.gitlab_token,
repo.gitlab_project_id, branch, repo.name, repo.path_with_namespace,
))
return {"kb_id": kb.id, "name": kb_name, "status": "processing"}
async def _kb_from_repo_background(kb_id, gitlab_url, gitlab_token, project_id, branch, repo_name, repo_path):
"""Background task: load every code file from repo into a KB."""
from backend.database import SessionLocal as BgSession
db = BgSession()
try:
result = await gitlab_service.load_project_files(
gitlab_url, gitlab_token, project_id, ref=branch,
)
files = result.get("files", [])
if not files:
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
if kb:
kb.description = f"From {repo_path} ({branch}) — no files found"
db.commit()
return
total_docs = 0
total_chunks = 0
total_chars = 0
for f in files:
content = f.get("content", "")
path = f.get("path", "unknown")
if not content or content.startswith("["):
continue
chunks = _chunk_for_kb(content, chunk_size=3000, overlap=300)
if not chunks:
continue
rag_service.add_documents(
collection_id=kb_id,
documents=chunks,
metadatas=[{"filename": path, "chunk_index": i} for i in range(len(chunks))],
)
doc = KnowledgeDocument(
knowledge_base_id=kb_id,
filename=path,
file_size=len(content),
chunk_count=len(chunks),
)
db.add(doc)
total_docs += 1
total_chunks += len(chunks)
total_chars += len(content)
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
if kb:
kb.document_count = total_docs
kb.chunk_count = total_chunks
kb.total_characters = total_chars
kb.description = f"From {repo_path} ({branch}) — {total_docs} files, {total_chunks} chunks"
db.commit()
print(f" ✅ KB from repo done: {repo_name} — {total_docs} files, {total_chunks} chunks, {total_chars:,} chars")
except Exception as e:
print(f" ❌ KB from repo failed for {repo_name}: {e}")
try:
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
if kb:
kb.description = f"From {repo_path} ({branch}) — ERROR: {str(e)[:200]}"
db.commit()
except Exception:
pass
finally:
db.close()
def _chunk_for_kb(text, chunk_size=3000, overlap=300):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
if end < len(text):
for sep in ["\n\n", "\n", ". ", " "]:
pos = text.rfind(sep, start + chunk_size // 2, end)
if pos > start:
end = pos + len(sep)
break
chunk = text[start:end].strip()
if chunk:
chunks.append(chunk)
start = end - overlap if end < len(text) else end
return chunks
# ═══════════════════════════════════════════════════ # ═══════════════════════════════════════════════════
# Helpers # Helpers
# ═══════════════════════════════════════════════════ # ═══════════════════════════════════════════════════
......
""" """
Background generation manager — v4.2.0 with web search + branch-aware repo context. Background generation manager — v4.1.0 with web search.
""" """
import asyncio import asyncio
...@@ -47,9 +47,6 @@ class GenerationManager: ...@@ -47,9 +47,6 @@ class GenerationManager:
state = self._active.get(chat_id) state = self._active.get(chat_id)
return state is not None and not state.done.is_set() return state is not None and not state.done.is_set()
def get_state(self, chat_id: str) -> Optional[GenerationState]:
return self._active.get(chat_id)
def start(self, chat_id, user_id, content, model, max_tokens, reasoning_budget, knowledge_base_id, attachment_ids, web_search=False): def start(self, chat_id, user_id, content, model, max_tokens, reasoning_budget, knowledge_base_id, attachment_ids, web_search=False):
old = self._active.get(chat_id) old = self._active.get(chat_id)
if old and not old.done.is_set(): if old and not old.done.is_set():
...@@ -82,27 +79,25 @@ class GenerationManager: ...@@ -82,27 +79,25 @@ class GenerationManager:
if not repo: return None if not repo: return None
settings = db.query(GitLabSettings).first() settings = db.query(GitLabSettings).first()
if not settings or not settings.is_active: return None if not settings or not settings.is_active: return None
branch = getattr(chat, 'linked_repo_branch', None) or repo.default_branch
try: try:
tree = _get_tree_cache(repo.id, branch) tree = _get_tree_cache(repo.id, repo.default_branch)
if tree is None: if tree is None:
tree = await gitlab_service.get_tree(settings.gitlab_url, settings.gitlab_token, repo.gitlab_project_id, ref=branch, recursive=True) tree = await gitlab_service.get_tree(settings.gitlab_url, settings.gitlab_token, repo.gitlab_project_id, ref=repo.default_branch, recursive=True)
_set_tree_cache(repo.id, branch, tree) _set_tree_cache(repo.id, repo.default_branch, tree)
prev = _chat_file_history.get(chat.id, set()) prev = _chat_file_history.get(chat.id, set())
result = await gitlab_service.load_smart_files(settings.gitlab_url, settings.gitlab_token, repo.gitlab_project_id, ref=branch, tree=tree, user_query=user_query, previous_files=prev) result = await gitlab_service.load_smart_files(settings.gitlab_url, settings.gitlab_token, repo.gitlab_project_id, ref=repo.default_branch, tree=tree, user_query=user_query, previous_files=prev)
loaded = set() loaded = set()
for f in result["priority_files"]: loaded.add(f["path"]) for f in result["priority_files"]: loaded.add(f["path"])
for f in result["query_files"]: loaded.add(f["path"]) for f in result["query_files"]: loaded.add(f["path"])
if chat.id not in _chat_file_history: _chat_file_history[chat.id] = set() if chat.id not in _chat_file_history: _chat_file_history[chat.id] = set()
_chat_file_history[chat.id].update(loaded) _chat_file_history[chat.id].update(loaded)
return self._format_smart_context(result, tree, repo, db, branch) return self._format_smart_context(result, tree, repo, db)
except Exception as e: except Exception as e:
return f"[Repository: {repo.name} — error: {str(e)[:200]}]" return f"[Repository: {repo.name} — error: {str(e)[:200]}]"
def _format_smart_context(self, result, tree, repo, db, branch=None): def _format_smart_context(self, result, tree, repo, db):
files_in_tree = sorted([i["path"] for i in tree if i["type"] == "blob"]) files_in_tree = sorted([i["path"] for i in tree if i["type"] == "blob"])
effective_branch = branch or repo.default_branch lines = [f"Repository: {repo.name}", f"Branch: {repo.default_branch}", f"Files loaded: {result['files_loaded']}", f"Characters: {result['total_characters']:,}"]
lines = [f"Repository: {repo.name}", f"Branch: {effective_branch}", f"Files loaded: {result['files_loaded']}", f"Characters: {result['total_characters']:,}"]
if repo.architecture_map and repo.map_status == "ready": if repo.architecture_map and repo.map_status == "ready":
lines.append(""); lines.append(repo.architecture_map); lines.append("") lines.append(""); lines.append(repo.architecture_map); lines.append("")
lines.append("═" * 50); lines.append("FILE TREE:"); lines.append("═" * 50) lines.append("═" * 50); lines.append("FILE TREE:"); lines.append("═" * 50)
......
{ {
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": { "dependencies": {
"lucide-react": "^0.469.0", "lucide-react": "^0.469.0",
"react": "^18.3.1", "react": "^18.3.1",
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment