# FolderForge (CLI prototype) FolderForge is a rule-based automatic file organizer: you define rules in a JSON file (a filename glob pattern -> a destination subfolder), point it at a messy flat directory (a "Downloads"-style inbox), and it moves every matching file into its correct destination folder in one collision-safe pass. This is a genuinely different mechanic from the sibling tool FilePilot, which performs manual, one-shot BULK RENAME (you run a command, files get renamed once). FolderForge instead runs continuously-reusable ORGANIZATION rules: define them once, re-run "organize" any time your inbox folder gets messy again, and files auto-sort by rule with zero per-run configuration. Dual-pane browsing, live previews, and an instant launcher/search (that last one covered by the sibling FindPilot tool) from the full product concept are on the roadmap -- see ../plan.md for the full product plan. ## Build from source Requires Go 1.24+, no external dependencies. go build -o folderforge . Cross-compile: GOOS=windows GOARCH=amd64 go build -o folderforge.exe . GOOS=darwin GOARCH=amd64 go build -o folderforge . GOOS=darwin GOARCH=arm64 go build -o folderforge . GOOS=linux GOARCH=amd64 go build -o folderforge . ## Usage folderforge organize --rules [--dest-root ] [--apply] folderforge preview --rules [--dest-root ] [--json] folderforge help organize scans non-recursively (files directly inside it only -- organizing rules apply to a flat "inbox"-style folder, not a deep tree) and, for each file, finds the FIRST rule in the rules file whose pattern matches the filename. Rules are evaluated in file order; a file that matches more than one rule's pattern only ever moves according to its first match -- later matching rules are never consulted for that file. Files matching NO rule are left exactly where they are and reported as "unmatched" (this is not an error). Before moving anything, the full batch is validated for COLLISIONS: - two different source files that would land at the exact same destination path, or - a destination path that already exists on disk as a file that is not itself part of this batch's own sources. If ANY collision is found, ALL collisions are reported and the ENTIRE BATCH is aborted: nothing is moved, not even files with no collision of their own. This is the same all-or-nothing collision safety used by the sibling FilePilot tool's rename command. Without --apply, organize is a dry run: it prints the full plan (file -> rule matched -> destination), the unmatched files, and any collisions, and touches nothing on disk. This is the default -- you must pass --apply to actually move files. With --apply (and zero collisions), organize performs every move via os.Rename, creating destination directories as needed with os.MkdirAll. --dest-root sets the root directory under which each rule's "dest" folder is created. If omitted, --dest-root defaults to itself, so destination folders are created as subfolders inside the source directory being organized. preview is a read-only shortcut: it is exactly the dry-run view of organize (there is no --apply flag for preview -- it can never move files), with an added --json flag to print the plan as machine-readable JSON instead of the human-readable text report. Internally it calls the identical planning/collision-detection code as organize, just always with apply=false, so its output is guaranteed to match what "organize" (without --apply) would show for the same inputs. ### Rules file format (JSON) { "rules": [ { "name": "images", "match": "*.png,*.jpg,*.jpeg", "dest": "Images" }, { "name": "documents", "match": "*.pdf,*.docx", "dest": "Documents" } ] } name: a label shown in reports (does not affect matching). match: a comma-separated list of filename glob patterns (Go's filepath.Match shell-style syntax: *, ?, [abc], etc). Matching is case-insensitive: both the filename and each pattern are lowercased before comparison, so "*.PNG" and "*.png" match the same files, and a file named "Photo.PNG" matches pattern "*.png". dest: a folder name (or relative path) created under --dest-root (or under the source directory if --dest-root is omitted). The moved file keeps its original filename -- only its containing folder changes. ## Prebuilt binaries See ../downloads/ for prebuilt binaries (Windows/macOS/Linux) and CHECKSUMS.txt for their SHA-256 hashes. Unsigned indie builds -- Windows SmartScreen and macOS Gatekeeper will warn on first run, expected until a code-signing certificate is in place.