codrninja 0.3.2__tar.gz → 0.4.0__tar.gz
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.
- {codrninja-0.3.2/src/codrninja.egg-info → codrninja-0.4.0}/PKG-INFO +1 -1
- {codrninja-0.3.2 → codrninja-0.4.0}/pyproject.toml +1 -1
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/agent.py +2 -2
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/cli.py +7 -7
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/tui.py +214 -33
- {codrninja-0.3.2 → codrninja-0.4.0/src/codrninja.egg-info}/PKG-INFO +1 -1
- {codrninja-0.3.2 → codrninja-0.4.0}/.gitignore +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/LICENSE +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/README.md +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/install.sh +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/setup.cfg +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/__init__.py +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/config.py +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/core.py +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/interactive.py +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/providers.py +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja/tools.py +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja.egg-info/SOURCES.txt +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja.egg-info/dependency_links.txt +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja.egg-info/entry_points.txt +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja.egg-info/requires.txt +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/src/codrninja.egg-info/top_level.txt +0 -0
- {codrninja-0.3.2 → codrninja-0.4.0}/tests/test_core.py +0 -0
|
@@ -121,7 +121,7 @@ class Agent:
|
|
|
121
121
|
|
|
122
122
|
# Progress callback
|
|
123
123
|
if self.on_progress:
|
|
124
|
-
self.on_progress(f"
|
|
124
|
+
self.on_progress(f"[OK] {tool_name}: {result.output[:100]}...")
|
|
125
125
|
|
|
126
126
|
return {
|
|
127
127
|
'success': True,
|
|
@@ -186,7 +186,7 @@ class Agent:
|
|
|
186
186
|
|
|
187
187
|
def _ask_permission(self, tool_name: str, params: Dict) -> bool:
|
|
188
188
|
"""Ask user for permission (interactive mode)."""
|
|
189
|
-
print(f"\n
|
|
189
|
+
print(f"\n[LOCK] Permission required for: {tool_name}")
|
|
190
190
|
print(f"Params: {json.dumps(params, indent=2)}")
|
|
191
191
|
|
|
192
192
|
try:
|
|
@@ -7,7 +7,7 @@ from typing import Optional
|
|
|
7
7
|
|
|
8
8
|
from .core import AICode
|
|
9
9
|
from .config import Config
|
|
10
|
-
from .
|
|
10
|
+
from .tui import TUI
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def main():
|
|
@@ -108,18 +108,18 @@ def main():
|
|
|
108
108
|
output(result, True)
|
|
109
109
|
else:
|
|
110
110
|
if result['success']:
|
|
111
|
-
print(f"\n
|
|
111
|
+
print(f"\n[OK] Task completed in {result['iterations']} iterations")
|
|
112
112
|
print(f"Tools used: {result['tool_calls']}")
|
|
113
113
|
print(f"\nResponse:\n{result['response'][:500]}...")
|
|
114
114
|
else:
|
|
115
|
-
print(f"\n
|
|
115
|
+
print(f"\n[FAIL] Error: {result.get('error', 'Unknown error')}")
|
|
116
116
|
|
|
117
117
|
# Interactive human mode commands
|
|
118
118
|
elif command == "chat" or command == "interactive" or command == "i":
|
|
119
119
|
session_name = sys.argv[2] if len(sys.argv) > 2 else "default"
|
|
120
120
|
session = ai.create_session(session_name)
|
|
121
121
|
|
|
122
|
-
interactive =
|
|
122
|
+
interactive = TUI(ai, session_name)
|
|
123
123
|
interactive.start()
|
|
124
124
|
|
|
125
125
|
elif command == "new":
|
|
@@ -131,7 +131,7 @@ def main():
|
|
|
131
131
|
output({"session": session_name, "status": "created"}, True)
|
|
132
132
|
else:
|
|
133
133
|
print(f"Created session: {session_name}")
|
|
134
|
-
interactive =
|
|
134
|
+
interactive = TUI(ai, session_name)
|
|
135
135
|
interactive.start()
|
|
136
136
|
|
|
137
137
|
elif command == "help" or command == "--help" or command == "-h":
|
|
@@ -242,11 +242,11 @@ def start_tui(session_name: str = "default"):
|
|
|
242
242
|
tui = TUI(ai, session_name)
|
|
243
243
|
tui.start()
|
|
244
244
|
except ImportError:
|
|
245
|
-
print("
|
|
245
|
+
print("[FAIL] Rich not installed. Install with: pip3 install rich")
|
|
246
246
|
print(" Or use: codrninja chat my-session")
|
|
247
247
|
sys.exit(1)
|
|
248
248
|
except Exception as e:
|
|
249
|
-
print(f"
|
|
249
|
+
print(f"[FAIL] Error starting TUI: {e}")
|
|
250
250
|
sys.exit(1)
|
|
251
251
|
|
|
252
252
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
codrninja TUI
|
|
3
|
+
codrninja TUI -- Interactive coding assistant using prompt_toolkit.
|
|
4
|
+
Features: Auto-onboarding, provider/model selection, slash commands.
|
|
4
5
|
"""
|
|
5
6
|
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
8
9
|
import sys
|
|
9
|
-
from typing import Optional
|
|
10
|
+
from typing import Optional, List
|
|
10
11
|
|
|
11
12
|
try:
|
|
12
13
|
from prompt_toolkit import PromptSession
|
|
13
14
|
from prompt_toolkit.completion import Completer, Completion
|
|
14
15
|
from prompt_toolkit.styles import Style
|
|
15
|
-
from prompt_toolkit.key_binding import KeyBindings
|
|
16
16
|
HAS_PROMPT = True
|
|
17
17
|
except ImportError:
|
|
18
18
|
HAS_PROMPT = False
|
|
@@ -35,18 +35,18 @@ from codrninja.tools import ToolRegistry
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
CODRNINJA_LOGO = """
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
+======================================================================================+
|
|
39
|
+
| |
|
|
40
|
+
| ####### ####### ####### ####### # # # # # # ####### |
|
|
41
|
+
| # # # # # # # ## # # ## # # # |
|
|
42
|
+
| # # # # # # # # # # # # # # # # |
|
|
43
|
+
| # # # # # # # # # # # # # # ### # # |
|
|
44
|
+
| # # # # # # # # ## # # ## # # |
|
|
45
|
+
| ####### ####### ####### # # # # # # # # ####### |
|
|
46
|
+
| |
|
|
47
|
+
| AI-first coding assistant for automation |
|
|
48
|
+
| |
|
|
49
|
+
+======================================================================================+
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
52
|
SLASH_COMMANDS = {
|
|
@@ -68,6 +68,37 @@ SLASH_COMMANDS = {
|
|
|
68
68
|
"/write": "Write to file",
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
# Provider configurations
|
|
72
|
+
PROVIDERS = {
|
|
73
|
+
"ollama": {
|
|
74
|
+
"name": "Ollama (local, free)",
|
|
75
|
+
"models": ["codellama", "llama2", "mistral", "phi", "deepseek-coder"],
|
|
76
|
+
"needs_key": False,
|
|
77
|
+
"default_url": "http://localhost:11434",
|
|
78
|
+
},
|
|
79
|
+
"openai": {
|
|
80
|
+
"name": "OpenAI (GPT-4, GPT-3.5)",
|
|
81
|
+
"models": ["gpt-4", "gpt-4-turbo", "gpt-3.5-turbo"],
|
|
82
|
+
"needs_key": True,
|
|
83
|
+
"env_var": "OPENAI_API_KEY",
|
|
84
|
+
},
|
|
85
|
+
"anthropic": {
|
|
86
|
+
"name": "Anthropic (Claude)",
|
|
87
|
+
"models": ["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"],
|
|
88
|
+
"needs_key": True,
|
|
89
|
+
"env_var": "ANTHROPIC_API_KEY",
|
|
90
|
+
},
|
|
91
|
+
"openrouter": {
|
|
92
|
+
"name": "OpenRouter (all models)",
|
|
93
|
+
"models": ["openai/gpt-4", "anthropic/claude-3-opus", "google/gemini-pro"],
|
|
94
|
+
"needs_key": True,
|
|
95
|
+
"env_var": "OPENROUTER_API_KEY",
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
CONFIG_DIR = os.path.expanduser("~/.config/codrninja")
|
|
100
|
+
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
|
|
101
|
+
|
|
71
102
|
|
|
72
103
|
class SlashCompleter(Completer):
|
|
73
104
|
"""Custom completer for slash commands."""
|
|
@@ -105,6 +136,10 @@ class TUI:
|
|
|
105
136
|
print("Run: pip3 install prompt_toolkit")
|
|
106
137
|
return
|
|
107
138
|
|
|
139
|
+
# Check for config -- auto-onboarding if missing
|
|
140
|
+
if not self._has_config():
|
|
141
|
+
self._onboarding()
|
|
142
|
+
|
|
108
143
|
self._show_welcome()
|
|
109
144
|
|
|
110
145
|
# Create prompt session with completion
|
|
@@ -144,6 +179,140 @@ class TUI:
|
|
|
144
179
|
except EOFError:
|
|
145
180
|
break
|
|
146
181
|
|
|
182
|
+
def _has_config(self) -> bool:
|
|
183
|
+
"""Check if configuration exists."""
|
|
184
|
+
return os.path.exists(CONFIG_FILE)
|
|
185
|
+
|
|
186
|
+
def _load_config(self) -> dict:
|
|
187
|
+
"""Load configuration from file."""
|
|
188
|
+
if not self._has_config():
|
|
189
|
+
return {}
|
|
190
|
+
with open(CONFIG_FILE, 'r') as f:
|
|
191
|
+
return json.load(f)
|
|
192
|
+
|
|
193
|
+
def _save_config(self, config: dict):
|
|
194
|
+
"""Save configuration to file."""
|
|
195
|
+
os.makedirs(CONFIG_DIR, exist_ok=True)
|
|
196
|
+
with open(CONFIG_FILE, 'w') as f:
|
|
197
|
+
json.dump(config, f, indent=2)
|
|
198
|
+
|
|
199
|
+
def _onboarding(self):
|
|
200
|
+
"""Interactive onboarding for first-time users."""
|
|
201
|
+
print("\n" + "=" * 80)
|
|
202
|
+
print(" Welcome to codrninja!")
|
|
203
|
+
print(" Let's set up your AI provider.")
|
|
204
|
+
print("=" * 80 + "\n")
|
|
205
|
+
|
|
206
|
+
# Step 1: Provider selection
|
|
207
|
+
print("Step 1: Choose your AI provider\n")
|
|
208
|
+
providers_list = list(PROVIDERS.items())
|
|
209
|
+
for i, (key, info) in enumerate(providers_list, 1):
|
|
210
|
+
print(f" {i}. {info['name']}")
|
|
211
|
+
print()
|
|
212
|
+
|
|
213
|
+
while True:
|
|
214
|
+
choice = input("Enter choice [1-4]: ").strip()
|
|
215
|
+
try:
|
|
216
|
+
idx = int(choice) - 1
|
|
217
|
+
if 0 <= idx < len(providers_list):
|
|
218
|
+
provider_key, provider_info = providers_list[idx]
|
|
219
|
+
break
|
|
220
|
+
else:
|
|
221
|
+
print("Invalid choice. Please enter 1-4.")
|
|
222
|
+
except ValueError:
|
|
223
|
+
print("Please enter a number.")
|
|
224
|
+
|
|
225
|
+
print(f"\n Selected: {provider_info['name']}\n")
|
|
226
|
+
|
|
227
|
+
# Step 2: API key (if needed)
|
|
228
|
+
api_key = None
|
|
229
|
+
if provider_info.get('needs_key'):
|
|
230
|
+
env_var = provider_info['env_var']
|
|
231
|
+
existing = os.environ.get(env_var, "")
|
|
232
|
+
|
|
233
|
+
if existing:
|
|
234
|
+
print(f" Found {env_var} in environment.")
|
|
235
|
+
use_existing = input(" Use existing key? [Y/n]: ").strip().lower()
|
|
236
|
+
if use_existing in ['', 'y', 'yes']:
|
|
237
|
+
api_key = existing
|
|
238
|
+
|
|
239
|
+
if not api_key:
|
|
240
|
+
print(f"\n Please enter your {provider_info['name'].split('(')[0].strip()} API key:")
|
|
241
|
+
api_key = input(" > ").strip()
|
|
242
|
+
|
|
243
|
+
if api_key:
|
|
244
|
+
# Save to shell config
|
|
245
|
+
shell_config = self._get_shell_config()
|
|
246
|
+
if shell_config:
|
|
247
|
+
with open(shell_config, "a") as f:
|
|
248
|
+
f.write(f"\n# codrninja config\n")
|
|
249
|
+
f.write(f"export {env_var}=\"{api_key}\"\n")
|
|
250
|
+
print(f"\n Saved to {shell_config}")
|
|
251
|
+
|
|
252
|
+
# Step 3: Model selection
|
|
253
|
+
print(f"\nStep 2: Choose a model for {provider_info['name']}\n")
|
|
254
|
+
models = provider_info['models']
|
|
255
|
+
for i, model in enumerate(models, 1):
|
|
256
|
+
print(f" {i}. {model}")
|
|
257
|
+
print()
|
|
258
|
+
|
|
259
|
+
while True:
|
|
260
|
+
choice = input("Enter choice [1-{}]: ".format(len(models))).strip()
|
|
261
|
+
try:
|
|
262
|
+
idx = int(choice) - 1
|
|
263
|
+
if 0 <= idx < len(models):
|
|
264
|
+
model = models[idx]
|
|
265
|
+
break
|
|
266
|
+
else:
|
|
267
|
+
print(f"Invalid choice. Please enter 1-{len(models)}.")
|
|
268
|
+
except ValueError:
|
|
269
|
+
print("Please enter a number.")
|
|
270
|
+
|
|
271
|
+
print(f"\n Selected model: {model}\n")
|
|
272
|
+
|
|
273
|
+
# Step 4: Ollama URL (if Ollama)
|
|
274
|
+
ollama_url = None
|
|
275
|
+
if provider_key == "ollama":
|
|
276
|
+
default_url = provider_info['default_url']
|
|
277
|
+
url = input(f"Ollama URL [{default_url}]: ").strip()
|
|
278
|
+
ollama_url = url if url else default_url
|
|
279
|
+
print(f" Using: {ollama_url}\n")
|
|
280
|
+
|
|
281
|
+
# Save config
|
|
282
|
+
config = {
|
|
283
|
+
"provider": provider_key,
|
|
284
|
+
"model": model,
|
|
285
|
+
}
|
|
286
|
+
if ollama_url:
|
|
287
|
+
config["ollama_url"] = ollama_url
|
|
288
|
+
|
|
289
|
+
self._save_config(config)
|
|
290
|
+
|
|
291
|
+
# Update AI config
|
|
292
|
+
self.ai.config.default_provider = provider_key
|
|
293
|
+
self.ai.config.default_model = model
|
|
294
|
+
if ollama_url:
|
|
295
|
+
self.ai.config.ollama_url = ollama_url
|
|
296
|
+
|
|
297
|
+
print("=" * 80)
|
|
298
|
+
print(" Setup complete!")
|
|
299
|
+
print(f" Provider: {provider_info['name']}")
|
|
300
|
+
print(f" Model: {model}")
|
|
301
|
+
print("=" * 80 + "\n")
|
|
302
|
+
print(" You can change this later with /model")
|
|
303
|
+
print(" Press Enter to continue...\n")
|
|
304
|
+
input()
|
|
305
|
+
|
|
306
|
+
def _get_shell_config(self) -> Optional[str]:
|
|
307
|
+
"""Get the user's shell config file path."""
|
|
308
|
+
if os.path.exists(os.path.expanduser("~/.zshrc")):
|
|
309
|
+
return os.path.expanduser("~/.zshrc")
|
|
310
|
+
elif os.path.exists(os.path.expanduser("~/.bashrc")):
|
|
311
|
+
return os.path.expanduser("~/.bashrc")
|
|
312
|
+
elif os.path.exists(os.path.expanduser("~/.bash_profile")):
|
|
313
|
+
return os.path.expanduser("~/.bash_profile")
|
|
314
|
+
return None
|
|
315
|
+
|
|
147
316
|
def _show_welcome(self):
|
|
148
317
|
"""Show welcome screen."""
|
|
149
318
|
if self.console:
|
|
@@ -199,23 +368,13 @@ class TUI:
|
|
|
199
368
|
result = self.tools.list_files(path=".", depth=2)
|
|
200
369
|
if result.success:
|
|
201
370
|
if self.console:
|
|
202
|
-
self.console.print(Panel(result.output, title="
|
|
371
|
+
self.console.print(Panel(result.output, title="Files", border_style="blue"))
|
|
203
372
|
else:
|
|
204
373
|
print(f"\n{result.output}")
|
|
205
374
|
else:
|
|
206
375
|
print(f"Error: {result.error}")
|
|
207
376
|
elif cmd == '/model':
|
|
208
|
-
|
|
209
|
-
table = Table(title="AI Configuration", box=box.ROUNDED)
|
|
210
|
-
table.add_column("Setting", style="cyan")
|
|
211
|
-
table.add_column("Value", style="white")
|
|
212
|
-
table.add_row("Provider", self.ai.config.default_provider)
|
|
213
|
-
table.add_row("Model", self.ai.config.default_model)
|
|
214
|
-
table.add_row("Ollama URL", self.ai.config.ollama_url)
|
|
215
|
-
self.console.print(table)
|
|
216
|
-
else:
|
|
217
|
-
print(f"\nProvider: {self.ai.config.default_provider}")
|
|
218
|
-
print(f"Model: {self.ai.config.default_model}")
|
|
377
|
+
self._show_model_config()
|
|
219
378
|
elif cmd == '/session':
|
|
220
379
|
history = self.ai.get_history(self.session_name)
|
|
221
380
|
if history:
|
|
@@ -233,7 +392,7 @@ class TUI:
|
|
|
233
392
|
result = self.tools.execute_command(cmd_str)
|
|
234
393
|
if result.success:
|
|
235
394
|
if self.console:
|
|
236
|
-
self.console.print(Panel(result.output, title=f"
|
|
395
|
+
self.console.print(Panel(result.output, title=f"{cmd_str}", border_style="green"))
|
|
237
396
|
else:
|
|
238
397
|
print(f"\n{result.output}")
|
|
239
398
|
else:
|
|
@@ -247,7 +406,7 @@ class TUI:
|
|
|
247
406
|
lang_map = {'.py': 'python', '.js': 'javascript', '.ts': 'typescript', '.jsx': 'javascript', '.tsx': 'typescript', '.json': 'json', '.css': 'css', '.html': 'html', '.md': 'markdown'}
|
|
248
407
|
lang = lang_map.get(ext, "text")
|
|
249
408
|
syntax = Syntax(result.output, lang, theme="monokai", line_numbers=True)
|
|
250
|
-
self.console.print(Panel(syntax, title=f"
|
|
409
|
+
self.console.print(Panel(syntax, title=f"{parts[1]}", border_style="blue"))
|
|
251
410
|
else:
|
|
252
411
|
print(f"\n{result.output}")
|
|
253
412
|
else:
|
|
@@ -273,7 +432,7 @@ class TUI:
|
|
|
273
432
|
result = self.tools.execute_command(target)
|
|
274
433
|
if result.success:
|
|
275
434
|
if self.console:
|
|
276
|
-
self.console.print(Panel(result.output, title=f"
|
|
435
|
+
self.console.print(Panel(result.output, title=f"{target}", border_style="green"))
|
|
277
436
|
else:
|
|
278
437
|
print(f"\n{result.output}")
|
|
279
438
|
else:
|
|
@@ -283,7 +442,7 @@ class TUI:
|
|
|
283
442
|
self.tools.execute_command('git add -A')
|
|
284
443
|
result = self.tools.execute_command(f'git commit -m "{msg}"')
|
|
285
444
|
if result.success:
|
|
286
|
-
print(f"
|
|
445
|
+
print(f"[OK] Committed: {msg}")
|
|
287
446
|
else:
|
|
288
447
|
print(f"Error: {result.error}")
|
|
289
448
|
else:
|
|
@@ -291,13 +450,35 @@ class TUI:
|
|
|
291
450
|
|
|
292
451
|
return True
|
|
293
452
|
|
|
453
|
+
def _show_model_config(self):
|
|
454
|
+
"""Show and allow changing model configuration."""
|
|
455
|
+
config = self._load_config()
|
|
456
|
+
current_provider = config.get('provider', self.ai.config.default_provider)
|
|
457
|
+
current_model = config.get('model', self.ai.config.default_model)
|
|
458
|
+
|
|
459
|
+
print("\n" + "=" * 60)
|
|
460
|
+
print(" Current Configuration")
|
|
461
|
+
print("=" * 60)
|
|
462
|
+
print(f" Provider: {current_provider}")
|
|
463
|
+
print(f" Model: {current_model}")
|
|
464
|
+
if 'ollama_url' in config:
|
|
465
|
+
print(f" Ollama URL: {config['ollama_url']}")
|
|
466
|
+
print("=" * 60)
|
|
467
|
+
|
|
468
|
+
change = input("\nChange configuration? [y/N]: ").strip().lower()
|
|
469
|
+
if change not in ['y', 'yes']:
|
|
470
|
+
return
|
|
471
|
+
|
|
472
|
+
# Re-run onboarding
|
|
473
|
+
self._onboarding()
|
|
474
|
+
|
|
294
475
|
def _display_response(self, response: str):
|
|
295
476
|
"""Display AI response with Rich."""
|
|
296
477
|
parts = response.split('```')
|
|
297
478
|
for i, part in enumerate(parts):
|
|
298
479
|
if i % 2 == 0:
|
|
299
480
|
if part.strip():
|
|
300
|
-
self.console.print(f"\n[bold blue]
|
|
481
|
+
self.console.print(f"\n[bold blue]AI:[/bold blue]")
|
|
301
482
|
self.console.print(Markdown(part.strip()))
|
|
302
483
|
else:
|
|
303
484
|
lines = part.split('\n')
|
|
@@ -314,7 +495,7 @@ class TUI:
|
|
|
314
495
|
table.add_column("Status", style="green")
|
|
315
496
|
table.add_column("Result", style="white")
|
|
316
497
|
for tool in tools_used:
|
|
317
|
-
status = "
|
|
498
|
+
status = "OK" if tool['success'] else "FAIL"
|
|
318
499
|
result = tool['output'][:60] + "..." if len(tool['output']) > 60 else tool['output']
|
|
319
500
|
table.add_row(tool['tool'], status, result)
|
|
320
501
|
self.console.print(table)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|