From 11344e27db14943168af2dd13bc9d9011a69e900 Mon Sep 17 00:00:00 2001 From: Spencer Charest Date: Sun, 14 Jun 2026 18:14:22 -0700 Subject: [PATCH] feat(crush): add ZERO-294 Crush adapter --- Makefile | 3 +- README.md | 19 +++--- guides/crush.md | 46 +++++++++++++ plugins/zero-crush/README.md | 33 ++++++++++ plugins/zero-crush/crush/crush.zero.json | 20 ++++++ .../crush/hooks/zero-pre-tool-use.sh | 65 ++++++++++++++++++ plugins/zero-crush/manifest.json | 8 +++ plugins/zero-gemini/gemini-extension.json | 2 +- plugins/zero/.claude-plugin/plugin.json | 2 +- plugins/zero/.codex-plugin/plugin.json | 2 +- plugins/zero/.factory-plugin/plugin.json | 2 +- scripts/build-crush.sh | 66 +++++++++++++++++++ 12 files changed, 255 insertions(+), 13 deletions(-) create mode 100644 guides/crush.md create mode 100644 plugins/zero-crush/README.md create mode 100644 plugins/zero-crush/crush/crush.zero.json create mode 100755 plugins/zero-crush/crush/hooks/zero-pre-tool-use.sh create mode 100644 plugins/zero-crush/manifest.json create mode 100755 scripts/build-crush.sh diff --git a/Makefile b/Makefile index 8c75263..91a93af 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,8 @@ MANIFESTS := \ plugins/zero/.claude-plugin/plugin.json \ plugins/zero/.codex-plugin/plugin.json \ plugins/zero/.factory-plugin/plugin.json \ - plugins/zero-gemini/gemini-extension.json + plugins/zero-gemini/gemini-extension.json \ + plugins/zero-crush/manifest.json # The manifest read for the current version (all of MANIFESTS stay in lockstep). VERSION_SOURCE := plugins/zero/.claude-plugin/plugin.json diff --git a/README.md b/README.md index ce4d9fb..0bcde4d 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ installation, use the guides below. Pick your agent: web, desktop, and mobile - **[Codex (CLI)](guides/codex.md)** - **[Codex app](guides/codex-app.md)** +- **[Crush](guides/crush.md)** - **[Droid](guides/droid.md)** - **[Gemini CLI](guides/gemini-cli.md)** - **[Anything else (standalone installer)](guides/generic.md)** — works in any @@ -37,18 +38,19 @@ you in and takes it from there. ## What the plugin does -Every install ships the same three ingredients: +Every install ships the same core skill and then the host-specific integration +points that host supports: - **The `zero` skill** — teaches the agent how to search Zero, call a capability, and review the result. -- **Hooks** — keep the Zero CLI runner provisioned and up to date, and remind - the agent that Zero is available. +- **Hooks, when supported** — keep the Zero CLI runner provisioned and up to + date, and remind the agent that Zero is available. - **The Zero MCP connector** — capability search and account status over MCP, on hosts that load it. All hosts share one login (`~/.zero/config.json`) and one runtime -(`~/.zero/runtime`) — sign in once per machine. Updates are automatic; the -per-agent guides have the details. +(`~/.zero/runtime`) — sign in once per machine. Hosts with hooks can update +automatically; the per-agent guides have the details. ## How this repo is organized @@ -63,7 +65,8 @@ plugins/zero/ # the shared plugin: skill + hooks (+ Claude's ├── .codex-plugin/ # Codex manifest └── .factory-plugin/ # Droid manifest plugins/zero-gemini/ # thin Gemini-only overlay (manifest + hook wiring) -scripts/build-gemini.sh # assembles the installable Gemini extension into dist/ +plugins/zero-crush/ # thin Crush overlay (skills + config templates) +scripts/build-*.sh # assemble host-specific install bundles into dist/ guides/ # per-host install guides + the agent install runbook ``` @@ -73,5 +76,5 @@ mechanics — live in the per-host guides. ## Status This repo is built up iteratively, one carefully reviewed PR at a time. Today -it ships the **Claude Code**, **Codex**, **Droid**, and **Gemini CLI** plugins; -additional hosts (Cursor) will land in subsequent PRs. +it ships the **Claude Code**, **Codex**, **Crush**, **Droid**, and +**Gemini CLI** plugins; additional hosts will land in subsequent PRs. diff --git a/guides/crush.md b/guides/crush.md new file mode 100644 index 0000000..4229876 --- /dev/null +++ b/guides/crush.md @@ -0,0 +1,46 @@ +# Zero for Crush + +How to install Zero in Crush and keep it up to date. + +## Install + +### Inside Crush + +Install the Zero runtime once: + +```bash +curl -fsSL https://zero.xyz/install.sh | bash +``` + +Build the Crush adapter from this repository, then copy it into your project: + +```bash +scripts/build-crush.sh +cp -R dist/zero-crush/. /path/to/your/project/ +``` + +Merge `dist/zero-crush/crush/crush.zero.json` into your project `crush.json`, +then restart Crush. Use the command palette (`Ctrl+P`) and choose +`project:zero`, or ask Crush: *"Help me set up and test Zero."* + +### From the terminal + +```bash +curl -fsSL https://zero.xyz/install.sh | bash +scripts/build-crush.sh +cp -R dist/zero-crush/. /path/to/your/project/ +``` + +Merge `dist/zero-crush/crush/crush.zero.json` into `crush.json` or +`~/.config/crush/crush.json`, then restart Crush. + +## Staying up to date + +Crush does not currently provide session-start hooks for this adapter. To +update Zero, re-run: + +```bash +curl -fsSL https://zero.xyz/install.sh | bash +scripts/build-crush.sh +cp -R dist/zero-crush/. /path/to/your/project/ +``` diff --git a/plugins/zero-crush/README.md b/plugins/zero-crush/README.md new file mode 100644 index 0000000..9554f39 --- /dev/null +++ b/plugins/zero-crush/README.md @@ -0,0 +1,33 @@ +# zero-crush (overlay - not directly installable) + +This directory is **not** a complete Crush install on its own. It holds the +Crush-specific files: + +- `manifest.json` - versioned release marker for this adapter +- `crush/crush.zero.json` - mergeable `crush.json` fragment for Zero MCP and + the PreToolUse hook +- `crush/hooks/zero-pre-tool-use.sh` - hook script for Zero command gating + +The `zero` skill is copied from [`../zero/`](../zero/) during packaging with +Crush frontmatter added (`user-invocable: true`). + +## How the adapter is built + +Crush reads project skills from `.crush/skills/` and `.agents/skills/`. It does +not have SessionStart or UserPromptSubmit hooks yet, but it can run +`PreToolUse` hooks from `crush.json`. + +`scripts/build-crush.sh` assembles those pieces into `dist/zero-crush/`: + +``` +dist/zero-crush/ + ├── .agents/skills/zero/SKILL.md + ├── .crush/hooks/zero-pre-tool-use.sh + ├── .crush/skills/zero/SKILL.md + ├── crush/crush.zero.json + └── manifest.json +``` + +The hook auto-approves read-only Zero runner commands and blocks `zero fetch` +commands that do not include an explicit `--max-pay` cap. Spending commands +with a cap still fall through to Crush's normal permission flow. diff --git a/plugins/zero-crush/crush/crush.zero.json b/plugins/zero-crush/crush/crush.zero.json new file mode 100644 index 0000000..86af29b --- /dev/null +++ b/plugins/zero-crush/crush/crush.zero.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://charm.land/crush.json", + "mcp": { + "zero": { + "type": "http", + "url": "https://mcp.zero.xyz", + "timeout": 120, + "disabled": false + } + }, + "hooks": { + "PreToolUse": [ + { + "name": "zero-command-policy", + "matcher": "^bash$", + "command": "./.crush/hooks/zero-pre-tool-use.sh" + } + ] + } +} diff --git a/plugins/zero-crush/crush/hooks/zero-pre-tool-use.sh b/plugins/zero-crush/crush/hooks/zero-pre-tool-use.sh new file mode 100755 index 0000000..8d3fc42 --- /dev/null +++ b/plugins/zero-crush/crush/hooks/zero-pre-tool-use.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# Crush PreToolUse hook for Zero runner commands. +# - Allow read-only Zero commands. +# - Block zero fetch without an explicit --max-pay cap. +# - Leave spending commands and unknown commands to Crush's normal permission flow. + +set -euo pipefail + +cmd="${CRUSH_TOOL_INPUT_COMMAND:-}" + +context_json='"Zero is available through the zero skill and runner. Use search, inspect, fetch with an explicit --max-pay cap, then review successful paid runs when appropriate."' + +trimmed="${cmd#"${cmd%%[![:space:]]*}"}" +read -r -a toks <<<"$trimmed" || true + +idx=0 +while [ "$idx" -lt "${#toks[@]}" ]; do + case "${toks[$idx]}" in + [A-Za-z_][A-Za-z0-9_]*=*) idx=$((idx + 1)) ;; + *) break ;; + esac +done + +exe="${toks[$idx]:-}" +sub="${toks[$((idx + 1))]:-}" +base="${exe##*/}" + +case "$base" in + zero | zerocli) ;; + *) + printf '{"context":%s}\n' "$context_json" + exit 0 + ;; +esac + +case "$sub" in + search | get | runs | review) + printf '{"decision":"allow","context":%s}\n' "$context_json" + ;; + auth) + if [ "${toks[$((idx + 2))]:-}" = "whoami" ]; then + printf '{"decision":"allow","context":%s}\n' "$context_json" + else + printf '{"context":%s}\n' "$context_json" + fi + ;; + config) + case "$cmd" in + *"--set"*) printf '{"context":%s}\n' "$context_json" ;; + *) printf '{"decision":"allow","context":%s}\n' "$context_json" ;; + esac + ;; + fetch) + case "$cmd" in + *"--max-pay"*) printf '{"context":%s}\n' "$context_json" ;; + *) + printf '{"decision":"deny","reason":"Zero fetch commands must include an explicit --max-pay cap.","context":%s}\n' "$context_json" + ;; + esac + ;; + *) + printf '{"context":%s}\n' "$context_json" + ;; +esac diff --git a/plugins/zero-crush/manifest.json b/plugins/zero-crush/manifest.json new file mode 100644 index 0000000..e1d734f --- /dev/null +++ b/plugins/zero-crush/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "zero-crush", + "version": "1.4.0", + "description": "Crush adapter for Zero: project skills, MCP config fragment, and PreToolUse hook.", + "homepage": "https://zero.xyz", + "repository": "https://github.com/officialzeroxyz/zero-plugins", + "license": "MIT" +} diff --git a/plugins/zero-gemini/gemini-extension.json b/plugins/zero-gemini/gemini-extension.json index 69ea920..ac48f52 100644 --- a/plugins/zero-gemini/gemini-extension.json +++ b/plugins/zero-gemini/gemini-extension.json @@ -1,6 +1,6 @@ { "name": "zero", - "version": "1.3.3", + "version": "1.4.0", "description": "Discover and call paid AI capabilities (x402 / MPP) with automatic, wallet-backed payment. Search a live index of external services — image/video/audio generation, web scraping, data enrichment, real-time data, messaging — then call them and pay per use. Bundles the 'zero' skill and the Zero MCP connector.", "mcpServers": { "zero": { diff --git a/plugins/zero/.claude-plugin/plugin.json b/plugins/zero/.claude-plugin/plugin.json index 6b83198..59e52a7 100644 --- a/plugins/zero/.claude-plugin/plugin.json +++ b/plugins/zero/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "zero", - "version": "1.3.3", + "version": "1.4.0", "description": "Discover and call paid AI capabilities (x402 / MPP) with automatic, wallet-backed payment. Search a live index of external services — image/video/audio generation, web scraping, data enrichment, real-time data, messaging — then call them and pay per use. Bundles the 'zero' skill and the Zero MCP connector.", "author": { "name": "Zero", "url": "https://zero.xyz" }, "homepage": "https://zero.xyz", diff --git a/plugins/zero/.codex-plugin/plugin.json b/plugins/zero/.codex-plugin/plugin.json index e90630a..3a8d304 100644 --- a/plugins/zero/.codex-plugin/plugin.json +++ b/plugins/zero/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "zero", - "version": "1.3.3", + "version": "1.4.0", "description": "Discover and call paid AI capabilities (x402 / MPP) with automatic, wallet-backed payment. Search a live index of external services — image/video/audio generation, web scraping, data enrichment, real-time data, messaging — then call them and pay per use. Bundles the 'zero' skill.", "author": { "name": "Zero", "url": "https://zero.xyz" }, "homepage": "https://zero.xyz", diff --git a/plugins/zero/.factory-plugin/plugin.json b/plugins/zero/.factory-plugin/plugin.json index 6b83198..59e52a7 100644 --- a/plugins/zero/.factory-plugin/plugin.json +++ b/plugins/zero/.factory-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "zero", - "version": "1.3.3", + "version": "1.4.0", "description": "Discover and call paid AI capabilities (x402 / MPP) with automatic, wallet-backed payment. Search a live index of external services — image/video/audio generation, web scraping, data enrichment, real-time data, messaging — then call them and pay per use. Bundles the 'zero' skill and the Zero MCP connector.", "author": { "name": "Zero", "url": "https://zero.xyz" }, "homepage": "https://zero.xyz", diff --git a/scripts/build-crush.sh b/scripts/build-crush.sh new file mode 100755 index 0000000..02267b5 --- /dev/null +++ b/scripts/build-crush.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# +# Assemble the Crush adapter into dist/zero-crush/. +# +# Usage: +# scripts/build-crush.sh # assemble dist/zero-crush/ +# scripts/build-crush.sh --tar OUT.tgz # also write a tarball + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SHARED="$ROOT/plugins/zero" +OVERLAY="$ROOT/plugins/zero-crush" +OUT="$ROOT/dist/zero-crush" + +TARBALL="" +if [ "${1:-}" = "--tar" ]; then + TARBALL="${2:?--tar requires an output path}" +fi + +for f in \ + "$OVERLAY/manifest.json" \ + "$OVERLAY/crush/crush.zero.json" \ + "$OVERLAY/crush/hooks/zero-pre-tool-use.sh" \ + "$SHARED/skills/zero/SKILL.md"; do + [ -f "$f" ] || { echo "build-crush: missing required input: $f" >&2; exit 1; } +done + +rm -rf "$OUT" +mkdir -p \ + "$OUT/.crush/skills/zero" \ + "$OUT/.crush/hooks" \ + "$OUT/.agents/skills/zero" \ + "$OUT/crush" + +write_crush_skill() { + local dest="$1" + cat > "$dest" <<'HEADER' +--- +name: zero +description: Use Zero for external capabilities. +user-invocable: true +--- +HEADER + awk ' + NR == 1 && $0 == "---" { in_frontmatter = 1; next } + in_frontmatter && $0 == "---" { in_frontmatter = 0; next } + !in_frontmatter { print } + ' "$SHARED/skills/zero/SKILL.md" >> "$dest" +} + +cp "$OVERLAY/manifest.json" "$OUT/manifest.json" +cp "$OVERLAY/crush/crush.zero.json" "$OUT/crush/crush.zero.json" +cp "$OVERLAY/crush/hooks/zero-pre-tool-use.sh" "$OUT/.crush/hooks/zero-pre-tool-use.sh" +write_crush_skill "$OUT/.crush/skills/zero/SKILL.md" +write_crush_skill "$OUT/.agents/skills/zero/SKILL.md" +chmod +x "$OUT/.crush/hooks/zero-pre-tool-use.sh" + +echo "build-crush: assembled $OUT" +find "$OUT" -type f | sed "s#^$ROOT/##" | sort + +if [ -n "$TARBALL" ]; then + mkdir -p "$(dirname "$TARBALL")" + tar -czf "$TARBALL" -C "$OUT" . + echo "build-crush: wrote $TARBALL" +fi