scc-cli 1.5.3__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 (153) 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 +311 -0
  8. scc_cli/cli_common.py +190 -0
  9. scc_cli/cli_helpers.py +244 -0
  10. scc_cli/commands/__init__.py +20 -0
  11. scc_cli/commands/admin.py +708 -0
  12. scc_cli/commands/audit.py +246 -0
  13. scc_cli/commands/config.py +528 -0
  14. scc_cli/commands/exceptions.py +696 -0
  15. scc_cli/commands/init.py +272 -0
  16. scc_cli/commands/launch/__init__.py +73 -0
  17. scc_cli/commands/launch/app.py +1247 -0
  18. scc_cli/commands/launch/render.py +309 -0
  19. scc_cli/commands/launch/sandbox.py +135 -0
  20. scc_cli/commands/launch/workspace.py +339 -0
  21. scc_cli/commands/org/__init__.py +49 -0
  22. scc_cli/commands/org/_builders.py +264 -0
  23. scc_cli/commands/org/app.py +41 -0
  24. scc_cli/commands/org/import_cmd.py +267 -0
  25. scc_cli/commands/org/init_cmd.py +269 -0
  26. scc_cli/commands/org/schema_cmd.py +76 -0
  27. scc_cli/commands/org/status_cmd.py +157 -0
  28. scc_cli/commands/org/update_cmd.py +330 -0
  29. scc_cli/commands/org/validate_cmd.py +138 -0
  30. scc_cli/commands/support.py +323 -0
  31. scc_cli/commands/team.py +910 -0
  32. scc_cli/commands/worktree/__init__.py +72 -0
  33. scc_cli/commands/worktree/_helpers.py +57 -0
  34. scc_cli/commands/worktree/app.py +170 -0
  35. scc_cli/commands/worktree/container_commands.py +385 -0
  36. scc_cli/commands/worktree/context_commands.py +61 -0
  37. scc_cli/commands/worktree/session_commands.py +128 -0
  38. scc_cli/commands/worktree/worktree_commands.py +734 -0
  39. scc_cli/config.py +647 -0
  40. scc_cli/confirm.py +20 -0
  41. scc_cli/console.py +562 -0
  42. scc_cli/contexts.py +394 -0
  43. scc_cli/core/__init__.py +68 -0
  44. scc_cli/core/constants.py +101 -0
  45. scc_cli/core/errors.py +297 -0
  46. scc_cli/core/exit_codes.py +91 -0
  47. scc_cli/core/workspace.py +57 -0
  48. scc_cli/deprecation.py +54 -0
  49. scc_cli/deps.py +189 -0
  50. scc_cli/docker/__init__.py +127 -0
  51. scc_cli/docker/core.py +467 -0
  52. scc_cli/docker/credentials.py +726 -0
  53. scc_cli/docker/launch.py +595 -0
  54. scc_cli/doctor/__init__.py +105 -0
  55. scc_cli/doctor/checks/__init__.py +166 -0
  56. scc_cli/doctor/checks/cache.py +314 -0
  57. scc_cli/doctor/checks/config.py +107 -0
  58. scc_cli/doctor/checks/environment.py +182 -0
  59. scc_cli/doctor/checks/json_helpers.py +157 -0
  60. scc_cli/doctor/checks/organization.py +264 -0
  61. scc_cli/doctor/checks/worktree.py +278 -0
  62. scc_cli/doctor/render.py +365 -0
  63. scc_cli/doctor/types.py +66 -0
  64. scc_cli/evaluation/__init__.py +27 -0
  65. scc_cli/evaluation/apply_exceptions.py +207 -0
  66. scc_cli/evaluation/evaluate.py +97 -0
  67. scc_cli/evaluation/models.py +80 -0
  68. scc_cli/git.py +84 -0
  69. scc_cli/json_command.py +166 -0
  70. scc_cli/json_output.py +159 -0
  71. scc_cli/kinds.py +65 -0
  72. scc_cli/marketplace/__init__.py +123 -0
  73. scc_cli/marketplace/adapter.py +74 -0
  74. scc_cli/marketplace/compute.py +377 -0
  75. scc_cli/marketplace/constants.py +87 -0
  76. scc_cli/marketplace/managed.py +135 -0
  77. scc_cli/marketplace/materialize.py +846 -0
  78. scc_cli/marketplace/normalize.py +548 -0
  79. scc_cli/marketplace/render.py +281 -0
  80. scc_cli/marketplace/resolve.py +459 -0
  81. scc_cli/marketplace/schema.py +506 -0
  82. scc_cli/marketplace/sync.py +279 -0
  83. scc_cli/marketplace/team_cache.py +195 -0
  84. scc_cli/marketplace/team_fetch.py +689 -0
  85. scc_cli/marketplace/trust.py +244 -0
  86. scc_cli/models/__init__.py +41 -0
  87. scc_cli/models/exceptions.py +273 -0
  88. scc_cli/models/plugin_audit.py +434 -0
  89. scc_cli/org_templates.py +269 -0
  90. scc_cli/output_mode.py +167 -0
  91. scc_cli/panels.py +113 -0
  92. scc_cli/platform.py +350 -0
  93. scc_cli/profiles.py +960 -0
  94. scc_cli/remote.py +443 -0
  95. scc_cli/schemas/__init__.py +1 -0
  96. scc_cli/schemas/org-v1.schema.json +456 -0
  97. scc_cli/schemas/team-config.v1.schema.json +163 -0
  98. scc_cli/services/__init__.py +1 -0
  99. scc_cli/services/git/__init__.py +79 -0
  100. scc_cli/services/git/branch.py +151 -0
  101. scc_cli/services/git/core.py +216 -0
  102. scc_cli/services/git/hooks.py +108 -0
  103. scc_cli/services/git/worktree.py +444 -0
  104. scc_cli/services/workspace/__init__.py +36 -0
  105. scc_cli/services/workspace/resolver.py +223 -0
  106. scc_cli/services/workspace/suspicious.py +200 -0
  107. scc_cli/sessions.py +425 -0
  108. scc_cli/setup.py +589 -0
  109. scc_cli/source_resolver.py +470 -0
  110. scc_cli/stats.py +378 -0
  111. scc_cli/stores/__init__.py +13 -0
  112. scc_cli/stores/exception_store.py +251 -0
  113. scc_cli/subprocess_utils.py +88 -0
  114. scc_cli/teams.py +383 -0
  115. scc_cli/templates/__init__.py +2 -0
  116. scc_cli/templates/org/__init__.py +0 -0
  117. scc_cli/templates/org/minimal.json +19 -0
  118. scc_cli/templates/org/reference.json +74 -0
  119. scc_cli/templates/org/strict.json +38 -0
  120. scc_cli/templates/org/teams.json +42 -0
  121. scc_cli/templates/statusline.sh +75 -0
  122. scc_cli/theme.py +348 -0
  123. scc_cli/ui/__init__.py +154 -0
  124. scc_cli/ui/branding.py +68 -0
  125. scc_cli/ui/chrome.py +401 -0
  126. scc_cli/ui/dashboard/__init__.py +62 -0
  127. scc_cli/ui/dashboard/_dashboard.py +794 -0
  128. scc_cli/ui/dashboard/loaders.py +452 -0
  129. scc_cli/ui/dashboard/models.py +185 -0
  130. scc_cli/ui/dashboard/orchestrator.py +735 -0
  131. scc_cli/ui/formatters.py +444 -0
  132. scc_cli/ui/gate.py +350 -0
  133. scc_cli/ui/git_interactive.py +869 -0
  134. scc_cli/ui/git_render.py +176 -0
  135. scc_cli/ui/help.py +157 -0
  136. scc_cli/ui/keys.py +615 -0
  137. scc_cli/ui/list_screen.py +437 -0
  138. scc_cli/ui/picker.py +763 -0
  139. scc_cli/ui/prompts.py +201 -0
  140. scc_cli/ui/quick_resume.py +116 -0
  141. scc_cli/ui/wizard.py +576 -0
  142. scc_cli/update.py +680 -0
  143. scc_cli/utils/__init__.py +39 -0
  144. scc_cli/utils/fixit.py +264 -0
  145. scc_cli/utils/fuzzy.py +124 -0
  146. scc_cli/utils/locks.py +114 -0
  147. scc_cli/utils/ttl.py +376 -0
  148. scc_cli/validate.py +455 -0
  149. scc_cli-1.5.3.dist-info/METADATA +401 -0
  150. scc_cli-1.5.3.dist-info/RECORD +153 -0
  151. scc_cli-1.5.3.dist-info/WHEEL +4 -0
  152. scc_cli-1.5.3.dist-info/entry_points.txt +2 -0
  153. scc_cli-1.5.3.dist-info/licenses/LICENSE +21 -0
