Shuvo.yml - #153
Closed
shuvogit2026 wants to merge 1 commit into
Closed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#!/bin/sh
set -eu
Flow CLI installer
Usage: curl -fsSL https://myflow.sh/install.sh | sh
Security posture:
- We require SHA-256 verification by default.
- Set FLOW_INSTALL_INSECURE=1 (or true/yes) to bypass verification.
#region logging
if [ "${FLOW_DEBUG-}" = "true" ] || [ "${FLOW_DEBUG-}" = "1" ]; then
debug() { echo "$@" >&2; }
else
debug() { :; }
fi
if [ "${FLOW_QUIET-}" = "1" ] || [ "${FLOW_QUIET-}" = "true" ]; then
info() { :; }
else
info() { echo "$@" >&2; }
fi
error() {
echo "error: $@" >&2
exit 1
}
is_truthy() {
case "${1:-}" in
1|true|TRUE|yes|YES|y|Y) return 0 ;;
*) return 1 ;;
esac
}
FLOW_SHIM_DIR_SELECTED=""
can_execute_flow_binary() {
bin_path="$1"
if [ ! -f "$bin_path" ]; then
return 1
fi
if [ ! -x "$bin_path" ]; then
chmod +x "$bin_path" 2>/dev/null || true
fi
"$bin_path" --version >/dev/null 2>&1
}
#endregion
#region platform detection
get_os() {
os="$(uname -s)"
if [ "$os" = Darwin ]; then
echo "macos"
elif [ "$os" = Linux ]; then
echo "linux"
else
error "unsupported OS: $os"
fi
}
get_arch() {
arch="$(uname -m)"
if [ "$arch" = x86_64 ]; then
echo "x64"
elif [ "$arch" = aarch64 ] || [ "$arch" = arm64 ]; then
echo "arm64"
else
error "unsupported architecture: $arch"
fi
}
get_target() {
os="$1"
arch="$2"
case "$os-$arch" in
macos-x64) echo "x86_64-apple-darwin" ;;
macos-arm64) echo "aarch64-apple-darwin" ;;
linux-x64) echo "x86_64-unknown-linux-gnu" ;;
linux-arm64) echo "aarch64-unknown-linux-gnu" ;;
*) error "unsupported platform: $os-$arch" ;;
esac
}
shasum_bin() {
if command -v shasum >/dev/null 2>&1; then
echo "shasum -a 256"
elif command -v sha256sum >/dev/null 2>&1; then
echo "sha256sum"
else
echo ""
fi
}
validate_repo() {
repo="$1"
if [ -z "${repo:-}" ]; then
error "FLOW_UPGRADE_REPO is empty"
fi
owner="${repo%/}"
name="${repo#/}"
if [ "$owner" = "$repo" ] || [ "$name" = "$repo" ]; then
error "invalid repo '${repo}' (expected owner/repo)"
fi
case "$owner" in /) error "invalid repo '${repo}' (expected owner/repo)" ;; esac
case "$name" in /) error "invalid repo '${repo}' (expected owner/repo)" ;; esac
case "$owner" in [!A-Za-z0-9._-])
error "invalid repo owner '${owner}' (allowed: A-Z a-z 0-9 . _ -)"
;;
esac
case "$name" in [!A-Za-z0-9._-])
error "invalid repo name '${name}' (allowed: A-Z a-z 0-9 . _ -)"
;;
esac
}
validate_token() {
token="$1"
if [ -z "${token:-}" ]; then
error "GitHub token is empty"
fi
case "$token" in
[!A-Za-z0-9._-])
error "invalid GitHub token characters (refusing to use it)"
;;
esac
}
validate_version() {
version="$1"
case "$version" in
v*) tag="${version#v}" ;;
) tag="$version" ;;
esac
case "$tag" in
""|[!0-9A-Za-z._-]*)
error "invalid release version '${version}'"
;;
esac
}
#endregion
should_install_source() {
case "${FLOW_INSTALL_SOURCE:-1}" in
0|false|FALSE|no|NO|n|N) return 1 ;;
*) return 0 ;;
esac
}
should_install_path_shim() {
case "${FLOW_INSTALL_PATH_SHIM:-1}" in
0|false|FALSE|no|NO|n|N) return 1 ;;
*) return 0 ;;
esac
}
ensure_flow_source_checkout() {
if ! should_install_source; then
info "flow: skipping source checkout (FLOW_INSTALL_SOURCE=0)"
return 0
fi
if ! command -v git >/dev/null 2>&1; then
error "git is required to install flow source to ~/code/flow (or set FLOW_INSTALL_SOURCE=0)"
fi
source_dir="${FLOW_SOURCE_DIR:-$HOME/code/flow}"
source_repo="${FLOW_SOURCE_REPO_URL:-https://github.com/nikivdev/flow.git}"
source_branch="${FLOW_SOURCE_BRANCH:-main}"
mkdir -p "$(dirname "$source_dir")"
if [ -d "$source_dir/.git" ]; then
info "flow: source checkout found at $source_dir"
fi
if [ -e "$source_dir" ]; then
error "flow source path exists but is not a git checkout: $source_dir"
fi
info "flow: cloning source checkout to $source_dir"
if ! git clone --branch "$source_branch" "$source_repo" "$source_dir" >/dev/null 2>&1; then
error "failed to clone flow source from $source_repo"
fi
}
find_shim_dir() {
if [ -n "${FLOW_SHIM_DIR:-}" ]; then
if [ ! -d "$FLOW_SHIM_DIR" ]; then
mkdir -p "$FLOW_SHIM_DIR" 2>/dev/null || true
fi
if [ -d "$FLOW_SHIM_DIR" ] && [ -w "$FLOW_SHIM_DIR" ]; then
echo "$FLOW_SHIM_DIR"
return 0
fi
fi
old_ifs="${IFS:- }"
IFS=':'
for dir in ${PATH:-}; do
[ -n "$dir" ] || continue
[ "$dir" = "." ] && continue
[ -d "$dir" ] || continue
[ -w "$dir" ] || continue
echo "$dir"
IFS="$old_ifs"
return 0
done
IFS="$old_ifs"
fallback="$HOME/.local/bin"
if [ ! -d "$fallback" ]; then
mkdir -p "$fallback" 2>/dev/null || true
fi
if [ -d "$fallback" ] && [ -w "$fallback" ]; then
echo "$fallback"
return 0
fi
return 1
}
install_path_shim() {
if ! should_install_path_shim; then
return 0
fi
install_path="${FLOW_INSTALL_PATH:-$HOME/.flow/bin/f}"
install_dir="$(dirname "$install_path")"
shim_dir="$(find_shim_dir 2>/dev/null || true)"
if [ -z "${shim_dir:-}" ]; then
info "flow: warning: no writable PATH directory found for immediate command shim"
return 0
fi
FLOW_SHIM_DIR_SELECTED="$shim_dir"
for name in f flow; do
target="$shim_dir/$name"
# Never overwrite the installed binary with a symlink to itself.
if [ "$target" = "$install_path" ]; then
continue
fi
if [ -e "$target" ] && [ ! -L "$target" ]; then
# Do not replace existing non-symlink binaries/scripts.
continue
fi
ln -sf "$install_path" "$target" 2>/dev/null || true
done
if [ "$shim_dir" != "$install_dir" ]; then
info "flow: command shim installed in $shim_dir"
fi
}
ensure_line_in_file() {
file="$1"
needle="$2"
line="$3"
parent="$(dirname "$file")"
[ -d "$parent" ] || mkdir -p "$parent" 2>/dev/null || true
[ -f "$file" ] || touch "$file" 2>/dev/null || true
if ! grep -F -q "$needle" "$file" 2>/dev/null; then
printf '%s\n' "$line" >> "$file"
fi
}
ensure_sh_path_entry() {
file="$1"
dir="$2"
ensure_line_in_file "$file" "$dir" "export PATH="$dir:$PATH""
}
#region download helpers
download_file() {
url="$1"
file="$2"
if command -v curl >/dev/null 2>&1; then
debug ">" curl -fsSL -o "$file" "$url"
if [ "${FLOW_DEBUG-}" = "true" ] || [ "${FLOW_DEBUG-}" = "1" ]; then
curl -fsSL --proto '=https' --tlsv1.2 -o "$file" "$url"
else
curl -fsSL --proto '=https' --tlsv1.2 -o "$file" "$url" 2>/dev/null
fi
elif command -v wget >/dev/null 2>&1; then
debug ">" wget -qO "$file" "$url"
wget -qO "$file" "$url"
else
error "curl or wget is required"
fi
}
fetch_url() {
url="$1"
if command -v curl >/dev/null 2>&1; then
case "$url" in
https://api.github.com/*)
token="${GITHUB_TOKEN:-${GH_TOKEN:-${FLOW_GITHUB_TOKEN:-}}}"
if [ -n "${token:-}" ]; then
validate_token "$token"
curl -fsSL --proto '=https' --tlsv1.2 -H "Authorization: Bearer ${token}" "$url"
else
curl -fsSL --proto '=https' --tlsv1.2 "$url"
fi
;;
*)
curl -fsSL --proto '=https' --tlsv1.2 "$url"
;;
esac
elif command -v wget >/dev/null 2>&1; then
wget -qO- "$url"
else
error "curl or wget is required"
fi
}
get_latest_version() {
repo="${FLOW_UPGRADE_REPO:-}"
if [ -z "${repo:-}" ] && [ -n "${FLOW_GITHUB_OWNER:-}" ] && [ -n "${FLOW_GITHUB_REPO:-}" ]; then
repo="${FLOW_GITHUB_OWNER}/${FLOW_GITHUB_REPO}"
fi
repo="${repo:-nikivdev/flow}"
validate_repo "$repo"
url="https://api.github.com/repos/${repo}/releases/latest"
version="$(fetch_url "$url" | grep '"tag_name":' | sed -E 's/."([^"]+)"./\1/')"
validate_version "$version"
echo "$version"
}
get_checksum() {
version="$1"
target="$2"
repo="${FLOW_UPGRADE_REPO:-}"
if [ -z "${repo:-}" ] && [ -n "${FLOW_GITHUB_OWNER:-}" ] && [ -n "${FLOW_GITHUB_REPO:-}" ]; then
repo="${FLOW_GITHUB_OWNER}/${FLOW_GITHUB_REPO}"
fi
repo="${repo:-nikivdev/flow}"
validate_repo "$repo"
url="https://github.com/${repo}/releases/download/${version}/checksums.txt"
checksums="$(fetch_url "$url" 2>/dev/null)" || return 1
echo "$checksums" | grep "flow-${target}.tar.gz" | awk '{print $1}'
}
get_checksum_for_file() {
version="$1"
file="$2"
repo="${FLOW_UPGRADE_REPO:-}"
if [ -z "${repo:-}" ] && [ -n "${FLOW_GITHUB_OWNER:-}" ] && [ -n "${FLOW_GITHUB_REPO:-}" ]; then
repo="${FLOW_GITHUB_OWNER}/${FLOW_GITHUB_REPO}"
fi
repo="${repo:-nikivdev/flow}"
validate_repo "$repo"
url="https://github.com/${repo}/releases/download/${version}/checksums.txt"
checksums="$(fetch_url "$url" 2>/dev/null)" || return 1
checksums.txt format: " "
echo "$checksums" | awk -v f="$file" '$2==f {print $1}'
}
#endregion
install_flow() {
version="${FLOW_VERSION:-latest}"
os="${FLOW_OS:-$(get_os)}"
arch="${FLOW_ARCH:-$(get_arch)}"
target="$(get_target "$os" "$arch")"
install_path="${FLOW_INSTALL_PATH:-$HOME/.flow/bin/f}"
install_dir="$(dirname "$install_path")"
info "flow: installing flow CLI..."
info "flow: platform: $os-$arch ($target)"
Get latest version if needed
if [ "$version" = "latest" ]; then
info "flow: fetching latest version..."
version="$(get_latest_version)"
if [ -z "$version" ]; then
error "failed to fetch latest version"
fi
fi
validate_version "$version"
info "flow: version: $version"
URLs - try CDN first, fallback to GitHub
cdn_url="https://cdn.myflow.sh/${version}/flow-${target}.tar.gz"
repo="${FLOW_UPGRADE_REPO:-}"
if [ -z "${repo:-}" ] && [ -n "${FLOW_GITHUB_OWNER:-}" ] && [ -n "${FLOW_GITHUB_REPO:-}" ]; then
repo="${FLOW_GITHUB_OWNER}/${FLOW_GITHUB_REPO}"
fi
repo="${repo:-nikivdev/flow}"
validate_repo "$repo"
github_url="https://github.com/${repo}/releases/download/${version}/flow-${target}.tar.gz"
download_dir="$(mktemp -d)"
tarball="$download_dir/flow.tar.gz"
download_source="unknown"
asset_file="flow-${target}.tar.gz"
legacy_os="$os"
if [ "$legacy_os" = "macos" ]; then
legacy_os="darwin"
fi
legacy_arch="amd64"
if [ "$arch" = "arm64" ]; then
legacy_arch="arm64"
fi
legacy_file="flow_${version}${legacy_os}${legacy_arch}.tar.gz"
legacy_url="https://github.com/${repo}/releases/download/${version}/${legacy_file}"
Try CDN first (faster)
info "flow: downloading..."
if command -v curl >/dev/null 2>&1 && curl -fsSL -o "$tarball" "$cdn_url" 2>/dev/null; then
debug "flow: downloaded from CDN"
download_source="cdn"
else
debug "flow: trying GitHub..."
if download_file "$github_url" "$tarball"; then
asset_file="flow-${target}.tar.gz"
download_source="github"
elif download_file "$legacy_url" "$tarball"; then
asset_file="$legacy_file"
download_source="legacy"
else
error "download failed"
fi
fi
Verify checksum if available
shasum="$(shasum_bin)"
if [ -n "$shasum" ]; then
expected="$(get_checksum_for_file "$version" "$asset_file" 2>/dev/null)" || true
if [ -z "${expected:-}" ]; then
# Back-compat: allow checksums.txt to contain either naming scheme.
if [ "$asset_file" = "$legacy_file" ]; then
expected="$(get_checksum_for_file "$version" "flow-${target}.tar.gz" 2>/dev/null)" || true
elif [ "$asset_file" = "flow-${target}.tar.gz" ]; then
expected="$(get_checksum_for_file "$version" "$legacy_file" 2>/dev/null)" || true
fi
fi
if [ -z "${expected:-}" ]; then
if is_truthy "${FLOW_INSTALL_INSECURE-}"; then
info "flow: warning: checksum not verified (FLOW_INSTALL_INSECURE=1)"
elif [ "${download_source:-}" = "cdn" ]; then
rm -rf "$download_dir" "$extract_dir" 2>/dev/null || true
error "checksum verification failed for CDN download (checksums.txt missing or entry not found). Refusing to install.\nSet FLOW_INSTALL_INSECURE=1 to bypass (not recommended)."
else
info "flow: warning: checksum not verified (checksums.txt missing or entry not found; legacy release?)"
expected=""
fi
fi
if [ -n "${expected:-}" ]; then
debug "flow: verifying checksum..."
actual="$($shasum "$tarball" | awk '{print $1}')"
if [ "$expected" != "$actual" ]; then
rm -rf "$download_dir"
error "checksum mismatch"
fi
info "flow: checksum verified"
fi
else
if is_truthy "${FLOW_INSTALL_INSECURE-}"; then
info "flow: warning: sha256 tool not found, skipping checksum verification (FLOW_INSTALL_INSECURE=1)"
else
error "sha256 tool not found (need shasum or sha256sum). Refusing to install.\nSet FLOW_INSTALL_INSECURE=1 to bypass (not recommended)."
fi
fi
Extract and install
mkdir -p "$install_dir"
extract_dir="$(mktemp -d)"
tar -xzf "$tarball" -C "$extract_dir"
Find binary
if [ -f "$extract_dir/f" ]; then
mv "$extract_dir/f" "$install_path"
else
binary="$(find "$extract_dir" -type f ( -name "f" -o -name "flow" ) 2>/dev/null | head -1)"
if [ -z "$binary" ]; then
binary="$(find "$extract_dir" -type f -perm +111 2>/dev/null | head -1)"
fi
if [ -z "$binary" ]; then
rm -rf "$download_dir" "$extract_dir"
error "binary not found in archive"
fi
mv "$binary" "$install_path"
fi
chmod +x "$install_path"
Provide both
fandflowas entrypoints.base="$(basename "$install_path")"
if [ "$base" = "f" ]; then
if [ -e "$install_dir/flow" ] && [ -d "$install_dir/flow" ]; then
info "flow: warning: cannot create symlink $install_dir/flow (path is a directory)"
else
ln -sf "f" "$install_dir/flow" 2>/dev/null || true
fi
elif [ "$base" = "flow" ]; then
if [ -e "$install_dir/f" ] && [ -d "$install_dir/f" ]; then
info "flow: warning: cannot create symlink $install_dir/f (path is a directory)"
else
ln -sf "flow" "$install_dir/f" 2>/dev/null || true
fi
fi
Cleanup
rm -rf "$download_dir" "$extract_dir"
if ! can_execute_flow_binary "$install_path"; then
if [ "$os" = "macos" ] && ! is_truthy "${FLOW_INSTALL_RETRY_ALT_ARCH:-0}"; then
alt_arch="x64"
if [ "$arch" = "x64" ]; then
alt_arch="arm64"
fi
info "flow: installed binary failed execution; retrying with macos-$alt_arch build"
FLOW_ARCH="$alt_arch" FLOW_INSTALL_RETRY_ALT_ARCH=1 install_flow
return 0
fi
fi
info "flow: installed to $install_path"
}
configure_shell() {
install_dir="$(dirname "${FLOW_INSTALL_PATH:-$HOME/.flow/bin/f}")"
shim_dir="${FLOW_SHIM_DIR_SELECTED:-}"
fallback_shim="$HOME/.local/bin"
registry_url="${FLOW_REGISTRY_URL:-https://myflow.sh}"
Fish
if [ -f "$HOME/.config/fish/config.fish" ]; then
ensure_line_in_file "$HOME/.config/fish/config.fish" "$install_dir" "fish_add_path $install_dir"
if [ -n "$shim_dir" ] && [ "$shim_dir" != "$install_dir" ]; then
ensure_line_in_file "$HOME/.config/fish/config.fish" "$shim_dir" "fish_add_path $shim_dir"
fi
ensure_line_in_file "$HOME/.config/fish/config.fish" "$fallback_shim" "fish_add_path $fallback_shim"
ensure_line_in_file "$HOME/.config/fish/config.fish" "FLOW_REGISTRY_URL" "set -gx FLOW_REGISTRY_URL "$registry_url""
info "flow: updated ~/.config/fish/config.fish"
fi
Zsh
for zsh_rc in "$HOME/.zshrc" "$HOME/.zprofile"; do
ensure_sh_path_entry "$zsh_rc" "$install_dir"
if [ -n "$shim_dir" ] && [ "$shim_dir" != "$install_dir" ]; then
ensure_sh_path_entry "$zsh_rc" "$shim_dir"
fi
ensure_sh_path_entry "$zsh_rc" "$fallback_shim"
ensure_line_in_file "$zsh_rc" "FLOW_REGISTRY_URL" "export FLOW_REGISTRY_URL="$registry_url""
done
info "flow: updated ~/.zshrc and ~/.zprofile"
Bash
bash_updated=0
for rc in "$HOME/.bashrc" "$HOME/.bash_profile"; do
if [ -f "$rc" ]; then
ensure_sh_path_entry "$rc" "$install_dir"
if [ -n "$shim_dir" ] && [ "$shim_dir" != "$install_dir" ]; then
ensure_sh_path_entry "$rc" "$shim_dir"
fi
ensure_sh_path_entry "$rc" "$fallback_shim"
ensure_line_in_file "$rc" "FLOW_REGISTRY_URL" "export FLOW_REGISTRY_URL="$registry_url""
bash_updated=1
fi
done
if [ "$bash_updated" = "0" ]; then
rc="$HOME/.bashrc"
ensure_sh_path_entry "$rc" "$install_dir"
if [ -n "$shim_dir" ] && [ "$shim_dir" != "$install_dir" ]; then
ensure_sh_path_entry "$rc" "$shim_dir"
fi
ensure_sh_path_entry "$rc" "$fallback_shim"
ensure_line_in_file "$rc" "FLOW_REGISTRY_URL" "export FLOW_REGISTRY_URL="$registry_url""
fi
}
after_install() {
source_dir="${FLOW_SOURCE_DIR:-$HOME/code/flow}"
install_path="${FLOW_INSTALL_PATH:-$HOME/.flow/bin/f}"
if [ ! -x "$install_path" ]; then
chmod +x "$install_path" 2>/dev/null || true
fi
if ! can_execute_flow_binary "$install_path"; then
if command -v xattr >/dev/null 2>&1; then
xattr -d com.apple.quarantine "$install_path" >/dev/null 2>&1 || true
fi
chmod +x "$install_path" 2>/dev/null || true
fi
if ! can_execute_flow_binary "$install_path"; then
info "flow: diagnostic: unable to execute fallback binary: $install_path"
info "flow: diagnostic: $(ls -l "$install_path" 2>/dev/null || echo missing)"
if command -v file >/dev/null 2>&1; then
info "flow: diagnostic: $(file "$install_path" 2>/dev/null || true)"
fi
error "installed flow binary is not runnable; rerun with FLOW_DEBUG=1 and share diagnostics"
fi
info ""
info "flow: installed successfully!"
if command -v f >/dev/null 2>&1; then
info "flow: command ready: $(command -v f)"
elif [ -n "${FLOW_SHIM_DIR_SELECTED:-}" ] && [ -x "${FLOW_SHIM_DIR_SELECTED}/f" ]; then
info "flow: command shim ready: ${FLOW_SHIM_DIR_SELECTED}/f"
info "flow: open a new shell to refresh PATH (zsh: exec zsh -l)"
else
info "flow: OPEN NEW SHELL to use 'f' by name"
info "flow: immediate fallback: $install_path --help"
fi
if should_install_source; then
info "flow: source checkout: $source_dir"
fi
info "flow: then run 'f --help' to get started"
info "flow: docs: https://myflow.sh"
}
should_setup_run() {
case "${FLOW_SETUP_RUN:-1}" in
0|false|FALSE|no|NO|n|N) return 1 ;;
*) return 0 ;;
esac
}
is_interactive() {
[ -t 0 ] && [ -t 1 ]
}
prompt_value() {
label="$1"
default="$2"
env_override="$3"
if [ -n "$env_override" ]; then
echo "$env_override"
return 0
fi
if ! is_interactive; then
echo "$default"
return 0
fi
printf '%s [%s]: ' "$label" "$default" >&2
read -r answer </dev/tty || answer=""
answer="${answer:-$default}"
echo "$answer"
}
prompt_yn() {
question="$1"
default="${2:-y}"
if ! is_interactive; then
case "$default" in y|Y) return 0 ;; *) return 1 ;; esac
fi
if [ "$default" = "y" ]; then
hint="Y/n"
else
hint="y/N"
fi
printf '%s (%s): ' "$question" "$hint" >&2
read -r answer </dev/tty || answer=""
answer="${answer:-$default}"
case "$answer" in
y|Y|yes|YES) return 0 ;;
*) return 1 ;;
esac
}
clone_if_missing() {
target_dir="$1"
repo_url="$2"
label="$3"
if [ -d "$target_dir/.git" ]; then
info "flow: $label already exists at $target_dir"
return 0
fi
if [ -e "$target_dir" ] && [ ! -d "$target_dir" ]; then
info "flow: warning: $target_dir exists but is not a directory; skipping $label"
return 1
fi
if [ -z "$repo_url" ] || [ "$repo_url" = "-" ] || [ "$repo_url" = "skip" ]; then
info "flow: skipping $label (no repo URL)"
return 0
fi
mkdir -p "$(dirname "$target_dir")"
info "flow: cloning $label -> $target_dir"
if git clone "$repo_url" "$target_dir" >/dev/null 2>&1; then
info "flow: $label ready"
else
info "flow: warning: failed to clone $label from $repo_url"
return 1
fi
}
setup_run_repos() {
if ! should_setup_run; then
info "flow: skipping ~/run setup (FLOW_SETUP_RUN=0)"
return 0
fi
if ! command -v git >/dev/null 2>&1; then
info "flow: warning: git not found; skipping ~/run setup"
return 0
fi
run_root="${RUN_ROOT:-$HOME/run}"
If both already exist, skip entirely
if [ -d "$run_root/.git" ] && [ -d "$run_root/i/.git" ]; then
info "flow: ~/run and ~/run/i already set up"
return 0
fi
info ""
if ! prompt_yn "Set up ~/run repos (task collections)?"; then
info "flow: skipping ~/run setup"
info "flow: you can set this up later with: f run-load"
return 0
fi
~/run (public)
if [ ! -d "$run_root/.git" ]; then
run_url="$(prompt_value "
/run repo URL (SSH or HTTPS)" "git@github.com:nikivdev/run.git" "${FLOW_RUN_REPO:-}")"/run"clone_if_missing "$run_root" "$run_url" "
fi
~/run/i (internal/private)
if [ ! -d "$run_root/i/.git" ]; then
run_i_url="$(prompt_value "
/run/i repo URL (SSH or HTTPS, or 'skip')" "git@github.com:nikivdev/run-i.git" "${FLOW_RUN_I_REPO:-}")"/run/i"clone_if_missing "$run_root/i" "$run_i_url" "
fi
info "flow: ~/run repos ready"
info "flow: run tasks with: f r (public) or f ri (internal)"
}
install_flow
install_path_shim
ensure_flow_source_checkout
configure_shell
setup_run_repos
after_install