clutterxp 1.1.0__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.
- clutterxp-1.1.0/.github/workflows/ci.yml +82 -0
- clutterxp-1.1.0/.gitignore +18 -0
- clutterxp-1.1.0/CHANGELOG.md +29 -0
- clutterxp-1.1.0/PKG-INFO +149 -0
- clutterxp-1.1.0/README.md +120 -0
- clutterxp-1.1.0/config.example.yaml +76 -0
- clutterxp-1.1.0/docs/00_PROJECT_PLAN.md +135 -0
- clutterxp-1.1.0/docs/01_ARCHITECTURE.md +182 -0
- clutterxp-1.1.0/docs/02_RULE_ENGINE.md +159 -0
- clutterxp-1.1.0/docs/03_CONFIG.md +108 -0
- clutterxp-1.1.0/docs/04_SAFE_MOVER.md +99 -0
- clutterxp-1.1.0/docs/05_HISTORY_UNDO.md +148 -0
- clutterxp-1.1.0/docs/06_WATCHER.md +92 -0
- clutterxp-1.1.0/docs/07_DUPLICATES.md +69 -0
- clutterxp-1.1.0/docs/08_CLI.md +143 -0
- clutterxp-1.1.0/docs/09_SAFETY.md +62 -0
- clutterxp-1.1.0/docs/10_TESTING.md +81 -0
- clutterxp-1.1.0/docs/11_PACKAGING.md +58 -0
- clutterxp-1.1.0/docs/12_GUI.md +43 -0
- clutterxp-1.1.0/docs/13_ROADMAP.md +46 -0
- clutterxp-1.1.0/docs/14_DECISIONS.md +41 -0
- clutterxp-1.1.0/docs/15_ACCEPTANCE.md +146 -0
- clutterxp-1.1.0/docs/16_RISKS.md +23 -0
- clutterxp-1.1.0/docs/reviews/.gitkeep +0 -0
- clutterxp-1.1.0/docs/reviews/acceptance_matrix.md +106 -0
- clutterxp-1.1.0/docs/reviews/perf_report.md +102 -0
- clutterxp-1.1.0/docs/reviews/security_review.md +244 -0
- clutterxp-1.1.0/packaging/clutterxp.service +15 -0
- clutterxp-1.1.0/pyproject.toml +91 -0
- clutterxp-1.1.0/scripts/.gitkeep +0 -0
- clutterxp-1.1.0/scripts/check_contracts.py +172 -0
- clutterxp-1.1.0/scripts/check_ownership.py +262 -0
- clutterxp-1.1.0/scripts/contracts.lock.json +61 -0
- clutterxp-1.1.0/scripts/e2e.sh +164 -0
- clutterxp-1.1.0/scripts/smoke_readme.sh +88 -0
- clutterxp-1.1.0/src/clutterxp/__init__.py +4 -0
- clutterxp-1.1.0/src/clutterxp/cli/__init__.py +7 -0
- clutterxp-1.1.0/src/clutterxp/cli/app.py +251 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_config.py +250 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_doctor.py +449 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_duplicates.py +253 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_gui.py +22 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_history.py +233 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_init.py +76 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_organize.py +226 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_rules.py +540 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_scan.py +109 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_service.py +130 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_undo.py +180 -0
- clutterxp-1.1.0/src/clutterxp/cli/cmd_watch.py +147 -0
- clutterxp-1.1.0/src/clutterxp/cli/errors.py +166 -0
- clutterxp-1.1.0/src/clutterxp/cli/render.py +390 -0
- clutterxp-1.1.0/src/clutterxp/config/__init__.py +40 -0
- clutterxp-1.1.0/src/clutterxp/config/jsonschema.py +12 -0
- clutterxp-1.1.0/src/clutterxp/config/loader.py +212 -0
- clutterxp-1.1.0/src/clutterxp/config/placeholders.py +79 -0
- clutterxp-1.1.0/src/clutterxp/config/runtime.py +30 -0
- clutterxp-1.1.0/src/clutterxp/config/schema.py +166 -0
- clutterxp-1.1.0/src/clutterxp/config/validate.py +226 -0
- clutterxp-1.1.0/src/clutterxp/config/writer.py +248 -0
- clutterxp-1.1.0/src/clutterxp/duplicates/__init__.py +22 -0
- clutterxp-1.1.0/src/clutterxp/duplicates/detector.py +438 -0
- clutterxp-1.1.0/src/clutterxp/duplicates/hashcache.py +149 -0
- clutterxp-1.1.0/src/clutterxp/duplicates/namenorm.py +45 -0
- clutterxp-1.1.0/src/clutterxp/duplicates/quarantine.py +196 -0
- clutterxp-1.1.0/src/clutterxp/duplicates/report.py +110 -0
- clutterxp-1.1.0/src/clutterxp/gui/__init__.py +1 -0
- clutterxp-1.1.0/src/clutterxp/gui/api.py +494 -0
- clutterxp-1.1.0/src/clutterxp/gui/app.py +205 -0
- clutterxp-1.1.0/src/clutterxp/gui/models_qt.py +544 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/__init__.py +21 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/dashboard.py +253 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/duplicates.py +404 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/folders.py +354 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/history.py +281 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/preview.py +277 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/rules.py +527 -0
- clutterxp-1.1.0/src/clutterxp/gui/views/settings.py +172 -0
- clutterxp-1.1.0/src/clutterxp/gui/workers.py +147 -0
- clutterxp-1.1.0/src/clutterxp/history/__init__.py +23 -0
- clutterxp-1.1.0/src/clutterxp/history/db.py +174 -0
- clutterxp-1.1.0/src/clutterxp/history/pathcodec.py +42 -0
- clutterxp-1.1.0/src/clutterxp/history/recovery.py +318 -0
- clutterxp-1.1.0/src/clutterxp/history/schema.sql +43 -0
- clutterxp-1.1.0/src/clutterxp/history/service.py +493 -0
- clutterxp-1.1.0/src/clutterxp/history/undo.py +373 -0
- clutterxp-1.1.0/src/clutterxp/interfaces.py +164 -0
- clutterxp-1.1.0/src/clutterxp/locking.py +113 -0
- clutterxp-1.1.0/src/clutterxp/logging_setup.py +73 -0
- clutterxp-1.1.0/src/clutterxp/models.py +266 -0
- clutterxp-1.1.0/src/clutterxp/mover/__init__.py +26 -0
- clutterxp-1.1.0/src/clutterxp/mover/collision.py +46 -0
- clutterxp-1.1.0/src/clutterxp/mover/fsops.py +191 -0
- clutterxp-1.1.0/src/clutterxp/mover/safe_move.py +741 -0
- clutterxp-1.1.0/src/clutterxp/organizer.py +388 -0
- clutterxp-1.1.0/src/clutterxp/paths.py +112 -0
- clutterxp-1.1.0/src/clutterxp/planner.py +339 -0
- clutterxp-1.1.0/src/clutterxp/py.typed +0 -0
- clutterxp-1.1.0/src/clutterxp/rules/__init__.py +16 -0
- clutterxp-1.1.0/src/clutterxp/rules/engine.py +374 -0
- clutterxp-1.1.0/src/clutterxp/rules/matcher.py +222 -0
- clutterxp-1.1.0/src/clutterxp/rules/template.py +171 -0
- clutterxp-1.1.0/src/clutterxp/rules/tokenizer.py +118 -0
- clutterxp-1.1.0/src/clutterxp/safety.py +111 -0
- clutterxp-1.1.0/src/clutterxp/scanner.py +196 -0
- clutterxp-1.1.0/src/clutterxp/service_install.py +208 -0
- clutterxp-1.1.0/src/clutterxp/units.py +59 -0
- clutterxp-1.1.0/src/clutterxp/watch/__init__.py +37 -0
- clutterxp-1.1.0/src/clutterxp/watch/debounce.py +84 -0
- clutterxp-1.1.0/src/clutterxp/watch/events.py +259 -0
- clutterxp-1.1.0/src/clutterxp/watch/ignore.py +77 -0
- clutterxp-1.1.0/src/clutterxp/watch/service.py +662 -0
- clutterxp-1.1.0/src/clutterxp/watch/settle.py +170 -0
- clutterxp-1.1.0/tests/.gitkeep +0 -0
- clutterxp-1.1.0/tests/__init__.py +0 -0
- clutterxp-1.1.0/tests/cli/__init__.py +14 -0
- clutterxp-1.1.0/tests/cli/conftest.py +85 -0
- clutterxp-1.1.0/tests/cli/schemas/__init__.py +0 -0
- clutterxp-1.1.0/tests/cli/schemas/doctor.json +37 -0
- clutterxp-1.1.0/tests/cli/schemas/duplicates.json +88 -0
- clutterxp-1.1.0/tests/cli/schemas/history.json +38 -0
- clutterxp-1.1.0/tests/cli/schemas/organize.json +49 -0
- clutterxp-1.1.0/tests/cli/schemas/rules_list.json +42 -0
- clutterxp-1.1.0/tests/cli/schemas/rules_test.json +70 -0
- clutterxp-1.1.0/tests/cli/schemas/scan.json +47 -0
- clutterxp-1.1.0/tests/cli/test_config.py +176 -0
- clutterxp-1.1.0/tests/cli/test_doctor.py +155 -0
- clutterxp-1.1.0/tests/cli/test_duplicates.py +128 -0
- clutterxp-1.1.0/tests/cli/test_general.py +136 -0
- clutterxp-1.1.0/tests/cli/test_gui.py +30 -0
- clutterxp-1.1.0/tests/cli/test_history.py +156 -0
- clutterxp-1.1.0/tests/cli/test_init.py +85 -0
- clutterxp-1.1.0/tests/cli/test_organize.py +189 -0
- clutterxp-1.1.0/tests/cli/test_rules.py +171 -0
- clutterxp-1.1.0/tests/cli/test_scan.py +153 -0
- clutterxp-1.1.0/tests/cli/test_service.py +96 -0
- clutterxp-1.1.0/tests/cli/test_undo.py +175 -0
- clutterxp-1.1.0/tests/cli/test_watch.py +97 -0
- clutterxp-1.1.0/tests/config/__init__.py +0 -0
- clutterxp-1.1.0/tests/config/conftest.py +23 -0
- clutterxp-1.1.0/tests/config/test_jsonschema_and_logging.py +52 -0
- clutterxp-1.1.0/tests/config/test_loader.py +273 -0
- clutterxp-1.1.0/tests/config/test_paths.py +106 -0
- clutterxp-1.1.0/tests/config/test_placeholders.py +59 -0
- clutterxp-1.1.0/tests/config/test_runtime.py +52 -0
- clutterxp-1.1.0/tests/config/test_validate.py +197 -0
- clutterxp-1.1.0/tests/config/test_writer.py +173 -0
- clutterxp-1.1.0/tests/conftest.py +208 -0
- clutterxp-1.1.0/tests/duplicates/__init__.py +0 -0
- clutterxp-1.1.0/tests/duplicates/test_detector.py +512 -0
- clutterxp-1.1.0/tests/duplicates/test_hashcache.py +169 -0
- clutterxp-1.1.0/tests/duplicates/test_namenorm.py +57 -0
- clutterxp-1.1.0/tests/duplicates/test_quarantine.py +130 -0
- clutterxp-1.1.0/tests/gui/__init__.py +1 -0
- clutterxp-1.1.0/tests/gui/conftest.py +56 -0
- clutterxp-1.1.0/tests/gui/test_A81_gui_launches_and_cli_never_imports_qt.py +54 -0
- clutterxp-1.1.0/tests/gui/test_A82_dashboard_shows_watcher_and_recent_operations.py +59 -0
- clutterxp-1.1.0/tests/gui/test_A83_folders_view_edits_watch_folders_and_settings.py +81 -0
- clutterxp-1.1.0/tests/gui/test_A84_rules_editor_includes_live_trace.py +69 -0
- clutterxp-1.1.0/tests/gui/test_A85_preview_data_equals_cli_json_plan.py +80 -0
- clutterxp-1.1.0/tests/gui/test_A86_history_view_undo_shows_exact_refusal_reasons.py +101 -0
- clutterxp-1.1.0/tests/gui/test_A87_duplicates_view_progress_and_cancel.py +83 -0
- clutterxp-1.1.0/tests/gui/test_A88_duplicates_quarantine_gui.py +136 -0
- clutterxp-1.1.0/tests/gui/test_A89_ui_thread_never_blocks_worker_cancellation.py +79 -0
- clutterxp-1.1.0/tests/gui/test_A90_gui_extra_documented.py +44 -0
- clutterxp-1.1.0/tests/gui/test_api_extra.py +174 -0
- clutterxp-1.1.0/tests/gui/test_models_qt.py +357 -0
- clutterxp-1.1.0/tests/helpers.py +429 -0
- clutterxp-1.1.0/tests/history/__init__.py +0 -0
- clutterxp-1.1.0/tests/history/test_db.py +253 -0
- clutterxp-1.1.0/tests/history/test_pathcodec.py +77 -0
- clutterxp-1.1.0/tests/history/test_recovery.py +471 -0
- clutterxp-1.1.0/tests/history/test_service.py +778 -0
- clutterxp-1.1.0/tests/history/test_undo.py +799 -0
- clutterxp-1.1.0/tests/integration/__init__.py +0 -0
- clutterxp-1.1.0/tests/integration/test_acceptance_sweep.py +365 -0
- clutterxp-1.1.0/tests/integration/test_fault_injection.py +328 -0
- clutterxp-1.1.0/tests/integration/test_properties.py +191 -0
- clutterxp-1.1.0/tests/integration/test_scenarios.py +408 -0
- clutterxp-1.1.0/tests/mover/__init__.py +11 -0
- clutterxp-1.1.0/tests/mover/conftest.py +11 -0
- clutterxp-1.1.0/tests/mover/test_collision.py +138 -0
- clutterxp-1.1.0/tests/mover/test_fsops.py +274 -0
- clutterxp-1.1.0/tests/mover/test_locking.py +141 -0
- clutterxp-1.1.0/tests/mover/test_safe_move.py +1219 -0
- clutterxp-1.1.0/tests/organizer/__init__.py +11 -0
- clutterxp-1.1.0/tests/organizer/conftest.py +18 -0
- clutterxp-1.1.0/tests/organizer/test_organizer.py +860 -0
- clutterxp-1.1.0/tests/organizer/test_planner.py +419 -0
- clutterxp-1.1.0/tests/organizer/test_scanner.py +235 -0
- clutterxp-1.1.0/tests/perf/__init__.py +0 -0
- clutterxp-1.1.0/tests/perf/test_perf_budgets.py +379 -0
- clutterxp-1.1.0/tests/rules/__init__.py +0 -0
- clutterxp-1.1.0/tests/rules/conftest.py +22 -0
- clutterxp-1.1.0/tests/rules/test_engine.py +401 -0
- clutterxp-1.1.0/tests/rules/test_matcher.py +155 -0
- clutterxp-1.1.0/tests/rules/test_template.py +165 -0
- clutterxp-1.1.0/tests/rules/test_tokenizer.py +121 -0
- clutterxp-1.1.0/tests/rules/test_vectors.py +221 -0
- clutterxp-1.1.0/tests/safety/__init__.py +0 -0
- clutterxp-1.1.0/tests/safety/test_invariants_full.py +817 -0
- clutterxp-1.1.0/tests/safety/test_safety_module.py +181 -0
- clutterxp-1.1.0/tests/test_harness.py +290 -0
- clutterxp-1.1.0/tests/test_service_install.py +418 -0
- clutterxp-1.1.0/tests/watch/__init__.py +0 -0
- clutterxp-1.1.0/tests/watch/conftest.py +308 -0
- clutterxp-1.1.0/tests/watch/test_acceptance.py +637 -0
- clutterxp-1.1.0/tests/watch/test_debounce.py +134 -0
- clutterxp-1.1.0/tests/watch/test_events.py +231 -0
- clutterxp-1.1.0/tests/watch/test_ignore.py +83 -0
- clutterxp-1.1.0/tests/watch/test_invariants.py +156 -0
- clutterxp-1.1.0/tests/watch/test_service.py +316 -0
- clutterxp-1.1.0/tests/watch/test_settle.py +156 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
lint:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/setup-python@v5
|
|
13
|
+
with:
|
|
14
|
+
python-version: "3.12"
|
|
15
|
+
- run: pip install -e ".[dev]"
|
|
16
|
+
- run: ruff check .
|
|
17
|
+
- run: ruff format --check .
|
|
18
|
+
- run: mypy
|
|
19
|
+
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
- run: pip install -e ".[dev]"
|
|
32
|
+
- run: pytest -m "not perf" --cov=clutterxp --cov-fail-under=90
|
|
33
|
+
|
|
34
|
+
crossfs:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.12"
|
|
41
|
+
- run: pip install -e ".[dev]"
|
|
42
|
+
- run: pytest -m crossfs
|
|
43
|
+
|
|
44
|
+
perf:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
continue-on-error: true
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
- uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.12"
|
|
52
|
+
- run: pip install -e ".[dev]"
|
|
53
|
+
- run: pytest -m perf
|
|
54
|
+
|
|
55
|
+
audit:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: "3.12"
|
|
62
|
+
- run: pip install pip-audit && pip install -e .
|
|
63
|
+
- run: pip-audit
|
|
64
|
+
|
|
65
|
+
wheel-install:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v4
|
|
69
|
+
- uses: actions/setup-python@v5
|
|
70
|
+
with:
|
|
71
|
+
python-version: "3.12"
|
|
72
|
+
- run: pip install build
|
|
73
|
+
- run: python -m build --wheel
|
|
74
|
+
- name: Install and verify wheel in clean environment
|
|
75
|
+
run: |
|
|
76
|
+
python -m venv /tmp/clean_env
|
|
77
|
+
/tmp/clean_env/bin/pip install dist/*.whl
|
|
78
|
+
export PATH="/tmp/clean_env/bin:$PATH"
|
|
79
|
+
clutterxp --version
|
|
80
|
+
cxp --version
|
|
81
|
+
bash scripts/smoke_readme.sh
|
|
82
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
venv/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
.hypothesis/
|
|
14
|
+
clutterxp.schema.json
|
|
15
|
+
*.clutterxp-partial
|
|
16
|
+
history.db*
|
|
17
|
+
cache.db*
|
|
18
|
+
.worktrees/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are recorded here. Format: Keep a Changelog; versions: SemVer.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.1.0] - 2026-11-06
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Complete PySide6 desktop GUI application launched via `clutterxp gui` with single-instance lock (`src/clutterxp/gui/`).
|
|
11
|
+
- Seven dedicated GUI tabs: Dashboard (watcher state, summary stats, quick actions, event feed), Folders (watch folder configuration), Rules (rule list, interactive add/edit dialog, live trace test panel), Preview (interactive scan plan table with status filters and execute), History (operations journal with per-file and batch undo and refusal diagnosis), Duplicates (staged duplicate scanner with progress, cancellation, and quarantine action), Settings (global settings editor).
|
|
12
|
+
- Engine duplicate quarantine service (`src/clutterxp/duplicates/quarantine.py`) moving redundant exact duplicates into timestamped isolation directories with full journal logging and undo capability.
|
|
13
|
+
- Offscreen test suite verifying GUI views, models, background threading, cancellation guarantees, and CLI/Qt import isolation (`tests/gui/`).
|
|
14
|
+
|
|
15
|
+
## [1.0.0] - 2026-11-06
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Configuration engine with YAML round-trip preservation, strict schema validation, and atomic writer (`src/clutterxp/config/`).
|
|
19
|
+
- Rule engine with tokenization, prefix/token/substring/glob/regex matching, priority ambiguity resolution, and collision handling (`src/clutterxp/rules/`).
|
|
20
|
+
- Safe mover implementing atomic same-filesystem `renameat2(RENAME_NOREPLACE)` and verified copy-then-unlink across filesystems (`src/clutterxp/mover/`).
|
|
21
|
+
- History service with SQLite journal, transactional logging, single and batch undo, and crash recovery (`src/clutterxp/history/`).
|
|
22
|
+
- Watch daemon with inotify event stream, settling and debouncing, self-event suppression, and loop prevention (`src/clutterxp/watch/`).
|
|
23
|
+
- On-demand duplicate scanner with staged hashing (size, prefix hash, full sha256), caching, and safe quarantine (`src/clutterxp/duplicates/`).
|
|
24
|
+
- Complete CLI application with `init`, `rules`, `organize`, `undo`, `history`, `watch`, `duplicates`, `service`, `doctor` commands and `--json` support (`src/clutterxp/cli/`).
|
|
25
|
+
- Systemd user service installation, uninstallation, status inspection, and journal log viewing (`src/clutterxp/service_install.py`).
|
|
26
|
+
- Automated smoke verification script for README quickstart (`scripts/smoke_readme.sh`).
|
|
27
|
+
- CI workflows testing Python 3.10 through 3.13, cross-filesystem operations, linting, typechecking, vulnerability auditing, and clean wheel installation.
|
|
28
|
+
- Packaging metadata with console scripts `clutterxp` and `cxp`, wheel package data inclusion (`schema.sql`, `py.typed`, `config.example.yaml`, `packaging/clutterxp.service`), and optional CPython acceleration via `ruamel.yaml.clib`.
|
|
29
|
+
|
clutterxp-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: clutterxp
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Configurable, safe, lightweight file organizer: rules, preview, undo, watch, duplicates.
|
|
5
|
+
Keywords: automation,cli,downloads,file-organizer,linux
|
|
6
|
+
Classifier: Environment :: Console
|
|
7
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Topic :: System :: Filesystems
|
|
10
|
+
Classifier: Topic :: Utilities
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: platformdirs>=4.0
|
|
13
|
+
Requires-Dist: pydantic>=2.6
|
|
14
|
+
Requires-Dist: ruamel-yaml-clib>=0.2.12; platform_python_implementation == 'CPython'
|
|
15
|
+
Requires-Dist: ruamel-yaml>=0.18
|
|
16
|
+
Requires-Dist: typer>=0.12
|
|
17
|
+
Requires-Dist: watchdog>=4.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
21
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
26
|
+
Provides-Extra: gui
|
|
27
|
+
Requires-Dist: pyside6>=6.6; extra == 'gui'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# ClutterXP
|
|
31
|
+
|
|
32
|
+
> **Tired of a messy Downloads folder?** ClutterXP quietly tidies your files into the right place—on your terms, without ever losing a single file.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
Whether your `Downloads` folder is buried under months of receipts, PDFs, wallpapers, installers, and screenshots, or you just want your desktop kept neat, **ClutterXP** does the heavy lifting for you.
|
|
37
|
+
|
|
38
|
+
Use the **clean visual desktop app** or run quick commands from your terminal.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Why you can actually trust it
|
|
43
|
+
|
|
44
|
+
Most file cleanup tools feel risky—what if they delete something important or move a file you didn't want touched? We built ClutterXP with ironclad safety guarantees:
|
|
45
|
+
|
|
46
|
+
- 🛡️ **Never deletes or overwrites**: Files only move if the destination is safe. If a file with the same name already exists, ClutterXP renames the new one (e.g. `report (1).pdf`) or skips it. Your existing files are never replaced or destroyed.
|
|
47
|
+
- 🎯 **Only moves what you tell it to**: If a file doesn't match a rule you created, it stays exactly where it is. No surprise moves, no mystery "unsorted" dump folders.
|
|
48
|
+
- ↩️ **One-click instant undo**: Every action is saved in a local journal. If you ever change your mind, `clutterxp undo` (or a single click in the app) puts every file right back where it was originally.
|
|
49
|
+
- 🔒 **100% private and offline**: Runs completely on your machine. No accounts, no cloud sync, no tracking, and zero internet requests.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Getting Started
|
|
54
|
+
|
|
55
|
+
### 1. Installation
|
|
56
|
+
|
|
57
|
+
Install ClutterXP with Python (3.10+):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Recommended: install with desktop app (GUI) support
|
|
61
|
+
pip install "clutterxp[gui]"
|
|
62
|
+
|
|
63
|
+
# Or on Kali Linux / Debian using pipx:
|
|
64
|
+
pipx install .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. Choose how you want to use it
|
|
68
|
+
|
|
69
|
+
#### Option A: The Desktop App
|
|
70
|
+
Just run:
|
|
71
|
+
```bash
|
|
72
|
+
clutterxp gui
|
|
73
|
+
```
|
|
74
|
+
You'll get an intuitive dashboard where you can:
|
|
75
|
+
- **Preview moves before they happen** with a clear interactive table.
|
|
76
|
+
- **Set up simple rules** with live matching tests (type a filename and see which rule catches it instantly).
|
|
77
|
+
- **Find duplicate files** and safely quarantine them without risking accidental deletion.
|
|
78
|
+
- **Undo any batch** with a single button.
|
|
79
|
+
|
|
80
|
+
#### Option B: The Terminal (Fast & Scriptable)
|
|
81
|
+
Prefer working from the command line? Use `clutterxp` or the short alias `cxp`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# 1. Create your default starter config
|
|
85
|
+
clutterxp init
|
|
86
|
+
|
|
87
|
+
# 2. Test which rule matches a filename
|
|
88
|
+
clutterxp rules test "invoice_march.pdf"
|
|
89
|
+
|
|
90
|
+
# 3. Preview what would be organized (dry-run, touches nothing)
|
|
91
|
+
clutterxp organize ~/Downloads --dry-run
|
|
92
|
+
|
|
93
|
+
# 4. Organize your Downloads folder
|
|
94
|
+
clutterxp organize ~/Downloads --yes
|
|
95
|
+
|
|
96
|
+
# 5. Changed your mind? Undo the entire batch instantly!
|
|
97
|
+
clutterxp undo --yes
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Features that make life easier
|
|
103
|
+
|
|
104
|
+
### 📁 Set it and forget it (Background Watcher)
|
|
105
|
+
Turn on automatic sorting. Whenever a new download finishes, ClutterXP waits until it's completely written to disk, then quietly sorts it into place.
|
|
106
|
+
```bash
|
|
107
|
+
clutterxp watch
|
|
108
|
+
```
|
|
109
|
+
You can even enable it as a system background service:
|
|
110
|
+
```bash
|
|
111
|
+
clutterxp service install
|
|
112
|
+
clutterxp service status
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 🔍 Find and clean duplicate downloads
|
|
116
|
+
Downloaded the same PDF or installer three times? ClutterXP safely identifies duplicate files taking up storage, shows you where they live, and lets you isolate them safely.
|
|
117
|
+
```bash
|
|
118
|
+
clutterxp duplicates scan ~/Downloads
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### ✏️ Customize your rules in seconds
|
|
122
|
+
Rules are dead simple. Group by keywords, file extensions, size, or age:
|
|
123
|
+
- *"Move anything with `invoice` or `receipt` to `~/Documents/Invoices`"*
|
|
124
|
+
- *"Move `.png` and `.jpg` with `wallpaper` to `~/Pictures/Wallpapers`"*
|
|
125
|
+
- *"Move `.deb`, `.iso`, `.rpm` to `~/Downloads/Installers`"*
|
|
126
|
+
|
|
127
|
+
You can edit them directly in the visual app or via `clutterxp rules add`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Cheat Sheet
|
|
132
|
+
|
|
133
|
+
| What you want to do | Command |
|
|
134
|
+
|:---|:---|
|
|
135
|
+
| Open the visual desktop app | `clutterxp gui` |
|
|
136
|
+
| Preview organization without moving files | `cxp organize ~/Downloads --dry-run` |
|
|
137
|
+
| Organize a folder | `cxp organize ~/Downloads` |
|
|
138
|
+
| Undo the last batch of moves | `cxp undo` |
|
|
139
|
+
| Watch a folder and sort new files automatically | `cxp watch` |
|
|
140
|
+
| Install background watcher service | `cxp service install` |
|
|
141
|
+
| Scan for duplicate files | `cxp duplicates scan ~/Downloads` |
|
|
142
|
+
| Check system health and file integrity | `cxp doctor` |
|
|
143
|
+
| View organization history | `cxp history` |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# ClutterXP
|
|
2
|
+
|
|
3
|
+
> **Tired of a messy Downloads folder?** ClutterXP quietly tidies your files into the right place—on your terms, without ever losing a single file.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Whether your `Downloads` folder is buried under months of receipts, PDFs, wallpapers, installers, and screenshots, or you just want your desktop kept neat, **ClutterXP** does the heavy lifting for you.
|
|
8
|
+
|
|
9
|
+
Use the **clean visual desktop app** or run quick commands from your terminal.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Why you can actually trust it
|
|
14
|
+
|
|
15
|
+
Most file cleanup tools feel risky—what if they delete something important or move a file you didn't want touched? We built ClutterXP with ironclad safety guarantees:
|
|
16
|
+
|
|
17
|
+
- 🛡️ **Never deletes or overwrites**: Files only move if the destination is safe. If a file with the same name already exists, ClutterXP renames the new one (e.g. `report (1).pdf`) or skips it. Your existing files are never replaced or destroyed.
|
|
18
|
+
- 🎯 **Only moves what you tell it to**: If a file doesn't match a rule you created, it stays exactly where it is. No surprise moves, no mystery "unsorted" dump folders.
|
|
19
|
+
- ↩️ **One-click instant undo**: Every action is saved in a local journal. If you ever change your mind, `clutterxp undo` (or a single click in the app) puts every file right back where it was originally.
|
|
20
|
+
- 🔒 **100% private and offline**: Runs completely on your machine. No accounts, no cloud sync, no tracking, and zero internet requests.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Getting Started
|
|
25
|
+
|
|
26
|
+
### 1. Installation
|
|
27
|
+
|
|
28
|
+
Install ClutterXP with Python (3.10+):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Recommended: install with desktop app (GUI) support
|
|
32
|
+
pip install "clutterxp[gui]"
|
|
33
|
+
|
|
34
|
+
# Or on Kali Linux / Debian using pipx:
|
|
35
|
+
pipx install .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Choose how you want to use it
|
|
39
|
+
|
|
40
|
+
#### Option A: The Desktop App
|
|
41
|
+
Just run:
|
|
42
|
+
```bash
|
|
43
|
+
clutterxp gui
|
|
44
|
+
```
|
|
45
|
+
You'll get an intuitive dashboard where you can:
|
|
46
|
+
- **Preview moves before they happen** with a clear interactive table.
|
|
47
|
+
- **Set up simple rules** with live matching tests (type a filename and see which rule catches it instantly).
|
|
48
|
+
- **Find duplicate files** and safely quarantine them without risking accidental deletion.
|
|
49
|
+
- **Undo any batch** with a single button.
|
|
50
|
+
|
|
51
|
+
#### Option B: The Terminal (Fast & Scriptable)
|
|
52
|
+
Prefer working from the command line? Use `clutterxp` or the short alias `cxp`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1. Create your default starter config
|
|
56
|
+
clutterxp init
|
|
57
|
+
|
|
58
|
+
# 2. Test which rule matches a filename
|
|
59
|
+
clutterxp rules test "invoice_march.pdf"
|
|
60
|
+
|
|
61
|
+
# 3. Preview what would be organized (dry-run, touches nothing)
|
|
62
|
+
clutterxp organize ~/Downloads --dry-run
|
|
63
|
+
|
|
64
|
+
# 4. Organize your Downloads folder
|
|
65
|
+
clutterxp organize ~/Downloads --yes
|
|
66
|
+
|
|
67
|
+
# 5. Changed your mind? Undo the entire batch instantly!
|
|
68
|
+
clutterxp undo --yes
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Features that make life easier
|
|
74
|
+
|
|
75
|
+
### 📁 Set it and forget it (Background Watcher)
|
|
76
|
+
Turn on automatic sorting. Whenever a new download finishes, ClutterXP waits until it's completely written to disk, then quietly sorts it into place.
|
|
77
|
+
```bash
|
|
78
|
+
clutterxp watch
|
|
79
|
+
```
|
|
80
|
+
You can even enable it as a system background service:
|
|
81
|
+
```bash
|
|
82
|
+
clutterxp service install
|
|
83
|
+
clutterxp service status
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 🔍 Find and clean duplicate downloads
|
|
87
|
+
Downloaded the same PDF or installer three times? ClutterXP safely identifies duplicate files taking up storage, shows you where they live, and lets you isolate them safely.
|
|
88
|
+
```bash
|
|
89
|
+
clutterxp duplicates scan ~/Downloads
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### ✏️ Customize your rules in seconds
|
|
93
|
+
Rules are dead simple. Group by keywords, file extensions, size, or age:
|
|
94
|
+
- *"Move anything with `invoice` or `receipt` to `~/Documents/Invoices`"*
|
|
95
|
+
- *"Move `.png` and `.jpg` with `wallpaper` to `~/Pictures/Wallpapers`"*
|
|
96
|
+
- *"Move `.deb`, `.iso`, `.rpm` to `~/Downloads/Installers`"*
|
|
97
|
+
|
|
98
|
+
You can edit them directly in the visual app or via `clutterxp rules add`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Cheat Sheet
|
|
103
|
+
|
|
104
|
+
| What you want to do | Command |
|
|
105
|
+
|:---|:---|
|
|
106
|
+
| Open the visual desktop app | `clutterxp gui` |
|
|
107
|
+
| Preview organization without moving files | `cxp organize ~/Downloads --dry-run` |
|
|
108
|
+
| Organize a folder | `cxp organize ~/Downloads` |
|
|
109
|
+
| Undo the last batch of moves | `cxp undo` |
|
|
110
|
+
| Watch a folder and sort new files automatically | `cxp watch` |
|
|
111
|
+
| Install background watcher service | `cxp service install` |
|
|
112
|
+
| Scan for duplicate files | `cxp duplicates scan ~/Downloads` |
|
|
113
|
+
| Check system health and file integrity | `cxp doctor` |
|
|
114
|
+
| View organization history | `cxp history` |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# yaml-language-server: $schema=./clutterxp.schema.json
|
|
2
|
+
# ClutterXP example configuration. Copy to ~/.config/clutterxp/config.yaml
|
|
3
|
+
# (or run `clutterxp init`). Every name, keyword and folder below is an EXAMPLE:
|
|
4
|
+
# nothing is built in. Edit, delete and add your own.
|
|
5
|
+
|
|
6
|
+
version: 1
|
|
7
|
+
|
|
8
|
+
settings:
|
|
9
|
+
collision: skip # skip | rename (what to do if the destination file name exists)
|
|
10
|
+
create_destinations: leaf # never | leaf | all
|
|
11
|
+
match: token # default keyword mode: token | prefix | substring
|
|
12
|
+
case_sensitive: false
|
|
13
|
+
skip_hidden: true
|
|
14
|
+
follow_symlinks: false
|
|
15
|
+
confirm_threshold: 10 # ask before moving more than this many files at once
|
|
16
|
+
settle_seconds: 3 # how long a file must be quiet before the watcher moves it
|
|
17
|
+
verify_copy: size # size | hash (only matters for cross-filesystem moves)
|
|
18
|
+
record_hash: false
|
|
19
|
+
history:
|
|
20
|
+
retention_days: 365
|
|
21
|
+
log_no_match: false
|
|
22
|
+
ignore: [] # extra patterns to ignore everywhere
|
|
23
|
+
allow_chain: false # keep false: destinations must not live inside watched folders
|
|
24
|
+
|
|
25
|
+
# Folders to watch (and process with `clutterxp organize` when no path is given).
|
|
26
|
+
# ANY folder works; Downloads is just an example.
|
|
27
|
+
watch:
|
|
28
|
+
- path: ~/Downloads
|
|
29
|
+
recursive: false
|
|
30
|
+
settle_seconds: 3
|
|
31
|
+
rules: all # or a list of rule names that apply to this folder
|
|
32
|
+
initial_scan: false
|
|
33
|
+
- path: ~/Desktop
|
|
34
|
+
recursive: false
|
|
35
|
+
rules: [Screenshots, Invoices]
|
|
36
|
+
ignore: ["*.log"]
|
|
37
|
+
|
|
38
|
+
rules:
|
|
39
|
+
- name: ProjectAlpha
|
|
40
|
+
priority: 10 # lower number wins
|
|
41
|
+
keywords: [alpha, pa, alpha-ctf] # short aliases like "pa" match whole tokens only
|
|
42
|
+
destination: ~/Projects/ProjectAlpha
|
|
43
|
+
collision: rename
|
|
44
|
+
|
|
45
|
+
- name: Invoices
|
|
46
|
+
priority: 20
|
|
47
|
+
keywords: [invoice, inv, bill, receipt]
|
|
48
|
+
exclude: [draft]
|
|
49
|
+
extensions: [pdf, png, jpg]
|
|
50
|
+
destination: ~/Documents/Invoices/{year}/{month}
|
|
51
|
+
|
|
52
|
+
- name: College
|
|
53
|
+
priority: 30
|
|
54
|
+
keywords: [assignment, semester, lab, lecture]
|
|
55
|
+
destination: ~/College
|
|
56
|
+
|
|
57
|
+
- name: Screenshots
|
|
58
|
+
priority: 40
|
|
59
|
+
keywords: ["regex:^screenshot[ _-]"]
|
|
60
|
+
extensions: [png]
|
|
61
|
+
destination: ~/Pictures/Screenshots/{mtime:%Y-%m}
|
|
62
|
+
|
|
63
|
+
- name: Installers
|
|
64
|
+
priority: 50
|
|
65
|
+
extensions: [deb, appimage, run, rpm]
|
|
66
|
+
conditions:
|
|
67
|
+
min_size: 1MB
|
|
68
|
+
destination: ~/Software/Installers
|
|
69
|
+
|
|
70
|
+
- name: OldArchives
|
|
71
|
+
priority: 90
|
|
72
|
+
extensions: [zip, tar.gz, 7z]
|
|
73
|
+
conditions:
|
|
74
|
+
older_than: 30d
|
|
75
|
+
destination: ~/Archive/{year}
|
|
76
|
+
collision: rename
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# ClutterXP: Project Plan v0.2
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| Tool name | **ClutterXP** (command: `clutterxp`, short alias `cxp`, Python package `clutterxp`) |
|
|
6
|
+
| Supersedes | `Clutter` plan v0.1 |
|
|
7
|
+
| Platform | Linux first (developed and tested on Kali; nothing is Kali-specific), portable design |
|
|
8
|
+
| Interfaces | CLI first (full product), GUI second (v1.1) |
|
|
9
|
+
| Language | Python 3.10+ |
|
|
10
|
+
|
|
11
|
+
## 1. Purpose
|
|
12
|
+
|
|
13
|
+
ClutterXP is a configurable, lightweight, local file organizer. The user defines **rules** (keywords, aliases, extensions, size/age conditions, destinations). ClutterXP then sorts files from **any folders the user chooses** (Downloads is only the most common one) either on demand or automatically in the background.
|
|
14
|
+
|
|
15
|
+
Nothing is built in. No category, keyword, or folder is hardcoded. Names such as "SecLeaf", "ThunderCipher" or "College" are only examples a user may configure.
|
|
16
|
+
|
|
17
|
+
## 2. Non-negotiable principles
|
|
18
|
+
|
|
19
|
+
1. **Explicit match or nothing.** A file moves only when a configured rule explicitly matches. No match: the file stays exactly where it is. No automatic "Unsorted" folder.
|
|
20
|
+
2. **Never guess.** Equal-priority conflicts are reported as *ambiguous*, not resolved silently.
|
|
21
|
+
3. **Never lose data.** No automatic deletion. A move never overwrites. The source is removed only after the destination is verified.
|
|
22
|
+
4. **Reversible.** Every move is journaled and can be undone, singly or per batch.
|
|
23
|
+
5. **Preview first.** Anything bulk can be previewed; bulk applies ask for confirmation.
|
|
24
|
+
6. **Cheap.** No content reading or hashing during sorting. Event-driven watching, no polling or periodic full scans.
|
|
25
|
+
7. **Local and private.** No network access, no telemetry, DB and logs are `0600`.
|
|
26
|
+
8. **One engine.** CLI, watcher and GUI all call the same `Organizer` facade.
|
|
27
|
+
|
|
28
|
+
## 3. Folders are fully custom
|
|
29
|
+
|
|
30
|
+
- Any folder can be organized or watched: `clutterxp organize ~/Desktop`, `clutterxp watch /mnt/data/inbox`.
|
|
31
|
+
- Folders can be listed in config (`watch:` section) with **per-folder settings**: recursive on/off, extra ignore patterns, settle time, and an optional **subset of rules** that apply to that folder.
|
|
32
|
+
- Files can arrive in a folder by download, copy, drag-and-drop, or being moved in by another program. The watcher handles `created`, `moved-in` and `closed-after-write` events uniformly.
|
|
33
|
+
- Ad-hoc folders never need to be in config: a path argument always wins.
|
|
34
|
+
|
|
35
|
+
## 4. What changed from v0.1 (review findings, all adopted)
|
|
36
|
+
|
|
37
|
+
| # | Finding | Resolution in v0.2 |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| 1 | Token matching undefined | Precise tokenizer, match modes, test vectors: `docs/02_RULE_ENGINE.md` |
|
|
40
|
+
| 2 | `priority` contradicts "ambiguous" | Lower number wins; ambiguity only at equal best priority with different destinations |
|
|
41
|
+
| 3 | Move atomicity and cross-filesystem safety | No-clobber atomic primitive and verified copy protocol: `docs/04_SAFE_MOVER.md` |
|
|
42
|
+
| 4 | Download completion vague, and not only Downloads | Settle algorithm for any folder, moved-in files, open-for-write check: `docs/06_WATCHER.md` |
|
|
43
|
+
| 5 | Undo/history integrity | Journal-first protocol, crash recovery, batches, verification: `docs/05_HISTORY_UNDO.md` |
|
|
44
|
+
| 6 | Config validation | Pydantic schema, strict keys, comment-preserving edits, JSON Schema export: `docs/03_CONFIG.md` |
|
|
45
|
+
| 7 | Safety gaps | Invariants, loop prevention, root refusal, lock, system-dir denylist: `docs/09_SAFETY.md` |
|
|
46
|
+
|
|
47
|
+
## 5. Feature set (prioritized for efficiency)
|
|
48
|
+
|
|
49
|
+
**P0: core product (v1.0 cannot ship without these)**
|
|
50
|
+
|
|
51
|
+
| Feature | Why it earns its place |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Rules: keywords, `all_of`, `exclude`, extensions, size, age | Covers real sorting needs with metadata that is already in `stat()` (free) |
|
|
54
|
+
| Safe token matching + `glob:` / `regex:` escape hatches | Avoids the `sl` matches `slides` class of bugs |
|
|
55
|
+
| Priority + ambiguity detection | Predictable, explainable outcomes |
|
|
56
|
+
| `rules test <filename>` | Debug rules in seconds; pure function, almost free to build |
|
|
57
|
+
| `scan` / `organize --dry-run` / `organize` | Preview before acting |
|
|
58
|
+
| Safe mover (no-clobber, verified cross-FS) | The riskiest code; gets the most tests |
|
|
59
|
+
| History, batch IDs, `undo` (op / batch / file) | Trust: everything reversible |
|
|
60
|
+
| `watch` with settle detection | Automatic sorting for any folder |
|
|
61
|
+
| Config validation + `config validate` + `doctor` | Fail early with clear messages |
|
|
62
|
+
| Lock file + root refusal + system-dir denylist | Safety invariants |
|
|
63
|
+
|
|
64
|
+
**P1: included in v1.0 (cheap, high value)**
|
|
65
|
+
|
|
66
|
+
| Feature | Notes |
|
|
67
|
+
|---|---|
|
|
68
|
+
| Destination templates `{year}` `{month}` `{ext}` `{mtime:%Y-%m}` | Sub-sorting without extra rules |
|
|
69
|
+
| Per-folder watch config + rule subsets | Makes "any folder" real |
|
|
70
|
+
| Built-in + custom ignore patterns | Browser temp files, torrent clients, dotfiles |
|
|
71
|
+
| Collision policy per rule: `skip` / `rename` | Defined behavior instead of "don't overwrite" |
|
|
72
|
+
| `--json` output on every command | Scripting; also the GUI test oracle |
|
|
73
|
+
| Duplicate scan: size, then partial hash, then full SHA-256, with hash cache | Fast on re-runs; never runs during sorting |
|
|
74
|
+
| `service install` (systemd `--user`) | Real background operation |
|
|
75
|
+
| `init`, `config schema`, shell completion | Onboarding; editor autocomplete via JSON Schema |
|
|
76
|
+
| Crash recovery (`doctor --fix`) | Pending journal reconciliation |
|
|
77
|
+
|
|
78
|
+
**P2: after v1.0**
|
|
79
|
+
|
|
80
|
+
| Feature | Release |
|
|
81
|
+
|---|---|
|
|
82
|
+
| PySide6 GUI (same engine) | v1.1 |
|
|
83
|
+
| `duplicates quarantine` (move, never delete, undoable, confirmed) | v1.1 |
|
|
84
|
+
| Desktop notifications (`notify-send`) | v1.2 |
|
|
85
|
+
| Rule import/export packs | v1.2 |
|
|
86
|
+
| Optional magic-byte type detection (opt-in, reads first bytes only) | v1.2 |
|
|
87
|
+
| Other OS support (macOS first) | v2.0 |
|
|
88
|
+
|
|
89
|
+
**Rejected on purpose:** AI/content classification, fuzzy filename matching, auto-delete, cloud sync, telemetry. Each adds cost or risk that contradicts the principles above.
|
|
90
|
+
|
|
91
|
+
## 6. Milestones
|
|
92
|
+
|
|
93
|
+
See `docs/13_ROADMAP.md`. Short form:
|
|
94
|
+
|
|
95
|
+
| Milestone | Delivers |
|
|
96
|
+
|---|---|
|
|
97
|
+
| M1 Core | config, rule engine, scanner, planner, safe mover, `scan/organize/rules` |
|
|
98
|
+
| M2 History | SQLite journal, `history`, `undo`, batches, recovery |
|
|
99
|
+
| M3 Watch | watcher, settle detection, per-folder config, reload |
|
|
100
|
+
| M4 Duplicates | staged hashing, cache, reports |
|
|
101
|
+
| M5 Product | CLI polish, `--json`, service, `doctor`, packaging |
|
|
102
|
+
| M6 Hardening | QA, fault injection, security review, perf budgets. **v1.0** |
|
|
103
|
+
| M7 GUI | PySide6 app. **v1.1** |
|
|
104
|
+
|
|
105
|
+
## 7. Document map
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
README.md what is in this package and how to start
|
|
109
|
+
STARTER_PROMPT.md paste-ready multi-agent kickoff prompt
|
|
110
|
+
AGENTS.md / CLAUDE.md standing rules for every AI agent
|
|
111
|
+
docs/00_PROJECT_PLAN.md this file
|
|
112
|
+
docs/01_ARCHITECTURE.md layers, data flow, threading, contracts
|
|
113
|
+
docs/02_RULE_ENGINE.md matching semantics and test vectors
|
|
114
|
+
docs/03_CONFIG.md schema, validation, locations, edits
|
|
115
|
+
docs/04_SAFE_MOVER.md atomic move and cross-FS protocol
|
|
116
|
+
docs/05_HISTORY_UNDO.md SQLite schema, journaling, undo, recovery
|
|
117
|
+
docs/06_WATCHER.md events, settle, loop prevention, signals
|
|
118
|
+
docs/07_DUPLICATES.md staged duplicate detection
|
|
119
|
+
docs/08_CLI.md every command, flag, output, exit code
|
|
120
|
+
docs/09_SAFETY.md invariants, threat model, denylist
|
|
121
|
+
docs/10_TESTING.md strategy, fixtures, fault injection, CI
|
|
122
|
+
docs/11_PACKAGING.md pyproject, pipx, systemd, Kali notes
|
|
123
|
+
docs/12_GUI.md PySide6 design (v1.1)
|
|
124
|
+
docs/13_ROADMAP.md milestones, waves, backlog
|
|
125
|
+
docs/14_DECISIONS.md decision log with rationale
|
|
126
|
+
docs/15_ACCEPTANCE.md numbered acceptance criteria per milestone
|
|
127
|
+
docs/16_RISKS.md risk register
|
|
128
|
+
prompts/ orchestrator + one prompt per agent
|
|
129
|
+
src/clutterxp/models.py FROZEN data contracts
|
|
130
|
+
src/clutterxp/interfaces.py FROZEN module interfaces
|
|
131
|
+
src/clutterxp/config/schema.py FROZEN config field names/types (Pydantic)
|
|
132
|
+
src/clutterxp/units.py size/duration parsing shared by config and rules
|
|
133
|
+
config.example.yaml annotated example configuration
|
|
134
|
+
packaging/, .github/ systemd unit template, CI workflow
|
|
135
|
+
```
|