diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8cd1dae..ca2afea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,12 +5,19 @@ 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 }} + commit_sha: ${{ steps.bump.outputs.commit_sha }} steps: - uses: actions/checkout@v4 @@ -67,30 +74,46 @@ 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]" 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 git commit -m "bump to v${VERSION}" git push fi + echo "commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + # =========================================================================== + # Step 2: Create draft release (tag + release atomically) + # =========================================================================== + create-release: + needs: version + runs-on: ubuntu-latest + outputs: + 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="${{ 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 - NOTES="Merged: ${{ github.event.pull_request.title }}" + NOTES="Merged: ${PR_TITLE}" fi { echo "notes<> "$GITHUB_OUTPUT" - - name: Create tag and release + - name: Create draft release + id: create_release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_NOTES: ${{ steps.notes.outputs.notes }} run: | - TAG="v${{ steps.version.outputs.version }}" + TAG="v${{ needs.version.outputs.version }}" + COMMIT="${{ needs.version.outputs.commit_sha }}" 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" + # Tag the version-bumped commit, not the stale checkout + git tag "$TAG" "$COMMIT" git push origin "$TAG" - gh release create "$TAG" --title "$TAG" --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" + + # =========================================================================== + # 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 + ASSET="bin/devkit-${{ matrix.os }}-${{ matrix.arch }}${EXT}" + echo "Uploading ${ASSET}" + gh release upload "v${{ needs.version.outputs.version }}" \ + "$ASSET" \ + --clobber + working-directory: src + + # =========================================================================== + # Step 4: Generate checksums and 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: 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 }} + 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.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 + + - name: Delete draft release on failure + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="v${{ needs.version.outputs.version }}" + 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 e0d4aa9..1fd7125 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 +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 +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. 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 e4d813c..829cc04 100644 --- a/commands/status.md +++ b/commands/status.md @@ -16,6 +16,19 @@ 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 \"\$(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 +``` + ### External CLIs ```bash diff --git a/commands/tri-debug.md b/commands/tri-debug.md index 943d4ea..51ee5fb 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 +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 +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. 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 d435034..0ffc5f5 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 +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 +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. 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 647ee74..83cdb92 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 +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 +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. 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..09710e6 --- /dev/null +++ b/scripts/ensure-engine.sh @@ -0,0 +1,49 @@ +#!/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 + # 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 new file mode 100755 index 0000000..de72c3c --- /dev/null +++ b/scripts/install-engine.sh @@ -0,0 +1,133 @@ +#!/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] [--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 + +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 (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 + +# Detect platform +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + arm64) ARCH="arm64" ;; + *) 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}" +BASE_URL="https://github.com/${REPO}/releases/latest/download" + +printf "Downloading devkit engine (%s/%s)...\n" "$OS" "$ARCH" +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 -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" || { 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" || { printf "Warning: could not download checksums.txt\n"; 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=$(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}') + 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 (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}" || { printf "Failed to install to %s\n" "$WIN_DIR"; exit 1; } + printf "Installed to %s/%s%s\n" "$WIN_DIR" "$BINARY" "$EXT" + # 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 + +# Unix: try INSTALL_DIR, fall back to ~/.local/bin +if [[ -w "$INSTALL_DIR" ]]; then + 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"; 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" + # 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" + exit 1 +fi diff --git a/skills/autoloop/SKILL.md b/skills/autoloop/SKILL.md index 8c29672..02649c3 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 +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: + +```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. 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 28bd45e..a6031aa 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 +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 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. 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 b4434bc..2d9df91 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 +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 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. 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 b535e2f..8cc920b 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: | 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 + @$(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)"