gitarsenal-cli 1.9.12 ā 1.9.14
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/package.json +1 -1
- package/python/requirements.txt +2 -1
- package/python/test_claude_fallback.py +118 -0
- package/python/test_modalSandboxScript.py +454 -83
- package/scripts/postinstall.js +26 -9
- package/test_modalSandboxScript.py +450 -82
package/scripts/postinstall.js
CHANGED
|
@@ -90,7 +90,7 @@ async function checkAndInstallUv() {
|
|
|
90
90
|
|
|
91
91
|
// Function to create and activate virtual environment using uv
|
|
92
92
|
async function createVirtualEnvironment() {
|
|
93
|
-
const packages = ['modal', 'gitingest', 'requests'];
|
|
93
|
+
const packages = ['modal', 'gitingest', 'requests', 'anthropic'];
|
|
94
94
|
const packageDir = path.join(__dirname, '..');
|
|
95
95
|
|
|
96
96
|
console.log(chalk.yellow(`š¦ Creating virtual environment with uv and installing packages: ${packages.join(', ')}`));
|
|
@@ -138,18 +138,30 @@ async function createVirtualEnvironment() {
|
|
|
138
138
|
|
|
139
139
|
console.log(chalk.gray(`š Installing packages in virtual environment with uv...`));
|
|
140
140
|
|
|
141
|
-
// Install packages using uv pip
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
// Install packages using uv pip from requirements.txt
|
|
142
|
+
const requirementsPath = path.join(packageDir, 'python', 'requirements.txt');
|
|
143
|
+
if (await fs.pathExists(requirementsPath)) {
|
|
144
|
+
console.log(chalk.gray(`š¦ Installing packages from requirements.txt...`));
|
|
145
|
+
await execAsync(`uv pip install -r ${requirementsPath}`, {
|
|
146
|
+
cwd: packageDir,
|
|
147
|
+
env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
|
|
148
|
+
stdio: 'inherit'
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
console.log(chalk.gray(`š¦ Installing packages: ${packages.join(', ')}`));
|
|
152
|
+
await execAsync(`uv pip install ${packages.join(' ')}`, {
|
|
153
|
+
cwd: packageDir,
|
|
154
|
+
env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
|
|
155
|
+
stdio: 'inherit'
|
|
156
|
+
});
|
|
157
|
+
}
|
|
147
158
|
|
|
148
159
|
console.log(chalk.green('ā
Python packages installed successfully in virtual environment!'));
|
|
149
160
|
|
|
150
161
|
// Verify packages are installed
|
|
151
162
|
const pythonPath = path.join(venvPath, 'bin', 'python');
|
|
152
|
-
|
|
163
|
+
const packagesToVerify = [...packages, 'anthropic']; // Ensure anthropic is checked
|
|
164
|
+
for (const pkg of packagesToVerify) {
|
|
153
165
|
try {
|
|
154
166
|
await execAsync(`${pythonPath} -c "import ${pkg}; print('${pkg} imported successfully')"`);
|
|
155
167
|
console.log(chalk.green(`ā
${pkg} verified`));
|
|
@@ -196,7 +208,7 @@ exec "$@"`;
|
|
|
196
208
|
console.log(chalk.yellow('š” Please run manually:'));
|
|
197
209
|
console.log(chalk.yellow(' cd /root/.nvm/versions/node/v22.18.0/lib/node_modules/gitarsenal-cli'));
|
|
198
210
|
console.log(chalk.yellow(' uv venv'));
|
|
199
|
-
console.log(chalk.yellow(' uv pip install
|
|
211
|
+
console.log(chalk.yellow(' uv pip install -r python/requirements.txt'));
|
|
200
212
|
return false;
|
|
201
213
|
}
|
|
202
214
|
}
|
|
@@ -382,6 +394,7 @@ if __name__ == "__main__":
|
|
|
382
394
|
- Modal
|
|
383
395
|
- GitIngest
|
|
384
396
|
- Requests
|
|
397
|
+
- Anthropic (for Claude fallback)
|
|
385
398
|
|
|
386
399
|
š” Next steps:
|
|
387
400
|
⢠Run: gitarsenal --help
|
|
@@ -393,6 +406,10 @@ if __name__ == "__main__":
|
|
|
393
406
|
⢠Windows: .venv\\Scripts\\activate.bat
|
|
394
407
|
⢠Or use: ./activate_venv.sh (Unix/macOS) or activate_venv.bat (Windows)
|
|
395
408
|
|
|
409
|
+
š” Claude Fallback:
|
|
410
|
+
⢠Set ANTHROPIC_API_KEY for Claude fallback functionality
|
|
411
|
+
⢠Run: python test_claude_fallback.py to test
|
|
412
|
+
|
|
396
413
|
Having issues? Run: gitarsenal --debug
|
|
397
414
|
`));
|
|
398
415
|
|
|
@@ -15,7 +15,7 @@ import uuid
|
|
|
15
15
|
import signal
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
import modal
|
|
18
|
-
|
|
18
|
+
import anthropic
|
|
19
19
|
# Import authentication manager
|
|
20
20
|
try:
|
|
21
21
|
from auth_manager import AuthManager
|
|
@@ -516,21 +516,57 @@ class PersistentShell:
|
|
|
516
516
|
REMOVE_COMMAND: <reason>
|
|
517
517
|
"""
|
|
518
518
|
|
|
519
|
-
#
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
519
|
+
# Try OpenAI API first
|
|
520
|
+
try:
|
|
521
|
+
import openai
|
|
522
|
+
client = openai.OpenAI(api_key=api_key)
|
|
523
|
+
|
|
524
|
+
response = client.chat.completions.create(
|
|
525
|
+
model="gpt-4.1",
|
|
526
|
+
messages=[
|
|
527
|
+
{"role": "system", "content": "You are a helpful assistant that suggests alternative commands that don't require user input."},
|
|
528
|
+
{"role": "user", "content": prompt}
|
|
529
|
+
],
|
|
530
|
+
max_tokens=150,
|
|
531
|
+
temperature=0.7
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
response_text = response.choices[0].message.content.strip()
|
|
535
|
+
|
|
536
|
+
except Exception as e:
|
|
537
|
+
print(f"ā ļø OpenAI API call failed: {e}")
|
|
538
|
+
|
|
539
|
+
# Try Claude as fallback if available
|
|
540
|
+
if anthropic is not None:
|
|
541
|
+
print("š Trying Claude-4-Sonnet as fallback for alternative command suggestion...")
|
|
542
|
+
try:
|
|
543
|
+
# Get Anthropic API key
|
|
544
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
545
|
+
if not anthropic_api_key:
|
|
546
|
+
print("ā ļø No ANTHROPIC_API_KEY found in environment")
|
|
547
|
+
return None
|
|
548
|
+
|
|
549
|
+
# Create Anthropic client
|
|
550
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
551
|
+
|
|
552
|
+
print("š¤ Calling Claude-4-Sonnet for alternative command suggestion...")
|
|
553
|
+
message = client.messages.create(
|
|
554
|
+
model="claude-3-5-sonnet-20241022",
|
|
555
|
+
max_tokens=150,
|
|
556
|
+
messages=[
|
|
557
|
+
{"role": "user", "content": prompt}
|
|
558
|
+
]
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
response_text = message.content[0].text.strip()
|
|
562
|
+
print("ā
Claude alternative command suggestion completed")
|
|
563
|
+
|
|
564
|
+
except Exception as claude_error:
|
|
565
|
+
print(f"ā Claude API call failed: {claude_error}")
|
|
566
|
+
return None
|
|
567
|
+
else:
|
|
568
|
+
print("ā ļø Claude fallback not available (anthropic library not installed)")
|
|
569
|
+
return None
|
|
534
570
|
|
|
535
571
|
# Check if the response suggests removing the command
|
|
536
572
|
if response_text.startswith("REMOVE_COMMAND:"):
|
|
@@ -967,23 +1003,59 @@ class CommandListManager:
|
|
|
967
1003
|
RUN: <reason>
|
|
968
1004
|
"""
|
|
969
1005
|
|
|
970
|
-
#
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1006
|
+
# Try OpenAI API first
|
|
1007
|
+
try:
|
|
1008
|
+
import openai
|
|
1009
|
+
client = openai.OpenAI(api_key=api_key)
|
|
1010
|
+
|
|
1011
|
+
print("š Analyzing if original command should be skipped...")
|
|
1012
|
+
|
|
1013
|
+
response = client.chat.completions.create(
|
|
1014
|
+
model="gpt-3.5-turbo",
|
|
1015
|
+
messages=[
|
|
1016
|
+
{"role": "system", "content": "You are a helpful assistant that analyzes command execution."},
|
|
1017
|
+
{"role": "user", "content": prompt}
|
|
1018
|
+
],
|
|
1019
|
+
max_tokens=100,
|
|
1020
|
+
temperature=0.3
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
response_text = response.choices[0].message.content.strip()
|
|
1024
|
+
|
|
1025
|
+
except Exception as e:
|
|
1026
|
+
print(f"ā ļø OpenAI API call failed: {e}")
|
|
1027
|
+
|
|
1028
|
+
# Try Claude as fallback if available
|
|
1029
|
+
if anthropic is not None:
|
|
1030
|
+
print("š Trying Claude-4-Sonnet as fallback for command skip analysis...")
|
|
1031
|
+
try:
|
|
1032
|
+
# Get Anthropic API key
|
|
1033
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1034
|
+
if not anthropic_api_key:
|
|
1035
|
+
print("ā ļø No ANTHROPIC_API_KEY found in environment")
|
|
1036
|
+
return False, "No API key available"
|
|
1037
|
+
|
|
1038
|
+
# Create Anthropic client
|
|
1039
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1040
|
+
|
|
1041
|
+
print("š¤ Calling Claude-4-Sonnet for command skip analysis...")
|
|
1042
|
+
message = client.messages.create(
|
|
1043
|
+
model="claude-3-5-sonnet-20241022",
|
|
1044
|
+
max_tokens=100,
|
|
1045
|
+
messages=[
|
|
1046
|
+
{"role": "user", "content": prompt}
|
|
1047
|
+
]
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
response_text = message.content[0].text.strip()
|
|
1051
|
+
print("ā
Claude command skip analysis completed")
|
|
1052
|
+
|
|
1053
|
+
except Exception as claude_error:
|
|
1054
|
+
print(f"ā Claude API call failed: {claude_error}")
|
|
1055
|
+
return False, f"Error: {claude_error}"
|
|
1056
|
+
else:
|
|
1057
|
+
print("ā ļø Claude fallback not available (anthropic library not installed)")
|
|
1058
|
+
return False, "No API key available"
|
|
987
1059
|
|
|
988
1060
|
# Parse the response
|
|
989
1061
|
if response_text.startswith("SKIP:"):
|
|
@@ -1111,24 +1183,60 @@ class CommandListManager:
|
|
|
1111
1183
|
Only include commands that need changes (SKIP, MODIFY, ADD_AFTER), not KEEP actions.
|
|
1112
1184
|
"""
|
|
1113
1185
|
|
|
1114
|
-
#
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1186
|
+
# Try OpenAI API first
|
|
1187
|
+
try:
|
|
1188
|
+
import openai
|
|
1189
|
+
import json
|
|
1190
|
+
client = openai.OpenAI(api_key=api_key)
|
|
1191
|
+
|
|
1192
|
+
print("š Analyzing command list for optimizations...")
|
|
1193
|
+
|
|
1194
|
+
response = client.chat.completions.create(
|
|
1195
|
+
model="gpt-4.1", # Use a more capable model for this complex task
|
|
1196
|
+
messages=[
|
|
1197
|
+
{"role": "system", "content": "You are a helpful assistant that analyzes and optimizes command lists."},
|
|
1198
|
+
{"role": "user", "content": prompt}
|
|
1199
|
+
],
|
|
1200
|
+
max_tokens=1000,
|
|
1201
|
+
temperature=0.2
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
response_text = response.choices[0].message.content.strip()
|
|
1205
|
+
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
print(f"ā ļø OpenAI API call failed: {e}")
|
|
1208
|
+
|
|
1209
|
+
# Try Claude as fallback if available
|
|
1210
|
+
if anthropic is not None:
|
|
1211
|
+
print("š Trying Claude-4-Sonnet as fallback for command list analysis...")
|
|
1212
|
+
try:
|
|
1213
|
+
# Get Anthropic API key
|
|
1214
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1215
|
+
if not anthropic_api_key:
|
|
1216
|
+
print("ā ļø No ANTHROPIC_API_KEY found in environment")
|
|
1217
|
+
return False
|
|
1218
|
+
|
|
1219
|
+
# Create Anthropic client
|
|
1220
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1221
|
+
|
|
1222
|
+
print("š¤ Calling Claude-4-Sonnet for command list analysis...")
|
|
1223
|
+
message = client.messages.create(
|
|
1224
|
+
model="claude-3-5-sonnet-20241022",
|
|
1225
|
+
max_tokens=1000,
|
|
1226
|
+
messages=[
|
|
1227
|
+
{"role": "user", "content": prompt}
|
|
1228
|
+
]
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
response_text = message.content[0].text.strip()
|
|
1232
|
+
print("ā
Claude command list analysis completed")
|
|
1233
|
+
|
|
1234
|
+
except Exception as claude_error:
|
|
1235
|
+
print(f"ā Claude API call failed: {claude_error}")
|
|
1236
|
+
return False
|
|
1237
|
+
else:
|
|
1238
|
+
print("ā ļø Claude fallback not available (anthropic library not installed)")
|
|
1239
|
+
return False
|
|
1132
1240
|
|
|
1133
1241
|
# Extract JSON from the response
|
|
1134
1242
|
try:
|
|
@@ -1780,8 +1888,186 @@ Do not provide any explanations, just the exact command to run.
|
|
|
1780
1888
|
last_error = error
|
|
1781
1889
|
|
|
1782
1890
|
if not result:
|
|
1783
|
-
print(f"ā All model attempts failed. Last error: {last_error}")
|
|
1784
|
-
|
|
1891
|
+
print(f"ā All OpenAI model attempts failed. Last error: {last_error}")
|
|
1892
|
+
|
|
1893
|
+
# Try Claude as fallback if available
|
|
1894
|
+
if anthropic is not None:
|
|
1895
|
+
print("š Trying Claude-4-Sonnet as fallback...")
|
|
1896
|
+
try:
|
|
1897
|
+
# Get Anthropic API key
|
|
1898
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1899
|
+
if not anthropic_api_key:
|
|
1900
|
+
print("ā ļø No ANTHROPIC_API_KEY found in environment")
|
|
1901
|
+
return None
|
|
1902
|
+
|
|
1903
|
+
# Create Anthropic client
|
|
1904
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1905
|
+
|
|
1906
|
+
# Prepare the same prompt for Claude
|
|
1907
|
+
claude_prompt = f"""
|
|
1908
|
+
I'm trying to run the following command in a Linux environment:
|
|
1909
|
+
|
|
1910
|
+
```
|
|
1911
|
+
{command}
|
|
1912
|
+
```
|
|
1913
|
+
|
|
1914
|
+
But it failed with this error:
|
|
1915
|
+
|
|
1916
|
+
```
|
|
1917
|
+
{error_output}
|
|
1918
|
+
```
|
|
1919
|
+
{system_info}
|
|
1920
|
+
{directory_context}
|
|
1921
|
+
{file_context}
|
|
1922
|
+
|
|
1923
|
+
AVAILABLE CREDENTIALS:
|
|
1924
|
+
{auth_context}
|
|
1925
|
+
|
|
1926
|
+
Please analyze the error and provide ONLY a single terminal command that would fix the issue.
|
|
1927
|
+
Consider the current directory, system information, directory contents, and available credentials carefully before suggesting a solution.
|
|
1928
|
+
|
|
1929
|
+
IMPORTANT GUIDELINES:
|
|
1930
|
+
1. For any commands that might ask for yes/no confirmation, use the appropriate non-interactive flag:
|
|
1931
|
+
- For apt/apt-get: use -y or --yes
|
|
1932
|
+
- For rm: use -f or --force
|
|
1933
|
+
|
|
1934
|
+
2. If the error indicates a file is not found:
|
|
1935
|
+
- FIRST try to search for the file using: find . -name "filename" -type f 2>/dev/null
|
|
1936
|
+
- If found, navigate to that directory using: cd /path/to/directory
|
|
1937
|
+
- If not found, then consider creating the file or installing missing packages
|
|
1938
|
+
|
|
1939
|
+
3. For missing packages or dependencies:
|
|
1940
|
+
- Use pip install for Python packages
|
|
1941
|
+
- Use apt-get install -y for system packages
|
|
1942
|
+
- Use npm install for Node.js packages
|
|
1943
|
+
|
|
1944
|
+
4. For authentication issues:
|
|
1945
|
+
- Analyze the error to determine what type of authentication is needed
|
|
1946
|
+
- ALWAYS use the actual credential values from the AVAILABLE CREDENTIALS section above (NOT placeholders)
|
|
1947
|
+
- Look for the specific API key or token needed in the auth_context and use its exact value
|
|
1948
|
+
- Common patterns:
|
|
1949
|
+
* wandb errors: use wandb login with the actual WANDB_API_KEY value from auth_context
|
|
1950
|
+
* huggingface errors: use huggingface-cli login with the actual HF_TOKEN or HUGGINGFACE_TOKEN value from auth_context
|
|
1951
|
+
* github errors: configure git credentials with the actual GITHUB_TOKEN value from auth_context
|
|
1952
|
+
* kaggle errors: create ~/.kaggle/kaggle.json with the actual KAGGLE_USERNAME and KAGGLE_KEY values from auth_context
|
|
1953
|
+
* API errors: export the appropriate API key as environment variable using the actual value from auth_context
|
|
1954
|
+
|
|
1955
|
+
5. Environment variable exports:
|
|
1956
|
+
- Use export commands for API keys that need to be in environment
|
|
1957
|
+
- ALWAYS use the actual credential values from auth_context, never use placeholders like "YOUR_API_KEY"
|
|
1958
|
+
- Example: export OPENAI_API_KEY="sk-..." (using the actual key from auth_context)
|
|
1959
|
+
|
|
1960
|
+
6. CRITICAL: When using any API key, token, or credential:
|
|
1961
|
+
- Find the exact value in the AVAILABLE CREDENTIALS section
|
|
1962
|
+
- Use that exact value in your command
|
|
1963
|
+
- Do not use generic placeholders or dummy values
|
|
1964
|
+
- The auth_context contains real, usable credentials
|
|
1965
|
+
|
|
1966
|
+
7. For Git SSH authentication failures:
|
|
1967
|
+
- If the error contains "Host key verification failed" or "Could not read from remote repository"
|
|
1968
|
+
- ALWAYS convert SSH URLs to HTTPS URLs for public repositories
|
|
1969
|
+
- Replace git@github.com:username/repo.git with https://github.com/username/repo.git
|
|
1970
|
+
- This works for public repositories without authentication
|
|
1971
|
+
- Example: git clone https://github.com/xg-chu/ARTalk.git
|
|
1972
|
+
|
|
1973
|
+
Do not provide any explanations, just the exact command to run.
|
|
1974
|
+
"""
|
|
1975
|
+
|
|
1976
|
+
print("š¤ Calling Claude-4-Sonnet to debug the failed command...")
|
|
1977
|
+
message = client.messages.create(
|
|
1978
|
+
model="claude-3-5-sonnet-20241022",
|
|
1979
|
+
max_tokens=300,
|
|
1980
|
+
messages=[
|
|
1981
|
+
{"role": "user", "content": claude_prompt}
|
|
1982
|
+
]
|
|
1983
|
+
)
|
|
1984
|
+
|
|
1985
|
+
fix_command = message.content[0].text.strip()
|
|
1986
|
+
print(f"š DEBUG: Raw Claude response content: {fix_command}")
|
|
1987
|
+
|
|
1988
|
+
# Process the response similar to OpenAI
|
|
1989
|
+
original_response = fix_command
|
|
1990
|
+
|
|
1991
|
+
# Extract just the command if it's wrapped in backticks or explanation
|
|
1992
|
+
if "```" in fix_command:
|
|
1993
|
+
# Extract content between backticks
|
|
1994
|
+
import re
|
|
1995
|
+
code_blocks = re.findall(r'```(?:bash|sh)?\s*(.*?)\s*```', fix_command, re.DOTALL)
|
|
1996
|
+
if code_blocks:
|
|
1997
|
+
fix_command = code_blocks[0].strip()
|
|
1998
|
+
print(f"ā
Extracted command from code block: {fix_command}")
|
|
1999
|
+
|
|
2000
|
+
# If the response still has explanatory text, try to extract just the command
|
|
2001
|
+
if len(fix_command.split('\n')) > 1:
|
|
2002
|
+
# First try to find lines that look like commands (start with common command prefixes)
|
|
2003
|
+
command_prefixes = ['sudo', 'apt', 'pip', 'npm', 'yarn', 'git', 'cd', 'mv', 'cp', 'rm', 'mkdir', 'touch',
|
|
2004
|
+
'chmod', 'chown', 'echo', 'cat', 'python', 'python3', 'node', 'export',
|
|
2005
|
+
'curl', 'wget', 'docker', 'make', 'gcc', 'g++', 'javac', 'java',
|
|
2006
|
+
'conda', 'uv', 'poetry', 'nvm', 'rbenv', 'pyenv', 'rustup']
|
|
2007
|
+
|
|
2008
|
+
# Check for lines that start with common command prefixes
|
|
2009
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
|
2010
|
+
if any(line.strip().startswith(prefix) for prefix in command_prefixes)]
|
|
2011
|
+
|
|
2012
|
+
if command_lines:
|
|
2013
|
+
# Use the first command line found
|
|
2014
|
+
fix_command = command_lines[0]
|
|
2015
|
+
print(f"ā
Identified command by prefix: {fix_command}")
|
|
2016
|
+
else:
|
|
2017
|
+
# Try to find lines that look like commands (contain common shell patterns)
|
|
2018
|
+
shell_patterns = [' | ', ' > ', ' >> ', ' && ', ' || ', ' ; ', '$(', '`', ' -y ', ' --yes ']
|
|
2019
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
|
2020
|
+
if any(pattern in line for pattern in shell_patterns)]
|
|
2021
|
+
|
|
2022
|
+
if command_lines:
|
|
2023
|
+
# Use the first command line found
|
|
2024
|
+
fix_command = command_lines[0]
|
|
2025
|
+
print(f"ā
Identified command by shell pattern: {fix_command}")
|
|
2026
|
+
else:
|
|
2027
|
+
# Fall back to the shortest non-empty line as it's likely the command
|
|
2028
|
+
lines = [line.strip() for line in fix_command.split('\n') if line.strip()]
|
|
2029
|
+
if lines:
|
|
2030
|
+
# Exclude very short lines that are likely not commands
|
|
2031
|
+
valid_lines = [line for line in lines if len(line) > 5]
|
|
2032
|
+
if valid_lines:
|
|
2033
|
+
fix_command = min(valid_lines, key=len)
|
|
2034
|
+
else:
|
|
2035
|
+
fix_command = min(lines, key=len)
|
|
2036
|
+
print(f"ā
Selected shortest line as command: {fix_command}")
|
|
2037
|
+
|
|
2038
|
+
# Clean up the command - remove any trailing periods or quotes
|
|
2039
|
+
fix_command = fix_command.rstrip('.;"\'')
|
|
2040
|
+
|
|
2041
|
+
# Remove common prefixes that LLMs sometimes add
|
|
2042
|
+
prefixes_to_remove = [
|
|
2043
|
+
"Run: ", "Execute: ", "Try: ", "Command: ", "Fix: ", "Solution: ",
|
|
2044
|
+
"You should run: ", "You can run: ", "You need to run: "
|
|
2045
|
+
]
|
|
2046
|
+
for prefix in prefixes_to_remove:
|
|
2047
|
+
if fix_command.startswith(prefix):
|
|
2048
|
+
fix_command = fix_command[len(prefix):].strip()
|
|
2049
|
+
print(f"ā
Removed prefix: {prefix}")
|
|
2050
|
+
break
|
|
2051
|
+
|
|
2052
|
+
# If the command is still multi-line or very long, it might not be a valid command
|
|
2053
|
+
if len(fix_command.split('\n')) > 1 or len(fix_command) > 500:
|
|
2054
|
+
print("ā ļø Extracted command appears invalid (multi-line or too long)")
|
|
2055
|
+
print("š Original response from Claude:")
|
|
2056
|
+
print("-" * 60)
|
|
2057
|
+
print(original_response)
|
|
2058
|
+
print("-" * 60)
|
|
2059
|
+
print("ā ļø Using best guess for command")
|
|
2060
|
+
|
|
2061
|
+
print(f"š§ Claude suggested fix: {fix_command}")
|
|
2062
|
+
print(f"š DEBUG: Returning Claude fix command: {fix_command}")
|
|
2063
|
+
return fix_command
|
|
2064
|
+
|
|
2065
|
+
except Exception as e:
|
|
2066
|
+
print(f"ā Claude API call failed: {e}")
|
|
2067
|
+
return None
|
|
2068
|
+
else:
|
|
2069
|
+
print("ā ļø Claude fallback not available (anthropic library not installed)")
|
|
2070
|
+
return None
|
|
1785
2071
|
|
|
1786
2072
|
# Process the response
|
|
1787
2073
|
try:
|
|
@@ -1988,7 +2274,65 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
|
|
|
1988
2274
|
return fixes
|
|
1989
2275
|
else:
|
|
1990
2276
|
print(f"ā OpenAI API error: {response.status_code} - {response.text}")
|
|
1991
|
-
|
|
2277
|
+
|
|
2278
|
+
# Try Claude as fallback if available
|
|
2279
|
+
if anthropic is not None:
|
|
2280
|
+
print("š Trying Claude-4-Sonnet as fallback for batch debugging...")
|
|
2281
|
+
try:
|
|
2282
|
+
# Get Anthropic API key
|
|
2283
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
2284
|
+
if not anthropic_api_key:
|
|
2285
|
+
print("ā ļø No ANTHROPIC_API_KEY found in environment")
|
|
2286
|
+
return []
|
|
2287
|
+
|
|
2288
|
+
# Create Anthropic client
|
|
2289
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
2290
|
+
|
|
2291
|
+
print("š¤ Calling Claude-4-Sonnet for batch debugging...")
|
|
2292
|
+
message = client.messages.create(
|
|
2293
|
+
model="claude-3-5-sonnet-20241022",
|
|
2294
|
+
max_tokens=1000,
|
|
2295
|
+
messages=[
|
|
2296
|
+
{"role": "user", "content": prompt}
|
|
2297
|
+
]
|
|
2298
|
+
)
|
|
2299
|
+
|
|
2300
|
+
content = message.content[0].text
|
|
2301
|
+
print(f"ā
Claude batch analysis completed")
|
|
2302
|
+
|
|
2303
|
+
# Parse the response to extract fix commands
|
|
2304
|
+
fixes = []
|
|
2305
|
+
for i in range(1, len(failed_commands) + 1):
|
|
2306
|
+
fix_pattern = f"FIX_COMMAND_{i}: (.+)"
|
|
2307
|
+
reason_pattern = f"REASON_{i}: (.+)"
|
|
2308
|
+
|
|
2309
|
+
fix_match = re.search(fix_pattern, content, re.MULTILINE)
|
|
2310
|
+
reason_match = re.search(reason_pattern, content, re.MULTILINE)
|
|
2311
|
+
|
|
2312
|
+
if fix_match:
|
|
2313
|
+
fix_command = fix_match.group(1).strip()
|
|
2314
|
+
reason = reason_match.group(1).strip() if reason_match else "Claude suggested fix"
|
|
2315
|
+
|
|
2316
|
+
# Clean up the fix command
|
|
2317
|
+
if fix_command.startswith('`') and fix_command.endswith('`'):
|
|
2318
|
+
fix_command = fix_command[1:-1]
|
|
2319
|
+
|
|
2320
|
+
fixes.append({
|
|
2321
|
+
'original_command': failed_commands[i-1]['command'],
|
|
2322
|
+
'fix_command': fix_command,
|
|
2323
|
+
'reason': reason,
|
|
2324
|
+
'command_index': i-1
|
|
2325
|
+
})
|
|
2326
|
+
|
|
2327
|
+
print(f"š§ Generated {len(fixes)} fix commands from Claude batch analysis")
|
|
2328
|
+
return fixes
|
|
2329
|
+
|
|
2330
|
+
except Exception as e:
|
|
2331
|
+
print(f"ā Claude API call failed: {e}")
|
|
2332
|
+
return []
|
|
2333
|
+
else:
|
|
2334
|
+
print("ā ļø Claude fallback not available (anthropic library not installed)")
|
|
2335
|
+
return []
|
|
1992
2336
|
|
|
1993
2337
|
except Exception as e:
|
|
1994
2338
|
print(f"ā Error during batch debugging: {e}")
|
|
@@ -3565,21 +3909,57 @@ Return only the JSON array, no other text.
|
|
|
3565
3909
|
print("ā ļø No OpenAI API key available for command preprocessing")
|
|
3566
3910
|
return setup_commands
|
|
3567
3911
|
|
|
3568
|
-
#
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3912
|
+
# Try OpenAI API first
|
|
3913
|
+
try:
|
|
3914
|
+
import openai
|
|
3915
|
+
client = openai.OpenAI(api_key=api_key)
|
|
3916
|
+
|
|
3917
|
+
response = client.chat.completions.create(
|
|
3918
|
+
model="gpt-3.5-turbo",
|
|
3919
|
+
messages=[
|
|
3920
|
+
{"role": "system", "content": "You are a command preprocessing assistant that modifies setup commands to use available credentials and make them non-interactive."},
|
|
3921
|
+
{"role": "user", "content": prompt}
|
|
3922
|
+
],
|
|
3923
|
+
temperature=0.1,
|
|
3924
|
+
max_tokens=2000
|
|
3925
|
+
)
|
|
3926
|
+
|
|
3927
|
+
result = response.choices[0].message.content.strip()
|
|
3928
|
+
|
|
3929
|
+
except Exception as e:
|
|
3930
|
+
print(f"ā ļø OpenAI API call failed: {e}")
|
|
3931
|
+
|
|
3932
|
+
# Try Claude as fallback if available
|
|
3933
|
+
if anthropic is not None:
|
|
3934
|
+
print("š Trying Claude-4-Sonnet as fallback for command preprocessing...")
|
|
3935
|
+
try:
|
|
3936
|
+
# Get Anthropic API key
|
|
3937
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
3938
|
+
if not anthropic_api_key:
|
|
3939
|
+
print("ā ļø No ANTHROPIC_API_KEY found in environment")
|
|
3940
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3941
|
+
|
|
3942
|
+
# Create Anthropic client
|
|
3943
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
3944
|
+
|
|
3945
|
+
print("š¤ Calling Claude-4-Sonnet for command preprocessing...")
|
|
3946
|
+
message = client.messages.create(
|
|
3947
|
+
model="claude-3-5-sonnet-20241022",
|
|
3948
|
+
max_tokens=2000,
|
|
3949
|
+
messages=[
|
|
3950
|
+
{"role": "user", "content": prompt}
|
|
3951
|
+
]
|
|
3952
|
+
)
|
|
3953
|
+
|
|
3954
|
+
result = message.content[0].text.strip()
|
|
3955
|
+
print("ā
Claude preprocessing completed")
|
|
3956
|
+
|
|
3957
|
+
except Exception as claude_error:
|
|
3958
|
+
print(f"ā Claude API call failed: {claude_error}")
|
|
3959
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3960
|
+
else:
|
|
3961
|
+
print("ā ļø Claude fallback not available (anthropic library not installed)")
|
|
3962
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3583
3963
|
|
|
3584
3964
|
# Debug: Print the raw response
|
|
3585
3965
|
print(f"š LLM Response: {result[:200]}...")
|
|
@@ -3929,19 +4309,7 @@ if __name__ == "__main__":
|
|
|
3929
4309
|
print(f"Setup Commands: {len(args.setup_commands)} custom commands")
|
|
3930
4310
|
else:
|
|
3931
4311
|
print("Setup Commands: Auto-detect from repository")
|
|
3932
|
-
|
|
3933
|
-
# Confirm settings
|
|
3934
|
-
if not args.yes:
|
|
3935
|
-
try:
|
|
3936
|
-
proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
|
|
3937
|
-
if proceed in ('n', 'no'):
|
|
3938
|
-
print("š Operation cancelled by user.")
|
|
3939
|
-
sys.exit(0)
|
|
3940
|
-
except KeyboardInterrupt:
|
|
3941
|
-
print("\nš Operation cancelled by user.")
|
|
3942
|
-
sys.exit(0)
|
|
3943
|
-
else:
|
|
3944
|
-
print("ā
Skipping confirmation prompt (--yes flag used)")
|
|
4312
|
+
|
|
3945
4313
|
|
|
3946
4314
|
# Interactive mode or missing required arguments
|
|
3947
4315
|
if args.interactive or not args.repo_url or not args.volume_name:
|