cade-cli 0.3.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.
Files changed (44) hide show
  1. cade_cli-0.3.3.dist-info/METADATA +151 -0
  2. cade_cli-0.3.3.dist-info/RECORD +44 -0
  3. cade_cli-0.3.3.dist-info/WHEEL +4 -0
  4. cade_cli-0.3.3.dist-info/entry_points.txt +2 -0
  5. cadecoder/__init__.py +1 -0
  6. cadecoder/ai/__init__.py +6 -0
  7. cadecoder/ai/prompts.py +572 -0
  8. cadecoder/cli/__init__.py +0 -0
  9. cadecoder/cli/app.py +147 -0
  10. cadecoder/cli/auth.py +483 -0
  11. cadecoder/cli/commands/__init__.py +5 -0
  12. cadecoder/cli/commands/auth.py +143 -0
  13. cadecoder/cli/commands/chat.py +264 -0
  14. cadecoder/cli/commands/mcp.py +477 -0
  15. cadecoder/cli/commands/tools.py +226 -0
  16. cadecoder/core/__init__.py +12 -0
  17. cadecoder/core/config.py +380 -0
  18. cadecoder/core/constants.py +281 -0
  19. cadecoder/core/errors.py +145 -0
  20. cadecoder/core/logging.py +148 -0
  21. cadecoder/core/types.py +235 -0
  22. cadecoder/core/utils.py +279 -0
  23. cadecoder/execution/__init__.py +46 -0
  24. cadecoder/execution/context_window.py +521 -0
  25. cadecoder/execution/orchestrator.py +562 -0
  26. cadecoder/execution/parallel.py +287 -0
  27. cadecoder/providers/__init__.py +60 -0
  28. cadecoder/providers/base.py +294 -0
  29. cadecoder/providers/openai.py +251 -0
  30. cadecoder/storage/__init__.py +0 -0
  31. cadecoder/storage/threads.py +489 -0
  32. cadecoder/templates/login_failed.html +21 -0
  33. cadecoder/templates/login_success.html +21 -0
  34. cadecoder/templates/styles.css +87 -0
  35. cadecoder/tools/__init__.py +19 -0
  36. cadecoder/tools/builtin.py +644 -0
  37. cadecoder/tools/filesystem.py +315 -0
  38. cadecoder/tools/git.py +221 -0
  39. cadecoder/tools/manager.py +1635 -0
  40. cadecoder/ui/__init__.py +7 -0
  41. cadecoder/ui/display.py +338 -0
  42. cadecoder/ui/input.py +145 -0
  43. cadecoder/ui/session.py +455 -0
  44. cadecoder/ui/state.py +20 -0
