Created
February 26, 2026 21:12
-
-
Save shikhirsingh/50a222f67e215ad34f5fc6faf73f592e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # find-environment.sh (v6) | |
| # Purpose: Gather system info so an LLM can recommend correct install commands. | |
| # Works on macOS + Linux. Outputs key=value lines grouped by sections. | |
| set -euo pipefail | |
| section(){ echo; echo "===== $1 ====="; } | |
| kv(){ printf "%s = %s\n" "$1" "${2:-}"; } | |
| have(){ command -v "$1" >/dev/null 2>&1; } | |
| one_line(){ tr '\n' ';' | sed 's/;*$//' ; } | |
| ver_line() { | |
| local c="$1" out="" | |
| if ! have "$c"; then kv "TOOL.${c}.installed" "no"; return; fi | |
| out="$("$c" --version 2>/dev/null | head -n 1 || true)" | |
| [[ -z "$out" ]] && out="$("$c" -V 2>/dev/null | head -n 1 || true)" | |
| [[ -z "$out" ]] && out="$("$c" -v 2>/dev/null | head -n 1 || true)" | |
| [[ -z "$out" ]] && out="(version flag not recognized)" | |
| kv "TOOL.${c}.path" "$(command -v "$c")" | |
| kv "TOOL.${c}.version" "$out" | |
| } | |
| public_ip_best_effort() { | |
| local ip="" | |
| for url in \ | |
| "https://api.ipify.org" \ | |
| "https://ifconfig.me/ip" \ | |
| "https://checkip.amazonaws.com" \ | |
| "https://icanhazip.com" | |
| do | |
| if have curl; then | |
| ip="$(curl -fsS --max-time 5 "$url" 2>/dev/null | tr -d ' \n\r\t' || true)" | |
| elif have wget; then | |
| ip="$(wget -qO- --timeout=5 "$url" 2>/dev/null | tr -d ' \n\r\t' || true)" | |
| else | |
| ip="" | |
| fi | |
| if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ || "$ip" =~ : ]]; then | |
| echo "$ip"; return 0 | |
| fi | |
| done | |
| echo "unavailable" | |
| } | |
| # --------------------------- | |
| # TOP MESSAGE + LEGEND | |
| # --------------------------- | |
| echo "LLM_README = This output is a machine environment report grouped by '===== SECTION ====='. Each following line is 'key = value'. Use OS/HW to pick compatible installers and binaries; use VIRTUALIZATION to know if this is a VM/container (affects networking/services/drivers); use NETWORK+FW for connectivity/firewall; use OPEN_PORTS+WEBSERVER to avoid port conflicts; use NODE_ECOSYSTEM to handle Node version managers/proxies/registries." | |
| echo "LLM_LEGEND.VIRTUALIZATION = Interpreting VIRT.* keys: (1) VIRT.detect from systemd-detect-virt (if present) may return values like 'none' (bare metal), 'kvm', 'vmware', 'oracle' (VirtualBox), 'microsoft' (Hyper-V), 'qemu', 'lxc', 'docker', 'podman', etc. (2) VIRT.detect.container is specifically about containers; VIRT.detect.vm is specifically about virtual machines. (3) VIRT.wsl=yes means Linux is running under Windows Subsystem for Linux; kernel/module installs behave differently. (4) VIRT.cgroup_container_hint=yes means process 1 appears to be inside a container (Docker/LXC/Kubernetes), which often restricts systemctl/service management and changes network interfaces. (5) VIRT.cpu_hypervisor_flag=present indicates the CPU reports being virtualized, but does NOT prove container vs VM by itself. Implications: containers/WSL often cannot run systemd normally, may have NAT networking, and cannot install kernel drivers; VMs may allow drivers but can have bridged/NAT network differences." | |
| echo "LLM_LEGEND.FIREWALL = Interpreting FW.* keys: UFW/firewalld/nftables/iptables may be active even if one tool is missing. If firewall is active, incoming ports may be blocked; installations that expose services (Postgres 5432, Redis 6379, web 80/443) may need allow rules. On macOS, socketfilterfw global state indicates built-in firewall." | |
| echo "LLM_LEGEND.OPEN_PORTS = OPEN_PORTS.tcp_listen enumerates listening sockets. If process/PID is shown (ss/lsof), that identifies what owns a port (nginx, node, docker-proxy). If no PID appears, permissions may be limited; try running the script with sudo or interpret as incomplete." | |
| echo "LLM_LEGEND.NODE = Node installs frequently break due to: version managers (nvm/fnm/volta/asdf) selecting a different node than expected; registries/proxies/certs (npm config registry/proxy/cafile/strict-ssl); and missing native build toolchain (Xcode CLT on macOS, build-essential/python3 on Linux). Use NODE.mgr.* + NPM.* + NODE_NATIVE_BUILD_PREREQS." | |
| # --------------------------- | |
| # SYSTEM | |
| # --------------------------- | |
| section "SYSTEM" | |
| kv "REPORT.name" "find-environment" | |
| kv "REPORT.version" "6" | |
| kv "DATE_UTC" "$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || true)" | |
| kv "HOSTNAME" "$(hostname 2>/dev/null || true)" | |
| kv "USER" "${USER:-}" | |
| kv "SHELL" "${SHELL:-}" | |
| kv "PWD" "$(pwd 2>/dev/null || true)" | |
| kv "UMASK" "$(umask 2>/dev/null || true)" | |
| kv "UPTIME" "$(uptime 2>/dev/null || true)" | |
| os_name="$(uname -s 2>/dev/null || true)" | |
| kv "OS.uname" "$os_name" | |
| kv "OS.kernel" "$(uname -r 2>/dev/null || true)" | |
| kv "HW.arch" "$(uname -m 2>/dev/null || true)" | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| kv "OS.family" "macOS" | |
| kv "OS.version" "$(sw_vers -productVersion 2>/dev/null || true)" | |
| kv "OS.build" "$(sw_vers -buildVersion 2>/dev/null || true)" | |
| kv "OS.product_name" "$(sw_vers -productName 2>/dev/null || true)" | |
| else | |
| kv "OS.family" "Linux" | |
| if [[ -f /etc/os-release ]]; then | |
| # shellcheck disable=SC1091 | |
| . /etc/os-release | |
| kv "OS.pretty_name" "${PRETTY_NAME:-}" | |
| kv "OS.distro_id" "${ID:-}" | |
| kv "OS.distro_version" "${VERSION_ID:-}" | |
| kv "OS.distro_like" "${ID_LIKE:-}" | |
| fi | |
| kv "OS.libc" "$( (have ldd && ldd --version 2>/dev/null | head -n1) || echo "unknown" )" | |
| fi | |
| kv "LLM_CONTEXT.SYSTEM" "Interpretation: OS.family + HW.arch decide packaging and binary compatibility (e.g., arm64 vs x86_64)." | |
| # --------------------------- | |
| # INIT SYSTEM | |
| # --------------------------- | |
| section "INIT_SYSTEM" | |
| if have systemctl; then | |
| kv "INIT.system" "systemd" | |
| elif have launchctl; then | |
| kv "INIT.system" "launchd" | |
| else | |
| kv "INIT.system" "unknown" | |
| fi | |
| kv "LLM_CONTEXT.INIT_SYSTEM" "Interpretation: databases and daemons are managed by systemd on many Linux systems, and by launchd on macOS. Containers often lack a full init system." | |
| # --------------------------- | |
| # HARDWARE | |
| # --------------------------- | |
| section "HARDWARE" | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| kv "CPU.brand" "$(sysctl -n machdep.cpu.brand_string 2>/dev/null || true)" | |
| kv "CPU.physical_cores" "$(sysctl -n hw.physicalcpu 2>/dev/null || true)" | |
| kv "CPU.logical_cores" "$(sysctl -n hw.logicalcpu 2>/dev/null || true)" | |
| kv "HW.model" "$(sysctl -n hw.model 2>/dev/null || true)" | |
| mem_bytes="$(sysctl -n hw.memsize 2>/dev/null || true)" | |
| if [[ -n "${mem_bytes:-}" ]]; then | |
| kv "RAM.bytes" "$mem_bytes" | |
| kv "RAM.gib" "$(awk -v b="$mem_bytes" 'BEGIN{printf "%.2f", b/1024/1024/1024}')" | |
| fi | |
| kv "RAM.swapusage" "$(sysctl -n vm.swapusage 2>/dev/null || true)" | |
| else | |
| kv "CPU.model" "$(awk -F: '/model name/ {print $2; exit}' /proc/cpuinfo 2>/dev/null | sed 's/^ //')" | |
| kv "CPU.logical_cores" "$(getconf _NPROCESSORS_ONLN 2>/dev/null || true)" | |
| [[ -r /proc/meminfo ]] && kv "RAM.meminfo_MemTotal_kB" "$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || true)" | |
| have free && kv "RAM.free_human" "$(free -h 2>/dev/null | one_line)" | |
| have lscpu && kv "CPU.lscpu_selected" "$(lscpu 2>/dev/null | awk -F: ' | |
| /Architecture|Model name|Vendor ID|CPU\(s\)|Thread\(s\) per core|Core\(s\) per socket|Socket\(s\)|Virtualization/{ | |
| gsub(/^[ \t]+/,"",$2); printf "%s=%s; ", $1, $2 | |
| }' | sed 's/; $//')" | |
| fi | |
| kv "LLM_CONTEXT.HARDWARE" "Interpretation: RAM/cores constrain Docker + DBs; very low RAM may require lightweight DB configs or container limits." | |
| # --------------------------- | |
| # STORAGE | |
| # --------------------------- | |
| section "STORAGE" | |
| kv "DISK.df_root" "$(df -h / 2>/dev/null | tail -n 1 || true)" | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| have diskutil && kv "DISK.diskutil_root" "$(diskutil info / 2>/dev/null | awk -F: ' | |
| /File System Personality|Volume Name|Device Identifier|Disk Size|APFS Container Reference/{ | |
| gsub(/^[ \t]+/,"",$2); printf "%s=%s; ", $1, $2 | |
| }' | sed 's/; $//')" | |
| else | |
| have lsblk && kv "DISK.lsblk" "$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE -r 2>/dev/null | one_line)" | |
| fi | |
| kv "LLM_CONTEXT.STORAGE" "Interpretation: Docker images + DB data dirs + build caches can quickly consume disk; low free space commonly breaks installs." | |
| # --------------------------- | |
| # GPU | |
| # --------------------------- | |
| section "GPU" | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| kv "GPU.summary" "$(system_profiler SPDisplaysDataType 2>/dev/null | awk ' | |
| /Chipset Model:/ {gsub(/^[ \t]+Chipset Model: /,""); printf "Chipset=%s; ", $0} | |
| /VRAM/ {gsub(/^[ \t]+/,""); printf "%s; ", $0} | |
| /Metal:/ {gsub(/^[ \t]+/,""); printf "%s; ", $0} | |
| ' | sed 's/; $//')" | |
| else | |
| if have nvidia-smi; then | |
| kv "GPU.nvidia" "$(nvidia-smi --query-gpu=name,driver_version,memory.total,memory.free,compute_mode --format=csv,noheader 2>/dev/null | one_line)" | |
| kv "GPU.nvidia.cuda_version_hint" "$(nvidia-smi 2>/dev/null | grep -oE 'CUDA Version: [0-9.]+' | head -n1 | sed 's/CUDA Version: //' || true)" | |
| fi | |
| have nvcc && kv "GPU.cuda_toolkit" "$(nvcc --version 2>/dev/null | one_line)" | |
| if have rocm-smi; then | |
| kv "GPU.rocm" "$(rocm-smi 2>/dev/null | head -30 | one_line)" | |
| fi | |
| if have lspci; then | |
| kv "GPU.pci" "$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | one_line)" | |
| elif [[ -d /sys/class/drm ]]; then | |
| kv "GPU.sysfs_drm" "$(ls -1 /sys/class/drm 2>/dev/null | tr '\n' ',' | sed 's/,$//')" | |
| fi | |
| fi | |
| kv "LLM_CONTEXT.GPU" "Interpretation: NVIDIA+driver suggests CUDA possible; AMD+rocm-smi suggests ROCm; otherwise assume CPU-only unless you explicitly install GPU stack." | |
| # --------------------------- | |
| # VIRTUALIZATION | |
| # --------------------------- | |
| section "VIRTUALIZATION" | |
| if have systemd-detect-virt; then | |
| kv "VIRT.detect" "$(systemd-detect-virt 2>/dev/null || true)" | |
| kv "VIRT.detect.container" "$(systemd-detect-virt --container 2>/dev/null || true)" | |
| kv "VIRT.detect.vm" "$(systemd-detect-virt --vm 2>/dev/null || true)" | |
| else | |
| kv "VIRT.detect" "unknown (systemd-detect-virt not installed)" | |
| fi | |
| if [[ -r /proc/version ]] && grep -qi microsoft /proc/version 2>/dev/null; then | |
| kv "VIRT.wsl" "yes" | |
| else | |
| kv "VIRT.wsl" "no" | |
| fi | |
| if [[ -r /proc/1/cgroup ]] && grep -Eqi 'docker|kubepods|containerd|lxc' /proc/1/cgroup 2>/dev/null; then | |
| kv "VIRT.cgroup_container_hint" "yes" | |
| kv "VIRT.cgroup_sample" "$(head -n 5 /proc/1/cgroup 2>/dev/null | one_line)" | |
| else | |
| kv "VIRT.cgroup_container_hint" "no" | |
| fi | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| hv="$(sysctl -n kern.hv_vmm_present 2>/dev/null || true)" | |
| [[ -n "${hv:-}" ]] && kv "VIRT.kern_hv_vmm_present" "$hv" | |
| else | |
| if [[ -r /proc/cpuinfo ]] && grep -q "^flags" /proc/cpuinfo 2>/dev/null; then | |
| grep -q " hypervisor " /proc/cpuinfo 2>/dev/null && kv "VIRT.cpu_hypervisor_flag" "present" || kv "VIRT.cpu_hypervisor_flag" "not_present_or_unknown" | |
| fi | |
| fi | |
| kv "LLM_CONTEXT.VIRTUALIZATION" "Interpretation: If VIRT.detect is 'docker/lxc/podman' OR VIRT.cgroup_container_hint=yes, treat as a container: system services may not be manageable and host networking differs. If VIRT.detect is 'kvm/vmware/oracle/microsoft/qemu', treat as a VM: you likely can run systemd and install packages normally, but IPs may be NATed. If VIRT.wsl=yes, treat as WSL: kernel drivers/systemd behaviors differ." | |
| kv "LLM_CONTEXT.VIRTUALIZATION.IMPLICATIONS" "Common impacts: (a) Installing GPU drivers/kernel modules often fails inside containers/WSL. (b) Opening inbound ports may require host/VM NAT config. (c) systemctl may not exist or may not work inside containers." | |
| # --------------------------- | |
| # NETWORK + FIREWALL | |
| # --------------------------- | |
| section "NETWORK" | |
| if have curl; then | |
| kv "NET.internet_https" "$(curl -fsS --max-time 5 https://example.com >/dev/null 2>&1 && echo "CONNECTED" || echo "OFFLINE/RESTRICTED")" | |
| else | |
| kv "NET.internet_https" "unknown (curl missing)" | |
| fi | |
| kv "NET.public_ip" "$(public_ip_best_effort)" | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| kv "NET.local_ip.en0" "$(ipconfig getifaddr en0 2>/dev/null || true)" | |
| kv "NET.local_ip.en1" "$(ipconfig getifaddr en1 2>/dev/null || true)" | |
| have ifconfig && kv "NET.ifconfig_summary" "$(ifconfig 2>/dev/null | awk ' | |
| /^[a-z0-9]/ {iface=$1; gsub(":", "", iface)} | |
| /inet / && $2!="127.0.0.1" {print iface" inet="$2} | |
| /ether / {print iface" mac="$2} | |
| ' | one_line)" | |
| kv "NET.default_route" "$(route -n get default 2>/dev/null | awk -F: '/gateway|interface/{gsub(/^[ \t]+/, "", $2); printf "%s=%s; ", $1, $2}' | sed 's/; $//')" | |
| kv "NET.dns_resolvers" "$(scutil --dns 2>/dev/null | awk '/nameserver\[[0-9]+\]/{print $3}' | paste -sd, - || true)" | |
| else | |
| if have ip; then | |
| kv "NET.interfaces" "$(ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | paste -sd, - || true)" | |
| kv "NET.ipv4_addrs" "$(ip -o -4 addr show 2>/dev/null | awk '{print $2"="$4}' | paste -sd';' - || true)" | |
| kv "NET.default_route" "$(ip route show default 2>/dev/null | head -n 1 || true)" | |
| else | |
| kv "NET.ipv4_addrs" "$(hostname -I 2>/dev/null | tr ' ' ',' | sed 's/,$//' || true)" | |
| fi | |
| if have resolvectl; then | |
| kv "NET.dns_resolvers" "$(resolvectl dns 2>/dev/null | one_line)" | |
| elif [[ -f /etc/resolv.conf ]]; then | |
| kv "NET.dns_resolvers" "$(awk '/^nameserver/{print $2}' /etc/resolv.conf 2>/dev/null | paste -sd, - || true)" | |
| fi | |
| fi | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| if [[ -f /usr/libexec/ApplicationFirewall/socketfilterfw ]]; then | |
| kv "FW.macos.global_state" "$(/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2>/dev/null || echo "unknown")" | |
| kv "FW.macos.stealth_mode" "$(/usr/libexec/ApplicationFirewall/socketfilterfw --getstealthmode 2>/dev/null || echo "unknown")" | |
| else | |
| kv "FW.macos" "unknown (socketfilterfw missing)" | |
| fi | |
| else | |
| if have ufw; then | |
| kv "FW.ufw.status" "$( (sudo -n ufw status 2>/dev/null || ufw status 2>/dev/null) | head -n 8 | one_line)" | |
| else | |
| kv "FW.ufw.installed" "no" | |
| fi | |
| if have firewall-cmd; then | |
| kv "FW.firewalld.state" "$(firewall-cmd --state 2>/dev/null || echo "unknown")" | |
| else | |
| kv "FW.firewalld.installed" "no" | |
| fi | |
| if have nft; then | |
| kv "FW.nftables.ruleset_present" "$( (sudo -n nft list ruleset 2>/dev/null || nft list ruleset 2>/dev/null) | head -n 1 >/dev/null && echo "yes" || echo "no_or_perm")" | |
| else | |
| kv "FW.nftables.installed" "no" | |
| fi | |
| if have iptables; then | |
| kv "FW.iptables.rule_count_lines" "$( (sudo -n iptables -S 2>/dev/null || iptables -S 2>/dev/null) | wc -l | tr -d ' ' || echo "unknown")" | |
| else | |
| kv "FW.iptables.installed" "no" | |
| fi | |
| fi | |
| kv "LLM_CONTEXT.NETWORK" "Interpretation: NET.public_ip is best-effort external IP (useful for allowlists/remote access). Local IPs may differ due to NAT/VPN/containers. If FW indicates active firewall, inbound ports may need allow rules." | |
| # --------------------------- | |
| # OPEN PORTS | |
| # --------------------------- | |
| section "OPEN_PORTS" | |
| if have ss; then | |
| kv "OPEN_PORTS.method" "ss" | |
| kv "OPEN_PORTS.tcp_listen" "$(ss -tulpen 2>/dev/null | awk 'NR==1{next} $1 ~ /^tcp/ && $5 ~ /:/ {print $1" "$5" pid="$7}' | one_line)" | |
| elif have lsof; then | |
| kv "OPEN_PORTS.method" "lsof" | |
| kv "OPEN_PORTS.tcp_listen" "$(lsof -nP -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR==1{next}{print $1" "$9" pid="$2" user="$3}' | one_line)" | |
| elif have netstat; then | |
| kv "OPEN_PORTS.method" "netstat" | |
| kv "OPEN_PORTS.tcp_listen" "$(netstat -tlnp 2>/dev/null | awk 'NR<=2{next}{print $4" pid/prog="$7}' | one_line)" | |
| else | |
| kv "OPEN_PORTS.method" "none" | |
| kv "OPEN_PORTS.tcp_listen" "unknown_no_ss_lsof_netstat" | |
| fi | |
| kv "LLM_CONTEXT.OPEN_PORTS" "Interpretation: These are *listening* ports on this machine. If installing a service that needs a port already LISTENING, either stop the existing service or reconfigure the new service to another port." | |
| # --------------------------- | |
| # WEBSERVER CHECK | |
| # --------------------------- | |
| section "WEBSERVER_CHECK" | |
| if have ps; then | |
| kv "WEBSERVER.process_hits" "$(ps aux 2>/dev/null | grep -Ei 'nginx|apache2|httpd|caddy|traefik|lighttpd|openresty' | grep -v grep | head -n 20 | one_line || true)" | |
| fi | |
| if have systemctl; then | |
| kv "WEBSERVER.systemd_active" "$(systemctl list-units --type=service --state=running 2>/dev/null | grep -Ei 'nginx|apache2|httpd|caddy|traefik|lighttpd' | one_line || true)" | |
| elif have launchctl; then | |
| kv "WEBSERVER.launchd_matches" "$(launchctl list 2>/dev/null | grep -Ei 'nginx|apache|httpd|caddy|traefik|lighttpd' | one_line || true)" | |
| fi | |
| probe_ports=(80 443 3000 3001 8080 8081 8000 8888) | |
| if have curl; then | |
| for p in "${probe_ports[@]}"; do | |
| r1="$(curl -sS -I --max-time 2 "http://127.0.0.1:$p" 2>/dev/null | head -n 1 || true)" | |
| r2="$(curl -sS -I -k --max-time 2 "https://127.0.0.1:$p" 2>/dev/null | head -n 1 || true)" | |
| [[ -n "$r1" ]] && kv "WEBSERVER.http_head.${p}" "$r1" | |
| [[ -n "$r2" ]] && kv "WEBSERVER.https_head.${p}" "$r2" | |
| done | |
| else | |
| kv "WEBSERVER.http_probe" "skipped_curl_missing" | |
| fi | |
| kv "LLM_CONTEXT.WEBSERVER_CHECK" "Interpretation: If you see responses on 80/443/8080/etc, a web app or reverse proxy is already running and may conflict with new installs." | |
| # --------------------------- | |
| # SECURITY / LIMITS | |
| # --------------------------- | |
| section "SECURITY_CONTEXT" | |
| kv "SUDO.present" "$(have sudo && echo yes || echo no)" | |
| if have sudo; then | |
| kv "SUDO.noninteractive" "$(sudo -n true 2>/dev/null && echo YES || echo NO_or_PASSWORD_REQUIRED)" | |
| fi | |
| if [[ "$os_name" != "Darwin" ]]; then | |
| kv "SEC.selinux" "$( (have getenforce && getenforce 2>/dev/null) || echo "unknown/not_present")" | |
| kv "SEC.apparmor" "$( (have aa-status && aa-status 2>/dev/null | head -n 1) || (test -d /sys/kernel/security/apparmor && echo "present" || echo "not_present") )" | |
| fi | |
| kv "LLM_CONTEXT.SECURITY_CONTEXT" "Interpretation: If sudo is restricted, installations may need user-space paths (HOME) rather than system paths; SELinux/AppArmor can block services from binding ports or reading/writing certain paths." | |
| section "LIMITS" | |
| kv "ULIMIT.nofile" "$(ulimit -n 2>/dev/null || true)" | |
| kv "ULIMIT.nproc" "$(ulimit -u 2>/dev/null || true)" | |
| kv "ULIMIT.stack_kb" "$(ulimit -s 2>/dev/null || true)" | |
| kv "LLM_CONTEXT.LIMITS" "Interpretation: Very low ulimits can cause DB/server crashes or build failures." | |
| # --------------------------- | |
| # TOOLS CORE | |
| # --------------------------- | |
| section "TOOLS_CORE" | |
| for t in curl wget git openssl ssh make gcc g++ clang cmake pkg-config jq rsync tar unzip \ | |
| python python3 pip pip3 conda virtualenv \ | |
| node npm npx pnpm yarn corepack bun deno \ | |
| docker docker-compose podman kubectl helm terraform \ | |
| sqlite3 psql mysql mariadb mongod mongosh redis-server redis-cli \ | |
| ss netstat lsof ip ifconfig; do | |
| ver_line "$t" | |
| done | |
| kv "LLM_CONTEXT.TOOLS_CORE" "Interpretation: Missing core tooling is often the real reason installs fail; prefer installing missing prerequisites first." | |
| # --------------------------- | |
| # CONTAINERS (Docker detail) | |
| # --------------------------- | |
| section "CONTAINERS" | |
| if have docker; then | |
| kv "DOCKER.version" "$(docker --version 2>/dev/null || true)" | |
| kv "DOCKER.daemon_access" "$(docker info >/dev/null 2>&1 && echo "OK" || echo "NO_or_DAEMON_DOWN")" | |
| if docker info >/dev/null 2>&1; then | |
| kv "DOCKER.info.selected" "$(docker info 2>/dev/null | awk -F: ' | |
| /Server Version|Docker Root Dir|Storage Driver|Cgroup Driver|Operating System|Architecture|CPUs|Total Memory/{ | |
| gsub(/^[ \t]+/,"",$2); printf "%s=%s; ", $1, $2 | |
| }' | sed 's/; $//')" | |
| fi | |
| kv "DOCKER.context" "$(docker context show 2>/dev/null || true)" | |
| kv "DOCKER.compose_v2" "$(docker compose version 2>/dev/null | head -n1 || echo "unavailable")" | |
| fi | |
| kv "LLM_CONTEXT.CONTAINERS" "Interpretation: If DOCKER.daemon_access is not OK, container-based installs will fail until the daemon is running and permissions are fixed." | |
| # --------------------------- | |
| # SERVICES HINTS | |
| # --------------------------- | |
| section "SERVICES_HINTS" | |
| if have systemctl; then | |
| kv "SERVICES.running.matches" "$(systemctl list-units --type=service --state=running 2>/dev/null | grep -iE 'docker|containerd|mysql|postgres|mongo|redis|nginx|apache|ollama' | head -n 30 | one_line || true)" | |
| elif have launchctl; then | |
| kv "SERVICES.launchctl.matches" "$(launchctl list 2>/dev/null | grep -iE 'docker|mysql|postgres|mongo|redis|ollama' | head -n 30 | one_line || true)" | |
| else | |
| kv "SERVICES.manager" "unknown" | |
| fi | |
| kv "LLM_CONTEXT.SERVICES_HINTS" "Interpretation: Existing running services can explain port conflicts and suggest pre-installed components." | |
| # --------------------------- | |
| # NODE ECOSYSTEM | |
| # --------------------------- | |
| section "NODE_ECOSYSTEM" | |
| if have node; then | |
| kv "NODE.process.version" "$(node -p 'process.version' 2>/dev/null || true)" | |
| kv "NODE.process.platform" "$(node -p 'process.platform' 2>/dev/null || true)" | |
| kv "NODE.process.arch" "$(node -p 'process.arch' 2>/dev/null || true)" | |
| kv "NODE.process.versions" "$(node -p 'JSON.stringify(process.versions)' 2>/dev/null || true)" | |
| fi | |
| for m in nvm fnm volta asdf mise; do | |
| if have "$m"; then | |
| kv "NODE.mgr.${m}.path" "$(command -v "$m")" | |
| kv "NODE.mgr.${m}.version" "$("$m" --version 2>/dev/null | head -n1 || true)" | |
| else | |
| kv "NODE.mgr.${m}.installed" "no" | |
| fi | |
| done | |
| kv "NODE.nvm.NVM_DIR" "${NVM_DIR:-}" | |
| [[ -z "${NVM_DIR:-}" && -d "$HOME/.nvm" ]] && kv "NODE.nvm.dir_detected" "$HOME/.nvm" | |
| if have npm; then | |
| kv "NPM.version" "$(npm --version 2>/dev/null || true)" | |
| kv "NPM.prefix" "$(npm prefix -g 2>/dev/null || true)" | |
| kv "NPM.global_root" "$(npm root -g 2>/dev/null || true)" | |
| kv "NPM.cache" "$(npm config get cache 2>/dev/null || true)" | |
| kv "NPM.registry" "$(npm config get registry 2>/dev/null || true)" | |
| kv "NPM.proxy" "$(npm config get proxy 2>/dev/null || true)" | |
| kv "NPM.https_proxy" "$(npm config get https-proxy 2>/dev/null || true)" | |
| kv "NPM.strict_ssl" "$(npm config get strict-ssl 2>/dev/null || true)" | |
| kv "NPM.cafile" "$(npm config get cafile 2>/dev/null || true)" | |
| kv "NPM.userconfig" "$(npm config get userconfig 2>/dev/null || true)" | |
| kv "NPM.globalconfig" "$(npm config get globalconfig 2>/dev/null || true)" | |
| fi | |
| if have pnpm; then | |
| kv "PNPM.version" "$(pnpm --version 2>/dev/null || true)" | |
| kv "PNPM.store_path" "$(pnpm store path 2>/dev/null || true)" | |
| fi | |
| if have yarn; then | |
| kv "YARN.version" "$(yarn --version 2>/dev/null || true)" | |
| kv "YARN.registry" "$(yarn config get registry 2>/dev/null || true)" | |
| fi | |
| if have corepack; then | |
| kv "COREPACK.version" "$(corepack --version 2>/dev/null || true)" | |
| kv "COREPACK.status_hint" "$(corepack -v >/dev/null 2>&1 && echo "present" || echo "present_but_restricted")" | |
| fi | |
| kv "LLM_CONTEXT.NODE_ECOSYSTEM" "Interpretation: NODE.mgr.* indicates Node version management that can override system node. NPM.* fields reveal registry/proxy/cert settings that commonly break installs in corporate networks." | |
| section "NODE_NATIVE_BUILD_PREREQS" | |
| if [[ "$os_name" == "Darwin" ]]; then | |
| kv "XCODE.select_path" "$(xcode-select -p 2>/dev/null || echo "missing")" | |
| kv "CLANG.version" "$(have clang && clang --version 2>/dev/null | head -n1 || echo "missing")" | |
| else | |
| kv "PYTHON3.version" "$(have python3 && python3 --version 2>/dev/null || echo "missing")" | |
| kv "MAKE.version" "$(have make && make --version 2>/dev/null | head -n1 || echo "missing")" | |
| kv "GCC.version" "$(have gcc && gcc --version 2>/dev/null | head -n1 || echo "missing")" | |
| fi | |
| kv "LLM_CONTEXT.NODE_NATIVE_BUILD_PREREQS" "Interpretation: Missing build tools cause node-gyp failures when installing native modules." | |
| # --------------------------- | |
| # ENV VARS | |
| # --------------------------- | |
| section "ENV_VARS" | |
| kv "ENV.PATH" "${PATH:-}" | |
| kv "ENV.HOME" "${HOME:-}" | |
| kv "ENV.LANG" "${LANG:-}" | |
| kv "ENV.TERM" "${TERM:-}" | |
| kv "ENV.HTTP_PROXY" "${HTTP_PROXY:-}" | |
| kv "ENV.HTTPS_PROXY" "${HTTPS_PROXY:-}" | |
| kv "ENV.NO_PROXY" "${NO_PROXY:-}" | |
| kv "ENV.NPM_CONFIG_REGISTRY" "${NPM_CONFIG_REGISTRY:-}" | |
| kv "ENV.NODE_OPTIONS" "${NODE_OPTIONS:-}" | |
| kv "ENV.DOCKER_HOST" "${DOCKER_HOST:-}" | |
| kv "ENV.JAVA_HOME" "${JAVA_HOME:-}" | |
| kv "ENV.GOPATH" "${GOPATH:-}" | |
| kv "ENV.CUDA_HOME" "${CUDA_HOME:-}" | |
| kv "ENV.LD_LIBRARY_PATH" "${LD_LIBRARY_PATH:-}" | |
| kv "ENV.VIRTUAL_ENV" "${VIRTUAL_ENV:-}" | |
| kv "ENV.CONDA_DEFAULT_ENV" "${CONDA_DEFAULT_ENV:-}" | |
| kv "LLM_CONTEXT.ENV_VARS" "Interpretation: These env vars can silently alter installs and runtime; proxies/registries are especially important." | |
| echo | |
| echo "===== END find-environment =====" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment