Compare commits

..
21 Commits
Author SHA1 Message Date
tgrosinger abc4677f55 Waybar: Distinguish rest breaks by color 2026-09-29 09:24:50 -07:00
tgrosinger 92e70f4fff Skills: Ignore Claude synced skills 2026-09-29 09:24:29 -07:00
tgrosinger 5ef317d3e1 Skills: Add blender skill from Simon Wilison 2026-09-29 09:23:31 -07:00
tgrosinger 550a97fccf Skills: Update mattpocock-skills 2026-09-29 09:23:31 -07:00
tgrosinger 60fc72892e Skills: Keep the resolving-merge-conflicts skill
This was removed from mattpocock-skills.
2026-09-29 09:21:26 -07:00
tgrosinger 24a2682e80 Claude: Switch to opus 5.5 as default model 2026-09-24 20:46:30 -07:00
tgrosinger 5f5e236903 Claude: Deny reading ansible variables 2026-09-24 20:46:15 -07:00
tgrosinger 53d9d69308 btop: Remove gpu config options 2026-09-24 20:45:56 -07:00
tgrosinger d2c016499a tmux: Further refinement of the session close hook filter 2026-09-24 20:45:14 -07:00
tgrosinger be0016c461 Skills: Add traefik-expose skill 2026-09-23 21:45:40 -07:00
tgrosinger cd5884598c tmux: Fix session close hook to exclude RAC sessions 2026-09-23 21:40:02 -07:00
tgrosinger e95af53bda Pi: Fix agent skills location 2026-09-16 08:29:30 -07:00
tgrosinger 625ee1a3d8 Claude: Turn off drafts 2026-09-16 08:27:58 -07:00
tgrosinger ed17cb13e2 Pi: Do not commit auth to dotfiles 2026-09-16 08:27:46 -07:00
tgrosinger 0f702dddf4 Claude: Move atrium on voyager 2026-09-16 08:27:46 -07:00
tgrosinger af9ffa6b34 Waybar: add new memory widget 2026-09-15 13:55:38 -07:00
tgrosinger 35aa88b549 Atuin: Disable tmux integration
This causes a problem when using a terminal in Remote Agent Console.
2026-09-15 13:38:05 -07:00
tgrosinger 8ab7e9f91e Skills: Add openscad skill 2026-09-15 13:37:38 -07:00
tgrosinger 2c5d9a135a Fish: Helper function to convert svg files to gcode 2026-09-11 21:28:20 -07:00
tgrosinger 4b6d096c1d Waybar: Fix btop path 2026-09-11 21:28:20 -07:00
tgrosinger 9fdf20d936 Fish: Move worktrees into repo.worktrees directory 2026-09-11 21:28:20 -07:00
21 changed files with 224 additions and 56 deletions
+4
View File
@@ -6,6 +6,10 @@
# Python
*.pyc
# Secrets
home/.pi/agent/auth.json
# dev-tickets CLI dependencies, installed by install-packages.sh (npm ci)
home/.local/lib/dev-tickets/node_modules/
home/.agents/skills/synced
+3
View File
@@ -1,3 +1,6 @@
[submodule "mattpocock-skills"]
path = mattpocock-skills
url = ssh://git@git.grosinger.net:22322/tgrosinger/mattpocock-skills.git
[submodule "openscad"]
path = home/.agents/skills/openscad
url = git@github.com:ModelRift/openscad-skill.git
+58
View File
@@ -0,0 +1,58 @@
---
name: blender-local
description: Build, edit, and render 3D scenes with the locally installed Blender on this Mac. Use when the user requests Blender work or an editable Blender scene.
---
# Local Blender
## Installed application
The verified executable is `/Applications/Blender.app/Contents/MacOS/Blender` (plural `Applications`, not `/Application/Blender`). It reported Blender 5.1.2 during the September 2026 pelican project. Check the executable if the installation changes; use this local app for Blender requests.
## Working workflow
Write scene-building scripts in the task's `work/` directory. Execute `bpy` scripts with Blender's bundled Python, not an ordinary Python interpreter:
```sh
/Applications/Blender.app/Contents/MacOS/Blender --background --python work/scene.py
```
For ordinary Python helpers outside Blender, use `uv run`. Set absolute output paths in the Blender script, create the destination directory, and save both the editable `.blend` and rendered PNG to the task's deliverable directory.
To edit a saved scene, call `bpy.ops.wm.open_mainfile(filepath=...)` before making changes. Only clear all objects when intentionally building a new scene. Prefer reusable functions or loading the existing `.blend` over executing fragments sliced out of earlier scripts.
This finalization pattern worked with the installed version; choose image dimensions and samples for the task:
```python
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
scene.cycles.samples = 64
scene.cycles.use_denoising = True
scene.render.resolution_x = 1600
scene.render.resolution_y = 1200
scene.render.resolution_percentage = 100
scene.view_settings.view_transform = 'AgX'
scene.render.image_settings.file_format = 'PNG'
scene.render.filepath = str(output_dir / 'scene.png')
bpy.ops.wm.save_as_mainfile(filepath=str(output_dir / 'scene.blend'))
bpy.ops.render.render(write_still=True)
```
Use roughly 32–48 samples for previews and 64–96 for a polished still with denoising as starting points. Do not assume GPU acceleration is configured. Saving before rendering preserves the scene if rendering fails.
## Execution and diagnosis
- The first sandboxed launch in this project exited with code 139 before running the script and wrote `blender.crash.txt` in the system temporary directory. A retry through the approved unsandboxed execution mechanism succeeded. Treat this as an observed environment issue, not proof that every crash needs escalation. On a similar startup failure, inspect the output and request the applicable execution permission; if a permitted retry still fails, investigate rather than repeatedly escalating.
- A USD `Arch_ValidateAssumptions` warning accompanied that startup failure; it did not establish the root cause.
- `Material.use_nodes` and `World.use_nodes` emitted deprecation warnings in 5.1.2 but worked. They are expected to change in Blender 6; verify the installed API when upgrading.
- Background rendering can produce little output for tens of seconds. If execution returns a session ID, poll that session until completion instead of launching duplicate renders. Confirm the exit status and saved files.
## Modeling and visual lessons
- Cylinders aligned with `direction.to_track_quat('Z', 'Y')`, beveled curves, UV spheres, and custom meshes worked well for stylized scene construction. Smooth shading does not fix a faceted silhouette: give focal shapes enough geometry. Cap exposed curve ends or cover them with modeled joints.
- Inspect the rendered image after meaningful changes. Check the silhouette, grip and pedal contacts, floating parts, frame cropping, shadows, and whether background details are actually visible. A successfully saved render is not visual QA.
- Avoid overlapping coplanar surfaces. The retained studio floor hid the new beach and ocean; coincident surfaces produced black bands. Remove obsolete geometry and deliberately separate surface heights. For a box, calculate the top as center Z plus half its height.
- Frame an orthographic camera deliberately: an infinite ground plane fills the background without a natural horizon. A distant sky backdrop perpendicular to the horizontal viewing direction can provide a level horizon. Check projected positions of the sun, clouds, and distant props; adding them does not guarantee they appear in frame.
- For a substantial quality improvement, prioritize coherent composition, character anatomy, materials, and light over adding more objects. Preview warm key light, cooler fill, and rim light; avoid washed-out colors and unlit distant props. Keep decorative details from obscuring the subject.
Deliver the inspected render inline and link the editable `.blend` using absolute paths. Preserve earlier versions when making major revisions unless the user requests replacement.
+1
View File
@@ -0,0 +1 @@
../../../mattpocock-skills/skills/engineering/implement-spec/
@@ -1 +0,0 @@
../../../mattpocock-skills/skills/engineering/resolving-merge-conflicts
@@ -0,0 +1,14 @@
---
name: resolving-merge-conflicts
description: "Use when you need to resolve an in-progress git merge/rebase conflict."
---
1. **See the current state** of the merge/rebase. Check git history, and the conflicting files.
2. **Find the primary sources** for each conflict. Understand deeply why each change was made, and what the original intent was. Read the commit messages, check the PRs, check original issues/tickets.
3. **Resolve each hunk.** Preserve both intents where possible. Where incompatible, pick the one matching the merge's stated goal and note the trade-off. Do **not** invent new behaviour. Always resolve; never `--abort`.
4. Discover the project's **automated checks** and run them, typically typecheck, then tests, then format. Fix anything the merge broke.
5. **Finish the merge/rebase.** Stage everything and commit. If rebasing, continue the rebase process until all commits are rebased.
@@ -0,0 +1,3 @@
interface:
display_name: "Resolving Merge Conflicts"
short_description: "Resolve merge and rebase conflicts"
@@ -0,0 +1,66 @@
---
name: traefik-expose
description: Make a service on the tachi host reachable at https://NAME.i.grosinger.net through Traefik, or remove it. Use when asked to expose, register, route, or publish a dev server, host process, or docker container on an internal hostname, or to unexpose/deregister one.
---
> [!info] This skill only applies to the host `voyager`
> This skill may be synced to other hosts, but it only applies to `voyager`. If you are running on a different host and need to expose a service to an internal or external hostname, stop and ask for guidance from the user.
# traefik-expose — put a service on NAME.i.grosinger.net
Traefik on this host (`/tachi/docker`) serves `*.i.grosinger.net` on its `internal` entrypoint. That entrypoint is the default and already carries the wildcard TLS certificate and the home-only IP allowlist, and Blocky resolves every `*.i.grosinger.net` name to this host. So registering a service is only a routing rule and a target; there is no DNS, certificate, or middleware step.
Pick the branch by where the service runs.
## Host process (not in a container)
Register with the script in the docker repo:
```sh
/tachi/docker/traefik/expose NAME PORT # https://NAME.i.grosinger.net -> host:PORT
/tachi/docker/traefik/expose -l # list
/tachi/docker/traefik/expose -d NAME # remove
```
It writes one file to `traefik/dynamic.d/`, which Traefik watches, so the route is live within seconds and needs no restart. The file is gitignored.
The process must listen on `0.0.0.0` or on `172.19.0.1`. Traefik connects from inside its container network and cannot reach a listener bound to `127.0.0.1`, which most dev servers default to; pass the tool's host flag (`--host 0.0.0.0` or equivalent).
## Docker container
Join the container to the `docker_traefik` network and add labels. From a compose file outside `/tachi/docker`, declare the network as external:
```yaml
services:
myapp:
networks: [traefik]
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.i.grosinger.net`)"
- "traefik.http.services.myapp.loadbalancer.server.port=3000"
networks:
traefik:
external: true
name: docker_traefik
```
Inside `/tachi/docker/docker-compose.yaml` the network is simply `traefik`. Traefik ignores containers without `traefik.enable=true`. Always set the port label: it is the port the app listens on inside the container, not a published one, and no `ports:` mapping is needed. Removing the labels or the container removes the route.
## Verify
```sh
curl -sk -o /dev/null -w '%{http_code}\n' https://NAME.i.grosinger.net/
```
- **200-ish**: done.
- **502**: Traefik has the route but cannot reach the target. Host process: check the bind address with `ss -ltnp | grep PORT`. Container: check the port label matches what the app listens on and the container is on `docker_traefik`.
- **404**: Traefik has no router for that host. Ask the dashboard API, which needs no docker socket:
```sh
curl -sk https://traefik.i.grosinger.net/api/http/routers/NAME@file # host process
curl -sk https://traefik.i.grosinger.net/api/http/routers/NAME@docker # container
```
## Public exposure is a different task
Everything above is reachable only from the LAN and Tailscale. Making a service internet-facing means a real `grosinger.net` name, an explicit `traefik.http.routers.NAME.entrypoints=websecure` label, and a deliberate decision by the user. Do not add `websecure` when asked for an internal hostname.
+4 -6
View File
@@ -30,7 +30,7 @@
],
"defaultMode": "auto"
},
"model": "claude-opus-4-8",
"model": "opus[1m]",
"disableClaudeAiConnectors": true,
"hooks": {
"PostToolUse": [
@@ -120,19 +120,16 @@
"~/.local/share/pnpm",
"~/.cache/pnpm",
"~/Documents/Atrium",
"/tachi/docker/atribot/vault"
"/tachi/atrium"
],
"denyRead": [
"~/.ssh",
"~/.config/Signal",
"~/obsidian/obsidian/ansible/variables",
"~/Documents",
"/tachi/backups",
"/tachi/documents",
"/tachi/docker"
],
"allowRead": [
"~/Documents/Atrium",
"/tachi/docker/atribot/vault"
]
},
"excludedCommands": [
@@ -140,6 +137,7 @@
"brew *"
]
},
"feedbackDrafts": "off",
"spinnerVerbs": {
"mode": "replace",
"verbs": [
+1 -1
View File
@@ -23,6 +23,6 @@ history_filter = [
filters = [ "global", "host", "workspace", "directory" ]
[tmux]
enabled = true
enabled = false
width = "80%"
height = "60%"
-36
View File
@@ -48,9 +48,6 @@ graph_symbol = "braille"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_cpu = "default"
# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
graph_symbol_gpu = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_mem = "default"
@@ -117,9 +114,6 @@ cpu_graph_upper = "Auto"
#* Select from a list of detected attributes from the options menu.
cpu_graph_lower = "Auto"
#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
show_gpu_info = "Auto"
#* Toggles if the lower CPU graph should be inverted.
cpu_invert_lower = true
@@ -254,33 +248,3 @@ log_level = "WARNING"
#* Automatically save current settings to config file on exit.
save_config_on_exit = true
#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
nvml_measure_pcie_speeds = true
#* Measure PCIe throughput on AMD cards, may impact performance on certain cards.
rsmi_measure_pcie_speeds = true
#* Horizontally mirror the GPU graph.
gpu_mirror_graph = true
#* Set which GPU vendors to show. Available values are "nvidia amd intel apple"
shown_gpus = "nvidia amd intel"
#* Custom gpu0 model name, empty string to disable.
custom_gpu_name0 = ""
#* Custom gpu1 model name, empty string to disable.
custom_gpu_name1 = ""
#* Custom gpu2 model name, empty string to disable.
custom_gpu_name2 = ""
#* Custom gpu3 model name, empty string to disable.
custom_gpu_name3 = ""
#* Custom gpu4 model name, empty string to disable.
custom_gpu_name4 = ""
#* Custom gpu5 model name, empty string to disable.
custom_gpu_name5 = ""
+3 -3
View File
@@ -30,9 +30,9 @@ function dev-wt-pr -d "Open a GitHub PR in a worktree in a new tmux window (Octo
set -l main_wt_path (git worktree list --porcelain | head -1 | string replace "worktree " "")
set -l parent_dir (dirname "$main_wt_path")
# Key the worktree on the PR number to stay unique and avoid slashes in
# branch names producing nested directories.
set -l wt_path "$parent_dir/$repo-pr-$pr_number"
# Group in the per-repo worktree container; key on the PR number to stay
# unique and avoid slashes in branch names producing nested directories.
set -l wt_path "$parent_dir/$repo.worktrees/pr-$pr_number"
# Create the worktree if it doesn't already exist.
if not test -d "$wt_path"
+3 -1
View File
@@ -62,7 +62,9 @@ function __dev_wt_impl -a name
if test -z "$wt_path"
set -l main_wt_path (git worktree list --porcelain | head -1 | string replace "worktree " "")
set -l parent_dir (dirname "$main_wt_path")
set wt_path "$parent_dir/$repo-$name"
# Group worktrees in a per-repo container so they don't look like sibling
# projects (e.g. ~/obsidian/community.worktrees/workers, not community-workers).
set wt_path "$parent_dir/$repo.worktrees/$name"
set wt_branch "$name"
if test -d "$wt_path"
@@ -0,0 +1,19 @@
# Requires vpype with gcode extension.
# uv tool install "vpype[all]" --with vpype-gcode
function svg2gcode -d "Convert an SVG to gcode for the Shapeoko pen plotter using vpype" -a input
set -l output (string replace -r '\.svg$' '.gcode' $input)
vpype \
# Load the SVG into the pipeline (curves are linearized to polylines)
read $input \
# Join lines whose endpoints nearly touch into single paths
linemerge \
# Reorder paths to minimize pen-up travel between them
linesort \
# Randomize where each closed path starts, hiding the seam mark
reloop \
# Drop redundant points within the default deviation tolerance
linesimplify \
# Emit gcode using the "shapeoko_pen" profile from the vpype config
gwrite --profile shapeoko_pen $output
end
+9 -2
View File
@@ -23,7 +23,7 @@
"network",
"power-profiles-daemon",
"cpu",
//"memory",
"custom/memtmp",
//"temperature",
"backlight",
//"sway/language",
@@ -167,7 +167,14 @@
"cpu": {
"format": "{usage}% ",
"tooltip": false,
"on-click": "alacritty -e /home/linuxbrew/.linuxbrew/bin/btop"
"on-click": "alacritty -e /home/tgrosinger/.local/share/mise/shims/btop"
},
"custom/memtmp": {
"exec": "bash $HOME/.config/waybar/memtmp.sh",
"return-type": "json",
"interval": 5,
"format": "{}",
"on-click": "alacritty -e /home/tgrosinger/.local/share/mise/shims/btop"
},
"memory": {
"format": "{}% "
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Waybar custom module: available RAM (GiB) and current /tmp usage.
#
# Available RAM uses MemAvailable, the kernel's honest "allocatable without
# swapping" figure. /tmp is tmpfs, so its used space is live RAM consumption;
# df -h auto-scales the unit (it is usually in the MB range).
set -euo pipefail
MEM_ICON=$''
TMP_ICON=$''
avail_gib=$(awk '/^MemAvailable:/ {printf "%.1f", $2/1048576}' /proc/meminfo)
read -r tmp_used tmp_size < <(df -h --output=used,size /tmp | awk 'NR==2 {print $1, $2}')
printf '{"text":"%sG %s %s %s","tooltip":"RAM available: %s GiB\\n/tmp used: %s of %s"}\n' \
"$avail_gib" "$MEM_ICON" "$tmp_used" "$TMP_ICON" \
"$avail_gib" "$tmp_used" "$tmp_size"
+10 -2
View File
@@ -1,7 +1,7 @@
@import "latte.css";
* {
font-family: 'JetBrainsMono';
font-family: 'Ubuntu Nerd Font';
font-size: 13px;
}
@@ -89,6 +89,7 @@ button:hover {
#battery,
#cpu,
#memory,
#custom-memtmp,
#disk,
#temperature,
#backlight,
@@ -196,7 +197,6 @@ label:focus {
}
#custom-break-timer.overdue {
background-color: @red;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: steps(12);
@@ -204,6 +204,14 @@ label:focus {
animation-direction: alternate;
}
#custom-break-timer.overdue-short {
background-color: @peach;
}
#custom-break-timer.overdue-long {
background-color: @red;
}
#pulseaudio.muted {
background-color: @crust;
color: @subtext1;
+2 -2
View File
@@ -5,8 +5,8 @@
"defaultModel": "gpt-5.6-luna",
"defaultThinkingLevel": "high",
"skills": [
"~/.claude/skills"
"~/.agents/skills"
],
"hideThinkingBlock": true,
"enableInstallTelemetry": false
}
}
+5 -1
View File
@@ -5,7 +5,11 @@ set-option -g renumber-windows on
set-option -g history-limit 10000
set -s escape-time 0
set-option -g detach-on-destroy off
set-hook -g session-closed 'choose-tree -Zs'
# This is a variant on the choose-tree hook that filters out Remote Access Console throw-away sessions
# to prevent dropping me into the session picker for automated actions.
#set-hook -g session-closed 'choose-tree -Zs'
set-hook -g session-closed 'if -F "#{||:#{m:rac-*,#{hook_session_name}},#{client_control_mode}}" "" "choose-tree -Zs"'
### Fix supporting italics
set -g default-terminal "tmux-256color"