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.
- package/.venv_status.json +1 -1
- package/bin/gitarsenal.js +276 -18
- package/gitingest-integration.js +274 -0
- package/kill_claude/prompts/claude-code-tool-prompts.md +11 -1
- package/kill_claude/tools/__pycache__/bash_output_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/bash_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/edit_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/exit_plan_mode_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/glob_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/grep_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/kill_bash_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/ls_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/multiedit_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/notebook_edit_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/read_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/task_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/todo_write_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/web_fetch_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/web_search_tool.cpython-312.pyc +0 -0
- package/kill_claude/tools/__pycache__/write_tool.cpython-312.pyc +0 -0
- package/package.json +1 -1
- package/python/__pycache__/analyze_repo_api_keys.cpython-312.pyc +0 -0
- package/python/__pycache__/credentials_manager.cpython-312.pyc +0 -0
- package/python/credentials_manager.py +0 -169
- package/python/gitarsenal_keys.py +8 -2
- package/python/test_modalSandboxScript.py +13 -43
- package/kill_claude/nanoGPT/.gitattributes +0 -3
- package/kill_claude/nanoGPT/LICENSE +0 -21
- package/kill_claude/nanoGPT/README.md +0 -227
- package/kill_claude/nanoGPT/assets/gpt2_124M_loss.png +0 -0
- package/kill_claude/nanoGPT/assets/nanogpt.jpg +0 -0
- package/kill_claude/nanoGPT/bench.py +0 -117
- package/kill_claude/nanoGPT/config/eval_gpt2.py +0 -8
- package/kill_claude/nanoGPT/config/eval_gpt2_large.py +0 -8
- package/kill_claude/nanoGPT/config/eval_gpt2_medium.py +0 -8
- package/kill_claude/nanoGPT/config/eval_gpt2_xl.py +0 -8
- package/kill_claude/nanoGPT/config/finetune_shakespeare.py +0 -25
- package/kill_claude/nanoGPT/config/train_gpt2.py +0 -25
- package/kill_claude/nanoGPT/config/train_shakespeare_char.py +0 -37
- package/kill_claude/nanoGPT/configurator.py +0 -47
- package/kill_claude/nanoGPT/data/openwebtext/prepare.py +0 -81
- package/kill_claude/nanoGPT/data/openwebtext/readme.md +0 -15
- package/kill_claude/nanoGPT/data/shakespeare/prepare.py +0 -33
- package/kill_claude/nanoGPT/data/shakespeare/readme.md +0 -9
- package/kill_claude/nanoGPT/data/shakespeare_char/prepare.py +0 -68
- package/kill_claude/nanoGPT/data/shakespeare_char/readme.md +0 -9
- package/kill_claude/nanoGPT/model.py +0 -330
- package/kill_claude/nanoGPT/sample.py +0 -89
- package/kill_claude/nanoGPT/scaling_laws.ipynb +0 -792
- package/kill_claude/nanoGPT/train.py +0 -336
- package/kill_claude/nanoGPT/transformer_sizing.ipynb +0 -402
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-08-
|
|
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
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|