office: open a lazygit session on the machine that owns the folder, and stop overwriting a working wrapper

This commit is contained in:
Sam
2026-11-13 10:54:10 +11:00
parent fd7183778a
commit c53f37dad9
4 changed files with 391 additions and 5 deletions

View File

@@ -47,14 +47,16 @@ def parse(argv):
opts = {"folder": ".", "folder_given": False, "project": "", "session": "",
"dry": False, "host": "", "no_switch": False, "new": "", "remote": "",
"git_init": "--git-init" in argv, "anywhere": "--anywhere" in argv,
"commit_all": "--commit-all" in argv, "git_init_commits": False}
"commit_all": "--commit-all" in argv, "git_init_commits": False,
"client_host": ""}
positional = []
i = 0
while i < len(argv):
a = argv[i]
if a in ("--project", "--session", "--host", "--new", "--remote"):
if a in ("--project", "--session", "--host", "--new", "--remote", "--client-host"):
if i + 1 < len(argv):
opts[a[2:]] = argv[i + 1]
# Keys are Python names, so a flag with a hyphen becomes an underscore.
opts[a[2:].replace("-", "_")] = argv[i + 1]
i += 2
continue
i += 1
@@ -146,6 +148,45 @@ def ensure_session(session, folder):
h.say("session: %s (created; switch to it with TUIOS's session switcher)" % session)
def open_git_session(folder, session, opts):
"""A session with lazygit already open in the folder, on the machine that has it.
The commit has to happen where the files are, and that machine is often not
the one being typed at. A session on that machine is how the person gets
there from TUIOS, which is the whole point of this program: one attach, no
ssh, no cd.
"""
helper = session + "-git"
created = ensure_session_quiet(helper, folder)
# The pane a tool needs a PATH of its own: a pane started this way does not
# inherit an interactive shell's.
prelude = ('export PATH="$HOME/.local/bin:$HOME/.nix-profile/bin:'
'/run/current-system/sw/bin:/etc/profiles/per-user/$USER/bin:$PATH"; ')
tuios.open_pane("git", "Git", folder, prelude + "exec lazygit", helper)
h.say("")
h.say(" A session called %r now holds lazygit in that folder%s." % (helper, "" if created else " (it was already there)"))
h.say(" Attach to it, stage and commit, then come back and repeat the command:")
h.say("")
client = opts.get("client_host") or ""
h.say(" tuios attach%s %s" % (" --host " + client if client else "", helper))
h.say("")
h.say(" When you are done with it: tuios kill-session %s" % helper)
return helper
def ensure_session_quiet(session, folder):
"""ensure_session, without the line about switching: the caller explains."""
if session in tuios.sessions():
return False
params = {"name": session, "cwd": folder, "window": False}
out = subprocess.run(["tuios", "call", "new-session", json.dumps(params)],
capture_output=True, text=True, timeout=60)
if out.returncode != 0 and "session_exists" not in (out.stderr or out.stdout):
raise h.Refused(3, "could not create session %r: %s"
% (session, (out.stderr or out.stdout).strip()[:300]))
return True
def scaffold(folder, name):
"""Write the files this project keeps, and only the ones that are missing.
@@ -237,6 +278,15 @@ def install_wrapper(addr, remote_bin):
capture_output=True, text=True, timeout=60).stdout.strip()
if not home:
return
# Never replace a wrapper that already works. Pushing from another machine
# would otherwise rewrite the owner's own `office` command to point at the
# copy that machine sent, which is how the canonical machine ended up
# running someone else's older copy.
existing = subprocess.run(
["ssh", "-o", "BatchMode=yes", addr, 'cat "$HOME/.local/bin/office" 2>/dev/null'],
capture_output=True, text=True, timeout=60).stdout
if "paperclip-harness/bin/office" in existing:
return
script = ("#!/bin/sh\n"
"# Written by office on another machine. Runs the copy it keeps here.\n"
"#\n"
@@ -291,6 +341,9 @@ def run_on_remote(host, folder, opts):
for flag in ("--git-init", "--commit-all", "--anywhere"):
if opts.get(flag[2:].replace("-", "_")):
inner.append(flag)
# So the instructions printed on the other machine say how to reach it from
# where the person is sitting.
inner += ["--client-host", host]
if opts["dry"]:
inner.append("--dry")
# "$HOME" must stay unquoted so the other machine's shell expands it; every
@@ -410,6 +463,12 @@ ON ANOTHER MACHINE
is not on PATH yet, so there you call it by its full path:
~/.local/share/paperclip-harness/bin/office <folder>
WHEN THE FILES ARE ON ANOTHER MACHINE
The git work has to happen where the files are, because that is where the
commit, the worktree and the agent are. So when a folder cannot hold a
worktree yet, office makes a session on THAT machine with lazygit already
open in the folder, and tells you how to attach to it. No ssh, no cd.
AFTER A REBOOT
TUIOS restores a session with a fresh shell in every pane and keeps the pane
names. Run board once for that project: it checks what each pane is actually
@@ -482,7 +541,7 @@ def repeat_command(folder, opts):
return " ".join(parts)
def prepare_git(folder, opts):
def prepare_git(folder, opts, session):
"""Get the folder as far as it can go toward a worktree per task.
A worktree needs a repository and at least one commit. Neither is made
@@ -568,6 +627,8 @@ def prepare_git(folder, opts):
h.say(" space stage or unstage e edit a file, .gitignore included")
h.say(" c commit ? every key")
h.say("")
open_git_session(folder, session, opts)
h.say("")
h.say(" Then repeat this command to create the project with worktrees:")
h.say("")
h.say(" %s" % repeat_command(folder, opts))
@@ -643,7 +704,7 @@ def main():
# Only now: a folder on another machine has already been handed over, and
# the machine that owns it does these two checks itself.
guard_machine(folder, opts)
ready = prepare_git(folder, opts)
ready = prepare_git(folder, opts, session)
if opts["dry"]:
if opts["new"]: