-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.py
More file actions
113 lines (91 loc) · 2.98 KB
/
app.py
File metadata and controls
113 lines (91 loc) · 2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""
SPARK — Supply-chain Risk Analytics & Prioritization Kit
Entry point for the terminal TUI.
"""
import argparse
import asyncio
import sys
import logging
from dotenv import load_dotenv
from core.cli_handler import UnifiedVendorTool
from core.ui import print_banner, print_error, print_goodbye, console
from core.theme import APP_VERSION, APP_NAME, APP_FULL_NAME
# ─── Logging ────────────────────────────────────────────────────────────────
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("app.log", encoding='utf-8'),
]
)
# Ensure stdout handles emojis on Windows
if sys.platform == "win32":
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
logger = logging.getLogger(__name__)
def build_parser() -> argparse.ArgumentParser:
"""Build the CLI argument parser."""
parser = argparse.ArgumentParser(
prog="spark",
description=f"{APP_FULL_NAME}",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"Examples:\n"
" spark \"Acme Corp\" \"acme.com\"\n"
" spark \"Acme Corp\" \"acme.com\" --prompt \"Focus on SOC2 compliance\"\n"
" spark --version\n"
),
)
parser.add_argument(
"company",
nargs="?",
help="Name of the vendor/company to analyze",
)
parser.add_argument(
"domain",
nargs="?",
default=None,
help="Domain of the vendor (e.g. acme.com)",
)
parser.add_argument(
"--version", "-v",
action="version",
version=f"{APP_NAME} v{APP_VERSION}",
)
return parser
async def main() -> None:
"""Main async entry point."""
load_dotenv()
parser = build_parser()
args = parser.parse_args()
if not args.company:
# Show help with styled console instead of plain argparse output
console.print()
console.print(f" [accent]{APP_NAME}[/accent] [dim]— {APP_FULL_NAME}[/dim]")
console.print()
parser.print_help()
console.print()
return
company = args.company
domain = args.domain
tool = UnifiedVendorTool(company, domain)
try:
# Show banner
backend = tool.analyzer.get_backend_info()
print_banner(company, domain, backend)
# Initialize context
await tool.initialize()
# Run interactive REPL
await tool.run_chat_loop()
except KeyboardInterrupt:
pass
except Exception as e:
logger.exception(f"Fatal error: {e}")
print_error(f"Fatal error: {e}")
finally:
print_goodbye()
def main_sync() -> None:
"""Synchronous wrapper for the entry point (used by pyproject.toml)."""
asyncio.run(main())
if __name__ == "__main__":
main_sync()