One independent Go module per subdirectory, module path
github.com/sfc-gh-eraigosa/dotfiles/sdk/<tool> (the sdk/ segment is part of
the canonical path — omitting it breaks go install), released on
path-prefixed sdk/<tool>/vX.Y.Z tags.
Go code lives here, not
src/(src/is non-Go tooling + agent skills) — cutover plan.README.md is the user-facing tour (per tool: problem, use case, demo). This file is the build + maintenance contract. Adding a module updates both — see Adding a module.
| Module | Binary | What it does |
|---|---|---|
gss/ |
gss |
Git Safe Sync — backups, approval-gated pushes, stacked feature worktrees. |
tmux-mgr/ |
tmux-mgr |
tmux session + parallel-agent orchestration (worktree-isolated). |
gsl/ |
gsl |
Powerline status line for Claude Code / Antigravity CLI. |
fleet/ |
fleet |
Multi-host install-drift status, TUI, wake ladder, SSH key management. |
gff/ |
gff |
git fast features — layered feature flags gating install.sh. |
wol/ |
wol |
Wake-on-LAN magic packets. |
wlink/ |
wlink |
WSL link — pins the resolver that knows your fleet; tunnel readiness, drift, ssh-keepalive doctor. |
libs/ |
(library) | Shared Go packages — log. Not a CLI. |
fleet, gff, and libs each shipped without being listed above; this
checklist exists so that stops recurring.
- Module at
sdk/<tool>/with the canonical path above. build.shsourcingversion.sh(stamps version via-ldflags -X).- Log through
libs/log— never hand-roll one. AGENTS.md+ aCLAUDE.md -> AGENTS.mdsymlink (ln -s AGENTS.md CLAUDE.md).README.md— the module's deep docs.- Wire into
install.sh(builds into~/opt/bin/). - Add a row to Modules and to README's "Pick your tool" table.
- Add a README.md section, matching the existing shape:
blockquote pitch → The problem → What it does about it → Reach for
it when (3–4 triggers) →
consoledemo → Gotchas → docs footer link. Easiest step to skip, most costly to miss. - Verify tracking:
git status --short -- <path>..gitignorestarts with*;!sdk/**opts this tree in, but confirm rather than assume (allowlist).
Renaming/removing: update both tables + the README section, and grep the old
name across install.sh, Makefile, ../README.md.
Demos must be real output. Re-run the command and paste what it prints when flags or output change. An invented transcript is worse than no demo — it fails only for the reader who trusts it.
- Versioning is tag-driven — no
VERSIONfile.build.shderives it fromgit describe --tags --match "sdk/<tool>/v*": clean release →X.Y.Z, dev →X.Y.Z-<n>-g<sha>..github/workflows/sdk-auto-bump.ymlplans the next semver (opt/scripts/system/bump-sdk-version.sh --plan) and pushes the tag directly — it never commits tomain. - Test/lint discovery is by directory under
sdk/(scripts/test.sh+ theMakefileGo loops);src/stays scanned until the cutover completes.
Every tool logs through libs/log — no hand-rolled
logger, file writer, or rotation.
import applog "github.com/sfc-gh-eraigosa/dotfiles/sdk/libs/log"
applog.SetDefaultTool("mytool") // once, at startup
applog.Default().WithField("host", h).Info("started")- Diagnostics (what the tool did) →
New/Default— logrus JSON, lumberjack rotation,$MYTOOL_LOG_FILE/$MYTOOL_LOG_LEVEL. - Captured output (bytes another process produced) →
NewCapture— plain text per run; a captured install log's value is being readable as-is. The lifecycle is standardized, not the format.
Construction never fails: a logger that cannot open its file discards, and a nil
*Capture is safe to call. Logging must never introduce a failure mode into
the thing it observes.
gsl still uses its own internal/observe; migrate it to libs/log when next
touched.