@@ -0,0 +1,12 @@
1
+ """Core modules for CadeCoder.
2
+
3
+ Note: config is intentionally NOT imported at module level to avoid
4
+ triggering auth validation on simple imports (e.g., importing constants).
5
+ Use `from cadecoder.core.config import get_config` when config is needed.
6
+ """
7
+
8
+ from cadecoder.core.errors import CadeCoderError
9
+
10
+ __all__ = [
11
+ "CadeCoderError",
12
+ ]
@@ -0,0 +1,380 @@
1
+ """Configuration management for CadeCoder CLI.
2
+
3
+ Handles loading Arcade credentials (via arcade-core) and CadeCoder specific settings.
4
+ Credentials are shared with arcade-cli at ~/.arcade/credentials.yaml.
5
+ """
6
+
7
+ import os
8
+ import threading
9
+ import tomllib
10
+ from functools import lru_cache
11
+ from pathlib import Path
12
+
13
+ import toml
14
+ import yaml
15
+ from arcade_core.auth_tokens import get_valid_access_token
16
+ from arcade_core.config_model import Config as ArcadeConfig
17
+ from arcade_core.constants import PROD_COORDINATOR_HOST
18
+ from pydantic import BaseModel, ConfigDict, Field
19
+
20
+ from cadecoder.core import constants
21
+ from cadecoder.core.errors import AuthError, ConfigError
22
+
23
+ # --- Constants ---
24
+
25
+ CADECODER_CONFIG_DIR_NAME = "cadecoder"
26
+ CADECODER_CONFIG_FILE_NAME = "cadecoder.toml"
27
+
28
+
29
+ # --- Pydantic Models for CadeCoder Settings ---
30
+
31
+
32
+ class ResponsesAPIConfig(BaseModel):
33
+ """Configuration for Responses API."""
34
+
35
+ model_config = ConfigDict(extra="ignore")
36
+ enabled: bool = Field(default=True, description="Enable Responses API when available")
37
+ streaming_enabled: bool = Field(
38
+ default=True, description="Enable streaming responses by default"
39
+ )
40
+
41
+
42
+ class ModelConfig(BaseModel):
43
+ """Model configuration settings."""
44
+
45
+ model_config = ConfigDict(extra="ignore")
46
+ provider: str = Field(
47
+ default="openai", description="AI provider name (openai, anthropic, etc.)"
48
+ )
49
+ model: str = Field(default=constants.DEFAULT_AI_MODEL, description="Model name")
50
+ host: str | None = Field(
51
+ default=None,
52
+ description="Custom API host (for OpenAI-compatible APIs)",
53
+ )
54
+ api_key: str | None = Field(default=None, description="API key (overrides environment)")
55
+
56
+
57
+ class ToolConfig(BaseModel):
58
+ """Tool configuration settings."""
59
+
60
+ model_config = ConfigDict(extra="ignore")
61
+ included_toolkits: list[str] = Field(
62
+ default_factory=lambda: list(constants.DEFAULT_INCLUDED_TOOLKITS),
63
+ description="List of toolkit names to include",
64
+ )
65
+ included_tools: list[str] = Field(
66
+ default_factory=lambda: list(constants.DEFAULT_INCLUDED_TOOLS),
67
+ description="List of specific tool names to include",
68
+ )
69
+ enabled_tools: list[str] = Field(
70
+ default_factory=list,
71
+ description="List of enabled tool names (local and remote)",
72
+ )
73
+ disabled_tools: list[str] = Field(
74
+ default_factory=list,
75
+ description="List of disabled tool names (local and remote)",
76
+ )
77
+
78
+
79
+ class CadeCoderSettings(BaseModel):
80
+ """Model for settings specific to cadecoder.toml."""
81
+
82
+ model_config = ConfigDict(extra="ignore")
83
+ default_model: str = constants.DEFAULT_AI_MODEL
84
+ debug_mode: bool = Field(
85
+ default=True,
86
+ description="Enable debug mode for verbose logging and error details.",
87
+ )
88
+ use_responses_api: bool = Field(
89
+ default=True, description="Use OpenAI Responses API when available"
90
+ )
91
+ responses_config: ResponsesAPIConfig = Field(
92
+ default_factory=ResponsesAPIConfig,
93
+ description="Responses API specific configuration",
94
+ )
95
+ model_settings: ModelConfig = Field(
96
+ default_factory=ModelConfig, description="Model configuration"
97
+ )
98
+ tool_settings: ToolConfig = Field(default_factory=ToolConfig, description="Tool configuration")
99
+
100
+
101
+ # --- Helper Functions ---
102
+
103
+
104
+ def _get_cadecoder_config_path() -> Path:
105
+ """Gets the path to the cadecoder specific config file."""
106
+ home = Path.home()
107
+ cadecoder_dir = home / ".cadecoder"
108
+ return cadecoder_dir / CADECODER_CONFIG_FILE_NAME
109
+
110
+
111
+ @lru_cache(maxsize=1)
112
+ def _load_cadecoder_settings() -> CadeCoderSettings:
113
+ """Loads the CadeCoder specific settings from cadecoder.toml.
114
+
115
+ Creates a default file if it doesn't exist.
116
+ Returns default settings if file is invalid or missing.
117
+ """
118
+ config_path = _get_cadecoder_config_path()
119
+
120
+ if config_path.exists():
121
+ try:
122
+ with config_path.open("rb") as f:
123
+ config_data = tomllib.load(f)
124
+ return CadeCoderSettings.model_validate(config_data)
125
+ except tomllib.TOMLDecodeError as e:
126
+ raise ConfigError(f"Error decoding TOML file '{config_path}': {e}. Using defaults.")
127
+ except OSError as e:
128
+ raise ConfigError(f"Error reading settings file '{config_path}': {e}. Using defaults.")
129
+ else:
130
+ default_settings = CadeCoderSettings()
131
+ _save_cadecoder_settings(default_settings)
132
+ return default_settings
133
+
134
+
135
+ def _save_cadecoder_settings(settings: CadeCoderSettings) -> None:
136
+ """Save the CadeCoder specific settings to cadecoder.toml."""
137
+ config_path = _get_cadecoder_config_path()
138
+ try:
139
+ config_path.parent.mkdir(parents=True, exist_ok=True)
140
+ config_data = settings.model_dump(mode="python")
141
+ with config_path.open("w", encoding="utf-8") as f:
142
+ toml.dump(config_data, f)
143
+ except (OSError, TypeError) as e:
144
+ raise ConfigError(f"Error writing CadeCoder settings file '{config_path}': {e}") from e
145
+
146
+
147
+ def _load_arcade_config() -> ArcadeConfig | None:
148
+ """Load arcade-core config if available."""
149
+ try:
150
+ return ArcadeConfig.load_from_file()
151
+ except (FileNotFoundError, ValueError):
152
+ return None
153
+
154
+
155
+ def _check_legacy_api_key() -> tuple[str, str] | None:
156
+ """Check for legacy API key credentials (backward compatibility).
157
+
158
+ Returns:
159
+ Tuple of (api_key, email) if found, None otherwise.
160
+ """
161
+ creds_path = Path.home() / ".arcade" / "credentials.yaml"
162
+ if not creds_path.exists():
163
+ return None
164
+
165
+ try:
166
+ with creds_path.open() as f:
167
+ data = yaml.safe_load(f) or {}
168
+
169
+ cloud = data.get("cloud", {})
170
+ if isinstance(cloud, dict) and "api" in cloud:
171
+ api_key = cloud.get("api", {}).get("key")
172
+ email = cloud.get("user", {}).get("email")
173
+ if api_key and email:
174
+ return api_key, email
175
+ except Exception:
176
+ pass
177
+
178
+ return None
179
+
180
+
181
+ # --- Singleton Config Class ---
182
+
183
+
184
+ class AppConfig:
185
+ """Singleton configuration class for CadeCoder.
186
+
187
+ Loads credentials from arcade-core (OAuth) or falls back to
188
+ legacy API key format. CadeCoder-specific settings are loaded
189
+ from ~/.cadecoder/cadecoder.toml.
190
+ """
191
+
192
+ _instance: "AppConfig | None" = None
193
+ _lock = threading.Lock()
194
+
195
+ def __new__(cls) -> "AppConfig":
196
+ """Create or return the singleton instance."""
197
+ if cls._instance is None:
198
+ with cls._lock:
199
+ if cls._instance is None:
200
+ cls._instance = super().__new__(cls)
201
+ cls._instance._load_configuration()
202
+ return cls._instance
203
+
204
+ def _load_configuration(self) -> None:
205
+ """Load configurations from arcade-core and cadecoder.toml."""
206
+ self._arcade_config: ArcadeConfig | None = None
207
+ self._legacy_api_key: str | None = None
208
+ self._legacy_email: str | None = None
209
+
210
+ # Try to load arcade-core config (new OAuth format)
211
+ self._arcade_config = _load_arcade_config()
212
+
213
+ # Check for environment variable override
214
+ env_api_key = os.environ.get("ARCADE_API_KEY")
215
+ env_email = os.environ.get("ARCADE_USER_EMAIL")
216
+
217
+ if env_api_key:
218
+ # Environment variable takes precedence
219
+ self._legacy_api_key = env_api_key
220
+ self._legacy_email = env_email
221
+ elif self._arcade_config and self._arcade_config.is_authenticated():
222
+ # New OAuth format is available
223
+ pass
224
+ else:
225
+ # Fall back to legacy API key format
226
+ legacy = _check_legacy_api_key()
227
+ if legacy:
228
+ self._legacy_api_key, self._legacy_email = legacy
229
+
230
+ # Load CadeCoder-specific settings
231
+ try:
232
+ self._settings = _load_cadecoder_settings()
233
+ except ConfigError:
234
+ self._settings = CadeCoderSettings()
235
+
236
+ @property
237
+ def is_authenticated(self) -> bool:
238
+ """Check if user is authenticated (OAuth or API key)."""
239
+ if self._legacy_api_key:
240
+ return True
241
+ if self._arcade_config and self._arcade_config.is_authenticated():
242
+ return True
243
+ return False
244
+
245
+ @property
246
+ def api_key(self) -> str:
247
+ """Get the API key or access token for Arcade API calls.
248
+
249
+ For OAuth auth, this returns a valid access token (auto-refreshed).
250
+ For legacy auth, this returns the API key.
251
+
252
+ Raises:
253
+ AuthError: If not authenticated.
254
+ """
255
+ # Environment variable override
256
+ if self._legacy_api_key:
257
+ return self._legacy_api_key
258
+
259
+ # Try OAuth token
260
+ if self._arcade_config and self._arcade_config.is_authenticated():
261
+ try:
262
+ coordinator_url = (
263
+ self._arcade_config.coordinator_url or f"https://{PROD_COORDINATOR_HOST}"
264
+ )
265
+ return get_valid_access_token(coordinator_url)
266
+ except ValueError as e:
267
+ raise AuthError(f"Failed to get access token: {e}") from e
268
+
269
+ raise AuthError("Not authenticated. Run 'cade login' to authenticate with Arcade Cloud.")
270
+
271
+ @property
272
+ def base_url(self) -> str | None:
273
+ """Returns the Arcade base URL from environment."""
274
+ return os.environ.get("ARCADE_BASE_URL")
275
+
276
+ @property
277
+ def model_api_key(self) -> str:
278
+ """Returns the API key for the model service (OpenAI, Anthropic, etc.)."""
279
+ model_service = constants.MODEL_SERVICE
280
+ try:
281
+ if model_service == "openai":
282
+ return os.environ["OPENAI_API_KEY"]
283
+ elif model_service == "anthropic":
284
+ return os.environ["ANTHROPIC_API_KEY"]
285
+ else:
286
+ raise ValueError(f"Invalid model service: {model_service}")
287
+ except KeyError:
288
+ raise AuthError(f"API key is not available for {model_service}.")
289
+
290
+ @property
291
+ def user_email(self) -> str:
292
+ """Returns the user's email address."""
293
+ # Legacy format or env override
294
+ if self._legacy_email:
295
+ return self._legacy_email
296
+
297
+ # OAuth format
298
+ if self._arcade_config and self._arcade_config.user:
299
+ return self._arcade_config.user.email or ""
300
+
301
+ return ""
302
+
303
+ @property
304
+ def org_id(self) -> str | None:
305
+ """Returns the active organization ID (OAuth only)."""
306
+ if self._arcade_config and self._arcade_config.context:
307
+ return self._arcade_config.context.org_id
308
+ return None
309
+
310
+ @property
311
+ def project_id(self) -> str | None:
312
+ """Returns the active project ID (OAuth only)."""
313
+ if self._arcade_config and self._arcade_config.context:
314
+ return self._arcade_config.context.project_id
315
+ return None
316
+
317
+ @property
318
+ def settings(self) -> CadeCoderSettings:
319
+ """Returns the CadeCoder specific settings."""
320
+ return self._settings
321
+
322
+ @property
323
+ def model_settings(self) -> ModelConfig:
324
+ """Returns the model configuration."""
325
+ return self._settings.model_settings
326
+
327
+ @property
328
+ def tool_settings(self) -> ToolConfig:
329
+ """Returns the tool configuration."""
330
+ return self._settings.tool_settings
331
+
332
+ @property
333
+ def app_dir(self) -> str:
334
+ """Returns the CadeCoder application directory path."""
335
+ base = os.environ.get("CADECODER_HOME", Path.home())
336
+ return str(Path(base) / ".cadecoder")
337
+
338
+ def ensure_app_dir(self) -> str:
339
+ """Ensures the application directory exists and returns its path."""
340
+ path = Path(self.app_dir)
341
+ try:
342
+ path.mkdir(parents=True, exist_ok=True)
343
+ return str(path)
344
+ except OSError as e:
345
+ raise ConfigError(f"Could not create application directory {path}: {e}") from e
346
+
347
+ def reload(self) -> None:
348
+ """Clear caches and reload configuration."""
349
+ _load_cadecoder_settings.cache_clear()
350
+ self._load_configuration()
351
+
352
+
353
+ # --- Public Access ---
354
+
355
+ # Lazy initialization - don't create instance on import
356
+ _config: AppConfig | None = None
357
+
358
+
359
+ def get_config() -> AppConfig:
360
+ """Get the singleton AppConfig instance.
361
+
362
+ Use this function instead of directly accessing `config` to ensure
363
+ lazy initialization (no AuthError on import).
364
+ """
365
+ global _config
366
+ if _config is None:
367
+ _config = AppConfig()
368
+ return _config
369
+
370
+
371
+ # For backward compatibility with code that imports `config` directly
372
+ # Note: prefer using get_config() for lazy initialization
373
+ class _ConfigProxy:
374
+ """Proxy that provides lazy access to AppConfig singleton."""
375
+
376
+ def __getattr__(self, name: str):
377
+ return getattr(get_config(), name)
378
+
379
+
380
+ config = _ConfigProxy()
@@ -0,0 +1,281 @@
1
+ """Constants used across the cade application."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import pathlib # Added for PROJECT_ROOT
7
+ from typing import Final
8
+
9
+ # ------------------------------------------------------------------------------
10
+ # General Application & AI Configuration
11
+ # ------------------------------------------------------------------------------
12
+
13
+ DEFAULT_AI_MODEL: Final[str] = "gpt-4.1"
14
+ DEFAULT_MAX_TOKENS: Final[int] = 1_000_000
15
+ MODEL_SERVICE: Final[str] = os.environ.get("CADE_MODEL_SERVICE", "openai").lower()
16
+ DEFAULT_TEMPERATURE: Final[float] = 0.7
17
+
18
+ # Backwards‑compatibility aliases for AI model (scheduled for deprecation)
19
+ DEFAULT_MODEL: Final[str] = DEFAULT_AI_MODEL
20
+ MAX_TOKENS: Final[int] = DEFAULT_MAX_TOKENS
21
+
22
+ # ------------------------------------------------------------------------------
23
+ # Project & File Operations
24
+ # ------------------------------------------------------------------------------
25
+
26
+ PROJECT_ROOT: Final[pathlib.Path] = pathlib.Path(os.getenv("PROJECT_ROOT", ".")).resolve()
27
+ MAX_PREVIEW_BYTES: Final[int] = 65_536 # For truncating file previews by tools
28
+ MAX_LIST_DEPTH: Final[int] = 40 # Max depth for listing files by tools
29
+ MAX_LIST_RESULTS: Final[int] = 10_000 # Max results for listing files by tools
30
+ MODE_OVERWRITE: Final[str] = "overwrite" # File write mode
31
+ MODE_APPEND: Final[str] = "append" # File write mode
32
+
33
+ DEFAULT_INCLUDED_TOOLKITS: Final[list[str]] = ["github", "googlesearch"]
34
+ DEFAULT_INCLUDED_TOOLS: Final[list[str]] = []
35
+ MAX_AGENT_TOOLS: Final[int] = 120 # Max tools to provide to agent (OpenAI limit is 128)
36
+ MAX_CONCURRENT_STEPS: Final[int] = (
37
+ 5 # Max steps to execute in parallel (prevents resource exhaustion)
38
+ )
39
+
40
+ # ------------------------------------------------------------------------------
41
+ # Orchestrator & Execution Configuration
42
+ # ------------------------------------------------------------------------------
43
+
44
+ # Execution limits
45
+ MAX_EXECUTION_ITERATIONS: Final[int] = 100 # Max iterations for streaming execution
46
+
47
+ # Planning defaults
48
+ DEFAULT_COMPLEXITY_HINT: Final[str] = "moderate" # Default complexity hint for planning
49
+
50
+ # ------------------------------------------------------------------------------
51
+ # Hosts & Paths
52
+ # ------------------------------------------------------------------------------
53
+
54
+ PROD_CLOUD_HOST: Final[str] = "cloud.arcade.dev"
55
+ PROD_ENGINE_HOST: Final[str] = "api.arcade.dev"
56
+ LOCALHOST: Final[str] = "localhost"
57
+
58
+ # Port constants
59
+ DEFAULT_DEV_PORT: Final[int] = 9099 # Default port for localhost development
60
+ DEFAULT_CALLBACK_PORT: Final[int] = 9905 # Port for OAuth callback server
61
+ DEFAULT_LOGIN_PORT: Final[int] = 8000 # Default port for login endpoint
62
+
63
+ # Output truncation limits (line length, not total characters)
64
+ MAX_DEBUG_OUTPUT_LENGTH: Final[int] = 500 # Max chars per line for debug/log output
65
+ MAX_DISPLAY_LINE_LENGTH: Final[int] = 100 # Content width (10-char margins on 120-char terminal)
66
+ MAX_ERROR_LINE_LENGTH: Final[int] = 100 # Max chars per line for error messages
67
+
68
+ # Tool display configuration
69
+ DEFAULT_PANEL_WIDTH: Final[int] = 100 # Width for tool result panels (matches content width)
70
+ DEFAULT_MAX_LINES: Final[int] = 10 # Default max lines for tool result display
71
+
72
+ # Output formatting
73
+ OUTPUT_MAX_WIDTH: Final[int] = 100 # Max content width (120 terminal - 10 margin each side)
74
+ OUTPUT_MARGIN: Final[int] = 10 # Margin on each side of terminal
75
+
76
+ # UI Style configuration
77
+ UI_STYLE: Final[str] = os.environ.get("CADE_UI_STYLE", "minimal") # Options: "minimal", "panels"
78
+
79
+ ARCADE_CONFIG_PATH: Final[str] = os.path.join(
80
+ os.path.expanduser(os.getenv("ARCADE_WORK_DIR", "~")), ".arcade"
81
+ )
82
+ CREDENTIALS_FILE_PATH: Final[str] = os.path.join(ARCADE_CONFIG_PATH, "credentials.yaml")
83
+
84
+ # ------------------------------------------------------------------------------
85
+ # Template Paths
86
+ # ------------------------------------------------------------------------------
87
+
88
+ TEMPLATES_DIR: Final[pathlib.Path] = pathlib.Path(__file__).parent.parent / "templates"
89
+ LOGIN_SUCCESS_TEMPLATE: Final[str] = "login_success.html"
90
+ LOGIN_FAILED_TEMPLATE: Final[str] = "login_failed.html"
91
+
92
+
93
+ def get_template_path(template_name: str) -> pathlib.Path:
94
+ """Get the full path to a template file."""
95
+ return TEMPLATES_DIR / template_name
96
+
97
+
98
+ def load_template(template_name: str) -> bytes:
99
+ """Load a template file and return its contents as bytes."""
100
+ template_path = get_template_path(template_name)
101
+ return template_path.read_bytes()
102
+
103
+
104
+ # ------------------------------------------------------------------------------
105
+ # Code Analysis & File Patterns
106
+ # ------------------------------------------------------------------------------
107
+
108
+ DEFAULT_IGNORE_PATTERNS: Final[list[str]] = [
109
+ # Build & distribution
110
+ "node_modules",
111
+ "dist",
112
+ "build",
113
+ "*.egg-info",
114
+ # Version control
115
+ ".git",
116
+ # Editors & IDEs
117
+ ".vscode",
118
+ ".idea",
119
+ ".cursor",
120
+ ".claude",
121
+ # OS files
122
+ ".DS_Store",
123
+ "Thumbs.db",
124
+ # Testing & coverage
125
+ "coverage",
126
+ ".pytest_cache",
127
+ ".ruff_cache",
128
+ ".mypy_cache",
129
+ "htmlcov",
130
+ ".coverage",
131
+ # Virtual environments
132
+ ".venv",
133
+ "venv",
134
+ "env",
135
+ # Environment & secrets
136
+ ".env",
137
+ ".env.local",
138
+ ".env.*.local",
139
+ # Compiled & bundled files
140
+ "*.min.js",
141
+ "*.bundle.js",
142
+ "*.map",
143
+ "*.pyc",
144
+ "*.pyo",
145
+ "*.pyd",
146
+ "*.so",
147
+ "*.dylib",
148
+ "__pycache__",
149
+ ]
150
+
151
+ EXTENSION_TO_LANGUAGE: Final[dict[str, str]] = {
152
+ "ts": "TypeScript",
153
+ "tsx": "TypeScript (React)",
154
+ "js": "JavaScript",
155
+ "jsx": "JavaScript (React)",
156
+ "py": "Python",
157
+ "java": "Java",
158
+ "c": "C",
159
+ "cpp": "C++",
160
+ "cs": "C#",
161
+ "go": "Go",
162
+ "rs": "Rust",
163
+ "php": "PHP",
164
+ "rb": "Ruby",
165
+ "swift": "Swift",
166
+ "kt": "Kotlin",
167
+ "scala": "Scala",
168
+ "html": "HTML",
169
+ "css": "CSS",
170
+ "scss": "SCSS",
171
+ "less": "Less",
172
+ "json": "JSON",
173
+ "md": "Markdown",
174
+ "yml": "YAML",
175
+ "yaml": "YAML",
176
+ "xml": "XML",
177
+ "sql": "SQL",
178
+ "sh": "Shell",
179
+ "bat": "Batch",
180
+ "ps1": "PowerShell",
181
+ "dockerfile": "Dockerfile",
182
+ "tf": "Terraform",
183
+ "hcl": "HCL",
184
+ }
185
+
186
+ CODE_EXTENSIONS: Final[set[str]] = {
187
+ "js",
188
+ "jsx",
189
+ "ts",
190
+ "tsx",
191
+ "py",
192
+ "java",
193
+ "c",
194
+ "cpp",
195
+ "cs",
196
+ "go",
197
+ "rs",
198
+ "php",
199
+ "rb",
200
+ "swift",
201
+ "kt",
202
+ "scala",
203
+ }
204
+
205
+ PYTHON_STDLIB_MODULES: Final[set[str]] = {
206
+ "os",
207
+ "sys",
208
+ "re",
209
+ "math",
210
+ "datetime",
211
+ "time",
212
+ "random",
213
+ "json",
214
+ "csv",
215
+ "collections",
216
+ "itertools",
217
+ "functools",
218
+ "pathlib",
219
+ "shutil",
220
+ "glob",
221
+ "pickle",
222
+ "urllib",
223
+ "http",
224
+ "logging",
225
+ "argparse",
226
+ "unittest",
227
+ "subprocess",
228
+ "threading",
229
+ "multiprocessing",
230
+ "typing",
231
+ "enum",
232
+ "io",
233
+ "tempfile",
234
+ "contextlib",
235
+ "decimal",
236
+ "fractions",
237
+ "statistics",
238
+ "asyncio",
239
+ "concurrent",
240
+ "socket",
241
+ "ssl",
242
+ "select",
243
+ "signal",
244
+ "struct",
245
+ "zlib",
246
+ "gzip",
247
+ "bz2",
248
+ "lzma",
249
+ "tarfile",
250
+ "zipfile",
251
+ "calendar",
252
+ "copy",
253
+ "pprint",
254
+ "base64",
255
+ "hashlib",
256
+ "hmac",
257
+ "secrets",
258
+ "uuid",
259
+ "xml",
260
+ "gettext",
261
+ "locale",
262
+ "platform",
263
+ "importlib",
264
+ "inspect",
265
+ "pdb",
266
+ "profile",
267
+ "traceback",
268
+ "warnings",
269
+ "weakref",
270
+ "abc",
271
+ "numbers",
272
+ "operator",
273
+ "heapq",
274
+ "bisect",
275
+ "array",
276
+ "queue",
277
+ "dataclasses",
278
+ "graphlib",
279
+ "sched",
280
+ "zoneinfo",
281
+ }