From 563f9078dfa177c30a8c3c1e27c263757d51dff0 Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Tue, 25 Nov 2025 09:11:51 +0000 Subject: [PATCH] . --- tool/{set_project_permissions => after_pull} | 0 tool/git-empty-dir | 1 - tool/sync | 2 +- tool_shared/{bespoke => authored}/env | 0 .../authored/git-empty-dir}/CLI.py | 0 .../authored/git-empty-dir}/Harmony.py | 0 .../git-empty-dir}/load_command_module.py | 0 .../authored/git-empty-dir}/meta.py | 0 .../authored/git-empty-dir}/source_sync | 0 .../authored/sync}/A_minus_B | 0 .../authored/sync}/CLI.py | 0 .../authored/sync}/GitIgnore.py | 0 .../authored/sync}/Harmony.py | 0 .../authored/sync}/Harmony_where | 0 .../authored/sync}/README.org | 0 .../authored/sync}/command.py | 0 .../authored/sync}/doc.py | 0 .../authored/sync}/in_between_and_below | 0 .../authored/sync}/load_command_module.py | 0 .../authored/sync}/make_Harmony_tree_dict | 0 .../manus_2025_11_20_215471873762383.txt | 0 .../authored/sync}/meta.py | 0 .../authored/sync}/newer | 0 .../authored/sync}/older | 0 .../authored/sync}/skeleton.py | 0 tool_shared/authored/version | 2 + tool_shared/bespoke/scratchpad | 225 ------------------ tool_shared/bespoke/version | 5 - tool_shared/patch/.gitkeep | 0 29 files changed, 3 insertions(+), 232 deletions(-) rename tool/{set_project_permissions => after_pull} (100%) delete mode 120000 tool/git-empty-dir rename tool_shared/{bespoke => authored}/env (100%) rename {tool/source_git-empty-dir => tool_shared/authored/git-empty-dir}/CLI.py (100%) rename {tool/source_git-empty-dir => tool_shared/authored/git-empty-dir}/Harmony.py (100%) rename {tool/source_git-empty-dir => tool_shared/authored/git-empty-dir}/load_command_module.py (100%) rename {tool/source_git-empty-dir => tool_shared/authored/git-empty-dir}/meta.py (100%) rename {tool/source_git-empty-dir => tool_shared/authored/git-empty-dir}/source_sync (100%) rename {tool/source_sync => tool_shared/authored/sync}/A_minus_B (100%) rename {tool/source_sync => tool_shared/authored/sync}/CLI.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/GitIgnore.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/Harmony.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/Harmony_where (100%) rename {tool/source_sync => tool_shared/authored/sync}/README.org (100%) rename {tool/source_sync => tool_shared/authored/sync}/command.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/doc.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/in_between_and_below (100%) rename {tool/source_sync => tool_shared/authored/sync}/load_command_module.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/make_Harmony_tree_dict (100%) rename {tool/source_sync => tool_shared/authored/sync}/manus_2025_11_20_215471873762383.txt (100%) rename {tool/source_sync => tool_shared/authored/sync}/meta.py (100%) rename {tool/source_sync => tool_shared/authored/sync}/newer (100%) rename {tool/source_sync => tool_shared/authored/sync}/older (100%) rename {tool/source_sync => tool_shared/authored/sync}/skeleton.py (100%) create mode 100644 tool_shared/authored/version delete mode 100755 tool_shared/bespoke/scratchpad delete mode 100755 tool_shared/bespoke/version delete mode 100644 tool_shared/patch/.gitkeep diff --git a/tool/set_project_permissions b/tool/after_pull similarity index 100% rename from tool/set_project_permissions rename to tool/after_pull diff --git a/tool/git-empty-dir b/tool/git-empty-dir deleted file mode 120000 index 51e7f2a..0000000 --- a/tool/git-empty-dir +++ /dev/null @@ -1 +0,0 @@ -source_git-empty-dir/CLI.py \ No newline at end of file diff --git a/tool/sync b/tool/sync index fc351d9..e58d04e 120000 --- a/tool/sync +++ b/tool/sync @@ -1 +1 @@ -source_sync/CLI.py \ No newline at end of file +../tool_shared/authored/sync/CLI.py \ No newline at end of file diff --git a/tool_shared/bespoke/env b/tool_shared/authored/env similarity index 100% rename from tool_shared/bespoke/env rename to tool_shared/authored/env diff --git a/tool/source_git-empty-dir/CLI.py b/tool_shared/authored/git-empty-dir/CLI.py similarity index 100% rename from tool/source_git-empty-dir/CLI.py rename to tool_shared/authored/git-empty-dir/CLI.py diff --git a/tool/source_git-empty-dir/Harmony.py b/tool_shared/authored/git-empty-dir/Harmony.py similarity index 100% rename from tool/source_git-empty-dir/Harmony.py rename to tool_shared/authored/git-empty-dir/Harmony.py diff --git a/tool/source_git-empty-dir/load_command_module.py b/tool_shared/authored/git-empty-dir/load_command_module.py similarity index 100% rename from tool/source_git-empty-dir/load_command_module.py rename to tool_shared/authored/git-empty-dir/load_command_module.py diff --git a/tool/source_git-empty-dir/meta.py b/tool_shared/authored/git-empty-dir/meta.py similarity index 100% rename from tool/source_git-empty-dir/meta.py rename to tool_shared/authored/git-empty-dir/meta.py diff --git a/tool/source_git-empty-dir/source_sync b/tool_shared/authored/git-empty-dir/source_sync similarity index 100% rename from tool/source_git-empty-dir/source_sync rename to tool_shared/authored/git-empty-dir/source_sync diff --git a/tool/source_sync/A_minus_B b/tool_shared/authored/sync/A_minus_B similarity index 100% rename from tool/source_sync/A_minus_B rename to tool_shared/authored/sync/A_minus_B diff --git a/tool/source_sync/CLI.py b/tool_shared/authored/sync/CLI.py similarity index 100% rename from tool/source_sync/CLI.py rename to tool_shared/authored/sync/CLI.py diff --git a/tool/source_sync/GitIgnore.py b/tool_shared/authored/sync/GitIgnore.py similarity index 100% rename from tool/source_sync/GitIgnore.py rename to tool_shared/authored/sync/GitIgnore.py diff --git a/tool/source_sync/Harmony.py b/tool_shared/authored/sync/Harmony.py similarity index 100% rename from tool/source_sync/Harmony.py rename to tool_shared/authored/sync/Harmony.py diff --git a/tool/source_sync/Harmony_where b/tool_shared/authored/sync/Harmony_where similarity index 100% rename from tool/source_sync/Harmony_where rename to tool_shared/authored/sync/Harmony_where diff --git a/tool/source_sync/README.org b/tool_shared/authored/sync/README.org similarity index 100% rename from tool/source_sync/README.org rename to tool_shared/authored/sync/README.org diff --git a/tool/source_sync/command.py b/tool_shared/authored/sync/command.py similarity index 100% rename from tool/source_sync/command.py rename to tool_shared/authored/sync/command.py diff --git a/tool/source_sync/doc.py b/tool_shared/authored/sync/doc.py similarity index 100% rename from tool/source_sync/doc.py rename to tool_shared/authored/sync/doc.py diff --git a/tool/source_sync/in_between_and_below b/tool_shared/authored/sync/in_between_and_below similarity index 100% rename from tool/source_sync/in_between_and_below rename to tool_shared/authored/sync/in_between_and_below diff --git a/tool/source_sync/load_command_module.py b/tool_shared/authored/sync/load_command_module.py similarity index 100% rename from tool/source_sync/load_command_module.py rename to tool_shared/authored/sync/load_command_module.py diff --git a/tool/source_sync/make_Harmony_tree_dict b/tool_shared/authored/sync/make_Harmony_tree_dict similarity index 100% rename from tool/source_sync/make_Harmony_tree_dict rename to tool_shared/authored/sync/make_Harmony_tree_dict diff --git a/tool/source_sync/manus_2025_11_20_215471873762383.txt b/tool_shared/authored/sync/manus_2025_11_20_215471873762383.txt similarity index 100% rename from tool/source_sync/manus_2025_11_20_215471873762383.txt rename to tool_shared/authored/sync/manus_2025_11_20_215471873762383.txt diff --git a/tool/source_sync/meta.py b/tool_shared/authored/sync/meta.py similarity index 100% rename from tool/source_sync/meta.py rename to tool_shared/authored/sync/meta.py diff --git a/tool/source_sync/newer b/tool_shared/authored/sync/newer similarity index 100% rename from tool/source_sync/newer rename to tool_shared/authored/sync/newer diff --git a/tool/source_sync/older b/tool_shared/authored/sync/older similarity index 100% rename from tool/source_sync/older rename to tool_shared/authored/sync/older diff --git a/tool/source_sync/skeleton.py b/tool_shared/authored/sync/skeleton.py similarity index 100% rename from tool/source_sync/skeleton.py rename to tool_shared/authored/sync/skeleton.py diff --git a/tool_shared/authored/version b/tool_shared/authored/version new file mode 100644 index 0000000..9e30ada --- /dev/null +++ b/tool_shared/authored/version @@ -0,0 +1,2 @@ +Harmony v1.0 2025-11-25 + diff --git a/tool_shared/bespoke/scratchpad b/tool_shared/bespoke/scratchpad deleted file mode 100755 index aa7c35a..0000000 --- a/tool_shared/bespoke/scratchpad +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env -S python3 -B -# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- - -import os, sys, shutil, stat, pwd, grp, subprocess - -HELP = """usage: scratchpad {ls|clear|help|make|write|size|find|lock|unlock} [ARGS] - ls| list List scratchpad in an indented tree with perms and owner (quiet if missing). - clear Remove all contents of scratchpad/ except top-level .gitignore. - clear NAME Remove scratchpad/NAME only. - make [NAME] Ensure scratchpad/ exists with .gitignore; with NAME, mkdir scratchpad/NAME. - write SRC [DST] Copy file/dir SRC into scratchpad (to DST if given; parents created). - size Print 'empty' if only .gitignore; else total bytes and item count. - find [OPTS...] Run system 'find' rooted at scratchpad/ with OPTS (omit literal 'scratchpad'). - lock PATH... Attempt 'chattr +i' on given paths under scratchpad/ (no state kept). - unlock PATH... Attempt 'chattr -i' on given paths under scratchpad/. - -Examples: - scratchpad make - scratchpad write ~/Downloads/test.tar.gz - scratchpad find -type f -mtime +30 -print # files older than 30 days - scratchpad lock some/dir important.txt - scratchpad unlock some/dir important.txt -""" - -CWD = os.getcwd() -SP = os.path.join(CWD, "scratchpad") -GITIGNORE = os.path.join(SP, ".gitignore") - -def have_sp() -> bool: - return os.path.isdir(SP) - -def ensure_sp(): - os.makedirs(SP, exist_ok=True) - ensure_gitignore() - -def ensure_gitignore(): - os.makedirs(SP, exist_ok=True) - if not os.path.isfile(GITIGNORE): - with open(GITIGNORE, "w", encoding="utf-8") as f: - f.write("*\n!.gitignore\n") - -def filemode(mode: int) -> str: - try: - return stat.filemode(mode) - except Exception: - return oct(mode & 0o777) - -def owner_group(st) -> str: - try: - return f"{pwd.getpwuid(st.st_uid).pw_name}:{grp.getgrgid(st.st_gid).gr_name}" - except Exception: - return f"{st.st_uid}:{st.st_gid}" - -def rel_depth(base: str, root: str) -> int: - rel = os.path.relpath(base, root) - return 0 if rel == "." else rel.count(os.sep) + 1 - -def ls_tree(root: str) -> None: - if not have_sp(): - return - print("scratchpad/") - - def walk(path: str, indent: str, is_root: bool) -> None: - try: - it = list(os.scandir(path)) - except FileNotFoundError: - return - - dirs = [e for e in it if e.is_dir(follow_symlinks=False)] - files = [e for e in it if not e.is_dir(follow_symlinks=False)] - dirs.sort(key=lambda e: e.name) - files.sort(key=lambda e: e.name) - - if is_root: - # 1) root-level hidden files first - for f in (e for e in files if e.name.startswith(".")): - st = os.lstat(f.path) - print(f"{filemode(st.st_mode)} {owner_group(st)} {indent}{f.name}") - # 2) then directories (and recurse so children sit under the parent) - for d in dirs: - st = os.lstat(d.path) - print(f"{filemode(st.st_mode)} {owner_group(st)} {indent}{d.name}/") - walk(d.path, indent + ' ', False) - # 3) then non-hidden files - for f in (e for e in files if not e.name.startswith(".")): - st = os.lstat(f.path) - print(f"{filemode(st.st_mode)} {owner_group(st)} {indent}{f.name}") - else: - # subdirs: keep previous order (dirs first, then files; dotfiles naturally sort first) - for d in dirs: - st = os.lstat(d.path) - print(f"{filemode(st.st_mode)} {owner_group(st)} {indent}{d.name}/") - walk(d.path, indent + ' ', False) - for f in files: - st = os.lstat(f.path) - print(f"{filemode(st.st_mode)} {owner_group(st)} {indent}{f.name}") - - walk(root, " ", True) - - -def clear_all() -> None: - if not have_sp(): - return - for name in os.listdir(SP): - p = os.path.join(SP, name) - if name == ".gitignore" and os.path.isfile(p): - continue # preserve only top-level .gitignore - if os.path.isdir(p) and not os.path.islink(p): - shutil.rmtree(p, ignore_errors=True) - else: - try: os.unlink(p) - except FileNotFoundError: pass - -def clear_subdir(sub: str) -> None: - if not have_sp(): - return - target = os.path.normpath(os.path.join(SP, sub)) - try: - if os.path.commonpath([SP]) != os.path.commonpath([SP, target]): - return - except Exception: - return - if os.path.isdir(target) and not os.path.islink(target): - shutil.rmtree(target, ignore_errors=True) - -def cmd_make(args): - ensure_sp() - if args: - os.makedirs(os.path.join(SP, args[0]), exist_ok=True) - -def cmd_write(args): - if len(args) < 1: - print(HELP); return - if not have_sp(): - ensure_sp() - src = args[0] - dst = args[1] if len(args) >= 2 else (os.path.basename(src.rstrip(os.sep)) or "untitled") - dst_path = os.path.normpath(os.path.join(SP, dst)) - try: - if os.path.commonpath([SP]) != os.path.commonpath([SP, dst_path]): - print("refusing to write outside scratchpad", file=sys.stderr); return - except Exception: - print("invalid destination", file=sys.stderr); return - os.makedirs(os.path.dirname(dst_path), exist_ok=True) - if os.path.isdir(src): - if os.path.exists(dst_path): - shutil.rmtree(dst_path, ignore_errors=True) - shutil.copytree(src, dst_path, dirs_exist_ok=False) - else: - shutil.copy2(src, dst_path) - -def cmd_size(): - if not have_sp(): - return - names = os.listdir(SP) - if [n for n in names if n != ".gitignore"] == []: - print("empty"); return - total = 0; count = 0 - for base, dirs, files in os.walk(SP): - for fn in files: - if fn == ".gitignore": - continue - p = os.path.join(base, fn) - try: - total += os.path.getsize(p); count += 1 - except OSError: - pass - print(f"bytes={total} items={count}") - -def cmd_find(args): - if not have_sp(): - return - try: - subprocess.run(["find", SP] + args, check=False) - except FileNotFoundError: - print("find not available", file=sys.stderr) - -def cmd_chattr(flag: str, paths): - if not have_sp() or not paths: - return - try: - subprocess.run(["chattr", "-V"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False) - except FileNotFoundError: - print("chattr not available; lock/unlock skipped", file=sys.stderr); return - for rel in paths: - target = os.path.normpath(os.path.join(SP, rel)) - try: - if os.path.commonpath([SP]) != os.path.commonpath([SP, target]): - continue - except Exception: - continue - try: - subprocess.run(["chattr", flag, target], check=False) - except Exception: - pass - -def CLI(): - if len(sys.argv) < 2: - print(HELP); return - cmd, *args = sys.argv[1:] - if cmd == "ls" or cmd =="list": - if have_sp(): ls_tree(SP) - else: return - elif cmd == "clear": - if len(args) >= 1: clear_subdir(args[0]) - else: clear_all() - elif cmd == "help": - print(HELP) - elif cmd == "make": - cmd_make(args) - elif cmd == "write": - cmd_write(args) - elif cmd == "size": - cmd_size() - elif cmd == "find": - cmd_find(args) - elif cmd == "lock": - cmd_chattr("+i", args) - elif cmd == "unlock": - cmd_chattr("-i", args) - else: - print(HELP) - -if __name__ == "__main__": - CLI() diff --git a/tool_shared/bespoke/version b/tool_shared/bespoke/version deleted file mode 100755 index 9d91a98..0000000 --- a/tool_shared/bespoke/version +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/env bash -script_afp=$(realpath "${BASH_SOURCE[0]}") - -echo "Harmony v0.1 2025-05-19" - diff --git a/tool_shared/patch/.gitkeep b/tool_shared/patch/.gitkeep deleted file mode 100644 index e69de29..0000000 -- 2.20.1