lucidscan 0.5.12__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 (91) hide show
  1. lucidscan/__init__.py +12 -0
  2. lucidscan/bootstrap/__init__.py +26 -0
  3. lucidscan/bootstrap/paths.py +160 -0
  4. lucidscan/bootstrap/platform.py +111 -0
  5. lucidscan/bootstrap/validation.py +76 -0
  6. lucidscan/bootstrap/versions.py +119 -0
  7. lucidscan/cli/__init__.py +50 -0
  8. lucidscan/cli/__main__.py +8 -0
  9. lucidscan/cli/arguments.py +405 -0
  10. lucidscan/cli/commands/__init__.py +64 -0
  11. lucidscan/cli/commands/autoconfigure.py +294 -0
  12. lucidscan/cli/commands/help.py +69 -0
  13. lucidscan/cli/commands/init.py +656 -0
  14. lucidscan/cli/commands/list_scanners.py +59 -0
  15. lucidscan/cli/commands/scan.py +307 -0
  16. lucidscan/cli/commands/serve.py +142 -0
  17. lucidscan/cli/commands/status.py +84 -0
  18. lucidscan/cli/commands/validate.py +105 -0
  19. lucidscan/cli/config_bridge.py +152 -0
  20. lucidscan/cli/exit_codes.py +17 -0
  21. lucidscan/cli/runner.py +284 -0
  22. lucidscan/config/__init__.py +29 -0
  23. lucidscan/config/ignore.py +178 -0
  24. lucidscan/config/loader.py +431 -0
  25. lucidscan/config/models.py +316 -0
  26. lucidscan/config/validation.py +645 -0
  27. lucidscan/core/__init__.py +3 -0
  28. lucidscan/core/domain_runner.py +463 -0
  29. lucidscan/core/git.py +174 -0
  30. lucidscan/core/logging.py +34 -0
  31. lucidscan/core/models.py +207 -0
  32. lucidscan/core/streaming.py +340 -0
  33. lucidscan/core/subprocess_runner.py +164 -0
  34. lucidscan/detection/__init__.py +21 -0
  35. lucidscan/detection/detector.py +154 -0
  36. lucidscan/detection/frameworks.py +270 -0
  37. lucidscan/detection/languages.py +328 -0
  38. lucidscan/detection/tools.py +229 -0
  39. lucidscan/generation/__init__.py +15 -0
  40. lucidscan/generation/config_generator.py +275 -0
  41. lucidscan/generation/package_installer.py +330 -0
  42. lucidscan/mcp/__init__.py +20 -0
  43. lucidscan/mcp/formatter.py +510 -0
  44. lucidscan/mcp/server.py +297 -0
  45. lucidscan/mcp/tools.py +1049 -0
  46. lucidscan/mcp/watcher.py +237 -0
  47. lucidscan/pipeline/__init__.py +17 -0
  48. lucidscan/pipeline/executor.py +187 -0
  49. lucidscan/pipeline/parallel.py +181 -0
  50. lucidscan/plugins/__init__.py +40 -0
  51. lucidscan/plugins/coverage/__init__.py +28 -0
  52. lucidscan/plugins/coverage/base.py +160 -0
  53. lucidscan/plugins/coverage/coverage_py.py +454 -0
  54. lucidscan/plugins/coverage/istanbul.py +411 -0
  55. lucidscan/plugins/discovery.py +107 -0
  56. lucidscan/plugins/enrichers/__init__.py +61 -0
  57. lucidscan/plugins/enrichers/base.py +63 -0
  58. lucidscan/plugins/linters/__init__.py +26 -0
  59. lucidscan/plugins/linters/base.py +125 -0
  60. lucidscan/plugins/linters/biome.py +448 -0
  61. lucidscan/plugins/linters/checkstyle.py +393 -0
  62. lucidscan/plugins/linters/eslint.py +368 -0
  63. lucidscan/plugins/linters/ruff.py +498 -0
  64. lucidscan/plugins/reporters/__init__.py +45 -0
  65. lucidscan/plugins/reporters/base.py +30 -0
  66. lucidscan/plugins/reporters/json_reporter.py +79 -0
  67. lucidscan/plugins/reporters/sarif_reporter.py +303 -0
  68. lucidscan/plugins/reporters/summary_reporter.py +61 -0
  69. lucidscan/plugins/reporters/table_reporter.py +81 -0
  70. lucidscan/plugins/scanners/__init__.py +57 -0
  71. lucidscan/plugins/scanners/base.py +60 -0
  72. lucidscan/plugins/scanners/checkov.py +484 -0
  73. lucidscan/plugins/scanners/opengrep.py +464 -0
  74. lucidscan/plugins/scanners/trivy.py +492 -0
  75. lucidscan/plugins/test_runners/__init__.py +27 -0
  76. lucidscan/plugins/test_runners/base.py +111 -0
  77. lucidscan/plugins/test_runners/jest.py +381 -0
  78. lucidscan/plugins/test_runners/karma.py +481 -0
  79. lucidscan/plugins/test_runners/playwright.py +434 -0
  80. lucidscan/plugins/test_runners/pytest.py +598 -0
  81. lucidscan/plugins/type_checkers/__init__.py +27 -0
  82. lucidscan/plugins/type_checkers/base.py +106 -0
  83. lucidscan/plugins/type_checkers/mypy.py +355 -0
  84. lucidscan/plugins/type_checkers/pyright.py +313 -0
  85. lucidscan/plugins/type_checkers/typescript.py +280 -0
  86. lucidscan-0.5.12.dist-info/METADATA +242 -0
  87. lucidscan-0.5.12.dist-info/RECORD +91 -0
  88. lucidscan-0.5.12.dist-info/WHEEL +5 -0
  89. lucidscan-0.5.12.dist-info/entry_points.txt +34 -0
  90. lucidscan-0.5.12.dist-info/licenses/LICENSE +201 -0
  91. lucidscan-0.5.12.dist-info/top_level.txt +1 -0
