gwarchive
A command-line tool for organizing folders under the GWArchive naming standard.
Five categories, each a directory, each with a letter:
| Letter | Directory | For |
|---|---|---|
P |
Project |
active work with an end |
R |
Recurring |
work that comes back |
M |
Material |
reference that is not a project |
A |
Archive |
finished, kept |
O |
Old |
retired, date-stamped |
Folders are named P0001 Descriptor, subfolders P0001.01 Descriptor, and
retired folders YYYY-MM-DD-P0001-Descriptor.
A prefix is a permanent identifier. It is allocated once, it is never
reissued, and it travels with the folder across category moves — so a folder
created in Project keeps its P prefix after it is moved into Archive. A
copy is a new thing and gets a fresh identifier. There is no index or
database; the filesystem is the source of truth.
Install
uv tool install gwarchive
Or run a single self-contained artifact with no install at all — g.pyz vendors
its dependencies:
./g.pyz --help
Use
gwarchive init # create the five category directories
gwarchive create P "Thesis draft" # -> Project/P0001 Thesis draft
gwarchive mksub P1 "Figures" # -> Project/P0001 Thesis draft/P0001.01 Figures
gwarchive list P # a table, or --json
gwarchive find thesis # exits 1 on no match, so it composes like grep
gwarchive mv P1 Archive # the prefix comes along
gwarchive oldify P1 # retire into Old/ with today's date
gwarchive verify # audit the whole tree
Every mutating command takes --dry-run, and dry-run output has the same shape
as the real thing (Would move vs Moved).
Shell navigation
eval "$(gwarchive shell-init)"
gcd P1 # cd to the folder with that prefix, wherever it now lives
gwarchive here # the folder you're in, short: P28:GWArchive
shell-init also sets the terminal title from here at each prompt:
| Where you are | Title |
|---|---|
Project/P0028 GWArchive/src/... |
P28:GWArchive |
Project/P0028 GWArchive/P0028.01 Docs |
P28:GWArchive (here --deepest: P28.01:Docs) |
Old/2026-01-05-P0001-Beta |
Old:Beta |
Project/ |
G:Project |
| the archive root | G: |
| anywhere else | the directory name |
It asks here only when the directory changes, and is skipped on
TERM=dumb. Put the eval after a prompt framework that sets titles of its
own (oh-my-zsh does), or pass --no-title to leave the title alone.
Remote sync
Backed by rclone. Set $GWARCHIVE_REMOTE or pass
--remote:
gwarchive push P1 # upload, keep the local copy
gwarchive offload P1 # upload, delete locally, leave a tombstone
gwarchive pull P1 # fetch back
gwarchive restore P1 # fetch back and clear the tombstone
gwarchive restore P1 --keep-local # clear the tombstone, keeping what's on disk, fetching nothing
An offloaded folder keeps its name and prefix on disk while the bytes live on
the remote. The tombstone it leaves — .gwarchive-offload.json — is also the
version index; the tool never lists the remote to find out what is there.
A folder that still holds local files despite being marked offloaded — pulled
and then edited, or left behind by an interrupted offload — is something
push now refuses rather than skipping as "already on the remote". Use
restore --keep-local to resolve it from what is already on disk, or restore
to fetch the remote's copy over it.
pull and restore ask before overwriting local files the remote also has,
with one prompt for the whole batch. Pass -y/--yes to skip it; --json
without --yes refuses rather than prompting, since there is nothing to
answer it with.
Folders are pushed as one compressed object (tar.zst, falling back to
tar.gz when zstd is absent), written as a sibling of the mirror path. The
extension is the codec's own on purpose: lose the tombstone entirely and
zstd -d < x.tar.zst | tar -tvf - is still a complete recovery path.
Exit codes
| Code | Meaning |
|---|---|
0 |
success |
1 |
runtime failure — not found, ambiguous prefix, refused overwrite, no matches |
2 |
invalid input — a bad category, a bad date, a malformed --remote |
Errors go to stderr. --json owns stdout: no spinner, no prompt, no trailing
receipt shares it.
Environment
| Variable | Effect |
|---|---|
GWARCHIVE_BASE |
archive root (default ~/gwarchive) |
GWARCHIVE_REMOTE |
default rclone target |
GWARCHIVE_CODEC |
force zstd or gzip |
GWARCHIVE_COMPRESS |
default for --no-compress |
GWARCHIVE_KEEP |
archived copies retained per remote |
GWARCHIVE_POKEMON |
sprite for clears |
Development
uv sync # installs the dev group
uv run pytest
uv run mypy
uv run ruff check . && uv run ruff format --check .
Conventions, the module layering, and the rules that keep the test seams working are in AGENTS.md.
Metadata
Release files for gwarchive 1.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gwarchive-1.3.1.tar.gz | 245.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gwarchive-1.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 320.8 kB
Release files / gwarchive-1.3.1.tar.gz
| Download URL | gwarchive-1.3.1.tar.gz |
|---|---|
| Size | 245.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fb8086778855e9c0bb6d3d91438d968a75187f60c141b71e2b0bfb35388f834d
|
|
BLAKE2b-256 checksum How to use checksums |
64e7a55fe7ca8ec1a1e8bca05645f6cdd1fb054e8794147644c047113839e3d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency logRelease files / gwarchive-1.3.1-py3-none-any.whl
| Download URL | gwarchive-1.3.1-py3-none-any.whl |
|---|---|
| Size | 75.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12a22794464f7aed395fc5b096e9a9149138239c406b22c3480169ab6bea57e0
|
|
BLAKE2b-256 checksum How to use checksums |
0b96a99091d805c9509a9002cf423952125796ece7ae5301a8a358eeab180cf4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency log