[Sync] replace the flat layout with the unified stow tree

Supersedes the old flat .config/ layout (last published 2026-06-28) with the
private repo's structure: one shared base plus per-host overlays.

- packages: common/ gui/ lw/ fl/ wm/ plus install.sh and bin/ tooling
  (dotsync, reconcile-hyde.sh)
- new README covering the layout, deploy order and the HyDE dependency
- current HyDE waybar rig (layouts/, cava), pi agent extensions, claude/
  config, tmux, presenterm, aichat roles
- fish: kp (keepassxc-cli + fzf picker, db path from $KP_DB) and
  bind_M_n_history (alt+1..9 recalls the nth history entry)
- drops cruft that should never have been tracked: the duplicate top-level
  .pi/ copy, btop.log, zellij config.kdl.bak, fish_variables
- .pi/agent/auth.json is gitignored; auth.json.example ships instead

Host-specific work sessions and the personal backlog stay in the private
tree. Endpoint locators in the llamacpp/whisper guides are placeholders
($SERVER, <own-domain>) — the guides themselves stay, since they are the
useful part.
This commit is contained in:
coja
2026-08-13 02:53:06 +02:00
parent f62cb40499
commit 1e39e0cb4b
314 changed files with 12306 additions and 10772 deletions
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""PostToolUse hook: format an edited file with the right formatter.
Reads the hook JSON from stdin, extracts the edited file path, and runs a
language-appropriate formatter -- but, to avoid imposing a style on a project
that never asked for it, opinionated formatters (stylua, ruff, prettier, taplo)
only run when the project opts in via a config file discoverable by walking up
from the edited file. Canonical single-style toolchains run on sight: gofmt and
rustfmt on their go.mod / Cargo.toml marker; fish_indent and shfmt whenever the
tool itself is installed (shell and fish have one de-facto style, no config).
Best-effort: always exits 0 so a format hiccup never blocks an edit. jq is not
guaranteed on the host, hence python for the stdin JSON parse.
"""
import json
import os
import shutil
import subprocess
import sys
def find_up(start, names):
"""Nearest ancestor dir (incl. the file's own) containing any of `names`."""
d = os.path.dirname(os.path.abspath(start))
while True:
if any(os.path.exists(os.path.join(d, n)) for n in names):
return d
parent = os.path.dirname(d)
if parent == d:
return None
d = parent
def has_toml_section(start, filename, section):
"""True if the nearest ancestor `filename` contains `section`."""
d = find_up(start, [filename])
if not d:
return False
try:
with open(os.path.join(d, filename), errors="ignore") as fh:
return section in fh.read()
except Exception:
return False
def stylua_cmd(path):
exe = os.path.expanduser("~/.local/share/nvim/mason/bin/stylua")
if not os.access(exe, os.X_OK):
exe = shutil.which("stylua")
if exe and find_up(path, [".stylua.toml", "stylua.toml"]):
return [exe, path]
return None
def ruff_cmd(path):
exe = shutil.which("ruff")
if exe and (find_up(path, ["ruff.toml", ".ruff.toml"])
or has_toml_section(path, "pyproject.toml", "[tool.ruff")):
return [exe, "format", path]
return None
_PRETTIER_CFGS = [
".prettierrc", ".prettierrc.json", ".prettierrc.yaml", ".prettierrc.yml",
".prettierrc.json5", ".prettierrc.js", ".prettierrc.cjs", ".prettierrc.mjs",
".prettierrc.toml", "prettier.config.js", "prettier.config.cjs",
"prettier.config.mjs",
]
def prettier_cmd(path):
exe = shutil.which("prettier")
if exe and find_up(path, _PRETTIER_CFGS):
return [exe, "--write", path]
return None
def gofmt_cmd(path):
exe = shutil.which("gofmt")
if exe and find_up(path, ["go.mod"]): # one canonical style
return [exe, "-w", path]
return None
def rustfmt_cmd(path):
exe = shutil.which("rustfmt")
if exe and find_up(path, ["Cargo.toml", "rustfmt.toml", ".rustfmt.toml"]):
return [exe, path]
return None
def fish_cmd(path):
exe = shutil.which("fish_indent") # ships with fish, one canonical style
return [exe, "-w", path] if exe else None
def shfmt_cmd(path):
exe = shutil.which("shfmt") # de-facto shell formatter, sane defaults
return [exe, "-w", path] if exe else None
def taplo_cmd(path):
exe = shutil.which("taplo")
# opt-in: TOML reflow/align is intrusive, so only where the project asked.
if exe and find_up(path, ["taplo.toml", ".taplo.toml"]):
return [exe, "format", path]
return None
HANDLERS = {
".lua": stylua_cmd,
".py": ruff_cmd,
".js": prettier_cmd, ".jsx": prettier_cmd, ".mjs": prettier_cmd,
".cjs": prettier_cmd, ".ts": prettier_cmd, ".tsx": prettier_cmd,
".css": prettier_cmd, ".scss": prettier_cmd, ".less": prettier_cmd,
".html": prettier_cmd, ".vue": prettier_cmd, ".json": prettier_cmd,
".md": prettier_cmd, ".yaml": prettier_cmd, ".yml": prettier_cmd,
".go": gofmt_cmd,
".rs": rustfmt_cmd,
".fish": fish_cmd,
".sh": shfmt_cmd, ".bash": shfmt_cmd,
".toml": taplo_cmd,
}
def main():
try:
data = json.load(sys.stdin)
except Exception:
return
ti = data.get("tool_input") or {}
tr = data.get("tool_response") or {}
path = ti.get("file_path") or tr.get("filePath") or ""
if not path or not os.path.isfile(path):
return
handler = HANDLERS.get(os.path.splitext(path)[1].lower())
if not handler:
return
cmd = handler(path)
if not cmd:
return
try:
subprocess.run(cmd, timeout=15, capture_output=True)
except Exception:
pass
if __name__ == "__main__":
main()