office: one word for everything — forward board/reset/delete/merge/review/triage, and a help page that lists all of it
This commit is contained in:
214
bin/office
214
bin/office
@@ -371,70 +371,65 @@ def run_on_remote(host, folder, opts):
|
||||
return 0
|
||||
|
||||
|
||||
USAGE = """office — open a folder as a project, and say what else can be run
|
||||
USAGE = """office — everything for one project, under one word
|
||||
|
||||
office <folder> … open a folder as a project
|
||||
office help this page
|
||||
office board … build or repair the panes
|
||||
office reset <folder> … clear panes and worktrees, keep the history
|
||||
office delete <folder> … remove sessions, the Paperclip project, .git, dot files
|
||||
office merge <task> … rebase, check, review, merge one task
|
||||
office review <task> … run ocr over one task's diff
|
||||
office triage <task> … ask Mouse-1 one question about a task
|
||||
|
||||
Every one of these is also on your PATH under its own name (board, merge,
|
||||
review, triage, project-reset, project-delete). Both spellings do the same
|
||||
thing; office is the one to remember.
|
||||
|
||||
THE RULE
|
||||
The folder and the session live on the same machine. If you are typing inside
|
||||
the session, on the machine that owns the folder, it is just: office <folder>
|
||||
Use --host only when the folder is on a different machine from your shell.
|
||||
|
||||
WHAT IT DOES
|
||||
1. reads the folder
|
||||
2. if a machine is named, checks whether that is this machine, and runs locally if so
|
||||
3. if it really is another machine, checks the login and runs the same command there
|
||||
4. adopts the Paperclip project, or creates it with --new
|
||||
5. writes what is missing in <folder>/.harness/ and never overwrites
|
||||
6. records the project in ~/.config/paperclip-harness/projects.toml
|
||||
7. creates the session on that machine's daemon, then builds eight panes
|
||||
OPENING A FOLDER
|
||||
office <folder> --project "<Name>" adopt a project Paperclip already has
|
||||
office <folder> --new "<Name>" create the Paperclip project as well
|
||||
office . adopt the folder you are standing in
|
||||
office <folder> --session <name> name the session instead of the folder
|
||||
office <folder> … --host <machine> the folder is on that machine
|
||||
office <folder> … --dry say what would happen, write nothing
|
||||
|
||||
COMMANDS
|
||||
What it does, in order: reads the folder; runs locally if the named machine is
|
||||
this one; otherwise checks the login and runs the same command there; adopts or
|
||||
creates the Paperclip project; writes what is missing in <folder>/.harness/ and
|
||||
never overwrites; records the project in ~/.config/paperclip-harness/projects.toml;
|
||||
creates the session on that machine's daemon; builds eight panes.
|
||||
|
||||
Each of these is its own command on your PATH, not a subcommand of office:
|
||||
type `board`, not `office board`.
|
||||
PAGES: office board [--folder <f>] [--replace] [--reset]
|
||||
board create any pane that is missing, close nothing
|
||||
board --replace close this project's panes, then build all eight again
|
||||
board --reset close every pane in the session, wait, then build
|
||||
board --folder <folder> do it for another project
|
||||
|
||||
office <folder> --project "<Name>" adopt a project Paperclip already has
|
||||
office <folder> --new "<Name>" create the Paperclip project as well
|
||||
office . adopt the folder you are standing in
|
||||
office <folder> --session <name> name the session instead of the folder
|
||||
office <folder> --project "<Name>" --host <machine> the folder is on that machine
|
||||
office <folder> --new "<Name>" --dry say what would happen, write nothing
|
||||
office <folder> --new "<Name>" (with no repository it asks Y or N)
|
||||
office <folder> --new "<Name>" --commit-all make the repository and commit everything
|
||||
office <folder> --new "<Name>" --anywhere allow it although agents cannot see it
|
||||
Inside a pane, R rebuilds everything and r re-reads Paperclip. After a reboot
|
||||
TUIOS restores a session with a bare shell in each pane and keeps the pane
|
||||
names, so run board once: it now checks what each pane is really running and
|
||||
replaces any pane that is only a prompt.
|
||||
|
||||
board create any missing pane in this project's session
|
||||
board --folder <folder> the same, for another project
|
||||
board --replace close this project's panes, then build all eight again
|
||||
board --reset close every pane in the session, then build
|
||||
project-reset <folder> clear the panes and the worktrees, keep the history
|
||||
project-delete <folder> remove the sessions, the Paperclip project, .git and the dot files
|
||||
(says what would go; add --yes to do it)
|
||||
merge <task> rebase, check, review and merge one task (asks first)
|
||||
review <task> [--post] run ocr over one task's diff, and record the verdict
|
||||
triage <task> [--ask next|failure] ask Mouse-1 one question about a task
|
||||
STARTING OVER
|
||||
office reset <folder> clear the panes and the worktrees; keeps the
|
||||
folder, .harness, the git history and every
|
||||
Paperclip record
|
||||
office reset <folder> --dry-run say what would go
|
||||
office reset <folder> --yes do it
|
||||
office reset <folder> --yes --delete-folder remove the folder too
|
||||
|
||||
MACHINES (name, or the address ssh reaches)
|
||||
nixos-desktop .13 192.168.20.13 hosts Paperclip, the daemon, the main session
|
||||
sam-4screen-desktop .27 192.168.20.27 your working desktop
|
||||
sam-thinkpad .51 192.168.20.51 the laptop
|
||||
|
||||
EVERY MACHINE, FROM THE TERMINAL
|
||||
|
||||
On .13, a folder that is on .13:
|
||||
office ~/paseo/projects/test-alpha --project "test-alpha"
|
||||
|
||||
On .27, adopting a folder that lives on .13:
|
||||
office /home/sam/paseo/projects/email_agent --project "Email Agent" --host nixos-desktop
|
||||
|
||||
On .27, a folder that is on .27 (stays local, no --host):
|
||||
office ~/src/thing --new "thing"
|
||||
|
||||
On .51, the same folder on .51:
|
||||
office ~/src/thing --project "thing" --host sam-thinkpad
|
||||
|
||||
Then attach. Nothing may move your client from inside a pane, so the last step is yours:
|
||||
tuios attach <session> on this machine
|
||||
tuios attach --host nixos-desktop <session> to a session on another machine
|
||||
office delete <folder> remove the TUIOS sessions, every Paperclip
|
||||
project that claims the folder, the registry
|
||||
lines, and .git / .gitignore / .harness /
|
||||
.paperclip / .pi. Keeps ordinary files.
|
||||
office delete <folder> --yes do it
|
||||
office delete <folder> --yes --delete-folder remove the folder as well
|
||||
|
||||
WORKTREES, AND WHY GIT MATTERS
|
||||
A new project gets a worktree policy: Paperclip makes a branch and a separate
|
||||
@@ -445,52 +440,75 @@ WORKTREES, AND WHY GIT MATTERS
|
||||
|
||||
When the folder cannot do that yet, office asks, and never writes history:
|
||||
|
||||
Y make the repository, write .gitignore containing .paperclip/, stage
|
||||
everything, and stop. Commit in lazygit, then run this again.
|
||||
Y make the repository, write .gitignore containing .paperclip/, and stop.
|
||||
Nothing is staged. Ignore first, then stage, then commit, in lazygit.
|
||||
N create the project in place (local_path): a task edits the folder
|
||||
directly, there is no diff and nothing to merge.
|
||||
|
||||
Y leaves you in lazygit's hands on purpose: what you commit is what the agent
|
||||
will see, and .gitignore is how you keep a file out of its view.
|
||||
--commit-all does the whole thing in one go, for anyone who does not care.
|
||||
Y opens a session with lazygit already in the folder, on the machine that owns
|
||||
it, and tells you how to attach. Ignore with i, stage all with a, commit with c.
|
||||
Then run the same office command again and it creates the project.
|
||||
|
||||
The base branch is read from the folder, not assumed to be main.
|
||||
.paperclip/ is written into .gitignore every time and is not optional: it is
|
||||
where Paperclip puts the worktrees, inside the project folder.
|
||||
Ignoring a file does NOT untrack one that is already committed: office warns
|
||||
and gives you the git rm --cached line when it finds that.
|
||||
|
||||
WHERE THINGS RUN
|
||||
Panes run on the machine that owns the folder. Agents run on the machine that
|
||||
serves Paperclip, because Paperclip starts them itself. If those differ, the
|
||||
panes work and the agents cannot see the files, so --new refuses unless
|
||||
panes work and the agents cannot see the files, so office --new refuses unless
|
||||
--anywhere is given.
|
||||
|
||||
ON ANOTHER MACHINE
|
||||
--host copies this folder of commands to the other machine, at
|
||||
~/.local/share/paperclip-harness/bin/
|
||||
and leaves a short ~/.local/bin/office there, so it can be run from that
|
||||
machine too. Nothing has to be installed by hand. On .27 and .51 ~/.local/bin
|
||||
is not on PATH yet, so there you call it by its full path:
|
||||
~/.local/share/paperclip-harness/bin/office <folder>
|
||||
MACHINES (name, or the address ssh reaches)
|
||||
nixos-desktop .13 192.168.20.13 hosts Paperclip, the daemon, the main session
|
||||
sam-4screen-desktop .27 192.168.20.27 your working desktop
|
||||
sam-thinkpad .51 192.168.20.51 the laptop
|
||||
|
||||
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.
|
||||
FROM A TERMINAL, ON ANY MACHINE
|
||||
a folder on .13, typed on .13:
|
||||
office ~/paseo/projects/test-alpha --project "test-alpha"
|
||||
a folder on .13, typed on .27:
|
||||
office /home/sam/TEST-BETA --new "TEST-BETA" --host nixos-desktop
|
||||
a folder on .27, typed on .27:
|
||||
office ~/src/thing --new "thing"
|
||||
then attach; nothing may move your client from inside a pane:
|
||||
tuios attach <session>
|
||||
tuios attach --host nixos-desktop <session>
|
||||
|
||||
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
|
||||
running and replaces any pane that is only a prompt.
|
||||
--host copies these commands to the other machine at
|
||||
~/.local/share/paperclip-harness/bin/ and leaves ~/.local/bin/office there, so
|
||||
a machine that has never seen this repository still works.
|
||||
|
||||
MERGE AND REVIEW
|
||||
office merge <task> rebase onto the base, run the project's check, run ocr,
|
||||
merge, re-run the check, roll back if it fails. It asks
|
||||
for confirmation before touching anything.
|
||||
office review <task> ocr over one task's diff; --post records the verdict
|
||||
office triage <task> one four-option Mouse-1 question, posted as a comment
|
||||
|
||||
KEYS INSIDE THE PANES
|
||||
( ) change tab tab change panel enter open / choose
|
||||
r re-read Paperclip R rebuild all panes ? list the keys
|
||||
t open lazygit in the project folder (stage, ignore, commit)
|
||||
t tools (lazygit, yazi, nvim, hunk, fzf, ocr, shell)
|
||||
g lazygit, straight away, in the project folder
|
||||
h j k l z n and digits belong to TUIOS and never reach the program.
|
||||
|
||||
THE PROJECT PANE
|
||||
overview · git · facts · costs · settings · projects
|
||||
The git tab reads the folder and Paperclip together: repository, branch,
|
||||
commits, remote, what is staged, what .gitignore holds, the worktree policy,
|
||||
and Paperclip's own scan of every task worktree (dirty, ahead, behind, merged).
|
||||
|
||||
WHILE A FOLDER CANNOT HOLD A WORKTREE
|
||||
Every pane carries one line at the top, naming what is wrong and where to look.
|
||||
It is read when the pane starts; fix the folder and press R to clear it.
|
||||
|
||||
MORE
|
||||
docs/STARTUP.md these commands, the rule, and the three rebuild levels
|
||||
docs/WORKSPACES.md what each of the eight panes is for
|
||||
docs/CLEAN-TESTS.md the two end-to-end tests and what they measured
|
||||
docs/STARTUP.md these commands, the rule, the rebuild levels
|
||||
docs/WORKSPACES.md what each of the eight panes is for
|
||||
docs/RUNS-AND-MERGE.md how a task becomes merged work
|
||||
docs/CLEAN-TESTS.md the two end-to-end tests and what they measured
|
||||
"""
|
||||
|
||||
|
||||
@@ -680,6 +698,38 @@ def note_ignore(folder, opts):
|
||||
h.say(" take Paperclip's task worktrees with it. Add it before you commit.")
|
||||
|
||||
|
||||
# The commands that have their own script next to this one. `office board` and
|
||||
# `board` must do the same thing: a person should only have to remember office.
|
||||
SIBLINGS = {
|
||||
"board": "board",
|
||||
"pages": "board",
|
||||
"build": "board",
|
||||
"reset": "project-reset",
|
||||
"project-reset": "project-reset",
|
||||
"delete": "project-delete",
|
||||
"project-delete": "project-delete",
|
||||
"forget": "project-delete",
|
||||
"merge": "merge",
|
||||
"review": "review",
|
||||
"triage": "triage",
|
||||
}
|
||||
|
||||
|
||||
def forward(argv):
|
||||
"""Run a sibling command with the rest of the arguments, or return None."""
|
||||
if not argv:
|
||||
return None
|
||||
name = SIBLINGS.get(argv[0])
|
||||
if name is None:
|
||||
return None
|
||||
script = os.path.join(HERE, name)
|
||||
if not os.path.isfile(script):
|
||||
raise h.Refused(2, "no %s next to office; the repository looks incomplete" % name)
|
||||
# exec, not a subprocess: this program is a router for this one call and has
|
||||
# nothing left to do, so the sibling should be the process the person sees.
|
||||
os.execv(sys.executable, [sys.executable, script] + argv[1:])
|
||||
|
||||
|
||||
def main():
|
||||
if os.geteuid() == 0:
|
||||
h.say("office: run this as your own user, not with sudo.")
|
||||
@@ -687,6 +737,10 @@ def main():
|
||||
h.say(" neither of those exists for root, and files it wrote would belong to root.")
|
||||
return 2
|
||||
argv = sys.argv[1:]
|
||||
if argv and argv[0] in ("help", "-h", "--help"):
|
||||
h.say(USAGE.strip())
|
||||
return 0
|
||||
forward(argv)
|
||||
if not argv or "-h" in argv or "--help" in argv:
|
||||
h.say(USAGE.strip())
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user