gitarsenal-cli 1.9.83 → 1.9.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.venv_status.json +1 -1
  2. package/bin/gitarsenal.js +110 -59
  3. package/kill_claude/README.md +2 -2
  4. package/kill_claude/TUI_IMPROVEMENTS.md +130 -0
  5. package/kill_claude/claude_code_agent.py +430 -126
  6. package/kill_claude/requirements.txt +3 -1
  7. package/kill_claude/tools/bash_tool.py +1 -1
  8. package/lib/sandbox.js +8 -4
  9. package/package.json +1 -1
  10. package/kill_claude/tools/__pycache__/bash_output_tool.cpython-312.pyc +0 -0
  11. package/kill_claude/tools/__pycache__/bash_output_tool.cpython-313.pyc +0 -0
  12. package/kill_claude/tools/__pycache__/bash_tool.cpython-312.pyc +0 -0
  13. package/kill_claude/tools/__pycache__/bash_tool.cpython-313.pyc +0 -0
  14. package/kill_claude/tools/__pycache__/edit_tool.cpython-312.pyc +0 -0
  15. package/kill_claude/tools/__pycache__/edit_tool.cpython-313.pyc +0 -0
  16. package/kill_claude/tools/__pycache__/exit_plan_mode_tool.cpython-312.pyc +0 -0
  17. package/kill_claude/tools/__pycache__/exit_plan_mode_tool.cpython-313.pyc +0 -0
  18. package/kill_claude/tools/__pycache__/glob_tool.cpython-312.pyc +0 -0
  19. package/kill_claude/tools/__pycache__/glob_tool.cpython-313.pyc +0 -0
  20. package/kill_claude/tools/__pycache__/grep_tool.cpython-312.pyc +0 -0
  21. package/kill_claude/tools/__pycache__/grep_tool.cpython-313.pyc +0 -0
  22. package/kill_claude/tools/__pycache__/kill_bash_tool.cpython-312.pyc +0 -0
  23. package/kill_claude/tools/__pycache__/kill_bash_tool.cpython-313.pyc +0 -0
  24. package/kill_claude/tools/__pycache__/ls_tool.cpython-312.pyc +0 -0
  25. package/kill_claude/tools/__pycache__/ls_tool.cpython-313.pyc +0 -0
  26. package/kill_claude/tools/__pycache__/multiedit_tool.cpython-312.pyc +0 -0
  27. package/kill_claude/tools/__pycache__/multiedit_tool.cpython-313.pyc +0 -0
  28. package/kill_claude/tools/__pycache__/notebook_edit_tool.cpython-312.pyc +0 -0
  29. package/kill_claude/tools/__pycache__/notebook_edit_tool.cpython-313.pyc +0 -0
  30. package/kill_claude/tools/__pycache__/read_tool.cpython-312.pyc +0 -0
  31. package/kill_claude/tools/__pycache__/read_tool.cpython-313.pyc +0 -0
  32. package/kill_claude/tools/__pycache__/task_tool.cpython-312.pyc +0 -0
  33. package/kill_claude/tools/__pycache__/task_tool.cpython-313.pyc +0 -0
  34. package/kill_claude/tools/__pycache__/todo_write_tool.cpython-312.pyc +0 -0
  35. package/kill_claude/tools/__pycache__/todo_write_tool.cpython-313.pyc +0 -0
  36. package/kill_claude/tools/__pycache__/web_fetch_tool.cpython-312.pyc +0 -0
  37. package/kill_claude/tools/__pycache__/web_fetch_tool.cpython-313.pyc +0 -0
  38. package/kill_claude/tools/__pycache__/web_search_tool.cpython-312.pyc +0 -0
  39. package/kill_claude/tools/__pycache__/web_search_tool.cpython-313.pyc +0 -0
  40. package/kill_claude/tools/__pycache__/write_tool.cpython-312.pyc +0 -0
  41. package/kill_claude/tools/__pycache__/write_tool.cpython-313.pyc +0 -0
