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.
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/PKG-INFO +34 -10
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/README.md +32 -9
- refactor_cli-0.3.0/TECHNICAL_IMPLEMENTATION.md +337 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/pyproject.toml +2 -2
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/cli.py +135 -22
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/config.py +1 -1
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/gemini.py +53 -0
- refactor_cli-0.3.0/refactor/hardware.py +129 -0
- refactor_cli-0.3.0/refactor/plan_cache.py +66 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/setup.py +34 -4
- refactor_cli-0.3.0/refactor/shim.py +340 -0
- refactor_cli-0.3.0/refactor/text.py +103 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/PKG-INFO +34 -10
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/SOURCES.txt +3 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/requires.txt +1 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/scm_file_list.json +3 -0
- refactor_cli-0.3.0/refactor_cli.egg-info/scm_version.json +8 -0
- refactor_cli-0.2.0/refactor/hardware.py +0 -86
- refactor_cli-0.2.0/refactor/shim.py +0 -73
- refactor_cli-0.2.0/refactor_cli.egg-info/scm_version.json +0 -8
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/.gitignore +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/.refactorignore +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/LICENSE +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/__init__.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/context_store.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/ignore.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/image_scanner.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/imagga.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/metadata.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/organizer.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/scanner.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/tree.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor/validation.py +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/dependency_links.txt +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/entry_points.txt +0 -0
- {refactor_cli-0.2.0 → refactor_cli-0.3.0}/refactor_cli.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
|
26
|
-
destination category,
|
|
27
|
-
- `refactor-images` — automatically refreshes image context
|
|
28
|
-
into
|
|
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
|
|
43
|
-
- Detects
|
|
44
|
-
- Recommends
|
|
45
|
-
-
|
|
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
|
|
14
|
-
destination category,
|
|
15
|
-
- `refactor-images` — automatically refreshes image context
|
|
16
|
-
into
|
|
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
|
|
31
|
-
- Detects
|
|
32
|
-
- Recommends
|
|
33
|
-
-
|
|
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]
|