Technical Manual chest(1) Manual

CHEST System Reference

Complete reference guide covering cross-platform installation, sorting grammar, high-speed fuzzy search, transaction rollbacks, and out-of-process RPC plugins.

Installation

Option 1: Go Install (Recommended)

Compiles directly from the official Go module proxy into your local toolchain:

$ go install github.com/Aswanidev-vs/chest/cmd/chest@latest

Ensure your GOBIN directory (typically ~/go/bin on Unix or %USERPROFILE%\go\bin on Windows) is present in your system PATH.

Option 2: Automated Linux & macOS One-Liner

Downloads and runs the automated bash installation script:

$ curl -sSL https://raw.githubusercontent.com/Aswanidev-vs/chest/main/install.sh | bash

Checks your local Go environment, builds from source, executes a self-test sanity check, and reports your PATH configuration.

Option 3: Automated Windows PowerShell

Downloads and executes the native PowerShell script (PowerShell 5.1+ or PowerShell 7+):

> irm https://raw.githubusercontent.com/Aswanidev-vs/chest/main/install.ps1 | iex

Option 4: Build from Source

Clone the repository and compile using Make or Go:

$ git clone https://github.com/Aswanidev-vs/chest.git
$ cd chest
$ make build

Quick Start

Test sorting your Downloads directory with a safe dry-run (no files are touched):

$ chest sort ~/Downloads -p downloads --dry-run

Apply the plan once satisfied:

$ chest sort ~/Downloads -p downloads

Accidentally sorted the wrong folder? Instantly revert all moved files to their exact previous paths:

$ chest undo
Always preview with --dry-run.
The dry-run flag computes the complete move graph, identifies any name collisions, and lists targets without touching the disk.

Architecture & Guarantees

CHEST is architected as five decoupled subsystems:

Subsystem Underlying Engine Responsibility
Scanner fastwalk concurrency Multi-threaded recursive directory traversal with exclusion filters and OS-agnostic hidden file logic.
Scorer fzf/src/algo FuzzyMatchV2 Microsecond scoring of filenames, extensions, and content grep matching.
Planner Directed Move Graph Collision detection and transactional history logging to ~/.chest/history.db.
Storage Index Embedded Pure-Go SQLite Metadata caching, duplicate hashing, and storage intelligence at ~/.chest/index.db.
Metadata Signature + Extractor Registry Content-signature format detection (magic bytes first, extension fallback) and embedded metadata extraction — EXIF, QuickTime, PDF, audio tags, and ZIP-based OOXML/ODF/EPUB properties — via an open registry that supports custom formats.

Command Reference

chest sort

Organizes target directory according to preset rules or custom conditions.

Flag Type Description
-p, --preset string Built-in rule bundle: downloads, media, developer, documents, photos.
-n, --dry-run boolean Simulate organization without moving any files.
--rule string Custom predicate rule: "type=image && size>10MB -> HighRes/". Rules can match on extension= (filename extension) or format= (content-detected format — routes a file even if its extension is missing or renamed), plus embedded metadata fields: camera=, artist=, album=, genre=, title=, composer=, encoder=, software=, codec=, duration=, width=, height=, or meta.<key>= for any other extracted field. Custom binary formats can be registered programmatically via the metadata extractor registry.
-y, --yes boolean Skip interactive confirmation prompt and execute immediately.
-i, --into string Override destination root directory.
-d, --date boolean Group files by date. Default source is modification time and default granularity is year, e.g. 2024/, 2025/, and 2026/.
--date-source string Date source for --date and date placeholders: modified (default), auto (embedded media date with modification-time fallback), or taken (embedded date only), or filename (mine the date from the filename: IMG_20230115_..., WhatsApp Image 2024-05-01..., report_20250126.pdf; modification-time fallback). Embedded dates are extracted from EXIF DateTimeOriginal/CreateDate, QuickTime mvhd, PDF Info dictionary, ID3/Vorbis/FLAC/OGG tags, RIFF-INFO chunks, and ZIP-based OOXML/ODF/EPUB core properties.
--collision string Destination-collision policy: skip, rename, replace, abort. Default (flag omitted): any occupied destination, whether already on disk or claimed by another source in the plan, is auto-renamed (photo (1).jpg), case-normalized per platform, and listed in the dry-run CONFLICTS block. An explicit skip leaves the later file in place instead.
--hash-collisions boolean Hash only files in a destination conflict. Identical SHA-256 content is treated as a duplicate and is skipped by default; different content remains protected by the normal auto-rename chain.
--keep-identical boolean With --hash-collisions, retain byte-identical conflict participants by assigning the next safe name, such as photo (1).jpg, instead of leaving the duplicate in place. The decision is reported in CONFLICTS.
--json boolean With --dry-run, print the plan as JSON (operations, folders to create, conflicts) instead of the human report.
--date-granularity string Date folder granularity: year (default), month (YYYY/Jan), or day (YYYY/Jan/05). Month folders use month names unless --month-format number selects YYYY/01 / YYYY/01/05.
--month-format string How --date and the {month} placeholder render months: name (default, Jan, Feb), or number (zero-padded, 01, 02). Only affects month granularity, day granularity, and {month}; years and days stay numeric.
-f, --format boolean Group files into folders by their content-detected format (e.g. MP4/, MP3/, PNG/). Detection reads the file's magic-byte signature first and falls back to its extension, so a renamed or extension-less file is still classified correctly. Custom formats registered programmatically (e.g. .gocut) participate too. Use --into to wrap the folders (e.g. --format --into out → out/MP4/).
--allow-system boolean Bypass system path protection guard. Required when targeting OS-level directories (e.g. C:\Windows, /usr). Use with extreme caution.

