nc1709 1.15.4__py3-none-any.whl → 1.18.8__py3-none-any.whl

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.
@@ -24,15 +24,65 @@ SAFE_COMMANDS = {
24
24
  "find", "grep", "rg", "ag", "fd", "which", "whereis", "locate",
25
25
  # Git read-only
26
26
  "git status", "git log", "git diff", "git branch", "git remote",
27
- "git show", "git ls-files", "git rev-parse",
27
+ "git show", "git ls-files", "git rev-parse", "git tag",
28
28
  # System info (read-only)
29
29
  "whoami", "date", "uptime", "uname", "hostname", "id",
30
30
  "df", "du", "free", "top -l 1", "ps",
31
- # Package info (read-only)
32
- "pip list", "pip show", "npm list", "npm ls", "yarn list",
33
- "brew list", "brew info",
31
+ # Package info (read-only) - Python
32
+ "pip list", "pip show", "pip freeze", "pip check",
33
+ "pip3 list", "pip3 show", "pip3 freeze",
34
+ "poetry show", "poetry version", "poetry env list",
35
+ "pipenv graph", "pipenv --version",
36
+ "uv pip list", "uv pip show",
37
+ "conda list", "conda info", "conda env list",
38
+ # Package info (read-only) - JavaScript/Node
39
+ "npm list", "npm ls", "npm outdated", "npm view", "npm version",
40
+ "yarn list", "yarn info", "yarn outdated", "yarn version",
41
+ "pnpm list", "pnpm ls", "pnpm outdated",
42
+ "bun pm ls",
43
+ # Package info (read-only) - Other languages
44
+ "cargo tree", "cargo metadata", "cargo version",
45
+ "go list", "go version", "go env",
46
+ "rustc --version", "rustup show",
47
+ "ruby --version", "gem list", "bundle list",
48
+ "composer show", "composer info",
49
+ "mix deps.tree", "mix hex.info",
50
+ # Package info (read-only) - System
51
+ "brew list", "brew info", "brew outdated",
52
+ "apt list", "dpkg -l",
34
53
  # Environment
35
54
  "env", "printenv", "echo",
55
+ # Ollama read-only (model listing)
56
+ "ollama list", "ollama ls", "ollama show", "ollama ps",
57
+ # Docker read-only
58
+ "docker ps", "docker images", "docker logs", "docker inspect",
59
+ "docker stats", "docker version", "docker info",
60
+ "docker-compose ps", "docker compose ps",
61
+ # Kubernetes read-only
62
+ "kubectl get", "kubectl describe", "kubectl logs", "kubectl top",
63
+ "kubectl version", "kubectl cluster-info", "kubectl config view",
64
+ "kubectl api-resources",
65
+ # Database read-only (listing/info only)
66
+ "psql --version", "mysql --version", "mongo --version",
67
+ "redis-cli --version", "sqlite3 --version",
68
+ "pg_isready",
69
+ # Cloud CLI read-only
70
+ "aws --version", "aws sts get-caller-identity",
71
+ "aws s3 ls", "aws ec2 describe-instances",
72
+ "gcloud --version", "gcloud config list", "gcloud projects list",
73
+ "az --version", "az account show", "az group list",
74
+ # Infrastructure read-only
75
+ "terraform version", "terraform providers", "terraform state list",
76
+ "terraform validate", "terraform fmt -check",
77
+ "ansible --version", "ansible-inventory --list",
78
+ # Testing frameworks (read-only)
79
+ "pytest --collect-only", "pytest --version",
80
+ "jest --version", "mocha --version", "vitest --version",
81
+ "go test -list",
82
+ # Misc tools
83
+ "make --version", "cmake --version",
84
+ "node --version", "python --version", "python3 --version",
85
+ "java --version", "javac --version",
36
86
  }
37
87
 
38
88
  # Dangerous commands that should always be blocked or warned about
@@ -58,6 +108,137 @@ CAUTIOUS_COMMANDS = {
58
108
  "curl | sh", "wget | sh", "curl | bash", "wget | bash",
59
109
  }
60
110
 
111
+ # Error patterns with helpful suggestions
112
+ ERROR_SUGGESTIONS = {
113
+ # Python errors
114
+ "ModuleNotFoundError": "Install the missing module with: pip install {module}",
115
+ "No module named": "Install the missing module with: pip install {module}",
116
+ "ImportError": "Check module installation or virtual environment activation",
117
+ # Node.js errors
118
+ "Cannot find module": "Install with: npm install {module}",
119
+ "MODULE_NOT_FOUND": "Run: npm install to install dependencies",
120
+ "ERR_MODULE_NOT_FOUND": "Run: npm install to install dependencies",
121
+ # Command not found
122
+ "command not found": "Install {command} or check your PATH",
123
+ "not found": "The command may not be installed. Try installing it first.",
124
+ # Permission errors
125
+ "Permission denied": "Try with sudo or check file permissions (chmod)",
126
+ "EACCES": "Permission denied. Check file/directory permissions.",
127
+ # Network errors
128
+ "Could not resolve host": "Check your network connection and DNS settings",
129
+ "Connection refused": "The service may not be running. Check if it's started.",
130
+ "ECONNREFUSED": "Connection refused. Is the service running?",
131
+ "Network is unreachable": "Check your network connection",
132
+ "Temporary failure in name resolution": "DNS resolution failed. Check network.",
133
+ # Git errors
134
+ "not a git repository": "Initialize with: git init",
135
+ "fatal: refusing to merge unrelated histories": "Use: git pull --allow-unrelated-histories",
136
+ "Your branch is behind": "Run: git pull to update your branch",
137
+ "CONFLICT": "Merge conflict detected. Resolve conflicts manually.",
138
+ # Docker errors
139
+ "Cannot connect to the Docker daemon": "Start Docker: sudo systemctl start docker",
140
+ "Error response from daemon": "Check Docker logs: docker logs <container>",
141
+ "port is already allocated": "Port in use. Stop the other service or use different port.",
142
+ # Database errors
143
+ "connection refused": "Database may not be running. Check service status.",
144
+ "FATAL: role": "Create the database user or check connection string",
145
+ "Access denied for user": "Check database username/password",
146
+ # Kubernetes errors
147
+ "Unable to connect to the server": "Check kubectl config: kubectl config view",
148
+ "error: the server doesn't have a resource type": "Invalid resource. Use: kubectl api-resources",
149
+ # File errors
150
+ "No such file or directory": "Check if the path exists. Use: ls {path}",
151
+ "Is a directory": "Expected a file but got a directory",
152
+ "Not a directory": "Expected a directory but got a file",
153
+ # Memory/Resource errors
154
+ "Cannot allocate memory": "System out of memory. Free up resources.",
155
+ "Killed": "Process killed (likely OOM). Try with less data or more memory.",
156
+ "ENOMEM": "Out of memory. Close other applications.",
157
+ # Disk errors
158
+ "No space left on device": "Disk full. Free up space: df -h",
159
+ "ENOSPC": "Disk full. Clean up files or expand storage.",
160
+ }
161
+
162
+ # Commands that need extended timeouts (in seconds)
163
+ EXTENDED_TIMEOUT_COMMANDS = {
164
+ # AI/ML model downloads
165
+ "ollama pull": 1800, # 30 minutes for large model downloads
166
+ "ollama run": 600, # 10 minutes for model loading
167
+ # Docker operations
168
+ "docker pull": 900, # 15 minutes for docker images
169
+ "docker build": 1200, # 20 minutes for docker builds
170
+ "docker-compose up": 600, # 10 minutes for compose
171
+ "docker compose up": 600,
172
+ # Python package managers
173
+ "pip install": 600, # 10 minutes for pip
174
+ "pip3 install": 600,
175
+ "poetry install": 600, # 10 minutes for poetry
176
+ "poetry add": 300,
177
+ "pipenv install": 600,
178
+ "uv pip install": 300, # uv is faster
179
+ "conda install": 600, # 10 minutes for conda
180
+ "conda create": 600,
181
+ # JavaScript package managers
182
+ "npm install": 600, # 10 minutes for npm
183
+ "npm ci": 600,
184
+ "yarn install": 600, # 10 minutes for yarn
185
+ "yarn add": 300,
186
+ "pnpm install": 600,
187
+ "bun install": 300, # bun is faster
188
+ # Rust/Cargo
189
+ "cargo build": 1200, # 20 minutes for large Rust projects
190
+ "cargo install": 900,
191
+ "cargo test": 600,
192
+ # Go
193
+ "go build": 600,
194
+ "go install": 600,
195
+ "go mod download": 300,
196
+ # Java/JVM
197
+ "mvn install": 1200, # 20 minutes for Maven
198
+ "mvn clean install": 1200,
199
+ "mvn package": 900,
200
+ "gradle build": 1200, # 20 minutes for Gradle
201
+ "./gradlew build": 1200,
202
+ "sbt compile": 900, # 15 minutes for Scala
203
+ # Ruby
204
+ "bundle install": 600,
205
+ "gem install": 300,
206
+ # PHP
207
+ "composer install": 600,
208
+ "composer update": 600,
209
+ # Database operations
210
+ "pg_dump": 1800, # 30 minutes for large DB dumps
211
+ "pg_restore": 3600, # 1 hour for large restores
212
+ "mysqldump": 1800,
213
+ "mongodump": 1800,
214
+ "mongorestore": 3600,
215
+ # Cloud/Infrastructure
216
+ "terraform apply": 1800, # 30 minutes for infra deployment
217
+ "terraform plan": 600,
218
+ "terraform destroy": 1800,
219
+ "pulumi up": 1800,
220
+ "ansible-playbook": 1800,
221
+ "aws s3 sync": 1800, # 30 minutes for large S3 syncs
222
+ "aws s3 cp": 900,
223
+ "gcloud builds submit": 1200,
224
+ # Kubernetes
225
+ "kubectl apply": 600,
226
+ "kubectl rollout status": 600,
227
+ "helm install": 600,
228
+ "helm upgrade": 600,
229
+ # Testing (can take a while for large test suites)
230
+ "pytest": 1200, # 20 minutes for full test suite
231
+ "npm test": 900,
232
+ "yarn test": 900,
233
+ "go test": 900,
234
+ "cargo test": 900,
235
+ "make test": 900,
236
+ # Build systems
237
+ "make": 900,
238
+ "cmake --build": 1200,
239
+ "ninja": 900,
240
+ }
241
+
61
242
 
62
243
  class BashTool(Tool):
63
244
  """Execute bash commands"""
@@ -122,8 +303,82 @@ class BashTool(Tool):
122
303
  if two_word in SAFE_COMMANDS:
123
304
  return True
124
305
 
306
+ # Check three-word safe commands (like "aws s3 ls", "docker compose ps")
307
+ if len(parts) >= 3:
308
+ three_word = f"{parts[0]} {parts[1]} {parts[2]}"
309
+ if three_word in SAFE_COMMANDS:
310
+ return True
311
+
125
312
  return False
126
313
 
314
+ @staticmethod
315
+ def get_extended_timeout(command: str) -> Optional[int]:
316
+ """Get extended timeout for commands that need more time.
317
+
318
+ Returns the extended timeout in seconds, or None if default should be used.
319
+ """
320
+ cmd_lower = command.strip().lower()
321
+
322
+ for cmd_prefix, timeout in EXTENDED_TIMEOUT_COMMANDS.items():
323
+ if cmd_lower.startswith(cmd_prefix):
324
+ return timeout
325
+
326
+ return None
327
+
328
+ @staticmethod
329
+ def get_error_suggestion(error_output: str, command: str = "") -> Optional[str]:
330
+ """Get a helpful suggestion based on error output.
331
+
332
+ Analyzes error messages and returns actionable suggestions.
333
+ """
334
+ import re
335
+
336
+ error_lower = error_output.lower()
337
+
338
+ for pattern, suggestion in ERROR_SUGGESTIONS.items():
339
+ if pattern.lower() in error_lower:
340
+ # Try to extract relevant context for placeholders
341
+ result = suggestion
342
+
343
+ # Extract module name for Python/Node errors
344
+ if "{module}" in suggestion:
345
+ # Python: No module named 'xyz'
346
+ match = re.search(r"no module named ['\"]?(\w+)", error_lower)
347
+ if match:
348
+ result = suggestion.replace("{module}", match.group(1))
349
+ else:
350
+ # Node: Cannot find module 'xyz'
351
+ match = re.search(r"cannot find module ['\"]?([^'\"]+)", error_lower)
352
+ if match:
353
+ result = suggestion.replace("{module}", match.group(1))
354
+ else:
355
+ result = suggestion.replace("{module}", "<module_name>")
356
+
357
+ # Extract command name for 'command not found'
358
+ if "{command}" in suggestion:
359
+ match = re.search(r"(\w+):\s*command not found", error_lower)
360
+ if match:
361
+ result = suggestion.replace("{command}", match.group(1))
362
+ else:
363
+ # Extract from original command
364
+ cmd_parts = command.split()
365
+ if cmd_parts:
366
+ result = suggestion.replace("{command}", cmd_parts[0])
367
+ else:
368
+ result = suggestion.replace("{command}", "<command>")
369
+
370
+ # Extract path for file errors
371
+ if "{path}" in suggestion:
372
+ match = re.search(r"['\"]?(/[^'\":\s]+|\.?/[^'\":\s]+)", error_output)
373
+ if match:
374
+ result = suggestion.replace("{path}", match.group(1))
375
+ else:
376
+ result = suggestion.replace("{path}", ".")
377
+
378
+ return result
379
+
380
+ return None
381
+
127
382
  def get_effective_permission(self, command: str) -> ToolPermission:
