refactor-cli 0.2.0__tar.gz → 0.3.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.
Files changed (38) hide show
  1. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/PKG-INFO +34 -10
  2. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/README.md +32 -9
  3. refactor_cli-0.3.0/TECHNICAL_IMPLEMENTATION.md +337 -0
  4. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/pyproject.toml +2 -2
  5. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/cli.py +135 -22
  6. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/config.py +1 -1
  7. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/gemini.py +53 -0
  8. refactor_cli-0.3.0/refactor/hardware.py +129 -0
  9. refactor_cli-0.3.0/refactor/plan_cache.py +66 -0
  10. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/setup.py +34 -4
  11. refactor_cli-0.3.0/refactor/shim.py +340 -0
  12. refactor_cli-0.3.0/refactor/text.py +103 -0
  13. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/PKG-INFO +34 -10
  14. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/SOURCES.txt +3 -0
  15. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/requires.txt +1 -0
  16. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/scm_file_list.json +3 -0
  17. refactor_cli-0.3.0/refactor_cli.egg-info/scm_version.json +8 -0
  18. refactor_cli-0.2.0/refactor/hardware.py +0 -86
  19. refactor_cli-0.2.0/refactor/shim.py +0 -73
  20. refactor_cli-0.2.0/refactor_cli.egg-info/scm_version.json +0 -8
  21. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/.github/workflows/publish.yml +0 -0
  22. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/.gitignore +0 -0
  23. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/.refactorignore +0 -0
  24. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/LICENSE +0 -0
  25. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/__init__.py +0 -0
  26. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/context_store.py +0 -0
  27. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/ignore.py +0 -0
  28. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/image_scanner.py +0 -0
  29. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/imagga.py +0 -0
  30. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/metadata.py +0 -0
  31. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/organizer.py +0 -0
  32. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/scanner.py +0 -0
  33. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/tree.py +0 -0
  34. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/validation.py +0 -0
  35. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/dependency_links.txt +0 -0
  36. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/entry_points.txt +0 -0
  37. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/top_level.txt +0 -0
  38. {refactor_cli-0.2.0 → refactor_cli-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactor-cli
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: CLI tool to organize files using AI
5
5
  Requires-Python: >=3.8
6
6
  Description-Content-Type: text/markdown
@@ -8,6 +8,7 @@ License-File: LICENSE
8
8
  Requires-Dist: typer
9
9
  Requires-Dist: requests
10
10
  Requires-Dist: python-dotenv
11
+ Requires-Dist: pypdf
11
12
  Dynamic: license-file
12
13
 
13
14
  # refactor.ai
@@ -22,11 +23,15 @@ The CLI entry point in [refactor/cli.py](refactor/cli.py) provides:
22
23
  - `tree` — prints a recursive directory tree through [refactor/tree.py](refactor/tree.py).
23
24
  - `scan` — collects basic file and directory metadata through [refactor/scanner.py](refactor/scanner.py) and [refactor/metadata.py](refactor/metadata.py).
24
25
  - `setup` — configures a multimodal backend (`hosted` or `local`), persisted to `~/.refactor/config.json`.
25
- - `get-image-context` — obtains Imagga V3 tags/captions, asks Gemini to choose a
26
- destination category, and writes JSON context.
27
- - `refactor-images` — automatically refreshes image context, then moves images
28
- into Gemini-selected existing or new category folders (`--copy` is available
29
- when the originals must be retained).
26
+ - `get-image-context` — obtains image tags/captions and asks the configured
27
+ backend to choose a destination category, then writes JSON context.
28
+ - `refactor-images` — automatically refreshes image context with the configured
29
+ backend, then moves images into model-selected category folders (`--copy` is
30
+ available when the originals must be retained).
31
+ - `refactor-text` — uses the configured backend to categorize `.txt`, `.md`,
32
+ `.pdf`, and `.docx` files.
33
+ - `now` — runs `refactor-text` and then `refactor-images`, so mixed folders are
34
+ organized in one invocation.
30
35
  - Path checks through [refactor/validation.py](refactor/validation.py), including existence, directory type, and read access.
31
36
  - Optional filename exclusions through [refactor/ignore.py](refactor/ignore.py).
32
37
 
@@ -39,10 +44,19 @@ After installing `refactor-cli`, the first CLI run prompts:
39
44
  1. **Hosted model (bring your own API keys)**
40
45
  - Prompts for `IMAGGA_API_KEY`, `IMAGGA_API_SECRET`, and `GEMINI_API_KEY`
41
46
  - Saves credentials to `~/.refactor/.env` with restricted file permissions
42
- 2. **Local multimodal recommendation (stub)**
43
- - Detects basic hardware (CPU, memory, NVIDIA GPU/VRAM if available)
44
- - Recommends an OSS multimodal model and records it in config
45
- - Uses a local shim stub today (full local runtime integration is a follow-up)
47
+ 2. **Local multimodal model with Ollama**
48
+ - Detects CPU, memory, and NVIDIA hardware (even if VRAM is temporarily unavailable)
49
+ - Recommends a vision model and verifies the local Ollama runtime
50
+ - Guides the user to install Ollama or pulls the recommended model when Ollama is ready
51
+ - Sends images to the local Ollama API for captions, tags, and category selection; documents are text-extracted locally and categorized through the same Ollama model. No image or document data is uploaded to Refactor services.
52
+
53
+ For a local setup, install [Ollama](https://ollama.com/download), then run the exact command printed by setup, for example:
54
+
55
+ ```shell
56
+ ollama pull gemma3:4b
57
+ ```
58
+
59
+ Run `refactor setup` again after the download finishes. Refactor verifies both the local Ollama service and the selected model before classifying images or documents.
46
60
 
47
61
  For the hosted workflow, local images are uploaded to Imagga and classified with
48
62
  the V3 `/tags` endpoint using `model=light` and `include_caption=true`. The
@@ -57,6 +71,16 @@ Run `refactor refactor-images ./incoming --output-dir ./organized` to generate
57
71
  fresh context and move the images. Add `--dry-run` to inspect destinations or
58
72
  `--copy` to retain the originals.
59
73
 
74
+ Run `refactor now ./incoming --output-dir ./organized` to organize supported
75
+ documents and images together. Hosted mode sends PDFs to Gemini as native
76
+ document input; local mode text-extracts PDFs, text, Markdown, and Word files
77
+ before sending their contents to Ollama.
78
+
79
+ `--dry-run` saves a source-validated organization plan. Running the same command
80
+ without `--dry-run` reuses that plan without another Imagga or Gemini request,
81
+ provided the source files and output directory are unchanged. Any added, removed,
82
+ or modified source file invalidates the plan and triggers fresh classification.
83
+
60
84
  ## Techniques used
61
85
 
62
86
  - **Recursive traversal with `pathlib`** — [refactor/scanner.py](refactor/scanner.py) uses [`Path.rglob()`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rglob) to discover nested paths. [refactor/tree.py](refactor/tree.py) recursively walks directory entries to render a text tree.
@@ -10,11 +10,15 @@ The CLI entry point in [refactor/cli.py](refactor/cli.py) provides:
10
10
  - `tree` — prints a recursive directory tree through [refactor/tree.py](refactor/tree.py).
11
11
  - `scan` — collects basic file and directory metadata through [refactor/scanner.py](refactor/scanner.py) and [refactor/metadata.py](refactor/metadata.py).
12
12
  - `setup` — configures a multimodal backend (`hosted` or `local`), persisted to `~/.refactor/config.json`.
13
- - `get-image-context` — obtains Imagga V3 tags/captions, asks Gemini to choose a
14
- destination category, and writes JSON context.
15
- - `refactor-images` — automatically refreshes image context, then moves images
16
- into Gemini-selected existing or new category folders (`--copy` is available
17
- when the originals must be retained).
13
+ - `get-image-context` — obtains image tags/captions and asks the configured
14
+ backend to choose a destination category, then writes JSON context.
15
+ - `refactor-images` — automatically refreshes image context with the configured
16
+ backend, then moves images into model-selected category folders (`--copy` is
17
+ available when the originals must be retained).
18
+ - `refactor-text` — uses the configured backend to categorize `.txt`, `.md`,
19
+ `.pdf`, and `.docx` files.
20
+ - `now` — runs `refactor-text` and then `refactor-images`, so mixed folders are
21
+ organized in one invocation.
18
22
  - Path checks through [refactor/validation.py](refactor/validation.py), including existence, directory type, and read access.
19
23
  - Optional filename exclusions through [refactor/ignore.py](refactor/ignore.py).
20
24
 
@@ -27,10 +31,19 @@ After installing `refactor-cli`, the first CLI run prompts:
27
31
  1. **Hosted model (bring your own API keys)**
28
32
  - Prompts for `IMAGGA_API_KEY`, `IMAGGA_API_SECRET`, and `GEMINI_API_KEY`
29
33
  - Saves credentials to `~/.refactor/.env` with restricted file permissions
30
- 2. **Local multimodal recommendation (stub)**
31
- - Detects basic hardware (CPU, memory, NVIDIA GPU/VRAM if available)
32
- - Recommends an OSS multimodal model and records it in config
33
- - Uses a local shim stub today (full local runtime integration is a follow-up)
34
+ 2. **Local multimodal model with Ollama**
35
+ - Detects CPU, memory, and NVIDIA hardware (even if VRAM is temporarily unavailable)
36
+ - Recommends a vision model and verifies the local Ollama runtime
37
+ - Guides the user to install Ollama or pulls the recommended model when Ollama is ready
38
+ - Sends images to the local Ollama API for captions, tags, and category selection; documents are text-extracted locally and categorized through the same Ollama model. No image or document data is uploaded to Refactor services.
39
+
40
+ For a local setup, install [Ollama](https://ollama.com/download), then run the exact command printed by setup, for example:
41
+
42
+ ```shell
43
+ ollama pull gemma3:4b
44
+ ```
45
+
46
+ Run `refactor setup` again after the download finishes. Refactor verifies both the local Ollama service and the selected model before classifying images or documents.
34
47
 
35
48
  For the hosted workflow, local images are uploaded to Imagga and classified with
36
49
  the V3 `/tags` endpoint using `model=light` and `include_caption=true`. The
@@ -45,6 +58,16 @@ Run `refactor refactor-images ./incoming --output-dir ./organized` to generate
45
58
  fresh context and move the images. Add `--dry-run` to inspect destinations or
46
59
  `--copy` to retain the originals.
47
60
 
61
+ Run `refactor now ./incoming --output-dir ./organized` to organize supported
62
+ documents and images together. Hosted mode sends PDFs to Gemini as native
63
+ document input; local mode text-extracts PDFs, text, Markdown, and Word files
64
+ before sending their contents to Ollama.
65
+
66
+ `--dry-run` saves a source-validated organization plan. Running the same command
67
+ without `--dry-run` reuses that plan without another Imagga or Gemini request,
68
+ provided the source files and output directory are unchanged. Any added, removed,
69
+ or modified source file invalidates the plan and triggers fresh classification.
70
+
48
71
  ## Techniques used
49
72
 
50
73
  - **Recursive traversal with `pathlib`** — [refactor/scanner.py](refactor/scanner.py) uses [`Path.rglob()`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rglob) to discover nested paths. [refactor/tree.py](refactor/tree.py) recursively walks directory entries to render a text tree.
@@ -0,0 +1,337 @@
1
+ # Refactor.ai: Technical Implementation Record
2
+
3
+ **Document status:** implementation record for research-paper preparation
4
+ **Repository:** `pskth/refactor.ai`
5
+ **Evidence cut-off:** 12 October 2026 (working tree inspected locally)
6
+ **Revision baseline:** commit `c384031dd91bedfea518a629461a4c9f2f632098` plus uncommitted workspace changes present at inspection time
7
+ **Package:** `refactor-cli`; Python CLI entry point: `refactor`
8
+
9
+ ## 1. Purpose and research framing
10
+
11
+ Refactor.ai is an AI-assisted local file-organization command-line tool. It discovers supported files recursively, obtains semantic evidence using either hosted APIs or a local multimodal model, selects exactly one category folder per file, and copies or moves files into category directories. The design includes a dry-run plan cache so a reviewed plan can be executed later without repeating model calls if the input snapshot is unchanged.
12
+
13
+ ### Research problem
14
+
15
+ Unstructured personal or project directories mix images and documents whose filenames alone may not reveal their content. Rule-only organization is brittle, while fully autonomous model-based moves risk incorrect, irreversible placement. The system investigates whether multimodal semantic classification can propose usable folder destinations while maintaining operational controls: constrained output, safe folder names, collision handling, dry runs, source validation, and managed-folder exclusion.
16
+
17
+ ### Candidate paper claims—only where supported
18
+
19
+ 1. Refactor.ai implements an end-to-end, two-backend workflow for categorizing eight image extensions and four document formats.
20
+ 2. The system separates semantic extraction from folder selection in the hosted image pipeline: Imagga produces tags/captions; Gemini selects a consistent category from that evidence and existing output folders.
21
+ 3. A source-validated plan cache prevents reuse after a file is added, removed, resized, or modified (based on absolute path, byte size, and nanosecond mtime).
22
+ 4. The implementation contains safety mechanisms, but **does not yet provide empirical evidence** that they improve semantic accuracy, user trust, or organization quality.
23
+
24
+ Do not claim superior accuracy, reduced cost, privacy guarantees, robustness, or conference-level evaluation until the experiments in Section 14 are run.
25
+
26
+ ## 2. Scope and implementation status
27
+
28
+ | Capability | Status | Evidence |
29
+ | --- | --- | --- |
30
+ | Recursive filesystem tree and metadata inspection | Implemented | `tree`, `scan` commands |
31
+ | Filename-only ignore rules | Implemented | `.refactorignore`, `fnmatch` |
32
+ | Hosted image semantics | Implemented | Imagga upload + V3 tags/captions |
33
+ | Hosted folder selection and document classification | Implemented | Gemini REST calls with JSON-constrained prompts |
34
+ | Local image and document classification | Implemented | Ollama `/api/chat` |
35
+ | Hardware-aware local model recommendation | Implemented | CPU/RAM/NVIDIA probes and rule-based policy |
36
+ | Document support | Implemented | `.txt`, `.md`, `.pdf`, `.docx` |
37
+ | Safe execution controls | Partially implemented | dry run, cache validation, sanitization, collision handling; no interactive approval before non-dry-run moves |
38
+ | Automated tests / benchmark suite | Not found | No test files or test configuration found |
39
+ | Evaluation results | Not found | Existing sample cache shows classifications, not ground-truth metrics |
40
+
41
+ ## 3. System architecture
42
+
43
+ ```text
44
+ CLI command
45
+ -> path validation + filename ignore rules
46
+ -> recursive modality-specific discovery
47
+ -> valid cached plan? ---- yes -> move/copy planner
48
+ | no |
49
+ v v
50
+ selected backend collision resolution
51
+ hosted or local |
52
+ | v
53
+ semantic context / folder file copy or rename
54
+ | |
55
+ +-- save source snapshot ----+-- save managed-category manifest
56
+ ```
57
+
58
+ The source and output directory can be the same. In that case, prior category directories recorded in a manifest are excluded from future scans, preventing repeated reorganization of already managed content.
59
+
60
+ ### Modules and responsibilities
61
+
62
+ | Module | Responsibility |
63
+ | --- | --- |
64
+ | `cli.py` | Typer command registration and workflow orchestration |
65
+ | `validation.py` | Directory existence, type, and read-access checks |
66
+ | `scanner.py`, `tree.py`, `metadata.py` | Baseline local inspection commands |
67
+ | `ignore.py` | Loads `.refactorignore`; matches names with `fnmatch` |
68
+ | `image_scanner.py` | Recursive discovery of supported image formats |
69
+ | `text.py` | Document discovery, extraction, categorization orchestration, text manifests |
70
+ | `imagga.py` | Hosted upload, tag/caption retrieval, image context structure |
71
+ | `gemini.py` | Hosted JSON-output folder selection and document categorization |
72
+ | `shim.py` | Common backend selection plus Ollama implementations |
73
+ | `hardware.py` | Local machine probing and recommendation policy |
74
+ | `setup.py`, `config.py` | Interactive configuration and credentials/config persistence |
75
+ | `plan_cache.py` | Snapshot serialization and exact cache validation |
76
+ | `organizer.py` | Move/copy planning, collision resolution, execution, image manifests |
77
+ | `context_store.py` | Legacy/general image-context JSON serialization |
78
+
79
+ ## 4. User-facing interface
80
+
81
+ | Command | Function |
82
+ | --- | --- |
83
+ | `refactor version` | Prints installed distribution version via `importlib.metadata` |
84
+ | `refactor setup` | Configures hosted or local backend |
85
+ | `refactor tree [PATH]` | Displays a recursive text tree |
86
+ | `refactor scan [PATH]` | Prints name, extension, byte size, and directory flag |
87
+ | `refactor get-image-context PATH` | Produces image semantic context and category decisions |
88
+ | `refactor refactor-images PATH` | Categorizes images and executes/plans move or copy |
89
+ | `refactor refactor-text PATH` | Categorizes supported documents and executes/plans move or copy |
90
+ | `refactor now PATH` | Runs text workflow then image workflow |
91
+
92
+ Common options include `--output-dir`, `--dry-run`, `--move/--copy`, and (for images) `--min-confidence`. Runtime validation requires a readable source directory and an existing output directory.
93
+
94
+ ## 5. Supported inputs and discovery
95
+
96
+ Image discovery accepts `.jpg`, `.jpeg`, `.png`, `.gif`, `.bmp`, `.tiff`, `.tif`, and `.webp`, case-insensitively. Text discovery accepts `.txt`, `.md`, `.pdf`, and `.docx`.
97
+
98
+ Both modality-specific scanners use `Path.rglob("*")`. Ignore matching is only against `item.name`; therefore patterns cannot reliably express relative-path or directory rules. The repository’s current `.refactorignore` contains `*.txt`, so `.txt` files are skipped in runs launched from the repository root even though the text pipeline supports them.
99
+
100
+ ## 6. Backend designs
101
+
102
+ ### 6.1 Hosted backend: Imagga + Gemini
103
+
104
+ Setup asks for `IMAGGA_API_KEY`, `IMAGGA_API_SECRET`, and `GEMINI_API_KEY`. Secrets are persisted to `~/.refactor/.env`; an attempted `chmod(0o600)` follows writing on platforms that support it. Non-secret configuration is stored at `~/.refactor/config.json`.
105
+
106
+ For every discovered image, `ImaggaClient`:
107
+
108
+ 1. uploads the bytes to Imagga V2 `/uploads`;
109
+ 2. queries Imagga V3 `/tags` with `model=light` and `include_caption=true`;
110
+ 3. flattens V3 grouped tags (while retaining compatibility with list-shaped results);
111
+ 4. drops tags below a configurable confidence threshold (default 40.0), sorts descending, and retains at most 10;
112
+ 5. creates an `ImageContext` containing absolute source path, remote upload id, tags, confidences, caption, and a provisional folder.
113
+
114
+ Gemini then receives the image filenames, retained tags, captions, and existing output-directory names. A temperature-zero prompt asks for JSON shaped as `{"folders":[{"id":0,"folder":"example"}]}` and requires one folder per input. The client rejects incomplete responses. Folder values are normalized to lower case and stripped to word characters, spaces, underscores, and hyphens before use.
115
+
116
+ For documents, Gemini receives an equivalent folder-selection prompt. PDFs up to 50 MiB are base64-encoded and sent as `application/pdf` inline data; the prompt states that the PDF is attached. For `.txt`, `.md`, and `.docx`, extracted text is inserted into the prompt. The configured default model string is `gemini-3.8-flash`; `GEMINI_MODEL` overrides it.
117
+
118
+ **Data boundary:** hosted image bytes go to Imagga. Hosted PDFs go to Gemini. Extracted text from hosted non-PDF documents is sent to Gemini. The project does not operate its own remote inference service, but these third-party transmissions must be disclosed in any study and assessed against provider terms and participant-consent requirements.
119
+
120
+ ### 6.2 Local backend: Ollama
121
+
122
+ The local backend checks the Ollama server at `http://127.0.0.1:11434`, verifies the selected model by querying `/api/tags`, and invokes `/api/chat` with `stream: false` and `format: "json"`.
123
+
124
+ For images, the complete source file is base64-encoded and placed in the chat message’s `images` field. The model must return a caption, 1–5 lowercase tags, and a folder. Missing tags become `unclassified`; a missing caption receives a fallback message; and unsafe or empty folders are normalized/fallen back to the first tag. A second JSON-only call receives all per-image semantic contexts and existing folders to choose batch-consistent destinations.
125
+
126
+ For documents, `.txt` and `.md` are read with UTF-8 and replacement on errors; `.docx` is parsed directly from `word/document.xml`; PDFs use `pypdf.PdfReader`. Extracted text is truncated to 60,000 characters. The text is transmitted only to local Ollama according to the process design; actual locality depends on the user’s Ollama configuration and selected model.
127
+
128
+ ### 6.3 Hardware recommendation policy
129
+
130
+ `detect_hardware()` reads CPU core count and physical memory through `os.sysconf`. NVIDIA presence is probed through `nvidia-smi -L`, `lspci`, `/proc/driver/nvidia/gpus`, or Windows WMIC; VRAM is queried from `nvidia-smi` where available. The current deterministic recommendation policy is:
131
+
132
+ | Condition | Model | Rationale encoded in implementation |
133
+ | --- | --- | --- |
134
+ | NVIDIA GPU and at least 8 GiB readable VRAM | `gemma3:4b` | multimodal instruction-following model |
135
+ | NVIDIA GPU but unavailable/low VRAM measurement | `gemma3:4b` | verifies the same supported-file model, subject to memory availability |
136
+ | No NVIDIA GPU, RAM >=16 GiB | `gemma3:4b` | multimodal model with sufficient document context |
137
+ | No NVIDIA GPU, RAM >=8 GiB | `gemma3:4b` | smallest recommended local option; CPU-only execution may be slow |
138
+ | Otherwise | `gemma3:4b` | no lightweight fallback is claimed reliable for all supported file types |
139
+
140
+ This is a heuristic policy, not a measured performance model. A paper should not imply the recommendations are hardware-optimal without latency, memory, and quality benchmarks.
141
+
142
+ ## 7. Classification and folder-selection constraints
143
+
144
+ The system intentionally separates semantic inference from filesystem mutation. Hosted image tags/captions supply evidence, then a generative model chooses a broad reusable folder informed by current categories. In local image mode, a per-image model response is similarly followed by a batch folder-selection response. This two-stage design aims to improve cross-file consistency, but it introduces extra model calls and has not been compared with single-stage or rule-based baselines.
145
+
146
+ Folder sanitizers prevent slashes and most punctuation. They normalize whitespace to underscores and use `uncategorized` for empty results. This reduces path traversal risk from model output. The controls do not semantically validate whether a category is appropriate, and a malicious or malformed response can still produce an undesirable but syntactically safe directory.
147
+
148
+ ## 8. Planning, execution, and cache validity
149
+
150
+ `plan_moves()` maps each classified context to:
151
+
152
+ ```text
153
+ <output_dir>/<primary_folder>/<original_filename>
154
+ ```
155
+
156
+ If a path exists—or has already been selected within the same plan—the planner appends `_1`, `_2`, and so on before the extension. `execute_moves()` creates destination parents, calls `shutil.copy2` for copy mode, or `Path.rename` for move mode, and continues after individual errors while counting successful and failed operations.
157
+
158
+ The CLI defaults to **move**, notwithstanding the older `organizer.py` module docstring and function default that describe copy as default. In the current command implementation, `move=True` and `copy=not move`; users must pass `--copy` to preserve originals. This discrepancy is documentation debt and a safety-relevant paper limitation.
159
+
160
+ ### Plan cache format and behavior
161
+
162
+ Plans are saved as JSON in the source directory by default:
163
+
164
+ | Modality | Default plan file |
165
+ | --- | --- |
166
+ | Images | `.refactor_image_context.json` |
167
+ | Text | `.refactor_text_context.json` |
168
+
169
+ A plan records modality (`kind`), resolved source and output directories, a sorted source snapshot, and context items. Each snapshot element is:
170
+
171
+ ```json
172
+ {"path":"/absolute/path","size":1234,"mtime_ns":1234567890}
173
+ ```
174
+
175
+ Reuse requires exact equality of all fields and of the entire sorted snapshot. Consequently, added, removed, renamed, resized, or mtime-modified inputs cause reclassification. The cache does not include backend choice, model identifier, prompt version, confidence threshold, dependency version, hash of file content, or output-category state; these are threats to reproducibility and cache correctness.
176
+
177
+ `--dry-run` prints planned actions but still generates and saves a plan when a valid cache does not exist. A later normal invocation reuses that valid cache and executes it. It therefore avoids filesystem moves/copies but is not an inference-free preview.
178
+
179
+ ## 9. Managed-category manifests
180
+
181
+ After a fully successful action, the tool writes a modality-specific manifest in the output root:
182
+
183
+ ```json
184
+ {"folders":["category_a","category_b"]}
185
+ ```
186
+
187
+ `managed_category_dirs()` and `managed_text_dirs()` read these manifests and exclude recorded directories only when the resolved source and output roots are the same. This makes repeated in-place runs avoid prior destinations. Manifests are not written after any partial failure, so a retry may rescan content already placed in successfully created folders during the partial run.
188
+
189
+ The supplied `testing-dir` includes a text plan that classifies `GATE2027-IB.pdf` as `exams` and `Hackloop S05.pdf` as `hackathons`, plus a text-category manifest containing those folders. This is an execution artifact, not ground truth and not a valid evaluation result.
190
+
191
+ ## 10. Reproducibility and operational protocol
192
+
193
+ ### Software environment
194
+
195
+ The package metadata declares Python `>=3.8` and dependencies: `typer`, `requests`, `python-dotenv`, and `pypdf`. Builds use `setuptools>=61.0` with `setuptools-scm`; the console script resolves to `refactor.cli:app`. Release automation builds and publishes on GitHub Release publication using PyPI Trusted Publishing.
196
+
197
+ The code uses modern built-in generic annotations such as `list[str]` and `set[Path]`, as well as union syntax (largely covered by postponed annotations). The declared Python 3.8 floor should be verified in CI rather than treated as established compatibility. At inspection, the host environment could compile the package but could not run its CLI because `typer` was absent from that active interpreter; this is an environment observation, not a package defect after normal dependency installation.
198
+
199
+ ### Minimal execution protocol
200
+
201
+ ```shell
202
+ python -m venv .venv
203
+ source .venv/bin/activate
204
+ python -m pip install --upgrade pip
205
+ python -m pip install -e .
206
+
207
+ refactor setup
208
+ refactor now ./incoming --output-dir ./organized --dry-run
209
+ refactor now ./incoming --output-dir ./organized
210
+ ```
211
+
212
+ For a controlled experiment, record the operating system, Python version, package commit, all package versions, backend type, exact model identifier, model-server version, relevant environment variables excluding secrets, command line, input manifest, generated plan, output manifest, wall-clock time, number of requests, and any errors. Store a cryptographic hash for each input in addition to the current size/mtime snapshot.
213
+
214
+ ## 11. Current repository evolution
215
+
216
+ | Date | Commit / milestone | Implementation effect |
217
+ | --- | --- | --- |
218
+ | 20 Apr 2026 | `5068567` | Initial repository |
219
+ | 20 Apr 2026 | `0ec0101` | `version` and `tree` commands |
220
+ | 13 Jun 2026 | `f633fdf` | Validation, ignore list, metadata |
221
+ | 21 Sep 2026 | `79429c9` | Image context and scanning |
222
+ | 5 Oct 2026 | `5ba9654` | Setup, hardware recommendation, backend shim |
223
+ | 11 Oct 2026 | `dc2b94b` / `28ba62e` | Gemini folder selection; release tag `v0.2.0` |
224
+ | 11 Oct 2026 | `d10da46` | Document categorization/organization |
225
+ | 12 Oct 2026 | `c384031` | Source-validated plans and dry-run reuse |
226
+
227
+ The working tree also contains uncommitted changes that add `pypdf`, extend local document support, revise the local model recommendation, and align documentation and CLI messages with the configured-backend abstraction. A formal artifact should either commit these changes or evaluate a clean tagged revision.
228
+
229
+ ## 12. Security, privacy, and safety analysis
230
+
231
+ | Area | Existing control | Residual risk |
232
+ | --- | --- | --- |
233
+ | Credentials | Separate `.env`; attempted owner-only permission | Secrets still reside on disk; no OS keychain or rotation support |
234
+ | Model output paths | Folder-name sanitization | Safe names can still be semantically harmful or collide with user folders |
235
+ | Accidental overwrites | Numeric collision suffixes | No transaction, rollback, or pre-move confirmation |
236
+ | Destructive behavior | `--dry-run`; `--copy` | CLI defaults to moving and no interactive confirmation precedes execution |
237
+ | Cache reuse | Exact resolved paths, size, mtime snapshot | Does not bind plan to model/backend/prompt/category state or content hashes |
238
+ | Recursive scanning | Managed manifests exclude prior destinations in-place | Manifests are written only on all-success execution; symlink behavior is unexamined |
239
+ | Hosted privacy | Explicit external API design | Images/PDFs/text leave the machine; retention and regional processing are provider-dependent |
240
+ | Local privacy | Loopback Ollama endpoint | Locality depends on user runtime/model setup; logs and model provenance are not managed |
241
+
242
+ Potential conference studies involving personal files must obtain consent, avoid publishing sensitive file contents, redact filenames and captions, and document third-party provider data handling. The implementation itself has no telemetry-consent mechanism or data-retention controls.
243
+
244
+ ## 13. Limitations and threats to validity
245
+
246
+ 1. **No benchmark or ground truth:** semantic claims cannot be quantified from the repository artifacts.
247
+ 2. **No automated tests:** behavior is not regression-protected; test coverage is unknown.
248
+ 3. **Provider/model variability:** hosted endpoints and local models may change independently of the code.
249
+ 4. **Prompt and model provenance:** prompts are versioned only implicitly in source; cache payloads omit them.
250
+ 5. **Non-atomic execution:** exceptions are handled per file, allowing partial reorganization with no rollback.
251
+ 6. **Name-only ignores:** users cannot express reliable path-based exclusions; ignore file discovery is relative to the process working directory, not the scanned directory.
252
+ 7. **Mixed output categories:** text and images use separate manifests but both enumerate all existing output folders for model reuse; resulting modality boundaries may be unclear.
253
+ 8. **Text extraction quality:** scanned PDFs, layouts, tables, embedded images, and DOCX elements may extract poorly or not at all.
254
+ 9. **No quality-control interface:** the dry run displays planned paths but does not provide edit/approve/reject per item.
255
+ 10. **Hardware policy is heuristic:** recommendations lack measured resource and accuracy validation.
256
+ 11. **Move semantics:** `Path.rename` may fail across filesystems; behavior is not covered by fallback copy-and-delete logic.
257
+ 12. **Time/cost accounting absent:** request count, token usage, rate limiting, retries, and latency are not instrumented.
258
+
259
+ ## 14. Research-paper-ready evaluation plan
260
+
261
+ ### Research questions
262
+
263
+ - **RQ1:** How accurately does Refactor.ai assign files to human-defined categories?
264
+ - **RQ2:** Does two-stage semantic extraction plus batch folder selection improve category consistency over direct single-stage categorization?
265
+ - **RQ3:** How do hosted and local backends compare in accuracy, latency, cost, and privacy exposure?
266
+ - **RQ4:** Do dry-run plans and safe execution mechanisms reduce unsafe or unwanted filesystem actions?
267
+
268
+ ### Dataset protocol
269
+
270
+ Create a stratified, consented dataset containing images, text/Markdown, born-digital PDFs, scanned PDFs, and DOCX files. Separate development, validation, and held-out test directories by user/domain. Define a controlled taxonomy and allow an `other/uncategorized` option. At least two independent annotators should label intended categories; report agreement (for example Cohen’s kappa) and adjudication policy. Never use the included `testing-dir` alone as a benchmark.
271
+
272
+ ### Baselines and ablations
273
+
274
+ | System | Purpose |
275
+ | --- | --- |
276
+ | Filename/extension rule baseline | Tests value beyond trivial cues |
277
+ | Top Imagga tag as folder | Tests Gemini batch folder-selection contribution |
278
+ | Single local-model call per image | Tests value of local batch consistency pass |
279
+ | Hosted two-stage pipeline | Main hosted condition |
280
+ | Local Ollama pipeline | Main private/local condition |
281
+ | Refactor.ai without existing-folder context | Tests reuse-context contribution |
282
+
283
+ ### Metrics
284
+
285
+ Report macro/micro precision, recall, and F1 for a fixed taxonomy; top-k accuracy if suggestions are exposed; normalized mutual information or adjusted Rand index for clustering-style category consistency; percentage of safe executable paths; collision rate; partial-failure rate; dry-run-to-execution cache-reuse rate; files/hour; end-to-end latency; requests/file; cost/file; and human acceptance/edit/rejection rates. Use paired confidence intervals or bootstrap intervals and predefine exclusion criteria.
286
+
287
+ ### Required artifacts
288
+
289
+ - Versioned source commit and dependency lockfile.
290
+ - Versioned prompts, backend/model identifiers, and API/runtime versions.
291
+ - De-identified input manifest with cryptographic hashes and ground-truth labels.
292
+ - Raw model outputs, sanitized outputs, plans, execution summaries, and error logs.
293
+ - Experiment scripts and fixed randomization/ordering policy.
294
+ - An ethics/privacy statement, data license, and participant consent protocol when applicable.
295
+
296
+ ## 15. Recommended implementation work before submission
297
+
298
+ 1. Add unit and integration tests for scans, ignores, sanitizers, cache invalidation, collisions, move/copy behavior, partial failures, and both backend adapters with mocked HTTP.
299
+ 2. Add a mandatory confirmation or explicit `--yes` flag for move mode; make copy the CLI default or clearly version the behavior change.
300
+ 3. Make execution transactional where possible: preflight every destination, log an operation journal, and provide rollback.
301
+ 4. Add content hashes and backend/model/prompt/config fingerprints to plans.
302
+ 5. Record structured metrics (latency, requests, cost estimates, errors, cache decisions) without collecting sensitive content by default.
303
+ 6. Resolve source/output and ignore-file semantics; support path-aware exclusions and symlink policy.
304
+ 7. Pin and validate supported Python versions in CI, especially the declared 3.8 baseline.
305
+ 8. Add a review file or interactive approval flow for per-item category corrections and use the corrections as evaluation data—not silent training data.
306
+
307
+ ## 16. References and implementation evidence
308
+
309
+ ### Repository evidence
310
+
311
+ - [README](README.md)
312
+ - [Package configuration](pyproject.toml)
313
+ - [CLI orchestration](refactor/cli.py)
314
+ - [Backend abstraction and Ollama implementation](refactor/shim.py)
315
+ - [Hosted Imagga client](refactor/imagga.py)
316
+ - [Hosted Gemini client](refactor/gemini.py)
317
+ - [Document pipeline](refactor/text.py)
318
+ - [Plan-cache implementation](refactor/plan_cache.py)
319
+ - [Organization/execution implementation](refactor/organizer.py)
320
+ - [Configuration and setup](refactor/config.py) and [interactive setup](refactor/setup.py)
321
+ - [Hardware policy](refactor/hardware.py)
322
+ - [PyPI publication workflow](.github/workflows/publish.yml)
323
+ - [Sample text plan](testing-dir/.refactor_text_context.json) and [sample category manifest](testing-dir/.refactor_text_categories.json)
324
+
325
+ ### External technical references used by the implementation
326
+
327
+ - [Python pathlib documentation](https://docs.python.org/3/library/pathlib.html)
328
+ - [Python fnmatch documentation](https://docs.python.org/3/library/fnmatch.html)
329
+ - [Typer documentation](https://typer.tiangolo.com/)
330
+ - [Requests documentation](https://requests.readthedocs.io/)
331
+ - [python-dotenv documentation](https://bbc2.github.io/python-dotenv/)
332
+ - [pypdf documentation](https://pypdf.readthedocs.io/)
333
+ - [setuptools documentation](https://setuptools.pypa.io/)
334
+ - [setuptools-scm documentation](https://setuptools-scm.readthedocs.io/)
335
+ - [Ollama API documentation](https://docs.ollama.com/api)
336
+ - [Google Gemini API documentation](https://ai.google.dev/gemini-api/docs)
337
+ - [Imagga API documentation](https://docs.imagga.com/)
@@ -4,7 +4,7 @@ dynamic = ["version"]
4
4
  description = "CLI tool to organize files using AI"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
7
- dependencies = ["typer", "requests", "python-dotenv"]
7
+ dependencies = ["typer", "requests", "python-dotenv", "pypdf"]
8
8
 
9
9
  [project.scripts]
10
10
  refactor = "refactor.cli:app"
@@ -13,4 +13,4 @@ refactor = "refactor.cli:app"
13
13
  requires = ["setuptools>=61.0", "setuptools-scm"]
14
14
  build-backend = "setuptools.build_meta"
15
15
 
16
- [tool.setuptools_scm]
16
+ [tool.setuptools_scm]