Holoscan Sensor Bridge demo bring-up Use this skill when the user wants to bring up the Holoscan Sensor Bridge demo environment end to end. This workflow has side effects. Never run it automatically. Only run it when the user explicitly invokes it. Before you start — required gates (do these first, in order) Gate 1 — Read environment variables. Before doing anything else, check these variables and print their resolved values to the user: SSH TARGET and REMOTE ROOT are required. Stop and ask the user for them if either is missing. Gate 2 — Present the phase plan. Before taking any action, show…

\\n' profiles=($(printf '%s\\n' \"${profiles[@]}\" | sort)); unset IFS\n\n if [[ ${#profiles[@]} -eq 0 ]]; then\n echo -e \"\\033[31mNo environment profiles found in $profile_dir\\033[0m\"\n echo -e \"\\033[33mCreate one by copying profiles/example-env.sh to profiles/\u003cname>-env.sh\\033[0m\"\n return 1\n fi\n\n echo \"\"\n echo -e \" \\033[36mHSB Environment Profiles\\033[0m\"\n echo -e \" \\033[36m========================\\033[0m\"\n echo \"\"\n\n local i\n for i in \"${!profiles[@]}\"; do\n local file=\"${profiles[$i]}\"\n local name\n name=\"$(basename \"$file\" .sh)\"\n name=\"${name%-env}\"\n\n local target=\"\" platform=\"\" preview=\"\"\n target=\"$(grep -m1 '^export SSH_TARGET=' \"$file\" | sed \"s/^export SSH_TARGET='\\\\([^']*\\\\)'.*/\\\\1/\")\"\n platform=\"$(grep -m1 '^export HSB_PLATFORM=' \"$file\" | sed \"s/^export HSB_PLATFORM='\\\\([^']*\\\\)'.*/\\\\1/\")\"\n [[ -n \"$target\" ]] && preview=\"$target\"\n [[ -n \"$platform\" ]] && preview=\"$preview ($platform)\"\n\n if [[ -n \"$preview\" ]]; then\n printf \" \\033[32m[%d] %s\\033[0m - \\033[90m%s\\033[0m\\n\" \"$((i + 1))\" \"$name\" \"$preview\"\n else\n printf \" \\033[32m[%d] %s\\033[0m\\n\" \"$((i + 1))\" \"$name\"\n fi\n done\n\n echo \"\"\n local choice\n read -rp \" Select profile [1-${#profiles[@]}]: \" choice\n\n if ! [[ \"$choice\" =~ ^[0-9]+$ ]]; then\n echo -e \"\\033[31mInvalid selection.\\033[0m\"\n return 1\n fi\n\n local idx=$((choice - 1))\n if (( idx \u003c 0 || idx >= ${#profiles[@]} )); then\n echo -e \"\\033[31mSelection out of range.\\033[0m\"\n return 1\n fi\n\n local selected=\"${profiles[$idx]}\"\n local profile_name\n profile_name=\"$(basename \"$selected\" .sh)\"\n profile_name=\"${profile_name%-env}\"\n\n cd \"$skill_path\" || return 1\n echo \"\"\n source \"$skill_path/set-hsb-env.sh\" \"$profile_name\"\n\n if [[ -z \"$SSH_TARGET\" ]]; then\n echo -e \"\\033[31mFailed to load environment variables.\\033[0m\"\n return 1\n fi\n\n echo -e \"\\033[36mLaunching Claude...\\033[0m\"\n claude\n}\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3042,"content_sha256":"103195a6268638f60b3be7e00c1d215bff6012d695a6c70b92a984e9a772ab08"},{"filename":"linux/set-hsb-env.sh","content":"#!/usr/bin/env bash\n# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: Apache-2.0\n# Profile loader for HSB environment profiles.\n# Usage: source set-hsb-env.sh \u003cprofile-name>\n#\n# This script must be sourced (not executed) so that exported\n# variables are available in the calling shell.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nPROFILE_DIR=\"$SCRIPT_DIR/profiles\"\n\nprofile=\"$1\"\n\nif [[ -z \"$profile\" ]]; then\n echo -e \"\\033[36mAvailable HSB profiles:\\033[0m\"\n found=false\n if [[ -d \"$PROFILE_DIR\" ]]; then\n for f in \"$PROFILE_DIR\"/*-env.sh; do\n [[ -f \"$f\" ]] || continue\n name=\"$(basename \"$f\" .sh)\"\n name=\"${name%-env}\"\n echo \" $name\"\n found=true\n done\n fi\n if [[ \"$found\" == false ]]; then\n echo \" (none found)\"\n echo \"\"\n echo -e \"\\033[33mCreate a profile by copying profiles/example-env.sh to profiles/\u003cname>-env.sh\\033[0m\"\n fi\n echo \"\"\n echo -e \"\\033[33mUsage: source set-hsb-env.sh \u003cname>\\033[0m\"\n return 0 2>/dev/null || exit 0\nfi\n\nconfig_file=\"$PROFILE_DIR/${profile}-env.sh\"\n\nif [[ ! -f \"$config_file\" ]]; then\n echo -e \"\\033[31mProfile not found: $config_file\\033[0m\" >&2\n echo \"Create it by copying profiles/example-env.sh to profiles/${profile}-env.sh\" >&2\n return 1 2>/dev/null || exit 1\nfi\n\nsource \"$config_file\"\n\nfor var in SSH_TARGET REMOTE_ROOT; do\n if [[ -z \"${!var}\" ]]; then\n echo -e \"\\033[31mMissing required environment variable: $var\\033[0m\" >&2\n return 1 2>/dev/null || exit 1\n fi\ndone\n\necho -e \"\\033[32mLoaded HSB profile: $profile\\033[0m\"\necho \" SSH_TARGET = $SSH_TARGET\"\necho \" REMOTE_ROOT = $REMOTE_ROOT\"\necho \" REMOTE_SUDO = $REMOTE_SUDO\"\necho \" SSH_OPTS = $REMOTE_SSH_OPTS\"\necho \" PLATFORM = $HSB_PLATFORM\"\necho \"\"\necho \"Start Claude Code in this same shell with: claude\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1954,"content_sha256":"aad41994d2f089214ef6bc98f07c3359622c3886a52949185e8044b7cea91199"},{"filename":"references/phase-details.md","content":"# Phase Details — hsb-setup\n\n## Execution rules\n\n### Auto-reboot and reconnection (shared procedure)\n\nMultiple phases (Phase 1 for Docker group changes, Phase 2 for power mode changes) may require a device reboot. When a reboot is needed, always follow this procedure:\n\n1. **Before issuing the reboot command**, save all completed state for the current phase to the remote session file, and note which sub-steps remain. The post-reboot heredoc block must pick up where the pre-reboot block left off.\n\n2. **Issue the command that triggers the reboot.** The SSH connection will drop — this is expected. Do not treat the SSH disconnect as a failure.\n\n ```bash\n sudo reboot\n ```\n\n3. **Wait and retry SSH connectivity** using a polling loop on the local machine.\n\n> The following SSH polling command works identically on both **Windows** (via Git Bash or WSL) and **Linux**.\n> If you are using PowerShell natively, run this in a Bash-compatible shell provided by Git for Windows or in WSL.\n\n ```bash\n echo \"Device is rebooting — waiting for SSH to come back...\"\n for i in $(seq 1 20); do\n sleep 15\n if ssh -o ConnectTimeout=10 -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET \"echo ok\" 2>/dev/null; then\n echo \"SSH reconnected after ~$((i * 15)) seconds\"\n break\n fi\n echo \"Attempt $i/20: not ready yet...\"\n done\n ```\n\n Allow up to 5 minutes (20 attempts x 15 seconds). If SSH does not come back within that window, stop and report:\n\n ```\n Device did not come back online within 5 minutes after reboot.\n Please verify the device is powered on and accessible, then re-invoke the skill.\n ```\n\n4. **After reconnecting**, verify the reboot took effect (e.g., `docker info --format '{{.ServerVersion}}'` for Docker group changes, `sudo nvpmodel -q` for power mode). Then continue with the remaining sub-steps in a new heredoc block that restores state from the session file.\n\n5. **Report the reboot in the phase summary.** In concise mode:\n\n ```\n - Device rebooted — SSH reconnected after ~45 seconds\n ```\n\n In verbose mode, show the full polling output.\n\n#### When to trigger auto-reboot\n\nOnly trigger auto-reboot when:\n\n- **Phase 1**: Docker group membership was changed (users added to `docker` group). Group changes require a reboot to take effect for all login sessions.\n- **Phase 2 / AGX Orin**: `nvpmodel -q` shows a mode other than MAXN (mode 0). If already MAXN, skip.\n- **Any future platform** whose setup documentation in the repo explicitly requires a reboot for a configuration change (e.g., `isolcpus` kernel parameter).\n\nDo **not** reboot for changes that take effect without a reboot (e.g., sysctl, nmcli, systemd service start).\n\n### Phase 1 - confirm platform, clone repo, and study user guide\n\n- **First**, run the SSH connectivity validation described in \"SSH connectivity validation (mandatory before session init)\" section. Then run the **session init** to create the remote state directory. Do not proceed to any remote commands until the session is initialized. All subsequent remote commands must use the **heredoc execution pattern** described in \"Persistent SSH session model\".\n- **Immediately after session init**, run the host platform auto-detection (see \"Host platform auto-detection\" section). Execute the detection script inside the first Phase 1 heredoc block to read `/sys/class/dmi/id/product_name` and reconcile it with `HSB_PLATFORM`. Apply the reconciliation rules and alert the user if the platform was changed or auto-detected. This must happen before any platform-dependent decisions are made.\n- Read `README.md`, `docker/`, and any host-setup docs in the repo if present.\n- Detect whether this is a fresh checkout or existing clone.\n- Check basic tools before building:\n - `git`\n - `git-lfs`\n - `docker`\n - `bash`\n - `xhost` when a GUI container is expected\n- If `git-lfs` is missing, install or instruct the user using the platform package manager.\n- If Docker exists but access fails with permission errors, add **all** non-system human users to the `docker` group (not just the current user). This ensures any user who logs into the devkit can run Docker without `sudo`:\n\n ```bash\n # Add every human user (UID >= 1000, excluding 'nobody') to the docker group\n for u in $(awk -F: '$3 >= 1000 && $1 != \"nobody\" {print $1}' /etc/passwd); do\n sudo usermod -aG docker \"$u\"\n done\n ```\n\n After updating group membership, a **reboot is required** for the change to take effect for all users and all login sessions. Follow the **auto-reboot and reconnection** shared procedure (see \"Auto-reboot and reconnection\" section above) to reboot the device and wait for SSH to come back:\n\n ```bash\n sudo reboot\n ```\n\n After reconnecting, verify Docker access works without `sudo`:\n\n ```bash\n docker info --format '{{.ServerVersion}}'\n ```\n\n If Docker still fails after reboot, fall back to running Docker commands with `sudo` for the remainder of the workflow and report the issue.\n\n#### Clone or refresh repo\n\nUse the latest top of tree from the configured GitHub repository.\n\n##### Determining the repo URL\n\nResolve the repo URL in this priority order:\n\n1. `--repo \u003cURL>` flag passed on the command line (highest priority)\n2. `HSB_REPO` environment variable\n3. Default: `https://github.com/nvidia-holoscan/holoscan-sensor-bridge.git`\n\nDerive the local directory name from the repo URL (e.g. `my-hsb-fork` from `https://github.com/myorg/my-hsb-fork.git`). Use `basename` on the URL and strip the `.git` suffix.\n\nPrint the resolved repo URL before cloning so the user can confirm.\n\n##### Preferred behavior\n\n- If repo does not exist locally:\n - `git clone $REPO_URL`\n- If repo already exists:\n - verify remote URL matches the resolved `$REPO_URL`. If it differs, warn the user and ask whether to re-clone or keep the existing repo.\n - fetch `origin`\n - switch to `main`\n - fast-forward pull only\n\n##### Safe sequence\n\n```bash\nREPO_URL=\"${HSB_REPO:-https://github.com/nvidia-holoscan/holoscan-sensor-bridge.git}\"\nREPO_DIR=$(basename \"$REPO_URL\" .git)\n\nif [ ! -d \"$REPO_DIR/.git\" ]; then\n git clone \"$REPO_URL\"\nelse\n cd \"$REPO_DIR\"\n CURRENT_URL=$(git remote get-url origin 2>/dev/null)\n if [ \"$CURRENT_URL\" != \"$REPO_URL\" ]; then\n echo \"WARNING: existing repo remote ($CURRENT_URL) differs from requested ($REPO_URL)\"\n # Skill should stop and ask the user before proceeding\n fi\n git fetch origin\n git checkout main\n git pull --ff-only origin main\nfi\n```\n\nIf `git lfs` is available, run `git lfs install` for **all** human users on the devkit (not just the current user), then run `git lfs pull` inside the repo. `git lfs install` writes filter configuration to each user's `~/.gitconfig`, so it must be run per-user:\n\n```bash\n# Install git-lfs hooks for every human user\nfor u in $(awk -F: '$3 >= 1000 && $1 != \"nobody\" {print $1}' /etc/passwd); do\n sudo -u \"$u\" git lfs install 2>/dev/null || true\ndone\ngit lfs pull\n```\n\n#### Study the user guide\n\n- Read the user guide at `docs/user_guide/setup.md` and `docs/user_guide/build.md` in the cloned repo.\n- Learn the host environment setup, demo container build process, application examples, and FPGA flashing instructions for the selected platform.\n- Identify platform-specific requirements (interface names, sysctl settings, PTP setup, SIPL/FuSa dependencies, NGC login, etc.).\n- **If all profile parameters are known** (`HSB_PLATFORM`, `SSH_TARGET`, etc.), use the user guide diagrams and setup descriptions to draw an ASCII or text-based diagram of the expected hardware topology (devkit, NIC/interface, cable, HSB board, sensor connections). Present it to the user and ask them to confirm this matches their physical setup before proceeding. If the user says the diagram does not match, ask what differs and adjust accordingly. If profile parameters are incomplete (platform not yet determined), skip the diagram and rely on the standard confirmation questions instead.\n- Use the knowledge gained here to drive Phase 2 and later phases.\n\n### Phase 2 - host prerequisite checks and platform network setup\n\nApply the host setup that matches the selected platform.\n\n#### Common checks\n\n- Ensure Docker daemon is running.\n- Ensure the user can talk to Docker.\n- Check that the board-side address `192.168.0.2` is reachable only **after** host networking is configured and the board is powered.\n\n#### Idempotent nmcli connection management\n\nAll platform network setup steps **must** use this helper function to create or update nmcli connections. The helper ensures that:\n\n1. If a valid connection with the correct name already exists and is properly configured, it is reused as-is.\n2. If duplicate connections with the same name exist, all duplicates are deleted and only one valid connection is kept (or a fresh one is created).\n3. A new connection is only created when none exists.\n\nDefine this shell function at the top of the Phase 2 heredoc block, before any network configuration commands:\n\n```bash\n# Idempotent nmcli connection helper\n# Usage: ensure_hololink_connection \u003ccon-name> \u003cifname> \u003cip4> [route] [ring-rx] [mtu]\n# - route, ring-rx, mtu can be empty strings to skip those settings\nensure_hololink_connection() {\n local CON_NAME=\"$1\" IFNAME=\"$2\" IP4=\"$3\" ROUTE=\"$4\" RING_RX=\"$5\" MTU=\"$6\"\n\n # Find all connection UUIDs with this name\n local UUIDS\n UUIDS=$(nmcli -g UUID,NAME con show 2>/dev/null | awk -F: -v name=\"$CON_NAME\" '$2 == name {print $1}')\n local COUNT=$(echo \"$UUIDS\" | grep -c . 2>/dev/null || echo 0)\n\n if [ \"$COUNT\" -gt 1 ]; then\n echo \"Found $COUNT duplicate connections named '$CON_NAME' — cleaning up\"\n # Delete all duplicates\n for uuid in $UUIDS; do\n sudo nmcli con delete uuid \"$uuid\" 2>/dev/null || true\n done\n UUIDS=\"\"\n COUNT=0\n fi\n\n if [ \"$COUNT\" -eq 1 ]; then\n # Validate the existing connection has the correct interface and IP\n local EXISTING_IFNAME EXISTING_IP4\n EXISTING_IFNAME=$(nmcli -g connection.interface-name con show \"$UUIDS\" 2>/dev/null)\n EXISTING_IP4=$(nmcli -g ipv4.addresses con show \"$UUIDS\" 2>/dev/null)\n if [ \"$EXISTING_IFNAME\" = \"$IFNAME\" ] && echo \"$EXISTING_IP4\" | grep -q \"${IP4%%/*}\"; then\n echo \"Connection '$CON_NAME' already exists and is valid (uuid=$UUIDS, ifname=$EXISTING_IFNAME, ip=$EXISTING_IP4)\"\n # Ensure optional settings are applied\n [ -n \"$ROUTE\" ] && sudo nmcli connection modify \"$UUIDS\" +ipv4.routes \"$ROUTE\" 2>/dev/null || true\n [ -n \"$RING_RX\" ] && sudo nmcli connection modify \"$UUIDS\" ethtool.ring-rx \"$RING_RX\" 2>/dev/null || true\n [ -n \"$MTU\" ] && sudo nmcli connection modify \"$UUIDS\" 802-3-ethernet.mtu \"$MTU\" 2>/dev/null || true\n sudo nmcli connection up \"$CON_NAME\" 2>&1 || echo \"WARNING: failed to activate $CON_NAME\"\n return 0\n else\n echo \"Connection '$CON_NAME' exists but has wrong ifname ($EXISTING_IFNAME) or IP ($EXISTING_IP4) — recreating\"\n sudo nmcli con delete uuid \"$UUIDS\" 2>/dev/null || true\n COUNT=0\n fi\n fi\n\n # Create fresh connection\n echo \"Creating new connection '$CON_NAME' on $IFNAME with $IP4\"\n sudo nmcli con add con-name \"$CON_NAME\" ifname \"$IFNAME\" type ethernet ip4 \"$IP4\"\n [ -n \"$ROUTE\" ] && sudo nmcli connection modify \"$CON_NAME\" +ipv4.routes \"$ROUTE\"\n [ -n \"$RING_RX\" ] && sudo nmcli connection modify \"$CON_NAME\" ethtool.ring-rx \"$RING_RX\"\n [ -n \"$MTU\" ] && sudo nmcli connection modify \"$CON_NAME\" 802-3-ethernet.mtu \"$MTU\"\n sudo nmcli connection up \"$CON_NAME\" 2>&1 || echo \"WARNING: failed to activate $CON_NAME\"\n}\n```\n\nAll platform sections below must call `ensure_hololink_connection` instead of running raw `nmcli con add` / `nmcli connection modify` commands directly.\n\n#### IGX Orin (CX7)\n\n- Discover `IN0` from `/sys/class/infiniband/*`\n- Derive `EN0` from `/sys/class/infiniband/$IN0/device/net/*`\n- Configure `EN0` to `192.168.0.101/24`\n- Add a route to `192.168.0.2/32`\n- Set `ethtool.ring-rx` to `4096`\n- Set `802-3-ethernet.mtu` to `4096`\n- Bring the connection up\n\nBefore creating any nmcli connection, use the **idempotent nmcli connection helper** (see \"Idempotent nmcli connection management\" below) to check for an existing valid connection, clean up duplicates, and only create a new connection if none exists.\n\nTypical commands:\n\n```bash\nLC_COLLATE=C IN=(/sys/class/infiniband/*)\nIN0=$(basename \"${IN[0]}\")\nEN0=$(basename /sys/class/infiniband/$IN0/device/net/*)\nensure_hololink_connection \"hololink-$EN0\" \"$EN0\" \"192.168.0.101/24\" \"192.168.0.2/32\" \"4096\" \"4096\"\n```\n\n#### AGX Orin\n\n- Default host port is typically `eno1`\n- Increase receive buffer\n- Configure static IP `192.168.0.101/24`\n- Set power mode to MAXN for optimal performance (requires reboot)\n- Set up `jetson_clocks` systemd service for maximum core clocks\n\nBefore creating any nmcli connection, use the **idempotent nmcli connection helper** (see \"Idempotent nmcli connection management\" below).\n\nTypical commands:\n\n```bash\necho 'net.core.rmem_max = 31326208' | sudo tee /etc/sysctl.d/52-hololink-rmem_max.conf\nsudo sysctl -p /etc/sysctl.d/52-hololink-rmem_max.conf\nEN0=eno1\nensure_hololink_connection \"hololink-$EN0\" \"$EN0\" \"192.168.0.101/24\" \"\" \"\" \"\"\n```\n\n##### MAXN power mode (AGX Orin)\n\nCheck the current power mode with `sudo nvpmodel -q`. If it is not already MAXN (mode 0), set it:\n\n```bash\necho \"YES\" | sudo nvpmodel -m 0\n```\n\n`nvpmodel -m 0` on AGX Orin requires a reboot and prompts for interactive confirmation. Piping `\"YES\"` answers the prompt and triggers an immediate reboot. After issuing this command, the SSH connection will drop. Follow the **auto-reboot and reconnection** procedure described below.\n\n##### jetson_clocks service (AGX Orin)\n\nCreate and enable a systemd service that runs `jetson_clocks` at startup:\n\n```bash\nJETSON_CLOCKS_SERVICE=/etc/systemd/system/jetson_clocks.service\ncat \u003c\u003cEOF | sudo tee $JETSON_CLOCKS_SERVICE >/dev/null\n[Unit]\nDescription=Jetson Clocks Startup\nAfter=nvpmodel.service\n\n[Service]\nType=oneshot\nExecStart=/usr/bin/jetson_clocks\n\n[Install]\nWantedBy=multi-user.target\nEOF\nsudo chmod u+x $JETSON_CLOCKS_SERVICE\nsudo systemctl enable jetson_clocks.service\nsudo systemctl start jetson_clocks.service\n```\n\nThis service activates after the MAXN reboot.\n\n#### DGX Spark\n\n- Same discovery pattern as IGX using `/sys/class/infiniband/*`\n- Configure first CX7 host netdev to `192.168.0.101/24`\n- Add route to `192.168.0.2/32`\n- Set RX ring to `4096`\n\n#### AGX Thor\n\n- Treat container build as `--igpu`\n- Prefer following repo docs if present for the exact MGBE interface naming\n- If running Linux socket based examples, increase receive buffers similar to AGX Orin\n- Do not assume `eno1`; detect the active 10GbE/MGBE interface from the repo/docs or system state\n\nWhen network setup cannot be safely inferred, stop and tell the user exactly what interface name you need.\n\n#### Auto-reboot and reconnection\n\nSome Phase 2 steps (e.g., setting MAXN power mode on AGX Orin) trigger an automatic reboot. Follow the **auto-reboot and reconnection** shared procedure (see \"Auto-reboot and reconnection\" section under \"Execution rules\") for the reboot, SSH polling, and verification steps.\n\n##### Phase 2 ordering when reboot is needed\n\nWhen a reboot is required, structure Phase 2 in two blocks:\n\n**Pre-reboot block** (single SSH heredoc):\n1. Docker daemon check\n2. `rmem_max` sysctl configuration\n3. Network interface configuration (nmcli)\n4. Any other config that persists across reboots\n5. Set MAXN power mode → triggers reboot\n\n**Post-reboot block** (new SSH heredoc after reconnection):\n1. Verify MAXN is active\n2. `jetson_clocks` service setup and start\n3. PTP setup (`phc2sys`, `ptp4l`)\n4. DLA compiler install\n5. NGC login check — and propagate credentials to all users (see \"NGC login for all users\" below)\n6. xhost / display detection (all users — see the xhost section under Phase 4)\n7. Board ping\n\nIf no reboot is needed (already MAXN), run all sub-steps in a single heredoc block.\n\n#### NGC login for all users\n\n`docker login nvcr.io` stores credentials in `~/.docker/config.json`, which is per-user. After the current user has successfully logged in to NGC, propagate the Docker credentials to **all** human users on the devkit so that any user can pull NGC container images:\n\n```bash\n# After the current user has a working NGC login, copy credentials to all human users\nSRC_DOCKER_CONFIG=\"$HOME/.docker/config.json\"\nif [ -f \"$SRC_DOCKER_CONFIG\" ]; then\n for u in $(awk -F: '$3 >= 1000 && $1 != \"nobody\" {print $1}' /etc/passwd); do\n DEST_DIR=\"/home/$u/.docker\"\n if [ \"$u\" != \"$USER\" ]; then\n sudo mkdir -p \"$DEST_DIR\"\n sudo cp \"$SRC_DOCKER_CONFIG\" \"$DEST_DIR/config.json\"\n sudo chown -R \"$u:$(id -gn \"$u\")\" \"$DEST_DIR\"\n fi\n done\n echo \"NGC credentials propagated to all users\"\nfi\n```\n\nIf NGC login has not been configured yet, ask the user to run `docker login nvcr.io` with their NGC API key, then propagate as above.\n\n### Phase 3 - native build of CLI tools (AGX Thor only)\n\nAGX Thor supports running `hololink-enumerate` and other C++ tools **natively on the host**, outside the demo container. This phase builds those tools so they can be used directly from the CLI.\n\n#### Prerequisites\n\nBefore building, verify these are installed on the Thor:\n\n- **CUDA toolkit** — expected at `/usr/local/cuda-13.0` (ships with JP 7.1)\n- **Holoscan SDK** — `sudo apt install -t r38.4 holoscan=3.9.0-2` (check with `dpkg -l holoscan`)\n- **cmake** — version 3.22+\n- **Build libraries** — `libfmt-dev`, `libssl-dev`, `libcurlpp-dev`, `libyaml-cpp-dev`, `libibverbs-dev`, `python3-dev`\n\nIf any are missing, install them following the AGX Thor tab in `docs/user_guide/setup.md` in the repo. The full dependency install command is:\n\n```bash\nsudo apt-get update\nPINNED_NVCOMP=5.0.0.6-1\nsudo apt install -y git-lfs cmake libfmt-dev libssl-dev libcurlpp-dev libyaml-cpp-dev libibverbs-dev python3-dev \\\n libnvcomp5-cuda-13=${PINNED_NVCOMP} \\\n libnvcomp5-dev-cuda-13=${PINNED_NVCOMP} \\\n libnvcomp5-static-cuda-13=${PINNED_NVCOMP} \\\n nvcomp-cuda-13=${PINNED_NVCOMP}\nsudo apt-mark hold libnvcomp5-cuda-13 libnvcomp5-dev-cuda-13 libnvcomp5-static-cuda-13 nvcomp-cuda-13\n```\n\n#### Build steps\n\n```bash\nexport PATH=/usr/local/cuda-13.0/bin:$PATH\nexport LD_LIBRARY_PATH=/usr/local/cuda-13.0/lib64:$LD_LIBRARY_PATH\ncd \u003crepo-root>\nmkdir -p build && cd build\ncmake -DHOLOLINK_BUILD_PYTHON=OFF ..\nmake -j$(nproc) hololink-enumerate\n```\n\nThis produces the native binary at `\u003crepo-root>/build/tools/enumerate/hololink-enumerate`.\n\nTo build all native C++ tools and examples (including SIPL/FuSa CoE examples), use:\n\n```bash\ncmake -DHOLOLINK_BUILD_SIPL=1 -DHOLOLINK_BUILD_FUSA=1 ..\nmake -j$(nproc)\n```\n\n#### Verification\n\nAfter building, run a quick sanity check:\n\n```bash\nls -la \u003crepo-root>/build/tools/enumerate/hololink-enumerate\n```\n\nThe binary should exist and be executable.\n\n### Phase 4 - build, run demo container, and verify connectivity\n\nFrom the repo root:\n\n```bash\nsh docker/build.sh --igpu\n```\n\nor\n\n```bash\nsh docker/build.sh --dgpu\n```\n\nBefore running the build:\n\n- confirm repo root\n- print the chosen mode and why\n- if the build script is not executable, use `sh` explicitly\n\n#### Common build failure handling\n\n- **Docker permission denied**\n - Add all non-system human users to the `docker` group (see Phase 1 for the command) and reboot if not already done in Phase 1.\n - If the user permits sudo, apply safe remedy where possible.\n- **NGC/auth or image pull errors**\n - explain that NVIDIA container pulls may require `docker login nvcr.io`\n - ask the user to authenticate if needed\n - after successful login, propagate credentials to all human users (see \"NGC login for all users\" in Phase 2)\n- **network timeout / DNS failures**\n - retry once\n - then surface the failing pull/build stage\n- **disk space issues**\n - report `df -h`\n - suggest targeted cleanup\n- **missing git-lfs content**\n - run `git lfs pull`\n - retry the build\n\n### Container lifecycle management\n\n**All `docker run` invocations** in this workflow must follow these rules to prevent orphaned containers that block ports and consume resources:\n\n#### Always use `--name` and `--rm`\n\nEvery `docker run` must include `--name \u003cunique_name>` and `--rm` so the container is identifiable and auto-removed on exit.\n\n#### Never rely on `timeout` alone to stop a container\n\n`timeout` sends SIGTERM/SIGKILL to the `docker run` **client process**, but the container itself keeps running in the Docker daemon. This means `timeout N docker run ...` does **not** stop the container.\n\n**Correct pattern** — use a background subshell with an explicit `docker stop`:\n\n```bash\n# Start container in detached mode\ndocker run -d --name my_container --rm [flags] hololink-demo:$VERSION \u003ccommand>\n\n# Collect logs for up to N seconds, then force-stop\ntimeout $N docker logs -f my_container 2>&1 || true\ndocker stop -t 2 my_container 2>/dev/null || true\n```\n\nOr for short-lived commands where you want output inline:\n\n```bash\nCONTAINER_NAME=\"hsb_enumerate_$\"\ndocker run -d --name \"$CONTAINER_NAME\" --rm [flags] hololink-demo:$VERSION \u003ccommand>\n\n# Wait up to N seconds for output\n( sleep $N; docker stop -t 2 \"$CONTAINER_NAME\" 2>/dev/null ) &\nWATCHDOG_PID=$!\ndocker logs -f \"$CONTAINER_NAME\" 2>&1 || true\nkill $WATCHDOG_PID 2>/dev/null\nwait $WATCHDOG_PID 2>/dev/null\n```\n\n#### Force-stop on timeout or failure\n\nWhenever a phase finishes (success or failure), ensure **no orphaned containers** remain:\n\n```bash\ndocker stop -t 2 \u003ccontainer_name> 2>/dev/null || true\ndocker rm -f \u003ccontainer_name> 2>/dev/null || true\n```\n\n#### Before running a container that binds a shared port\n\nCheck for and stop any conflicting containers first:\n\n```bash\ndocker rm -f \u003cprevious_container_name> 2>/dev/null || true\nsudo ss -ulnp | grep \u003cport_or_pattern> || true\n```\n\nThis is especially important for `hololink enumerate` and `hololink-enumerate`, which both bind the same UDP broadcast port and cannot coexist.\n\n#### Run the demo container\n\n**Scope**: This sub-step verifies that the demo container **starts correctly** and that the Holoscan SDK and hololink package are available inside it. Do **not** run `hololink enumerate`, `hololink-enumerate`, or any command that communicates with the HSB board in this phase. All board-facing commands belong in the ping-and-enumerate sub-step below.\n\nAcceptable verification commands during container startup inside the container:\n\n- `echo \"Container started successfully\"`\n- `python3 -c \"import hololink; print(hololink.__version__)\"` (may fail gracefully — that is OK)\n- `ls /usr/local/bin/hololink*` (list available binaries)\n\nDo **not** run: `hololink enumerate`, `hololink-enumerate`, `ping 192.168.0.2`, or any sensor/camera example.\n\n##### xhost over SSH\n\nWhen running over SSH, `DISPLAY=:0` often does not exist. Detect the correct display:\n\n1. Check which X sockets exist: `ls /tmp/.X11-unix/`\n2. Check active GUI sessions: `w` (look for a `tty` login with a desktop session)\n3. Set `DISPLAY` to match (e.g., if `X1` exists, use `DISPLAY=:1`)\n\nThen grant Docker X11 access for **all** human users on the devkit (not just the current user). This ensures any user who logs into the devkit can run GUI containers without re-running `xhost`:\n\n```bash\n# Detect the active display\nDISPLAY_NUM=$(ls /tmp/.X11-unix/ 2>/dev/null | head -1 | tr -d 'X')\nexport DISPLAY=\":${DISPLAY_NUM:-0}\"\n\n# Grant xhost access for every human user\nfor u in $(awk -F: '$3 >= 1000 && $1 != \"nobody\" {print $1}' /etc/passwd); do\n XAUTH_FILE=\"/home/$u/.Xauthority\"\n if [ -f \"$XAUTH_FILE\" ]; then\n XAUTHORITY=\"$XAUTH_FILE\" xhost +local:docker 2>/dev/null || true\n fi\ndone\n\n# Also run for the current user as a fallback\nexport XAUTHORITY=\"/home/$USER/.Xauthority\"\nxhost +local:docker 2>/dev/null || xhost + 2>/dev/null || true\n```\n\nIf `xhost +local:docker` still fails for all users, fall back to `xhost +`.\n\n##### demo.sh requires a TTY — use docker run directly over SSH\n\n`demo.sh` hardcodes `docker run -it`, which fails over non-interactive SSH with:\n\n```\nthe input device is not a TTY\n```\n\n**Resolution**: When running over SSH, invoke `docker run` directly without the `-it` flag, replicating all other arguments from `demo.sh`. Always use a unique `--name` so the container can be force-stopped if it hangs or exceeds a timeout:\n\n```bash\ncd /path/to/holoscan-sensor-bridge\nVERSION=$(cat VERSION)\nCONTAINER_NAME=\"hsb_demo_$\"\ndocker run \\\n --rm \\\n --net host \\\n --gpus all \\\n --runtime=nvidia \\\n --shm-size=1gb \\\n --privileged \\\n --name \"$CONTAINER_NAME\" \\\n -v $PWD:$PWD \\\n -v $ROOT:$ROOT \\\n -v $HOME:$HOME \\\n -v /sys/bus/pci/devices:/sys/bus/pci/devices \\\n -v /sys/kernel/mm/hugepages:/sys/kernel/mm/hugepages \\\n -v /dev:/dev \\\n -v /tmp/.X11-unix:/tmp/.X11-unix \\\n -v /tmp/argus_socket:/tmp/argus_socket \\\n -v /sys/devices:/sys/devices \\\n -v /var/nvidia/nvcam/settings:/var/nvidia/nvcam/settings \\\n -w $PWD \\\n -e NVIDIA_DRIVER_CAPABILITIES=graphics,video,compute,utility,display \\\n -e NVIDIA_VISIBLE_DEVICES=all \\\n -e DISPLAY=$DISPLAY \\\n -e enableRawReprocess=2 \\\n hololink-demo:$VERSION \\\n \u003ccommand>\n```\n\n**Cleanup after every container run**: If a container may hang or run indefinitely, always ensure it is stopped afterward:\n\n```bash\ndocker stop -t 2 \"$CONTAINER_NAME\" 2>/dev/null || true\ndocker rm -f \"$CONTAINER_NAME\" 2>/dev/null || true\n```\n\nWhen running from a local GUI session (not SSH), `sh docker/demo.sh` works as-is.\n\nIf the selected platform is iGPU and the container prints:\n\n- `Failed to detect NVIDIA driver version`\n\nreport that this is expected and continue.\n\nIf visualizer access fails or apps segfault, verify:\n\n- `DISPLAY` is set to the correct value (not necessarily `:0`)\n- `XAUTHORITY` points to the user's `.Xauthority` file\n- `xhost +local:docker` or equivalent ran on the host\n\n#### Ping and summarize\n\nVerify host connectivity to the board:\n\n```bash\nping -c 4 192.168.0.2\n```\n\nIf ping succeeds:\n\n- clearly say the HSB board is reachable on the control plane\n- note that successful ping alone does **not** prove enumeration or data plane health\n- run `hololink enumerate` inside the container using the detached + watchdog pattern described in \"Container lifecycle management\". The enumerate command runs indefinitely with no `--count` flag, so you **must** force-stop the container after collecting enough output. Use the following pattern:\n\n ```bash\n CONTAINER_NAME=\"hsb_enumerate_$\"\n docker run -d --name \"$CONTAINER_NAME\" --rm \\\n [all standard flags] \\\n hololink-demo:$VERSION \\\n hololink enumerate\n\n # Watchdog: force-stop after 10 seconds\n ( sleep 10; docker stop -t 2 \"$CONTAINER_NAME\" 2>/dev/null ) &\n WATCHDOG_PID=$!\n\n # Collect output until the container stops\n docker logs -f \"$CONTAINER_NAME\" 2>&1 || true\n\n # Clean up watchdog\n kill $WATCHDOG_PID 2>/dev/null\n wait $WATCHDOG_PID 2>/dev/null\n\n # Ensure container is gone (belt and suspenders)\n docker rm -f \"$CONTAINER_NAME\" 2>/dev/null || true\n ```\n\n Collect whatever responses appear in that window, then print MAC address, FPGA version, and board type. Do not wait longer than 10 seconds.\n\n##### Board type detection via UUID\n\nParse the `fpga_uuid` field from the enumerate output to determine the board type:\n\n| `fpga_uuid` | Board Type |\n|---|---|\n| `889b7ce3-65a5-4247-8b05-4ff1904c3359` | HSB Lattice (CPNX100-ETH-SENSOR-BRIDGE) |\n| `f1627640-b4dc-48af-a360-c55b09b3d230` | Leopard Imaging VB1940 (Eagle Camera) |\n\nIf the `fpga_uuid` field is present and matches one of the known UUIDs, report the board type. If the UUID is not reported (older firmware) or does not match either known value, note the board type as unknown and continue — the user may provide it later. Save the detected board type as `BOARD_TYPE` (`lattice` or `vb1940` or empty) in the session state.\n\n##### Native enumerate on AGX Thor\n\nOn AGX Thor, **also** run `hololink-enumerate` natively (outside the container) using the binary built in Phase 3:\n\n```bash\ncd \u003crepo-root>/build\ntimeout 5 ./tools/enumerate/hololink-enumerate\n```\n\n**Important**: the native binary and the containerized `hololink enumerate` both bind the same UDP broadcast port. They cannot run simultaneously. Before running the native binary:\n\n1. Force-stop any running demo/hololink containers: `docker stop -t 2 \u003cname> 2>/dev/null; docker rm -f \u003cname> 2>/dev/null`\n2. Check for lingering listeners: `sudo ss -ulnp | grep holo`\n3. If the port is still in use, identify and stop the holding process\n\nIf the native enumerate fails with `bind failed with errno=98: \"Address already in use\"`, this is the cause — stop the conflicting container or process, then retry.\n\nCompare the native output with the container output. Both should report the same MAC address, FPGA version, and serial number. The native binary may additionally report `fpga_uuid` and `board` fields. If the container enumerate did not report `fpga_uuid`, check whether the native output includes it and use it for board type detection (see \"Board type detection via UUID\" above).\n\n##### FPGA version verification\n\nAfter collecting the enumerate output, verify that the FPGA version is compatible with the HSB host software version on the devkit.\n\n**Step 1 — Extract the FPGA version.** Parse the FPGA version from the `hololink enumerate` output (look for `fpga_version` or a four-digit version like `24XX` or `25XX`). If enumerate did not report an FPGA version, fall back to reading register 0x80 inside the demo container:\n\n```bash\nCONTAINER_NAME=\"hsb_regread_$\"\ndocker run -d --name \"$CONTAINER_NAME\" --rm \\\n [all standard flags] \\\n hololink-demo:$VERSION \\\n python3 -c \"\nimport hololink\n# Read register 0x80 to extract FPGA version\n# Adapt the exact API call based on the installed hololink version\n\"\n\ntimeout 15 docker logs -f \"$CONTAINER_NAME\" 2>&1 || true\ndocker stop -t 2 \"$CONTAINER_NAME\" 2>/dev/null || true\ndocker rm -f \"$CONTAINER_NAME\" 2>/dev/null || true\n```\n\nIf both methods fail, report the failure and note that FPGA version could not be verified. Inform the user that the `/hsb-flash` skill will assume the FPGA version is 2407 (oldest supported) if they choose to flash later.\n\n**Step 2 — Read the HSB software version.** Get the software version from the repo's `VERSION` file:\n\n```bash\nHSB_VERSION=$(cat VERSION 2>/dev/null | tr -d '[:space:]')\necho \"HSB software version: $HSB_VERSION\"\n```\n\n**Step 3 — Check compatibility.** Use the following known compatibility mapping. The FPGA version must match the HSB software version's expected FPGA. If the repo or user guide documents a different mapping, prefer that.\n\n| HSB Software Version | Expected FPGA Version |\n|---|---|\n| v2.0.0 | 2407 or 2412 |\n| v2.2.0, v2.3.1 | 2507 |\n| v2.5.0 | 2510 |\n| v2.6.0+ | Check `docs/user_guide/` in the repo for the required FPGA version |\n\n**Step 4 — Report and suggest.** If the detected FPGA version does not match the expected version for the installed HSB software:\n\n```\nWARNING: FPGA version mismatch detected.\n Detected FPGA version: \u003cDETECTED_FPGA>\n HSB software version: \u003cHSB_VERSION>\n Expected FPGA version: \u003cEXPECTED_FPGA>\n\nThe HSB board firmware does not match the host software.\nYou can use the /hsb-flash skill to flash the board to FPGA version \u003cEXPECTED_FPGA>:\n /hsb-flash\n\nThis is not a blocking error — setup is complete — but applications may not\nwork correctly until the FPGA firmware is updated.\n```\n\nIf the versions match, confirm:\n\n```\nFPGA version verified: \u003cDETECTED_FPGA> matches HSB software \u003cHSB_VERSION>.\n```\n\nIf the FPGA version could not be determined, note it as an unverified item in the Phase 5 issues report and inform the user that `/hsb-flash` will assume FPGA version 2407 as the starting point if they choose to flash.\n\nIf ping fails:\n\n- explain whether the failure looks like routing, link, power, or interface-selection related\n- check:\n - board power state\n - cable seating\n - host interface static IP\n - route to `192.168.0.2`\n - `ip addr`\n - `ip route`\n - `nmcli con show`\n- **prompt the user** asking if the board might be at a different IP address. If the user provides an alternative IP, retry ping and enumerate with that address instead.\n- if ping works but later enumeration does not, explain possible firmware mismatch rather than connectivity loss. The FPGA version verification step above will detect and report any mismatch — suggest `/hsb-flash` if needed\n\n### Phase 5 - issues report\n\nProduce a summary report of every issue encountered during the workflow and how it was resolved. For each issue, include:\n\n- What happened (the symptom or error)\n- Root cause (why it happened)\n- Resolution (what fix was applied, or why no fix was needed)\n- Whether the issue is blocking or non-blocking\n\nAlso include a final summary table showing all phases and their pass/fail status.\n\n**Allow the user to export the report.** After displaying the report, ask:\n\n```\nWould you like to save this report to a file? [Y/n]\n```\n\nIf the user agrees, write the report to an `.md` file in the repo root directory (or `REMOTE_ROOT` if no repo) with a timestamped filename, e.g., `hsb-setup-report-2026-03-20.md`. If running remotely, create the file on the remote host. Confirm the file path after saving.\n\n### Phase 6 - close applications and hand off to user\n\nAfter the issues report is complete (or whenever the workflow ends), clean up and return control of the devkit to the user:\n\n1. **Stop any running containers** started by the skill:\n\n ```bash\n # Stop any HSB containers that may still be running\n docker ps --filter \"name=hsb_\" --format '{{.Names}}' | xargs -r docker stop -t 2 2>/dev/null || true\n docker ps --filter \"name=hololink\" --format '{{.Names}}' | xargs -r docker stop -t 2 2>/dev/null || true\n ```\n\n2. **Exit any active container shells** — ensure no container sessions are left open.\n\n3. **Navigate to the repo home directory** so the user's terminal is ready for work:\n\n ```bash\n cd $REMOTE_ROOT/$REPO_DIR\n ```\n\n4. **Print a handoff message**:\n\n ```\n Setup complete. You are now at the repo root directory: $REMOTE_ROOT/$REPO_DIR\n All containers have been stopped. The devkit is ready for your use.\n ```\n\n5. **Run session teardown** (see below).\n\n### Session teardown (after final phase)\n\nAfter Phase 6 (or whenever execution ends, including on failure), clean up the remote session state:\n\n```bash\nssh -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET \"rm -rf /tmp/.claude_hsb_session\"\n```\n\n\n## Output style\n\nBe operational and concrete.\n\n### Verbose mode (`--verbose`)\n\nShow full command output and use this detailed structure:\n\n```text\nPhase 2 — Host network setup\nStatus: partial\nRan:\n- detected interface enP5p3s0f0np0\n- created/updated NetworkManager connection hololink-enP5p3s0f0np0\nFailure:\n- ping 192.168.0.2 timed out\nLikely cause:\n- board not powered or cable not seated on port 0\nRepair attempted:\n- re-activated NetworkManager connection\nNext:\n- ask user to confirm board power and SFP+/QSFP cabling, then retry ping\n\nProceed to Phase 3? [Y/n]\n```\n\n### Concise mode (default, no `--verbose`)\n\nSuppress raw output and use this compact structure:\n\n```text\n**Phase 2 — Host network setup**\n- Docker daemon running\n- Network interface mgbe0_0 configured: 192.168.0.101/24\n- Route to 192.168.0.2 confirmed\n- rmem_max set to 31326208\n- PTP services (phc2sys, ptp4l) active\n- Board ping: 4/4 packets, 0% loss\n- Status: PASS\n\nProceed to Phase 3? [Y/n]\n```\n\nWith an issue:\n\n```text\n**Phase 2 — Host network setup**\n- Docker daemon running\n- Network interface mgbe0_0 configured: 192.168.0.101/24\n- Board ping: FAILED (timeout)\n- Attempted fix: re-activated nmcli connection\n- Board ping after fix: 4/4 packets, 0% loss\n- Status: PASS\n\n> Issue: Initial ping to 192.168.0.2 timed out\n> Cause: NetworkManager connection was down\n> Resolution: Re-activated hololink-mgbe0_0 connection\n> Blocking: No (resolved)\n\nProceed to Phase 3? [Y/n]\n```\n\n## Auto-approve mode (`--y`)\n\nThe skill supports a `--y` flag that skips all phase gates and runs the entire workflow from start to finish without waiting for user confirmation between phases. This is **not recommended** for normal use — interactive phase gates exist to give the user control over each step and the opportunity to review results, ask questions, or abort.\n\n### Detecting the flag\n\nCheck whether `$ARGUMENTS` contains `--y` (case-insensitive). Strip all flags from arguments before further parsing.\n\n### Confirmation warning\n\nWhen `--y` is detected, **do not proceed immediately**. First, display a warning and ask the user to confirm:\n\n```\n⚠ WARNING: Auto-approve mode (--y) is enabled.\n\nThis is NOT RECOMMENDED. All phase gates will be skipped and the entire\nworkflow will run without pausing for your confirmation between phases.\n\nYou will not be able to review intermediate results, ask questions, or\nabort between phases. All output will be saved to a timestamped log file.\n\nAre you sure you want to continue with auto-approve mode? [yes/NO]\n```\n\n- If the user responds with **\"yes\"** (exact match, case-insensitive) → enable auto-approve mode and proceed.\n- Any other response (including \"y\", \"ok\", blank, etc.) → cancel auto-approve mode, inform the user that the skill will run in normal interactive mode, and proceed without `--y`.\n\nThis double-confirmation is intentional — auto-approve mode bypasses a critical safety mechanism.\n\n### Behavior when `--y` is active\n\n1. **Phase gates are skipped**: After each phase summary, do not prompt `Proceed to Phase \u003cN+1>? [Y/n]`. Instead, immediately proceed to the next phase.\n\n2. **Log file**: At the start of the workflow (before Phase 0), create a timestamped log file to record all output:\n\n - **Log file name**: `hsb-setup-log-YYYY-MM-DD-HHMMSS.md`\n - **Log file location**: If running remotely, save to `$REMOTE_ROOT/` on the remote host. If running locally, save to the current working directory.\n - **Log content**: Accumulate the full phase summary (concise or verbose, depending on `--verbose`) for every phase, including any issues encountered and how they were resolved.\n - **Announce the log file** at the start:\n ```\n Auto-approve mode active. All output will be saved to:\n \u003clog_file_path>\n ```\n\n3. **Phase summaries are still shown**: Even though phase gates are skipped, still display each phase summary to the user so they can follow progress in real time.\n\n4. **At the end of the workflow**, write the final accumulated log to the log file and inform the user:\n ```\n Workflow complete. Full log saved to:\n \u003clog_file_path>\n ```\n\n5. **Failures still stop the workflow**: If a phase fails and the recovery playbook cannot fix it, stop the workflow even in auto-approve mode. Write the log up to that point and report the failure. Do not skip failures.\n\n### Combining with other flags\n\n- `--y --verbose`: Auto-approve with full raw output. Log file contains verbose output.\n- `--y --repo \u003cURL>`: Auto-approve with a custom repo.\n- `--y` alone: Auto-approve with concise output (default).\n\n\n## Verbosity mode (`--verbose`)\n\nThe skill supports a `--verbose` flag that controls how much output is shown to the user during execution.\n\n### Detecting the flag\n\nCheck whether `$ARGUMENTS` (the text after the slash command) contains any of: `--help` / `-h`, `--verbose`, `--y`, or `--repo \u003cURL>` (case-insensitive). Strip all flags (and their values) from arguments before parsing the platform name.\n\n- If `--help` or `-h` is present, print the built-in help text (see \"Built-in help\" section) and stop — do not run the workflow.\n- If `--y` is present, enter auto-approve mode (see \"Auto-approve mode\" section).\n- To extract the repo URL: match `--repo` followed by a whitespace-separated URL token. Example: `--repo https://github.com/myorg/my-fork.git`.\n\n### Behavior when `--verbose` is **set**\n\nThis is the **full-log mode** (the legacy/default behavior prior to this feature):\n\n- Show the complete raw output of every SSH command as it executes.\n- Show full tables of all prerequisite checks (even passing ones).\n- Show Docker build output, cmake output, and full enumerate logs.\n- Show the detailed phase status block (phase name, what ran, result, next action) after each phase.\n\n### Behavior when `--verbose` is **not set** (default / concise mode)\n\nShow a **concise bullet-point summary** after each phase. The user should be able to follow progress at a glance without scrolling through raw logs.\n\nRules for concise mode:\n\n1. **Do NOT show raw command output** to the user. Still execute the same SSH heredoc commands, but suppress their output from the conversation. Internally parse the output to extract status information.\n\n2. **After each phase, show exactly this structure:**\n\n ```\n **Phase N — \u003cphase title>**\n - \u003cbullet 1: key outcome or action taken>\n - \u003cbullet 2: key outcome or action taken>\n - ...\n - Status: PASS / PARTIAL / FAIL\n ```\n\n Keep bullets to 3–6 items max. Each bullet should be one short sentence.\n\n3. **Issues get special treatment.** If any issue was encountered during the phase (a command failed, a package was missing, a config needed fixing), add an `Issues:` block:\n\n ```\n **Phase N — \u003cphase title>**\n - \u003cbullet>\n - \u003cbullet>\n - Status: PASS\n\n > Issue: `nvidia-l4t-dla-compiler` package not found\n > Cause: Package is L4T/Orin-specific, not available on Thor JP 7.1\n > Resolution: Skipped — not needed for core HSB functionality\n > Blocking: No\n ```\n\n If there are multiple issues, list each one with the same 4-line format.\n\n4. **Tables are OK** for structured data that benefits from alignment (e.g., the final board summary in Phase 4, the Phase 5 issues report). Keep them compact.\n\n5. **Phase 1 prerequisite checks**: Instead of showing every check line, show a single summary like:\n ```\n - Prerequisites: git 2.43.0, git-lfs 3.4.1, docker 29.1.4, cmake 4.2.3, xhost — all OK\n - Missing: nvidia-l4t-dla-compiler (non-blocking)\n ```\n\n6. **Phase 4 (container build)**: Instead of showing Docker build output, show:\n ```\n - Building `hololink-demo:2.5.0` with `--igpu` mode\n - Build completed (all layers cached / N layers rebuilt)\n - Image size: 8.9 GB\n ```\n\n7. **Phase 4 (enumerate)**: Show the board summary table but not the raw repeated enumerate lines. Include the FPGA version verification result (match or mismatch with HSB software version).\n\n### Example: concise mode full run\n\n```\n**Phase 1 — Confirm platform, clone repo, and study user guide**\n- SSH connectivity verified to [email protected]\n- Platform auto-detected: AGX Thor (product_name: NVIDIA Jetson AGX Thor)\n- Session initialized\n- Prerequisites: git 2.43.0, git-lfs 3.4.1, docker 29.1.4, cmake 4.2.3, xhost — all OK\n- CUDA 13.0 found, Holoscan SDK 3.9.0-2 installed\n- Repo cloned at /home/work/holoscan-sensor-bridge (main, up to date)\n- User guide studied — platform-specific setup identified\n- Hardware topology diagram presented — user confirmed setup matches\n- Status: PASS\n\nProceed to Phase 2? [Y/n]\n```\n\n## Phase gate — user confirmation between phases\n\nAfter completing each phase (Phases 0–5), **always prompt the user for confirmation** before starting the next phase. This gives the user a chance to review results, ask questions, or abort.\n\n**Exception**: When `--y` (auto-approve mode) is active, phase gates are skipped and all phases run automatically. See \"Auto-approve mode (`--y`)\" section for details.\n\n### Prompt format\n\nAfter the phase summary (verbose or concise), end with:\n\n```\nProceed to Phase \u003cN+1>? [Y/n]\n```\n\n### User response handling\n\n- **\"y\"**, **\"yes\"**, **\"Y\"**, **blank/empty**, **\"1\"**, **\"ok\"**, **\"go\"**, **\"continue\"**, **\"next\"** → proceed to the next phase.\n- **\"n\"**, **\"no\"**, **\"stop\"**, **\"abort\"** → stop execution. Print:\n ```\n Workflow paused after Phase N. You can resume by re-invoking the skill.\n ```\n Then run session teardown.\n- **Any other text** → treat as a question or instruction about the current phase. Answer it, then re-prompt with the same `Proceed to Phase \u003cN+1>?` question.\n- **If the user asks to re-run the current phase** (e.g., \"retry\", \"run phase 3 again\") → re-execute that phase, show the summary again, then re-prompt.\n\n### Exceptions\n\n- **Phase 6** (close applications and hand off) is the final phase — do not prompt after it. Just show the handoff message and run session teardown.\n- **If a phase FAILs** and the recovery playbook cannot fix it, do not prompt to proceed. Instead stop and report the failure clearly.\n\n### Combining with verbose mode\n\nThe phase gate applies in **both** verbose and concise modes. The only difference is how much detail appears before the prompt.\n\n\n## Persistent SSH session model\n\nWhen running from Linux/Windows in SSH-native mode, open a **single SSH session per phase** and run **all commands for that phase in one remote shell invocation** using a heredoc block. This avoids re-authenticating for every command, preserves shell state (working directory, environment variables) within each phase, and maintains state across phases via a remote session file.\n\n**Do NOT run individual SSH commands for each remote operation.** Instead, compose all commands for a phase into a single heredoc block and execute them in one SSH call.\n\n### Session lifecycle\n\n1. **Session init** (before Phase 1): Validate SSH connectivity, then create a remote state directory.\n2. **Phase execution**: Each phase runs as a **single SSH call** with a heredoc block. All commands execute sequentially in the same remote shell. At the end of each block, save the working directory and key environment variables to a remote state file.\n3. **Session teardown** (after final phase): Clean up the remote state directory.\n\n### SSH command prefix\n\nUse key-based SSH authentication consistently for **all** remote calls:\n\n```bash\nssh -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET bash -s \u003c\u003c'REMOTE'\n# ... commands ...\nREMOTE\n```\n\n### Heredoc execution pattern (all phases)\n\nEvery phase MUST follow this pattern — a single SSH call with all phase commands inside:\n\n```bash\nssh -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET bash -s \u003c\u003c'REMOTE'\nset -e\n\n# ── restore state from previous phase ──\nsource /tmp/.claude_hsb_session/state.sh 2>/dev/null || true\ncd \"${_CLAUDE_CWD:-__REMOTE_ROOT__}\"\n\n# ── phase commands ──\necho \"=== Phase N: description ===\"\ncommand1\ncommand2\ncommand3\n\n# ── save state for next phase ──\nmkdir -p /tmp/.claude_hsb_session\n{\n echo \"export _CLAUDE_CWD=\\\"$(pwd)\\\"\"\n echo \"export PATH=\\\"$PATH\\\"\"\n echo \"export LD_LIBRARY_PATH=\\\"${LD_LIBRARY_PATH:-}\\\"\"\n # preserve any phase-specific vars (VERSION, DISPLAY, etc.)\n env | grep -E '^(VERSION|DISPLAY|XAUTHORITY|EN0|IN0)=' | sed 's/^/export /' 2>/dev/null\n} > /tmp/.claude_hsb_session/state.sh\nREMOTE\n```\n\nReplace `__REMOTE_ROOT__` with the literal value of `$REMOTE_ROOT` when composing the heredoc. Since the heredoc uses single-quoted `'REMOTE'`, local shell variables are **not** expanded — you must inline their values.\n\nFor commands that are allowed to fail without stopping the phase, append `|| true`:\n```bash\ndocker info || true # daemon may not be running yet\nping -c 1 -W 2 192.168.0.2 || true\n```\n\n### Privileged commands inside the heredoc\n\nWhen `REMOTE_SUDO` is non-empty, prefix privileged commands with it inside the heredoc:\n\n```bash\n# Inside the heredoc block:\nensure_hololink_connection \"hololink-$EN0\" \"$EN0\" \"192.168.0.101/24\" \"192.168.0.2/32\" \"4096\" \"4096\"\nsudo sysctl -p ...\n```\n\n### Session init (before Phase 1)\n\n```bash\nssh -o ConnectTimeout=10 -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET bash -s \u003c\u003c'REMOTE'\nmkdir -p /tmp/.claude_hsb_session\necho \"export _CLAUDE_CWD=\\\"__REMOTE_ROOT__\\\"\" > /tmp/.claude_hsb_session/state.sh\necho \"session initialized\"\nREMOTE\n```\n\n### File transfer\n\nFile transfers still use individual commands (they cannot be part of the heredoc):\n\n```bash\nscp $REMOTE_SSH_OPTS localfile $SSH_TARGET:/remote/path\n```\n\n### General rules\n\n- Wrap remote paths in single quotes inside the heredoc.\n- Use `REMOTE_ROOT` as the parent directory for cloning and builds.\n- If the user is on Linux or Windows and environment variables are not set, tell them to load the wrapper config first, then continue.\n- When a phase needs to read intermediate output (e.g., detect an interface name) and make decisions before continuing, split into two heredoc blocks within the same phase if necessary. Always save and restore state between blocks.\n\n### SSH connectivity validation (mandatory before session init)\n\nBefore executing any remote work, validate that key-based SSH authentication works. If it fails, **automatically diagnose and fix the issue** rather than asking the user to do it manually. The full resolution flow is below.\n\n#### Step 1 — Test connectivity\n\n```bash\nssh -o ConnectTimeout=10 -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET \"echo ok\"\n```\n\nIf this succeeds, print the following and proceed to session init:\n\n```\nSSH connectivity verified to $SSH_TARGET — opening persistent session\n```\n\nIf it fails, continue to Step 2.\n\n#### Step 2 — Diagnose the failure\n\nClassify the error output:\n\n| Error pattern | Diagnosis |\n|---|---|\n| `Permission denied (publickey)` or `Permission denied (publickey,password)` | Key not accepted — agent not running, key not loaded, or public key not deployed |\n| `Connection refused` | SSH daemon not running on remote host, or wrong port |\n| `Connection timed out` or `No route to host` | Network/firewall issue — host unreachable |\n\nFor **Connection refused** or **timed out**: report the error and stop. These require the user to fix network or SSH daemon issues on the remote host.\n\nFor **Permission denied**: proceed to Step 3 (automatic SSH key remediation).\n\n#### Step 3 — Ensure a local SSH key exists\n\nThis process works the same on both **Windows (Git Bash / MSYS2)** and **Linux**.\nIf your system does not follow these conventions, adjust paths and commands accordingly.\n\nDiscover the first available SSH public key on the host. Search in priority order:\n\n```bash\nSSH_PUBKEY=\"\"\nfor candidate in ~/.ssh/id_ed25519.pub ~/.ssh/id_ecdsa.pub ~/.ssh/id_rsa.pub; do\n if [ -f \"$candidate\" ]; then\n SSH_PUBKEY=\"$candidate\"\n break\n fi\ndone\n# Also check for non-standard key names\nif [ -z \"$SSH_PUBKEY\" ]; then\n SSH_PUBKEY=$(ls ~/.ssh/*.pub 2>/dev/null | head -n 1)\nfi\necho \"Found public key: ${SSH_PUBKEY:-NONE}\"\n```\n\n- If **a public key is found**, note its path (store as `$SSH_PUBKEY`) and proceed to Step 4. Derive the private key path by stripping the `.pub` suffix (e.g., `~/.ssh/id_ecdsa.pub` → `~/.ssh/id_ecdsa`). Use `$SSH_PUBKEY` and the derived private key path in all subsequent steps instead of hardcoding a key filename.\n- If **no SSH key exists** (`$SSH_PUBKEY` is empty), tell the user:\n\n ```\n No SSH key pair found on this machine. I will generate one now.\n ```\n\n Then generate a new Ed25519 key pair non-interactively (empty passphrase for automation):\n\n ```bash\n ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N \"\" -C \"$USER@$(hostname)\"\n SSH_PUBKEY=~/.ssh/id_ed25519.pub\n ```\n\n After generation, confirm the key files exist:\n\n ```bash\n ls -la ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub\n ```\n\n Then proceed to Step 4.\n\n#### Step 4 — Ensure the SSH agent is running and the key is loaded\n\nThese instructions apply to both **Windows (Git Bash / MSYS2)** and **Linux**, but details for launching an agent in some Linux distros or desktop environments may differ. The following works for most typical shells:\n\n```bash\nssh-add -l 2>&1\n```\n\n- If the output contains `Could not open a connection to your authentication agent` or `Error connecting to agent`:\n\n Start the agent and load the key discovered in Step 3 (`$SSH_PUBKEY` minus the `.pub` suffix):\n\n ```bash\n eval $(ssh-agent -s)\n ssh-add \"${SSH_PUBKEY%.pub}\"\n ```\n\n- If the agent is running but lists no identities (`The agent has no identities`):\n\n Load the key:\n\n ```bash\n ssh-add \"${SSH_PUBKEY%.pub}\"\n ```\n\n- If the agent already has the key loaded, proceed to Step 5.\n\nAfter loading, verify:\n\n```bash\nssh-add -l\n```\n\n> **Note for Linux Desktop Users:**\n> Some desktop environments automatically run an SSH agent and may even provide a keyring for passphrase unlock.\n> If you have a GUI popup asking for a passphrase, accept it to unlock your key.\n> For advanced scenarios (like custom key locations, no agent, or agent forwarding), consult your system's documentation for `ssh-agent` usage.\n\n#### Step 5 — Retry SSH with the loaded key\n\n```bash\nssh -o ConnectTimeout=10 -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET \"echo ok\"\n```\n\nIf this succeeds, print:\n\n```\nSSH connectivity verified to $SSH_TARGET — opening persistent session\n```\n\nIf it still fails with `Permission denied`, the public key is **not deployed** on the remote host. Proceed to Step 6.\n\n#### Step 6 — Deploy the public key to the remote host\n\nTell the user:\n\n```\nSSH key-based authentication failed — your public key is not yet authorized on the remote host.\n```\n\n**Do not attempt to deploy the key automatically.** Instead, present the following manual commands as the **first and recommended option**, based on the user's OS.\n\nExtract the userid and host from `$SSH_TARGET` (e.g., if `[email protected]`, userid is `nvidia` and host is `10.111.66.223`).\n\nUse the public key discovered in Step 3 (`$SSH_PUBKEY`). Use the **actual filename** found on the machine — do not hardcode a specific key type.\n\n**For Windows**\nTell the user to run this command in a **separate Windows PowerShell terminal**:\n\n```powershell\nGet-Content \"$env:USERPROFILE\\.ssh\\\u003ckey_filename>.pub\" | ssh \u003cuserid>@\u003chost> \"mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys\"\n```\n\nReplace `\u003ckey_filename>` with the actual key filename discovered in Step 3 (e.g., `id_ed25519`, `id_ecdsa`, `id_rsa`, or any custom name). Replace `\u003cuserid>@\u003chost>` with the actual `$SSH_TARGET` value.\n\n> **Note**: Do not use `type %USERPROFILE%\\...` — that is CMD syntax and will fail in PowerShell. Always use `Get-Content \"$env:USERPROFILE\\...\"` for PowerShell.\n\n**For Linux (including Git Bash/MSYS2):**\nTell the user to run this command in a **separate Linux terminal**:\n\n```bash\ncat ~/.ssh/\u003ckey_filename>.pub | ssh \u003cuserid>@\u003chost> \"mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys\"\n```\n\nReplace `\u003ckey_filename>` with the actual key filename discovered in Step 3 (e.g., `id_ed25519`, `id_ecdsa`, `id_rsa`, or any custom name). Replace `\u003cuserid>@\u003chost>` with the actual `$SSH_TARGET` value.\n\nTell the user:\n```\nThis will prompt you for the password for \u003cuserid>@\u003chost> once.\nAfter it completes, come back here and tell me to retry.\n```\n\n**Wait for the user to confirm** they have run the appropriate command (or that it failed) before trying any other approach. Do not suggest alternative options until the user reports this one did not work.\n\nOnly if the user explicitly says the above command failed, then suggest these fallback options:\n\n1. Use `ssh-copy-id` (available on Linux or Git Bash):\n ```bash\n ssh-copy-id -i $SSH_PUBKEY \u003cuserid>@\u003chost>\n ```\n (where `$SSH_PUBKEY` is the key discovered in Step 3)\n2. Manually copy the contents of the discovered public key file and append them to `~/.ssh/authorized_keys` on the remote host via any available access method (console, BMC, another SSH session).\n\n#### Step 7 — Final verification\n\nAfter deployment, re-test with `BatchMode=yes`:\n\n```bash\nssh -o ConnectTimeout=10 -o BatchMode=yes $REMOTE_SSH_OPTS $SSH_TARGET \"echo ok\"\n```\n\n- If this succeeds: print `SSH key deployed and verified — opening persistent session` and proceed to session init.\n- If this still fails: stop and report:\n\n ```\n SSH key deployment did not resolve the issue. Please verify:\n 1. The password entered was correct for $SSH_TARGET\n 2. The remote user's home directory and ~/.ssh have correct ownership\n 3. The remote SSH daemon allows pubkey authentication (check /etc/ssh/sshd_config for PubkeyAuthentication yes)\n ```\n\n Do not retry — report the error and stop.\n\n#### Summary of the auto-remediation flow\n\n```\nTest SSH → OK? → proceed\n ↓ (Permission denied)\n Check local keys → none? → generate Ed25519 key\n ↓\n Start agent / load key\n ↓\n Retry SSH → OK? → proceed\n ↓ (still denied)\n Show user the Windows key deploy command (type ... | ssh ...)\n ↓\n Wait for user to confirm → retry SSH → OK? → proceed\n ↓ (user says command failed)\n Show fallback options (ssh-copy-id, manual copy)\n ↓\n Wait for user → retry SSH → OK? → proceed\n ↓\n Stop with diagnosis\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":56938,"content_sha256":"a628c2c688bad5e68418ce9deb5da7614482df905acbd3c76d56715a9517268c"},{"filename":"scripts/hsb_phase_runner.sh","content":"#!/usr/bin/env bash\n# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: Apache-2.0\nset -euo pipefail\n\n# Helper for structured phase execution and log capture.\n# Intended for Claude Code to call when it wants consistent shell logging.\n\nLOG_DIR=\"${LOG_DIR:-.hsb-skill-logs}\"\nmkdir -p \"$LOG_DIR\"\n\nphase=\"${1:-}\"\nshift || true\n\nif [[ -z \"$phase\" ]]; then\n echo \"usage: $0 \u003cphase-name> \u003ccommand...>\" >&2\n exit 2\nfi\n\nif [[ $# -eq 0 ]]; then\n echo \"usage: $0 \u003cphase-name> \u003ccommand...>\" >&2\n exit 2\nfi\n\ntimestamp=\"$(date +%Y%m%d-%H%M%S)\"\nlog_file=\"$LOG_DIR/${timestamp}-${phase}.log\"\n\necho \"[HSB-SKILL] phase=$phase\"\necho \"[HSB-SKILL] log=$log_file\"\necho \"[HSB-SKILL] cmd=$*\"\n\nset +e\n\"$@\" 2>&1 | tee \"$log_file\"\nrc=${PIPESTATUS[0]}\nset -e\n\necho \"[HSB-SKILL] rc=$rc\"\nexit \"$rc\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":847,"content_sha256":"2760ec25543d9aef4fb88244d33582c46048719dfe81cdda4c3a77fb3c012177"},{"filename":"skill-card.md","content":"## Description: \u003cbr>\nClone the latest NVIDIA Holoscan Sensor Bridge repo, ask which supported devkit is being used, configure the host per platform, build the correct demo container, run it, and verify HSB connectivity by pinging 192.168.0.2. \u003cbr>\n\nThis skill is ready for commercial/non-commercial use. \u003cbr>\n\n## Owner\nNVIDIA \u003cbr>\n\n### License/Terms of Use: \u003cbr>\nApache-2.0 \u003cbr>\n## Use Case: \u003cbr>\nDevelopers and engineers setting up NVIDIA Holoscan Sensor Bridge demo environments on supported devkits (IGX Orin, AGX Orin, AGX Thor, DGX Spark). \u003cbr>\n\n### Deployment Geography for Use: \u003cbr>\nGlobal \u003cbr>\n\n## Requirements / Dependencies: \u003cbr>\n**Requires API Key or External Credential:** [Optional] \u003cbr>\n**Credential Type(s):** [Other [SSH key for remote devkit access]] \u003cbr> \n\nDo not include secrets in prompts/logs/output; use least-privilege credentials; rotate keys as appropriate. \u003cbr>\n\n## Known Risks and Mitigations: \u003cbr>\nRisk: Review before execution as proposals could introduce incorrect or misleading guidance into skills. \u003cbr>\nMitigation: Review and scan skill before deployment. \u003cbr>\n\n## Reference(s): \u003cbr>\n- [Phase Details](references/phase-details.md) \u003cbr>\n- [Platform Mapping](docs/platform-mapping.md) \u003cbr>\n- [Failure Playbook](docs/failure-playbook.md) \u003cbr>\n- [NVIDIA Holoscan Sensor Bridge (GitHub)](https://github.com/nvidia-holoscan/holoscan-sensor-bridge) \u003cbr>\n\n\n## Skill Output: \u003cbr>\n**Output Type(s):** [Shell commands, Configuration instructions, Analysis] \u003cbr>\n**Output Format:** [Markdown with inline bash code blocks] \u003cbr>\n**Output Parameters:** [1D] \u003cbr>\n**Other Properties Related to Output:** [Issues report exportable to file] \u003cbr>\n\n## Evaluation Agents Used: \u003cbr>\n- Claude Code (`claude-code`) \u003cbr>\n- Codex (`codex`) \u003cbr>\n\n\n\n## Evaluation Tasks: \u003cbr>\nEvaluated against 1 evaluation task (1 positive skill-activation case) via NVSkills-Eval external profile. \u003cbr>\n\n## Evaluation Metrics Used: \u003cbr>\nReported benchmark dimensions: \u003cbr>\n- Security: Checks whether skill-assisted execution avoids unsafe behavior such as secret leakage, destructive commands, or unauthorized access. \u003cbr>\n- Correctness: Checks whether the agent follows the expected workflow and produces the correct final output. \u003cbr>\n- Discoverability: Checks whether the agent loads the skill when relevant and avoids using it when irrelevant. \u003cbr>\n- Effectiveness: Checks whether the agent performs measurably better with the skill than without it. \u003cbr>\n- Efficiency: Checks whether the agent uses fewer tokens and avoids redundant work. \u003cbr>\n\nUnderlying evaluation signals used in this run: \u003cbr>\n- `security`: Checks for unsafe operations, secret leakage, and unauthorized access. \u003cbr>\n- `skill_execution`: Verifies that the agent loaded the expected skill and workflow. \u003cbr>\n- `skill_efficiency`: Checks routing quality, decoy avoidance, and redundant tool usage. \u003cbr>\n- `accuracy`: Grades final-answer correctness against the reference answer. \u003cbr>\n- `goal_accuracy`: Checks whether the overall user task completed successfully. \u003cbr>\n- `behavior_check`: Verifies expected behavior steps, including safety expectations. \u003cbr>\n- `token_efficiency`: Compares token usage with and without the skill. \u003cbr>\n\n\n\n## Evaluation Results: \u003cbr>\n| Dimension | Num | `claude-code` | `codex` |\n|---|---:|---:|---:|\n| Security | 1 | 100% (+0%) | 100% (+0%) |\n| Correctness | 1 | 0% (-50%) | 97% (+81%) |\n| Discoverability | 1 | 0% (-100%) | 89% (+48%) |\n| Effectiveness | 1 | 0% (+0%) | 78% (+78%) |\n| Efficiency | 1 | 27% (-68%) | 81% (+26%) |\n\n## Skill Version(s): \u003cbr>\n1.0.0 (source: frontmatter) \u003cbr>\n\n## Ethical Considerations: \u003cbr>\nNVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal team to ensure this skill meets requirements for the relevant industry and use case and addresses unforeseen product misuse. \u003cbr>\n\n(For Release on NVIDIA Platforms Only) \u003cbr>\nPlease report quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://app.intigriti.com/programs/nvidia/nvidiavdp/detail). \u003cbr>\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4215,"content_sha256":"80827d3206fc64640b012f2f8531f37d9be41d25e4de9beff486e13e393407d2"},{"filename":"windows/README-WINDOWS.md","content":"# Windows wrapper for SSH-native HSB Claude skill\n\nThis wrapper is for running Claude Code on Windows while executing the actual Holoscan Sensor Bridge workflow on a remote devkit over SSH.\n\n## Files\n\n- `profiles/example-env.ps1` - example environment settings\n- `set-hsb-env.ps1` - loads your SSH settings into the current PowerShell session\n- `run-hsb.cmd` - launches Claude Code with the environment preloaded from PowerShell\n- `test-hsb-thor-ssh.ps1` - validates SSH reachability, sudo mode, Docker access, and remote workspace path\n\n## Quick start\n\n1. Copy `profiles/example-env.ps1` to `profiles/AgxThor-env.ps1`\n2. Edit the variables for your host\n3. In PowerShell:\n\n```powershell\n. .\\set-hsb-env.ps1 -Profile AgxThor\n.\\test-hsb-thor-ssh.ps1\nclaude\n```\n\nOr use:\n\n```cmd\nrun-hsb.cmd AgxThor\n```\n\n## Environment variables used by the skill\n\n- `SSH_TARGET` - remote user and host, for example `[email protected]`\n- `REMOTE_ROOT` - directory on the remote machine where the repo will be cloned\n- `REMOTE_SUDO` - `sudo`, `sudo -n`, or empty string\n- `REMOTE_SSH_OPTS` - optional SSH options such as key path or strict host key settings\n- `HSB_PLATFORM` - optional preset platform hint such as `AGX Thor`, `AGX Orin`, `DGX Spark`\n- `HSB_REPO` - optional custom GitHub repository URL to clone. Defaults to `https://github.com/nvidia-holoscan/holoscan-sensor-bridge.git` if not set\n\n## Authentication\n\nThe skill uses **key-based SSH authentication**. Your SSH key must be loaded in the agent or specified via `REMOTE_SSH_OPTS` (e.g., `-i ~/.ssh/my_key`).\n\n## Notes\n\n- The skill still prompts when required, but these variables reduce repetitive setup.\n- `sudo -n` is best when passwordless sudo is already configured on the remote host.\n- Keep `REMOTE_ROOT` on the remote host's local filesystem, not on a mounted network drive.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1830,"content_sha256":"c7f49a818df961470bcb5b856d164559a1e34f5f2580387da17228985ad2f137"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Holoscan Sensor Bridge demo bring-up","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when the user wants to bring up the Holoscan Sensor Bridge demo environment end to end.","type":"text"}]},{"type":"paragraph","content":[{"text":"This workflow has side effects. Never run it automatically. Only run it when the user explicitly invokes it.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Before you start — required gates (do these first, in order)","type":"text"}]},{"type":"paragraph","content":[{"text":"Gate 1 — Read environment variables.","type":"text","marks":[{"type":"strong"}]},{"text":" Before doing anything else, check these variables and print their resolved values to the user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"SSH_TARGET Remote devkit login (e.g. [email protected]). Ask the user if not set.\nREMOTE_ROOT Remote working directory (e.g. /home/nvidia). Ask the user if not set.\nREMOTE_SUDO sudo / sudo -n / \"\" — default to \"sudo\" if not set.\nREMOTE_SSH_OPTS Additional SSH options (optional).\nHSB_PLATFORM Platform hint — may be empty; will detect from hardware.\nHSB_REPO Custom repo URL — defaults to https://github.com/nvidia-holoscan/holoscan-sensor-bridge.git","type":"text"}]},{"type":"paragraph","content":[{"text":"SSH_TARGET and REMOTE_ROOT are required. Stop and ask the user for them if either is missing.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Gate 2 — Present the phase plan.","type":"text","marks":[{"type":"strong"}]},{"text":" Before taking any action, show the user this exact plan and wait for acknowledgement:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"HSB Setup — Phase Plan\n Phase 0: Token-budget preflight\n Phase 1: Confirm platform, set up SSH, clone repo, study user guide\n Phase 2: Host prerequisite checks and network setup\n Phase 3: Native CLI build (AGX Thor only — skipped for other platforms)\n Phase 4: Build demo container, run it, ping 192.168.0.2, verify FPGA version\n Phase 5: Issues report (with option to save)\n Phase 6: Stop apps, exit container, hand control back to user","type":"text"}]},{"type":"paragraph","content":[{"text":"Gate 3 — Token-budget preflight (Phase 0).","type":"text","marks":[{"type":"strong"}]},{"text":" Run this before any SSH connection or devkit change. See ","type":"text"},{"text":"## Token-budget preflight","type":"text","marks":[{"type":"code_inline"}]},{"text":" section for the full procedure. Do not proceed to Phase 1 until the budget check passes.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Instructions","type":"text"}]},{"type":"paragraph","content":[{"text":"Invoke this skill by typing ","type":"text"},{"text":"/hsb-setup [PLATFORM] [OPTIONS]","type":"text","marks":[{"type":"code_inline"}]},{"text":". The skill walks through each phase interactively, prompting for confirmation before making changes.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"What this skill must do","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run the mandatory token-budget preflight before any remote command or devkit configuration change.","type":"text","marks":[{"type":"strong"}]},{"text":" Estimate the tokens needed to complete all setup phases, check the user's remaining subscription-plan usage with the best available Claude Code/account usage mechanism, display the estimate and result to the user, and stop if the available budget is insufficient or cannot be verified.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"prompt the user to confirm that the devkit is connected to the holoscan sensor bridge and everything is powered up and there is an active network connection to the outside world and that the devkit was installed with the proper OS version. if all profile parameters are known look into the repo user guide and draw a diagram of the devkit to sensor for the user to confirm that this is the setup they have.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Once the user confirms the setup is ready, build the ssh connection to the devkit if the user is running the claude skill from an external computer. you can skip this step if claude installed directly on the devkit.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify the host devkit platform by running ","type":"text"},{"text":"cat /sys/class/dmi/id/product_name","type":"text","marks":[{"type":"code_inline"}]},{"text":" on the devkit and comparing the result to the ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" environment variable using the product-name-to-platform mapping (see \"Host platform auto-detection\" section). If the command returns a recognized non-empty platform name that differs from ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":", or if ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty, update ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" to match the detected platform and alert the user about the change. If the command returns empty or fails and ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" is already set, keep the existing value.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clone or refresh the GitHub repository from the latest ","type":"text"},{"text":"main","type":"text","marks":[{"type":"code_inline"}]},{"text":" branch. By default this is the public ","type":"text"},{"text":"nvidia-holoscan/holoscan-sensor-bridge","type":"text","marks":[{"type":"code_inline"}]},{"text":" repo, but the user can override it with a custom repo URL via the ","type":"text"},{"text":"HSB_REPO","type":"text","marks":[{"type":"code_inline"}]},{"text":" environment variable or the ","type":"text"},{"text":"--repo \u003cURL>","type":"text","marks":[{"type":"code_inline"}]},{"text":" command-line flag. if the repo is an ssh repo, alert the user if no ssh key is set and provide instructions how to set up the ssh key.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask the user which devkit/platform they want to use ","type":"text"},{"text":"if it is not already clear","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"under the cloned repo root dir, study and understand the user guide at docs/user_guide to learn how to set up host environment for each devkit and OS, demo container, running applications inside and outside the container (where applicable) and flashing the FPGA.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Map that platform to the correct host setup and container build mode and make sure host set up is configured properly per user guide instructions, fix and add any missing configuration or prompt the user with instruction how to fix.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build the demo container.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run the demo container.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify connectivity to the board at ","type":"text"},{"text":"192.168.0.2","type":"text","marks":[{"type":"code_inline"}]},{"text":". if the connection to the board fails, prompt the user for a possiblity of a different ip address.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify the FPGA version reading register 0x80. if the FPGA version on the sensor does not match the hsb host software that is on the devkit, suggest the user to use the hsb-flash-skill to flash the board to the proper FPGA version.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Report progress in phases, explain failures clearly, and attempt safe fixes before giving up.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For every issue encountered, create a report that specifies what was the issue and how you overcame it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Allow the user an option to export the final report to an md file.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"once you are done setup, stop any running apps and exit the container giving up control on the devkit to the user at repo home directory on terminal window.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Supported platforms and build mapping","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the following mapping unless the repository or current docs in the working tree clearly say otherwise:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IGX Orin with dGPU OS/configuration","type":"text","marks":[{"type":"strong"}]},{"text":" → build with ","type":"text"},{"text":"sh docker/build.sh --dgpu","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IGX Orin iGPU","type":"text","marks":[{"type":"strong"}]},{"text":" → build with ","type":"text"},{"text":"sh docker/build.sh --igpu","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AGX Orin","type":"text","marks":[{"type":"strong"}]},{"text":" → build with ","type":"text"},{"text":"sh docker/build.sh --igpu","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AGX Thor","type":"text","marks":[{"type":"strong"}]},{"text":" → build with ","type":"text"},{"text":"sh docker/build.sh --igpu","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DGX Spark","type":"text","marks":[{"type":"strong"}]},{"text":" → build with ","type":"text"},{"text":"sh docker/build.sh --igpu","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"If the user says only “IGX Orin”, explicitly ask whether it is ","type":"text"},{"text":"iGPU","type":"text","marks":[{"type":"strong"}]},{"text":" or ","type":"text"},{"text":"dGPU OS/configuration","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Host platform auto-detection","type":"text"}]},{"type":"paragraph","content":[{"text":"During Phase 1 (after SSH is established or when running locally), verify the actual devkit hardware by reading the DMI product name and comparing it to the ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" environment variable.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Product-name-to-platform mapping","type":"text"}]},{"type":"paragraph","content":[{"text":"The following table maps known ","type":"text"},{"text":"/sys/class/dmi/id/product_name","type":"text","marks":[{"type":"code_inline"}]},{"text":" values to supported ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" values. Match using ","type":"text"},{"text":"case-insensitive substring","type":"text","marks":[{"type":"strong"}]},{"text":" search — the product name may contain additional text (e.g., \"Developer Kit\", revision numbers).","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"product_name","type":"text","marks":[{"type":"code_inline"}]},{"text":" contains (case-insensitive)","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mapped ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"IGX Orin","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"IGX Orin","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Still need to ask iGPU vs dGPU if not already known","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AGX Orin","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AGX Orin","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AGX Thor","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AGX Thor","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DGX Spark","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DGX Spark","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]}]},{"type":"paragraph","content":[{"text":"If the product name does not match any known pattern, treat it as ","type":"text"},{"text":"unrecognized","type":"text","marks":[{"type":"strong"}]},{"text":" and fall through to the manual platform question in step 5.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Detection and reconciliation logic","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the following on the devkit (inside the Phase 1 SSH heredoc or locally):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"DETECTED_PRODUCT=\"\"\nif [ -f /sys/class/dmi/id/product_name ]; then\n DETECTED_PRODUCT=$(cat /sys/class/dmi/id/product_name 2>/dev/null | tr -d '\\n')\nfi\n\nDETECTED_PLATFORM=\"\"\nif echo \"$DETECTED_PRODUCT\" | grep -qi \"IGX Orin\"; then\n DETECTED_PLATFORM=\"IGX Orin\"\nelif echo \"$DETECTED_PRODUCT\" | grep -qi \"AGX Orin\"; then\n DETECTED_PLATFORM=\"AGX Orin\"\nelif echo \"$DETECTED_PRODUCT\" | grep -qi \"AGX Thor\"; then\n DETECTED_PLATFORM=\"AGX Thor\"\nelif echo \"$DETECTED_PRODUCT\" | grep -qi \"DGX Spark\"; then\n DETECTED_PLATFORM=\"DGX Spark\"\nfi\n\necho \"DETECTED_PRODUCT=$DETECTED_PRODUCT\"\necho \"DETECTED_PLATFORM=$DETECTED_PLATFORM\"\necho \"HSB_PLATFORM=${HSB_PLATFORM:-}\"","type":"text"}]},{"type":"paragraph","content":[{"text":"After collecting the output, apply the following reconciliation rules:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is non-empty and ","type":"text","marks":[{"type":"strong"}]},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is empty","type":"text","marks":[{"type":"strong"}]},{"text":" → set ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":". Alert the user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Platform auto-detected from hardware: \u003cDETECTED_PLATFORM> (product_name: \u003cDETECTED_PRODUCT>).\nHSB_PLATFORM was not set — updating to \"\u003cDETECTED_PLATFORM>\".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is non-empty and differs from ","type":"text","marks":[{"type":"strong"}]},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" → override ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":". Alert the user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"WARNING: Hardware reports \"\u003cDETECTED_PLATFORM>\" (product_name: \u003cDETECTED_PRODUCT>),\nbut HSB_PLATFORM was set to \"\u003cHSB_PLATFORM>\".\nUpdating HSB_PLATFORM to match the detected hardware: \"\u003cDETECTED_PLATFORM>\".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is non-empty and matches ","type":"text","marks":[{"type":"strong"}]},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" → no change needed. Confirm:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Platform verified: \u003cHSB_PLATFORM> matches hardware (product_name: \u003cDETECTED_PRODUCT>).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is empty","type":"text","marks":[{"type":"strong"}]},{"text":" (file missing, unreadable, or unrecognized product name) ","type":"text"},{"text":"and ","type":"text","marks":[{"type":"strong"}]},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is set","type":"text","marks":[{"type":"strong"}]},{"text":" → keep the existing ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":". Warn:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Could not auto-detect platform from hardware (product_name: \"\u003cDETECTED_PRODUCT>\").\nKeeping existing HSB_PLATFORM: \"\u003cHSB_PLATFORM>\".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Both ","type":"text","marks":[{"type":"strong"}]},{"text":"DETECTED_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" and ","type":"text","marks":[{"type":"strong"}]},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" are empty","type":"text","marks":[{"type":"strong"}]},{"text":" → fall through to the manual platform question in step 5.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"After reconciliation, persist the updated ","type":"text"},{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the remote session state file so subsequent phases use the correct value.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Linux/Windows-friendly wrapper variables","type":"text"}]},{"type":"paragraph","content":[{"text":"When this skill is used from Linux/Windows with a local Claude Code session that shells out to SSH, prefer these environment variables when present:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SSH_TARGET","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the remote login target such as ","type":"text"},{"text":"nvidia@agx-thor-host","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"REMOTE_ROOT","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the remote working directory where the repo should live","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"REMOTE_SUDO","type":"text","marks":[{"type":"code_inline"}]},{"text":" for privileged commands. Accept ","type":"text"},{"text":"sudo","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"sudo -n","type":"text","marks":[{"type":"code_inline"}]},{"text":", or empty string","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"REMOTE_SSH_OPTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" for additional SSH options","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HSB_PLATFORM","type":"text","marks":[{"type":"code_inline"}]},{"text":" as an optional platform hint","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HSB_REPO","type":"text","marks":[{"type":"code_inline"}]},{"text":" for a custom GitHub repository URL to clone (e.g. ","type":"text"},{"text":"https://github.com/myorg/my-hsb-fork.git","type":"text","marks":[{"type":"code_inline"}]},{"text":"). If not set, defaults to ","type":"text"},{"text":"https://github.com/nvidia-holoscan/holoscan-sensor-bridge.git","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"If these are set, notify the user of these settings and use them without re-asking unless the user explicitly overrides them.","type":"text"}]},{"type":"paragraph","content":[{"text":"Before Phase 1, print the resolved remote execution settings you will use, with secrets redacted if needed.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Mandatory interaction pattern","type":"text"}]},{"type":"paragraph","content":[{"text":"Present the phase plan from Gate 2 above before making any changes. Skip Phase 3 for non-Thor platforms.","type":"text"}]},{"type":"paragraph","content":[{"text":"Then execute one phase at a time.","type":"text"}]},{"type":"paragraph","content":[{"text":"After each non-final phase (Phases 0–5):","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show a phase summary. The detail level depends on ","type":"text"},{"text":"--verbose","type":"text","marks":[{"type":"code_inline"}]},{"text":" mode (see \"Verbosity mode\" section):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verbose","type":"text","marks":[{"type":"strong"}]},{"text":": full output + detailed status block (phase name, what ran, result, next action).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Concise","type":"text","marks":[{"type":"strong"}]},{"text":" (default): bullet-point summary with issues highlighted.","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prompt the user","type":"text","marks":[{"type":"strong"}]},{"text":" with ","type":"text"},{"text":"Proceed to Phase \u003cN+1>? [Y/n]","type":"text","marks":[{"type":"code_inline"}]},{"text":" while specifing what is phase N+1 and wait for confirmation before continuing (see \"Phase gate\" section).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If something fails, do ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" just dump raw logs. Summarize:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"the exact command that failed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"the likely root cause","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"what safe repair you will try next","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"whether the repair succeeded","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Token-budget preflight","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 0 - token-budget preflight","type":"text"}]},{"type":"paragraph","content":[{"text":"This phase is mandatory and must run before any SSH connection, repo clone, package/configuration check, container build, reboot, or devkit setting change.","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Estimate the full-run token budget","type":"text","marks":[{"type":"strong"}]},{"text":" for the entire setup workflow, not just the next phase. The values below are conservative heuristics, not measured historical usage. Treat them as initial safety budgets and refine them from actual ","type":"text"},{"text":"/hsb-setup","type":"text","marks":[{"type":"code_inline"}]},{"text":" run logs once measured token usage is available:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reserve at least ","type":"text"},{"text":"280,000 tokens","type":"text","marks":[{"type":"strong"}]},{"text":" for a complete setup run on IGX Orin, AGX Orin, or DGX Spark.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reserve at least ","type":"text"},{"text":"340,000 tokens","type":"text","marks":[{"type":"strong"}]},{"text":" for AGX Thor because native build and SIPL/FuSa checks add more phases and troubleshooting.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"60,000 tokens","type":"text","marks":[{"type":"strong"}]},{"text":" when ","type":"text"},{"text":"--verbose","type":"text","marks":[{"type":"code_inline"}]},{"text":", custom repo handling, SSH key remediation, reboot recovery, or extra troubleshooting is expected.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use the larger estimate if the platform is not yet known.","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check remaining usage","type":"text","marks":[{"type":"strong"}]},{"text":" using the best available Claude Code/account usage source for the current subscription plan. Prefer machine-readable or product-provided usage data when available. If no reliable usage source is available, ask the user to provide their current remaining usage/quota from the Claude Code account or plan UI.","type":"text"}]},{"type":"paragraph","content":[{"text":"When asking the user because usage cannot be self-verified, present the options in this exact order so the safe stop choices appear first:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"I can't verify — stop","type":"text","marks":[{"type":"strong"}]},{"text":": The user cannot determine remaining usage. Stop before Phase 1.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"I have \u003c {estimate} available — stop","type":"text","marks":[{"type":"strong"}]},{"text":": The user checked their plan/account UI and confirms less than the estimated budget remains. Stop before Phase 1.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"I have ≥ {estimate} available — proceed","type":"text","marks":[{"type":"strong"}]},{"text":": The user checked their plan/account UI and confirms at least the estimated budget remains. Proceed to Phase 1.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Type something","type":"text","marks":[{"type":"strong"}]},{"text":": Treat as a question or free-form instruction, answer it, then re-prompt with the same ordered options.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Do not put the proceed option first. The user must intentionally move past the stop choices before selecting proceed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Display the result to the user","type":"text","marks":[{"type":"strong"}]},{"text":" before continuing:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Token-budget preflight\n- Estimated tokens required for complete /hsb-setup run: \u003cestimate>\n- Estimate basis: conservative heuristic; refine from actual run logs when available\n- Safety margin included: \u003cmargin>\n- Remaining plan usage available: \u003cavailable or \"unverified\">\n- Result: PASS / FAIL","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Stop on insufficient or unverifiable budget","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If remaining usage is lower than the estimate, stop before Phase 1 and explain that the skill is refusing to start because it may run out of tokens while modifying devkit settings.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If remaining usage cannot be verified, stop before Phase 1 and ask the user to start a fresh session, upgrade/refresh usage, or provide verifiable remaining usage.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--y","type":"text","marks":[{"type":"code_inline"}]},{"text":" must not bypass this preflight.","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Platform questions to ask when missing","type":"text"}]},{"type":"paragraph","content":[{"text":"Ask only the minimum required questions:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Which platform are you using?","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IGX Orin iGPU","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IGX Orin dGPU","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AGX Orin","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AGX Thor","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DGX Spark","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Is the HSB board already physically connected and powered on?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Are you okay with commands that require ","type":"text"},{"text":"sudo","type":"text","marks":[{"type":"code_inline"}]},{"text":" for network and Docker setup?","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If the user already provided any of these, do not ask again.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Available Scripts","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Arguments","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/hsb_phase_runner.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Structured shell execution with timestamped logs per phase","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cphase_name> \u003ccommand>","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"run_script(scripts/hsb_phase_runner.sh, \u003cphase_name>, \u003ccommand>)","type":"text","marks":[{"type":"code_inline"}]},{"text":" to run phase steps with automatic logging.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Phase details","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/phase-details.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/phase-details.md","title":null}}]},{"text":" for full step-by-step phase instructions, output style, verbosity behavior, auto-approve mode, phase gate rules, and the persistent SSH session model.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Recovery playbook","type":"text"}]},{"type":"paragraph","content":[{"text":"Try these fixes in order when applicable:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-run the failing command once if the failure looks transient.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fix missing prerequisites (","type":"text"},{"text":"git-lfs","type":"text","marks":[{"type":"code_inline"}]},{"text":", Docker access, ","type":"text"},{"text":"xhost","type":"text","marks":[{"type":"code_inline"}]},{"text":", network route).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Refresh repo state and LFS content.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-run only the failed phase, not the whole workflow.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If still blocked, stop with a concise diagnosis and a copy-paste command list for the user.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Supporting files in this skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"docs/platform-mapping.md","type":"text","marks":[{"type":"link","attrs":{"href":"docs/platform-mapping.md","title":null}}]},{"text":" for the authoritative build and host-setup summary used by this skill.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"docs/failure-playbook.md","type":"text","marks":[{"type":"link","attrs":{"href":"docs/failure-playbook.md","title":null}}]},{"text":" for common remediation logic.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"scripts/hsb_phase_runner.sh","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/hsb_phase_runner.sh","title":null}}]},{"text":" as a helper when you want structured shell execution and timestamped logs.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Built-in help (","type":"text"},{"text":"--help","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" contains ","type":"text"},{"text":"--help","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"-h","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"do not run the workflow","type":"text","marks":[{"type":"strong"}]},{"text":". Instead, print the following help text verbatim and stop:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Holoscan Sensor Bridge — Demo Bring-Up Skill\n\nUSAGE\n /hsb-setup [PLATFORM] [OPTIONS]\n\nPLATFORM (optional — will prompt if omitted)\n AGX Orin NVIDIA Jetson AGX Orin (iGPU, build with --igpu)\n AGX Thor NVIDIA Jetson AGX Thor (iGPU, build with --igpu)\n IGX Orin iGPU NVIDIA IGX Orin in iGPU configuration (build with --igpu)\n IGX Orin dGPU NVIDIA IGX Orin with discrete GPU (build with --dgpu)\n DGX Spark NVIDIA DGX Spark (iGPU, build with --igpu)\n\nOPTIONS\n --help, -h Show this help message and exit\n --verbose Show full raw command output for every phase\n (default is concise bullet-point summaries)\n --y Auto-approve all phase gates (skip user confirmation\n between phases). Not recommended — a confirmation\n warning is shown before proceeding. All output is\n saved to a timestamped log file.\n --repo \u003cURL> Clone a custom GitHub repo instead of the default\n nvidia-holoscan/holoscan-sensor-bridge.\n Can also be set via the HSB_REPO env var.\n Priority: --repo flag > HSB_REPO env var > default repo\n\nENVIRONMENT VARIABLES (set before invoking the skill)\n SSH_TARGET Remote login target (e.g. [email protected])\n REMOTE_ROOT Remote working directory for repo clone and builds\n REMOTE_SUDO Privilege escalation: 'sudo', 'sudo -n', or ''\n REMOTE_SSH_OPTS Additional SSH options (e.g. -o ServerAliveInterval=30)\n HSB_PLATFORM Platform hint (same values as PLATFORM above)\n HSB_REPO Custom GitHub repo URL (overridden by --repo flag)\n\nWORKFLOW PHASES\n Phase 0 Token-budget preflight; verify enough plan usage for a full run\n Phase 1 Confirm platform, clone repo, and study user guide\n Phase 2 Host prerequisite checks and network setup\n Phase 3 Native build of CLI tools (AGX Thor only, skipped otherwise)\n Phase 4 Build, run demo container, and verify connectivity\n Phase 5 Produce issues report, optionally export to file\n Phase 6 Stop apps, exit container, hand off to user\n\n The skill prompts for confirmation between each phase.\n\nEXAMPLES\n /hsb-setup AGX Thor\n /hsb-setup AGX Thor --verbose\n /hsb-setup AGX Thor --y\n /hsb-setup IGX Orin dGPU --repo https://github.com/myorg/my-fork.git\n /hsb-setup --help","type":"text"}]},{"type":"paragraph","content":[{"text":"After printing the help text, do not proceed with any phases or ask any questions.","type":"text"}]},{"type":"paragraph","content":[{"text":"See the ","type":"text"},{"text":"EXAMPLES","type":"text","marks":[{"type":"code_inline"}]},{"text":" section in ","type":"text"},{"text":"Built-in help (","type":"text","marks":[{"type":"link","attrs":{"href":"#built-in-help---help","title":null}}]},{"text":"--help","type":"text","marks":[{"type":"link","attrs":{"href":"#built-in-help---help","title":null}},{"type":"code_inline"}]},{"text":")","type":"text","marks":[{"type":"link","attrs":{"href":"#built-in-help---help","title":null}}]},{"text":" for invocation examples.","type":"text"}]},{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" contains a platform, use it instead of asking again. Strip ","type":"text"},{"text":"--verbose","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--y","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--repo \u003cURL>","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"--help","type":"text","marks":[{"type":"code_inline"}]},{"text":" from the arguments before parsing the platform name.","type":"text"}]}]},"metadata":{"date":"2026-11-02","name":"hsb-setup","tags":["holoscan-sensor-bridge","hsb","setup"],"tools":["Read","Write","Edit","Grep","Glob","Bash"],"author":"Holoscan Team \[email protected]>","source":{"stars":3825,"repo_name":"skills","origin_url":"https://github.com/NVIDIA/skills/blob/HEAD/skills/hsb-setup/SKILL.md","repo_owner":"NVIDIA","body_sha256":"479332edc6cbdcd4b571782d208cab4ce08460193b743c39d895b5e2dec5384b","cluster_key":"abf250ccb3593ae0fe481fcea5dd4cbb50876d99cd41a651cee1192657253a86","clean_bundle":{"format":"clean-skill-bundle-v1","source":"NVIDIA/skills/skills/hsb-setup/SKILL.md","text_bytes":87484,"source_commit":"25340619336e89bcc40365c28bfe9e790430468e","skills_sh_rank":9415,"attachment_count":10,"text_attachments":10,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/hsb-setup/SKILL.md","import_metadata":{"date":"2026-11-02","author":"Holoscan Team \[email protected]>","version":"1.0.0","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"license":"Apache-2.0","version":"1.0.0","category":"devops-infrastructure","metadata":{"tags":["holoscan-sensor-bridge","hsb","setup"],"team":"holoscan","agents":["claude-code","codex"],"author":"Holoscan Team \[email protected]>"},"import_tag":"skills-sh-top20k-2026-11","description":"Clone the latest NVIDIA Holoscan Sensor Bridge repo, ask which supported devkit is being used, configure the host per platform, build the correct demo container, run it, and verify HSB connectivity by pinging 192.168.0.2. Use for Holoscan Sensor Bridge setup, build, container launch, and first-connectivity bring-up.","allowed-tools":"Read,Write,Edit,MultiEdit,Grep,Glob,Bash","disable-model-invocation":true}},"renderedAt":1793661336965}

Holoscan Sensor Bridge demo bring-up Use this skill when the user wants to bring up the Holoscan Sensor Bridge demo environment end to end. This workflow has side effects. Never run it automatically. Only run it when the user explicitly invokes it. Before you start — required gates (do these first, in order) Gate 1 — Read environment variables. Before doing anything else, check these variables and print their resolved values to the user: SSH TARGET and REMOTE ROOT are required. Stop and ask the user for them if either is missing. Gate 2 — Present the phase plan. Before taking any action, show…