package/.venv_status.json CHANGED
@@ -1 +1 @@
1
- {"created":"2025-08-18T08:57:32.974Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-08-20T12:21:13.646Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
package/bin/gitarsenal.js CHANGED
@@ -208,83 +208,113 @@ async function previewRecommendations(repoUrl, optsOrShowSummary = true) {
208
208
 
209
209
  function printGpuTorchCudaSummary(result) {
210
210
  try {
211
- console.log(chalk.bold('\nšŸ“Š RESULT SUMMARY (GPU/Torch/CUDA)'));
212
- console.log('────────────────────────────────────────────────────────');
211
+ console.log();
212
+ console.log(chalk.bold.cyan(' āš™ļø Analysis Results'));
213
+ console.log(chalk.dim(' ─────────────────'));
214
+ console.log();
213
215
 
214
216
  const cuda = result.cudaRecommendation;
215
217
  if (cuda) {
216
- console.log(chalk.bold('šŸŽÆ CUDA Recommendation'));
217
- if (cuda.recommendedCudaVersion) console.log(` - CUDA: ${cuda.recommendedCudaVersion}`);
218
- if (Array.isArray(cuda.compatibleTorchVersions) && cuda.compatibleTorchVersions.length)
219
- console.log(` - Torch Compatibility: ${cuda.compatibleTorchVersions.join(', ')}`);
220
- if (cuda.dockerImage) console.log(` - Docker Image: ${cuda.dockerImage}`);
218
+ console.log(chalk.bold.yellow(' šŸŽÆ CUDA Configuration'));
219
+ if (cuda.recommendedCudaVersion) {
220
+ console.log(chalk.dim(' Version: ') + chalk.white(cuda.recommendedCudaVersion));
221
+ }
222
+ if (Array.isArray(cuda.compatibleTorchVersions) && cuda.compatibleTorchVersions.length) {
223
+ console.log(chalk.dim(' Torch: ') + chalk.white(cuda.compatibleTorchVersions.join(', ')));
224
+ }
225
+ if (cuda.dockerImage) {
226
+ console.log(chalk.dim(' Image: ') + chalk.white(cuda.dockerImage));
227
+ }
221
228
  if (Array.isArray(cuda.installCommands) && cuda.installCommands.length) {
222
- console.log(' - Install Commands:');
223
- cuda.installCommands.forEach((c) => console.log(` $ ${c}`));
229
+ console.log(chalk.dim(' Commands:'));
230
+ cuda.installCommands.forEach((c) => {
231
+ console.log(chalk.dim(' $ ') + chalk.cyan(c));
232
+ });
233
+ }
234
+ if (cuda.notes) {
235
+ console.log(chalk.dim(' Notes: ') + chalk.gray(cuda.notes));
224
236
  }
225
- if (cuda.notes) console.log(` - Notes: ${cuda.notes}`);
226
237
  console.log();
227
238
  }
228
239
 
229
240
  const torch = result.torchRecommendation;
230
241
  if (torch) {
231
- console.log(chalk.bold('šŸ”„ PyTorch Recommendation'));
232
- if (torch.recommendedTorchVersion) console.log(` - Torch: ${torch.recommendedTorchVersion}`);
233
- if (torch.cudaVariant) console.log(` - CUDA Variant: ${torch.cudaVariant}`);
242
+ console.log(chalk.bold.red(' šŸ”„ PyTorch Setup'));
243
+ if (torch.recommendedTorchVersion) {
244
+ console.log(chalk.dim(' Version: ') + chalk.white(torch.recommendedTorchVersion));
245
+ }
246
+ if (torch.cudaVariant) {
247
+ console.log(chalk.dim(' CUDA: ') + chalk.white(torch.cudaVariant));
248
+ }
234
249
  if (torch.pipInstallCommand) {
235
- console.log(' - Install:');
236
- console.log(` $ ${torch.pipInstallCommand}`);
250
+ console.log(chalk.dim(' Install:'));
251
+ console.log(chalk.dim(' $ ') + chalk.cyan(torch.pipInstallCommand));
252
+ }
253
+ if (Array.isArray(torch.extraPackages) && torch.extraPackages.length) {
254
+ console.log(chalk.dim(' Extras: ') + chalk.white(torch.extraPackages.join(', ')));
255
+ }
256
+ if (torch.notes) {
257
+ console.log(chalk.dim(' Notes: ') + chalk.gray(torch.notes));
237
258
  }
238
- if (Array.isArray(torch.extraPackages) && torch.extraPackages.length)
239
- console.log(` - Extra Packages: ${torch.extraPackages.join(', ')}`);
240
- if (torch.notes) console.log(` - Notes: ${torch.notes}`);
241
259
  console.log();
242
260
  }
243
261
 
244
262
  const gpu = result.gpuRecommendation;
245
263
  if (gpu) {
246
- console.log(chalk.bold('šŸ–„ļø GPU Recommendation'));
247
- if (gpu.minimumVramGb !== undefined) console.log(` - Min VRAM: ${gpu.minimumVramGb} GB`);
248
- if (gpu.recommendedVramGb !== undefined) console.log(` - Recommended VRAM: ${gpu.recommendedVramGb} GB`);
249
- if (gpu.minComputeCapability) console.log(` - Min Compute Capability: ${gpu.minComputeCapability}`);
250
- if (Array.isArray(gpu.recommendedModels) && gpu.recommendedModels.length)
251
- console.log(` - Recommended Models: ${gpu.recommendedModels.join(', ')}`);
252
- if (Array.isArray(gpu.budgetOptions) && gpu.budgetOptions.length)
253
- console.log(` - Budget Options: ${gpu.budgetOptions.join(', ')}`);
254
- if (Array.isArray(gpu.cloudInstances) && gpu.cloudInstances.length)
255
- console.log(` - Cloud Instances: ${gpu.cloudInstances.join(', ')}`);
256
- if (gpu.notes) console.log(` - Notes: ${gpu.notes}`);
264
+ console.log(chalk.bold.green(' šŸ–„ļø Hardware Requirements'));
265
+ if (gpu.minimumVramGb !== undefined) {
266
+ console.log(chalk.dim(' Min VRAM: ') + chalk.white(`${gpu.minimumVramGb} GB`));
267
+ }
268
+ if (gpu.recommendedVramGb !== undefined) {
269
+ console.log(chalk.dim(' Optimal VRAM: ') + chalk.white(`${gpu.recommendedVramGb} GB`));
270
+ }
271
+ if (gpu.minComputeCapability) {
272
+ console.log(chalk.dim(' Min Compute: ') + chalk.white(gpu.minComputeCapability));
273
+ }
274
+ if (Array.isArray(gpu.recommendedModels) && gpu.recommendedModels.length) {
275
+ console.log(chalk.dim(' Recommended: ') + chalk.white(gpu.recommendedModels.join(', ')));
276
+ }
277
+ if (Array.isArray(gpu.budgetOptions) && gpu.budgetOptions.length) {
278
+ console.log(chalk.dim(' Budget: ') + chalk.white(gpu.budgetOptions.join(', ')));
279
+ }
280
+ if (Array.isArray(gpu.cloudInstances) && gpu.cloudInstances.length) {
281
+ console.log(chalk.dim(' Cloud: ') + chalk.white(gpu.cloudInstances.join(', ')));
282
+ }
283
+ if (gpu.notes) {
284
+ console.log(chalk.dim(' Notes: ') + chalk.gray(gpu.notes));
285
+ }
257
286
  console.log();
258
287
  }
259
288
 
260
- // Print API key requirements if available
289
+ // Enhanced API key display
261
290
  if (result.requiredApiKeys && Array.isArray(result.requiredApiKeys) && result.requiredApiKeys.length > 0) {
262
- // Separate critical and optional API keys
263
291
  const criticalKeys = result.requiredApiKeys.filter(key => key.priority === 'critical' || key.required);
264
292
  const optionalKeys = result.requiredApiKeys.filter(key => key.priority === 'optional' || !key.required);
265
293
 
266
294
  if (criticalKeys.length > 0) {
267
- console.log(chalk.bold('šŸ”‘ CRITICAL API KEYS'));
295
+ console.log(chalk.bold.red(' šŸ”‘ Required API Keys'));
268
296
  criticalKeys.forEach(apiKey => {
269
- console.log(` - ${apiKey.name} ${chalk.red('(REQUIRED)')}`);
270
- console.log(` Service: ${apiKey.service}`);
271
- console.log(` Purpose: ${apiKey.description}`);
272
- if (apiKey.example) console.log(` Format: ${apiKey.example}`);
273
- if (apiKey.documentation_url) console.log(` Docs: ${apiKey.documentation_url}`);
274
- console.log();
297
+ console.log(chalk.dim(' • ') + chalk.white(apiKey.name) + ' ' + chalk.red('(required)'));
298
+ console.log(chalk.dim(' Service: ') + chalk.gray(apiKey.service));
299
+ console.log(chalk.dim(' Purpose: ') + chalk.gray(apiKey.description));
300
+ if (apiKey.documentation_url) {
301
+ console.log(chalk.dim(' Docs: ') + chalk.blue(apiKey.documentation_url));
302
+ }
275
303
  });
304
+ console.log();
276
305
  }
277
306
 
278
307
  if (optionalKeys.length > 0) {
279
- console.log(chalk.bold('šŸ”§ OPTIONAL API KEYS'));
308
+ console.log(chalk.bold.yellow(' šŸ”§ Optional Enhancements'));
280
309
  optionalKeys.forEach(apiKey => {
281
- console.log(` - ${apiKey.name} ${chalk.yellow('(OPTIONAL)')}`);
282
- console.log(` Service: ${apiKey.service}`);
283
- console.log(` Purpose: ${apiKey.description}`);
284
- if (apiKey.example) console.log(` Format: ${apiKey.example}`);
285
- if (apiKey.documentation_url) console.log(` Docs: ${apiKey.documentation_url}`);
286
- console.log();
310
+ console.log(chalk.dim(' • ') + chalk.white(apiKey.name) + ' ' + chalk.yellow('(optional)'));
311
+ console.log(chalk.dim(' Service: ') + chalk.gray(apiKey.service));
312
+ console.log(chalk.dim(' Purpose: ') + chalk.gray(apiKey.description));
313
+ if (apiKey.documentation_url) {
314
+ console.log(chalk.dim(' Docs: ') + chalk.blue(apiKey.documentation_url));
315
+ }
287
316
  });
317
+ console.log();
288
318
  }
289
319
  }
290
320
  } catch {}
@@ -646,11 +676,11 @@ async function fetchFullSetupAndRecs(repoUrl, storedCredentials = null) {
646
676
  console.log(chalk.gray('šŸ› DEBUG: Received response from API:'));
647
677
  console.log(chalk.gray(' - Response has commands:', !!data.commands));
648
678
  console.log(chalk.gray(' - Commands count:', data.commands ? data.commands.length : 0));
649
- console.log(chalk.gray(' - Response has API keys:', !!data.requiredApiKeys));
650
- console.log(chalk.gray(' - API keys count:', data.requiredApiKeys ? data.requiredApiKeys.length : 0));
651
- console.log(chalk.gray(' - Response has GPU rec:', !!data.gpuRecommendation));
652
- console.log(chalk.gray(' - Response has CUDA rec:', !!data.cudaRecommendation));
653
- console.log(chalk.gray(' - Response has Torch rec:', !!data.torchRecommendation));
679
+ // console.log(chalk.gray(' - Response has API keys:', !!data.requiredApiKeys));
680
+ // console.log(chalk.gray(' - API keys count:', data.requiredApiKeys ? data.requiredApiKeys.length : 0));
681
+ // console.log(chalk.gray(' - Response has GPU rec:', !!data.gpuRecommendation));
682
+ // console.log(chalk.gray(' - Response has CUDA rec:', !!data.cudaRecommendation));
683
+ // console.log(chalk.gray(' - Response has Torch rec:', !!data.torchRecommendation));
654
684
  spinner.succeed('Repository analysis complete');
655
685
  return data;
656
686
  }
@@ -673,7 +703,7 @@ async function fetchFullSetupAndRecs(repoUrl, storedCredentials = null) {
673
703
  // Function to send user data to web application
674
704
  async function sendUserData(userId, userName, userEmail) {
675
705
  try {
676
- console.log(chalk.blue(`šŸ”— Attempting to register user: ${userName} (${userId})`));
706
+ console.log(chalk.blue(`Attempting to register user: ${userName} (${userId})`));
677
707
 
678
708
  const userData = {
679
709
  email: userEmail, // Use userId as email (assuming it's an email)
@@ -701,7 +731,7 @@ async function sendUserData(userId, userName, userEmail) {
701
731
  webhookUrl = process.env.GITARSENAL_WEBHOOK_URL;
702
732
  }
703
733
 
704
- console.log(chalk.gray(`šŸ“¦ Data: ${data}`));
734
+ console.log(chalk.gray(`Data: ${data}`));
705
735
 
706
736
  const urlObj = new URL(webhookUrl);
707
737
  const options = {
@@ -729,10 +759,10 @@ async function sendUserData(userId, userName, userEmail) {
729
759
  res.on('end', () => {
730
760
 
731
761
  if (res.statusCode >= 200 && res.statusCode < 300) {
732
- console.log(chalk.green('āœ… User registered on GitArsenal dashboard'));
762
+ console.log(chalk.green('User registered on GitArsenal dashboard'));
733
763
  resolve(responseData);
734
764
  } else if (res.statusCode === 409) {
735
- console.log(chalk.green('āœ… User already exists on GitArsenal dashboard'));
765
+ console.log(chalk.green('User already exists on GitArsenal dashboard'));
736
766
  resolve(responseData);
737
767
  } else {
738
768
  console.log(chalk.yellow(`āš ļø Failed to register user (status: ${res.statusCode})`));
@@ -970,6 +1000,7 @@ activateVirtualEnvironment();
970
1000
  // Check for updates
971
1001
  updateNotifier({ pkg }).notify();
972
1002
 
1003
+
973
1004
  // Display banner
974
1005
 
975
1006
  try {
@@ -986,6 +1017,26 @@ try {
986
1017
  }));
987
1018
  }
988
1019
 
1020
+ // Display info box first
1021
+ console.log(boxen(
1022
+ chalk.bold.cyan('šŸš€ GitArsenal - GPU-Accelerated Development Environments') + '\n\n' +
1023
+ chalk.white('Secure, cloud-based development environments with GPU acceleration.') + '\n' +
1024
+ chalk.white('Everything runs safely in isolated containers - no risk to your system.') + '\n\n' +
1025
+ chalk.green('āœ… No local installation required') + '\n' +
1026
+ chalk.green('āœ… Instant access to powerful GPUs') + '\n' +
1027
+ chalk.green('āœ… Pre-configured for any repository') + '\n' +
1028
+ chalk.green('āœ… Secure cloud isolation') + '\n\n' +
1029
+ chalk.gray('Your code is processed securely in the cloud.') + '\n' +
1030
+ chalk.gray('No worries about dependencies or compatibility issues.'),
1031
+ {
1032
+ padding: 1,
1033
+ margin: 0,
1034
+ borderStyle: 'round',
1035
+ borderColor: 'cyan',
1036
+ fullscreen: true
1037
+ }
1038
+ ));
1039
+
989
1040
  // Set up main command
990
1041
  program
991
1042
  .version(version)
@@ -1070,7 +1121,7 @@ async function runContainerCommand(options) {
1070
1121
  const { userId, userName, userEmail } = userCredentials;
1071
1122
 
1072
1123
  // Register user on dashboard immediately after collecting credentials
1073
- console.log(chalk.blue('\nšŸ“ Registering user on GitArsenal dashboard...'));
1124
+ console.log(chalk.blue('\nRegistering user on GitArsenal dashboard...'));
1074
1125
  // Send user data immediately so the dashboard records users
1075
1126
  await sendUserData(userId, userName, userEmail);
1076
1127
 
@@ -1317,7 +1368,7 @@ async function handleKeysAdd(options) {
1317
1368
  const { userId, userName } = userCredentials;
1318
1369
 
1319
1370
  // Register user on dashboard
1320
- console.log(chalk.blue('\nšŸ“ Registering user on GitArsenal dashboard...'));
1371
+ console.log(chalk.blue('\nRegistering user on GitArsenal dashboard...'));
1321
1372
  // Note: User data will be sent by the Python script after authentication
1322
1373
  // await sendUserData(userId, userName);
1323
1374
 
@@ -1393,7 +1444,7 @@ async function handleKeysList() {
1393
1444
  const { userId, userName } = userCredentials;
1394
1445
 
1395
1446
  // Register user on dashboard
1396
- console.log(chalk.blue('\nšŸ“ Registering user on GitArsenal dashboard...'));
1447
+ console.log(chalk.blue('\nRegistering user on GitArsenal dashboard...'));
1397
1448
  // Note: User data will be sent by the Python script after authentication
1398
1449
  // await sendUserData(userId, userName);
1399
1450
 
@@ -1429,7 +1480,7 @@ async function handleKeysView(options) {
1429
1480
  const { userId, userName } = userCredentials;
1430
1481
 
1431
1482
  // Register user on dashboard
1432
- console.log(chalk.blue('\nšŸ“ Registering user on GitArsenal dashboard...'));
1483
+ console.log(chalk.blue('\nRegistering user on GitArsenal dashboard...'));
1433
1484
  // Note: User data will be sent by the Python script after authentication
1434
1485
  // await sendUserData(userId, userName);
1435
1486
 
@@ -1481,7 +1532,7 @@ async function handleKeysDelete(options) {
1481
1532
  const { userId, userName } = userCredentials;
1482
1533
 
1483
1534
  // Register user on dashboard
1484
- console.log(chalk.blue('\nšŸ“ Registering user on GitArsenal dashboard...'));
1535
+ console.log(chalk.blue('\nRegistering user on GitArsenal dashboard...'));
1485
1536
  // Note: User data will be sent by the Python script after authentication
1486
1537
  // await sendUserData(userId, userName);
1487
1538
 
@@ -69,7 +69,7 @@ kill_claude/
69
69
  1. **Clone or download** all the files to a directory
70
70
  2. **Install dependencies** (if needed):
71
71
  ```bash
72
- pip install dataclasses # Python 3.6 only, built-in for 3.7+
72
+ uv pip install dataclasses # Python 3.6 only, built-in for 3.7+
73
73
  ```
74
74
  3. **Make the main script executable**:
75
75
  ```bash
@@ -138,7 +138,7 @@ python example_usage.py
138
138
  ### Code Execution
139
139
  - `"run pytest"` - Execute commands
140
140
  - `"git status"` - Git operations
141
- - `"npm install express"` - Package management
141
+ - `"uv pip install express"` - Package management
142
142
 
143
143
  ### Web Access
144
144
  - `"fetch https://api.github.com"` - Fetch web content
@@ -0,0 +1,130 @@
1
+ # šŸŽØ TUI Improvements for Claude Code Agent
2
+
3
+ ## Overview
4
+
5
+ The Claude Code Agent now features a beautiful Terminal User Interface (TUI) powered by [Typer](https://typer.tiangolo.com/) and [Rich](https://rich.readthedocs.io/), providing:
6
+
7
+ - **Beautiful colored output** with syntax highlighting
8
+ - **Professional command-line interface** with proper help system
9
+ - **Rich formatting** for tool execution results
10
+ - **Progress indicators** for long-running operations
11
+ - **Structured panels** for better information organization
12
+
13
+ ## New Commands
14
+
15
+ ### Interactive Mode
16
+ ```bash
17
+ python claude_code_agent.py interactive
18
+ # or
19
+ python claude_code_agent.py interactive --api-key your_key
20
+ ```
21
+
22
+ ### Single Query
23
+ ```bash
24
+ python claude_code_agent.py query "your question here"
25
+ # or
26
+ python claude_code_agent.py query "read main.py" --api-key your_key
27
+ ```
28
+
29
+ ### Setup Information
30
+ ```bash
31
+ python claude_code_agent.py setup
32
+ ```
33
+
34
+ ### Help
35
+ ```bash
36
+ python claude_code_agent.py --help
37
+ ```
38
+
39
+ ## Key Improvements
40
+
41
+ ### 1. Tool Execution Display
42
+ - **Before**: Simple print statements with basic emojis
43
+ - **After**: Rich panels with proper formatting, parameter tables, and progress indicators
44
+
45
+ ### 2. File Content Display
46
+ - **Before**: Plain text output
47
+ - **After**: Syntax highlighted code with proper line numbers and file type detection
48
+
49
+ ### 3. Error Handling
50
+ - **Before**: Plain error messages
51
+ - **After**: Beautiful error panels with helpful instructions
52
+
53
+ ### 4. Help System
54
+ - **Before**: Single text block
55
+ - **After**: Organized columns with categorized commands and examples
56
+
57
+ ### 5. Status Display
58
+ - **Before**: Simple text list
59
+ - **After**: Professional tables and trees showing system information
60
+
61
+ ### 6. Interactive Mode
62
+ - **Before**: Basic input/output
63
+ - **After**: Rich prompts, welcome banners, and formatted responses
64
+
65
+ ## Technical Details
66
+
67
+ ### Dependencies Added
68
+ - `typer>=0.12.0` - Modern CLI framework
69
+ - `rich>=13.0.0` - Rich text formatting and TUI components
70
+
71
+ ### Key Features Implemented
72
+
73
+ #### Rich Console Integration
74
+ - All output now uses Rich Console for beautiful formatting
75
+ - Automatic color detection and fallback for different terminals
76
+ - Consistent styling across all output types
77
+
78
+ #### Smart Content Formatting
79
+ - **Code files**: Automatic syntax highlighting based on file extension
80
+ - **Directory listings**: Clean tree-like structure with proper indentation
81
+ - **Command output**: Color-coded based on success/error status
82
+ - **Todo lists**: Professional tables with status indicators
83
+
84
+ #### Progress Indicators
85
+ - Spinning progress indicators for tool execution
86
+ - Non-blocking progress display that disappears when complete
87
+ - Clear visual feedback for long-running operations
88
+
89
+ #### Error Handling
90
+ - Beautiful error panels with clear instructions
91
+ - Structured error information with proper styling
92
+ - Helpful suggestions for common configuration issues
93
+
94
+ ### Backwards Compatibility
95
+
96
+ The agent maintains full backwards compatibility:
97
+ - `python claude_code_agent.py` still starts interactive mode
98
+ - Single arguments are still treated as queries
99
+ - All existing functionality preserved
100
+
101
+ ## Usage Examples
102
+
103
+ ### Beautiful File Reading
104
+ When you run `query "read main.py"`, you now see:
105
+ - Syntax highlighted code
106
+ - Proper line numbers
107
+ - Clean panel borders
108
+ - File path information
109
+
110
+ ### Enhanced Todo Management
111
+ Todo lists are now displayed as professional tables with:
112
+ - Status indicators (ā³ Pending, šŸ”„ In Progress, āœ… Completed)
113
+ - Organized columns
114
+ - Clean borders and styling
115
+
116
+ ### Improved Error Messages
117
+ Configuration errors show:
118
+ - Clear problem description
119
+ - Step-by-step solutions
120
+ - Proper color coding
121
+ - Professional panel layout
122
+
123
+ ## Future Enhancements
124
+
125
+ The TUI foundation enables future improvements like:
126
+ - Interactive file selection
127
+ - Real-time command output streaming
128
+ - Tabbed interfaces for multiple operations
129
+ - Interactive configuration wizard
130
+ - Dashboard views for project statistics
@@ -24,6 +24,17 @@ from typing import List, Dict, Any, Optional, Union
24
24
  from dataclasses import dataclass
25
25
  from enum import Enum
26
26
  import anthropic
27
+ import typer
28
+ from rich.console import Console
29
+ from rich.table import Table
30
+ from rich.panel import Panel
31
+ from rich.columns import Columns
32
+ from rich.text import Text
33
+ from rich.progress import Progress, SpinnerColumn, TextColumn
34
+ from rich.tree import Tree
35
+ from rich.syntax import Syntax
36
+ from rich.prompt import Prompt
37
+ from rich import print as rprint
27
38
 
28
39
  def load_tool_modules():
29
40
  """Load all tool modules from the tools directory."""
@@ -412,61 +423,165 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
412
423
  tool_name = tool_call.name
413
424
  tool_input = tool_call.input
414
425
 
415
- # Print clean tool usage information like Claude Code
426
+ # Initialize Rich console for beautiful output
427
+ console = Console()
428
+
429
+ # Create tool execution panel
430
+ tool_title = f"šŸ› ļø Using Tool: {tool_name}"
416
431
  if total_calls > 1:
417
- print(f"šŸ› ļø Using tool: {tool_name}")
418
- else:
419
- print(f"šŸ› ļø Using tool: {tool_name}")
420
-
421
- # Show parameters in a clean format
432
+ tool_title += f" ({call_num}/{total_calls})"
433
+
434
+ # Format parameters beautifully
422
435
  if tool_input:
423
436
  # Special handling for TodoWrite to show actual todos
424
437
  if tool_name == "TodoWrite" and "todos" in tool_input:
425
438
  todos = tool_input["todos"]
426
- print(f" Parameters: todos=[{len(todos)} items]")
427
- if todos:
428
- print(" Todo Items:")
429
- for i, todo in enumerate(todos, 1):
430
- status_emoji = {"pending": "ā³", "in_progress": "šŸ”„", "completed": "āœ…"}.get(todo.get("status", "pending"), "ā“")
431
- content = todo.get("content", "No description")
432
- if len(content) > 60:
433
- content = content[:60] + "..."
434
- print(f" {i}. {status_emoji} {content}")
439
+
440
+ # Create todo table
441
+ todo_table = Table(show_header=True, header_style="bold magenta")
442
+ todo_table.add_column("#", style="dim", width=3)
443
+ todo_table.add_column("Status", width=10)
444
+ todo_table.add_column("Task", style="cyan")
445
+
446
+ for i, todo in enumerate(todos, 1):
447
+ status_emoji = {"pending": "ā³ Pending", "in_progress": "šŸ”„ In Progress", "completed": "āœ… Completed"}.get(todo.get("status", "pending"), "ā“ Unknown")
448
+ content = todo.get("content", "No description")
449
+ if len(content) > 80:
450
+ content = content[:80] + "..."
451
+ todo_table.add_row(str(i), status_emoji, content)
452
+
453
+ params_panel = Panel(
454
+ todo_table,
455
+ title=f"Todo List ({len(todos)} items)",
456
+ border_style="blue"
457
+ )
435
458
  else:
436
- params_display = []
459
+ # Format other parameters in a table
460
+ param_table = Table(show_header=True, header_style="bold green")
461
+ param_table.add_column("Parameter", style="yellow", width=20)
462
+ param_table.add_column("Value", style="white")
463
+
437
464
  for key, value in tool_input.items():
438
- if isinstance(value, str) and len(value) > 80:
439
- params_display.append(f"{key}={value[:80]}...")
440
- elif isinstance(value, list) and len(value) > 3:
441
- params_display.append(f"{key}=[{len(value)} items]")
465
+ if isinstance(value, str) and len(value) > 100:
466
+ display_value = value[:100] + "..."
467
+ elif isinstance(value, list) and len(value) > 5:
468
+ display_value = f"[List with {len(value)} items]"
469
+ elif isinstance(value, dict):
470
+ display_value = f"[Dict with {len(value)} keys]"
442
471
  else:
443
- params_display.append(f"{key}={value}")
444
- print(f" Parameters: {', '.join(params_display)}")
472
+ display_value = str(value)
473
+ param_table.add_row(key, display_value)
474
+
475
+ params_panel = Panel(
476
+ param_table,
477
+ title="Parameters",
478
+ border_style="green"
479
+ )
480
+
481
+ # Display tool info with parameters
482
+ console.print(Panel(
483
+ params_panel,
484
+ title=tool_title,
485
+ border_style="bright_blue",
486
+ padding=(0, 1)
487
+ ))
488
+ else:
489
+ # Display simple tool info without parameters
490
+ console.print(Panel(
491
+ "[dim]No parameters[/dim]",
492
+ title=tool_title,
493
+ border_style="bright_blue",
494
+ padding=(0, 1)
495
+ ))
445
496
 
446
497
  try:
447
- # Execute the tool
448
- result = self._execute_builtin_tool(tool_name, tool_input)
449
-
450
- # Print success message
451
- print(f"\nāœ… Tool {tool_name} completed successfully")
498
+ # Execute the tool with progress indicator
499
+ with Progress(
500
+ SpinnerColumn(),
501
+ TextColumn("[progress.description]{task.description}"),
502
+ console=console,
503
+ transient=True
504
+ ) as progress:
505
+ task = progress.add_task(f"Executing {tool_name}...", total=None)
506
+ result = self._execute_builtin_tool(tool_name, tool_input)
452
507
 
453
- # Print result in a clean format
508
+ # Display success message with result
454
509
  if result and result.strip():
455
- print(f"\nšŸ“‹ Tool Result:")
456
- # Truncate very long results for readability
457
- if len(result) > 5000:
458
- print(result[:5000] + f"\n\n[Output truncated - showing first 5000 characters of {len(result)} total]")
510
+ # Format result based on content type
511
+ if tool_name == "Read" and "→" in result: # File content with line numbers
512
+ # Syntax highlight for code files
513
+ if any(ext in str(tool_input.get('file_path', '')) for ext in ['.py', '.js', '.ts', '.json', '.yaml', '.md']):
514
+ file_ext = str(tool_input.get('file_path', '')).split('.')[-1] if '.' in str(tool_input.get('file_path', '')) else 'text'
515
+ lexer = {
516
+ 'py': 'python', 'js': 'javascript', 'ts': 'typescript',
517
+ 'json': 'json', 'yaml': 'yaml', 'yml': 'yaml', 'md': 'markdown'
518
+ }.get(file_ext, 'text')
519
+
520
+ # Clean the line numbers for syntax highlighting
521
+ clean_content = '\n'.join(line.split('→', 1)[1] if '→' in line else line for line in result.split('\n'))
522
+ syntax = Syntax(clean_content, lexer, line_numbers=True, theme="monokai")
523
+ result_panel = Panel(
524
+ syntax,
525
+ title=f"šŸ“„ File Content: {tool_input.get('file_path', 'Unknown')}",
526
+ border_style="green"
527
+ )
528
+ else:
529
+ result_panel = Panel(
530
+ result,
531
+ title="šŸ“„ File Content",
532
+ border_style="green"
533
+ )
534
+ elif tool_name == "LS": # Directory listing
535
+ result_panel = Panel(
536
+ result,
537
+ title="šŸ“ Directory Contents",
538
+ border_style="blue"
539
+ )
540
+ elif tool_name == "Bash": # Command output
541
+ result_panel = Panel(
542
+ Text(result, style="green" if "Error" not in result else "red"),
543
+ title="šŸ’» Command Output",
544
+ border_style="yellow"
545
+ )
459
546
  else:
460
- print(result)
547
+ # Truncate very long results for readability
548
+ display_result = result
549
+ if len(result) > 5000:
550
+ display_result = result[:5000] + f"\n\n[dim][Output truncated - showing first 5000 characters of {len(result)} total][/dim]"
551
+
552
+ result_panel = Panel(
553
+ display_result,
554
+ title="šŸ“‹ Tool Result",
555
+ border_style="green"
556
+ )
557
+
558
+ # Success panel
559
+ success_panel = Panel(
560
+ f"āœ… [bold green]Tool {tool_name} completed successfully[/bold green]",
561
+ border_style="green",
562
+ padding=(0, 1)
563
+ )
564
+
565
+ console.print(success_panel)
566
+ console.print(result_panel)
461
567
  else:
462
- print(f"\nšŸ“‹ Tool Result: (no output)")
568
+ console.print(Panel(
569
+ "āœ… [bold green]Tool completed successfully[/bold green]\n[dim](no output)[/dim]",
570
+ title=f"Tool {tool_name}",
571
+ border_style="green",
572
+ padding=(0, 1)
573
+ ))
463
574
 
464
- print() # Empty line for readability
575
+ console.print() # Empty line for readability
465
576
  return result if result is not None else ""
466
577
 
467
578
  except Exception as e:
468
- print(f"\nāŒ Tool {tool_name} failed: {str(e)}")
469
- print()
579
+ console.print(Panel(
580
+ f"āŒ [bold red]Tool {tool_name} failed:[/bold red]\n{str(e)}",
581
+ border_style="red",
582
+ padding=(0, 1)
583
+ ))
584
+ console.print()
470
585
  return f"Error executing {tool_name}: {str(e)}"
471
586
 
472
587
  def _execute_builtin_tool(self, tool_name: str, tool_input: Dict[str, Any]) -> str:
@@ -752,19 +867,35 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
752
867
 
753
868
  def interactive_mode(self):
754
869
  """
755
- Run the agent in interactive mode, similar to Claude Code's CLI.
870
+ Run the agent in interactive mode with beautiful TUI using Rich and Typer.
756
871
  """
757
- print("šŸ¤– Claude Code Agent - Interactive Mode")
758
- print("Powered by Anthropic API with claude-sonnet-4-20250514")
759
- print("Type 'help' for available commands, 'exit' to quit")
760
- print("-" * 60)
872
+ console = Console()
873
+
874
+ # Welcome banner
875
+ welcome_panel = Panel(
876
+ "[bold blue]šŸ¤– Claude Code Agent[/bold blue]\n" +
877
+ "[dim]Powered by Anthropic API with claude-sonnet-4-20250514[/dim]\n\n" +
878
+ "[yellow]Commands:[/yellow]\n" +
879
+ "• Type your request naturally\n" +
880
+ "• 'help' - Show available commands\n" +
881
+ "• 'status' - Show current status\n" +
882
+ "• 'exit' - Quit the agent",
883
+ title="šŸš€ Welcome to Interactive Mode",
884
+ border_style="bright_blue",
885
+ padding=(1, 2)
886
+ )
887
+ console.print(welcome_panel)
761
888
 
762
889
  while True:
763
890
  try:
764
- user_input = input("\nšŸ‘¤ You: ").strip()
891
+ # Use Rich prompt for better input experience
892
+ user_input = Prompt.ask("\n[bold cyan]šŸ‘¤ You[/bold cyan]").strip()
765
893
 
766
894
  if user_input.lower() in ['exit', 'quit', 'bye']:
767
- print("šŸ‘‹ Goodbye!")
895
+ console.print(Panel(
896
+ "[bold yellow]šŸ‘‹ Thank you for using Claude Code Agent![/bold yellow]",
897
+ border_style="yellow"
898
+ ))
768
899
  break
769
900
  elif user_input.lower() == 'help':
770
901
  self.show_help()
@@ -775,108 +906,281 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
775
906
  elif not user_input:
776
907
  continue
777
908
 
778
- print("šŸ¤– Claude Code Agent:")
909
+ # Show that Claude is thinking
910
+ thinking_panel = Panel(
911
+ "[bold blue]šŸ¤– Claude Code Agent[/bold blue] is processing your request...",
912
+ border_style="blue",
913
+ padding=(0, 1)
914
+ )
915
+ console.print(thinking_panel)
916
+
779
917
  response = self.process_query(user_input)
780
918
  if response.strip():
781
- print(response)
919
+ response_panel = Panel(
920
+ response,
921
+ title="šŸ¤– Claude Code Agent Response",
922
+ border_style="bright_green",
923
+ padding=(0, 1)
924
+ )
925
+ console.print(response_panel)
782
926
 
783
927
  except KeyboardInterrupt:
784
- print("\n\nšŸ‘‹ Goodbye!")
928
+ console.print("\n\n[bold yellow]šŸ‘‹ Goodbye![/bold yellow]")
785
929
  break
786
930
  except Exception as e:
787
- print(f"āŒ Error: {str(e)}")
931
+ console.print(Panel(
932
+ f"[bold red]āŒ Error:[/bold red] {str(e)}",
933
+ border_style="red"
934
+ ))
788
935
 
789
936
  def show_help(self):
790
- """Show help information."""
791
- help_text = """
792
- Available commands and capabilities:
793
-
794
- šŸ“ File Operations:
795
- - read <file> : Read file contents
796
- - list [directory] : List files and directories
797
- - find <pattern> : Find files matching pattern
798
- - edit <file> : Edit file contents
799
-
800
- šŸ” Search:
801
- - search <term> : Search in files
802
- - web search <term> : Search the web
803
-
804
- āš™ļø Code Execution:
805
- - run <command> : Execute bash commands
806
- - npm/pip/git commands : Execute package manager commands
807
-
808
- 🌐 Web Access:
809
- - fetch <url> : Fetch content from URL
810
- - web search <query> : Search the web
811
-
812
- šŸ“‹ Task Management:
813
- - Complex tasks automatically create todo lists
814
- - Multi-step operations are tracked
815
-
816
- šŸ’” Examples:
817
- - "read main.py"
818
- - "search for TODO comments"
819
- - "run pytest tests/"
820
- - "implement user authentication system"
821
- - "web search latest Python features 2024"
822
-
823
- Special commands:
824
- - help : Show this help
825
- - status : Show current status
826
- - exit : Quit the agent
827
-
828
- This agent uses the real Anthropic API with claude-sonnet-4-20250514
829
- and implements all Claude Code tools with proper system prompts.
830
- """
831
- print(help_text)
937
+ """Show help information with beautiful formatting."""
938
+ console = Console()
939
+
940
+ # Create help sections
941
+ file_ops = Table(show_header=False, box=None, padding=(0, 1))
942
+ file_ops.add_column(style="yellow")
943
+ file_ops.add_column(style="white")
944
+ file_ops.add_row("read <file>", "Read file contents")
945
+ file_ops.add_row("list [directory]", "List files and directories")
946
+ file_ops.add_row("find <pattern>", "Find files matching pattern")
947
+ file_ops.add_row("edit <file>", "Edit file contents")
948
+
949
+ search_ops = Table(show_header=False, box=None, padding=(0, 1))
950
+ search_ops.add_column(style="yellow")
951
+ search_ops.add_column(style="white")
952
+ search_ops.add_row("search <term>", "Search in files")
953
+ search_ops.add_row("web search <term>", "Search the web")
954
+
955
+ code_ops = Table(show_header=False, box=None, padding=(0, 1))
956
+ code_ops.add_column(style="yellow")
957
+ code_ops.add_column(style="white")
958
+ code_ops.add_row("run <command>", "Execute bash commands")
959
+ code_ops.add_row("npm/uv pip/git commands", "Execute package manager commands")
960
+
961
+ web_ops = Table(show_header=False, box=None, padding=(0, 1))
962
+ web_ops.add_column(style="yellow")
963
+ web_ops.add_column(style="white")
964
+ web_ops.add_row("fetch <url>", "Fetch content from URL")
965
+ web_ops.add_row("web search <query>", "Search the web")
966
+
967
+ examples = Table(show_header=False, box=None, padding=(0, 1))
968
+ examples.add_column(style="green")
969
+ examples.add_row('"read main.py"')
970
+ examples.add_row('"search for TODO comments"')
971
+ examples.add_row('"run pytest tests/"')
972
+ examples.add_row('"implement user authentication system"')
973
+ examples.add_row('"web search latest Python features 2024"')
974
+
975
+ special = Table(show_header=False, box=None, padding=(0, 1))
976
+ special.add_column(style="cyan")
977
+ special.add_column(style="white")
978
+ special.add_row("help", "Show this help")
979
+ special.add_row("status", "Show current status")
980
+ special.add_row("exit", "Quit the agent")
981
+
982
+ # Create main help layout
983
+ help_panels = [
984
+ Panel(file_ops, title="šŸ“ File Operations", border_style="blue"),
985
+ Panel(search_ops, title="šŸ” Search", border_style="green"),
986
+ Panel(code_ops, title="āš™ļø Code Execution", border_style="yellow"),
987
+ Panel(web_ops, title="🌐 Web Access", border_style="magenta"),
988
+ Panel("Complex tasks automatically create todo lists\nMulti-step operations are tracked", title="šŸ“‹ Task Management", border_style="cyan"),
989
+ Panel(examples, title="šŸ’” Examples", border_style="bright_green"),
990
+ Panel(special, title="šŸŽ® Special Commands", border_style="red")
991
+ ]
992
+
993
+ # Display help in columns
994
+ console.print(Panel(
995
+ Columns(help_panels, equal=True, expand=True),
996
+ title="šŸ†˜ Claude Code Agent Help",
997
+ subtitle="Powered by Anthropic API with claude-sonnet-4-20250514",
998
+ border_style="bright_blue",
999
+ padding=(1, 2)
1000
+ ))
832
1001
 
833
1002
  def show_status(self):
834
- """Show current status."""
835
- print(f"""
836
- šŸ“Š Claude Code Agent Status:
837
-
838
- šŸ”§ Configuration:
839
- - API Key: {'āœ… Set' if self.api_key else 'āŒ Missing'}
840
- - Model: claude-sonnet-4-20250514
841
- - Working Directory: {self.working_dir}
842
- - Git Repository: {'Yes' if self.is_git_repo else 'No'}
1003
+ """Show current status with beautiful formatting."""
1004
+ console = Console()
1005
+
1006
+ # Configuration status
1007
+ config_table = Table(show_header=False, box=None)
1008
+ config_table.add_column("Setting", style="yellow")
1009
+ config_table.add_column("Value", style="green")
1010
+ config_table.add_row("API Key", "āœ… Set" if self.api_key else "āŒ Missing")
1011
+ config_table.add_row("Model", "claude-sonnet-4-20250514")
1012
+ config_table.add_row("Working Directory", self.working_dir)
1013
+ config_table.add_row("Git Repository", "Yes" if self.is_git_repo else "No")
1014
+
1015
+ # Session status
1016
+ session_table = Table(show_header=False, box=None)
1017
+ session_table.add_column("Metric", style="yellow")
1018
+ session_table.add_column("Count", style="cyan")
1019
+ session_table.add_row("Conversation Messages", str(len(self.conversation_history)))
1020
+ session_table.add_row("Todo Items", str(len(self.todo_list)))
1021
+ session_table.add_row("Loaded Prompts", str(len(PROMPT_CONTENT)))
1022
+ session_table.add_row("Available Tools", str(len(TOOL_SCHEMAS)))
1023
+
1024
+ # Tools tree
1025
+ tools_tree = Tree("šŸ› ļø Available Tools")
1026
+ for tool in TOOL_SCHEMAS:
1027
+ tools_tree.add(f"[cyan]{tool['name']}[/cyan]")
1028
+
1029
+ # Status panels
1030
+ status_panels = [
1031
+ Panel(config_table, title="šŸ”§ Configuration", border_style="blue"),
1032
+ Panel(session_table, title="šŸ“ Session", border_style="green"),
1033
+ Panel(tools_tree, title="šŸ› ļø Tools", border_style="yellow")
1034
+ ]
1035
+
1036
+ console.print(Panel(
1037
+ Columns(status_panels, equal=True),
1038
+ title="šŸ“Š Claude Code Agent Status",
1039
+ border_style="bright_blue",
1040
+ padding=(1, 1)
1041
+ ))
843
1042
 
844
- šŸ“ Session:
845
- - Conversation History: {len(self.conversation_history)} messages
846
- - Todo Items: {len(self.todo_list)} items
847
1043
 
848
- šŸ“‹ Loaded Prompts: {len(PROMPT_CONTENT)} markdown files
849
-
850
- šŸ› ļø Available Tools: {len(TOOL_SCHEMAS)} tools loaded from modules
851
- - {', '.join([tool['name'] for tool in TOOL_SCHEMAS])}
852
- """)
1044
+ # Create Typer app
1045
+ app = typer.Typer(
1046
+ name="claude-code-agent",
1047
+ help="šŸ¤– Claude Code Agent - Anthropic API Integration with Beautiful TUI",
1048
+ add_completion=False,
1049
+ rich_markup_mode="rich"
1050
+ )
853
1051
 
1052
+ @app.command()
1053
+ def interactive(
1054
+ api_key: str = typer.Option(None, "--api-key", "-k", help="Anthropic API key (or set ANTHROPIC_API_KEY env var)"),
1055
+ ):
1056
+ """šŸš€ Start interactive mode with beautiful TUI."""
1057
+ console = Console()
1058
+
1059
+ try:
1060
+ agent = ClaudeCodeAgent(api_key=api_key)
1061
+ agent.interactive_mode()
1062
+ except ValueError as e:
1063
+ console.print(Panel(
1064
+ f"[bold red]āŒ Configuration Error:[/bold red]\n{str(e)}\n\n" +
1065
+ "[yellow]To fix this:[/yellow]\n" +
1066
+ "1. Get your Anthropic API key from: https://console.anthropic.com/\n" +
1067
+ "2. Set environment variable: [cyan]export ANTHROPIC_API_KEY=your_key_here[/cyan]\n" +
1068
+ "3. Or use: [cyan]--api-key your_key[/cyan] parameter",
1069
+ title="Setup Required",
1070
+ border_style="red"
1071
+ ))
1072
+ raise typer.Exit(1)
1073
+ except Exception as e:
1074
+ console.print(Panel(
1075
+ f"[bold red]āŒ Error:[/bold red] {str(e)}",
1076
+ border_style="red"
1077
+ ))
1078
+ raise typer.Exit(1)
854
1079
 
855
- def main():
856
- """Main entry point for the Claude Code Agent."""
1080
+ @app.command()
1081
+ def query(
1082
+ prompt: str = typer.Argument(..., help="Your query or command"),
1083
+ api_key: str = typer.Option(None, "--api-key", "-k", help="Anthropic API key (or set ANTHROPIC_API_KEY env var)"),
1084
+ ):
1085
+ """šŸ’¬ Process a single query and exit."""
1086
+ console = Console()
1087
+
857
1088
  try:
858
- agent = ClaudeCodeAgent()
1089
+ agent = ClaudeCodeAgent(api_key=api_key)
1090
+
1091
+ # Show processing indicator
1092
+ with Progress(
1093
+ SpinnerColumn(),
1094
+ TextColumn("[progress.description]{task.description}"),
1095
+ console=console,
1096
+ transient=True
1097
+ ) as progress:
1098
+ task = progress.add_task("Processing your query...", total=None)
1099
+ response = agent.process_query(prompt)
859
1100
 
860
- if len(sys.argv) > 1:
861
- # Process single query from command line
862
- query = ' '.join(sys.argv[1:])
863
- response = agent.process_query(query)
864
- print(response)
1101
+ if response.strip():
1102
+ console.print(Panel(
1103
+ response,
1104
+ title="šŸ¤– Claude Code Agent Response",
1105
+ border_style="bright_green"
1106
+ ))
865
1107
  else:
866
- # Interactive mode
867
- agent.interactive_mode()
1108
+ console.print("[dim]No response generated.[/dim]")
868
1109
 
869
1110
  except ValueError as e:
870
- print(f"āŒ Configuration Error: {str(e)}")
871
- print("\nTo fix this:")
872
- print("1. Get your Anthropic API key from: https://console.anthropic.com/")
873
- print("2. Set environment variable: export ANTHROPIC_API_KEY=your_key_here")
874
- print("3. Or pass it as parameter: ClaudeCodeAgent(api_key='your_key')")
875
- sys.exit(1)
1111
+ console.print(Panel(
1112
+ f"[bold red]āŒ Configuration Error:[/bold red]\n{str(e)}\n\n" +
1113
+ "[yellow]To fix this:[/yellow]\n" +
1114
+ "1. Get your Anthropic API key from: https://console.anthropic.com/\n" +
1115
+ "2. Set environment variable: [cyan]export ANTHROPIC_API_KEY=your_key_here[/cyan]\n" +
1116
+ "3. Or use: [cyan]--api-key your_key[/cyan] parameter",
1117
+ title="Setup Required",
1118
+ border_style="red"
1119
+ ))
1120
+ raise typer.Exit(1)
876
1121
  except Exception as e:
877
- print(f"āŒ Error: {str(e)}")
878
- sys.exit(1)
1122
+ console.print(Panel(
1123
+ f"[bold red]āŒ Error:[/bold red] {str(e)}",
1124
+ border_style="red"
1125
+ ))
1126
+ raise typer.Exit(1)
1127
+
1128
+ @app.command()
1129
+ def setup():
1130
+ """āš™ļø Show setup instructions and system info."""
1131
+ console = Console()
1132
+
1133
+ # System info
1134
+ system_info = Table(show_header=False, box=None)
1135
+ system_info.add_column("Property", style="yellow")
1136
+ system_info.add_column("Value", style="cyan")
1137
+ system_info.add_row("Platform", os.uname().sysname if hasattr(os, 'uname') else 'unknown')
1138
+ system_info.add_row("Working Directory", os.getcwd())
1139
+ system_info.add_row("Git Repository", "Yes" if os.path.exists('.git') else "No")
1140
+ system_info.add_row("API Key Status", "āœ… Set" if os.getenv('ANTHROPIC_API_KEY') else "āŒ Missing")
1141
+
1142
+ # Setup instructions
1143
+ setup_instructions = """[bold yellow]1. Get your API key:[/bold yellow]
1144
+ Visit: https://console.anthropic.com/
1145
+
1146
+ [bold yellow]2. Set environment variable:[/bold yellow]
1147
+ [cyan]export ANTHROPIC_API_KEY=your_key_here[/cyan]
1148
+
1149
+ [bold yellow]3. Install dependencies:[/bold yellow]
1150
+ [cyan]uv pip install -r requirements.txt[/cyan]
1151
+
1152
+ [bold yellow]4. Run the agent:[/bold yellow]
1153
+ [cyan]python claude_code_agent.py interactive[/cyan]
1154
+ or
1155
+ [cyan]python claude_code_agent.py query "your question"[/cyan]"""
1156
+
1157
+ console.print(Panel(
1158
+ Columns([
1159
+ Panel(system_info, title="šŸ” System Info", border_style="blue"),
1160
+ Panel(setup_instructions, title="šŸ“‹ Setup Instructions", border_style="green")
1161
+ ], equal=True),
1162
+ title="āš™ļø Claude Code Agent Setup",
1163
+ border_style="bright_blue",
1164
+ padding=(1, 1)
1165
+ ))
1166
+
1167
+ def main():
1168
+ """Main entry point - delegate to Typer app."""
1169
+ app()
879
1170
 
880
1171
 
881
1172
  if __name__ == "__main__":
882
- main()
1173
+ # Support legacy mode for backwards compatibility
1174
+ if len(sys.argv) == 1:
1175
+ # No arguments - start interactive mode
1176
+ app(["interactive"])
1177
+ elif len(sys.argv) == 2 and sys.argv[1] not in ["interactive", "query", "setup", "--help", "-h"]:
1178
+ # Single argument that's not a command - treat as query
1179
+ app(["query", sys.argv[1]])
1180
+ elif len(sys.argv) > 2 and sys.argv[1] not in ["interactive", "query", "setup"]:
1181
+ # Multiple arguments not starting with command - treat as query
1182
+ query_text = " ".join(sys.argv[1:])
1183
+ app(["query", query_text])
1184
+ else:
1185
+ # Use normal Typer command parsing
1186
+ app()
@@ -1 +1,3 @@
1
- anthropic>=0.25.0
1
+ anthropic>=0.25.0
2
+ typer>=0.12.0
3
+ rich>=13.0.0
@@ -51,7 +51,7 @@ class BashTool:
51
51
  "type": "string"
52
52
  },
53
53
  "description": {
54
- "description": " Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: npm install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'",
54
+ "description": " Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: uv pip install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'",
55
55
  "type": "string"
56
56
  },
57
57
  "run_in_background": {
package/lib/sandbox.js CHANGED
@@ -150,22 +150,26 @@ async function runContainer(options) {
150
150
  return new Promise((resolve, reject) => {
151
151
  pythonProcess.on('close', (code) => {
152
152
  if (code === 0) {
153
- console.log(chalk.green('āœ… Container launched successfully'));
153
+ console.log(chalk.green('āœ“ Environment ready'));
154
+ console.log(chalk.dim(' Your secure development environment is now active'));
154
155
  resolve();
155
156
  } else {
156
- console.log(chalk.red(`āŒ Container launch failed with exit code ${code}`));
157
+ console.log(chalk.red('āœ— Launch failed'));
158
+ console.log(chalk.dim(` Exit code: ${code}`));
157
159
  reject(new Error(`Process exited with code ${code}`));
158
160
  }
159
161
  });
160
162
 
161
163
  // Handle process errors
162
164
  pythonProcess.on('error', (error) => {
163
- console.log(chalk.red(`āŒ Failed to start Python process: ${error.message}`));
165
+ console.log(chalk.red('āœ— Process error'));
166
+ console.log(chalk.dim(` ${error.message}`));
164
167
  reject(error);
165
168
  });
166
169
  });
167
170
  } catch (error) {
168
- console.log(chalk.red(`āŒ Error launching container: ${error.message}`));
171
+ console.log(chalk.red('āœ— Launch error'));
172
+ console.log(chalk.dim(` ${error.message}`));
169
173
  throw error;
170
174
  }
171
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.9.83",
3
+ "version": "1.9.85",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {