新しいリリースv0.12.4Aug 19, 2026

Boostの新しいバージョンをリリースしましたSmarter savings and cleaner output

$ customer-installation

Jamf Pro MDM で Boost を展開

これらの Jamf Pro アセットで管理 macOS フリートに Boost をインストールし、結果をインベントリ化します。スクリプトは docs/customer-installation/mdm/jamf-pro にあり、このページはリポジトリのファイルを直接表示するため、次のビルドでサイトが更新されます。

テスト済み環境

この展開パスは Jamf Pro でテスト済みです。将来の MDM プロバイダーは docs/customer-installation/mdm/<provider> 配下で同じソースレイアウトに従ってください。


ロールアウトの仕組み

1

インストールスクリプトを追加

install.sh から Jamf Pro スクリプトを作成します。ログイン中のコンソールユーザーに Boost をインストールし、選択したエージェント統合を初期化し、Jamf インベントリを更新します。

2

デプロイポリシーを作成

対象 Mac で root としてスクリプトを実行します。Jamf パラメータ 4 で Boost 統合を選び、アクティブなコンソールユーザーがいない Mac には Login トリガーを組み合わせます。

3

拡張属性を追加

extension-attribute.sh から Jamf Pro Extension Attribute を作成し、インストール状態、バージョン、パス、統合、検出 AI ツール、収集時刻を表示します。


Jamf Pro ポリシー設定

Jamf Pro でインストーラーをスクリプトとして追加し、Boost を受け取る Mac にスコープしたポリシーに添付します。ポリシーはべき等に保ち、再実行でバイナリを更新し選択した統合を再適用します。

スクリプト
install.sh を Jamf Pro スクリプトに貼り付けます。
実行ユーザー
Root。boost init のためにログイン中のユーザーに切り替えます。
デフォルトインストールパス
コンソールユーザーの $HOME/.local/bin/boost
パラメータ 4
任意の空白区切り Boost ターゲット(例: cursor claude)。

ターゲット例

ロールアウトJamf パラメータ 4
デフォルトのローカルエージェント展開cursor claude
Cursor、Claude、Codexcursor claude codex
CLI ファーストチームcodex
docs/customer-installation/mdm/jamf-pro/install.sh
#!/bin/bash
# Boost CLI - Jamf install (per-user, macOS).
# Installs to $HOME/.local/bin/boost for the console user, runs `boost init`
# as them, removes the legacy /usr/local/bin/boost if present, and refreshes
# Jamf inventory so the Boost CLI Status EA reflects this run immediately.
#
# Optional:
#   BOOST_TARGETS      default: "cursor claude"
#   Jamf parameter $4  same as BOOST_TARGETS
set -euo pipefail

repo="jfrog/boost"
targets="${4:-${BOOST_TARGETS:-cursor claude}}"

log() { printf '[boost] %s\n' "$*"; }
die() { log "ERROR: $*" >&2; exit 1; }

# curl with retries — handles transient corporate-network resets (curl 56,
# 7, 18, 35, …). --retry-all-errors covers mid-stream resets that plain
# --retry won't catch.
fetch() {
  curl -fsSL \
    --retry 5 --retry-delay 2 --retry-all-errors \
    --connect-timeout 10 --max-time 300 \
    "$@"
}

# Repair root-owned leftovers in the dirs `boost init` writes to. Earlier
# POC runs may have created these as root, leaving the user unable to
# overwrite them. Recursive chown is safe: if the dir itself is root-owned,
# the user couldn't have created files inside it anyway.
normalize_boost_paths() {
  local user="$1" home="$2" path owner
  for path in \
    "$home/.local" \
    "$home/.boost" \
    "$home/.claude" \
    "$home/.cursor" \
    "$home/.codex"; do
    [ -e "$path" ] || continue
    owner="$(/usr/bin/stat -f '%Su' "$path" 2>/dev/null || true)"
    if [ "$owner" != "$user" ]; then
      log "fixing ownership: $path (was $owner)"
      /usr/sbin/chown -R "$user" "$path" || log "chown failed for $path (non-fatal)"
    fi
  done
}

# ---- Console user is required for per-user install ----------------------
user="$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || true)"
if [ -z "$user" ] || [ "$user" = "root" ] || [ "$user" = "loginwindow" ]; then
  log "no console user; per-user install needs a logged-in account — skipping"
  # Exit 0 so Jamf doesn't flag this as a failure and storm retries.
  # Pair with a Login trigger on the policy to catch this machine later.
  exit 0
fi
uid="$(/usr/bin/id -u "$user")"
home="$(/usr/bin/dscl . -read "/Users/$user" NFSHomeDirectory 2>/dev/null | /usr/bin/awk '{print $2}')"
[ -n "$home" ] || home="/Users/$user"
install_dir="$home/.local/bin"
boost="$install_dir/boost"

# ---- Architecture --------------------------------------------------------
arch="$(uname -m)"
case "$arch" in arm64|aarch64) arch=arm64 ;; x86_64|amd64) arch=amd64 ;; *) die "unsupported arch: $arch" ;; esac
archive="boost-darwin-${arch}.tar.gz"

# ---- Resolve latest tag and download ------------------------------------
tag="$(fetch -I -o /dev/null -w '%{url_effective}' "https://github.com/$repo/releases/latest" | sed 's#.*/tag/##')"
[ -n "$tag" ] || die "could not resolve latest Boost release"

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

log "downloading $archive ($tag)"
fetch "https://github.com/$repo/releases/download/$tag/$archive" -o "$tmp/$archive"
tar -xzf "$tmp/$archive" -C "$tmp"
[ -f "$tmp/boost" ] || die "archive missing boost binary"

# ---- Install into the user's ~/.local/bin, owned by them ----------------
/bin/mkdir -p "$install_dir"
/usr/sbin/chown "$user" "$home/.local" "$install_dir" 2>/dev/null || true
/usr/bin/install -o "$user" -g staff -m 0755 "$tmp/boost" "$boost"
log "installed $(/usr/bin/sudo -H -u "$user" "$boost" version 2>/dev/null || echo "$tag") at $boost"

# ---- Remove legacy system install so the EA reports the right path ------
# The EA prefers $HOME/.local/bin/boost but falls back to /usr/local/bin/boost.
# Leaving the old one around means PATH lookups for the user may still hit
# the stale binary depending on shell config. Self-cleanup on every run.
if [ -e /usr/local/bin/boost ]; then
  log "removing legacy /usr/local/bin/boost"
  /bin/rm -f /usr/local/bin/boost || log "legacy removal failed (non-fatal)"
fi

# ---- Fix any root-owned config leftovers before init --------------------
normalize_boost_paths "$user" "$home"

# ---- Run `boost init` as the console user -------------------------------
flags=(--accept-terms)
for target in $targets; do flags+=(--"$target"); done
log "running '$boost init ${flags[*]}' as $user"
/bin/launchctl asuser "$uid" /usr/bin/sudo -H -u "$user" \
  /usr/bin/env HOME="$home" USER="$user" LOGNAME="$user" \
  "$boost" init "${flags[@]}"

# ---- Push Jamf inventory so the EA picks up this run --------------------
if [ -x /usr/local/bin/jamf ]; then
  log "triggering jamf recon"
  /usr/local/bin/jamf recon >/dev/null 2>&1 || log "recon failed (non-fatal)"
fi

Raw source: install.sh

