diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index f157ff55..9889ae00 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest env: # The md-naming / file-length hooks are thin shims that exec the shared - # gate out of the utils repo. A runner has no ~/utils, so without this + # gate out of the utils repo. A runner has no ~/src/utils, so without this # checkout they fail with "shared gate not found" -- which is exactly # how this workflow went red on 2026-08-24. UTILS_ROOT: ${{ github.workspace }}/.utils diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b782144..93a373ce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -585,7 +585,7 @@ repos: # Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the # rule that a TODO carries "REMOVE ME AFTER FINISH" and nothing else may. - # Shared gate in ~/utils owns the namespaces and the exemption list, so + # Shared gate in ~/src/utils owns the namespaces and the exemption list, so # one repo's idea of a valid name cannot drift from every other repo's. - id: md-naming name: markdown naming convention diff --git a/AGENTS.md b/AGENTS.md index 06991922..174c844e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,19 +32,19 @@ Extracted to their own repos: [`steam-backlog-enforcer`](https://github.com/kuhy [`dufs-cloud`](https://github.com/kuhyx/dufs-cloud), [`build-your-x`](https://github.com/kuhyx/build-your-x) (lives at `~/build_your_x`), [`reverse-survivors`](https://github.com/kuhyx/reverse-survivors), -[`kcd2-dice-solver`](https://github.com/kuhyx/kcd2-dice-solver) (lives at `~/kcd2-dice-solver`), +[`kcd2-dice-solver`](https://github.com/kuhyx/kcd2-dice-solver) (lives at `~/src/kcd2-dice-solver`), [`focus-owner`](https://github.com/kuhyx/focus-owner), [`mtk-root`](https://github.com/kuhyx/mtk-root), [`system-maintenance`](https://github.com/kuhyx/system-maintenance) (lives at -`~/system-maintenance`), +`~/src/system-maintenance`), [`android-guardian`](https://github.com/kuhyx/android-guardian) (lives at `~/android-guardian`), [`phone-focus-mode`](https://github.com/kuhyx/phone-focus-mode) (with `python_pkg/focus_policy`), [`hosts-blocker`](https://github.com/kuhyx/hosts-blocker) (lives at -`~/hosts-blocker`), +`~/src/hosts-blocker`), [`digital-wellbeing`](https://github.com/kuhyx/digital-wellbeing) (lives at -`~/digital-wellbeing`; carries the six suites that used to sit in +`~/src/digital-wellbeing`; carries the six suites that used to sit in `linux_configuration/tests/`). Monorepo callers resolve both via `linux_configuration/lib/extracted_repos.sh`, which reads the invoking user's home from `SUDO_USER` — never `$HOME`, which is `/root` under sudo and under diff --git a/DOCS-installer-fix.md b/DOCS-installer-fix.md index 23457331..7cef5eeb 100644 --- a/DOCS-installer-fix.md +++ b/DOCS-installer-fix.md @@ -4,10 +4,10 @@ Paste everything below into a **fresh** Claude session (`/clear` first). --- -`~/testsAndMisc/linux_configuration/install_core_system.sh` is the documented +`~/src/testsAndMisc/linux_configuration/install_core_system.sh` is the documented way to set this machine up from scratch. It does not work. Three defects were found on 2026-08-22 by running the real installer in a disposable Arch VM -(`~/utils/vmbox`) rather than by reading it — 2 of its 7 modules cannot install +(`~/src/utils/vmbox`) rather than by reading it — 2 of its 7 modules cannot install at all, and a third installs something that silently does nothing. **Do not trust this file's line numbers without re-checking them.** They were @@ -37,14 +37,14 @@ machine already exists in the repos, but the documented install path never runs Both packages were extracted into their own repos. Verified on disk 2026-08-22: -- `~/screen-locker/install_systemd.sh` (5637 b, executable) -- `~/steam-backlog-enforcer/install.sh` (3248 b, executable) +- `~/src/screen-locker/install_systemd.sh` (5637 b, executable) +- `~/src/steam-backlog-enforcer/install.sh` (3248 b, executable) "Workout screen locker" is a **CORE** module, so a fresh machine fails on the first thing the installer does. -Note this makes the installer **cross-repo**: `~/testsAndMisc` no longer owns -that code. That is design question (b) below — do not just hardcode `~/screen-locker` +Note this makes the installer **cross-repo**: `~/src/testsAndMisc` no longer owns +that code. That is design question (b) below — do not just hardcode `~/src/screen-locker` and call it done without putting the question to kuhy. ## Defect 2 — guard-lib is required but never installed @@ -52,14 +52,14 @@ and call it done without putting the question to kuhy. `scripts/periodic_background/digital_wellbeing/setup_midnight_shutdown.sh` depends on `guardctl` and dies with `guardctl not found on PATH`. Nothing in `install_core_system.sh` installs guard-lib, which lives at -`~/utils/guard-lib/install.sh` — a **third** sibling repo. +`~/src/utils/guard-lib/install.sh` — a **third** sibling repo. So the CORE module "Midnight shutdown timer" always fails on a fresh machine. ## Defect 3 — the hosts file-guards are installed only by a one-shot script `install.sh` in the extracted `hosts-blocker` repo -(github.com/kuhyx/hosts-blocker, checked out at `~/hosts-blocker`; it was +(github.com/kuhyx/hosts-blocker, checked out at `~/src/hosts-blocker`; it was `scripts/periodic_background/hosts/install.sh` when this was written) never installs the hosts/nsswitch/resolved file-guards. Its own comment block around **line 109** @@ -90,10 +90,10 @@ diff; the second stops `single_use/fixes/` from being load-bearing, which is arguably what "single use" is supposed to mean. **(b) How should a testsAndMisc installer reach code in sibling repos?** -`~/screen-locker`, `~/steam-backlog-enforcer` and `~/utils/guard-lib` are +`~/src/screen-locker`, `~/src/steam-backlog-enforcer` and `~/src/utils/guard-lib` are separate repos now. Options, none obviously right: -1. **Expect-adjacent** — check for `~/screen-locker` etc., fail with a clear +1. **Expect-adjacent** — check for `~/src/screen-locker` etc., fail with a clear message naming the repo to clone. Simple; the installer stops being self-contained. 2. **Clone if missing** — the installer clones the siblings it needs. Works on @@ -112,7 +112,7 @@ an actionable message rather than silently skipping a module. The fix is done when, in a **fresh** vmbox guest: ```bash -vm share ~/testsAndMisc && vm share ~/utils +vm share ~/src/testsAndMisc && vm share ~/src/utils vm new inst vm run inst 'git clone --no-hardlinks -q /mnt/hostrepo/testsAndMisc ~/tam' # plus whatever cloning question (b)'s answer implies for the sibling repos @@ -134,16 +134,16 @@ vm run inst 'cd ~/tam && echo y | bash linux_configuration/install_core_system.s - **Sandbox first, always.** This is a root installer that writes `/etc`, systemd units, pacman hooks and `chattr +i`. Never run it on the host to - test it. `~/utils/vmbox`: `vm new` / `vm run` / `vm reset`. A sandbox pass + test it. `~/src/utils/vmbox`: `vm new` / `vm run` / `vm reset`. A sandbox pass does **not** prove the host is fine — report it that way. - Installers prompt; `vm run` closes stdin, so pipe `echo y |`. - vmbox is fast: `vm run` ~14s cold / ~3s warm, `vm reset` ~2s. Reuse one sandbox, do not rebuild per command. -- `~/utils/vmbox/SESSION_RESULTS.md` has the full 2026-08-22 measurements. -- The 250-line file cap applies (`~/utils/file_length`). `install.sh` is at +- `~/src/utils/vmbox/SESSION_RESULTS.md` has the full 2026-08-22 measurements. +- The 250-line file cap applies (`~/src/utils/file_length`). `install.sh` is at 130 lines and the migration at 169 — merging them wholesale will breach it, which is worth weighing in design question (a). -- `~/testsAndMisc` requires a `docs/superpowers/evidence/*.json` artifact for +- `~/src/testsAndMisc` requires a `docs/superpowers/evidence/*.json` artifact for any code change (pre-commit gate `ai-evidence-contract`); copy `docs/superpowers/evidence/template.json`. diff --git a/DOCS-restructure-handoff.md b/DOCS-restructure-handoff.md index 0a711087..f54ac68e 100644 --- a/DOCS-restructure-handoff.md +++ b/DOCS-restructure-handoff.md @@ -29,10 +29,10 @@ Verify any of it: ## What landed this session **`hosts-blocker`** — [github.com/kuhyx/hosts-blocker](https://github.com/kuhyx/hosts-blocker), -cloned at `~/hosts-blocker`. 52 commits. +cloned at `~/src/hosts-blocker`. 52 commits. **`digital-wellbeing`** — [github.com/kuhyx/digital-wellbeing](https://github.com/kuhyx/digital-wellbeing), -cloned at `~/digital-wellbeing`. 89 commits, and it carries the six suites that +cloned at `~/src/digital-wellbeing`. 89 commits, and it carries the six suites that used to sit in `linux_configuration/tests/`. **The cap enforces.** `--warn` is gone from the `directory-depth-cap` hook, and @@ -76,7 +76,7 @@ copies were the only ones on the machine. - **`syncyomi-guard` is red** since 00:00 on 2026-08-23, working as designed: the library collapsed from 2185 manga to 8. See the `syncyomi-restore-silent-partial` memory; do NOT checkpoint the WAL before - recovering. Newest snapshot: `~/syncyomi/snapshots/`, 2026-08-15. + recovering. Newest snapshot: `~/services/syncyomi/snapshots/`, 2026-08-15. ## Traps worth keeping diff --git a/TODO-design-audit.md b/TODO-design-audit.md index 08379483..775799a3 100644 --- a/TODO-design-audit.md +++ b/TODO-design-audit.md @@ -9,7 +9,7 @@ not measured from a rendered page. --- -## kcd2_dice_solver (`~/testsAndMisc/kcd2_dice_solver`, Vite/TS) +## kcd2_dice_solver (`~/src/testsAndMisc/kcd2_dice_solver`, Vite/TS) Design tokens live in `src/index.css` (422 lines), a single `:root` block of CSS custom properties consumed by all 5 component files. This is the correct @@ -60,7 +60,7 @@ system (see Rule 4). --- -## bucket_catch frontend (`~/testsAndMisc/bucket_catch/packages/frontend`, Vite/TS) +## bucket_catch frontend (`~/src/testsAndMisc/bucket_catch/packages/frontend`, Vite/TS) **Confirmed: no shared design tokens exist.** `src/index.css` is a 12-line reset only (box-sizing + html/body/#root margin/font/background/color) — no diff --git a/docs/DOCS-agent-harness-evaluation-2026-08-18.md b/docs/DOCS-agent-harness-evaluation-2026-08-18.md index 2e669a5f..6072df49 100644 --- a/docs/DOCS-agent-harness-evaluation-2026-08-18.md +++ b/docs/DOCS-agent-harness-evaluation-2026-08-18.md @@ -27,7 +27,7 @@ Backed by repeated, concrete evidence across multiple sessions/repos. ### MCP Inspector (mcp, typescript, 10.6k★) You author and maintain **7+ MCP servers** (`i3wm-mcp`, `yay-mcp`, `aseprite-mcp`, `reaper-mcp`, `opengameart-mcp`, -`freesound-mcp`, plus 23 more under `~/mcp-servers/servers/`) per `project-mcp-server-fleet.md` and the deployment +`freesound-mcp`, plus 23 more under `~/src/mcp-servers/servers/`) per `project-mcp-server-fleet.md` and the deployment skill `project-mcp-per-repo-pattern.md`. Nothing in your memory or tooling survey shows a dedicated tool/resource/prompt debugger for the servers you _build_ — you've been testing them indirectly through Claude Code sessions themselves. MCP Inspector is a GUI built exactly for this gap: exercising tools/resources/prompts in isolation before wiring a new @@ -44,7 +44,7 @@ which your own hook (`track_reads_pretool.sh`, since disabled) was already tryin ### Docker MCP Gateway (mcp, sandbox, cli, 1.5k★) -You run 23+ Dockerfiles for self-hosted MCP server containers (`~/mcp-servers/servers/*`) per the repo survey, on top +You run 23+ Dockerfiles for self-hosted MCP server containers (`~/src/mcp-servers/servers/*`) per the repo survey, on top of an already-Docker-heavy self-hosted stack (Gitea, SearXNG, Syncyomi, Joplin Server, all `docker-compose.yml`-based). A container-aware MCP gateway is a direct fit for consolidating discovery/auth across a fleet this size, rather than each server being wired in ad hoc. Speculative on the _specific_ tool, high-confidence on the _need_ — you already @@ -153,7 +153,7 @@ demonstrated preference for deterministic scripts you can read, diff, and put un ### browser-use, Stagehand, Steel (standalone browser-agent harnesses) Rejected as _redundant_, not irrelevant — you already have `claude-in-chrome` MCP tools wired in and actively used this -session, plus `servo-fetch` (Servo-engine fetch/crawl/screenshot MCP) under `~/mcp-servers/servers/`. Both solve the +session, plus `servo-fetch` (Servo-engine fetch/crawl/screenshot MCP) under `~/src/mcp-servers/servers/`. Both solve the "drive a real browser from an agent" problem you'd otherwise reach for these tools to solve. No session shows friction with the browser tooling you already have that a swap would fix. diff --git a/docs/DOCS-device-owner-research.md b/docs/DOCS-device-owner-research.md index 2a7313b8..387b9d17 100644 --- a/docs/DOCS-device-owner-research.md +++ b/docs/DOCS-device-owner-research.md @@ -75,7 +75,7 @@ touching anything. one.** This is the highest-value artifact on the phone. ```bash adb -s 23181JEGR08034 shell 'find /sdcard -iname "*.kdbx" 2>/dev/null' - adb -s 23181JEGR08034 pull ~/phone-backup/ + adb -s 23181JEGR08034 pull ~/data/phone-backup/ ``` 3. **Write down on paper**: mBank customer ID + password, Revolut passcode, Signal PIN, KeePassDX master password, both Google passwords. @@ -87,13 +87,13 @@ touching anything. 5. **Signal**: Settings → Backups → On-device backups. **Record the 30-digit passphrase on paper** — it is not your Signal PIN. ```bash - adb -s 23181JEGR08034 pull /sdcard/Signal/Backups/ ~/phone-backup/signal/ + adb -s 23181JEGR08034 pull /sdcard/Signal/Backups/ ~/data/phone-backup/signal/ ``` Registration Lock: forgetting the PIN locks you out for up to 7 days. 6. **Photos and downloads**: ```bash - adb -s 23181JEGR08034 pull /sdcard/DCIM/ ~/phone-backup/DCIM/ - adb -s 23181JEGR08034 pull /sdcard/Download/ ~/phone-backup/Download/ + adb -s 23181JEGR08034 pull /sdcard/DCIM/ ~/data/phone-backup/DCIM/ + adb -s 23181JEGR08034 pull /sdcard/Download/ ~/data/phone-backup/Download/ ``` 7. **mBank**: in the transactional service, note how to remove a paired device. The old phone stays listed as active after a reset and must be detached. diff --git a/docs/DOCS-dns-filtering-parked.md b/docs/DOCS-dns-filtering-parked.md index 447a1878..61e14f1d 100644 --- a/docs/DOCS-dns-filtering-parked.md +++ b/docs/DOCS-dns-filtering-parked.md @@ -36,7 +36,7 @@ To remove it entirely: ```bash sudo systemctl disable --now stunnel-dot.service dot-cert-sync.timer sudo rm -f /etc/stunnel/dot-resolver.conf /etc/systemd/system/stunnel-dot.service -rm -f ~/gitea/sites/dns.caddy && docker exec gitea-caddy caddy reload \ +rm -f ~/services/gitea/sites/dns.caddy && docker exec gitea-caddy caddy reload \ --config /etc/caddy/Caddyfile --adapter caddyfile ``` diff --git a/docs/DOCS-restructure-live-state.md b/docs/DOCS-restructure-live-state.md index 8ad66679..1d6b8af4 100644 --- a/docs/DOCS-restructure-live-state.md +++ b/docs/DOCS-restructure-live-state.md @@ -17,56 +17,56 @@ its timer next fires. ``` ## systemd units /etc/systemd/system/dns-blocklist-refresh.service - 7:ExecStart=/home/kuhy/testsAndMisc/linux_configuration/features/setup_dns_blocker.sh refresh + 7:ExecStart=/home/kuhy/src/testsAndMisc/linux_configuration/features/setup_dns_blocker.sh refresh /etc/systemd/system/media-organizer.service - 10:ExecStart=/home/kuhy/testsAndMisc/linux_configuration/utils/organize_downloads.sh + 10:ExecStart=/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh /home/kuhy/.config/systemd/user/endurain-import.service - 3:Documentation=file:///home/kuhy/testsAndMisc/endurain/README.md - 11:WorkingDirectory=/home/kuhy/testsAndMisc - 21:ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/testsAndMisc/endurain/.api_key)" /home/kuhy/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import' + 3:Documentation=file:///home/kuhy/src/testsAndMisc/endurain/README.md + 11:WorkingDirectory=/home/kuhy/src/testsAndMisc + 21:ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/src/testsAndMisc/endurain/.api_key)" /home/kuhy/src/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import' /home/kuhy/.config/systemd/user/morning-routine.service - 13:Environment=PYTHONPATH=%h/testsAndMisc - 16:WorkingDirectory=%h/testsAndMisc + 13:Environment=PYTHONPATH=%h/src/testsAndMisc + 16:WorkingDirectory=%h/src/testsAndMisc /home/kuhy/.config/systemd/user/syncyomi-guard.service - 8:WorkingDirectory=/home/kuhy/testsAndMisc - 9:Environment=PYTHONPATH=/home/kuhy/testsAndMisc + 8:WorkingDirectory=/home/kuhy/src/testsAndMisc + 9:Environment=PYTHONPATH=/home/kuhy/src/testsAndMisc ## /etc references /etc/guard-lib/targets/nsswitch.conf - 4:PLUGIN="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/nsswitch-plugin.sh" + 4:PLUGIN="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/nsswitch-plugin.sh" /etc/guard-lib/targets/resolved.conf - 4:PLUGIN="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/resolved-plugin.sh" + 4:PLUGIN="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/resolved-plugin.sh" ## installed scripts referencing the repo /usr/local/bin/hosts-file-monitor.sh - 10:HOSTS_INSTALL_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh" + 10:HOSTS_INSTALL_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh" /usr/local/bin/periodic-system-maintenance.sh - 26:PACMAN_WRAPPER_INSTALL="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh" - 27:MAKEPKG_WRAPPER_INSTALL="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_makepkg_wrapper.sh" - 28:HOSTS_INSTALL_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh" + 26:PACMAN_WRAPPER_INSTALL="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh" + 27:MAKEPKG_WRAPPER_INSTALL="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_makepkg_wrapper.sh" + 28:HOSTS_INSTALL_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh" /usr/local/bin/wsg-grabber 4:export PYTHONPATH="/home/kuhy/testsAndMisc${PYTHONPATH:+:$PYTHONPATH}" ## symlinks into the repo - /home/kuhy/.config/i3blocks/claude_usage.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/claude_usage.sh - /home/kuhy/.config/i3blocks/config -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/config - /home/kuhy/.config/i3blocks/cpu_monitor.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/cpu_monitor.sh - /home/kuhy/.config/i3blocks/disk.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/disk.sh - /home/kuhy/.config/i3blocks/ethernet.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/ethernet.sh - /home/kuhy/.config/i3blocks/gpu_monitor.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/gpu_monitor.sh - /home/kuhy/.config/i3blocks/memory.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/memory.sh - /home/kuhy/.config/i3blocks/motherboard_temp.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/motherboard_temp.sh - /home/kuhy/.config/i3blocks/pc_startup_status.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/pc_startup_status.sh - /home/kuhy/.config/i3blocks/persist_common.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/persist_common.sh - /home/kuhy/.config/i3blocks/shutdown_countdown.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/shutdown_countdown.sh - /home/kuhy/.config/i3blocks/time.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/time.sh - /home/kuhy/.config/i3blocks/volume.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/volume.sh - /home/kuhy/.local/bin/keepass-open -> /home/kuhy/testsAndMisc/linux_configuration/features/keepass-open.sh - /home/kuhy/.local/bin/line-speed-probe.sh -> /home/kuhy/testsAndMisc/linux_configuration/diagnostics/line-speed-probe.sh - /home/kuhy/.local/bin/steam-download-duty.sh -> /home/kuhy/testsAndMisc/linux_configuration/diagnostics/steam-download-duty.sh - /home/kuhy/.oh-my-zsh/custom/calc-live.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/calc-live.zsh - /home/kuhy/.oh-my-zsh/custom/claude-tty-fix.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/claude-tty-fix.zsh - /home/kuhy/.oh-my-zsh/custom/token-savers.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/token-savers.zsh + /home/kuhy/.config/i3blocks/claude_usage.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/claude_usage.sh + /home/kuhy/.config/i3blocks/config -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/config + /home/kuhy/.config/i3blocks/cpu_monitor.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/cpu_monitor.sh + /home/kuhy/.config/i3blocks/disk.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/disk.sh + /home/kuhy/.config/i3blocks/ethernet.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/ethernet.sh + /home/kuhy/.config/i3blocks/gpu_monitor.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/gpu_monitor.sh + /home/kuhy/.config/i3blocks/memory.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/memory.sh + /home/kuhy/.config/i3blocks/motherboard_temp.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/motherboard_temp.sh + /home/kuhy/.config/i3blocks/pc_startup_status.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/pc_startup_status.sh + /home/kuhy/.config/i3blocks/persist_common.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/persist_common.sh + /home/kuhy/.config/i3blocks/shutdown_countdown.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/shutdown_countdown.sh + /home/kuhy/.config/i3blocks/time.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/time.sh + /home/kuhy/.config/i3blocks/volume.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/volume.sh + /home/kuhy/.local/bin/keepass-open -> /home/kuhy/src/testsAndMisc/linux_configuration/features/keepass-open.sh + /home/kuhy/.local/bin/line-speed-probe.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/diagnostics/line-speed-probe.sh + /home/kuhy/.local/bin/steam-download-duty.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/diagnostics/steam-download-duty.sh + /home/kuhy/.oh-my-zsh/custom/calc-live.zsh -> /home/kuhy/src/testsAndMisc/linux_configuration/zsh/calc-live.zsh + /home/kuhy/.oh-my-zsh/custom/claude-tty-fix.zsh -> /home/kuhy/src/testsAndMisc/linux_configuration/zsh/claude-tty-fix.zsh + /home/kuhy/.oh-my-zsh/custom/token-savers.zsh -> /home/kuhy/src/testsAndMisc/linux_configuration/zsh/token-savers.zsh ``` ## Depth-cap progress diff --git a/docs/DOCS-shared-checkout-safety.md b/docs/DOCS-shared-checkout-safety.md index 43895e29..22896955 100644 --- a/docs/DOCS-shared-checkout-safety.md +++ b/docs/DOCS-shared-checkout-safety.md @@ -1,4 +1,4 @@ -# Working safely in a shared `~/testsAndMisc` checkout +# Working safely in a shared `~/src/testsAndMisc` checkout Split out of `refactor_claude_todo_resume.md` (which hit the 250-line cap it exists to enforce). Nothing here is specific to the file-length refactor -- it diff --git a/docs/DOCS-syncyomi-restore-runbook.md b/docs/DOCS-syncyomi-restore-runbook.md index 4d6dd62b..d54fb9f2 100644 --- a/docs/DOCS-syncyomi-restore-runbook.md +++ b/docs/DOCS-syncyomi-restore-runbook.md @@ -25,7 +25,7 @@ Upstream, open and unfixed: [#1634] and [#1638]. ## Fixed on this phone (2026-08-28) The phone no longer runs the upstream APK. It runs -[kuhyx/TachiyomiSY](https://github.com/kuhyx/TachiyomiSY) (`~/tachiyomisy`, +[kuhyx/TachiyomiSY](https://github.com/kuhyx/TachiyomiSY) (`~/vendor/tachiyomisy`, commit "Serialize backup restore and refuse to sync a collapsed library"), which carries three changes upstream does not have: @@ -52,8 +52,8 @@ the official APK rather than sitting beside it, and is signed with ```bash JAVA_HOME=/usr/lib/jvm/java-21-openjdk \ - ~/tachiyomisy/gradlew -p ~/tachiyomisy assembleFoss -PsyReplaceUpstream -adb install -r ~/tachiyomisy/app/build/outputs/apk/foss/app-arm64-v8a-foss.apk + ~/vendor/tachiyomisy/gradlew -p ~/vendor/tachiyomisy assembleFoss -PsyReplaceUpstream +adb install -r ~/vendor/tachiyomisy/app/build/outputs/apk/foss/app-arm64-v8a-foss.apk ``` Never install an official TachiyomiSY APK over it: the signatures differ, so @@ -137,13 +137,13 @@ systemctl --user list-timers syncyomi-guard.timer journalctl --user -u syncyomi-guard.service -n 20 ``` -Snapshots live in `~/syncyomi/snapshots/` and are ordinary `.tachibk` files — +Snapshots live in `~/services/syncyomi/snapshots/` and are ordinary `.tachibk` files — `adb push` one to the phone and restore it directly. **Retention: 14 snapshots at 30-minute intervals is about 7 hours of history.** That covers a fast incident — the 2026-08-09 window was 51 seconds — but not a slow drift noticed days later. For that, the reference copy is -`~/syncyomi/recovery/`, which is never pruned. +`~/services/syncyomi/recovery/`, which is never pruned. After a **deliberate** library purge the guard will report a collapse; accept the new state with `--accept` to re-baseline. @@ -167,7 +167,7 @@ and 1 won. The phone was not losing a merge — it was honestly reporting what i had. The restore writes manga and chapters but silently drops the category tables, which is the narrow form of the same 1.13.2 bug. -The categories still exist in `~/syncyomi/recovery/syncyomi_recovered_2026-08-09.tachibk` +The categories still exist in `~/services/syncyomi/recovery/syncyomi_recovered_2026-08-09.tachibk` (18 definitions, 1724 memberships). Getting them into the app needs the downgrade path — 1.12.0 → restore → verify → upgrade — which was not attempted. If a future SY release fixes #1634, retry the restore before the downgrade. @@ -181,7 +181,7 @@ payload may still exist as stale pages in `syncyomi.db-wal`. the WAL and the only remaining copy with it. Snapshot all three files first: ```bash -sudo cp -a ~/syncyomi/config/syncyomi.db{,-shm,-wal} /some/safe/dir/ +sudo cp -a ~/services/syncyomi/config/syncyomi.db{,-shm,-wal} /some/safe/dir/ ``` Then rebuild by applying every WAL frame over the base database, last writer @@ -189,7 +189,7 @@ wins, and extract `sync_data.data`. The 2026-08-09 reconstruction and its verification are recorded in `docs/superpowers/evidence/syncyomi-library-recovery-2026-08-09.json`. -**`~/syncyomi/recovery/recovered.db` is a forensic artifact, not a database.** +**`~/services/syncyomi/recovery/recovered.db` is a forensic artifact, not a database.** It was assembled by splicing pages from several checkpoint generations, so it is not any single committed SQLite state — `quick_check` passes and the payload blob decodes, but do **not** copy it over `config/syncyomi.db`. The deliverable diff --git a/docs/explanations/2026-07-12-explanation-dufs-cloud.html b/docs/explanations/2026-07-12-explanation-dufs-cloud.html index bf609924..ad1a7538 100644 --- a/docs/explanations/2026-07-12-explanation-dufs-cloud.html +++ b/docs/explanations/2026-07-12-explanation-dufs-cloud.html @@ -139,7 +139,7 @@

