diffsage 1.2.0__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.
- diffsage/__init__.py +0 -0
- diffsage/__main__.py +4 -0
- diffsage/cli.py +38 -0
- diffsage/commands/__init__.py +0 -0
- diffsage/commands/ask.py +62 -0
- diffsage/commands/auth.py +153 -0
- diffsage/commands/commit.py +125 -0
- diffsage/commands/config.py +216 -0
- diffsage/commands/doctor.py +18 -0
- diffsage/config/__init__.py +0 -0
- diffsage/config/defaults.py +20 -0
- diffsage/config/file_loader.py +21 -0
- diffsage/config/loader.py +8 -0
- diffsage/config/paths.py +61 -0
- diffsage/config/resolver.py +118 -0
- diffsage/config/schema.py +53 -0
- diffsage/config/scope.py +8 -0
- diffsage/config/settings.py +9 -0
- diffsage/exceptions/__init__.py +34 -0
- diffsage/exceptions/base.py +32 -0
- diffsage/exceptions/credentials.py +8 -0
- diffsage/exceptions/git.py +9 -0
- diffsage/exceptions/provider.py +17 -0
- diffsage/git/__init__.py +0 -0
- diffsage/git/client.py +155 -0
- diffsage/logging/__init__.py +0 -0
- diffsage/logging/formatter.py +0 -0
- diffsage/logging/logger.py +41 -0
- diffsage/models/__init__.py +0 -0
- diffsage/models/commit_message.py +9 -0
- diffsage/models/config.py +16 -0
- diffsage/models/credentials.py +8 -0
- diffsage/models/doctor.py +13 -0
- diffsage/models/git.py +32 -0
- diffsage/models/provider.py +20 -0
- diffsage/parsers/commit_message_parser.py +43 -0
- diffsage/prompts/__init__.py +0 -0
- diffsage/providers/__init__.py +0 -0
- diffsage/providers/base.py +8 -0
- diffsage/providers/factory.py +14 -0
- diffsage/providers/gemini_provider.py +77 -0
- diffsage/services/__init__.py +0 -0
- diffsage/services/ai_service.py +63 -0
- diffsage/services/commit_service.py +38 -0
- diffsage/services/config_service.py +109 -0
- diffsage/services/credentials_service.py +61 -0
- diffsage/services/doctor_service.py +47 -0
- diffsage/services/editor_service.py +75 -0
- diffsage/services/git_service.py +20 -0
- diffsage/services/prompt_service.py +60 -0
- diffsage/storage/__init__.py +0 -0
- diffsage/storage/base.py +0 -0
- diffsage/storage/config_repository.py +85 -0
- diffsage/storage/credentials_repository.py +121 -0
- diffsage/ui/__init__.py +0 -0
- diffsage/ui/ask_view.py +10 -0
- diffsage/ui/auth_view.py +51 -0
- diffsage/ui/base.py +15 -0
- diffsage/ui/commit_view.py +100 -0
- diffsage/ui/config_view.py +58 -0
- diffsage/ui/doctor_view.py +39 -0
- diffsage-1.2.0.dist-info/METADATA +471 -0
- diffsage-1.2.0.dist-info/RECORD +67 -0
- diffsage-1.2.0.dist-info/WHEEL +5 -0
- diffsage-1.2.0.dist-info/entry_points.txt +2 -0
- diffsage-1.2.0.dist-info/licenses/LICENSE +21 -0
- diffsage-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from tomlkit import TOMLDocument, document, parse, table
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConfigRepository:
|
|
7
|
+
"""Persists DiffSage configuration."""
|
|
8
|
+
|
|
9
|
+
_PATH_MAP = {
|
|
10
|
+
"provider": ("ai", "provider"),
|
|
11
|
+
"model": ("ai", "model"),
|
|
12
|
+
"timeout": ("network", "timeout"),
|
|
13
|
+
"max_retries": ("network", "max_retries"),
|
|
14
|
+
"log_level": ("logging", "level"),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def __init__(self, path: Path) -> None:
|
|
18
|
+
self._path = path
|
|
19
|
+
|
|
20
|
+
def _load(self) -> TOMLDocument:
|
|
21
|
+
if not self._path.exists():
|
|
22
|
+
return document()
|
|
23
|
+
|
|
24
|
+
content = self._path.read_text()
|
|
25
|
+
doc = parse(content)
|
|
26
|
+
|
|
27
|
+
return doc
|
|
28
|
+
|
|
29
|
+
def _save(self, doc: TOMLDocument) -> None:
|
|
30
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
|
|
32
|
+
content = doc.as_string()
|
|
33
|
+
self._path.write_text(content)
|
|
34
|
+
|
|
35
|
+
def list(self) -> dict[str, str | int]:
|
|
36
|
+
doc = self._load()
|
|
37
|
+
|
|
38
|
+
config = {}
|
|
39
|
+
|
|
40
|
+
for key, (section, option) in self._PATH_MAP.items():
|
|
41
|
+
if section in doc and option in doc[section]:
|
|
42
|
+
config[key] = doc[section][option]
|
|
43
|
+
|
|
44
|
+
return config
|
|
45
|
+
|
|
46
|
+
def get(self, key: str) -> str | int | None:
|
|
47
|
+
doc = self._load()
|
|
48
|
+
|
|
49
|
+
section, option = self._PATH_MAP[key]
|
|
50
|
+
|
|
51
|
+
if section not in doc:
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
if option not in doc[section]:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
return doc[section][option]
|
|
58
|
+
|
|
59
|
+
def set(self, key: str, value: str | int) -> None:
|
|
60
|
+
doc = self._load()
|
|
61
|
+
section, option = self._PATH_MAP[key]
|
|
62
|
+
|
|
63
|
+
if section not in doc:
|
|
64
|
+
doc[section] = table()
|
|
65
|
+
|
|
66
|
+
doc[section][option] = value
|
|
67
|
+
|
|
68
|
+
self._save(doc)
|
|
69
|
+
|
|
70
|
+
def unset(self, key: str) -> None:
|
|
71
|
+
doc = self._load()
|
|
72
|
+
section, option = self._PATH_MAP[key]
|
|
73
|
+
|
|
74
|
+
if section not in doc:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
if option not in doc[section]:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
del doc[section][option]
|
|
81
|
+
|
|
82
|
+
if not doc[section]:
|
|
83
|
+
del doc[section]
|
|
84
|
+
|
|
85
|
+
self._save(doc)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from tomlkit import document, parse, table
|
|
4
|
+
|
|
5
|
+
from diffsage.models.credentials import Credential
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CredentialsRepository:
|
|
9
|
+
"""Persists DiffSage API keys configuration."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, path: Path) -> None:
|
|
12
|
+
self._path = path
|
|
13
|
+
|
|
14
|
+
def save(self, credential: Credential) -> None:
|
|
15
|
+
if self._path.exists():
|
|
16
|
+
doc = parse(self._path.read_text())
|
|
17
|
+
else:
|
|
18
|
+
doc = document()
|
|
19
|
+
|
|
20
|
+
credentials = doc.get("credentials")
|
|
21
|
+
|
|
22
|
+
if "credentials" not in doc:
|
|
23
|
+
credentials = table()
|
|
24
|
+
doc["credentials"] = credentials
|
|
25
|
+
|
|
26
|
+
provider = credentials.get(credential.provider)
|
|
27
|
+
|
|
28
|
+
if provider is None:
|
|
29
|
+
provider = table()
|
|
30
|
+
credentials[credential.provider] = provider
|
|
31
|
+
|
|
32
|
+
profile = provider.get(credential.name)
|
|
33
|
+
|
|
34
|
+
if profile is None:
|
|
35
|
+
profile = table()
|
|
36
|
+
provider[credential.name] = profile
|
|
37
|
+
|
|
38
|
+
profile["api_key"] = credential.api_key
|
|
39
|
+
|
|
40
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
self._path.write_text(doc.as_string())
|
|
42
|
+
|
|
43
|
+
def get(self, provider: str, name: str = "default") -> Credential | None:
|
|
44
|
+
if not self._path.exists():
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
doc = parse(self._path.read_text())
|
|
48
|
+
credentials = doc.get("credentials")
|
|
49
|
+
|
|
50
|
+
if credentials is None:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
provider_table = credentials.get(provider)
|
|
54
|
+
if provider_table is None:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
profile = provider_table.get(name)
|
|
58
|
+
if profile is None:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
api_key = profile.get("api_key")
|
|
62
|
+
if api_key is None:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
return Credential(
|
|
66
|
+
provider=provider,
|
|
67
|
+
name=name,
|
|
68
|
+
api_key=api_key,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def list(self) -> list[Credential]:
|
|
72
|
+
if not self._path.exists():
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
doc = parse(self._path.read_text())
|
|
76
|
+
credentials = doc.get("credentials")
|
|
77
|
+
|
|
78
|
+
if credentials is None:
|
|
79
|
+
return []
|
|
80
|
+
|
|
81
|
+
result: list[Credential] = []
|
|
82
|
+
|
|
83
|
+
for provider_name, provider in credentials.items():
|
|
84
|
+
for profile_name, profile in provider.items():
|
|
85
|
+
api_key = profile.get("api_key")
|
|
86
|
+
|
|
87
|
+
if api_key is None:
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
result.append(
|
|
91
|
+
Credential(provider=provider_name, name=profile_name, api_key=api_key)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
def delete(self, provider: str, name: str = "default") -> bool:
|
|
97
|
+
if not self._path.exists():
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
doc = parse(self._path.read_text())
|
|
101
|
+
credentials = doc.get("credentials")
|
|
102
|
+
|
|
103
|
+
if credentials is None:
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
provider_table = credentials.get(provider)
|
|
107
|
+
|
|
108
|
+
if provider_table is None:
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
if provider_table.get(name) is None:
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
del provider_table[name]
|
|
115
|
+
|
|
116
|
+
if not provider_table:
|
|
117
|
+
del credentials[provider]
|
|
118
|
+
|
|
119
|
+
self._path.write_text(doc.as_string())
|
|
120
|
+
|
|
121
|
+
return True
|
diffsage/ui/__init__.py
ADDED
|
File without changes
|
diffsage/ui/ask_view.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from diffsage.models.provider import ProviderResponse
|
|
2
|
+
from diffsage.ui.base import BaseView
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AskView(BaseView):
|
|
6
|
+
"""Handles terminal rendering for the ask command."""
|
|
7
|
+
|
|
8
|
+
def show_response(self, response: ProviderResponse) -> None:
|
|
9
|
+
self._console.print(f"[green]{response.content}[/green]")
|
|
10
|
+
self._console.print()
|
diffsage/ui/auth_view.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from rich.panel import Panel
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
|
|
4
|
+
from diffsage.models.credentials import Credential
|
|
5
|
+
from diffsage.ui.base import BaseView
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuthView(BaseView):
|
|
9
|
+
"""Handles terminal rendering for the auth command."""
|
|
10
|
+
|
|
11
|
+
def show_success(self, message: str) -> None:
|
|
12
|
+
self._console.print(f"[bold green]✓ {message}[bold green]")
|
|
13
|
+
|
|
14
|
+
def show_credential(self, credential: Credential) -> None:
|
|
15
|
+
table = Table(show_header=False, box=False, expand=False)
|
|
16
|
+
|
|
17
|
+
table.add_row("Provider", credential.provider)
|
|
18
|
+
table.add_row("Profile", credential.name)
|
|
19
|
+
table.add_row("API Key", self._mask_api_key(credential.api_key))
|
|
20
|
+
|
|
21
|
+
self._console.print(
|
|
22
|
+
Panel(
|
|
23
|
+
table,
|
|
24
|
+
title="[bold cyan]DiffSage Credential[/bold cyan]",
|
|
25
|
+
expand=False,
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def show_credentials(self, credentials: list[Credential]) -> None:
|
|
30
|
+
table = Table(show_header=False, box=False, expand=False)
|
|
31
|
+
|
|
32
|
+
for credential in credentials:
|
|
33
|
+
table.add_row(
|
|
34
|
+
credential.provider,
|
|
35
|
+
credential.name,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
self._console.print(
|
|
39
|
+
Panel(
|
|
40
|
+
table,
|
|
41
|
+
title="[bold cyan]DiffSage Credential[/bold cyan]",
|
|
42
|
+
expand=False,
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def _mask_api_key(api_key: str) -> str:
|
|
48
|
+
if len(api_key) <= 8:
|
|
49
|
+
return "••••••••"
|
|
50
|
+
|
|
51
|
+
return f"{api_key[:4]}••••••••{api_key[-4:]}"
|
diffsage/ui/base.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BaseView:
|
|
5
|
+
def __init__(self) -> None:
|
|
6
|
+
self._console = Console()
|
|
7
|
+
|
|
8
|
+
def show_error(self, message: str) -> None:
|
|
9
|
+
self._console.print(f"[red]✗ {message}[/red]")
|
|
10
|
+
|
|
11
|
+
def show_warning(self, message: str) -> None:
|
|
12
|
+
self._console.print(f"[yellow]! {message}[/yellow]")
|
|
13
|
+
|
|
14
|
+
def show_info(self, message: str) -> None:
|
|
15
|
+
self._console.print(message)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
|
|
3
|
+
from rich.console import Group
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.rule import Rule
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
|
|
9
|
+
from diffsage.models.commit_message import CommitMessage
|
|
10
|
+
from diffsage.ui.base import BaseView
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CommitView(BaseView):
|
|
14
|
+
"""Handles terminal rendering for the commit command."""
|
|
15
|
+
|
|
16
|
+
@contextmanager
|
|
17
|
+
def generating(self):
|
|
18
|
+
with self._console.status("Generating commit message..."):
|
|
19
|
+
yield
|
|
20
|
+
|
|
21
|
+
def show_generated(self) -> None:
|
|
22
|
+
self._console.print("[green]✓ Commit message generated successfully.[/green]")
|
|
23
|
+
|
|
24
|
+
def show_not_git_repository(self):
|
|
25
|
+
self._console.print("[red]Not inside a Git repository[/red]")
|
|
26
|
+
|
|
27
|
+
def show_no_staged_changes(self):
|
|
28
|
+
self._console.print("[yellow]No staged changes found[/yellow]")
|
|
29
|
+
|
|
30
|
+
def show_commit(self, message: CommitMessage) -> None:
|
|
31
|
+
grid = Table.grid(expand=True)
|
|
32
|
+
grid.add_column(style="cyan", width=10)
|
|
33
|
+
grid.add_column()
|
|
34
|
+
|
|
35
|
+
if message.type:
|
|
36
|
+
grid.add_row("Type", message.type)
|
|
37
|
+
|
|
38
|
+
if message.scope:
|
|
39
|
+
grid.add_row("Scope", message.scope)
|
|
40
|
+
|
|
41
|
+
grid.add_row("Subject", message.subject)
|
|
42
|
+
|
|
43
|
+
body = Text()
|
|
44
|
+
|
|
45
|
+
if message.body:
|
|
46
|
+
for i, line in enumerate(message.body):
|
|
47
|
+
line = line.strip()
|
|
48
|
+
if not line:
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if line.startswith("- "):
|
|
52
|
+
line = line.removeprefix("- ")
|
|
53
|
+
|
|
54
|
+
body.append(f"• {line}")
|
|
55
|
+
if i < len(message.body) - 1:
|
|
56
|
+
body.append("\n")
|
|
57
|
+
|
|
58
|
+
content = Group(
|
|
59
|
+
grid,
|
|
60
|
+
Rule(style="cyan"),
|
|
61
|
+
body,
|
|
62
|
+
)
|
|
63
|
+
panel = Panel(
|
|
64
|
+
content,
|
|
65
|
+
title="[bold cyan]Commit Message[/bold cyan]",
|
|
66
|
+
expand=False,
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
panel = Panel(
|
|
70
|
+
grid,
|
|
71
|
+
title="[bold cyan]Suggested Commit Message[/bold cyan]",
|
|
72
|
+
expand=False,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self._console.print()
|
|
76
|
+
self._console.print(panel)
|
|
77
|
+
|
|
78
|
+
def prompt_action(self) -> str:
|
|
79
|
+
self._console.print()
|
|
80
|
+
|
|
81
|
+
return self._console.input(
|
|
82
|
+
"[bold cyan][Y][/bold cyan] Commit (default) "
|
|
83
|
+
"[bold cyan][E][/bold cyan] Edit "
|
|
84
|
+
"[bold cyan][R][/bold cyan] Regenerate "
|
|
85
|
+
"[bold cyan][N][/bold cyan] Cancel: "
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def show_success(self, message: str) -> None:
|
|
89
|
+
self._console.print()
|
|
90
|
+
self._console.print("[bold green]✓ Commit created successfully.[/bold green]")
|
|
91
|
+
self._console.print()
|
|
92
|
+
self._console.print("[dim]Commit:[/dim]")
|
|
93
|
+
self._console.print(message)
|
|
94
|
+
self._console.print()
|
|
95
|
+
|
|
96
|
+
def show_cancelled(self) -> None:
|
|
97
|
+
self._console.print("[yellow]Cancelled.[/yellow]")
|
|
98
|
+
|
|
99
|
+
def show_invalid_option(self) -> None:
|
|
100
|
+
self._console.print("[red]Invalid option. Please choose Y, E, R or N.[/red]")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from rich.panel import Panel
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
|
|
6
|
+
from diffsage.models.config import ConfigReport, ConfigValueReport
|
|
7
|
+
from diffsage.ui.base import BaseView
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigView(BaseView):
|
|
11
|
+
"""Handles terminal rendering for the config command."""
|
|
12
|
+
|
|
13
|
+
def _display_value(self, value: str | int | None) -> str:
|
|
14
|
+
if value is None:
|
|
15
|
+
return "None"
|
|
16
|
+
|
|
17
|
+
return str(value)
|
|
18
|
+
|
|
19
|
+
def show_configuration(self, report: ConfigReport) -> None:
|
|
20
|
+
table = Table(show_header=False, box=False, expand=False)
|
|
21
|
+
|
|
22
|
+
table.add_row("Provider", self._display_value(report.provider))
|
|
23
|
+
table.add_row("Model", self._display_value(report.model))
|
|
24
|
+
table.add_row("Timeout", self._display_value(report.timeout))
|
|
25
|
+
table.add_row("Max Retries", self._display_value(report.max_retries))
|
|
26
|
+
table.add_row("Log Level", self._display_value(report.log_level))
|
|
27
|
+
|
|
28
|
+
self._console.print(
|
|
29
|
+
Panel(
|
|
30
|
+
table,
|
|
31
|
+
title="[bold cyan]DiffSage Configuration[/bold cyan]",
|
|
32
|
+
expand=False,
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def show_value(self, report: ConfigValueReport) -> None:
|
|
37
|
+
self._console.print(
|
|
38
|
+
Panel(
|
|
39
|
+
self._display_value(report.value),
|
|
40
|
+
title=f"[bold cyan]{report.key.replace('_', ' ').title()}[/bold cyan]",
|
|
41
|
+
expand=False,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def show_success(self, message: str) -> None:
|
|
46
|
+
self._console.print()
|
|
47
|
+
self._console.print(f"[bold green]✓ {message}[bold green]")
|
|
48
|
+
self._console.print()
|
|
49
|
+
|
|
50
|
+
def show_path(self, path: Path) -> None:
|
|
51
|
+
display = str(path).replace(str(Path.home()), "~")
|
|
52
|
+
|
|
53
|
+
self._console.print("[dim]Location:[/]")
|
|
54
|
+
self._console.print(
|
|
55
|
+
f"[cyan]{display}[/]",
|
|
56
|
+
no_wrap=True,
|
|
57
|
+
)
|
|
58
|
+
self._console.print()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from diffsage.config.paths import get_log_file_path
|
|
4
|
+
from diffsage.models.doctor import DoctorReport
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DoctorView:
|
|
8
|
+
def _status_icon(self, status: bool) -> str:
|
|
9
|
+
"""Return a status icon for terminal output."""
|
|
10
|
+
|
|
11
|
+
return "✓" if status else "✗"
|
|
12
|
+
|
|
13
|
+
def show_report(self, report: DoctorReport) -> None:
|
|
14
|
+
typer.echo()
|
|
15
|
+
typer.echo("DiffSage Doctor")
|
|
16
|
+
typer.echo()
|
|
17
|
+
|
|
18
|
+
typer.echo(f"{self._status_icon(True)} Python Version : {report.python_version}")
|
|
19
|
+
|
|
20
|
+
git_version = report.git_version or "Not Found"
|
|
21
|
+
typer.echo(f"{self._status_icon(report.git_installed)} Git Installed : {git_version}")
|
|
22
|
+
|
|
23
|
+
virtual_env_status = "Active" if report.virtual_environment else "Inactive"
|
|
24
|
+
typer.echo(
|
|
25
|
+
f"{self._status_icon(report.virtual_environment)} Virtual Env. : {virtual_env_status}"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
typer.echo(
|
|
29
|
+
f"{self._status_icon(report.configuration_loaded)} "
|
|
30
|
+
f"Configuration : {report.configuration_loaded}"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
typer.echo()
|
|
34
|
+
|
|
35
|
+
typer.echo(f"Provider : {report.provider}")
|
|
36
|
+
typer.echo(f"Timeout : {report.timeout}")
|
|
37
|
+
typer.echo(f"Max Retries : {report.max_retries}")
|
|
38
|
+
typer.echo(f"Log Level : {report.log_level}")
|
|
39
|
+
typer.echo(f"Diffsage Log : {get_log_file_path()}")
|