docs/customer-installation/mdm/jamf-pro/extension-attribute.sh
#!/bin/bash
# Boost CLI — Jamf Pro Extension Attribute
#
# Looks for boost in this priority order:
#   1. $home/.local/bin/boost   (per-user, current layout)
#   2. /usr/local/bin/boost     (legacy fallback during migration)
#   3. /Users/*/.local/bin/boost (last resort if no console user)
#
# IMPORTANT: EA values refresh ONLY when `jamf recon` runs. The Boost
# install script must call `/usr/local/bin/jamf recon` at its end (or the
# install policy must include a Maintenance → Update Inventory payload),
# otherwise this attribute will keep showing the previously-recorded value.
#
# Deployment:
#   Jamf Pro → Settings → Computer Management → Extension Attributes → New
#     Display Name: Boost CLI Status
#     Inventory:    Applications
#     Data Type:    String
#     Input Type:   Script

set -u
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin"

home=""
boost=""
boost_scope=""

emit() { printf '%s=%s\n' "$1" "$2"; }

resolve_home_for() {
  local u="$1" h
  h="$(/usr/bin/dscl . -read "/Users/$u" NFSHomeDirectory 2>/dev/null | /usr/bin/awk '{print $2}')"
  [ -z "$h" ] && h="/Users/$u"
  printf '%s' "$h"
}

have_cli() {
  local name="$1" p
  for p in \
    "$home/.local/bin/$name" \
    "/usr/local/bin/$name" \
    "/opt/homebrew/bin/$name" \
    "$home/.npm-global/bin/$name" \
    "$home/.bun/bin/$name" \
    "$home/.volta/bin/$name"; do
    [ -x "$p" ] && return 0
  done
  return 1
}

echo "<result>"

# ---- EA metadata (most useful field for diagnosing stale inventory) ----
emit ea_collected_at "$(/bin/date '+%Y-%m-%dT%H:%M:%S%z')"

# ---- System --------------------------------------------------------------
emit os       "$(/usr/bin/sw_vers -productName 2>/dev/null) $(/usr/bin/sw_vers -productVersion 2>/dev/null)"
emit os_build "$(/usr/bin/sw_vers -buildVersion 2>/dev/null)"
emit arch     "$(/usr/bin/uname -m 2>/dev/null)"

# ---- Console user --------------------------------------------------------
console_user="$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || echo "")"
if [ -z "$console_user" ] || [ "$console_user" = "root" ] || [ "$console_user" = "loginwindow" ]; then
  emit console_user none
  console_user=""
else
  emit console_user "$console_user"
  emit console_uid  "$(/usr/bin/id -u "$console_user")"
  home="$(resolve_home_for "$console_user")"
  emit home  "$home"
  shell="$(/usr/bin/dscl . -read "/Users/$console_user" UserShell 2>/dev/null | /usr/bin/awk '{print $2}')"
  emit shell "${shell:-unknown}"
fi

# ---- Locate boost (priority: console user → system → other users) -------
if [ -n "$home" ] && [ -x "$home/.local/bin/boost" ]; then
  boost="$home/.local/bin/boost"
  boost_scope="user"
elif [ -x "/usr/local/bin/boost" ]; then
  boost="/usr/local/bin/boost"
  boost_scope="system"
