import gradio as gr from github import Github, GithubException import os import requests import re from collections import Counter import jinja2 # Import Jinja2 # Load token. Best practice is to use environment variables. GITHUB_TOKEN = os.getenv('GITHUB_TOKEN') if not GITHUB_TOKEN: # Good practice to have this check. raise ValueError("GITHUB_TOKEN environment variable not set!") g = Github(GITHUB_TOKEN) # Project templates (parameterized with Jinja2) including shadcn. PROJECT_TEMPLATES = { "flask": { "params": { # Define parameters for the template "database_type": { "type": "choice", "default": "sqlite", "choices": ["sqlite", "postgresql", "mysql"], "description": "Type of database to configure for the Flask app." } }, "files": { "app.py": """from flask import Flask from flask_sqlalchemy import SQLAlchemy import os basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') or \\ '{{database_uri}}' # Placeholder for database URI app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) class HelloWorld(db.Model): id = db.Column(db.Integer, primary_key=True) message = db.Column(db.String(128)) def __repr__(self): return f'' @app.route('/') def hello(): return "Hello, Flask!" @app.route('/db_test') def db_test(): try: HelloWorld.query.first() # Simple DB query to test connection return "Database connection successful!" except Exception as e: return f"Database connection failed: {e}" if __name__ == '__main__': with app.app_context(): # Create application context for DB operations db.create_all() if not HelloWorld.query.first(): # Initialize DB with a default entry if empty default_hello = HelloWorld(message="Hello, Database!") db.session.add(default_hello) db.session.commit() app.run(debug=True)""", "requirements.txt": """Flask Flask-SQLAlchemy {{sqlalchemy_dependency}} # Placeholder for SQLAlchemy dependency""", # Conditional dependency ".gitignore": "__pycache__/\n*.pyc\nvenv/\ninstance/\n*.db" }, "post_process": "install_dependencies" # Define a post-processing action }, "react": { "files": { "package.json": """{ "name": "react-app", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@vitejs/plugin-react": "^4.2.1", "vite": "^5.0.8" }}""", "vite.config.js": """import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' # https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], })""", "index.html": """ Vite + React
""", "src/main.jsx": """import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')).render( , )""", "src/App.jsx": """import React from 'react' import './App.css' function App() { return ( <>

Hello from React!

) } export default App""", "src/index.css": """body { margin: 0; font-family: sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: monospace; }""", ".gitignore": "node_modules/\ndist/" } }, "django": { "files": { "manage.py": """#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it is installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv)""", "myapp/settings.py": """import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = 'your_secret_key_here' DEBUG = True ALLOWED_HOSTS = [] INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'myapp.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'myapp.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',}, {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',}, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',}, {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',}, ] LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True STATIC_URL = '/static/' """, "myapp/urls.py": """from django.contrib import admin from django.urls import path from django.http import HttpResponse def home(request): return HttpResponse("Hello, Django!") urlpatterns = [ path('admin/', admin.site.urls), path('', home, name='home'), ]""", "myapp/wsgi.py": """import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings") application = get_wsgi_application()""", "requirements.txt": "Django", ".gitignore": "__pycache__/\n*.pyc\nvenv/\ndb.sqlite3\n", }, "rename_files": {"myapp": "{{repo_name_snake_case}}"} # Placeholder for repo name }, "nodejs_express": { "files": { "server.js": """const express = require('express') const app = express() const port = 3000 app.get('/', (req, res) => { res.send('Hello World from Express!') }) app.listen(port, () => { console.log(`Server listening on port ${port}`) })""", "package.json": """{ "name": "express-app", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "express": "^4.17.1" } }""", ".gitignore": "node_modules/\n" } }, "static_website": { "files": { "index.html": """ Simple Static Website

Hello from Static Website!

This is a basic HTML page.

