gitarsenal-cli 1.9.76 → 1.9.78

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 (51) hide show
  1. package/.venv_status.json +1 -1
  2. package/bin/gitarsenal.js +276 -18
  3. package/gitingest-integration.js +274 -0
  4. package/kill_claude/prompts/claude-code-tool-prompts.md +11 -1
  5. package/kill_claude/tools/__pycache__/bash_output_tool.cpython-312.pyc +0 -0
  6. package/kill_claude/tools/__pycache__/bash_tool.cpython-312.pyc +0 -0
  7. package/kill_claude/tools/__pycache__/edit_tool.cpython-312.pyc +0 -0
  8. package/kill_claude/tools/__pycache__/exit_plan_mode_tool.cpython-312.pyc +0 -0
  9. package/kill_claude/tools/__pycache__/glob_tool.cpython-312.pyc +0 -0
  10. package/kill_claude/tools/__pycache__/grep_tool.cpython-312.pyc +0 -0
  11. package/kill_claude/tools/__pycache__/kill_bash_tool.cpython-312.pyc +0 -0
  12. package/kill_claude/tools/__pycache__/ls_tool.cpython-312.pyc +0 -0
  13. package/kill_claude/tools/__pycache__/multiedit_tool.cpython-312.pyc +0 -0
  14. package/kill_claude/tools/__pycache__/notebook_edit_tool.cpython-312.pyc +0 -0
  15. package/kill_claude/tools/__pycache__/read_tool.cpython-312.pyc +0 -0
  16. package/kill_claude/tools/__pycache__/task_tool.cpython-312.pyc +0 -0
  17. package/kill_claude/tools/__pycache__/todo_write_tool.cpython-312.pyc +0 -0
  18. package/kill_claude/tools/__pycache__/web_fetch_tool.cpython-312.pyc +0 -0
  19. package/kill_claude/tools/__pycache__/web_search_tool.cpython-312.pyc +0 -0
  20. package/kill_claude/tools/__pycache__/write_tool.cpython-312.pyc +0 -0
  21. package/package.json +1 -1
  22. package/python/__pycache__/analyze_repo_api_keys.cpython-312.pyc +0 -0
  23. package/python/__pycache__/credentials_manager.cpython-312.pyc +0 -0
  24. package/python/credentials_manager.py +0 -169
  25. package/python/gitarsenal_keys.py +8 -2
  26. package/python/test_modalSandboxScript.py +13 -43
  27. package/kill_claude/nanoGPT/.gitattributes +0 -3
  28. package/kill_claude/nanoGPT/LICENSE +0 -21
  29. package/kill_claude/nanoGPT/README.md +0 -227
  30. package/kill_claude/nanoGPT/assets/gpt2_124M_loss.png +0 -0
  31. package/kill_claude/nanoGPT/assets/nanogpt.jpg +0 -0
  32. package/kill_claude/nanoGPT/bench.py +0 -117
  33. package/kill_claude/nanoGPT/config/eval_gpt2.py +0 -8
  34. package/kill_claude/nanoGPT/config/eval_gpt2_large.py +0 -8
  35. package/kill_claude/nanoGPT/config/eval_gpt2_medium.py +0 -8
  36. package/kill_claude/nanoGPT/config/eval_gpt2_xl.py +0 -8
  37. package/kill_claude/nanoGPT/config/finetune_shakespeare.py +0 -25
  38. package/kill_claude/nanoGPT/config/train_gpt2.py +0 -25
  39. package/kill_claude/nanoGPT/config/train_shakespeare_char.py +0 -37
  40. package/kill_claude/nanoGPT/configurator.py +0 -47
  41. package/kill_claude/nanoGPT/data/openwebtext/prepare.py +0 -81
  42. package/kill_claude/nanoGPT/data/openwebtext/readme.md +0 -15
  43. package/kill_claude/nanoGPT/data/shakespeare/prepare.py +0 -33
  44. package/kill_claude/nanoGPT/data/shakespeare/readme.md +0 -9
  45. package/kill_claude/nanoGPT/data/shakespeare_char/prepare.py +0 -68
  46. package/kill_claude/nanoGPT/data/shakespeare_char/readme.md +0 -9
  47. package/kill_claude/nanoGPT/model.py +0 -330
  48. package/kill_claude/nanoGPT/sample.py +0 -89
  49. package/kill_claude/nanoGPT/scaling_laws.ipynb +0 -792
  50. package/kill_claude/nanoGPT/train.py +0 -336
  51. package/kill_claude/nanoGPT/transformer_sizing.ipynb +0 -402
