gitarsenal-cli 1.9.31 → 1.9.33

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 CHANGED
@@ -1 +1 @@
1
- {"created":"2025-08-10T15:27:06.463Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-08-10T17:52:52.499Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
package/bin/gitarsenal.js CHANGED
@@ -104,6 +104,145 @@ function activateVirtualEnvironment() {
104
104
  return true;
105
105
  }
106
106
 
107
+ // Lightweight preview of GPU/Torch/CUDA recommendations prior to GPU selection
108
+ async function previewRecommendations(repoUrl) {
109
+ const spinner = ora('Analyzing repository for GPU/Torch/CUDA recommendations...').start();
110
+ try {
111
+ const apiUrl = process.env.GITARSENAL_API_URL || 'https://www.gitarsenal.dev/api/gitingest-setup-commands';
112
+ const payload = {
113
+ repoUrl,
114
+ // Minimal GitIngest data to allow backend to run LLM analysis
115
+ gitingestData: {
116
+ system_info: {
117
+ platform: process.platform,
118
+ python_version: process.version,
119
+ detected_language: 'Unknown',
120
+ detected_technologies: [],
121
+ file_count: 0,
122
+ repo_stars: 0,
123
+ repo_forks: 0,
124
+ primary_package_manager: 'Unknown',
125
+ complexity_level: 'Unknown'
126
+ },
127
+ repository_analysis: {
128
+ summary: `Repository: ${repoUrl}`,
129
+ tree: '',
130
+ content_preview: ''
131
+ },
132
+ success: true
133
+ }
134
+ };
135
+
136
+ const res = await httpPostJson(apiUrl, payload);
137
+ spinner.stop();
138
+
139
+ if (!res) {
140
+ console.log(chalk.yellow('⚠️ Could not fetch recommendations preview.'));
141
+ return null;
142
+ }
143
+
144
+ printGpuTorchCudaSummary(res);
145
+ return res;
146
+ } catch (e) {
147
+ spinner.stop();
148
+ console.log(chalk.yellow(`⚠️ Preview failed: ${e.message}`));
149
+ return null;
150
+ }
151
+ }
152
+
153
+ function httpPostJson(urlString, body) {
154
+ return new Promise((resolve) => {
155
+ try {
156
+ const urlObj = new URL(urlString);
157
+ const data = JSON.stringify(body);
158
+ const options = {
159
+ hostname: urlObj.hostname,
160
+ port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
161
+ path: urlObj.pathname,
162
+ method: 'POST',
163
+ headers: {
164
+ 'Content-Type': 'application/json',
165
+ 'Content-Length': Buffer.byteLength(data),
166
+ 'User-Agent': 'GitArsenal-CLI/1.0'
167
+ }
168
+ };
169
+ const client = urlObj.protocol === 'https:' ? https : http;
170
+ const req = client.request(options, (res) => {
171
+ let responseData = '';
172
+ res.on('data', (chunk) => {
173
+ responseData += chunk;
174
+ });
175
+ res.on('end', () => {
176
+ try {
177
+ const parsed = JSON.parse(responseData);
178
+ resolve(parsed);
179
+ } catch (err) {
180
+ resolve(null);
181
+ }
182
+ });
183
+ });
184
+ req.on('error', () => resolve(null));
185
+ req.write(data);
186
+ req.end();
187
+ } catch (e) {
188
+ resolve(null);
189
+ }
190
+ });
191
+ }
192
+
193
+ function printGpuTorchCudaSummary(result) {
194
+ try {
195
+ console.log(chalk.bold('\n📊 API RESULT SUMMARY (GPU/Torch/CUDA)'));
196
+ console.log('────────────────────────────────────────────────────────');
197
+
198
+ const cuda = result.cudaRecommendation;
199
+ if (cuda) {
200
+ console.log(chalk.bold('🎯 CUDA Recommendation'));
201
+ if (cuda.recommendedCudaVersion) console.log(` - CUDA: ${cuda.recommendedCudaVersion}`);
202
+ if (Array.isArray(cuda.compatibleTorchVersions) && cuda.compatibleTorchVersions.length)
203
+ console.log(` - Torch Compatibility: ${cuda.compatibleTorchVersions.join(', ')}`);
204
+ if (cuda.dockerImage) console.log(` - Docker Image: ${cuda.dockerImage}`);
205
+ if (Array.isArray(cuda.installCommands) && cuda.installCommands.length) {
206
+ console.log(' - Install Commands:');
207
+ cuda.installCommands.forEach((c) => console.log(` $ ${c}`));
208
+ }
209
+ if (cuda.notes) console.log(` - Notes: ${cuda.notes}`);
210
+ console.log();
211
+ }
212
+
213
+ const torch = result.torchRecommendation;
214
+ if (torch) {
215
+ console.log(chalk.bold('🔥 PyTorch Recommendation'));
216
+ if (torch.recommendedTorchVersion) console.log(` - Torch: ${torch.recommendedTorchVersion}`);
217
+ if (torch.cudaVariant) console.log(` - CUDA Variant: ${torch.cudaVariant}`);
218
+ if (torch.pipInstallCommand) {
219
+ console.log(' - Install:');
220
+ console.log(` $ ${torch.pipInstallCommand}`);
221
+ }
222
+ if (Array.isArray(torch.extraPackages) && torch.extraPackages.length)
223
+ console.log(` - Extra Packages: ${torch.extraPackages.join(', ')}`);
224
+ if (torch.notes) console.log(` - Notes: ${torch.notes}`);
225
+ console.log();
226
+ }
227
+
228
+ const gpu = result.gpuRecommendation;
229
+ if (gpu) {
230
+ console.log(chalk.bold('🖥️ GPU Recommendation'));
231
+ if (gpu.minimumVramGb !== undefined) console.log(` - Min VRAM: ${gpu.minimumVramGb} GB`);
232
+ if (gpu.recommendedVramGb !== undefined) console.log(` - Recommended VRAM: ${gpu.recommendedVramGb} GB`);
233
+ if (gpu.minComputeCapability) console.log(` - Min Compute Capability: ${gpu.minComputeCapability}`);
234
+ if (Array.isArray(gpu.recommendedModels) && gpu.recommendedModels.length)
235
+ console.log(` - Recommended Models: ${gpu.recommendedModels.join(', ')}`);
236
+ if (Array.isArray(gpu.budgetOptions) && gpu.budgetOptions.length)
237
+ console.log(` - Budget Options: ${gpu.budgetOptions.join(', ')}`);
238
+ if (Array.isArray(gpu.cloudInstances) && gpu.cloudInstances.length)
239
+ console.log(` - Cloud Instances: ${gpu.cloudInstances.join(', ')}`);
240
+ if (gpu.notes) console.log(` - Notes: ${gpu.notes}`);
241
+ console.log();
242
+ }
243
+ } catch {}
244
+ }
245
+
107
246
  // Function to send user data to web application
108
247
  async function sendUserData(userId, userName) {
109
248
  try {
@@ -189,7 +328,7 @@ async function sendUserData(userId, userName) {
189
328
  resolve();
190
329
  });
191
330
 
192
- req.write(data);
331
+ req.write(JSON.stringify(userData));
193
332
  req.end();
194
333
  });