scc_cli/json_output.py ADDED
@@ -0,0 +1,159 @@
1
+ """
2
+ JSON envelope builder for CLI output.
3
+
4
+ Provide structured, versioned JSON envelopes for machine-readable output.
5
+ All JSON output MUST use this builder to ensure consistency.
6
+
7
+ Usage:
8
+ from scc_cli.json_output import build_envelope
9
+ from scc_cli.kinds import Kind
10
+
11
+ envelope = build_envelope(Kind.TEAM_LIST, data={"teams": [...]})
12
+ """
13
+
14
+ from datetime import datetime, timezone
15
+ from typing import Any
16
+
17
+ from . import __version__
18
+ from .core.errors import SCCError
19
+ from .kinds import Kind
20
+
21
+ # ═══════════════════════════════════════════════════════════════════════════════
22
+ # Constants
23
+ # ═══════════════════════════════════════════════════════════════════════════════
24
+
25
+ API_VERSION = "scc.cli/v1"
26
+
27
+
28
+ # ═══════════════════════════════════════════════════════════════════════════════
29
+ # Envelope Builder
30
+ # ═══════════════════════════════════════════════════════════════════════════════
31
+
32
+
33
+ def build_envelope(
34
+ kind: Kind,
35
+ *,
36
+ data: dict[str, Any] | None = None,
37
+ ok: bool = True,
38
+ errors: list[str] | None = None,
39
+ warnings: list[str] | None = None,
40
+ ) -> dict[str, Any]:
41
+ """Build a JSON envelope with standard structure.
42
+
43
+ All JSON output follows this contract for consistency and parseability.
44
+
45
+ Args:
46
+ kind: The envelope kind (from Kind enum)
47
+ data: The command-specific payload
48
+ ok: Whether the operation was successful
49
+ errors: List of error messages (sets ok=False if non-empty)
50
+ warnings: List of warning messages
51
+
52
+ Returns:
53
+ A structured envelope dict ready for JSON serialization:
54
+ {
55
+ "apiVersion": "scc.cli/v1",
56
+ "kind": "TeamList",
57
+ "metadata": {
58
+ "generatedAt": "2025-12-23T10:00:00Z",
59
+ "cliVersion": "1.2.3"
60
+ },
61
+ "status": {
62
+ "ok": true,
63
+ "errors": [],
64
+ "warnings": []
65
+ },
66
+ "data": { ... }
67
+ }
68
+ """
69
+ # Normalize optional parameters
70
+ if data is None:
71
+ data = {}
72
+ if errors is None:
73
+ errors = []
74
+ if warnings is None:
75
+ warnings = []
76
+
77
+ # If errors provided, ok should be False
78
+ if errors and ok:
79
+ ok = False
80
+
81
+ # Generate ISO 8601 timestamp in UTC
82
+ generated_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
83
+
84
+ return {
85
+ "apiVersion": API_VERSION,
86
+ "kind": str(kind.value) if hasattr(kind, "value") else str(kind),
87
+ "metadata": {
88
+ "generatedAt": generated_at,
89
+ "cliVersion": __version__,
90
+ },
91
+ "status": {
92
+ "ok": ok,
93
+ "errors": errors,
94
+ "warnings": warnings,
95
+ },
96
+ "data": data,
97
+ }
98
+
99
+
100
+ def build_error_envelope(exc: Exception) -> dict[str, Any]:
101
+ """Build a JSON error envelope from an exception.
102
+
103
+ This is the canonical error format for JSON mode output.
104
+ All errors should go through this function to ensure consistency.
105
+
106
+ Args:
107
+ exc: The exception to convert to JSON envelope
108
+
109
+ Returns:
110
+ A structured error envelope dict ready for JSON serialization:
111
+ {
112
+ "apiVersion": "scc.cli/v1",
113
+ "kind": "Error",
114
+ "metadata": { ... },
115
+ "status": {
116
+ "ok": false,
117
+ "errors": ["Error message"],
118
+ "warnings": []
119
+ },
120
+ "data": {
121
+ "error_type": "SCCError",
122
+ "user_message": "...",
123
+ "suggested_action": "...",
124
+ "debug_context": "..." # Only for SCCError
125
+ }
126
+ }
127
+ """
128
+ # Generate ISO 8601 timestamp in UTC
129
+ generated_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
130
+
131
+ # Build error data depending on exception type
132
+ error_data: dict[str, Any] = {
133
+ "error_type": type(exc).__name__,
134
+ }
135
+
136
+ if isinstance(exc, SCCError):
137
+ error_data["user_message"] = exc.user_message
138
+ if exc.suggested_action:
139
+ error_data["suggested_action"] = exc.suggested_action
140
+ if exc.debug_context:
141
+ error_data["debug_context"] = exc.debug_context
142
+ error_message = exc.user_message
143
+ else:
144
+ error_message = str(exc)
145
+
146
+ return {
147
+ "apiVersion": API_VERSION,
148
+ "kind": str(Kind.ERROR.value),
149
+ "metadata": {
150
+ "generatedAt": generated_at,
151
+ "cliVersion": __version__,
152
+ },
153
+ "status": {
154
+ "ok": False,
155
+ "errors": [error_message],
156
+ "warnings": [],
157
+ },
158
+ "data": error_data,
159
+ }
scc_cli/kinds.py ADDED
@@ -0,0 +1,65 @@
1
+ """Define centralized JSON envelope kind names to prevent drift.
2
+
3
+ Define all JSON envelope `kind` values here as enum members.
4
+ This prevents inconsistencies like "TeamList" vs "TeamsList" across the codebase.
5
+
6
+ Usage:
7
+ from scc_cli.kinds import Kind
8
+
9
+ envelope = build_envelope(Kind.TEAM_LIST, data={...})
10
+ """
11
+
12
+ from enum import Enum
13
+
14
+
15
+ class Kind(str, Enum):
16
+ """Define JSON envelope kind identifiers.
17
+
18
+ Inherit from str so enum values serialize directly to JSON without .value.
19
+ Add new kinds here to ensure consistency across all commands.
20
+ """
21
+
22
+ # Team commands
23
+ TEAM_LIST = "TeamList"
24
+ TEAM_INFO = "TeamInfo"
25
+ TEAM_CURRENT = "TeamCurrent"
26
+ TEAM_SWITCH = "TeamSwitch"
27
+ TEAM_VALIDATE = "TeamValidate"
28
+
29
+ # Status/Doctor
30
+ STATUS = "Status"
31
+ DOCTOR_REPORT = "DoctorReport"
32
+
33
+ # Worktree commands
34
+ WORKTREE_LIST = "WorktreeList"
35
+ WORKTREE_CREATE = "WorktreeCreate"
36
+ WORKTREE_REMOVE = "WorktreeRemove"
37
+
38
+ # Session/Container
39
+ SESSION_LIST = "SessionList"
40
+ CONTAINER_LIST = "ContainerList"
41
+
42
+ # Org admin
43
+ ORG_VALIDATION = "OrgValidation"
44
+ ORG_SCHEMA = "OrgSchema"
45
+ ORG_STATUS = "OrgStatus"
46
+ ORG_IMPORT = "OrgImport"
47
+ ORG_IMPORT_PREVIEW = "OrgImportPreview"
48
+ ORG_INIT = "OrgInit"
49
+ ORG_TEMPLATE_LIST = "OrgTemplateList"
50
+ ORG_UPDATE = "OrgUpdate"
51
+
52
+ # Support
53
+ SUPPORT_BUNDLE = "SupportBundle"
54
+
55
+ # Config
56
+ CONFIG_EXPLAIN = "ConfigExplain"
57
+
58
+ # Start
59
+ START_DRY_RUN = "StartDryRun"
60
+
61
+ # Init
62
+ INIT_RESULT = "InitResult"
63
+
64
+ # Error (used by handle_errors in JSON mode)
65
+ ERROR = "Error"
@@ -0,0 +1,123 @@
1
+ """
2
+ Marketplace and plugin management for SCC.
3
+
4
+ This package provides organization-level plugin governance with:
5
+ - Multi-source marketplace definitions (GitHub, Git, URL, directory)
6
+ - Team-based plugin sets with org defaults inheritance
7
+ - Security policies (blocked plugins with audit trail)
8
+ - Project-local materialization for Docker sandbox compatibility
9
+
10
+ Public API:
11
+ - Schema models: OrgConfig, MarketplaceSource, TeamProfile, SecurityConfig
12
+ - Constants: IMPLICIT_MARKETPLACES, EXIT_CODES
13
+ - Normalization: normalize_plugin(), matches_pattern()
14
+ - Computation: compute_effective_plugins(), EffectivePlugins
15
+ - Materialization: materialize_marketplace(), MaterializedMarketplace
16
+ - Settings: render_settings(), merge_settings()
17
+ - State: ManagedState, load_managed_state(), save_managed_state()
18
+
19
+ Example:
20
+ >>> from scc_cli.marketplace import OrgConfig, compute_effective_plugins
21
+ >>> config = OrgConfig.model_validate(json_data)
22
+ >>> effective = compute_effective_plugins(config, team_id="backend")
23
+ >>> print(effective.enabled_plugins)
24
+ """
25
+
26
+ from scc_cli.marketplace.compute import (
27
+ BlockedPlugin,
28
+ EffectivePlugins,
29
+ TeamNotFoundError,
30
+ compute_effective_plugins,
31
+ )
32
+ from scc_cli.marketplace.constants import (
33
+ EXIT_CODES,
34
+ IMPLICIT_MARKETPLACES,
35
+ MANAGED_STATE_FILE,
36
+ MARKETPLACE_CACHE_DIR,
37
+ )
38
+
39
+ # Managed State
40
+ from scc_cli.marketplace.managed import (
41
+ ManagedState,
42
+ clear_managed_state,
43
+ load_managed_state,
44
+ save_managed_state,
45
+ )
46
+
47
+ # Materialization
48
+ from scc_cli.marketplace.materialize import (
49
+ GitNotAvailableError,
50
+ InvalidMarketplaceError,
51
+ MaterializationError,
52
+ MaterializedMarketplace,
53
+ materialize_marketplace,
54
+ )
55
+ from scc_cli.marketplace.normalize import (
56
+ AmbiguousMarketplaceError,
57
+ InvalidPluginRefError,
58
+ matches_any_pattern,
59
+ matches_pattern,
60
+ normalize_plugin,
61
+ )
62
+
63
+ # Rendering
64
+ from scc_cli.marketplace.render import (
65
+ check_conflicts,
66
+ merge_settings,
67
+ render_settings,
68
+ )
69
+ from scc_cli.marketplace.schema import (
70
+ DefaultsConfig,
71
+ MarketplaceSource,
72
+ MarketplaceSourceDirectory,
73
+ MarketplaceSourceGit,
74
+ MarketplaceSourceGitHub,
75
+ MarketplaceSourceURL,
76
+ OrganizationConfig,
77
+ SecurityConfig,
78
+ TeamProfile,
79
+ )
80
+
81
+ __all__ = [
82
+ # Constants
83
+ "EXIT_CODES",
84
+ "IMPLICIT_MARKETPLACES",
85
+ "MARKETPLACE_CACHE_DIR",
86
+ "MANAGED_STATE_FILE",
87
+ # Schema models
88
+ "OrganizationConfig",
89
+ "MarketplaceSource",
90
+ "MarketplaceSourceGitHub",
91
+ "MarketplaceSourceGit",
92
+ "MarketplaceSourceURL",
93
+ "MarketplaceSourceDirectory",
94
+ "TeamProfile",
95
+ "SecurityConfig",
96
+ "DefaultsConfig",
97
+ # Normalization
98
+ "normalize_plugin",
99
+ "matches_pattern",
100
+ "matches_any_pattern",
101
+ "InvalidPluginRefError",
102
+ "AmbiguousMarketplaceError",
103
+ # Computation
104
+ "compute_effective_plugins",
105
+ "EffectivePlugins",
106
+ "BlockedPlugin",
107
+ "TeamNotFoundError",
108
+ # Materialization
109
+ "materialize_marketplace",
110
+ "MaterializedMarketplace",
111
+ "MaterializationError",
112
+ "GitNotAvailableError",
113
+ "InvalidMarketplaceError",
114
+ # Rendering
115
+ "render_settings",
116
+ "merge_settings",
117
+ "check_conflicts",
118
+ # Managed state
119
+ "ManagedState",
120
+ "load_managed_state",
121
+ "save_managed_state",
122
+ "clear_managed_state",
123
+ ]
@@ -0,0 +1,74 @@
1
+ """
2
+ Adapter for translating external org config format to internal Pydantic format.
3
+
4
+ This module implements the Anti-Corruption Layer pattern between:
5
+ - External format (JSON Schema): Human-readable, semver strings, nested organization
6
+ - Internal format (Pydantic): Python-native types, integer versioning, flat structure
7
+
8
+ The translation happens AFTER JSON Schema validation (Validation Gate) but
9
+ BEFORE Pydantic model_validate() to ensure proper type conversion.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import copy
15
+ from typing import Any
16
+
17
+
18
+ def translate_org_config(external: dict[str, Any]) -> dict[str, Any]:
19
+ """Translate external JSON format to internal Pydantic format.
20
+
21
+ External format (from org config JSON):
22
+ - organization.name, organization.id (nested)
23
+ - schema_version: "1.0.0" (semver string)
24
+
25
+ Internal format (for Pydantic):
26
+ - name (flat at root level)
27
+ - schema_version: 1 (integer, major version only)
28
+
29
+ Uses deepcopy to prevent side-effects on cached external configuration.
30
+
31
+ Args:
32
+ external: External org config dict (from cache or remote fetch)
33
+
34
+ Returns:
35
+ Internal format dict ready for Pydantic model_validate()
36
+
37
+ Examples:
38
+ >>> external = {
39
+ ... "schema_version": "1.0.0",
40
+ ... "organization": {"name": "Acme Corp", "id": "acme"},
41
+ ... "profiles": {}
42
+ ... }
43
+ >>> internal = translate_org_config(external)
44
+ >>> internal["name"]
45
+ 'Acme Corp'
46
+ >>> internal["schema_version"]
47
+ 1
48
+ """
49
+ # Use deepcopy to prevent cache poisoning via shallow copy mutations
50
+ internal = copy.deepcopy(external)
51
+
52
+ # ── Flatten organization object ───────────────────────────────────────────
53
+ # Pop the nested organization structure, default to empty dict if missing
54
+ org_data = internal.pop("organization", {})
55
+
56
+ # Only map 'name' if it wasn't already at the top level (precedence rule)
57
+ # This handles the case where config is already in internal format
58
+ if "name" not in internal and "name" in org_data:
59
+ internal["name"] = org_data["name"]
60
+
61
+ # ── Convert semver string to integer ──────────────────────────────────────
62
+ raw_version = internal.get("schema_version")
63
+ if isinstance(raw_version, str):
64
+ # Remove common 'v' prefix if present (e.g., "v1.0.0" -> "1.0.0")
65
+ clean_version = raw_version.lstrip("vV")
66
+ try:
67
+ # Handle both "1.0.0" -> 1 and "1" -> 1
68
+ internal["schema_version"] = int(clean_version.split(".")[0])
69
+ except ValueError:
70
+ # If parsing fails, leave as-is; Pydantic will catch the type error
71
+ # and provide better error context than a generic ValueError
72
+ pass
73
+
74
+ return internal