#!/usr/bin/env python3 """Start a project again: close its pages, remove its worktrees, keep its work. bin/project-reset --dry-run bin/project-reset --yes bin/project-reset --yes --delete-folder What it removes, and nothing else: - the project's screen panes in its TUIOS session (the shell pane stays, so the session survives) - the git worktrees Paperclip made for the project's tasks, and the branches those tasks were on What it leaves alone: the folder, its `.harness`, its git history, and every Paperclip record — tasks, comments, runs, cost. Paperclip owns those, so a reset here cannot erase the history of what happened; it only clears the machinery. `--delete-folder` is the only destructive option and it asks for the folder name to be typed. Without it, nothing outside git and TUIOS is touched. """ import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(HERE, "lib")) import harness as h # noqa: E402 import tuios # noqa: E402 import project as pj # noqa: E402 PAGES = ["tree", "reply", "log", "new"] def main(): argv = sys.argv[1:] args = [a for a in argv if not a.startswith("--")] if not args: h.say("usage: bin/project-reset [--dry-run] [--yes] [--delete-folder]") return 2 folder = os.path.abspath(os.path.expanduser(args[0])) dry = "--dry-run" in argv yes = "--yes" in argv delete_folder = "--delete-folder" in argv cfg = pj.load(folder) pj.check_machine(cfg) session = cfg["session"] project_id = cfg["paperclip"]["project"] tasks = pj.active_tasks(project_id) worktrees = [] for t in tasks: full = h.api("GET", "/api/issues/%s" % t["id"]) ws = full.get("currentExecutionWorkspace") or {} if ws.get("cwd"): worktrees.append((t["identifier"], ws["cwd"], ws.get("branchName") or "")) h.say("project: %s" % project_id) h.say("folder: %s" % folder) h.say("session: %s" % session) h.say("") h.say("would remove:") h.say(" panes: %s in %s" % (", ".join(PAGES), session)) for ref, cwd, branch in worktrees: h.say(" worktree: %s (%s, branch %s)" % (cwd, ref, branch or "none")) if delete_folder: h.say(" the folder: %s (including .harness and git history)" % folder) h.say("kept: the Paperclip project, its tasks, comments, runs and cost") if dry: h.say("") h.say("dry run: nothing was removed") return 0 if not yes: h.say("") h.say("Type the folder name to continue: %s" % os.path.basename(folder)) typed = input("> ").strip() if typed != os.path.basename(folder): h.say("nothing was removed") return 1 for name in PAGES: if tuios.close_pane(name, session): h.say("closed pane %s" % name) repo = folder for ref, cwd, branch in worktrees: code, out = h.run(["git", "-C", repo, "worktree", "remove", "--force", cwd]) h.say("worktree %s: %s" % (ref, "removed" if code == 0 else out.strip()[:120])) if branch: code, out = h.run(["git", "-C", repo, "branch", "-D", branch]) h.say("branch %s: %s" % (branch, "deleted" if code == 0 else out.strip()[:120])) if delete_folder: h.say("") h.say("Type the folder name again to delete %s: " % folder) typed = input("> ").strip() if typed != os.path.basename(folder): h.say("folder kept") return 0 code, out = h.run(["rm", "-rf", folder]) h.say("folder %s" % ("deleted" if code == 0 else "not deleted: " + out.strip())) return 0 if __name__ == "__main__": h.main_guard(main)