@@ -0,0 +1,328 @@
1
+ """Language detection module.
2
+
3
+ Detects programming languages in a project by analyzing:
4
+ - File extensions
5
+ - Marker files (package.json, pyproject.toml, go.mod, etc.)
6
+ - Configuration files
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+ # Directories to skip during detection
17
+ SKIP_DIRS = {
18
+ ".git",
19
+ ".svn",
20
+ ".hg",
21
+ "node_modules",
22
+ "__pycache__",
23
+ ".venv",
24
+ "venv",
25
+ ".env",
26
+ "env",
27
+ ".tox",
28
+ ".pytest_cache",
29
+ ".mypy_cache",
30
+ ".ruff_cache",
31
+ "dist",
32
+ "build",
33
+ "target",
34
+ "vendor",
35
+ ".next",
36
+ ".nuxt",
37
+ "coverage",
38
+ ".coverage",
39
+ "htmlcov",
40
+ }
41
+
42
+ # File extension to language mapping
43
+ EXTENSION_MAP = {
44
+ ".py": "python",
45
+ ".pyw": "python",
46
+ ".pyi": "python",
47
+ ".js": "javascript",
48
+ ".mjs": "javascript",
49
+ ".cjs": "javascript",
50
+ ".jsx": "javascript",
51
+ ".ts": "typescript",
52
+ ".tsx": "typescript",
53
+ ".mts": "typescript",
54
+ ".cts": "typescript",
55
+ ".go": "go",
56
+ ".rs": "rust",
57
+ ".java": "java",
58
+ ".kt": "kotlin",
59
+ ".kts": "kotlin",
60
+ ".scala": "scala",
61
+ ".rb": "ruby",
62
+ ".php": "php",
63
+ ".cs": "csharp",
64
+ ".swift": "swift",
65
+ ".c": "c",
66
+ ".h": "c",
67
+ ".cpp": "cpp",
68
+ ".cc": "cpp",
69
+ ".cxx": "cpp",
70
+ ".hpp": "cpp",
71
+ }
72
+
73
+ # Marker files that indicate a language
74
+ MARKER_FILES = {
75
+ "python": ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile"],
76
+ "javascript": ["package.json"],
77
+ "typescript": ["tsconfig.json"],
78
+ "go": ["go.mod"],
79
+ "rust": ["Cargo.toml"],
80
+ "java": ["pom.xml", "build.gradle", "build.gradle.kts"],
81
+ "ruby": ["Gemfile"],
82
+ "php": ["composer.json"],
83
+ }
84
+
85
+
86
+ @dataclass
87
+ class LanguageInfo:
88
+ """Information about a detected language."""
89
+
90
+ name: str
91
+ """Language name (lowercase)."""
92
+
93
+ version: Optional[str] = None
94
+ """Detected version (if available)."""
95
+
96
+ file_count: int = 0
97
+ """Number of files with this language."""
98
+
99
+
100
+ def detect_languages(project_root: Path) -> list[LanguageInfo]:
101
+ """Detect programming languages in a project.
102
+
103
+ Args:
104
+ project_root: Path to the project root directory.
105
+
106
+ Returns:
107
+ List of detected languages with metadata.
108
+ """
109
+ # Count files by extension
110
+ extension_counts: dict[str, int] = {}
111
+
112
+ for file_path in _walk_files(project_root):
113
+ ext = file_path.suffix.lower()
114
+ if ext in EXTENSION_MAP:
115
+ lang = EXTENSION_MAP[ext]
116
+ extension_counts[lang] = extension_counts.get(lang, 0) + 1
117
+
118
+ # Check for marker files
119
+ marker_languages = set()
120
+ for lang, markers in MARKER_FILES.items():
121
+ for marker in markers:
122
+ if (project_root / marker).exists():
123
+ marker_languages.add(lang)
124
+ break
125
+
126
+ # Combine results
127
+ all_languages = set(extension_counts.keys()) | marker_languages
128
+
129
+ # Build LanguageInfo objects
130
+ results = []
131
+ for lang in all_languages:
132
+ info = LanguageInfo(
133
+ name=lang,
134
+ file_count=extension_counts.get(lang, 0),
135
+ version=_detect_version(lang, project_root),
136
+ )
137
+ results.append(info)
138
+
139
+ # Sort by file count (descending)
140
+ results.sort(key=lambda x: x.file_count, reverse=True)
141
+
142
+ return results
143
+
144
+
145
+ def _walk_files(root: Path, max_depth: int = 10) -> list[Path]:
146
+ """Walk directory tree yielding files.
147
+
148
+ Args:
149
+ root: Root directory to walk.
150
+ max_depth: Maximum recursion depth.
151
+
152
+ Returns:
153
+ List of file paths.
154
+ """
155
+ files = []
156
+
157
+ def _walk(path: Path, depth: int) -> None:
158
+ if depth > max_depth:
159
+ return
160
+
161
+ try:
162
+ for item in path.iterdir():
163
+ if item.is_dir():
164
+ if item.name not in SKIP_DIRS and not item.name.startswith("."):
165
+ _walk(item, depth + 1)
166
+ elif item.is_file():
167
+ files.append(item)
168
+ except PermissionError:
169
+ pass
170
+
171
+ _walk(root, 0)
172
+ return files
173
+
174
+
175
+ def _detect_version(language: str, project_root: Path) -> Optional[str]:
176
+ """Detect the version of a language from config files.
177
+
178
+ Args:
179
+ language: Language name.
180
+ project_root: Project root directory.
181
+
182
+ Returns:
183
+ Version string or None.
184
+ """
185
+ if language == "python":
186
+ return _detect_python_version(project_root)
187
+ elif language == "typescript":
188
+ return _detect_typescript_version(project_root)
189
+ elif language == "go":
190
+ return _detect_go_version(project_root)
191
+ elif language == "rust":
192
+ return _detect_rust_version(project_root)
193
+ elif language == "java":
194
+ return _detect_java_version(project_root)
195
+ return None
196
+
197
+
198
+ def _detect_python_version(project_root: Path) -> Optional[str]:
199
+ """Detect Python version from pyproject.toml or other files."""
200
+ pyproject = project_root / "pyproject.toml"
201
+ if pyproject.exists():
202
+ try:
203
+ content = pyproject.read_text()
204
+ # Look for requires-python
205
+ match = re.search(r'requires-python\s*=\s*["\']([^"\']+)["\']', content)
206
+ if match:
207
+ version_spec = match.group(1)
208
+ # Extract version number (e.g., ">=3.10" -> "3.10")
209
+ version_match = re.search(r"(\d+\.\d+)", version_spec)
210
+ if version_match:
211
+ return version_match.group(1)
212
+ except Exception:
213
+ pass
214
+
215
+ # Check .python-version file
216
+ python_version_file = project_root / ".python-version"
217
+ if python_version_file.exists():
218
+ try:
219
+ version = python_version_file.read_text().strip()
220
+ return version.split(".")[0] + "." + version.split(".")[1] if "." in version else version
221
+ except Exception:
222
+ pass
223
+
224
+ return None
225
+
226
+
227
+ def _detect_typescript_version(project_root: Path) -> Optional[str]:
228
+ """Detect TypeScript version from package.json."""
229
+ package_json = project_root / "package.json"
230
+ if package_json.exists():
231
+ try:
232
+ import json
233
+ data = json.loads(package_json.read_text())
234
+ deps = {**data.get("dependencies", {}), **data.get("devDependencies", {})}
235
+ if "typescript" in deps:
236
+ version = deps["typescript"]
237
+ # Strip version prefix (^, ~, etc.)
238
+ return re.sub(r"^[\^~>=<]+", "", version)
239
+ except Exception:
240
+ pass
241
+ return None
242
+
243
+
244
+ def _detect_go_version(project_root: Path) -> Optional[str]:
245
+ """Detect Go version from go.mod."""
246
+ go_mod = project_root / "go.mod"
247
+ if go_mod.exists():
248
+ try:
249
+ content = go_mod.read_text()
250
+ match = re.search(r"^go\s+(\d+\.\d+)", content, re.MULTILINE)
251
+ if match:
252
+ return match.group(1)
253
+ except Exception:
254
+ pass
255
+ return None
256
+
257
+
258
+ def _detect_rust_version(project_root: Path) -> Optional[str]:
259
+ """Detect Rust edition from Cargo.toml."""
260
+ cargo_toml = project_root / "Cargo.toml"
261
+ if cargo_toml.exists():
262
+ try:
263
+ content = cargo_toml.read_text()
264
+ match = re.search(r'edition\s*=\s*["\'](\d+)["\']', content)
265
+ if match:
266
+ return match.group(1)
267
+ except Exception:
268
+ pass
269
+ return None
270
+
271
+
272
+ def _detect_java_version(project_root: Path) -> Optional[str]:
273
+ """Detect Java version from pom.xml or build.gradle."""
274
+ # Check pom.xml (Maven)
275
+ pom_xml = project_root / "pom.xml"
276
+ if pom_xml.exists():
277
+ try:
278
+ content = pom_xml.read_text()
279
+ # Look for maven.compiler.source or java.version property
280
+ match = re.search(
281
+ r"<(?:maven\.compiler\.source|java\.version)>(\d+)</",
282
+ content,
283
+ )
284
+ if match:
285
+ return match.group(1)
286
+ # Look for release property
287
+ match = re.search(r"<release>(\d+)</release>", content)
288
+ if match:
289
+ return match.group(1)
290
+ except Exception:
291
+ pass
292
+
293
+ # Check build.gradle (Gradle)
294
+ for gradle_file in ["build.gradle", "build.gradle.kts"]:
295
+ gradle_path = project_root / gradle_file
296
+ if gradle_path.exists():
297
+ try:
298
+ content = gradle_path.read_text()
299
+ # Look for sourceCompatibility or targetCompatibility
300
+ match = re.search(
301
+ r"(?:source|target)Compatibility\s*=\s*['\"]?(?:JavaVersion\.VERSION_)?(\d+)",
302
+ content,
303
+ )
304
+ if match:
305
+ return match.group(1)
306
+ # Look for toolchain languageVersion
307
+ match = re.search(
308
+ r"languageVersion\.set\s*\(\s*JavaLanguageVersion\.of\s*\(\s*(\d+)\s*\)",
309
+ content,
310
+ )
311
+ if match:
312
+ return match.group(1)
313
+ except Exception:
314
+ pass
315
+
316
+ # Check .java-version file
317
+ java_version_file = project_root / ".java-version"
318
+ if java_version_file.exists():
319
+ try:
320
+ version = java_version_file.read_text().strip()
321
+ # Extract major version (e.g., "17.0.2" -> "17")
322
+ match = re.match(r"(\d+)", version)
323
+ if match:
324
+ return match.group(1)
325
+ except Exception:
326
+ pass
327
+
328
+ return None
@@ -0,0 +1,229 @@
1
+ """Tool configuration detection module.
2
+
3
+ Detects existing tool configurations for:
4
+ - Linters (Ruff, ESLint, Biome, etc.)
5
+ - Type checkers (mypy, TypeScript, Pyright, etc.)
6
+ - Formatters (Black, Prettier, etc.)
7
+ - Security scanners (Trivy, etc.)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Any, Dict, Optional
16
+
17
+
18
+ @dataclass
19
+ class ToolConfig:
20
+ """Detected tool configuration."""
21
+
22
+ tool: str
23
+ """Tool name (e.g., 'ruff', 'eslint')."""
24
+
25
+ config_file: Optional[Path] = None
26
+ """Path to the configuration file."""
27
+
28
+ config_location: Optional[str] = None
29
+ """Location type: 'file', 'pyproject.toml', 'package.json'."""
30
+
31
+
32
+ # Tool detection definitions
33
+ # Format: tool_name -> (config_files, pyproject_section, package_json_key)
34
+ TOOL_CONFIGS: Dict[str, Dict[str, Any]] = {
35
+ # Python linters
36
+ "ruff": {
37
+ "files": ["ruff.toml", ".ruff.toml"],
38
+ "pyproject_section": "tool.ruff",
39
+ },
40
+ "flake8": {
41
+ "files": [".flake8", "setup.cfg", "tox.ini"],
42
+ "pyproject_section": None, # flake8 doesn't use pyproject.toml natively
43
+ },
44
+ "pylint": {
45
+ "files": [".pylintrc", "pylintrc"],
46
+ "pyproject_section": "tool.pylint",
47
+ },
48
+ "black": {
49
+ "files": [".black.toml"],
50
+ "pyproject_section": "tool.black",
51
+ },
52
+ "isort": {
53
+ "files": [".isort.cfg"],
54
+ "pyproject_section": "tool.isort",
55
+ },
56
+
57
+ # Python type checkers
58
+ "mypy": {
59
+ "files": ["mypy.ini", ".mypy.ini"],
60
+ "pyproject_section": "tool.mypy",
61
+ },
62
+ "pyright": {
63
+ "files": ["pyrightconfig.json"],
64
+ "pyproject_section": "tool.pyright",
65
+ },
66
+
67
+ # Python test tools
68
+ "pytest": {
69
+ "files": ["pytest.ini", "setup.cfg"],
70
+ "pyproject_section": "tool.pytest",
71
+ },
72
+ "coverage": {
73
+ "files": [".coveragerc"],
74
+ "pyproject_section": "tool.coverage",
75
+ },
76
+
77
+ # JavaScript/TypeScript linters
78
+ "eslint": {
79
+ "files": [
80
+ ".eslintrc", ".eslintrc.js", ".eslintrc.cjs", ".eslintrc.mjs",
81
+ ".eslintrc.json", ".eslintrc.yaml", ".eslintrc.yml",
82
+ "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs",
83
+ ],
84
+ "package_json_key": "eslintConfig",
85
+ },
86
+ "biome": {
87
+ "files": ["biome.json", "biome.jsonc"],
88
+ },
89
+ "prettier": {
90
+ "files": [
91
+ ".prettierrc", ".prettierrc.json", ".prettierrc.yaml",
92
+ ".prettierrc.yml", ".prettierrc.js", ".prettierrc.cjs",
93
+ "prettier.config.js", "prettier.config.cjs",
94
+ ],
95
+ "package_json_key": "prettier",
96
+ },
97
+
98
+ # TypeScript
99
+ "typescript": {
100
+ "files": ["tsconfig.json"],
101
+ },
102
+
103
+ # Security scanners
104
+ "trivy": {
105
+ "files": [".trivy.yaml", "trivy.yaml"],
106
+ },
107
+ "semgrep": {
108
+ "files": [".semgrep.yaml", ".semgrep.yml", "semgrep.yaml"],
109
+ },
110
+ "checkov": {
111
+ "files": [".checkov.yaml", ".checkov.yml"],
112
+ },
113
+
114
+ # JavaScript/TypeScript test runners
115
+ "jest": {
116
+ "files": ["jest.config.js", "jest.config.ts", "jest.config.mjs", "jest.config.cjs"],
117
+ "package_json_key": "jest",
118
+ },
119
+ "karma": {
120
+ "files": ["karma.conf.js", "karma.conf.ts", "karma.config.js", "karma.config.ts"],
121
+ },
122
+ "playwright": {
123
+ "files": ["playwright.config.js", "playwright.config.ts", "playwright.config.mjs"],
124
+ },
125
+
126
+ # Other
127
+ "pre-commit": {
128
+ "files": [".pre-commit-config.yaml"],
129
+ },
130
+ }
131
+
132
+
133
+ def detect_tools(project_root: Path) -> dict[str, ToolConfig]:
134
+ """Detect existing tool configurations in a project.
135
+
136
+ Args:
137
+ project_root: Path to the project root directory.
138
+
139
+ Returns:
140
+ Dictionary mapping tool names to their configurations.
141
+ """
142
+ detected = {}
143
+
144
+ # Load pyproject.toml if it exists
145
+ pyproject_content = None
146
+ pyproject_path = project_root / "pyproject.toml"
147
+ if pyproject_path.exists():
148
+ try:
149
+ pyproject_content = pyproject_path.read_text()
150
+ except Exception:
151
+ pass
152
+
153
+ # Load package.json if it exists
154
+ package_json_content = None
155
+ package_json_path = project_root / "package.json"
156
+ if package_json_path.exists():
157
+ try:
158
+ import json
159
+ package_json_content = json.loads(package_json_path.read_text())
160
+ except Exception:
161
+ pass
162
+
163
+ # Check each tool
164
+ for tool_name, config in TOOL_CONFIGS.items():
165
+ result = _check_tool(
166
+ tool_name,
167
+ config,
168
+ project_root,
169
+ pyproject_content,
170
+ package_json_content,
171
+ )
172
+ if result:
173
+ detected[tool_name] = result
174
+
175
+ return detected
176
+
177
+
178
+ def _check_tool(
179
+ tool_name: str,
180
+ config: dict,
181
+ project_root: Path,
182
+ pyproject_content: Optional[str],
183
+ package_json_content: Optional[dict],
184
+ ) -> Optional[ToolConfig]:
185
+ """Check if a specific tool is configured.
186
+
187
+ Args:
188
+ tool_name: Name of the tool.
189
+ config: Tool configuration definition.
190
+ project_root: Project root directory.
191
+ pyproject_content: Content of pyproject.toml (if exists).
192
+ package_json_content: Parsed package.json (if exists).
193
+
194
+ Returns:
195
+ ToolConfig if found, None otherwise.
196
+ """
197
+ # Check for standalone config files
198
+ for config_file in config.get("files", []):
199
+ file_path = project_root / config_file
200
+ if file_path.exists():
201
+ return ToolConfig(
202
+ tool=tool_name,
203
+ config_file=file_path,
204
+ config_location="file",
205
+ )
206
+
207
+ # Check pyproject.toml section
208
+ pyproject_section = config.get("pyproject_section")
209
+ if pyproject_section and pyproject_content:
210
+ # Convert section to regex pattern (e.g., "tool.ruff" -> [tool.ruff])
211
+ section_pattern = pyproject_section.replace(".", r"\.")
212
+ if re.search(rf"\[{section_pattern}(\.[^\]]+)?\]", pyproject_content):
213
+ return ToolConfig(
214
+ tool=tool_name,
215
+ config_file=project_root / "pyproject.toml",
216
+ config_location="pyproject.toml",
217
+ )
218
+
219
+ # Check package.json key
220
+ package_key = config.get("package_json_key")
221
+ if package_key and package_json_content:
222
+ if package_key in package_json_content:
223
+ return ToolConfig(
224
+ tool=tool_name,
225
+ config_file=project_root / "package.json",
226
+ config_location="package.json",
227
+ )
228
+
229
+ return None
@@ -0,0 +1,15 @@
1
+ """Configuration generation module.
2
+
3
+ This module provides generators for:
4
+ - lucidscan.yml configuration files
5
+ - Package manager tool installation
6
+ """
7
+
8
+ from lucidscan.generation.config_generator import ConfigGenerator, InitChoices
9
+ from lucidscan.generation.package_installer import PackageInstaller
10
+
11
+ __all__ = [
12
+ "ConfigGenerator",
13
+ "InitChoices",
14
+ "PackageInstaller",
15
+ ]