128
383
  """Get the effective permission for a specific command."""
129
384
  if self.is_safe_command(command):
@@ -140,6 +395,11 @@ class BashTool(Tool):
140
395
 
141
396
  # Safety checks
142
397
  safety_result = self._check_safety(command)
398
+
399
+ # Use extended timeout for certain commands if not explicitly specified
400
+ extended_timeout = self.get_extended_timeout(command)
401
+ if extended_timeout and timeout == 120: # Only if using default timeout
402
+ timeout = extended_timeout
143
403
  if safety_result:
144
404
  return safety_result
145
405
 
@@ -175,11 +435,22 @@ class BashTool(Tool):
175
435
  except subprocess.TimeoutExpired:
176
436
  process.kill()
177
437
  stdout, stderr = process.communicate()
438
+
439
+ # Suggest a longer timeout or background execution
440
+ suggestion = (
441
+ f"The command took longer than {timeout}s. "
442
+ "Consider:\n"
443
+ " 1. Running with a longer timeout\n"
444
+ " 2. Running in background for long-running processes\n"
445
+ " 3. Breaking into smaller steps"
446
+ )
447
+
178
448
  return ToolResult(
179
449
  success=False,
180
450
  output=stdout or "",
181
- error=f"Command timed out after {timeout} seconds\n{stderr}",
451
+ error=f"Command timed out after {timeout} seconds\n{stderr}\n\n💡 Suggestion: {suggestion}",
182
452
  target=command[:40],
453
+ data={"timeout": timeout, "suggestion": suggestion},
183
454
  )
184
455
  finally:
185
456
  self._running_processes.discard(process)
@@ -207,20 +478,36 @@ class BashTool(Tool):
207
478
  },
208
479
  )
209
480
  else:
481
+ # Build error message with suggestion if available
482
+ error_msg = f"Command failed with exit code {process.returncode}\n{stderr}"
483
+ suggestion = self.get_error_suggestion(stderr, command)
484
+ if suggestion:
485
+ error_msg += f"\n\n💡 Suggestion: {suggestion}"
486
+
210
487
  return ToolResult(
211
488
  success=False,
212
489
  output=stdout,
213
- error=f"Command failed with exit code {process.returncode}\n{stderr}",
490
+ error=error_msg,
214
491
  target=command[:40],
215
- data={"return_code": process.returncode},
492
+ data={
493
+ "return_code": process.returncode,
494
+ "suggestion": suggestion,
495
+ },
216
496
  )
217
497
 
218
498
  except Exception as e:
499
+ error_str = str(e)
500
+ suggestion = self.get_error_suggestion(error_str, command)
501
+ error_msg = f"Error executing command: {e}"
502
+ if suggestion:
503
+ error_msg += f"\n\n💡 Suggestion: {suggestion}"
504
+
219
505
  return ToolResult(
220
506
  success=False,
221
507
  output="",
222
- error=f"Error executing command: {e}",
508
+ error=error_msg,
223
509
  target=command[:40],
510
+ data={"suggestion": suggestion} if suggestion else None,
224
511
  )
225
512
 
226
513
  def _check_safety(self, command: str) -> Optional[ToolResult]: