#!/usr/bin/env bash # kcap: install, workspace, and agent setup # ========================================= # # This page is written to be read and acted on by a coding agent. If a user # asked you to install kcap and set up their workspace by following # https://www.kurrent.io/install, these are the steps, and nothing else is # needed: at that URL this text is the comment block at the top of the # installer script, so the one URL is both the instructions and the script # they run. The same text is served on its own at # https://www.kurrent.io/install.txt. # # Because agents execute what they read here, this file contains only setup # instructions the reader has asked for. It does not describe what kcap # records or what its tools can do: that is the documentation, at # https://www.kurrent.io/docs/kcap/getting-started/quickstart/. # # kcap records coding-agent sessions and streams them to a workspace at # https://.kcap.ai. Setup needs the person at the keyboard once, to # approve a sign-in. # # The shape of this: ask the user everything first, then run setup once with # their answers. The setup command is a wizard, and it runs start to finish - # it cannot be stopped part way to ask a question and resumed. So gather the # answers, then hand them over in a single non-interactive run. # # # How to ask the user things # -------------------------- # # Five decisions are the user's: which workspace, who can see the recorded # sessions, which coding agents to record, which repositories and folders to # record, and which past sessions to import. # # If you have a tool for putting a multiple-choice question to the user, use it # for each of those, offering the options the step names. If you do not, ask in # plain text and wait for the answer. Either way: do not assume a default and # carry on, and do not ask the user for anything the commands below can find # out for you. # # Two of the commands below wait for the user to approve a sign-in in their # browser, which can take longer than your tool allows a command to run. Run # each of those in the background with its output going to a file, read the # URL and code from the file, and wait for the command to finish before going # on. Steps 3 and 5 say which they are. # # # 1. Install the CLI # ------------------ # # First check whether it is already there: # # kcap --version # # If that prints a version, kcap is installed: keep it, skip the rest of this # step, and go to step 2. The installer would replace it, and a beta build # would be replaced by the stable one. # # Otherwise, on macOS or Linux: # # curl -fsSL https://www.kurrent.io/install | bash -s -- --no-setup # # On Windows, in PowerShell: # # $env:KCAP_NO_SETUP = '1'; irm https://www.kurrent.io/install.ps1 | iex # # Keep the no-setup option. Without it the installer starts the setup wizard # on the user's terminal, which is the interactive run this page avoids. Setup # runs at step 5, with the user's answers. # # On macOS and Linux the command is linked into ~/.local/bin. If the installer # warns that this is not on PATH, call kcap by its full path, ~/.local/bin/kcap, # for every command below: your shell will not pick up a profile change. Show # the user the line the installer printed, for their own terminal. # # If it warns that another kcap comes first on PATH, that is an older npm # install. Ask the user before removing it with npm uninstall -g @kurrent/kcap, # and use the full path until then. # # Verify with: # # kcap --version # # # 2. Find out where you are # ------------------------- # # kcap status --json # # If `configured` is false, continue to step 3. # # If `configured` is true, this machine already has a workspace and # credentials, so do not run setup again: skip steps 3 and 5. The output also # names `profile`, the profile in effect in this directory. The commands below # change the active profile, the one `kcap profile show` prints. If the two # names differ, ask the user which workspace they mean and, with their # agreement, make that one active before changing anything: # # kcap use # # Being signed in does not mean every agent is recorded, so still ask the # step 4 questions, leaving out visibility unless the user wants to change it: # # kcap config set default_visibility # # Then wire up each agent they picked that `kcap harness list --json` does not # report as wired, one command per agent, with that agent's flag (no flag for # Claude Code): # # kcap plugin install --codex # # and carry on from step 6. Step 7 starts with a note for this path: rules set # earlier on this machine stay in force until they are cleared. # # # 3. Find the workspace # --------------------- # # Ask the user whether they already know their workspace - most people invited # to one do, and it is a slug like `acme` or a URL like https://acme.kcap.ai. # Offer: "I know it", "I do not know it", "I need to create one". # # If they know it, ask one more thing: whether their team signs in with # GitHub, as opposed to the organization's own single sign-on. If it does, # step 5 adds --github to the setup command; without it, the sign-in goes to # the wrong place. Then go to step 4: nothing else here is needed. # # If they need to create one, go to step 4, with one thing said first: an # account that already belongs to a workspace cannot create another. Setup # refuses and names the workspace it belongs to. If the user is not sure # whether their account has one, run the discovery below before creating. # # If they do not know it: # # kcap setup --discover --device --json # # This signs in and reports the workspaces the account belongs to, changing # nothing. It waits for the user, so run it in the background with its output # in a file. It prints a URL and a short code on the way: show the user both, # exactly as printed, and wait for the command to return. Do not open a browser # yourself. # # Read `workspaces` from the output and ask the user which one to use. # # An empty list does not yet mean there is no workspace. This sign-in lists # the workspaces that use org SSO, and a workspace whose members sign in with # the GitHub App is not among them. Ask whether their team signs in with # GitHub. If so, run it again with --github, and remember it for step 5: # # kcap setup --discover --device --github --json # # If the list is still empty, `can_create` says whether this sign-in may # create a workspace at step 4. It is eligibility, not a promise that creating # will succeed. If it is false, stop and tell the user to ask whoever runs # their workspace how to join it. # # Note that this route costs a second sign-in, because it deliberately stores # nothing. That is why the question above comes first. # # # 4. Ask the rest # --------------- # # Ask these before running anything, so setup can run once. # # **Who can see these sessions.** Offer the four values, with `org_public` as # the suggested one, and say what they mean: `private` (only the user), # `project` (the project's members), `org_public` (the organization's own # rules for the repository), `public` (every signed-in user of the server; a # link on its own never grants access, and nothing is published on the web). # # **Which coding agents to record.** Find out what is on the machine: # # kcap harness list --json # # Every supported agent is reported as installed, already wired up, or absent. # Ask which of the installed ones to record, as a multiple-choice question, with # the agent you are running in pre-selected and any already-wired agent shown as # already on. # # Be accurate about what unselecting means: an agent that is already wired up # keeps recording until it is removed, which is step 6, not something this # question does by itself. # # **What to record.** Offer: "everything", "only some repositories or # folders", "everything except some repositories or folders". The answer can # mix the two, for example "only acme/widgets, and never ~/notes". One shape # cannot be set: "only these repositories, plus this folder". Allow rules # narrow each other rather than add up, so say so and ask the user to pick one # or the other. This is worth asking before anything is recorded or imported: # a client project or a personal folder is easier kept out than taken back # out. It is applied at step 7, which says how to turn each part of the answer # into a rule. # # **Which past sessions to import.** First find out whether there is a # repository here to import: # # git remote get-url origin # # If that prints a URL, offer: "this repository only" and "nothing for now". # If it fails, this directory is not a clone with a remote, so the import has # nothing to work on: say so, and offer only "nothing for now", or ask the # user for the path of the repository they mean and run the rest of the steps # from there. Do not offer the whole machine, and never run # `kcap import --all` yourself: it uploads every past session of every coding # agent on this machine, from every repository, and that is the user's to run. # Step 8 says what to hand them if they ask. The import itself is step 8, # after setup. # # # 5. Run setup once # ----------------- # # Build one command from the answers. # # The workspace, one of: # # kcap setup # join one they named # kcap setup --server-url https://kcap.example.com # a self-hosted server # kcap setup --org "" --slug # create a new one # # Then add, in the same command: # # --device show the user a code to approve, as in step 3 # --github if step 3 established the team signs in # with GitHub # --no-prompt answer nothing interactively # --default-visibility from step 4 # --skip-import always - the import is step 8 # --skip-- every skip flag for each installed agent they # did NOT pick # # The skips matter. Without --skip-import, a non-interactive run imports every # past session on the machine, whatever the user answered. Without the agent # skips, it wires up every agent it detects, which is not what the user chose # either. Each agent has a hooks skip, such as --skip-codex-hooks, and most # have more: mcp, skills, instructions, and for Codex network access. The # hooks skip alone is not enough; Kiro, for one, still registers kcap's MCP # servers and skills in ~/.kiro. Read the skip flags off the help: # # kcap setup --help # # and pass every --skip- flag it names for each agent not picked. # # So a full example, for a user joining `acme`, recording only Claude Code on a # machine that also has Codex and Kiro: # # kcap setup acme --device --no-prompt --default-visibility org_public \ # --skip-import --skip-codex-hooks --skip-codex-network-access \ # --skip-kiro-hooks --skip-kiro-mcp --skip-kiro-skills # # This command also waits for the user to approve the sign-in, so run it in # the background with its output in a file, show the user the URL and code it # prints, and wait for it to finish. # # Creating a workspace takes a couple of minutes: --org and --slug go together, # the slug becomes a permanent public hostname that cannot be changed, so show # it back to the user first, and if it is taken or malformed the run ends saying # so - ask for another and run again. # # Setup can return before a new workspace is reachable, and it can also report # success for a workspace that is not. So after setup, whatever route it took, # check before going on: # # kcap status --json # # Read `server.reachable`. If it is false, wait 15 seconds and check again, for # up to ten minutes, telling the user once that the workspace is being set up. # If it is still false after that, stop: tell the user the workspace at # `server.url` is not answering (`server.status_code` says what it returned), # and that nothing further can be done until it is. Do not go on to the # remaining steps against a workspace that is not reachable. # # # 6. Turn off an agent they deselected # ------------------------------------ # # Only if step 4 found an agent already wired up that the user did not pick: # # kcap plugin remove --codex # # One command per agent, with that agent's flag. Skip this step otherwise. # # # 7. Set what is recorded # ----------------------- # # On a machine that was already configured (step 2), rules set earlier are # still in force, whatever the user just answered, and the answer replaces # all of them, not only the kinds it mentions. Show what is set: # # kcap allow --list # kcap ignore --list # kcap config show # # Recording is already live on this machine, so replace the rules without a # moment in which none is in force: put the new rules in place first, with # the commands below, and only then take away what the answer does not keep. # config set replaces a whole list in one go, so a repository list is set # straight to its new value, or removed when the answer has none: # # kcap config unset allowed_repos # kcap config unset excluded_repos # # A folder rule is added before the old one it replaces is removed: # # kcap allow --remove # each old path the answer does not keep # kcap ignore --remove # each old path the answer does not keep # # If the user chose "everything", there is nothing to put in place first: # remove every rule that is set, with the commands above, and skip the rest # of this step. Otherwise set the new rules below, then come back and remove # the old ones, so the rules end up as exactly the user's answer. # # On a fresh machine, skip this step if the user chose "everything". Otherwise # there are two kinds of rule, and the answer may need both. # # Repositories, by owner/name: # # kcap config set allowed_repos acme/widgets,acme/api # only these # kcap config set excluded_repos acme/secrets # never these # # Folders, by path, one command per folder: # # kcap allow ~/dev # only sessions in this folder or below it # kcap ignore ~/notes # never sessions in this folder or below it # # Pick by what the user named. A repository rule matches the repository's git # remote, so it covers every clone and worktree of it, wherever it is on disk. # A folder with no remote, such as a notes folder, can only be ruled out as a # folder. If the user names a repository by its short name, read its owner # from the clone's remote, or ask. # # config set replaces the whole list, so pass every repository in one value, # separated by commas: running it twice keeps only the second. # # There is no rule for a whole organization. List its repositories, or use the # folder they are cloned under. # # The rules combine. A session is recorded only if every allow rule that is # set lets it in, and no exclude rule takes it out. So "only acme/widgets, # never ~/notes" is: # # kcap config set allowed_repos acme/widgets # kcap ignore ~/notes # # While the allow list stands, the ignore adds nothing, but it keeps ~/notes # out if the user later widens the allow list. With any allow rule set, a # session whose repository or folder cannot be worked out is not recorded. # # That is also why "only acme/widgets, plus ~/notes" cannot be set. An allowed # repository and an allowed folder narrow each other: a clone of acme/widgets # outside ~/notes would be dropped, and so would ~/notes, which has no # repository. Step 4 asks the user to choose one or the other. # # Do this before the import and before the restart: the rules apply to live # recording and to the import, and a session they rule out is dropped before # anything is sent. # # Show the user the result: # # kcap allow --list # kcap ignore --list # kcap config show # # # 8. Import past sessions # ----------------------- # # Skip this if the user chose "nothing for now". Otherwise: # # kcap import --repo . --yes # this repository only # # Repeating an import is safe: what is already stored is not uploaded again. # If the command times out, run it in the background, or give the user the # command for their own terminal. # # Do not pass --all. It imports every past session of every coding agent on # this machine, from every repository, and is the user's to run, not yours. If # they ask for more than this repository, give them `kcap import --all` for # their own terminal and say what it covers. # # # 9. Confirm it worked # -------------------- # # kcap whoami # kcap status --json # # `whoami` exits non-zero if the server rejects the credentials. In the status # output, `configured` and `server.reachable` should both be true. If # `server.reachable` is false, the workspace is not answering: say so, with # `server.url` and `server.status_code`, rather than reporting success. # # # Where to send the user next # --------------------------- # # Documentation: https://www.kurrent.io/docs/kcap/getting-started/quickstart/ # Machine-readable: https://www.kurrent.io/llms.txt # Server setup: https://www.kurrent.io/docs/kcap/getting-started/setup-server/ # # # 10. Tell the user what happens next # ----------------------------------- # # Hooks, skills and MCP servers are loaded when an agent session starts, so this # session is not being recorded and does not have them. End with these three # things, in plain words, in this order. # # First, start a new session: this one is not being recorded, and the next one # is. Say it as an instruction, with the way to do it in their agent if you # know it (for Claude Code, `claude` again, or `claude --continue` to keep # this conversation). # # Second, in that new session, say "Start kcap guided tour". The tour shows # what kcap records and what it does with it, from their own first session. # # Third, where to look: their workspace, `server.url` from step 9, is where # every recorded session appears, with its evaluation once the session ends. # If step 8 imported sessions, those are there already. # # That is the whole hand-off. Do not summarise what kcap is or what it will do # beyond this: the tour does that, from their own sessions, better than a # paragraph here can. # # Installs kcap, the kcap CLI, on macOS and Linux. # # curl -fsSL https://www.kurrent.io/install | bash # curl -fsSL https://www.kurrent.io/install | bash -s -- --beta # curl -fsSL https://www.kurrent.io/install | bash -s -- --version 1.2.3 # curl -fsSL https://www.kurrent.io/install | bash -s -- --no-setup # curl -fsSL https://www.kurrent.io/install | bash -s -- -- --server-url https://acme.kcap.ai # # Everything runs from main at the bottom, so a download cut short executes nothing. set -euo pipefail BASE_URL="${KCAP_INSTALL_BASE_URL:-https://www.kurrent.io}" # The archive URL is built here, never read from the manifest: a replaced manifest can then only # name genuine release assets, so the checksum and the bytes it covers come from two hosts. RELEASES_URL="${KCAP_INSTALL_RELEASES_URL:-https://github.com/kurrent-io/kcap-cli/releases/download}" INSTALL_ROOT="${KCAP_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/kcap}" BIN_DIR="${KCAP_BIN_DIR:-$HOME/.local/bin}" TMP_DIR="" STAGING="" cleanup() { if [ -n "$STAGING" ]; then rm -rf -- "$STAGING"; fi if [ -n "$TMP_DIR" ]; then rm -rf -- "$TMP_DIR"; fi } trap cleanup EXIT BOLD='' GREEN='' YELLOW='' RED='' RESET='' if [ -t 2 ]; then BOLD=$'\033[1m' GREEN=$'\033[32m' YELLOW=$'\033[33m' RED=$'\033[31m' RESET=$'\033[0m' fi say() { printf '%s\n' "$*" >&2; } ok() { printf '%sāœ“%s %s\n' "$GREEN" "$RESET" "$*" >&2; } warn() { printf '%s!%s %s\n' "$YELLOW" "$RESET" "$*" >&2; } die() { printf '%sāœ—%s %s\n' "$RED" "$RESET" "$*" >&2; exit 1; } usage() { cat >&2 <] [--no-setup] [-- ] --beta Install the latest beta --stable Install the latest stable release (default) --version Install an exact version --no-setup Do not run kcap setup after a first install -- Run kcap setup with these options, on an upgrade too. With no terminal, --no-prompt is added: see kcap setup --help Environment: KCAP_INSTALL_DIR Where versions are kept (default: ~/.local/share/kcap) KCAP_BIN_DIR Where the kcap command is linked (default: ~/.local/bin) EOF } detect_rid() { local os arch os="$(uname -s)"; arch="$(uname -m)" case "$os" in Darwin) # A shell under Rosetta reports x86_64 on Apple silicon. if [ "$arch" = x86_64 ] && [ "$(sysctl -in sysctl.proc_translated 2>/dev/null)" = 1 ]; then arch=arm64; fi [ "$arch" = arm64 ] || die "kcap runs on Apple silicon Macs only (found $arch)." echo osx-arm64 ;; Linux) case "$arch" in x86_64|amd64) arch=x64 ;; aarch64|arm64) arch=arm64 ;; *) die "Unsupported Linux architecture: $arch (kcap supports x64 and arm64)." ;; esac if is_musl; then echo "linux-musl-$arch"; else echo "linux-$arch"; fi ;; MINGW*|MSYS*|CYGWIN*) die "On Windows, run in PowerShell: irm $BASE_URL/install.ps1 | iex" ;; *) die "Unsupported operating system: $os" ;; esac } is_musl() { compgen -G '/lib/ld-musl-*.so.1' >/dev/null && return 0 ldd --version 2>&1 | grep -qi musl } download() { # if command -v curl >/dev/null 2>&1; then curl -fsSL --retry 3 -o "$2" "$1" elif command -v wget >/dev/null 2>&1; then wget -q -O "$2" "$1" else die "Neither curl nor wget is installed." fi } sha256_of() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 else die "Neither sha256sum nor shasum is installed; cannot verify the download." fi } # Reads one field without jq. The manifest is flat per platform, so a platform's object holds # no nested braces. json_field() { # — top-level string field local re="\"$2\"[[:space:]]*:[[:space:]]*\"([^\"]*)\"" [[ "$1" =~ $re ]] && printf '%s' "${BASH_REMATCH[1]}" } platform_object() { # local re="\"$2\"[[:space:]]*:[[:space:]]*\\{([^{}]*)\\}" [[ "$1" =~ $re ]] && printf '%s' "${BASH_REMATCH[1]}" } # Refreshes the agent integrations a previous kcap installed, so an upgrade picks up new hooks # and skills. Each step is a no-op unless the user opted in earlier, and a failure never fails # the install. refresh_integrations() { # # A kcap pinned with --version can predate `kcap refresh`. Its help is the probe: it exits 0 # only where the command exists, and refreshes nothing. if "$1" refresh --help >/dev/null 2>&1; then # Its stderr lists any step that failed. if "$1" refresh >/dev/null; then ok "Refreshed agent integrations" else warn "Some agent integrations were not refreshed. Run ${BOLD}kcap refresh${RESET} to retry." fi return 0 fi local vendor for vendor in --skills --codex --cursor --copilot --pi --opencode --gemini --kiro --antigravity --grok --muse --mistral-vibe ""; do # shellcheck disable=SC2086 "$1" plugin install $vendor --if-installed >/dev/null 2>&1 || true done ok "Refreshed agent integrations" } # The terminal kcap setup reads its answers from. Piped into bash, stdin is the script itself, so # setup cannot prompt on it; without a terminal to open (CI, a container) nothing is printed. setup_tty() { local tty="${KCAP_INSTALL_TTY:-/dev/tty}" if { : <"$tty"; } 2>/dev/null; then printf '%s' "$tty"; fi } path_hint() { local profile case "${SHELL##*/}" in zsh) profile="\$HOME/.zshrc" ;; bash) if [ "$(uname -s)" = Darwin ]; then profile="\$HOME/.bash_profile"; else profile="\$HOME/.bashrc"; fi ;; fish) say " fish_add_path $BIN_DIR"; return ;; *) profile="your shell profile" ;; esac say " echo 'export PATH=\"$BIN_DIR:\$PATH\"' >> $profile" } main() { local channel=latest version="" run_setup=true local setup_args=() while [ $# -gt 0 ]; do case "$1" in --beta) channel=beta ;; --stable) channel=latest ;; --version) [ $# -ge 2 ] || die "--version needs a value"; version="$2"; shift ;; --no-setup) run_setup=false ;; -h|--help) usage; exit 0 ;; --) shift; setup_args=("$@"); break ;; *) usage; die "Unknown option: $1" ;; esac shift done if ! $run_setup && [ ${#setup_args[@]} -gt 0 ]; then die "--no-setup cannot be combined with kcap setup options."; fi say "${BOLD}Installing kcap${RESET}" local rid; rid="$(detect_rid)" # Refusals come before anything changes. if [ -e "$BIN_DIR/kcap" ] && [ ! -L "$BIN_DIR/kcap" ]; then die "$BIN_DIR/kcap exists and was not installed by this script. Remove it and run the installer again." fi if [ -e "$INSTALL_ROOT/current" ] && [ ! -L "$INSTALL_ROOT/current" ]; then die "$INSTALL_ROOT/current is a directory, not a link this script made. Remove it and run the installer again." fi TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/kcap-install.XXXXXX")" local tmp="$TMP_DIR" local manifest_url if [ -n "$version" ]; then manifest_url="$BASE_URL/download/cli/${version#v}/manifest.json" else manifest_url="$BASE_URL/download/cli/channels/$channel.json" fi download "$manifest_url" "$tmp/manifest.json" || die "Could not fetch $manifest_url" local json; json="$(tr -d '\r\n' <"$tmp/manifest.json")" local target; target="$(json_field "$json" version)" || die "The manifest names no version." [[ "$target" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || die "The manifest names an invalid version: $target" local platform sha url="$RELEASES_URL/v$target/kcap-$rid.tar.gz" platform="$(platform_object "$json" "$rid")" || die "kcap $target has no build for $rid." sha="$(json_field "$platform" sha256)" || die "The manifest has no checksum for $rid." [[ "$sha" =~ ^[0-9a-f]{64}$ ]] || die "The manifest has an invalid checksum for $rid." say "Downloading kcap $target ($rid)" download "$url" "$tmp/kcap.tar.gz" || die "Could not download $url" local actual; actual="$(sha256_of "$tmp/kcap.tar.gz")" [ "$actual" = "$sha" ] || die "Checksum mismatch for $url: expected $sha, got $actual. Nothing was installed." ok "Checksum verified" mkdir -p "$INSTALL_ROOT/versions" STAGING="$INSTALL_ROOT/versions/.staging-$$" rm -rf -- "$STAGING"; mkdir -p "$STAGING" tar -xzf "$tmp/kcap.tar.gz" -C "$STAGING" || die "Could not extract the archive." [ -x "$STAGING/bin/kcap" ] || die "The archive does not contain bin/kcap." local upgrading=false [ -e "$INSTALL_ROOT/current/bin/kcap" ] && upgrading=true # A running kcap keeps its open files after the directory is replaced. rm -rf -- "$INSTALL_ROOT/versions/$target" mv "$STAGING" "$INSTALL_ROOT/versions/$target" STAGING="" ln -sfn "versions/$target" "$INSTALL_ROOT/current" printf '{"source":"script","channel":"%s"}\n' "$channel" >"$INSTALL_ROOT/install.json" mkdir -p "$BIN_DIR" ln -sfn "$INSTALL_ROOT/current/bin/kcap" "$BIN_DIR/kcap" ok "Installed kcap $target to $INSTALL_ROOT" if $upgrading; then refresh_integrations "$BIN_DIR/kcap" fi local on_path; on_path="$(command -v kcap 2>/dev/null || true)" say "" case ":$PATH:" in *":$BIN_DIR:"*) if [ -n "$on_path" ] && [ "$on_path" != "$BIN_DIR/kcap" ]; then warn "Another kcap comes first on your PATH: $on_path" case "$on_path" in *node_modules*|*npm*) say " Remove the npm install with: npm uninstall -g @kurrent/kcap" ;; *) say " Remove it, or put $BIN_DIR earlier on your PATH." ;; esac fi ;; *) warn "$BIN_DIR is not on your PATH. Add it with:" path_hint say " then open a new terminal." ;; esac # Options passed through are a request for setup, upgrade or not. With nobody at a terminal # to answer, setup must not prompt, and a failure has to fail the install. local tty="" unattended=false if [ ${#setup_args[@]} -gt 0 ]; then tty="$(setup_tty)" if [ -z "$tty" ]; then unattended=true case " ${setup_args[*]} " in *" --no-prompt "*) ;; *) setup_args=(--no-prompt "${setup_args[@]}") ;; esac fi elif ! $upgrading && $run_setup; then tty="$(setup_tty)" fi if $unattended || [ -n "$tty" ]; then say "${BOLD}kcap $target is installed.${RESET} Starting kcap setup." say "" if ! PATH="$BIN_DIR:$PATH" "$BIN_DIR/kcap" setup ${setup_args[@]+"${setup_args[@]}"} <"${tty:-/dev/null}"; then say "" $unattended && die "kcap setup failed. kcap $target is installed but not configured." # Interactively, the install stands whatever setup does; it can always be run again. warn "kcap setup did not finish. Run ${BOLD}kcap setup${RESET} to try again." fi if $upgrading; then say "Run ${BOLD}kcap daemon restart${RESET} if you run the daemon."; fi elif $upgrading; then say "${BOLD}kcap $target is ready.${RESET} Run ${BOLD}kcap daemon restart${RESET} if you run the daemon." else say "${BOLD}kcap $target is ready.${RESET} Run ${BOLD}kcap setup${RESET} to get started." fi } main "$@"