devklean 1.0.1__tar.gz → 1.0.2__tar.gz
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.
- {devklean-1.0.1 → devklean-1.0.2}/CHANGELOG.md +15 -1
- {devklean-1.0.1 → devklean-1.0.2}/PKG-INFO +4 -1
- {devklean-1.0.1 → devklean-1.0.2}/README.md +2 -0
- {devklean-1.0.1 → devklean-1.0.2}/pyproject.toml +1 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/__main__.py +1 -1
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/_version.py +1 -1
- devklean-1.0.2/src/devklean/cli/__init__.py +11 -0
- devklean-1.0.2/src/devklean/cli/commands/__init__.py +13 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/commands/clean.py +7 -1
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/main.py +3 -1
- devklean-1.0.2/src/devklean/deletion/__init__.py +31 -0
- devklean-1.0.2/src/devklean/output/__init__.py +15 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/console.py +5 -3
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/tui.py +7 -1
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_windows_guard.py +3 -0
- devklean-1.0.1/src/devklean/cli/__init__.py +0 -3
- devklean-1.0.1/src/devklean/cli/commands/__init__.py +0 -4
- devklean-1.0.1/src/devklean/deletion/__init__.py +0 -17
- devklean-1.0.1/src/devklean/output/__init__.py +0 -5
- {devklean-1.0.1 → devklean-1.0.2}/.gitignore +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/LICENSE +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/__init__.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/commands/common.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/commands/doctor.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/commands/history.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/commands/restore.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/commands/scan.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/confirmation.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/dispatcher.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/cli/parser.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/config/__init__.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/config/defaults.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/config/manager.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/config/models.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/config/paths.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/config/targets.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/deletion/history.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/deletion/integrity.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/deletion/metadata.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/deletion/paths.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/deletion/safety.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/deletion/trash.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/formatting.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/logging_setup.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/models.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/base.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/history_payload.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/json.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/scan_payload.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/sorting.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/text.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/output/theme.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/scanner/__init__.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/scanner/filters.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/src/devklean/scanner/scanner.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/benchmark_scanner.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/conftest.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_clean_flow.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_cli_parser.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_config.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_config_precedence.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_confirmation.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_console.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_deletion.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_doctor.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_dry_run.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_formatting.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_history.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_integration.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_integrity.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_json_output.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_logging.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_main_signal.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_models.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_output_sorting.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_packaging.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_perf_scan.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_restore.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_safety.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_scan_json_cli.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_scan_permissions.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_scan_settings.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_scanner.py +0 -0
- {devklean-1.0.1 → devklean-1.0.2}/tests/test_text_renderer.py +0 -0
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.2] - 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replaced `print()` calls in the console layer with `click.echo()` for improved
|
|
15
|
+
Unicode and Windows terminal compatibility.
|
|
16
|
+
- Fixed `src/devklean/__main__.py` to correctly import `main` from
|
|
17
|
+
`devklean.cli.main`.
|
|
18
|
+
- Made package `__init__.py` imports lazy to avoid pulling in `send2trash`
|
|
19
|
+
during unrelated imports.
|
|
20
|
+
- Added `click` as a runtime dependency.
|
|
21
|
+
- Updated Windows guard test to work in subprocesses with `PYTHONPATH=src`.
|
|
22
|
+
|
|
10
23
|
## [1.0.1] - 2026-06-30
|
|
11
24
|
|
|
12
25
|
### Fixed
|
|
@@ -91,7 +104,8 @@ First public pre-release.
|
|
|
91
104
|
per-target sizing.
|
|
92
105
|
- **Packaging** — distributable via `pip`/`pipx`; MIT licensed.
|
|
93
106
|
|
|
94
|
-
[Unreleased]: https://github.com/smurftyy/devklean/compare/v1.0.
|
|
107
|
+
[Unreleased]: https://github.com/smurftyy/devklean/compare/v1.0.2...HEAD
|
|
108
|
+
[1.0.2]: https://github.com/smurftyy/devklean/compare/v1.0.1...v1.0.2
|
|
95
109
|
[1.0.1]: https://github.com/smurftyy/devklean/compare/v1.0.0...v1.0.1
|
|
96
110
|
[1.0.0]: https://github.com/smurftyy/devklean/compare/v0.1.0...v1.0.0
|
|
97
111
|
[0.1.0]: https://github.com/smurftyy/devklean/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devklean
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Clean up common development artifacts (node_modules, .venv, caches) to reclaim disk space
|
|
5
5
|
Project-URL: Homepage, https://github.com/smurftyy/devklean
|
|
6
6
|
Project-URL: Repository, https://github.com/smurftyy/devklean
|
|
@@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
24
24
|
Classifier: Topic :: System :: Filesystems
|
|
25
25
|
Classifier: Topic :: Utilities
|
|
26
26
|
Requires-Python: >=3.8
|
|
27
|
+
Requires-Dist: click>=8.1.0
|
|
27
28
|
Requires-Dist: send2trash>=1.8.2
|
|
28
29
|
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
29
30
|
Provides-Extra: dev
|
|
@@ -43,6 +44,8 @@ Description-Content-Type: text/markdown
|
|
|
43
44
|
|
|
44
45
|
**Reclaim disk space by safely cleaning up development artifacts** — `node_modules`, `.venv`, build caches, and other regenerable directories — with a trash-backed safety net so nothing is ever lost to a typo.
|
|
45
46
|
|
|
47
|
+