else
  # No console user, or console user has nothing — scan other user homes
  # so overnight recon on an unattended Mac still finds boost.
  for udir in /Users/*/; do
    [ -d "$udir" ] || continue
    [ "${udir%/}" = "/Users/Shared" ] && continue
    [ "${udir%/}" = "$home" ]         && continue
    cand="${udir%/}/.local/bin/boost"
    if [ -x "$cand" ]; then
      boost="$cand"
      boost_scope="other_user"
      [ -z "$home" ] && home="${udir%/}"
      break
    fi
  done
fi

if [ -z "$boost" ]; then
  emit status not_installed
  echo "</result>"
  exit 0
fi

# ---- Boost binary report -------------------------------------------------
emit status      installed
emit boost_path  "$boost"
emit boost_scope "$boost_scope"
emit boost_mtime "$(/bin/date -r "$boost" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null)"
emit boost_sha12 "$(/usr/bin/shasum -a 256 "$boost" 2>/dev/null | /usr/bin/awk '{print substr($1,1,12)}')"

# Pass HOME so a per-user binary reads the right config dir, not /var/root.
if [ -n "$home" ] && [ -d "$home" ]; then
  raw_version="$(/usr/bin/env HOME="$home" "$boost" version 2>/dev/null | /usr/bin/head -n1 | /usr/bin/tr -d '\r')"
else
  raw_version="$("$boost" version 2>/dev/null | /usr/bin/head -n1 | /usr/bin/tr -d '\r')"
fi
parsed_version="$(printf '%s\n' "$raw_version" | /usr/bin/grep -Eo 'v?[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9]+)*' | /usr/bin/head -n1)"
emit version     "${parsed_version:-unknown}"
emit version_raw "${raw_version:-empty}"

# ---- Integrations + AI tools (need a home dir) --------------------------
if [ -n "$home" ] && [ -d "$home" ]; then
  integrations=()
  [ -f "$home/.cursor/hooks/boost-rewrite.sh" ] && integrations+=("cursor")
  [ -f "$home/.claude/hooks/boost-rewrite.sh" ] && integrations+=("claude")
  [ -f "$home/.codex/BOOST.md" ]                && integrations+=("codex")
  if [ ${#integrations[@]} -eq 0 ]; then
    emit boost_integrations none
  else
    IFS=','; emit boost_integrations "${integrations[*]}"; unset IFS
  fi

  ai_tools=()
  [ -d "/Applications/Cursor.app" ] && ai_tools+=("cursor_app")
  [ -d "/Applications/Claude.app" ] && ai_tools+=("claude_app")
  have_cli claude   && ai_tools+=("claude_cli")
  have_cli codex    && ai_tools+=("codex_cli")
  if [ ${#ai_tools[@]} -eq 0 ]; then
    emit ai_tools none
  else
    IFS=','; emit ai_tools "${ai_tools[*]}"; unset IFS
  fi

  if [ -f "$home/.boost/config.toml" ]; then
    emit config_global present
  else
    emit config_global absent
  fi

  last_init=""
  for ag in cursor claude codex; do
    hook="$home/.$ag/hooks/boost-rewrite.sh"
    if [ -f "$hook" ]; then
      last_init="$(/bin/date -r "$hook" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null)"
      [ -n "$last_init" ] && break
    fi
  done
  [ -n "$last_init" ] && emit last_init "$last_init"
fi

echo "</result>"

Raw source: extension-attribute.sh


Jamf Pro インベントリ

拡張属性を追加し、Boost のインストール有無、バージョン、バイナリの場所、アクティブな統合、コンソールユーザーで検出された AI ツールを Jamf インベントリに報告します。

表示名
Boost CLI Status
インベントリ
Applications
データ型
String
入力タイプ
Script

Smart Group の例

  • status=not_installed は Boost バイナリのない Mac を検出します。
  • version=v0.6.4 は古いリリースのマシンを特定できます。
  • ai_tools contains cursorboost_integrations does not contain cursor で Boost フックのない Cursor ユーザーを見つけます。

運用上の注意

  • Boost は $HOME/.local/bin 配下にユーザー単位でインストールされるため、ログイン中のコンソールユーザーが必要です。
  • コンソールユーザーがいない場合、スクリプトはインストールせず正常終了します。後で捕捉するには Login トリガーを使います。
  • レガシー /usr/local/bin/boost を削除し、シェルと拡張属性がユーザーインストールを優先します。
  • boost init --accept-terms の前に、以前の実行で root 所有になった Boost とエージェント設定ディレクトリを修復します。
  • Jamf が利用可能なら jamf recon を実行し、拡張属性が最新のインストールを即反映します。

関連ドキュメント

個別の開発者セットアップは Boost クイックスタート を参照。インストールフローの詳細は インストールフロー仕様 を参照。