mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
ci: publish Sprout Agent Bundle (sprout-acp + sprout-agent + sprout-dev-mcp) to GitHub Releases (#603)
Signed-off-by: Tyler Longwell <109685178+tlongwell-block@users.noreply.github.com>
This commit is contained in:
221
.github/workflows/sprout-agent-bundle.yml
vendored
Normal file
221
.github/workflows/sprout-agent-bundle.yml
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
name: Sprout Agent Bundle
|
||||
|
||||
# Builds and publishes the Sprout Agent Bundle — a Linux tarball containing
|
||||
# the three binaries an external service (e.g. sprout-backend-blox) needs to
|
||||
# run a Sprout agent end-to-end:
|
||||
#
|
||||
# sprout-acp ACP harness that bridges Sprout events to the LLM agent
|
||||
# sprout-agent ACP-compliant agent (spawns MCP, calls LLMs)
|
||||
# sprout-dev-mcp Developer MCP server (multicall: rg, tree, sprout,
|
||||
# git-credential-nostr, git-sign-nostr)
|
||||
#
|
||||
# Targets: x86_64-unknown-linux-musl and aarch64-unknown-linux-musl (static
|
||||
# musl so the tarball runs on any modern Linux without libc surprises).
|
||||
#
|
||||
# Triggers:
|
||||
# - push to main → updates rolling `sprout-agent-bundle-latest` release
|
||||
# - tag `sprout-agent-bundle-v*` → versioned release
|
||||
# - workflow_dispatch → manual canary build (no release publish)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ["sprout-agent-bundle-v*"]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish:
|
||||
description: "Publish to the rolling release (otherwise artifacts only)"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.target }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-unknown-linux-musl
|
||||
- aarch64-unknown-linux-musl
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
|
||||
|
||||
- name: Install cross
|
||||
uses: taiki-e/install-action@055f5df8c3f65ea01cd41e9dc855becd88953486 # v2.75.18
|
||||
with:
|
||||
tool: cross@0.2.5
|
||||
|
||||
- name: Resolve version
|
||||
id: ver
|
||||
run: |
|
||||
set -euo pipefail
|
||||
WORKSPACE_VERSION=$(cargo metadata --no-deps --format-version=1 \
|
||||
| jq -r '.workspace_default_members[0] as $m | .packages[] | select(.id==$m) | .version')
|
||||
if [[ -z "$WORKSPACE_VERSION" || "$WORKSPACE_VERSION" == "null" ]]; then
|
||||
# Fallback: read sprout-acp's version directly.
|
||||
WORKSPACE_VERSION=$(cargo metadata --no-deps --format-version=1 \
|
||||
| jq -r '.packages[] | select(.name=="sprout-acp") | .version')
|
||||
fi
|
||||
|
||||
REF="${GITHUB_REF#refs/tags/}"
|
||||
if [[ "$GITHUB_REF" == refs/tags/sprout-agent-bundle-v* ]]; then
|
||||
VERSION="${REF#sprout-agent-bundle-v}"
|
||||
CHANNEL="tag"
|
||||
else
|
||||
SHORT_SHA="${GITHUB_SHA::7}"
|
||||
VERSION="${WORKSPACE_VERSION}+git.${SHORT_SHA}"
|
||||
CHANNEL="rolling"
|
||||
fi
|
||||
{
|
||||
echo "workspace_version=$WORKSPACE_VERSION"
|
||||
echo "version=$VERSION"
|
||||
echo "channel=$CHANNEL"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved version=$VERSION channel=$CHANNEL"
|
||||
|
||||
- name: Build & package bundle
|
||||
id: pkg
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
VERSION: ${{ steps.ver.outputs.version }}
|
||||
CHANNEL: ${{ steps.ver.outputs.channel }}
|
||||
GIT_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Rolling releases use stable, version-less filenames so the
|
||||
# asset overwrites cleanly on every push to main. Tagged
|
||||
# releases keep the version in the filename for traceability.
|
||||
# The git SHA + version always live inside bundle.json.
|
||||
if [[ "$CHANNEL" == "tag" ]]; then
|
||||
ARCHIVE_BASENAME="sprout-agent-bundle-${VERSION}-${TARGET}"
|
||||
else
|
||||
ARCHIVE_BASENAME="sprout-agent-bundle-${TARGET}"
|
||||
fi
|
||||
ARCHIVE_BASENAME="$ARCHIVE_BASENAME" \
|
||||
./scripts/build-agent-bundle.sh "$VERSION" "$TARGET"
|
||||
ARCHIVE="dist/${ARCHIVE_BASENAME}.tar.gz"
|
||||
test -f "$ARCHIVE"
|
||||
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||
echo "archive_name=${ARCHIVE_BASENAME}.tar.gz" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload workflow artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sprout-agent-bundle-${{ matrix.target }}
|
||||
path: |
|
||||
${{ steps.pkg.outputs.archive }}
|
||||
${{ steps.pkg.outputs.archive }}.sha256
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
|
||||
publish:
|
||||
name: Publish rolling release
|
||||
needs: build
|
||||
if: |
|
||||
github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
|| (github.event_name == 'workflow_dispatch' && inputs.publish)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Download all bundle artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
path: dist
|
||||
pattern: sprout-agent-bundle-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: List assets
|
||||
run: ls -lh dist/
|
||||
|
||||
- name: Update rolling release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="sprout-agent-bundle-latest"
|
||||
TITLE="Sprout Agent Bundle (rolling)"
|
||||
NOTES="Rolling Linux build of the Sprout Agent Bundle (sprout-acp + sprout-agent + sprout-dev-mcp), tracking \`main\` (\`${SHA}\`)."
|
||||
|
||||
# Force-move the underlying git tag to the current SHA before
|
||||
# we touch the release. `gh release edit` updates release
|
||||
# metadata but does *not* move the tag, so without this the
|
||||
# tag would stick to whatever SHA first created the release.
|
||||
if gh api "repos/${REPO}/git/refs/tags/${TAG}" >/dev/null 2>&1; then
|
||||
gh api -X PATCH "repos/${REPO}/git/refs/tags/${TAG}" \
|
||||
-f sha="${SHA}" -F force=true >/dev/null
|
||||
else
|
||||
gh api -X POST "repos/${REPO}/git/refs" \
|
||||
-f ref="refs/tags/${TAG}" -f sha="${SHA}" >/dev/null
|
||||
fi
|
||||
|
||||
# Create the release if it doesn't exist; otherwise reuse it.
|
||||
if ! gh release view "$TAG" >/dev/null 2>&1; then
|
||||
gh release create "$TAG" \
|
||||
--prerelease \
|
||||
--target "${SHA}" \
|
||||
--title "$TITLE" \
|
||||
--notes "$NOTES"
|
||||
else
|
||||
gh release edit "$TAG" \
|
||||
--prerelease \
|
||||
--target "${SHA}" \
|
||||
--title "$TITLE" \
|
||||
--notes "$NOTES"
|
||||
fi
|
||||
# Asset filenames are stable for rolling builds (no version
|
||||
# in filename — see the package step), so --clobber overwrites
|
||||
# them in place. No stale-asset accumulation.
|
||||
gh release upload "$TAG" dist/* --clobber
|
||||
|
||||
publish-tag:
|
||||
name: Publish tagged release
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/sprout-agent-bundle-v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Download all bundle artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
path: dist
|
||||
pattern: sprout-agent-bundle-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Resolve tag version
|
||||
id: ver
|
||||
run: |
|
||||
REF="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${REF#sprout-agent-bundle-v}"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=$REF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create tagged release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ steps.ver.outputs.tag }}
|
||||
VERSION: ${{ steps.ver.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh release create "$TAG" \
|
||||
--title "Sprout Agent Bundle v${VERSION}" \
|
||||
--notes "Sprout Agent Bundle v${VERSION} — Linux builds of sprout-acp + sprout-agent + sprout-dev-mcp." \
|
||||
dist/*
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
# Build artifacts
|
||||
/target/
|
||||
/dist/
|
||||
|
||||
# Environment files (may contain secrets)
|
||||
.env
|
||||
|
||||
216
scripts/build-agent-bundle.sh
Executable file
216
scripts/build-agent-bundle.sh
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the Sprout Agent Bundle — a tarball containing the three binaries
|
||||
# needed to run a Sprout agent end-to-end:
|
||||
#
|
||||
# sprout-acp ACP harness
|
||||
# sprout-agent ACP-compliant agent (spawns MCP, calls LLMs)
|
||||
# sprout-dev-mcp Developer MCP server (multicall: rg/tree/sprout/git-*)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build-agent-bundle.sh [version] [target]
|
||||
#
|
||||
# Environment overrides:
|
||||
# TARGET cross-compile target (defaults to host)
|
||||
# USE_CROSS=1 use `cross` instead of `cargo` for the build
|
||||
# SKIP_BUILD=1 skip the cargo/cross build (use prebuilt binaries
|
||||
# already present in target/[<target>/]release)
|
||||
# ARCHIVE_BASENAME override the archive basename (sans .tar.gz). Useful
|
||||
# for rolling releases where the asset filename should
|
||||
# be stable across builds (e.g. `sprout-agent-bundle-
|
||||
# <target>`). Defaults to
|
||||
# `sprout-agent-bundle-<version>-<target>`.
|
||||
# DIST_DIR output directory (default: dist)
|
||||
#
|
||||
# Output:
|
||||
# ${DIST_DIR}/${ARCHIVE_BASENAME}.tar.gz
|
||||
# ${DIST_DIR}/${ARCHIVE_BASENAME}.tar.gz.sha256
|
||||
#
|
||||
# The tarball contains:
|
||||
# sprout-acp
|
||||
# sprout-agent
|
||||
# sprout-dev-mcp
|
||||
# README.md
|
||||
# bundle.json { version, git_sha, target, binaries: [{name, sha256, size}] }
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:-${VERSION:-0.0.0-dev}}"
|
||||
HOST_TARGET="$(rustc -vV | sed -n 's|host: ||p')"
|
||||
TARGET="${2:-${TARGET:-$HOST_TARGET}}"
|
||||
DIST_DIR="${DIST_DIR:-dist}"
|
||||
|
||||
# Resolve git SHA (best effort — works in CI checkout and local clones).
|
||||
if GIT_SHA="$(git rev-parse HEAD 2>/dev/null)"; then
|
||||
:
|
||||
else
|
||||
GIT_SHA="unknown"
|
||||
fi
|
||||
|
||||
BINARIES=(sprout-acp sprout-agent sprout-dev-mcp)
|
||||
|
||||
echo "==> Building Sprout Agent Bundle v${VERSION} for ${TARGET}"
|
||||
echo " git_sha=${GIT_SHA}"
|
||||
echo " binaries=${BINARIES[*]}"
|
||||
|
||||
# Pick build driver. `cross` is required for cross-compilation in CI;
|
||||
# for host builds we use plain `cargo` so contributors don't need Docker.
|
||||
if [[ "${USE_CROSS:-0}" == "1" ]] || [[ "$TARGET" != "$HOST_TARGET" ]]; then
|
||||
if ! command -v cross >/dev/null 2>&1; then
|
||||
echo "error: cross-compiling to $TARGET requires \`cross\` (install: cargo install cross --version 0.2.5)" >&2
|
||||
exit 1
|
||||
fi
|
||||
BUILDER=(cross build --release --target "$TARGET")
|
||||
BIN_DIR="target/${TARGET}/release"
|
||||
else
|
||||
BUILDER=(cargo build --release)
|
||||
BIN_DIR="target/release"
|
||||
fi
|
||||
|
||||
PKG_ARGS=()
|
||||
for bin in "${BINARIES[@]}"; do
|
||||
PKG_ARGS+=(-p "$bin")
|
||||
done
|
||||
|
||||
if [[ "${SKIP_BUILD:-0}" == "1" ]]; then
|
||||
echo " (SKIP_BUILD=1 set — expecting prebuilt binaries in ${BIN_DIR}/)"
|
||||
else
|
||||
"${BUILDER[@]}" "${PKG_ARGS[@]}"
|
||||
fi
|
||||
|
||||
# Verify all binaries exist.
|
||||
for bin in "${BINARIES[@]}"; do
|
||||
if [[ ! -f "${BIN_DIR}/${bin}" ]]; then
|
||||
echo "error: ${BIN_DIR}/${bin} not found after build" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Stage into a tempdir.
|
||||
mkdir -p "${DIST_DIR}"
|
||||
STAGING="$(mktemp -d)"
|
||||
trap 'rm -rf "${STAGING}"' EXIT
|
||||
|
||||
# sha256 helper: prefer sha256sum (linux), fall back to shasum -a 256 (macos).
|
||||
sha256_of() {
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$1" | awk '{print $1}'
|
||||
else
|
||||
shasum -a 256 "$1" | awk '{print $1}'
|
||||
fi
|
||||
}
|
||||
|
||||
# Copy + strip binaries, collect manifest entries.
|
||||
MANIFEST_ENTRIES=()
|
||||
for bin in "${BINARIES[@]}"; do
|
||||
cp "${BIN_DIR}/${bin}" "${STAGING}/${bin}"
|
||||
chmod 0755 "${STAGING}/${bin}"
|
||||
# Best-effort strip. `cross` images include the cross-target strip; on host
|
||||
# we use the system strip. Skip silently if unavailable (e.g. cross-arch
|
||||
# local builds on macOS).
|
||||
if command -v strip >/dev/null 2>&1; then
|
||||
strip "${STAGING}/${bin}" 2>/dev/null || true
|
||||
fi
|
||||
sha="$(sha256_of "${STAGING}/${bin}")"
|
||||
size="$(wc -c < "${STAGING}/${bin}" | tr -d ' ')"
|
||||
MANIFEST_ENTRIES+=("{\"name\":\"${bin}\",\"sha256\":\"${sha}\",\"size\":${size}}")
|
||||
done
|
||||
|
||||
# bundle.json — machine-readable manifest.
|
||||
ENTRIES_JSON="$(IFS=,; echo "${MANIFEST_ENTRIES[*]}")"
|
||||
cat > "${STAGING}/bundle.json" <<JSON
|
||||
{
|
||||
"name": "sprout-agent-bundle",
|
||||
"version": "${VERSION}",
|
||||
"git_sha": "${GIT_SHA}",
|
||||
"target": "${TARGET}",
|
||||
"binaries": [${ENTRIES_JSON}]
|
||||
}
|
||||
JSON
|
||||
|
||||
# README — human-readable.
|
||||
cat > "${STAGING}/README.md" <<'EOF'
|
||||
# Sprout Agent Bundle
|
||||
|
||||
Linux build of the three binaries needed to run a Sprout agent end-to-end:
|
||||
|
||||
- `sprout-acp` — ACP harness that bridges Sprout channel events to an
|
||||
ACP-compliant agent over stdio.
|
||||
- `sprout-agent` — ACP-compliant agent (spawns MCP servers, calls LLMs).
|
||||
- `sprout-dev-mcp` — Developer MCP server (shell, str_replace, todo) and
|
||||
multicall entrypoint for `rg`, `tree`, `sprout`, `git-credential-nostr`,
|
||||
`git-sign-nostr`.
|
||||
|
||||
See `bundle.json` for binary SHA-256s, sizes, and the source git SHA.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
tar -xzf sprout-agent-bundle-*.tar.gz -C /opt/sprout-agent
|
||||
export PATH="/opt/sprout-agent:$PATH"
|
||||
```
|
||||
|
||||
## Configure
|
||||
|
||||
```bash
|
||||
# Agent provider
|
||||
export SPROUT_AGENT_PROVIDER=anthropic # or openai
|
||||
export ANTHROPIC_API_KEY=sk-...
|
||||
export ANTHROPIC_MODEL=claude-sonnet-4-20250514
|
||||
|
||||
# Nostr identity (shared by sprout-acp, git auth, signing, and sprout CLI)
|
||||
export NOSTR_PRIVATE_KEY=nsec1...
|
||||
export SPROUT_PRIVATE_KEY="$NOSTR_PRIVATE_KEY"
|
||||
export SPROUT_RELAY_URL=https://your-relay.example.com
|
||||
```
|
||||
|
||||
## Git Integration
|
||||
|
||||
When `NOSTR_PRIVATE_KEY` is set, `sprout-dev-mcp` automatically configures
|
||||
git to use nostr-based credential auth and commit signing for all shell
|
||||
commands. This is ephemeral (session-scoped via `GIT_CONFIG_*` env vars) —
|
||||
your persistent git config is never modified.
|
||||
|
||||
The nostr credential helper is additive: it silently declines non-Sprout
|
||||
remotes so git falls through to your system credential helpers for GitHub,
|
||||
GitLab, etc. `NOSTR_PRIVATE_KEY` is written to a 0600 keyfile and removed
|
||||
from the process environment — shell commands cannot read it from env.
|
||||
|
||||
## Multicall Binary
|
||||
|
||||
`sprout-dev-mcp` is a multicall binary. When symlinked/invoked as:
|
||||
|
||||
- `rg` — ripgrep-compatible search
|
||||
- `tree` — directory tree with line counts
|
||||
- `sprout` — Sprout relay CLI
|
||||
- `git-credential-nostr` — NIP-98 git credential helper
|
||||
- `git-sign-nostr` — NIP-GS git commit/tag signing
|
||||
|
||||
…it dispatches to the corresponding subcommand. The installer is free to
|
||||
symlink these names next to `sprout-dev-mcp` on the PATH.
|
||||
EOF
|
||||
|
||||
# Tar.
|
||||
ARCHIVE_BASENAME="${ARCHIVE_BASENAME:-sprout-agent-bundle-${VERSION}-${TARGET}}"
|
||||
ARCHIVE_NAME="${ARCHIVE_BASENAME}.tar.gz"
|
||||
ARCHIVE_PATH="${DIST_DIR}/${ARCHIVE_NAME}"
|
||||
|
||||
# Deterministic-ish tar: sorted entries, no owner/group info.
|
||||
tar \
|
||||
--sort=name \
|
||||
--owner=0 --group=0 --numeric-owner \
|
||||
-czf "${ARCHIVE_PATH}" \
|
||||
-C "${STAGING}" \
|
||||
. 2>/dev/null || \
|
||||
tar -czf "${ARCHIVE_PATH}" -C "${STAGING}" . # fallback for BSD tar (macOS)
|
||||
|
||||
# Sidecar checksum.
|
||||
sha256_of "${ARCHIVE_PATH}" > "${ARCHIVE_PATH}.sha256"
|
||||
# Pretty-print the form `<sha> <filename>` like sha256sum -c expects.
|
||||
echo "$(cat "${ARCHIVE_PATH}.sha256") ${ARCHIVE_NAME}" > "${ARCHIVE_PATH}.sha256"
|
||||
|
||||
echo ""
|
||||
echo "==> Built: ${ARCHIVE_PATH}"
|
||||
ls -lh "${ARCHIVE_PATH}" "${ARCHIVE_PATH}.sha256"
|
||||
echo ""
|
||||
echo "==> bundle.json:"
|
||||
sed 's/^/ /' "${STAGING}/bundle.json"
|
||||
@@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Build a release tarball containing sprout-agent + sprout-dev-mcp.
|
||||
# Usage: ./scripts/build-agent-release.sh [version]
|
||||
# TARGET=aarch64-unknown-linux-musl ./scripts/build-agent-release.sh 0.1.0
|
||||
# Output: dist/sprout-agent-v<version>-<target>.tar.gz
|
||||
|
||||
VERSION="${1:-0.1.0}"
|
||||
HOST_TARGET="$(rustc -vV | sed -n 's|host: ||p')"
|
||||
TARGET="${TARGET:-$HOST_TARGET}"
|
||||
DIST_DIR="dist"
|
||||
|
||||
echo "Building sprout-agent release v${VERSION} for ${TARGET}..."
|
||||
|
||||
# Build release binaries — use --target only when cross-compiling.
|
||||
if [[ "$TARGET" == "$HOST_TARGET" ]]; then
|
||||
cargo build --release -p sprout-agent -p sprout-dev-mcp
|
||||
BIN_DIR="target/release"
|
||||
else
|
||||
cargo build --release --target "$TARGET" -p sprout-agent -p sprout-dev-mcp
|
||||
BIN_DIR="target/${TARGET}/release"
|
||||
fi
|
||||
|
||||
# Verify binaries exist
|
||||
for bin in sprout-agent sprout-dev-mcp; do
|
||||
if [[ ! -f "${BIN_DIR}/${bin}" ]]; then
|
||||
echo "error: ${BIN_DIR}/${bin} not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Package
|
||||
mkdir -p "${DIST_DIR}"
|
||||
ARCHIVE_NAME="sprout-agent-v${VERSION}-${TARGET}.tar.gz"
|
||||
STAGING=$(mktemp -d)
|
||||
trap 'rm -rf "${STAGING}"' EXIT
|
||||
|
||||
cp "${BIN_DIR}/sprout-agent" "${STAGING}/"
|
||||
cp "${BIN_DIR}/sprout-dev-mcp" "${STAGING}/"
|
||||
|
||||
cat > "${STAGING}/README.md" << 'EOF'
|
||||
# Sprout Agent
|
||||
|
||||
Minimal ACP agent + developer MCP toolchain.
|
||||
|
||||
## Contents
|
||||
|
||||
- `sprout-agent` — ACP-compliant agent (spawns MCP servers, calls LLMs)
|
||||
- `sprout-dev-mcp` — Developer MCP server (shell, str_replace, todo, rg, tree,
|
||||
sprout CLI, git-credential-nostr, git-sign-nostr)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Place both binaries on your PATH
|
||||
export PATH="/path/to/this/dir:$PATH"
|
||||
|
||||
# Set required env vars
|
||||
export SPROUT_AGENT_PROVIDER=anthropic # or openai
|
||||
export ANTHROPIC_API_KEY=sk-...
|
||||
export ANTHROPIC_MODEL=claude-sonnet-4-20250514
|
||||
|
||||
# Nostr identity (same key for git auth, signing, and relay CLI)
|
||||
export NOSTR_PRIVATE_KEY=nsec1...
|
||||
export SPROUT_PRIVATE_KEY=$NOSTR_PRIVATE_KEY
|
||||
export SPROUT_RELAY_URL=https://your-relay.example.com
|
||||
```
|
||||
|
||||
## Git Integration
|
||||
|
||||
When `NOSTR_PRIVATE_KEY` is set, the dev-mcp automatically configures git to
|
||||
use nostr-based credential auth and commit signing for all shell commands.
|
||||
This is ephemeral (session-scoped via `GIT_CONFIG_*` env vars) — your
|
||||
persistent git config is never modified.
|
||||
|
||||
The nostr credential helper is additive: it silently declines non-Sprout
|
||||
remotes so git falls through to your system credential helpers for GitHub,
|
||||
GitLab, etc. `NOSTR_PRIVATE_KEY` is written to a 0600 keyfile and removed
|
||||
from the process environment — shell commands cannot read it from env.
|
||||
|
||||
Set `SPROUT_PRIVATE_KEY` to the same key for the `sprout` relay CLI.
|
||||
|
||||
## Multicall Binary
|
||||
|
||||
`sprout-dev-mcp` is a multicall binary. When symlinked/invoked as:
|
||||
- `rg` — ripgrep-compatible search
|
||||
- `tree` — directory tree with line counts
|
||||
- `sprout` — Sprout relay CLI
|
||||
- `git-credential-nostr` — NIP-98 git credential helper
|
||||
- `git-sign-nostr` — NIP-GS git commit/tag signing
|
||||
EOF
|
||||
|
||||
tar -czf "${DIST_DIR}/${ARCHIVE_NAME}" -C "${STAGING}" .
|
||||
|
||||
echo "Built: ${DIST_DIR}/${ARCHIVE_NAME}"
|
||||
ls -lh "${DIST_DIR}/${ARCHIVE_NAME}"
|
||||
Reference in New Issue
Block a user