# loom-cli **Not built yet.** *This is the spec, written while it was fresh.* **A small tool for the operations a person should not do by hand:** *fetch a document you depend on, and find out when it changed.* --- ## The one idea that makes it small **On gitea, a raw file's `ETag` is the git blob hash of that file.** *Verified on both a public and a private repository — the header and `git rev-parse` return the same value.* > **So there is nothing to record.** *`git hash-object ` **is** the > lock. Compare it to the remote's `ETag` and you have your answer.* **No lock file, no state, no `pull` metadata to drift.** *And it catches a case we had not considered: **if somebody edits the local copy, the hash stops matching and `check` reports it** — which is correct, because an adopted copy that has been edited is no longer a copy of anything.* ## Commands ### `loom check` **For every file under `.loom/externals/`:** *compute its hash, `HEAD` its source, compare.* | result | means | |---|---| | **hashes match** | nothing changed | | **hashes differ** | **upstream moved** — the new copy is a candidate, not a replacement | | **local hash differs from what the remote had at last fetch** | *somebody edited a copy that is not ours* | | **`404`** | **gone, or access lost — these are indistinguishable over HTTP.** *Say so; do not pick one.* | **Report. Do not fix.** *A changed external becomes a polad in the cart, and somebody decides.* ### `loom pull ` **Fetch one document into `.loom/externals//`.** *The path mirrors the URL, so nothing has to record an origin.* **Do not create a `.usages.md`.** *An empty facet asserts that we have something to say and we do not — **a file that carries no delta should not exist.*** ### `loom publish ` **Verify a document in `.loom/published/` is fetchable by somebody who is not you.** *One anonymous request.* > **Publishing is not an act you can complete alone.** *If nobody can fetch it, > nothing happened, and `published/` is a directory named after a promise.* ### `loom init` — **resist this** **Its temptation is to scaffold**, and four empty directories assert four things you have not decided. *The useful version creates `.loom/README.md` and asks one question.* **Creating the tree is not a feature.** ## Credentials **Read over HTTP with a read-only token. Write over ssh.** *Split by operation, not by whether a repository is public.* **Name the token for its scope — `loom-read`.** *A token cannot be asked what it may do: `/api/v1/repos/…` reports the **account's** rights, not the token's. **Scope is set once at creation and invisible afterwards, so the name is the only record.*** *Token in `~`, never in a repository.* --- ## Why so little *Every act in the model is a file in somebody's repository.* **Publishing is writing one; adopting is fetching a URL; reporting a gap is writing one; homing it is an `mv`.** *Nothing sends a service a request.* **So this is a fetcher and a comparator, and it should stay one.** *If it starts holding state, ask what it knows that no repository knows — that is the thing that will rot.*