skylos 0.0.1__cp313-cp313-macosx_11_0_arm64.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 skylos might be problematic. Click here for more details.

skylos/__init__.py ADDED
@@ -0,0 +1,30 @@
1
+ from importlib import metadata as _md
2
+
3
+ try:
4
+ __version__ = _md.version(__name__)
5
+ except _md.PackageNotFoundError:
6
+ __version__ = "0.0.0.dev0"
7
+
8
+
9
+ from ._core import analyze as _analyze
10
+
11
+ def analyze(path: str) -> str:
12
+ """Return the JSON produced by Rust's ``analyze``."""
13
+ return _analyze(path)
14
+
15
+ # ------------------------------------------------------------------ #
16
+ # helpers the tests expect
17
+ # ------------------------------------------------------------------ #
18
+ def debug_test() -> str:
19
+ return "debug-ok"
20
+
21
+ __all__ = ["analyze", "debug_test", "__version__"]
22
+
23
+ def _patch_metadata() -> None:
24
+ orig_version = _md.version
25
+ def _fake_version(name: str): # type: ignore
26
+ if name == __name__:
27
+ return __version__
28
+ return orig_version(name)
29
+ _md.version = _fake_version
30
+ _patch_metadata()
Binary file
skylos/cli.py ADDED
@@ -0,0 +1,332 @@
1
+ import argparse
2
+ import json
3
+ import sys
4
+ import logging
5
+ import ast
6
+ import skylos
7
+
8
+ try:
9
+ import inquirer
10
+ INTERACTIVE_AVAILABLE = True
11
+ except ImportError:
12
+ INTERACTIVE_AVAILABLE = False
13
+
14
+ class Colors:
15
+ RED = '\033[91m'
16
+ GREEN = '\033[92m'
17
+ YELLOW = '\033[93m'
18
+ BLUE = '\033[94m'
19
+ MAGENTA = '\033[95m'
20
+ CYAN = '\033[96m'
21
+ WHITE = '\033[97m'
22
+ BOLD = '\033[1m'
23
+ UNDERLINE = '\033[4m'
24
+ RESET = '\033[0m'
25
+ GRAY = '\033[90m'
26
+
27
+ class ColoredFormatter(logging.Formatter):
28
+ def format(self, record):
29
+ return super().format(record)
30
+
31
+ def setup_logger(output_file=None):
32
+ logger = logging.getLogger('skylos')
33
+ logger.setLevel(logging.INFO)
34
+
35
+ logger.handlers.clear()
36
+
37
+ formatter = ColoredFormatter('%(message)s')
38
+
39
+ console_handler = logging.StreamHandler(sys.stdout)
40
+ console_handler.setFormatter(formatter)
41
+ logger.addHandler(console_handler)
42
+
43
+ if output_file:
44
+ file_handler = logging.FileHandler(output_file)
45
+ file_handler.setFormatter(formatter)
46
+ logger.addHandler(file_handler)
47
+
48
+ return logger
49
+
50
+ def remove_unused_import(file_path: str, import_name: str, line_number: int) -> bool:
51
+ try:
52
+ with open(file_path, 'r') as f:
53
+ lines = f.readlines()
54
+
55
+ line_idx = line_number - 1
56
+ original_line = lines[line_idx].strip()
57
+
58
+ if original_line.startswith(f'import {import_name}'):
59
+ lines[line_idx] = ''
60
+ elif original_line.startswith('import ') and f' {import_name}' in original_line:
61
+ parts = original_line.split(' ', 1)[1].split(',')
62
+ new_parts = [p.strip() for p in parts if p.strip() != import_name]
63
+ if new_parts:
64
+ lines[line_idx] = f'import {", ".join(new_parts)}\n'
65
+ else:
66
+ lines[line_idx] = ''
67
+ elif original_line.startswith('from ') and import_name in original_line:
68
+ if f'import {import_name}' in original_line and ',' not in original_line:
69
+ lines[line_idx] = ''
70
+ else:
71
+ parts = original_line.split('import ', 1)[1].split(',')
72
+ new_parts = [p.strip() for p in parts if p.strip() != import_name]
73
+ if new_parts:
74
+ prefix = original_line.split(' import ')[0]
75
+ lines[line_idx] = f'{prefix} import {", ".join(new_parts)}\n'
76
+ else:
77
+ lines[line_idx] = ''
78
+
79
+ with open(file_path, 'w') as f:
80
+ f.writelines(lines)
81
+
82
+ return True
83
+ except Exception as e:
84
+ logging.error(f"Failed to remove import {import_name} from {file_path}: {e}")
85
+ return False
86
+
87
+ def remove_unused_function(file_path: str, function_name: str, line_number: int) -> bool:
88
+ try:
89
+ with open(file_path, 'r') as f:
90
+ content = f.read()
91
+
92
+ tree = ast.parse(content)
93
+
94
+ lines = content.splitlines()
95
+ for node in ast.walk(tree):
96
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
97
+ if (node.name in function_name and
98
+ node.lineno == line_number):
99
+
100
+ start_line = node.lineno - 1
101
+
102
+ if node.decorator_list:
103
+ start_line = node.decorator_list[0].lineno - 1
104
+
105
+ end_line = len(lines)
106
+ base_indent = len(lines[start_line]) - len(lines[start_line].lstrip())
107
+
108
+ for i in range(node.end_lineno, len(lines)):
109
+ if lines[i].strip() == '':
110
+ continue
111
+ current_indent = len(lines[i]) - len(lines[i].lstrip())
112
+ if current_indent <= base_indent and lines[i].strip():
113
+ end_line = i
114
+ break
115
+
116
+ while end_line < len(lines) and lines[end_line].strip() == '':
117
+ end_line += 1
118
+
119
+ new_lines = lines[:start_line] + lines[end_line:]
120
+
121
+ with open(file_path, 'w') as f:
122
+ f.write('\n'.join(new_lines) + '\n')
123
+
124
+ return True
125
+
126
+ return False
127
+ except Exception as e:
128
+ logging.error(f"Failed to remove function {function_name} from {file_path}: {e}")
129
+ return False
130
+
131
+ def interactive_selection(logger, unused_functions, unused_imports):
132
+ if not INTERACTIVE_AVAILABLE:
133
+ logger.error("Interactive mode requires 'inquirer' package. Install with: pip install inquirer")
134
+ return [], []
135
+
136
+ selected_functions = []
137
+ selected_imports = []
138
+
139
+ if unused_functions:
140
+ logger.info(f"\n{Colors.CYAN}{Colors.BOLD}Select unused functions to remove:{Colors.RESET}")
141
+
142
+ function_choices = []
143
+ for item in unused_functions:
144
+ choice_text = f"{item['name']} ({item['file']}:{item['line']})"
145
+ function_choices.append((choice_text, item))
146
+
147
+ questions = [
148
+ inquirer.Checkbox('functions',
149
+ message="Select functions to remove",
150
+ choices=function_choices,
151
+ )
152
+ ]
153
+
154
+ answers = inquirer.prompt(questions)
155
+ if answers:
156
+ selected_functions = answers['functions']
157
+
158
+ if unused_imports:
159
+ logger.info(f"\n{Colors.MAGENTA}{Colors.BOLD}Select unused imports to remove:{Colors.RESET}")
160
+
161
+ import_choices = []
162
+ for item in unused_imports:
163
+ choice_text = f"{item['name']} ({item['file']}:{item['line']})"
164
+ import_choices.append((choice_text, item))
165
+
166
+ questions = [
167
+ inquirer.Checkbox('imports',
168
+ message="Select imports to remove",
169
+ choices=import_choices,
170
+ )
171
+ ]
172
+
173
+ answers = inquirer.prompt(questions)
174
+ if answers:
175
+ selected_imports = answers['imports']
176
+
177
+ return selected_functions, selected_imports
178
+
179
+ def main() -> None:
180
+ parser = argparse.ArgumentParser(
181
+ description="Detect unreachable functions and unused imports in a Python project"
182
+ )
183
+ parser.add_argument("path", help="Path to the Python project to analyze")
184
+ parser.add_argument(
185
+ "--json",
186
+ action="store_true",
187
+ help="Output raw JSON instead of formatted text",
188
+ )
189
+ parser.add_argument(
190
+ "--output",
191
+ "-o",
192
+ type=str,
193
+ help="Write output to file instead of stdout",
194
+ )
195
+ parser.add_argument(
196
+ "--verbose", "-v",
197
+ action="store_true",
198
+ help="Enable verbose output"
199
+ )
200
+ parser.add_argument(
201
+ "--interactive", "-i",
202
+ action="store_true",
203
+ help="Interactively select items to remove (requires inquirer)"
204
+ )
205
+ parser.add_argument(
206
+ "--dry-run",
207
+ action="store_true",
208
+ help="Show what would be removed without actually modifying files"
209
+ )
210
+
211
+ args = parser.parse_args()
212
+ logger = setup_logger(args.output)
213
+
214
+ if args.verbose:
215
+ logger.setLevel(logging.DEBUG)
216
+ logger.debug(f"Analyzing path: {args.path}")
217
+
218
+ try:
219
+ result_json = skylos.analyze(args.path)
220
+ result = json.loads(result_json)
221
+ except Exception as e:
222
+ logger.error(f"Error during analysis: {e}")
223
+ sys.exit(1)
224
+
225
+ if args.json:
226
+ logger.info(result_json)
227
+ else:
228
+ unused_functions = result.get("unused_functions", [])
229
+ unused_imports = result.get("unused_imports", [])
230
+
231
+ logger.info(f"\n{Colors.CYAN}{Colors.BOLD}🔍 Python Static Analysis Results{Colors.RESET}")
232
+ logger.info(f"{Colors.CYAN}{'=' * 35}{Colors.RESET}")
233
+
234
+ logger.info(f"\n{Colors.BOLD}Summary:{Colors.RESET}")
235
+ logger.info(f" • Unreachable functions: {Colors.YELLOW}{len(unused_functions)}{Colors.RESET}")
236
+ logger.info(f" • Unused imports: {Colors.YELLOW}{len(unused_imports)}{Colors.RESET}")
237
+
238
+ if args.interactive and (unused_functions or unused_imports):
239
+ logger.info(f"\n{Colors.BOLD}Interactive Mode:{Colors.RESET}")
240
+ selected_functions, selected_imports = interactive_selection(logger, unused_functions, unused_imports)
241
+
242
+ if selected_functions or selected_imports:
243
+ logger.info(f"\n{Colors.BOLD}Selected items to remove:{Colors.RESET}")
244
+
245
+ if selected_functions:
246
+ logger.info(f" Functions: {len(selected_functions)}")
247
+ for func in selected_functions:
248
+ logger.info(f" - {func['name']} ({func['file']}:{func['line']})")
249
+
250
+ if selected_imports:
251
+ logger.info(f" Imports: {len(selected_imports)}")
252
+ for imp in selected_imports:
253
+ logger.info(f" - {imp['name']} ({imp['file']}:{imp['line']})")
254
+
255
+ if not args.dry_run:
256
+ questions = [
257
+ inquirer.Confirm('confirm',
258
+ message="Are you sure you want to remove these items?",
259
+ default=False)
260
+ ]
261
+ answers = inquirer.prompt(questions)
262
+
263
+ if answers and answers['confirm']:
264
+ logger.info(f"\n{Colors.YELLOW}Removing selected items...{Colors.RESET}")
265
+
266
+ for func in selected_functions:
267
+ success = remove_unused_function(func['file'], func['name'], func['line'])
268
+ if success:
269
+ logger.info(f" {Colors.GREEN}✓{Colors.RESET} Removed function: {func['name']}")
270
+ else:
271
+ logger.error(f" {Colors.RED}✗{Colors.RESET} Failed to remove: {func['name']}")
272
+
273
+ for imp in selected_imports:
274
+ success = remove_unused_import(imp['file'], imp['name'], imp['line'])
275
+ if success:
276
+ logger.info(f" {Colors.GREEN}✓{Colors.RESET} Removed import: {imp['name']}")
277
+ else:
278
+ logger.error(f" {Colors.RED}✗{Colors.RESET} Failed to remove: {imp['name']}")
279
+
280
+ logger.info(f"\n{Colors.GREEN}Cleanup complete!{Colors.RESET}")
281
+ else:
282
+ logger.info(f"\n{Colors.YELLOW}Operation cancelled.{Colors.RESET}")
283
+ else:
284
+ logger.info(f"\n{Colors.YELLOW}Dry run - no files were modified.{Colors.RESET}")
285
+ else:
286
+ logger.info(f"\n{Colors.BLUE}No items selected.{Colors.RESET}")
287
+
288
+ else:
289
+ if unused_functions:
290
+ logger.info(f"\n{Colors.RED}{Colors.BOLD}📦 Unreachable Functions{Colors.RESET}")
291
+ logger.info(f"{Colors.RED}{'=' * 23}{Colors.RESET}")
292
+ for i, item in enumerate(unused_functions, 1):
293
+ logger.info(f"{Colors.GRAY}{i:2d}. {Colors.RESET}{Colors.RED}{item['name']}{Colors.RESET}")
294
+ logger.info(f" {Colors.GRAY}└─ {item['file']}:{item['line']}{Colors.RESET}")
295
+ else:
296
+ logger.info(f"\n{Colors.GREEN}✓ All functions are reachable!{Colors.RESET}")
297
+
298
+ if unused_imports:
299
+ logger.info(f"\n{Colors.MAGENTA}{Colors.BOLD}📥 Unused Imports{Colors.RESET}")
300
+ logger.info(f"{Colors.MAGENTA}{'=' * 16}{Colors.RESET}")
301
+ for i, item in enumerate(unused_imports, 1):
302
+ logger.info(f"{Colors.GRAY}{i:2d}. {Colors.RESET}{Colors.MAGENTA}{item['name']}{Colors.RESET}")
303
+ logger.info(f" {Colors.GRAY}└─ {item['file']}:{item['line']}{Colors.RESET}")
304
+ else:
305
+ logger.info(f"\n{Colors.GREEN}✓ All imports are being used!{Colors.RESET}")
306
+
307
+ logger.info(f"\n{Colors.GRAY}{'─' * 50}{Colors.RESET}")
308
+ logger.info(f"{Colors.GRAY}Analysis complete.{Colors.RESET}")
309
+
310
+ logger.info(f"\n{Colors.GRAY}{'─' * 50}{Colors.RESET}")
311
+ logger.info(f"{Colors.GRAY}Analysis complete.{Colors.RESET}")
312
+
313
+ dead_code_count = len(unused_functions) + len(unused_imports)
314
+
315
+ if dead_code_count == 0:
316
+ logger.info(f"\n✨ Your code is 100% dead code free! Add this badge to your README:")
317
+ logger.info("```markdown")
318
+ logger.info("![Dead Code Free](https://img.shields.io/badge/Dead_Code-Free-brightgreen?logo=moleculer&logoColor=white)")
319
+ logger.info("```")
320
+ else:
321
+ logger.info(f"Found {dead_code_count} dead code items. Add this badge to your README:")
322
+ logger.info("```markdown")
323
+ logger.info(f"![Dead Code: {dead_code_count}](https://img.shields.io/badge/Dead_Code-{dead_code_count}_detected-orange?logo=codacy&logoColor=red)")
324
+ logger.info("```")
325
+
326
+ if unused_functions or unused_imports:
327
+ logger.info(f"\n{Colors.BOLD}Next steps:{Colors.RESET}")
328
+ logger.info(f" • Use --interactive to select specific items to remove")
329
+ logger.info(f" • Use --dry-run to preview changes before applying them")
330
+
331
+ if __name__ == "__main__":
332
+ main()
@@ -0,0 +1,295 @@
1
+ Metadata-Version: 2.4
2
+ Name: skylos
3
+ Version: 0.0.1
4
+ Requires-Dist: inquirer>=3.0.0
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
7
+
8
+ # Skylos 🔍
9
+
10
+ ![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)
11
+ ![100% Local](https://img.shields.io/badge/privacy-100%25%20local-brightgreen)
12
+ ![PyPI version](https://img.shields.io/pypi/v/tacz)
13
+ ![100% Dead Code Free](https://img.shields.io/badge/Dead%20Code-100%25%20Free-brightgreen)
14
+
15
+
16
+ > A static analysis tool for Python codebases written in Rust that detects unreachable functions and unused imports, aka dead code.
17
+
18
+ ## Features
19
+
20
+ - **Unreachable Function Detection**: Find functions and methods that are never called aka dead code
21
+ - **Unused Import Detection**: Imports that are never used
22
+ - **High Performance**: Built with Rust
23
+ - **Nice Output**: Colorized CLI output
24
+ - **Interactive Mode**: Select and remove specific items interactively
25
+ - **Dry Run Support**: Preview changes before applying them
26
+ - **Auto-removal**: Auto clean up
27
+ - **Cross-module Analysis**: Tracks imports and calls across your entire project
28
+ - **Nice badges!!**: Pin your badge if you get 100% clean code!
29
+
30
+ ## Benchmark (You can find this benchmark test in `test/sample_project`)
31
+
32
+ | Tool | Time (s) | Functions | Imports | Total |
33
+ |------|----------|-----------|---------|-------|
34
+ | Skylos | 0.039 | 48 | 8 | 56 |
35
+ | Vulture (100%) | 0.040 | 0 | 3 | 3 |
36
+ | Vulture (60%) | 0.041 | 28 | 3 | 31 |
37
+ | Vulture (0%) | 0.041 | 28 | 3 | 31 |
38
+ | Flake8 | 0.274 | 0 | 8 | 8 |
39
+ | Pylint | 0.285 | 0 | 6 | 6 |
40
+ | Dead | 0.035 | 0 | 0 | 0 |
41
+
42
+ ## Installation
43
+
44
+ ### Basic Installation
45
+
46
+ ```bash
47
+ pip install skylos
48
+ ```
49
+
50
+ ### With Interactive Features
51
+
52
+ For the interactive selection mode, install with:
53
+
54
+ ```bash
55
+ pip install skylos[interactive]
56
+ ```
57
+
58
+ ### From Source
59
+
60
+ ```bash
61
+ # Clone the repository
62
+ git clone https://github.com/duriantaco/skylos.git
63
+ cd skylos
64
+
65
+ # Install maturin (if not already installed)
66
+ pip install maturin
67
+
68
+ # Build and install
69
+ maturin develop
70
+ ```
71
+
72
+ ## Quick Start
73
+
74
+ ```bash
75
+ # Analyze a project
76
+ skylos /path/to/your/project
77
+
78
+ # Interactive mode - select items to remove
79
+ skylos /path/to/your/project --interactive
80
+
81
+ # Dry run - see what would be removed
82
+ skylos /path/to/your/project --interactive --dry-run
83
+
84
+ # Output to JSON
85
+ skylos /path/to/your/project --json
86
+ ```
87
+
88
+ ## CLI Options
89
+
90
+ ```
91
+ Usage: skylos [OPTIONS] PATH
92
+
93
+ Arguments:
94
+ PATH Path to the Python project to analyze
95
+
96
+ Options:
97
+ -h, --help Show this help message and exit
98
+ -j, --json Output raw JSON instead of formatted text
99
+ -o, --output FILE Write output to file instead of stdout
100
+ -v, --verbose Enable verbose output
101
+ -i, --interactive Interactively select items to remove
102
+ --dry-run Show what would be removed without modifying files
103
+ ```
104
+
105
+ ## Example Output
106
+
107
+ ```
108
+ 🔍 Python Static Analysis Results
109
+ ===================================
110
+
111
+ Summary:
112
+ • Unreachable functions: 48
113
+ • Unused imports: 8
114
+
115
+ 📦 Unreachable Functions
116
+ ========================
117
+ 1. module_13.test_function
118
+ └─ /Users/oha/project/module_13.py:5
119
+ 2. module_13.unused_function
120
+ └─ /Users/oha/project/module_13.py:13
121
+ ...
122
+
123
+ 📥 Unused Imports
124
+ =================
125
+ 1. os
126
+ └─ /Users/oha/project/module_13.py:1
127
+ 2. json
128
+ └─ /Users/oha/project/module_13.py:3
129
+ ...
130
+
131
+ Next steps:
132
+ • Use --interactive to select specific items to remove
133
+ • Use --dry-run to preview changes before applying them
134
+ ```
135
+
136
+ ## Interactive Mode
137
+
138
+ The interactive mode lets you select specific functions and imports to remove:
139
+
140
+ ![Interactive Demo](docs/interactive-demo.gif)
141
+
142
+ 1. **Select items**: Use arrow keys and space to select/deselect
143
+ 2. **Confirm changes**: Review selected items before applying
144
+ 3. **Auto-cleanup**: Files are automatically updated
145
+
146
+ ## Architecture
147
+
148
+ Skylos uses a hybrid architecture combining Rust and Python:
149
+
150
+ - **Rust Core**: Fast tree-sitter based parsing and analysis
151
+ - **Python CLI**: User-friendly interface and file manipulation
152
+ - **maturin**: Seamless Python-Rust integration
153
+
154
+ ### Core Components
155
+
156
+ ```
157
+ skylos/
158
+ ├── src/lib.rs # Main Rust analysis engine
159
+ ├── src/queries.rs # Tree-sitter query definitions
160
+ ├── src/types.rs # Data structures for results
161
+ ├── src/utils.rs # Helper functions
162
+ ├── skylos/
163
+ │ └── cli.py # Python CLI interface
164
+ └── pyproject.toml # Project configuration
165
+ ```
166
+
167
+ ## Development
168
+
169
+ ### Prerequisites
170
+
171
+ - Python ≥3.8
172
+ - Rust and Cargo
173
+ - maturin
174
+
175
+ ### Setup
176
+
177
+ ```bash
178
+ # Clone the repository
179
+ git clone https://github.com/duriantaco/skylos.git
180
+ cd skylos
181
+
182
+ # Create a virtual environment
183
+ python -m venv venv
184
+ source venv/bin/activate # On Windows: venv\Scripts\activate
185
+
186
+ # Install development dependencies
187
+ pip install maturin inquirer pytest
188
+
189
+ # Build in development mode
190
+ maturin develop
191
+ ```
192
+
193
+ ### Running Tests
194
+
195
+ ```bash
196
+ # Run Rust tests
197
+ cd src
198
+ cargo test
199
+
200
+ # Run Python tests
201
+ python -m pytest tests/
202
+ ```
203
+
204
+ ### Adding New Features
205
+
206
+ 1. **Rust Analysis**: Add new queries in `src/queries.rs`
207
+ 2. **Python CLI**: Extend functionality in `skylos/cli.py`
208
+ 3. **Documentation**: Update this README and add docstrings
209
+
210
+ ## Configuration
211
+
212
+ Skylos supports configuration via `.skylos.toml` (coming soon):
213
+
214
+ ```toml
215
+ [analysis]
216
+ # Exclude patterns
217
+ exclude = [
218
+ "*/migrations/*",
219
+ "*/tests/*",
220
+ "__pycache__"
221
+ ]
222
+
223
+ # Custom patterns for dead code detection
224
+ patterns = [
225
+ "test_*", # Test functions
226
+ "_*_internal" # Internal functions
227
+ ]
228
+
229
+ [output]
230
+ # Default output format
231
+ format = "colored" # Options: colored, plain, json
232
+
233
+ # Color scheme
234
+ colors = "default" # Options: default, dark, light
235
+ ```
236
+
237
+ ## Troubleshooting
238
+
239
+ ### Common Issues
240
+
241
+ 1. **Tree-sitter Errors**
242
+ ```
243
+ Error: tree-sitter parse failed
244
+ ```
245
+ Ensure your Python files have valid syntax.
246
+
247
+ 2. **Permission Errors**
248
+ ```
249
+ Error: Permission denied when removing function
250
+ ```
251
+ Check file permissions before running in interactive mode.
252
+
253
+ 3. **Missing Dependencies**
254
+ ```
255
+ Interactive mode requires 'inquirer' package
256
+ ```
257
+ Install with: `pip install skylos[interactive]`
258
+
259
+ ## Contributing
260
+
261
+ We welcome contributions! Please read our [Contributing Guidelines](CONTRIBUTING.md) before submitting pull requests.
262
+
263
+ ### Quick Contribution Guide
264
+
265
+ 1. Fork the repository
266
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
267
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
268
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
269
+ 5. Open a Pull Request
270
+
271
+ ## Roadmap
272
+
273
+ - [ ] Configuration file support (White lists etc)
274
+ - [ ] Custom analysis rules
275
+ - [ ] Git hooks integration
276
+ - [ ] CI/CD integration examples
277
+ - [ ] Web interface
278
+ - [ ] Support for other languages
279
+ - [ ] Further optimization
280
+
281
+ ## License
282
+
283
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
284
+
285
+ ## Acknowledgments
286
+
287
+ - [tree-sitter](https://tree-sitter.github.io/) for the powerful parsing framework
288
+ - [maturin](https://github.com/PyO3/maturin) for seamless Rust-Python integration
289
+ - [inquirer](https://github.com/magmax/python-inquirer) for the interactive CLI
290
+
291
+ ## Contact
292
+
293
+ - **Author**: oha
294
+ - **Email**: aaronoh2015@gmail.com
295
+ - **GitHub**: [@duriantaco](https://github.com/duriantaco)
@@ -0,0 +1,7 @@
1
+ skylos-0.0.1.dist-info/METADATA,sha256=8UKTIFGT6PE1F7T5EK_9r0rdL9m7exUNNHkDLvgqnFc,7280
2
+ skylos-0.0.1.dist-info/WHEEL,sha256=_czbP61TsBkf9T201RekHMHlqESnWn7yJwXBJC9P-w0,104
3
+ skylos-0.0.1.dist-info/entry_points.txt,sha256=0_aXKDSaSH1REotD0g5GU419ykfL-S5H0XFtmDwRHyU,41
4
+ skylos/__init__.py,sha256=xHdVS6MpULFr3-JZSxs4oKh9P2_FdV3lCuJU48Aanr8,846
5
+ skylos/cli.py,sha256=Pq71Gtc3d8E7JVYnEORK_LoLclG96A6b76SIGZh-C6s,13821
6
+ skylos/_core.cpython-313-darwin.so,sha256=f7LvC67K5_L4GgTdxnnkKc0BPwqI9MHY4jpe-Lyfh8c,2659296
7
+ skylos-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.8.3)
3
+ Root-Is-Purelib: false
4
+ Tag: cp313-cp313-macosx_11_0_arm64
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ skylos=skylos.cli:main