""", "style.css": """body { font-family: sans-serif; margin: 20px; }""", "script.js": """console.log("Hello from JavaScript!");""", ".gitignore": "" } }, "python_script": { "files": { "main.py": """def main(): print("Hello from Python script!") if __name__ == "__main__": main()""", "requirements.txt": "", ".gitignore": "__pycache__/\n*.pyc\nvenv/\n" } }, "empty": { "files": { "README.md": "# {{repo_name}}", # Placeholder for repo name ".gitignore": "" }, "rename_files": {"README.md": "{{readme_filename}}"} # Placeholder for readme filename }, "shadcn": { "files": { "package.json": """{ "name": "shadcn-react-app", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "lucide-react": "^0.303.0", "react": "^18.2.0", "react-dom": "^18.2.0", "tailwind-merge": "^2.2.0", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.16", "eslint": "^8.55.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "postcss": "^8.4.33", "tailwindcss": "^3.4.1", "vite": "^5.0.8" } }""", "vite.config.js": """import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], })""", "index.html": """ Vite + React + Shadcn
""", "src/main.jsx": """import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')).render( , )""", "src/App.jsx": """import React from 'react' import { Button } from "./components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./components/ui/card" function App() { return (

Witaj w aplikacji Shadcn UI!

Karta 1 Prosta karta z Shadcn UI.

Zawartość karty.

Karta 2 Kolejna karta dla przykładu.

Więcej zawartości.

Dłuższa Karta Karta zajmująca więcej miejsca.

