gitarsenal-cli
Version:
CLI tool for creating Modal sandboxes with GitHub repositories
1,501 lines (1,250 loc) • 469 kB
Plain Text
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