scc-cli 1.4.1__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.

Potentially problematic release.


This version of scc-cli might be problematic. Click here for more details.

Files changed (113) hide show
  1. scc_cli/__init__.py +15 -0
  2. scc_cli/audit/__init__.py +37 -0
  3. scc_cli/audit/parser.py +191 -0
  4. scc_cli/audit/reader.py +180 -0
  5. scc_cli/auth.py +145 -0
  6. scc_cli/claude_adapter.py +485 -0
  7. scc_cli/cli.py +259 -0
  8. scc_cli/cli_admin.py +706 -0
  9. scc_cli/cli_audit.py +245 -0
  10. scc_cli/cli_common.py +166 -0
  11. scc_cli/cli_config.py +527 -0
  12. scc_cli/cli_exceptions.py +705 -0
  13. scc_cli/cli_helpers.py +244 -0
  14. scc_cli/cli_init.py +272 -0
  15. scc_cli/cli_launch.py +1454 -0
  16. scc_cli/cli_org.py +1428 -0
  17. scc_cli/cli_support.py +322 -0
  18. scc_cli/cli_team.py +892 -0
  19. scc_cli/cli_worktree.py +865 -0
  20. scc_cli/config.py +583 -0
  21. scc_cli/console.py +562 -0
  22. scc_cli/constants.py +79 -0
  23. scc_cli/contexts.py +377 -0
  24. scc_cli/deprecation.py +54 -0
  25. scc_cli/deps.py +189 -0
  26. scc_cli/docker/__init__.py +127 -0
  27. scc_cli/docker/core.py +466 -0
  28. scc_cli/docker/credentials.py +726 -0
  29. scc_cli/docker/launch.py +604 -0
  30. scc_cli/doctor/__init__.py +99 -0
  31. scc_cli/doctor/checks.py +1074 -0
  32. scc_cli/doctor/render.py +346 -0
  33. scc_cli/doctor/types.py +66 -0
  34. scc_cli/errors.py +288 -0
  35. scc_cli/evaluation/__init__.py +27 -0
  36. scc_cli/evaluation/apply_exceptions.py +207 -0
  37. scc_cli/evaluation/evaluate.py +97 -0
  38. scc_cli/evaluation/models.py +80 -0
  39. scc_cli/exit_codes.py +55 -0
  40. scc_cli/git.py +1521 -0
  41. scc_cli/json_command.py +166 -0
  42. scc_cli/json_output.py +96 -0
  43. scc_cli/kinds.py +62 -0
  44. scc_cli/marketplace/__init__.py +123 -0
  45. scc_cli/marketplace/adapter.py +74 -0
  46. scc_cli/marketplace/compute.py +377 -0
  47. scc_cli/marketplace/constants.py +87 -0
  48. scc_cli/marketplace/managed.py +135 -0
  49. scc_cli/marketplace/materialize.py +723 -0
  50. scc_cli/marketplace/normalize.py +548 -0
  51. scc_cli/marketplace/render.py +257 -0
  52. scc_cli/marketplace/resolve.py +459 -0
  53. scc_cli/marketplace/schema.py +506 -0
  54. scc_cli/marketplace/sync.py +260 -0
  55. scc_cli/marketplace/team_cache.py +195 -0
  56. scc_cli/marketplace/team_fetch.py +688 -0
  57. scc_cli/marketplace/trust.py +244 -0
  58. scc_cli/models/__init__.py +41 -0
  59. scc_cli/models/exceptions.py +273 -0
  60. scc_cli/models/plugin_audit.py +434 -0
  61. scc_cli/org_templates.py +269 -0
  62. scc_cli/output_mode.py +167 -0
  63. scc_cli/panels.py +113 -0
  64. scc_cli/platform.py +350 -0
  65. scc_cli/profiles.py +960 -0
  66. scc_cli/remote.py +443 -0
  67. scc_cli/schemas/__init__.py +1 -0
  68. scc_cli/schemas/org-v1.schema.json +456 -0
  69. scc_cli/schemas/team-config.v1.schema.json +163 -0
  70. scc_cli/sessions.py +425 -0
  71. scc_cli/setup.py +588 -0
  72. scc_cli/source_resolver.py +470 -0
  73. scc_cli/stats.py +378 -0
  74. scc_cli/stores/__init__.py +13 -0
  75. scc_cli/stores/exception_store.py +251 -0
  76. scc_cli/subprocess_utils.py +88 -0
  77. scc_cli/teams.py +382 -0
  78. scc_cli/templates/__init__.py +2 -0
  79. scc_cli/templates/org/__init__.py +0 -0
  80. scc_cli/templates/org/minimal.json +19 -0
  81. scc_cli/templates/org/reference.json +74 -0
  82. scc_cli/templates/org/strict.json +38 -0
  83. scc_cli/templates/org/teams.json +42 -0
  84. scc_cli/templates/statusline.sh +75 -0
  85. scc_cli/theme.py +348 -0
  86. scc_cli/ui/__init__.py +124 -0
  87. scc_cli/ui/branding.py +68 -0
  88. scc_cli/ui/chrome.py +395 -0
  89. scc_cli/ui/dashboard/__init__.py +62 -0
  90. scc_cli/ui/dashboard/_dashboard.py +677 -0
  91. scc_cli/ui/dashboard/loaders.py +395 -0
  92. scc_cli/ui/dashboard/models.py +184 -0
  93. scc_cli/ui/dashboard/orchestrator.py +390 -0
  94. scc_cli/ui/formatters.py +443 -0
  95. scc_cli/ui/gate.py +350 -0
  96. scc_cli/ui/help.py +157 -0
  97. scc_cli/ui/keys.py +538 -0
  98. scc_cli/ui/list_screen.py +431 -0
  99. scc_cli/ui/picker.py +700 -0
  100. scc_cli/ui/prompts.py +200 -0
  101. scc_cli/ui/wizard.py +675 -0
  102. scc_cli/update.py +680 -0
  103. scc_cli/utils/__init__.py +39 -0
  104. scc_cli/utils/fixit.py +264 -0
  105. scc_cli/utils/fuzzy.py +124 -0
  106. scc_cli/utils/locks.py +101 -0
  107. scc_cli/utils/ttl.py +376 -0
  108. scc_cli/validate.py +455 -0
  109. scc_cli-1.4.1.dist-info/METADATA +369 -0
  110. scc_cli-1.4.1.dist-info/RECORD +113 -0
  111. scc_cli-1.4.1.dist-info/WHEEL +4 -0
  112. scc_cli-1.4.1.dist-info/entry_points.txt +2 -0
  113. scc_cli-1.4.1.dist-info/licenses/LICENSE +21 -0