chest.toml configuration

Rules persist in a chest.toml file. CHEST walks up from the target directory to find a project config, and also reads ~/.chest/chest.toml for user-wide defaults. Rule sources stack with precedence flags > --rule > project chest.toml > user chest.toml > preset.

# chest.toml
default_preset = "downloads"

[[rules]]
name = "Large videos"
priority = 100
destination = "Videos/HiFi"
conditions = [
  { field = "type", operator = "=", value = "Video" },
  { field = "size", operator = ">", value = "2GB" },
]

[[rules]]
name = "Camera photos"
priority = 90
destination = "Photos/Camera"
conditions = [{ field = "camera", operator = "=", value = "Canon" }]

Convenience helpers (extensions, type, min_size, max_size) work exactly as in built-in presets, so the same shape is accepted everywhere.

chest rule check

Validates the rule set that would apply to a directory — flags, presets, and any discovered chest.toml — without scanning or moving files. It surfaces the problems that would otherwise be silently ignored at evaluation time: unparseable min_size/max_size values (which used to make a rule match everything), unsupported field/operator combinations, and invalid regexes.

$ chest rule check ~/Downloads -p media
✓ 12 rule(s) validated, no problems found.

$ chest rule check . --rule "name =~ '[unclosed' -> Images"
1 problem(s) found in 1 rule(s):
  • rule 'name =~ '[unclosed' -> Images': invalid regex "[unclosed" for field "name"

chest repl

Build a sort rule set interactively, with a preview that re-plans after every edit. The target directory is scanned once; from then on add, edit and rm refine the in-memory rules while preview recomputes the plan from the current set and renders the same dry-run report as sort --dry-run. preview is strictly read-only — it creates no folders and moves nothing. Only apply executes, and it records history so the run can be reverted with chest undo.

$ chest repl ~/Downloads
CHEST rules builder — /home/you/Downloads (128 file(s) scanned)
Type 'help' for commands.
chest> add type=Video -> Videos
added type=Video -> Videos
chest> add size>1GB -> Large Files
added size>1GB -> Large Files
chest> preview
CHEST PLAN
────────────────────────

movie.mkv
  FROM: movie.mkv
  TO:   Videos/movie.mkv
  WHY:  type = Video
────────────────────────
1 files would be moved.
No changes made.
chest> apply
1 file(s) will be moved.
Continue? [y/N]: y
[DONE] 1 file(s) organized into 1 folder(s).
chest> save media-rules
saved 2 rule(s) to /home/you/.chest/templates/media-rules.toml
chest> quit

Prompt commands

Command Description
help List the prompt commands.
list Show the current rule set, numbered as you would type it.
add <rule> Add a rule using the same syntax as sort --rule. Rules added earlier take priority.
edit <n> <rule> Replace the 1-based rule n, keeping its priority.
rm <n> Remove the 1-based rule n.
preset [name] Load a built-in preset, replacing the current rules. With no argument, list the available presets.
scan Re-read the target directory, picking up files added while the session is open.
preview Re-plan from the current rules and print the dry run. Mutates nothing.
apply Execute the current plan after a [y/N] confirmation (skipped with --yes).
save <name> Save the rule set to ~/.chest/templates/<name>.toml.
load <name> Replace the current rules from a saved template.
templates List saved templates.
quit / exit Leave the builder. Ctrl-D also exits.
Flag Type Description
-p, --preset string Start the session from a built-in rule bundle: downloads, media, developer, documents, photos.
-i, --into string Base destination directory that category folders are placed inside.
-r, --recursive boolean Include subdirectories in the scan.
-H, --hidden boolean Include hidden files and folders.
-x, --exclude string Skip files or folders, e.g. .git,node_modules.
-c, --collision string Collision policy: skip (default), rename, replace, abort.
-y, --yes boolean Skip the confirmation prompt on apply.

Rules can be saved to ~/.chest/templates/<name>.toml and loaded again later. Templates are stored as TOML rather than as rule strings, so a saved rule keeps every field a built-in preset rule can carry — including size bounds and extension lists that the --rule text syntax cannot express. Template names may not contain path separators. Unlike sort, repl refuses protected system directories outright and has no --allow-system escape hatch.

Walkthrough: build, then update

A complete session in five steps. The directory is scanned once; from then on every command re-plans from the current rules.

1. Build — start broad, check as you go
$ chest repl ~/Downloads
chest> add type=Video -> Videos
added type=Video -> Videos
chest> add type=Image -> Images
added type=Image -> Images
chest> list
2 rule(s):
   1. type=Video -> Videos
   2. type=Image -> Images
chest> preview
CHEST PLAN
────────────────────────

bigvideo.mp4
  FROM: bigvideo.mp4
  TO:   Videos/bigvideo.mp4
  WHY:  type = Video

movie.mp4
  FROM: movie.mp4
  TO:   Videos/movie.mp4
  WHY:  type = Video

photo.png
  FROM: photo.png
  TO:   Images/photo.png
  WHY:  type = Image
────────────────────────
3 files would be moved.
2 folders would be created.
No changes made.

preview is strictly read-only: it re-plans from the current rules on every call, so it always shows what apply would do, and it never creates a folder or moves a file.

2. Update — mind the priority trap

Rules are evaluated top to bottom and the first match wins, so a narrow rule added after a broad one is dead code. This looks right in list, but it never fires — rule 1 already claimed the file.

chest> add type=Video && size>2MB -> Videos/HiFi
added type=Video && size>2MB -> Videos/HiFi
chest> preview

bigvideo.mp4
  TO:   Videos/bigvideo.mp4     <-- still rule 1, the HiFi rule is dead
  WHY:  type = Video

Fix it by re-ordering: drop the broad rule, then add the narrow one first.

chest> rm 1
removed rule 1 (1 remaining)
chest> add type=Video && size>2MB -> Videos/HiFi
chest> add type=Video -> Videos
chest> list
3 rule(s):
   1. type=Image -> Images
   2. type=Video && size>2MB -> Videos/HiFi
   3. type=Video -> Videos
chest> preview

bigvideo.mp4
  TO:   Videos/HiFi/bigvideo.mp4
  WHY:  type = Video, size > 2MB

movie.mp4
  TO:   Videos/movie.mp4
  WHY:  type = Video
3. Update one rule in place

edit replaces a rule while keeping its position and priority, so it never disturbs the order around it. Reach for it when you only want to retune one line.

chest> edit 1 type=Image -> Photos/Screenshots
replaced rule 1 type=Image -> Photos/Screenshots
4. Reuse the set
chest> save downloads-v2
saved 2 rule(s) to ~/.chest/templates/downloads-v2.toml
chest> templates
- downloads-v2
chest> load downloads-v2
loaded 2 rule(s) from template 'downloads-v2'

load replaces the whole current rule set, and restores the priority order that was saved.

5. Commit

apply is the only command that moves files. It records history, so the run can be reverted with chest undo; pass --yes to skip the confirmation in scripts.

chest> apply
3 file(s) will be moved.
Continue? [y/N]: y
[DONE] 3 file(s) organized into 3 folder(s).

Rule syntax cheat-sheet

Field Aliases Matches
type — Image, Video, Audio, Document, Archive, Executable, Code, Other.
extension ext The filename extension, e.g. extension=mp4 or extension=.mp4.
format — The content-detected format (magic bytes first), so a renamed file still matches.
size — A size bound such as size>1GB or size<=100MB.
name filename The filename. Supports contains, starts_with, ends_with, glob and =~regex.
date modified, modtime A date or range, e.g. date>2025-01 or date=2024.
taken_date takendate The embedded media date (EXIF, QuickTime, PDF, ID3…).
mime mimetype The detected MIME type.

Destinations support the placeholders {ext}, {date}, {year}, {month} and {day}, and clauses combine with &&. A bare pattern such as *.mp4 -> Videos is shorthand for matching the filename. The type values are exactly the eight category names; anything else simply never matches.

Ultra-fast filesystem search using fastwalk for parallel multi-core directory traversal and the fzf algorithm for scoring. Patterns match fuzzily by default (switch to exact substring with -x or regular expressions with -r), and filters can be freely combined: file category, extension, size, modification date, and streaming content grep that reports line numbers and snippets. Results are color-coded by category, or emitted as JSON for scripting.

$ chest search "movie"             # Fuzzy filename search (default mode)
$ chest search --type video --size ">1GB"  # Combine category + size filters
$ chest search -e pdf,docx -m "<30d" ~/Documents
$ chest search -c "TODO" internal/   # Grep inside files, with line numbers
$ chest search -e go,md -i chest -j  # JSON output for scripting
Flag Type Description
[pattern] [path] arguments Filename/path pattern to match, and the root directory to traverse (default .).
-t, --type string Filter by category: image, video, document, audio, code, archive, etc. Plugin-registered categories are included too.
-e, --ext string Filter by comma-separated extensions: pdf,docx,xlsx.
-s, --size string Size condition: >100MB, <10KB, >=1GB, =5MB.
-m, --modified string Modification time condition: >30d (newer than), <7d (older than), or a calendar date like 2026-01-01.
-c, --content string Grep inside file contents for matching string; returns line number and snippet.
-i, --ignore-case boolean Case-insensitive matching. Enabled by default.
-r, --regex boolean Treat the pattern as a regular expression instead of fuzzy text.
-x, --exact boolean Exact substring matching instead of fuzzy ranking.
-H, --hidden boolean Include hidden files and folders in the traversal.
-l, --limit int Stop after returning n matches.
-j, --json boolean Output matches in structured JSON format for scripting.

chest watch

Background daemon monitoring real-time filesystem changes using fsnotify. New files are sorted as they arrive, folder creations and removals are reported instantly, and --initial sorts files that are already present before watching begins. Installed plugin classifiers and their GetCustomRules() apply here exactly as in sort, and each debounce window is recorded as a single history entry (one undo reverts the whole batch).

$ chest watch ~/Downloads --preset media --debounce 500ms
$ chest watch ~/Desktop --initial  # Sort existing files first, then watch
Flag Type Description
-p, --preset string Rule bundle: downloads, media, developer, documents, photos.
--debounce duration Wait interval before sorting a newly created file. Default 500ms.
-i, --initial boolean Sort files already present in the folder first, then keep watching for new ones.
--allow-system boolean Bypass system path protection when watching OS-level directories.

chest schedule

Periodic organizing without a foreground process. Schedules live in ~/.chest/schedules.toml and are registered with the OS scheduler: a managed crontab block on Linux/macOS, a scheduled task on Windows (daily cron expressions; more complex expressions can be printed with --print-cron and wired up manually).

$ chest schedule add --cron "0 2 * * *" --preset downloads ~/Downloads
$ chest schedule list
$ chest schedule run-now s1
$ chest schedule remove s1
Flag Type Description
--cron string 5-field cron expression (minute hour dom month dow), e.g. "0 2 * * *".
--preset / --rule string What the scheduled run applies; falls back to the target's chest.toml / default preset.
--print-cron boolean Print the crontab line instead of installing anything (also works for expressions schtasks cannot express).

chest undo & history

Inspect previous operations or rollback a batch of moves. Every run is recorded honestly: Complete when every file moved, Partial when some failed (the failures are listed in the entry), Failed when nothing moved. Undo validates the entire batch before restoring anything and rolls back cleanly if interrupted.

$ chest history        # Display recorded operation history
$ chest undo           # Undo the most recent run
$ chest undo 4         # Undo specific transaction by ID
Flag Type Description
--allow-system boolean Bypass system path guard when undoing into protected OS directories.

chest index & clean

Index file paths and hashes into SQLite for fast duplicate discovery. Indexing is incremental (unchanged files are skipped), runs in parallel across CPU cores, shows a live progress bar, and re-indexing prunes rows for files that were deleted/moved or are now covered by --except. The --except flag is available on index, stats, analyze, and duplicates:

$ chest index ~/Documents --hash    # Incremental parallel SHA-256 with progress bar
$ chest index ~/Projects --except node_modules,venv # Skip noisy dirs on index
$ chest clean                       # Empty cached index (re-index to rebuild)
$ chest clean --all                 # Reset index database completely
$ chest clean --history           # Also wipe undo history (~/.chest/history.json)

stats, duplicates & analyze

Storage intelligence commands. A scoped run always refreshes its folder first; a bare run re-populates an empty cache from the current directory instead of reporting all zeros. --except prunes noisy directories (and drops their previously-indexed rows), and --refresh forces a fresh re-scan with stale-row purge. Largest files are reported with absolute paths. All three commands (and history) export machine-readable output with --json, --csv, or --out <file>:

$ chest stats ~/Projects --except node_modules # Auto-refresh scope + category/size breakdown
$ chest stats --refresh          # Force a fresh re-scan + purge of the current dir
$ chest duplicates ~/Downloads  # Locate byte-for-byte duplicates
$ chest analyze ~/Documents --refresh # Force fresh audit (zero-byte + stale >180d)
$ chest stats --json --out stats.json # Machine-readable export to file
$ chest duplicates --csv  # CSV rows for scripts

chest speedtest

Measures connection speed without an API key by running independent Ookla and Cloudflare tests side by side. The report includes download, upload, ping, and jitter. Select a backend with --ookla or --cloudflare, or use --json for machine-readable output. Use --disk for disk benchmark mode, measuring local storage throughput (sequential read/write MB/s plus random 4K IOPS) with direct, unbuffered I/O so reads bypass the OS page cache.

$ chest speedtest                 # Run both tests and compare
$ chest speedtest --ookla        # Run the accurate Ookla test only
$ chest speedtest --cloudflare # Run the Cloudflare reference test only
$ chest speedtest --json        # Emit structured JSON instead of the table
$ chest speedtest --disk        # Run disk benchmark mode
$ chest speedtest --disk --disk-size 512 --disk-path D: # Larger test file on drive D:
Flag Type Description
--ookla boolean Run only the Ookla test against the nearest available speedtest.net server.
--cloudflare boolean Run only the Cloudflare test against speed.cloudflare.com.
--json boolean Output structured JSON instead of the human-readable comparison table.
--disk boolean Run disk benchmark mode: sequential read/write MB/s and random 4K read/write IOPS, reported in a -- Disk section (and in the detail field of JSON output).
--disk-path string Directory in which the temporary benchmark file is created. Defaults to the OS temp directory; pass a folder on another drive to test that drive.
--disk-size int (MiB) Size of the temporary benchmark file. Defaults to 256 MiB and is capped at 8192 MiB.

Output & diamond gauge

The default report groups successful measurements by backend. Download and upload rows include a 16-cell gauge: ◆ marks a filled cell and ◇ marks an empty cell. Transfer bars are scaled against the fastest successful download or upload in the comparison, making relative throughput easy to scan; ping rows show latency and jitter without a gauge. Failed measurements are marked FAIL with the error detail, while --json returns the same measurements as structured rows.

Disk benchmark mode reports a -- Disk section listing write/read throughput and 4K read/4K write IOPS. Reads use direct (unbuffered) I/O where the platform supports it so results are not inflated by the OS page cache; on platforms without direct I/O support the mode line says buffered instead. Writes are flushed to the device before timing ends. The disk benchmark uses a single temporary file that is removed when the run completes.

$ chest speedtest
  CHEST SPEED TEST
  Ookla
    download 800.00 Mbps  [◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆]
    upload   200.00 Mbps  [◆◆◆◆◇◇◇◇◇◇◇◇◇◇◇◇]
  Cloudflare
    download 400.00 Mbps  [◆◆◆◆◆◆◆◆◇◇◇◇◇◇◇◇]
    upload   100.00 Mbps  [◆◆◇◇◇◇◇◇◇◇◇◇◇◇◇◇]

chest battery

Reports battery charge, power state, and drain rate. Aliased as power and batt. With no flags it takes a single reading; --record and --daemon build the history database that the period reports (--day, --week, --month, --year) read back. Readers are per-platform, and every report names the source it read from.

Several flags only mean something alongside another one — --apps apportions activity over a window, --top sizes the --apps list, --interval sets a sampling rate, and --db names the history database. Given on their own they say which mode they need rather than printing a report identical to a run without them.

$ chest battery                       # Single reading
$ chest battery --low 20 --fail-under 10  # Mark 20% or below as LOW, exit 3 at 10%
$ chest battery --seconds 30m --interval 5s # Measure a drain rate over 30 minutes
$ chest battery --record --daemon   # Record history continuously until Ctrl+C
$ chest battery --week --apps --top 8   # Period report, with per-app estimates
$ chest battery --realtime     # Redraw the reading in place until Ctrl+C
Flag Type Description
--record boolean Append each reading to the history database. Works on its own: battery --record records the single reading it takes.
--seconds <duration> duration Measure over this long instead of taking a single reading.
--interval <duration> duration Sampling interval while measuring. Default 2s. Needs a mode that measures: --seconds, --realtime or --daemon. The sampler floors it at 5s and reports the interval actually in force.
--json boolean Output the reading as machine-readable JSON instead of the table.
--low <pct> int Report charge at or below this percentage as low. 0 disables the warning. Must be 0–100.
--fail-under <pct> int Exit with code 3 when charge is at or below this percentage, for scripted checks. 0 disables it. Must be 0–100.
--db <path> string History database path. Default ~/.chest/battery.db. Names where history is read or written, so it needs --record or a period flag.
--apps boolean Report per-application activity apportioned from measured CPU and I/O. An estimate, see below. Needs a window to apportion over: a period flag or --seconds.
--top <n> int How many applications to list with --apps. Default 5, minimum 1. Only meaningful alongside --apps.
--daemon boolean Keep recording in the foreground until Ctrl+C.
--realtime boolean Redraw the reading in place until Ctrl+C.
--day / --week / --month / --year boolean Report that period from recorded history. Mutually exclusive.

Reading output

The report lists charge, power state, and drain rate, then the fields the platform supplies: power draw, battery health, charge cycles, and time remaining. Anything the platform does not expose is listed under Not reported with the reason — including time remaining, which exists only while discharging — rather than printed as a zero. --json returns the same reading as structured rows, with a *_known flag beside every measurement and a rate_provenance field, so a consumer cannot read an estimate as a reading.

$ chest battery --json
{
  "source": "GetSystemPowerStatus",
  "full_capacity_wh": 35.74,
  "design_capacity_wh": 45,
  "health_pct": 79,
  "charge_cycles": 1541
}

Excerpt from a real Windows laptop: 45 Wh design capacity, 35.74 Wh full capacity, 79% health, 1541 charge cycles, and time remaining while discharging. The full JSON also carries charge, power state, wattage, and rate fields.

Drain rate is an estimate

Charge is quantised to whole percentage points.
A drain rate is derived from the change in charge over a window, so the window has to be long enough to contain a change — roughly one point per 70 seconds on Windows. A window shorter than that reports n/a with the reason instead of a fabricated number; a computed rate is prefixed with ~ and its detail line names the window and the sample count.

History and period reports

--record appends each reading; --daemon keeps recording in the foreground until Ctrl+C. Readings go to their own SQLite database at ~/.chest/battery.db, separate from the file index at ~/.chest/index.db, so chest clean --all cannot destroy it. chest clean --battery clears it and is opt-in only.

Periods are cut in UTC, and --week is the ISO week, starting Monday. A period report prints its coverage first, then the charge change across the period, the drain rate, the charging and discharging sample counts, the top applications, and a vertical bar chart of their share underneath the table.

Per-application figures are estimates

No platform reports per-application battery use to an unprivileged process.
--apps apportions the measured CPU and I/O into per-application numbers, and the output states this limitation itself. A lit display, a wireless radio, and thermal management draw real power at near-zero CPU and are invisible to the method.

Platform sources

Windows, Linux, and macOS are supported, each with its own reader. Windows takes charge, mains state, and time remaining from GetSystemPowerStatus, then reads cycle count, full and design capacity, health, wattage, and voltage from the battery class driver unprivileged. macOS reads ioreg AppleSmartBattery; Linux reads /sys/class/power_supply.

man, preset & version

$ chest man sort       # View built-in Unix man page for command
$ chest preset         # List all built-in rulesets
$ chest version        # Print binary version, commit, and platform

chest completion

Installs shell tab-completion so your shell autocompletes chest subcommands and flags — type chest s and press Tab to complete to sort. The recommended setup is a single command; it detects your shell from $SHELL, writes the script to the standard location for that shell, and wires it into your shell's startup file so completion loads in every new terminal.

$ chest completion --install      # Detect your shell, install, and wire it up (recommended)
$ chest completion zsh --install   # Install zsh completion permanently
$ chest completion fish --install  # Install fish completion permanently
$ chest completion bash > _chest # Print the raw script to stdout instead
Flag Type Description
[shell] argument Target shell: bash, zsh, or fish. Optional with --install (auto-detected from $SHELL).
-i, --install boolean Detect your shell (or use the shell argument) and install completion permanently, wiring it into your shell profile.
-o, --output <file> string Write the completion script to this file instead of stdout.
-s, --shell <name> string Shell to generate for: bash, zsh, fish.

Plan, Safety & Integrity

Every command in this section is safe by default. plan, manifest, verify, doctor, tree and rule eval never move, rename or delete a file. The only ones that mutate are rename and plan apply — and both record history, so chest undo reverts them.

chest plan

Turns a sorting plan into a reviewable, replayable JSON artifact. chest plan --out runs the same scan/classify/plan pipeline as sort but moves nothing.

FlagTypeDescription
--out <file>stringWrite the plan JSON to FILE. Strictly read-only: no folder is created and no file moves.
-y, --yesbooleanSkip confirmation on apply.
-r, --recursivebooleanInclude subdirectories (same meaning as chest sort -r).
-p, --presetstringBuilt-in preset. Rules resolve with identical precedence to sort.

chest plan apply is all-or-nothing: it re-validates every source first and refuses the entire batch if any file has moved, rather than half-applying. It records history, so chest undo reverts it.

chest checkpoint & undo --to

Mark a named point in history, then revert everything after it as one atomic batch.

FlagTypeDescription
-n, --name <label>stringLabel for the checkpoint (may also be given positionally). Moves no files itself.
--to <label>stringOn undo: revert every operation recorded after that checkpoint, as one atomic batch.
--file <name>stringOn undo: revert only the operations of that entry matching this basename (repeatable).
--prune --keep <n>integerOn history: drop the oldest entries, keeping the newest n (default 100), so the log cannot grow unbounded.

chest manifest & chest verify

Hash a folder into a reproducible SHA-256 inventory, then prove it has not drifted.

FlagTypeDescription
-o, --out <file>stringWrite the manifest to FILE. Byte-reproducible: no run timestamp, so an unchanged tree regenerates an identical file.
--manifest <file>stringOn verify: the manifest to verify against (required).
--max-size <size>stringSkip hashing files larger than this (default 500MB). Skipped files are reported, never silently dropped.
--json / --csvbooleanMachine-readable output for scripting.

chest verify exits 0 when everything matches and 4 when any file differs, so a script can distinguish "the backup drifted" from "verify could not run". (CHEST reserves 1 for a genuine error and 3 for a battery threshold breach.)

chest rename

Batch-rename files in place from a template. It never moves a file between directories.

FlagTypeDescription
--to <template>stringRename template (required). Placeholders: {ext} (lowercase extension without its dot — write photo.{ext}), {date}, {year}, {month}, {day}. Must not contain path separators.
--date-sourcestringWhich date the placeholders use: modified (default), auto, taken, filename.
-n, --dry-runbooleanPreview every rename without touching disk.
--collisionstringskip, rename (default), replace, abort. The default walks the photo (1).jpg chain so nothing is ever overwritten silently.

chest doctor & chest tree

Read-only inspection of CHEST's own state, and of a folder's shape.

FlagTypeDescription
--strictbooleanOn doctor: exit non-zero when any check fails. Warnings alone exit 0.
--depth <n>integerOn tree: how many levels to descend (default 3). When truncated, the output states what was left unread.
--sort <field>stringOn tree: size (default) or name.

chest rule eval

Explain which rule matches a given file, why, and where it would go — read-only. rule check validates syntax; rule eval validates behaviour.

Content-aware collisions

chest sort --hash-collisions resolves same-destination conflicts by content instead of guessing. CHEST hashes only the files that actually collide: distinct content is preserved through the normal photo (1).jpg rename chain, while an identical SHA-256 match is skipped by default. Use --keep-identical when the second path must be retained too.

FlagTypeDescription
--hash-collisionsbooleanHash only the files involved in a collision — never the whole tree. Same SHA-256 means a genuine duplicate, so the second copy is skipped; different content means both are distinct, so the second is auto-renamed rather than clobbering the first.
--keep-identicalbooleanUse with --hash-collisions to retain a byte-identical duplicate under the next safe name, such as photo (1).jpg. This makes the keep decision explicit and visible instead of silently dropping the second planned move.
--similarbooleanOn duplicates: group near-duplicate images by perceptual hash (DCT-based, stdlib only), reported separately from exact SHA-256 matches.
--threshold <0-64>integerHamming distance tolerance for --similar (default 10).
--quarantine <dir>stringOn duplicates: opt-in, moves duplicates into a staging folder after confirmation. Never deletes, and is recorded in history so undo restores them.
Retain identical files deliberately
Add --keep-identical when the duplicate's path still matters. The second file is assigned the next collision-safe name — photo (1).jpg here — and the plan reports it as retained rather than skipped.
$ chest sort --format --hash-collisions --keep-identical
CHEST PLAN  # hash only the conflicting files
  photo.jpg             →  JPG/photo.jpg
  photo-copy.jpg        →  JPG/photo (1).jpg

CONFLICTS
  identical content (sha256 match)  retained as JPG/photo (1).jpg

✔ 2 files prepared · 1 identical collision retained · 0 files skipped

Continuous indexing & trends

FlagTypeDescription
--watchbooleanOn index: keep the index warm as files change, until Ctrl+C. Reuses the same incremental size + mtime skip.
--debounce <dur>durationSettle time before re-indexing a burst of changes (default 500ms).
--trendbooleanOn stats: report growth against a recorded snapshot.
--day / --week / --monthbooleanTrend window (default --month).

Plugin Architecture & Guide

Process Isolation:
CHEST plugins use HashiCorp go-plugin over RPC. Plugins execute in independent operating system processes. A bug or panic in a plugin will not affect CHEST core.

Plugin Structure

A plugin consists of an executable binary and a manifest file (manifest.json or manifest.toml) placed in ~/.chest/plugins/<name>/.

System Path Protection
CHEST automatically blocks destructive operations (sort, watch, undo) targeting system directories like C:\Windows, /usr, /etc, or drive roots. Use --allow-system to override with an interactive confirmation.

Building a Sample Plugin

1. Initialize project

$ mkdir chest-custom-plugin
$ cd chest-custom-plugin
$ go mod init chest-custom-plugin

2. Implement plugin logic (main.go)

package main

import (
    "strings"
    hplugin "github.com/hashicorp/go-plugin"
    "github.com/Aswanidev-vs/chest/internal/plugin"
)

type CustomClassifier struct{}

func (c *CustomClassifier) GetInfo() (plugin.Manifest, error) {
    return plugin.Manifest{
        Name:         "custom-filter",
        Version:      "1.0.0",
        Description:  "Classifies custom test files",
        Capabilities: []string{"classifier"},
        Binary:       "custom-filter",
    }, nil
}

func (c *CustomClassifier) Classify(name string, ext string) (string, error) {
    if strings.HasSuffix(name, ".spec.ts") {
        return "UnitTests", nil
    }
    return "Other", nil
}

func main() {
    hplugin.Serve(&hplugin.ServeConfig{
        HandshakeConfig: plugin.HandshakeConfig,
        Plugins: map[string]hplugin.Plugin{
            "classifier": &plugin.ClassifierPluginRPC{Impl: &CustomClassifier{}},
        },
    })
}

3. Define manifest (JSON or TOML)

CHEST discovers either manifest.json or manifest.toml. Use whichever you prefer:

manifest.json
{
  "name": "custom-filter",
  "version": "1.0.0",
  "description": "Classifies custom test files",
  "author": "Developer",
  "capabilities": ["classifier"],
  "binary": "custom-filter"
}
manifest.toml
name          = "custom-filter"
version       = "1.0.0"
description   = "Classifies custom test files"
author        = "Developer"
capabilities  = ["classifier"]
binary        = "custom-filter"

4. Build and install

$ go build -o custom-filter .
$ chest plugin install .

Plugin CLI Management

$ chest plugin install .      # Install plugin from local folder
$ chest plugin list           # List installed plugins
$ chest plugin info <name>    # View plugin metadata
$ chest plugin remove <name>  # Uninstall a plugin

Metadata Capability (protocol v2)

Plugins declaring the metadata capability implement plugin.MetadataService and enrich every scanned file with sample-based intelligence during chest sort and chest watch. The host sends a bounded models.FileSample (path, name, extension, size, plus the first 4 KiB and last 1 KiB of content) and merges the returned models.FileMetadata — format, MIME type, category, embedded capture date (TakenDate/DateSource) and arbitrary Fields — into the file record, where rule conditions and the index can use them. Enrichment is best-effort: broken plugins are skipped, empty results change nothing, and the built-in signature extractor remains the fallback.

func (p *RichMetaPlugin) Inspect(sample models.FileSample) (models.FileMetadata, error) {
    // e.g. sniff camera RAW containers or parse XMP capture dates
    return models.FileMetadata{
        Format:     "CR3",
        MIMEType:   "image/x-cr3",
        Category:   "Image",
        TakenDate:  &ts,
        DateSource: "xmp:photoshop:DateCreated",
        Fields:     map[string]string{"raw_container": "ISO-BMFF/CRX"},
    }, nil
}

A complete working example — camera RAW sniffing plus XMP sidecar date extraction — ships in examples/plugins/rich-meta. Protocol version 2 is enforced by the handshake, so plugins built against v1 must be recompiled.