Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit beb1751

Browse files
z23ccclaude
andcommitted
feat(install): auto-provision flowctl binary on SessionStart
Plugin users no longer need to manually compile flowctl. A SessionStart hook runs scripts/hooks/ensure-flowctl.sh which: 1. Checks $PLUGIN_ROOT/bin/flowctl vs pinned version in .claude-plugin/flowctl-version (fast-path ~10ms when healthy) 2. Downloads the matching release archive from GitHub (with sha256 verification) 3. Falls back to cargo build from bundled source if download fails and a Rust toolchain is available 4. Never blocks session start — logs warnings on failure Also fixes the REPO constant in flowctl/install.sh from the stale anthropics/flow-code to z23cc/flow-code, and rewrites /flow-code:update to reflect the zero-compile flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9d4daea commit beb1751

5 files changed

Lines changed: 174 additions & 12 deletions

File tree

.claude-plugin/flowctl-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v0.1.26

commands/flow-code/update.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,22 @@ argument-hint: ""
66

77
# Update flow-code plugin
88

9-
Run these commands in sequence to update to the latest version:
9+
Tell the user to run these CLI commands in order (they are Claude Code commands, NOT shell — do NOT run them via Bash):
1010

11-
```bash
12-
# Step 1: Remove old marketplace cache
11+
```
1312
/plugin marketplace remove flow-code
14-
15-
# Step 2: Re-add marketplace (fetches latest)
1613
/plugin marketplace add https://github.com/z23cc/flow-code
17-
18-
# Step 3: Install latest version
1914
/plugin install flow-code
20-
21-
# Step 4: Reload
2215
/reload-plugins
2316
```
2417

25-
**Run each command above in order.** Tell the user to execute them — do NOT try to run them via Bash (they are Claude Code CLI commands, not shell commands).
18+
After `/reload-plugins`, the next SessionStart hook will automatically provision the correct `flowctl` binary (downloads from GitHub Releases, falls back to `cargo build` if source is present). No manual compile or copy needed.
19+
20+
**To sync immediately without waiting for the next session**, you MAY run the ensure script via Bash:
21+
22+
```bash
23+
PLUGIN_ROOT=$(ls -dt ~/.claude/plugins/cache/flow-code/flow-code/*/ 2>/dev/null | head -1)
24+
CLAUDE_PLUGIN_ROOT="${PLUGIN_ROOT%/}" "${PLUGIN_ROOT%/}/scripts/hooks/ensure-flowctl.sh"
25+
```
26+
27+
This runs the same logic the SessionStart hook uses — idempotent and safe to rerun.

flowctl/install.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/bin/sh
22
# flowctl installer — downloads the latest release binary from GitHub.
3-
# Usage: curl -fsSL https://raw.githubusercontent.com/anthropics/flow-code/main/flowctl/install.sh | sh
3+
# Usage: curl -fsSL https://raw.githubusercontent.com/z23cc/flow-code/main/flowctl/install.sh | sh
44
set -eu
55

6-
REPO="anthropics/flow-code"
6+
REPO="z23cc/flow-code"
77
INSTALL_DIR="${FLOWCTL_INSTALL_DIR:-/usr/local/bin}"
88

99
# ── Platform detection ────────────────────────────────────────────────

hooks/hooks.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
"description": "Ralph workflow guards - only active when FLOW_RALPH=1 and ralph-init has been run",
33
"hooks": {
44
"SessionStart": [
5+
{
6+
"hooks": [
7+
{
8+
"type": "command",
9+
"command": "S=\"${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/scripts/hooks/ensure-flowctl.sh\"; [ -x \"$S\" ] && \"$S\" || true",
10+
"timeout": 180
11+
}
12+
],
13+
"description": "Auto-download or build flowctl binary if missing/outdated"
14+
},
515
{
616
"hooks": [
717
{

scripts/hooks/ensure-flowctl.sh

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/bin/sh
2+
# ensure-flowctl.sh — idempotent binary provisioner for the flow-code plugin.
3+
#
4+
# Called from SessionStart hook. Ensures $PLUGIN_ROOT/bin/flowctl exists and
5+
# matches the version pinned in .claude-plugin/flowctl-version.
6+
#
7+
# Strategy:
8+
# 1. Fast path: binary present and version matches → exit 0 in <100ms
9+
# 2. Download from GitHub Releases (with checksum verification)
10+
# 3. Fallback: cargo build from source (if toolchain + source available)
11+
# 4. On any failure: warn but exit 0 (never block session start)
12+
#
13+
# Env overrides:
14+
# FLOWCTL_SKIP_ENSURE=1 — skip entirely (for CI/testing)
15+
# FLOWCTL_FORCE_BUILD=1 — skip download, build from source directly
16+
17+
set -eu
18+
19+
[ "${FLOWCTL_SKIP_ENSURE:-0}" = "1" ] && exit 0
20+
21+
PLUGIN_ROOT="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
22+
[ -n "$PLUGIN_ROOT" ] || exit 0
23+
[ -d "$PLUGIN_ROOT" ] || exit 0
24+
25+
PINNED_FILE="$PLUGIN_ROOT/.claude-plugin/flowctl-version"
26+
[ -f "$PINNED_FILE" ] || exit 0
27+
28+
PINNED="$(tr -d ' \t\n\r' < "$PINNED_FILE")"
29+
[ -n "$PINNED" ] || exit 0
30+
31+
BIN="$PLUGIN_ROOT/bin/flowctl"
32+
33+
# Fast path: binary exists and version matches
34+
if [ -x "$BIN" ] && [ "${FLOWCTL_FORCE_BUILD:-0}" != "1" ]; then
35+
CURRENT="$("$BIN" --version 2>/dev/null | awk 'NR==1{print $2}' || echo "")"
36+
if [ -n "$CURRENT" ]; then
37+
# PINNED may be "v0.1.26" or "0.1.26"; normalize
38+
PINNED_NUM="${PINNED#v}"
39+
if [ "$CURRENT" = "$PINNED_NUM" ]; then
40+
exit 0
41+
fi
42+
fi
43+
fi
44+
45+
# Need to provision
46+
log() { printf '[flow-code] %s\n' "$1" >&2; }
47+
48+
log "provisioning flowctl $PINNED..."
49+
50+
mkdir -p "$PLUGIN_ROOT/bin"
51+
52+
# ── Try GitHub Release download ─────────────────────────────────────
53+
try_download() {
54+
OS="$(uname -s)"
55+
ARCH="$(uname -m)"
56+
case "$OS" in
57+
Linux) OS_T="unknown-linux-gnu" ;;
58+
Darwin) OS_T="apple-darwin" ;;
59+
MINGW*|MSYS*|CYGWIN*) OS_T="pc-windows-msvc" ;;
60+
*) log "unsupported OS: $OS"; return 1 ;;
61+
esac
62+
case "$ARCH" in
63+
x86_64|amd64) ARCH_T="x86_64" ;;
64+
aarch64|arm64) ARCH_T="aarch64" ;;
65+
*) log "unsupported arch: $ARCH"; return 1 ;;
66+
esac
67+
PLATFORM="${ARCH_T}-${OS_T}"
68+
69+
case "$OS_T" in
70+
pc-windows-msvc) EXT="zip"; BIN_NAME="flowctl.exe" ;;
71+
*) EXT="tar.gz"; BIN_NAME="flowctl" ;;
72+
esac
73+
74+
REPO="z23cc/flow-code"
75+
ARCHIVE="flowctl-${PINNED}-${PLATFORM}.${EXT}"
76+
URL="https://github.com/${REPO}/releases/download/${PINNED}/${ARCHIVE}"
77+
78+
command -v curl >/dev/null 2>&1 || { log "curl not found"; return 1; }
79+
80+
TMP="$(mktemp -d)"
81+
# shellcheck disable=SC2064
82+
trap "rm -rf '$TMP'" EXIT INT TERM
83+
84+
log "downloading $ARCHIVE"
85+
if ! curl -fsSL --connect-timeout 10 --max-time 120 "$URL" -o "$TMP/$ARCHIVE"; then
86+
log "download failed: $URL"
87+
return 1
88+
fi
89+
90+
# Verify checksum if available (best-effort)
91+
if curl -fsSL --connect-timeout 5 --max-time 15 "${URL}.sha256" -o "$TMP/$ARCHIVE.sha256" 2>/dev/null; then
92+
(cd "$TMP" && {
93+
if command -v sha256sum >/dev/null 2>&1; then
94+
sha256sum -c "$ARCHIVE.sha256" >/dev/null 2>&1
95+
elif command -v shasum >/dev/null 2>&1; then
96+
shasum -a 256 -c "$ARCHIVE.sha256" >/dev/null 2>&1
97+
else
98+
true
99+
fi
100+
}) || { log "checksum verification failed"; return 1; }
101+
fi
102+
103+
# Extract
104+
case "$EXT" in
105+
tar.gz)
106+
tar xzf "$TMP/$ARCHIVE" -C "$TMP" || { log "extract failed"; return 1; }
107+
;;
108+
zip)
109+
(cd "$TMP" && unzip -q "$ARCHIVE") || { log "extract failed"; return 1; }
110+
;;
111+
esac
112+
113+
[ -f "$TMP/$BIN_NAME" ] || { log "archive missing $BIN_NAME"; return 1; }
114+
115+
mv "$TMP/$BIN_NAME" "$BIN"
116+
chmod +x "$BIN"
117+
log "flowctl $PINNED installed from GitHub Release ✓"
118+
return 0
119+
}
120+
121+
# ── Fallback: build from source ─────────────────────────────────────
122+
try_build() {
123+
SRC="$PLUGIN_ROOT/flowctl"
124+
[ -d "$SRC/crates" ] || { log "source not available at $SRC"; return 1; }
125+
command -v cargo >/dev/null 2>&1 || { log "cargo not found"; return 1; }
126+
127+
log "building from source (may take ~1-2min, downloads ~130MB on first run)..."
128+
(cd "$SRC" && cargo build --release -p flowctl-cli >&2) || { log "cargo build failed"; return 1; }
129+
130+
BUILT="$SRC/target/release/flowctl"
131+
[ -x "$BUILT" ] || { log "built binary not found at $BUILT"; return 1; }
132+
133+
cp "$BUILT" "$BIN"
134+
chmod +x "$BIN"
135+
log "flowctl built from source ✓"
136+
return 0
137+
}
138+
139+
# ── Execute strategy ────────────────────────────────────────────────
140+
if [ "${FLOWCTL_FORCE_BUILD:-0}" = "1" ]; then
141+
try_build || log "build failed — flow-code features disabled"
142+
else
143+
try_download || try_build || {
144+
log "flowctl unavailable — flow-code features will be disabled"
145+
log "retry manually: $PLUGIN_ROOT/scripts/hooks/ensure-flowctl.sh"
146+
}
147+
fi
148+
149+
exit 0

0 commit comments

Comments
 (0)