scc_cli/ui/prompts.py ADDED
@@ -0,0 +1,200 @@
1
+ """Simple Rich-based prompts for CLI interactions.
2
+
3
+ This module provides straightforward prompt utilities for user input that don't
4
+ require full TUI screens. For more complex interactive pickers with keyboard
5
+ navigation, see picker.py and wizard.py.
6
+
7
+ Functions:
8
+ render_error: Display an SCCError with user-friendly formatting
9
+ select_session: Interactive session selection from a list
10
+ select_team: Interactive team selection menu
11
+ prompt_custom_workspace: Prompt for custom workspace path
12
+ prompt_repo_url: Prompt for Git repository URL
13
+ """
14
+
15
+ from pathlib import Path
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ from rich import box
19
+ from rich.console import Console
20
+ from rich.panel import Panel
21
+ from rich.prompt import Confirm, IntPrompt, Prompt
22
+ from rich.table import Table
23
+
24
+ from scc_cli.theme import Borders, Colors
25
+
26
+ if TYPE_CHECKING:
27
+ from scc_cli.errors import SCCError
28
+
29
+
30
+ def render_error(console: Console, error: "SCCError", debug: bool = False) -> None:
31
+ """Render an error with user-friendly formatting.
32
+
33
+ Philosophy: "One message, one action"
34
+ - Display what went wrong (user_message)
35
+ - Display what to do next (suggested_action)
36
+ - Display debug info only if --debug flag is used
37
+
38
+ Args:
39
+ console: Rich console for output.
40
+ error: The SCCError to render.
41
+ debug: Whether to show debug context.
42
+ """
43
+ lines = []
44
+
45
+ # Main error message
46
+ lines.append(f"[bold]{error.user_message}[/bold]")
47
+
48
+ # Suggested action (if available)
49
+ if error.suggested_action:
50
+ lines.append("")
51
+ lines.append(f"[{Colors.SECONDARY}]->[/{Colors.SECONDARY}] {error.suggested_action}")
52
+
53
+ # Debug context (only with --debug)
54
+ if debug and error.debug_context:
55
+ lines.append("")
56
+ lines.append(f"[{Colors.SECONDARY}]--- Debug Info ---[/{Colors.SECONDARY}]")
57
+ lines.append(f"[{Colors.SECONDARY}]{error.debug_context}[/{Colors.SECONDARY}]")
58
+ elif error.debug_context and not debug:
59
+ lines.append("")
60
+ lines.append(
61
+ f"[{Colors.SECONDARY}]Run with --debug for technical details[/{Colors.SECONDARY}]"
62
+ )
63
+
64
+ # Create panel with error styling
65
+ panel = Panel(
66
+ "\n".join(lines),
67
+ title=f"[{Colors.ERROR_BOLD}]Error[/{Colors.ERROR_BOLD}]",
68
+ border_style=Borders.PANEL_ERROR,
69
+ padding=(0, 1),
70
+ )
71
+
72
+ console.print()
73
+ console.print(panel)
74
+ console.print()
75
+
76
+
77
+ def select_session(console: Console, sessions_list: list[dict[str, Any]]) -> dict[str, Any] | None:
78
+ """Display an interactive session selection menu.
79
+
80
+ Args:
81
+ console: Rich console for output.
82
+ sessions_list: List of session dicts with 'name', 'workspace', 'last_used', etc.
83
+
84
+ Returns:
85
+ Selected session dict or None if cancelled.
86
+ """
87
+ if not sessions_list:
88
+ console.print(f"[{Colors.WARNING}]No sessions available.[/{Colors.WARNING}]")
89
+ return None
90
+
91
+ console.print(f"\n[{Colors.BRAND_BOLD}]Select a session:[/{Colors.BRAND_BOLD}]\n")
92
+
93
+ table = Table(box=box.SIMPLE, show_header=False, padding=(0, 2))
94
+ table.add_column("Option", style=Colors.WARNING, width=4)
95
+ table.add_column("Name", style=Colors.BRAND)
96
+ table.add_column("Workspace", style=Colors.PRIMARY)
97
+ table.add_column("Last Used", style=Colors.SECONDARY)
98
+
99
+ for i, session in enumerate(sessions_list, 1):
100
+ name = session.get("name", "-")
101
+ workspace = session.get("workspace", "-")
102
+ last_used = session.get("last_used", "-")
103
+ table.add_row(f"[{i}]", name, workspace, last_used)
104
+
105
+ table.add_row("[0]", "<- Cancel", "", "")
106
+
107
+ console.print(table)
108
+
109
+ valid_choices = [str(i) for i in range(0, len(sessions_list) + 1)]
110
+ choice = IntPrompt.ask(
111
+ f"\n[{Colors.BRAND}]Select session[/{Colors.BRAND}]",
112
+ default=1,
113
+ choices=valid_choices,
114
+ )
115
+
116
+ if choice == 0:
117
+ return None
118
+
119
+ return sessions_list[choice - 1]
120
+
121
+
122
+ def select_team(console: Console, cfg: dict[str, Any]) -> str | None:
123
+ """Display an interactive team selection menu and return the chosen team.
124
+
125
+ Args:
126
+ console: Rich console for output.
127
+ cfg: Configuration dict containing 'profiles' key with team definitions.
128
+
129
+ Returns:
130
+ Selected team name or None if no teams available.
131
+ """
132
+ teams: dict[str, Any] = cfg.get("profiles", {})
133
+ team_list: list[str] = list(teams.keys())
134
+
135
+ if not team_list:
136
+ return None
137
+
138
+ console.print(f"\n[{Colors.BRAND_BOLD}]Select your team:[/{Colors.BRAND_BOLD}]\n")
139
+
140
+ table = Table(box=box.SIMPLE, show_header=False, padding=(0, 2))
141
+ table.add_column("Option", style=Colors.WARNING, width=4)
142
+ table.add_column("Team", style=Colors.BRAND)
143
+ table.add_column("Description", style=Colors.PRIMARY)
144
+
145
+ for i, team_name in enumerate(team_list, 1):
146
+ team_info = teams[team_name]
147
+ desc = team_info.get("description", "")
148
+ table.add_row(f"[{i}]", team_name, desc)
149
+
150
+ console.print(table)
151
+
152
+ choice = IntPrompt.ask(
153
+ f"\n[{Colors.BRAND}]Select team[/{Colors.BRAND}]",
154
+ default=1,
155
+ choices=[str(i) for i in range(1, len(team_list) + 1)],
156
+ )
157
+
158
+ selected = team_list[choice - 1]
159
+ console.print(f"\n[{Colors.SUCCESS}]Selected: {selected}[/{Colors.SUCCESS}]")
160
+
161
+ return selected
162
+
163
+
164
+ def prompt_custom_workspace(console: Console) -> str | None:
165
+ """Prompt the user to enter a custom workspace path.
166
+
167
+ Args:
168
+ console: Rich console for output.
169
+
170
+ Returns:
171
+ Resolved absolute path string, or None if cancelled or path invalid.
172
+ """
173
+ path = Prompt.ask(f"\n[{Colors.BRAND}]Enter workspace path[/{Colors.BRAND}]")
174
+
175
+ if not path:
176
+ return None
177
+
178
+ expanded = Path(path).expanduser().resolve()
179
+
180
+ if not expanded.exists():
181
+ console.print(f"[{Colors.ERROR}]Path does not exist: {expanded}[/{Colors.ERROR}]")
182
+ if Confirm.ask(f"[{Colors.BRAND}]Create this directory?[/{Colors.BRAND}]", default=False):
183
+ expanded.mkdir(parents=True, exist_ok=True)
184
+ return str(expanded)
185
+ return None
186
+
187
+ return str(expanded)
188
+
189
+
190
+ def prompt_repo_url(console: Console) -> str:
191
+ """Prompt the user to enter a Git repository URL.
192
+
193
+ Args:
194
+ console: Rich console for output.
195
+
196
+ Returns:
197
+ The entered URL string (may be empty if user pressed Enter).
198
+ """
199
+ url = Prompt.ask(f"\n[{Colors.BRAND}]Repository URL (HTTPS or SSH)[/{Colors.BRAND}]")
200
+ return url