From 64c11bdca3f326c66547124615ce70b84d110223 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:09:37 -0700 Subject: [PATCH 1/9] chore(release): .goreleaser.yml for multi-platform builds + Homebrew/Scoop taps Goreleaser v2 config for rememberize-cli: - Pure-Go builds (CGO_ENABLED=0) for darwin/linux amd64+arm64 and windows amd64 - ldflags inject main.version from the tag - tar.gz archives for darwin/linux, zip for windows - sha256 checksums file - Homebrew formula pushed to captured-ventures/homebrew-rememberize - Scoop manifest pushed to captured-ventures/scoop-rememberize - GitHub release: auto prerelease detection based on tag pattern Validated with `goreleaser check` and `goreleaser build --snapshot --single-target`. Co-Authored-By: Claude Opus 4.7 (1M context) --- .goreleaser.yml | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .goreleaser.yml diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..0a85ebc --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,102 @@ +version: 2 + +project_name: rememberize-cli + +before: + hooks: + - go mod tidy + +builds: + - id: rememberize + binary: rememberize + main: ./cmd/rememberize + env: + - CGO_ENABLED=0 + ldflags: + - -s -w -X main.version={{.Version}} + goos: + - darwin + - linux + - windows + goarch: + - amd64 + - arm64 + ignore: + - goos: windows + goarch: arm64 + +archives: + - id: rememberize + name_template: >- + rememberize_{{ .Version }}_{{ .Os }}_{{ .Arch }} + formats: + - tar.gz + format_overrides: + - goos: windows + formats: + - zip + files: + - LICENSE + - README.md + +checksum: + name_template: "checksums.txt" + algorithm: sha256 + +snapshot: + version_template: "{{ incpatch .Version }}-snapshot-{{.ShortCommit}}" + +changelog: + use: github + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - "^chore:" + - "^ci:" + - "Merge pull request" + - "Merge branch" + +release: + github: + owner: captured-ventures + name: rememberize-cli + draft: false + prerelease: auto + mode: replace + name_template: "v{{ .Version }}" + +brews: + - name: rememberize + repository: + owner: captured-ventures + name: homebrew-rememberize + branch: main + token: "{{ .Env.GITHUB_TOKEN }}" + directory: Formula + homepage: "https://rememberize.app" + description: "CLI client for rememberize — a portable, multi-directional memory system for AI." + license: "MIT" + commit_author: + name: rememberize-bot + email: bot@captured.ventures + install: | + bin.install "rememberize" + test: | + assert_match "rememberize", shell_output("#{bin}/rememberize --version") + +scoops: + - name: rememberize + repository: + owner: captured-ventures + name: scoop-rememberize + branch: main + token: "{{ .Env.GITHUB_TOKEN }}" + directory: bucket + homepage: "https://rememberize.app" + description: "CLI client for rememberize — a portable, multi-directional memory system for AI." + license: MIT + commit_author: + name: rememberize-bot + email: bot@captured.ventures From 66ff9f10334896fe57c05a162930fbd217b77d98 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:09:54 -0700 Subject: [PATCH 2/9] ci: lint + test + cross-platform build matrix on PR Three jobs on pull_request to main and push to main: - test: go vet + go test -race on ubuntu-latest - build-matrix: CGO_ENABLED=0 go build on ubuntu/macos/windows to catch platform regressions - lint: golangci-lint via the official action Go version is pulled from go.mod via actions/setup-go so CI stays in sync with the module. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..24abfc4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: read + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Go mod download + run: go mod download + + - name: Vet + run: go vet ./... + + - name: Test + run: go test -race -v ./... + + build-matrix: + name: Build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Build + env: + CGO_ENABLED: "0" + run: go build ./... + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: false + + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + args: --timeout=5m From c9c1d517de591fad1679668cc4d2ff91bc70bd38 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:10:05 -0700 Subject: [PATCH 3/9] ci(release): goreleaser workflow on tag push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Triggers on v* tags. Runs goreleaser/goreleaser-action@v6 with --clean, fetch-depth: 0 for changelog history. Uses a RELEASE_PAT secret (classic PAT with repo scope) instead of the default GITHUB_TOKEN, since goreleaser pushes formulas/manifests to the captured-ventures/homebrew-rememberize and captured-ventures/scoop-rememberize repos — cross-repo writes require a PAT or GitHub App token. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4b36ff5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + packages: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + # RELEASE_PAT is a classic PAT with `repo` scope, required for + # cross-repo pushes to captured-ventures/homebrew-rememberize and + # captured-ventures/scoop-rememberize. The default GITHUB_TOKEN + # cannot push to other repos. + GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }} From 545ebe78a9e41de142705df05b31632269951bf4 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:10:24 -0700 Subject: [PATCH 4/9] feat: install.sh curl-installer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POSIX sh installer for macOS, Linux, and Windows (Git Bash/WSL/MSYS). - Detects OS via uname -s (Darwin/Linux/MINGW*/MSYS*/CYGWIN* → darwin/linux/windows) - Detects arch via uname -m (x86_64/amd64 → amd64, arm64/aarch64 → arm64) - Resolves latest release from the GitHub API (no jq — grep+sed) - Honors INSTALL_DIR (default \$HOME/.local/bin), VERSION, REPO env overrides - Downloads via curl or wget, extracts tar.gz or zip, drops binary at INSTALL_DIR - Prints post-install guidance (PATH hint, --version, pair command) - Fails fast with clean error messages at every stage Canonical endpoint: curl -sSL https://rememberize.app/install.sh | sh Fallback: curl -sSL https://raw.githubusercontent.com/captured-ventures/rememberize-cli/main/install.sh | sh Validated with sh -n. Co-Authored-By: Claude Opus 4.7 (1M context) --- install.sh | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 install.sh diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..3bfe205 --- /dev/null +++ b/install.sh @@ -0,0 +1,187 @@ +#!/bin/sh +# rememberize CLI installer +# +# Usage: +# curl -sSL https://rememberize.app/install.sh | sh +# curl -sSL https://raw.githubusercontent.com/captured-ventures/rememberize-cli/main/install.sh | sh +# +# Environment variables: +# INSTALL_DIR Where to install the binary (default: $HOME/.local/bin) +# VERSION Specific version/tag to install (default: latest) +# REPO Override repo slug (default: captured-ventures/rememberize-cli) + +set -eu + +REPO="${REPO:-captured-ventures/rememberize-cli}" +INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}" +BIN_NAME="rememberize" + +log() { + printf '%s\n' "$*" >&2 +} + +die() { + log "error: $*" + exit 1 +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "required command not found: $1" +} + +need_cmd uname +need_cmd mkdir +need_cmd mv +need_cmd rm +need_cmd chmod + +# ----------------------------------------------------------------------------- +# Detect OS +# ----------------------------------------------------------------------------- +detect_os() { + os_raw=$(uname -s 2>/dev/null || echo unknown) + case "$os_raw" in + Darwin) echo "darwin" ;; + Linux) echo "linux" ;; + MINGW*|MSYS*|CYGWIN*|Windows_NT) echo "windows" ;; + *) die "unsupported OS: $os_raw" ;; + esac +} + +# ----------------------------------------------------------------------------- +# Detect arch +# ----------------------------------------------------------------------------- +detect_arch() { + arch_raw=$(uname -m 2>/dev/null || echo unknown) + case "$arch_raw" in + x86_64|amd64) echo "amd64" ;; + arm64|aarch64) echo "arm64" ;; + *) die "unsupported architecture: $arch_raw" ;; + esac +} + +OS=$(detect_os) +ARCH=$(detect_arch) + +if [ "$OS" = "windows" ] && [ "$ARCH" = "arm64" ]; then + die "windows arm64 is not currently published" +fi + +# ----------------------------------------------------------------------------- +# Pick a downloader +# ----------------------------------------------------------------------------- +if command -v curl >/dev/null 2>&1; then + FETCH="curl -fsSL" + FETCH_OUT="curl -fsSL -o" +elif command -v wget >/dev/null 2>&1; then + FETCH="wget -qO-" + FETCH_OUT="wget -qO" +else + die "need curl or wget installed" +fi + +# ----------------------------------------------------------------------------- +# Resolve version +# ----------------------------------------------------------------------------- +TAG="${VERSION:-}" +if [ -z "$TAG" ]; then + log "resolving latest release from github.com/$REPO..." + RELEASES_URL="https://api.github.com/repos/$REPO/releases/latest" + # Extract "tag_name": "vX.Y.Z" without jq. + TAG=$($FETCH "$RELEASES_URL" 2>/dev/null \ + | grep '"tag_name":' \ + | head -n 1 \ + | sed -E 's/.*"tag_name":[[:space:]]*"([^"]+)".*/\1/') || TAG="" + if [ -z "$TAG" ]; then + die "could not determine latest release tag (no releases published yet?)" + fi +fi + +# Strip leading v for archive name +VERSION_NUM=$(printf '%s' "$TAG" | sed 's/^v//') + +# ----------------------------------------------------------------------------- +# Build download URL +# ----------------------------------------------------------------------------- +if [ "$OS" = "windows" ]; then + EXT="zip" +else + EXT="tar.gz" +fi + +ARCHIVE="rememberize_${VERSION_NUM}_${OS}_${ARCH}.${EXT}" +URL="https://github.com/${REPO}/releases/download/${TAG}/${ARCHIVE}" + +# ----------------------------------------------------------------------------- +# Download + extract +# ----------------------------------------------------------------------------- +TMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t rememberize) +trap 'rm -rf "$TMP_DIR"' EXIT INT TERM + +log "downloading $URL" +if ! $FETCH_OUT "$TMP_DIR/$ARCHIVE" "$URL"; then + die "download failed: $URL" +fi + +log "extracting $ARCHIVE" +case "$EXT" in + tar.gz) + need_cmd tar + tar -xzf "$TMP_DIR/$ARCHIVE" -C "$TMP_DIR" || die "extract failed" + ;; + zip) + if command -v unzip >/dev/null 2>&1; then + unzip -q "$TMP_DIR/$ARCHIVE" -d "$TMP_DIR" || die "extract failed" + else + die "unzip required to install on windows" + fi + ;; +esac + +# Locate binary in extracted tree +BIN_SRC="" +for candidate in \ + "$TMP_DIR/$BIN_NAME" \ + "$TMP_DIR/$BIN_NAME.exe" \ + "$TMP_DIR/rememberize_${VERSION_NUM}_${OS}_${ARCH}/$BIN_NAME" \ + "$TMP_DIR/rememberize_${VERSION_NUM}_${OS}_${ARCH}/$BIN_NAME.exe"; do + if [ -f "$candidate" ]; then + BIN_SRC="$candidate" + break + fi +done + +if [ -z "$BIN_SRC" ]; then + die "could not find $BIN_NAME binary in extracted archive" +fi + +# ----------------------------------------------------------------------------- +# Install +# ----------------------------------------------------------------------------- +mkdir -p "$INSTALL_DIR" || die "could not create $INSTALL_DIR" + +DEST="$INSTALL_DIR/$BIN_NAME" +if [ "$OS" = "windows" ]; then + DEST="$INSTALL_DIR/$BIN_NAME.exe" +fi + +mv "$BIN_SRC" "$DEST" || die "could not move binary to $DEST" +chmod +x "$DEST" 2>/dev/null || true + +# ----------------------------------------------------------------------------- +# Report +# ----------------------------------------------------------------------------- +cat >&2 < + +EOF From 081f7ac9f05e68cc24a8c76804ddc29da7ffa40d Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:10:35 -0700 Subject: [PATCH 5/9] chore: Makefile + .golangci.yml for dev convenience MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makefile targets: - build build bin/rememberize with dev version stamp - test go test -race ./... - lint golangci-lint run - install-dev build + copy to \$INSTALL_DIR (default ~/.local/bin) - clean remove bin/ and dist/ - release-snapshot goreleaser release --snapshot --clean (local dry-run) .golangci.yml enables a minimal set: govet, staticcheck, ineffassign, errcheck, gofmt. Kept tight on purpose — the CLI is small and we'd rather not fight noise linters. Co-Authored-By: Claude Opus 4.7 (1M context) --- .golangci.yml | 16 ++++++++++++++++ Makefile | 26 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .golangci.yml create mode 100644 Makefile diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..5ec8fb9 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,16 @@ +run: + timeout: 5m + tests: true + +linters: + disable-all: true + enable: + - govet + - staticcheck + - ineffassign + - errcheck + - gofmt + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8fdfcac --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +.PHONY: build test lint install-dev clean release-snapshot + +GIT_SHA := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) +VERSION ?= dev-$(GIT_SHA) +BIN := bin/rememberize +INSTALL_DIR ?= $(HOME)/.local/bin + +build: + CGO_ENABLED=0 go build -o $(BIN) -ldflags "-s -w -X main.version=$(VERSION)" ./cmd/rememberize + +test: + go test -race ./... + +lint: + golangci-lint run + +install-dev: build + mkdir -p $(INSTALL_DIR) + cp $(BIN) $(INSTALL_DIR)/rememberize + @echo "Installed to $(INSTALL_DIR)/rememberize" + +clean: + rm -rf bin/ dist/ + +release-snapshot: + goreleaser release --snapshot --clean From 9d0c7b4aac0756b3b4ecfd58f30a0fcf56eabd39 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:45:00 -0700 Subject: [PATCH 6/9] fix(ci): bump golangci-lint to v2 for Go 1.25 compat golangci-lint v1.64.8 (pulled by action@v6 with version=latest) was built against Go 1.24 and rejects configs targeting Go 1.25+. Pin to v2.1.6 via golangci-lint-action@v8, migrate .golangci.yml to v2 format (default: none instead of disable-all, gofmt moved to formatters section). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 5 ++--- .golangci.yml | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24abfc4..f173530 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,6 @@ jobs: cache: false - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: - version: latest - args: --timeout=5m + version: v2.1.6 diff --git a/.golangci.yml b/.golangci.yml index 5ec8fb9..ad3cbc3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,19 @@ +version: "2" + run: timeout: 5m tests: true linters: - disable-all: true + default: none enable: - govet - staticcheck - ineffassign - errcheck + +formatters: + enable: - gofmt issues: From 907bc258e812b2b3e61a573a904307c262f9c7c2 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:51:52 -0700 Subject: [PATCH 7/9] fix(ci): bump golangci-lint to v2.11.4 (built with Go 1.25) v2.1.6 was still built with Go 1.24. v2.11.4 (latest, 2026-03-22) is built against Go 1.25 and accepts the 1.25.5 go.mod target. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f173530..58dc6d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,4 +75,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v8 with: - version: v2.1.6 + version: v2.11.4 From 3d5ec86ed665dee9862872f5d11899faeb2fde3f Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 02:57:47 -0700 Subject: [PATCH 8/9] fix(lint): exclude stdout/close errcheck noise in CLI code fmt.Fprint* to stdout/stderr and Close() on response bodies / config files are not actionable errors in a CLI. Standard exclusions match the Go community's common practice for CLI projects. Co-Authored-By: Claude Opus 4.7 (1M context) --- .golangci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index ad3cbc3..387fd64 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,6 +11,15 @@ linters: - staticcheck - ineffassign - errcheck + settings: + errcheck: + exclude-functions: + - fmt.Fprint + - fmt.Fprintf + - fmt.Fprintln + - (io.Closer).Close + - (*os.File).Close + - (*text/tabwriter.Writer).Flush formatters: enable: From 6dde4cb104234bdb822996b68ff0058bb407e3c0 Mon Sep 17 00:00:00 2001 From: Ironystock Date: Sat, 18 Apr 2026 03:04:14 -0700 Subject: [PATCH 9/9] fix(lint): address real errcheck + staticcheck findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Wrap multipart writer Write/WriteField/Close with error handling in the import path — malformed uploads were a real risk. - Explicit _,_ ignore on os.Stdout.Write (export-to-stdout path). - t.Fatalf on json.Unmarshal failure in pair_test.go (was silently swallowed). - Convert sb.WriteString(fmt.Sprintf(...)) to fmt.Fprintf(&sb, ...) in memory_md formatter (staticcheck QF1012). - gofmt all touched files. Local go build + go test pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- cmd/rememberize/main.go | 18 +++++++++++++----- cmd/rememberize/pair_flow_test.go | 8 ++++---- cmd/rememberize/pair_test.go | 4 +++- internal/transfer/memory_md.go | 6 +++--- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/cmd/rememberize/main.go b/cmd/rememberize/main.go index 2fb7ac3..e1f12c7 100644 --- a/cmd/rememberize/main.go +++ b/cmd/rememberize/main.go @@ -644,14 +644,22 @@ func runImport(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("create form file: %w", err) } - part.Write(data) + if _, err := part.Write(data); err != nil { + return fmt.Errorf("write form part: %w", err) + } if importNamespace != "" { - writer.WriteField("namespace", importNamespace) + if err := writer.WriteField("namespace", importNamespace); err != nil { + return fmt.Errorf("write namespace field: %w", err) + } } if importFormat != "" { - writer.WriteField("format", importFormat) + if err := writer.WriteField("format", importFormat); err != nil { + return fmt.Errorf("write format field: %w", err) + } + } + if err := writer.Close(); err != nil { + return fmt.Errorf("close multipart writer: %w", err) } - writer.Close() req, err := client.newRequest("POST", "/api/import", body) if err != nil { @@ -741,7 +749,7 @@ func runExport(cmd *cobra.Command, args []string) error { } fmt.Printf("Exported to %s\n", exportOutput) } else { - os.Stdout.Write(data) + _, _ = os.Stdout.Write(data) } return nil } diff --git a/cmd/rememberize/pair_flow_test.go b/cmd/rememberize/pair_flow_test.go index 9e9703c..2914a2a 100644 --- a/cmd/rememberize/pair_flow_test.go +++ b/cmd/rememberize/pair_flow_test.go @@ -35,10 +35,10 @@ type recordedRequest struct { } type exchangeResponse struct { - APIKey string `json:"api_key"` - ServerURL string `json:"server_url"` - Connection exchangeRespConnection `json:"connection"` - Namespaces []exchangeRespNamespace `json:"namespaces"` + APIKey string `json:"api_key"` + ServerURL string `json:"server_url"` + Connection exchangeRespConnection `json:"connection"` + Namespaces []exchangeRespNamespace `json:"namespaces"` } type exchangeRespConnection struct { diff --git a/cmd/rememberize/pair_test.go b/cmd/rememberize/pair_test.go index c0f209b..9c22aa8 100644 --- a/cmd/rememberize/pair_test.go +++ b/cmd/rememberize/pair_test.go @@ -82,7 +82,9 @@ func TestWriteMCPConfig_MergesExistingFile(t *testing.T) { data, _ := os.ReadFile(path) var cfg map[string]any - json.Unmarshal(data, &cfg) + if err := json.Unmarshal(data, &cfg); err != nil { + t.Fatalf("unmarshal written config: %v", err) + } servers := cfg["mcpServers"].(map[string]any) if _, ok := servers["other-server"]; !ok { diff --git a/internal/transfer/memory_md.go b/internal/transfer/memory_md.go index 6f7c484..69277fd 100644 --- a/internal/transfer/memory_md.go +++ b/internal/transfer/memory_md.go @@ -172,11 +172,11 @@ func (f *MemoryMDFormatter) Format(memories []Memory) ([]byte, error) { sb.WriteString("\n") } sb.WriteString("---\n") - sb.WriteString(fmt.Sprintf("name: %s\n", truncate(m.Content, 60))) + fmt.Fprintf(&sb, "name: %s\n", truncate(m.Content, 60)) if m.Metadata != nil { - sb.WriteString(fmt.Sprintf("description: %s\n", *m.Metadata)) + fmt.Fprintf(&sb, "description: %s\n", *m.Metadata) } - sb.WriteString(fmt.Sprintf("type: %s\n", m.Type)) + fmt.Fprintf(&sb, "type: %s\n", m.Type) sb.WriteString("---\n\n") sb.WriteString(m.Content) sb.WriteString("\n")