The two things this change adds a home for

dufs is the chosen tool: a single ~2.5 MB Rust binary that serves a real filesystem folder over HTTP with a web UI and built-in WebDAV. Because it serves the folder directly, an rsync (or any local write) into that folder shows up instantly — no database, no re-scan step. Contrast Seafile, which stores files in an internal block database that rsync cannot write into.

@@ -171,7 +171,7 @@
→
rsynconly the .kdbx
→
-
~/cloud/Keepassphone pulls via WebDAV
+
~/data/cloud/Keepassphone pulls via WebDAV

On the phone, KeePassDX opens the database straight from the WebDAV URL — so the “mobile app” requirement is met with zero custom code.

@@ -252,14 +252,14 @@ bad="$(printf '%s\n' "$staged" | grep -E '(^|/)\.dufs_cloud\.conf$' || true)"

4 · Playground — the serve-dir guard

Type a candidate CLOUD_ROOT and see the exact verdict assert_cloud_root_safe would give. (Assumes $HOME=/home/kuhy, repo /home/kuhy/testsAndMisc. Paths are resolved like readlink -m, so .. collapses.)

- +
- ~/cloud + ~/cloud ~ (home) ~/.ssh ~/.config/dufs - inside the repo + inside the repo traversal → .gnupg /srv/cloud
@@ -288,9 +288,9 @@ bad="$(printf '%s\n' "$staged" | grep -E '(^|/)\.dufs_cloud\.conf$' || true)" } function guard(cloudRoot){ // Mirrors assert_cloud_root_safe: HOME and / are blocked ONLY as an exact - // match (subdirs of HOME like ~/cloud are fine); .ssh/.config/.gnupg and + // match (subdirs of HOME like ~/data/cloud are fine); .ssh/.config/.gnupg and // the repo are blocked as exact-or-prefix. - var HOME='/home/kuhy', REPO='/home/kuhy/testsAndMisc'; + var HOME='/home/kuhy', REPO='/home/kuhy/src/testsAndMisc'; var r = resolve(cloudRoot); if (r===HOME) return {ok:false, why:'Resolves to '+r+' — that is $HOME itself; serving it would expose everything.'}; if (r==='/') return {ok:false, why:'Resolves to / — the whole filesystem. Refused.'}; @@ -363,7 +363,7 @@ bad="$(printf '%s\n' "$staged" | grep -E '(^|/)\.dufs_cloud\.conf$' || true)" rat: "assert_config_gitignored refuses to persist the secret unless git confirms it's ignored — a runtime, fail-closed check." }, { - q: "Why does the sync copy only Passwords.kdbx instead of the whole ~/Keepass directory?", + q: "Why does the sync copy only Passwords.kdbx instead of the whole ~/data/Keepass directory?", opts: [ "Because rsync cannot recurse into directories that contain hidden dot-folders.", "Because copying the directory would exceed dufs's per-file upload size limit.", diff --git a/docs/explanations/2026-07-13-explanation-personal-website-edge.html b/docs/explanations/2026-07-13-explanation-personal-website-edge.html index a884b474..26fd1145 100644 --- a/docs/explanations/2026-07-13-explanation-personal-website-edge.html +++ b/docs/explanations/2026-07-13-explanation-personal-website-edge.html @@ -186,7 +186,7 @@ kuhy-sync.duckdns.org { reverse_proxy 127.0.0.1:8282 } # SyncYomi
preflight            # deps, docker up, gitea-caddy present
 migrate_caddy_snippets   # split TODAY's Caddyfile → sites/*.caddy (keep dufs, sync)
 relocate_gitea       # re-run setup_gitea.sh (now writes import + gitea.caddy)
-build_website        # pnpm build → ~/personal-website/dist
+build_website        # pnpm build → ~/src/personal-website/dist
 write_website_stack  # loopback caddy compose + inner Caddyfile
 write_website_snippet# sites/website.caddy → kuhy.duckdns.org
 start_website        # container up on 127.0.0.1:8088
diff --git a/docs/explanations/2026-07-13-explanation-setup-syncyomi.html b/docs/explanations/2026-07-13-explanation-setup-syncyomi.html
index 5c895ea0..e4451a24 100644
--- a/docs/explanations/2026-07-13-explanation-setup-syncyomi.html
+++ b/docs/explanations/2026-07-13-explanation-setup-syncyomi.html
@@ -184,7 +184,7 @@
   

Try it: how the desk routes a request

-

Pick a hostname and watch Caddy decide where it goes. This mirrors the real site blocks in ~/gitea/Caddyfile.

+

Pick a hostname and watch Caddy decide where it goes. This mirrors the real site blocks in ~/services/gitea/Caddyfile.

diff --git a/docs/explanations/2026-07-19-explanation-shared-app-icon-generator.html b/docs/explanations/2026-07-19-explanation-shared-app-icon-generator.html index b675268e..59a15520 100644 --- a/docs/explanations/2026-07-19-explanation-shared-app-icon-generator.html +++ b/docs/explanations/2026-07-19-explanation-shared-app-icon-generator.html @@ -503,7 +503,7 @@ worth explaining why:

# The live app: ~/dufs_client is a stale, remote-less checkout that # was superseded when dufs-cloud absorbed it (see testsAndMisc # CLAUDE.md). Same package name, so it is easy to icon the wrong one. -repo=_HOME / "dufs-cloud" / "app", +repo=_HOME / "src/dufs-cloud" / "app",

There were two checkouts with the same name:, the same applicationId, and the same directory layout. Only one had a git remote and diff --git a/docs/superpowers/contracts/endurain-runnerup-webdav-2026-08-23.json b/docs/superpowers/contracts/endurain-runnerup-webdav-2026-08-23.json index 2562b99f..5cf6ab64 100644 --- a/docs/superpowers/contracts/endurain-runnerup-webdav-2026-08-23.json +++ b/docs/superpowers/contracts/endurain-runnerup-webdav-2026-08-23.json @@ -1,13 +1,13 @@ { "title": "Endurain viewer fed by RunnerUp WebDAV exports", - "objective": "Stand up self-hosted Endurain behind the existing Caddy edge and import RunnerUp activities into it, primarily over WebDAV from the phone with an adb pull as fallback. Endurain is a VIEWER only: screen-locker's workout gate keeps reading the phone over adb and is not modified, because a WebDAV drop folder is spoofable by anything that can write to ~/cloud and must never become a screen-unlock authority. Success means real activities from the phone are visible in Endurain, importing twice never creates a duplicate, and screen-locker still passes its full suite.", + "objective": "Stand up self-hosted Endurain behind the existing Caddy edge and import RunnerUp activities into it, primarily over WebDAV from the phone with an adb pull as fallback. Endurain is a VIEWER only: screen-locker's workout gate keeps reading the phone over adb and is not modified, because a WebDAV drop folder is spoofable by anything that can write to ~/data/cloud and must never become a screen-unlock authority. Success means real activities from the phone are visible in Endurain, importing twice never creates a duplicate, and screen-locker still passes its full suite.", "acceptance_criteria": [ "Endurain reachable at https://endurain.kuhy.duckdns.org with a valid Let's Encrypt certificate", "At least 4 real RunnerUp activities imported and visible, with correct distance and duration", "Re-running the importer over already-imported files creates ZERO new activities", "The .gpx and .tcx of a single run import as ONE activity, not two", "The seeded admin/admin login is rejected before the service is ever published", - "The phone-facing WebDAV credential cannot read ~/cloud/Keepass or gdrive_backups", + "The phone-facing WebDAV credential cannot read ~/data/cloud/Keepass or gdrive_backups", "screen-locker's full test suite still passes at 100% coverage" ], "out_of_scope": [ diff --git a/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json b/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json index c4e3b8e6..8b5e2e0e 100644 --- a/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json +++ b/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json @@ -12,7 +12,7 @@ "pre-commit passes without --no-verify." ], "out_of_scope": [ - "Moving heavy_job_lock.sh to ~/utils: the measurement does not support it, and every consumer but the installer already reads the installed copy.", + "Moving heavy_job_lock.sh to ~/src/utils: the measurement does not support it, and every consumer but the installer already reads the installed copy.", "The adb push half of update_android_hosts.sh, which needs a device in a writable state.", "digital_wellbeing itself, which is the next and last extraction." ], diff --git a/docs/superpowers/contracts/extract-digital-wellbeing-2026-08-23.json b/docs/superpowers/contracts/extract-digital-wellbeing-2026-08-23.json index cd4abd98..fe3ab309 100644 --- a/docs/superpowers/contracts/extract-digital-wellbeing-2026-08-23.json +++ b/docs/superpowers/contracts/extract-digital-wellbeing-2026-08-23.json @@ -5,7 +5,7 @@ "check_directory_depth.sh --all exits 0 with --warn removed from the hook entry.", "The extracted repo's history spans all three paths the unit has lived at (63 commits), plus the 24 from the separately-filtered test suites.", "All 80 tracked files are byte-identical to the monorepo copies; node_modules and __pycache__ are excluded.", - "All six moved suites plus every lib/tests/run_all.sh pass from ~/digital-wellbeing, with no root, pacman or Arch userland -- verified under env -i.", + "All six moved suites plus every lib/tests/run_all.sh pass from ~/src/digital-wellbeing, with no root, pacman or Arch userland -- verified under env -i.", "Every moved test name is deleted from BOTH hardcoded arrays in .github/workflows/shell-tests.yml; every name still listed there resolves to a file that exists.", "check_and_enable_services.sh runs to completion and reports ok for pacman_wrapper, makepkg_wrapper, midnight_shutdown, startup_monitor, periodic_systems and hosts.", "The pacman-wrapper drift manifest verifies with sha256sum -c after regeneration.", @@ -18,5 +18,5 @@ "The Nextcloud dedup, measured and recommended against in the handoff.", "Reformatting vendor/common.sh, which is a verbatim copy." ], - "verifier": "./meta/scripts/check_directory_depth.sh --all exits 0; ~/digital-wellbeing/tests/run_all.sh passes all seven suites; grep for the six moved test names in shell-tests.yml returns 0; sudo check_and_enable_services.sh completes with no unbound-variable error and no missing-script error; sudo sha256sum -c --status /var/lib/pacman-wrapper/source.sha256 exits 0; report_live_state.sh --check greps 0 for linux_configuration/scripts/; shellcheck clean on all nine changed shell files." + "verifier": "./meta/scripts/check_directory_depth.sh --all exits 0; ~/src/digital-wellbeing/tests/run_all.sh passes all seven suites; grep for the six moved test names in shell-tests.yml returns 0; sudo check_and_enable_services.sh completes with no unbound-variable error and no missing-script error; sudo sha256sum -c --status /var/lib/pacman-wrapper/source.sha256 exits 0; report_live_state.sh --check greps 0 for linux_configuration/scripts/; shellcheck clean on all nine changed shell files." } diff --git a/docs/superpowers/contracts/extract-hosts-blocker-2026-08-23.json b/docs/superpowers/contracts/extract-hosts-blocker-2026-08-23.json index 7bea7e60..aefe34e1 100644 --- a/docs/superpowers/contracts/extract-hosts-blocker-2026-08-23.json +++ b/docs/superpowers/contracts/extract-hosts-blocker-2026-08-23.json @@ -4,8 +4,8 @@ "acceptance_criteria": [ "The extracted repo's history spans all four paths the unit has lived at, not just the current one — 52 commits, reaching the original 'block websites uising etc hosts'.", "Every extracted source file is byte-identical to its monorepo copy (diff -r reports no differences).", - "lib/tests/run_all.sh passes from ~/hosts-blocker, the real clone location, with 25 assertions.", - "All six monorepo callers resolve to /home/kuhy/hosts-blocker, including under sudo, where $HOME would be /root.", + "lib/tests/run_all.sh passes from ~/src/hosts-blocker, the real clone location, with 25 assertions.", + "All six monorepo callers resolve to /home/kuhy/src/hosts-blocker, including under sudo, where $HOME would be /root.", "A missing checkout fails loudly with a clone instruction and a non-zero return — never a silent skip.", "check_directory_depth.sh --all reports 41 violations, all of them digital_wellbeing.", "report_live_state.sh --check output is byte-identical before and after: no new stale references.", @@ -20,5 +20,5 @@ "Fixing the 8 pre-existing stale live-state references — a separate commit after both extractions.", "Deleting .hippo/ or the untracked extraction residue — separate commits." ], - "verifier": "diff -r --exclude=.git between the monorepo unit and the extracted repo reports IDENTICAL; ~/hosts-blocker/lib/tests/run_all.sh exits 0 with 25 passed; pre-commit run --all-files --hook-stage pre-push in the extracted repo passes with no file modified; shellcheck is clean on the helper and all six callers; sudo -n resolution prints /home/kuhy/hosts-blocker; HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo returns 1 with a clone instruction; check_directory_depth.sh --all reports 41; diff of report_live_state.sh --check before/after is empty." + "verifier": "diff -r --exclude=.git between the monorepo unit and the extracted repo reports IDENTICAL; ~/src/hosts-blocker/lib/tests/run_all.sh exits 0 with 25 passed; pre-commit run --all-files --hook-stage pre-push in the extracted repo passes with no file modified; shellcheck is clean on the helper and all six callers; sudo -n resolution prints /home/kuhy/src/hosts-blocker; HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo returns 1 with a clone instruction; check_directory_depth.sh --all reports 41; diff of report_live_state.sh --check before/after is empty." } diff --git a/docs/superpowers/contracts/nftables-drift-self-check-2026-08-30.json b/docs/superpowers/contracts/nftables-drift-self-check-2026-08-30.json index aa617c03..aec9ccb5 100644 --- a/docs/superpowers/contracts/nftables-drift-self-check-2026-08-30.json +++ b/docs/superpowers/contracts/nftables-drift-self-check-2026-08-30.json @@ -13,5 +13,5 @@ "Notifications: a failed unit is the gate; notify-send from a root system unit is not.", "Section 1 of the handoff (signal-bot boot resilience), which shipped separately in kuhyx/signal-bot." ], - "verifier": "linux_configuration/features/lib/tests/test_wg_verify_units.sh under meta/scripts/shell_coverage_jail.sh; plus two real reboots in ~/utils/vmbox (drifted -> failed unit, clean -> active unit) and a live run on the host." + "verifier": "linux_configuration/features/lib/tests/test_wg_verify_units.sh under meta/scripts/shell_coverage_jail.sh; plus two real reboots in ~/src/utils/vmbox (drifted -> failed unit, clean -> active unit) and a live run on the host." } diff --git a/docs/superpowers/evidence/app-icons-bowl-star-2026-08-29.json b/docs/superpowers/evidence/app-icons-bowl-star-2026-08-29.json index 261c3aa2..14a7d131 100644 --- a/docs/superpowers/evidence/app-icons-bowl-star-2026-08-29.json +++ b/docs/superpowers/evidence/app-icons-bowl-star-2026-08-29.json @@ -1,5 +1,5 @@ { - "intent": "Give the new ~/restaurant-rater app a launcher icon from the shared family, so it stops shipping the stock Flutter beach icon.", + "intent": "Give the new ~/src/restaurant-rater app a launcher icon from the shared family, so it stops shipping the stock Flutter beach icon.", "scope": [ "python_pkg/app_icons/_glyph_art_more.py - the new SVG fragment", "python_pkg/app_icons/glyphs.py - register the glyph in GLYPHS", @@ -14,14 +14,14 @@ ], "verification": [ { - "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons list", + "command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons list", "result": "pass", - "evidence": "bowl-star appears under Glyphs; 'restaurant_rater #B8862E bowl-star /home/kuhy/restaurant-rater' appears under Apps." + "evidence": "bowl-star appears under Glyphs; 'restaurant_rater #B8862E bowl-star /home/kuhy/src/restaurant-rater' appears under Apps." }, { - "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app restaurant_rater", + "command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --app restaurant_rater", "result": "pass", - "evidence": "wrote 6 asset files to /home/kuhy/restaurant-rater/assets/icon (icon/foreground/monochrome, svg+png each). The generator's safe-box and negative-space checks did not fire." + "evidence": "wrote 6 asset files to /home/kuhy/src/restaurant-rater/assets/icon (icon/foreground/monochrome, svg+png each). The generator's safe-box and negative-space checks did not fire." }, { "command": "visual review of the rendered 640px icon.png", diff --git a/docs/superpowers/evidence/dependency-freshness-gate-2026-08-28.json b/docs/superpowers/evidence/dependency-freshness-gate-2026-08-28.json index 748bf331..68b31ef8 100644 --- a/docs/superpowers/evidence/dependency-freshness-gate-2026-08-28.json +++ b/docs/superpowers/evidence/dependency-freshness-gate-2026-08-28.json @@ -8,7 +8,7 @@ "changes": [ "Exact-pinned 95 previously range-specified dependencies to their newest stable release, via ~/.claude/scripts/apply_dep_pins.py driven by the gate's own --json findings.", "Moved the gatelock git-tag dependency from gatelock-v0.1.0 to gatelock-v0.7.2 -- six minor versions of drift that a PEP 508 direct reference hid from every previous check.", - "Installed the shared gate's three pieces (delegate, pre-commit hook, CI workflow) from ~/utils/dep_freshness/templates." + "Installed the shared gate's three pieces (delegate, pre-commit hook, CI workflow) from ~/src/utils/dep_freshness/templates." ], "verification": [ { @@ -17,7 +17,7 @@ "evidence": "exit 0; clean venv rebuilt from the repinned meta/requirements.txt, pre-commit --all-files and the full suite green inside it" }, { - "command": "~/utils/scripts/check_dependency_freshness.sh --all", + "command": "~/src/utils/scripts/check_dependency_freshness.sh --all", "result": "pass", "evidence": "exit 0 -- 97 findings before, 0 after" } diff --git a/docs/superpowers/evidence/extract-digital-wellbeing-2026-08-23.json b/docs/superpowers/evidence/extract-digital-wellbeing-2026-08-23.json index 63483736..6778cf64 100644 --- a/docs/superpowers/evidence/extract-digital-wellbeing-2026-08-23.json +++ b/docs/superpowers/evidence/extract-digital-wellbeing-2026-08-23.json @@ -1,5 +1,5 @@ { - "intent": "Extract the last over-cap unit into github.com/kuhyx/digital-wellbeing (cloned at ~/digital-wellbeing), then delete --warn from the directory-depth-cap hook so the cap enforces for real. This is the closing step of the restructure campaign.", + "intent": "Extract the last over-cap unit into github.com/kuhyx/digital-wellbeing (cloned at ~/src/digital-wellbeing), then delete --warn from the directory-depth-cap hook so the cap enforces for real. This is the closing step of the restructure campaign.", "scope": [ "REMOVED: linux_configuration/periodic_background/digital_wellbeing/ (80 tracked files)", "REMOVED: 6 test suites + 2 test libs from linux_configuration/tests/", @@ -33,7 +33,7 @@ "evidence": "ALL 80 TRACKED FILES IDENTICAL. node_modules (340 files) and __pycache__ correctly excluded -- the repo has 80 tracked files, not the 423 on disk." }, { - "command": "~/digital-wellbeing/tests/run_all.sh", + "command": "~/src/digital-wellbeing/tests/run_all.sh", "result": "pass", "evidence": "All six regression suites plus the discovered lib/tests/run_all.sh pass: 'All digital-wellbeing suites passed.'" }, @@ -55,7 +55,7 @@ { "command": "sudo sha256sum -c --status /var/lib/pacman-wrapper/source.sha256", "result": "pass", - "evidence": "Exit 0. The manifest embeds ABSOLUTE paths and had named deleted scripts/ paths since the flatten, so the drift check reported a permanent false 'stale or tampered'. It now names ~/digital-wellbeing and verifies -- with unchanged hashes, confirming the extracted bytes are identical." + "evidence": "Exit 0. The manifest embeds ABSOLUTE paths and had named deleted scripts/ paths since the flatten, so the drift check reported a permanent false 'stale or tampered'. It now names ~/src/digital-wellbeing and verifies -- with unchanged hashes, confirming the extracted bytes are identical." }, { "command": "report_live_state.sh --check | grep -c linux_configuration/scripts/", diff --git a/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json b/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json index e5d77328..225c617e 100644 --- a/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json +++ b/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json @@ -1,5 +1,5 @@ { - "intent": "Extract linux_configuration/periodic_background/hosts/ into its own public repo (github.com/kuhyx/hosts-blocker, cloned at ~/hosts-blocker), clearing 5 of the 46 remaining directory-depth violations without disturbing the live hosts blocker or its guards.", + "intent": "Extract linux_configuration/periodic_background/hosts/ into its own public repo (github.com/kuhyx/hosts-blocker, cloned at ~/src/hosts-blocker), clearing 5 of the 46 remaining directory-depth violations without disturbing the live hosts blocker or its guards.", "scope": [ "REMOVED: linux_configuration/periodic_background/hosts/ (15 files)", "ADDED: linux_configuration/lib/extracted_repos.sh (shared resolver)", @@ -14,7 +14,7 @@ "No source file was modified during extraction: all 15 files are byte-identical to the monorepo copies (verified with diff -r). install.sh already resolved its lib/ via BASH_SOURCE and needed no rewrite.", "Added linux_configuration/lib/extracted_repos.sh: resolves an extracted repo's checkout via SUDO_USER's passwd entry (NOT $HOME, which is /root under sudo), supports a _DIR override, and fails loudly with a clone instruction when absent.", "Reused the existing REAL_HOME resolution already present in check_and_enable_services.sh for screen-locker rather than sourcing a second resolver into it.", - "fresh-install/main.sh uses \"$HOME/hosts-blocker/install.sh\" because it runs from ~/linux-configuration (a different repo) and is not sudo-re-exec'd at that point.", + "fresh-install/main.sh uses \"$HOME/src/hosts-blocker/install.sh\" because it runs from ~/src/testsAndMisc/linux_configuration (a different repo) and is not sudo-re-exec'd at that point.", "The new repo ships NO shfmt hook, deliberately: guard/plugins/*.sh are space-indented and unformatted in the monorepo too (its git hook shfmt's only STAGED files), and their installed copies under /usr/local/share/guard-lib-plugins/ are byte-identical by md5. An shfmt hook would rewrite both on first commit and break that identity." ], "verification": [ @@ -29,7 +29,7 @@ "evidence": "IDENTICAL — no source file altered by the extraction." }, { - "command": "./lib/tests/run_all.sh (in ~/hosts-blocker, the real clone location)", + "command": "./lib/tests/run_all.sh (in ~/src/hosts-blocker, the real clone location)", "result": "pass", "evidence": "test_hosts_protect.sh: 25 passed, 0 failed. All hosts install test files passed." }, @@ -41,7 +41,7 @@ { "command": "sudo -n bash -c 'source lib/extracted_repos.sh; extracted_repo_dir hosts-blocker'", "result": "pass", - "evidence": "HOME=/home/kuhy resolved=/home/kuhy/hosts-blocker — resolves the invoking user, not /root/hosts-blocker." + "evidence": "HOME=/home/kuhy resolved=/home/kuhy/src/hosts-blocker — resolves the invoking user, not /root/hosts-blocker." }, { "command": "HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo hosts-blocker", @@ -75,7 +75,7 @@ } ], "risks": [ - "A machine without ~/hosts-blocker cloned now fails the hosts install step. That is deliberate and loud (clone instruction printed); the previous silent-skip mode is what caused five months of dead self-repair after the 2026-05-15 reorg.", + "A machine without ~/src/hosts-blocker cloned now fails the hosts install step. That is deliberate and loud (clone instruction printed); the previous silent-skip mode is what caused five months of dead self-repair after the 2026-05-15 reorg.", "install_core_system.sh on a genuinely fresh machine will hard-stop at the hosts module until the repo is cloned. INSTALLER_FIX_TASK.md already tracks fresh-machine installer defects and has been updated to name the new location.", "The guard plugins remain unformatted. If an shfmt hook is ever added to hosts-blocker it will rewrite them, and the installed copies must be re-synced in the same change.", "The live hosts blocker was NOT re-installed as part of this change; /etc/hosts and its guards are untouched and still enforced from their installed copies." diff --git a/docs/superpowers/evidence/fix-precommit-utils-checkout-2026-08-25.json b/docs/superpowers/evidence/fix-precommit-utils-checkout-2026-08-25.json index 393115e6..4f6ab2d1 100644 --- a/docs/superpowers/evidence/fix-precommit-utils-checkout-2026-08-25.json +++ b/docs/superpowers/evidence/fix-precommit-utils-checkout-2026-08-25.json @@ -6,13 +6,13 @@ "Non-goal: changing any gate's rules, or restyling the existing icon family" ], "changes": [ - "pre-commit.yml now checks out kuhyx/utils into .utils/ and points UTILS_ROOT at it. The md-naming and file-length hooks are thin shims that exec the real gate out of that repo; a runner has no ~/utils, so both failed with 'shared gate not found at /home/runner/utils/...' and the workflow has been red since 2026-08-24. The dedicated md-naming.yml already did this checkout — this copies the same approach rather than inventing one.", + "pre-commit.yml now checks out kuhyx/utils into .utils/ and points UTILS_ROOT at it. The md-naming and file-length hooks are thin shims that exec the real gate out of that repo; a runner has no ~/src/utils, so both failed with 'shared gate not found at /home/runner/utils/...' and the workflow has been red since 2026-08-24. The dedicated md-naming.yml already did this checkout — this copies the same approach rather than inventing one.", "Registered app_icons entry 'punchme' (shared family accent #B8862E, glyph punch-card, linux=False since the app is Android-only).", "Added the _PUNCH_CARD glyph: a time card with a clipped corner, a column of four punched holes and three ruled lines. The first draft put two holes above two wide slots and read unmistakably as a face (eyes over teeth) at every size, which is why the holes are now a vertical column." ], "verification": [ { - "command": "UTILS_ROOT=/home/kuhy/utils bash scripts/check_md_naming.sh README.md", + "command": "UTILS_ROOT=/home/kuhy/src/utils bash scripts/check_md_naming.sh README.md", "result": "pass", "evidence": "Exit 0. With UTILS_ROOT=/nonexistent the same shim fails with 'shared gate not found at /nonexistent/scripts/check_md_naming.sh', so the override is what the fix relies on and it is proven in both directions." }, @@ -32,7 +32,7 @@ "evidence": "All hooks passed, including pylint, bandit and 'pytest with coverage enforcement'." }, { - "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app punchme --android", + "command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --app punchme --android", "result": "pass", "evidence": "Wrote 6 asset files and regenerated the Android mipmaps with no safe-box warning. Rendered at full size, circle-masked and 48dp and reviewed each; legible at all three." } diff --git a/docs/superpowers/evidence/focus-owner-icon-registration-2026-08-24.json b/docs/superpowers/evidence/focus-owner-icon-registration-2026-08-24.json index cd5183df..5d03803f 100644 --- a/docs/superpowers/evidence/focus-owner-icon-registration-2026-08-24.json +++ b/docs/superpowers/evidence/focus-owner-icon-registration-2026-08-24.json @@ -6,10 +6,10 @@ "python_pkg/app_icons/_glyph_art_more.py - the glyph's SVG fragment", "Non-goal: no new accent. The family accent #B8862E is shared; apps are distinguished by glyph shape only.", "Non-goal: no change to the 13 existing glyphs or to any other app's entry.", - "The consuming repo (~/phone-focus-mode) is committed separately; it blocks binaries, so the icon lands there in vector form." + "The consuming repo (~/src/phone-focus-mode) is committed separately; it blocks binaries, so the icon lands there in vector form." ], "changes": [ - "Added AppIcon(key='focus_owner', repo=~/phone-focus-mode/focus-owner, glyph='padlock-closed', accent='#B8862E', linux=False). The repo path points into the monorepo because focus-owner was absorbed as a subtree on 2026-08-24 and both the standalone clone and its GitHub repo are gone.", + "Added AppIcon(key='focus_owner', repo=~/src/phone-focus-mode/focus-owner, glyph='padlock-closed', accent='#B8862E', linux=False). The repo path points into the monorepo because focus-owner was absorbed as a subtree on 2026-08-24 and both the standalone clone and its GitHub repo are gone.", "Added the _PADLOCK_CLOSED glyph fragment: shackle stroked (inheriting the wrapping group's round caps), body filled in the accent, keyhole punched back out in the field colour via fill-rule='evenodd'. Splitting stroke and fill is deliberate - a fully stroked padlock loses its keyhole at 48dp, a fully filled one loses the shackle's opening.", "Geometry computed against style.py rather than eyeballed, per the lesson recorded on _NOTE_ASCENDER: body 292..732 x 520..792; shackle centreline r=132 about (512,520) giving an outer top of 352, inside the 232 safe edge, with legs at x=380/644 inside the body; keyhole leaves 60px of body above and 76px below the stem, both clear of MIN_NEGATIVE_SPACE=36." ], diff --git a/docs/superpowers/evidence/installer-cross-repo-fix-2026-08-24.json b/docs/superpowers/evidence/installer-cross-repo-fix-2026-08-24.json index f6ff6899..4e03aa42 100644 --- a/docs/superpowers/evidence/installer-cross-repo-fix-2026-08-24.json +++ b/docs/superpowers/evidence/installer-cross-repo-fix-2026-08-24.json @@ -14,7 +14,7 @@ "hosts-blocker: stop_hosts_guard/restart_hosts_guard now handle the guard-lib instances (guard-file@hosts.*, guard-bind-mount@hosts.service) as well as the legacy per-file units, and refresh the canonical copy with 'guardctl file-guard sync hosts' -- without this a SECOND install run fought a live read-only bind mount and the install was not idempotent.", "screen-locker: install_systemd.sh installs its own runtime dependencies (tk for the tkinter shared library, plus the pinned gatelock and crdt-sync git dependencies). It previously installed none, so the post-install workout check crashed and the installer exited 1 on any fresh machine.", "steam-backlog-enforcer: install.sh no longer aborts under `set -euo pipefail` when a bare `read` finds no stdin; it defaults to not installing the systemd service when stdin is not a TTY, with STEAM_ENFORCER_INSTALL_SERVICE=y to opt in.", - "tests/lib/security_apps.sh: the screen_locker section resolves the extracted repo via extracted_repo_dir instead of a hardcoded $HOME/testsAndMisc/python_pkg path, and its assertions were rewritten against the current implementation (SPORT_CHOICES, SPORT_TABLE_TENNIS, _validate_table_tennis, sets_won, MANUAL_WORKOUT_REFLECTION_MIN_CHARS) because the four symbols it used to grep for no longer exist anywhere in the codebase." + "tests/lib/security_apps.sh: the screen_locker section resolves the extracted repo via extracted_repo_dir instead of a hardcoded $HOME/src/testsAndMisc/python_pkg path, and its assertions were rewritten against the current implementation (SPORT_CHOICES, SPORT_TABLE_TENNIS, _validate_table_tennis, sets_won, MANUAL_WORKOUT_REFLECTION_MIN_CHARS) because the four symbols it used to grep for no longer exist anywhere in the codebase." ], "verification": [ { @@ -25,7 +25,7 @@ { "command": "bash linux_configuration/tests/test_security_hardening.sh (host, after the path+assertion fix)", "result": "23 passed / 0 skipped", - "evidence": "The five screen_locker checks now execute against ~/screen-locker and pass. Note this is a changed measuring instrument as well as a changed measurement: the suite is 24 checks today, so the task file's '18 passed / 6 skipped out of 17' target was stale and could not be matched literally." + "evidence": "The five screen_locker checks now execute against ~/src/screen-locker and pass. Note this is a changed measuring instrument as well as a changed measurement: the suite is 24 checks today, so the task file's '18 passed / 6 skipped out of 17' target was stale and could not be matched literally." }, { "command": "vm run 'bash guest_verify.sh' -- pristine vmbox guest, installer clones its own siblings", diff --git a/docs/superpowers/evidence/md-naming-namespaces-2026-08-24.json b/docs/superpowers/evidence/md-naming-namespaces-2026-08-24.json index a7f57a29..6b59d3f0 100644 --- a/docs/superpowers/evidence/md-naming-namespaces-2026-08-24.json +++ b/docs/superpowers/evidence/md-naming-namespaces-2026-08-24.json @@ -31,14 +31,14 @@ "evidence": "exit 0 -- the basis for deleting both refactor todos" }, { - "command": "python3 -m pytest md_naming/tests/ (in ~/utils)", + "command": "python3 -m pytest md_naming/tests/ (in ~/src/utils)", "result": "pass", "evidence": "71 passed; 100% branch coverage on the gate" } ], "risks": [ "A rename breaks an inbound link not caught by the reference sweep. Mitigated: the sweep covered ~/.claude/CLAUDE.md, rules/, skills/, memory/ and every tracked .md; a bare-basename match was deliberately narrowed after a dry run showed it would corrupt vendored node_modules files.", - "A doc classified TASK is actually finished, or vice versa. Mitigated: verdicts came from content audits cross-checked against repo code, and are recorded in ~/utils/md_naming/migrate/classify.py.", + "A doc classified TASK is actually finished, or vice versa. Mitigated: verdicts came from content audits cross-checked against repo code, and are recorded in ~/src/utils/md_naming/migrate/classify.py.", "The gate depends on kuhyx/utils being present; CI checks it out, and the shim fails loudly with install instructions if it is missing." ], "rollback": [ diff --git a/docs/superpowers/evidence/register-betting-sim-icon-2026-08-28.json b/docs/superpowers/evidence/register-betting-sim-icon-2026-08-28.json index d7a09b5f..9f9f1493 100644 --- a/docs/superpowers/evidence/register-betting-sim-icon-2026-08-28.json +++ b/docs/superpowers/evidence/register-betting-sim-icon-2026-08-28.json @@ -5,19 +5,19 @@ "Non-goal: no change to the generator, the glyph set, or any other app's icon" ], "changes": [ - "Registered betting_sim: repo ~/betting-sim/app, accent #B8862E, glyph track-bars (rising bars with a plotted trend point, which reads as odds movement).", + "Registered betting_sim: repo ~/src/betting-sim/app, accent #B8862E, glyph track-bars (rising bars with a plotted trend point, which reads as odds movement).", "Set linux=false: betting-sim's desktop target is a Chrome-wrapped web build rather than the GTK embedder, so the hicolor PNG set would go unread." ], "verification": [ { "command": "PYTHONPATH=. python3 -m python_pkg.app_icons list", "result": "pass", - "evidence": "betting_sim #B8862E track-bars /home/kuhy/betting-sim/app" + "evidence": "betting_sim #B8862E track-bars /home/kuhy/src/betting-sim/app" }, { "command": "PYTHONPATH=. python3 -m python_pkg.app_icons generate --app betting_sim --android", "result": "pass", - "evidence": "wrote 6 asset files to /home/kuhy/betting-sim/app/assets/icon; mipmaps generated after adding flutter_launcher_icons to the app pubspec" + "evidence": "wrote 6 asset files to /home/kuhy/src/betting-sim/app/assets/icon; mipmaps generated after adding flutter_launcher_icons to the app pubspec" }, { "command": "visual check of assets/icon/icon.png", diff --git a/docs/superpowers/evidence/repair-stale-live-references-2026-08-23.json b/docs/superpowers/evidence/repair-stale-live-references-2026-08-23.json index 76a974aa..13b1d29b 100644 --- a/docs/superpowers/evidence/repair-stale-live-references-2026-08-23.json +++ b/docs/superpowers/evidence/repair-stale-live-references-2026-08-23.json @@ -32,7 +32,7 @@ { "command": "resolver across all four invocation modes", "result": "pass", - "evidence": "as user -> /home/kuhy/hosts-blocker; via sudo -> same; as root with SUDO_USER/USER unset -> same; HOSTS_BLOCKER_DIR=/opt/hb -> /opt/hb (override still wins)." + "evidence": "as user -> /home/kuhy/src/hosts-blocker; via sudo -> same; as root with SUDO_USER/USER unset -> same; HOSTS_BLOCKER_DIR=/opt/hb -> /opt/hb (override still wins)." }, { "command": "sudo visudo -c", diff --git a/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json b/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json index decbf435..408afd64 100644 --- a/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json +++ b/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json @@ -21,7 +21,7 @@ { "command": "./meta/scripts/report_live_state.sh --check; echo $?", "result": "pass", - "evidence": "Exit code 1 with 'live references into /home/kuhy/testsAndMisc remain', which is correct at baseline since nothing has moved yet." + "evidence": "Exit code 1 with 'live references into /home/kuhy/src/testsAndMisc remain', which is correct at baseline since nothing has moved yet." }, { "command": "14 hand-built path cases through check_directory_depth.sh", diff --git a/docs/superpowers/evidence/syncyomi-local-fork-build-2026-08-28.json b/docs/superpowers/evidence/syncyomi-local-fork-build-2026-08-28.json index 595a7820..03634b02 100644 --- a/docs/superpowers/evidence/syncyomi-local-fork-build-2026-08-28.json +++ b/docs/superpowers/evidence/syncyomi-local-fork-build-2026-08-28.json @@ -8,7 +8,7 @@ ], "changes": [ "SYNCYOMI_IMAGE defaults to the local build syncyomi-kuhy:local, overridable back to a registry tag; adds SYNCYOMI_SRC and the fork URL.", - "ensure_local_image() builds that image from ~/syncyomi-src (cloning the fork if absent) before the container starts, under DOCKER_BUILDKIT=1 because the Dockerfile reads $BUILDPLATFORM; SYNCYOMI_REBUILD=1 forces a rebuild.", + "ensure_local_image() builds that image from ~/vendor/syncyomi-src (cloning the fork if absent) before the container starts, under DOCKER_BUILDKIT=1 because the Dockerfile reads $BUILDPLATFORM; SYNCYOMI_REBUILD=1 forces a rebuild.", "configure_caddy() now owns gitea/sites/.caddy instead of appending to the shared Caddyfile, deletes a legacy inline block, and validates the config before reloading.", "Preflight installs git and docker-buildx alongside the existing packages." ], @@ -36,12 +36,12 @@ ], "risks": [ "The deployed build is develop (v1.1.14 + dependency bumps), not the v1.1.11 image it replaced; schema stays at version 8 and internal/database is unchanged between those points, so no migration runs.", - "A machine without ~/syncyomi-src now clones the fork on first setup, which needs network and a Docker build (~4 min) instead of an image pull.", + "A machine without ~/vendor/syncyomi-src now clones the fork on first setup, which needs network and a Docker build (~4 min) instead of an image pull.", "The Caddy snippet is only rewritten when its content differs, so hand edits to that file are overwritten by design." ], "rollback": [ "SYNCYOMI_IMAGE=ghcr.io/syncyomi/syncyomi:v1.1.11 ./linux_configuration/features/setup_syncyomi.sh setup", - "Cold database copy: ~/syncyomi_safe_2026-08-28/pre-local-build/cold/syncyomi.db (schema version 8).", + "Cold database copy: ~/archive/2026-08-28-syncyomi_safe/pre-local-build/cold/syncyomi.db (schema version 8).", "After rollback, re-run the guard and confirm 2163 manga, then a phone sync round-trip." ] } diff --git a/docs/superpowers/evidence/website-admin-editor-unit-2026-08-29.json b/docs/superpowers/evidence/website-admin-editor-unit-2026-08-29.json index be86ac4b..d17e7488 100644 --- a/docs/superpowers/evidence/website-admin-editor-unit-2026-08-29.json +++ b/docs/superpowers/evidence/website-admin-editor-unit-2026-08-29.json @@ -30,7 +30,7 @@ "evidence": "No findings against the generated unit; the only output was pre-existing warnings about unrelated units on this host." }, { - "command": "systemd-run --user --wait -p ProtectSystem=strict -p ProtectHome=read-only -p ReadWritePaths=/home/kuhy/personal-website ... running the three real build steps", + "command": "systemd-run --user --wait -p ProtectSystem=strict -p ProtectHome=read-only -p ReadWritePaths=/home/kuhy/src/personal-website ... running the three real build steps", "result": "pass", "evidence": "vite build ok / ssr build ok / prerender ok, and `touch $HOME/.pw-sandbox-should-fail` was refused with 'Read-only file system'. The sandbox permits exactly the checkout." } diff --git a/docs/superpowers/plans/TODO-phone-focus-recovery.md b/docs/superpowers/plans/TODO-phone-focus-recovery.md index 3426ebeb..5676c27f 100644 --- a/docs/superpowers/plans/TODO-phone-focus-recovery.md +++ b/docs/superpowers/plans/TODO-phone-focus-recovery.md @@ -336,7 +336,7 @@ printf '\nResults: %d passed, %d failed\n' "${PASS}" "${FAIL}" - [ ] **Step 1.3: Run the test to verify it passes** ```bash -cd /home/kuhy/testsAndMisc +cd /home/kuhy/src/testsAndMisc bash phone_focus_mode/lib/tests/test_adb_common.sh ``` @@ -1762,7 +1762,7 @@ After=network.target [Service] Type=oneshot -ExecStart=%h/testsAndMisc/scripts/run_all/run_phone.sh auto +ExecStart=%h/src/testsAndMisc/scripts/run_all/run_phone.sh auto StandardOutput=journal StandardError=journal ``` diff --git a/endurain/README.md b/endurain/README.md index 94c6c5af..fb225ecf 100644 --- a/endurain/README.md +++ b/endurain/README.md @@ -5,7 +5,7 @@ fed by RunnerUp exports from the phone. **Endurain is a viewer.** `screen-locker`'s workout gate still reads the phone over adb and is untouched by anything here — a WebDAV drop folder is trivially -spoofable by anything that can write to `~/cloud`, so it must never become an +spoofable by anything that can write to `~/data/cloud`, so it must never become an unlock authority. ## Paths @@ -14,7 +14,7 @@ unlock authority. | ------------- | --------------------------------------------------------------------------- | | Public URL | `https://endurain.kuhy.duckdns.org` | | Local URL | `http://127.0.0.1:8085` (loopback only) | -| WebDAV inbox | `https://kuhy-cloud.duckdns.org/RunnerUp` → `~/cloud/RunnerUp/` | +| WebDAV inbox | `https://kuhy-cloud.duckdns.org/RunnerUp` → `~/data/cloud/RunnerUp/` | | Runtime data | `/var/opt/endurain/` | | Ledger | `~/.local/state/endurain-import/ledger.json` | | Importer code | `../python_pkg/endurain_import/` (repo requires Python under `python_pkg/`) | @@ -23,9 +23,9 @@ unlock authority. ## Flow ``` -phone --(WebDAV, manual tap)--> dufs ~/cloud/RunnerUp/ --\ +phone --(WebDAV, manual tap)--> dufs ~/data/cloud/RunnerUp/ --\ >-- importer --> Endurain -phone --(adb pull, fallback)--> ~/cloud/RunnerUp/ -------/ +phone --(adb pull, fallback)--> ~/data/cloud/RunnerUp/ -------/ ``` A systemd user timer (`endurain-import.timer`) runs every 15 min. @@ -48,7 +48,7 @@ cd .. && .venv/bin/python -m pytest python_pkg/endurain_import/tests/ -q # 52 t - **The WebDAV URL must not end in a slash.** RunnerUp builds the target as `url + fileBase + fileExt` where `fileBase` already starts with `/`. - **`MKCOL` is not supported by dufs.** RunnerUp `PROPFIND`s the directory and - only issues `MKCOL` on 404, so `~/cloud/RunnerUp/` must exist on disk or + only issues `MKCOL` on 404, so `~/data/cloud/RunnerUp/` must exist on disk or adding the account fails with an opaque generic error (upstream #1147/#1172). - **HTTPS is mandatory.** RunnerUp targets `targetSdk 36` with no `usesCleartextTraffic`, so Android blocks plain HTTP. Self-signed certs are diff --git a/endurain/scripts/publish_endurain.sh b/endurain/scripts/publish_endurain.sh index 409a60a4..113accaf 100755 --- a/endurain/scripts/publish_endurain.sh +++ b/endurain/scripts/publish_endurain.sh @@ -20,7 +20,7 @@ SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_NAME readonly APP_URL="http://127.0.0.1:8085" readonly PUBLIC_HOST="endurain.kuhy.duckdns.org" -readonly EDGE_DIR="${HOME}/gitea" +readonly EDGE_DIR="${HOME}/services/gitea" readonly SITE_FILE="${EDGE_DIR}/sites/endurain.caddy" readonly EDGE_CONTAINER="gitea-caddy" diff --git a/endurain/systemd/endurain-import.service b/endurain/systemd/endurain-import.service index 6111d3f5..c20bc205 100644 --- a/endurain/systemd/endurain-import.service +++ b/endurain/systemd/endurain-import.service @@ -1,6 +1,6 @@ [Unit] Description=Import RunnerUp activities into Endurain -Documentation=file:///home/kuhy/testsAndMisc/endurain/README.md +Documentation=file:///home/kuhy/src/testsAndMisc/endurain/README.md # Endurain runs in Docker; without this the timer fires during boot before the # stack is listening and logs a spurious "not reachable" failure. After=network-online.target docker.service @@ -8,9 +8,9 @@ Wants=network-online.target [Service] Type=oneshot -WorkingDirectory=/home/kuhy/testsAndMisc +WorkingDirectory=/home/kuhy/src/testsAndMisc Environment=ENDURAIN_URL=http://127.0.0.1:8085 -Environment=ENDURAIN_INBOX=/home/kuhy/cloud/RunnerUp +Environment=ENDURAIN_INBOX=/home/kuhy/data/cloud/RunnerUp Environment=ENDURAIN_STATE=/home/kuhy/.local/state/endurain-import Environment=ENDURAIN_BULK_DIR=/var/opt/endurain/backend/data/activity_files/bulk_import # Two adb devices are usually attached (USB phone + a network target), and the @@ -18,7 +18,7 @@ Environment=ENDURAIN_BULK_DIR=/var/opt/endurain/backend/data/activity_files/bulk Environment=ENDURAIN_ADB_SERIAL=23181JEGR08034 # The API key is read from a mode-600 file rather than embedded here: systemd # unit files are world-readable. -ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/testsAndMisc/endurain/.api_key)" /home/kuhy/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import' +ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/src/testsAndMisc/endurain/.api_key)" /home/kuhy/src/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import' [Install] WantedBy=default.target diff --git a/linux_configuration/.githooks/pre-commit b/linux_configuration/.githooks/pre-commit index 78307cc0..f5643400 100755 --- a/linux_configuration/.githooks/pre-commit +++ b/linux_configuration/.githooks/pre-commit @@ -15,8 +15,9 @@ printf ' ✓ toolchain complete\n' printf 'Running auto-fixers and shell_check before committing...\n' -# Get list of staged shell files -mapfile -t staged_files < <(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sh|bash|zsh)$' || true) +# Get list of staged shell files. zsh is deliberately excluded: neither shfmt +# nor shellcheck parses zsh (SC1071), so .zsh files get `zsh -n` further down. +mapfile -t staged_files < <(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sh|bash)$' || true) if [[ ${#staged_files[@]} -gt 0 ]]; then printf 'Auto-fixing %d shell file(s)...\n' "${#staged_files[@]}" @@ -44,7 +45,7 @@ printf 'Running shell_check validation...\n' # Run shell_check to validate all checks # Note: We only validate the staged files since we just auto-formatted them # Validate only shellcheck, not shfmt (we already applied formatting) -mapfile -d '' -t staged_shell_files < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.(sh|bash|zsh)$' || true) +mapfile -d '' -t staged_shell_files < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.(sh|bash)$' || true) if [[ ${#staged_shell_files[@]} -gt 0 ]]; then # Run shellcheck on staged files (guaranteed present by verify_toolchain) @@ -57,6 +58,15 @@ if [[ ${#staged_shell_files[@]} -gt 0 ]]; then fi fi +# zsh files: syntax check only, with the shell that will actually run them. +mapfile -d '' -t staged_zsh_files < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.zsh$' || true) +for file in "${staged_zsh_files[@]}"; do + if ! zsh -n "$file"; then + printf '\nCommit aborted: zsh -n found a syntax error in %s.\n' "$file" >&2 + exit 1 + fi +done + printf 'shell_check passed.\n' # Run duplicate code detection diff --git a/linux_configuration/.github/copilot-instructions.md b/linux_configuration/.github/copilot-instructions.md index 6052e41b..414f55bf 100644 --- a/linux_configuration/.github/copilot-instructions.md +++ b/linux_configuration/.github/copilot-instructions.md @@ -24,7 +24,7 @@ This repo automates Linux desktop bootstrap, hardening, and i3 setup. It’s pri ## Core workflows (what to run) - Fresh machine: run from repo root - - `fresh-install/main.sh` (bootstraps configs, GPU, hosts, i3, pacman wrapper, services). It assumes the repo is at `~/linux-configuration` in some steps. + - `fresh-install/main.sh` (bootstraps configs, GPU, hosts, i3, pacman wrapper, services). It assumes the repo is at `~/src/testsAndMisc/linux_configuration` in some steps. - Periodic services: `sudo periodic_background/setup_periodic_system.sh` (installs timer, startup service, hosts monitor, and browser pre-exec wrapper; then performs an initial run). - Pacman wrapper only: `sudo periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh` (backs up pacman and wires the wrapper). The wrapper auto-runs hosts-guard pre/post hooks and can self-setup periodic services when missing. - Hosts guard: @@ -66,4 +66,4 @@ For in-depth understanding of specific components, see these dedicated guides: | Midnight Shutdown | Auto-shutdown at night | `periodic_background/digital_wellbeing/setup_midnight_shutdown.sh` | | Compulsive Block | Limit app launches | `periodic_background/digital_wellbeing/block_compulsive_opening.sh` | | Music Wrapper | Block music during focus | `periodic_background/digital_wellbeing/youtube-music-wrapper.sh` | -| Screen Locker | Require workout to unlock | External: `~/testsAndMisc/python_pkg/screen_locker/` | +| Screen Locker | Require workout to unlock | External: `~/src/testsAndMisc/python_pkg/screen_locker/` | diff --git a/linux_configuration/docs/security-hardening/DOCS-01-current-state.md b/linux_configuration/docs/security-hardening/DOCS-01-current-state.md index f6d0599e..12cf0ade 100644 --- a/linux_configuration/docs/security-hardening/DOCS-01-current-state.md +++ b/linux_configuration/docs/security-hardening/DOCS-01-current-state.md @@ -57,7 +57,7 @@ ### 3. Screen Locker (Python - External Repo) -**File:** `/home/kuhy/testsAndMisc/python_pkg/screen_locker/screen_lock.py` +**File:** `/home/kuhy/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py` **Current Workout Types:** diff --git a/linux_configuration/docs/security-hardening/DOCS-03-implementation.md b/linux_configuration/docs/security-hardening/DOCS-03-implementation.md index b69cc944..686b4af7 100644 --- a/linux_configuration/docs/security-hardening/DOCS-03-implementation.md +++ b/linux_configuration/docs/security-hardening/DOCS-03-implementation.md @@ -8,7 +8,7 @@ ```` I need to implement comprehensive security hardening for a Linux digital wellbeing system. -The codebase is at ~/linux-configuration/ with these components needing changes: +The codebase is at ~/src/testsAndMisc/linux_configuration/ with these components needing changes: ## 1. HOSTS PROTECTION - nsswitch.conf Guard @@ -42,7 +42,7 @@ Changes needed: ## 3. SCREEN LOCKER - External Repo -Location: ~/testsAndMisc/python_pkg/screen_locker/screen_lock.py +Location: ~/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py Changes needed: - REMOVE the "Running" workout option entirely (too easy to fake) @@ -173,7 +173,7 @@ Modified files: External repo (separate changes): -- ~/testsAndMisc/python_pkg/screen_locker/screen_lock.py (remove running, harden table tennis) +- ~/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py (remove running, harden table tennis) ``` diff --git a/linux_configuration/docs/security-hardening/DOCS-04-agent-personas.md b/linux_configuration/docs/security-hardening/DOCS-04-agent-personas.md index 5d69745f..df5e9a59 100644 --- a/linux_configuration/docs/security-hardening/DOCS-04-agent-personas.md +++ b/linux_configuration/docs/security-hardening/DOCS-04-agent-personas.md @@ -167,7 +167,7 @@ LIMITATION (needs fix): You are an expert on the screen_lock.py workout locker. You understand: -FILE LOCATION: ~/testsAndMisc/python_pkg/screen_locker/screen_lock.py (1261 lines) +FILE LOCATION: ~/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py (1261 lines) PURPOSE: diff --git a/linux_configuration/docs/security-hardening/DOCS-05-llm-readmes.md b/linux_configuration/docs/security-hardening/DOCS-05-llm-readmes.md index 13f995d0..48aa63ce 100644 --- a/linux_configuration/docs/security-hardening/DOCS-05-llm-readmes.md +++ b/linux_configuration/docs/security-hardening/DOCS-05-llm-readmes.md @@ -39,8 +39,8 @@ systemctl status hosts-guard.path hosts-bind-mount.service sudo /usr/local/sbin/unlock-hosts # Reinstall/repair -sudo ~/linux-configuration/hosts/install.sh -sudo ~/linux-configuration/hosts/guard/setup_hosts_guard.sh +sudo ~/src/hosts-blocker/install.sh +sudo ~/src/hosts-blocker/guard/setup_hosts_guard.sh ```` ## DO NOT diff --git a/linux_configuration/docs/security-hardening/DOCS-06-test-template.md b/linux_configuration/docs/security-hardening/DOCS-06-test-template.md index ebb931ea..e448391c 100644 --- a/linux_configuration/docs/security-hardening/DOCS-06-test-template.md +++ b/linux_configuration/docs/security-hardening/DOCS-06-test-template.md @@ -51,7 +51,7 @@ else fi # Test 5: google-chrome is blocked -if grep -qi "google-chrome" ~/linux-configuration/scripts/periodic_background/digital_wellbeing/pacman/pacman_blocked_keywords.txt; then +if grep -qi "google-chrome" ~/src/testsAndMisc/linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/pacman_blocked_keywords.txt; then test_result "google-chrome in blocked list" "pass" else test_result "google-chrome in blocked list" "fail" diff --git a/linux_configuration/dwm/bin/dwm-session b/linux_configuration/dwm/bin/dwm-session index 7434e963..54ab393f 100755 --- a/linux_configuration/dwm/bin/dwm-session +++ b/linux_configuration/dwm/bin/dwm-session @@ -20,7 +20,7 @@ command -v simplenote >/dev/null 2>&1 && simplenote & [ -x "$HOME/.fehbg" ] && "$HOME/.fehbg" & # repo helpers (real current paths under single_use/utils); guarded in case moved -idle="$HOME/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh" +idle="$HOME/src/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh" [ -x "$idle" ] && bash "$idle" --watch-controller & # lightweight status bar (root window name) diff --git a/linux_configuration/dwm/config.h b/linux_configuration/dwm/config.h index d7727ead..b351670c 100644 --- a/linux_configuration/dwm/config.h +++ b/linux_configuration/dwm/config.h @@ -201,13 +201,13 @@ static const Key keys[] = { * backend as the media keys, with a notification and NO pacman side-effects. * (toggle_mic.sh is deliberately avoided here: it runs `sudo pacman -S dunst` * if dunst is missing, which deadlocks pacman when spawned without a tty.) */ - { MODKEY, XK_m, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") }, + { MODKEY, XK_m, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") }, /* media keys — routed through volume_control.sh, same as the (now-fixed) i3 config */ - { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh up") }, - { 0, XF86XK_AudioLowerVolume, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh down") }, - { 0, XF86XK_AudioMute, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh mute") }, - { 0, XF86XK_AudioMicMute, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh up") }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh down") }, + { 0, XF86XK_AudioMute, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh mute") }, + { 0, XF86XK_AudioMicMute, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") }, /* tags 1-10 (i3 workspaces 1-10) */ TAGKEYS( XK_1, 0) diff --git a/linux_configuration/features/keepass_consolidate.sh b/linux_configuration/features/keepass_consolidate.sh index 0796f079..63355638 100755 --- a/linux_configuration/features/keepass_consolidate.sh +++ b/linux_configuration/features/keepass_consolidate.sh @@ -17,7 +17,7 @@ set -euo pipefail -CANONICAL="${KP_CANONICAL:-$HOME/cloud/Keepass/Passwords.kdbx}" +CANONICAL="${KP_CANONICAL:-$HOME/data/cloud/Keepass/Passwords.kdbx}" SEARCH_ROOT="${KP_SEARCH_ROOT:-$HOME}" DRY_RUN=0 STATE_DIR="${KP_STATE_DIR:-$HOME/.config/keepass-sync}" @@ -25,16 +25,34 @@ STATE_DIR="${KP_STATE_DIR:-$HOME/.config/keepass-sync}" EXCLUDE_ARGS=() while [[ $# -gt 0 ]]; do case "$1" in - --canonical) CANONICAL="$2"; shift 2 ;; - --root) SEARCH_ROOT="$2"; shift 2 ;; - --exclude) EXCLUDE_ARGS+=("$2"); shift 2 ;; - --dry-run|-n) DRY_RUN=1; shift ;; - *) echo "unknown arg: $1" >&2; exit 2 ;; + --canonical) + CANONICAL="$2" + shift 2 + ;; + --root) + SEARCH_ROOT="$2" + shift 2 + ;; + --exclude) + EXCLUDE_ARGS+=("$2") + shift 2 + ;; + --dry-run | -n) + DRY_RUN=1 + shift + ;; + *) + echo "unknown arg: $1" >&2 + exit 2 + ;; esac done log() { printf '[keepass-consolidate] %s\n' "$*" >&2; } -die() { printf '[keepass-consolidate] ERROR: %s\n' "$*" >&2; exit 1; } +die() { + printf '[keepass-consolidate] ERROR: %s\n' "$*" >&2 + exit 1 +} MASTER_PW="${KP_MASTER_PW:-}" [[ -n "$MASTER_PW" ]] || die "KP_MASTER_PW not set (needed to open/merge vaults)" @@ -47,18 +65,23 @@ FD="$(command -v fd || command -v fdfind || true)" opens() { printf '%s\n' "$MASTER_PW" | keepassxc-cli ls "$1" >/dev/null 2>&1; } opens "$CANONICAL" || die "master password does not open the canonical vault — aborting" -backup() { # backup — copy into a timestamped sibling .backup_ dir +backup() { # backup — copy into a timestamped sibling .backup_ dir local f="$1" bdir bdir="$(dirname "$f")/.backup_$(date +%Y%m%d_%H%M%S)_consolidate" - mkdir -p "$bdir"; cp -f "$f" "$bdir/" + mkdir -p "$bdir" + cp -f "$f" "$bdir/" } # --- exclude prefixes: dirs whose vaults are NOT strays ----------------------- # Critically the dufs serve-path: on the machine running dufs that directory IS -# the remote canonical store (e.g. ~/cloud/Keepass) — it must never be treated +# the remote canonical store (e.g. ~/data/cloud/Keepass) — it must never be treated # as a stray and removed. Plus any --exclude args and KP_EXCLUDE (colon-list). EXCLUDES=() -_add_exclude() { local p; p="$(readlink -f "$1" 2>/dev/null || echo "$1")"; [[ -n "$p" ]] && EXCLUDES+=("$p"); } +_add_exclude() { + local p + p="$(readlink -f "$1" 2>/dev/null || echo "$1")" + [[ -n "$p" ]] && EXCLUDES+=("$p") +} _dufs_cfg="$HOME/.config/dufs/dufs.yaml" if [[ -f "$_dufs_cfg" ]]; then _sp="$(sed -nE 's/^serve-path:[[:space:]]*//p' "$_dufs_cfg" | head -1)" @@ -67,7 +90,7 @@ fi IFS=':' read -r -a _kp_excl <<<"${KP_EXCLUDE:-}" for e in "${_kp_excl[@]}" "${EXCLUDE_ARGS[@]}"; do [[ -n "$e" ]] && _add_exclude "$e"; done -is_excluded() { # is_excluded — under any excluded prefix? +is_excluded() { # is_excluded — under any excluded prefix? local p="$1" ex for ex in "${EXCLUDES[@]}"; do [[ "$p" == "$ex" || "$p" == "$ex"/* ]] && return 0 @@ -90,8 +113,8 @@ STRAYS=() for f in "${CANDIDATES[@]}"; do [[ -f "$f" ]] || continue local_abs="$(readlink -f "$f")" - [[ "$local_abs" == "$CANON_ABS" ]] && continue # the canonical itself - is_excluded "$local_abs" && continue # dufs store / excluded dirs + [[ "$local_abs" == "$CANON_ABS" ]] && continue # the canonical itself + is_excluded "$local_abs" && continue # dufs store / excluded dirs # Skip our own timestamped backups defensively (in case fd glob missed). [[ "$f" == *"/.backup_"* ]] && continue STRAYS+=("$f") diff --git a/linux_configuration/features/lib/tests/test_wg_verify_units.sh b/linux_configuration/features/lib/tests/test_wg_verify_units.sh index 199601db..66b7b376 100755 --- a/linux_configuration/features/lib/tests/test_wg_verify_units.sh +++ b/linux_configuration/features/lib/tests/test_wg_verify_units.sh @@ -22,7 +22,7 @@ trap _t_teardown EXIT # shellcheck source=../wg_verify_units.sh . "$SCRIPT_DIR/../wg_verify_units.sh" -readonly ENTRY="/home/kuhy/testsAndMisc/linux_configuration/features/setup_wireguard_ssh.sh" +readonly ENTRY="/home/kuhy/src/testsAndMisc/linux_configuration/features/setup_wireguard_ssh.sh" UNIT_DIR="$TEST_TMPDIR/units" mkdir -p "$UNIT_DIR" diff --git a/linux_configuration/features/migrate_github_to_gitea.sh b/linux_configuration/features/migrate_github_to_gitea.sh index 0138d48d..6214dc23 100755 --- a/linux_configuration/features/migrate_github_to_gitea.sh +++ b/linux_configuration/features/migrate_github_to_gitea.sh @@ -27,8 +27,8 @@ source "$SCRIPT_DIR/../lib/common.sh" readonly GITEA_DOMAIN="kuhy.duckdns.org" readonly GITEA_OWNER="kuhyx" readonly GITHUB_OWNER="kuhyx" -readonly GITEA_TOKEN_FILE="${HOME}/gitea/.admin_token" -readonly GITHUB_MIRROR_TOKEN_FILE="${HOME}/gitea/.github_mirror_token" +readonly GITEA_TOKEN_FILE="${HOME}/services/gitea/.admin_token" +readonly GITHUB_MIRROR_TOKEN_FILE="${HOME}/services/gitea/.github_mirror_token" die() { log_error "$1" diff --git a/linux_configuration/features/setup_gitea.sh b/linux_configuration/features/setup_gitea.sh index 01965b88..e3b2d7c7 100755 --- a/linux_configuration/features/setup_gitea.sh +++ b/linux_configuration/features/setup_gitea.sh @@ -34,7 +34,7 @@ source "$SCRIPT_DIR/../lib/common.sh" readonly GITEA_DOMAIN="gitea.kuhy.duckdns.org" readonly GITEA_ADMIN_USER="kuhyx" readonly GITEA_ADMIN_EMAIL="krzysztofrudnicki0@gmail.com" -readonly GITEA_DATA_DIR="${HOME}/gitea" +readonly GITEA_DATA_DIR="${HOME}/services/gitea" readonly COMPOSE_FILE="${GITEA_DATA_DIR}/docker-compose.yml" readonly CADDYFILE="${GITEA_DATA_DIR}/Caddyfile" # Per-service Caddy site blocks live here as individual *.caddy snippets so diff --git a/linux_configuration/features/setup_kcd2_dice_solver.sh b/linux_configuration/features/setup_kcd2_dice_solver.sh index cd09072e..5902335d 100755 --- a/linux_configuration/features/setup_kcd2_dice_solver.sh +++ b/linux_configuration/features/setup_kcd2_dice_solver.sh @@ -42,14 +42,14 @@ source "${SCRIPT_DIR}/../lib/common.sh" readonly DICE_DOMAIN="dice.kuhy.duckdns.org" readonly DICE_PORT="8089" # The solver was extracted to its own repo; this script only deploys it. -readonly DICE_SRC="${HOME}/kcd2-dice-solver" +readonly DICE_SRC="${HOME}/src/kcd2-dice-solver" readonly REPO_ROOT="${DICE_SRC}" readonly DICE_BUILD="${DICE_SRC}/deploy_build.sh" -readonly DICE_DATA_DIR="${HOME}/kcd2-dice-serve" +readonly DICE_DATA_DIR="${HOME}/services/kcd2-dice-serve" readonly DICE_COMPOSE="${DICE_DATA_DIR}/docker-compose.yml" readonly DICE_INNER_CADDY="${DICE_DATA_DIR}/Caddyfile" # Shared edge owned by setup_gitea.sh. -readonly GITEA_DATA_DIR="${HOME}/gitea" +readonly GITEA_DATA_DIR="${HOME}/services/gitea" readonly CADDYFILE="${GITEA_DATA_DIR}/Caddyfile" readonly SITES_DIR="${GITEA_DATA_DIR}/sites" readonly DICE_SNIPPET="${SITES_DIR}/dice.caddy" diff --git a/linux_configuration/features/setup_keepass_sync.sh b/linux_configuration/features/setup_keepass_sync.sh index 5934e50f..f13a7a4f 100755 --- a/linux_configuration/features/setup_keepass_sync.sh +++ b/linux_configuration/features/setup_keepass_sync.sh @@ -2,7 +2,7 @@ # setup_keepass_sync.sh — set up "one KeePass vault, synced everywhere". # # IDENTICAL on every Linux device (PC and laptop alike): each keeps one local -# working vault at ~/Keepass/Passwords.kdbx and syncs it to the canonical copy +# working vault at ~/data/Keepass/Passwords.kdbx and syncs it to the canonical copy # on the dufs server over WebDAV, at the moment you open KeePass. You open the # vault with a single command — `keepass-open` — on every machine. The phone # uses KeePassDX (same idea: enter password → Synchronize over WebDAV → use). @@ -22,13 +22,19 @@ readonly CONFIG_DIR="$HOME/.config/keepass-sync" readonly CONFIG="$CONFIG_DIR/config.env" readonly BIN_DIR="$HOME/.local/bin" readonly KEYRING_SERVICE="${KP_KEYRING_SERVICE:-keepass-sync}" -readonly LOCAL_DB="$HOME/Keepass/Passwords.kdbx" +readonly LOCAL_DB="$HOME/data/Keepass/Passwords.kdbx" C() { printf '\033[1;34m[keepass-setup]\033[0m %s\n' "$*"; } OK() { printf '\033[1;32m ✓\033[0m %s\n' "$*"; } WARN() { printf '\033[1;33m !\033[0m %s\n' "$*"; } -die() { printf '\033[1;31m[keepass-setup] ERROR:\033[0m %s\n' "$*" >&2; exit 1; } -usage() { grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'; exit 0; } +die() { + printf '\033[1;31m[keepass-setup] ERROR:\033[0m %s\n' "$*" >&2 + exit 1 +} +usage() { + grep -E '^#( |$)' "$0" | sed -E 's/^# ?//' + exit 0 +} install_deps() { C "Installing dependencies" @@ -55,7 +61,8 @@ main() { read -r -p "dufs vault URL [https://kuhy-cloud.duckdns.org/Keepass/Passwords.kdbx]: " url url="${url:-https://kuhy-cloud.duckdns.org/Keepass/Passwords.kdbx}" read -r -p "dufs web username: " user - read -r -s -p "dufs web password (server credential — cached in keyring): " dpass; echo + read -r -s -p "dufs web password (server credential — cached in keyring): " dpass + echo [[ -n "$user" && -n "$dpass" ]] || die "dufs username/password required" printf '%s' "$dpass" | secret-tool store --label='dufs server password (keepass-sync)' \ service "$KEYRING_SERVICE" key dufs @@ -86,8 +93,8 @@ main() { # --- retire the old one-way mirror unit (dufs host only) ---------------- # is-enabled/is-active are pipe-free and work as non-root (unlike # `list-unit-files | grep -q`, which misfires under set -o pipefail). - if systemctl is-enabled keepass-cloud-sync.path >/dev/null 2>&1 \ - || systemctl is-active keepass-cloud-sync.path >/dev/null 2>&1; then + if systemctl is-enabled keepass-cloud-sync.path >/dev/null 2>&1 || + systemctl is-active keepass-cloud-sync.path >/dev/null 2>&1; then C "Retiring the old one-way keepass-cloud-sync units" sudo systemctl disable --now keepass-cloud-sync.path keepass-cloud-sync.service 2>/dev/null || true OK "old one-way sync retired" diff --git a/linux_configuration/features/setup_personal_website.sh b/linux_configuration/features/setup_personal_website.sh index 889e32e1..b5f8b0a1 100755 --- a/linux_configuration/features/setup_personal_website.sh +++ b/linux_configuration/features/setup_personal_website.sh @@ -43,14 +43,14 @@ readonly WEBSITE_DOMAIN="kuhy.duckdns.org" readonly GITEA_DOMAIN="gitea.kuhy.duckdns.org" readonly WEBSITE_PORT="8088" # React/TS source lives in its own standalone repo (github.com/kuhyx/personal-website), -# cloned at ~/personal-website; the runtime data dir holds the generated compose + +# cloned at ~/src/personal-website; the runtime data dir holds the generated compose + # inner Caddyfile. -readonly WEBSITE_SRC="${HOME}/personal-website" -readonly WEBSITE_DATA_DIR="${HOME}/personal-website-serve" +readonly WEBSITE_SRC="${HOME}/src/personal-website" +readonly WEBSITE_DATA_DIR="${HOME}/services/personal-website-serve" readonly WEBSITE_COMPOSE="${WEBSITE_DATA_DIR}/docker-compose.yml" readonly WEBSITE_INNER_CADDY="${WEBSITE_DATA_DIR}/Caddyfile" # Shared edge owned by setup_gitea.sh. -readonly GITEA_DATA_DIR="${HOME}/gitea" +readonly GITEA_DATA_DIR="${HOME}/services/gitea" readonly CADDYFILE="${GITEA_DATA_DIR}/Caddyfile" readonly SITES_DIR="${GITEA_DATA_DIR}/sites" readonly WEBSITE_SNIPPET="${SITES_DIR}/website.caddy" @@ -60,8 +60,8 @@ readonly GITEA_SCRIPT="${SCRIPT_DIR}/setup_gitea.sh" readonly WG_SCRIPT="${SCRIPT_DIR}/setup_wireguard_ssh.sh" # Canonical CV data (rendered by the CV repo's build_cv.py); copied into the # website build. A committed copy in the repo keeps the build standalone. -readonly CV_JSON_SRC="${HOME}/CV/generic/cv.json" -readonly CV_PDF_SRC="${HOME}/CV/generic/cv-en.pdf" +readonly CV_JSON_SRC="${HOME}/src/CV/generic/cv.json" +readonly CV_PDF_SRC="${HOME}/src/CV/generic/cv-en.pdf" die() { log_error "$1" diff --git a/linux_configuration/features/setup_searxng.sh b/linux_configuration/features/setup_searxng.sh index 6ee470d1..a02b1f10 100755 --- a/linux_configuration/features/setup_searxng.sh +++ b/linux_configuration/features/setup_searxng.sh @@ -11,7 +11,7 @@ # # What it does (idempotent — safe to re-run): # - Installs runtime deps. -# - Writes ~/searxng/{docker-compose.yml,core-config/settings.yml}. +# - Writes ~/services/searxng/{docker-compose.yml,core-config/settings.yml}. # - Runs valkey (limiter backend) on 127.0.0.1:6379. # - Runs searxng on 127.0.0.1:8090, both with network_mode: host. # - Adds a reverse-proxy + security-header snippet to the shared edge. @@ -53,13 +53,13 @@ readonly VALKEY_PORT="6379" # searxng/searxng:latest as of 2026-08-09 == release 2026.8.4. See the comment # on the image: line in the compose heredoc for why this is pinned. readonly SEARX_IMAGE="searxng/searxng@sha256:f4c8e59de166ed71f6380c0847c312ca51f0d41996e31d0559163b6b09ecde52" -readonly SEARX_DATA_DIR="${HOME}/searxng" +readonly SEARX_DATA_DIR="${HOME}/services/searxng" readonly SEARX_COMPOSE="${SEARX_DATA_DIR}/docker-compose.yml" readonly SEARX_CONFIG_DIR="${SEARX_DATA_DIR}/core-config" readonly SEARX_SETTINGS="${SEARX_CONFIG_DIR}/settings.yml" readonly SEARX_LIMITER="${SEARX_CONFIG_DIR}/limiter.toml" # Shared edge owned by setup_gitea.sh. -readonly GITEA_DATA_DIR="${HOME}/gitea" +readonly GITEA_DATA_DIR="${HOME}/services/gitea" readonly SITES_DIR="${GITEA_DATA_DIR}/sites" readonly SEARX_SNIPPET="${SITES_DIR}/searx.caddy" readonly CADDY_CONTAINER="gitea-caddy" @@ -161,7 +161,6 @@ source "$SCRIPT_DIR/lib/searxng_expose.sh" # shellcheck source=lib/searxng_status.sh source "$SCRIPT_DIR/lib/searxng_status.sh" - main() { local cmd="${1:-setup}" case "$cmd" in diff --git a/linux_configuration/features/setup_syncyomi.sh b/linux_configuration/features/setup_syncyomi.sh index c91368a4..b84ed610 100755 --- a/linux_configuration/features/setup_syncyomi.sh +++ b/linux_configuration/features/setup_syncyomi.sh @@ -29,13 +29,13 @@ set -euo pipefail # SYNCYOMI_IMAGE=ghcr.io/syncyomi/syncyomi:v1.1.11 ./setup_syncyomi.sh # Any value containing a "/" is treated as a registry image and pulled as-is. readonly SYNCYOMI_IMAGE="${SYNCYOMI_IMAGE:-syncyomi-kuhy:local}" -readonly SYNCYOMI_SRC="${SYNCYOMI_SRC:-${HOME}/syncyomi-src}" +readonly SYNCYOMI_SRC="${SYNCYOMI_SRC:-${HOME}/vendor/syncyomi-src}" readonly SYNCYOMI_FORK_URL="https://github.com/kuhyx/syncyomi.git" readonly SYNCYOMI_PORT=8282 readonly SYNCYOMI_LOCAL="http://127.0.0.1:${SYNCYOMI_PORT}" -# --- Data dir + secrets (outside the git repo, like ~/gitea) ----------------- -readonly DATA_DIR="${HOME}/syncyomi" +# --- Data dir + secrets (outside the git repo, like ~/services/gitea) ----------------- +readonly DATA_DIR="${HOME}/services/syncyomi" readonly COMPOSE_FILE="${DATA_DIR}/docker-compose.yml" readonly CONFIG_TOML="${DATA_DIR}/config/config.toml" readonly CONF_FILE="${DATA_DIR}/.syncyomi.conf" @@ -43,8 +43,8 @@ readonly TOKEN_FILE="${DATA_DIR}/.api_token" # --- Existing host infrastructure (from setup_gitea.sh / install_joplin.sh) -- readonly CADDY_CONTAINER="gitea-caddy" -readonly CADDYFILE="${HOME}/gitea/Caddyfile" -readonly CADDY_SITES_DIR="${HOME}/gitea/sites" +readonly CADDYFILE="${HOME}/services/gitea/Caddyfile" +readonly CADDY_SITES_DIR="${HOME}/services/gitea/sites" readonly CADDYFILE_IN_CONTAINER="/etc/caddy/Caddyfile" readonly DUCKDNS_UPDATER="${HOME}/.joplin-server/duckdns-update.sh" @@ -121,7 +121,7 @@ load_config() { save_config() { # 0600 — holds the plaintext SyncYomi password so re-runs stay - # non-interactive. Lives under ~/syncyomi (outside the git repo). + # non-interactive. Lives under ~/services/syncyomi (outside the git repo). umask 077 { printf 'SYNCYOMI_USER=%q\n' "${SYNCYOMI_USER}" diff --git a/linux_configuration/features/setup_syncyomi_guard.sh b/linux_configuration/features/setup_syncyomi_guard.sh index 1eac9d40..df425b91 100755 --- a/linux_configuration/features/setup_syncyomi_guard.sh +++ b/linux_configuration/features/setup_syncyomi_guard.sh @@ -21,9 +21,9 @@ readonly REPO_ROOT readonly UNIT_DIR="${HOME}/.config/systemd/user" readonly SERVICE_NAME="syncyomi-guard.service" readonly TIMER_NAME="syncyomi-guard.timer" -readonly SNAPSHOT_DIR="${HOME}/syncyomi/snapshots" +readonly SNAPSHOT_DIR="${HOME}/services/syncyomi/snapshots" readonly STATE_DIR="${HOME}/.local/share/syncyomi_guard" -readonly DB_PATH="${HOME}/syncyomi/config/syncyomi.db" +readonly DB_PATH="${HOME}/services/syncyomi/config/syncyomi.db" log_info() { echo "[*] $*"; } log_ok() { echo "[+] $*"; } diff --git a/linux_configuration/features/systemd/keepass-cloud-sync.service b/linux_configuration/features/systemd/keepass-cloud-sync.service index ea14807c..bc1bcf13 100644 --- a/linux_configuration/features/systemd/keepass-cloud-sync.service +++ b/linux_configuration/features/systemd/keepass-cloud-sync.service @@ -8,6 +8,6 @@ Description=Mirror KeePass vault into dufs cloud folder [Service] Type=oneshot User=__USER__ -# Only the vault file — not the whole ~/Keepass dir (keeps local .backup_* vaults +# Only the vault file — not the whole ~/data/Keepass dir (keeps local .backup_* vaults # off the public server). No --delete: the cloud folder may hold other files. ExecStart=/usr/bin/rsync -a __HOME__/Keepass/Passwords.kdbx __CLOUD_ROOT__/Keepass/ diff --git a/linux_configuration/fixes/fix_systemctl.sh b/linux_configuration/fixes/fix_systemctl.sh index b4a9863c..c9296677 100755 --- a/linux_configuration/fixes/fix_systemctl.sh +++ b/linux_configuration/fixes/fix_systemctl.sh @@ -11,8 +11,8 @@ set -euo pipefail SERVICE_NAME="media-organizer" SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" -DEFAULT_ORGANIZE_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/utils/organize_downloads.sh" -LEGACY_ORGANIZE_SCRIPT="/home/kuhy/linux-configuration/utils/organize_downloads.sh" +DEFAULT_ORGANIZE_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh" +LEGACY_ORGANIZE_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh" if [[ -f $DEFAULT_ORGANIZE_SCRIPT ]]; then ORGANIZE_SCRIPT="$DEFAULT_ORGANIZE_SCRIPT" elif [[ -f $LEGACY_ORGANIZE_SCRIPT ]]; then diff --git a/linux_configuration/fixes/lib/arch_perf_fixes.sh b/linux_configuration/fixes/lib/arch_perf_fixes.sh index def5e548..f999876c 100755 --- a/linux_configuration/fixes/lib/arch_perf_fixes.sh +++ b/linux_configuration/fixes/lib/arch_perf_fixes.sh @@ -65,8 +65,8 @@ fix_media_organizer() { # Find the organize_downloads.sh script local script_path="" local candidates=( - "${ORGANIZE_SCRIPT_CANDIDATES:-/home/kuhy/testsAndMisc/linux_configuration/utils/organize_downloads.sh}" - "/home/kuhy/linux-configuration/utils/organize_downloads.sh" + "${ORGANIZE_SCRIPT_CANDIDATES:-/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh}" + "/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh" ) for candidate in "${candidates[@]}"; do if [[ -f $candidate ]]; then diff --git a/linux_configuration/fixes/lib/tests/test_arch_perf_fixes.sh b/linux_configuration/fixes/lib/tests/test_arch_perf_fixes.sh index a82d7a22..b7facfc3 100755 --- a/linux_configuration/fixes/lib/tests/test_arch_perf_fixes.sh +++ b/linux_configuration/fixes/lib/tests/test_arch_perf_fixes.sh @@ -71,7 +71,7 @@ _t_lacks "$(_t_calls)" "systemctl disable" \ # candidate paths are absolute; the first is overridden into the tmpdir and # the second is a real home path that does not exist on this machine. arch_reset -if [[ -f /home/kuhy/linux-configuration/utils/organize_downloads.sh ]]; then +if [[ -f /home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh ]]; then _t_pass "fix_media_organizer: SKIP not-found case (the second candidate exists here)" else out="$(fix_media_organizer 2>&1)" diff --git a/linux_configuration/fresh-install/install_amd_driver.sh b/linux_configuration/fresh-install/install_amd_driver.sh index 76c56770..8f146908 100755 --- a/linux_configuration/fresh-install/install_amd_driver.sh +++ b/linux_configuration/fresh-install/install_amd_driver.sh @@ -13,8 +13,8 @@ set -e [ "${GPU_VENDOR}" = "amd" ] || { - echo "AMD installer invoked but GPU_VENDOR=${GPU_VENDOR}" - exit 0 + echo "AMD installer invoked but GPU_VENDOR=${GPU_VENDOR}" + exit 0 } AMD_INSTALL_XF86=${AMD_INSTALL_XF86:-0} @@ -32,9 +32,9 @@ warn() { echo "[amd][warn] $*" >&2; } # Detect multilib enabled if grep -q '^\[multilib\]' /etc/pacman.conf; then - MULTILIB_ENABLED=1 + MULTILIB_ENABLED=1 else - MULTILIB_ENABLED=0 + MULTILIB_ENABLED=0 fi # Basic packages @@ -58,49 +58,49 @@ LIB32_AMDVLK_PKG="lib32-amdvlk" # Simple AUR builder (reused from NVIDIA script style) _build_aur_pkg() { - local pkg="$1" - local url="https://aur.archlinux.org/${pkg}.git" - mkdir -p "$HOME/aur" - cd "$HOME/aur" - if [ ! -d "$pkg" ]; then git clone "$url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi - cd "$pkg" - rm -f -- *.pkg.tar.* 2> /dev/null || true - yes | makepkg -s -c -C --noconfirm --needed - local built=(*.pkg.tar.zst) - yes | sudo pacman -U --noconfirm "${built[@]}" + local pkg="$1" + local url="https://aur.archlinux.org/${pkg}.git" + mkdir -p "$HOME/sdk/aur" + cd "$HOME/sdk/aur" + if [ ! -d "$pkg" ]; then git clone "$url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi + cd "$pkg" + rm -f -- *.pkg.tar.* 2>/dev/null || true + yes | makepkg -s -c -C --noconfirm --needed + local built=(*.pkg.tar.zst) + yes | sudo pacman -U --noconfirm "${built[@]}" } _install_repo_or_aur() { - local pkg="$1" - if pacman -Si "$pkg" > /dev/null 2>&1; then - if pacman -Qi "$pkg" > /dev/null 2>&1; then - vlog "$pkg already installed" - else - yes | sudo pacman -Sy --noconfirm "$pkg" - fi - else - info "Building AUR package: $pkg" - _build_aur_pkg "$pkg" - fi + local pkg="$1" + if pacman -Si "$pkg" >/dev/null 2>&1; then + if pacman -Qi "$pkg" >/dev/null 2>&1; then + vlog "$pkg already installed" + else + yes | sudo pacman -Sy --noconfirm "$pkg" + fi + else + info "Building AUR package: $pkg" + _build_aur_pkg "$pkg" + fi } info "Installing AMD GPU stack" for p in "${BASE_PKGS[@]}" "$VULKAN_PKG"; do _install_repo_or_aur "$p"; done if [ "$AMD_INSTALL_XF86" = 1 ]; then - _install_repo_or_aur "$XF86_PKG" + _install_repo_or_aur "$XF86_PKG" fi # AMDVLK optional (install after vulkan-radeon if requested) if [ "$AMD_INSTALL_AMDVLK" = 1 ]; then - _install_repo_or_aur "$AMDVLK_PKG" + _install_repo_or_aur "$AMDVLK_PKG" fi if [ $MULTILIB_ENABLED = 1 ] || [ "$AMD_INSTALL_LIB32" = 1 ]; then - for p in "${LIB32_BASE[@]}" "$LIB32_VULKAN_PKG"; do _install_repo_or_aur "$p"; done - if [ "$AMD_INSTALL_AMDVLK" = 1 ]; then _install_repo_or_aur "$LIB32_AMDVLK_PKG"; fi + for p in "${LIB32_BASE[@]}" "$LIB32_VULKAN_PKG"; do _install_repo_or_aur "$p"; done + if [ "$AMD_INSTALL_AMDVLK" = 1 ]; then _install_repo_or_aur "$LIB32_AMDVLK_PKG"; fi else - vlog "Skipping 32-bit packages (multilib disabled)" + vlog "Skipping 32-bit packages (multilib disabled)" fi # Detect SI / CIK codename presence for optional amdgpu enablement @@ -113,41 +113,41 @@ for n in "${SI_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_SI=1 && bre for n in "${CIK_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_CIK=1 && break; done if [ "$AMD_ENABLE_SI_CIK" = "1" ] || { [ "$AMD_ENABLE_SI_CIK" = "auto" ] && { [ $IS_SI = 1 ] || [ $IS_CIK = 1 ]; }; }; then - info "Configuring amdgpu for SI/CIK (IS_SI=$IS_SI IS_CIK=$IS_CIK)" - TMP_CONF=$(mktemp) - printf 'options amdgpu si_support=1\noptions amdgpu cik_support=1\n' > "$TMP_CONF" - printf 'options radeon si_support=0\noptions radeon cik_support=0\n' >> "$TMP_CONF" - sudo mkdir -p /etc/modprobe.d - sudo cp "$TMP_CONF" /etc/modprobe.d/10-amdgpu-si-cik.conf - rm -f "$TMP_CONF" - # Ensure amdgpu early in MODULES - if [ -f /etc/mkinitcpio.conf ]; then - if ! grep -q '^MODULES=.*amdgpu' /etc/mkinitcpio.conf; then - sudo sed -i 's/^MODULES=\(.*\)/MODULES=(amdgpu radeon)/' /etc/mkinitcpio.conf || true - fi - if ! grep -q 'modconf' /etc/mkinitcpio.conf; then - warn "modconf hook not found in mkinitcpio.conf (needed for module options)" - fi - if [ "$AMD_SKIP_INITRAMFS" != 1 ]; then - info "Regenerating initramfs (mkinitcpio -P)" - sudo mkinitcpio -P || warn "mkinitcpio failed; review manually" - else - info "Skipping initramfs regeneration per AMD_SKIP_INITRAMFS=1" - fi - else - warn "/etc/mkinitcpio.conf not found; skipping MODULES update" - fi + info "Configuring amdgpu for SI/CIK (IS_SI=$IS_SI IS_CIK=$IS_CIK)" + TMP_CONF=$(mktemp) + printf 'options amdgpu si_support=1\noptions amdgpu cik_support=1\n' >"$TMP_CONF" + printf 'options radeon si_support=0\noptions radeon cik_support=0\n' >>"$TMP_CONF" + sudo mkdir -p /etc/modprobe.d + sudo cp "$TMP_CONF" /etc/modprobe.d/10-amdgpu-si-cik.conf + rm -f "$TMP_CONF" + # Ensure amdgpu early in MODULES + if [ -f /etc/mkinitcpio.conf ]; then + if ! grep -q '^MODULES=.*amdgpu' /etc/mkinitcpio.conf; then + sudo sed -i 's/^MODULES=\(.*\)/MODULES=(amdgpu radeon)/' /etc/mkinitcpio.conf || true + fi + if ! grep -q 'modconf' /etc/mkinitcpio.conf; then + warn "modconf hook not found in mkinitcpio.conf (needed for module options)" + fi + if [ "$AMD_SKIP_INITRAMFS" != 1 ]; then + info "Regenerating initramfs (mkinitcpio -P)" + sudo mkinitcpio -P || warn "mkinitcpio failed; review manually" + else + info "Skipping initramfs regeneration per AMD_SKIP_INITRAMFS=1" + fi + else + warn "/etc/mkinitcpio.conf not found; skipping MODULES update" + fi else - vlog "SI/CIK enablement not required (AMD_ENABLE_SI_CIK=$AMD_ENABLE_SI_CIK IS_SI=$IS_SI IS_CIK=$IS_CIK)" + vlog "SI/CIK enablement not required (AMD_ENABLE_SI_CIK=$AMD_ENABLE_SI_CIK IS_SI=$IS_SI IS_CIK=$IS_CIK)" fi # Check active kernel driver -KDRV=$(lspci -k -d ::0300 2> /dev/null | awk '/Kernel driver in use:/ {print $5; exit}') +KDRV=$(lspci -k -d ::0300 2>/dev/null | awk '/Kernel driver in use:/ {print $5; exit}') [ -z "$KDRV" ] && KDRV=$(lsmod | grep -E 'amdgpu|radeon' | head -n1 | awk '{print $1}') info "Kernel driver in use: ${KDRV:-unknown}" if [ "$KDRV" = "radeon" ] && { [ $IS_SI = 1 ] || [ $IS_CIK = 1 ]; }; then - warn "radeon driver still active for SI/CIK; reboot may be required to switch to amdgpu" + warn "radeon driver still active for SI/CIK; reboot may be required to switch to amdgpu" fi export AMD_STACK_DONE=1 diff --git a/linux_configuration/fresh-install/install_nvidia_driver.sh b/linux_configuration/fresh-install/install_nvidia_driver.sh index ee126dc0..c624232e 100755 --- a/linux_configuration/fresh-install/install_nvidia_driver.sh +++ b/linux_configuration/fresh-install/install_nvidia_driver.sh @@ -5,96 +5,96 @@ set -e [ "$GPU_VENDOR" = "nvidia" ] || { - echo "NVIDIA installer invoked but GPU_VENDOR=$GPU_VENDOR" - exit 0 + echo "NVIDIA installer invoked but GPU_VENDOR=$GPU_VENDOR" + exit 0 } _build_aur_pkg() { - local pkg="$1" - local repo_url="https://aur.archlinux.org/${pkg}.git" - mkdir -p "$HOME/aur" - cd "$HOME/aur" - if [ ! -d "$pkg" ]; then git clone "$repo_url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi - cd "$pkg" - rm -f -- *.pkg.tar.* 2> /dev/null || true - yes | makepkg -s -c -C --noconfirm --needed || return 1 - local built=(*.pkg.tar.zst) - yes | sudo pacman -U --noconfirm "${built[@]}" + local pkg="$1" + local repo_url="https://aur.archlinux.org/${pkg}.git" + mkdir -p "$HOME/sdk/aur" + cd "$HOME/sdk/aur" + if [ ! -d "$pkg" ]; then git clone "$repo_url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi + cd "$pkg" + rm -f -- *.pkg.tar.* 2>/dev/null || true + yes | makepkg -s -c -C --noconfirm --needed || return 1 + local built=(*.pkg.tar.zst) + yes | sudo pacman -U --noconfirm "${built[@]}" } _choose_nvidia_pkg() { - local have_linux have_linux_lts multiple_kernels driver_pkg prefer_open detect_out legacy_detected=0 - prefer_open=${NVIDIA_PREFER_OPEN:-1} - pacman -Qq | grep -qx linux && have_linux=1 || have_linux=0 - pacman -Qq | grep -qx linux-lts && have_linux_lts=1 || have_linux_lts=0 - if [ $((have_linux + have_linux_lts)) -gt 1 ]; then multiple_kernels=1; else multiple_kernels=0; fi + local have_linux have_linux_lts multiple_kernels driver_pkg prefer_open detect_out legacy_detected=0 + prefer_open=${NVIDIA_PREFER_OPEN:-1} + pacman -Qq | grep -qx linux && have_linux=1 || have_linux=0 + pacman -Qq | grep -qx linux-lts && have_linux_lts=1 || have_linux_lts=0 + if [ $((have_linux + have_linux_lts)) -gt 1 ]; then multiple_kernels=1; else multiple_kernels=0; fi - # Optionally skip attempting to install nvidia-detect (some minimal repo setups don't have it yet) - if [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect > /dev/null 2>&1; then - if pacman -Si nvidia-detect > /dev/null 2>&1; then - echo "Attempting to install helper utility: nvidia-detect" >&2 - # Use --needed to avoid forcing refresh (& avoid partial upgrade semantics with -Sy) - yes | sudo pacman -S --needed --noconfirm nvidia-detect || echo "nvidia-detect install failed (continuing with heuristic)" >&2 - else - echo "nvidia-detect not present in enabled repos; using heuristic selection." >&2 - fi - fi + # Optionally skip attempting to install nvidia-detect (some minimal repo setups don't have it yet) + if [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect >/dev/null 2>&1; then + if pacman -Si nvidia-detect >/dev/null 2>&1; then + echo "Attempting to install helper utility: nvidia-detect" >&2 + # Use --needed to avoid forcing refresh (& avoid partial upgrade semantics with -Sy) + yes | sudo pacman -S --needed --noconfirm nvidia-detect || echo "nvidia-detect install failed (continuing with heuristic)" >&2 + else + echo "nvidia-detect not present in enabled repos; using heuristic selection." >&2 + fi + fi - if command -v nvidia-detect > /dev/null 2>&1; then - detect_out="$(nvidia-detect 2> /dev/null || true)" - fi + if command -v nvidia-detect >/dev/null 2>&1; then + detect_out="$(nvidia-detect 2>/dev/null || true)" + fi - if [ -n "$detect_out" ]; then - if echo "$detect_out" | grep -q '470'; then - driver_pkg='nvidia-470xx-dkms' - legacy_detected=1 - fi - if echo "$detect_out" | grep -q '390'; then - driver_pkg='nvidia-390xx-dkms' - legacy_detected=1 - fi - if echo "$detect_out" | grep -q '340'; then - driver_pkg='nvidia-340xx-dkms' - legacy_detected=1 - fi - fi + if [ -n "$detect_out" ]; then + if echo "$detect_out" | grep -q '470'; then + driver_pkg='nvidia-470xx-dkms' + legacy_detected=1 + fi + if echo "$detect_out" | grep -q '390'; then + driver_pkg='nvidia-390xx-dkms' + legacy_detected=1 + fi + if echo "$detect_out" | grep -q '340'; then + driver_pkg='nvidia-340xx-dkms' + legacy_detected=1 + fi + fi - if [ "$legacy_detected" = 0 ]; then - # Heuristic modern driver selection - if [ "$multiple_kernels" = 1 ]; then - if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-dkms > /dev/null 2>&1; then driver_pkg='nvidia-open-dkms'; else driver_pkg='nvidia-dkms'; fi - else - if [ "$have_linux_lts" = 1 ] && [ "$have_linux" = 0 ]; then - if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-lts > /dev/null 2>&1; then driver_pkg='nvidia-open-lts'; else driver_pkg='nvidia-lts'; fi - else - if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open > /dev/null 2>&1; then driver_pkg='nvidia-open'; else driver_pkg='nvidia'; fi - fi - fi - else - echo "Legacy NVIDIA generation detected via nvidia-detect output; choosing $driver_pkg" >&2 - fi + if [ "$legacy_detected" = 0 ]; then + # Heuristic modern driver selection + if [ "$multiple_kernels" = 1 ]; then + if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-dkms >/dev/null 2>&1; then driver_pkg='nvidia-open-dkms'; else driver_pkg='nvidia-dkms'; fi + else + if [ "$have_linux_lts" = 1 ] && [ "$have_linux" = 0 ]; then + if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-lts >/dev/null 2>&1; then driver_pkg='nvidia-open-lts'; else driver_pkg='nvidia-lts'; fi + else + if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open >/dev/null 2>&1; then driver_pkg='nvidia-open'; else driver_pkg='nvidia'; fi + fi + fi + else + echo "Legacy NVIDIA generation detected via nvidia-detect output; choosing $driver_pkg" >&2 + fi - echo "$driver_pkg" + echo "$driver_pkg" } _remove_conflicting_nvidia_pkgs() { - local keep="$1" - local candidates=(nvidia nvidia-lts nvidia-dkms nvidia-open nvidia-open-lts nvidia-open-dkms nvidia-470xx-dkms nvidia-390xx-dkms nvidia-340xx-dkms) - local to_remove=() - for p in "${candidates[@]}"; do - if pacman -Qi "$p" > /dev/null 2>&1 && [ "$p" != "$keep" ]; then to_remove+=("$p"); fi - done - if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi + local keep="$1" + local candidates=(nvidia nvidia-lts nvidia-dkms nvidia-open nvidia-open-lts nvidia-open-dkms nvidia-470xx-dkms nvidia-390xx-dkms nvidia-340xx-dkms) + local to_remove=() + for p in "${candidates[@]}"; do + if pacman -Qi "$p" >/dev/null 2>&1 && [ "$p" != "$keep" ]; then to_remove+=("$p"); fi + done + if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi } _install_nvidia_stack() { - local driver_pkg="$1" - if [[ $driver_pkg == nvidia-*xx-dkms ]]; then _build_aur_pkg "$driver_pkg"; else yes | sudo pacman -Sy --noconfirm "$driver_pkg"; fi - local utils_pkg="nvidia-utils" utils32_pkg="lib32-nvidia-utils" - if ! pacman -Qi "$utils_pkg" > /dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils_pkg"; fi - if grep -q '^\[multilib\]' /etc/pacman.conf; then - if ! pacman -Qi "$utils32_pkg" > /dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils32_pkg" || true; fi - fi + local driver_pkg="$1" + if [[ $driver_pkg == nvidia-*xx-dkms ]]; then _build_aur_pkg "$driver_pkg"; else yes | sudo pacman -Sy --noconfirm "$driver_pkg"; fi + local utils_pkg="nvidia-utils" utils32_pkg="lib32-nvidia-utils" + if ! pacman -Qi "$utils_pkg" >/dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils_pkg"; fi + if grep -q '^\[multilib\]' /etc/pacman.conf; then + if ! pacman -Qi "$utils32_pkg" >/dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils32_pkg" || true; fi + fi } echo "Detected NVIDIA GPU. Selecting driver..." diff --git a/linux_configuration/fresh-install/lib/packages.sh b/linux_configuration/fresh-install/lib/packages.sh index 76962d00..2ebae312 100755 --- a/linux_configuration/fresh-install/lib/packages.sh +++ b/linux_configuration/fresh-install/lib/packages.sh @@ -6,116 +6,116 @@ # Function to play a sound on error play_error_sound() { - #pactl set-sink-volume @DEFAULT_SINK@ +50% - for _ in 1 2 3; do - paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga - done - #pactl set-sink-volume @DEFAULT_SINK@ -50% + #pactl set-sink-volume @DEFAULT_SINK@ +50% + for _ in 1 2 3; do + paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga + done + #pactl set-sink-volume @DEFAULT_SINK@ -50% } install_from_aur() { - local repo_url pkg_name repo_dir - repo_url="$1" - pkg_name="$2" + local repo_url pkg_name repo_dir + repo_url="$1" + pkg_name="$2" - mkdir -p "$HOME/aur" - cd "$HOME/aur" || return 1 - repo_dir="$(basename "$repo_url" .git)" + mkdir -p "$HOME/sdk/aur" + cd "$HOME/sdk/aur" || return 1 + repo_dir="$(basename "$repo_url" .git)" - if [ ! -d "$repo_dir" ]; then - git clone "$repo_url" - else - echo "Repository $repo_dir already cloned; updating" - (cd "$repo_dir" && git fetch --all -q && git reset --hard origin/HEAD -q || git pull --ff-only || true) - fi - cd "$repo_dir" || return 1 + if [ ! -d "$repo_dir" ]; then + git clone "$repo_url" + else + echo "Repository $repo_dir already cloned; updating" + (cd "$repo_dir" && git fetch --all -q && git reset --hard origin/HEAD -q || git pull --ff-only || true) + fi + cd "$repo_dir" || return 1 - if pacman -Qi "$pkg_name" > /dev/null 2>&1; then - echo "$pkg_name is already installed" - return 0 - fi + if pacman -Qi "$pkg_name" >/dev/null 2>&1; then + echo "$pkg_name is already installed" + return 0 + fi - echo "Cleaning old package artifacts to avoid duplicate -U targets" - find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2> /dev/null || true + echo "Cleaning old package artifacts to avoid duplicate -U targets" + find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2>/dev/null || true - echo "Building $pkg_name (clean build)" - # -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first) - if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then - echo "Build failed for $pkg_name" >&2 - return 1 - fi + echo "Building $pkg_name (clean build)" + # -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first) + if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then + echo "Build failed for $pkg_name" >&2 + return 1 + fi - # Collect only the freshly built packages (should now be only current version) - mapfile -t built_pkgs < <(find . -maxdepth 1 -type f -name '*.pkg.tar.zst' -printf './%f\n') - if [ ${#built_pkgs[@]} -eq 0 ]; then - echo "No package files produced for $pkg_name" >&2 - return 1 - fi + # Collect only the freshly built packages (should now be only current version) + mapfile -t built_pkgs < <(find . -maxdepth 1 -type f -name '*.pkg.tar.zst' -printf './%f\n') + if [ ${#built_pkgs[@]} -eq 0 ]; then + echo "No package files produced for $pkg_name" >&2 + return 1 + fi - echo "Installing built package(s): ${built_pkgs[*]}" - if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then - echo "Installation failed for $pkg_name" >&2 - return 1 - fi + echo "Installing built package(s): ${built_pkgs[*]}" + if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then + echo "Installation failed for $pkg_name" >&2 + return 1 + fi } # Helper: try to install from AUR and log result to done.txt/failed.txt try_aur_install() { - local repo_url="$1" - local pkg_name="$2" - if install_from_aur "$repo_url" "$pkg_name"; then - echo "$pkg_name" >> done.txt - else - echo "$pkg_name" >> failed.txt - fi + local repo_url="$1" + local pkg_name="$2" + if install_from_aur "$repo_url" "$pkg_name"; then + echo "$pkg_name" >>done.txt + else + echo "$pkg_name" >>failed.txt + fi } process_packages() { - local file_path - file_path="$1" - : > failed.txt - : > done.txt + local file_path + file_path="$1" + : >failed.txt + : >done.txt - while IFS= read -r pkg_name; do - if [ -z "$pkg_name" ]; then - continue - fi + while IFS= read -r pkg_name; do + if [ -z "$pkg_name" ]; then + continue + fi - local repo_url repo_dir - repo_url="https://aur.archlinux.org/${pkg_name}-git.git" - repo_dir="${pkg_name}-git" + local repo_url repo_dir + repo_url="https://aur.archlinux.org/${pkg_name}-git.git" + repo_dir="${pkg_name}-git" - git clone "$repo_url" - if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then - echo "Repository $repo_dir is empty, trying without -git suffix" - repo_url="https://aur.archlinux.org/${pkg_name}.git" - repo_dir="${pkg_name}" + git clone "$repo_url" + if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then + echo "Repository $repo_dir is empty, trying without -git suffix" + repo_url="https://aur.archlinux.org/${pkg_name}.git" + repo_dir="${pkg_name}" - git clone "$repo_url" - if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then - echo "Repository $repo_dir is empty, trying to install with pacman" - if sudo pacman -Sy --noconfirm "$pkg_name"; then - echo "$pkg_name" >> done.txt - else - echo "$pkg_name" >> failed.txt - fi - else - try_aur_install "$repo_url" "$pkg_name" - fi - else - try_aur_install "$repo_url" "$pkg_name" - fi - done < "$file_path" + git clone "$repo_url" + if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then + echo "Repository $repo_dir is empty, trying to install with pacman" + if sudo pacman -Sy --noconfirm "$pkg_name"; then + echo "$pkg_name" >>done.txt + else + echo "$pkg_name" >>failed.txt + fi + else + try_aur_install "$repo_url" "$pkg_name" + fi + else + try_aur_install "$repo_url" "$pkg_name" + fi + done <"$file_path" } # Helper: Check if all subpackages are installed # Returns 0 if ALL subpackages are installed, 1 otherwise all_subpackages_installed() { - local -n sub_pkgs_ref=$1 - for subpkg in "${sub_pkgs_ref[@]}"; do - if ! pacman -Qi "$subpkg" &> /dev/null; then - return 1 - fi - done - return 0 + local -n sub_pkgs_ref=$1 + for subpkg in "${sub_pkgs_ref[@]}"; do + if ! pacman -Qi "$subpkg" &>/dev/null; then + return 1 + fi + done + return 0 } diff --git a/linux_configuration/fresh-install/main.sh b/linux_configuration/fresh-install/main.sh index 213bdc3e..81d514fb 100755 --- a/linux_configuration/fresh-install/main.sh +++ b/linux_configuration/fresh-install/main.sh @@ -164,7 +164,7 @@ for entry in "${aur_packages[@]}"; do install_from_aur "$repo_url" "$pkg_name" done -cd ~/linux-configuration/fresh-install +cd ~/src/testsAndMisc/linux_configuration/fresh-install if [ ! -d "$HOME/.config/mpv" ]; then mkdir -p "$HOME/.config/mpv" fi @@ -176,10 +176,10 @@ else echo "Oh My Zsh is already installed" fi -cd ~/linux-configuration -sudo "$HOME/hosts-blocker/install.sh" +cd ~/src/testsAndMisc/linux_configuration +sudo "$HOME/src/hosts-blocker/install.sh" i3/install.sh -"$HOME/digital-wellbeing/pacman/install_pacman_wrapper.sh" +"$HOME/src/digital-wellbeing/pacman/install_pacman_wrapper.sh" fixes/nvidia_troubleshoot.sh sudo features/setup_activitywatch.sh sudo utils/setup_media_organizer.sh diff --git a/linux_configuration/gaming/README.md b/linux_configuration/gaming/README.md index cfbe752f..7edbf9cd 100644 --- a/linux_configuration/gaming/README.md +++ b/linux_configuration/gaming/README.md @@ -28,7 +28,7 @@ sudo chmod 440 /etc/sudoers.d/player2-steam **4. Symlink the script into PATH:** ```bash -sudo ln -sf ~/testsAndMisc/linux_configuration/gaming/start-player2.sh /usr/local/bin/start-player2 +sudo ln -sf ~/src/testsAndMisc/linux_configuration/gaming/start-player2.sh /usr/local/bin/start-player2 ``` **5. Start the getty on tty2** (needed if LightDM autologin is configured, otherwise tty2 has no login prompt): diff --git a/linux_configuration/i3/config b/linux_configuration/i3/config index 6a0396ab..129d8496 100644 --- a/linux_configuration/i3/config +++ b/linux_configuration/i3/config @@ -35,7 +35,7 @@ exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork exec --no-startup-id nm-applet # Keep screen awake and unlocked; also treat controller input as activity -exec --no-startup-id bash /home/kuhy/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh --watch-controller +exec --no-startup-id bash /home/kuhy/src/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh --watch-controller # Use pactl to adjust volume in PulseAudio. set $refresh_i3status killall -SIGUSR1 i3status @@ -59,7 +59,7 @@ bindsym $mod+Return exec terminator # scratchpad calculator: floating popup running the live-calc widget (type # math, results appear as you type; Ctrl-D to close) -bindsym $mod+c exec --no-startup-id /home/kuhy/testsAndMisc/linux_configuration/zsh/calc-popup.sh +bindsym $mod+c exec --no-startup-id /home/kuhy/src/testsAndMisc/linux_configuration/zsh/calc-popup.sh for_window [window_role="calc"] floating enable, border pixel 3, resize set 820 px 220 px, move position center # kill focused window diff --git a/linux_configuration/install_core_system.sh b/linux_configuration/install_core_system.sh index 73bf596a..bb050ba2 100755 --- a/linux_configuration/install_core_system.sh +++ b/linux_configuration/install_core_system.sh @@ -6,14 +6,14 @@ # a clone that fails is a loud module failure, never a silent skip. # # CORE modules (always installed): -# 0. Guard library (guardctl) – github.com/kuhyx/utils (~/utils/guard-lib) -# 1. Workout screen locker – github.com/kuhyx/screen-locker (~/screen-locker) -# 2. Hosts blocking setup – github.com/kuhyx/hosts-blocker (~/hosts-blocker) +# 0. Guard library (guardctl) – github.com/kuhyx/utils (~/src/utils/guard-lib) +# 1. Workout screen locker – github.com/kuhyx/screen-locker (~/src/screen-locker) +# 2. Hosts blocking setup – github.com/kuhyx/hosts-blocker (~/src/hosts-blocker) # 3. Midnight shutdown timer – github.com/kuhyx/digital-wellbeing # # SECONDARY modules (prompted unless --all / --none given): # 4. Steam backlog enforcer – github.com/kuhyx/steam-backlog-enforcer -# 5. Pacman wrapper – github.com/kuhyx/digital-wellbeing (~/digital-wellbeing) +# 5. Pacman wrapper – github.com/kuhyx/digital-wellbeing (~/src/digital-wellbeing) # 6. i3 configuration – i3/ # 7. Compulsive opening block – block_compulsive_opening.sh # diff --git a/linux_configuration/lib/extracted_repos.sh b/linux_configuration/lib/extracted_repos.sh index 67615ae3..a1909125 100755 --- a/linux_configuration/lib/extracted_repos.sh +++ b/linux_configuration/lib/extracted_repos.sh @@ -59,7 +59,7 @@ extracted_repo_dir() { # yourself" is not actionable -- there is nothing to clone it with yet. # # The clone runs as the HUMAN user, never as root. Installers re-exec themselves -# via sudo, and a root-owned ~/screen-locker silently breaks every later git +# via sudo, and a root-owned ~/src/screen-locker silently breaks every later git # operation the user makes in it. A sandbox never catches this (the guest runs # as root throughout), so the ownership is forced here rather than assumed. # diff --git a/linux_configuration/periodic_background/check_and_enable_services.sh b/linux_configuration/periodic_background/check_and_enable_services.sh index f23d825f..937fcadc 100755 --- a/linux_configuration/periodic_background/check_and_enable_services.sh +++ b/linux_configuration/periodic_background/check_and_enable_services.sh @@ -84,7 +84,7 @@ LEECHBLOCK_SCRIPT="$DW_REPO/install_leechblock.sh" REMOVE_GUEST_MODE_SCRIPT="$DW_REPO/remove_guest_mode.sh" VBOX_HOSTS_SCRIPT="$DW_REPO/virtualbox/enforce_vbox_hosts.sh" # screen-locker was EXTRACTED out of this monorepo into its own repo -# (github.com/kuhyx/screen-locker, checked out at ~/screen-locker), so these +# (github.com/kuhyx/screen-locker, checked out at ~/src/screen-locker), so these # paths deliberately live outside testsAndMisc. They used to point at # python_pkg/screen_locker/, which stopped existing at extraction time — the # result was check_workout_locker reporting a red "error" for a service that was diff --git a/linux_configuration/test_results.log b/linux_configuration/test_results.log index 708c7ffa..518f0159 100644 --- a/linux_configuration/test_results.log +++ b/linux_configuration/test_results.log @@ -2,7 +2,7 @@ Security Hardening Test Suite ========================================== -Testing components in: /home/kuhy/linux-configuration +Testing components in: /home/kuhy/src/testsAndMisc/linux_configuration --- HOSTS GUARD --- ✅ PASS: /etc/hosts is immutable diff --git a/linux_configuration/tests/conftest.py b/linux_configuration/tests/conftest.py index e69248a5..6075f1c3 100644 --- a/linux_configuration/tests/conftest.py +++ b/linux_configuration/tests/conftest.py @@ -24,6 +24,7 @@ _REPO_ROOT = Path(__file__).resolve().parents[2] _SCRIPT_DIRS = ( _REPO_ROOT / "meta" / "scripts", # validate_evidence, validate_contract _REPO_ROOT / "linux_configuration" / "utils", # fast_count + _REPO_ROOT / "linux_configuration" / "fixes", # g29_shifter_capture _REPO_ROOT / "linux_configuration" / "misc" diff --git a/linux_configuration/tests/lib/security_apps.sh b/linux_configuration/tests/lib/security_apps.sh index 5de6e6b8..e444057b 100755 --- a/linux_configuration/tests/lib/security_apps.sh +++ b/linux_configuration/tests/lib/security_apps.sh @@ -73,7 +73,7 @@ sec_tests_screen_locker() { echo "--- SCREEN LOCKER ---" # screen_locker was EXTRACTED to github.com/kuhyx/screen-locker. This used - # to hardcode $HOME/testsAndMisc/python_pkg/screen_locker/, which stopped + # to hardcode $HOME/src/testsAndMisc/python_pkg/screen_locker/, which stopped # existing -- so all five checks below silently SKIPPED, reporting a clean # run for tests that never executed. $HOME is also wrong under sudo; use the # shared resolver, which handles the sudo/systemd cases. diff --git a/linux_configuration/tests/test_session_autopsy_hooks.py b/linux_configuration/tests/test_session_autopsy_hooks.py index cbe30d2d..b8c452da 100644 --- a/linux_configuration/tests/test_session_autopsy_hooks.py +++ b/linux_configuration/tests/test_session_autopsy_hooks.py @@ -153,7 +153,7 @@ class TestSessionEndHookLaunchesIngest: assert "ingest" in argv assert str(transcript) in argv assert "--quiet" in argv - assert f"PYTHONPATH={home}/mcp-servers/session-autopsy" in argv + assert f"PYTHONPATH={home}/src/mcp-servers/session-autopsy" in argv assert "timeout 120" in argv def test_creates_the_autopsy_home(self, tmp_path: Path) -> None: diff --git a/linux_configuration/utils/check_brother_printer.sh b/linux_configuration/utils/check_brother_printer.sh index 17b3d028..4c110528 100755 --- a/linux_configuration/utils/check_brother_printer.sh +++ b/linux_configuration/utils/check_brother_printer.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Check Brother laser printer consumable/maintenance status. # Thin wrapper that ensures dependencies are present, then runs the -# Python implementation in ~/testsAndMisc/python_pkg/brother_printer. +# Python implementation in ~/src/testsAndMisc/python_pkg/brother_printer. # # Usage: # ./check_brother_printer.sh # auto-detect USB or network @@ -9,7 +9,7 @@ set -euo pipefail -PYTHON_PKG_DIR="${HOME}/testsAndMisc/python_pkg" +PYTHON_PKG_DIR="${HOME}/src/testsAndMisc/python_pkg" BROTHER_MODULE="brother_printer" # ── Ensure dependencies ───────────────────────────────────────────── diff --git a/linux_configuration/utils/find_keepassxc.sh b/linux_configuration/utils/find_keepassxc.sh index 192c0dea..ae33b45a 100755 --- a/linux_configuration/utils/find_keepassxc.sh +++ b/linux_configuration/utils/find_keepassxc.sh @@ -3,28 +3,28 @@ # Uses 'fd' for fast searching - install with: sudo pacman -S fd # # Usage: ./find_keepassxc.sh [destination_directory] -# Default destination: ~/Keepass +# Default destination: ~/data/Keepass set -euo pipefail # Source common library if available SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" if [[ -f "$SCRIPT_DIR/../lib/common.sh" ]]; then - # shellcheck source=../lib/common.sh - source "$SCRIPT_DIR/../lib/common.sh" + # shellcheck source=../lib/common.sh + source "$SCRIPT_DIR/../lib/common.sh" else - log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; } + log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; } fi # Configuration -DEST_DIR="${1:-$HOME/Keepass}" +DEST_DIR="${1:-$HOME/data/Keepass}" SEARCH_ROOT="/" TIMEOUT_SECONDS=30 # Ensure fd is installed -if ! command -v fd &> /dev/null; then - log "ERROR: 'fd' is not installed. Install with: sudo pacman -S fd" - exit 1 +if ! command -v fd &>/dev/null; then + log "ERROR: 'fd' is not installed. Install with: sudo pacman -S fd" + exit 1 fi # Create destination directory if it doesn't exist @@ -41,31 +41,31 @@ log "Searching for .kdbx files across the system (timeout: ${TIMEOUT_SECONDS}s). # Use timeout to ensure the search doesn't take too long # Exclude /proc, /sys, /dev, /run, /tmp, /var/cache, /var/tmp for speed FOUND_FILES=$(timeout "$TIMEOUT_SECONDS" fd \ - -e kdbx \ - -u \ - -a \ - --exclude '/proc' \ - --exclude '/sys' \ - --exclude '/dev' \ - --exclude '/run' \ - --exclude '/tmp' \ - --exclude '/var/cache' \ - --exclude '/var/tmp' \ - --exclude '/snap' \ - --exclude '/.snapshots' \ - --exclude '/lost+found' \ - . "$SEARCH_ROOT" 2> /dev/null || true) + -e kdbx \ + -u \ + -a \ + --exclude '/proc' \ + --exclude '/sys' \ + --exclude '/dev' \ + --exclude '/run' \ + --exclude '/tmp' \ + --exclude '/var/cache' \ + --exclude '/var/tmp' \ + --exclude '/snap' \ + --exclude '/.snapshots' \ + --exclude '/lost+found' \ + . "$SEARCH_ROOT" 2>/dev/null || true) if [[ -z $FOUND_FILES ]]; then - log "No .kdbx files found." - exit 0 + log "No .kdbx files found." + exit 0 fi # Count and display found files FILE_COUNT=$(echo "$FOUND_FILES" | wc -l) log "Found $FILE_COUNT .kdbx file(s):" echo "$FOUND_FILES" | while read -r file; do - echo " - $file" + echo " - $file" done # Move files to destination @@ -74,51 +74,51 @@ MOVED_COUNT=0 SKIPPED_COUNT=0 while IFS= read -r src_file; do - [[ -z $src_file ]] && continue + [[ -z $src_file ]] && continue - # Skip if file is already in destination - if [[ "$(dirname "$src_file")" == "$DEST_DIR" ]]; then - log "Skipping (already in destination): $src_file" - ((SKIPPED_COUNT++)) || true - continue - fi + # Skip if file is already in destination + if [[ "$(dirname "$src_file")" == "$DEST_DIR" ]]; then + log "Skipping (already in destination): $src_file" + ((SKIPPED_COUNT++)) || true + continue + fi - # Get the base filename - base_name=$(basename "$src_file") - dest_file="$DEST_DIR/$base_name" + # Get the base filename + base_name=$(basename "$src_file") + dest_file="$DEST_DIR/$base_name" - # Handle filename conflicts by adding a number suffix - if [[ -f $dest_file ]]; then - # Check if it's the exact same file (by content) - if cmp -s "$src_file" "$dest_file"; then - log "Skipping (identical file exists): $src_file" - # Remove the duplicate source file - rm -v "$src_file" - ((SKIPPED_COUNT++)) || true - continue - fi + # Handle filename conflicts by adding a number suffix + if [[ -f $dest_file ]]; then + # Check if it's the exact same file (by content) + if cmp -s "$src_file" "$dest_file"; then + log "Skipping (identical file exists): $src_file" + # Remove the duplicate source file + rm -v "$src_file" + ((SKIPPED_COUNT++)) || true + continue + fi - # Different file with same name - add suffix - counter=1 - name_without_ext="${base_name%.kdbx}" - while [[ -f $dest_file ]]; do - dest_file="$DEST_DIR/${name_without_ext} ($counter).kdbx" - ((counter++)) - done - log "Renaming to avoid conflict: $base_name -> $(basename "$dest_file")" - fi + # Different file with same name - add suffix + counter=1 + name_without_ext="${base_name%.kdbx}" + while [[ -f $dest_file ]]; do + dest_file="$DEST_DIR/${name_without_ext} ($counter).kdbx" + ((counter++)) + done + log "Renaming to avoid conflict: $base_name -> $(basename "$dest_file")" + fi - # Move the file - if mv -v "$src_file" "$dest_file"; then - ((MOVED_COUNT++)) || true - else - log "ERROR: Failed to move $src_file" - fi -done <<< "$FOUND_FILES" + # Move the file + if mv -v "$src_file" "$dest_file"; then + ((MOVED_COUNT++)) || true + else + log "ERROR: Failed to move $src_file" + fi +done <<<"$FOUND_FILES" log "Done! Moved $MOVED_COUNT file(s), skipped $SKIPPED_COUNT file(s)." log "All KeePassXC databases are now in: $DEST_DIR" # List final contents log "Contents of $DEST_DIR:" -ls -la "$DEST_DIR"/*.kdbx 2> /dev/null || log "No .kdbx files in destination" +ls -la "$DEST_DIR"/*.kdbx 2>/dev/null || log "No .kdbx files in destination" diff --git a/linux_configuration/utils/sync_keepassxc.sh b/linux_configuration/utils/sync_keepassxc.sh index 547a8557..57cef7c3 100755 --- a/linux_configuration/utils/sync_keepassxc.sh +++ b/linux_configuration/utils/sync_keepassxc.sh @@ -7,52 +7,52 @@ # Make sure all databases use the same master password, or know each password. # # Usage: ./sync_keepassxc.sh [directory] -# Default directory: ~/Keepass +# Default directory: ~/data/Keepass set -euo pipefail # Source common library if available SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" if [[ -f "$SCRIPT_DIR/../lib/common.sh" ]]; then - # shellcheck source=../lib/common.sh - source "$SCRIPT_DIR/../lib/common.sh" + # shellcheck source=../lib/common.sh + source "$SCRIPT_DIR/../lib/common.sh" else - log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; } + log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; } fi # Configuration -KEEPASS_DIR="${1:-$HOME/Keepass}" +KEEPASS_DIR="${1:-$HOME/data/Keepass}" BACKUP_DIR="$KEEPASS_DIR/.backup_$(date +%Y%m%d_%H%M%S)" # Ensure keepassxc-cli is installed -if ! command -v keepassxc-cli &> /dev/null; then - log "ERROR: 'keepassxc-cli' is not installed. Install with: sudo pacman -S keepassxc" - exit 1 +if ! command -v keepassxc-cli &>/dev/null; then + log "ERROR: 'keepassxc-cli' is not installed. Install with: sudo pacman -S keepassxc" + exit 1 fi # Check if directory exists if [[ ! -d $KEEPASS_DIR ]]; then - log "ERROR: Directory does not exist: $KEEPASS_DIR" - exit 1 + log "ERROR: Directory does not exist: $KEEPASS_DIR" + exit 1 fi # Find all .kdbx files mapfile -t KDBX_FILES < <(find "$KEEPASS_DIR" -maxdepth 1 -name "*.kdbx" -type f | sort) if [[ ${#KDBX_FILES[@]} -eq 0 ]]; then - log "No .kdbx files found in $KEEPASS_DIR" - exit 0 + log "No .kdbx files found in $KEEPASS_DIR" + exit 0 fi if [[ ${#KDBX_FILES[@]} -eq 1 ]]; then - log "Only one .kdbx file found. Nothing to merge." - log "File: ${KDBX_FILES[0]}" - exit 0 + log "Only one .kdbx file found. Nothing to merge." + log "File: ${KDBX_FILES[0]}" + exit 0 fi log "Found ${#KDBX_FILES[@]} .kdbx files in $KEEPASS_DIR:" for f in "${KDBX_FILES[@]}"; do - echo " - $(basename "$f")" + echo " - $(basename "$f")" done # Create backup directory @@ -61,7 +61,7 @@ log "Creating backups in: $BACKUP_DIR" # Backup all files before any operation for f in "${KDBX_FILES[@]}"; do - cp -v "$f" "$BACKUP_DIR/" + cp -v "$f" "$BACKUP_DIR/" done log "All files backed up successfully." @@ -75,8 +75,8 @@ echo "==============================================" echo "" read -rp "Do you want to continue? (yes/no): " CONFIRM if [[ $CONFIRM != "yes" ]]; then - log "Aborted by user." - exit 1 + log "Aborted by user." + exit 1 fi # Select the target database (the one to merge INTO) @@ -93,9 +93,9 @@ read -rsp "Enter master password for TARGET database ($(basename "$TARGET_DB")): echo "" # Verify target password works -if ! echo "$TARGET_PASSWORD" | keepassxc-cli ls "$TARGET_DB" &> /dev/null; then - log "ERROR: Failed to open target database. Wrong password?" - exit 1 +if ! echo "$TARGET_PASSWORD" | keepassxc-cli ls "$TARGET_DB" &>/dev/null; then + log "ERROR: Failed to open target database. Wrong password?" + exit 1 fi log "Target database password verified." @@ -107,41 +107,41 @@ SAME_PASSWORD="${SAME_PASSWORD,,}" # lowercase # Merge each source database into the target MERGE_COUNT=0 for ((i = 1; i < ${#KDBX_FILES[@]}; i++)); do - SOURCE_DB="${KDBX_FILES[$i]}" - log "" - log "Merging $(basename "$SOURCE_DB") into $(basename "$TARGET_DB")..." + SOURCE_DB="${KDBX_FILES[$i]}" + log "" + log "Merging $(basename "$SOURCE_DB") into $(basename "$TARGET_DB")..." - # Reuse target password if user confirmed all are the same - if [[ $SAME_PASSWORD == "y" || $SAME_PASSWORD == "yes" ]]; then - SOURCE_PASSWORD="$TARGET_PASSWORD" - else - # Ask for source password (might be different) - echo "" - read -rsp "Enter master password for SOURCE database ($(basename "$SOURCE_DB")): " SOURCE_PASSWORD - echo "" - fi + # Reuse target password if user confirmed all are the same + if [[ $SAME_PASSWORD == "y" || $SAME_PASSWORD == "yes" ]]; then + SOURCE_PASSWORD="$TARGET_PASSWORD" + else + # Ask for source password (might be different) + echo "" + read -rsp "Enter master password for SOURCE database ($(basename "$SOURCE_DB")): " SOURCE_PASSWORD + echo "" + fi - # Verify source password - if ! echo "$SOURCE_PASSWORD" | keepassxc-cli ls "$SOURCE_DB" &> /dev/null; then - log "ERROR: Failed to open source database $(basename "$SOURCE_DB"). Wrong password?" - log "Skipping this database. You can try again later." - continue - fi + # Verify source password + if ! echo "$SOURCE_PASSWORD" | keepassxc-cli ls "$SOURCE_DB" &>/dev/null; then + log "ERROR: Failed to open source database $(basename "$SOURCE_DB"). Wrong password?" + log "Skipping this database. You can try again later." + continue + fi - # Perform the merge - # keepassxc-cli merge requires: target_db source_db - # It will prompt for passwords - if echo -e "${TARGET_PASSWORD}\n${SOURCE_PASSWORD}" | keepassxc-cli merge "$TARGET_DB" "$SOURCE_DB"; then - log "Successfully merged $(basename "$SOURCE_DB")" + # Perform the merge + # keepassxc-cli merge requires: target_db source_db + # It will prompt for passwords + if echo -e "${TARGET_PASSWORD}\n${SOURCE_PASSWORD}" | keepassxc-cli merge "$TARGET_DB" "$SOURCE_DB"; then + log "Successfully merged $(basename "$SOURCE_DB")" - # Delete the source database after successful merge - log "Deleting source database: $(basename "$SOURCE_DB")" - rm -v "$SOURCE_DB" - ((MERGE_COUNT++)) || true - else - log "ERROR: Failed to merge $(basename "$SOURCE_DB")" - log "Source database NOT deleted. Check the backup and try manually." - fi + # Delete the source database after successful merge + log "Deleting source database: $(basename "$SOURCE_DB")" + rm -v "$SOURCE_DB" + ((MERGE_COUNT++)) || true + else + log "ERROR: Failed to merge $(basename "$SOURCE_DB")" + log "Source database NOT deleted. Check the backup and try manually." + fi done echo "" @@ -159,15 +159,15 @@ find "$KEEPASS_DIR" -maxdepth 1 -name "*.kdbx" -type f -exec basename {} \; # Rename to clean name if desired FINAL_COUNT=$(find "$KEEPASS_DIR" -maxdepth 1 -name "*.kdbx" -type f | wc -l) if [[ $FINAL_COUNT -eq 1 ]]; then - log "" - FINAL_NAME="$KEEPASS_DIR/Passwords.kdbx" - if [[ $TARGET_DB != "$FINAL_NAME" ]]; then - read -rp "Rename final database to 'Passwords.kdbx'? (y/n): " RENAME_CONFIRM - if [[ $RENAME_CONFIRM == "y" ]]; then - mv -v "$TARGET_DB" "$FINAL_NAME" - log "Final database: $FINAL_NAME" - fi - fi - log "" - log "SUCCESS: You now have exactly ONE KeePassXC database!" + log "" + FINAL_NAME="$KEEPASS_DIR/Passwords.kdbx" + if [[ $TARGET_DB != "$FINAL_NAME" ]]; then + read -rp "Rename final database to 'Passwords.kdbx'? (y/n): " RENAME_CONFIRM + if [[ $RENAME_CONFIRM == "y" ]]; then + mv -v "$TARGET_DB" "$FINAL_NAME" + log "Final database: $FINAL_NAME" + fi + fi + log "" + log "SUCCESS: You now have exactly ONE KeePassXC database!" fi diff --git a/linux_configuration/zsh/token-savers.zsh b/linux_configuration/zsh/token-savers.zsh index 525b782d..c4e9eef9 100644 --- a/linux_configuration/zsh/token-savers.zsh +++ b/linux_configuration/zsh/token-savers.zsh @@ -15,7 +15,7 @@ phone-deploy() { bash "$HOME/.claude/scripts/phone_deploy.sh" "${1:-.}" "${@:2}" # autopsy [subcommand ...] — the transcript analyzer; defaults to `report`. # Try: autopsy candidates | autopsy measure | autopsy report --mark-reviewed -autopsy() { PYTHONPATH="$HOME/testsAndMisc" python3 -m python_pkg.session_autopsy "${1:-report}" "${@:2}"; } +autopsy() { PYTHONPATH="$HOME/src/testsAndMisc" python3 -m python_pkg.session_autopsy "${1:-report}" "${@:2}"; } # yay-clean [--dry-run] ...|--all — remove yay cache dirs plain rm chokes # on (read-only fakeroot/Bazel trees). The cache was 314GB on 2026-07-24. diff --git a/meta/.fvmrc b/meta/.fvmrc index 16a96ef4..d7f4b8c6 100644 --- a/meta/.fvmrc +++ b/meta/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.47.2" + "flutter": "3.47.3" } diff --git a/meta/pyproject.toml b/meta/pyproject.toml index 075987c1..7c218ea9 100644 --- a/meta/pyproject.toml +++ b/meta/pyproject.toml @@ -173,7 +173,7 @@ disallow_untyped_decorators = false # bare name (the same dirs linux_configuration/tests/conftest.py adds to # sys.path at runtime) resolve under static analysis instead of raising E0401. # Paths are relative to the repo root, which is pre-commit's working directory. -init-hook = "import sys; sys.path[:0] = ['meta/scripts', 'python_pkg', 'linux_configuration/utils', 'linux_configuration/misc/tools']" +init-hook = "import sys; sys.path[:0] = ['meta/scripts', 'python_pkg', 'linux_configuration/utils', 'linux_configuration/fixes', 'linux_configuration/misc/tools']" # Analyse import fallback blocks analyse-fallback-blocks = true # Pickle collected data for later comparisons diff --git a/meta/requirements-test.txt b/meta/requirements-test.txt index 20639928..9f7efede 100644 --- a/meta/requirements-test.txt +++ b/meta/requirements-test.txt @@ -21,16 +21,16 @@ # suite itself fails loudly here on a missing import. # ============================================================================== -anthropic==1.2.0 +anthropic==1.5.0 beautifulsoup4==4.15.0 coverage==7.16.0 defusedxml==0.7.1 markdownify==1.2.3 -numpy==2.5.2 +numpy==2.5.3 pillow==12.3.0 pytest==9.1.1 pytest-cov==7.1.0 -pytest-randomly==4.1.0 +pytest-randomly==5.0.0 pytest-sugar==1.1.1 pytest-timeout==2.4.0 pytest-xdist==3.8.0 diff --git a/meta/requirements.txt b/meta/requirements.txt index 1c3a718c..c5214059 100644 --- a/meta/requirements.txt +++ b/meta/requirements.txt @@ -6,7 +6,7 @@ add-trailing-comma==4.0.0 aiohttp==3.14.3 -anthropic==1.2.0 +anthropic==1.5.0 autoflake==2.4.0 autopep8==2.3.2 bandit==1.9.4 @@ -24,7 +24,7 @@ fixit==2.2.1 flake8==7.3.0 flake8-annotations==3.2.0 flake8-bandit==4.1.1 -flake8-bugbear==25.11.29 +flake8-bugbear==26.9.9 flake8-comprehensions==3.17.0 flake8-docstrings==1.7.0 flake8-eradicate==1.5.0 @@ -38,23 +38,23 @@ gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.7.3#subdirectory=gatel genanki==0.13.1 geopandas==1.1.4 howlongtobeatpy==1.0.23 -hypothesis==6.167.0 +hypothesis==6.168.0 importlib-metadata==9.0.1 interrogate==1.7.0 # isort is deliberately not pinned: nothing here imports it (ruff's isort # rules replaced it) and pylint constrains it (`isort!=5.13,<10,>=5` as of # 4.0.8), so pinning it independently risks an unsolvable environment for a # package no code here imports. pylint brings a compatible one. -lxml==6.1.2 +lxml==6.1.3 markdownify==1.2.3 matplotlib==3.11.1 mccabe==0.7.0 mypy==2.3.1 -numpy==2.5.2 +numpy==2.5.3 opencv-python==5.0.0.93 pillow==12.3.0 pip-audit==2.10.1 -pipdeptree==4.2.2 +pipdeptree==4.2.5 pre-commit==4.6.2 prospector==1.19.1 pycodestyle==2.14.0 @@ -62,10 +62,10 @@ pydocstyle==6.3.0 pyflakes==3.4.0 pygame==2.6.1 pylint==4.0.8 -pyright==1.1.411 +pyright==1.1.414 pytest==9.1.1 pytest-cov==7.1.0 -pytest-randomly==4.1.0 +pytest-randomly==5.0.0 pytest-sugar==1.1.1 pytest-timeout==2.4.0 pytest-xdist==3.8.0 @@ -76,15 +76,15 @@ radon==6.0.1 reorder-python-imports==3.17.0 requests==2.34.2 rich==15.0.0 -ruff==0.16.5 +ruff==0.16.7 # safety is deliberately not pinned: its only reference in this repo is a # commented-out pre-commit hook, and safety 3.8.1 declares `typer<0.26`, # which would hold the typer this repo actually imports below latest. -selenium==4.48.0 +selenium==4.49.0 typer==0.27.2 types-python-dateutil==2.9.0.20260807 -types-PyYAML==6.0.12.20260815 -types-requests==2.33.0.20260712 +types-PyYAML==6.0.12.20260906 +types-requests==2.33.0.20260906 types-setuptools==84.0.0.20260812 typing_extensions==4.16.0 # Self annotation on py310 target (session_autopsy store lock) websockets==17.1 diff --git a/meta/scripts/disk_cleanup_check.sh b/meta/scripts/disk_cleanup_check.sh index ff201d31..1dbf587c 100755 --- a/meta/scripts/disk_cleanup_check.sh +++ b/meta/scripts/disk_cleanup_check.sh @@ -206,7 +206,7 @@ if ((journal_kb > 204800)); then fi # 19. AUR source dirs — old archives (>30 days) -aur_dir="$HOME/aur" +aur_dir="$HOME/sdk/aur" if [[ -d "$aur_dir" ]]; then old_archives_kb=$(find "$aur_dir" \ \( -name "*.pkg.tar.zst" -o -name "*.tar.gz" -o -name "*.tar.xz" \ @@ -215,7 +215,7 @@ if [[ -d "$aur_dir" ]]; then awk '{t+=$1} END{print t+0}') if ((old_archives_kb > 0)); then report "AUR old archives (>30d)" "$old_archives_kb" \ - "find ~/aur '*.pkg.tar.zst' -mtime +30 -delete" + "find ~/sdk/aur '*.pkg.tar.zst' -mtime +30 -delete" if confirm "Delete old AUR archives (>30 days)?"; then find "$aur_dir" \ \( -name "*.pkg.tar.zst" -o -name "*.tar.gz" -o -name "*.tar.xz" \ diff --git a/meta/scripts/lib/disk_cleanup_common.sh b/meta/scripts/lib/disk_cleanup_common.sh index b89f71fe..d24d5cc2 100755 --- a/meta/scripts/lib/disk_cleanup_common.sh +++ b/meta/scripts/lib/disk_cleanup_common.sh @@ -83,7 +83,7 @@ print_report_only() { for manual_entry in \ "$HOME/Downloads/too_big:~/Downloads/too_big" \ "$HOME/Downloads:~/Downloads total" \ - "$HOME/inne:~/inne" \ + "$HOME/media/inne:~/media/inne" \ "/Games:/Games — still playing?"; do dir="${manual_entry%%:*}" label="${manual_entry#*:}" diff --git a/meta/scripts/report_live_state.sh b/meta/scripts/report_live_state.sh index b3483e9e..e0c1f162 100755 --- a/meta/scripts/report_live_state.sh +++ b/meta/scripts/report_live_state.sh @@ -19,7 +19,7 @@ set -euo pipefail SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_NAME -readonly REPO="${REPO:-$HOME/testsAndMisc}" +readonly REPO="${REPO:-$HOME/src/testsAndMisc}" CHECK_ONLY=0 @@ -40,8 +40,8 @@ report_systemd() { [[ -n "$unit" ]] || continue found=1 printf ' %s\n' "$unit" - grep -n "$REPO\|%h/testsAndMisc" "$unit" | sed 's/^/ /' - done < <(grep -rl "$REPO\|%h/testsAndMisc" \ + grep -n "$REPO\|%h/src/testsAndMisc" "$unit" | sed 's/^/ /' + done < <(grep -rl "$REPO\|%h/src/testsAndMisc" \ /etc/systemd/system "$HOME/.config/systemd/user" 2>/dev/null | sort) ((found)) || echo " (none)" return "$found" diff --git a/python_pkg/android_ui/README.md b/python_pkg/android_ui/README.md index bfe69012..5f292e92 100644 --- a/python_pkg/android_ui/README.md +++ b/python_pkg/android_ui/README.md @@ -1,7 +1,7 @@ # android_ui — drive Android apps by element, never by coordinates -Shared across every repo with an Android surface (`~/screen-locker`'s -workout_app, `~/todo`, `~/dufs-cloud/app`). Works against a plain release APK +Shared across every repo with an Android surface (`~/src/screen-locker`'s +workout_app, `~/src/todo`, `~/src/dufs-cloud/app`). Works against a plain release APK on a physical device over `adb` — no root, no emulator, no Flutter debug connection, no change to app source. @@ -31,7 +31,7 @@ false results before they were understood: ## Use ```bash -export PYTHONPATH=~/testsAndMisc +export PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.android_ui dump python3 -m python_pkg.android_ui find "Connect Firebase" python3 -m python_pkg.android_ui --exact tap "Back" diff --git a/python_pkg/android_ui/__main__.py b/python_pkg/android_ui/__main__.py index 05175462..c99326b8 100644 --- a/python_pkg/android_ui/__main__.py +++ b/python_pkg/android_ui/__main__.py @@ -1,7 +1,7 @@ """Module entry point for the Android UI driver. Usage: - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.android_ui --help + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.android_ui --help """ from __future__ import annotations diff --git a/python_pkg/app_icons/README.md b/python_pkg/app_icons/README.md index d701cda2..29800e3a 100644 --- a/python_pkg/app_icons/README.md +++ b/python_pkg/app_icons/README.md @@ -8,17 +8,17 @@ drawn at one stroke weight and optically centred so a row of them lines up. ```bash # what is registered -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons list +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons list # contact sheet for visual review: full size / circle-masked / 48dp -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons preview -o /tmp/sheet.png +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons preview -o /tmp/sheet.png # write assets into every app repo and rebuild the Android mipmaps -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --android +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --android # one app, plus the hicolor PNGs for a Linux desktop target -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons \ - generate --app todo --android --linux-out ~/todo/linux/icons +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons \ + generate --app todo --android --linux-out ~/src/todo/linux/icons ``` ## What gets written @@ -58,7 +58,7 @@ review the contact sheet, then verify on the phone. ## Tests ```bash -cd ~/testsAndMisc +cd ~/src/testsAndMisc python3 -m pytest python_pkg/app_icons --cov=python_pkg.app_icons \ --cov-report=term-missing ``` diff --git a/python_pkg/app_icons/__main__.py b/python_pkg/app_icons/__main__.py index 683518e8..a469eb61 100644 --- a/python_pkg/app_icons/__main__.py +++ b/python_pkg/app_icons/__main__.py @@ -1,7 +1,7 @@ """Module entry point for the shared app-icon generator. Usage: - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons --help + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons --help """ from __future__ import annotations diff --git a/python_pkg/app_icons/_glyph_art_more.py b/python_pkg/app_icons/_glyph_art_more.py index e229978c..1391434a 100644 --- a/python_pkg/app_icons/_glyph_art_more.py +++ b/python_pkg/app_icons/_glyph_art_more.py @@ -130,3 +130,47 @@ _BOWL_STAR = """\ L 430 493 L 459 397 L 379 337 L 479 335 Z" fill="{{ACCENT}}" stroke="none"/> """ + + +# A handset under a broadcast arc: the bot's control panel, reached from +# somewhere else. The arc is what makes it "remote" rather than "settings", +# and it is detached from the body on purpose -- an arc that *meets* the body +# is a padlock shackle, and padlock-closed is already in this family. +# +# Filled body with punched-out buttons, not a stroked outline, for the reason +# the nib and the anvil are filled: a 200-wide stroked body leaves a 56px +# interior at STROKE_WIDTH=72, which fills in solid at 48dp. +# +# Geometry, all inside SAFE_BOX (232..792) and clear of MIN_NEGATIVE_SPACE=36: +# body x=412..612, y=440..792, corner radius 56 +# buttons r=40 at (512,540) and (512,680). Edge gap between them 60; to the +# body's top edge 60, to its bottom edge 72, to each side wall 60 +# arc r=132 about the body's top centre (512,440), drawn between its +# 135 and 45 degree points (419,347)-(605,347) +# +# The arc's *lowest* ink is its endpoints at y=347 plus half the stroke = 383, +# which is 57 clear of the body at 440 -- not the arc's centre, which is +# inside the body. Total ink y=272..792 and x=383..641, both within the box. +_REMOTE_WAVE = """\ + + """ + + +# House with a tick inside: the home, cleared. A stroked house outline rather +# than a broom or sparkle -- a broom reads as a paintbrush at 48dp, and the +# family already leans on "check" for done-ness (see checklist). +# +# Geometry, all inside SAFE_BOX (232..792) and clear of MIN_NEGATIVE_SPACE=36: +# house apex (512,280), eaves y=480 at x=272/752, walls down to y=752. +# Apex ink with the round join reaches ~y=244, inside the box. +# tick (400,560)-(480,640)-(630,500). Bottom ink 676 vs floor ink 716 +# -> 40 gap; right ink 666 vs wall ink 716 -> 50; left ink 364 vs +# wall ink 308 -> 56; top ink 464 vs the roof line at x=630 +# (y=363, ink to 399) -> 65. +_HOUSE_TICK = """\ + + """ diff --git a/python_pkg/app_icons/apps.py b/python_pkg/app_icons/apps.py index 81fd84d6..87393765 100644 --- a/python_pkg/app_icons/apps.py +++ b/python_pkg/app_icons/apps.py @@ -51,7 +51,7 @@ APPS: Final[dict[str, AppIcon]] = { # The live app: ~/dufs_client is a stale, remote-less checkout that # was superseded when dufs-cloud absorbed it (see testsAndMisc # CLAUDE.md). Same package name, so it is easy to icon the wrong one. - repo=_HOME / "dufs-cloud" / "app", + repo=_HOME / "src/dufs-cloud" / "app", accent="#B8862E", glyph="cloud-down", icon_name="dufs-client", @@ -61,7 +61,7 @@ APPS: Final[dict[str, AppIcon]] = { key="focus_owner", # Absorbed into the phone-focus-mode monorepo on 2026-08-24; the # standalone ~/focus-owner clone is gone, as is its GitHub repo. - repo=_HOME / "phone-focus-mode" / "focus-owner", + repo=_HOME / "src/phone-focus-mode" / "focus-owner", accent="#B8862E", glyph="padlock-closed", icon_name="focus-owner", @@ -69,7 +69,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="workout_app", - repo=_HOME / "screen-locker" / "stronglift_replacement" / "workout_app", + repo=_HOME / "src/screen-locker" / "stronglift_replacement" / "workout_app", accent="#B8862E", glyph="barbell", icon_name="workout-app", @@ -77,7 +77,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="wake_alarm_sync", - repo=_HOME / "wake-alarm" / "phone_app", + repo=_HOME / "src/wake-alarm" / "phone_app", accent="#B8862E", glyph="clock", icon_name="wake-alarm-sync", @@ -85,7 +85,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="diet_guard_app", - repo=_HOME / "diet-guard" / "app", + repo=_HOME / "src/diet-guard" / "app", accent="#B8862E", glyph="shield-cutlery", icon_name="diet-guard-app", @@ -93,7 +93,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="todo", - repo=_HOME / "todo", + repo=_HOME / "src/todo", accent="#B8862E", glyph="checklist", icon_name="todo", @@ -101,7 +101,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="untools", - repo=_HOME / "untools", + repo=_HOME / "src/untools", accent="#B8862E", glyph="decision-tree", icon_name="untools", @@ -111,7 +111,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="punchme", - repo=_HOME / "punchme", + repo=_HOME / "src/punchme", accent="#B8862E", glyph="punch-card", icon_name="punchme", @@ -120,7 +120,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="restaurant_rater", - repo=_HOME / "restaurant-rater", + repo=_HOME / "src/restaurant-rater", accent="#B8862E", glyph="bowl-star", icon_name="restaurant-rater", @@ -129,7 +129,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="habit_stack", - repo=_HOME / "habit_stack", + repo=_HOME / "src/habit_stack", accent="#B8862E", glyph="chain-link", icon_name="habit-stack", @@ -137,7 +137,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="home_inventory", - repo=_HOME / "home_inventory", + repo=_HOME / "src/home_inventory", accent="#B8862E", glyph="storage-box", icon_name="home-inventory", @@ -145,7 +145,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="epopeja_karta", - repo=_HOME / "epopeja_karta", + repo=_HOME / "src/epopeja_karta", accent="#B8862E", glyph="quill-nib", icon_name="epopeja-karta", @@ -158,7 +158,7 @@ APPS: Final[dict[str, AppIcon]] = { key="octoforge", # The Flutter app is a subdirectory of the repo: the repo root # holds the pure-Dart core and the tooling alongside it. - repo=_HOME / "octoforge" / "octoforge" / "app", + repo=_HOME / "src/octoforge" / "app", accent="#B8862E", glyph="anvil", icon_name="octoforge", @@ -168,7 +168,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="kuhylog", - repo=_HOME / "kuhylog" / "kuhylog", + repo=_HOME / "src/kuhylog", accent="#B8862E", glyph="track-bars", icon_name="kuhylog", @@ -179,7 +179,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="lyricanki", - repo=_HOME / "lyricanki", + repo=_HOME / "src/lyricanki", accent="#B8862E", glyph="note-ascender", icon_name="lyricanki", @@ -189,7 +189,7 @@ APPS: Final[dict[str, AppIcon]] = { ), AppIcon( key="betting_sim", - repo=_HOME / "betting-sim" / "app", + repo=_HOME / "src/betting-sim" / "app", accent="#B8862E", glyph="track-bars", icon_name="betting_sim", @@ -198,6 +198,27 @@ APPS: Final[dict[str, AppIcon]] = { # would go unread. linux=False, ), + AppIcon( + key="home_guard_app", + repo=_HOME / "src/home-guard" / "app", + accent="#B8862E", + glyph="house-tick", + icon_name="home-guard", + linux=False, + ), + AppIcon( + key="signalbot_remote", + # Inside the bot's own repository, not beside it: the app is a + # client of that repo's management API, and kuhy's call was that a + # route change and the client change land in one commit. + repo=_HOME / "src/signal-bot" / "app", + accent="#B8862E", + glyph="remote-wave", + icon_name="signalbot_remote", + # Android only. The bot's desktop control is the TypeScript page + # it already has, served on loopback. + linux=False, + ), ) } diff --git a/python_pkg/app_icons/cli.py b/python_pkg/app_icons/cli.py index 2cf66e31..9fa82ec4 100644 --- a/python_pkg/app_icons/cli.py +++ b/python_pkg/app_icons/cli.py @@ -1,9 +1,9 @@ """Command-line entry point for the shared app-icon generator. Usage: - PYTHONPATH=~/testsAndMisc python -m python_pkg.app_icons list - PYTHONPATH=~/testsAndMisc python -m python_pkg.app_icons preview -o sheet.png - PYTHONPATH=~/testsAndMisc python -m python_pkg.app_icons generate --app todo + PYTHONPATH=~/src/testsAndMisc python -m python_pkg.app_icons list + PYTHONPATH=~/src/testsAndMisc python -m python_pkg.app_icons preview -o sheet.png + PYTHONPATH=~/src/testsAndMisc python -m python_pkg.app_icons generate --app todo """ from __future__ import annotations diff --git a/python_pkg/app_icons/glyphs.py b/python_pkg/app_icons/glyphs.py index 2ea93b3d..bb218d6b 100644 --- a/python_pkg/app_icons/glyphs.py +++ b/python_pkg/app_icons/glyphs.py @@ -31,9 +31,11 @@ from python_pkg.app_icons._glyph_art import ( ) from python_pkg.app_icons._glyph_art_more import ( _BOWL_STAR, + _HOUSE_TICK, _NOTE_ASCENDER, _PADLOCK_CLOSED, _PUNCH_CARD, + _REMOTE_WAVE, _TRACK_BARS, ) @@ -87,6 +89,12 @@ GLYPHS: Final[dict[str, Glyph]] = { _PADLOCK_CLOSED, ), Glyph("bowl-star", "Filled star above an open bowl", _BOWL_STAR), + Glyph("house-tick", "House outline with a tick inside", _HOUSE_TICK), + Glyph( + "remote-wave", + "Handset with punched buttons under a broadcast arc", + _REMOTE_WAVE, + ), ) } diff --git a/python_pkg/archwiki_rag/README.md b/python_pkg/archwiki_rag/README.md index 80d1c28f..12db2ea7 100644 --- a/python_pkg/archwiki_rag/README.md +++ b/python_pkg/archwiki_rag/README.md @@ -22,7 +22,7 @@ search. ./install.sh # Manual refresh. -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.archwiki_rag sync --reindex +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.archwiki_rag sync --reindex # Query it. claude-archwiki diff --git a/python_pkg/archwiki_rag/__main__.py b/python_pkg/archwiki_rag/__main__.py index be3bb5ab..70993c87 100644 --- a/python_pkg/archwiki_rag/__main__.py +++ b/python_pkg/archwiki_rag/__main__.py @@ -1,7 +1,7 @@ """Module entry point for the offline Arch Wiki RAG corpus builder. Usage: - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.archwiki_rag --help + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.archwiki_rag --help """ from __future__ import annotations diff --git a/python_pkg/archwiki_rag/cli.py b/python_pkg/archwiki_rag/cli.py index 34c9d767..95380317 100644 --- a/python_pkg/archwiki_rag/cli.py +++ b/python_pkg/archwiki_rag/cli.py @@ -1,8 +1,8 @@ """Command-line entry point for the offline Arch Wiki RAG corpus. Usage: - PYTHONPATH=~/testsAndMisc python -m python_pkg.archwiki_rag sync - PYTHONPATH=~/testsAndMisc python -m python_pkg.archwiki_rag sync --reindex + PYTHONPATH=~/src/testsAndMisc python -m python_pkg.archwiki_rag sync + PYTHONPATH=~/src/testsAndMisc python -m python_pkg.archwiki_rag sync --reindex """ from __future__ import annotations diff --git a/python_pkg/artgate/styles/pixel_icon.toml b/python_pkg/artgate/styles/pixel_icon.toml index fea3ba04..ec041d68 100644 --- a/python_pkg/artgate/styles/pixel_icon.toml +++ b/python_pkg/artgate/styles/pixel_icon.toml @@ -2,7 +2,7 @@ # "AI slop" is most detectable. # # CALIBRATION (measured, not guessed) over 250 genuine 32x32 CC0 icons from -# ~/opengameart-downloads: +# ~/data/opengameart-downloads: # p50=7 p75=10 p90=13 p95=14 p99=26 max=30 # Rejection rate of real hand-made art by cap: # cap 16 -> 3.6% cap 24 -> 1.2% cap 32 -> 0.0% diff --git a/python_pkg/artgate/styles/pixel_icon_64.toml b/python_pkg/artgate/styles/pixel_icon_64.toml index c5a1d2f7..87d81ece 100644 --- a/python_pkg/artgate/styles/pixel_icon_64.toml +++ b/python_pkg/artgate/styles/pixel_icon_64.toml @@ -6,7 +6,7 @@ # a lantern's glass panes all mush at 32px and read clearly at 64px. # # CALIBRATION. `max_colors` is deliberately NOT raised from pixel_icon's 32. -# Measured over the 300 genuine 48x48 icons in ~/opengameart-downloads -- the +# Measured over the 300 genuine 48x48 icons in ~/data/opengameart-downloads -- the # largest real-art sample available locally above 32px: # p50=9 p75=11 p90=13 p95=13 p99=14 max=14 # Rejection rate of that real art: cap 32 -> 0.00%, and every larger cap also diff --git a/python_pkg/endurain_import/__main__.py b/python_pkg/endurain_import/__main__.py index 1d9e4a1f..5ccb9860 100644 --- a/python_pkg/endurain_import/__main__.py +++ b/python_pkg/endurain_import/__main__.py @@ -4,7 +4,7 @@ Run from a systemd user timer. Configuration comes from the environment: ENDURAIN_URL base URL (default http://127.0.0.1:8085) ENDURAIN_API_KEY API key with activities:upload (required) - ENDURAIN_INBOX WebDAV inbox (default ~/cloud/RunnerUp) + ENDURAIN_INBOX WebDAV inbox (default ~/data/cloud/RunnerUp) ENDURAIN_STATE ledger directory (default ~/.local/state/endurain-import) ENDURAIN_BULK_DIR bulk_import dir for files the server rejected ENDURAIN_NO_ADB set to 1 to disable the phone fallback @@ -59,7 +59,7 @@ if TYPE_CHECKING: _logger = logging.getLogger("endurain_import") _DEFAULT_URL = "http://127.0.0.1:8085" -_DEFAULT_INBOX = Path.home() / "cloud" / "RunnerUp" +_DEFAULT_INBOX = Path.home() / "data/cloud" / "RunnerUp" _DEFAULT_STATE = Path.home() / ".local" / "state" / "endurain-import" _DEFAULT_USER_ID = 1 diff --git a/python_pkg/morning_routine/morning-routine.service b/python_pkg/morning_routine/morning-routine.service index d894d53a..e3238838 100644 --- a/python_pkg/morning_routine/morning-routine.service +++ b/python_pkg/morning_routine/morning-routine.service @@ -10,10 +10,10 @@ Type=simple # DISPLAY/PYTHONPATH mirror workout-locker.service so Tk can open the X server; # the short sleep lets the session export DISPLAY first. Environment=DISPLAY=:0 -Environment=PYTHONPATH=%h/testsAndMisc +Environment=PYTHONPATH=%h/src/testsAndMisc ExecStartPre=/bin/sleep 1 ExecStart=/usr/bin/python -m python_pkg.morning_routine._orchestrator --production -WorkingDirectory=%h/testsAndMisc +WorkingDirectory=%h/src/testsAndMisc Restart=on-failure RestartSec=2s RestartPreventExitStatus=0 diff --git a/python_pkg/syncyomi_guard/__main__.py b/python_pkg/syncyomi_guard/__main__.py index 6c5e8312..e17b0df6 100644 --- a/python_pkg/syncyomi_guard/__main__.py +++ b/python_pkg/syncyomi_guard/__main__.py @@ -28,9 +28,9 @@ from python_pkg.syncyomi_guard.store import ( ) from python_pkg.syncyomi_guard.verdict import Status, Thresholds, Verdict, compare -_DEFAULT_DB = Path("/home/kuhy/syncyomi/config/syncyomi.db") +_DEFAULT_DB = Path("/home/kuhy/services/syncyomi/config/syncyomi.db") _DEFAULT_STATE = Path.home() / ".local/share/syncyomi_guard" -_DEFAULT_SNAPSHOTS = Path.home() / "syncyomi/snapshots" +_DEFAULT_SNAPSHOTS = Path.home() / "services/syncyomi/snapshots" _DEFAULT_KEEP = 14 diff --git a/python_pkg/wsg_grabber/README.md b/python_pkg/wsg_grabber/README.md index 62fcf8a7..0aafb84e 100644 --- a/python_pkg/wsg_grabber/README.md +++ b/python_pkg/wsg_grabber/README.md @@ -5,9 +5,9 @@ before, and shows them one at a time for a keep/pass decision — mpv embedded i a Tk window with the buttons directly underneath. ```bash -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber # review -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber scrape # no UI -PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber stats +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber # review +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber scrape # no UI +PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber stats ``` `k` / `→` keeps, `j` / `space` / `←` passes, `u` / `Backspace` undoes, `q` / diff --git a/python_pkg/wsg_grabber/__main__.py b/python_pkg/wsg_grabber/__main__.py index c54c0f65..866df047 100644 --- a/python_pkg/wsg_grabber/__main__.py +++ b/python_pkg/wsg_grabber/__main__.py @@ -1,7 +1,7 @@ """Module entry point. Usage: - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber --help + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber --help """ from __future__ import annotations diff --git a/python_pkg/wsg_grabber/cli.py b/python_pkg/wsg_grabber/cli.py index e3bfa3bd..39da72f7 100644 --- a/python_pkg/wsg_grabber/cli.py +++ b/python_pkg/wsg_grabber/cli.py @@ -1,9 +1,9 @@ """Command-line entry point for the /wsg/ grabber. Usage: - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber scrape --seconds 60 - PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber stats + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber scrape --seconds 60 + PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.wsg_grabber stats """ from __future__ import annotations diff --git a/scripts/check_dependency_freshness.sh b/scripts/check_dependency_freshness.sh index 6b096ccc..6e45dd0c 100755 --- a/scripts/check_dependency_freshness.sh +++ b/scripts/check_dependency_freshness.sh @@ -3,7 +3,7 @@ # ============================================================================ # Fail if any dependency in the commit is behind its ecosystem's newest stable. # -# Thin delegate to the shared gate in ~/utils, which owns the registry +# Thin delegate to the shared gate in ~/src/utils, which owns the registry # adapters, the pre-release rules and the allowlist semantics. Copying that # logic here is what lets one repo's idea of "current" drift from every other # repo's -- so this script only locates the shared checker and forwards its @@ -18,12 +18,12 @@ set -euo pipefail -readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_dependency_freshness.sh" +readonly SHARED_GATE="${UTILS_ROOT:-$HOME/src/utils}/scripts/check_dependency_freshness.sh" main() { if [[ ! -x "$SHARED_GATE" ]]; then echo "Error: shared dependency-freshness gate not found at $SHARED_GATE" >&2 - echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2 + echo " Clone github.com/kuhyx/utils to ~/src/utils, or set" >&2 echo " UTILS_ROOT to where it lives." >&2 exit 1 fi diff --git a/scripts/check_md_naming.sh b/scripts/check_md_naming.sh index 27f3664b..8d3429c0 100755 --- a/scripts/check_md_naming.sh +++ b/scripts/check_md_naming.sh @@ -3,7 +3,7 @@ # ============================================================================ # Fail if any markdown file breaks the shared naming convention. # -# Thin delegate to the shared gate in ~/utils, which owns the four namespaces +# Thin delegate to the shared gate in ~/src/utils, which owns the four namespaces # (README / CLAUDE* / DOCS* / TODO*), the "REMOVE ME AFTER FINISH" marker rule # and the exemption list. Copying that logic here is what lets one repo's idea # of a valid name drift from every other repo's -- so this script only locates @@ -16,12 +16,12 @@ set -euo pipefail -readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_md_naming.sh" +readonly SHARED_GATE="${UTILS_ROOT:-$HOME/src/utils}/scripts/check_md_naming.sh" main() { if [[ ! -x "$SHARED_GATE" ]]; then echo "Error: shared markdown-naming gate not found at $SHARED_GATE" >&2 - echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2 + echo " Clone github.com/kuhyx/utils to ~/src/utils, or set" >&2 echo " UTILS_ROOT to where it lives." >&2 exit 1 fi