package/.venv_status.json CHANGED
@@ -1 +1 @@
1
- {"created":"2025-08-17T05:50:53.850Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-08-17T17:20:43.416Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
package/bin/gitarsenal.js CHANGED
@@ -15,6 +15,7 @@ const { spawn } = require('child_process');
15
15
  const fs = require('fs');
16
16
  const https = require('https');
17
17
  const http = require('http');
18
+ const { fetchGitIngestData } = require('../gitingest-integration');
18
19
 
19
20
  // Function to activate virtual environment
20
21
  function activateVirtualEnvironment() {
@@ -22,7 +23,7 @@ function activateVirtualEnvironment() {
22
23
  const venvPath = path.join(__dirname, '..', '.venv');
23
24
  const statusFile = path.join(__dirname, '..', '.venv_status.json');
24
25
 
25
- // Check if virtual environment exists
26
+ // Check if virtual environment exists
26
27
  if (!fs.existsSync(venvPath)) {
27
28
  console.log(chalk.red('❌ Virtual environment not found. Please reinstall the package:'));
28
29
  console.log(chalk.yellow(' npm uninstall -g gitarsenal-cli'));
@@ -57,13 +58,7 @@ function activateVirtualEnvironment() {
57
58
  const traditionalPipPath = isWindows ?
58
59
  path.join(venvPath, 'Scripts', 'pip.exe') :
59
60
  path.join(venvPath, 'bin', 'pip');
60
-
61
- // Determine which structure exists
62
- // console.log(chalk.gray(`🔍 Checking virtual environment structure:`));
63
- // console.log(chalk.gray(` Python: ${uvPythonPath} (exists: ${fs.existsSync(uvPythonPath)})`));
64
- // console.log(chalk.gray(` Pip: ${uvPipPath} (exists: ${fs.existsSync(uvPipPath)})`));
65
-
66
- // For uv virtual environments, we only need Python to exist
61
+
67
62
  // uv doesn't create a pip executable, it uses 'uv pip' instead
68
63
  if (fs.existsSync(uvPythonPath)) {
69
64
  pythonPath = uvPythonPath;
@@ -261,9 +256,156 @@ function printGpuTorchCudaSummary(result) {
261
256
  if (gpu.notes) console.log(` - Notes: ${gpu.notes}`);
262
257
  console.log();
263
258
  }
259
+
260
+ // Print API key requirements if available
261
+ if (result.requiredApiKeys && Array.isArray(result.requiredApiKeys) && result.requiredApiKeys.length > 0) {
262
+ console.log(chalk.bold('🔑 REQUIRED API KEYS'));
263
+ result.requiredApiKeys.forEach(apiKey => {
264
+ const status = apiKey.required ? chalk.red('REQUIRED') : chalk.yellow('OPTIONAL');
265
+ console.log(` - ${apiKey.name} (${status})`);
266
+ console.log(` Service: ${apiKey.service}`);
267
+ console.log(` Purpose: ${apiKey.description}`);
268
+ if (apiKey.example) console.log(` Format: ${apiKey.example}`);
269
+ if (apiKey.documentation_url) console.log(` Docs: ${apiKey.documentation_url}`);
270
+ console.log();
271
+ });
272
+ }
264
273
  } catch {}
265
274
  }
266
275
 
276
+ // Function to load stored API keys
277
+ async function loadStoredApiKeys() {
278
+ try {
279
+ const scriptPath = path.join(__dirname, '..', 'python', 'gitarsenal_keys.py');
280
+ const pythonExecutable = process.env.PYTHON_EXECUTABLE || 'python';
281
+
282
+ return new Promise((resolve) => {
283
+ const pythonProcess = spawn(pythonExecutable, [
284
+ scriptPath,
285
+ 'list',
286
+ '--json'
287
+ ], { stdio: 'pipe' });
288
+
289
+ let output = '';
290
+ pythonProcess.stdout.on('data', (data) => {
291
+ output += data.toString();
292
+ });
293
+
294
+ pythonProcess.on('close', (code) => {
295
+ if (code === 0) {
296
+ try {
297
+ const keys = JSON.parse(output);
298
+ resolve(keys);
299
+ } catch (e) {
300
+ resolve({});
301
+ }
302
+ } else {
303
+ resolve({});
304
+ }
305
+ });
306
+ });
307
+ } catch (error) {
308
+ return {};
309
+ }
310
+ }
311
+
312
+ // Function to prompt for missing required API keys
313
+ async function promptForMissingApiKeys(requiredApiKeys, storedKeys) {
314
+ if (!requiredApiKeys || !Array.isArray(requiredApiKeys) || requiredApiKeys.length === 0) {
315
+ return {};
316
+ }
317
+
318
+ const missingKeys = {};
319
+ const requiredMissingKeys = requiredApiKeys.filter(apiKey =>
320
+ apiKey.required && !storedKeys[apiKey.service.toLowerCase()]
321
+ );
322
+
323
+ if (requiredMissingKeys.length === 0) {
324
+ console.log(chalk.green('✅ All required API keys are already stored'));
325
+ return {};
326
+ }
327
+
328
+ console.log(chalk.yellow('\n🔑 Missing Required API Keys'));
329
+ console.log(chalk.gray('The following API keys are required for this repository:'));
330
+
331
+ for (const apiKey of requiredMissingKeys) {
332
+ console.log(chalk.bold(`\n📝 ${apiKey.name} (${apiKey.service})`));
333
+ console.log(chalk.gray(`Purpose: ${apiKey.description}`));
334
+ if (apiKey.documentation_url) {
335
+ console.log(chalk.blue(`Documentation: ${apiKey.documentation_url}`));
336
+ }
337
+
338
+ const answers = await inquirer.prompt([
339
+ {
340
+ type: 'confirm',
341
+ name: 'provideKey',
342
+ message: `Do you want to provide your ${apiKey.service} API key now?`,
343
+ default: true
344
+ }
345
+ ]);
346
+
347
+ if (answers.provideKey) {
348
+ const keyAnswer = await inquirer.prompt([
349
+ {
350
+ type: 'password',
351
+ name: 'key',
352
+ message: `Enter your ${apiKey.service} API key:`,
353
+ mask: '*',
354
+ validate: (input) => {
355
+ const key = input.trim();
356
+ if (key === '') return `${apiKey.service} API key is required`;
357
+ return true;
358
+ }
359
+ }
360
+ ]);
361
+
362
+ const storeAnswer = await inquirer.prompt([
363
+ {
364
+ type: 'confirm',
365
+ name: 'store',
366
+ message: `Store this API key locally for future use?`,
367
+ default: true
368
+ }
369
+ ]);
370
+
371
+ missingKeys[apiKey.service.toLowerCase()] = keyAnswer.key;
372
+
373
+ if (storeAnswer.store) {
374
+ // Store the key using the existing key management system
375
+ try {
376
+ const scriptPath = path.join(__dirname, '..', 'python', 'gitarsenal_keys.py');
377
+ const pythonExecutable = process.env.PYTHON_EXECUTABLE || 'python';
378
+
379
+ await new Promise((resolve, reject) => {
380
+ const pythonProcess = spawn(pythonExecutable, [
381
+ scriptPath,
382
+ 'add',
383
+ '--service', apiKey.service.toLowerCase(),
384
+ '--key', keyAnswer.key
385
+ ], { stdio: 'pipe' });
386
+
387
+ pythonProcess.on('close', (code) => {
388
+ if (code === 0) {
389
+ console.log(chalk.green(`✅ ${apiKey.service} API key stored successfully`));
390
+ resolve();
391
+ } else {
392
+ console.log(chalk.yellow(`⚠️ Could not store ${apiKey.service} API key locally`));
393
+ resolve(); // Don't fail the whole process
394
+ }
395
+ });
396
+ });
397
+ } catch (error) {
398
+ console.log(chalk.yellow(`⚠️ Could not store ${apiKey.service} API key: ${error.message}`));
399
+ }
400
+ }
401
+ } else {
402
+ console.log(chalk.yellow(`⚠️ Skipping ${apiKey.service} API key. Repository setup may fail without it.`));
403
+ }
404
+ }
405
+
406
+ return missingKeys;
407
+ }
408
+
267
409
  // Helper to derive a default volume name from the repository URL
268
410
  function getDefaultVolumeName(repoUrl) {
269
411
  try {
@@ -321,10 +463,106 @@ function getDefaultVolumeName(repoUrl) {
321
463
  }
322
464
 
323
465
  // Full fetch to get both setup commands and recommendations in one request
324
- async function fetchFullSetupAndRecs(repoUrl) {
325
- // For now, just use the preview function but don't show summary to avoid duplicates
326
- // The Python implementation will handle setup commands
327
- return await previewRecommendations(repoUrl, { showSummary: false, hideSpinner: true });
466
+ async function fetchFullSetupAndRecs(repoUrl, storedCredentials = null) {
467
+ const spinner = ora('Analyzing repository with GitIngest...').start();
468
+
469
+ try {
470
+ // Try to use local GitIngest CLI first
471
+ spinner.text = 'Running GitIngest analysis...';
472
+ const gitingestData = await fetchGitIngestData(repoUrl);
473
+
474
+ let finalGitingestData;
475
+ if (!gitingestData) {
476
+ spinner.warn('GitIngest CLI not available, using basic analysis');
477
+ // Fallback to basic data
478
+ finalGitingestData = {
479
+ system_info: {
480
+ platform: process.platform,
481
+ python_version: process.version,
482
+ detected_language: 'Unknown',
483
+ detected_technologies: [],
484
+ file_count: 0,
485
+ repo_stars: 0,
486
+ repo_forks: 0,
487
+ primary_package_manager: 'Unknown',
488
+ complexity_level: 'Unknown'
489
+ },
490
+ repository_analysis: {
491
+ summary: `Repository: ${repoUrl}`,
492
+ tree: '',
493
+ content_preview: ''
494
+ },
495
+ success: false
496
+ };
497
+ } else {
498
+ finalGitingestData = gitingestData;
499
+ spinner.text = 'GitIngest complete, generating AI recommendations...';
500
+ }
501
+
502
+ const envUrl = process.env.GITARSENAL_API_URL;
503
+ const endpoints = envUrl ? [envUrl] : ['https://www.gitarsenal.dev/api/best_gpu'];
504
+
505
+ const payload = {
506
+ repoUrl,
507
+ gitingestData: finalGitingestData,
508
+ storedCredentials,
509
+ preview: false // This is a full analysis, not preview
510
+ };
511
+
512
+ // console.log(chalk.gray('🐛 DEBUG: Payload being sent to API:'));
513
+ // console.log(chalk.gray(' - Repo URL:', repoUrl));
514
+ // console.log(chalk.gray(' - GitIngest Data Summary Length:', payload.gitingestData.repository_analysis.summary.length));
515
+ // console.log(chalk.gray(' - GitIngest Data Tree Length:', payload.gitingestData.repository_analysis.tree.length));
516
+ // console.log(chalk.gray(' - GitIngest Data Content Length:', payload.gitingestData.repository_analysis.content_preview.length));
517
+ // console.log(chalk.gray(' - Detected Language:', payload.gitingestData.system_info.detected_language));
518
+ // console.log(chalk.gray(' - Detected Technologies:', payload.gitingestData.system_info.detected_technologies.join(', ')));
519
+ // console.log(chalk.gray(' - Stored Credentials:', storedCredentials ? Object.keys(storedCredentials).length + ' keys' : 'none'));
520
+ // console.log(chalk.gray(' - Preview Mode:', payload.preview));
521
+
522
+ let data = null;
523
+ let lastErrorText = '';
524
+
525
+ for (const url of endpoints) {
526
+ try {
527
+ spinner.text = `Analyzing repository: ${url}`;
528
+ const res = await fetch(url, {
529
+ method: 'POST',
530
+ headers: { 'Content-Type': 'application/json', 'User-Agent': 'GitArsenal-CLI/1.0' },
531
+ body: JSON.stringify(payload),
532
+ redirect: 'follow'
533
+ });
534
+ if (!res.ok) {
535
+ const text = await res.text().catch(() => '');
536
+ lastErrorText = `${res.status} ${text.slice(0, 300)}`;
537
+ continue;
538
+ }
539
+ data = await res.json().catch(() => null);
540
+ if (data) {
541
+ console.log(chalk.gray('🐛 DEBUG: Received response from API:'));
542
+ console.log(chalk.gray(' - Response has commands:', !!data.commands));
543
+ console.log(chalk.gray(' - Commands count:', data.commands ? data.commands.length : 0));
544
+ console.log(chalk.gray(' - Response has API keys:', !!data.requiredApiKeys));
545
+ console.log(chalk.gray(' - API keys count:', data.requiredApiKeys ? data.requiredApiKeys.length : 0));
546
+ console.log(chalk.gray(' - Response has GPU rec:', !!data.gpuRecommendation));
547
+ console.log(chalk.gray(' - Response has CUDA rec:', !!data.cudaRecommendation));
548
+ console.log(chalk.gray(' - Response has Torch rec:', !!data.torchRecommendation));
549
+ spinner.succeed('Repository analysis complete');
550
+ return data;
551
+ }
552
+ } catch (err) {
553
+ lastErrorText = err && err.message ? err.message : 'request failed';
554
+ continue;
555
+ }
556
+ }
557
+
558
+ spinner.fail('Failed to analyze repository');
559
+ if (lastErrorText) console.log(chalk.gray(`Reason: ${lastErrorText}`));
560
+ return null;
561
+
562
+ } catch (e) {
563
+ spinner.fail(`Analysis failed: ${e.message}`);
564
+ return null;
565
+ }
328
566
  }
329
567
 
330
568
  // Function to send user data to web application
@@ -761,8 +999,15 @@ async function runContainerCommand(options) {
761
999
  repoUrl = answers.repoUrl;
762
1000
  }
763
1001
 
764
- // Analyze repository for GPU recommendations (repository setup is now handled by Agent)
1002
+ // Analyze repository for GPU recommendations and API key requirements
1003
+ let analysisData = null;
1004
+ let collectedApiKeys = {};
1005
+
765
1006
  if (repoUrl) {
1007
+ // Load stored API keys first
1008
+ console.log(chalk.blue('🔍 Loading stored API keys...'));
1009
+ const storedKeys = await loadStoredApiKeys();
1010
+
766
1011
  // Start a main spinner that will show overall progress
767
1012
  const mainSpinner = ora('Analyzing repository...').start();
768
1013
 
@@ -773,16 +1018,24 @@ async function runContainerCommand(options) {
773
1018
  mainSpinner.text = 'Analyzing repository for GPU/Torch/CUDA recommendations...';
774
1019
  const previewPromise = previewRecommendations(repoUrl, { showSummary: false, abortSignal: previewAbort.signal, hideSpinner: true }).catch(() => null);
775
1020
 
776
- // Run full fetch in parallel; prefer its results if available.
777
- mainSpinner.text = 'Finding the best machine for your code...';
778
- const fullData = await fetchFullSetupAndRecs(repoUrl).catch(() => null);
1021
+ // Run full fetch in parallel with stored credentials; prefer its results if available.
1022
+ mainSpinner.text = 'Finding the best machine for your code and detecting API requirements...';
1023
+ const fullData = await fetchFullSetupAndRecs(repoUrl, storedKeys).catch(() => null);
779
1024
 
780
1025
  if (fullData) {
781
1026
  // Stop preview spinner immediately since we have a response
782
1027
  previewAbort.abort();
783
1028
  mainSpinner.succeed('Analysis complete!');
784
1029
  printGpuTorchCudaSummary(fullData);
785
- // Repository setup will be handled by Agent in container
1030
+ analysisData = fullData;
1031
+
1032
+ // Handle API key requirements
1033
+ if (fullData.requiredApiKeys && Array.isArray(fullData.requiredApiKeys) && fullData.requiredApiKeys.length > 0) {
1034
+ const missingKeys = await promptForMissingApiKeys(fullData.requiredApiKeys, storedKeys);
1035
+ collectedApiKeys = { ...storedKeys, ...missingKeys };
1036
+ } else {
1037
+ collectedApiKeys = storedKeys;
1038
+ }
786
1039
  } else {
787
1040
  // Full fetch failed, wait for preview and show its results
788
1041
  mainSpinner.text = 'Waiting for preview analysis to complete...';
@@ -790,16 +1043,19 @@ async function runContainerCommand(options) {
790
1043
  if (previewData) {
791
1044
  mainSpinner.succeed('Preview analysis complete!');
792
1045
  printGpuTorchCudaSummary(previewData);
1046
+ analysisData = previewData;
793
1047
  } else {
794
1048
  mainSpinner.fail('Analysis failed - both preview and full analysis timed out or failed');
795
1049
  console.log(chalk.yellow('⚠️ Unable to analyze repository automatically.'));
796
1050
  console.log(chalk.gray('Repository setup will still be handled by Agent in container.'));
797
1051
  }
1052
+ collectedApiKeys = storedKeys;
798
1053
  }
799
1054
  } catch (error) {
800
1055
  mainSpinner.fail(`Analysis failed: ${error.message}`);
801
1056
  console.log(chalk.yellow('⚠️ Unable to analyze repository automatically.'));
802
1057
  console.log(chalk.gray('Repository setup will still be handled by Agent in container.'));
1058
+ collectedApiKeys = await loadStoredApiKeys();
803
1059
  }
804
1060
  }
805
1061
 
@@ -934,7 +1190,9 @@ async function runContainerCommand(options) {
934
1190
  yes: skipConfirmation,
935
1191
  userId,
936
1192
  userName,
937
- userEmail
1193
+ userEmail,
1194
+ apiKeys: collectedApiKeys,
1195
+ analysisData
938
1196
  });
939
1197
 
940
1198
  } catch (containerError) {
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const chalk = require('chalk');
5
+
6
+ // Function to check if GitIngest CLI is available and working
7
+ async function checkGitIngestCLI() {
8
+ try {
9
+ // Try a simple help command first
10
+ const checkProcess = spawn('gitingest', ['--version'], {
11
+ stdio: 'pipe',
12
+ timeout: 5000 // 5 second timeout
13
+ });
14
+
15
+ let stderr = '';
16
+ checkProcess.stderr.on('data', (data) => {
17
+ stderr += data.toString();
18
+ });
19
+
20
+ return new Promise((resolve) => {
21
+ checkProcess.on('close', (code) => {
22
+ // If there are Python errors in stderr, consider it failed even if exit code is 0
23
+ if (stderr.includes('TypeError') || stderr.includes('Traceback') || stderr.includes('Error')) {
24
+ // console.log(chalk.yellow('⚠️ GitIngest CLI has Python compatibility issues'));
25
+ resolve(false);
26
+ } else if (code === 0) {
27
+ resolve(true);
28
+ } else {
29
+ resolve(false);
30
+ }
31
+ });
32
+
33
+ checkProcess.on('error', () => {
34
+ resolve(false);
35
+ });
36
+
37
+ // Handle timeout
38
+ setTimeout(() => {
39
+ if (!checkProcess.killed) {
40
+ checkProcess.kill();
41
+ resolve(false);
42
+ }
43
+ }, 5000);
44
+ });
45
+ } catch (error) {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ // Function to fetch GitIngest data using local GitIngest CLI
51
+ async function fetchGitIngestData(repoUrl) {
52
+ try {
53
+ // First check if GitIngest CLI is available
54
+ const gitingestAvailable = await checkGitIngestCLI();
55
+ if (!gitingestAvailable) {
56
+ // console.log(chalk.yellow('⚠️ GitIngest CLI not available or has compatibility issues.'));
57
+ // console.log(chalk.blue('💡 For best results, install with: pipx install gitingest'));
58
+ // console.log(chalk.blue(' Alternative: pip install gitingest (requires Python 3.10+)'));
59
+ // console.log(chalk.blue('📖 More info: https://github.com/coderamp-labs/gitingest'));
60
+ // console.log(chalk.gray(' Falling back to basic repository analysis...'));
61
+ return null;
62
+ }
63
+
64
+ console.log(chalk.gray('📥 Running GitIngest locally...'));
65
+
66
+ // Run GitIngest CLI command with optimal settings for AI analysis
67
+ const gitingestProcess = spawn('gitingest', [
68
+ repoUrl,
69
+ '-o', '-', // Output to stdout
70
+ ], {
71
+ stdio: ['pipe', 'pipe', 'pipe']
72
+ });
73
+
74
+ let gitingestOutput = '';
75
+ let errorOutput = '';
76
+
77
+ gitingestProcess.stdout.on('data', (data) => {
78
+ gitingestOutput += data.toString();
79
+ });
80
+
81
+ gitingestProcess.stderr.on('data', (data) => {
82
+ errorOutput += data.toString();
83
+ });
84
+
85
+ return new Promise((resolve) => {
86
+ gitingestProcess.on('close', (code) => {
87
+ if (code === 0 && gitingestOutput.trim().length > 0) {
88
+ console.log(chalk.green('✅ GitIngest analysis complete'));
89
+ console.log(chalk.gray(`📊 Captured ${gitingestOutput.length} characters of repository content`));
90
+ resolve(parseGitIngestOutput(gitingestOutput, repoUrl));
91
+ } else {
92
+ console.log(chalk.yellow(`⚠️ GitIngest failed (exit code: ${code})`));
93
+ if (errorOutput) {
94
+ console.log(chalk.gray(`Error details: ${errorOutput.slice(0, 300)}`));
95
+ }
96
+ resolve(null);
97
+ }
98
+ });
99
+
100
+ gitingestProcess.on('error', (error) => {
101
+ console.log(chalk.yellow(`⚠️ GitIngest CLI error: ${error.message}`));
102
+ resolve(null);
103
+ });
104
+ });
105
+
106
+ } catch (error) {
107
+ console.log(chalk.yellow(`⚠️ GitIngest execution failed: ${error.message}`));
108
+ return null;
109
+ }
110
+ }
111
+
112
+ // Function to parse GitIngest text output into structured data
113
+ function parseGitIngestOutput(gitingestText, repoUrl) {
114
+ try {
115
+ // Extract repository info from URL
116
+ const urlMatch = repoUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/);
117
+ const owner = urlMatch ? urlMatch[1] : 'unknown';
118
+ const repo = urlMatch ? urlMatch[2].replace('.git', '') : 'unknown';
119
+
120
+ // GitIngest output format:
121
+ // Repository: owner/repo-name
122
+ // Files analyzed: 42
123
+ // Estimated tokens: 15.2k
124
+ //
125
+ // Directory structure:
126
+ // └── project-name/
127
+ // ├── src/
128
+ // │ ├── main.py
129
+ // └── README.md
130
+ //
131
+ // ================================================
132
+ // FILE: src/main.py
133
+ // ================================================
134
+ // [file content]
135
+
136
+ const lines = gitingestText.split('\n');
137
+ let summary = '';
138
+ let tree = '';
139
+ let content_preview = '';
140
+ let detectedLanguage = 'Unknown';
141
+ let detectedTechnologies = [];
142
+ let primaryPackageManager = 'Unknown';
143
+
144
+ // Find sections
145
+ let summaryEnd = -1;
146
+ let treeStart = -1;
147
+ let treeEnd = -1;
148
+ let contentStart = -1;
149
+
150
+ for (let i = 0; i < lines.length; i++) {
151
+ const line = lines[i];
152
+
153
+ if (line.startsWith('Repository:') && summaryEnd === -1) {
154
+ // Find end of summary (first empty line after Repository line)
155
+ for (let j = i; j < lines.length; j++) {
156
+ if (lines[j].trim() === '' && j > i) {
157
+ summaryEnd = j;
158
+ break;
159
+ }
160
+ }
161
+ }
162
+
163
+ if (line.startsWith('Directory structure:')) {
164
+ treeStart = i;
165
+ }
166
+
167
+ if (line.includes('===') && line.includes('FILE:')) {
168
+ if (treeStart > -1 && treeEnd === -1) {
169
+ treeEnd = i;
170
+ }
171
+ if (contentStart === -1) {
172
+ contentStart = i;
173
+ }
174
+ }
175
+ }
176
+
177
+ // Extract sections
178
+ if (summaryEnd > 0) {
179
+ summary = lines.slice(0, summaryEnd).join('\n');
180
+ } else {
181
+ // Fallback: take first 10 lines as summary
182
+ summary = lines.slice(0, 10).join('\n');
183
+ }
184
+
185
+ if (treeStart > -1) {
186
+ const endIdx = treeEnd > -1 ? treeEnd : (contentStart > -1 ? contentStart : Math.min(treeStart + 50, lines.length));
187
+ tree = lines.slice(treeStart, endIdx).join('\n');
188
+ }
189
+
190
+ if (contentStart > -1) {
191
+ // Take first 300 lines of content to provide good context without overwhelming
192
+ content_preview = lines.slice(contentStart, Math.min(contentStart + 300, lines.length)).join('\n');
193
+ }
194
+
195
+ // Detect technologies from content
196
+ const contentLower = gitingestText.toLowerCase();
197
+
198
+ // Language detection
199
+ if (contentLower.includes('import torch') || contentLower.includes('pytorch') || contentLower.includes('def ') || contentLower.includes('import ')) {
200
+ detectedLanguage = 'Python';
201
+ primaryPackageManager = 'pip';
202
+ } else if (contentLower.includes('package.json') || contentLower.includes('require(') || contentLower.includes('import ') || contentLower.includes('function ')) {
203
+ detectedLanguage = 'JavaScript';
204
+ primaryPackageManager = 'npm';
205
+ } else if (contentLower.includes('cargo.toml') || contentLower.includes('fn ') || contentLower.includes('use ')) {
206
+ detectedLanguage = 'Rust';
207
+ primaryPackageManager = 'cargo';
208
+ } else if (contentLower.includes('go.mod') || contentLower.includes('func ') || contentLower.includes('package ')) {
209
+ detectedLanguage = 'Go';
210
+ primaryPackageManager = 'go mod';
211
+ }
212
+
213
+ // AI/ML Technology detection
214
+ if (contentLower.includes('torch') || contentLower.includes('pytorch')) {
215
+ detectedTechnologies.push('PyTorch');
216
+ }
217
+ if (contentLower.includes('tensorflow') || contentLower.includes('tf.')) {
218
+ detectedTechnologies.push('TensorFlow');
219
+ }
220
+ if (contentLower.includes('transformers') || contentLower.includes('huggingface')) {
221
+ detectedTechnologies.push('Hugging Face');
222
+ }
223
+ if (contentLower.includes('numpy') || contentLower.includes('np.')) {
224
+ detectedTechnologies.push('NumPy');
225
+ }
226
+ if (contentLower.includes('openai') && (contentLower.includes('import openai') || contentLower.includes('openai.')) && !contentLower.includes('# example') && !contentLower.includes('# TODO')) {
227
+ detectedTechnologies.push('OpenAI API');
228
+ }
229
+ if (contentLower.includes('anthropic') && contentLower.includes('import anthropic')) {
230
+ detectedTechnologies.push('Anthropic API');
231
+ }
232
+
233
+ // Count files from summary
234
+ const filesMatch = summary.match(/Files analyzed: (\d+)/);
235
+ const fileCount = filesMatch ? parseInt(filesMatch[1]) : 0;
236
+
237
+ const structuredData = {
238
+ system_info: {
239
+ platform: process.platform,
240
+ python_version: process.version,
241
+ detected_language: detectedLanguage,
242
+ detected_technologies: detectedTechnologies,
243
+ file_count: fileCount,
244
+ repo_stars: 0, // Would need GitHub API
245
+ repo_forks: 0, // Would need GitHub API
246
+ primary_package_manager: primaryPackageManager,
247
+ complexity_level: fileCount > 50 ? 'high' : fileCount > 20 ? 'medium' : 'low'
248
+ },
249
+ repository_analysis: {
250
+ summary: summary || `Repository: ${owner}/${repo}\nAnalyzed with GitIngest`,
251
+ tree: tree || 'Directory structure not available',
252
+ content_preview: content_preview || 'Content preview not available'
253
+ },
254
+ success: true
255
+ };
256
+
257
+ console.log(chalk.gray('🔍 Analysis Summary:'));
258
+ console.log(chalk.gray(` - Language: ${detectedLanguage}`));
259
+ console.log(chalk.gray(` - Technologies: ${detectedTechnologies.join(', ') || 'None detected'}`));
260
+ console.log(chalk.gray(` - Files: ${fileCount}`));
261
+ console.log(chalk.gray(` - Package Manager: ${primaryPackageManager}`));
262
+
263
+ return structuredData;
264
+
265
+ } catch (error) {
266
+ console.log(chalk.yellow(`⚠️ Failed to parse GitIngest output: ${error.message}`));
267
+ return null;
268
+ }
269
+ }
270
+
271
+ module.exports = {
272
+ fetchGitIngestData,
273
+ checkGitIngestCLI
274
+ };
@@ -44,7 +44,8 @@ Before executing the command, please follow these steps:
44
44
  - cd /Users/name/My Documents (incorrect - will fail)
45
45
  - python "/path/with spaces/script.py" (correct)
46
46
  - python /path/with spaces/script.py (incorrect - will fail)
47
- - After ensuring proper quoting, execute the command.
47
+ - **PLACEHOLDER HANDLING**: When executing commands that contain placeholder values (e.g., `<filename>`, `{variable}`, `[value]`), you MUST first use the Task tool with a general-purpose agent to read the relevant files and gather the necessary information to fill in the placeholder values before executing the command.
48
+ - After ensuring proper quoting and placeholder resolution, execute the command.
48
49
  - Capture the output of the command.
49
50
 
50
51
  Usage notes:
@@ -56,6 +57,15 @@ Usage notes:
56
57
  - VERY IMPORTANT: You MUST avoid using search commands like `find` and `grep`. Instead use Grep, Glob, or Task to search. You MUST avoid read tools like `cat`, `head`, `tail`, and `ls`, and use Read and LS to read files.
57
58
  - If you _still_ need to run `grep`, STOP. ALWAYS USE ripgrep at `rg` first, which all Claude Code users have pre-installed.
58
59
  - IMPORTANT: Always use `uv pip` instead of just `pip` when running pip commands. For example, use `uv pip install package_name` instead of `pip install package_name`.
60
+ - TESTING MODE: For ML training and inference commands, always use minimal/quick parameters to enable fast testing:
61
+ - FIRST: Read and examine the training script/config files to understand which parameters actually control training speed and duration
62
+ - Add `--max_steps 10` or `--epochs 1` for training commands
63
+ - Add `--batch_size 1` or `--batch_size 2` for memory efficiency
64
+ - Add `--eval_steps 5` for quick evaluation
65
+ - Use `--fast_dev_run` flag when available (PyTorch Lightning)
66
+ - Check for GPU availability with `nvidia-smi` or `python -c "import torch; print(torch.cuda.is_available())"` and adjust device settings accordingly
67
+ - Use `--device cuda` or `--gpu` flags when GPU is available, `--device cpu` when not
68
+ - Example: `python train.py --epochs 1 --batch_size 2 --max_steps 10 --device cuda`
59
69
  - When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings).
60
70
  - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it.
61
71