UNPKG

gitarsenal-cli

Version:

CLI tool for creating Modal sandboxes with GitHub repositories

1,501 lines (1,250 loc) 469 kB
Directory structure: └── gitarsenal-cli/ ├── README.md ├── activate_venv.sh ├── ascii_banner.txt ├── config.json ├── gitingest-integration.js ├── index.js ├── package.json ├── repos.json ├── Step ├── test_credentials_integration.py ├── .npmignore ├── .venv_status.json ├── kill_claude/ │ ├── README.md │ ├── claude_code_agent.py │ ├── requirements.txt │ ├── prompts/ │ │ ├── claude-code-agent-prompts.md │ │ ├── claude-code-environment-context.md │ │ ├── claude-code-git-workflows.md │ │ ├── claude-code-hook-system.md │ │ ├── claude-code-response-formatting.md │ │ ├── claude-code-security-constraints.md │ │ ├── claude-code-system-prompt.md │ │ ├── claude-code-system-reminders.md │ │ ├── claude-code-task-workflows.md │ │ ├── claude-code-thinking-mode-prompts.md │ │ ├── claude-code-tool-prompts.md │ │ └── claude-code-tool-usage-policies.md │ ├── tools/ │ │ ├── __init__.py │ │ ├── bash_output_tool.py │ │ ├── bash_tool.py │ │ ├── edit_tool.py │ │ ├── exit_plan_mode_tool.py │ │ ├── glob_tool.py │ │ ├── grep_tool.py │ │ ├── kill_bash_tool.py │ │ ├── ls_tool.py │ │ ├── multiedit_tool.py │ │ ├── notebook_edit_tool.py │ │ ├── read_tool.py │ │ ├── task_tool.py │ │ ├── todo_write_tool.py │ │ ├── web_fetch_tool.py │ │ ├── web_search_tool.py │ │ └── write_tool.py │ └── .claude/ │ └── settings.local.json ├── lib/ │ ├── dependencies.js │ └── sandbox.js ├── python/ │ ├── auth_manager.py │ ├── command_manager.py │ ├── credentials_manager.py │ ├── debug_modal_minimal.py │ ├── fetch_modal_tokens.py │ ├── fix_modal_token.py │ ├── fix_modal_token_advanced.py │ ├── gitarsenal_keys.py │ ├── gitarsenal_proxy_client.py │ ├── llm_debugging.py │ ├── requirements.txt │ ├── setup.py │ ├── setup_modal_token.py │ ├── shell.py │ ├── test_container_fail.py │ ├── test_container_pass.py │ ├── test_modal.py │ ├── test_modalSandboxScript.py │ └── .env.example └── scripts/ └── postinstall.js ================================================ FILE: README.md ================================================ # GitArsenal CLI **Run any GitHub repository instantly with pre-configured GPU environments.** GitArsenal CLI makes it incredibly easy to run any GitHub repository without worrying about setup, dependencies, or environment configuration. Just point it at a repository and start coding with GPU acceleration. ## Why GitArsenal CLI? - **Zero Setup**: No need to install dependencies, configure environments, or manage GPU drivers - **GPU Ready**: Every environment comes with GPU acceleration (A10G, A100, H100) - **Persistent Storage**: Your work and data persist between sessions - **SSH Access**: Connect directly to your running environment - **API Key Management**: Securely store and auto-inject API keys for services like OpenAI, Weights & Biases, and Hugging Face ## Quick Start ### Run any GitHub repository ```bash # Basic usage - clone and run any repo gitarsenal --repo-url https://github.com/username/awesome-project.git # With GPU acceleration gitarsenal --gpu A10G --repo-url https://github.com/username/awesome-project.git # With custom setup commands gitarsenal --gpu A100 --repo-url https://github.com/username/awesome-project.git --setup-commands "pip install -r requirements.txt" "python setup.py install" ``` ### Examples ```bash # Run a machine learning project gitarsenal --gpu A100 --repo-url https://github.com/username/transformer-project.git --setup-commands "pip install torch transformers" "wandb login" # Run a web development project gitarsenal --repo-url https://github.com/username/react-app.git --setup-commands "npm install" "npm start" # Run a data science project with persistent storage gitarsenal --gpu A10G --repo-url https://github.com/username/data-analysis.git --volume-name my-data --setup-commands "pip install pandas numpy matplotlib" ``` ## API Key Management Store your API keys once and use them across all projects: ```bash # Add API keys for seamless integration gitarsenal keys add --service openai gitarsenal keys add --service wandb gitarsenal keys add --service huggingface # View your saved keys gitarsenal keys list # Remove a key gitarsenal keys delete --service openai ``` ### Supported Services - **OpenAI** - For debugging and AI assistance - **Weights & Biases** - For experiment tracking - **Hugging Face** - For model access and downloads ## Features ### Automatic Environment Setup The CLI automatically: - Clones your repository - Installs dependencies based on your setup commands - Configures GPU acceleration - Sets up persistent storage - Injects your saved API keys ### Persistent Storage Keep your work safe with persistent volumes: ```bash # Create a persistent environment gitarsenal --repo-url https://github.com/username/project.git --volume-name my-work # Your data, models, and work will persist between sessions ``` ### SSH Access Connect directly to your running environment: ```bash # Get SSH connection details gitarsenal --repo-url https://github.com/username/project.git --ssh # Connect via SSH to your environment ssh user@your-environment-ip ``` ## Workflow 1. **Choose a repository** - Any GitHub repo you want to work with 2. **Run the command** - Specify GPU, setup commands, and storage 3. **Start coding** - Your environment is ready with all dependencies installed 4. **Save your work** - Use persistent volumes to keep your progress ## Perfect For - **Machine Learning Projects** - GPU-accelerated training with pre-configured environments - **Data Science** - Jupyter notebooks with all dependencies ready - **Web Development** - Full-stack projects with development servers - **Research** - Reproducible environments for academic work - **Hackathons** - Quick setup for rapid prototyping ## Getting Started 1. Install the CLI (see installation instructions) 2. Add your API keys: `gitarsenal keys add --service openai` 3. Run any repository: `gitarsenal --repo-url https://github.com/username/project.git` 4. Start coding! No more "works on my machine" - every environment is identical and ready to go. ================================================ FILE: activate_venv.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ".venv/bin/activate" exec "$@" ================================================ FILE: ascii_banner.txt ================================================ ╭────────────────────────────────────────────────────────────────────────────────╮ │ │ │ ██████╗ ██╗████████╗ █████╗ ██████╗ ███████╗███████╗███╗ ██╗ █████╗ ██╗ │ │ ██╔════╝ ██║╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██╔════╝████╗ ██║██╔══██╗██║ │ │ ██║ ███╗██║ ██║ ███████║██████╔╝███████╗█████╗ ██╔██╗ ██║███████║██║ │ │ ██║ ██║██║ ██║ ██╔══██║██╔══██╗╚════██║██╔══╝ ██║╚██╗██║██╔══██║██║ │ │ ╚██████╔╝██║ ██║ ██║ ██║██║ ██║███████║███████╗██║ ╚████║██║ ██║███████╗│ │ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝│ │ │ │ GPU-Accelerated Development Environments │ │ │ ╰────────────────────────────────────────────────────────────────────────────────╯ ================================================ FILE: config.json ================================================ { "userId": "rs545837", "userName": "ROhan Sharma", "webhookUrl": "https://www.gitarsenal.dev/api/users" } ================================================ FILE: gitingest-integration.js ================================================ #!/usr/bin/env node const { spawn } = require('child_process'); const chalk = require('chalk'); // Function to check if GitIngest CLI is available and working async function checkGitIngestCLI() { try { // Try a simple help command first const checkProcess = spawn('gitingest', ['--version'], { stdio: 'pipe', timeout: 5000 // 5 second timeout }); let stderr = ''; checkProcess.stderr.on('data', (data) => { stderr += data.toString(); }); return new Promise((resolve) => { checkProcess.on('close', (code) => { // If there are Python errors in stderr, consider it failed even if exit code is 0 if (stderr.includes('TypeError') || stderr.includes('Traceback') || stderr.includes('Error')) { // console.log(chalk.yellow('⚠️ GitIngest CLI has Python compatibility issues')); resolve(false); } else if (code === 0) { resolve(true); } else { resolve(false); } }); checkProcess.on('error', () => { resolve(false); }); // Handle timeout setTimeout(() => { if (!checkProcess.killed) { checkProcess.kill(); resolve(false); } }, 5000); }); } catch (error) { return false; } } // Function to fetch GitIngest data using local GitIngest CLI async function fetchGitIngestData(repoUrl) { try { // First check if GitIngest CLI is available const gitingestAvailable = await checkGitIngestCLI(); if (!gitingestAvailable) { // console.log(chalk.yellow('⚠️ GitIngest CLI not available or has compatibility issues.')); // console.log(chalk.blue('💡 For best results, install with: pipx install gitingest')); // console.log(chalk.blue(' Alternative: pip install gitingest (requires Python 3.10+)')); // console.log(chalk.blue('📖 More info: https://github.com/coderamp-labs/gitingest')); // console.log(chalk.gray(' Falling back to basic repository analysis...')); return null; } console.log(chalk.gray('📥 Running GitIngest locally...')); // Run GitIngest CLI command with optimal settings for AI analysis const gitingestProcess = spawn('gitingest', [ repoUrl, '-o', '-', // Output to stdout ], { stdio: ['pipe', 'pipe', 'pipe'] }); let gitingestOutput = ''; let errorOutput = ''; gitingestProcess.stdout.on('data', (data) => { gitingestOutput += data.toString(); }); gitingestProcess.stderr.on('data', (data) => { errorOutput += data.toString(); }); return new Promise((resolve) => { gitingestProcess.on('close', (code) => { if (code === 0 && gitingestOutput.trim().length > 0) { console.log(chalk.green('✅ GitIngest analysis complete')); console.log(chalk.gray(`📊 Captured ${gitingestOutput.length} characters of repository content`)); resolve(parseGitIngestOutput(gitingestOutput, repoUrl)); } else { console.log(chalk.yellow(`⚠️ GitIngest failed (exit code: ${code})`)); if (errorOutput) { console.log(chalk.gray(`Error details: ${errorOutput.slice(0, 300)}`)); } resolve(null); } }); gitingestProcess.on('error', (error) => { console.log(chalk.yellow(`⚠️ GitIngest CLI error: ${error.message}`)); resolve(null); }); }); } catch (error) { console.log(chalk.yellow(`⚠️ GitIngest execution failed: ${error.message}`)); return null; } } // Function to parse GitIngest text output into structured data function parseGitIngestOutput(gitingestText, repoUrl) { try { // Extract repository info from URL const urlMatch = repoUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/); const owner = urlMatch ? urlMatch[1] : 'unknown'; const repo = urlMatch ? urlMatch[2].replace('.git', '') : 'unknown'; // GitIngest output format: // Repository: owner/repo-name // Files analyzed: 42 // Estimated tokens: 15.2k // // Directory structure: // └── project-name/ // ├── src/ // │ ├── main.py // └── README.md // // ================================================ // FILE: src/main.py // ================================================ // [file content] const lines = gitingestText.split('\n'); let summary = ''; let tree = ''; let content_preview = ''; let detectedLanguage = 'Unknown'; let detectedTechnologies = []; let primaryPackageManager = 'Unknown'; // Find sections let summaryEnd = -1; let treeStart = -1; let treeEnd = -1; let contentStart = -1; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.startsWith('Repository:') && summaryEnd === -1) { // Find end of summary (first empty line after Repository line) for (let j = i; j < lines.length; j++) { if (lines[j].trim() === '' && j > i) { summaryEnd = j; break; } } } if (line.startsWith('Directory structure:')) { treeStart = i; } if (line.includes('===') && line.includes('FILE:')) { if (treeStart > -1 && treeEnd === -1) { treeEnd = i; } if (contentStart === -1) { contentStart = i; } } } // Extract sections if (summaryEnd > 0) { summary = lines.slice(0, summaryEnd).join('\n'); } else { // Fallback: take first 10 lines as summary summary = lines.slice(0, 10).join('\n'); } if (treeStart > -1) { const endIdx = treeEnd > -1 ? treeEnd : (contentStart > -1 ? contentStart : Math.min(treeStart + 50, lines.length)); tree = lines.slice(treeStart, endIdx).join('\n'); } if (contentStart > -1) { // Take first 300 lines of content to provide good context without overwhelming content_preview = lines.slice(contentStart, Math.min(contentStart + 300, lines.length)).join('\n'); } // Detect technologies from content const contentLower = gitingestText.toLowerCase(); // Language detection if (contentLower.includes('import torch') || contentLower.includes('pytorch') || contentLower.includes('def ') || contentLower.includes('import ')) { detectedLanguage = 'Python'; primaryPackageManager = 'pip'; } else if (contentLower.includes('package.json') || contentLower.includes('require(') || contentLower.includes('import ') || contentLower.includes('function ')) { detectedLanguage = 'JavaScript'; primaryPackageManager = 'npm'; } else if (contentLower.includes('cargo.toml') || contentLower.includes('fn ') || contentLower.includes('use ')) { detectedLanguage = 'Rust'; primaryPackageManager = 'cargo'; } else if (contentLower.includes('go.mod') || contentLower.includes('func ') || contentLower.includes('package ')) { detectedLanguage = 'Go'; primaryPackageManager = 'go mod'; } // AI/ML Technology detection if (contentLower.includes('torch') || contentLower.includes('pytorch')) { detectedTechnologies.push('PyTorch'); } if (contentLower.includes('tensorflow') || contentLower.includes('tf.')) { detectedTechnologies.push('TensorFlow'); } if (contentLower.includes('transformers') || contentLower.includes('huggingface')) { detectedTechnologies.push('Hugging Face'); } if (contentLower.includes('numpy') || contentLower.includes('np.')) { detectedTechnologies.push('NumPy'); } if (contentLower.includes('openai') && (contentLower.includes('import openai') || contentLower.includes('openai.')) && !contentLower.includes('# example') && !contentLower.includes('# TODO')) { detectedTechnologies.push('OpenAI API'); } if (contentLower.includes('anthropic') && contentLower.includes('import anthropic')) { detectedTechnologies.push('Anthropic API'); } // Count files from summary const filesMatch = summary.match(/Files analyzed: (\d+)/); const fileCount = filesMatch ? parseInt(filesMatch[1]) : 0; const structuredData = { system_info: { platform: process.platform, python_version: process.version, detected_language: detectedLanguage, detected_technologies: detectedTechnologies, file_count: fileCount, repo_stars: 0, // Would need GitHub API repo_forks: 0, // Would need GitHub API primary_package_manager: primaryPackageManager, complexity_level: fileCount > 50 ? 'high' : fileCount > 20 ? 'medium' : 'low' }, repository_analysis: { summary: summary || `Repository: ${owner}/${repo}\nAnalyzed with GitIngest`, tree: tree || 'Directory structure not available', content_preview: content_preview || 'Content preview not available' }, success: true }; console.log(chalk.gray('🔍 Analysis Summary:')); console.log(chalk.gray(` - Language: ${detectedLanguage}`)); console.log(chalk.gray(` - Technologies: ${detectedTechnologies.join(', ') || 'None detected'}`)); console.log(chalk.gray(` - Files: ${fileCount}`)); console.log(chalk.gray(` - Package Manager: ${primaryPackageManager}`)); return structuredData; } catch (error) { console.log(chalk.yellow(`⚠️ Failed to parse GitIngest output: ${error.message}`)); return null; } } module.exports = { fetchGitIngestData, checkGitIngestCLI }; ================================================ FILE: index.js ================================================ #!/usr/bin/env node // This file is the main entry point for the package // It simply re-exports the main functionality const { runModalSandbox } = require('./lib/sandbox'); const { checkDependencies } = require('./lib/dependencies'); module.exports = { runModalSandbox, checkDependencies }; ================================================ FILE: package.json ================================================ { "name": "gitarsenal-cli", "version": "1.9.78", "description": "CLI tool for creating Modal sandboxes with GitHub repositories", "main": "index.js", "bin": { "gitarsenal": "./bin/gitarsenal.js" }, "scripts": { "postinstall": "node scripts/postinstall.js" }, "keywords": [ "modal", "sandbox", "cli", "gpu", "github", "repository", "setup" ], "author": "", "license": "MIT", "dependencies": { "@supabase/supabase-js": "^2.53.0", "boxen": "^5.1.2", "chalk": "^4.1.2", "commander": "^9.4.1", "execa": "^5.1.1", "fs-extra": "^11.1.0", "g": "^2.0.1", "inquirer": "^8.2.4", "ora": "^5.4.1", "update-notifier": "^5.1.0", "which": "^3.0.0" }, "engines": { "node": ">=14.0.0" } } ================================================ FILE: repos.json ================================================ { "repositories": [ { "name": "FlashAvatar", "url": "https://github.com/FlashAvatar/FlashAvatar", "gpu": "A10G", "volume": "flashavatar-volume", "description": "Avatar generation repository" }, { "name": "DECA", "url": "https://github.com/YadiraF/DECA", "gpu": "A100-40", "volume": "deca-volume", "description": "Detailed Expression Capture and Animation" }, { "name": "DisenBooth", "url": "https://github.com/Fictionarry/DisenBooth", "gpu": "A10G", "volume": "disenbooth-volume", "description": "Disentangled subject-driven generation" }, { "name": "FederatedGPT-Shepherd", "url": "https://github.com/FederatedGPT/FederatedGPT-Shepherd", "gpu": "A100-40", "volume": "federatedgpt-volume", "description": "Federated GPT training framework" }, { "name": "FLAME_PyTorch", "url": "https://github.com/HavenFeng/FLAME_PyTorch", "gpu": "A10G", "volume": "flame-pytorch-volume", "description": "FLAME face model in PyTorch" }, { "name": "flame-head-tracker", "url": "https://github.com/CMU-Perceptual-Computing-Lab/flame-head-tracker", "gpu": "A10G", "volume": "flame-tracker-volume", "description": "Head tracking using FLAME model" }, { "name": "litex", "url": "https://github.com/enjoy-digital/litex", "gpu": "T4", "volume": "litex-volume", "description": "FPGA framework and SoC builder" }, { "name": "MICA", "url": "https://github.com/Zielon/MICA", "gpu": "A100-40", "volume": "mica-volume", "description": "3D face reconstruction" }, { "name": "photometric_optimization", "url": "https://github.com/facebookresearch/photometric_optimization", "gpu": "A10G", "volume": "photometric-volume", "description": "Photometric optimization for 3D reconstruction" }, { "name": "reflex", "url": "https://github.com/reflex-dev/reflex", "gpu": "A10G", "volume": "reflex-volume", "description": "Web framework for Python" } ], "metadata": { "description": "Repository list for GitArsenal evaluation", "created": "2025-08-04", "version": "1.0", "total_repos": 10 } } ================================================ FILE: Step ================================================ [Empty file] ================================================ FILE: test_credentials_integration.py ================================================ #!/usr/bin/env python3 """ Test script to verify that stored credentials are being passed correctly to SSH containers. """ import os import json from pathlib import Path def test_credentials_loading(): """Test that credentials can be loaded from the local file""" print("🔍 Testing credentials loading...") # Test get_stored_credentials function try: from test_modalSandboxScript import get_stored_credentials credentials = get_stored_credentials() if credentials: print(f"✅ Found {len(credentials)} stored credentials:") for key, value in credentials.items(): masked_value = value[:8] + "..." if len(value) > 8 else "***" print(f" - {key}: {masked_value}") else: print("⚠️ No stored credentials found") return credentials except Exception as e: print(f"❌ Error loading credentials: {e}") return {} def test_credentials_file(): """Test that the credentials file exists and is readable""" print("\n🔍 Testing credentials file...") credentials_file = Path.home() / ".gitarsenal" / "credentials.json" if credentials_file.exists(): print(f"✅ Credentials file exists at: {credentials_file}") try: with open(credentials_file, 'r') as f: credentials = json.load(f) print(f"✅ Successfully loaded {len(credentials)} credentials from file") return credentials except Exception as e: print(f"❌ Error reading credentials file: {e}") return {} else: print(f"⚠️ Credentials file not found at: {credentials_file}") print("💡 You can create it by running the credentials manager") return {} def test_environment_variables(): """Test that credentials are available as environment variables""" print("\n🔍 Testing environment variables...") credential_env_vars = [ 'OPENAI_API_KEY', 'WANDB_API_KEY', 'HF_TOKEN', 'HUGGINGFACE_TOKEN', 'GITHUB_TOKEN', 'KAGGLE_USERNAME', 'KAGGLE_KEY' ] found_creds = {} for env_var in credential_env_vars: value = os.environ.get(env_var) if value: found_creds[env_var] = value masked_value = value[:8] + "..." if len(value) > 8 else "***" print(f"✅ {env_var}: {masked_value}") if not found_creds: print("⚠️ No credential environment variables found") return found_creds def test_credentials_integration(): """Test the full credentials integration""" print("\n" + "="*60) print("🔐 CREDENTIALS INTEGRATION TEST") print("="*60) # Test all credential sources file_creds = test_credentials_file() env_creds = test_environment_variables() function_creds = test_credentials_loading() # Combine all credentials all_creds = {} all_creds.update(file_creds) all_creds.update(env_creds) all_creds.update(function_creds) print(f"\n📊 SUMMARY:") print(f" - File credentials: {len(file_creds)}") print(f" - Environment credentials: {len(env_creds)}") print(f" - Function credentials: {len(function_creds)}") print(f" - Total unique credentials: {len(all_creds)}") if all_creds: print(f"\n✅ Credentials are available for SSH container integration") print("💡 These credentials will be automatically passed to SSH containers") else: print(f"\n⚠️ No credentials found") print("💡 Consider setting up credentials using the credentials manager") return all_creds if __name__ == "__main__": test_credentials_integration() ================================================ FILE: .npmignore ================================================ # Development files .git .github .gitignore .editorconfig .eslintrc .prettierrc .vscode *.log *.tgz # Test files test/ tests/ __tests__/ coverage/ .nyc_output/ # Documentation docs/ doc/ examples/ example/ # Source files src/ .babelrc tsconfig.json tslint.json webpack.config.js rollup.config.js # Misc .DS_Store .env .env.* node_modules/ tmp/ temp/ ================================================ FILE: .venv_status.json ================================================ {"created":"2025-08-17T17:20:43.416Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"} ================================================ FILE: kill_claude/README.md ================================================ # Claude Code Agent A comprehensive Python implementation that replicates Claude Code's functionality by orchestrating all available tools to handle user queries intelligently. ## 🎯 Overview This project contains: - **All 16 Claude Code tool definitions** as individual Python modules - **Main orchestrator** (`claude_code_agent.py`) that handles user queries - **Query parsing and intent recognition** system - **Task management** with automatic todo list creation - **Interactive CLI mode** similar to Claude Code's interface ## 📁 Project Structure ``` kill_claude/ ├── claude_code_agent.py # Main orchestrator file ├── example_usage.py # Usage examples and demos ├── README.md # This file ├── ├── Tool Definitions: ├── task_tool.py # Task/Agent management ├── bash_tool.py # Command execution ├── glob_tool.py # File pattern matching ├── grep_tool.py # Content search ├── ls_tool.py # Directory listing ├── read_tool.py # File reading ├── edit_tool.py # File editing ├── multiedit_tool.py # Multiple file edits ├── write_tool.py # File writing ├── notebook_edit_tool.py # Jupyter notebook editing ├── web_fetch_tool.py # Web content fetching ├── web_search_tool.py # Web searching ├── todo_write_tool.py # Task management ├── bash_output_tool.py # Background shell output ├── kill_bash_tool.py # Shell termination ├── exit_plan_mode_tool.py # Plan mode management └── └── Documentation (from Claude Code): ├── claude-code-*.md # Claude Code internal docs └── ... ``` ## 🚀 Features ### Core Capabilities - **File Operations**: Read, write, edit, list files and directories - **Search**: Content search in files, web search, pattern matching - **Code Execution**: Run bash commands, package managers, git operations - **Web Access**: Fetch web content, search the internet - **Task Management**: Automatic todo list creation for complex tasks - **Planning**: Support for multi-step task planning and execution ### Intelligent Query Processing - **Intent Recognition**: Automatically detects what the user wants to do - **Tool Selection**: Chooses appropriate tools based on query analysis - **Parameter Extraction**: Extracts relevant parameters from natural language - **Context Awareness**: Maintains conversation context and task state ### Interactive Features - **CLI Mode**: Interactive command-line interface - **Help System**: Built-in help and tool documentation - **Error Handling**: Graceful error handling and user feedback - **Task Tracking**: Visual task management with status updates ## 🛠️ Installation 1. **Clone or download** all the files to a directory 2. **Install dependencies** (if needed): ```bash pip install dataclasses # Python 3.6 only, built-in for 3.7+ ``` 3. **Make the main script executable**: ```bash chmod +x claude_code_agent.py ``` ## 💻 Usage ### Interactive Mode Launch the interactive CLI: ```bash python claude_code_agent.py ``` Example interaction: ``` 🤖 Claude Code Agent - Interactive Mode Type 'help' for available commands, 'exit' to quit -------------------------------------------------- 👤 You: read main.py 🤖 Claude Code Agent: Processing query: read main.py Detected intent: file_operation (confidence: 0.80) Suggested tools: Read Reading file: main.py [file contents...] 👤 You: implement user authentication system 🤖 Claude Code Agent: Processing query: implement user authentication system Detected intent: complex_task (confidence: 0.90) Suggested tools: TodoWrite, Task Complex task identified. I've created a todo list to track progress... ``` ### Single Query Mode Process a single query from command line: ```bash python claude_code_agent.py "search for TODO comments" python claude_code_agent.py "run pytest tests/" python claude_code_agent.py "list all Python files" ``` ### Example Usage Run the examples to see all capabilities: ```bash python example_usage.py ``` ## 📋 Supported Query Types ### File Operations - `"read main.py"` - Read file contents - `"list files"` - List directory contents - `"find *.py"` - Find files matching patterns - `"edit config.json"` - Edit file contents ### Search Operations - `"search for TODO comments"` - Search in file contents - `"find functions named login"` - Search for specific patterns - `"web search Python best practices"` - Search the web ### Code Execution - `"run pytest"` - Execute commands - `"git status"` - Git operations - `"npm install express"` - Package management ### Web Access - `"fetch https://api.github.com"` - Fetch web content - `"search web for AI news"` - Web search ### Complex Tasks - `"implement REST API"` - Multi-step development tasks - `"build chat application"` - Complex project creation - `"setup CI/CD pipeline"` - Infrastructure tasks ## 🧠 How It Works ### 1. Query Parsing The agent analyzes user input using: - **Pattern matching** against common command structures - **Keyword detection** for intent classification - **Parameter extraction** from natural language - **Confidence scoring** for tool selection ### 2. Intent Recognition Classifies queries into categories: - `FILE_OPERATION` - File and directory operations - `SEARCH` - Content and web searching - `CODE_EXECUTION` - Running commands and scripts - `WEB_ACCESS` - Web content and APIs - `COMPLEX_TASK` - Multi-step development tasks - `TASK_MANAGEMENT` - Todo and planning operations ### 3. Tool Orchestration - **Automatic tool selection** based on intent and patterns - **Parameter mapping** from parsed query to tool arguments - **Sequential execution** for multi-step operations - **Error handling** with user-friendly messages ### 4. Task Management For complex queries, automatically: - **Creates todo lists** to track progress - **Breaks down tasks** into manageable steps - **Updates status** as work progresses - **Manages context** across tool executions ## 🎨 Customization ### Adding New Tools 1. Create a new tool file following the existing pattern: ```python class MyNewTool: name = "MyNewTool" @staticmethod def schema(): return { /* JSON schema */ } def execute(self, **kwargs): # Tool implementation pass ``` 2. Import and register in `claude_code_agent.py`: ```python from my_new_tool import MyNewTool # In ClaudeCodeAgent.__init__: self.tools['MyNewTool'] = MyNewTool() ``` ### Adding Query Patterns Add new patterns to `tool_patterns` in `ClaudeCodeAgent`: ```python self.tool_patterns = { r'\b(my|custom)\s+pattern\b': 'MyNewTool', # ... existing patterns } ``` ### Customizing Intent Recognition Modify the `parse_query` method to add new intents or improve classification logic. ## 🔧 Technical Details ### Architecture - **Modular design** with separate tool definitions - **Plugin-style architecture** for easy tool addition - **State management** for conversation context - **Event-driven execution** based on query parsing ### Error Handling - **Graceful degradation** when tools fail - **User-friendly error messages** - **Logging and debugging** support - **Recovery mechanisms** for common issues ### Performance - **Lazy loading** of tool resources - **Caching** for repeated operations - **Parallel execution** where possible - **Memory-efficient** query processing ## 📖 Examples See `example_usage.py` for comprehensive examples of: - File operations workflow - Development task management - Debugging procedures - Research and learning workflows - Interactive feature demonstrations ## 🤝 Contributing This project replicates Claude Code's functionality for educational and research purposes. To contribute: 1. **Add new tools** following the existing patterns 2. **Improve query parsing** with better pattern recognition 3. **Enhance error handling** for edge cases 4. **Add more examples** and use cases 5. **Optimize performance** for large codebases ## 📄 License This project is for educational purposes and demonstrates how Claude Code's functionality can be replicated using Python. Please respect Claude Code's actual terms of service when using this educational implementation. --- **Built with ❤️ to understand and replicate Claude Code's powerful agent capabilities** ================================================ FILE: kill_claude/claude_code_agent.py ================================================ #!/usr/bin/env python3 """ Claude Code Agent - Anthropic API Integration This is the main file that replicates Claude Code's functionality by using the Anthropic API with proper tool orchestration and system prompt integration. Features: - Real Anthropic API integration using claude-3-5-sonnet-20241022 - Dynamic loading of tool implementations from tools/ directory - System prompt integration from prompts/ directory - Tool orchestration with intelligent selection and execution - Task management with TodoWrite integration - Interactive CLI mode similar to Claude Code """ import os import sys import json import re import glob import importlib.util from typing import List, Dict, Any, Optional, Union from dataclasses import dataclass from enum import Enum import anthropic def load_tool_modules(): """Load all tool modules from the tools directory.""" tools_dir = os.path.join(os.path.dirname(__file__), 'tools') tool_modules = {} for tool_file in glob.glob(os.path.join(tools_dir, '*_tool.py')): if os.path.basename(tool_file) == '__init__.py': continue module_name = os.path.basename(tool_file)[:-3] # Remove .py spec = importlib.util.spec_from_file_location(module_name, tool_file) if spec and spec.loader: module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) # Find tool class in module for attr_name in dir(module): attr = getattr(module, attr_name) if (hasattr(attr, 'name') and hasattr(attr, 'schema') and callable(getattr(attr, 'schema', None))): tool_modules[attr.name] = attr break return tool_modules def load_system_prompts(): """Load all markdown files from prompts directory.""" prompts_dir = os.path.join(os.path.dirname(__file__), 'prompts') prompt_content = {} for md_file in glob.glob(os.path.join(prompts_dir, '*.md')): filename = os.path.basename(md_file) try: with open(md_file, 'r', encoding='utf-8') as f: content = f.read() prompt_content[filename] = content except Exception as e: print(f"Warning: Could not load {filename}: {e}") return prompt_content # Load tools and prompts dynamically TOOL_MODULES = load_tool_modules() PROMPT_CONTENT = load_system_prompts() # Generate tool schemas from loaded modules TOOL_SCHEMAS = [] for tool_name, tool_class in TOOL_MODULES.items(): schema = tool_class.schema() TOOL_SCHEMAS.append({ "name": tool_name, "description": getattr(tool_class, '__doc__', f"Tool: {tool_name}"), "input_schema": schema }) # Combine all prompt content into system prompt def build_system_prompt(): """Build the complete system prompt from all markdown files.""" prompt_parts = [] # Start with the main system prompt if 'claude-code-system-prompt.md' in PROMPT_CONTENT: prompt_parts.append(PROMPT_CONTENT['claude-code-system-prompt.md']) # Add other prompt files in a logical order prompt_order = [ 'claude-code-security-constraints.md', 'claude-code-response-formatting.md', 'claude-code-task-workflows.md', 'claude-code-tool-usage-policies.md', 'claude-code-tool-prompts.md', 'claude-code-git-workflows.md', 'claude-code-hook-system.md', 'claude-code-environment-context.md', 'claude-code-agent-prompts.md', 'claude-code-system-reminders.md', 'claude-code-thinking-mode-prompts.md' ] for filename in prompt_order: if filename in PROMPT_CONTENT: prompt_parts.append(f"\n\n# {filename.replace('.md', '').replace('-', ' ').title()}\n") prompt_parts.append(PROMPT_CONTENT[filename]) return '\n'.join(prompt_parts) class QueryIntent(Enum): """Different types of user intents that can be handled.""" FILE_OPERATION = "file_operation" SEARCH = "search" CODE_EXECUTION = "code_execution" WEB_ACCESS = "web_access" TASK_MANAGEMENT = "task_management" PLANNING = "planning" INFORMATION = "information" COMPLEX_TASK = "complex_task" @dataclass class UserQuery: """Represents a parsed user query with intent and parameters.""" text: str intent: QueryIntent confidence: float extracted_params: Dict[str, Any] suggested_tools: List[str] class ClaudeCodeAgent: """ Main agent that orchestrates all tools using the Anthropic API. This class replicates Claude Code's behavior by: 1. Using the actual Anthropic API with claude-3-5-sonnet-20241022 2. Implementing all tool schemas exactly as Claude Code uses them 3. Using the proper system prompt for agent control 4. Managing tasks with TodoWrite integration 5. Providing an interactive CLI interface """ def __init__(self, api_key: Optional[str] = None): """Initialize the Claude Code Agent with Anthropic API integration.""" # Get API key from parameter or environment self.api_key = api_key or os.getenv('ANTHROPIC_API_KEY') if not self.api_key: raise ValueError( "Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable " "or pass api_key parameter." ) # Initialize Anthropic client self.client = anthropic.Anthropic(api_key=self.api_key) # Environment context self.working_dir = os.getcwd() self.platform = os.uname().sysname.lower() if hasattr(os, 'uname') else 'unknown' self.os_version = os.uname().release if hasattr(os, 'uname') else 'unknown' # Check if git repo self.is_git_repo = os.path.exists(os.path.join(self.working_dir, '.git')) # Current date from datetime import datetime self.today_date = datetime.now().strftime('%Y-%m-%d') # Conversation state self.conversation_history = [] self.todo_list = [] # Tool usage patterns for query parsing self.tool_patterns = { # File operations r'\b(read|open|show|view|cat)\s+(.+\.(py|js|ts|md|txt|json|yaml|yml|html|css))\b': 'Read', r'\b(write|create|make)\s+(file|script)\b': 'Write', r'\b(edit|modify|change|update|replace)\s+(.+)\b': 'Edit', r'\b(list|ls|show)\s+(files|directory|dir)\b': 'LS', # Search operations r'\b(find|search|grep|look\s+for)\s+(.+)\b': 'Grep', r'\b(find|search)\s+files?\s+(.+)\b': 'Glob', # Code execution r'\b(run|execute|bash|shell|command)\s+(.+)\b': 'Bash', r'\b(npm|pip|git|python|node|cargo)\s+(.+)\b': 'Bash', # Web access r'\b(fetch|get|download|web|url)\s+(.+)\b': 'WebFetch', r'\b(search\s+web|google|search\s+for)\s+(.+)\b': 'WebSearch', # Task management r'\b(todo|task|plan|organize)\b': 'TodoWrite', r'\b(help\s+me|implement|build|create)\s+(.+)\b': 'Task', } def get_system_prompt(self) -> str: """Get the system prompt with environment context filled in.""" base_prompt = build_system_prompt() # Add environment context env_context = f""" # Environment Information <env> Working directory: {self.working_dir} Is directory a git repo: {"Yes" if self.is_git_repo else "No"} Platform: {self.platform} OS Version: {self.os_version} Today's date: {self.today_date} </env> # Available Tools The following {len(TOOL_SCHEMAS)} tools are loaded and available: {', '.join([tool['name'] for tool in TOOL_SCHEMAS])} """ return base_prompt + env_context def parse_query(self, query: str) -> UserQuery: """ Parse user query to understand intent and extract parameters. Args: query: The user's input query Returns: UserQuery object with parsed information """ query_lower = query.lower().strip() # Determine intent based on keywords and patterns intent = QueryIntent.INFORMATION # default confidence = 0.5 extracted_params = {} suggested_tools = [] # Check for specific patterns for pattern, tool in self.tool_patterns.items(): if re.search(pattern, query_lower): suggested_tools.append(tool) confidence = 0.8 # Extract parameters from the match match = re.search(pattern, query_lower) if match and len(match.groups()) > 0: # Get the last capturing group that has content for i in range(len(match.groups()), 0, -1): if match.group(i): extracted_params['target'] = match.group(i).strip() break # Determine primary intent if any(tool in ['Read', 'Write', 'Edit', 'LS', 'Glob'] for tool in suggested_tools): intent = QueryIntent.FILE_OPERATION elif any(tool in ['Grep', 'WebSearch'] for tool in suggested_tools): intent = QueryIntent.SEARCH elif 'Bash' in suggested_tools: intent = QueryIntent.CODE_EXECUTION elif any(tool in ['WebFetch', 'WebSearch'] for tool in suggested_tools): intent = QueryIntent.WEB_ACCESS elif 'TodoWrite' in suggested_tools: intent = QueryIntent.TASK_MANAGEMENT elif 'Task' in suggested_tools: intent = QueryIntent.COMPLEX_TASK # Look for complex multi-step queries complex_indicators = ['implement', 'build', 'create', 'develop', 'setup', 'configure'] if any(indicator in query_lower for indicator in complex_indicators): intent = QueryIntent.COMPLEX_TASK confidence = 0.9 suggested_tools = ['TodoWrite', 'Task'] + suggested_tools return UserQuery( text=query, intent=intent, confidence=confidence, extracted_params=extracted_params, suggested_tools=list(set(suggested_tools)) # Remove duplicates ) def should_use_todo_list(self, query: UserQuery) -> bool: """ Determine if the query requires using TodoWrite for task management. Based on Claude Code's criteria: - Complex multi-step tasks (3+ steps) - Non-trivial and complex tasks - User explicitly requests todo list - Multiple tasks provided """ complex_keywords = [ 'implement', 'build', 'create', 'develop', 'setup', 'configure', 'refactor', 'optimize', 'migrate', 'integrate', 'deploy' ] multi_step_indicators = [ 'and then', 'after that', 'next', 'also', 'additionally', 'first', 'second', 'then', 'finally' ] # Check for complexity has_complex_keywords = any(keyword in query.text.lower() for keyword in complex_keywords) has_multi_step = any(indicator in query.text.lower() for indicator in multi_step_indicators) has_multiple_sentences = len(query.text.split('.')) > 2 explicit_todo = 'todo' in query.text.lower() or 'task' in query.text.lower() return (has_complex_keywords or has_multi_step or has_multiple_sentences or explicit_todo or query.intent == QueryIntent.COMPLEX_TASK) def call_api(self, user_message: str, use_tools: bool = True) -> str: """ Make an API call to Claude with proper tool handling - matches Claude Code exactly. Args: user_message: The user's message/query use_tools: Whether to include tools in the API call Returns: Claude's response text """ try: # Start conversation with user message messages = self.conversation_history + [ {"role": "user", "content": user_message} ] # Continue conversation until Claude stops making tool calls final_response_text = "" while True: # Make API call call_params = { "model": "claude-sonnet-4-20250514", "max_tokens": 4096, "system": self.get_system_prompt(), "messages": messages } if use_tools: call_params["tools"] = TOOL_SCHEMAS response = self.client.messages.create(**call_params) # Extract response content and tool calls response_text = "" tool_calls = [] for content_block in response.content: if content_block.type == "text": response_text += content_block.text elif content_block.type == "tool_use": tool_calls.append(content_block) # Add response text to final output if response_text.strip(): final_response_text += response_text # Update conversation with assistant response messages.append({"role": "assistant", "content": response.content}) # If no tool calls, we're done if not tool_calls: break # Execute tool calls if tool_calls: tool_results = [] for i, tool_call in enumerate(tool_calls, 1): result = self.execute_tool_call(tool_call, i, len(tool_calls)) tool_results.append({ "tool_use_id": tool_call.id, "type": "tool_result", "content": str(result) }) # Add tool results to conversation messages.append({ "role": "user", "content": tool_results }) # Update conversation history self.conversation_history = messages return final_response_text.s