diff --git a/CLAUDE.md b/CLAUDE.md index a1f7970..09e6c35 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,8 @@ Project context for any Claude Code session that opens this repo. Keep concise. +> **First time in this repo?** Start with [`docs/agent-briefing.md`](docs/agent-briefing.md) — it walks through orientation (read playbook → recall rememberize-dev context → file first memory note → verify install end-to-end). After the first session, this file is your day-to-day reference. + ## What this repo is Public-facing CLI and Claude Code skill for [rememberize](https://rememberize.app), a portable multi-directional memory system for AI. @@ -85,3 +87,51 @@ mcp__rememberize__recall namespace="rememberize-cli-agent" query="last relea 3. The agent runtime injects it via Claude Code settings. Never commit a real key. `.gitignore` already covers common env files. + +## Lessons & gotchas (hard-won from the bootstrap sprint) + +These are things future-you wants to know before they bite. Add to this list when you learn something non-obvious. + +### Go toolchain alignment + +- **`golangci-lint` version is load-bearing when Go minor versions shift.** The bootstrap failed CI three times before we landed on the right combination: `golangci/golangci-lint-action@v8` + pinned `version: v2.11.4`. Earlier versions (v1.64.8, v2.1.6) were built with Go 1.24 and refused configs targeting Go 1.25+. When Go bumps a minor version, check that the pinned lint version has a release built against the new toolchain before merging. +- **Pin, don't `latest`.** Auto-latest picks up whatever GitHub flagged as `Latest` at run time — which may not match your Go target. Explicit version pin in CI is worth the occasional manual bump. + +### `.golangci.yml` v2 vs v1 + +This repo is on the v2 config format (`version: "2"` at the top). Legacy v1 stanzas like `linters.disable-all: true` and enabling `gofmt` as a linter will silently lint nothing. v2 uses `linters.default: none` and moves `gofmt` into a separate `formatters:` section. + +### errcheck in CLI code — exclude the usual noise + +`.golangci.yml` already excludes `fmt.Fprint`, `fmt.Fprintf`, `fmt.Fprintln`, `(io.Closer).Close`, `(*os.File).Close`, `(*text/tabwriter.Writer).Flush`. Checking these in CLI code produces 20+ false positives and zero insight. If you add a new area that writes to stdout/stderr, you do NOT need to check those returns. + +What you DO still need to check (the bootstrap bit us): **`multipart.Writer.Write`, `WriteField`, `Close`**. A silently-dropped error here produces malformed upload bodies. errcheck catches it; don't exclude it. + +### Pair protocol — backwards-compat sentinel is load-bearing + +`cmd/rememberize/pair.go` sends `client_name: "@auto"` + `hostname: `. Server composes the full display name from its stored `client_hint` + the received hostname. **Older CLI versions still in production send a pre-composed `client_name`** (not `@auto`). The server path keeps both branches alive — if you ever see code that drops the `@auto` check, you've broken every currently-installed binary. Don't. If you genuinely need to change the sentinel, coordinate with the server side first (main repo). + +The OTK exchange response includes `connection.name` and `connection.config_target` — **trust those fields.** Use `connection.name` for display, use `connection.config_target` to decide which config file the client writes (claude-code → `.mcp.json`, cursor → `~/.cursor/mcp.json`, cli → no MCP config, generic → print to stdout). Don't resurrect the cwd-sniffing `detectClient()` that used to live here; it was the whole point of F7+F9. + +### Two-repo discipline + +You cannot import `github.com/ironystock/rememberize/...` — that's the private server repo. If you find yourself reaching for it, you're about to duplicate logic (acceptable, local copies are fine) or design a protocol change (not acceptable here — file a `[MAIN-REPO]` issue and @-mention Brad). + +The `internal/transfer/` package here is a minimal local copy, not a sync target. It has its own `Memory` struct (field set reflects only wire formats the CLI parses/emits). If the main repo evolves its `internal/memory.Memory`, this copy does NOT need to follow unless import/export wire formats themselves change. + +### IDE diagnostics on a Windows dev host + +If you open this repo alongside the private rememberize main repo (`D:\new-projects\rememberize.app\`), gopls may flag spurious "undefined: X" errors on files in `rememberize-cli` — the main repo's workspace doesn't see the sibling module. **These are not real.** Source of truth is `go build ./...` + `go test ./...` + CI, not the IDE. Add a `go.work` at the parent directory if you want cross-repo IDE coherence; don't change source to silence the warnings. + +### Release mechanics + +- goreleaser cuts on tag push (`v*`). Tags are append-only — don't retag an existing version; cut `v0.x.y+1`. +- `RELEASE_PAT` secret is what lets the release workflow push formula/manifest updates to the sibling `homebrew-rememberize` + `scoop-rememberize` repos. `GITHUB_TOKEN` alone can't do cross-repo pushes. +- Homebrew tap uses the `brews:` goreleaser block (deprecation notice on `brews:` → `homebrew_casks:` exists; we stayed on `brews:` because it still works and `casks` is for GUI apps). +- Windows builds skip `arm64` (goreleaser `ignore: windows/arm64` + `install.sh` errors cleanly on that combo). Add it if demand arrives. + +### Misc + +- **Binary name is `rememberize`, not `rememberize-cli`.** The repo is `rememberize-cli` for namespace clarity, but the built binary on disk is `rememberize`. Homebrew formula, Scoop manifest, and install.sh all reflect this. +- **Pure Go, `CGO_ENABLED=0` everywhere.** Don't add a CGO dep without a very good reason — cross-compile-for-5-platforms-in-CI becomes a mess. +- **README's canonical install URL is `https://rememberize.app/install.sh`** (served via CF Pages redirect). Raw-GitHub fallback works today but is ugly in docs. If the redirect breaks, fix the redirect, don't edit the README back to raw URLs.