|
|
48
|
+
|
|
46
49
|
<!-- TODO: insert terminal recording GIF of `devklean clean` here -->
|
|
47
50
|
|
|
48
51
|
## Why
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
**Reclaim disk space by safely cleaning up development artifacts** — `node_modules`, `.venv`, build caches, and other regenerable directories — with a trash-backed safety net so nothing is ever lost to a typo.
|
|
9
9
|
|
|
10
|
+

|
|
11
|
+
|
|
10
12
|
<!-- TODO: insert terminal recording GIF of `devklean clean` here -->
|
|
11
13
|
|
|
12
14
|
## Why
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
__all__ = ["main"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __getattr__(name: str):
|
|
9
|
+
if name == "main":
|
|
10
|
+
return import_module("devklean.cli.main").main
|
|
11
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
__all__ = ["run_clean", "run_scan"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __getattr__(name: str):
|
|
9
|
+
if name == "run_clean":
|
|
10
|
+
return import_module("devklean.cli.commands.clean").run_clean
|
|
11
|
+
if name == "run_scan":
|
|
12
|
+
return import_module("devklean.cli.commands.scan").run_scan
|
|
13
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -9,11 +9,17 @@ from devklean.cli.confirmation import (
|
|
|
9
9
|
exceeds_threshold,
|
|
10
10
|
)
|
|
11
11
|
from devklean.config.models import AppConfig
|
|
12
|
-
from devklean.deletion import SafetyValidator
|
|
12
|
+
from devklean.deletion.safety import SafetyValidator
|
|
13
13
|
from devklean.models import CleanableItem
|
|
14
14
|
from devklean.output.base import Renderer
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def delete_items(*args, **kwargs):
|
|
18
|
+
from devklean.deletion import delete_items as _delete_items
|
|
19
|
+
|
|
20
|
+
return _delete_items(*args, **kwargs)
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
def _confirm_deletion(
|
|
18
24
|
renderer: Renderer,
|
|
19
25
|
count: int,
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
|
+
import click
|
|
6
|
+
|
|
5
7
|
from devklean.cli.dispatcher import dispatch
|
|
6
8
|
from devklean.cli.parser import build_parser, resolve_bare_invocation
|
|
7
9
|
from devklean.config import ConfigManager
|
|
@@ -62,5 +64,5 @@ def main() -> None:
|
|
|
62
64
|
sys.exit(exit_code)
|
|
63
65
|
except KeyboardInterrupt:
|
|
64
66
|
# 130 is the Unix convention for a SIGINT-terminated process (128 + 2).
|
|
65
|
-
|
|
67
|
+
click.echo("\nAborted.", file=sys.stderr)
|
|
66
68
|
sys.exit(130)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"IntegrityReport",
|
|
7
|
+
"MetadataManager",
|
|
8
|
+
"SafetyValidator",
|
|
9
|
+
"SafetyViolation",
|
|
10
|
+
"check_integrity",
|
|
11
|
+
"delete_items",
|
|
12
|
+
"get_deletion_metadata_dir",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def __getattr__(name: str):
|
|
17
|
+
if name == "IntegrityReport":
|
|
18
|
+
return import_module("devklean.deletion.integrity").IntegrityReport
|
|
19
|
+
if name == "check_integrity":
|
|
20
|
+
return import_module("devklean.deletion.integrity").check_integrity
|
|
21
|
+
if name == "MetadataManager":
|
|
22
|
+
return import_module("devklean.deletion.metadata").MetadataManager
|
|
23
|
+
if name == "get_deletion_metadata_dir":
|
|
24
|
+
return import_module("devklean.deletion.paths").get_deletion_metadata_dir
|
|
25
|
+
if name == "SafetyValidator":
|
|
26
|
+
return import_module("devklean.deletion.safety").SafetyValidator
|
|
27
|
+
if name == "SafetyViolation":
|
|
28
|
+
return import_module("devklean.deletion.safety").SafetyViolation
|
|
29
|
+
if name == "delete_items":
|
|
30
|
+
return import_module("devklean.deletion.trash").delete_items
|
|
31
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
__all__ = ["JsonRenderer", "Renderer", "TextRenderer"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __getattr__(name: str):
|
|
9
|
+
if name == "Renderer":
|
|
10
|
+
return import_module("devklean.output.base").Renderer
|
|
11
|
+
if name == "JsonRenderer":
|
|
12
|
+
return import_module("devklean.output.json").JsonRenderer
|
|
13
|
+
if name == "TextRenderer":
|
|
14
|
+
return import_module("devklean.output.text").TextRenderer
|
|
15
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -4,6 +4,8 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
from typing import TextIO
|
|
6
6
|
|
|
7
|
+
import click
|
|
8
|
+
|
|
7
9
|
from devklean.output.theme import Palette, get_theme
|
|
8
10
|
|
|
9
11
|
# Semantic symbols — fixed across every command and renderer.
|
|
@@ -73,7 +75,7 @@ class Console:
|
|
|
73
75
|
|
|
74
76
|
def _line(self, symbol: str, role: str, message: str) -> None:
|
|
75
77
|
prefix = self.paint(symbol, role)
|
|
76
|
-
|
|
78
|
+
click.echo(f"{prefix} {message}", file=self._stream)
|
|
77
79
|
|
|
78
80
|
def success(self, message: str) -> None:
|
|
79
81
|
self._line(SYM_SUCCESS, "success", message)
|
|
@@ -88,7 +90,7 @@ class Console:
|
|
|
88
90
|
self._line(SYM_INFO, "info", message)
|
|
89
91
|
|
|
90
92
|
def detail(self, message: str) -> None:
|
|
91
|
-
|
|
93
|
+
click.echo(self.paint(message, "detail"), file=self._stream)
|
|
92
94
|
|
|
93
95
|
def plain(self, message: str = "") -> None:
|
|
94
|
-
|
|
96
|
+
click.echo(message, file=self._stream)
|
|
@@ -5,13 +5,19 @@ from devklean.cli.confirmation import (
|
|
|
5
5
|
confirm_large_deletion,
|
|
6
6
|
exceeds_threshold,
|
|
7
7
|
)
|
|
8
|
-
from devklean.deletion import SafetyValidator
|
|
8
|
+
from devklean.deletion.safety import SafetyValidator
|
|
9
9
|
from devklean.formatting import format_size, truncate
|
|
10
10
|
from devklean.models import CleanableItem
|
|
11
11
|
from devklean.output.base import Renderer
|
|
12
12
|
from devklean.output.sorting import items_by_size_desc
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def delete_items(*args, **kwargs):
|
|
16
|
+
from devklean.deletion import delete_items as _delete_items
|
|
17
|
+
|
|
18
|
+
return _delete_items(*args, **kwargs)
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
def interactive_ui(stdscr, items: list[CleanableItem], dry_run: bool) -> list[int] | None:
|
|
16
22
|
import curses # Unix-only; imported lazily so this module loads on Windows.
|
|
17
23
|
|
|
@@ -10,6 +10,7 @@ These are the Windows-relevant behaviors, so they run on every platform.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import io
|
|
13
|
+
import os
|
|
13
14
|
import subprocess
|
|
14
15
|
import sys
|
|
15
16
|
from argparse import Namespace
|
|
@@ -52,10 +53,12 @@ def test_tui_module_imports_cold_without_curses() -> None:
|
|
|
52
53
|
crash every command on Windows) and an import-order/circular-import
|
|
53
54
|
regression that an in-process import would mask.
|
|
54
55
|
"""
|
|
56
|
+
env = {**os.environ, "PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src")}
|
|
55
57
|
result = subprocess.run(
|
|
56
58
|
[sys.executable, "-c", "import devklean.tui; print(devklean.tui.run_interactive.__name__)"],
|
|
57
59
|
capture_output=True,
|
|
58
60
|
text=True,
|
|
61
|
+
env=env,
|
|
59
62
|
)
|
|
60
63
|
assert result.returncode == 0, result.stderr
|
|
61
64
|
assert "run_interactive" in result.stdout
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from devklean.deletion.integrity import IntegrityReport, check_integrity
|
|
4
|
-
from devklean.deletion.metadata import MetadataManager
|
|
5
|
-
from devklean.deletion.paths import get_deletion_metadata_dir
|
|
6
|
-
from devklean.deletion.safety import SafetyValidator, SafetyViolation
|
|
7
|
-
from devklean.deletion.trash import delete_items
|
|
8
|
-
|
|
9
|
-
__all__ = [
|
|
10
|
-
"IntegrityReport",
|
|
11
|
-
"MetadataManager",
|
|
12
|
-
"SafetyValidator",
|
|
13
|
-
"SafetyViolation",
|
|
14
|
-
"check_integrity",
|
|
15
|
-
"delete_items",
|
|
16
|
-
"get_deletion_metadata_dir",
|
|
17
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|