#!/usr/bin/env bash # # Orb setup for idx-cli. # # A fresh orb has no Rust toolchain and no Nix, while the repository builds and # verifies through its flake dev shell (`nix develop`). This script installs Nix # (single-user), realizes the dev shell, makes that environment available to the # login shells Amp and orb services start, warms the Cargo build, and pre-loads # local data (ownership snapshot + market cache) so the first agent command is # incremental and the shipped read commands have something to serve. # # Idempotent by design: an orb restored from a stale snapshot re-runs this and # keeps /nix, ~/.cargo, and target/ instead of rebuilding them. set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" nix_profile="$HOME/.nix-profile" nix_bin="$nix_profile/bin/nix" nix_conf="$HOME/.config/nix/nix.conf" bash_profile="$HOME/.bash_profile" agent_guidance="$HOME/.config/amp/AGENTS.md" profile_marker="# idx-cli orb dev shell (managed by .agents/setup)" idx_bin="$repo_root/target/debug/idx" guidance_start="" guidance_end="" # Amp runs setup from the repository root; be explicit so relative paths and # the dev shell shellHook behave the same however the script is invoked. cd "$repo_root" step() { printf '\n==> %s\n' "$*"; } # Runs "$@" with timing so setup.log shows which step is slow or failing. run() { local label="$1" shift local start=$SECONDS if "$@"; then printf ' ok: %s (%ss)\n' "$label" "$((SECONDS - start))" return 0 fi local status=$? printf ' FAILED: %s (%ss, exit %s)\n' "$label" "$((SECONDS - start))" "$status" >&2 return "$status" } # Warm-up work whose failure should not stop snapshot publication. run_warn() { if ! run "$@"; then printf 'warning: %s failed; rerun .agents/setup once the cause is fixed\n' "$1" >&2 fi } step "Nix package manager" if [ -x "$nix_bin" ]; then printf ' ok: already installed (%s)\n' "$("$nix_bin" --version)" else installer="$(mktemp)" curl -fsSL https://nixos.org/nix/install -o "$installer" run "install nix (single-user)" sh "$installer" --no-daemon --no-modify-profile --yes rm -f "$installer" fi # The installer cannot update the environment of this already-running script. # shellcheck source=/dev/null if [ -f "$nix_profile/etc/profile.d/nix.sh" ]; then . "$nix_profile/etc/profile.d/nix.sh" fi step "Nix flake support" mkdir -p "$(dirname "$nix_conf")" touch "$nix_conf" if ! grep -qE '^[[:space:]]*experimental-features[[:space:]]*=.*nix-command' "$nix_conf"; then printf 'experimental-features = nix-command flakes\n' >>"$nix_conf" fi if ! grep -qE '^[[:space:]]*warn-dirty[[:space:]]*=' "$nix_conf"; then printf 'warn-dirty = false\n' >>"$nix_conf" fi printf ' ok: %s\n' "$nix_conf" step "Flake dev shell" run "realize dev shell" "$nix_bin" develop --command true # Adopt the dev shell environment for the rest of this script. This is the same # `nix print-dev-env` mechanism the login-shell hook uses below, so a broken # dev shell fails setup loudly instead of only failing later for agents. dev_shell_env="$("$nix_bin" print-dev-env "$repo_root")" eval "$dev_shell_env" unset dev_shell_env step "Warm Cargo build (kept in the snapshot for the first agent command)" run_warn "cargo build --locked" cargo build --locked run_warn "cargo test --no-run --locked" cargo test --no-run --locked run_warn "cargo clippy --locked -- -D warnings" cargo clippy --locked -- -D warnings step "Warm local data" # Preferred ownership bootstrap: installs the maintained SQLite snapshot from # the published manifest, so `ownership releases|ticker|changes` work in a # fresh orb without a first sync. Re-running against a current DB is a no-op. run_warn "ownership sync" "$idx_bin" -q ownership sync # Warm the file cache for the shipped read commands. TTLs are short (5 min for # quotes, 1 h for fundamentals), so this mainly keeps the first commands # instant and makes `--offline` work; a miss just refetches from the provider. warm_market_cache() { local -a commands=( "stocks quote BBCA" "stocks history BBCA --period 1mo" "stocks technical BBCA" "stocks growth BBCA" "stocks valuation BBCA" "stocks risk BBCA" "stocks fundamental BBCA" "stocks profile BBCA" "stocks financials BBCA" "stocks earnings BBCA" "stocks sentiment BBCA" "stocks insights BBCA" "stocks news BBCA --limit 5" "stocks screen --limit 10" "stocks compare BBCA,BBRI" ) local failures=0 command for command in "${commands[@]}"; do # shellcheck disable=SC2086 # the command line is intentionally re-split if ! "$idx_bin" -q $command >/dev/null 2>&1; then printf ' could not warm: idx %s\n' "$command" >&2 failures=$((failures + 1)) fi done [ "$failures" -eq 0 ] } run_warn "market cache warm" warm_market_cache step "Login shell environment" # Amp starts its login shells (the headless executor and orb services) from /, # so activate the dev shell by repository path instead of by $PWD. The # IN_NIX_SHELL guard keeps nested shells and `nix develop` itself cheap. profile_hook() { cat </dev/null; then printf ' ok: dev shell hook already present in %s\n' "$bash_profile" else printf '\n' >>"$bash_profile" profile_hook >>"$bash_profile" printf ' ok: added dev shell hook to %s\n' "$bash_profile" fi step "Orb guidance for agents" mkdir -p "$(dirname "$agent_guidance")" if grep -Fq "$guidance_start" "$agent_guidance" 2>/dev/null; then # Replace a previous block so text updates reach orbs that already have one. sed -i "/$guidance_start/,/$guidance_end/d" "$agent_guidance" # Drop the blank separator lines the removed block left at the end of file. sed -i -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}' "$agent_guidance" fi cat >>"$agent_guidance" <&2 exit 1 fi ownership_db="$HOME/.local/share/idx/ownership.db" if [ -f "$ownership_db" ]; then printf ' ok: ownership DB present (%s)\n' "$ownership_db" else printf 'warning: no ownership DB at %s; `idx ownership sync` will install it on demand\n' "$ownership_db" >&2 fi