195
334
  } catch (error) {
@@ -406,6 +545,11 @@ async function runContainerCommand(options) {
406
545
  repoUrl = answers.repoUrl;
407
546
  }
408
547
 
548
+ // NEW: Preview CUDA/Torch/GPU recommendations before choosing GPU (only if auto-detect enabled)
549
+ if (useApi && repoUrl) {
550
+ await previewRecommendations(repoUrl);
551
+ }
552
+
409
553
  // Prompt for GPU type if not specified
410
554
  if (!gpuType) {
411
555
  const gpuAnswers = await inquirer.prompt([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.9.31",
3
+ "version": "1.9.33",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -914,6 +914,68 @@ def fetch_setup_commands_from_api(repo_url):
914
914
  print(f"📄 Response size: {len(response.text)} bytes")
915
915
  print(f"📄 Response URL: {response.url}")
916
916
 
917
+ # NEW: Print a concise summary of only GPU, Torch, and CUDA recommendations
918
+ try:
919
+ print("\n📊 API RESULT SUMMARY (GPU/Torch/CUDA)")
920
+ print("────────────────────────────────────────────────────────")
921
+
922
+ # CUDA Recommendation
923
+ cuda = data.get("cudaRecommendation")
924
+ if cuda:
925
+ print("🎯 CUDA Recommendation")
926
+ print(" - CUDA: ", cuda.get("recommendedCudaVersion", "Unknown"))
927
+ ct = cuda.get("compatibleTorchVersions") or []
928
+ if ct:
929
+ print(" - Torch Compatibility: ", ", ".join(ct))
930
+ if cuda.get("dockerImage"):
931
+ print(" - Docker Image: ", cuda.get("dockerImage"))
932
+ install_cmds = cuda.get("installCommands") or []
933
+ if install_cmds:
934
+ print(" - Install Commands:")
935
+ for c in install_cmds:
936
+ print(f" $ {c}")
937
+ if cuda.get("notes"):
938
+ print(" - Notes: ", cuda.get("notes"))
939
+
940
+ # Torch Recommendation
941
+ torch = data.get("torchRecommendation")
942
+ if torch:
943
+ print("\n🔥 PyTorch Recommendation")
944
+ print(" - Torch: ", torch.get("recommendedTorchVersion", "Unknown"))
945
+ print(" - CUDA Variant: ", torch.get("cudaVariant", "Unknown"))
946
+ if torch.get("pipInstallCommand"):
947
+ print(" - Install:")
948
+ print(f" $ {torch.get('pipInstallCommand')}")
949
+ extras = torch.get("extraPackages") or []
950
+ if extras:
951
+ print(" - Extra Packages: ", ", ".join(extras))
952
+ if torch.get("notes"):
953
+ print(" - Notes: ", torch.get("notes"))
954
+
955
+ # GPU Recommendation
956
+ gpu = data.get("gpuRecommendation")
957
+ if gpu:
958
+ print("\n🖥️ GPU Recommendation")
959
+ if gpu.get("minimumVramGb") is not None:
960
+ print(f" - Min VRAM: {gpu.get('minimumVramGb')} GB")
961
+ if gpu.get("recommendedVramGb") is not None:
962
+ print(f" - Recommended VRAM: {gpu.get('recommendedVramGb')} GB")
963
+ if gpu.get("minComputeCapability"):
964
+ print(f" - Min Compute Capability: {gpu.get('minComputeCapability')}")
965
+ rec_models = gpu.get("recommendedModels") or []
966
+ if rec_models:
967
+ print(" - Recommended Models: ", ", ".join(rec_models))
968
+ budget = gpu.get("budgetOptions") or []
969
+ if budget:
970
+ print(" - Budget Options: ", ", ".join(budget))
971
+ clouds = gpu.get("cloudInstances") or []
972
+ if clouds:
973
+ print(" - Cloud Instances: ", ", ".join(clouds))
974
+ if gpu.get("notes"):
975
+ print(" - Notes: ", gpu.get("notes"))
976
+ except Exception as summary_err:
977
+ print(f"⚠️ Failed to print API summary: {summary_err}")
978
+
917
979
  # Extract setup commands from the response
918
980
  if "setupInstructions" in data and "commands" in data["setupInstructions"]:
919
981
  commands = data["setupInstructions"]["commands"]
@@ -947,6 +1009,16 @@ def fetch_setup_commands_from_api(repo_url):
947
1009
  return fixed_commands
948
1010
  else:
949
1011
  print("⚠️ API response did not contain setupInstructions.commands field")
1012
+ # If top-level commands exist (newer API), use them
1013
+ if "commands" in data and isinstance(data["commands"], list):
1014
+ commands = data["commands"]
1015
+ print(f"✅ Found top-level commands array with {len(commands)} entries")
1016
+ fixed_commands = fix_setup_commands(commands)
1017
+ print("\n📋 Fixed commands:")
1018
+ for i, cmd in enumerate(fixed_commands, 1):
1019
+ print(f" {i}. {cmd}")
1020
+ return fixed_commands
1021
+
950
1022
  print("📋 Available fields in response:")
951
1023
  for key in data.keys():
952
1024
  print(f" - {key}")
@@ -1620,6 +1692,68 @@ def get_setup_commands_from_gitingest(repo_url):
1620
1692
  try:
1621
1693
  result = response.json()
1622
1694
 
1695
+ # NEW: Print a concise summary of only GPU, Torch, and CUDA recommendations
1696
+ try:
1697
+ print("\n📊 API RESULT SUMMARY (GPU/Torch/CUDA)")
1698
+ print("────────────────────────────────────────────────────────")
1699
+
1700
+ # CUDA Recommendation
1701
+ cuda = result.get("cudaRecommendation")
1702
+ if cuda:
1703
+ print("🎯 CUDA Recommendation")
1704
+ print(" - CUDA: ", cuda.get("recommendedCudaVersion", "Unknown"))
1705
+ ct = cuda.get("compatibleTorchVersions") or []
1706
+ if ct:
1707
+ print(" - Torch Compatibility: ", ", ".join(ct))
1708
+ if cuda.get("dockerImage"):
1709
+ print(" - Docker Image: ", cuda.get("dockerImage"))
1710
+ install_cmds = cuda.get("installCommands") or []
1711
+ if install_cmds:
1712
+ print(" - Install Commands:")
1713
+ for c in install_cmds:
1714
+ print(f" $ {c}")
1715
+ if cuda.get("notes"):
1716
+ print(" - Notes: ", cuda.get("notes"))
1717
+
1718
+ # Torch Recommendation
1719
+ torch = result.get("torchRecommendation")
1720
+ if torch:
1721
+ print("\n🔥 PyTorch Recommendation")
1722
+ print(" - Torch: ", torch.get("recommendedTorchVersion", "Unknown"))
1723
+ print(" - CUDA Variant: ", torch.get("cudaVariant", "Unknown"))
1724
+ if torch.get("pipInstallCommand"):
1725
+ print(" - Install:")
1726
+ print(f" $ {torch.get('pipInstallCommand')}")
1727
+ extras = torch.get("extraPackages") or []
1728
+ if extras:
1729
+ print(" - Extra Packages: ", ", ".join(extras))
1730
+ if torch.get("notes"):
1731
+ print(" - Notes: ", torch.get("notes"))
1732
+
1733
+ # GPU Recommendation
1734
+ gpu = result.get("gpuRecommendation")
1735
+ if gpu:
1736
+ print("\n🖥️ GPU Recommendation")
1737
+ if gpu.get("minimumVramGb") is not None:
1738
+ print(f" - Min VRAM: {gpu.get('minimumVramGb')} GB")
1739
+ if gpu.get("recommendedVramGb") is not None:
1740
+ print(f" - Recommended VRAM: {gpu.get('recommendedVramGb')} GB")
1741
+ if gpu.get("minComputeCapability"):
1742
+ print(f" - Min Compute Capability: {gpu.get('minComputeCapability')}")
1743
+ rec_models = gpu.get("recommendedModels") or []
1744
+ if rec_models:
1745
+ print(" - Recommended Models: ", ", ".join(rec_models))
1746
+ budget = gpu.get("budgetOptions") or []
1747
+ if budget:
1748
+ print(" - Budget Options: ", ", ".join(budget))
1749
+ clouds = gpu.get("cloudInstances") or []
1750
+ if clouds:
1751
+ print(" - Cloud Instances: ", ", ".join(clouds))
1752
+ if gpu.get("notes"):
1753
+ print(" - Notes: ", gpu.get("notes"))
1754
+ except Exception as summary_err:
1755
+ print(f"⚠️ Failed to print API summary: {summary_err}")
1756
+
1623
1757
  # Check if we have commands in the response
1624
1758
  commands = None
1625
1759