// loomctl fetches documents this repository depends on, and finds out when they // change. // // It reports and never repairs. Everything it writes, it writes to the working // tree — committing and pushing are yours, because the consequences of a push // land on people a tool cannot experience. package main import ( "flag" "fmt" "os" "git.hypertheory-labs.dev/loom/loom-cli/internal/external" ) const usage = `loomctl — fetch what you depend on, and find out when it changed. loomctl external list what a repository publishes loomctl external add [--path p] adopt one document and lock it loomctl external check ask every publisher whether theirs moved loomctl external apply [path...] move a staged polad into place, lock and all check reports and does not fix. A document that moved is staged as a polad in .loom/cart/current/polad/ — a candidate shaped exactly like what it would become — and somebody decides. Its exits are apply or discard; nothing there drifts into being kept. With no cart open, check says what moved and stages nothing, because opening a round is somebody's act and not a side effect. add adopts what is not here yet, and refuses what is already adopted. Given a document that is present but unlocked — fetched by hand before this existed — it supplies the missing origin: identical bytes lock it, differing bytes are staged, and the local copy is never overwritten, because a copy that differs is the only evidence that anything moved while nothing was watching. Adopted documents live in .loom/externals////.md, and their origins in .loom/externals/.locks. The path is for a person to read; the lock is what a machine uses, because the path does not round-trip to a URL. Credentials are read-only and per host, in ~/.config/loomctl/config.json or in LOOMCTL_TOKEN_. loomctl never writes over the network, so a token it is given should never carry write scope. Requires git on PATH, for list only. ` func main() { if err := run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, "loomctl: "+err.Error()) os.Exit(1) } } func run(args []string) error { if len(args) == 0 || args[0] == "-h" || args[0] == "--help" || args[0] == "help" { fmt.Print(usage) return nil } switch args[0] { case "external": return runExternal(args[1:]) default: return fmt.Errorf("unknown command %q\n\n%s", args[0], usage) } } func runExternal(args []string) error { if len(args) == 0 { return fmt.Errorf("external needs a subcommand: list, add, check, apply") } switch args[0] { case "list": if len(args) != 2 { return fmt.Errorf("usage: loomctl external list ") } return external.List(args[1], os.Stdout) case "add": fs := flag.NewFlagSet("add", flag.ContinueOnError) path := fs.String("path", "", "where to keep it, relative to .loom/externals (default: derived from the URL)") if err := fs.Parse(args[1:]); err != nil { return err } if fs.NArg() != 1 { return fmt.Errorf("usage: loomctl external add [--path p]") } root, err := root() if err != nil { return err } return external.Add(root, fs.Arg(0), *path, os.Stdout) case "check": root, err := root() if err != nil { return err } return external.Check(root, os.Stdout) case "apply": root, err := root() if err != nil { return err } return external.Apply(root, args[1:], os.Stdout) default: return fmt.Errorf("unknown external subcommand %q", args[0]) } } func root() (string, error) { wd, err := os.Getwd() if err != nil { return "", err } return external.FindRoot(wd) }