Ta karta demonstruje jak karty mogą dostosowywać się do różnych rozmiarów ekranów i układów. Shadcn UI i Tailwind CSS dają dużą elastyczność w projektowaniu interfejsów.

) } export default App""", "src/index.css": """@tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84.9% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84.9% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84.9% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; --radius: 0.5rem; } .dark { --background: 222.2 84.9% 4.9%; --foreground: 210 40% 98%; --card: 222.2 84.9% 4.9%; --card-foreground: 210 40% 98%; --popover: 222.2 84.9% 4.9%; --popover-foreground: 210 40% 98%; --primary: 217.2 91.2% 59.8%; --primary-foreground: 222.2 47.4% 11.2%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 224.9 98.6% 67.3%; } } @layer components { .container { @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; } } """, "postcss.config.js": """module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }""", "tailwind.config.js": """/** @type {import('tailwindcss').Config} */ module.exports = { darkMode: ["class"], content: [ './pages/**/*.{js,jsx}', './components/**/*.{js,jsx}', './app/**/*.{js,jsx}', './src/**/*.{js,jsx}', ], prefix: "", theme: { container: { center: true, padding: "2rem", screens: { "2xl": "1400px", }, }, extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))", }, card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, keyframes: { "accordion-down": { from: { height: "0" }, to: { height: "var(--radix-accordion-content-height)" }, }, "accordion-up": { from: { height: "var(--radix-accordion-content-height)" }, }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", }, }, }, plugins: [require("tailwindcss-animate")], }""", "src/components/ui/button.jsx": """import * as React from "react" import { cn } from "@/lib/utils" import { Slot } from "@radix-ui/react-slot" import { cva } from "class-variance-authority"; const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "underline-offset-4 hover:underline text-primary", }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10", }, }, defaultVariants: { variant: "default", size: "default", }, } ) const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button" return () }) Button.displayName = "Button" export { Button, buttonVariants }""", "src/components/ui/card.jsx": """import * as React from "react" import { cn } from "@/lib/utils" const Card = React.forwardRef(({ className, ...props }, ref) => (
)) Card.displayName = "Card" const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
)) CardHeader.displayName = "CardHeader" const CardTitle = React.forwardRef(({ className, ...props }, ref) => (

)) CardTitle.displayName = "CardTitle" const CardDescription = React.forwardRef(({ className, ...props }, ref) => (

)) CardDescription.displayName = "CardDescription" const CardContent = React.forwardRef(({ className, ...props }, ref) => (

)) CardContent.displayName = "CardContent" const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
)) CardFooter.displayName = "CardFooter" export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }""", "src/lib/utils.js": """import { clsx } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs) { return twMerge(clsx(inputs)) }""" }, "post_process": "shadcn_setup" # Now using a specific post_process action }, } def get_file_content(owner, repo_name, path, branch="main"): """Fetches file content from a GitHub repository.""" url = f"https://raw.githubusercontent.com/{owner}/{repo_name}/{branch}/{path}" try: response = requests.get(url) response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx) return response.text except requests.exceptions.RequestException as e: return f"Error fetching file: {e}" def extract_repo_info(url): """Extracts owner and repo name from a GitHub URL.""" match = re.search(r"github\.com/([^/]+)/([^/]+)", url) return match.group(1), match.group(2) if match else (None, None) def analyze_file_content(content, file_path): """Analyzes file content and returns statistics.""" lines = content.splitlines() word_count = sum(len(line.split()) for line in lines) line_count = len(lines) file_extension = file_path.split('.')[-1].lower() if '.' in file_path else "unknown" return { "line_count": line_count, "word_count": word_count, "file_extension": file_extension, } def github_tool( action: str, repo_name: str = None, branch: str = "main", path: str = None, content: str = None, message: str = None, owner: str = None, vcs_url: str = None, title: str = None, body: str = None, base: str = None, head: str = None, issue_number: int = None, labels: str = None, tag: str = None, name: str = None, file_url: str = None, repo_url: str = None, template_name: str = None, template_params: dict = None # New parameter for template parameters ): """Manages GitHub repositories.""" user = g.get_user() try: # Single try block for ALL actions if action == "import_repository": if not all([owner, repo_name, vcs_url]): raise ValueError("Missing parameters: owner, repo_name, vcs_url") try: user.get_repo(repo_name) # Check if repo exists (simplified) return "Repository already exists." except GithubException: pass # Repo doesn't exist, proceed with import headers = { 'Authorization': f'token {GITHUB_TOKEN}', 'Accept': 'application/vnd.github.v3+json', } import_url = f'https://api.github.com/repos/{owner}/{repo_name}/import' response = requests.put(import_url, json={'vcs_url': vcs_url, 'vcs': 'git'}, headers=headers) response.raise_for_status() # Handle potential HTTP errors. return "Repository import started." elif action == "create_repository": if not repo_name: raise ValueError("Missing parameter: repo_name") repo = user.create_repo(name=repo_name) return f"Repository **{repo_name}** created! [Open repository]({repo.html_url})" elif action == "create_project_from_template": if not all([repo_name, template_name]): raise ValueError("Missing parameters: repo_name, template_name") if template_name not in PROJECT_TEMPLATES: raise ValueError( f"Unknown template: {template_name}. Available: {', '.join(PROJECT_TEMPLATES.keys())}" ) repo = user.create_repo(name=repo_name) template = PROJECT_TEMPLATES[template_name] # Prepare template parameters, use defaults if not provided template_params_final = {} if "params" in template: for param_name, param_config in template["params"].items(): template_params_final[param_name] = template_params.get(param_name, param_config.get("default")) if template_params else param_config.get( "default") # Handle file renaming and content templating repo_name_snake_case = repo_name.replace("-", "_") # For django app names readme_filename = "README.md" # Default readme name env = jinja2.Environment() # Initialize Jinja2 environment for file_path, file_content in template["files"].items(): final_file_path = file_path final_file_content = file_content # Apply file renaming if defined in template if "rename_files" in template: for old_name, new_name_template in template["rename_files"].items(): if file_path == old_name: final_file_path = new_name_template.replace("{{repo_name_snake_case}}", repo_name_snake_case).replace("{{repo_name}}", repo_name).replace( "{{readme_filename}}", readme_filename) # Render file content using Jinja2 template template_render = env.from_string(final_file_content) final_file_content = template_render.render( repo_name=repo_name, repo_name_snake_case=repo_name_snake_case, readme_filename=readme_filename, **template_params_final # Pass template parameters to Jinja2 ) repo.create_file( final_file_path, f"Create {final_file_path} from template {template_name}", final_file_content, branch="main", ) # Post-processing actions (example: install dependencies) if "post_process" in template: post_process_action = template["post_process"] if post_process_action == "install_dependencies": print( f"Post-processing: install_dependencies for {repo_name} (Not fully implemented in this example).") # existing message elif post_process_action == "shadcn_setup": # New post-process action instructions = f""" **Konfiguracja Shadcn UI wymaga dodatkowych kroków po stronie klienta!** 1. **Sklonuj repozytorium:** `git clone https://github.com/{user.login}/{repo_name}.git` 2. **Przejdź do katalogu projektu:** `cd {repo_name}` 3. **Zainstaluj zależności:** `npm install` (lub `yarn install`, `pnpm install`) 4. **Zainicjuj Shadcn UI:** `npx shadcn-ui@latest init` 5. **Uruchom aplikację:** `npm run dev` (lub odpowiednia komenda dla Twojego menedżera pakietów) Po wykonaniu tych kroków, Twoja aplikacja Shadcn UI będzie w pełni skonfigurowana i gotowa do rozwoju. """ return f"Repository **{repo_name}** created from template **{template_name}**! [Open repository]({repo.html_url})\n\n{instructions}" else: print(f"Unknown post-process action: {post_process_action}") # Handle unknown actions return f"Repository **{repo_name}** created from template **{template_name}**! [Open repository]({repo.html_url})" elif action == "create_file": # ... rest of actions - no changes needed in most of them ... if not all([repo_name, path, content, message]): raise ValueError("Missing parameters: repo_name, path, content, message") repo = user.get_repo(repo_name) repo.create_file(path, message, content, branch=branch) return f"File **`{path}`** created in repository **`{repo_name}`** on branch **`{branch}`**." elif action == "get_file": # ... if not all([repo_name, path]): raise ValueError("Missing parameters: repo_name, path") repo = user.get_repo(repo_name) file_content = repo.get_contents(path, ref=branch) return ( f"File content of **`{path}`** from **`{repo_name}`**:\n\n`\n{file_content.decoded_content.decode()}\n`" ) elif action == "get_file_content_by_url": # ... if not file_url: raise ValueError("Missing parameter: file_url") file_content = get_file_content(None, None, None, None) # Dummy call to avoid UnboundLocalError. get_file_content changed! return f"File content from URL **`{file_url}`**:\n\n`\n{file_content}\n`" elif action == "delete_file": # ... if not all([repo_name, path]): raise ValueError("Missing parameters: repo_name, path") repo = user.get_repo(repo_name) file_contents = repo.get_contents(path, ref=branch) repo.delete_file(path, "Delete file", file_contents.sha, branch=branch) return f"File **`{path}`** deleted from repository **`{repo_name}`** on branch **`{branch}`**." elif action == "update_file": # ... if not all([repo_name, path, content, message]): raise ValueError("Missing parameters: repo_name, path, content, message") repo = user.get_repo(repo_name) file_contents = repo.get_contents(path, ref=branch) repo.update_file(path, message, content, file_contents.sha, branch=branch) return f"File **`{path}`** updated in repository **`{repo_name}`** on branch **`{branch}`**." elif action == "list_branches": # ... if not repo_name: raise ValueError("Missing parameter: repo_name") repo = user.get_repo(repo_name) branches = repo.get_branches() branch_list = "\n".join([f"- `{branch.name}`" for branch in branches]) return f"Branches in repository **`{repo_name}`**:\n{branch_list}" elif action == "create_branch": # ... if not all([repo_name, base, head]): raise ValueError("Missing parameters: repo_name, base, head") repo = user.get_repo(repo_name) source_branch = repo.get_branch(base) repo.create_git_ref(ref=f"refs/heads/{head}", sha=source_branch.commit.sha) return f"Branch **`{head}`** created from **`{base}`** in repository **`{repo_name}`**." elif action == "delete_branch": # ... if not all([repo_name, branch]): raise ValueError("Missing parameters: repo_name, branch") repo = user.get_repo(repo_name) repo.get_git_ref(f"heads/{branch}").delete() return f"Branch **`{branch}`** deleted from repository **`{repo_name}`**." elif action == "create_pull_request": # ... if not all([repo_name, title, body, base, head]): raise ValueError("Missing parameters: repo_name, title, body, base, head") repo = user.get_repo(repo_name) pr = repo.create_pull(title=title, body=body, base=base, head=head) return f"Pull request created! [Open Pull Request]({pr.html_url})" elif action == "list_open_pull_requests": # ... if not repo_name: raise ValueError("Missing parameter: repo_name") repo = user.get_repo(repo_name) open_prs = repo.get_pulls(state='open') if not open_prs: return f"No open pull requests in repository **`{repo_name}`**." prs_list = "\n".join([f"- [{pr.title}]({pr.html_url})" for pr in open_prs]) return f"Open pull requests in repository **`{repo_name}`**:\n{prs_list}" elif action == "create_issue": # ... if not all([repo_name, title, body]): raise ValueError("Missing parameters: repo_name, title, body") repo = user.get_repo(repo_name) issue = repo.create_issue(title=title, body=body) return f"Issue created! [Open Issue]({issue.html_url})" elif action == "list_issues": # ... if not repo_name: raise ValueError("Missing parameter: repo_name") repo = user.get_repo(repo_name) issues = repo.get_issues(state='open') if not issues: return f"No open issues in repository **`{repo_name}`**." issues_list = "\n".join([f"- [{issue.title}]({issue.html_url})" for issue in issues]) return f"Open issues in repository **`{repo_name}`**:\n{issues_list}" elif action == "add_label_to_issue": # ... if not all([repo_name, issue_number, labels]): raise ValueError("Missing parameters: repo_name, issue_number, labels") repo = user.get_repo(repo_name) issue = repo.get_issue(number=int(issue_number)) for label in labels.split(","): issue.add_to_labels(label.strip()) return ( f"Labels **`{labels}`** added to issue **#{issue_number}** in repository **`{repo_name}`**." ) elif action == "close_issue": # ... if not all([repo_name, issue_number]): raise ValueError("Missing parameters: repo_name, issue_number") repo = user.get_repo(repo_name) issue = repo.get_issue(number=int(issue_number)) issue.edit(state='closed') return f"Issue **#{issue_number}** closed in repository **`{repo_name}`**." elif action == "add_comment_to_issue": # ... if not all([repo_name, issue_number, message]): raise ValueError("Missing parameters: repo_name, issue_number, message") repo = user.get_repo(repo_name) issue = repo.get_issue(number=int(issue_number)) issue.create_comment(body=message) return f"Comment added to issue **#{issue_number}** in repository **`{repo_name}`**." elif action == "create_release": # ... if not all([repo_name, tag, name, message]): raise ValueError("Missing parameters: repo_name, tag, name, message") repo = user.get_repo(repo_name) release = repo.create_git_release(tag=tag, name=name, message=message) return ( f"Release **`{name}`** created in repository **`{repo_name}`**! [Open Release]({release.html_url})" ) elif action == "list_releases": # ... if not repo_name: raise ValueError("Missing parameter: repo_name") repo = user.get_repo(repo_name) releases = repo.get_releases() if not releases: return f"No releases in repository **`{repo_name}`**." releases_list = "\n".join([f"- [{release.tag_name}]({release.html_url})" for release in releases]) return f"Releases in repository **`{repo_name}`**:\n{releases_list}" elif action == "fork_repository": # ... if not repo_name: raise ValueError("Missing parameter: repo_name") repo = g.get_repo(repo_name) fork = user.create_fork(repo) return f"Repository **`{repo_name}`** forked! [Open fork]({fork.html_url})" elif action == "list_forks": # ... if not repo_name: raise ValueError("Missing parameter: repo_name") repo = g.get_repo(repo_name) forks = repo.get_forks() if not forks: return f"No forks of repository **`{repo_name}`**." forks_list = "\n".join([f"- [{fork.full_name}]({fork.html_url})" for fork in forks]) return f"Forks of repository **`{repo_name}`**:\n{forks_list}" elif action == "list_files": # ... if not all([owner, repo_name]): raise ValueError("Missing parameters: owner, repo_name") repo = g.get_repo(f"{owner}/{repo_name}") contents = repo.get_contents("" if not path else path) # Simplified path handling if not contents: return f"No files in path **`{path}`** of repository **`{repo_name}`**." files_list = "\n".join([f"- [{content.name}]({content.download_url})" for content in contents]) return f"Files in path **`{path}`** of repository **`{repo_name}`**:\n{files_list}" elif action == "get_repository_info": # ... if not all([owner, repo_name]): raise ValueError("Missing parameters: owner, repo_name") repo = g.get_repo(f"{owner}/{repo_name}") info = { "Name": repo.name, "Description": repo.description, "URL": repo.html_url, "Owner": repo.owner.login, "Default branch": repo.default_branch, "Language": repo.language, "Stars": repo.stargazers_count, "Forks": repo.forks_count, "Created at": str(repo.created_at), "Last updated": str(repo.updated_at), } info_md = "\n".join([f"- **{key}**: {value}" for key, value in info.items()]) return f"Repository info for **`{repo_name}`**:\n{info_md}" elif action == "get_file_content": # ... if not all([owner, repo_name, path]): raise ValueError("Missing parameters: owner, repo_name, path") content_text = get_file_content(owner, repo_name, path, branch) return ( f"File content of **`{path}`** from repository **`{repo_name}`**:\n\n`\n{content_text}\n`" ) elif action == "analyze_repository_by_url": # ... if not repo_url: raise ValueError("Missing parameter: repo_url") owner, repo_name = extract_repo_info(repo_url) if not owner or not repo_name: raise ValueError("Invalid repository URL") repo = g.get_repo(f"{owner}/{repo_name}") contents = repo.get_contents("") file_analyses = [] for content in contents: if content.type == "file": file_content = content.decoded_content.decode() analysis = analyze_file_content(file_content, content.path) file_analyses.append({ "name": content.name, "path": content.path, "analysis": analysis, }) analysis_md = "Repository file analysis:\n" + "\n".join([ f"- **{f['path']}**:\n" f" - Lines: {f['analysis']['line_count']}\n" f" - Words: {f['analysis']['word_count']}\n" f" - Extension: {f['analysis']['file_extension']}" for f in file_analyses ]) return analysis_md elif action == "analyze_repository_content": # ... if not all([owner, repo_name]): raise ValueError("Missing parameters: owner, repo_name") repo = g.get_repo(f"{owner}/{repo_name}") contents = repo.get_contents("") file_analyses = [] for content in contents: if content.type == "file": file_content = get_file_content(owner, repo_name, content.path, branch) # Use helper analysis = analyze_file_content(file_content, content.path) file_analyses.append({ "name": content.name, "path": content.path, "analysis": analysis, }) analysis_md = "Repository content analysis:\n" + "\n".join([ f"- **{f['path']}**:\n" f" - Lines: {f['analysis']['line_count']}\n" f" - Words: {f['analysis']['word_count']}\n" f" - Extension: {f['analysis']['file_extension']}" for f in file_analyses ]) return analysis_md else: # Correct else for handling unknown actions raise ValueError(f"Unknown action: {action}") except GithubException as e: return f"**GitHub Error:** {e}" except ValueError as e: return f"**Error:** {e}" # This is line 434 (now corrected) except requests.exceptions.RequestException as e: return f"**Connection Error:** {e}" except Exception as e: return f"**Unexpected Error:** {e}" # Catch all. with gr.Blocks() as demo: gr.Markdown("# GitHub Tool Plugingit (Simplified Interface)") with gr.Column(): action = gr.Dropdown( choices=[ "import_repository", "create_repository", "create_project_from_template", # Modular actions - to be added later in UI if needed # "create_repository_structure", # "add_dependencies", # "configure_database", # "add_authentication", "create_file", "get_file", "get_file_content_by_url", "delete_file", "update_file", "list_branches", "create_branch", "delete_branch", "create_pull_request", "list_open_pull_requests", "create_issue", "list_issues", "add_label_to_issue", "close_issue", "add_comment_to_issue", "create_release", "list_releases", "fork_repository", "list_forks", "list_files", "get_repository_info", "get_file_content", "analyze_repository_by_url", "analyze_repository_content", ], label="Select Action", ) repo_name = gr.Textbox(label="Repository Name") template_name = gr.Dropdown( choices=[""] + list(PROJECT_TEMPLATES.keys()), # Add "" as a choice label="Project Template", value="", # Set default value to "" (no selection) allow_custom_value=True, # Use allow_custom_value instead of allow_none ) # Template parameters UI - dynamically generated based on template template_params_ui = {} with gr.Row(): # Display parameters in a row for better layout for template_key, template_config in PROJECT_TEMPLATES.items(): if "params" in template_config: with gr.Column(visible=False, elem_classes=f"params_{template_key}") as params_section: # Hide initially for param_name, param_details in template_config["params"].items(): if param_details["type"] == "choice": template_params_ui[param_name] = gr.Dropdown( choices=param_details["choices"], label=param_details["description"] or param_name, value=param_details["default"] ) else: # Add other input types if needed (e.g., Textbox for string parameters) template_params_ui[param_name] = gr.Textbox( label=param_details["description"] or param_name, value=param_details["default"] if "default" in param_details else "" ) template_params_ui[template_key] = params_section # Store the section itself for visibility control branch = gr.Textbox(label="Branch", value="main") path = gr.Textbox(label="File Path") content = gr.Code(label="File Content", lines=5, language='python') # Larger code box message = gr.Textbox(label="Message/Comment") owner = gr.Textbox(label="Owner") vcs_url = gr.Textbox(label="VCS URL") title = gr.Textbox(label="Title") body = gr.Textbox(label="Body") base = gr.Textbox(label="Base Branch") head = gr.Textbox(label="Head Branch/New Branch") issue_number = gr.Number(label="Issue Number", precision=0) labels = gr.Textbox(label="Labels (comma-separated)") tag = gr.Textbox(label="Tag") release_name = gr.Textbox(label="Release Name") file_url = gr.Textbox(label="File URL") repo_url = gr.Textbox(label="Repository URL") # Function to show/hide template parameters based on template selection def show_template_params(template_name): visibility_map = {key: gr.Column.update(visible=False) for key in PROJECT_TEMPLATES if "params" in PROJECT_TEMPLATES[key]} # Initially hide all if template_name and template_name in PROJECT_TEMPLATES and "params" in PROJECT_TEMPLATES[template_name]: visibility_map[template_name] = gr.Column.update(visible=True) # Show for selected template return [visibility_map.get(key, gr.Column.update(visible=False)) for key in PROJECT_TEMPLATES if "params" in PROJECT_TEMPLATES[key]] # Return list in consistent order template_param_visibility_outputs = [template_params_ui[key] for key in PROJECT_TEMPLATES if "params" in PROJECT_TEMPLATES[key]] # Get list of output components for visibility template_name.change( show_template_params, inputs=[template_name], outputs=template_param_visibility_outputs ) run_button = gr.Button("Execute") output = gr.Markdown(label="Result") # Use Markdown for richer output input_components = [ action, repo_name, branch, path, content, message, owner, vcs_url, title, body, base, head, issue_number, labels, tag, release_name, file_url, repo_url, template_name, ] # Add template parameters dynamically to input components for template_key in PROJECT_TEMPLATES: if "params" in PROJECT_TEMPLATES[template_key]: for param_name in template_params_ui: # Use keys from template_params_ui which are param names if param_name in PROJECT_TEMPLATES[template_key]["params"]: # Check if parameter belongs to template input_components.append(template_params_ui[param_name]) # Append parameter component run_button.click( github_tool, inputs=input_components, # Use dynamically built input list outputs=output, api_name="github_tool" ) demo.launch()