From 8baba6fcc99b1c8f752145626da2384f06a75480 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Sun, 5 Apr 2026 23:00:15 -0400 Subject: [PATCH 1/3] Sync plugin.json version from git tag on build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds sync-version target to Makefile that writes the latest semver tag into plugin.json before compiling. Eliminates stale version on first Claude Code launch — the binary and plugin metadata now always agree. --- src/Makefile | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index d6decde..d8b3139 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,16 +1,28 @@ BINARY := devkit VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +# Clean version for plugin.json (strip leading v, git suffix — just semver) +SEMVER := $(shell git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.0") LDFLAGS := -s -w -X main.version=$(VERSION) GOFLAGS := -trimpath +PLUGIN_JSON := $(CURDIR)/../.claude-plugin/plugin.json -.PHONY: build install link clean test vet fmt check all +.PHONY: build install link clean test vet fmt check all sync-version all: check build -build: +sync-version: + @if [ -f "$(PLUGIN_JSON)" ] && command -v jq >/dev/null 2>&1; then \ + CURRENT=$$(jq -r '.version' "$(PLUGIN_JSON)"); \ + if [ "$$CURRENT" != "$(SEMVER)" ]; then \ + jq --arg v "$(SEMVER)" '.version = $$v' "$(PLUGIN_JSON)" > "$(PLUGIN_JSON).tmp" && mv "$(PLUGIN_JSON).tmp" "$(PLUGIN_JSON)"; \ + echo "Synced plugin.json version: $$CURRENT → $(SEMVER)"; \ + fi; \ + fi + +build: sync-version go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY) . -install: +install: sync-version go install $(GOFLAGS) -ldflags '$(LDFLAGS)' . @echo "Installed to $$(go env GOPATH)/bin/$(BINARY)" @echo "Ensure $$(go env GOPATH)/bin is in your PATH" From e15ad3906226a34633550c1e240b2471cec86d38 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Sun, 5 Apr 2026 23:02:15 -0400 Subject: [PATCH 2/3] Fix tri-review findings: fallback pipeline, silent failures, tmp cleanup - Fix broken fallback: use subshell so 0.0.0 actually fires when no tags - Warn instead of silent no-op when jq is missing or no tags found - Skip writing 0.0.0 to avoid downgrading real versions - Clean up .tmp file on jq/mv failure - Use order-only prerequisites to prevent parallel-make races --- src/Makefile | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index d8b3139..a737d9c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ BINARY := devkit VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") -# Clean version for plugin.json (strip leading v, git suffix — just semver) -SEMVER := $(shell git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.0") +# Clean semver for plugin.json (strip leading v — fallback only if no tags exist) +SEMVER := $(shell V=$$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//'); echo "$${V:-0.0.0}") LDFLAGS := -s -w -X main.version=$(VERSION) GOFLAGS := -trimpath PLUGIN_JSON := $(CURDIR)/../.claude-plugin/plugin.json @@ -11,18 +11,26 @@ PLUGIN_JSON := $(CURDIR)/../.claude-plugin/plugin.json all: check build sync-version: - @if [ -f "$(PLUGIN_JSON)" ] && command -v jq >/dev/null 2>&1; then \ + @if ! command -v jq >/dev/null 2>&1; then \ + echo "WARNING: jq not found, skipping plugin.json version sync"; \ + elif [ ! -f "$(PLUGIN_JSON)" ]; then \ + echo "WARNING: $(PLUGIN_JSON) not found, skipping version sync"; \ + elif [ "$(SEMVER)" = "0.0.0" ]; then \ + echo "WARNING: no git tag found, skipping plugin.json version sync"; \ + else \ CURRENT=$$(jq -r '.version' "$(PLUGIN_JSON)"); \ if [ "$$CURRENT" != "$(SEMVER)" ]; then \ - jq --arg v "$(SEMVER)" '.version = $$v' "$(PLUGIN_JSON)" > "$(PLUGIN_JSON).tmp" && mv "$(PLUGIN_JSON).tmp" "$(PLUGIN_JSON)"; \ + jq --arg v "$(SEMVER)" '.version = $$v' "$(PLUGIN_JSON)" > "$(PLUGIN_JSON).tmp" \ + && mv "$(PLUGIN_JSON).tmp" "$(PLUGIN_JSON)" \ + || { rm -f "$(PLUGIN_JSON).tmp"; echo "ERROR: failed to sync plugin.json"; exit 1; }; \ echo "Synced plugin.json version: $$CURRENT → $(SEMVER)"; \ fi; \ fi -build: sync-version +build: | sync-version go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY) . -install: sync-version +install: | sync-version go install $(GOFLAGS) -ldflags '$(LDFLAGS)' . @echo "Installed to $$(go env GOPATH)/bin/$(BINARY)" @echo "Ensure $$(go env GOPATH)/bin is in your PATH" From 655e8e6c11da40f7402e58299c86580e507e830c Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Sun, 5 Apr 2026 23:04:30 -0400 Subject: [PATCH 3/3] Fix PR review findings: JSON validation, semver guard, ASCII output - Fail fast on malformed plugin.json instead of cascading to misleading error - Add semver format validation to reject non-semver git tags - Replace Unicode arrow with ASCII -> for CI/terminal portability --- src/Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index a737d9c..b535e2f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ BINARY := devkit VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") -# Clean semver for plugin.json (strip leading v — fallback only if no tags exist) +# Clean semver for plugin.json (strip leading v; falls back to 0.0.0 when untagged) SEMVER := $(shell V=$$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//'); echo "$${V:-0.0.0}") LDFLAGS := -s -w -X main.version=$(VERSION) GOFLAGS := -trimpath @@ -17,13 +17,16 @@ sync-version: echo "WARNING: $(PLUGIN_JSON) not found, skipping version sync"; \ elif [ "$(SEMVER)" = "0.0.0" ]; then \ echo "WARNING: no git tag found, skipping plugin.json version sync"; \ + elif ! echo "$(SEMVER)" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then \ + echo "WARNING: git tag is not valid semver ($(SEMVER)), skipping sync"; \ else \ - CURRENT=$$(jq -r '.version' "$(PLUGIN_JSON)"); \ + CURRENT=$$(jq -r '.version' "$(PLUGIN_JSON)" 2>/dev/null) || \ + { echo "ERROR: plugin.json is not valid JSON"; exit 1; }; \ if [ "$$CURRENT" != "$(SEMVER)" ]; then \ jq --arg v "$(SEMVER)" '.version = $$v' "$(PLUGIN_JSON)" > "$(PLUGIN_JSON).tmp" \ && mv "$(PLUGIN_JSON).tmp" "$(PLUGIN_JSON)" \ || { rm -f "$(PLUGIN_JSON).tmp"; echo "ERROR: failed to sync plugin.json"; exit 1; }; \ - echo "Synced plugin.json version: $$CURRENT → $(SEMVER)"; \ + echo "Synced plugin.json version: $$CURRENT -> $(SEMVER)"; \ fi; \ fi