From 04fc2ffc0d9cf1fde155e1dea405eee721184baf Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Thu, 9 Apr 2026 20:23:25 -0400 Subject: [PATCH 1/4] =?UTF-8?q?Add=20engine=20binary=20distribution=20?= =?UTF-8?q?=E2=80=94=20cross-compile,=20release,=20auto-bootstrap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Go engine was never distributed with the plugin, so all YAML workflows fell through to markdown fallbacks and Claude ran without determinism. - Add build-for/build-all targets to Makefile (linux, darwin, windows x amd64, arm64) - Rewrite release workflow: draft release → parallel matrix build → upload binaries → publish - Add scripts/install-engine.sh: auto-detect platform, download from GitHub releases - Wire bootstrap check into all 4 commands and 3 skills that invoke the engine - Remove all "Fallback (no engine)" markdown — engine is required, no honor system - Add engine health check to /devkit:status --- .github/workflows/release.yml | 109 +++++++++++++++++++++++++++++++--- commands/pr-ready.md | 12 +++- commands/status.md | 12 ++++ commands/tri-debug.md | 12 +++- commands/tri-review.md | 12 +++- commands/tri-security.md | 12 +++- scripts/install-engine.sh | 83 ++++++++++++++++++++++++++ skills/autoloop/SKILL.md | 19 +++--- skills/deep-research/SKILL.md | 21 ++----- skills/research/SKILL.md | 19 ++---- src/Makefile | 15 ++++- 11 files changed, 273 insertions(+), 53 deletions(-) create mode 100755 scripts/install-engine.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8cd1dae..7dd37a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,12 +5,18 @@ on: types: [closed] branches: [main] +permissions: + contents: write + jobs: - release: + # =========================================================================== + # Step 1: Determine version and commit bump + # =========================================================================== + version: if: github.event.pull_request.merged == true runs-on: ubuntu-latest - permissions: - contents: write + outputs: + version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 @@ -72,7 +78,6 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add .claude-plugin/plugin.json - # Only commit if there are changes if git diff --cached --quiet; then echo "Version files already at $VERSION — no commit needed" else @@ -80,13 +85,23 @@ jobs: git push fi + # =========================================================================== + # Step 2: Create draft release + # =========================================================================== + create-release: + needs: version + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.create_release.outputs.release_created }} + steps: + - uses: actions/checkout@v4 + - name: Generate release notes id: notes run: | - VERSION="${{ steps.version.outputs.version }}" + VERSION="${{ needs.version.outputs.version }}" NOTES=$(awk "/^## ${VERSION}$/,/^## /{if(/^## ${VERSION}$/)next; if(/^## /)exit; print}" CHANGELOG.md 2>/dev/null) if [ -z "$NOTES" ]; then - # Try with v prefix NOTES=$(awk "/^## v${VERSION}$/,/^## /{if(/^## v${VERSION}$/)next; if(/^## /)exit; print}" CHANGELOG.md 2>/dev/null) fi if [ -z "$NOTES" ]; then @@ -98,15 +113,91 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" - - name: Create tag and release + - name: Create draft release + id: create_release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - TAG="v${{ steps.version.outputs.version }}" + TAG="v${{ needs.version.outputs.version }}" if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "$TAG"; then echo "Tag $TAG already exists — skipping release" + echo "release_created=false" >> "$GITHUB_OUTPUT" exit 0 fi git tag "$TAG" git push origin "$TAG" - gh release create "$TAG" --title "$TAG" --notes "${{ steps.notes.outputs.notes }}" + gh release create "$TAG" \ + --title "$TAG" \ + --draft \ + --notes "${{ steps.notes.outputs.notes }}" + echo "release_created=true" >> "$GITHUB_OUTPUT" + + # =========================================================================== + # Step 3: Build binaries (parallel matrix) + # =========================================================================== + binaries: + needs: [version, create-release] + if: needs.create-release.outputs.release_created == 'true' + runs-on: ubuntu-latest + defaults: + run: + working-directory: src + strategy: + matrix: + os: [linux, darwin, windows] + arch: [amd64, arm64] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: src/go.mod + cache-dependency-path: src/go.sum + + - name: Build binary + run: make build-for GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} + + - name: Upload release asset + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + EXT="" + if [ "${{ matrix.os }}" = "windows" ]; then EXT=".exe"; fi + gh release upload "v${{ needs.version.outputs.version }}" \ + "bin/devkit-${{ matrix.os }}-${{ matrix.arch }}${EXT}" \ + --clobber + working-directory: src + + # =========================================================================== + # Step 4: Publish release + # =========================================================================== + publish: + needs: [version, create-release, binaries] + if: needs.create-release.outputs.release_created == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release edit "v${{ needs.version.outputs.version }}" --draft=false + + # =========================================================================== + # Cleanup on failure + # =========================================================================== + cleanup-on-failure: + needs: [version, create-release, binaries, publish] + if: always() && (needs.binaries.result == 'failure' || needs.publish.result == 'failure') && needs.create-release.outputs.release_created == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Delete draft release on failure + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="v${{ needs.version.outputs.version }}" + echo "Cleaning up draft release $TAG due to workflow failure" + gh release delete "$TAG" --yes || true + git push origin --delete "$TAG" || true diff --git a/commands/pr-ready.md b/commands/pr-ready.md index e0d4aa9..03635bc 100644 --- a/commands/pr-ready.md +++ b/commands/pr-ready.md @@ -2,4 +2,14 @@ description: Full PR preparation pipeline — validate branch, DRY review, lint, test, security, changelog, create PR. --- -Run `devkit workflow run pr-ready` to execute the deterministic PR preparation workflow. +Ensure the devkit engine is installed, then run the workflow: + +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +``` + +```bash +devkit workflow run pr-ready +``` + +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. diff --git a/commands/status.md b/commands/status.md index e4d813c..e438287 100644 --- a/commands/status.md +++ b/commands/status.md @@ -16,6 +16,18 @@ echo -n "codex plugin: " && (/codex:status >/dev/null 2>&1 && echo "installed" | echo -n "gemini plugin: " && (/gemini:status >/dev/null 2>&1 && echo "installed" || echo "not installed") ``` +### Devkit Engine + +```bash +echo "=== Devkit Engine ===" +if command -v devkit >/dev/null 2>&1; then + echo "engine: ✓ installed ($(devkit --version 2>/dev/null || echo 'unknown version'))" +else + echo "engine: ✗ NOT INSTALLED — workflows will not run deterministically" + echo " install: bash scripts/install-engine.sh" +fi +``` + ### External CLIs ```bash diff --git a/commands/tri-debug.md b/commands/tri-debug.md index 943d4ea..fec6187 100644 --- a/commands/tri-debug.md +++ b/commands/tri-debug.md @@ -2,4 +2,14 @@ description: Triple-agent debugging — independent root-cause hypotheses from Claude, Codex, and Gemini, then consensus fix. --- -Run `devkit workflow run tri-debug` to execute the deterministic debug workflow. +Ensure the devkit engine is installed, then run the workflow: + +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +``` + +```bash +devkit workflow run tri-debug +``` + +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. diff --git a/commands/tri-review.md b/commands/tri-review.md index d435034..2a2c0ce 100644 --- a/commands/tri-review.md +++ b/commands/tri-review.md @@ -2,4 +2,14 @@ description: Triple-agent code review — dispatches to Claude, Codex, and Gemini in parallel, consolidates findings. --- -Run `devkit workflow run tri-review` to execute the deterministic review workflow. +Ensure the devkit engine is installed, then run the workflow: + +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +``` + +```bash +devkit workflow run tri-review +``` + +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. diff --git a/commands/tri-security.md b/commands/tri-security.md index 647ee74..9db4977 100644 --- a/commands/tri-security.md +++ b/commands/tri-security.md @@ -2,4 +2,14 @@ description: Triple-agent security audit — independent security reviews from Claude, Codex, and Gemini, consolidated with severity ranking. --- -Run `devkit workflow run tri-security` to execute the deterministic security audit workflow. +Ensure the devkit engine is installed, then run the workflow: + +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +``` + +```bash +devkit workflow run tri-security +``` + +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. diff --git a/scripts/install-engine.sh b/scripts/install-engine.sh new file mode 100755 index 0000000..94c4f4f --- /dev/null +++ b/scripts/install-engine.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Install the devkit engine binary from GitHub releases. +# Called by skills/commands before running `devkit workflow run`. +# +# Usage: ./scripts/install-engine.sh [--check] +# --check Exit 0 if devkit is already on PATH, 1 if not (no install) + +set -euo pipefail + +REPO="5uck1ess/devkit" +BINARY="devkit" +INSTALL_DIR="${DEVKIT_INSTALL_DIR:-/usr/local/bin}" + +# --check mode: just test if binary exists +if [ "${1:-}" = "--check" ]; then + command -v "$BINARY" >/dev/null 2>&1 + exit $? +fi + +# Skip if already installed +if command -v "$BINARY" >/dev/null 2>&1; then + echo "devkit engine already installed: $(command -v "$BINARY")" + exit 0 +fi + +# Detect platform +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + arm64) ARCH="arm64" ;; + *) echo "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +EXT="" +case "$OS" in + linux|darwin) ;; + mingw*|msys*|cygwin*) OS="windows"; EXT=".exe" ;; + *) printf "Unsupported OS: %s\n" "$OS"; exit 1 ;; +esac + +ASSET="${BINARY}-${OS}-${ARCH}${EXT}" +URL="https://github.com/${REPO}/releases/latest/download/${ASSET}" + +printf "Downloading devkit engine (%s/%s)...\n" "$OS" "$ARCH" +TMPFILE="$(mktemp)" +trap 'rm -f "$TMPFILE"' EXIT + +if command -v curl >/dev/null 2>&1; then + curl -fsSL "$URL" -o "$TMPFILE" +elif command -v wget >/dev/null 2>&1; then + wget -q "$URL" -O "$TMPFILE" +else + printf "Error: curl or wget required\n" + exit 1 +fi + +chmod +x "$TMPFILE" + +# Windows: install to user's local bin +if [[ "$OS" == "windows" ]]; then + WIN_DIR="${LOCALAPPDATA:-$HOME/AppData/Local}/devkit" + mkdir -p "$WIN_DIR" + mv "$TMPFILE" "${WIN_DIR}/${BINARY}${EXT}" + printf "Installed to %s/%s%s\n" "$WIN_DIR" "$BINARY" "$EXT" + printf "Add to PATH: %s\n" "$WIN_DIR" + exit 0 +fi + +# Unix: try INSTALL_DIR, fall back to ~/.local/bin +if [[ -w "$INSTALL_DIR" ]]; then + mv "$TMPFILE" "${INSTALL_DIR}/${BINARY}" + printf "Installed to %s/%s\n" "$INSTALL_DIR" "$BINARY" +elif mkdir -p "$HOME/.local/bin" 2>/dev/null; then + mv "$TMPFILE" "$HOME/.local/bin/${BINARY}" + printf "Installed to %s/.local/bin/%s\n" "$HOME" "$BINARY" + printf "Add to PATH: export PATH=\"\$HOME/.local/bin:\$PATH\"\n" +else + printf "Cannot write to %s or ~/.local/bin\n" "$INSTALL_DIR" + printf "Run with sudo or set DEVKIT_INSTALL_DIR to a writable directory\n" + exit 1 +fi diff --git a/skills/autoloop/SKILL.md b/skills/autoloop/SKILL.md index 8c29672..5206657 100644 --- a/skills/autoloop/SKILL.md +++ b/skills/autoloop/SKILL.md @@ -48,22 +48,19 @@ If the user says "everything" or skips, leave scope open. ## Invoke the Workflow -Assemble the input as a single string and invoke: +Ensure the devkit engine is installed: +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" ``` + +Assemble the input as a single string and invoke: + +```bash devkit workflow autoloop " | metric: | direction: -is-better | iterations: | scope: " ``` -Or if the Go harness is not available, follow the workflow steps manually: - -1. **Baseline** — run the metric command, record the starting number -2. **Audit** — analyze codebase, pick single highest-impact hypothesis -3. **Fix** — make the recommended change (minimal, focused) -4. **Measure** — run the same metric command again -5. **Compare** — IMPROVED or REGRESSED based on direction -6. **Keep** (if improved) — git commit, update scratchpad, loop back to audit -7. **Revert** (if regressed) — git checkout -- , update scratchpad, loop back to audit -8. **Report** — final summary with kept changes, reverted attempts, net improvement +If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps — the engine is required for determinism. ## Rules diff --git a/skills/deep-research/SKILL.md b/skills/deep-research/SKILL.md index 28bd45e..a32ee75 100644 --- a/skills/deep-research/SKILL.md +++ b/skills/deep-research/SKILL.md @@ -11,28 +11,19 @@ Costs more tokens (~400k budget) but produces higher-confidence results by activ ## Invoke -Run the workflow via the devkit engine: +Ensure the devkit engine is installed, then run the workflow: +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" ``` + +```bash devkit workflow run deep-research "{input}" ``` The YAML workflow (`workflows/deep-research.yml`) enforces the full ACH sequence deterministically. Claude handles thinking within each step; the engine owns the order. -## Fallback (no engine) - -If `devkit workflow` is not available, follow these steps manually: - -1. **Clarify** — Use `AskUserQuestion` to sharpen the question; ask what a wrong answer would cost -2. **Discover perspectives** — Search for 2-3 overview articles; extract schools of thought, key voices, debates; summarize immediately -3. **Decompose** — 5-8 sub-questions with retrieval goals and perspective labels; at least 2 must seek disconfirming evidence -4. **Search** — Parallel fan-out using `researcher` agent (max 3 per batch); collect titles, URLs, snippets, dates -5. **Extract claims** — Fetch top 5-8 URLs via Jina Reader; extract atomic claims (3-8 per source); do NOT carry raw content forward -6. **Hypotheses** — Generate 2-4 competing hypotheses; include at least one contrarian; each must be testable -7. **Directed disconfirmation** — For EACH hypothesis, search for evidence that DISPROVES it; this is the critical ACH step -8. **Evidence matrix + sensitivity check** — Rows = claims, columns = hypotheses; mark CC/C/N/I/II; score by FEWEST inconsistencies (not most consistencies); identify linchpin evidence — what single fact, if wrong, changes the conclusion? -9. **Self-critique** — Did you genuinely try to disprove? Missed perspectives? Over-weighting a source? One more search round if gaps found (loop max 2) -10. **Synthesize** — Direct answer with confidence (HIGH/MEDIUM/LOW) → hypotheses evaluated → evidence matrix → key findings → sensitivity analysis → recommendation +If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps — the engine is required for determinism. ## Rules diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index b4434bc..4b1600c 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -9,26 +9,19 @@ Deterministic research workflow: clarify → decompose → parallel search → s ## Invoke -Run the workflow via the devkit engine: +Ensure the devkit engine is installed, then run the workflow: +```bash +command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" ``` + +```bash devkit workflow run research "{input}" ``` The YAML workflow (`workflows/research.yml`) enforces the step sequence deterministically. Claude handles thinking within each step; the engine owns the order. -## Fallback (no engine) - -If `devkit workflow` is not available, follow these steps manually. Token budget: ~200k. Early exit if first search pass clearly answers the question. - -1. **Clarify** — Use `AskUserQuestion` to sharpen the question before searching -2. **Decompose** — Break into 3-5 sub-questions with explicit retrieval goals; include at least one disconfirming query -3. **Search** — Launch searches in parallel using the `researcher` agent (max 3); collect titles, URLs, snippets -4. **Summarize** — Fetch top URLs via Jina Reader (`WebFetch https://r.jina.ai/{url}`); extract 3-5 claims per source immediately; do NOT carry raw content forward -5. **Corroborate** — Mark each claim CONFIRMED (2+ sources) / UNCORROBORATED (1 source) / CONTESTED (sources disagree) -6. **Escalation check** — Ask the user to upgrade to `/devkit:deep-research` if ANY: 3+ CONTESTED claims, high-stakes domain, user expressed uncertainty, or most claims UNCORROBORATED. Never auto-escalate. -7. **Follow-up** — For UNCORROBORATED/CONTESTED claims, run one targeted search to resolve (loop max 2) -8. **Synthesize** — Direct answer → key findings with corroboration status → tradeoffs → open questions → recommendation with confidence level +If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps — the engine is required for determinism. ## Rules diff --git a/src/Makefile b/src/Makefile index b535e2f..d411bab 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,7 +6,7 @@ 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 sync-version +.PHONY: build build-for build-all install link clean test vet fmt check all sync-version all: check build @@ -33,6 +33,19 @@ sync-version: build: | sync-version go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY) . +build-for: ## Cross-compile for specified GOOS/GOARCH + $(eval EXT := $(if $(filter windows,$(GOOS)),.exe,)) + @CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY)-$(GOOS)-$(GOARCH)$(EXT) . + @echo "Built: bin/$(BINARY)-$(GOOS)-$(GOARCH)$(EXT)" + +build-all: ## Build all platform binaries + @$(MAKE) build-for GOOS=linux GOARCH=amd64 + @$(MAKE) build-for GOOS=linux GOARCH=arm64 + @$(MAKE) build-for GOOS=darwin GOARCH=amd64 + @$(MAKE) build-for GOOS=darwin GOARCH=arm64 + @$(MAKE) build-for GOOS=windows GOARCH=amd64 + @$(MAKE) build-for GOOS=windows GOARCH=arm64 + install: | sync-version go install $(GOFLAGS) -ldflags '$(LDFLAGS)' . @echo "Installed to $$(go env GOPATH)/bin/$(BINARY)" From dc6d79795c30d27ef1c45a15081f32a091f603be Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Thu, 9 Apr 2026 20:34:34 -0400 Subject: [PATCH 2/4] Fix all review findings from mega PR review (6 agents) Release workflow: - Fix tag-before-release race: inline cleanup if gh release create fails - Fix shallow checkout: fetch-depth 0, ref main, tag version-bumped commit - Fix PR title injection: pass notes via env var, not ${{ }} interpolation - Fix cleanup conditions: handle cancelled state, use ::warning:: annotations - Add checksums.txt generation in publish step Install script: - Add checksum verification (sha256sum/shasum) against checksums.txt - Use curl -fSL (capital S) to keep error messages visible - Validate downloaded file is non-empty before installing - Export PATH after ~/.local/bin fallback so binary is immediately usable - Remove 2>/dev/null from mkdir to surface failure reasons - Add explicit error messages on mv failures - Add --upgrade flag for re-downloading Makefile: - Replace $(eval) footgun with shell conditional for .exe extension - Add sync-version prerequisite to build-for target Bootstrap (DRY fix): - Extract shared ensure-engine.sh wrapper script - All 7 commands/skills now call ensure-engine.sh instead of inline find - ensure-engine.sh finds install-engine.sh relative to itself first - Guard against empty find result with explicit error - Fix relative paths in fallback messages to absolute GitHub URLs --- .github/workflows/release.yml | 58 ++++++++++++++++++++------ commands/pr-ready.md | 4 +- commands/status.md | 3 +- commands/tri-debug.md | 4 +- commands/tri-review.md | 4 +- commands/tri-security.md | 4 +- scripts/ensure-engine.sh | 28 +++++++++++++ scripts/install-engine.sh | 76 ++++++++++++++++++++++++++--------- skills/autoloop/SKILL.md | 4 +- skills/deep-research/SKILL.md | 4 +- skills/research/SKILL.md | 4 +- src/Makefile | 8 ++-- 12 files changed, 151 insertions(+), 50 deletions(-) create mode 100755 scripts/ensure-engine.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7dd37a8..ca2afea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,7 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} + commit_sha: ${{ steps.bump.outputs.commit_sha }} steps: - uses: actions/checkout@v4 @@ -73,6 +74,7 @@ jobs: jq --arg v "$VERSION" '.version = $v' .claude-plugin/plugin.json > tmp.json && mv tmp.json .claude-plugin/plugin.json - name: Commit version bump + id: bump run: | VERSION="${{ steps.version.outputs.version }}" git config user.name "github-actions[bot]" @@ -84,9 +86,10 @@ jobs: git commit -m "bump to v${VERSION}" git push fi + echo "commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" # =========================================================================== - # Step 2: Create draft release + # Step 2: Create draft release (tag + release atomically) # =========================================================================== create-release: needs: version @@ -95,9 +98,14 @@ jobs: release_created: ${{ steps.create_release.outputs.release_created }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main - name: Generate release notes id: notes + env: + PR_TITLE: ${{ github.event.pull_request.title }} run: | VERSION="${{ needs.version.outputs.version }}" NOTES=$(awk "/^## ${VERSION}$/,/^## /{if(/^## ${VERSION}$/)next; if(/^## /)exit; print}" CHANGELOG.md 2>/dev/null) @@ -105,7 +113,7 @@ jobs: NOTES=$(awk "/^## v${VERSION}$/,/^## /{if(/^## v${VERSION}$/)next; if(/^## /)exit; print}" CHANGELOG.md 2>/dev/null) fi if [ -z "$NOTES" ]; then - NOTES="Merged: ${{ github.event.pull_request.title }}" + NOTES="Merged: ${PR_TITLE}" fi { echo "notes<> "$GITHUB_OUTPUT" exit 0 fi - git tag "$TAG" + # Tag the version-bumped commit, not the stale checkout + git tag "$TAG" "$COMMIT" git push origin "$TAG" - gh release create "$TAG" \ - --title "$TAG" \ - --draft \ - --notes "${{ steps.notes.outputs.notes }}" + # Create release; clean up tag on failure + if ! gh release create "$TAG" --title "$TAG" --draft --notes "$RELEASE_NOTES"; then + echo "::warning::Release creation failed — cleaning up orphaned tag" + git push origin --delete "$TAG" || true + echo "release_created=false" >> "$GITHUB_OUTPUT" + exit 1 + fi echo "release_created=true" >> "$GITHUB_OUTPUT" # =========================================================================== @@ -163,13 +177,15 @@ jobs: run: | EXT="" if [ "${{ matrix.os }}" = "windows" ]; then EXT=".exe"; fi + ASSET="bin/devkit-${{ matrix.os }}-${{ matrix.arch }}${EXT}" + echo "Uploading ${ASSET}" gh release upload "v${{ needs.version.outputs.version }}" \ - "bin/devkit-${{ matrix.os }}-${{ matrix.arch }}${EXT}" \ + "$ASSET" \ --clobber working-directory: src # =========================================================================== - # Step 4: Publish release + # Step 4: Generate checksums and publish release # =========================================================================== publish: needs: [version, create-release, binaries] @@ -178,6 +194,18 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Download all release assets and generate checksums + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="v${{ needs.version.outputs.version }}" + mkdir -p /tmp/release-assets + gh release download "$TAG" --dir /tmp/release-assets + cd /tmp/release-assets + sha256sum devkit-* > checksums.txt + cat checksums.txt + gh release upload "$TAG" checksums.txt --clobber + - name: Publish release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -188,7 +216,11 @@ jobs: # =========================================================================== cleanup-on-failure: needs: [version, create-release, binaries, publish] - if: always() && (needs.binaries.result == 'failure' || needs.publish.result == 'failure') && needs.create-release.outputs.release_created == 'true' + if: >- + always() && + needs.create-release.outputs.release_created == 'true' && + (needs.binaries.result == 'failure' || needs.binaries.result == 'cancelled' || + needs.publish.result == 'failure' || needs.publish.result == 'cancelled') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -198,6 +230,6 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="v${{ needs.version.outputs.version }}" - echo "Cleaning up draft release $TAG due to workflow failure" - gh release delete "$TAG" --yes || true - git push origin --delete "$TAG" || true + echo "::warning::Cleaning up draft release $TAG due to workflow failure" + gh release delete "$TAG" --yes || echo "::warning::Failed to delete release $TAG — manual cleanup required" + git push origin --delete "$TAG" || echo "::warning::Failed to delete tag $TAG — manual cleanup required" diff --git a/commands/pr-ready.md b/commands/pr-ready.md index 03635bc..27cf07b 100644 --- a/commands/pr-ready.md +++ b/commands/pr-ready.md @@ -5,11 +5,11 @@ description: Full PR preparation pipeline — validate branch, DRY review, lint, Ensure the devkit engine is installed, then run the workflow: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` ```bash devkit workflow run pr-ready ``` -If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps. diff --git a/commands/status.md b/commands/status.md index e438287..829cc04 100644 --- a/commands/status.md +++ b/commands/status.md @@ -24,7 +24,8 @@ if command -v devkit >/dev/null 2>&1; then echo "engine: ✓ installed ($(devkit --version 2>/dev/null || echo 'unknown version'))" else echo "engine: ✗ NOT INSTALLED — workflows will not run deterministically" - echo " install: bash scripts/install-engine.sh" + echo " install: bash \"\$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)\"" + echo " or download from: https://github.com/5uck1ess/devkit/releases" fi ``` diff --git a/commands/tri-debug.md b/commands/tri-debug.md index fec6187..98b9eff 100644 --- a/commands/tri-debug.md +++ b/commands/tri-debug.md @@ -5,11 +5,11 @@ description: Triple-agent debugging — independent root-cause hypotheses from C Ensure the devkit engine is installed, then run the workflow: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` ```bash devkit workflow run tri-debug ``` -If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps. diff --git a/commands/tri-review.md b/commands/tri-review.md index 2a2c0ce..2876c82 100644 --- a/commands/tri-review.md +++ b/commands/tri-review.md @@ -5,11 +5,11 @@ description: Triple-agent code review — dispatches to Claude, Codex, and Gemin Ensure the devkit engine is installed, then run the workflow: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` ```bash devkit workflow run tri-review ``` -If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps. diff --git a/commands/tri-security.md b/commands/tri-security.md index 9db4977..1ccf263 100644 --- a/commands/tri-security.md +++ b/commands/tri-security.md @@ -5,11 +5,11 @@ description: Triple-agent security audit — independent security reviews from C Ensure the devkit engine is installed, then run the workflow: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` ```bash devkit workflow run tri-security ``` -If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps. +If the engine cannot be installed (no network, no write access), tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps. diff --git a/scripts/ensure-engine.sh b/scripts/ensure-engine.sh new file mode 100755 index 0000000..43e2815 --- /dev/null +++ b/scripts/ensure-engine.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Ensure the devkit engine binary is on PATH. +# Finds and runs install-engine.sh if devkit is not installed. +# Called by commands and skills before `devkit workflow run`. + +set -euo pipefail + +if command -v devkit >/dev/null 2>&1; then + exit 0 +fi + +# Find install-engine.sh relative to this script (works when called from plugin cache) +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INSTALLER="${SCRIPT_DIR}/install-engine.sh" + +if [[ -f "$INSTALLER" ]]; then + bash "$INSTALLER" +else + # Fallback: search plugin cache + INSTALLER=$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1) + if [[ -n "$INSTALLER" ]]; then + bash "$INSTALLER" + else + printf "Cannot find install-engine.sh.\n" + printf "Install manually: https://github.com/5uck1ess/devkit/releases\n" + exit 1 + fi +fi diff --git a/scripts/install-engine.sh b/scripts/install-engine.sh index 94c4f4f..8df0219 100755 --- a/scripts/install-engine.sh +++ b/scripts/install-engine.sh @@ -2,8 +2,9 @@ # Install the devkit engine binary from GitHub releases. # Called by skills/commands before running `devkit workflow run`. # -# Usage: ./scripts/install-engine.sh [--check] -# --check Exit 0 if devkit is already on PATH, 1 if not (no install) +# Usage: ./scripts/install-engine.sh [--check] [--upgrade] +# --check Exit 0 if devkit is on PATH, 1 if not (no install) +# --upgrade Re-download even if devkit is already installed set -euo pipefail @@ -12,14 +13,14 @@ BINARY="devkit" INSTALL_DIR="${DEVKIT_INSTALL_DIR:-/usr/local/bin}" # --check mode: just test if binary exists -if [ "${1:-}" = "--check" ]; then +if [[ "${1:-}" == "--check" ]]; then command -v "$BINARY" >/dev/null 2>&1 exit $? fi -# Skip if already installed -if command -v "$BINARY" >/dev/null 2>&1; then - echo "devkit engine already installed: $(command -v "$BINARY")" +# Skip if already installed (unless --upgrade) +if [[ "${1:-}" != "--upgrade" ]] && command -v "$BINARY" >/dev/null 2>&1; then + printf "devkit engine already installed: %s\n" "$(command -v "$BINARY")" exit 0 fi @@ -30,39 +31,73 @@ case "$ARCH" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; arm64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH"; exit 1 ;; + *) printf "Unsupported architecture: %s\n" "$ARCH"; exit 1 ;; esac EXT="" case "$OS" in linux|darwin) ;; + # Windows: only reachable via MSYS2, Git Bash, or Cygwin mingw*|msys*|cygwin*) OS="windows"; EXT=".exe" ;; *) printf "Unsupported OS: %s\n" "$OS"; exit 1 ;; esac ASSET="${BINARY}-${OS}-${ARCH}${EXT}" -URL="https://github.com/${REPO}/releases/latest/download/${ASSET}" +BASE_URL="https://github.com/${REPO}/releases/latest/download" printf "Downloading devkit engine (%s/%s)...\n" "$OS" "$ARCH" -TMPFILE="$(mktemp)" -trap 'rm -f "$TMPFILE"' EXIT +TMPDIR_CLEAN="$(mktemp -d)" +trap 'rm -rf "$TMPDIR_CLEAN"' EXIT +TMPFILE="${TMPDIR_CLEAN}/${ASSET}" +TMPCHECKSUM="${TMPDIR_CLEAN}/checksums.txt" +# Download binary — use -fSL (capital S keeps error messages visible) if command -v curl >/dev/null 2>&1; then - curl -fsSL "$URL" -o "$TMPFILE" + curl -fSL "${BASE_URL}/${ASSET}" -o "$TMPFILE" || { printf "Download failed: %s/%s\nCheck network and that the release exists.\n" "$BASE_URL" "$ASSET"; exit 1; } + curl -fSL "${BASE_URL}/checksums.txt" -o "$TMPCHECKSUM" 2>/dev/null || TMPCHECKSUM="" elif command -v wget >/dev/null 2>&1; then - wget -q "$URL" -O "$TMPFILE" + wget -q "${BASE_URL}/${ASSET}" -O "$TMPFILE" || { printf "Download failed: %s/%s\nCheck network and that the release exists.\n" "$BASE_URL" "$ASSET"; exit 1; } + wget -q "${BASE_URL}/checksums.txt" -O "$TMPCHECKSUM" 2>/dev/null || TMPCHECKSUM="" else printf "Error: curl or wget required\n" exit 1 fi +# Validate download is non-empty +if [[ ! -s "$TMPFILE" ]]; then + printf "Downloaded file is empty — release may not exist for %s/%s\n" "$OS" "$ARCH" + exit 1 +fi + +# Verify checksum if checksums.txt was downloaded +if [[ -n "$TMPCHECKSUM" ]] && [[ -s "$TMPCHECKSUM" ]]; then + EXPECTED=$(grep "$ASSET" "$TMPCHECKSUM" | awk '{print $1}') + if [[ -n "$EXPECTED" ]]; then + if command -v sha256sum >/dev/null 2>&1; then + ACTUAL=$(sha256sum "$TMPFILE" | awk '{print $1}') + elif command -v shasum >/dev/null 2>&1; then + ACTUAL=$(shasum -a 256 "$TMPFILE" | awk '{print $1}') + else + printf "Warning: cannot verify checksum (no sha256sum or shasum)\n" + ACTUAL="$EXPECTED" + fi + if [[ "$EXPECTED" != "$ACTUAL" ]]; then + printf "Checksum mismatch! Expected %s, got %s\n" "$EXPECTED" "$ACTUAL" + exit 1 + fi + printf "Checksum verified.\n" + fi +else + printf "Warning: checksums.txt not available — skipping integrity check\n" +fi + chmod +x "$TMPFILE" -# Windows: install to user's local bin +# Windows: install to user's local bin (MSYS2/Git Bash/Cygwin only) if [[ "$OS" == "windows" ]]; then WIN_DIR="${LOCALAPPDATA:-$HOME/AppData/Local}/devkit" mkdir -p "$WIN_DIR" - mv "$TMPFILE" "${WIN_DIR}/${BINARY}${EXT}" + mv "$TMPFILE" "${WIN_DIR}/${BINARY}${EXT}" || { printf "Failed to install to %s\n" "$WIN_DIR"; exit 1; } printf "Installed to %s/%s%s\n" "$WIN_DIR" "$BINARY" "$EXT" printf "Add to PATH: %s\n" "$WIN_DIR" exit 0 @@ -70,12 +105,17 @@ fi # Unix: try INSTALL_DIR, fall back to ~/.local/bin if [[ -w "$INSTALL_DIR" ]]; then - mv "$TMPFILE" "${INSTALL_DIR}/${BINARY}" + mv "$TMPFILE" "${INSTALL_DIR}/${BINARY}" || { printf "Failed to install to %s\n" "$INSTALL_DIR"; exit 1; } printf "Installed to %s/%s\n" "$INSTALL_DIR" "$BINARY" -elif mkdir -p "$HOME/.local/bin" 2>/dev/null; then - mv "$TMPFILE" "$HOME/.local/bin/${BINARY}" +elif mkdir -p "$HOME/.local/bin"; then + mv "$TMPFILE" "$HOME/.local/bin/${BINARY}" || { printf "Failed to install to %s/.local/bin\n" "$HOME"; exit 1; } printf "Installed to %s/.local/bin/%s\n" "$HOME" "$BINARY" - printf "Add to PATH: export PATH=\"\$HOME/.local/bin:\$PATH\"\n" + # Make binary available in current session + export PATH="$HOME/.local/bin:$PATH" + if ! command -v "$BINARY" >/dev/null 2>&1; then + printf "Warning: installed to ~/.local/bin but it's not on PATH.\n" + printf "Add to your shell profile: export PATH=\"\$HOME/.local/bin:\$PATH\"\n" + fi else printf "Cannot write to %s or ~/.local/bin\n" "$INSTALL_DIR" printf "Run with sudo or set DEVKIT_INSTALL_DIR to a writable directory\n" diff --git a/skills/autoloop/SKILL.md b/skills/autoloop/SKILL.md index 5206657..3b480cc 100644 --- a/skills/autoloop/SKILL.md +++ b/skills/autoloop/SKILL.md @@ -51,7 +51,7 @@ If the user says "everything" or skips, leave scope open. Ensure the devkit engine is installed: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` Assemble the input as a single string and invoke: @@ -60,7 +60,7 @@ Assemble the input as a single string and invoke: devkit workflow autoloop " | metric: | direction: -is-better | iterations: | scope: " ``` -If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps — the engine is required for determinism. +If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps — the engine is required for determinism. ## Rules diff --git a/skills/deep-research/SKILL.md b/skills/deep-research/SKILL.md index a32ee75..30f05b3 100644 --- a/skills/deep-research/SKILL.md +++ b/skills/deep-research/SKILL.md @@ -14,7 +14,7 @@ Costs more tokens (~400k budget) but produces higher-confidence results by activ Ensure the devkit engine is installed, then run the workflow: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` ```bash @@ -23,7 +23,7 @@ devkit workflow run deep-research "{input}" The YAML workflow (`workflows/deep-research.yml`) enforces the full ACH sequence deterministically. Claude handles thinking within each step; the engine owns the order. -If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps — the engine is required for determinism. +If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps — the engine is required for determinism. ## Rules diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index 4b1600c..11ca77e 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -12,7 +12,7 @@ Deterministic research workflow: clarify → decompose → parallel search → s Ensure the devkit engine is installed, then run the workflow: ```bash -command -v devkit >/dev/null 2>&1 || bash "$(dirname "$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1)")/install-engine.sh" +bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" ``` ```bash @@ -21,7 +21,7 @@ devkit workflow run research "{input}" The YAML workflow (`workflows/research.yml`) enforces the step sequence deterministically. Claude handles thinking within each step; the engine owns the order. -If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Run `bash scripts/install-engine.sh` manually." Do NOT fall back to manual steps — the engine is required for determinism. +If the engine cannot be installed, tell the user: "The devkit engine binary is required for deterministic workflow execution. Install manually from https://github.com/5uck1ess/devkit/releases" Do NOT fall back to manual steps — the engine is required for determinism. ## Rules diff --git a/src/Makefile b/src/Makefile index d411bab..8cc920b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,10 +33,10 @@ sync-version: build: | sync-version go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY) . -build-for: ## Cross-compile for specified GOOS/GOARCH - $(eval EXT := $(if $(filter windows,$(GOOS)),.exe,)) - @CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY)-$(GOOS)-$(GOARCH)$(EXT) . - @echo "Built: bin/$(BINARY)-$(GOOS)-$(GOARCH)$(EXT)" +build-for: | sync-version ## Cross-compile for specified GOOS/GOARCH + @EXT=""; [ "$(GOOS)" = "windows" ] && EXT=".exe"; \ + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY)-$(GOOS)-$(GOARCH)$${EXT} . && \ + echo "Built: bin/$(BINARY)-$(GOOS)-$(GOARCH)$${EXT}" build-all: ## Build all platform binaries @$(MAKE) build-for GOOS=linux GOARCH=amd64 From 7359d99b6e82600aa59173a025de01b8a7e60bdb Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Thu, 9 Apr 2026 20:40:58 -0400 Subject: [PATCH 3/4] Fix round 2 review findings + Windows path support Review fixes: - Checksum grep: use awk exact column match instead of substring grep - Guard empty find in all command/skill markdown (prevents bash "" hang) - Source install-engine.sh instead of subprocess (PATH exports propagate) - Remove 2>/dev/null from checksum download (surface failure reasons) - Post-install PATH verification in ensure-engine.sh Windows support: - Search APPDATA and LOCALAPPDATA plugin paths in ensure-engine.sh - Search Windows plugin paths in all command/skill find patterns - Check LOCALAPPDATA/devkit in post-install PATH scan --- commands/pr-ready.md | 2 +- commands/tri-debug.md | 2 +- commands/tri-review.md | 2 +- commands/tri-security.md | 2 +- scripts/ensure-engine.sh | 41 ++++++++++++++++++++++++++--------- scripts/install-engine.sh | 6 ++--- skills/autoloop/SKILL.md | 2 +- skills/deep-research/SKILL.md | 2 +- skills/research/SKILL.md | 2 +- 9 files changed, 41 insertions(+), 20 deletions(-) diff --git a/commands/pr-ready.md b/commands/pr-ready.md index 27cf07b..1fd7125 100644 --- a/commands/pr-ready.md +++ b/commands/pr-ready.md @@ -5,7 +5,7 @@ description: Full PR preparation pipeline — validate branch, DRY review, lint, Ensure the devkit engine is installed, then run the workflow: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` ```bash diff --git a/commands/tri-debug.md b/commands/tri-debug.md index 98b9eff..51ee5fb 100644 --- a/commands/tri-debug.md +++ b/commands/tri-debug.md @@ -5,7 +5,7 @@ description: Triple-agent debugging — independent root-cause hypotheses from C Ensure the devkit engine is installed, then run the workflow: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` ```bash diff --git a/commands/tri-review.md b/commands/tri-review.md index 2876c82..0ffc5f5 100644 --- a/commands/tri-review.md +++ b/commands/tri-review.md @@ -5,7 +5,7 @@ description: Triple-agent code review — dispatches to Claude, Codex, and Gemin Ensure the devkit engine is installed, then run the workflow: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` ```bash diff --git a/commands/tri-security.md b/commands/tri-security.md index 1ccf263..83cdb92 100644 --- a/commands/tri-security.md +++ b/commands/tri-security.md @@ -5,7 +5,7 @@ description: Triple-agent security audit — independent security reviews from C Ensure the devkit engine is installed, then run the workflow: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` ```bash diff --git a/scripts/ensure-engine.sh b/scripts/ensure-engine.sh index 43e2815..09710e6 100755 --- a/scripts/ensure-engine.sh +++ b/scripts/ensure-engine.sh @@ -13,16 +13,37 @@ fi SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" INSTALLER="${SCRIPT_DIR}/install-engine.sh" -if [[ -f "$INSTALLER" ]]; then - bash "$INSTALLER" -else - # Fallback: search plugin cache - INSTALLER=$(find ~/.claude/plugins -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1) - if [[ -n "$INSTALLER" ]]; then - bash "$INSTALLER" - else - printf "Cannot find install-engine.sh.\n" - printf "Install manually: https://github.com/5uck1ess/devkit/releases\n" +if [[ ! -f "$INSTALLER" ]]; then + # Fallback: search plugin cache (Unix: ~/.claude, Windows: $APPDATA/.claude) + for PLUGIN_ROOT in "$HOME/.claude/plugins" "${APPDATA:+$APPDATA/.claude/plugins}" "${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins}"; do + [[ -z "$PLUGIN_ROOT" ]] && continue + [[ -d "$PLUGIN_ROOT" ]] || continue + INSTALLER=$(find "$PLUGIN_ROOT" -path '*/devkit/scripts/install-engine.sh' 2>/dev/null | head -1) + [[ -n "$INSTALLER" ]] && break + done +fi + +if [[ -z "$INSTALLER" ]] || [[ ! -f "$INSTALLER" ]]; then + printf "Cannot find install-engine.sh.\n" + printf "Install manually: https://github.com/5uck1ess/devkit/releases\n" + exit 1 +fi + +# Source instead of subprocess so PATH exports propagate +# shellcheck disable=SC1090 +source "$INSTALLER" + +# Verify devkit is now available (handles ~/.local/bin PATH addition) +if ! command -v devkit >/dev/null 2>&1; then + # Last resort: check common install locations directly + for dir in /usr/local/bin "$HOME/.local/bin" "${LOCALAPPDATA:-}/devkit"; do + if [[ -x "${dir}/devkit" ]]; then + export PATH="${dir}:${PATH}" + break + fi + done + if ! command -v devkit >/dev/null 2>&1; then + printf "devkit installed but not on PATH. Add the install directory to your PATH.\n" exit 1 fi fi diff --git a/scripts/install-engine.sh b/scripts/install-engine.sh index 8df0219..278a96c 100755 --- a/scripts/install-engine.sh +++ b/scripts/install-engine.sh @@ -54,10 +54,10 @@ TMPCHECKSUM="${TMPDIR_CLEAN}/checksums.txt" # Download binary — use -fSL (capital S keeps error messages visible) if command -v curl >/dev/null 2>&1; then curl -fSL "${BASE_URL}/${ASSET}" -o "$TMPFILE" || { printf "Download failed: %s/%s\nCheck network and that the release exists.\n" "$BASE_URL" "$ASSET"; exit 1; } - curl -fSL "${BASE_URL}/checksums.txt" -o "$TMPCHECKSUM" 2>/dev/null || TMPCHECKSUM="" + curl -fSL "${BASE_URL}/checksums.txt" -o "$TMPCHECKSUM" || { printf "Warning: could not download checksums.txt\n"; TMPCHECKSUM=""; } elif command -v wget >/dev/null 2>&1; then wget -q "${BASE_URL}/${ASSET}" -O "$TMPFILE" || { printf "Download failed: %s/%s\nCheck network and that the release exists.\n" "$BASE_URL" "$ASSET"; exit 1; } - wget -q "${BASE_URL}/checksums.txt" -O "$TMPCHECKSUM" 2>/dev/null || TMPCHECKSUM="" + wget -q "${BASE_URL}/checksums.txt" -O "$TMPCHECKSUM" || { printf "Warning: could not download checksums.txt\n"; TMPCHECKSUM=""; } else printf "Error: curl or wget required\n" exit 1 @@ -71,7 +71,7 @@ fi # Verify checksum if checksums.txt was downloaded if [[ -n "$TMPCHECKSUM" ]] && [[ -s "$TMPCHECKSUM" ]]; then - EXPECTED=$(grep "$ASSET" "$TMPCHECKSUM" | awk '{print $1}') + EXPECTED=$(awk -v asset="$ASSET" '$2 == asset || $2 == "./"asset {print $1}' "$TMPCHECKSUM" | head -1) if [[ -n "$EXPECTED" ]]; then if command -v sha256sum >/dev/null 2>&1; then ACTUAL=$(sha256sum "$TMPFILE" | awk '{print $1}') diff --git a/skills/autoloop/SKILL.md b/skills/autoloop/SKILL.md index 3b480cc..02649c3 100644 --- a/skills/autoloop/SKILL.md +++ b/skills/autoloop/SKILL.md @@ -51,7 +51,7 @@ If the user says "everything" or skips, leave scope open. Ensure the devkit engine is installed: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` Assemble the input as a single string and invoke: diff --git a/skills/deep-research/SKILL.md b/skills/deep-research/SKILL.md index 30f05b3..a6031aa 100644 --- a/skills/deep-research/SKILL.md +++ b/skills/deep-research/SKILL.md @@ -14,7 +14,7 @@ Costs more tokens (~400k budget) but produces higher-confidence results by activ Ensure the devkit engine is installed, then run the workflow: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` ```bash diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index 11ca77e..2d9df91 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -12,7 +12,7 @@ Deterministic research workflow: clarify → decompose → parallel search → s Ensure the devkit engine is installed, then run the workflow: ```bash -bash "$(find ~/.claude/plugins -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)" +ENSURE="$(find ~/.claude/plugins ${APPDATA:+$APPDATA/.claude/plugins} ${LOCALAPPDATA:+$LOCALAPPDATA/.claude/plugins} -path '*/devkit/scripts/ensure-engine.sh' 2>/dev/null | head -1)"; [ -n "$ENSURE" ] && bash "$ENSURE" || { echo "devkit plugin not found — install from https://github.com/5uck1ess/devkit/releases"; exit 1; } ``` ```bash From af557f3707e4409a9d5ba7fd278897dc4f393879 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Thu, 9 Apr 2026 20:41:48 -0400 Subject: [PATCH 4/4] Fix Windows PATH persistence (PR comment feedback) - export PATH for current bash session after Windows install - Use PowerShell SetEnvironmentVariable to persist to user PATH (safer than setx which truncates PATH > 1024 chars) - Graceful fallback if powershell.exe not available --- scripts/install-engine.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/install-engine.sh b/scripts/install-engine.sh index 278a96c..de72c3c 100755 --- a/scripts/install-engine.sh +++ b/scripts/install-engine.sh @@ -99,7 +99,17 @@ if [[ "$OS" == "windows" ]]; then mkdir -p "$WIN_DIR" mv "$TMPFILE" "${WIN_DIR}/${BINARY}${EXT}" || { printf "Failed to install to %s\n" "$WIN_DIR"; exit 1; } printf "Installed to %s/%s%s\n" "$WIN_DIR" "$BINARY" "$EXT" - printf "Add to PATH: %s\n" "$WIN_DIR" + # Add to PATH for current session + export PATH="${WIN_DIR}:${PATH}" + # Persist to user PATH for future sessions via PowerShell + # (safer than setx which silently truncates PATH > 1024 chars) + if command -v powershell.exe >/dev/null 2>&1; then + powershell.exe -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';${WIN_DIR}', 'User')" 2>/dev/null \ + && printf "Added %s to user PATH (persistent).\n" "$WIN_DIR" \ + || printf "Warning: could not persist PATH. Add %s to your PATH manually.\n" "$WIN_DIR" + else + printf "Add to PATH: %s\n" "$WIN_DIR" + fi exit 0 fi